Skip to content
This repository was archived by the owner on Jun 9, 2026. It is now read-only.

Latest commit

 

History

History
1824 lines (1482 loc) · 103 KB

File metadata and controls

1824 lines (1482 loc) · 103 KB

Iteration Log

This file records each meaningful implementation, fix, refactor, and experiment. It is intended to support later review, decision traceability, and project history reconstruction.

Use the template below for every new entry. Append new entries to the end of the file and keep earlier entries intact.

YYYY-MM-DD HH:mm - Iteration Title

Goal

Changes Made

Why

Validation

Current Conclusion

Next Step

2026-04-11 00:20 - Add Iteration Logging Rule

Goal

  • Establish an append-only Markdown log for every meaningful implementation, fix, refactor, or experiment in the repo.

Changes Made

  • Added an iteration logging requirement to AGENTS.md.
  • Created docs/iteration_log.md with a reusable entry template.
  • Added a short reminder to README.md so the rule is visible from the main project overview.

Why

  • The project needs a durable record of design decisions, implementation intent, and validation outcomes.
  • A consistent log makes it easier to review regressions, reconstruct context, and understand why changes were made later.

Validation

  • Verified that the new rule was appended without removing existing AGENTS content.
  • Verified that the log file contains a title, description, and reusable template.
  • Verified that the README addition is a minimal, non-disruptive note.

Current Conclusion

  • The repository now has a clear, low-friction process for recording each iteration in Markdown.

Next Step

  • Use this file for the next meaningful change, and keep appending new entries instead of overwriting previous ones.

2026-04-11 01:XX - Add Query Normalization Experiment Scaffold

Goal

  • Add a small, experimental query normalization / bilingual expansion layer for the existing 19 retrieval regression queries without touching embedding, indexing, reranking, or the production default retrieval path.

Changes Made

  • Added backend/app/retrieval/query_normalization.py with a tiny explicit rule set for working student, internship, photovoltaic/solar/PV, Go/Golang, IoT/IIoT, DevOps/CI/CD/platform/Kubernetes, backend developer/engineer, and software developer/engineer variants.
  • Added backend/scripts/experiment_query_normalization.py to compare baseline vs normalized retrieval on the existing 19-query regression set and to emit experiment-ready JSON/Markdown outputs.
  • Kept the experiment isolated from production retrieval code paths.

Why

  • The current baseline shows both-hit candidate formation is the main retrieval weakness, especially for sparse and cross-lingual queries.
  • A small, interpretable preprocessing layer is the lowest-risk way to test whether query-side expansion can improve both-hit formation without changing the underlying retrieval stack.

Validation

  • Attempted to validate the new scripts with local Python tooling, but the host environment does not expose a working Python interpreter.
  • Attempted to use Docker and WSL for execution, but Docker daemon access is denied and WSL service creation is blocked in this sandbox.
  • Because execution was blocked, the experiment could not be run end-to-end and no new results files were generated yet.

Current Conclusion

  • The implementation scaffold is in place, but the experiment remains unvalidated due environment restrictions rather than code defects.

Next Step

  • Re-run backend/scripts/experiment_query_normalization.py in an environment with a working Python runtime or Docker daemon, then persist docs/query_normalization_experiment.md and output/query_normalization_experiment.json.

2026-04-11 00:47 - Prepare Version Lock Snapshot

Goal

  • Turn the current working tree into a clean, versioned snapshot that can be committed and tagged without dragging in local build artifacts.

Changes Made

  • Added ignore rules for local runtime environments, Node dependencies, Playwright caches, generated logs, smoke test artifacts, and other transient workspace outputs in .gitignore.
  • Kept the existing code, planning, and documentation changes intact for the upcoming commit.

Why

  • The repository had meaningful implementation changes mixed with local-only artifacts such as node_modules/, .venv311/, logs, screenshots, and ad hoc smoke-test files.
  • A version lock is more useful when the commit boundary excludes machine-specific noise and only captures durable project state.

Validation

  • Reviewed the current git status and confirmed the workspace contains both tracked changes and untracked local artifacts.
  • Verified the ignore list now covers the most visible generated directories and files.

Current Conclusion

  • The tree is better suited for a clean commit and future checkout because transient development outputs are no longer meant to be tracked.

Next Step

  • Stage the intended source, planning, and documentation changes, create the commit, and add a git tag for the locked snapshot.

2026-04-11 01:03 - Environment Acceptance and Minimal Run Check

Goal

  • Verify whether the migrated Python environment is actually usable and determine whether the query normalization experiment can be executed in the current workspace.

Changes Made

  • Performed a read-only environment check on backend\.venv311\Scripts\python.exe.
  • Located the query normalization experiment script at backend/scripts/experiment_query_normalization.py.
  • Checked whether docs/query_normalization_experiment.md and output/query_normalization_experiment.json already exist and are non-empty.

Why

  • The repository is in retrieval quality hardening, so the immediate need was to verify runtime readiness rather than extend functionality.
  • The migrated interpreter path was expected to replace the broken old environment, but that claim needed direct validation before any further work.

Validation

  • backend\.venv311\Scripts\python.exe -V failed with: Unable to create process using '"C:\Users\LIU\AppData\Local\Programs\Python\Python311\python.exe" -V'
  • backend\.venv311\Scripts\python.exe -c "import sys; print(sys.executable)" failed with the same launcher error.
  • where.exe python returned no system Python installation.
  • py -0p reported: No installed Pythons found!
  • Verified that backend/scripts/experiment_query_normalization.py exists.
  • Verified that docs/query_normalization_experiment.md exists and is non-empty.
  • Verified that output/query_normalization_experiment.json exists and is non-empty.

Current Conclusion

  • The migrated backend\.venv311 interpreter is not currently runnable because it still points at a missing base Python 3.11 installation.
  • The experiment artifacts are already present on disk and non-empty, but this round did not successfully re-run the experiment, so the experiment cannot be marked complete.

Next Step

  • Restore a valid Python 3.11 base runtime for backend\.venv311 or otherwise repair the launcher target, then rerun the experiment and re-validate both artifacts before declaring success.

2026-04-11 15:30 - Environment Reconstruction and Query Normalization Experiment Run

Goal

  • Rebuild a working Python environment from scratch (Python 3.11.9 + new venv at backend/.venv311), install all backend dependencies, and execute the query normalization experiment script to validate the bilingual term expansion layer against the 19 regression queries.

Changes Made

  • Created backend/.venv311 using py -3.11 -m venv backend/.venv311.
  • Upgraded pip/setuptools/wheel in the new venv.
  • Installed insightflow-backend in editable mode via pip install -e backend/, pulling all dependencies from pyproject.toml (fastapi, faiss-cpu, httpx, datasets, pydantic, sqlalchemy, uvicorn, etc.).
  • Ran backend/scripts/experiment_query_normalization.py using backend/.venv311/Scripts/python.exe.
  • Verified both experiment artifacts were written:
    • output/query_normalization_experiment.json (1 197 707 bytes)
    • docs/query_normalization_experiment.md (21 323 bytes)

Why

  • The old backend/.venv was corrupted because its base Python interpreter no longer existed, and files were locked by a running process, preventing deletion.
  • The previous iteration log entry recorded the experiment as "unvalidated due to environment restrictions." The environment needed to be rebuilt before the experiment could be run.
  • All scope boundaries were respected: no embedding changes, no index rebuilds, no reranker changes, no frontend changes.

Validation

  • backend/.venv311/Scripts/python.exe -V → Python 3.11.9 confirmed.
  • import fastapi, import numpy, import faiss all succeeded.
  • from backend.app.main import app succeeded from project root.
  • Script emitted both files without error: JSON at output/, MD at docs/.
  • Both output files are non-empty (JSON ~1.2 MB, MD ~21 KB).

Current Conclusion

  • Environment reconstruction is complete and fully functional.
  • Query normalization experiment ran end-to-end successfully and produced both expected artifacts.

Next Step

  • Review docs/query_normalization_experiment.md for the A/B/C conclusion and the recommended next step from the experiment summary, then decide whether to adopt the bilingual expansion rules as a production retrieval preprocessing step.

2026-04-11 15:38 - Environment Re-verification and Experiment Re-run

Goal

  • Confirm the backend/.venv311 environment is functional and that the query normalization experiment can be re-run successfully, producing fresh artifacts.

Changes Made

  • Verified py -0p shows Python 3.11.9 and 3.13.3 both available.
  • Confirmed py -3.11 -V returns Python 3.11.9 successfully.
  • Confirmed backend/.venv311/Scripts/python.exe -V works (Python 3.11.9).
  • Ran backend/scripts/experiment_query_normalization.py using backend/.venv311/Scripts/python.exe.
  • Both artifacts overwritten with fresh timestamps:
    • output/query_normalization_experiment.json (1 197 707 bytes, mtime 2026-04-11T01:30:08)
    • docs/query_normalization_experiment.md (21 323 bytes, mtime 2026-04-11T01:30:08)

Why

  • System reminders indicated possible environment instability; re-verified all steps end-to-end.
  • Confirmed no old backend/.venv files were copied or migrated — all dependencies resolved from pyproject.toml.

Validation

  • py -3.11 -V → Python 3.11.9 confirmed.
  • py -0p → Python 3.11 and 3.13 both listed and functional.
  • backend/.venv311/Scripts/python.exe -c "import fastapi,numpy,faiss,pydantic,httpx,sqlalchemy" → all ok.
  • from backend.app.main import app → ok.
  • Both experiment artifacts regenerated and verified non-empty with current timestamps.

Current Conclusion

  • Environment is fully functional and reproducible.
  • Experiment re-run confirmed — both artifacts are fresh, not stale.

Next Step

  • Read docs/query_normalization_experiment.md for the experiment conclusion (A/B/C grade and recommended next step), then decide whether the bilingual term expansion layer should be promoted to the production retrieval path.

2026-04-11 01:53 - Query Normalization Decision Hold

Goal

  • Close the decision loop on whether query normalization / bilingual term expansion should enter the formal retrieval preprocessing path in InsightFlow V1.

Changes Made

  • Added a decision record only; no business logic, retrieval code, or frontend code was changed.
  • Captured the current policy position for the query normalization candidate inside the project iteration log.

Why

  • The experiment showed clear upside in a subset of sparse and role-specific queries, but the gains are not yet stable enough for unconditional default-on rollout.
  • Cross-lingual coverage remains weaker and sample-limited, and there is still localized drift risk in precision and source card focus.
  • That makes the feature worth keeping as a gated capability, but not as a global preprocessing default.

Validation

  • Reviewed the completed experiment artifacts and the decision analysis already produced from docs/query_normalization_experiment.md and output/query_normalization_experiment.json.
  • Confirmed the decision state is based on completed evidence, not on a new run or a new metric pass.

Current Conclusion

  • Hold.
  • Query normalization does not enter global retrieval preprocessing by default.
  • The capability remains a gated option / candidate capability for controlled use.

Next Step

  • If this direction is revisited later, only validate controlled enablement.
  • Prioritize broader cross-lingual samples.
  • Keep watching both-hit formation, top-k focus, and source card drift.
  • Do not turn it on globally unless the decision changes.

2026-04-11 17:10 - Gated Normalization Implementation and Validation Blocker

Goal

  • Shrink query normalization from a full-candidate capability into an explicit off | gated | on retrieval option, keep the default at off, preserve normalization decision fields in retrieval trace, and re-run the 19-query regression comparison for off vs gated.

