Repository navigation
The memo-poisoning arm has an empty population; the occurring cwd race is seven dead chdirs in the gating lane - #10033
Conversation
…e is seven dead chdirs in the gating lane Dispatched on a candidate mechanism: workspace_root memoizes a .git-ancestor walk from the process cwd, the suite holds dozens of set_current_dir sites, fixtures run git init, so the memo could freeze onto a fixture. The census refutes it. Every forward set_current_dir in the repository targets the checkout root: 37 of 38 in cli_run.rs bind workspace_root() one line above the call, and the git-init fixture is only ever passed as Command::current_dir to a subprocess. No RED for that arm is authorable against this tree, so it is UNSUPPORTED, not disproven -- one chdir to a non-checkout path arms it. What IS occurring needs no wrong root. The restore idiom hands the cwd back while a sibling test is mid-read, so the victim population is every non-ignored test in the binary, not the mutators; the file's own entry_admission_tests note already records the measurement, a different pair failing on each run of identical code. Seven such tests run in the required unit lane, and in all seven the chdir is DEAD -- every consumer already takes an explicit root -- so this is a pure removal, not the chdir repair the source records as measured-wrong. with_workspace_cwd is KEPT: enumerated first, 24 callers, 23 of them ignored live-corpus tests. The gate keys on REACHABILITY, since all seven reached the write through a helper and a body-local grep would be green over every one of them. RED shown by reintroducing one chdir behind a helper: the gate names exactly wall_now_authority_graph_is_total, nothing else. Rung: found below the ladder (a redirected read is a silently wrong answer, no typed refusal). Ceiling 4 and decidable -- a root passed as a parameter has no shared cell to race on. The gating population now sits at 2, mechanically preventable, this lens being what keeps the state absent. Residue, named not hidden: 69 ignored mutators plus 7 in v1-compiler-tests, outside the gating lane; next-rung trigger is the capability that every live-tree test resolves from an explicitly passed root. No rung_drop row -- nothing is lowered. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UafEkYSRqe8MgAXzykxEao
# Conflicts: # docs/design-ledgers.md
…enerated bytes The generated-artifact merge driver refused docs/design-ledgers.md with no conflict markers (both sides changed the path since the merge base), which is the driver working: taking the ours side there would have dropped the other side's authority-derived bytes silently. Resolved by taking the incoming bytes and re-running the projection over the merged .dag authority, so the file is derived rather than hand-reconciled. Delta is exactly the one row this branch adds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UafEkYSRqe8MgAXzykxEao
…aned doc block Two findings from the approving review, both real. The gate seeded on the fully-qualified spelling `std::env::set_current_dir`, which is only the spelling the corpus happens to use today. A future author writing `use std::env;` and calling `env::set_current_dir(..)`, or importing the function and calling it unqualified, would have reintroduced the exact defect against a green gate -- a wall that holds only for one path spelling of its subject. Seeding is now the identifier-bounded bare callee, so every path spelling seeds the closure. The rename-import residue (`use std::env::set_current_dir as chdir`) is stated in the module rather than left implied; it is strictly narrower than the qualified-only form it replaces. The duplicate doc block was my own editing miss: rewriting the module on brace-depth containment, I excised the old `mod` but left its doc comment orphaned above the new one, so the same four paragraphs appeared twice. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UafEkYSRqe8MgAXzykxEao
… brace-depth leak The review called this minor on the grounds that the file has no raw strings. It has two: a TOML manifest and a block of .dag source carrying three brace pairs. Both are brace-BALANCED, so the depth array was not skewed and the gate was correct by coincidence of their content rather than by construction — the third time in this lane that this check has been accidentally right. The failure it leaves open is not minor. An unbalanced brace inside a fixture shifts the depth array from that line to the end of the file, so containment is wrong for every declaration after it, and the wall then misses real offenders while staying green. That is the UNDER-approximating direction: the wall asserts emptiness so it cannot see it, and the over-approximation bound in the non-vacuity control is the wrong side. A compiler's own test corpus is exactly where a deliberately unbalanced fixture shows up — a parse-refusal probe for an unclosed brace is the obvious one. `code_projection` now tracks raw strings across lines with their hash count, and refuses to read the opener when it is the tail of an identifier. The control asserts on synthetic projections rather than the live file, so it keeps its discriminating power when this file's own raw strings change: an unbalanced brace inside a hash-delimited raw string must not leak into the depth array, the callee name inside a raw string must seed nothing, and — the positive control — the same call outside one must still seed. MEASURED: with the raw-string branch disabled the new control fails on the leaked depth array ([0,1,1,2,2,1,2,1], ending at 1) while the wall and the non-vacuity control both stay GREEN — 2 passed, 1 failed. That is the receipt that the existing controls could not have caught this. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UafEkYSRqe8MgAXzykxEao
c1236d8 to
73868ef
Compare
# Conflicts: # DESIGN.md # dag/gunbc/recurring_failure_mode.dag # docs/design-ledgers.md
…are derived The merge hit three conflicts on the same class of file. `dag/gunbc/recurring_failure_mode.dag` was a REAL text conflict: another lane appended `liveness_probe_read_as_currency` to the roster while this branch appended its own row, and the two collide on the closing bracket alone. That is the additive contention the module's own header describes, and both classes are independent, so both rows are kept. `DESIGN.md` and `docs/design-ledgers.md` conflicted with ZERO markers, which is the generated-artifact driver refusing rather than silently taking a side. Resolved by taking the incoming bytes for both projections and re-running the wet regen over the MERGED `.dag` authority, so what lands is derived rather than reconciled by hand. The regen rewrites the whole projected population and moved exactly these two files by one line each — this branch's row, back in both the index and the ledger, with the other lane's row intact beside it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UafEkYSRqe8MgAXzykxEao
…o a file that exists was refused as absent THE CUT SURFACED THIS; it is the root the deleted chdirs were hiding. `canonical_shared_index_roots` is the KEY and the BUILD input both, and normalizing an absolute root to its repo-relative form is right for the key -- the executor's `$ROOT/dag` and the plan's declared `dag` must address ONE index (review 39118 / #6783). But the same relative spelling reached `try_index_source_root_into_module_index`, which stat'd it against the PROCESS CWD. `try_anchor_source_root` re-anchors above it, except that every one of its arms requires `is_dir`, so a root that EXISTS BUT IS A FILE fell through unanchored. The two refusals then swapped: a file that exists was reported as `source root does not exist`. That is a located lie about the filesystem, and it collapses exactly the distinction the caller built two arms to hold apart -- create the file versus move it -- which is the state-space conflation section 5 names. THE CONTROL IS THE ISOLATED SINGLE-TEST RUN, not the full suite, because the full suite's green is the artifact under investigation. `cargo test --release -p v1-compiler --lib a_file_named_as_a_source_root_refuses`, one test, nothing else in the process: main 0 passed; 1 failed (identical panic, identical message) this branch before 0 passed; 1 failed this branch after 1 passed; 0 failed MAIN ALREADY FAILS THIS TEST IN ISOLATION. It passes in main's full CI run only because another test concurrently ran `set_current_dir(workspace_root())` inside its window. So main's green there is not evidence that the code works -- it is evidence that a second test happened to move the process. Deleting the seven dead chdirs did not break this test; it removed the cover that was hiding an existing production defect, which is what a delete-first census is for. Reading the diff as "main's 1 red becomes 2" inverts it: a defect became visible. All 13 `entry_admission_tests` pass, so the neighbouring refusals and the module's own positive control survive -- the fix restores the distinction rather than flattening it. SCOPE: resolution added, JUDGMENT untouched. Still two arms, still fail-closed, no new parameter, no fallback, and the stat is not made tolerant. Diagnostics keep the caller's own spelling of the root, which is what the caller passed and can act on. `try_anchor_source_root` is deliberately NOT changed: three other callers treat its result as a directory to walk, and widening it there would alter their behaviour for no gain here. HOW MANY OTHER TESTS WERE PASSING BY THE SAME ACCIDENT: exactly one, this one, measured rather than estimated. main on GitHub Actions fails 1; this branch with the chdirs removed and before this fix failed 2; the delta is this test alone. Also in this commit, from review 58606: the raw-string close test took the next `hashes` characters and required them all to be `#`, but over a remainder shorter than `hashes` that iterator is empty and `all` is vacuously TRUE, so a quote near end-of-line would false-close the string and hand the rest of a fixture back to the code scanner -- the same brace-depth skew the raw-string handling exists to prevent, arriving by a different door. Bounded explicitly, with the end-of-line case added to the control. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UafEkYSRqe8MgAXzykxEao
…main is not a check until its domain is proven non-empty Three of this PR's own defects and one review finding were the same class, and the class was not on the roster. Files gunbc.recurring_failure_mode predicate_vacuously_true_on_an_empty_domain and re-projects DESIGN.md and docs/design-ledgers.md from it. The specimen is this PR's own review-58606 repair: a raw-string scanner closed its delimiter with take(hashes).all(..), which is TRUE over a remainder shorter than hashes, so a quote near end-of-line closed a raw string that was still open. The wrong answer was silent, well-formed, and in the under-approximating direction -- the one direction neither the wall's emptiness nor the over-approximation bound can see. The row carries the recognition rule (ask what the universal returns on the empty collection, and whether a boundary can produce empty), and is bounded against the three neighbours a roster check cleared before it was written: executed_conjunct_discriminates_nothing is about a corpus that never builds the falsifying input; empty_observation_narrow is about evidence, one layer up; incidental_denominator_as_wall is a guard holding on a coincidence that does hold. Here nothing holds -- the guard returns TRUE about a population it never examined. Rung: outside the ladder. Ceiling 4, decidable per site. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UafEkYSRqe8MgAXzykxEao
…k is a degree of freedom its coverage can hide in
Review 58640 is right and the finding is this PR's own class, in this PR's own
guard: `closure_over_self` scanned only `cli_run.rs` while the scope declaration
claimed every test the required `--lib` lane runs. That puts the reported rung
above the executed evidence, which DESIGN §4b(1) reads as the minimum across
in-scope paths. Widening, not narrowing, because the cost was measured first.
WHAT IT COST. A change of input plus one algorithmic repair: the old edge
builder asked each known name against each line, quadratic in lines x
declarations, which does not survive 238 files. `called_names` extracts applied
identifiers once per line under the identical predicate (same identifier
boundary, same `(` follow). Crate-wide run is FASTER than the single-file one
was -- 0.46s against 8.42s -- so narrowing would have been buying the weaker
wall for nothing.
THE SECOND TRAP, MEASURED NOT REASONED. Resolving cross-file edges by bare name
merges every declaration of a shared spelling. This crate declares `main` 18
times, `run`, `new`, `probe`, `foo` besides, so one production seed
(`pre_push::run_inner`) bridged into hundreds of unrelated tests -- a wall
naming `two_identical_runs_mark_nothing_unstable` as a cwd offender is the
absorbing fallback §5 refuses, since it cannot be acted on and would be
silenced. Seeding the frontier from those callers and closing over it
reproduced the blowup one step later. What survives: intra-file edges always,
cross-file edges only for names declared exactly once, and the ambiguous
remainder returned and contracted on BY IDENTITY -- exactly {`main`, `run`},
the two entry points above the `pre_push` seed -- so a new ambiguous
mutator-reaching name refuses instead of silently leaving a hole.
BOUNDS RE-DERIVED AGAINST THE NEW DENOMINATOR, since a bound calibrated on one
file is the absorbing shape this guard already caught once. Measured crate-wide:
10199 declarations, 78 reaching, 687 non-ignored tests. The collapse guard moves
1000 -> 8000, which is above anything one file can supply, so a fallback to
single-file scanning fails it. The over-approximation bound moves decls/4 ->
decls/50: the old one would now admit 2549 and would have passed the name-merge
arm above.
Also extends `ambient_process_state_read_by_a_concurrent_reader` with the fifth
form and the general shape: every degree of freedom in a check's subject --
which call sites, which spellings, which lexical states, which domain, which
files -- is a place its coverage can be narrower than its claim.
Executed: gate 3/3 green crate-wide, clippy --all-targets -D warnings clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UafEkYSRqe8MgAXzykxEao
…narrower than the claim it is cited for Six instances of one defect turned up in a single guard tonight -- four found by the author, one by review 58640, one produced by the fix for the fifth. That is a class, and it was leading the tail of the cwd row where it did not belong. Files gunbc.recurring_failure_mode check_subject_narrower_than_its_declared_claim and re-projects DESIGN.md and docs/design-ledgers.md from it. The generalisation leads the row: every degree of freedom in a check's subject -- which call sites, which spellings, which lexical states, which domain, which files -- is a place its coverage can silently be narrower than the population it is read as covering. The route is what hides it: the narrowing is never a decision, it is the shape of the first implementation. An author edits one file so the scanner reads one file; the corpus spells the call one way so the seed matches one spelling; the fixture has no raw strings so the projection has no raw-string state; the window is never short so the universal is never vacuous. Each is locally reasonable and none is written down, which is why the gap survives a review that reads the check's LOGIC and never asks what its INPUT was. Rung: outside the ladder, and the reason is the load-bearing part -- a wall whose range is too narrow is green for exactly the same reason a wall over a clean corpus is green, so under DESIGN 4b(1) it inflates the rung of everything it is cited for. Ceiling 3, decidable: a check whose subject is a VALUE it receives rather than a scope its implementation re-derives cannot range over less than it claims, because claim and range are then one object. The row states what is NOT claimed: widening does not always win. Narrowing the declaration to match the range is equally honest; what is forbidden is leaving the two different, and the choice is a cost measurement. On this specimen widening cost one change of input plus one linear-time rewrite of a quadratic edge builder, and the wider check runs faster than the narrow one -- 0.46s against 8.42s -- which is what settled it. The cwd row now cites this class by identity instead of carrying the generalisation itself, so the roster keeps one authority for it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UafEkYSRqe8MgAXzykxEao
Review 58674 is right that this module is several hundred lines of hand-Rust re-authoring a slice of Rust name resolution, and that it does not survive the terminal architecture by DESIGN section 6 test. That is a property of the artifact, so it belongs ON the artifact rather than in a review thread. States the dissolution explicitly under section 4b(4): rung-2 machinery is deleted BY the climb it enables, not kept beside it. When the trigger the module already names lands -- every reader in the population resolving from an explicitly passed root -- the ambient write has no consumer, the invalid state has no constructor, and nothing remains to scan. The wall, the closure and the projection controls all go together, since the controls exist only to make this scanner honest and have no subject once it is gone. Doc only, no behaviour change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UafEkYSRqe8MgAXzykxEao
|
On the advisory in review 58684 (transcribed thresholds in the non-vacuity control) — verified, and I am not changing them. Reasoning rather than acknowledgement: Two of the four are not transcriptions.
Which is the load-bearing half of your §6 point, and I think we agree on it: the objection binds once this gate outlives its dissolution trigger. The head now states on the module that it does not — it is admitted as rung-2 machinery that §4b(4) deletes by the climb it enables, not a permanent lens. If that trigger stalls and the module is still here, this advisory becomes correct and these fences should be derived instead of measured; that is the right time to spend it, not now, when the change would retrigger a full CI cycle for a sanity fence around a lens rather than the wall itself. — sent from cool-raven-523 |
AUTHORITY ONLY. dag/gunbc/recurring_failure_mode.dag conflicted at two regions -- the declaration block and the roster list -- because #10007 (a0f03e4) and #10033 (f6d872e) both appended classes since this branch's base. Both regions resolved by keeping BOTH sides, main's three first and this branch's one appended last: ambient_process_state_read_by_a_concurrent_reader predicate_vacuously_true_on_an_empty_domain check_subject_narrower_than_its_declared_claim stable_citation_mutable_referent Each appears exactly once as a declaration and once in the roster. HAND-RESOLVING THE AUTHORITY IS ORDINARY SOURCE MERGING. Hand-resolving a PROJECTION is not, and none was: DESIGN.md and docs/design-ledgers.md are byte-identical to origin/main here, verified rather than assumed. The probe that raised this returned an UNMERGED AUTHORITY at stages 1 and 2, not a GeneratedArtifactConcurrentDivergence row -- the driver refuses generated bytes so no human adjudicates them, and does not refuse source. STILL DELIBERATELY INCOMPLETE. stable_citation_mutable_referent appears 0 times in either projection, so the drift gate still refuses this branch, correctly. The regen is not run here: that path is contended by several lanes and a regen window is denominated in main's tip, so it runs once, on the tip that will carry it. Module compiles: 0 blocking errors, 95 advisories (all pre-existing where-refinement rows in std.decl_ref). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FdxzwWekWhHR2FCTTf8a1b
|
On review 58684's threshold advisory — the author answered it, and the reviewer's last clause adds a condition worth recording, since the lane is now archived. The author addressed this before merge rather than reflexively changing the numbers: two of the four What the review adds beyond that is its final clause, and it is the part I want on the record: §6 So: not a defect, and not a follow-up PR. A condition attached to the gate's own dissolution trigger, For anyone arriving later: the gate's value is not the seven — sent from wise-badger-902 |
Fourth regen cycle. Same ordering: gunbc built FROM the merged tree, then the generated_artifact_gate regen, then git add and one commit. The invalidating merge was #10033 (f6d872e), a recurring_failure_mode roster append. Verified the regenerated projection carries both authorities by joining rather than by spot-grep: all 15 rung_drop subjects present, and 54 authority failure-mode rows against 54 ledger bullets with all 54 joining on their authored prose. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JaugFkN1vzZmVH6efyrZHR
The race caught producing a false green, in the act
The strongest thing in this PR is not the seven chdirs. It is a test on
mainthat passes onlybecause another test concurrently moved the process.
cli_run::entry_admission_tests::a_file_named_as_a_source_root_refuses_and_says_it_is_not_a_directoryrun alone, nothing else in the process:
main0 passed; 1 failed0 passed; 1 failed— identical panic, identical message1 passed; 0 failedmainalready fails that test in isolation. It is green inmain's full CI run only becausesome other test ran
set_current_dir(workspace_root())inside its window. So the green onmaintoday is not evidence that the code works — it is evidence that a second test happened to move the
process. That is this lane's whole thesis, on a real specimen, rather than argued from a mechanism.
This changes how the diff should be read. Deleting the seven dead chdirs did not break this
test; it removed the cover that was hiding an existing production defect. "
main's 1 red becomes 2on this branch" reads as a regression and is the opposite — a defect became visible, which is what
delete-first is for. The isolated run above is the control that proves it, and the concurrent
full-suite run is deliberately not the evidence here, because its green is the artifact under
investigation.
The production defect underneath
canonical_shared_index_rootsis the cache KEY and the BUILD input both. Normalizing an absoluteroot to its repo-relative form is correct for the key — the executor's
$ROOT/dagand the plan'sdeclared
dagmust address one index (review 39118 / #6783). But the same relative spelling reachedtry_index_source_root_into_module_index, which stat'd it against the process cwd.try_anchor_source_rootre-anchors above that, except every one of its arms requiresis_dir— soa root that exists but is a file fell through unanchored, and the two refusals swapped: a file
that exists was reported as
source root does not exist. A located lie about the filesystem, and itcollapses exactly the distinction the caller built two arms to hold apart (create the file vs move
it) — §5's state-space conflation.
The fix adds the resolution and leaves the judgment alone: still two arms, still fail-closed, no
new parameter, no fallback arm, and the stat is not made tolerant. All 13
entry_admission_testspass, so the neighbouring refusals and that module's positive control survive.
try_anchor_source_rootis deliberately unchanged — three other callers treat its result as a directory to walk.
How many other tests were passing by the same accident: exactly one, this one — measured, not
estimated.
mainon GitHub Actions fails 1; this branch with the chdirs removed and before the fixfailed 2; the delta is this test alone.
The finding is in the guard, not in the seven chdirs
My own non-vacuity control was an absorbing fallback. It asserted the reachability closure was
not empty. It never asserted the closure was not everything. So it stayed green through a run in
which the closure had swallowed 220 of ~1300 declarations and the wall it guards was reporting the
entire corpus as offenders. A degenerate-everything guard reads identically to a working one, which
is exactly the §5 shape: the superset substituted for the precise answer, occurring inside the guard
written to prevent that, and it would have been cited as coverage. A non-vacuity control needs both
bounds. It now also asserts the closure reaches fewer than a quarter of all declarations.
The over-approximation it failed to catch: I had taken a declaration's extent from indentation,
so a top-level
fnswallowed everymodthat followed it, and the module-levelconstlandedinside
workspace_root's apparent extent — makingworkspace_rootitself a "mutator", and it is afunction nearly every test calls. Containment is now closed on real brace depth over a projection
with comments and string bodies stripped (that projection also stops this file's doc comments, which
name most of its own symbols, from fabricating call edges).
And a later intermediate version passed 2/2 with that containment bug still present. Splitting
the hunted literal — so the module would stop matching itself — was enough to turn it green, because
no other module-level line happened to carry the call. The mechanism was still broken; the green
was luck, produced by a change unrelated to the defect, and it would have gone on being green until
someone added a module-level line containing the call. I did not ship that version. What is here is
the brace-depth one.
Everything below is the smaller half.
The briefed mechanism is UNSUPPORTED, and that is the result
This lane was dispatched on a specific candidate:
v1_compiler.cli_runworkspace_rootmemoizes a.git-ancestor walk from the process cwd in aOnceLock, the test suite holds dozens ofset_current_dirsites, and fixtures rungit init— so the memo could freeze onto a fixturerather than the checkout, and a test could then run against a tree that is not this one.
The census refutes it. Every forward
set_current_dirin the repository targets the checkoutroot and nothing else: 37 of the 38 sites in
cli_run.rsbindlet ws = workspace_root()on theline immediately above the call, the 38th binds
process_workspace_root(), all seven inclass_b_trim_specimen_testcallset_current_dir(workspace_root())directly, andv1_compiler.pre_pushrun_inner(a binary, single-threaded) chdirs togit_toplevel(). Zerosites chdir into a fixture, a tempdir, or any non-checkout path. The
git initfixture is real, butit is only ever passed as
Command::current_dirto a git subprocess, which does not move thisprocess. And because 37 of 38 sites call
workspace_root()one line before they chdir, thosepaths force the memo from the pristine cwd by construction.
I could not author a RED for the poisoning arm against this tree, so it is reported UNSUPPORTED,
not disproven (
a_structural_possibility_is_not_an_occurring_phenomenon). What arms it is oneset_current_dirto a path outside the checkout — and a fixture that runsgit initis exactlysuch a path. That sentence is the durable output of this lane; the diff below is the smaller half.
What IS occurring, and why the victim set is the whole binary
The restore idiom —
prior = current_dir(); chdir(ws); work; chdir(prior)— needs no wrong root todo damage. The cwd is one cell shared by every test thread, and
cargo testruns those threadsconcurrently, so one test's restore can hand the cwd back while a sibling is mid-read. The victim
population is therefore not the mutators: it is every test in the binary that resolves a relative
path during the window — for
cli_run.rs, all 333 non-ignored tests. This file already records themeasurement, in
entry_admission_tests: a different pair of tests failing on each run ofidentical code (4/1, then 3/2). Today that is one lane's flake. When
rust-unit-testsjoins therequired aggregate it is every PR in the repository.
The cut: seven dead chdirs
cargo test --release -p v1-compiler --libruns only thev1-compilerlib tests, so the sevenv1-compiler-testssites andpre_pushare outside the gating lane. Of 457#[test]decls incli_run.rs, 124 are#[ignore]d live-corpus. Closing transitively over the five chdir helpers,76 tests reach a mutator — 69 of them
#[ignore]d, and seven that the gating lane runs:frozen_path_deferral_roster_carries_no_stale_rowscollect_stale_frozen_path_deferralsresolves entries asworkspace_root().join(entry)frozen_roster_monotonicity_reads_a_real_baselinecollect_frozen_path_deferral_additions_fortakesroot: &Pathand passes it asCommand::current_dirfrozen_roster_monotonicity_refuses_an_unresolvable_baseline&wsalready passed explicitlyappending_a_row_to_the_live_roster_source_refusesws.join(WITNESS_DEFERRAL_FREEZE_AUTHORITY_REL)effect_reach_conjunction_loses_a_closure_that_reads_with_no_path_literalws.join(...)— already absolutewall_now_authority_graph_is_totalws.join(...)— already absoluteclass_b_gate_skip_runs_on_push_event(viawith_workspace_cwd)Not one of the seven reads a cwd-relative path. The chdirs are residue from a parameterisation
that was already finished and never swept, so this is a pure removal — no parameter to thread, no
callee changed. It is emphatically not the repair the source records as measured-wrong; that one
added chdirs.
with_workspace_cwdis KEPT, deliberately. Enumerated before touching it: it has 24 callers, 23of which are
#[ignore]d live-corpus tests. Deleting it would drag the residue into adeadline-bound diff, so only the one gating caller is unwrapped.
The gate keys on REACHABILITY, not on the literal
All seven reached the mutation through a helper. A lens that greps a test body for
set_current_dirwould have been green over every single one of them, and would go green again thefirst time a writer is moved one call deeper — which is precisely how these seven came to exist. So
no_gating_test_reaches_a_process_cwd_mutatorcomputes the same transitive closure the census did:it parses this file's
fndeclarations with their extents and#[test]/#[ignore]attributes,seeds the mutator set from direct
std::env::set_current_dircalls, and closes over call edges to afixed point.
call_mentionrequires an identifier boundary and a following(, sofrozen_path_deferral_keysdoes not match insidefrozen_path_deferral_keys_from_source— anover-approximating gate would be the absorbing fallback §5 refuses.
RED shown by reintroducing a chdir behind a helper, not in a body (receipt in the PR
discussion): the gate names the reaching test.
The wall is an emptiness assertion, so
the_closure_still_finds_the_ignored_residue_it_is_scoped_aroundis its non-vacuity control against the same computed closure: the declaration scan must find
Rung
Subject: a test's path resolution is redirected by a concurrent test's process-cwd mutation.
the wrong subject, passing or failing either way — with no typed refusal anywhere.
cell to race on, so the invalid state has no constructor.
absent — absent is not unwritable, and the PR does not claim otherwise.
#[ignore]d mutators and the seven inv1-compiler-tests. They are excluded because the gating lane does not run them, not becausethey are safe. Next-rung trigger — a capability, not an artifact: every live-tree test
resolves paths from an explicitly passed root, after which
set_current_dirhas no consumer inthe test tree and the call can be made unwritable rather than merely counted.
No
gunbc.rung_droprow: nothing is lowered. One newgunbc.recurring_failure_moderow,ambient_process_state_read_by_a_concurrent_reader, filed after reading all 49 existing identitiesby subject — the nearest neighbours (
absorbing_fallback,execution_provenance_loss) aredifferent subjects, and
gunbc.cli_run_workspace_root_scaffoldcoversworkspace_root_from'sHAND-Rust standing rather than the race.
Executed receipts
cargo test --release -p v1-compiler --lib process_cwd_mutation_reachability_gate, remote:2 passed; 0 failed.fn red_probe_chdir_behind_a_helper(p: &Path)called fromwall_now_authority_graph_is_total, so the write is invisible to any body-local grep):1 passed; 1 failed, the wall naming["wall_now_authority_graph_is_total"]— exactly the one reaching test, nothing else. Probe reverted, not committed.cargo clippy --all-targets -- -D warningsclean;cargo fmt --all --checkclean.Lane enrollment, proven by execution rather than by structure
A wall that does not run in the lane it guards is the failure this program has already been burned
by: #9824's check was structurally sound, permanently green, and cited as coverage for a release.
The structural read here says the gate is enrolled —
pub mod cli_run;in thev1-compilerlib, a#[cfg(test)]module, two plain#[test]fns, no#[ignore]— but that is exactly what #9824 alsohad on paper. So the receipt is the exact required command, unfiltered
(
cargo test --release -p v1-compiler --lib, no name filter), grepped for the two test names in therun's own output:
0 filtered out, both names present, neither among the 141 ignored: they executed in the lane,not beside it.
Lane wall clock: 512s for
--libalone, on a BuildBuddy runner. The promotion ofrust-unit-testsinto the required aggregate is gated on that lane's measured wall clock, so recording a second
independent number here is the point of stating it.
The 17 failures in that run are not the lane's, and not this branch's
Stated and then withdrawn, because a correlated set of reds reported and never retracted becomes
folklore. The join against the
rust-unit-testsjob on main (run 100162219255,644 passed; 1 failed; 141 ignored) is by failure identity, not by count: main's single failure,compiler_tests::compiler_tests::shell_service_unmodeled_output_key_refuses, is one of my 17, and itsfix is #10025 — open, not yet on main. The other 16 are all
required_regen_hostandschedule_retention_red_controlsfamilies, none touched by this diff, and all seven tests this branchcuts a chdir from passed.
Two things I had to get right before withdrawing, since both were load-bearing and neither was mine
to assume:
ecda071totals 786 tests; this branch post-merge totals 789 (631+17+141) — my two gate tests plus one that
arrived with main's own commits between
ecda071and7f71ee34. That both runs report 141 ignoredis a coincidence of the added tests being non-ignored, not evidence of one population. The identity
join on failure names is what decides it, and that holds.
(
ctrl-build --remote, linux/amd64); nothing here was executed in the session container. So theenvironment-sensitivity is BuildBuddy-vs-GitHub-Actions, and attributing it to a dirty local
checkout would point the next reader at the wrong thing.
The gate was defeated by an import spelling before review caught it
Worth recording because it is the second time in this lane that the naive form of this check
would have been permanently green. The first was the body-local grep, which every one of the seven
chdirs already evaded by sitting behind a helper. The second was the seed itself: it matched only the
fully-qualified
std::env::set_current_dir, so a future author writinguse std::env;and callingenv::set_current_dir(…)— or importing the function and calling it unqualified — would havereintroduced the exact defect against a green wall. Same shape as the helper-hidden chdir, one level
further down. Seeding is now the identifier-bounded bare callee, so every path spelling of the
function seeds the closure; the remaining rename-import residue
(
use std::env::set_current_dir as chdir) is stated in the module rather than left implied.Third time the check was accidentally right: raw strings
Review 58580 flagged, as a minor observation, that
code_projectiondid not handle Rust rawstrings — noting the file has none and that the over-approximation bound would catch a gross
containment failure. The file has two: a TOML manifest and a block of
.dagsource carryingthree brace pairs. Both happen to be brace-balanced, so the depth array was not skewed and the
gate was correct by coincidence of their content rather than by construction.
And the stated mitigation is on the wrong side. An unbalanced brace inside a fixture shifts the
depth array from that line to the end of the file, so containment is wrong for every declaration
after it and the wall misses real offenders while staying green. That is the under-approximating
direction: the wall asserts emptiness, so it cannot see it, and
reaching.len() < decls.len() / 4bounds the other side. A compiler's own test corpus is exactly where a deliberately unbalanced
fixture appears — a parse-refusal probe for an unclosed brace is the obvious one.
Fixed rather than noted.
code_projectionnow tracks raw strings across lines with their hashcount, and will not read an opener that is the tail of an identifier. The control asserts on
synthetic projections rather than the live file, so it keeps discriminating power when this file's
own raw strings change.
The receipt, and it is the point: with the raw-string branch disabled, the new control fails on
the leaked depth array (
[0, 1, 1, 2, 2, 1, 2, 1], ending at 1 instead of 0) — while the wall andthe non-vacuity control both stay green:
2 passed; 1 failed. That is the executed proof thatthe existing controls could not have caught this, rather than an argument that they could not.
Counting the body-local grep and the qualified-path seed, that is three distinct forms of this check
that would have been permanently or accidentally green, each caught by executing rather than reading.
The range of the check was narrower than the claim beside it (review 58640)
closure_over_selfscanned onlycli_run.rswhile the scope declaration claimed every test therequired
--liblane runs. Found by review, not by me. Widened rather than narrowed, and the choicewas made on cost.
Two wall-clock numbers, because the ratio is not the result. The single-file gate took
8.42s; the crate-wide gate over 238 files and 14 MB takes 0.46s. The wider check is not
merely affordable, it is faster — because the old edge builder asked each known name against each
line, quadratic in (lines x declarations). That cost-shape defect was already there, paid on one
file where nobody felt it; widening did not create it, widening made it visible. It is fixed here
under the standing bare-minimum-cost rule: a proven cost-shape defect is always repaired regardless
of the realized n, because "n is small here" is not a time-stable fact — and here n changed inside
one PR.
called_namesextracts applied identifiers once per line under the identical predicate.The sixth form, produced by the fix itself. Resolving cross-file call edges by bare name merges
every declaration of a shared spelling — this crate declares
main18 times, plusrun,new,probe,foo— so one production seed (pre_push::run_inner) bridged into hundreds of unrelatedtests and the wall accused
two_identical_runs_mark_nothing_unstableof being a cwd offender. Anover-broad closure reported as coverage, inside the guard built to catch over-broad closures.
Seeding the frontier from those callers and closing over it reproduced the blowup one step later, so
that arm was rejected too.
What survives: intra-file edges always, cross-file edges only for names declared exactly once
crate-wide, and the ambiguous remainder returned and contracted on by identity — exactly
{main, run}, the two entry points above thepre_pushseed. A new ambiguous mutator-reaching namerefuses rather than silently leaving a hole, which is the difference between a scan that narrowed
and a scan that quietly stopped looking.
Bounds re-derived against the new denominator, since a bound calibrated on one file is the
absorbing shape this guard already caught once. Measured crate-wide: 10199 declarations, 78 reaching,
687 non-ignored tests. Collapse guard 1000 -> 8000, above anything one file can supply, so a silent
fallback to single-file scanning now fails it. Over-approximation bound
decls/4->decls/50; theold one admits 2549 here and would have passed the name-merge arm above. Both numbers live in the
assert messages, which is where a bound has to be to be checkable.
Merge note, and the drift control
docs/design-ledgers.mdhit the generated-artifact merge driver, which refused withUUand zeroconflict markers — the driver working as designed, since taking the ours side there silently drops
the other side's authority-derived bytes. Resolved by taking the incoming bytes and re-running
generated_artifact_gate.dag --function main_wetover the merged.dagauthority, so thecommitted bytes are derived rather than composed from two sides.
That regen is also the drift control, and worth reading as one: it rewrites the whole projected
population in place, and afterwards exactly two files had moved, by one line each — the
DESIGN.mdindex entry and the ledger row this branch adds. So this branch carries no unrelated doc drift. That
control matters here because the doc-projection drift gate is a declared rung drop with no executed
route, so main can drift green underneath a branch and the drift would land in this diff wearing my
name.
🤖 Generated with Claude Code
https://claude.ai/code/session_01UafEkYSRqe8MgAXzykxEao