Repository navigation
Stop merging the failure-mode ledger on every append - #10822
Conversation
|
Verified against current head from review 62305 (dashboard-only, reviewed 4163d30).
|
|
Verified against current head from review 62325.
|
|
Verified against current head from review 62332.
|
528bea1 to
2959772
Compare
|
Verified against current head from review 62348.
|
|
Verified against current head from review 62367.
|
|
review 62378 (Opus REQUEST_CHANGES on 2885484):
|
|
review 62400 (cursor/auto REQUEST_CHANGES): landed. |
The committed markdown projection and the hand-appended roster were two shared merge regions for independent class filings. Membership is now the row directory (derived before compile), and the markdown is generated on demand rather than committed. Co-authored-by: Cursor <cursoragent@cursor.com>
The failure-mode ledger has no checkout bytes to agree; requiring them would put the 1.19MB file back on the merge path. Regen can still write a local copy. Rung-drops stay on the agreement path. Co-authored-by: Cursor <cursoragent@cursor.com>
The derived roster is a missing module when unread, not an empty fold; write it through a rename so a concurrent reader cannot see a torn file. Co-authored-by: Cursor <cursoragent@cursor.com>
A non-utf8 .dag stem is a shortened membership, not a skip; read_dir errors already stop the collect. Co-authored-by: Cursor <cursoragent@cursor.com>
…path. A short derived list stayed well-formed; the projection join is roster-to-markdown, so it could not see an omitted file. The required-CI join now identity-compares sibling .dag stems to roster members, independent of the writer. Co-authored-by: Cursor <cursoragent@cursor.com>
…nly declared-versus-rostered. Co-authored-by: Cursor <cursoragent@cursor.com>
The four collect walks now share one call; the directory match is gunbc/recurring_failure_mode, not two uncorrelated folder names. Co-authored-by: Cursor <cursoragent@cursor.com>
…e file. A maintainer should not hunt a missing list entry in gitignored roster.dag. Co-authored-by: Cursor <cursoragent@cursor.com>
Required CI runs the production join; these REDs are the rust_unit_tests_off_the_merge_path drop. Co-authored-by: Cursor <cursoragent@cursor.com>
The restoration trigger is unchanged: they are --lib tests, so the same runner-supply capability restores them with the rest of that population. Co-authored-by: Cursor <cursoragent@cursor.com>
A shared roster.dag.deriving let concurrent writers rename a truncated body into place. Unique pid+nanos temps close that. The join now walks the same ROW_DIR_REL the writer matches. Co-authored-by: Cursor <cursoragent@cursor.com>
…edgers. The NFR reason and the coherence note still said DesignFailureModesArtifact was a heal subject after it left the committed population. Co-authored-by: Cursor <cursoragent@cursor.com>
The instrument-name paragraph is in design_document.dag; the rebase left the generated file on the earlier wording. Co-authored-by: Cursor <cursoragent@cursor.com>
That walk is the first merge-path reader and it was not one of the four collect sites, so a clean checkout had no roster module and the join refused the intended state. Co-authored-by: Cursor <cursoragent@cursor.com>
Ledger-Repair-Judged: docs/design-rung-drops.md
…s roster. That member is a directory fold beside the declared value-binding climb; the stall still holds for every carrier that hand-appends, including this one. Co-authored-by: Cursor <cursoragent@cursor.com>
The gate compares committed bytes; DesignFailureModesArtifact is NotCommitted, so that arm could only succeed. collect_dag_files_tolerant now ignores roster write errors instead of panicking, matching its swallow of unreadable dirs. Co-authored-by: Cursor <cursoragent@cursor.com>
The committed mirror already called ensure_if_row_dir_or_panic; the .dag source did not, so required generated-artifact reported compiler_tests.rs drift. Heal does not install stage0 mirrors. Co-authored-by: Cursor <cursoragent@cursor.com>
The inserted ensure call added a concat without a matching paren, so v1-stage0-v1-infer failed to compile on the required build. Co-authored-by: Cursor <cursoragent@cursor.com>
Hand-wrapping concat() around two string args made concat ternary and broke v1-stage0-v1-infer. Rebuild the mirror from the last green emit with the same nesting as every other fragment. Co-authored-by: Cursor <cursoragent@cursor.com>
Unenumerated hand Rust in src/v1 is a stop-line. The stall names the dissolution capability; it does not enroll the added items. Wire the row into seed_growth_justification_roster. Co-authored-by: Cursor <cursoragent@cursor.com>
92bf1a8 to
8bce1ac
Compare
main #10822 dissolved both conflicting files: the failure-mode roster is now DERIVED from the row directory (gitignored) and the markdown projection is generated on demand rather than committed. Both deletions accepted; my class row survives as its row FILE, which is the membership under the new join. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011D6tYivYRP6YoGk8FJo3M7
…rojection #10822 made recurring-failure-mode membership the row DIRECTORY and stopped committing docs/design-failure-modes.md, which is exactly the shared append region this branch kept conflicting on. Resolution accepts the deletion: this branch's whole contribution is 8 added lines in the row file itself, which is now the only place the class is filed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF
…ctory #10822 stopped committing docs/design-failure-modes.md, which was the shared append region this branch conflicted on. Resolution accepts the deletion. The two append rosters merged cleanly and were verified as strict supersets in both directions: the floor_expected_red chunk chain keeps every chunk main carries, with this branch's disjoint_refinement_chains spliced in. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF
…ctory #10822 stopped committing docs/design-failure-modes.md. Resolution accepts the deletion; this branch's whole contribution is the specimen on its row file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF
Same resolution as the regen-digest branch: main #10822 dissolved the roster and the committed markdown, so both deletions are accepted and membership is the row FILE. Additionally v1_std_core.rs conflicted; taking the ours side dropped main's new AlgebraApplicationEvidenceUnavailable variant and failed to compile, so the mirror was regenerated from the merged .dag authority via --required-regen and the candidate installed (18 deletions, the two prose-blob fns this migration removes). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011D6tYivYRP6YoGk8FJo3M7
… its roster #10822 deleted dag/gunbc/recurring_failure_mode/roster.dag and docs/design-failure-modes.md outright. Both arrived as modify/delete against this branch, and both deletions are taken: membership is the row DIRECTORY now, derived before compile, and the markdown projection is generated on demand rather than committed. This branch's six failure-mode rows are members by existing as files; no roster edit remains. That dissolves at the root the merge class this PR spent the day absorbing -- the append-vs-append roster conflict that silently deleted #10801's row, the duplicate that took out two floor witnesses, and every --ours resolution of the 1.19MB projection. The both-direction roster check this branch built has no subject any more, which is the correct outcome: DESIGN section 5 prefers making a state unwritable over checking for it. VERIFIED, because this is the merge where content goes missing quietly: all 178 of main's row files present (mine is 184 = 178 + this branch's 6); exactly one common row body differs, check_subject_narrower_than_its_declared_claim, which is this branch's own edit. roster.dag is deleted from the index and regenerated locally as an ignored derived artifact, as gunbc.repo_workspace declares it (RegenerableFromSource). Regenerated BEFORE this commit rather than after, because every commit in this worktree is auto-pushed by the CI account -- the commit is the publish, so a follow-up regen commit guarantees one red head. Every conflicted path in this merge and the four before it was a DERIVED artifact; the .dag sources have never conflicted once. Local on this tree: lane=build phases_run=2 phases_failed=0, mirrors at first_generation_equal=true, docs-projections OK, registry-projections 39/39 drifted=0 (40 -> 39 is #10822's ledger leaving the adjudicated set), fixed_point_equal=true. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtfacHQfiYMAD9SiH6oZhi
…ng its head record The namespace wave-admission baseline is the head index with the diff applied in reverse at FILE grain, which assumes one thing about every file the diff did not name: that its base text equals its head text. That holds for a tracked file. It is false for a source generated before compile and gitignored, because such a file never appears in `git diff` no matter how far its content moved -- so its head record was carried into the BASE index and the wall compared the head against itself. #10822 made the failure-mode roster derived and gitignored. This branch carries the first row to land after it, and had its own authored addition reported as NewPoolCoincidenceResolution -- a name resolving from a pool with nobody authoring a reference -- when the reference was authored by this very change. That disposition is not auto-admitted, so a required lane blocked, and it would have blocked for every later row. The binding delta was right; the authorship evidence under it was poisoned. The carry now requires the base tree to contain the path, which is what makes unchangedness proof of byte identity. A derived path is re-rendered from the base revision's row files through the SAME renderer the writer uses and parsed by base_records, so no hand-built record and no second derivation authority exist. Any other untouched head-index record absent from the base tree is NotEvaluated: a path listing supplies existence, never content, and an untracked path may equally be local state, a scratch fixture or a dirty worktree file -- inheriting or silently dropping it fabricates a baseline either way. binding_disposition, locally_authored_claim_added and disposition_auto_admitted are untouched, and NAMESPACE_TRANSITION_ADMISSIONS stays empty: nothing is admitted by permission. Measured on this head, the wall goes from 1 unadjudicated delta to ADMITTED with 7 real deltas, binding_rows_compared 759867 -> 759866 -- the one row that should never have been compared, because with a correct base the name does not exist there. Evidence: the wall's own run on this base/head reports `namespace-wave-admission ADMITTED`. Two unit tests enrolled in the deriver's seed-growth population cover the derivation, and the one encoding the defect is discriminating -- it fails under a mutation that renders the base as the head while its three siblings stay green. Those tests sit under the declared drop rust_unit_tests_off_the_merge_path and are local diligence, not enrolled evidence. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg
…e class file #10822 (aac9545) made recurring failure modes ONE CLASS PER FILE with membership derived from the directory, and deleted both the hand-appended roster and the combined markdown projection, adding both to .gitignore. This branch still carried edits to both. Resolved by ACCEPTING THE DELETION on: dag/gunbc/recurring_failure_mode/roster.dag docs/design-failure-modes.md Restoring either to quiet the merge would re-create the exact authority #10822 removed -- the surviving-X attractor DESIGN section 3 names in the replacement-migration rule. Their imports still resolve because the roster is generated before compile; it is derived, not absent. What this branch registers is unchanged: the class file unapplied_function_value_admitted_at_a_value_position.dag IS the registration now, and it carries forward as-is. dag/gunbc/guarantee_stall/roster.dag is ALIVE and was an ordinary content conflict -- both sides appending. Resolved additively, main's rows first, mine after, with no row from either side dropped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CQuBs4mLu9ArojSWFiRcey
…list #10822 deleted the hand-appended failure-mode roster: membership is the row DIRECTORY now, derived before compile. This row's corollary prescribed a count comparison against the base for append-only carriers and cited the roster at 163 against 173 as a live countable specimen. Half that citation is a superseded claim, and it had not landed yet -- so this corrects the row before it ever becomes an authority rather than editing a published one. I had declined this edit earlier on the grounds that the row was a live authority and that changing prose inside a PR waiting on an unrelated gate is how a superseded claim survives review. The reasoning was sound and its PREMISE WAS FALSE: the row is absent from origin/main and exists only on this branch. There is no landed authority to preserve. The corrected text is taken verbatim from #10853 (deep-crane-107, 9a2358a), which added the identical file and differed only in this one receipt string; that PR closes rather than landing a second copy for this one to later drop. The file is now byte-identical to its version, so one row has one authority. What the text now does: scopes the rule to append-only carriers that are STILL A LIST, keeps extdeps.exec.command admit_callers at 67-vs-68 as the surviving specimen, marks the roster SUPERSEDED FOR ONE CARRIER ONLY citing #10822, and keeps the closing instruction not to invent a replacement count for the dissolved carrier -- directory membership is a different subject. The correction is self-applying: refreshing that citation as though the roster were still countable would itself be a_live_authority_name_carries_a_superseded_claim. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN
…#10674) * Compose the micro-VM launch plan, and mint the credential last Four stages have to hold before a guest may start: the fabric cell must be owned, the VM config must resolve, the admission must pass, and the App credential must carry organization self-hosted-runners write. This composes them in that order and returns one typed plan. The order is load-bearing rather than stylistic. Every stage but the last is pure and free to refuse; a JIT mint is not, because it creates a runner identity at GitHub. A refusal after a mint leaves that identity registered against the organization for a VM that will never start, nothing on the host knows to delete it, and orphaned registrations are reconcilable only by name. So the mint runs last, and its authorized value is reachable only from inside LaunchAdmitted -- a caller cannot hold a mint authorization for a launch that was refused. Every admission arm is matched where the launch rules on it rather than through a helper over RunnerMicroVmAdmission. Such a helper needs an arm for the admitted case that cannot occur there, and the only thing to put in it is a fabricated refusal, in the one place nothing can observe it. plan_microvm_launch_of_binding carries the decisions and takes a binding a witness can build; plan_microvm_launch is the five-line projection over the sealed RequiredBuildCellSnapshot. Putting the ordering behind the seal would have made it unwitnessable, which is how the previous PR ended up with an annotation claiming coverage that did not exist. Refusal arms stay separate rather than collapsing into one cause string: the cell belongs to fabric_required_build_cell, the size to runner_slot_allocation, the topology to runner_unit, the credential to auth.github_credential, and collapsing them sends an operator to the wrong module. Credential fixtures are imported from test.claim.github_app_registry rather than rebuilt, so there is one answer to what an installation holds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * temp: diagnostic arm probes for the launch fixture Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Take the VMM reserve as an input, and state that production admits no guest The launch plan read gunbc_runner_microvm_realization_reserve() inside the composition, which made the admitted arm unreachable from any caller. Production's reserve is ReserveUnmodeled -- deliberately, because no VMM resident footprint is a cited quantity in extdeps.virtualization.firecracker and inventing one would be a fabricated plausible output -- so every launch refuses at MemoryFitUnresolved and the mint stage below it was dead code no input could reach. The lane caught this: the positive control asserted LaunchAdmitted, a state no input can produce. That is the same decoration class this branch documented for members_owned, this time written into a test rather than a production path, and it would have shipped as coverage for an ordering nothing could exercise. The reserve is now a parameter. The fit judgment is decided by the caller that owns the quantity, the ordering below becomes reachable and therefore witnessable, and production keeps refusing at the entry point rather than by accident inside a composition. Adds the control that says what is true today: with production's own reserve, every launch refuses, so NO CI JOB RUNS IN A MICRO-VM until that quantity is cited or measured and declared. It goes red the day the reserve becomes declared, which is when the launch path becomes live and the claim has to be re-read. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Observe whether a runner host can measure a VMM's footprint The micro-VM path admits no guest because the realization reserve is ReserveUnmodeled: no VMM resident footprint is a cited quantity, and inventing one would be a fabricated plausible output. The quantity has to be OBSERVED on a host before it can be declared, so which instrument exists is a host readiness fact in the same way /dev/kvm is -- a host that boots a guest but cannot report what the VMM cost cannot retire the reserve. Reports GNU time, cgroup v2 memory.peak, and the systemd version line, in the receipt the existing microvm_host_converge mode already prints. No new module and no new workflow job: this had to have a consumer the moment it existed. The instruments are named rather than ranked and nothing here picks one. Each reports the same quantity exactly, and which is available is a property of the host's systemd and kernel, so selecting among them belongs with the measurement. The systemd version is read rather than inferred because MemoryPeak is version-gated at v254. Sampling /proc/<pid>/status VmHWM is deliberately not among them: it needs the process alive, so a guest that powers itself off can be missed, and a missed sample under-reports -- the wrong direction for a value that becomes a safety reserve. Readiness deliberately does not move with the instruments, with a control for it: wiring them into firecracker_host_ready would have taken every host not-ready the moment this field landed, to answer a measurement question. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Import systemctl_version_command, which no local gate had checked The fleet-converge dispatch died on srv2 with NoSuchFunction while the whole witnesses lane had reported RC=0 and FloorClean over the same tree. Two reasons stack and either alone is enough. runner_microvm_host_ready sits declined_outside_gate_closure, so no CI check compiles it. And claims evaluate only the functions they call, so the witnesses added beside this observer cover its pure functions while the func body is never evaluated -- wet bodies run only on a host. A green floor is not evidence that a wet function resolves. gunbc compile --entry DOES catch it, confirmed by running the red arm with the mutation verified applied first. So the gate existed and was not run; it is now run before any dispatch that executes a .dag entry on a host. Worth recording that this is the opposite blindness from the indented-annotation refusal earlier on this branch, where the lane's parse phase caught what compile --entry missed. Neither instrument dominates; a touched module needs both. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Model the VMM footprint probe, sampled across guest sizes The micro-VM path refuses every launch because the realization reserve is ReserveUnmodeled. This is the observation that could retire it, and it produces a RELATIONSHIP OVER SIZES rather than a constant, because the model's own reasoning is that the overhead is not constant. Sampling several sizes is not thoroughness, it is the whole experiment. Guest RAM is lazily faulted, so a page joins the VMM's resident set only when the guest touches it, and a guest that boots and powers off touches a small and roughly size-independent amount. One reading at one size therefore measures the workload, not the size. A peak that stays flat as declared size rises says the declared size is not committed; a peak that tracks it says it is. Those are different worlds for an admission rule and one sample cannot tell them apart. Stated in the module and not only here: this does NOT establish a worst case. That is what a guest costs when it USES its declared memory, which needs a guest workload that touches all of it. Reporting these numbers as if they bounded that case would be the fabricated plausible output the reserve refusal exists to prevent, arriving by way of a measurement instead of a guess. The instruments were chosen on evidence rather than assumption: the srv2 receipt reports gnu_time present, systemd 255 so MemoryPeak is available, and cgroup memory.peak absent. GNU time's %M is a kernel-maintained high-water mark, which is why it was preferred over sampling VmHWM -- sampling needs the process alive and under-reports a subject that exits on its own, the wrong direction for a reserve. The span check carries its own negative arm as a fixture rather than deferring to a mutation run: the collapses it exists to catch -- one size, two adjacent, three clustered -- are asserted to fail on every run. The third is the one reasoning alone would miss, since count >= 3 passes it and only the span requirement does not. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Convert mebibytes to bytes explicitly; pin the sizes in absolutes The declared sample sizes were 512..4096 BYTES, not mebibytes. Assigning mebibyte(count: N) into a ByteSize field is accepted and does not convert -- the magnitude rides across unchanged -- and std.measure mebibyte_to_byte_size is the conversion that was available and unused. The type system stopped the READ and permitted the WRITE: mebibyte_count on a widened value is a hard error, so the obvious repair is to switch to byte_size_count, which compiles clean and relabels the same number as bytes. That repair is what I made earlier on this branch and reported as a units win. It was not one. WHY NOTHING ELSE CAUGHT IT, which is the part worth fixing rather than the typo. The span control asserts largest >= smallest * 4 over the same list. A uniform unit error preserves every ratio, so it passed identically on bytes and on mebibytes; only the absolute assertion in the render test went red, and that one covers the witness's own fixtures rather than the list production samples. So this also pins the production sizes in absolute bytes. The literals are the specification -- these are the sizes the probe declares it will sample -- so the control is a controlled fixture rather than a measurement copied out of the tree. General rule, recorded because it generalises past this file: when a suite tests a RELATIONSHIP -- ratio, ordering, spread, delta -- at least one control must pin an ABSOLUTE, or a scale error is invisible to the whole suite. Also enrolls the empty-receipt control permanently rather than as a one-off, per 4b(4): footprint_receipt_is_complete folds with init: true, so an empty outcome list is vacuously complete and only the count guard beside it refuses. That guard is load-bearing and had no control. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Convert the one mebibyte site a digits-only regex could not match The previous commit claimed to fix the unit bug and did not. It converted with a pattern requiring digits, so every literal call site was repaired while mebibyte(count: mib) in the sampled() helper -- an identifier, not a literal -- was silently skipped. That helper is exactly what the failing render claim drives, so the fix looked complete, compiled clean, and left the same claim red. A mechanical edit that matches most of its population and misses the rest is indistinguishable from a complete one at the diff, and the widening it failed to remove is legal, so nothing local objected. What found it was running the lane before pushing rather than reporting the fix from the edit. So the check is now over the POPULATION rather than the pattern: every mebibyte( in both files is wrapped in mebibyte_to_byte_size, verified by a grep whose expected output is empty, not by trusting the substitution's own reach. The absolute control added in the previous commit is what isolated this. It passed while the render claim failed, which said conversion works and the fault is in the render path -- without it the two hypotheses were indistinguishable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Re-read the launch control now that the reserve is declared production_refuses_every_launch_while_the_vmm_reserve_is_unmodeled asserted that every launch refused at MemoryFitUnresolved, and it said in its own text that it would turn RED the day the reserve became declared -- at which point the launch path becomes live and the control has to be re-read rather than silently starting to pass a guest through. #10657 was that day. It set the allowance aside and made the reserve ReserveDeclared, so production now reaches an admitted guest and the assertion is false. The control was not broken; it fired. This is the re-reading it demanded. The red was only visible on the PR merge ref: my branch alone still has the unmodeled reserve, so both local instruments greened while CI -- which builds branch-merged-with-main -- refused. Running an instrument against a tree that lacks the change under test measures the wrong subject no matter how correct the instrument is, and a green from it looks exactly like a green from the right one. What replaces the row is the same question asked of the live path, in three parts. The admission control pins that the guest admitted is the DECLARED size rather than any default. The refusal control keeps that from being satisfied by a fit judgment that answers yes to everything: a reserve larger than the cell must admit no guest of any size. And the original discriminating RED stays enrolled with an explicitly unmodeled reserve -- 4b(4) keeps a class's red executing after its wall lands, and an unresolved fit is exactly what the launch path must never pass a guest through, whoever the caller is. The arithmetic is deliberately NOT re-derived in the witness. guest_memory_fits already carries the comparison, and recomputing it here would be a second representation of one constraint, satisfiable by editing the test while the production path still lied. Both new controls were verified by mutation rather than by reading: shrinking the oversized reserve and expecting a size production does not declare turn each of them red, while the untouched third stays green -- without that last one a run that failed everything would have looked like proof. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Require the execution grant, so the micro-VM is a realization under the fabric runner_microvm_launch_plan composed a launch straight from a cell binding and started a VMM. gunbc.fabric_ci_program FCI-3 owns that question -- what causes the target process -- and its exit predicate is that a persistent executor re-reads the committed Grant before starting, so an absent, corrupt or expired Grant yields ZERO target processes. FCI-1's non-goals name Work-process-start and ExecutionGrant explicitly; FCI-2's name process-start. The module therefore did FCI-3's job while skipping FCI-2, which is the second execution system ROADMAP forbids when it says the fabric is four consumers of one contract. That is also why nothing consumed the module. The correct consumer is the FCI-3 executor, and it could not consume a launch whose signature omitted the grant -- so writing any caller would have cemented the bypass rather than closing it. The grant is now a REQUIRED PARAMETER rather than something this module looks up. The absent case has no constructor, so it is unwritable rather than validated; the fence check against the issuing account covers corrupt and expired, because a grant whose lease generation no longer admits is a superseded authorization. Authorization is the first stage: nothing starts without it whatever the cell's state, and a control pins that ordering by presenting an unowned cell and requiring the grant refusal to still win. THE CONTROLS CAUGHT A BUG THAT PASSED THEM. issue_grant returns the account carrying the recorded lease, and the first version fenced against the PRE-issuance account -- so every launch refused, and both grant controls passed against a mechanism that was refusing everything. Only the seven positive claims failing revealed it. The grant and its committed account are now one IssuedAuthorization value, because splitting them is what made the error writable. Verified by mutation in PRODUCTION rather than in the test: neutering the fence check turns both grant controls red and leaves the other seven green. No claim asserts the absent-grant case, because its RED is unauthorable once the parameter is required -- that check would be a 4b decoration, permanently green and cited as coverage. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Move the VMM footprint measurement to its own branch The host-readiness observation and the footprint probe answer a different question than the launch plan does, and they stand alone: no reference to the launch plan, the execution grant or the mint. They are now PR #10806 off main. This branch keeps one subject -- what causes a micro-VM to start, and under whose authorization -- so its title is the one squash-merge writes into main's history. The extdeps rows move with the code that needs them: gnu_time_max_rss_command and systemctl_version_command are admitted for the probe, so they belong to the probe's branch and are reverted here rather than left admitting callers that no longer exist on this branch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Join the grant to the reservation it authorizes, and split planning from authorization Requiring an ExecutionGrant parameter closed the absent-grant case and nothing else. The grant and the cell binding arrived as independent arguments and the only thing read off the grant was its money reservation, so a grant issued for execution A composed with the reserved cell for execution B was ADMITTED whenever the supplied account fenced A. That is worse than no grant: its presence reads as authorization while authorizing nothing about the subject. Found in review; the PR text claiming this bound the launch under the fabric contract overclaimed. MicroVmExecutionBinding is sole_constructor and reachable only through seal_execution_binding, which refuses unless Work, Demand and Offer all agree between the grant and the reservation. The mismatched pair now has no constructor rather than being caught by a check inside the planner. THE RULE IS FACTORED OUT OF THE SEAL SO ITS RED IS AUTHORABLE. RequiredBuildCellSnapshot is sole_constructor precisely so a caller cannot manufacture a reservation, which also means no witness outside its module can build a DISAGREEING one -- a control aimed at the seal could never construct the input it exists to catch, so it would be permanently green and cited as coverage. Exposing a snapshot builder for tests would punch the hole the seal exists to close, the tradeoff runner_microvm_attempt's own witness already records. So grant_authorizes_reservation takes the coordinates, where a disagreeing pair IS constructible, and the seal reads them off the snapshot and delegates to it. PLANNING NO LONGER PRETENDS TO BE AUTHORIZATION. plan_microvm_launch_of_binding takes no grant at all, because cell ownership, VM config, admission and the mint are not authorization questions and a signature that accepted a grant while ignoring its subject is what created the opening. Production reaches it only through the sealed value, so the gate cannot be routed around; witnesses drive each level where that level's red is writable. WHAT THIS STILL DOES NOT ESTABLISH, recorded because the type name would read as more. ExecutionGrant is an ordinary constructible record, so this proves AGREEMENT between a presented grant and a presented reservation, not that fabric issuance durably committed either. The unforgeable form is a persistent executor re-reading the committed grant from durable storage after the submitter terminates, which is FCI-3's own exit predicate and the next slice. Ten claims pass. The substitution control was verified by removing ONLY the Work join in production: it reds alone while the fence and positive controls stay green, so it is bound to this defect rather than to a rule that refuses broadly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Reduce to the authorization rule, and file what the two homes cost The launch plan half is deleted. Not because it was unconsumed -- because it was REDUNDANT. runner_microvm_witness_test already carries a_reserve_larger_than_the_whole_envelope_admits_no_guest, the_production_reserve_is_the_declared_allowance, the_production_reserve_lets_the_declared_configuration_reach_admission_under_authored_topology, orphanable_unit_refuses_the_microvm_migration, shared_slot_directory_credential_is_refused and the whole guest-memory boundary family; cell ownership is in runner_microvm_attempt_witness_test and the credential join in the jit mint witness. Deleting the seven planning claims removes NO coverage. I re-derived them because this branch predated #10657 and I never read the neighbouring witness, which is the same root as a roster edit on this branch that would have dropped ten rows other lanes had landed. What survives is the part nothing else answers. Grant and cell binding arrived as independent arguments with only the money reservation read, so a grant issued for execution A authorized execution B whenever the account fenced A -- the grant's presence read as authorization while authorizing nothing about the subject. grant_authorizes_reservation now requires Work, Demand and Offer to agree, MicroVmExecutionBinding is sole_constructor so the mismatched pair has no constructor, and three controls carry it. The module is renamed for what it now contains. A module called launch_plan holding no launch plan is a name promising something it does not carry. ON THE SEAL, because the question was whether it is a 4b decoration. SoleConstructorViolation for cross-module construction is already proven by sole_constructor_violation_outside_module over a synthetic two-module pair, so the red IS authorable and the seal is not a decoration. A type-specific fixture would re-prove the general wall in a second home, which is the same redundancy this commit deletes. Stated honestly: that evidence runs under cargo test -p v1-compiler --lib, which no CI step executes -- the declared drop rust_unit_tests_off_the_merge_path -- so the wall is compile-enforced but the evidence one would cite for it is not on the merge path. This refusal and #10733's do not duplicate. jitconfig-bytes=0 is about CREDENTIAL MATERIAL; this is about AUTHORIZATION SUBJECT, and it orders strictly earlier because it refuses before any drive is built. They compose. Verified after the reduction rather than before: three claims pass, and removing ONLY the Work join in the renamed module reds the substitution control alone while the fence and positive controls stay green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Record the count-first merge check on the two-homes row The near-misses this branch produced belong with the class they are nearest to: an append-only carrier losing a row renders as reordering, so the diff is blind and only the count against the base raises it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Restore two dark roster rows, and cut the launch-plan residue REVIEW FINDING 1, CONFIRMED AND IT IS THIS PR'S OWN CLASS. Roster was 175 on main and 174 here: added production_actuation... and silently dropped decision_surface_truncation (#10712) and lookup_miss_read_as_positive_classification (#10727), both landed after the branch point and both with their module files still present. The triple-dot diff renders it as reorder-plus-two and shows nothing. Found by the count comparison the PR's own row prescribes, named by an identity join, and repaired by merging main so the roster is 176 with an empty join in both directions -- imports and list body checked separately, since either half can drop alone. REVIEW FINDING 2, CONFIRMED. The reduction left the launch graph's imports (32 lines -> 8 in the module, 37 -> 18 in the witness) and an annotation asserting mint-last/LaunchAdmitted ordering that no longer has a mint or a launch to range over -- a 4c annotation no Accepted program reads, so a false one never reds and it drifts to flatter the change. Annotation replaced with what the module now does, including why the ordering argument is NOT restated here. Orphan fixtures deleted (launch_org, launch_net, launch_binding, witness_reserve, both lifecycle intents, launch_test_slot); witness_reserve's own note was stale too, still saying production refuses every launch. Also declares the seal a 3c frontier rather than leaving it implicit: nothing consumes it, and no witness CAN -- its input is a sole_constructor snapshot -- so the trigger is named beside it, with the honest alternative stated. Three claims pass. Compile shows the 3 pre-existing extdeps/gunbc shell transport errors and nothing in these files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * chore: regenerate drifted generated artifacts (ci auto-heal) Ledger-Repair-Judged: docs/design-failure-modes.md Ledger-Rows-Repaired: docs/design-failure-modes.md production_actuation_unmodeled_beside_a_modeled_probe Ledger-Rows-Repaired: docs/design-failure-modes.md decision_surface_truncation Ledger-Repair-Judged: docs/design-rung-drops.md * Give the Demand and Offer joins their own discriminating REDs Review 62349 is right and it is the sharpest catch on this branch. The rule has FOUR refusal arms -- fence, Work, Demand, Offer -- and the witness enrolled REDs for TWO. A rule that dropped the Demand and Offer checks entirely would have greened every claim in the file, so the join was reported at a rung its executed evidence does not establish. That is rung inflation in the module built to stop a substitution. Two controls added, same shape as the Work case. Arm-to-assertion count is now 4 and 4. MUTATION-PROVEN INDIVIDUALLY, because five passing claims do not establish discrimination -- a control that fails alongside others is not a discriminating RED: remove ONLY the Demand arm -> ONLY a_grant_for_another_demand reds remove ONLY the Offer arm -> ONLY a_grant_for_another_offer reds Both times the other four stay green. Also records in the witness WHY these REDs are authorable when the seal's is not, since the questions sound identical and get opposite answers: the seal's input is a sole_constructor snapshot, so a disagreeing one has no constructor a fixture can reach and a control there would be a 4b decoration; FabricIdentity is an ordinary record, so a disagreeing demand or offer is writable right here. The constructor decides it, not how important the check feels. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Delete the unconsumed seal, and restore two more dark roster rows Review 62362, both findings correct. THE SEAL IS DELETED, WHICH IS WHAT MY OWN ANNOTATION SAID TO DO. I had declared it a 3c frontier, but 3c admits one only with a NAMED LATER CHANGE, and mine was FCI-3 -- which the displacement analysis shows is blocked behind a prerequisite nobody has started. The review's phrasing is right: a gate id plus a missing plan and an unpriceable prerequisite is not a named later change. And the annotation cited docs/plans/microvm-launch-displacement-analysis.md, WHICH DOES NOT EXIST ON THIS BRANCH -- it is on the unmerged #10823. So the justification for keeping unconsumed code rested on an artifact that does not resolve, which is the citation defect this session has now made three times across unmerged branch boundaries. This also DISCHARGES rather than overrides the earlier ruling to keep the seal: that keep was conditional on a fixture being able to author the forbidden state and be refused. No witness can call it at all -- its input is a sole_constructor snapshot -- so the condition failed. Module is 79 lines: one rule, four refusal arms, five claims. All five still pass with the seal gone, which is the evidence that they were driving the RULE and never the seal. ROSTER WAS BEHIND MAIN AGAIN, 176 vs 177, missing guarded_filter_branch_dropped_by_emitter and match_variant_lookup_does_not_peel_type_alias. Merged: 178 imports and 178 list entries, empty join against main in both directions. Second time on this branch -- the ledger surface moves faster than an edit-review cycle, and only the count comparison catches it. The projection came back UU with NO conflict markers, the merge driver refusing as designed. That artifact is HealRegeneratesAfterProvisionalMerge, so the provisional state is staged for heal rather than hand-written. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * chore: regenerate drifted generated artifacts (ci auto-heal) Ledger-Repair-Judged: docs/design-failure-modes.md Ledger-Rows-Repaired: docs/design-failure-modes.md guarded_filter_branch_dropped_by_emitter Ledger-Rows-Repaired: docs/design-failure-modes.md match_variant_lookup_does_not_peel_type_alias Ledger-Repair-Judged: docs/design-rung-drops.md * Declare the frontier for the rule this module keeps, and record a false annotation as false Review 62386, both findings correct. THE ANNOTATION WAS FALSE, NOT MERELY STALE. It said the rule was "factored out of the seal above it" and that "the seal delegates to it". The seal was deleted last commit, and bind_microvm_to_cell takes only a snapshot and never sees an ExecutionGrant -- so the sentence asserted a relationship between two declarations that does not exist in the corpus. Recorded as false rather than quietly removed: a 4c annotation is read by no Accepted program, so a false one never reds, and the next reader deserves to know the claim was wrong rather than find a tidy comment. THE FRONTIER WAS DECLARED FOR WHAT I DELETED AND NOT FOR WHAT I KEPT, which is the review's actual point and it is exact. 3c's two admissible states differ by whether the consumer and trigger are stated BESIDE the declaration; for grant_authorizes_reservation they were not, so the rule was dangling rather than declared. Now stated: named consumer is the FCI-3 executor whose exit predicate this rule is the agreement half of, trigger is that executor existing, and the route today is that nothing in the runner path calls it. STATED WITH ITS COST RATHER THAN AS A REASSURANCE: that trigger is not near, because the actuation FCI-3 must displace is authored in no repository. A reader weighing whether the module earned its place should have that beside what it does carry -- four typed refusal arms, each mutation-proven to red ALONE. Correctness established by execution; only production consumption deferred. AND A TENSION IN MY OWN REASONING, NAMED RATHER THAN LEFT FOR A REVIEWER: I deleted the seal partly on "the trigger is not near", and I now declare a frontier for the rule on that same trigger. 3c requires a trigger to be STATED, not to be near, so "not near" was over-applied there. The seal still went for the right reason -- no fixture could construct its sole_constructor input, so it had ZERO executed evidence. The seal failed consumption AND evidence; the rule fails only consumption. 5 claims pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Home the grant-authorization join beside the issuer, with typed refusal variants Review 62520 raised two findings and both were real. The refusal type collapsed four named causes into AuthorizationDisagrees { cause: String } while the annotation directly above it asserted "THE REFUSAL ARMS ARE NOT COLLAPSED INTO ONE `cause` STRING". The four arms were four if-branches producing four prose strings. That is DESIGN section 2's anemic String leaf, and because a section 4c annotation is read by no Accepted program, the false claim could never red. The type is now a sum matching the sibling ReceiptAdmission: GrantAuthorizes | GrantFenceRefused | GrantAuthorizesDifferentWork | GrantAnswersDifferentDemand | GrantIssuedAgainstDifferentOffer. The five controls match a named variant instead of string_contains, which strengthens them -- a refusal naming the wrong authority could still contain the right words. The home finding was also right, but its prescribed destination does not compile. product.fabric.budget already imports product.fabric.execution, so homing a rule that needs MoneyAccount and fence_admits into execution.dag creates an import cycle -- the one structural law section 4 states. The rule is homed in product.fabric.grant_issue instead, which already imports execution, budget, identity and work, and which issues the grant: the agreement join is the direct counterpart of issue_grant, at FCI-2's grain. gunbc.runner_microvm_execution_authorization is deleted and the witness moved beside the issuance fixture it already imported. The section 3c frontier is re-declared at the new home: the rule still has no production consumer, its named consumer is the FCI-3 executor, and the trigger is stated. Executed evidence: five claims pass under claim_batch --claim-run against dag/test/claim/fabric/fabric_grant_authorization_witness_test.dag. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Scope the append-only-carrier corollary to carriers that are still a list #10822 deleted the hand-appended failure-mode roster: membership is the row DIRECTORY now, derived before compile. This row's corollary prescribed a count comparison against the base for append-only carriers and cited the roster at 163 against 173 as a live countable specimen. Half that citation is a superseded claim, and it had not landed yet -- so this corrects the row before it ever becomes an authority rather than editing a published one. I had declined this edit earlier on the grounds that the row was a live authority and that changing prose inside a PR waiting on an unrelated gate is how a superseded claim survives review. The reasoning was sound and its PREMISE WAS FALSE: the row is absent from origin/main and exists only on this branch. There is no landed authority to preserve. The corrected text is taken verbatim from #10853 (deep-crane-107, 9a2358a), which added the identical file and differed only in this one receipt string; that PR closes rather than landing a second copy for this one to later drop. The file is now byte-identical to its version, so one row has one authority. What the text now does: scopes the rule to append-only carriers that are STILL A LIST, keeps extdeps.exec.command admit_callers at 67-vs-68 as the surviving specimen, marks the roster SUPERSEDED FOR ONE CARRIER ONLY citing #10822, and keeps the closing instruction not to invent a replacement count for the dissolved carrier -- directory membership is a different subject. The correction is self-applying: refreshing that citation as though the roster were still countable would itself be a_live_authority_name_carries_a_superseded_claim. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Fence the money reservation with the money generation, not the lease's Review 62587 found two real defects and the second explains why the first survived. THE RULE JOINED TWO INDEPENDENTLY ADVANCING COUNTERS. fence_admits compares the generation the ACCOUNT holds for a reference, and product.fabric.budget reserve_money records that from the MONEY request (`generation: request.generation`). The rule presented grant_lease_generation, which answers the RESOURCE lease's counter. So a grant authorized whenever the two happened to coincide, and a legitimate grant whose counters had diverged was refused as GrantFenceRefused -- a cause naming the wrong fact. product.fabric.execution already records this class one authority over: comparing generations alone was a real fencing hole, and it made prose describe a wall the code did not build. This was that class re-introduced against a different generation authority. ExecutionGrant carries NO money generation -- money_reservation is a bare ReservationRef -- so there was no correct value to read off the grant. It is now a presented coordinate, which is what the rest of this rule already does and what makes its REDs authorable. The witness presents money_request.generation, what the ISSUER asked for, against what the account RECORDED: two independently authored facts. Reading it back off the account would compare a value to itself and green by construction, which is the 4b decoration this rule exists to avoid. THE POSITIVE CONTROL WAS GREEN BY FIXTURE COINCIDENCE. money_request.generation is 9 and fixture_lease.generation is 9, so the old control passed while proving only that two unrelated counters agreed. The fixtures are left at 9 and the discrimination is carried by a new claim that presents the RIGHT account against a WRONG generation -- the arm the coincidence was hiding, since the existing exhausted_account claim refuses because the account holds no lease for the reference AT ALL and would pass a rule that ignored the generation entirely. MUTATION-PROVEN, and the result is the review's finding as an executed measurement: with the defect restored, EXACTLY ONE claim fails -- a_grant_fenced_at_another_money_generation_authorizes_nothing -- while the other five pass, INCLUDING the old positive control. The pre-existing suite was structurally blind to this defect, which is how it survived. Reverted, all six pass. The now-unused grant_lease_generation import is dropped: leaving it would advertise a dependency the code no longer has. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN --------- Co-authored-by: Brian Searls <cutencool.work@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Brian Searls <briansearls1@gmail.com> Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
…aths, fold the two trigger histories #10822 deleted docs/design-failure-modes.md and dag/gunbc/recurring_failure_mode/roster.dag (both now gitignored and generated before compile; membership is the directory). Both deletions accepted -- restoring either would re-create the attractor DESIGN §3 names. inflation_fold_cannot_name_the_environment_it_measures.dag is kept: that per-class file IS the registration now. docs/design-rung-drops.md is taken from the base side verbatim, unregenerated, per the generated-artifact driver's declared repair route; set difference over both row-identity shapes reports no row gone dark. determinism_transitive_reachability.dag's restoration_trigger was edited on both sides and neither subsumed the other. Merged rather than chosen: this branch's capability-grain trigger (the index prescription is refuted by execution, so the trigger names the COST SHAPE) is the head, and main's later paragraphs are folded in -- the artifact-not-a-clock instrument correction, the partition-by-eval_steps rule, the worsening pre-repair distribution, the three repaired shapes in the past tense, and the map-dedup obligation. One sentence of main's was rewritten where it cited the CAPABILITY clause that no longer says what it said. Compiles: 0 blocking errors on the row's own entry. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CQuBs4mLu9ArojSWFiRcey
#10814) * Delete world_converge at the root: read-only policy was bound as an actuator that only refuses gunbc.world_converge fused observation, comparison and actuation into one handler with an apply stage. Three measured consequences, none of which a refinement could have reached: The successful actuation path had never executed anywhere in the corpus -- the generic's own fixture bound apply to FixtureApplyShouldNotRun, and the sole production binding is read-only. There was no green to preserve. Read-only policy was therefore modelled as an actuator that always refuses, so three of repo_ruleset's seven refusal causes described a write that does not exist. One of them, RepositoryRulesetUnexpectedLiveRule, was a second representation of a fact the divergence list already carried. And that cost a finding: because the holds were ordered above the divergence step, an unreadable bypass roster -- which is EVERY live read under this repository's credential -- returned the hold and DISCARDED the drift list. verify reported that the roster could not be read and never reported that the enforcement, required contexts or ruleset name had also drifted. std.goal_assessment replaces it, keeping the two layers as two layers and making the empty divergence unconstructible: GoalDiverged and GoalIndeterminate carry std.algebra FreeSemigroup, which has no empty inhabitant. No NonEmptyList was minted -- FreeSemigroup is the corpus's nonempty carrier and its own grounding note withholds that alias. repo_ruleset cuts to inspect_goal in the same motion. Two causes are deleted rather than moved, RulesetBypassRosterUnobserved leaves the DIVERGENCE coproduct to become an unknown, and two helpers that already recorded having no production consumer go with the last thing that could be mistaken for one. Every refusal that made verify exit non-zero still does; what changed is that the drift survives a hold. Not landed, with triggers stated in the module: reconcile_once, whose only honest first consumer is the fleet admission spine; and a goal revision, whose inhabitant is the plan basis. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg * Delete goal_is_satisfied: a one-bit summary of a four-arm coproduct, consumed by nothing Review 62257. Verified: `git grep goal_is_satisfied` returned its own definition and the witness asserting it, and nothing else. The one real binding this branch lands, gunbc.repo_ruleset verify, matches GoalSatisfied directly -- correctly, because the other three arms carry payloads it has to report. So the declaration's only consumer was a test, which DESIGN section 3c and section 5 both red: a witness is not a consumer, and its own annotation named a caller that did not exist. It is the same test this branch already applied when it deleted bypass_roster_projection_refusal and merge_queue_projection_refusal, which had recorded the identical standing. Missing it on a declaration I had just written is the reason the reviewer's question is asked from the consumers inward rather than from the declaration outward. The law the deleted witness asserted -- that indeterminate must not read as agreement -- is not weakened by losing it. It is strengthened: with no one-bit summary in the module, the conflation has no spelling to be written in, which is construction where there was validation. An annotation now stands where the predicate did, saying why the convenience is refused, so the next author does not restore it as a kindness. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg * chore: regenerate drifted generated artifacts (ci auto-heal) Ledger-Repair-Judged: docs/design-failure-modes.md Ledger-Rows-Repaired: docs/design-failure-modes.md read_only_policy_bound_as_an_actuator_that_only_refuses Ledger-Repair-Judged: docs/design-rung-drops.md * Enroll the new witnesses as test fn, and attach the is_satisfied rationale to a declaration TWO DEFECTS, ONE OF THEM THE REASON THE OTHER WAS NOT CAUGHT. Review 62263: every assertion in the two new witness files was declared `fn`, not `test fn`. Verified against the modelled rule rather than by inspection -- v2.workflow.floor_naming_hygiene enrolls a decl only when its line starts with `test fn ` or `test data `, and floor_discovery_barren_test_sidecar_reason_prefix refuses a `*_test.dag` entry declaring none. So floor discovery enrolled NOTHING from either file. The whole evidence set for the new std.goal_assessment root -- the layer guard, the nonempty-divergence law, the unknowns-outrank-deviations precedence -- was inert. That is section 4b(4) inverted: a climb deletes the lower-rung PRODUCTION machinery and its discriminating RED stays enrolled. This migration deleted the enrolled world_converge witnesses and put unenrolled functions in their place. All ten are now `test fn`. The second defect is the annotation this branch put where goal_is_satisfied had been: a trailing `//` block at end of file, which section 4c refuses because an annotation must attach to a module-scope declaration and that one named no subject. Ten located parse errors, and they are what refused the floor phase in CI. The rationale now sits above `type GoalAssessment`, which is the carrier it is actually about. WHY MY OWN VERIFICATION MISSED BOTH, recorded because the gap is the useful part: `claim_batch --functions <names>` runs whatever it is handed, so it proves the assertions HOLD and says nothing about whether anything would ever ask them; and it resolves an entry closure rather than running the parse phase over the corpus, so the annotation refusal never reached it. Naming functions to a runner is not evidence that a lane runs them. `gunbc compile --source-root dag --source-root src/v2` is the instrument for the second, and it now reports zero errors where CI reported ten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg * Delete the dead binding fixture and every import that outlived its consumer Review 62280, all four findings verified against the tree and all four real: `fixture_subject` in the binding witness had no caller anywhere; the `Bool` import in std.goal_assessment outlived the deleted goal_is_satisfied; and RepositoryRulesetConvergenceSubject, ObservationRefused, ObservationEstablished and RulesetBypassRosterUnobservable were imported and never referenced. RATHER THAN FIX THE FOUR THAT WERE NAMED, THE SAME QUESTION WAS ASKED OF EVERY DECLARATION AND EVERY IMPORT THESE FILES CARRY, which found three more the review did not reach: `List` and `GoalInspection` in the generic witness, `NoRequiredStatusChecksRule` in the binding witness -- and `gunbc_repository`, which became unused only once the dead fixture was deleted. A reviewer finding four instances of one class is evidence about the class, not a list of four repairs, and the fourth of those would have survived a finding-by-finding fix. That is the third time this branch has landed an unconsumed declaration: goal_is_satisfied, then this fixture, both mine, both after the branch had already deleted two such helpers for exactly that reason. The pattern is that section 3c is easy to apply to code being removed and easy to forget on code being written, so it now runs as a sweep over the diff rather than a judgement at each site. Ten witnesses re-run after the deletions: 10 PASS, 0 FAIL. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg * Remove the assessor's remaining short-circuit: a one-dimension goal defect suppressed every comparison Review 62287, verified and correct. After the actuation holds were deleted, `ruleset_assess` still answered `signed_desire_projection_refusal()` FIRST and returned a whole-assessment refusal, never reaching the divergence fold. So a merge-queue projection gap suppressed live enforcement, target, name and required-context drift that have nothing to do with it -- the same ordering harm this branch deleted the holds to fix, reproduced one layer down on the arm that was left, and an instance of the class this same branch rosters as read_only_policy_bound_as_an_actuator_that_only_refuses. The helper's own prose still reads "Refusing rather than actuating", which is what it was for: a refusal on behalf of a writer this module does not bind. THE DISPOSITION IS NOT THE MINIMUM THE REVIEW ASKED FOR, because the honest one is available. The gap is not merely "do not let it suppress the rest": the goal states the merge-queue desire TWICE -- as the signed policy and as the rules wire the projection carries -- and when those disagree, WHICH ONE IS THE GOAL is undetermined. merge_queue_divergences can still compare the live queue against the signed policy, so the dimension is not unassessable for want of a comparison; it is unassessable for want of an unambiguous goal. That is an UNKNOWN on one dimension, RulesetMergeQueueDesireSelfInconsistent, carried beside every other dimension's proved drift. With that arm gone, no state of this subject leaves its inputs incomparable, and the type now says so rather than leaving an unreachable variant to be mistaken for a live one: AssessRefusal is v2.std.cardinality Never. The arm survives in verify's match because the coproduct is closed -- which is the property that makes a fifth assessment arm a compile error rather than a silent fall-through -- and it is unreachable by construction. Two witnesses added: the desire is self-consistent today and contributes no unknown, and a name drift is still reported with the desire self-consistent, which a reinstated short-circuit anywhere above the comparison fails. 36 witnesses over both repo_ruleset entries: 36 PASS, 0 FAIL. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg * Fix forward the world_converge census that fired at merge time, not at deletion time Review 62304. Verified and correct, and the CAUSE is the part worth keeping: when this branch deleted gunbc.world_converge the census was clean -- four referents, all handled. Main then landed #10712 (std.decision, "selection precedes convergence"), which added a NEW world_converge consumer while this branch was deleting it. Merging main was textually clean, so the break arrived silently: no conflict, no marker, an unresolved import on a claim witness. A textual merge cannot see a symbol deleted on one side and newly referenced on the other. DESIGN section 3 says the deletion IS the census and what breaks is exactly what was load-bearing. That holds -- but only against the tree at deletion time. Against a moving main the census must be RE-RUN after every merge, and this branch had merged four times without doing so. Fixed forward rather than restored, per the same section: - dag/test/claim/realization_selection_witness_test.dag migrates to std.goal_assessment. Two stages disappear rather than being rewritten: it supplied WorldApplied over a context it threaded through, asserting nothing about actuation and existing only because the interface demanded a stage. There is now nothing to supply. The section 3d claim it exists for is unchanged -- a SelectedWithin candidate feeds the convergence root as its SUBJECT and the root reaches a satisfied assessment -- and the receipt was returned unchanged by the old handler too, so nothing asserted was dropped. Its two test names named the deleted symbol and are renamed with it. - gunbc.design_document, gunbc.design_argument and std.decision cited world_converge in the prose and in the section 3d reviewer question. That is the stale-name defect section 3's standing rule names, sitting in the document that GOVERNS the criterion, so the rename lands with the deletion. DESIGN.md regenerated: zero occurrences. 18 witnesses on the migrated entry: 18 PASS, 0 FAIL. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg * State the trigger for GoalAssessmentRefused: a declared frontier, not an unconsumed arm Found by re-reading DESIGN against the diff rather than by a reviewer, which is the only reason it is worth recording separately. GoalAssessmentRefused has no constructor anywhere in the corpus. Both bindings reach it never: repo_ruleset instantiates AssessRefusal at v2.std.cardinality Never because no state of that subject leaves its inputs incomparable, and the realization-selection witness never mints it. Under section 3c that is a DECLARED FRONTIER, admissible only with its trigger stated beside it, and no trigger was stated. The trigger is a NAMED CONSUMER rather than a date, because section 4b(3)'s rule that a trigger must name the capability applies to a frontier the same way: product.fan_tach_health already computes exactly this fact -- FanTachRefused over DuplicateExpectedHeader, DuplicateObservedHeader and HostIdentityMismatch, each an ill-formed QUESTION SET rather than an unhealthy fan -- and the arm discharges when that module binds to this algebra. DELETING THE ARM WAS THE OTHER CANDIDATE AND IS THE WRONG ONE. The distinction between "the world differs" and "the question was malformed" is one this module exists to keep; a coproduct that cannot express the second forces the first binding with an ill-formed input to file it as a divergence, which is the conflation section 5 forbids. The frontier is the honest state. This is the fifth unconsumed declaration on this branch. The first four were found by review; this one by reading the authority. Both are cheaper than the sixth. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg * Move the unreachable-arm rationale to module-item grain: an in-body annotation is a parse refusal CI on c6a2626: required-witnesses-floor refused with four parse errors, all mine, all one class -- "source annotation sits inside a declaration body. Only module-item grain is modeled." The note explaining why verify's GoalAssessmentRefused arm is unreachable sat INSIDE the match. It now sits above verify, where DESIGN section 4c says an annotation attaches, and it records that fact so the next author does not rediscover it the same way. WHY THIS SHIPPED, WHICH IS THE PART WORTH KEEPING. This branch already broke CI once on section 4c, with a TRAILING annotation that named no subject. I fixed that and then verified this round with the check I had built for it: "does the file end on a declaration". That check is structurally incapable of seeing a body-level comment. I tested the previous BUG instead of the RULE, which is the same shape as a regression control that greens on everything except the one input it was written for. The rule is one line of DESIGN and decides both variants at once: annotations attach at module-item grain. The check that follows from the rule rather than from the last failure is `^[[:space:]]+//` over the changed files, and it now returns empty across all of them. Verified with the instrument rather than a proxy this time: `gunbc compile --source-root dag --source-root src/v2` completes its frontend phase -- the phase that refused -- with zero occurrences of the annotation class and zero errors in any file this branch touches. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg * Restore the rationale two surviving witnesses lost with their deleted neighbours The world_converge deletion removed the tests around them, and their leading annotation blocks went with the wrong neighbour. Both survivors were left bare: w_RED_an_incomplete_merge_queue_rule_refuses_and_names_the_missing_fields and w_RED_verify_reports_an_undeclared_bypass_actor. The restored text is rewritten rather than reverted, because the original prose described a shape this branch removed: one half cited a type-tag guard test that no longer exists, and the other contrasted verify against a converge actuation path that is gone. What survives is the irreducible rationale DESIGN section 4c asks for -- absent and zero are different observations and only the optional carries that difference; a refusal that names nothing tells the operator that something is wrong without telling them what. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg * chore: regenerate drifted generated artifacts (ci auto-heal) Ledger-Repair-Judged: docs/design-failure-modes.md Ledger-Rows-Repaired: docs/design-failure-modes.md read_only_policy_bound_as_an_actuator_that_only_refuses Ledger-Repair-Judged: docs/design-rung-drops.md * Restore the parameter seam that makes the merge-queue unknown reachable merge_queue_desire_unknowns read the declared rows through a nullary wrapper, so with this repository's merge queue unsigned the fold answered Absent on every input the corpus can produce: the Present arm and the RulesetMergeQueueDesireSelfInconsistent renderer it feeds were unreachable, and the only claim over them was a positive control. DESIGN section 4b asks whether a check's RED is authorable before the check is written, and settles it on whether the forbidden state is authorable AS A FIXTURE rather than whether it exists in the accepted corpus. It is. So the seam is a parameter, as signed_desire_projection_refusal_for already was and as the deleted repo_ruleset_observed_actuation_admission carried it for this same reason -- it was unreachability that let an inversion sit in that arm undetected. The nullary wrapper is deleted rather than kept beside the parameterized one. The RED is discriminating by execution, not by inspection: with the operand set back to what the nullary wrapper supplied, claim_batch reports FAIL on RED_a_signed_policy_the_projection_does_not_carry_is_an_unknown_on_that_dimension. Restored, all 7 witnesses in the file pass. Also fixes prose the actuation deletion left stale: the refusal string still read "converge would send a ruleset that cannot converge. Refusing rather than actuating" in a value that is now an unknown and not a refusal, and an annotation cited that sentence as present tense. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg * Reconstruct a derived source at the base revision instead of inheriting its head record The namespace wave-admission baseline is the head index with the diff applied in reverse at FILE grain, which assumes one thing about every file the diff did not name: that its base text equals its head text. That holds for a tracked file. It is false for a source generated before compile and gitignored, because such a file never appears in `git diff` no matter how far its content moved -- so its head record was carried into the BASE index and the wall compared the head against itself. #10822 made the failure-mode roster derived and gitignored. This branch carries the first row to land after it, and had its own authored addition reported as NewPoolCoincidenceResolution -- a name resolving from a pool with nobody authoring a reference -- when the reference was authored by this very change. That disposition is not auto-admitted, so a required lane blocked, and it would have blocked for every later row. The binding delta was right; the authorship evidence under it was poisoned. The carry now requires the base tree to contain the path, which is what makes unchangedness proof of byte identity. A derived path is re-rendered from the base revision's row files through the SAME renderer the writer uses and parsed by base_records, so no hand-built record and no second derivation authority exist. Any other untouched head-index record absent from the base tree is NotEvaluated: a path listing supplies existence, never content, and an untracked path may equally be local state, a scratch fixture or a dirty worktree file -- inheriting or silently dropping it fabricates a baseline either way. binding_disposition, locally_authored_claim_added and disposition_auto_admitted are untouched, and NAMESPACE_TRANSITION_ADMISSIONS stays empty: nothing is admitted by permission. Measured on this head, the wall goes from 1 unadjudicated delta to ADMITTED with 7 real deltas, binding_rows_compared 759867 -> 759866 -- the one row that should never have been compared, because with a correct base the name does not exist there. Evidence: the wall's own run on this base/head reports `namespace-wave-admission ADMITTED`. Two unit tests enrolled in the deriver's seed-growth population cover the derivation, and the one encoding the defect is discriminating -- it fails under a mutation that renders the base as the head while its three siblings stay green. Those tests sit under the declared drop rust_unit_tests_off_the_merge_path and are local diligence, not enrolled evidence. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg * Check that base and head share one renderer, and pay the consumed rows this touch brings due TWO OBLIGATIONS, ONE FILE. Review 62579 found a promise standing where a check belongs. The base-side reconstruction renders base inputs with the HEAD renderer, which is sound only while both revisions carry the same one -- and the diff said so in a comment while nothing enforced it. A later edit to render_roster would have had the wall render a base with head logic, compare it against a head rendered the same way, and report the renderer's own change as zero: the very class this branch repairs one revision earlier, re-admitted as an author obligation. DESIGN section 5 asks for the wrong state to be unwritable, not described. The check compares the text of render_roster on both sides and refuses with NotEvaluated when it differs. The subject is the RENDERER, not its file: whole- file bytes were measured first and refuse too much -- they would refuse this very change, and every later one adding a doc comment beside the renderer, for a reason unrelated to the hazard. A line stopped where nothing is wrong teaches authors to route around the wall. The membership rule is a stated residual, not a covered case, because this change moves it into a declaration the base lacks so no text comparison spans both sides. Verified both directions against the real base blob: the texts are equal today, so the guard passes rather than blocking this branch, and a mutation changing the rendered import syntax makes it refuse. THIRTY-FIFTH DISSOLUTION. Touching this file brings the two gunbc#10688 rows due, and CI said so: `0 unadjudicated delta(s) ... 2 consumed admission(s) due for deletion on this roster-touching change`. Those rows demanded their own adjudication -- "not by trusting this sentence" -- so the join was run: on main v1.std.core DECLARES call_semantics_target while v1.compiler.emit_rust references it four times and declares it none, so both sides resolve the spelling to v1.std.core and the TargetChanged delta is no longer producible. Rows and label go together, as the two label deletions before them did, and the roster is empty again. gunbc.namespace_wave_admission drops the label from its enumerated declarations and re-tenses the prose that claimed it was listed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg * Restore the record my deletion left headless, and re-tense seven dead actuation claims TWO FIXES. The first is mine to own: deleting CALL_SEMANTICS_TARGET_REHOME_LABEL from the enumerated declarations dropped only the `decl_name` line and left the enclosing DeclarationRef standing, so gunbc.namespace_wave_admission carried a record with no name. Every required lane parses the corpus, so one malformed literal failed all four on fd0098e. The compiler named it exactly -- missing required field 'decl_name' in literal of type 'DeclarationRef' -- which is the fail-closed behaviour working; the generated-artifact gate now resolves again. The second is review 62605, which found the sign-off standing still asserting the world this branch deleted: a WHOLE-RULESET PUT, a converge run that would delete an out-of-band rule, and "the actuation refusal below" -- a positional pointer to deleted code -- beside a dissolution string promising "a divergence converge repairs" two sentences before saying SIGNING THIS ROW ACTUATES NOTHING. A meaning fork inside one operator-facing obligation. Sweeping the module found seven such sites, not one, and they are fixed as a class because DESIGN section 3 is explicit that a dead assumption is an attractor: while it stands, nearby questions keep being answered in its vocabulary. The UI-flip warning gets the sharpest correction, because the hazard did not soften -- it INVERTED. It read: a rule enabled in the web form is deleted by the next run, a scheduled silent self-reversal. With no writer, a flip is not reversed at all; it PERSISTS, unmodeled, reported as a divergence on every assessment forever, with nothing in this repository able to clear it. The operator sees a configured queue and the assessment sees drift it can never repair, so signing the row is now the only route rather than merely the best one. Where a hazard survives as an obligation on a future writer rather than a current fact -- the whole-ruleset replace, the unexpected-rule refusal, the signed desire the projection cannot build -- it is stated that way rather than deleted, because the reason those checks exist does not go away with the writer that consumed them. All 7 witnesses in repo_ruleset_goal_assessment_witness pass, including the discriminating RED. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg * Delete the dissolution record my own edit duplicated, and the stale entry it left standing Review 62621 found ~90 lines of the module's dissolution record present twice. It is not a merge artifact: my scripted replacement of the SIXTH TRANSITION block computed the block's END offset with an index search from position 0, which matched an EARLIER occurrence of the same trigger sentence in a preceding entry. With end < start, concatenating the two slices duplicated everything between them. Deleting the span turned up more than the review reported. The original SIXTH TRANSITION block was still standing as well, so the file at once documented the two gunbc#10688 rows as live and recorded their dissolution -- two sources free to disagree about exactly the rows this roster adjudicates, which is the section 3 violation the review named, in a sharper form than it found. Verified by counting occurrences against origin/main rather than by reading the diff, because this is the second time a scripted text edit of mine damaged this file: every pre-existing record now appears exactly once as it does on main, the THIRTY-FIFTH DISSOLUTION is the only addition, and SIXTH TRANSITION is gone. A delta scan across every changed file finds no other block duplicated relative to main. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg * Record why the gunbc#10813 admission row went with this merge Kept separate from the merge commit so the reasoning is reviewable on its own. Main's gunbc#10813 hit the defect this branch repairs. Adding one recurring-failure-mode class made the roster's existing declaration resolve a new name, the wall reported NewPoolCoincidenceResolution -- a name resolving from a pool with nobody authoring a reference -- and since that disposition does not auto-admit, the class could not land without a permission row. Its entry called that "the honest cost of the pool being adjudicated rather than assumed". It was not the pool's cost. It was a wrong baseline's. The roster is derived before compile and gitignored, so it never appears in git diff, and the reconstruction carried its head record into the BASE index -- the wall compared the head roster against itself, so a reference authored by the change looked authored by nobody. Every future class would have paid the same row. With the base re-derived from the base revision's own row files, the class is absent from the base roster, the binding row exists on one side only, and the comparison skips it under the rule the wall already states: only rows present on both sides, because a newly authored name has no prior denotation to change. No delta is produced, so the row matches nothing -- and a row matching no delta is STALE, which refuses. Deleting it is that rule firing, not a preference of mine. Adjudicated by measurement: the class file is ABSENT at this change's merge base and present at its head, the exact shape that produced the delta before and produces none after. My own THIRTY-FIFTH DISSOLUTION entry is dropped in the same resolution. Main independently recorded the same gunbc#10688 deletion while this branch did, and keeping both would be two sources free to disagree about which rows were adjudicated -- the duplicated-record fork review 62621 found here one head ago. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg * Yield the baseline-reconstruction fix to main's, which landed the same cut Main's gunbc#10856 implemented the repair this branch carried: a derived roster has no base side in a diff, so a baseline that drops only the paths the diff TOUCHED inherits the head's roster as the base's, the module's own source reads as unchanged, the authorship discriminator answers false, and an ordinary append classifies NewPoolCoincidenceResolution. Same diagnosis, same cut, arrived at independently from the other side. Two implementations of one fact is the section 3 fork, so this branch takes main's and deletes its own. All four files now diff to zero against main. MAIN'S IS ALSO BETTER IN A PLACE MINE WAS WRONG. roster_from_path_listing returns None when the base tree carries no row files, keeping "the roster module was genuinely absent at this revision" distinct from "present and empty". Mine rendered an empty roster for both, which fabricates a module the base never had -- the empty-observation narrow, in the code written to remove one. Main also generalizes over sweep roots rather than hardcoding the dag/ prefix, and it retired the gunbc#10813 admission row on the same reasoning this branch reached. WHAT IS LOST AND WHERE IT SHOULD LAND: the renderer-sameness guard from review 62579. Main's version renders base inputs with the HEAD renderer, so a change to render_roster would have the wall compare a head-rendered base against a head-rendered head and report that renderer's own change as zero. The finding is moot against the code it was written about, since that code is gone; the hazard is not, and it now sits in main's implementation rather than this PR's. It wants its own change against main's shape, not a rushed transplant here. This PR is now the convergence consolidation alone: 14 files, no Rust, no seed-growth roster, no wall. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg * Migrate the four world_converge consumers main added while this branch was open Review 62717 is right that the root cut was unfinished. The fe50f1d merge brought gunbc#10683's consumers of a module this branch deletes, the textual merge was clean, and the break arrived silently -- the same gap a14a66d already named once. The census is the only thing that finds this, which is why the deletion IS the census. Migrated onto std.goal_assessment rather than restoring world_converge, because each of these is the failure class this PR rosters. Both carried an `apply` that could only refuse, and that refusal had to inhabit the same coproduct as the causes meaning the subject could not be OBSERVED -- so an unread leg and a miscoded leg, an uncaptured lane and a misconfigured one, arrived as one verdict answering two questions. PlantCodingCannotBeActuatedHere and SwitchCannotBeActuatedHere are deleted, not ported: each restated as a REFUSAL what the difference list already carried as a VALUE, and the weaker copy was the arm a reader reached first. fabric_switch_desired carried the same conflation one layer out. Its DesiredSpeedUndecidedCause had a single PlantDidNotConverge arm holding a plant refusal, so a caller could not tell "the plant was never read" from "the plant was read and diverges" -- go read the plant versus go change the copper, two remedies behind one name. It is now PlantNotObservable and PlantDivergedFromRequirement. EVERY DEFECT IN THE FIRST PASS WAS CAUGHT BY EXECUTION, NOT BY READING. The corpus compile found a stale import rename and two matches where switch refusal causes were left standing as arms of an assessment; the witness run then found two more, where the list-and-unwrap ceremony of the old List shape was carried into a FreeSemigroup that answers with .head directly. All 13 witnesses across both files now pass, and the compile reports zero errors in any file this branch touches. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg --------- Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Summary
docs/design-failure-modes.mdis not committed, andgunbc.recurring_failure_mode.rosteris derived from the row directory instead of hand-appended.dag/gunbc/recurring_failure_mode/. Heal still regenerates the rung-drop ledger only.Absent is loud (the four writers are convenience)
Three modules import
gunbc.recurring_failure_mode.roster:gunbc.design_ledgers,gunbc.ledger_row_coherence, andtest.claim.generated_artifact_merge_driver_real_execution_witness. A reader that never hits the four collect writers still fails at resolve: missing module, not an empty fold. The fourensure_sites exist so a clone compiles without a committed roster; they are not load-bearing for the absent case.Short roster is refused (not silent)
(a) A non-UTF-8
.dagstem is a typed refusal in the writer, notcontinue.read_dir/ dirent errors already propagate.(b)
ledger_row_coherenceis roster-to-projection, so a short roster still projects a short document and stays green. The required-CI parse join now identity-joins sibling.dagfiles (exceptroster.dag) to roster members, independent of the writer:RowFileNotRosteredandRosterMemberNotARowFile. That is the 175/175 stem measurement as an executing check.length(rows) == 0 => NotALedgerArtifactstill classifies an empty roster as "not a ledger" rather than an empty ledger (predicate vacuously true on an empty domain). It does not bite while absent-is-loud holds; it is not a second line of defence.File-join wall vs discriminating evidence (§4b(1))
run_rostered_row_joinruns in the required-CI parse phase (claim_executor). The declared-versus-rostered arm has an on-path control:test.fixture.rostered_row_join.omitted_control.The two new arms (
RowFileNotRostered,RosterMemberNotARowFile) do not have an equivalent on-path fixture. Their discriminating REDs are#[test]functions inrostered_row_join.rs(file_roster_join_tests), which run only undercargo test -p v1-compiler --lib— the declared dropgunbc.rung_drop.rust_unit_tests_off_the_merge_path. Compile-and-CI enforce the production join on every required run; the evidence one would point at to show those two arms discriminate is off the merge path. "The join executes" is not "the join is proven to discriminate" for those two arms.Q2 — write during read (DESIGN §7)
Stated §3b divergence: those four collects write a gitignored file into the source tree.
*.dag.derivingthenrename; last rename wins with identical content.A compile that cannot write source is a new failure mode for those four collect consumers (local sessions, remote CI, BuildBuddy).
Seed growth and filename-as-name
The substrate cannot enumerate a directory into typed value bindings. Directory-as-membership is a §4b wall-after-grounding realized in the host. Retired by declaration-value binding over the declared population (
gunbc.guarantee_stall.roster_re_enumerates_its_own_rows_stall).Filename stem = declaration name is a spelling join, not typed inhabitance. Same retirement trigger.
Gate 3 is not a §4b(3) drop
A committed-bytes wall over a file that no longer exists had its subject removed. Content is still checked by
required_lane_claim_agreement_witnessagainstexpected_design_failure_modes_md()as generated text.Drop population (
rust_unit_tests_off_the_merge_path)This change enrols
v1_compiler.cli_run.rostered_row_joinfile_roster_join_testsinto that declared drop's--libpopulation. The restoration trigger is unchanged and covers them: they are--libtests.Test plan