Changes Made

  • Reworked backend/app/retrieval/query_normalization.py into an explicit normalization decision layer with:
    • off, gated, and on modes
    • explicit bilingual and technical alias families
    • explainable matched-family and applied-decision outputs
  • Threaded normalization mode through retrieval and summary request paths:
    • backend/app/schemas/retrieval.py
    • backend/app/schemas/summary.py
    • backend/app/retrieval/hybrid.py
    • backend/app/services/summarizer.py
    • backend/app/observability/trace_types.py
    • backend/app/observability/trace_builder.py
  • Added retrieval/search response fields and retrieval trace fields for:
    • raw_query
    • normalized_query
    • normalization_applied
    • matched_rule_families
    • normalization_mode
  • Updated backend/scripts/experiment_query_normalization.py to compare off vs gated and target the new metrics:
    • both-hit delta
    • top-k both-hit delta
    • trigger coverage
    • useful trigger rate
    • harm rate
    • source card drift count
  • Added targeted retrieval tests in backend/tests/test_retrieval_hybrid_api.py for gated normalization behavior and retrieval API normalization fields.

Why

  • The prior decision was Hold for global normalization rollout: local gains existed, but cross-lingual evidence was still thin and precision / source-card drift risk remained.
  • That made a fully enabled default inappropriate, while still leaving room for a controlled gated option in the retrieval layer.
  • The implementation therefore narrowed the capability to an explicit, reversible retrieval decision rather than a silent default behavior change.

Validation

  • Static validation completed by re-reading all modified source files after patching:
    • backend/app/retrieval/query_normalization.py
    • backend/app/retrieval/hybrid.py
    • backend/app/services/summarizer.py
    • backend/scripts/experiment_query_normalization.py
    • backend/tests/test_retrieval_hybrid_api.py
  • Attempted to run retrieval tests with backend\.venv311\Scripts\python.exe, but execution failed with:
    • Unable to create process using '"C:\Users\LIU\AppData\Local\Programs\Python\Python311\python.exe" ...'
  • Verified the underlying launcher problem is coupled with sandbox restrictions:
    • direct execution of C:\Users\LIU\AppData\Local\Programs\Python\Python311\python.exe returned Access is denied
    • direct directory access / copy attempts against that runtime also returned Access is denied
    • attempted hard-link creation into the workspace also failed with Access is denied
  • Because of the runtime blocker, this round did not successfully run:
    • pytest validation
    • the updated off vs gated experiment script
    • new experiment JSON / markdown artifact generation

Current Conclusion

  • The gated normalization code path has been implemented, but this round is not complete.
  • The hard requirement to run the real experiment and generate fresh artifacts was not met because Python execution is blocked in the current sandbox/runtime state.
  • Default behavior remains safely aligned with the requested policy because the new normalization mode defaults to off.

Next Step

  • Restore a Python execution path that is runnable inside the workspace sandbox, then:
    • run the retrieval tests
    • execute backend/scripts/experiment_query_normalization.py
    • generate the new off vs gated report and JSON artifacts
    • review the triggered-query drift cases before deciding whether gated is stable enough as a non-default option

2026-04-11 18:55 - Lexical Hardening Experiment

Goal

  • Run a precision-preserving retrieval hardening experiment that does not change query semantics and does not touch embeddings, reranker, frontend, or summary generation.
  • Focus only on lightweight lexical retrieval changes, starting with phrase-aware plus field-aware keyword scoring, and compare the default baseline against a single explicit lexical hardening mode across the existing 19 regression queries.

Changes Made

  • Added a new lexical retrieval mode scaffold in backend/app/retrieval/keyword.py:
    • off
    • phrase_field_v1
  • Implemented a minimal phrase-aware and field-aware lexical scoring layer on top of existing keyword retrieval:
    • explicit preferred phrase list
    • lightweight title / tags / location / chunk_type field bonuses
    • no query expansion
    • no synonym injection
    • no embedding changes
  • Kept the hardening precision-preserving by removing candidate-pool widening and limiting the lexical bonus to a small capped rescore.
  • Threaded lexical_hardening_mode through retrieval and summary request/response paths:
    • backend/app/schemas/retrieval.py
    • backend/app/schemas/summary.py
    • backend/app/retrieval/hybrid.py
    • backend/app/services/summarizer.py
    • backend/app/observability/trace_types.py
    • backend/app/observability/trace_builder.py
    • backend/app/api/retrieval.py
  • Added / updated tests for the lexical hardening mode and trace propagation:
    • backend/tests/test_retrieval_hybrid_api.py
    • backend/tests/test_reranker_integration.py
  • Added a new experiment script:
    • backend/scripts/experiment_lexical_hardening.py
  • Generated new experiment artifacts:
    • output/lexical_hardening_experiment.json
    • docs/lexical_hardening_experiment.md
  • To unblock validation inside the sandbox, downloaded the official Python 3.11 embeddable runtime into the workspace and configured python311._pth so the workspace-local interpreter can import the existing backend environment.

Why

  • Query normalization / expansion is now frozen as a non-mainline scaffold because source-card focus drift was too high.
  • The next retrieval-quality lever needed to preserve original query meaning and instead improve lexical focus and phrase retention.
  • Phrase-aware plus field-aware keyword scoring was chosen as the smallest explainable change that could be rolled back cleanly if it failed.
  • The first scoring draft was too aggressive and accidentally expanded the effective candidate surface via an enlarged keyword pool; that behavior was removed to stay aligned with the "do not expand recall, preserve focus" goal.

Validation

  • Verified the workspace-local embedded Python runtime runs successfully:
    • ./python.exe -V -> Python 3.11.9
    • ./python.exe -c "import sys, pytest; print(sys.version); print(pytest.__version__)"
  • Ran targeted retrieval tests:
    • ./python.exe -m pytest backend/tests/test_retrieval_hybrid_api.py -q -> 11 passed
    • ./python.exe -m pytest backend/tests/test_reranker_integration.py -q -> 29 passed
  • Ran the lexical hardening experiment end-to-end:
    • ./python.exe backend/scripts/experiment_lexical_hardening.py
  • Verified fresh artifact generation:
    • output/lexical_hardening_experiment.json
    • docs/lexical_hardening_experiment.md
  • Current aggregate experiment result from the generated report:
    • average both-hit delta: 0.000
    • average top-k both-hit delta: 0.263
    • average source-card focus delta: 0.013
    • source-card drift count: 7
    • improved queries: 0/19
    • harmed queries: 8/19
    • sparse subset top-k both-hit delta: 0.333

Current Conclusion

  • The lightweight lexical hardening version is much safer than the first overly aggressive draft, but it still does not produce a clearly positive enough win to promote immediately.
  • The experiment improved or preserved both-hit behavior on average without reopening query expansion, and sparse queries were slightly more stable, but source-card drift is still present in 7 of 19 queries and there are no clean "promote now" wins.
  • This means the lexical hardening path remains a valid mainline investigation, but this specific phrase_field_v1 variant should stay experimental rather than replace the baseline today.

Next Step

  • Review the 7 drifted queries and classify whether the remaining drift mostly comes from:
    • phrase bonuses on role titles
    • title-vs-tags weighting
    • over-sensitive source-card ordering on near-tie results
  • If there is a clear dominant failure pattern, run one narrower follow-up iteration that further reduces drift without reintroducing candidate-pool expansion.

2026-04-11 18:14 - Gated Normalization Acceptance Test Pass

Goal

  • Validate that the gated normalization implementation is runnable, that the retrieval/schema/trace paths are intact, and that the off vs gated experiment produces fresh artifacts.

Changes Made

  • No code changes; this was a pure validation pass.
  • Installed pytest into backend/.venv311 to enable test execution (pytest was in optional dev deps but not yet installed).
  • Ran all import checks for core modules, schema, trace, retrieval, and summarizer.
  • Ran targeted pytest suite: backend/tests/test_retrieval_hybrid_api.py (10 tests) and backend/tests/test_summary_orchestration_api.py + backend/tests/test_summary_provider_and_schema.py (8 tests).
  • Executed backend/scripts/experiment_query_normalization.py to generate fresh off vs gated experiment artifacts.

Why

  • The prior round (17:10) was blocked by a runtime access issue and could not run pytest or the experiment script.
  • This round confirms the implementation is fully executable and the default off policy is upheld.

Validation

  • All imports OK: fastapi, numpy, faiss, pydantic, httpx, sqlalchemy, app.main, query_normalization, retrieval/summary schemas, trace_types, hybrid retrieval, summarizer.
  • Schema verification: RetrievalSearchRequest.normalization_mode present; SummaryRequest.normalization_mode present; RetrievalTrace contains all 5 normalization fields (raw_query, normalized_query, normalization_applied, matched_rule_families, normalization_mode).
  • test_retrieval_hybrid_api.py: 10/10 passed, including test_gated_normalization_only_applies_to_matched_families and test_gated_normalization_stays_off_without_family_match.
  • test_summary_orchestration_api.py + test_summary_provider_and_schema.py: 8/8 passed.
  • Experiment script produced fresh gated artifacts:
    • output/query_normalization_gated_experiment.json — 1 204 784 bytes, mtime 2026-04-11T02:14:15
    • docs/query_normalization_gated_experiment.md — 19 171 bytes, mtime 2026-04-11T02:14:15

Current Conclusion

  • Gated normalization implementation passes acceptance: all tests green, all schema fields present, experiment runs end-to-end.
  • Default remains off as required.
  • Experiment data shows gated mode triggers on 9/19 queries (47.4%) but has 88.9% harm rate (source card drift), confirming gated should remain a manual/opt-in option only.
  • Python runtime blocker from the 17:10 entry is resolved.

Next Step

  • Review the 8 harmful trigger cases in docs/query_normalization_gated_experiment.md for specific drift patterns, then decide whether to narrow the rule dictionary to only the 1 useful trigger (backend developer Go/Rust) or keep gated as an opt-in flag for advanced users.

2026-04-11 03:27 - Lexical Precision Control Experiment

Goal

  • Run a stricter lexical precision control experiment that does not change query semantics and only touches lexical retrieval behavior.
  • Focus on lowering source-card drift through field-aware weighting, phrase or proximity protection, minimum-match style lexical constraint, and score decomposition.
  • Compare the current default baseline against a new explicit lexical mode across the existing 19 regression queries.

Changes Made

  • Extended lexical retrieval mode support to include precision_control_v1 while preserving existing off and phrase_field_v1 modes.
  • Implemented a stricter keyword scoring path in backend/app/retrieval/keyword.py:
    • stronger title / tags / chunk_type-aware lexical weighting
    • exact phrase and short-gap proximity bonuses for original query phrases
    • minimum-match style lexical gate to suppress weakly anchored keyword candidates
    • explicit lexical score decomposition:
      • keyword_base_score
      • keyword_phrase_bonus
      • keyword_field_bonus
      • keyword_min_match_penalty
      • keyword_score
  • Propagated lexical score decomposition through retrieval result and trace structures:
    • backend/app/retrieval/hybrid.py
    • backend/app/schemas/retrieval.py
    • backend/app/schemas/summary.py
    • backend/app/observability/trace_types.py
    • backend/app/observability/trace_builder.py
  • Added and updated tests to cover the new lexical mode and decomposition fields:
    • backend/tests/test_retrieval_hybrid_api.py
    • backend/tests/test_reranker_integration.py
    • backend/tests/test_retrieval_embeddings_and_vector_index.py
  • Added a new experiment script and generated fresh artifacts:
    • backend/scripts/experiment_lexical_precision_control.py
    • output/lexical_precision_control_experiment.json
    • docs/lexical_precision_control_experiment.md

