refresh-since-manifest-race-deflake
Goal
refresh --since routes the —changed scope deterministically under parallel-session background syncs; the CLI harness case never flakes.
Diagnosis
scripts/intel-query.sh --json refresh --since HEAD~3 --dry-runfalls back toCODE_ARGS=(--full)when_refresh_manifest_compiler_shareturns empty or an unresolvable SHA (scripts/intel_query/02b-refresh.sh).- NOT a manifest-write race: writes are atomic (
graph_manifest.py_atomic_writemkstemp + os.replace under_manifest_lockflock) — torn/mid-rewrite reads are impossible. - The pytest failure is DETERMINISTIC: conftest.py’s autouse
_isolate_graph_manifestpinsORI_GRAPH_MANIFESTto a never-created tmp path that leaks throughsubprocess.runinto the bash harness — the manifest is genuinely absent for the whole subprocess lifetime. Cure: harness-case hermetic manifest fixture. - The separate PRODUCTION hazard is write-side semantic nulling: dirty-tree/errored background syncs passed
None(not the_UNSETsentinel) into the manifest writers, clobbering the last-known-goodcompiler_shafor minutes-to-hours. Cure: PRESERVE_COMPILER_SHA on unverifiable generations. - The —full fail-safe stays correct production behavior for a GENUINELY absent manifest.
Work Items
- Root-cause and cure the deterministic
refresh --since <SHA>fallback to--full: empirical reproduction shows this is NOT a manifest-write race — writes are atomic viagraph_manifest.py’s_atomic_write(mkstemp + os.replace) under_manifest_lock(flock), so torn/mid-rewrite reads are impossible. The pytest failure oftests/intel_query_cli/04-refresh.sh::test_refresh_since_dry_run_forwards_changed_scope(undertest_intel_query_cli_runner.py) is deterministic: the autouse_isolate_graph_manifestfixture (intel_repo/tests/conftest.py:83-94) pinsORI_GRAPH_MANIFESTto a per-test tmp path that leaks throughsubprocess.run’s inherited env into the bash harness, so the manifest is genuinely (not transiently) absent for the whole subprocess lifetime. Cure has TWO required surfaces: (i) harness hermeticity — give the harness case (or the runner invoking it) its own valid manifest fixture by composingORI_GRAPH_MANIFEST+ORI_COMPILER_ROOT, per the existing pattern already used inintel_repo/tests/intel_query_cli/06-ensure-fresh-tracing.sh:100, instead of relying on whatever the ambient/inherited env happens to provide; (ii) write-sidecompiler_shapreservation — change the two call sitessync_ori_graph.py:852-855(incremental,stats["errors"] != 0) and:1085-1086(full sync,committed_scope_matchesfailure) to pass the existing_UNSETsentinel instead ofNoneintoupdate_manifest, so a merely-unverifiable generation (dirty tree / sync error) PRESERVES the last-known-good sha rather than nulling it — an older preserved sha only over-collects the--changedscope for--since, never under-collects, so this is safe for every consumer. As part of this cure, fold the ad hocrunpyheredoc in_refresh_manifest_compiler_sha(scripts/intel_query/02b-refresh.sh:198-210) into agraph_manifest.pyCLI subcommand (sibling of the existingensure-freshsubcommand) so the shell has one SSOT read API instead of a second embedded manifest reader (closes the latentLEAK:algorithmic-duplication). NEVER weaken, skip, or make the harness pin tolerant of--full; NEVER cure via a bare retry loop around the read — retry fixes neither mechanism (the env-pinned absence is permanent for the subprocess’s whole lifetime; the write-side null persists until the next clean-tree sync, not until a retry succeeds). - Regression coverage for BOTH real cure surfaces from w-e8f0638e (the recorded ‘mid-rewrite window during the read’ premise is false — manifest writes are atomic; do not attempt to reproduce it): (1) a hermetic version of the
--sinceharness case (or the case itself, made hermetic) that composes its own validORI_GRAPH_MANIFEST+ORI_COMPILER_ROOTfixture instead of relying on ambient/inherited env, provingtest_refresh_since_dry_run_forwards_changed_scopepasses deterministically under BOTH pytest and standalone invocation — proof the fix closes the harness-hermeticity gap, not that the assertion was loosened; (2) a Python-side test (alongsidetest_manifest_reconcile.py/test_ensure_fresh.py) assertingupdate_manifest/run_incrementalPRESERVE a previously-recordedcompiler_shavia_UNSETrather than nulling it viaNonewhen a sync is dirty-tree-unverifiable (committed_paths_match/committed_scope_matchesfails) or errored, closing the currently-unpinned write-side policy gap. The existing harness case (04-refresh.sh:76-102) stays as the production-path pin verbatim — its assertion is never loosened, only its execution environment is made hermetic.
Fresh intel (regenerated)
The section’s recorded diagnosis is attributing the flake to the WRONG mechanism, and the prescribed cure (bounded-retry manifest read) will NOT fix the failing harness case. Empirical reproduction today (2026-07-14):
timeout 150 intel_repo/.venv/bin/python -m pytest intel_repo/tests/test_intel_query_cli_runner.py::test_intel_query_cli_harness_passes— FAILED (rc=1, 98s), at exactly the refresh group.ORI_GRAPH_MANIFEST=/tmp/.../nonexistent.json bash intel_repo/tests/test_intel_query_cli.sh— isolates the failure to exactlytest_refresh_since_dry_run_forwards_changed_scopeand nothing else.ORI_GRAPH_MANIFEST=<nonexistent> scripts/intel-query.sh --json refresh --since HEAD~3 --dry-run→code_cmd: ["sync-ori-graph.sh","--full"]; without the override →--changed <scope>.
[JOIN] (the decisive one): intel_repo/tests/conftest.py:83-94 defines _isolate_graph_manifest — an autouse=True fixture that monkeypatch.setenv("ORI_GRAPH_MANIFEST", str(tmp_path / "test-graph-manifest.json")) for EVERY test in the suite, including test_intel_query_cli_harness_passes (test_intel_query_cli_runner.py:36). That test subprocess.run(["bash", ...])s the CLI harness (:28-33), and the subprocess INHERITS os.environ — so inside the pytest run the bash harness’s refresh --since reads a manifest path that NEVER EXISTS, read_manifest() returns None, _refresh_manifest_compiler_sha prints nothing, and 02b-refresh.sh:216 routes CODE_ARGS=(--full). This is DETERMINISTIC under the pytest runner and impossible standalone — which exactly matches the observed “two consecutive pytest runs failed while every standalone invocation passed”. It is a test-isolation boundary leak, not a race. A retried/atomic manifest read (w-e8f0638e’s prescribed cure) cannot fix it: the manifest is genuinely absent for the entire subprocess lifetime, and --full is the CORRECT production fallback for genuinely-absent.
A real production hazard DOES exist, but it is not a torn read either. All manifest writes go through _atomic_write (graph_manifest.py:89, temp-file + os.replace) under _manifest_lock (:104), so a reader can never observe partial JSON. What a reader CAN observe is a manifest whose compiler_sha was semantically nulled by the parallel post-commit background sync (lefthook.yml:419, nohup scripts/intel-query.sh refresh):
- Incremental error path:
sync_ori_graph.py:852-855calls_maybe_update_manifest(..., None)wheneverstats["errors"] != 0— andupdate_manifest(graph_manifest.py:187-188) OVERWRITEScompiler_shawithNonebecauseNone is not _UNSET. - Dirty-tree path:
sync_ori_graph.py:827-835setsgeneration_sha = Nonewhenevercommitted_paths_match(rust_scip_snapshot.py:85-97) sees ANY synced path dirty/untracked vs captured HEAD; the full path does the same viacommitted_scope_matches(:100) atsync_ori_graph.py:1003-1005beforewrite_full_manifest(source_root, compiler_sha=generation_sha)(:1085-1086). In this project’s normal multi-session environment (“dirty/shared tree is NORMAL”), post-commit syncs routinely stampcompiler_sha: null, and the null PERSISTS until the next clean-tree sync — a window of minutes to hours, not microseconds.
Decisions this package forces before any work:
- Re-ground both work items. w-e8f0638e’s “atomic or retried manifest read” premise is falsified: the read IS atomic; retry cannot cure either mechanism (env-pinned absence is permanent for the subprocess; compiler_sha-null persists until the next clean sync). The cure splits into two surfaces: (i) harness hermeticity at the pytest→subprocess boundary (give the harness case a real manifest fixture, or have the runner/case compose its own
ORI_GRAPH_MANIFEST+ORI_COMPILER_ROOTpointing at a valid fixture — the exact patterntests/intel_query_cli/06-ensure-fresh-tracing.sh:100already uses); (ii) write-side last-known-goodcompiler_shapreservation (pass_UNSET, notNone, when the generation is merely unverifiable — safe for the--sinceconsumer because an OLDER baseline only over-collects the changed set, never under-collects). - Do not weaken the pin —
test_refresh_since_dry_run_forwards_changed_scope(tests/intel_query_cli/04-refresh.sh:76-102) is the production-path pin the section explicitly protects; both cures must make it green under pytest AND standalone (INVERTED-TDD:positive-test-modification/:disabled-negative-pinotherwise). - This terrain is graph-dark. Zero
:Symbol/:Filerows exist forscripts/**orintel_repo/**(the code graph indexescompiler/,tests/,library/only) — the platform-estate ingestion is the deliverable of sibling sectionss-b9cacd0d(platform-domain-python-estate) ands-3a837610(platform-domain-artifact-fabric), both not-started. All code recon below is filesystem-grounded and every claim carries a runnable command.
(full dossier: refresh-since-manifest-race-deflake—s-031257a4.intel.md)