Repository navigation
Walk the real corpus with the copied-accumulator lens, and refuse instead of aborting - #10645
Conversation
…tead of aborting
The copied-accumulator lens could already decide a real module; what it could not
do was say which modules it had asked. Its population was nine hand-authored
paths, so "no suspects" meant "none among those nine" and the rest of the corpus
was not clean but unasked.
v2.lens.complexity_accumulator_copy.corpus_gate takes the subject universe from
module_declaration_facts_live -- the producer the module graph already resolves
the corpus with -- and carries suspects and unreadable subjects at identity
grain, with the Unclassifiable causes counted per cause rather than summed. It
consumes the same accumulator_copy_findings authority the compile gate runs; the
ingest chain is factored to source_tree so findings and tree are two consumers of
one chain rather than two spellings of it.
Three failure arms found by executing it, all the same class -- a failure that
absorbs or aborts instead of refusing:
* The roster named src/v2/workflow/glob_discovery_law.dag, moved to
src/v2/test/workflow/ by the #9637 reorganisation. The row was never
repointed and, the gate being offline, nothing ran it to notice.
* That missing path did not fail the gate, it ENDED THE PROCESS: the subject
read used the filesystem_read intrinsic, whose result carries content and no
success channel. At corpus grain every subject after the missing one is never
asked. The read now folds through filesystem_read_outcome and the analysis
standing gains a SourceUnreadable arm; the same defect in
v2.lens.identity_captured_navigation.roster_gate is fixed with it.
* module_declaration_facts panics on an absent pool root, so the below-floor
arm's red is only authorable from a directory that exists and holds no
modules. That refusal is correct; the witness is repointed rather than the
host changed.
Executed evidence, all green by execution with its control:
* an_unreadable_subject_is_refused_with_its_cause + a_readable_subject_still_
reaches_the_lens. The red control is measured, not asserted: against the
pre-fix filesystem_read spelling the first fails with a runtime type error
and the second never runs at all.
* a_planted_copied_accumulator_is_caught_through_the_corpus_path + a_clean_
accumulating_fold_stays_clean_through_the_corpus_path, over two fixtures
differing in exactly one construction and read from disk, so the corpus path
is what discriminates rather than the detector alone.
* corpus_subtree_gate_reports_its_unreadable_population and
an_empty_population_is_below_floor_rather_than_clean.
What the census says, and it relocates the lane's blocker. Re-derive with
census_for_paths / census_for_root in
v2.test.claim.long.accumulator_copy_corpus_census_test; the numbers are in the
pull request rather than transcribed here. Reach is bounded first by INGEST
COVERAGE -- most sampled subjects are refused by the v2 grammar before any lens
runs -- then by cost, then by a process-lifetime segfault. Roster policy is
nowhere on that list, and neither is decl_facts.
The asymptotic half is separately answered, negatively, and the instruments that
answered it are kept because the evidence reads the wrong way at first glance:
the ingest yields a grammar production tree encoded in kernel nodes, so cost_lens
folds over it and returns a class for the PARSE SHAPE, not the program. The
budget gate's subjects are semantic Arrows; bridging needs lowering. The shape
detector needs only the production tree, which is why it walks real modules
today.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H746YJwMsd7HxnijtCFGps
…to an Int Review remark on #10645: the -1 returned for a not-established subject was "unlovely but deliberate". The reasoning behind it was right -- an unreadable fixture must fail BOTH witnesses rather than satisfying the clean one -- and the carrier was wrong. A sentinel in an Int is the same collapse of a typed standing onto a scalar that the rest of this change exists to undo, and it is safe only while every caller remembers what -1 means. row_has_suspect and row_scanned_without_suspect each answer their own question and each answer false for a not-established subject, so an unreadable fixture is neither suspect nor clean and fails both witnesses by construction rather than by arithmetic. The guarantee is re-established by execution, not by inspection: pointing fixture_rows at two absent paths turns BOTH witnesses red, and they are green again on the real fixtures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H746YJwMsd7HxnijtCFGps
Ledger-Repair-Judged: docs/design-failure-modes.md Ledger-Rows-Repaired: docs/design-failure-modes.md roster_is_its_own_denominator Ledger-Repair-Judged: docs/design-rung-drops.md
…ause totally Floor blocker on #10645, and it is the class this branch keeps finding, turned on the branch itself: widening a coproduct is defeated at the CONSUMER, not at the declaration. I grepped for total matches before adding SourceUnreadable and concluded there were none. The grep looked for the cause variants; this match is over the OUTER SourceFindingsStanding with the cause nested inside the pattern, so it never appeared in the results. accumulator_copy_roster_standing_test.dag:72:3: error: non-exhaustive match: missing variant(s) SourceFindingsNotEstablished { cause: SourceUnreadable } The wall caught what the search missed, which is the whole argument for the match being total with no wildcard: a catch-all there would have absorbed the new variant silently and the widening would have shipped looking complete. SourceUnreadable is unreachable on that call -- source_findings takes the text it is handed and never reads a file -- so the arm answers false, and the annotation explaining why sits ABOVE the declaration: the first cut put it inside the match body, which §4c refuses at module-item grain (six blocking errors, caught by the same local check). Verified under the gate-equivalent tree view rather than a plain resolve, because a plain resolve does not decide exhaustiveness: gunbc compile --dependency-pool-index primary-precedence over this entry and the three other entries the widening reaches -- 0 blocking errors on all four. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H746YJwMsd7HxnijtCFGps
…us into the verdict Two things, both found by CI rather than by reading, and both about a verdict that said less than it knew. REVIEW 61280 (REQUEST_CHANGES) IS HALF RIGHT, AND THE HALF IT IS RIGHT ABOUT IS REAL. It reported that the no-suspect arm discarded refusal_sites and refusal_causes, and it did: corpus_report computed both and the disposition threw them away, so a caller reading the disposition could not tell a population with no refusals from one with hundreds. That is the collapse of a typed standing this branch exists to stop, committed at its own last step. The arm now carries the census and is renamed CorpusNoSuspectsObserved, because "clean" asserted more than was established. The other half is declined, and by a standing ruling rather than by preference. The review asked for a refusal disposition BEFORE the corpus may be declared clean -- that is, for refusals to gate. The operator ruling of 2026-07-13, carried in this lens's own compile_gate_law, splits the Finding coproduct so that a Poly2Suspect rejects while Unclassifiable causes ride the accepted channel "typed per-cause, located, counted, NEVER GATING AND NEVER SILENTLY DROPPED". Gating would also be wrong on the facts: refusals are the EXPECTED state of a real subject -- the sibling roster budgets them per file with ceilings -- so an arm refusing on refusal_sites > 0 would refuse essentially every real population and convey nothing by doing it. The new witness asserts the payload, not the arm name, on a real subject whose residue the roster already budgets. THE FLOOR BLOCKED FIVE WITNESSES WITH interrupted_before_verdict, AND THE CAUSE IS PRICE, NOT CORRECTNESS. Measured per witness: a five-line fixture ~4.7s, a 186-line module ~50s, the 874-line analyze.dag ~1272s -- ingest cost scales with subject size and does so superlinearly. The floor budgets an ENTRY rather than a witness, so one twenty-minute member reported every sibling in its file as interrupted, taking cheap evidence down with it. So the witnesses are split by cost, not by importance. Every discriminating control -- the typed-read pair, the corpus-path planted/clean pair, the population-floor red -- now sits in claim/complexity/ entries that run in seconds and stay ON the floor; the positive control was repointed from a 186-line module to the five-line fixture, which establishes the identical claim at 2.8s instead of fifty. Only the corpus-grain witnesses, whose price is the corpus, move behind a floor exclusion. AND THE EXCLUSION HAD TO GO WHERE THE FLOOR ACTUALLY LOOKS. The ci_layer_roots row added earlier governs DISCOVERY only; run_required_floor consults floor_prepared_subject_exclusions and nothing else, as that list's own note records. The roster-gate entry is the sharper case: it is operator-ruled OFFLINE by its own note and had NO floor exclusion at all -- it stayed off the floor only because nobody edited it, and repointing its stale row was the first edit. "Nobody has touched it lately" is not an exclusion. Verified: gunbc compile --dependency-pool-index primary-precedence over all three entries (0 blocking errors), cargo check -p v1-compiler, and the five floor-bound witnesses green at 29ms..3.6s. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H746YJwMsd7HxnijtCFGps
|
Addressed review 61280 in 43af991 — one half fixed, the other declined with a citation rather than silently. Fixed, and it was a real defect. The no-suspect arm did discard New witness Declined: refusals must not gate — and this is a standing ruling, not a preference. The operator ruling of 2026-07-13, carried in this lens's own It would also be wrong on the facts. Refusals are the expected state of a real subject — the sibling roster gate budgets them per file with ceilings up to 13 rather than forbidding them — so an arm refusing on For scale, from the corpus census in this PR: of 15 stratified real subjects, 5 were analysable and those 5 carried 0 refusal sites — but — sent from smart-wolf-554 |
…repared subject The floor refused the WHOLE subject at run 34016411960 before any witness ran: excluding test/claim/complexity/accumulator_copy_roster_gate_test.dag from floor_prepared_subject_exclusions does not skip its witnesses, it drops the path from prepare_repository_closure, so live_read_classification_test.dag:46 -- which imports that module by name for its ReadsLiveTree classification -- refused with `unresolved import`. Nothing in the change was measured. The exclusion was also priced against the wrong number. Measured on this branch, the entry's one changed witness costs 22.2s wall (22.1s of it entry resolve), not the ~15m its own note quotes; it reported interrupted_before_verdict alongside the two genuinely corpus-grain census witnesses and was excluded on that association. Those two remain excluded and nothing imports them. The standing constraint is now recorded on the list itself: a module with importers may not be excluded here, only one nothing names. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H746YJwMsd7HxnijtCFGps
…points, and wall the exclusion list Run 34018018622 refused with cause=ChangedWitnessOutsidePreparedSubject: the required floor admits a CHANGED witness regardless of any exclusion, so the two corpus-grain `test fn` rows introduced by this PR could not be excluded by the change that introduced them. That is correct and it is the point -- an exclusion a new witness could opt into on its own landing commit is the escape hatch DESIGN section 5 forbids. At ~80s per subject those rows can never run, and a witness that cannot run is roster membership standing in for a verdict. So they stop being witnesses. claim/long/accumulator_copy_corpus_census_test.dag moves to v2.lens.complexity_accumulator_copy.corpus_census as entry points beside the lens (a barren *_test.dag is itself refused by floor_naming_hygiene, so the rename is required, not cosmetic). Both exclusion rows -- the ci_layer_roots discovery row and the floor-preparation row -- delete with them. The executing evidence for the corpus path is unchanged and is now the whole of the claim: the cheap planted/clean pair in claim/complexity/accumulator_copy_corpus_path_test.dag, which runs on every push over a population corpus_gate discovers. Second: the constraint on floor_prepared_subject_exclusions is now a wall rather than a note. assemble_prepared_subject_closure refuses with cause=ExclusionOrphansImporter, naming the importer, the excluded module and the row that matched, when any retained module imports an excluded one. Previously that situation surfaced as `unresolved import` against a file nobody had touched, which is why its first two diagnoses both concluded the module had been moved. The import extractor is the one `import_resolution_facts` folds, and preparation already holds every source's bytes, so it costs no extra read. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H746YJwMsd7HxnijtCFGps
…the exclusion wall DESIGN 4b(2): the corpus census is now a named entry point enrolled in nothing. That is honest and it is also the state that decays without any red appearing, so the class names its trigger rather than leaving it as "when someone runs it". It is can-climb-after-one-grounding: an ingest realization that does not re-parse source in the interpreter. The row states what that trigger must be SUFFICIENT FOR, because the loss is corpus-grain while the tempting trigger is per-subject: corpus_gate returning a verdict over the DISCOVERED population, inside one required-lane budget AND inside one process. Neither half implies the other -- a tenfold per-subject speedup leaves the sweep hours long, and a sweep that fits the budget still dies with SIGSEGV around the twentieth subject. An artifact delivering one half contributes to the trigger and does not retire it. The row also states what is NOT claimed: that the tracked corpus is free of copied accumulators. The ExclusionOrphansImporter wall's RED was executed, not assumed: excluding analyze.dag -- imported by three modules and changed by nobody -- refuses at preparation and names the exclusion row beside each importer, which is the half missing when the same situation was diagnosed twice as a module having moved. The comment names the command rather than transcribing the run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H746YJwMsd7HxnijtCFGps
…exclusion row and its figures The census sweep dying by SIGSEGV was a subordinate clause in a message and existed nowhere else. It is a compiler failing to fail closed: DESIGN section 5 admits succeed-fully or fail-with-a-typed-located-diagnostic, and process death is neither -- no cause, no locus, no count, so nothing can enrol it, attribute it or bound it, and a sweep that died at subject k presents in the same shape as one that finished. Filed as gunbc.recurring_failure_mode sweep_terminated_by_process_death_rather_than_a_typed_refusal, rung found at BELOW the ladder (silent wrongness, not the bottom rung), ceiling structurally guaranteed because the property is decidable -- an explicit stack or depth bound in the interpreter's walk converts unbounded native recursion into a located refusal. The trigger states its sufficiency: any traversal deep enough to exhaust the native stack refuses, not a guard around one instrument and not a bigger stack. Fixing it is not in this PR. Separately, the floor exclusion row for accumulator_copy_corpus_census_test.dag was still standing after the file moved out from under it, so it matched nothing -- an inert row with a long justification, which is the shape the ledger warns about. It and its comment delete. What replaces it is the pair of constraints the list actually has, both enforced elsewhere rather than asked for in prose: a row may not name a module anything imports (ExclusionOrphansImporter), and a row is not how a changed witness gets out of running (ChangedWitnessOutsidePreparedSubject). Review 61303's advisory applied: the transcribed wall-clock figures are replaced by the entry points that re-derive them (census_for_paths, population_for_root). One of those figures had already rotted and bought a wrong exclusion, so this is the same lesson twice. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H746YJwMsd7HxnijtCFGps
… instead of reporting its survivors The class filed one commit ago sits BELOW the ladder, not on its bottom rung, and DESIGN section 5 forbids silent wrongness outright rather than admitting it as a low rung. So it does not wait for its trigger. Getting from outside the ladder onto rung 1 is owed as soon as the class is classified honestly -- an honest classification that changes nothing about what gets built is rung inflation in the opposite costume. corpus_gate_disposition_over decides coverage first, because every other arm is a statement ABOUT a population and none of them is true of a population that was not walked. The discovered list and the reported rows are walked in lockstep; the first divergence, or the rows running out, ends the covered prefix and everything after it is reported by name as CorpusCoverageIncomplete. It is an identity join, not a count equality (DESIGN section 5). A batch re-run after a death reports the right NUMBER of rows over the wrong subjects, and a count comparison is green on exactly that. The lockstep walk is also why this is not quadratic: a membership test per discovered path would be the nested fold over one collection this lens exists to catch, and "the corpus is only a few thousand" is the not-time-stable excuse section 6 refuses. Three witnesses, ~3s each, on the floor: a truncated sweep refuses; a right-sized report over the wrong subjects refuses; and a complete report over the same population reaches its ordinary verdict -- the positive control, without which a guard that refused everything would satisfy both reds. Verified discriminating by mutation rather than by reading: neutering the join to answer "nothing missing" turns both reds red and leaves the control green. They do not retire when the depth-bounded walk lands; they become its regression control. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H746YJwMsd7HxnijtCFGps
Ledger-Repair-Judged: docs/design-failure-modes.md Ledger-Rows-Repaired: docs/design-failure-modes.md sweep_terminated_by_process_death_rather_than_a_typed_refusal Ledger-Repair-Judged: docs/design-rung-drops.md
… in this lens, at any subject size Run 34022136976 interrupted seven identities. The log discriminates the cause and it is not a shared budget or a process death: each row reads raised_by=cpu_deadline cpu_at_least=~506ms/500ms, and the totals are interrupted_cpu_deadline=6 interrupted_wall_deadline=1. Per witness, on the CPU clock. The local figures agree rather than disagreeing -- a preempted row is cut just past the line and reported UNMEASURED, so 506ms is the deadline, not the cost, and the cost is the ~3s claim_batch measures. The consequence is larger than these witnesses. Interpreted ingest of a FIVE-LINE fixture measures ~3s, and a four-line String snippet with no filesystem read measures about the same: six times the line at the smallest subject expressible. So no assertion in this lens that reaches the ingest can be enrolled at any subject size -- which is why every witness this lens has ever had is frozen or deferred, a fact visible in the roster that had never been explained. So the evidence is re-aimed rather than trimmed, and the scope is stated rather than rounded up. The witnesses that remain touch no ingest and measure 0-1ms: the coverage join, whose subject IS the join and whose inputs are rows, and the typed read arm, whose subject IS the read -- the mechanism the SourceUnreadable red can fail open into. Both are different claims from the ingest claim, not cheaper proxies for it. The ingest-priced predicates -- planted caught, clean stays clean, readable subject reaches the lens -- become plain fns beside the corpus census: they pass by execution under claim_batch and are enrolled in nothing, and they dissolve on the same trigger. The one-line repoint of the stale glob_discovery_law row is reverted. Any edit to that entry admits its 22s witness to the floor, so the repair is not landable until the trigger clears; the specimen stays filed in roster_is_its_own_denominator. Also recorded, in the class filed this morning: a floor budget preemption is NOT that class. It is typed, located, counted and blocking -- the arm DESIGN section 5 asks for -- and the distinguishing question is not whether a run ended early but whether ending early produced a cause or produced silence. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H746YJwMsd7HxnijtCFGps
…ment in the trigger Two sentences were being carried by one. "The ingest is unenrolled" was recorded; "the discovery-to-join seam is unverified" was not, and only the second locates where a real defect would sit. The executing reds prove the join is correct GIVEN well-formed rows. The unenrolled predicates prove the lens is correct GIVEN real files. Nothing executing joins those halves: that corpus_rows_for_paths, folded over what corpus_paths discovers, yields rows in the same order, at the same identities, at the same arity the lockstep walk assumes. That assumption is load-bearing and no type states it -- a producer that reordered or dropped a path reads as a truncated sweep, the right refusal for the wrong reason, and one that silently repaired an order mismatch would make the guard permanently green. Recorded beside the join, with the row to write first when the trigger clears: not another join case, but one witness that discovers a small real population and checks the produced rows against it at identity grain. Second, the measurement that sets the bar moves into the trigger's sufficiency clause, because it is the finding that outlives this PR. Interpreted ingest of a five-line fixture, and of a four-line String with no filesystem read, both measure about six times the 500ms line -- at the smallest subject expressible. That is not a budget that could be raised to fit the work; it is a floor no ingest-reaching assertion can ever clear, and it retroactively explains why every witness this lens has ever had is frozen or deferred. A trigger that made large subjects affordable and left the smallest at six times the line would restore nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H746YJwMsd7HxnijtCFGps
Ledger-Repair-Judged: docs/design-failure-modes.md Ledger-Rows-Repaired: docs/design-failure-modes.md sweep_terminated_by_process_death_rather_than_a_typed_refusal Ledger-Repair-Judged: docs/design-rung-drops.md
…bt contract is not a door
Two corrections from reading the authority instead of the number.
The floor's per-witness CPU line is now cited as
v2.workflow.required_floor required_floor_claim_cpu_safety_limit_ms everywhere this
lane mentions it. The figure is deliberately not carried: required_floor.dag's own
prose already states a stale ten-fold value for that function in nine places, two
hundred lines from the declaration, and five files corpus-wide repeat it. Copying
the digit here would have made this lane the tenth site of the exact class it spent
the morning filing. Not fixing that prose -- not this lane -- but not inheriting it
either.
Second, the trigger was written as though the ceiling might move. It will not: the
line did not drift, gunbc#9517 RESTORED it and froze the over-cost population as a
monotone debt contract in the same change. So it is an operator ceiling with a debt
contract behind it, and the trigger now says what must become true UNDER it -- an
ingest realization whose cost at corpus grain fits inside that line -- rather than
waiting for a budget that was deliberately put back.
And the debt contract is not an admissions queue. v2.workflow.floor_cost_debt
declares itself shrink-only ("from here the direction is shrink-only in earnest"),
so it is the roster of what the restored ceiling caught. "Enrolled in nothing" is
therefore not a state these entry points can leave by asking to be excused; the only
exit is the trigger. That now stands in the file rather than in a thread.
The ledger projection is regenerated in the same commit rather than left to the heal
job, whose push has now lost a non-fast-forward race twice.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H746YJwMsd7HxnijtCFGps
…gned supersede A repair job that MODIFIES a branch can exit non-zero for a reason belonging to its own protocol rather than to the content under review, and the only channel most readers have is a red square beside the pull request. The red is true about the job and false about the change, and nothing at that grain distinguishes them. Filed as gunbc.recurring_failure_mode repair_job_red_is_attributed_to_the_content_it_repaired. Two arms, and they are not one mechanism -- I reported them as one before reading the second log, which is the co-occurrence inference this ledger already records. ARM ONE, a genuine race (run 34026632467): heal regenerated correctly and its push was rejected non-fast-forward because I had pushed to the same branch while it was building. ARM TWO, and this is the more interesting half (run 34027083483): heal SUCCEEDED -- HealProduced, prior_head fc576da, healed_head f2d00e6, artifact pushed -- and then exited 1 with SupersededByHealedHead ... revalidation-required. That is by design: the protocol refuses to report green for a head that no longer exists. Every step worked and the pull request shows a failing job. It cannot be fixed by retrying, because nothing is wrong. So the row states its population honestly rather than claiming recurrence it does not have: recurrence for arm one is NOT established by one receipt; arm two recurs by construction, once per heal that lands. The trigger is sufficient for both -- a retry-with-rebase ends arm one and leaves arm two red on every successful heal, so a fix that only handles the race does not retire the row. Rung mitigatable, ceiling mechanically preventable and deliberately not higher: the job knows which branch it took, so the two outcomes are decidable and separable at the reporting boundary -- but no compiler refuses a reader who misreads a red square, so the wall is at the boundary, not in the reader. Not fixing heal; not this lane. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H746YJwMsd7HxnijtCFGps
…ming the join contains the crash Both findings are real and both are mine. ONE, THE COVERAGE WALK ONLY CHECKED ONE END. It returned "nothing missing" as soon as the DISCOVERED list ran out, whatever rows remained, so a report carrying subjects the population does not contain was accepted. The degenerate shape went all the way through: an empty discovered population, one clean reported row, floor 1 reached the no-suspect arm -- because the floor counts REPORTED ROWS, so the guard meant to run before it defeated the floor's own red control. A surplus identity is not a lesser problem than a missing one: it means the rows did not come from this population, so nothing about them is a statement about it. The walk is now a typed three-way -- aligned, missing, surplus -- and CorpusCoverageSurplus refuses with the extra subjects named. Two new witnesses, one per finding, at the exact shapes the review named; both verified discriminating by mutation (neutering the surplus arm reddens both and leaves the truncation red and the positive control green). TWO, THE MITIGATION CLAIM WAS OVERSTATED, and in the one row that may least afford it. corpus_gate evaluates corpus_rows_for_paths to completion before the join runs, so a SIGSEGV during collection kills the process with the join never reached. The join does NOT contain the in-process crash and never could from that position. What it converts is a REPORT SHORT OF ITS POPULATION -- rows assembled across batches after a death, a truncated row set arriving from anywhere. So the row now says which arm climbed: the assembled-report arm reached rung 1, the crash arm is untouched and stays below the ladder, and containing it needs a surviving reporting boundary outside the dying process, which does not exist here and is not claimed. Filing a row about silent wrongness and then inflating its own mitigation is the failure that row exists to name. Caught by reading the code rather than the sentence, which is the right way to have caught it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H746YJwMsd7HxnijtCFGps
What this is
The copied-accumulator lens (
v2.lens.complexity_accumulator_copy) could already decide a real module. What it could not do was say which modules it had asked. Its population was nine hand-authored paths, so "no suspects" meant "none among those nine", and the rest of the corpus was not clean — it was unasked.This lands a corpus-grain consumer whose population is discovered rather than authored, fixes three failure arms that abort or absorb instead of refusing, and reports what walking the real corpus actually says.
The measured answer, and it relocates the lane's blocker
Re-derive with
census_for_paths/census_for_rootinv2.test.claim.long.accumulator_copy_corpus_census_test.On a stratified sample of the tracked corpus (every 125th
.dagunderdag/andsrc/v2/), one completed batch of 15 subjects:SourceParseRejectedSourceNormalizationRejectedA second real population,
dag/extdeps/filesystem(6 modules, discovered by root): 3 scanned, 3 not established. Across the 21 real subjects sampled, 13 could not be ingested at all.So the lens's reach on the real corpus is bounded in this order:
corpus_subtree_gate_reports_its_unreadable_population: 489 s wall for 6 subjects). Against 5007 tracked.dagfiles that is ~110 h serial.Roster policy is nowhere on that list, and neither is
decl_facts#5966. On the part the lens can read the verdict is genuinely clean: no copied accumulators, with a planted-copy control proving the detector still fires.Three failure arms, one class
Each aborts or absorbs where it should refuse — found by executing the corpus walk, not by reading.
src/v2/workflow/glob_discovery_law.dagmoved tosrc/v2/test/workflow/in the Reorganize dag/gunbc files into domain-specific subdirectories #9637 reorganisation; the row was never repointed, and because the gate is operator-ruled offline nothing ran it to notice.filesystem_readintrinsic, whose result type carriescontentand no success channel. At roster grain that reads as one red test; at corpus grain every subject after the missing one is never asked. The read now folds throughextdeps.filesystem.filesystem_io'sfilesystem_read_outcome, andSourceAnalysisNotEstablishedCausegains aSourceUnreadablearm. The identical defect inv2.lens.identity_captured_navigation.roster_gateis fixed alongside it — same cause, same coproduct.module_declaration_factspanics on an absent pool root. That refusal is correct behaviour for a mistyped root, but it means the below-floor arm's red is only authorable from a directory that exists and holds no modules. The witness is repointed atdocs/; the host is not changed.Evidence — every claim green by execution, with its control
an_unreadable_subject_is_refused_with_its_causefilesystem_readspelling it fails with a runtime type error, and the second witness never runs at all — the absorbing behaviour, reproduceda_readable_subject_still_reaches_the_lensa_planted_copied_accumulator_is_caught_through_the_corpus_pathlist_append(acc, x)fold, read from diska_clean_accumulating_fold_stays_clean_through_the_corpus_pathacc + x— one construction apart, so the corpus path discriminates rather than answering a constantcorpus_subtree_gate_reports_its_unreadable_populationan_empty_population_is_below_floor_rather_than_cleanSibling lens re-verified after the shared widening:
red_control_planted_identity_escape_still_alarms,red_control_planted_identity_escape_finding_count_is_one,planted_source_analysis_is_establishedall pass.claim_executor --required-regen:first_generation_equal=true, 158/158 adjudicated.The asymptotic half, answered negatively
Kept as instruments because the evidence reads the wrong way at first glance, and I read it the wrong way first.
ingested_root_shapeanswersTypeNode/Conjfor a real module, andcost_lensover that root answersLinearrather than refusing. Together those look like the asymptotic engine returning a verdict on a real module's cost. They are not:ingested_root_child_labelsnames the root's edges asgrammar_production_identity_node_projectionandgrammar_production_captured_node_projection, so the tree is a grammar production tree encoded in kernel nodes and theLinearverdict is a cost of the parse shape.ingested_decl_cost_classanswersdecl-not-foundfor the same reason — normalize has not lowered anything, so there are no declaration-named edges to select.The consequence is the lane's shape: the copied-accumulator walker wants exactly this production tree and runs on real modules today; the budget gate's subjects are semantic
Arrows, so feeding it a real module needs resolve/infer/fold_lowering. That blocker is real for the asymptotic path and is not a blocker for the shape detector — conflating the two is what makes the coverage audit's prose read as if nothing can move.Rung, stated honestly
Mechanically preventable, for the copied-accumulator shape only, over the population actually walked — which this change makes reportable at identity grain for the first time. Not a wall: the gate is offline by cost, and
v2.lens.complexitystaysRatchetForever, correctly and permanently.Deliberately not done
Enrolling a real subject into
subject_complexity_budget_rosteron the required lane. Those rows declareSubstrateInputsOnly; a real subject reads the live tree at runtime — the same dependency-invisible-to-closure-attribution property that put the roster gate offline. That is the floor owner's call, and quietly changing what a required lane reads is not mine to make.docs/design-failure-modes.mdis not regenerated here: the receipt added toroster_is_its_own_denominatorfollows the #10592 precedent of landing the authority append and letting the CI auto-heal commit regenerate the projection.🤖 Generated with Claude Code
https://claude.ai/code/session_01H746YJwMsd7HxnijtCFGps