Why

  • Query expansion and normalization are already frozen as non-mainline scaffolds because they caused too much source-card focus drift.
  • The next retrieval hardening attempt needed to preserve raw query meaning and instead make lexical matching stricter and more explainable.
  • This round intentionally prioritized precision over recall, so any improvement had to come from better lexical focus rather than broader candidate coverage.

Validation

  • Ran targeted retrieval tests:
    • ./python.exe -m pytest backend/tests/test_retrieval_hybrid_api.py -q -> 12 passed
    • ./python.exe -m pytest backend/tests/test_reranker_integration.py -q -> 29 passed
    • ./python.exe -m pytest backend/tests/test_retrieval_embeddings_and_vector_index.py -q -> 8 passed
  • Ran the precision-control experiment end-to-end:
    • ./python.exe backend/scripts/experiment_lexical_precision_control.py
  • Verified fresh artifact generation:
    • output/lexical_precision_control_experiment.json
    • docs/lexical_precision_control_experiment.md
  • Aggregate experiment result from the generated report:
    • average both-hit delta: -3.632
    • average top-k both-hit delta: -2.263
    • average source-card focus delta: 0.011
    • average role-boundary precision delta: -0.012
    • average adjacent-job noise delta: 0.002
    • source-card drift count: 12
    • improved queries: 0/19
    • harmed queries: 12/19

Current Conclusion

  • This round did not pass.
  • precision_control_v1 does not enter the default mainline.
  • The default switch does not change and remains off.
  • Although the experiment produced a tiny positive average focus-score delta, it materially worsened both-hit coverage, slightly worsened role-boundary precision, and increased source-card drift to 12 of 19 queries.
  • The minimum-match style lexical control is therefore too destructive in its current form to serve as the next mainline retrieval hardening step.

Next Step

  • Use the new lexical score decomposition output to classify the 12 drifted or degraded queries by failure mode:
    • over-strict minimum-match pruning
    • phrase/proximity bonus reordering near-tie candidates
    • field-weighting bias that overpromotes generic titles
  • If one dominant failure mode explains most of the harm, run one narrow rollback-style iteration on that single component; otherwise freeze this lexical precision-control branch as experimental scaffold only.

2026-04-11 19:00 - Query-Side Lexical Tuning Freeze and Document-Side Audit Pivot

Goal

  • Formally close the query-side lexical tuning line (phrase_field_v1) and redirect主线 to document-side mismatch auditing as the next stable retrieval precision lever.

Changes Made

  • No code changes; this is a decision-only entry.
  • Documented the final ruling on phrase_field_v1: Keep-Experimental-Only, not promoted to baseline.
  • Default retrieval baseline remains unchanged (lexical_hardening_mode: off).
  • The query normalization scaffold (gated / off) remains intact as a non-default capability.
  • Frozen experiment lines: query normalization gated, lexical hardening phrase_field_v1.
  • Main line exits query-side lexical tuning and query-side expansion tuning.

Why

  • phrase_field_v1 produced 0/19 clean wins, 8/19 harmed queries, and 7/19 source-card drift cases in the 19-query regression set.
  • Query normalization gated had 88.9% harm rate on triggered queries (8/9) in the gated experiment.
  • Both query-side directions (expansion normalization and lexical hardening) have now independently converged on the same failure mode: source-card focus drift.
  • This pattern suggests the underlying instability is not in the query preprocessing layer but in the document retrieval surface itself — specifically, how documents are surfaced relative to the true information need.
  • Document-side mismatch auditing is the structurally correct next lever because it addresses the root cause of why near-miss documents appear in the top-k in the first place.

Validation

  • Based on completed experiment artifacts already on disk:
    • docs/lexical_hardening_experiment.md
    • docs/query_normalization_gated_experiment.md
    • output/lexical_hardening_experiment.json
    • output/query_normalization_gated_experiment.json
  • No new execution required; decision is a policy ruling based on existing evidence.

Current Conclusion

  • phrase_field_v1: Keep-Experimental-Only. Does not enter default main retrieval链路.
  • Default baseline: unchanged, lexical_hardening_mode = off.
  • Query normalization scaffold: gated capability only, default off.
  • Experiment line: frozen as scaffold.
  • Main line: exits query-side lexical tuning, pivots to document-side mismatch auditing.

Next Step

  • Run a document-side mismatch audit on the existing 19-query regression set, targeting role, employment-type, seniority, domain, and location mismatches between the retrieved documents and the query intent.
  • Use existing trace and retrieval metadata to identify structural reasons for noisy source cards rather than further query-side tuning.

2026-04-10 19:41 - Document-Side Metadata Extraction and Mismatch Audit

Goal

  • Extract role_family, employment_type, and seniority from existing source card title+tag metadata via rule-based inference, run a structured mismatch audit on all 19 regression queries, and determine whether document-side metadata hardening is the right next main retrieval precision lever.

Changes Made

  • No code changes; post-hoc analysis only.
  • Ran rule-based metadata extraction on all 76 source cards (top-4 per query, 19 queries) from the lexical hardening baseline.
  • Derived role_family via ordered title keyword matching (first-match wins) with tag fallback — 67/76 docs (88%) extract successfully.
  • Derived employment_type via tag-precedence then title keyword — only 13/76 docs (17%) extract to non-unknown; 63/76 are unknown. Critical metadata coverage gap, not a rule deficiency.
  • Derived seniority via tag-precedence then title keyword — 37/76 docs (49%) extract to non-unknown.
  • Audited role mismatch, employment-type mismatch, seniority mismatch per query.
  • Generated artifacts:
    • output/document_side_metadata_audit.json
    • docs/document_side_metadata_audit.md

Why

  • Prior title-only inference confirmed role boundary confusion is dominant but needed formal extraction with coverage quantification.
  • Employment-type was suspected to be underdetected; this round quantifies the coverage gap precisely.
  • Seniority was suspected to be secondary; this round confirms it is narrowly scoped to junior-vs-senior queries only.

Validation

  • Source: output/lexical_hardening_experiment.json baseline (19 queries, top-4 source cards each = 76 documents).
  • Additional reference: output/query_normalization_gated_experiment.json and output/lexical_precision_control_experiment.json for drift query cross-reference.
  • No new retrieval, no code changes, no re-indexing.

Current Conclusion

  • Role boundary confusion is the dominant structural problem. 10/19 queries have detectable role mismatch; 6/7 drift queries have role mismatch. The specific failure: query targets Role A → system returns adjacent Role B due to keyword overlap (data_engineer ↔ data_analyst, backend_developer → full_stack, frontend → full_stack).
  • Employment-type contamination is real but metadata-invisible. Only 17% document coverage means 83% of docs bypass any employment-type filter. Unpaid Mandatory Internship cards appearing in IoT/typescript queries cannot be caught without an explicit employment_type schema field.
  • Seniority mismatch is secondary and narrowly scoped. Only 1/19 queries shows seniority mismatch (junior query returning senior positions); the same query also has role mismatch, so seniority is not an independent primary driver.
  • Role-boundary metadata-aware filter is the correct next mainline investment. Employment-type filtering requires schema-level work first.
  • dagster exception: 1 drift query has zero detectable role/emp/seniority mismatch — noise source is chunk-level keyword proximity, not metadata mismatch. Separate failure mode.

Next Step

  • Extract employment_type as an explicit schema field from title patterns (Werkstudent/Working Student → working_student, Unpaid/Praktikum/Internship → internship, Full-time → full_time), then prototype two lightweight post-retrieval components on the 19-query traces:
    1. A role-family ranker that boosts/demotes documents by role_family intersection with query role signals
    2. An employment-type filter that demotes internship/unpaid documents when query has no internship/junior constraint (only on docs with extractable signals)
  • If role-family ranker reduces drift in ≥ 4/7 drift queries without increasing it elsewhere, promote it as the next mainline retrieval hardening step.
  • If employment-type coverage remains below 50% after extraction, deprioritize employment-type filtering until schema-level work is done.

2026-04-11 04:03 - Role-Family Demotion Prototype Experiment

Goal

  • Prototype post-retrieval, pre-rerank role_family-aware soft demotion to reduce source-card drift without changing query semantics. Based on the document-side audit conclusion that role boundary confusion is the dominant structural problem.

Changes Made

  • Created backend/app/retrieval/role_family.py:
    • extract_role_family(title, tags_json) — ordered keyword matching on title, tag fallback, first match wins
    • extract_query_role_intent(query) — exact keyword match from query string, no expansion
    • ADJACENT_ROLE_FAMILIES adjacency map derived from audit observations
    • compute_role_demotion() — returns factor (0.7 for adjacent, 1.0 otherwise)
    • apply_role_demotion() — applies factor to merged_score, re-sorts
  • Created backend/scripts/experiment_role_family_demotion.py — baseline vs experiment on 19 queries
  • Added optional role-family trace fields to RetrievalEvidenceResult schema (backward-compatible)
  • Added vector timeout fallback to keyword-only when embedding provider is unreachable
  • Generated output/role_family_demotion_experiment.json and docs/role_family_demotion_experiment.md

Why

  • The document-side audit identified role boundary confusion as the primary structural cause of noisy source cards. A post-retrieval demotion is the minimal invasive change to address this without rebuilding the pipeline.

Validation

  • 19 regression queries, baseline vs experiment (both with normalization=off, lexical_hardening=off)
  • Metric thresholds: improved ≥ 4 queries AND drift count < 3 to pass
  • Result: improved=12, harmed=6, drift=6 — did not pass thresholds

Current Conclusion

  • Prototype does not pass. Default switch stays off.
  • Drift count = 6, exceeding the < 3 threshold. Harmed queries (6): machine learning engineer tensorflow pytorch, backend developer Go Rust job requirements, junior software developer Python internship, AWS cloud DevOps remote contract, clickhouse snowflake dbt data engineer, Praktikum data science Munich.
  • Root cause of failure: adjacency map is wrong in both directions. cloud_engineerdevops_engineer adjacency causes Cloud Engineers (correct for "AWS cloud DevOps") to be demoted and Solutions Architects (worse) to replace them. backend_engineerbackend_developer adjacency demotes near-identical backend roles. ai_engineerml_engineer adjacency demotes correct AI Engineer docs for ML Engineer queries. The adjacency map was derived from observed baseline returns, not from explicit role taxonomy — it captures "these roles appeared together in baseline" not "these roles are truly adjacent."
  • The 0.7 factor is too aggressive — causes demoted docs to fall below non-demoted docs with equal/close scores, creating unnecessary ranking disorder.
  • The "improved" count (12) is misleading: most improved queries had no explicit role intent, so the demotion was a no-op and the unchanged ranking was counted as "improved."

Next Step

  • Do NOT iterate on the demotion factor or threshold. The adjacency map itself is the bottleneck.
  • Before any further prototype work, invest in a correct role taxonomy and adjacency map derived from the actual job schema (not from baseline returns). Specifically:
    1. Define explicit role equivalence classes (backend_developer ≈ backend_engineer, software_developer ≈ software_engineer, etc.)
    2. Define asymmetric adjacency only where truly harmful: e.g., full_stack is adjacent to backend_only (not the reverse), data_analyst is adjacent to data_engineer but data_engineer is NOT adjacent to data_analyst
    3. Run a new audit to re-derive the adjacency map from confirmed mismatch cases only
  • Alternatively: abandon role-boundary filtering and focus on the dagster-type failure mode (drift with zero detectable metadata mismatch) — chunk-level keyword proximity is a separate noise channel that role filtering cannot address

2026-04-11 20:25 - Dataset Coverage Audit for Current Corpus

Goal

  • Audit the currently available job-posting corpus to determine whether it is sufficient for the current InsightFlow retrieval stage: benchmark v1, hybrid retrieval comparison, mismatch or drift analysis, and document-side metadata refinement planning.

Changes Made

  • Performed a read-only audit against data/processed/insightflow.db.
  • Verified the current on-disk corpus size and found a state mismatch with the originally stated 89-record assumption: the current main SQLite corpus contains 195 job_contents rows, while the eval copy contains 118.
  • Read project planning documents, retrieval evaluation docs, schema, the current role-family extraction module, and the v0 evaluation query set to ground the audit in the active project stage.
  • Computed corpus distributions using lightweight title/tag/location-based rules for:
    • role family
    • employment type
    • seniority
    • geography / work mode
    • skill clusters
  • Reviewed hard-negative availability by checking whether adjacent role or constraint pairs exist in enough quantity to support retrieval-noise analysis.

Why

  • The project has already moved past “can the system run” and is now making decisions about benchmark coverage, retrieval comparisons, and document-side metadata hardening.
  • Those decisions depend on corpus shape, not just corpus size, so the audit needed to identify where the current dataset is representative enough for MVP work and where it is too skewed for stronger conclusions.

Validation

  • Confirmed current corpus counts directly from SQLite:
    • data/processed/insightflow.db: 195 job_contents, 1235 job_chunks
    • backend/data/processed/insightflow.db: 195 job_contents, 1235 job_chunks
    • backend/data_copy_for_eval/processed/insightflow.db: 118 job_contents, 713 job_chunks
  • Verified current evaluation fixture shape from backend/data/eval_queries_v0.json and backend/docs/evaluation_v0.md.
  • Used only existing stored document fields (title, employment_type, location_text, tags_json, description) plus explicit lightweight audit rules; no retrieval reruns, re-indexing, or code-path changes.

Current Conclusion

  • The current corpus is large enough to support MVP-stage retrieval plumbing checks, benchmark v1 scaffolding, and metadata-hardening prioritization.
  • It is not balanced enough to support strong generalized conclusions about job-retrieval quality because role coverage is highly skewed, Germany dominates geography, employment metadata is sparse, and data-analytics hard negatives remain too thin.
  • The immediate value is diagnostic rather than definitive: the corpus is good enough to tell the team what to fix next, but not good enough to declare the retrieval direction stable.

Next Step

  • Prioritize targeted corpus augmentation for underrepresented adjacent role families and employment constraints so the benchmark can expose precision failures on realistic near-miss jobs instead of mostly broad-category noise.

2026-04-11 20:40 - Targeted Supplementation Plan v1

Goal

  • Turn the dataset coverage audit into a concrete 20-30 high-value job supplementation plan that improves benchmark usefulness, hard-negative density, and metadata refinement value without changing retrieval or running new experiments.

Changes Made

  • Produced a role-prioritized supplementation design focused on the current corpus weaknesses:
    • data analyst
    • data engineer
    • ML/AI
    • DevOps / cloud / platform
    • small boundary slices for database admin and frontend / UX
  • Defined intended mixes for employment type, seniority, geography, and work mode rather than only role counts.
  • Defined explicit hard-negative pairs the new samples should create on purpose so the benchmark can better expose role-boundary and constraint-boundary errors.
  • Added sampling constraints and a minimal field spec for new postings so future supplements preserve title, location, role, and employment signals needed by document-side metadata extraction.

Why

  • The current 195-row corpus is enough for MVP validation but still too skewed for stronger retrieval-route conclusions.
  • The main blocker is not raw volume; it is the lack of dense adjacent-role and adjacent-constraint examples in the exact areas the benchmark is trying to judge.
  • A small but intentional supplement is therefore higher leverage than broad undirected corpus growth.

Validation

  • Plan grounded in the completed dataset audit and current corpus counts already verified from SQLite.
  • No retrieval runs, no indexing work, no code changes, and no fabricated data were introduced.

Current Conclusion

  • The fastest path from "MVP-capable" toward "more stable retrieval judgment" is a narrow supplementation round centered on hard negatives, not a general corpus expansion round.
  • The highest-value additions are analyst vs engineer, ML/AI vs data engineer, and DevOps vs cloud/platform, with explicit junior/full-time/internship/remote contrasts.

Next Step

  • Freeze a concrete target quota sheet and start collecting against those quotas instead of continuing broad unsupervised ingestion.

2026-04-11 20:55 - Sampling Execution Spec for 30 Targeted Jobs

Goal

  • Convert the supplementation plan into a concrete 30-job execution spec with fixed quotas, collection rules, required fields, checklist items, and stop conditions.

Changes Made

  • Defined a final 30-job quota split with role-prioritized allocation rather than even expansion.
  • Added explicit target mixes for employment type, seniority, geography, and work mode.
  • Specified hard-negative pairings the collection pass must intentionally create.
  • Defined collection-time acceptance and rejection rules so the sampling round improves benchmark value instead of just increasing volume.
  • Added stop conditions that depend on structural coverage, not just hitting a raw row count.

Why

  • The current corpus already supports MVP validation, so the next data round must maximize retrieval-route signal rather than corpus size.
  • A fixed execution spec prevents the supplementation pass from drifting back into Berlin-heavy, senior-heavy, full-time-heavy generic collection.

Validation

  • Built directly from the completed dataset audit and targeted supplementation plan; no retrieval runs, no code changes, and no synthetic data generation.

Current Conclusion

  • The 30-job round should be treated as a constrained balancing pass focused on high-value hard negatives and metadata clarity.
  • Success depends on fulfilling the quota structure and pair coverage, not merely collecting any 30 additional postings.

Next Step

  • Materialize the quota sheet and use it as the only intake target for the next collection pass.

2026-04-11 21:10 - Land Sampling Execution Spec Artifacts

Goal

  • Turn the already approved supplementation plan into concrete execution artifacts: a fixed sampling spec, a quota JSON, a tracking sheet template, and an iteration log entry.

Changes Made

  • Created docs/sampling_execution_spec_v1.md as the executable supplementation spec.
  • Created output/supplementation_quota_v1.json with the structured final quota.
  • Created output/supplementation_tracking_sheet_v1.csv as a collection template.
  • Applied the required minimum arithmetic corrections only:
    • changed the devops / cloud / platform employment mix from 1 working_student + 3 full_time + 2 contract to 1 working_student + 4 full_time + 1 contract
    • fixed the frontend / ux_design boundary seniority to student

Why

  • The plan was already decided, but it still needed executable artifacts before any directed collection could begin.
  • The quota arithmetic needed to be consistent so future sampling can be tracked against one stable source of truth.

Validation

  • No retrieval logic, embedding logic, indexing, reranking, frontend, or summary code was changed.
  • Verified the corrected horizontal employment totals are now:
    • internship = 5
    • working_student = 5
    • full_time = 17
    • contract = 3
  • Verified the corrected horizontal seniority totals are now:
    • junior_entry = 7
    • mid = 10
    • senior = 8
    • student = 4
    • lead = 1
  • Verified the tracking sheet is a template only and does not contain fabricated supplementation results.

Current Conclusion

  • The supplementation execution spec is now grounded in concrete files and can be used immediately for collection.
  • The quota arithmetic is corrected without changing the main role-family allocation strategy.

Next Step

  • Start directed collection against the fixed quota sheet, beginning with the analyst, engineer, ML/AI, and devops-cloud-platform groups.

2026-04-11 21:35 - Candidate List Build for Analyst and Data Engineer

Goal

  • Prepare the first two directed supplementation candidate pools: data_analyst and data_engineer, with enough over-selection to support later screening into the tracking sheet.

Changes Made

  • Built a read-only candidate pool from currently accessible public job sources, focusing on:
    • data analyst / analytics / BI / business-analytics roles
    • data engineer / analytics engineer / analytical data engineer roles
  • Kept the candidate pools larger than the final quota so later screening can remove weak-fit or duplicate items.
  • Prioritized candidates that help cover employment type, seniority, geography, work mode, and hard-negative structure.
  • Did not modify retrieval, indexing, embeddings, reranking, frontend, or summary behavior.

Why

  • The supplementation spec is already fixed; the next practical step is to prepare real candidate pools that can be filtered into the tracking sheet.
  • Over-selecting candidates before acceptance reduces the chance that the final 8+8 role quotas get filled with low-signal or overly repetitive jobs.

Validation

  • No retrieval run was executed.
  • No code paths were changed.
  • Candidate pools were built from visible, concrete job postings rather than fabricated rows.
  • The output of this round is a screening pool, not a claim that supplementation intake is complete.

Current Conclusion

  • The analyst and data engineer candidate pools are now large enough to support the next screening step into the tracking sheet.
  • Additional curation is still required before these candidates become accepted supplementation rows.

Next Step

  • Screen the analyst and data engineer candidate pools against the quota sheet and move accepted rows into the tracking sheet.

2026-04-11 21:55 - Candidate-to-Tracking-Sheet Screening Pass v1

Goal

  • Move the analyst and data engineer candidate pools into the supplementation tracking sheet and assign first-pass provisional screening decisions.

Changes Made

  • Replaced the placeholder tracking sheet with concrete analyst and data engineer candidate rows.
  • Added per-candidate screening fields including:
    • quota bucket
    • normalized role family
    • employment type
    • seniority
    • country, city, and work mode
    • hard-negative pair
    • duplicate check
    • signal quality check
    • provisional accepted / hold / reject status
  • Marked a recommended analyst set of 8 provisional accepts.
  • Marked a recommended engineer set of 7 provisional accepts.
  • Preserved the explicit engineer internship gap instead of forcing a false completion.

Why

  • The supplementation plan is only actionable once candidates are turned into a traceable screening sheet.
  • A provisional pass helps preserve the best coverage candidates while keeping weaker or redundant rows visible for backup use.

Validation

  • No retrieval logic was changed.
  • No retrieval run or new experiment was executed.
  • No synthetic job postings were invented; only concrete candidate rows were used.
  • The engineer group remains intentionally incomplete on the internship dimension, as required.

Current Conclusion

  • The analyst pool is now screened tightly enough to support an 8-row provisional accepted set.
  • The engineer pool is screened tightly enough to support a 7-row provisional accepted set, but still needs one explicit internship-style data engineer candidate before that quota can be treated as complete.

Next Step

  • Find one high-signal data engineer internship candidate and then run the next screening pass to close the engineer slot.

2026-04-11 22:10 - Engineer Internship Gap Fill and Screening Pass v2

Goal

  • Close the explicit data engineer internship gap and finalize the first engineer accepted set of 8.

Changes Made

  • Added two real internship-style data engineering candidates to the tracking sheet:
    • E14 Data Engineer Intern - Equativ
    • E15 Praktikum Data Engineering (m/w/d) - AXA Konzern AG
  • Marked E14 as the internship candidate that closes the engineer gap.
  • Marked E15 as a high-quality backup hold candidate.
  • Finalized an engineer accepted set of 8 based on role clarity, coverage value, and gap closure.

Why

  • The previous screening pass intentionally stopped at 7 accepted engineer rows because the internship dimension was still missing.
  • That gap had to be filled before the engineer group could be treated as structurally complete for v1 supplementation intake.

Validation

  • No retrieval logic was changed.
  • No retrieval run or experiment was executed.
  • No synthetic postings were introduced.
  • The internship gap is now explicitly covered by a real data-engineering internship candidate with strong platform/pipeline signals.

Current Conclusion

  • The engineer group now has a final recommended accepted set of 8.
  • The internship gap is filled.
  • One additional internship-style engineer row remains in hold status as a backup candidate.

Next Step

  • Freeze the engineer accepted set and move on to the next role group screening pass.

2026-04-11 22:30 - ML/AI Candidate-to-Tracking-Sheet Screening Pass v1

Goal

  • Build the first ml_ai candidate pool, write it into the tracking sheet, and assign provisional accepted / hold / reject decisions.

Changes Made

  • Added 12 ml_ai candidate rows to the supplementation tracking sheet.
  • Covered the target role signals with concrete titles including:
    • Machine Learning Engineer Intern
    • Working Student Agentic AI & Automation
    • AI Engineer
    • AI Engineer (LLMs + Knowledge Graphs)
    • Senior AI/ML Engineer
    • Senior Generative AI Engineer
    • Machine Learning Scientist
  • Marked a provisional accepted set of 6 that best fits the current quota shape.
  • Preserved additional strong ml_ai rows in hold status for backup use rather than overfilling senior Germany full-time coverage.

Why

  • After freezing analyst and data engineer accepted sets, the next supplementation priority is ml_ai.
  • Writing the candidate pool into the tracking sheet creates a concrete screening state instead of leaving the role group as an informal list.

Validation

  • No retrieval logic was changed.
  • No retrieval run or new experiment was executed.
  • No synthetic job data was invented.
  • The ml_ai role group now has a candidate pool large enough for tracking-sheet screening.

Current Conclusion

  • The ml_ai candidate pool is sufficient to enter tracking-sheet screening.
  • The provisional accepted set of 6 is structurally usable, but the group still has one soft tension: the internship and working-student requirements both naturally pull toward student-like seniority, so the quota fit is achieved by mapping the internship role as junior-entry.

Next Step

  • Freeze the provisional ml_ai accepted set unless a stronger non-Berlin or non-Germany mid-level replacement appears.

2026-04-11 22:45 - ML/AI Screening Semantic Cleanup

Goal

  • Remove the quota-driven seniority distortion in the ml_ai accepted set and restate the recommendation using only semantically correct labels.

Changes Made

  • Corrected M01 from junior_entry to student in the tracking sheet because it is an internship role and does not carry an explicit junior-entry title signal.
  • Kept M12 as the true junior_entry backup candidate because its title explicitly says Junior AI Engineer.
  • Did not change retrieval logic, candidate sourcing, or the broader supplementation structure.

Why

  • The previous ml_ai provisional set was operationally convenient but semantically impure.
  • That kind of relabeling would contaminate later metadata, benchmark, and mismatch-audit interpretation.

Validation

  • No new experiment was run.
  • No code was changed.
  • The only update was semantic cleanup in the tracking sheet plus clarification of how the ml_ai quota should be interpreted.
  • The remaining tension is structural: the ml_ai quota asks for both internship/working-student coverage and a separate junior-entry slot, but realistic ml_ai candidate pools often concentrate early-career roles into internship and student formats.

Current Conclusion

  • The ml_ai labels are now semantically clean.
  • The ml_ai accepted recommendation can be frozen if the quota is interpreted minimally as requiring early-career coverage rather than forcing both student and junior_entry to appear simultaneously inside the final six.
  • If strict independent junior_entry presence is still required, then the smallest blocker is the need to swap in M12 for one mid-level full-time role.

Next Step

  • Freeze ml_ai with semantically correct labels under the early-career coverage interpretation, or explicitly approve the stricter junior-entry requirement and replace one mid-level accepted row with M12.

2026-04-11 22:55 - ML/AI Quota Interpretation Cleanup and Freeze

Goal

  • Formally document the ml_ai quota interpretation adjustment, freeze the accepted 6, and mark the true junior-entry row as backup.

Changes Made

  • Added the ml_ai seniority interpretation rule to docs/sampling_execution_spec_v1.md.
  • Added the same bucket-specific interpretation to output/supplementation_quota_v1.json.
  • Updated output/supplementation_tracking_sheet_v1.csv to:
    • freeze M01 through M06 as accepted
    • mark M12 as backup
    • preserve semantically correct seniority labels without relabeling internship as junior-entry

Why

  • The candidate pool was already sufficient, but the quota interpretation needed to be explicit so the accepted set could be frozen without semantic distortion.
  • This keeps the supplementation artifacts aligned with later metadata and benchmark work.

Validation

  • No code was changed.
  • No retrieval run or new experiment was executed.
  • The ml_ai interpretation rule is now documented in both the human-readable spec and the structured quota JSON.
  • The tracking sheet now distinguishes accepted rows from the junior-entry backup row.

Current Conclusion

  • The ml_ai accepted 6 is now frozen.
  • M12 is explicitly marked as the junior-entry backup.
  • The quota interpretation cleanup is fully documented and no longer depends on implicit discussion context.

Next Step

  • Move on to the next role bucket screening pass with the ml_ai bucket treated as frozen.

2026-04-11 23:15 - DevOps / Cloud / Platform Screening Pass v1

Goal

  • Build the first devops / cloud / platform candidate pool, write it into the tracking sheet, and assign provisional accepted / hold / reject decisions.

Changes Made

  • Added 12 devops/cloud/platform candidate rows to the supplementation tracking sheet.
  • Covered the target title families with concrete postings including:
    • DevOps Engineer
    • Site Reliability Engineer
    • Cloud Engineer
    • Junior Platform Engineer
    • Teamlead IT-Infrastructure
  • Marked a provisional accepted set of 6.
  • Preserved additional strong rows in hold status where they improve backups but would overconcentrate Berlin/Germany senior full-time coverage if accepted immediately.

Why

  • After freezing analyst, data_engineer, and ml_ai, the next supplementation priority is devops/cloud/platform.
  • This role bucket needs real overlap between cloud, platform, and devops vocabulary so later retrieval checks can expose near-miss behavior.

Validation

  • No retrieval logic was changed.
  • No retrieval run or new experiment was executed.
  • No synthetic job data was invented.
  • The candidate pool is large enough for tracking-sheet screening.

Current Conclusion

  • The devops/cloud/platform candidate pool is sufficient to enter tracking-sheet screening.
  • A provisional accepted set of 6 can be formed now.
  • There is one remaining structural tension: the seniority sub-quota sums to 7 labels across only 6 accepted slots, so the current accepted set optimizes employment, geography, and hard-negative coverage first and leaves one seniority preference as a soft gap rather than forcing semantic distortion.

Next Step

  • Freeze the provisional accepted set unless a clearly stronger non-Germany onsite senior or lead cloud/platform candidate appears.

2026-04-11 23:30 - DevOps / Cloud / Platform Quota Interpretation Cleanup and Freeze Readiness Check

Goal

  • Formally document the devops/cloud/platform quota interpretation adjustment and decide whether the current accepted 6 can be frozen without semantic distortion.

Changes Made

  • Added the devops/cloud/platform seniority interpretation rule to docs/sampling_execution_spec_v1.md.
  • Added the same bucket-specific interpretation to output/supplementation_quota_v1.json.
  • Updated output/supplementation_tracking_sheet_v1.csv to freeze the current accepted 6:
    • D01
    • D03
    • D04
    • D05
    • D07
    • D10

Why

  • The candidate pool was already sufficient, but the raw seniority sub-quota was internally tighter than the six available accepted slots.
  • A minimal interpretation rule was needed to avoid forcing unnatural relabeling or unnecessary reshuffling.

Validation

  • No code was changed.
  • No retrieval run or new experiment was executed.
  • No synthetic job data was invented.
  • The interpretation rule is now documented in both the human-readable spec and the structured quota JSON.
  • The tracking sheet now marks the accepted 6 as frozen rather than provisional.

Current Conclusion

  • The devops/cloud/platform accepted 6 can now be frozen.
  • The remaining tension has been converted from an implicit conflict into an explicit, documented bucket-specific interpretation rule.
  • There is no remaining sourcing blocker for this bucket.

Next Step

  • Move on to the final remaining bucket or final supplementation review with the devops/cloud/platform bucket treated as frozen.

2026-04-11 23:45 - Final Boundary Supplementation Pass

Goal

  • Close the last two supplementation slots with one database_admin boundary sample and one frontend/UX boundary sample, completing the accepted 30-sample supplementation set.

Changes Made

  • Added and accepted B01 as the final database_admin boundary sample:
    • Oracle Database Administrator (m/f/d) - Indra Avitech GmbH
  • Added and accepted B02 as the final frontend_ux_boundary sample:
    • Frontend Software Engineer Working Student - QuantCo
  • Wrote both rows into output/supplementation_tracking_sheet_v1.csv.

Why

  • The first four role groups were already frozen, leaving only the two boundary samples needed to close the supplementation plan.
  • These final two rows were selected for strong role clarity and boundary value rather than broad category expansion.

Validation

  • No code was changed.
  • No retrieval run or new experiment was executed.
  • No synthetic postings were invented.
  • Both final rows carry clear role signals and fit their intended boundary buckets:
    • DBA vs data-engineer/platform
    • frontend vs UX/web/product

Current Conclusion

  • Both final boundary rows are accepted.
  • The full 30-sample supplementation set is now complete and closed at the tracking-sheet level.

Next Step

  • Run the final supplementation completeness review and summarize the accepted 30-row set by bucket and coverage.

2026-04-12 00:05 - Accepted 30-Sample Supplementation Completeness Review

Goal

  • Review the full accepted supplementation set for completeness against the execution spec and confirm whether it is ready for benchmark-phase use.

Changes Made

  • Audited the accepted supplementation rows against:
    • role-family quotas
    • employment-type coverage
    • seniority coverage, including bucket-specific interpretations
    • geography and work-mode coverage
    • hard-negative coverage
    • signal quality and duplicate-risk flags
  • Verified that the intended accepted set reaches the planned 30 rows across all role buckets.
  • Identified one administrative inconsistency in the tracking sheet: the frozen analyst and data-engineer rows still carry provisional_yes instead of accepted, even though the supplementation decisions already treat them as frozen.

Why

  • The supplementation pass only becomes truly usable for the next benchmark phase when coverage is complete and any remaining blockers are clearly distinguished as structural or merely administrative.

Validation

  • No code was changed.
  • No retrieval run or new experiment was executed.
  • No new samples were added.
  • Verified target bucket counts are satisfied at the decision level:
    • analyst 8
    • data_engineer 8
    • ml_ai 6
    • devops_cloud_platform 6
    • database_admin 1
    • frontend_ux_boundary 1
  • Verified the only remaining inconsistency is status-label bookkeeping in the CSV, not supplementation coverage.

Current Conclusion

  • The accepted 30-sample supplementation set is complete at the decision level and is ready for benchmark-phase use.
  • The only remaining caveat is administrative: some frozen rows still need their tracking-sheet status normalized from provisional_yes to accepted.

Next Step

  • Normalize the remaining frozen tracking-sheet statuses and then treat the accepted 30-row set as the benchmark-phase supplementation corpus.

2026-04-12 00:20 - Supplementation Freeze Finalization

Goal

  • Finalize the supplementation freeze by normalizing all frozen rows in the tracking sheet to accepted and confirming the accepted set is ready for benchmark-phase use.

Changes Made

  • Updated all frozen analyst and data-engineer rows from provisional_yes to accepted in output/supplementation_tracking_sheet_v1.csv.
  • Left backup, hold, and reject rows unchanged.
  • Added docs/supplementation_freeze_review.md to summarize the final accepted set and its readiness state.

Why

  • The supplementation decisions were already complete, but the ledger still mixed frozen rows with provisional status labels.
  • Finalizing those labels removes the last administrative blocker before using the accepted set as benchmark-phase augmentation data.

Validation

  • No retrieval logic, benchmark logic, or application code was changed.
  • No retrieval run or new experiment was executed.
  • Confirmed the final state counts are:
    • accepted = 30
    • backup = 1
    • hold = 16
    • reject = 7
  • Confirmed the accepted bucket totals remain unchanged and sum to 30.

Current Conclusion

  • The supplementation freeze is finalized.
  • The accepted 30-sample set is now formally synchronized in the tracking sheet and can be used as the benchmark-phase enhancement corpus.

Next Step

  • Start the next benchmark / hybrid evaluation pass using the finalized accepted 30-sample supplementation set.

2026-04-13 03:25 - GitHub Release Readiness Audit and README Refresh

Goal

  • Audit the repository before a GitHub push for unnecessary files and possible secret exposure, then refresh the root README so the public project description is accurate and easy to understand.

Changes Made

  • Reviewed project context, roadmap, current state, tracked files, ignored files, and current worktree status.
  • Scanned tracked and visible repository files for common secret patterns, private-key markers, and token-like strings.
  • Updated .gitignore to block root-level portable Python/runtime artifacts that were still appearing as untracked candidates for commit.
  • Rewrote the root README.md into a concise public-facing project overview with scope, stack, quick start, API surface, provider configuration, and project status.

Why this change was made

  • The repository had several local runtime artifacts in the root directory that are not source code and are easy to accidentally commit during a manual GitHub push.
  • The previous README was too long, mixed internal notes with public-facing documentation, and contained visible encoding noise in several sections, which would weaken the first impression of the repo.
  • A release-readiness pass reduces the chance of pushing noisy files or leaking environment-specific information.

Validation / test results

  • Reviewed git status --short and git status --ignored --short to distinguish tracked work, ignored runtime output, and still-exposed root artifacts.
  • Confirmed the newly added ignore rules now cover the root portable-runtime patterns that were previously not ignored.
  • Ran pattern-based secret checks across tracked files and non-ignored workspace files.
  • No concrete live API keys or private keys were found in the scanned repository content.
  • Did observe example placeholders and documentation references to environment variable names such as CHAT_API_KEY and EMBEDDING_API_KEY; these appear to be examples rather than leaked secrets.
  • Did not run the full backend/frontend test suites in this audit pass because the task focus was repository hygiene and documentation readiness.

Current Conclusion

  • No obvious credential leak was found in the repository content scanned during this pass.
  • The main pre-push risk was accidental inclusion of local portable Python/runtime files from the repo root; .gitignore has now been tightened to reduce that risk.
  • The repository still has several untracked docs and local experiment artifacts that should be intentionally reviewed before pushing, rather than committed by default.

Next Step

  • Review the remaining untracked documentation files and decide which ones are worth publishing in the first GitHub push versus keeping local.
  • Run one final git status check before commit/push and verify that only intentional source changes and selected documentation remain.

2026-04-13 03:40 - README Bilingual Release Pass

Goal

  • Convert the refreshed root README into a concise bilingual Chinese/English version suitable for a public GitHub repository.

Changes Made

  • Replaced the single-language public README with a bilingual structure.
  • Added mirrored Chinese and English sections for project overview, scope, stack, quick start, provider configuration, testing, design principles, and project status.
  • Kept the README focused on externally useful onboarding information instead of internal experiment details.

Why this change was made

  • The repository owner is preparing a GitHub push and asked for a bilingual presentation.
  • A bilingual README improves accessibility for both Chinese-speaking reviewers and broader public readers without forcing them into internal planning documents first.
  • Keeping the root README concise helps the repository make a stronger first impression.

Validation / test results

  • Reviewed the updated README content for structure consistency across both languages.
  • Confirmed the commands and referenced endpoints remain aligned with the current repository layout and backend/frontend entrypoints.
  • Did not run application tests because this iteration was documentation-only.

Current Conclusion

  • The repository now has a bilingual root README that is much better suited for public presentation than the previous internal-heavy version.

Next Step

  • Make a final inclusion decision on untracked benchmark and design docs before creating the Git commit for GitHub.

2026-04-13 03:55 - Open Source License and Release Notes Prep

Goal

  • Prepare a proper open-source project license and a first-release note draft suitable for the upcoming GitHub publication.

Changes Made

  • Added a root-level LICENSE file using the MIT License template.
  • Added docs/release_notes_v0.1.0.md with bilingual release copy for the first public version.
  • Updated the root README.md to link to the project license and the first release notes.

Why this change was made

  • The repository previously only had an untracked LICENSE.txt from the bundled Python runtime, which is not the correct project-level open-source license.
  • A public GitHub push benefits from having explicit reuse terms and a clean release narrative ready at the same time.
  • MIT is a pragmatic default for a personal/public engineering project that aims to stay easy to fork, review, and extend.

Validation / test results

  • Confirmed the new license is placed at the repository root in the conventional LICENSE path.
  • Confirmed the release-notes document is linked from the root README.
  • Did not run code tests because this iteration only added documentation and repository metadata.

Current Conclusion

  • The repository now has the minimum open-source publication materials expected for a public GitHub release: a project license and an initial release note.

Next Step

  • Decide which code and documentation files belong in the first public commit, then stage and publish the curated set.

2026-04-12 13:18 - Strict Remote/Location Benchmark Expansion + Targeted Rerun

Goal

  • Register the two nucleus-ready strict remote/location queries into the benchmark assets and rerun hybrid vs hybrid_plus_remote_location_refinement_v1.2 only on those two queries.

Changes Made

  • Added two new query definitions to backend/data/eval_queries_v0.json:
    • strict_remote_analytics_or_analyst_requirements
    • strict_germany_remote_or_hybrid_requirements
  • Updated output/benchmark_query_buckets_v1.json to:
    • increase the source query count from 25 to 27
    • register both query ids under strict_metadata_constraint
    • add both query ids to nucleus_expansion_candidates with rationale and acceptance notes
  • Generated output/strict_remote_queries_rerun.json
  • Added docs/benchmark_run_record_v1_strict_remote_queries.md
  • Added docs/strict_remote_query_candidates_v1.md

Why

  • remote_data_requirements had already been shown to be a poor primary acceptance query for remote/location refinement because it behaves like a broad summary prompt with metadata preference.
  • The benchmark needed stricter, more interpretable acceptance prompts before the remote/location refinement direction could be judged fairly.

Validation

  • No retrieval architecture changes were made.
  • No full benchmark rerun was performed.
  • Only the two strict remote/location queries were rerun.
  • Results:
    • subset size: 2
    • top-1 changes vs hybrid: 0 / 2
    • hybrid top-1 metadata satisfaction: 2 / 2
    • refined top-1 metadata satisfaction: 2 / 2
    • source-card drift count: 0 / 2
  • Interpretation:
    • the new queries are more diagnostic and more interpretable than remote_data_requirements
    • but this rerun did not produce new top-1 gains because plain hybrid already satisfied the constraints on both strict prompts

Current Conclusion

  • The benchmark expansion is valid and useful: these two strict queries are better remote/location acceptance prompts than the previous broad summary query.
  • But the rerun still does not provide new evidence strong enough to promote remote/location refinement, because hybrid already returns metadata-satisfying top-1 results on both strict queries.

Next Step

  • Keep the two strict queries in nucleus expansion and use them as the primary acceptance slice for the next remote/location refinement change, rather than using broad summary prompts.

2026-04-12 13:33 - Remote/Location Refinement Closure v1

Goal

  • Formally close the current remote/location refinement branch and record its state as parked rather than promoted.

Changes Made

  • Added docs/remote_location_refinement_closure_v1.md.
  • Consolidated the branch outcome into a single closure note covering:
    • what was validated
    • what was fixed
    • what still did not improve
    • why the branch is parked
    • reopen conditions
    • next mainline focus

Why

  • The branch had accumulated enough experiments, diagnostics, and benchmark corrections that its current state needed to be made explicit.
  • Without a closure note, future work would risk revisiting the same remote/location loop without a clear statement of what was already learned and why the line was not promoted.

Validation

  • No code was changed.
  • No retrieval experiment was run.
  • No benchmark rerun was performed.
  • The closure note is consistent with the documented evidence already in the repository:
    • grouped work_mode exposure was successfully implemented and verified
    • strict remote/location acceptance queries were registered and rerun
    • the strict rerun still showed insufficient promotion evidence because hybrid already satisfied the main metadata intent at top-1

Current Conclusion

  • Remote/location refinement is not a failed branch.
  • The grouped signal exposure fix was successful.
  • The benchmark correction toward strict acceptance queries was also correct.
  • But the branch still lacks enough incremental evidence to be promoted.
  • Status: Parked, not promoted.

Next Step

  • Leave the branch parked unless one of the documented reopen conditions is met, and keep the mainline focus on broader benchmark and retrieval work.

2026-04-11 13:20 - hybrid_plus_remote_location_refinement_v1.1

Goal

  • Run a cleaner remote/location-only refinement pass on top of hybrid and determine whether this narrower branch is ready to become a retrieval-line candidate.

Changes Made

  • Executed a real benchmark comparison between hybrid and hybrid_plus_remote_location_refinement_v1.1 on the locked 19-query nucleus.
  • Restricted refinement to the explicit remote/location subset only:
    • remote_data_requirements
    • remote_analytics_expectations
    • english_speaking_jobs_germany
    • remote_vs_onsite_expectations
  • Generated output/hybrid_plus_remote_location_refinement_v1_1.json.
  • Added:
    • docs/benchmark_run_record_v1_hybrid_plus_remote_location_refinement_v1_1.md
    • docs/benchmark_run_summary_v1_remote_location_addendum.md

Why

  • The previous mixed metadata refinement pass suggested the remote/location branch was more promising than the role-boundary branch.
  • This round was meant to isolate that branch and verify whether the positive signal still holds when role-family logic is removed entirely.

Validation

  • No retrieval architecture, embedding/index pipeline, or summary logic was changed.
  • No new dataset supplementation work was performed.
  • The run used the locked benchmark snapshot and the same 19-query nucleus.
  • Subset metrics for the remote/location slice were:
    • subset size = 4
    • top-1 changes = 0 / 4
    • metadata top-1 satisfied count:
      • hybrid = 2
      • refined = 2
    • metadata top-3 satisfied count:
      • hybrid = 2
      • refined = 2
    • source-card drift count = 0 / 4
    • outside-subset collateral harm = 0

Current Conclusion

  • The remote/location-only branch is safe and interpretable, but this clean v1.1 pass did not produce enough gain to justify promotion.
  • With only 4 triggered queries and no top-1 or metadata-satisfaction lift, the right conclusion is evidence-insufficient rather than success.

Next Step

  • Run a focused diagnostic pass on remote_data_requirements and remote_analytics_expectations to determine whether the blocker is weak document-side metadata or too-conservative adjustment strength.

2026-04-11 14:10 - Remote Signal Coverage Audit

Goal

  • Determine whether the weak remote/location refinement result is driven mainly by:
    • candidate scarcity
    • retrieval head miss
    • or extraction weakness

Changes Made

  • Audited the live SQLite corpus for explicit remote / hybrid / onsite_or_unspecified signals across the target role buckets.
  • Compared signal provenance by field:
    • location_text
    • title
    • description
  • Cross-checked the two failing remote queries against the stored hybrid and hybrid_plus_remote_location_refinement_v1_1 outputs.
  • Checked the accepted supplementation overlay separately to distinguish live-corpus scarcity from not-yet-ingested benchmark augmentation data.

Why

  • The previous diagnosis showed that remote refinement was not moving rankings, but the project still needed to know whether the blocker was:
    • lack of viable remote candidates
    • candidates existing but failing to enter the retrieved head set
    • or candidates entering the head set but being mislabeled as onsite

Validation

  • No code, retrieval logic, or benchmark configuration was changed.
  • No new ranking experiment was run.
  • Live main-corpus findings:
    • data_analyst: 0 remote, 0 hybrid, 1 onsite_or_unspecified
    • data_engineer: 0 remote, 1 hybrid, 1 onsite_or_unspecified
    • ml_ai: 3 remote, 2 hybrid, 3 onsite_or_unspecified
    • devops_cloud_platform: 6 remote, 1 hybrid, 2 onsite_or_unspecified
  • For the target buckets in the live main corpus, recoverable remote/hybrid signals came from:
    • description: yes
    • title: no
    • location_text: no
  • In the two failing remote queries, top-5 grouped candidates under the stored run contained 0 cards labeled as remote or hybrid.
  • But at least one relevant head candidate, Data Engineer / Analytics Engineer (w/m/d), contains an explicit 3/2 hybrid in Berlin signal in the document description, meaning the stored v1.1 head-level work-mode inference missed a real hybrid clue.

Current Conclusion

  • The dominant blocking pattern is mixed:
    • candidate scarcity in the live main corpus for remote data/analytics roles
    • plus extraction weakness because the usable remote/hybrid clues often live only in description
  • Retrieval-head miss is secondary, not primary:
    • at least one relevant hybrid candidate already enters the head set
    • but its work-mode signal is not carried into the head-level refinement features
  • The best classification is:
    • extraction weakness + candidate scarcity
    • with retrieval-head miss as a smaller contributor

Next Step

  • Inspect whether the retrieval/refinement path can safely expose description-derived remote/hybrid evidence at ranking time before any further weight tuning is considered.

2026-04-12 10:05 - Ranking-Time Signal Exposure Audit for Remote/Hybrid Clues

Goal

  • Trace where remote/hybrid clues are lost between raw documents and refinement-time ranking features for:
    • remote_data_requirements
    • remote_analytics_expectations

Changes Made

  • Inspected the retrieval schema and grouping path in:
    • backend/app/schemas/retrieval.py
    • backend/app/retrieval/hybrid.py
  • Re-read the stored v1.1 remote-location experiment output.
  • Queried the live retrieval response for the two remote queries and inspected grouped evidence chunks for top candidates.
  • Verified whether known candidates such as Data Engineer / Analytics Engineer (w/m/d) and Werkstudent (m/w/d) Python / Data & Energy Analytics already carry remote/hybrid clues inside their retrieved evidence.

Why

  • Earlier diagnosis showed the main issue was extraction weakness, but the project still needed to know the exact layer where the signal stops being available to ranking-time refinement.
  • That distinction matters because the smallest safe fix is different if the signal is missing from:
    • document ingestion
    • chunk/schema exposure
    • grouped candidate metadata
    • or refinement wiring

Validation

  • No code or retrieval behavior was changed.
  • No new ranking experiment or weight-tuning pass was run.
  • Confirmed that:
    • raw job_contents rows still contain the description text
    • job_chunks.chunk_text preserves description-derived clues
    • RetrievalEvidenceResult preserves chunk_text
    • GroupedJobResult preserves evidence
  • Confirmed that relevant remote/hybrid clues do appear in retrieved evidence:
    • Data Engineer / Analytics Engineer (w/m/d) contains we work 3/2 hybrid in Berlin
    • Werkstudent (m/w/d) Python / Data & Energy Analytics contains Tags: [\"IT\", \"Remote\", \"Working student\"] in a retrieved title_plus_summary chunk
  • Confirmed that the stored v1.1 refinement input still labeled those candidates as onsite_or_unspecified

Current Conclusion

  • The remote/hybrid clue is not primarily lost in raw document storage or chunk retrieval.
  • It is lost at ranking-time exposure:
    • the clue survives into retrieved evidence
    • but is not promoted into a stable grouped/source-card metadata field that refinement actually consumes
  • The minimal repair target is therefore the grouped-candidate / refinement-input boundary, not weight tuning.

Next Step

  • Design the smallest metadata exposure change that promotes description-derived or summary-chunk-derived work-mode clues into refinement-visible grouped candidate metadata before any new remote/location refinement run is attempted.

2026-04-12 10:20 - Grouped Candidate Work Mode Exposure Design Spec

Goal

  • Write the minimal design spec for exposing evidence-level remote/hybrid clues as grouped-candidate work_mode metadata and wiring that field into refinement input.

Changes Made

  • Created docs/work_mode_exposure_design_v1.md.
  • Documented:
    • current signal flow
    • grouped-layer metadata gap
    • proposed minimal field additions
    • source-priority order
    • explicit extraction rules
    • grouped exposure plan
    • refinement-input wiring plan
    • validation approach before implementation

Why

  • Previous audits showed that remote/hybrid clues already survive into retrieved evidence, but they do not become stable grouped-candidate metadata.
  • The project needed a minimal design target before any implementation or further refinement experiment could be justified.

Validation

  • No code was changed.
  • No new retrieval experiment was run.
  • The design was grounded in the existing retrieval schema, grouped-candidate structure, and the known false-negative cases from the remote-signal audits.
  • The spec explicitly rejects vague wording and limits promotion to explicit remote/hybrid clues.

Current Conclusion

  • The smallest meaningful repair should happen at the grouped-candidate exposure layer.
  • The key additions are:
    • work_mode
    • work_mode_signal_source
  • Weight tuning should remain deferred until those fields are available to refinement.

Next Step

  • Convert the design spec into a small implementation plan for grouped work_mode exposure and refinement-input wiring.

2026-04-12 10:40 - Grouped Work Mode Exposure Minimal Implementation

Goal

  • Implement the smallest runnable version of grouped-candidate work_mode exposure and make that metadata available for refinement input, without changing weights or expanding scope.

Changes Made

  • Added grouped-level work_mode and work_mode_signal_source to backend/app/schemas/retrieval.py.
  • Added backend/app/retrieval/work_mode.py to derive explicit remote/hybrid signals from:
    • title
    • location_text
    • tags
    • title_plus_summary
    • evidence_chunk_text
  • Updated backend/app/retrieval/hybrid.py so grouped candidates derive and expose work-mode metadata during group_by_job(...).
  • Added docs/work_mode_exposure_implementation_notes_v1.md.

Why

  • Prior audits showed that remote/hybrid clues were already present in retrieved evidence but were not promoted into stable grouped metadata.
  • The smallest meaningful fix was therefore to expose grouped work-mode metadata before touching refinement weights.

Validation

  • No retrieval weights, summary logic, embedding/index pipeline, or frontend behavior were changed.
  • No full benchmark run was executed.
  • Targeted validation confirmed:
    • Data Engineer / Analytics Engineer (w/m/d) now exposes:
      • work_mode = hybrid
      • work_mode_signal_source = evidence_chunk_text
    • Werkstudent (m/w/d) Python / Data & Energy Analytics now exposes:
      • work_mode = remote
      • work_mode_signal_source = multiple
    • control sample Data Analyst remains:
      • work_mode = onsite_or_unspecified
      • work_mode_signal_source = none

Current Conclusion

  • The minimal grouped-candidate repair is in place.
  • Remote/hybrid clues that already survive into retrieved evidence can now surface as stable grouped metadata for downstream refinement use.
  • Weight tuning is still correctly deferred.

Next Step

  • Wire the narrow remote/location refinement path to consume grouped work_mode directly and rerun only the targeted remote subset.

2026-04-12 11:05 - Remote/Location Refinement v1.2 on Grouped Work Mode

Goal

  • Rerun the narrow remote/location refinement on the 4-query subset after grouped work_mode exposure, and check whether the signal-exposure repair creates visible gains.

Changes Made

  • Executed a subset-only comparison between:
    • hybrid
    • hybrid_plus_remote_location_refinement_v1.2
  • Used grouped candidate:
    • work_mode
    • work_mode_signal_source
  • Generated:
    • output/hybrid_plus_remote_location_refinement_v1_2.json
    • docs/benchmark_run_record_v1_hybrid_plus_remote_location_refinement_v1_2.md
    • docs/benchmark_run_summary_v1_remote_location_v1_2_addendum.md

Why

  • The previous remote/location branch was blocked by missing grouped-candidate work-mode exposure.
  • After fixing that exposure, the project needed one clean rerun to verify whether the repaired signal path is enough to create visible improvement on the intended subset.

Validation

  • No summary logic, frontend logic, embedding/index pipeline, or broad benchmark scope was changed.
  • Only the 4 remote/location queries were rerun.
  • Results:
    • subset size = 4
    • top-1 changes = 0 / 4
    • top-1 metadata satisfaction:
      • hybrid = 3
      • v1.2 = 3
    • top-3 metadata satisfaction:
      • hybrid = 4
      • v1.2 = 4
    • source-card drift count = 2 / 4
  • Signal-source usage in refined top-3:
    • none = 4
    • evidence_chunk_text = 3
    • multiple = 3
    • tags = 2

Current Conclusion

  • Grouped work-mode exposure is working and the refinement now consumes real remote/hybrid clues that were previously hidden.
  • But the rerun still produced no top-1 gain and no metadata-satisfaction gain on the 4-query subset.
  • So the new blocker is no longer signal exposure alone; it is the combination of:
    • head score separation
    • broad query intent
    • and some remote-tagged lower-rank noise

Next Step

  • Run a focused query-quality diagnostic on remote_data_requirements before considering any new remote/location refinement iteration.

2026-04-12 11:25 - Query Taxonomy Note for Metadata-Aware Refinement Evaluation

Goal

  • Define a benchmark-facing query taxonomy that distinguishes broad summary prompts from strict metadata-constraint prompts, so metadata-aware refinement is evaluated on the right query class.

Changes Made

  • Added docs/query_taxonomy_for_refinement_v1.md.
  • Updated output/benchmark_query_buckets_v1.json with:
    • query_taxonomy
    • taxonomy_notes
  • Classified current metadata-relevant nucleus queries into:
    • broad_summary_with_metadata_preference
    • strict_metadata_constraint
    • mixed_or_ambiguous

Why

  • The remote/location diagnostics showed that some benchmark queries mention metadata terms like remote, but still behave more like broad summary prompts than strict constraint queries.
  • Without an explicit taxonomy, refinement evaluation can be misread and broad prompts can be used as inappropriate acceptance gates.

Validation

  • No code or retrieval behavior was changed.
  • No new experiment was run.
  • Classification was grounded in the existing locked query set and the completed remote/location diagnostics.
  • remote_data_requirements was explicitly classified as broad_summary_with_metadata_preference.

Current Conclusion

  • The benchmark artifacts now better reflect how metadata-aware refinement should be judged.
  • Going forward, remote/location refinement should be evaluated mainly on strict or near-strict metadata-constraint prompts, not on broad summary prompts that merely mention remote.

Next Step

  • Use the new taxonomy field in future refinement summaries so acceptance decisions are reported by query type rather than only by bucket.

2026-04-11 13:45 - Remote-Signal Failure Diagnosis

Goal

  • Diagnose why hybrid_plus_remote_location_refinement_v1.1 failed to move rankings on the two highest-value remote queries:
    • remote_data_requirements
    • remote_analytics_expectations

Changes Made

  • Inspected the stored hybrid run record and the hybrid_plus_remote_location_refinement_v1_1.json comparison output for the two target queries.
  • Compared the top grouped source cards, inferred work-mode labels, visible location signals, and per-card refinement adjustments.
  • Classified the blocking pattern without running a new benchmark pass or changing retrieval code.

Why

  • The previous v1.1 experiment showed no collateral harm, but also no gain.
  • Before considering any further refinement pass, the project needed a cleaner diagnosis of whether the blocker is:
    • weak document-side remote metadata
    • too-weak weighting
    • or both

Validation

  • No code, retrieval logic, summary logic, or dataset state was changed.
  • No new retrieval experiment was executed.
  • For both target queries, the inspected top grouped cards carried onsite_or_unspecified work-mode labels and received the same -0.03 adjustment under v1.1.
  • No top-3 candidate for either query exposed a usable remote or hybrid signal, so the refinement could not create a differential lift.

Current Conclusion

  • The dominant blocker is document-side remote/work-mode signal weakness in the actually retrieved head candidates.
  • Weighting strength is a secondary factor, but not the primary one, because the current top cards were penalized almost uniformly and there was no nearby remote-tagged alternative to promote.
  • The best classification is C. both, with A clearly dominating B.

Next Step

  • Inspect whether the corpus contains any strong remote/hybrid candidates for these intents that are failing to enter the retrieved head set at all, before considering any further weight tuning.

2026-04-12 00:40 - Benchmark-Phase Bootstrap v1

Goal

  • Lock the non-execution baseline artifacts needed for the next benchmark phase: dataset snapshot, query buckets, experiment matrix, and logging contract.

Changes Made

  • Created docs/benchmark_phase_plan_v1.md.
  • Created output/benchmark_dataset_snapshot_v1.json.
  • Created output/benchmark_query_buckets_v1.json.
  • Defined the benchmark dataset baseline as:
    • the current main SQLite corpus
    • plus the accepted 30-sample supplementation layer from the tracking sheet
  • Defined a practical benchmark v1 scope:
    • 19-query regression nucleus
    • 6 staged expansion queries from the existing 25-query eval fixture
  • Defined the minimum experiment matrix:
    • baseline_current
    • dense_only
    • lexical_only
    • hybrid
    • hybrid_plus_metadata_refinement_placeholder
  • Wrote down the experiment logging contract for future runs.

Why

  • The supplementation freeze is complete, so the next priority is to make the benchmark phase reproducible before running another round of comparisons.
  • Locking the baseline artifacts now prevents the first benchmark-phase run from inventing dataset or query definitions ad hoc.

Validation

  • No retrieval logic was changed.
  • No retrieval run or experiment was executed.
  • No synthetic results were produced.
  • The dataset snapshot and query-bucket files are planning artifacts only.

Current Conclusion

  • Benchmark-phase bootstrap v1 is in place.
  • The project now has a locked dataset snapshot, a realistic query-scope definition, a minimum experiment matrix, and a logging contract for the next evaluation cycle.

Next Step

  • Start the first benchmark-phase run record using the locked dataset snapshot and the 19-query nucleus.

2026-04-12 21:55 - Benchmark-Phase Run Records v1

Goal

  • Execute the first real benchmark-phase run records against the locked 19-query nucleus and document the four-mode comparison in a reusable format.

Changes Made

  • Executed the benchmark nucleus for:
    • baseline_current
    • dense_only
    • lexical_only
    • hybrid
  • Wrote machine-readable run outputs to:
    • output/benchmark_runs_v1/baseline_current.json
    • output/benchmark_runs_v1/dense_only.json
    • output/benchmark_runs_v1/lexical_only.json
    • output/benchmark_runs_v1/hybrid.json
    • output/benchmark_runs_v1/summary.json
  • Created markdown run records:
    • docs/benchmark_run_record_v1_baseline_current.md
    • docs/benchmark_run_record_v1_dense_only.md
    • docs/benchmark_run_record_v1_lexical_only.md
    • docs/benchmark_run_record_v1_hybrid.md
  • Created a cross-mode comparison document:
    • docs/benchmark_run_summary_v1.md

Why

  • The benchmark bootstrap phase was already locked, so the next step was to stop planning and generate the first actual comparison records.
  • Writing both JSON outputs and markdown run records makes the benchmark phase easier to repeat and compare without rerunning exploratory notebook-style analysis.

Validation

  • No retrieval code was modified.
  • No supplementation changes were made.
  • All four requested modes executed successfully on the locked 19-query nucleus.
  • Observed high-level results:
    • baseline_current: 19/19 queries returned results
    • dense_only: 19/19 queries returned results
    • lexical_only: 19/19 queries returned results
    • hybrid: 19/19 queries returned results
  • Cross-mode summary from this pass:
    • dense_only top-1 differed from baseline_current on 7/19 queries
    • lexical_only top-1 differed from baseline_current on 19/19 queries
    • hybrid top-1 differed from baseline_current on 0/19 queries

Current Conclusion

  • The benchmark phase now has a real first-pass comparison record instead of only planning artifacts.
  • hybrid remains the right family to continue with, while lexical_only is clearly diagnostic-only and dense_only remains weaker on metadata-constrained prompts.
  • The next likely gain is in metadata-aware refinement and precision control rather than another immediate fusion redesign.

Next Step

  • Add the next comparison row on top of the same 19-query nucleus for metadata-aware refinement or another precision-oriented post-retrieval pass.

2026-04-12 23:05 - baseline_current vs hybrid Clarification + Metadata Refinement v1

Goal

  • Clarify the real implementation difference between baseline_current and hybrid, then run a minimal metadata-aware refinement experiment on top of hybrid using only lightweight, reversible soft adjustments.

Changes Made

  • Generated output/hybrid_plus_metadata_refinement_v1.json.
  • Created docs/benchmark_run_record_v1_hybrid_plus_metadata_refinement.md.
  • Created docs/benchmark_run_summary_v1_addendum.md.
  • Ran a new comparison between:
    • hybrid
    • hybrid_plus_metadata_refinement_v1
  • Kept the refinement scoped to:
    • role_family
    • employment_type
    • location / work_mode
  • Explicitly tightened the employment signal so it uses only:
    • raw employment_type
    • clear title clues and does not parse description text for internship-style signals.

Why

  • The earlier benchmark pass showed that hybrid had no visible top-1 gain over baseline_current, but the exact reason needed to be tied back to the real code path rather than intuition.
  • A minimal metadata-aware refinement pass was the next logical experiment because the benchmark summary showed continued weakness on role-boundary and metadata-constrained queries.
  • Tightening the employment signal was necessary to avoid semantic pollution from false positives in free-text descriptions.

Validation

  • No retrieval code, embedding config, index artifacts, frontend, or summary logic were changed.
  • The experiment reused:
    • benchmark_dataset_snapshot_v1
    • the locked 19-query nucleus
  • Observed metrics:
    • top-1 changes vs hybrid: 0/19
    • source-card drift count: 4/19
    • average top-k role mismatch rate:
      • hybrid = 0.2500
      • refined = 0.2167
    • metadata top-1 satisfied count:
      • hybrid = 4
      • refined = 4
    • metadata top-3 satisfied count:
      • hybrid = 5
      • refined = 5
  • Low-evidence behavior remained unchanged.

Current Conclusion

  • baseline_current and hybrid are currently very close in implementation: the active difference is mainly the small merge-stage metadata boost, while the final reranker remains noop.
  • hybrid_plus_metadata_refinement_v1 produced a mild top-k denoising effect, but no visible top-1 improvement and no metadata-satisfaction improvement on the current nucleus.
  • Metadata-aware refinement remains promising as a direction, but this v1 should stay experimental rather than being promoted to the mainline path.

Next Step

  • Design a narrower hybrid_plus_metadata_refinement_v2 focused on explicit role-boundary and remote-constraint failures instead of broad weak boosts.

2026-04-12 23:45 - Metadata Refinement v2 (Narrow Scope)

Goal

  • Test whether a narrower, stronger metadata-aware refinement can create clearer gains on the query types most likely to benefit: explicit role-boundary queries and explicit remote/location constraint queries.

Changes Made

  • Generated output/hybrid_plus_metadata_refinement_v2.json.
  • Created docs/benchmark_run_record_v1_hybrid_plus_metadata_refinement_v2.md.
  • Created docs/benchmark_run_summary_v1_v2_addendum.md.
  • Ran hybrid_plus_metadata_refinement_v2 against the locked 19-query nucleus.
  • Restricted v2 triggers to a 5-query subset only:
    • remote_data_requirements
    • analyst_vs_ml_roles
    • remote_analytics_expectations
    • english_speaking_jobs_germany
    • remote_vs_onsite_expectations
  • Explicitly did not trigger visa_sponsorship_requirements because the current corpus lacks a reliable visa-support field.

Why

  • The v1 refinement was too broad and too weak, which diluted any possible benefit.
  • Narrowing the trigger scope makes it easier to see whether metadata-aware refinement helps where it should help, instead of averaging the effect away across unrelated queries.
  • The final v2 pass also corrected two overly broad behaviors discovered during iteration:
    • Germany-only queries should not automatically receive work-mode shaping
    • role-boundary queries need a small penalty on unknown role-family rows to avoid irrelevant unknown-role items rising after adjacent-role demotion

Validation

  • No retrieval code, embedding/index configuration, frontend logic, or summary pipeline logic was changed.
  • The experiment reused:
    • benchmark_dataset_snapshot_v1
    • the locked 19-query nucleus
  • Final subset metrics:
    • subset top-1 changes: 1/5
    • subset metadata top-1 satisfied count:
      • hybrid = 3
      • refined = 4
    • subset metadata top-3 satisfied count:
      • hybrid = 4
      • refined = 4
    • subset source-card drift count: 3/5
    • subset average top-k role mismatch rate:
      • hybrid = 0.5000
      • refined = 0.0000
  • Outside the triggered subset:
    • top-1 changes = 0
    • source-card drift = 0
  • Low-evidence side effects:
    • none observed, because the low-evidence query was not triggered

Current Conclusion

  • The narrow-scope v2 experiment produced the clearest positive metadata-refinement signal so far, but only for the remote/location branch.
  • remote_data_requirements improved in a meaningful way, including top-1 metadata satisfaction.
  • The role-boundary branch is still not trustworthy enough to promote, because its mismatch metric can improve even when lower-ranked results drift to semantically weaker roles.
  • Metadata-aware refinement should continue, but only as split branches rather than one broad shared knob.

Next Step

  • Evaluate remote/location refinement and role-boundary refinement as two separate experimental candidates.

2026-04-12 00:20 - Supplementation Freeze Finalization

Goal

  • Finalize the supplementation freeze by normalizing all frozen rows in the tracking sheet to accepted and confirming the accepted set is ready for benchmark-phase use.

Changes Made

  • Updated all frozen analyst and data-engineer rows from provisional_yes to accepted in output/supplementation_tracking_sheet_v1.csv.
  • Left backup, hold, and reject rows unchanged.
  • Added docs/supplementation_freeze_review.md to summarize the final accepted set and its readiness state.

Why

  • The supplementation decisions were already complete, but the ledger still mixed frozen rows with provisional status labels.
  • Finalizing those labels removes the last administrative blocker before using the accepted set as benchmark-phase augmentation data.

Validation

  • No retrieval logic, benchmark logic, or application code was changed.
  • No retrieval run or new experiment was executed.
  • Confirmed the final state counts are:
    • accepted = 30
    • backup = 1
    • hold = 16
    • reject = 7
  • Confirmed the accepted bucket totals remain unchanged and sum to 30.

Current Conclusion

  • The supplementation freeze is finalized.
  • The accepted 30-sample set is now formally synchronized in the tracking sheet and can be used as the benchmark-phase enhancement corpus.

Next Step

  • Start the next benchmark / hybrid evaluation pass using the finalized accepted 30-sample supplementation set.