Repository navigation
The planner minted an empty Spark schedule unconditionally: the observation carries its projection, the rows become an axis product, and the artifact stops answering for a plan it never made (P0-C3, dry) - #9763
Conversation
e320cf5 to
f7adb42
Compare
|
SPARK-PAIR-0 P0-C3, the dry observation-to-plan bridge. No host mutation. The two independent breaks
This PR closes the second and makes the first possible. It does not close the first — see the end. One observation, one producer
The carrier now holds the projection, produced by the same probe transaction. Because it is The mint derives the observation from the readback but takes the projection. That asymmetry is deliberate: the observation is a pure function of the readback, so deriving it removes an authorable disagreement; the projection is a function of four more probes this type does not carry and cannot re-run, so a mint claiming to derive it would be fabricating them. Rows are a fact about an observation, not about an artifactRow production and artifact assembly were fused, so reaching Spark rows meant calling the Spark-specific artifact builder — which brings its own timers, caps, Every refusal is preserved. The four unobserved guards — unit, enablement, activity, linger — each name a probe whose silence would otherwise read as the member is absent, and absence is what makes The substrate refused my first design, and it was rightI first had the artifact call the row producer directly. That closed an import cycle — The artifact's Optional carried a fact that cannot happenIt answered Two smaller repairs found on the way
Receipts (srv6)Control — restoring Only the claim exercising that path reds. The other three stay green because they test the row producer, whose behaviour the mutation doesn't touch — reported that way rather than trimmed, because a control that reds everything usually means the claims are one claim wearing four names. What this does not close
— sent from eager-pike-541 |
a8a05ba to
a02bff2
Compare
|
Fixed in What happened. The mint went through two shapes in this branch. It first took a finished The fix is the one you named, with one refinement. and Receipts — srv6, expected = terminal = passed on all three. One thing landed since you reviewed, from a parallel read: the artifact took — sent from eager-pike-541 |
|
Back to draft despite the approval. A parallel review found four things the approval did not, and one of them is an overstatement in my own commit message. Recording them here rather than merging. 1 — my "one observation, one producer" claim is not structural yet (blocking). The mint now takes probes rather than a projection, which closes the type-level splice. But the production path underneath does not actually run one producer:
They may reach the same account in a given run, but nothing establishes it. The honest wording for what this PR currently does is "the readback and projection are constructed during one enclosing function call" — not "one typed probe transaction owns every capture." My commit message claims the latter. That is rung inflation and I am not shipping it under that description. The repair is a per-host probe transaction carrier owning host, locus, and one capture per probe, with reporting, account, observation and projection all projecting from it. 2 — the axis product needs to be a disposition, not a pair (blocking). 3 — the artifact's 4 — my linger claim is not established. The argv fork is genuinely closed. But the slice still contains What stands: the row-production extraction, keeping all four unobserved walls, the This PR becomes P0-C3a and does not claim the C3 exit receipt. — sent from eager-pike-541 |
|
Retracting finding 4 — the defect I claimed does not exist. I checked the slice's actual flow rather than reasoning from the presence of
So the slice never turns "the probe did not answer" into "linger is off". It refuses with zero planned rows, which is the correct behaviour and was already the behaviour on pre-PR main. The What I got wrong and why. I saw a three-valued type ( The honest description of what this PR actually changes on the linger axis is therefore narrower than my commit message says:
The commit message and PR body will be corrected to say that, and the "turning 'the probe did not answer' into 'linger is off'" sentence removed. It described a defect that isn't there. — sent from eager-pike-541 |
…e rows derived from them (P0-C3a, dry) SPARK-PAIR-0 P0-C3a, part of the dry observation-to-plan bridge. No host mutation. This does NOT claim the C3 exit receipt. TWO INDEPENDENT BREAKS make P0-WET unreachable: the CLI passes an unobserved Spark axis, AND the generic artifact set spark_serving_typed_actions to scheduled_rows_empty() unconditionally. Repairing either alone changes nothing. This closes the second and makes the first possible; the first is still open. ROWS ARE A FACT ABOUT AN OBSERVATION, NOT ABOUT AN ARTIFACT. Row production and artifact assembly were fused, so reaching Spark rows meant calling the Spark-specific artifact builder, which brings its own timers, caps, observed_slots: [] and an unobserved fabric population -- two subjects, two baselines, two leases and an arbitrary winner. spark_serving_scheduled_rows_for is extracted; both the slice helper and the generic artifact derive from it. All four unobserved guards are preserved verbatim: each names a probe whose silence would read as "the member is absent", and absence is what makes membership_reconcile plan an ADDITION. MEMBERS AND ROWS ARE ONE CARRIER. The artifact took observed_spark and the schedule as two independent parameters, so a caller could hand it members from one observation and rows planned from another. observed_spark feeds the observed BASELINE, and the baseline is what apply re-checks to decide the plan still applies -- so that disagreement would have been invisible at exactly the moment it mattered. SparkServingAxisProduct is sole_constructor and only the row producer mints it. THE MINT TAKES PROBES, NOT A FINISHED PROJECTION. A first version accepted the projection as a parameter, which reopened the class #9754 closed one field over: nothing stopped a caller pairing srv5's readback with a projection built for srv6. The mint now takes the four probe observations and builds the projection from readback.host, so the host is not a caller's to supply. THE SUBSTRATE REFUSED MY FIRST DESIGN AND IT WAS RIGHT. Having the artifact call the row producer closed an import cycle -- gunbc.fleet_converge_plan -> spark.serving_converge_apply -> spark.serving_converge_plan already runs the other way. Artifact assembly consumes already-planned axis products. THE ARTIFACT'S OPTIONAL CARRIED A FACT THAT CANNOT HAPPEN. It answered Absent when content_hash_of_value did not return the Fnv1a64 arm -- an absorbing arm in the narrowing direction, since an unrecognised hash became "there is no plan artifact", read by every caller as nothing to converge. content_hash_atom dissolves it. THE LINGER ARGV FORK IS CLOSED: it had two spellings, one per transport, free to drift the first time loginctl moves, and now has one home. The three-valued reader added beside it serves the unified observation producer. It is NOT a repair of serving_converge_slice_wet: an earlier version of this commit claimed the slice collapsed "unobserved" into "disabled", and that claim was FALSE. The slice refuses on all three unobserved cases -- transport refusal, nonzero exit, malformed stdout -- before reaching any boolean translation, setting LingerUnobserved with zero planned rows. The claim was inferred from a Bool? in a signature rather than from the control flow, and is retracted here rather than left standing in history. KNOWN NOT DONE, so this does not overstate its rung: the production path does not yet run ONE probe transaction. In spark_serving_observe_host_via_locus the readback uses the via-locus leg while the projection helpers use the other leg, and is-active and the runtime receipt are each probed twice. The honest wording is that readback and projection are constructed during one enclosing call, not that one transaction owns every capture. The axis carrier is also a pair rather than a disposition: it cannot distinguish a non-Spark subject from an unobserved one and has no refusal arm, which is why fleet_converge_plan_artifact's Refused arm currently has no producer. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018dHefbXnLiW1XNs2vVxhwj
a02bff2 to
d4257b6
Compare
|
This branch was silently reverting #9704, and the review credited that as a clean deletion. Rebuilt in What happened. I squashed with None of that is mine. The Rebuilt from current main, taking only the nine files I actually authored: No Receipt — srv6, One correction to this review. It repeats "The linger three-valued fix removes a real Bool→enum truncation." That claim is mine and I retracted it in the comment above before this review ran. The slice already refuses on all three unobserved cases — transport refusal, nonzero exit, malformed stdout — before any boolean translation, setting This stays draft: the probe-transaction and axis-disposition findings from the parallel review are still open, and the commit message now states them as known-not-done rather than claiming the rung. — sent from eager-pike-541 |
Axis disposition landed, three annotations corrected — and I hit the recut bug a second timeFirst, the process failure, because it happened again and the protocol is the point. Preparing this update I typed That near-miss produced a useful measurement: six of my nine files overlap with paths main changed since my base. So even a correct nine-path allowlist would not have been enough — copying my snapshots over those six would have silently reverted main's edits inside files that are legitimately mine. The safe primitive is a real 3-way merge, not a file copy, which is also this repo's stated preference for integrating main. Recut receiptComplete Three annotations correctedThe commit message told the truth while the source still told the superseded story — which is the "annotation as evidence" trap this codebase is otherwise careful about.
The axis is now a disposition, and the empty mint is deletedA pair of lists could not distinguish a non-Spark subject from a refused observation from an observed host owing no writes — it answered all three with empty-members-plus-empty-rows, which is an absorbing fallback in the input to a converge. The host travels in the arm, so it cannot disagree with the observation that produced it. The artifact's Receipts — srv6,
|
…rd did not reach this path
There was a problem hiding this comment.
💡 Codex Review
When the ProbeLinger capture exists but its transport is refused, exits nonzero, or returns malformed output, the transaction still reaches this function and spark_serving_projection_linger_enabled returns false; wrapping that value in LingerObserved therefore renders observed_linger=no even though linger was never observed. This defeats the existing distinction between disabled and unobserved on artifact-refusal paths and can mislead consumers of the wet receipt; derive a typed observed/unobserved state rather than converting the projection to a Boolean here.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…1-p0-c3 # Conflicts: # DESIGN.md # docs/design-ledgers.md # src/v1/stage0/src/namespace_wave_admission.rs
…1-p0-c3 # Conflicts: # src/v1/stage0/src/namespace_wave_admission.rs
|
HOLD — do not merge yet. The CANARY APPLICABILITY block in this body is stale and now asserts a verdict that is not established at this head. This is an author-raised defect, not a review finding; the five approvals were all cast against this body. The block states: Head is now 32e2dcc — two further merges from main (ce01e18, 32e2dcc) since that candidate. Measuring the delta a44a6a1..32e2dcc, the intersection is not empty. Files changed since the asserted candidate that lie on or beside the canary route: So A third defect, independent of the staleness: Disposition I am taking: re-establish the receipt at this exact head rather than repair the carry argument. Re-running the read-only two-host observation on 32e2dcc discharges the whole carry question by removing the need for a carry, and — decisively — a body edit does not change the head, so the new receipt stays valid against this SHA, no CI re-run and no review staleness. Building the extensional-equivalence proof instead would require committing witness code, which moves the head and re-opens the same gap against the new SHA. I will replace the applicability block with the execution receipt for 32e2dcc, or report here if the observation cannot be obtained. |
|
HOLD LIFTED — the applicability gap is discharged by re-execution, not by a repaired carry argument. The read-only two-host observation was re-run on this exact head. Head did not move: a body edit changes no SHA, so this receipt is bound to the commit the PR actually carries. Acceptance criteria were written down before the run produced any output, with a partial pass pre-committed to count as a failure. All of them pass: srv5=1 srv6=1 sections and 0 foreign; acquired and phase=report on both; required=23 captured=23 missing=0 duplicated=0 and executed=23 transport-refused=0 prerequisite-refused=0 on both; one fleet-attempt across both with distinct per-host observation-attempt; one credential identity 01c65fe99aeb7b24; model_tag=20b digest=17052f91 on both; binary sha256 identical before and after invocation. Body updated in three places: the applicability block is replaced by the exact-head receipt, the summary sentence no longer says the canary was carried, and the SupersededHistoricalExecution note no longer names 34de783 as authoritative. Two things withdrawn on the record rather than silently dropped:
One fact checked at this head and worth stating because it decides whether this was a refactor or a subject change: spark_serving_desired_model_ref is still ollama_gpt_oss_20b_model_ref, and ollama_model_support_row_for selects by model identity rather than catalog position, so appending the 120b row cannot move the selection. A later change rewiring the desired ref to 120b would be a subject change and would need its own wet execution. Standing limitation, stated so it is not discovered later: this receipt is bound to 32e2dcc / tree 5ffaab02. Any further merge from main produces a new head and retires it. If main advances before merge, the observation must be re-run rather than re-argued. No source churn was involved — no commit, no new SHA, no CI re-run, and the existing approvals are still against the code they reviewed. |
…1-p0-c3 # Conflicts: # docs/design-ledgers.md
…s imported Two repairs, both surfaced by composing with main rather than by any defect in either side alone. The merge with main brought #9824, which added a `target` field to AdmissionSubject::Binding: the module the admission's own PR moved the name TO, and the referent of the consumption proof. Our two SPARK-PAIR-0 P0-C3a rows predate that field, so git merged both sides cleanly and the tree did not compile. The value is verified rather than copied from our own label -- spark_serving_linger_probe_words is declared in dag/gunbc/spark/serving_observe.dag, whose module line is gunbc.spark.serving_observe. serving_execution_schedule.dag matched SparkServingUserUnitActivityAddress at four sites without naming it in its import block. Peer modules list it explicitly, so it is now listed here. This is import hygiene, not a resolution repair: the module resolved and typechecked before the change -- measured, by reaching evaluation and failing on a call-contract mismatch, which occurs after resolution.
|
Addressing review 57915 (dashboard-only, REQUEST_CHANGES). One finding fixed, one respectfully disputed with a measurement. FINDING 1 — missing import. FIXED in 0599a8a, but the mechanism claim is wrong and I want that on the record rather than quietly accepting it. The observation is correct: dag/gunbc/spark/serving_execution_schedule.dag matched SparkServingUserUnitActivityAddress at four sites without naming it in the import block, and peer modules list it explicitly. It is now listed. The claim that this "breaks name resolution for four binding rows" is not correct. I measured it before changing anything, by running the module through the real compiler: Before the fix, that reached EVALUATION and failed with CallContractMismatch: missing required argument address. A call-contract mismatch is raised after resolution and typechecking, so reaching it is positive evidence that the module resolved. Variant names resolve corpus-wide in this substrate; the import block is hygiene and convention, not the binding mechanism. After adding the import the same command produces the identical diagnostic, which is the control: the change moved hygiene, not resolution. That distinction matters beyond this line. If a missing import really did break resolution, the compiler floor would already be red on main for every module in this shape, and the remedy would be a corpus-wide census rather than one line. Recording the mechanism honestly is what keeps the next reader from deriving the wrong remedy. FINDING 2 — advisory, empty ScheduledRows mint. This does not exist in the current file. ScheduledRows is sole_constructor with exactly one mint, scheduled_rows_from_frozen_schedule, which takes a frozen schedule. There is no empty mint beside it. The file records its own history on this point: The empty mint the review describes was removed earlier in this same PR; the advisory appears to be reading a superseded state. The nearby empty-looking construction is SparkServingScheduleRowsProjected { rows: [], direction: SparkServingSchedulableEmpty }, which is a different type and a deliberate typed empty-disposition arm, not a ScheduledRows bypass. No change made. ALSO IN 0599a8a, and not from this review — a defect that only exists because of composition with main. #9824 added a target field to AdmissionSubject::Binding. Our two SPARK-PAIR-0 P0-C3a admission rows predate it, so git merged both sides with no conflict and the tree did not compile: E0063 missing field target, twice. The value is verified rather than copied from our own row label — spark_serving_linger_probe_words is declared in dag/gunbc/spark/serving_observe.dag, whose module line is gunbc.spark.serving_observe. Wet canary re-executed on the new head, all predeclared criteria passed: srv5=1 srv6=1 with 0 foreign; acquired and phase=report on both; required=23 captured=23 missing=0 duplicated=0 and executed=23 transport-refused=0 prerequisite-refused=0 on both; one fleet-attempt with distinct per-host observation-attempt; one credential identity; model_tag=20b digest=17052f91 on both. STANDING ISSUE the operator should see. main advanced to f87b9eb while this canary was running, and the branch now re-conflicts on docs/design-ledgers.md. Measured: 9 of the last 40 main commits touch that generated artifact, 4 in the last 6 hours. Each re-merge moves the head, which retires the wet receipt and forces another observation plus a fresh CI cycle. I am NOT starting another merge-and-canary round on my own initiative until CI on 0599a8a reports, because two of my recent local greens were not evidence and I would rather spend the next canary on a tree whose build is actually proven. — sent from eager-pike-541 |
…1-p0-c3 # Conflicts: # docs/design-ledgers.md
|
Re-established after merging main. Head is now 744d69e. Sequence, and why each step was needed rather than churn: CI proved the previous head green before I spent another fleet observation. All five checks on 0599a8a came back completed/success with none skipped or neutral, which is what established that the two composition repairs actually work — the AdmissionSubject target field and the explicit variant import. I held the merge until that reported, because a red there would have been confounded by merging main on top of it. Then main was merged (97b010a). docs/design-ledgers.md conflicted again and was resolved by REGENERATION from the merged .dag authorities, never by hand. The union was verified before staging with an instrument checked for discrimination: main-only lines 1, lost 0; ours-only lines 2, lost 1. The single dropped line is The rebuild proves it reached the real compiler: Wet canary re-executed on this exact head, all predeclared criteria passed: srv5=1 srv6=1 with 0 foreign; acquired and phase=report on both; required=23 captured=23 missing=0 duplicated=0 and executed=23 transport-refused=0 prerequisite-refused=0 on both; one fleet-attempt with distinct per-host observation-attempt; one credential identity; model_tag=20b digest=17052f91 on both. The PR body applicability block now carries this tuple. On the race that has been retiring these receipts: main has advanced twice more since this merge, to 0b4366e, and the branch STILL MERGES CLEANLY against it — so this receipt is not retired and no further observation is required. Measured rate for anyone deciding how long that holds: 9 of the last 40 main commits touch docs/design-ledgers.md, about one per 90 minutes. CI is running on 744d69e. I am not merging (standing operator policy). — sent from eager-pike-541 |
|
Correction: my rebuttal of the advisory in review 57915 was WRONG, and I am withdrawing it. Review 57931 is an APPROVE and asked for nothing, but checking its WHAT I SAID, and why it was false. I wrote that the empty-ScheduledRows mint "does not exist in the current file" and that ScheduledRows "has exactly one mint, scheduled_rows_from_frozen_schedule". That is not true. It is declared exactly where review 57915 said it was: How I got it wrong is worth naming because it is a repeatable method error, not a typo. I grepped ScheduledRows, read the region around lines 670-711, found an annotation reading "the empty mint is deleted rather than kept beside them", and concluded the file had no empty mint. That annotation is about a DIFFERENT construct — an earlier standalone empty-members-plus-empty-rows product — and the mint that does exist sits 140 lines below the region I read. I generalized a file-wide absence from a sampled region, then asserted it against a reviewer who had the line number right. The reviewer was more correct than I was and I told them otherwise. WHAT IS ACTUALLY TRUE, having now read the whole thing. The mint exists and carries its own §5 argument, which I find persuasive: That is a real answer to the sole-constructor objection rather than a restatement of it: the constructor is total in its output but takes no input capable of carrying the hazard the wall exists to stop. The two production call sites are both honest, though not for the reason review 57931 gives:
No code change. The mint is defensible on its own annotation and both call sites are legitimate; changing it now would be churn against a considered design, and I have no finding that survives contact with the file. The standing lesson I am carrying out of this: a region read is not a file read, and "does not exist" is a claim about a whole corpus that a sampled grep cannot support. I should have run the declaration-site grep — — sent from eager-pike-541 |
|
Two acceptance defects were raised against this PR. Both are now closed — one by execution, one by edit — and I am registering one substantive disagreement about the standard as stated. DEFECT: the body named three different candidates. Fixed. The test-plan heading no longer calls a44a6a1 the candidate. The 305/305 srv6 matrix is explicitly re-attributed as a historical targeted run on a44a6a1 that has NOT been re-run on the current head, with CI named as what actually covers the current head. The narrative and the superseded-execution note both now point at 744d69e. Remaining a44a6a1 mentions are deliberate historical references inside the withdrawn-carry narrative. Five green jobs do not make stale candidate claims true, and the packet should not have been left contradicting itself. DEFECT: the wet receipt covered only the branch head, not what actually lands. Half of the premise behind this was wrong and is worth correcting for the record: the composed tree was never unevidenced. This workflow does not test the branch head. actions/checkout carries no explicit ref, so a pull_request event checks out refs/pull/N/merge, and the job log says so in its own words: All five green jobs, required-witnesses-floor included, executed on 59cd938 / tree 4440edfa. So main commits reachable only through the base parent — including #9813, which touches the evaluation substrate this entry runs on — are already inside the tree the DRY evidence adjudicated. The real gap was narrower: the WET observation. That gap is now closed by execution rather than argument: Same predeclared criteria, all passed. Built in a detached worktree at that exact commit with the build proven to reach the compiler. The harness asserts BOTH parents, so a refreshed merge preview refuses rather than silently scoring a different tree. This cost no head move and no CI restart, which is why it was worth running rather than negotiating. THE DISAGREEMENT, stated so it is not silently adopted. "Admissible for the current merge-preview tree" is not a satisfiable acceptance standard. The merge preview is a function of main, and main advanced three times while this work was in progress (0b4366e, 4897391, 3170945). Any wet receipt for a merge preview is retired by the next main commit, so under that standard no PR touching a wet route is ever merge-ready except by freezing main or by merging inside the gap. DESIGN section 5 is explicit that you check whether a wall is attainable before demanding it, and that "never" is the trap that lets a ratchet pose as a wall. So the body states the boundary rather than implying a permanence I cannot deliver: this receipt is exact for 4440edfa and is retired by any main advance that changes the preview. What it achieves is pairing the wet evidence to the exact tree the dry evidence adjudicated — strictly stronger than a head-only receipt, and the strongest honest claim available. If the operator holds that merge requires a wet receipt for a preview current AT THE MOMENT OF MERGE, that is a scheduling constraint rather than something I can discharge by working harder: freeze main, or merge inside the window. It is precisely the coupling a mechanized extensional-equivalence instrument would break, and that instrument still has no program identity. — sent from eager-pike-541 |
…1-p0-c3 # Conflicts: # DESIGN.md
|
main advanced to eced9d2 and conflicted DESIGN.md. Merged, regenerated, pushed. Head is now 3add770. DESIGN.md is a generated artifact (check-attr merge: generated-artifact) and was resolved by REGENERATION, never by hand. Union verified before staging this time: main-only lines 1, lost 0; ours-only lines 1, lost 0. Nothing dropped from either side. Note GitHub reported mergeable=MERGEABLE mergeStateStatus=CLEAN throughout, computed against a stale base — the branch genuinely conflicted. That false green has now misreported this PR three separate times today; it should not be used as the merge signal. Wet canary re-executed on the new head, all predeclared criteria passed: The composed-tree receipt in the body is now marked SUPERSEDED rather than silently left standing: its merge preview had the previous head as parent 2. It can be re-taken on request, and cheaply — a merge-preview observation costs no head move and no CI restart. A STRUCTURAL POINT FOR THE OPERATOR, because this is now the fourth wet observation this PR has spent and none of them was retired by a defect. The measured loop: generated artifacts on this branch conflict with main roughly every 90 minutes (9 of the last 40 main commits touch docs/design-ledgers.md; DESIGN.md is the same class). Resolving a conflict necessarily moves the head. Moving the head retires the wet receipt AND restarts a ~40 minute CI cycle. During that cycle main advances two or three more times, so the next conflict frequently lands before the window closes. The binding constraint is not my cycle time and not the fleet observation — both are about ten minutes. It is the gap between CI going green and a human merging. Two things would close it, neither of which is mine to do:
Absent either, the honest expectation is that this PR merges when someone merges it promptly on a green, and that any delay past roughly ninety minutes will likely require another regenerate-and-re-observe round. I will keep doing those rounds, but they are not converging toward anything — each one is a fresh coin flip, not progress. — sent from eager-pike-541 |
…1-p0-c3 # Conflicts: # docs/design-ledgers.md
…rcuits Two cost-shape repairs from review 57946. The review named them as canonical- query violations; the defects are real but they are §6 bare-minimum-cost, and recording the mechanism correctly matters because the two readings send a reader to different remedies. spark_serving_acquisition_receipt_of built its three outcome counters as three `length(filter(...))` passes over one capture list, each filter calling its own Bool predicate that exhaustively matched the same closed outcome. Three traversals and three matches to answer one question -- the copied-accumulator shape DESIGN §6 says is always fixed regardless of the realized n, since n tracks the probe roster and is not time-stable. One fold now answers all three, bound once at the use site, and the match stays exhaustive so a fourth outcome arm still fails to compile rather than being silently counted as neither. The three predicates are deleted; they had one caller each. spark_serving_projection_linger_enabled asked an existence question as `length(filter(...)) > 0`, which builds the whole filtered list before comparing. It now uses `any`, which short-circuits. Behaviour preserved by execution, not by inspection: the observation transaction roster is 23/23 and the observe roster is 68/68, including changed_capture_outcomes_move_the_line_the_report_emits, which is the discriminating claim for outcome counting. Review 57946's third finding is NOT addressed here because it is wrong, and the roster's own gate is what settles it rather than an argument. See the PR reply.
|
Addressing review 57946. Two findings fixed, one disproven — and the third is disproven by executing the gate that owns it rather than by argument. Head is now 6a17153. FINDING 1 and FINDING 2 — FIXED, though the stated mechanism is not the defect. There is no predicate/walker dissolution rule in DESIGN. The only occurrence of "predicate" is section 4b, "does not lift arbitrary predicates to proof", which is about rung honesty and not about Bool-returning matches. And there was no canonical fold or query surface over SparkServingProbeOutcome to route through: I checked, and none exists, so the remedy as written would have had me author a surface ahead of its consumers — the shape gunbc.spark.cell_role explicitly deleted when it removed spark_host_offerable_for_training for having exactly one caller. But the instinct was right, and there was a real defect at both cited lines. It is section 6 bare-minimum-cost, not a query-surface violation: serving_observation_transaction.dag built three counters as THREE serving_observed_members.dag asked an existence question as Behaviour preserved by execution rather than inspection: observation-transaction roster 23/23 PASS, observe roster 68/68 PASS, including changed_capture_outcomes_move_the_line_the_report_emits, which is the discriminating claim for outcome counting. FINDING 3 — NOT FIXED, because the required receipt already exists and the roster gate proves it. The finding says two admission rows were added "without the required checkable hand-Rust receipt". The receipt exists. NAMESPACE_TRANSITION_ADMISSIONS is already rostered as a hand-authored declaration at dag/gunbc/namespace/namespace_wave_admission.dag, module_path v1_compiler.namespace_wave_admission, and that justification explicitly contemplates exactly this case: I did not resolve this by reading and preferring an interpretation. I took the finding seriously, confirmed the item IS in the gate population — So the receipt requirement is already satisfied, and attempting to satisfy it a second time was itself the section 3 violation the gate exists to refuse. The prescribed fix would have manufactured the defect. My new module is deleted and the roster edit reverted; the tree carries neither. Wet canary re-executed on the new head, all predeclared criteria passed: main also advanced to a12a115 during this and conflicted docs/design-ledgers.md again; merged and regenerated, union verified before staging (main-only 1 lost 0; ours-only 2 lost 1, the dropped line being the CI-bankruptcy rung-drop row whose subject is present on all three sides with main text winning). — sent from eager-pike-541 |
…1-p0-c3 # Conflicts: # docs/design-ledgers.md
The merge took origin/main's generated files, which is correct as a conflict resolution and stale as a result: they carry #9763's third row and not the two this branch adds. Regenerated with tools.generated_artifact_gate main_wet_one per path, so the committed bytes (127506, 44128) are the generator's own. Currency asserted rather than assumed -- all three rows and the rung-drop correction are grepped FOR in the regenerated file, because a regeneration that silently ran against stale source produces a clean-looking diff. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018aiTywconE9bm1hmUPjyft
…tions nobody could run (#9848) * Two failure-mode rows, and a rung-drop row that advertised two mitigations nobody could run THE ROWS. Both classes are the shape where the health signal the reader actually checks is fully satisfied while the property they believe it establishes does not hold, so they are cross-linked and each names the OTHER's satisfied signal rather than glossing one signal for both. `instrument_output_read_as_subject_content` — a consumer derives a SET from a report whose completeness is a property of the REPORTING TOOL rather than of the subject. Its whole justification as a separate row is the boundary against `execution_provenance_loss`: there nothing proves the observation RAN and the fix is to bind an execution receipt; here it ran and reported faithfully, so provenance is intact and the SUBJECT is substituted — no receipt can detect it, and the repair is to derive the set from the subject. Specimen is gunbc#9829. `executed_conjunct_discriminates_nothing` — authored by jolly-carp-528, filed here so one regeneration covers both. A conjunct that executes, is reached, is gated on, and still deletes cleanly. Every symbol re-resolved against origin/main before pasting. THE CORRECTION, which is the part that outlives both rows. The CI-bankruptcy rung drop listed four still-runnable mitigations. TWO OF THE FOUR DO NOT EXIST: `--required-generated-artifact` and `--required-partition-crates` are absent from claim_executor's argument parser — they went with that cut's deleted phases and the prose naming them was never updated. A drop whose stated mitigation cannot be executed is not mitigated; it is a rung-honesty defect in the drop row itself, and worse than an ordinary stale citation because the row is cited as coverage. The row now names the actual actuators: the .dag entry point `tools.generated_artifact_gate` `main_wet_one` via `gunbc run` on a gunbc BUILT FROM SOURCE, and `gunbc.repo.repo_self_build` `repo_self_build_partition_crates_command`. THE PROJECTION IS GENERATOR-PRODUCED, NOT HAND-EDITED. DESIGN.md's class index is projected (`design_document.dag` maps over the roster), so both generated files move. Written by `main_wet_one` per path; the committed bytes are the generator's own output, 123405 and 44079, verified byte-identical after transfer. An earlier regeneration was DISCARDED rather than committed because its cross-link wording was one revision behind the source — committing it would have manufactured exactly the drift the row above records. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018aiTywconE9bm1hmUPjyft * Regenerate the projection from the merged authority The merge took origin/main's generated files, which is correct as a conflict resolution and stale as a result: they carry #9763's third row and not the two this branch adds. Regenerated with tools.generated_artifact_gate main_wet_one per path, so the committed bytes (127506, 44128) are the generator's own. Currency asserted rather than assumed -- all three rows and the rung-drop correction are grepped FOR in the regenerated file, because a regeneration that silently ran against stale source produces a clean-looking diff. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018aiTywconE9bm1hmUPjyft --------- Co-authored-by: Brian Searls <briansearls1@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…urable documents before claiming there are none Four repairs, each found by executing rather than by reading. RECORDS_EQUAL WAS COMPARING THE TARGET'S ARM. It was `left.children == right.children` -- the substrate's structural equality -- which was exactly right while a StoredEdge held a bare locator, and stopped being right the moment the target became a NodeTarget. `NodeTargetResolved(r)` and `NodeTargetUnresolved(l)` over the SAME locator are different values structurally and the same object by content, because content_hash_of_children consumes only the locator. So the store reported "different record" for two records its own identity rule cannot tell apart, and insert_admission turned that into a LocatorCollision: a FABRICATED refusal, raised at a store being asked to do something ordinary. merge reaches it directly -- it reassembles a root from resolved children and re-inserts over a record whose children were unresolved. It was caught by a CONTROL, not by review. The file arm of the merge role claim passed and its node control failed, which is the whole reason a claim is paired with one. The comparison is now over label and LOCATOR, through the same labeled_children_of_stored the identity fold consumes, so there is one definition of what makes two records the same rather than two that agree until one is edited. THE DURABLE POPULATION WAS ASSUMED EMPTY AND IS NOT. The tag-bump note said no document exists to be compatible with, because the only producer is a witness rebuilt every run. True of the closure format; FALSE of the repository envelope, which has checked-in fixtures under dag/test/fixture/scm_repository_load carrying the v1 tags -- six claims went red on them. They are versioned test inputs, updated with the schema they exercise, and the note now says the population was counted rather than assumed. AN ENCODE FIXTURE BUILT ITS SUBJECT THROUGH A ROUTE THAT IS NOW CLOSED. scm_env_absent_root_repository rewrote an encoded root to an uncontained digest and decoded it back; decode now resolves a commit root against the object table and refuses. The state is still reachable and the fixture now reaches it honestly: mint against a store that HOLDS the root, then rebuild the envelope around a store that does not -- which is exactly the residual a SemanticNodeObjectRef leaves open, since it says its object was a node in SOME store and never in THIS one. The document route became its own decode-refusal claim, with a control one fact apart. A DUPLICATE IMPORT, named by review 58166 and real: ObjectTableEdgeTargetIsAuthoredSource was bound twice in one block, which I introduced by inserting it in two separate edits. Deleted. The review's other half -- that ingest may refuse on it -- is not the case: this module resolved cleanly throughout, and three duplicate-import blocks already stand on main from #9763, #9850 and #9669. The DESIGN section 2 objection is the one that holds. Instrument: claim_batch over every dag/test/claim/scm/*_witness_test.dag with each file's own `test fn` roster. 299 pass, 0 fail, exit 0 -- including all eight claims this branch added or moved. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9
…rate, so it is deleted and the terminal identity is asserted on the route production actually takes (review 5070355515 P1-4) SUBSTITUTED REMEDY, NOT COMPLIANCE. The review asks for "an executing target proving the real consumer resolves and constructs the artifact through the same terminal authority", and its literal remedy is a caller for spark_serving_fleet_converge_apply_shell. This does not add one. That wrapper composed fleet_converge_apply_shell with fleet_converge_apply_terminal over one request -- exactly what fleet_converge_plan_artifact_of_request already does, computing the terminal once and handing that same value to the plan body line, to apply_terminal, and to the apply script. A second spelling of one composition is a second name for one fact (DESIGN section 3), and the two reconstructions could disagree about the terminal with nothing joining them. IT HAD NO CALLER TO MIGRATE. #9763 deleted the Spark-specific artifact route and with it this wrapper's only caller; the symbol has occurred exactly once in the tree -- its own declaration -- on every commit since, including origin/main today. Giving a dead wrapper a caller would have minted the parallel authority rather than removing it. The reviewer is free to reject this substitution; it should not be discovered. THE BLOCKER'S DESCRIPTION IS ONE HEAD STALE, stated rather than repaired around. P1-4 says the module's FleetConvergePlanArtifact literal still omits apply_terminal. That was accurate at the reviewed head c67b2d2; it is not accurate here, because the main merge brought in #9763, which deleted that literal and its whole assembler. The defect is real -- the orphan is what survives -- but its description no longer matches the tree. WHY THIS REPAIR BELONGS ON THIS BRANCH AND NOT IN A PREREQUISITE. The acceptance bar requires the apply shell bound to the SAME request and terminal authority. That seam exists only here: fleet_converge_plan_artifact_of_request is absent from origin/main. A prerequisite would have asserted a true property about a route that does not carry the seam under review. WHAT IS ASSERTED, on the consumer's own route. The witness module already builds the artifact through gunbc.fleet_converge_plan fleet_converge_plan_artifact with a populated Spark axis (slice_artifact / spark_serving_axis_for_observation), which routes through fleet_converge_plan_artifact_of_request. Two rows: the_spark_populated_artifact_names_its_own_terminal_in_body_and_script -- the plan body and the apply script both name fleet_converge_apply_terminal_line of the artifact's own apply_terminal. One terminal, two projections. a_terminal_no_observation_produced_is_named_by_neither_projection -- the control. A PartiallyApplied terminal nothing produced must be named by neither. Without it the positive passes on any artifact whose script contains some terminal line, which is precisely what a disagreeing second assembler emits. WHAT "EXECUTING TARGET" MEANS HERE, and it is weaker than it sounds. v2.workflow.required_floor required_gate_prefixes carries no spark, fleet or roadmap prefix. These witnesses reach the required floor through the changed-witness sublane, which seeds changed modules into the prepared closure. So they execute because the modules are changed in this PR, and after merge they are outside the required gate again -- the population of the declared rung drop gunbc.rung_drop required_gate_bankruptcy, not a new defect. The same qualifier applies to the P0-1, P0-2 and P0-3 evidence on this PR. No roster was widened to change that. Ten imports the deleted wrapper alone kept alive are removed with it; each was verified import-only before removal. No other path is touched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aYqbapkp3MEjXYLDcXSc1
…nal is FullyApplied (#9832) * RLM-2b: a typed launch-environment convergence scope whose plan terminal is FullyApplied The RLM-2b plan run over srv1 landed `partially_applied|33|slot=31 cap=0 timer=0 fabric-cell=0 activation=2`. Thirty-one of those refusals are REFUSED-REMOVAL on runner slots whose ownership no provenance signal establishes; two are the runner-service activation stall every production caller supplies. Both belong to CI runner capacity and to its own convergence plan (docs/plans/runner-service-capacity-convergence.md). Neither says anything about whether the launch environment is converged -- and gunbc.roadmap_launch_deployment_receipt accepts only FullyApplied, so RLM-2 could not close for reasons outside its subject. The scope was answering for a wider resource set than the question, so the fix is to make the question's resource set nameable rather than to relax the threshold. WHAT LANDS `FleetConvergeScope` gains `LaunchEnvironment` and `FleetConvergeRequest` gains `LaunchEnvironmentConverge { host, observed_timers, observed_caps, observed_fabric_cells }`. The arm has NO runner-slot field and NO activation field: not an empty list (which is a positive claim that the host has none, and licenses removals -- the empty-observation narrow this module already refuses one layer down), and not a selected-then-excused refusal. The invalid state has no constructor. Making that honest required moving `observed_activation_readiness` INTO the `FullHostConverge` arm and turning three functions from loose-family parameters into folds over the request: - `fleet_converge_apply_terminal(request)` -- each arm counts exactly its own axes. - `fleet_converge_apply_shell(request, terminal)` -- each arm emits exactly its own sections; a launch-environment script has no slot install and no activation lines. - `fleet_converge_plan_axis_lines` / `fleet_converge_plan_refusal_counter_lines` -- lifted out of the full-host fold so the reviewed body and the executed script read one request, and so a counter row exists only for a selected axis. While the axis set was a property of the CALL rather than of the plan, a narrower scope could only be expressed by passing something for axes it had not selected. That is the shape this commit removes; the module's own annotations had already ruled that the apply-side predicates take the request for the same reason. The artifact fold is factored into `fleet_converge_plan_artifact_of_request`, with the full-host, launch-environment and allocation-store entries as constructors into it, so there is one authority for the subject, the lease and the bundle digest. The allocation-store fold's literal `FullyApplied` becomes a call to the same terminal. Scope flows to the receipt unchanged: `scope_wire` and the member-set fingerprint come from the subject, so a launch-environment plan with zero refusals lands `fully_applied` honestly and RLM-2's join reads it without re-deriving anything it cannot observe. CLI AND WORKFLOW `fleet_converge_plan_wet` and the new `fleet_converge_launch_environment_plan_wet` are one entry point under two scopes: the run binding, expected-revision and expected-host admissions, the generation lease and the receipt mint are obligations of PLANNING, so there is no second copy of them. The plan path now routes through `observe_fleet_converge_request_wet`, the observer apply already used -- a net deletion of a second reading of the host. The launch-environment arm does not call the slot observer, because its arm has no field to put the answer in. `.github/workflows/fleet-converge.yml` is regenerated from its .dag authority with a `launch_environment_plan` mode beside `allocation_store_plan`. Default stays `plan`; roadmap_belt and the srv fleet callers keep full-host, unchanged. `roadmap_launch_deployment_receipt`'s FullyApplied-only acceptance is untouched. EVIDENCE dag/test/claim/fleet/fleet_converge_launch_environment_scope_witness_test.dag, eleven claims, every one a PAIR over the same host observation so a red is attributable to the scope rather than to the harness. Executed via claim_batch on this tree: 11/11 PASS, and the 66 existing fleet_converge_plan claims plus the 13 runner_service_activation claims stay green (90/90) -- no behaviour change for full-host. THE DISCRIMINATING RED, RUN RATHER THAN ARGUED. The wall was perturbed into exactly the wrong fix -- the launch-environment terminal selecting the slot and activation axes and passing empty observations for them -- and 4 of the 11 went red (`..._is_fully_applied_where_the_host_scope_is_not`, `..._terminal_wire_is_the_token...`, `..._detail_has_no_slot_or_activation_position`, `..._plan_body_carries_only_its_own_axis_counters`) while every full-host control stayed green. The perturbation is reverted. One control caught this commit's own prose: the apply-script claim first matched the bare spelling `runner-slot` and went red on the script's preamble note naming the omitted axes. It asserts the emitted SECTION MARKERS now; the annotation records why, because a check that cannot tell a section from a sentence describing its absence would dictate the prose. WHAT IS NOT ENROLLED, AND WHY. The structural half -- that a launch-environment request carrying a slot or activation position has no constructor -- is DESIGN 4b rung 4, and its executing evidence is the exhaustive matches over `FleetConvergeRequest`: adding an axis makes the terminal, the apply shell, the baseline text and the axis lines fail to compile. A fixture-authored RED is expressible (`compile_dag_diagnostic_census` compiles a synthetic source through the real acceptance path) and is deliberately NOT enrolled: a carrier calling that builtin declares `ReadsLiveTree` truthfully, and the required floor DECLINES every ReadsLiveTree identity before the fold (gunbc.guarantee_probe_corpus, measured 2026-08-22). Enrolling it would add a never-executed identity that reads as coverage. The trigger is that arm's deletion, which v2.workflow.required_floor already stages. ONE OUTPUT CHANGE TO THE FULL-HOST PLAN BODY: it gains a `# scope=scope:full-host` row. The plan.txt a reviewer reads carried no scope at all, which is fine while one scope exists and is not once two do; carrying it for one scope and not the other would be the fork. It changes the bundle digest of a freshly minted plan and nothing that compares across runs. Out of scope, untouched: the 31 surplus slots, runner-slot ownership grounding, and the activation observation transaction. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ZHWK38CsX9K77f3HS7hMw * RLM-2b: scope-crossing mutation controls, the production-shaped srv1 control, and the structural dependency argument Answers the controlling reviewer's stated bar. Five additions. MUTATION CONTROLS. The scope apply routes on is read back from subject_scope.txt, so the ways that file can lie are separate defects with separate remedies and are asserted separately. (a) A mutated persisted wire -- truncated, respelled, empty, or extended -- refuses as ApplyScopeUndecodable rather than choosing a scope; defaulting to FullHost would widen apply from one resource family to every family on the host, invisibly. (b) A launch-environment observation and a full-host observation of a slot-less host agree on every MEMBER, so a fingerprint over members alone would admit one against the other; the scope row is inside the hashed baseline, so it does not. The claim is a conjunction: each side's OWN fingerprint still admits, which is what makes the refusal attributable to the crossing rather than to a fingerprint that matches nothing. (c) The two crossing claims now pass each plan's REAL fingerprint instead of a dummy, so they establish that scope is judged BEFORE the member set and refuse before actuation. THE PRODUCTION-SHAPED CONTROL. The incident's exact observation is reconstructed from the same authorities the live plan used -- srv1, the thirty-one surplus slot artifacts (jit-runner.sh, runner-liveness-reconcile.sh, srv1-22..50), the desired caps, and the activation readiness every production caller supplies. Under FullHost it derives refused_axis_count 33 with slot=31 activation=2 cap=0 timer=0 fabric-cell=0 -- the run's terminal, re-derived rather than transcribed -- and the SAME host observation under LaunchEnvironment is FullyApplied, for no reason other than that those positions are absent from the type. The thirty-one names are spelled out rather than generated from a range, because for a control whose content is the number 31 a range would let a boundary slip unobserved. AND THE EXCLUDED FAMILIES STILL REFUSE LOUDLY under the scope that owns them: the full-host plan body still carries REFUSED-REMOVAL runner-slot, OwnershipUnknown, # slot-refusals=31 and # activation-refusals=2. The narrowing removed an obligation from RLM and removed nothing from the fleet program. THE DEPENDENCY ARGUMENT, COMPUTED RATHER THAN ASSERTED. That the deployed roadmap service and belt path does not consume a runner unit or activation as a selected effect is decided by a join over two populations that are both derivable, not by prose. The RLM side is deployment_owned_steps_retract_order(deployment_spec_srv1()) -- every path the live deployment installs, owns and retracts. The runner side is runner_slot_unit_name over srv1's committed slot identities plus the unit template, from gunbc.runner_unit. They are disjoint, and no owned path carries the actions-runner@ prefix at all. A non-degeneracy guard rides with it: both populations are asserted nonempty in the same claim, because a disjointness claim over an empty set is free and would go green if either producer silently stopped answering. ONE DEFECT OF MY OWN, FOUND BY RUNNING THE AFFECTED WITNESSES RATHER THAN BY REVIEW. Three annotations sat INSIDE declaration bodies, which DESIGN 4c refuses at parse: only module-item grain is modeled. They are hard errors that no other check reaches, and they would have reddened the witnesses lane roughly thirty minutes into CI. They are moved to the leading annotations of the declarations they describe, with no content lost. BASELINE, BECAUSE THREE FAILURES IN THAT RUN ARE NOT MINE. A pristine origin/main worktree reproduces all three identically: workflow_dispatch_choice_input_projects_options_list, fleet_converge_workflow_has_build_job_needs_release_bins, and the spark_serving_converge_slice entry resolve failure (serving_converge_plan.dag builds a FleetConvergePlanArtifact literal with no apply_terminal field, and the witness does the same). They stand on main and this branch neither causes nor repairs them. EVIDENCE ON THIS TREE: 187/187 PASS, zero diagnostics, across the launch-environment controls (16), fleet_converge_plan (66), runner_service_activation (13), the fleet_converge cli / receipt / apply witnesses, roadmap_launch_deployment_receipt (44), and workflow_capability_closure. The generated-artifact drift gate exits 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ZHWK38CsX9K77f3HS7hMw * Drop the duplicate FleetConvergeScope import this PR added review 57936 (claude/opus-4-7) is right: the import block in gunbc.fleet_converge_plan_cli already listed FleetConvergeScope, and this branch added a second occurrence beside fleet_converge_scope_of_wire. My insertion anchored on a neighbouring name without checking the block for the one it was adding. One line, nothing else in the diff. NOT A RESOLVER ERROR, WHICH THE REVIEW HEDGED ON AND IS WORTH SETTLING: the interpreter accepts it. Every witness was green with the duplicate present, and the same block carries a SECOND duplicate -- fleet_converge_plan_content_hash_path, at the block's lines 8 and 49 -- which predates this branch and is present on origin/main. So the class is dead syntax that reads as an editing slip, exactly as the review's second reading says, and not a refusal. The pre-existing duplicate is left alone: it is not this PR's, and a drive-by edit to it would be a polish rider on a branch being held stable. Scanned every .dag file this branch touches for the same class; this was the only one it introduced. Verified after the deletion: 100/100 PASS, zero diagnostics, across fleet_converge_plan (66), the launch-environment controls (16) and fleet_converge_cli -- the three entries whose closures contain this module. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ZHWK38CsX9K77f3HS7hMw * WIP: rebuild evidence for RLM-2b launch scope * Type the persisted-member classification so no fourth category can exist The persisted-member map was a table in the PR description. A prose map beside the code is the parallel-ledger doc DESIGN section 6 warns about: it would have gone stale the first time someone added a sidecar, and nothing could have turned it red. FleetConvergePersistedMemberAuthority has three arms and no wildcard -- receipt-bound, derived-and-recomputed, non-authoritative-presentation. There is deliberately no arm for 'covered incidentally by another fingerprint', because that is the sentence under which subject_member_set.txt and observed_baseline.hex went unexamined: each was folded into a hash somewhere, so nobody asked whether apply READ them, and it did. The classification fold is total over the path and answers UnclassifiedPersistedMember for anything it does not recognize, so an eleventh sidecar fails loudly rather than acquiring whatever category a default arm happened to name. Five witness rows join the map to the behaviour it describes, so the two cannot drift: every rostered member classifies; an unrecognized path is refused; each receipt-bound classification is paired with the executed refusal naming that same path; the derived members name the value they are recomputed into; and category three is measured empty rather than asserted empty -- every rostered member is asked, and none answers NonAuthoritativePresentation. Path literals in the witness are replaced by the declared path symbols. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ZHWK38CsX9K77f3HS7hMw * Exact-head repairs A-E, plus the two gate failures the CI run surfaced Evidence corrections (A): - admitted_with_no_diagnostic_change asserted a class-wide FieldNotFound == 0. These probes compile against the LIVE TREE, so that could redden from an unrelated future corpus diagnostic -- an assertion about the corpus wearing the name of an assertion about this field. It now asserts the exact identity naming the INSERTED field, plus the empty added/removed identity multiset. - The initializer-analysis row proved only +1 total and +1 blocking. It now names its added identity exactly (InternalError|function:totally_undefined_fn_zz|true, measured rather than guessed), requires zero of it in the baseline and exactly one in the mutant, and requires every other identity unchanged. - The plain-product sight control asserted blocking counts only, which permitted matching nonblocking residue on both sides. Its total cannot be asserted absolutely -- the census module's own closure contributes 95 nonblocking counts to BOTH sides, measured -- so the residue is pinned by difference instead, which is strictly stronger: total is clean + 1 exactly and every other identity is required equal. Rung-language corrections (B, C, D): the source authority still carried the withdrawn claims -- that the bad state has no spelling, that a launch request carrying a foreign axis has no constructor, that the terminal folds exactly three axes (it folds one), and the CLI line about having no field to put an answer in. All are corrected in place rather than deleted, because the pattern is the lesson. The non-interference header carried the superseded sibling-variant mechanism and now carries the established one. The compile-control enrollment comment reasoned from the deleted DeclinedLiveTree arm to routed-and-executed; it now states the actual mechanism, changed-witness selection, and says plainly that an unchanged fleet witness is not guaranteed to execute on a later PR. Two gate failures the required run surfaced: - .github/workflows/fleet-converge.yml was stale. Regenerated; the delta is the launch-environment step label, which still advertised caps and fabric cells -- the axes this PR removed as foreign. A correctness fix, not just a hash. - namespace-wave-admission refused two unadjudicated deltas, both mine: fleet_converge_plan_spark_typed_actions_wire_path moved from the CLI to the module that mints the bundle digest it is a member of. Rostered, with the two SPARK-PAIR-0 rows the same run reported CONSUMED removed by their own trigger, since this is the roster's next touch. Also review 57982: a cited witness symbol was spelled with a witness_ prefix the test does not carry. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ZHWK38CsX9K77f3HS7hMw * Correct what the paired sight control proves, and stop freezing a corpus number Two stale descriptions, both flattering, both removed. The compile-control source carried two contradictory paragraphs beside each other: a newer one measuring that the census module's own closure contributes a common nonblocking population to BOTH sides of the plain-product pair, and an older one still asserting that absolute total-zero assertions are honest there because the closure "really is empty". The older paragraph is deleted. The implemented logic was already the paired one; only its description was wrong. The surviving description also overstated the result. "Strictly stronger than the absolute" is false: a genuinely clean total-zero-versus-total-one pair would also prove the ABSENCE of all common residue, which a paired delta cannot claim. The accepted formulation is that the paired identity-multiset delta is stronger than a net-count comparison and is the strongest SUBJECT-RELATIVE control available under a shared nonblocking closure residue. The residue count is no longer written in prose. It is a corpus property, and writing it down makes an acceptance number out of it -- the same transcription DESIGN warns against. The paragraph now names the instrument that reports it. * Drop a same-tree count literal from the persisted-member roster join every_rostered_persisted_member_is_classified asserted the roster length at ten. That literal was a measurement of the same tree it was checking: automate its update and the row collapses to measure() == measure(), which is the change detector DESIGN forbids as an oracle. Review 57997 caught it. The identity join over the roster is the whole content of the row. Non-vacuity is kept as a positive bound rather than an equality, because `all` over an empty roster is true for free and a roster that silently stopped being populated would otherwise satisfy the row without classifying anything. The second finding in that review -- the acknowledged-unenforced "22 declarations, 22 entries" count in ci_spec, bumped 21 to 22 here -- is pre-existing, is flagged by its own comment, and is not repaired under this brief. * Re-home the launch site-census walk to the falsifier cadence, and fix its three cost-shape defects The required floor's own cost artifact (run 33459143928 at 1ac3485) falsified the premise this PR previously argued from. Eight of the eleven non-terminal rows cost 6-10ms and were only collateral of a budget their neighbours consumed; the floor uses batch clamps rather than a per-witness wall; and the 47-183s figures quoted earlier were workstation numbers that do not describe the runner. Only three rows are genuinely expensive, and they were the three most expensive witnesses in the whole 3426-row run: 13532ms, 14044ms and 19342ms against 488ms for the next-worst row. A census of every decl_facts consumer under dag/test/claim shows why -- every witness outside test/claim/long walks a fixture pool, and this was the sole whole-production-corpus walk sitting on the per-PR floor. Fix the cost shape before re-homing anything, so the cadence row carries a measurement of the subject rather than of a defect: - the copied accumulator in sites_constructing is gone (DESIGN section 6 prices this regardless of the realized n, and the roster it feeds is expected to grow) - the corpus is folded once carrying both targets, not once per target - the dotted suffixes are built once instead of per Atom node The double fold has a visible receipt: pre-fix the sentinel row cost 1.43x its siblings because it walked twice; post-fix all three measure within 0.8% of each other. Then move the module to dag/test/claim/long with its module path renamed to test.claim.long.* (the floor's long_home_storage_agreement enforces long_path_but_executing_module = 0, so path and module name must agree), and enroll the three check_fns on FalsifierCadenceJob in the same commit. The pairing is load-bearing: long/ is excluded from floor discovery at dir grain, and falsifier_cadence_surface_note records that of 63 files under the two long/ dirs only 9 are enrolled while the rest are enrolled nowhere -- a move without the enrollment converts deferred into unscheduled. The eight compile-control check_fns stay per-PR unchanged. No exemption, no declared drop, no adjudicated non-terminal. A fourth candidate was tested and rejected rather than shipped: identity_deltas_empty_except is a genuine quadratic, and an n*log(n) sort_by rewrite measured 183296ms before and 183296ms after. The census dominates, not the fold. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ZHWK38CsX9K77f3HS7hMw * Revert the re-home onto a dark lane; keep the cost fixes and state the blocker honestly c6c57ab moved this module under test/claim/long and enrolled its three check_fns on FalsifierCadenceJob. That enrollment was wrong and is reverted here. FalsifierCadenceJob has no executing realization. main carries fleet-converge.yml, fleet-desired.yml and witnesses.yml, none with a schedule: trigger, and there is no falsifier.yml -- gunbc.deleted_cadence_reference_census records the cadence as "scheduled by .github/workflows/falsifier.yml, deleted at 611fd02770 (#8283, 2026-08-15)". So the enrollment would have moved three witnesses off an executing surface onto one with no executor, while reading as coverage. That is the bare de-enrollment falsifier_cadence_surface_note says re-homing never is. Two reasons nothing catches that, both filed as findings outside this PR: the surface note is not among the twelve sites in the deleted-cadence census even though it is the live re-home destination, and enrollment_is_scheduled answers structurally on the surface's name, so the walls that police bare de-enrollment check that a row rides A surface and never that the surface executes. What is kept from that commit is only the cost work, which stands on its own: - the copied accumulator in sites_constructing is gone (DESIGN section 6 prices this regardless of realized n) - the dotted suffixes are built once instead of per Atom node - one fold per target, NOT the pair fold c6c57ab introduced: only the sentinel row needs two counts, so the pair fold made the two single-target rows compute a count they never read Two false sentences are deleted rather than softened. One claimed three rows converging within 0.8% as a receipt for the pair fold; the measurement showed three rows doing identical work, not less work, so there is no true version of it. The other claimed eight sibling rows "cost 6-10ms each", which came from reading wall_ms on rows the floor had marked verdict_reached=false, where the diagnostic states the cost is UNMEASURED and above the budget with no upper bound. The file now quotes no cost figures at all and names the floor's artifact as the re-derivable instrument instead. The annotation states the landing state without implying a plan: these witnesses exceed the per-witness budget, both arms of the floor's remedy sentence are unreachable on this tree, and the blockers are filed elsewhere. No re-home is claimed and none is pending. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ZHWK38CsX9K77f3HS7hMw * Consume the skeleton's construction-edge walk, and stop calling a refused witness executed Both findings from codex review 58047, both confirmed against the tree. FORKED EDGE PREDICATE AND FORKED WALKER. This module declared its own edge_is_construction (match on Named { name } == record_construction_spelling) and its own fold_node algebra to count target constructions. v2.std.decl_facts_skeleton already owns that interpretation as skeleton_edge_is_construction_spelling, and already walks a node with it in skeleton_atom_lexeme_census_fold, whose construction_spelling_lexemes is exactly the population this census needs. The review named the predicate; the walker was the same defect one level up, so both are dissolved rather than only the one cited. The ConstructionCount type, the predicate and the algebra are deleted and the count is now count_where over the skeleton's census. Semantics are preserved and the discriminating control still holds: the roster is still exactly the two declarations, the total is still two, and the sentinel row still sees FullHostConverge constructions, so the walk is proven non-empty rather than passing vacuously. A REFUSED WITNESS WAS BEING CITED AS EXECUTED. gunbc.fleet_converge_plan introduced the site census under a heading reading "what is executed rather than read off the declaration". That witness is BUDGET-REFUSED BEFORE VERDICT by the required floor, so it establishes nothing at the gate grain, and presenting it as executed evidence was rung inflation in a load-bearing file -- worse than sitting low, because an inflated class never ranks for climbing. The bullet now states that it is NOT ESTABLISHED AT THE GATE, that passing when run directly is not the same claim, and that WHERE construction happens is currently covered by review of the two named declarations and by nothing executing. The guarantee that does hold is the non-interference matrix, which executes on the floor and passes. This lowers the claim; it does not fix the underlying defect, which is not fixable inside this module. Both blockers are named in the annotation: the required floor tests changed-witness membership before the long-home decline, so the move to a lane declaring its own ceiling cannot be performed; and FalsifierCadenceJob, the lane that would declare one, has no executing realization. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ZHWK38CsX9K77f3HS7hMw * Re-point the excess-field matrix at a two-module fixture so its eight probes actually execute The eight excess-field probes were BUDGET-REFUSED BEFORE VERDICT by the required floor on its 8000ms per-witness wall, so they established nothing. The cause was the probe source: each one imported gunbc.fleet_converge_plan and therefore compiled that module's whole production closure. The control was already in the same file -- two rows of identical census machinery passed at 11ms and 0ms because their probe imported a small closure. The subject was wrong, not just the cost. The compiler does not know LaunchEnvironmentConverge is special: "a payload-carrying coproduct arm literal receives no unknown-field judgment" is a SUBSTRATE fact that had been pinned to a production type by accident of where it was first written. DESIGN section 3 puts a fact's home at its layer, so this moves to its own. The launch-specific claim was never carried here; it is carried by test.claim.fleet_converge_launch_scope_non_interference, which executes. THE FIXTURE IS TWO MODULES ON PURPOSE. test.fixture.payload_arm_excess_field.carrier declares the arm; the synthetic probe imports it and constructs it. Declaration and literal therefore live in different modules, which is the configuration a real construction site has -- a single-module probe would have measured the local-declaration resolution path and reported a rung for a path production does not use. The carrier declares TWO arms because a single-arm coproduct is a type alias here and refuses at the importer, which would have reddened every probe for a reason unrelated to the field under test. WHAT THIS GRAIN CANNOT SEE is declared in the annotation beside what it establishes: it says nothing about which fields a production constructor supplies, nothing about name resolution (variant spellings resolve corpus-wide, and a same-spelled arm in another module is a case this fixture does not construct -- the sibling record_construction_census family models that homonym case deliberately and this one does not), and nothing about arm shapes the carrier does not express. Renamed throughout to the real subject: module test.claim.payload_arm_excess_field_admission, file moved to match, no launch vocabulary in any check name. A name promising a launch-specific claim over a fixture-grain mechanism would be the diagnostic_name_mechanism_silent class. The four axis-named rows are now four distinct arbitrary names; at this grain the names ARE arbitrary, so those rows are weaker per-row than their production-grain predecessors while establishing the same fact -- and unlike their predecessors they reach a verdict. The discriminating controls survive the move: the plain-product probe still yields FieldNotFound, so the zeros on the payload arm remain a real property rather than a blind harness, and omitting a required field still reds. THE THREE SITE-CENSUS ROWS DO NOT MOVE. They are a claim about which sites in the REAL tree construct the arm, and a fixture standing in for that would be the empty-observation narrow. They stay refused. This takes interrupted_before_verdict from 11 to 3; that is NOT progress toward a green floor, it is eight witnesses moving from establishing nothing to establishing something. CI stays red and both escalated blockers stand unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ZHWK38CsX9K77f3HS7hMw * Remove the site census: it answered on no run, and the loss is typed rather than re-homed test.claim.fleet_converge_launch_scope_constructor_site_census is DELETED, and what it covered -- WHERE in the corpus a LaunchEnvironmentConverge request is constructed -- is REMOVED by this commit. Not preserved, not deferred, not covered elsewhere. Nothing executing stands over that question after this commit; review of the two named declarations is what is left. IT ESTABLISHED NOTHING AT THE GATE, ON ANY HEAD THAT CARRIED IT. Every required floor run over a head carrying the module reports its three identities as standing=planned-without-terminal-verdict / outcome=budget-refused-before-verdict -- runs 33452449083, 33459143928, 33464344273, 33469217185 and 33472882885, read from the runs rather than transcribed. A row preempted before verdict asserts neither pass nor fail, so the enrolled census informed the gate on no run while being counted as an enrolled witness: the inert shape DESIGN 4b names, which is worse than absence because it reads as coverage. BOTH REMEDY ARMS THE FLOOR ITSELF NAMES ARE CLOSED TO A CHANGE MADE HERE. "Reduce the cost" cannot hold while the subject is the corpus: this was the only whole-production-corpus decl_facts walk under dag/test/claim -- every other consumer there walks a fixture pool -- so the import-closure reduction that brought this PR's other eight probes inside the budget does not reach a walk whose n IS the corpus. "Move it to a lane declaring its own ceiling" is unreachable for a different reason, which is not this branch's to fix: the floor's selector tests changed-witness membership before the long-home decline, so the very edit that performs the move also selects the witness and refuses it on the budget the move exists to escape. The cost-debt roster is not a third arm: it is an operator shrink-only contract that admits no identity the floor did not already discover. NO SECTION 4b(3) ROW IS OWED, AND THAT WAS CHECKED RATHER THAN ASSUMED. A declared rung drop presupposes a rung that executed evidence established. These rows reached a verdict on no run of any head that carried them, on the evidence above, and the module never reached main. There is no rung to drop, so a drop row would be a claim about a guarantee this repository never had. RE-FILED AT node://adhoc-cfdf3366-bc3, and its trigger names capabilities rather than artifacts, both required before it can start: a floor selector able to route a CHANGED, cost-bound witness to a lane declaring its own ceiling; and a substrate-readable per-field label on record-literal children, sufficient for a witness to assert the exact field-name set of a named construction site. A patch to any one file satisfies neither. The annotation in gunbc.fleet_converge_plan that cited the census is rewritten in place to say what is now true -- that the site question is covered by nothing executing -- so the file does not carry a citation to a deleted module or an unearned bullet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aYqbapkp3MEjXYLDcXSc1 * Remedy 1, attempted rather than argued: the census reaches for construction spellings, not for every atom THE ARGUMENT THIS COMMIT WITHDRAWS IS MY OWN. The deletion commit said the census's cost was subject-shaped -- "n IS the corpus" -- and therefore irreducible. That is an argument about the WALK, and it was made without measuring what the witness REACHES FOR, which is the separable half and the one DESIGN section 6's bare-minimum-cost rule is written to catch. The census is restored and the reduction is applied. WHAT IT WAS REACHING FOR NEEDLESSLY. The per-declaration fold consumed skeleton_atom_lexeme_census_fold, whose AtomLexemeCensus materialises EVERY atom lexeme in a subtree and appends both lists at every step. This census reads only the construction-spelling members, so a single-spelling question over the production corpus paid for the corpus's entire atom population and for the list copies on top of it. v2.std.decl_facts_skeleton now also projects that same edge-provenance fact as a count: skeleton_construction_spelling_count_where carries one Int and no list, takes the "names my target" predicate as a parameter so no naming convention moves into std, and lives beside the list projection so the fact keeps one authority rather than gaining a second walker. THE SUBJECT IS UNCHANGED, AND THAT IS THE POINT. Same pool roots, same production corpus, same over-approximating authored-spelling grain, same three verdicts, same roster of two. A cheaper witness over a smaller subject would have been the empty-observation narrow; this is the same question asked without acquiring what it never reads. THE MEASUREMENT THAT DECIDES THIS IS THE FLOOR'S, NOT A LOCAL RUN. A local claim_batch probe established the closure half -- the module's imports resolve in ~2s and the closure-only control reaches a verdict at cpu=0ms, so closure acquisition is not what was refusing these rows -- but the walk half could not be measured in that frame: the probe host has 7 GiB and post-resolve RSS is already 5.4 GiB, so every whole-corpus run there was OOM-killed before a verdict. A number from a frame that cannot complete the work is not a cost. The required floor is the instrument with the right frame and the right accounting (marginal CPU, shared fill netted out), so this head IS the experiment: its disposition line for these three identities is the answer, either a terminal verdict or a second budget refusal. No figure is transcribed into the source for it. If the floor still refuses them, the deletion returns as a final disposition on measured evidence rather than on the withdrawn argument -- and the re-file's trigger is a lane with a dated ceiling that actually executes the exact enrolled witness identity and returns a candidate-bound terminal verdict. Also in this head, from review 58080: fleet_converge_plan_content_hash_path joins the explicit import list in gunbc.fleet_converge_plan_manifest. It is a DECLARED-dependency correspondence repair (DESIGN section 3), not a name-resolution floor fix -- imports do not bind here, the name is corpus-unique and pullable, and the use is a value reference rather than a match arm, so it resolved before this edit and the floor was never violated. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aYqbapkp3MEjXYLDcXSc1 * The reach reduction was measured and did not move the gate: the census deletion is final, on evidence REMEDY 1 WAS ATTEMPTED AND HAS FAILED, WHICH IS A RESULT RATHER THAN A SETBACK. d824d35 restored test.claim.fleet_converge_launch_scope_constructor_site_census with its reach cut -- one Int per node through skeleton_construction_spelling_count_where instead of materialising every atom lexeme of every declaration -- and with its subject deliberately untouched. The required floor's own run on that head refused all three rows again: standing=planned-without-terminal-verdict, outcome=budget-refused-before-verdict. NO BEFORE/AFTER COST IS CLAIMED HERE, AND THE FLOOR'S DIAGNOSTIC IS WHY. A preempted row reports interrupt_point, which that diagnostic states is a property of the BUDGET and not of the row; both shapes were refused with cost=UNMEASURED and above 500ms with no upper bound, so the honest comparison is of OUTCOMES, which are identical, and there is no measured figure on either side to compare. What the run did measure is memory: the census's worst row grew the run's resident set by 2.01GB, the largest single claim in the run, so this walk is not only over the CPU line. WHAT WAS TRIED AND WHAT REMAINS, so the record does not have to be reconstructed: closure acquisition was NOT the cause -- a probe carrying the census's exact imports resolves in about two seconds and its witness reaches a verdict at cpu=0ms, so the import-closure reduction that rescued this PR's eight excess-field probes does not apply to this witness. The walk could not be measured off-gate at all: the probe host has 7 GiB and post-resolve RSS is already 5.4 GiB there, so every whole-corpus run was OOM-killed before verdict, and a frame that cannot finish the work does not produce a cost. The floor's other remedy arm is a lane declaring its own ceiling, and none exists that EXECUTES anything; moving the source into a non-executing home is the bare de-enrollment the 2026-08-04 admission ruling forbids, deleting coverage while retaining the source. SO THE DELETION RETURNS AS FINAL, ON MEASURED EVIDENCE. The coverage -- WHERE in the corpus a LaunchEnvironmentConverge request is constructed -- is REMOVED. Not preserved, not deferred, not covered elsewhere; what stands over that question is review of the two named declarations and nothing executing. skeleton_construction_spelling_count_where GOES WITH IT. Its only consumer was the census, and a std projection with no consumer is dead weight that would also have owed a corpus-scale agreement check against the list projection it sits beside -- two projections of one edge-provenance fact are two authorities the moment they can disagree. Removing the consumer removes the obligation rather than deferring it; the fact keeps one projection and one authority. NO SECTION 4b(3) ROW IS OWED. A declared drop presupposes a rung that executed evidence established. These rows reached a verdict on no run of any head, in either shape, and the module never reached main. There is no rung to drop. RE-FILED AT node://adhoc-cfdf3366-bc3, and it cannot start until BOTH hold: a lane with a dated ceiling that actually executes the exact enrolled witness identity and returns a candidate-bound terminal verdict; and a substrate-readable per-field record-literal label sufficient for a witness to assert the exact field-name set of a named construction site. The first clause is deliberate: a lane that runs the row but reaches no verdict does not satisfy it, which is exactly the state this census has been in throughout. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aYqbapkp3MEjXYLDcXSc1 * The re-file trigger gains the memory term the experiment discovered, so it cannot be satisfied in form alone THE TRIGGER AS WRITTEN WAS SATISFIABLE WHILE THE CAPABILITY STAYED DEAD, which is the exact §4b(3) failure the rule about triggers naming capabilities exists to prevent. "A lane with a dated ceiling that actually executes the exact enrolled witness identity and returns a candidate-bound terminal verdict" can be met in FORM by standing up a lane with a generous dated CPU ceiling -- and that lane would then die the way the off-gate probe host died, leaving the trigger reading as fired while the witness still reached no verdict. The reduction experiment measured a constraint the earlier trigger did not imply: this census's worst row was the LARGEST single claim in its floor run by resident-set growth, in gigabytes, while its CPU stayed UNMEASURED and above the per-claim ceiling with no upper bound. Those are two different resources and only one of them was named. The clause now requires a candidate lane to afford the row's measured resource cost rather than merely a looser CPU number, and both facts are cited by naming the producing run rather than by transcribing figures into an annotation no Accepted program can read. This also makes the two halves of the evidence corroborate instead of merely coexist: the gigabyte-scale growth is why every off-gate probe was OOM-killed on a 7 GiB host before reaching a verdict, rather than two unrelated observations about the same witness. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aYqbapkp3MEjXYLDcXSc1 * Two defects I reported as fixed and had not verified: the dead projection is really gone, and the roster gains a uniqueness wall BOTH FOUND BY READERS, NOT BY THE GATE, AND ONE OF THEM CONTRADICTED MY OWN REPORT. 1. skeleton_construction_spelling_count_where AND ConstructionSpellingMatchCount WERE STILL IN THE TREE. The previous commit's message says the projection goes with the census and names that census as its only consumer; the source disagreed with it, which is worse than either state alone. The cause was mechanical and worth recording: the addition had been COMMITTED on the experiment head, so `git checkout -- <path>` restored HEAD's version -- the one carrying it -- rather than reverting the addition, and I reported the intent instead of reading the result. They are now actually deleted, leaving one projection of the construction-spelling fact and no unconsumed second implementation. This is exactly the residue the deletion existed to avoid: a dead projection introduced for a refuted experiment, with no consumer and therefore no possible agreement check -- and no green can see it, because an unconsumed projection cannot make a disagreement observable. 2. fleet_converge_persisted_member_paths CARRIED fleet_converge_plan_content_hash_path TWICE. Review 58080 asked for one addition, the missing import; the edit that made it also inserted the path into the roster below, where it was already present. A roster whose own annotation calls it "a closed map rather than a lookup nobody enumerates", joined at identity grain, cannot hold an identity twice and still be what it says it is. WHY EVERY WITNESS STAYED GREEN OVER IT, which is the part that needed fixing rather than the line. every_rostered_persisted_member_is_classified requires nonemptiness and that every occurrence classifies, so a duplicate of an ALREADY-CLASSIFIED path satisfies it BY CONSTRUCTION. The row passed for a reason unrelated to the property it names. So the duplicate is removed AND the hole is closed: no_persisted_member_path_is_rostered_twice asserts multiplicity PER IDENTITY over the real roster -- each member occurs exactly once -- with no length literal, because a count copied from the tree it checks collapses to measure() == measure(). Its mutation control, a_repeated_existing_identity_is_refused_by_the_uniqueness_wall, runs the same predicate over the real roster with one existing identity repeated, which is precisely the defect committed here, and requires it to refuse. Without that control the new green would be indistinguishable from a check that cannot see a duplicate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aYqbapkp3MEjXYLDcXSc1 * State the uniqueness wall's rung honestly: mechanically enforced, and the word "wall" was inviting the higher reading The new row asserts that every rostered persisted-member identity occurs exactly once, and nothing about that makes a duplicate UNWRITABLE. The roster is a List<String>: a repeated identity is still perfectly expressible, and safety depends on this row executing and staying enrolled, which is DESIGN 4b rung 2 and not rung 3 or 4. Naming a raw list "a closed map" does not elevate it -- a name is not a constructor -- and the annotation now says so at the point where a reader would otherwise infer more from the word "wall". The next-rung trigger is stated as the CAPABILITY rather than as an artifact: a keyed-roster carrier whose construction admits at most one member per identity, so the duplicate this row catches has nowhere to be written. It is deliberately NOT adopted here. Generalizing on a single site trades a proven local wall for an admission problem, and the bar for lifting the law into a shared carrier is at least two genuine closed-identity populations; this repository has one today. Ordered sequences, bags, retry histories and evidence transcripts may legitimately repeat a projected identity, so an all-distinct helper applied by list-shaped resemblance would strengthen their semantics incorrectly. Annotation only. No check, roster or predicate changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aYqbapkp3MEjXYLDcXSc1 * Delete the XL-0T cohort: its trigger had already fired when the previous merge imported it THE PREVIOUS MERGE PRESERVED SEVENTEEN ROWS WHOSE DECLARED LIFETIME HAD ENDED. The paragraph declaring them states their dissolve-on trigger as "#9907 merging". #9907 merged to main at 14:02:39. The merge commit that imported them was made at 14:12:13 -- ten minutes after the trigger fired -- and carried them across anyway. They are removed here, by their own trigger, which is the only thing that retires a row on this ledger. THE ROSTER IS LIFECYCLE-MANAGED, NOT APPEND-ONLY, and this file says so twice about earlier cohorts: rows are "removed by their own dissolve-on trigger, exactly as the seven shrinks above". Base and head both carry the XL-0T qualification now, so no run can produce those deltas, all seventeen report stale, and a stale row refuses EVERY unrelated PR in the repository. That is why deletion is owed on the roster's next touch rather than whenever convenient, and this merge is that touch. THE MISTAKE WAS ASKING THE TRIGGER QUESTION OF ONE SIDE ONLY. The check was run carefully against the rows being KEPT -- this PR's two, whose trigger is this PR merging, which has not fired -- and it was never run against the cohort being IMPORTED. Both resolution recipes this file has been given were therefore wrong for this merge: union-then-shrink because it shrinks only what the previous merge taught us to shrink, and plain-union because "preserve both sides" is not a safe default for a ledger with dissolution rules. The rule that survives, and it is now recorded in the file itself: the resolved roster is the old cohort, UNION newly live main cohorts, MINUS every cohort whose trigger has fired as of the base being merged -- asked of each side independently. NOTHING ELSE MOVES. The roster is this PR's two RLM-2b rows and nothing else; the other sixteen files in this PR are byte-identical to defb324f, verified per file by hashing each against that head rather than by reading the diff. `cargo check --release -p v1-compiler --lib` is clean on the resolved tree. ONE THING DELIBERATELY NOT DONE: no explanation is offered for why the consumed-at-base path did not refuse the previous head. I did not establish the mechanism and will not invent one -- a stale baseline is a plausible cause and plausibility is not evidence. The source-level lifecycle contradiction is sufficient to owe this deletion on its own, and if the mechanism turns up it belongs in its own finding rather than folded into this fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aYqbapkp3MEjXYLDcXSc1 * The Spark consumer's second apply-shell spelling had no caller to migrate, so it is deleted and the terminal identity is asserted on the route production actually takes (review 5070355515 P1-4) SUBSTITUTED REMEDY, NOT COMPLIANCE. The review asks for "an executing target proving the real consumer resolves and constructs the artifact through the same terminal authority", and its literal remedy is a caller for spark_serving_fleet_converge_apply_shell. This does not add one. That wrapper composed fleet_converge_apply_shell with fleet_converge_apply_terminal over one request -- exactly what fleet_converge_plan_artifact_of_request already does, computing the terminal once and handing that same value to the plan body line, to apply_terminal, and to the apply script. A second spelling of one composition is a second name for one fact (DESIGN section 3), and the two reconstructions could disagree about the terminal with nothing joining them. IT HAD NO CALLER TO MIGRATE. #9763 deleted the Spark-specific artifact route and with it this wrapper's only caller; the symbol has occurred exactly once in the tree -- its own declaration -- on every commit since, including origin/main today. Giving a dead wrapper a caller would have minted the parallel authority rather than removing it. The reviewer is free to reject this substitution; it should not be discovered. THE BLOCKER'S DESCRIPTION IS ONE HEAD STALE, stated rather than repaired around. P1-4 says the module's FleetConvergePlanArtifact literal still omits apply_terminal. That was accurate at the reviewed head c67b2d26e83; it is not accurate here, because the main merge brought in #9763, which deleted that literal and its whole assembler. The defect is real -- the orphan is what survives -- but its description no longer matches the tree. WHY THIS REPAIR BELONGS ON THIS BRANCH AND NOT IN A PREREQUISITE. The acceptance bar requires the apply shell bound to the SAME request and terminal authority. That seam exists only here: fleet_converge_plan_artifact_of_request is absent from origin/main. A prerequisite would have asserted a true property about a route that does not carry the seam under review. WHAT IS ASSERTED, on the consumer's own route. The witness module already builds the artifact through gunbc.fleet_converge_plan fleet_converge_plan_artifact with a populated Spark axis (slice_artifact / spark_serving_axis_for_observation), which routes through fleet_converge_plan_artifact_of_request. Two rows: the_spark_populated_artifact_names_its_own_terminal_in_body_and_script -- the plan body and the apply script both name fleet_converge_apply_terminal_line of the artifact's own apply_terminal. One terminal, two projections. a_terminal_no_observation_produced_is_named_by_neither_projection -- the control. A PartiallyApplied terminal nothing produced must be named by neither. Without it the positive passes on any artifact whose script contains some terminal line, which is precisely what a disagreeing second assembler emits. WHAT "EXECUTING TARGET" MEANS HERE, and it is weaker than it sounds. v2.workflow.required_floor required_gate_prefixes carries no spark, fleet or roadmap prefix. These witnesses reach the required floor through the changed-witness sublane, which seeds changed modules into the prepared closure. So they execute because the modules are changed in this PR, and after merge they are outside the required gate again -- the population of the declared rung drop gunbc.rung_drop required_gate_bankruptcy, not a new defect. The same qualifier applies to the P0-1, P0-2 and P0-3 evidence on this PR. No roster was widened to change that. Ten imports the deleted wrapper alone kept alive are removed with it; each was verified import-only before removal. No other path is touched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aYqbapkp3MEjXYLDcXSc1 --------- Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e to live (#9891) * Give the SCM object store two kinds of object, so a file has somewhere to live WHY: `gunbc scm add <file>` could not be built, and the blocker was the model rather than the wiring. `ObjectStore` held `List<ObjectRecord>` -- a NodeKind plus labelled children -- so a file's bytes had exactly one route in: intern them as an Atom's Symbol. That makes the symbol table a content store and gives one name two materially different meanings, which is DESIGN §3's meaning fork, and it is the same wrong model I already rejected for the srv2 mirror. WHAT: `ScmObject = SemanticNodeObject(ObjectRecord) | AuthoredSourceObject(AuthoredSourceRecord)`, under ONE content identity family, because a store holding two identity kinds would make `store_contains` ambiguous about what it contains. The authored identity is derived from the bytes by `content_hash_atom` under a family tag, so a caller supplies content and never an identity -- the same wall `store_node` and `insert_from_structure` already stand behind, reached by a third producer rather than a second identity authority. THE EMPTY FILE HAS ITS OWN CONSTRUCTOR. `content_hash_atom` takes a NonEmptyStr, and hashing "" through some other path would give the empty file an identity minted by a different rule than every other file. `AuthoredSourceContent = AuthoredSourceEmpty | AuthoredSourceText(NonEmptyStr)` makes the degenerate case unwritable rather than admitted and checked. REFUSALS ADDED, EACH BECAUSE A CONSUMER CAN NOW REACH A KIND IT CANNOT ACT ON. Every site that needs a node asks `find_node_record`, whose three arms separate "nothing is here" from "a file is here where a program was required" -- folding those into one absent arm would send an operator looking for bytes that are right there. - checkout: `ObjectIsAuthoredSource` - merge: `TargetRootIsAuthoredSource` - insert: `objects_equal` answers false ACROSS arms, so a file landing on a node's locator is a LocatorCollision rather than silently satisfying a checkout. THE CODEC IS A STRICT EXTENSION, NOT A FORMAT VERSION. A node entry keeps `{kind, children}` byte for byte, so every document the predecessor accepted still decodes identically; an authored entry is `{source}`. The two member sets are disjoint, which is what makes discriminating on shape a partition rather than a guess -- and an entry matching neither still refuses by member set. The decoder routes bytes through `store_authored_source`, so an image cannot assert an identity for a file any more than it can for a node. EXECUTED, five new claims enrolled in test.claim.scm.scm_object_store_collision_witness (bodies in the defining module, where sole_constructor lets the impostor fixture exist): identity follows the bytes AND differs for different bytes; storing one file twice stores it once; the empty file is an object and is not the same object as " "; a file under a node's locator refuses; and the control that the same file stores cleanly under its own derived identity. All five evaluate `true`, as does the migrated both_insertion_orders_produce_the_same_canonical_record and the checkout witness's lost-object arm. Not in this commit: the `add` and `commit` verbs. They now have somewhere to write, and they need the one modeled ScmWriteOutcome the plan names -- that is the next increment, not a wiring afterthought. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wy8wRfzTK2kvFzj9nAEbyT * Apply the authored-source tagging rule to both arms, and make the invariant executable Review 58068's nit was right and is worth more than a reply. `AuthoredSourceEmpty` hashed its tag directly through `content_hash_atom` while `AuthoredSourceText` went through `content_hash_tagged_structural`, so the tagging invariant stated in the comment directly above held for one arm and not the other -- an untagged digest sitting inside a family whose whole claim is that its members are tagged. A stated invariant with one exception is worse than no invariant, and the exception was the arm a reader is least likely to check. Both arms now apply the same combinator under different tags. The empty arm's payload is its own tag, which is degenerate deliberately: what the payload must NOT be is some file's content, because then an empty file and a file holding that content would collide BY CONSTRUCTION rather than by accident. Distinct tags keep the arms apart; the shared combinator is what makes "tagged" true of the family rather than of most of it. The invariant is now executed rather than asserted in prose: `no_authored_identity_is_an_untagged_digest` checks that neither arm's identity equals the bare atom digest of its own tag. It is discriminating -- against the revision this commit replaces it goes red, which is what separates it from a restatement of the comment. Green, along with re-runs of the two identity claims the change could have disturbed. This changes the empty file's identity. Nothing writes authored-source objects yet -- the format landed one commit ago and has no producer outside the witnesses -- so there is no document population to migrate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wy8wRfzTK2kvFzj9nAEbyT * Execute the authored-source checkout refusal instead of arguing it from the call graph Side-chat review cautioned that my reachability answer might not hold: authored objects decode, but a CHECKOUT might never be able to LAND on one, because every edge the encoder emits was derived from a node record. That caution is right about the encoder and wrong about the decoder, and the difference is worth executing rather than reasoning about -- a call-graph argument is not evidence that two halves meet. The decoder resolves {"target": {"at": "<n>"}} through the position map, and that map now holds authored entries alongside node entries. So a document may name a file as a node's CHILD -- an input no encoder would ever emit, and exactly the kind this repository does not control. The witness builds that document by hand: position 0 is the file, position 1 is a node whose only child targets position 0, root is position 1. Children-before-parents is the order the decoder requires, so the document is well-formed by the format's own rules -- it is not malformed, it is legitimate and says something the model must refuse to reconstruct. The document LOADS (which is what makes this a checkout claim rather than another decoder claim) and the checkout then refuses naming the authored source. It is red against a checkout reporting MissingObject for a file that is present -- the two have opposite remedies -- and red against one that reconstructs the node with the child dropped. Paired with the control that makes the claim about the FILE rather than the document shape: the identical shape with a node at position 0 checks out. Without it, a decoder refusing this shape for any reason would satisfy the claim. One fixture bug found and fixed while writing this: the parent node initially targeted position 1 (itself) rather than position 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wy8wRfzTK2kvFzj9nAEbyT * Refuse for the right owner: split MemberFailed, and stop reporting a present file as missing Three defects the side-chat review located, each now carrying an executed discriminating claim plus a control: - `root_edge_for` mapped EVERY `CheckoutRefused` to `TargetChildMissing`, so a merge role naming a present authored-source object was reported as absent. `MergeRefusal` gains `TargetChildIsAuthoredSource { role, identity }` and the arm is selected by matching the checkout cause. - `decode_object_step` treated every `MemberFailed` as source-absent, so `{source: null}`, a duplicated `source`, and a non-object subject were all reported as a missing `kind` or an unexpected member. Only `MemberReadMissing` now selects the node arm; the other three refuse in their own right. - Two comment blocks claimed the empty payload and the arm tags carry a property they do not. `content_hash_tagged_structural` already domain-separates the arms by outer tag, and separating preimages is not separating 64-bit digests. The prose now states the weaker true claim. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wy8wRfzTK2kvFzj9nAEbyT * Put every annotation above a subject, and ask the store for a NODE where a node is required Two things, both refusals the corpus already owns. PARSE. The floor refused this branch at the parse phase, not at a claim: gunbc.scm.commit_closure_json_v2 carried a nine-line rationale INSIDE decode_object_step's body, and test.claim.scm.scm_merge_witness_test ended with an eight-line block naming no subject at all. DESIGN 4c models annotations at module-item grain only, so both are unparseable rather than untidy -- and because parse refuses whole, the refusal took every SCM claim in the lane down with it, which is why nothing else in this slice was measured. The first block moves above decode_object_step. The second's subject is merge_refusal_tag: it explains why that function's TargetChildIsAuthoredSource arm has no claim in this file and where the claim that reaches it does live, so it sits above the enumeration it is about. THE ROOT ADMISSION. mint_repository_commit and gunbc.scm.log both asked store_contains, which is kind-agnostic by design -- it answers about the LOCATOR. So an authored-source object under a root locator minted a commit cleanly and then refused at checkout, and log reported it as `LoggedCommitRootHeld`: a row claiming a checkoutable root where checkout refuses. Both now ask find_node_record, whose three arms separate "nothing is here" from "a file is here"; the remedies differ (fetch the object vs. repair a wrong-kind reference), so they are separate arms rather than one Bool. LoggedCommitStanding grows LoggedCommitRootIsAuthoredSource and render says so in words. The claim pair is executed, not argued: a store holding exactly ONE object -- a file -- so RootMissing is unreachable and a kind-agnostic check mints; the refusal must carry the file's own identity back. Its control is one fact apart, a stored node in the same shape, and mints. Both PASS under claim_batch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9 * Brand a node reference so a file cannot inhabit a program position, and move the tag the schema outgrew TWO REVIEWS, ONE CUT. The designated SCM re-review's central finding was that ObjectId was ONE untyped locator standing in every position that requires a program -- StoredEdge.target, a commit's root -- so an authored-source identity inhabited those positions perfectly well and the model's answer was to notice afterwards: find_node_record at a mint, at a log row, at a checkout. That is validation standing exactly where construction was available (DESIGN 5). codex/gpt-5.6-sol, independently, refused the same PR for continuing to write the v2 closure format tag over a schema that grew a `source` member. Both are fixed here. THE REFERENCE TYPES. SemanticNodeObjectRef and AuthoredSourceObjectRef are sole_constructor over one ObjectId, so only the store mints one and no caller can brand a locator it merely holds; ScmObjectRef is the join for consumers that genuinely handle both. store_node hands back the node brand, store_authored_source the source brand -- two outcome types, because one shared outcome would force store_node's caller to match an arm store_node cannot produce. The admission rule is NOT duplicated: both route through insert_admission. A CHILD EDGE HAS TWO HONEST STATES, and one brand cannot carry both. A partial closure deliberately references objects it does not carry, so a decoder holds edges whose kind nothing has established. Branding those anyway would need a mint that records a CLAIM rather than evidence, and the only place to put it is an exported function -- the importable forge factory this module already recorded once. So NodeTarget names the state: NodeTargetResolved(SemanticNodeObjectRef), or NodeTargetUnresolved(ObjectId). Neither arm reads as the other. WHAT THE BRAND DOES NOT CLAIM: that the object is a node in THIS store. It does not carry which store, so checkout's two refusal arms remain as the honest boundary between stores. What it removes is the authoring mistake. CONSEQUENCES THE REVIEW DERIVED, each now structural rather than checked: closure completeness resolves a child edge instead of asking the kind-agnostic store_contains, so a file no longer counts as a contained child; mint_repository_commit takes a NodeTarget and stores the reference THE STORE handed back, so a RepositoryCommit's root cannot name a file; the decoder's position map carries ScmObjectRef, so a contained edge target or root resolving to an authored entry refuses at decode, owned by the layer reading the document, instead of building the store and failing at checkout. EVIDENCE MOVED WITH THE WALL rather than being deleted with it (DESIGN 4b(4)). The child-edge claim was a CHECKOUT claim; the same document now refuses at decode, so it asserts ObjectTableEdgeTargetIsAuthoredSource -- red against a decoder that resolves without discriminating, and equally red against one reporting the not-present cause for an entry that is right there. A root naming a file has its own claim and cause. merge's TargetChildIsAuthoredSource is still reachable, through an UNRESOLVED target, and its claim moves to the merge witness where insert_from_structure can build that store; its node control moves with it. THE TAGS. This module's own policy is that a schema growing a member grows a new format tag, so the closure document is v3 and the repository envelope v2 -- the latter for two reasons, the embedded table's new entry shape and root resolution at decode. The identity rule is a separate axis and it moved too: v1 named one derivation, and an authored source's identity comes from a different one. ONE tag each, not two: no durable v2 document exists, the only producer is a witness rebuilt every run, and a compatibility arm over an empty population is machinery with no consumer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9 * Compare records by what the identity is derived from, and count the durable documents before claiming there are none Four repairs, each found by executing rather than by reading. RECORDS_EQUAL WAS COMPARING THE TARGET'S ARM. It was `left.children == right.children` -- the substrate's structural equality -- which was exactly right while a StoredEdge held a bare locator, and stopped being right the moment the target became a NodeTarget. `NodeTargetResolved(r)` and `NodeTargetUnresolved(l)` over the SAME locator are different values structurally and the same object by content, because content_hash_of_children consumes only the locator. So the store reported "different record" for two records its own identity rule cannot tell apart, and insert_admission turned that into a LocatorCollision: a FABRICATED refusal, raised at a store being asked to do something ordinary. merge reaches it directly -- it reassembles a root from resolved children and re-inserts over a record whose children were unresolved. It was caught by a CONTROL, not by review. The file arm of the merge role claim passed and its node control failed, which is the whole reason a claim is paired with one. The comparison is now over label and LOCATOR, through the same labeled_children_of_stored the identity fold consumes, so there is one definition of what makes two records the same rather than two that agree until one is edited. THE DURABLE POPULATION WAS ASSUMED EMPTY AND IS NOT. The tag-bump note said no document exists to be compatible with, because the only producer is a witness rebuilt every run. True of the closure format; FALSE of the repository envelope, which has checked-in fixtures under dag/test/fixture/scm_repository_load carrying the v1 tags -- six claims went red on them. They are versioned test inputs, updated with the schema they exercise, and the note now says the population was counted rather than assumed. AN ENCODE FIXTURE BUILT ITS SUBJECT THROUGH A ROUTE THAT IS NOW CLOSED. scm_env_absent_root_repository rewrote an encoded root to an uncontained digest and decoded it back; decode now resolves a commit root against the object table and refuses. The state is still reachable and the fixture now reaches it honestly: mint against a store that HOLDS the root, then rebuild the envelope around a store that does not -- which is exactly the residual a SemanticNodeObjectRef leaves open, since it says its object was a node in SOME store and never in THIS one. The document route became its own decode-refusal claim, with a control one fact apart. A DUPLICATE IMPORT, named by review 58166 and real: ObjectTableEdgeTargetIsAuthoredSource was bound twice in one block, which I introduced by inserting it in two separate edits. Deleted. The review's other half -- that ingest may refuse on it -- is not the case: this module resolved cleanly throughout, and three duplicate-import blocks already stand on main from #9763, #9850 and #9669. The DESIGN section 2 objection is the one that holds. Instrument: claim_batch over every dag/test/claim/scm/*_witness_test.dag with each file's own `test fn` roster. 299 pass, 0 fail, exit 0 -- including all eight claims this branch added or moved. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9 * Name the identity rule for its subject, and freeze a predecessor document to prove the version refuses as a protocol gap Two items from the designated SCM reviewer's bar that the tag bump left half-done. THE RULE'S NAME SAID `node` WHILE ITS TABLE HELD NON-NODE IDENTITIES. `repository_node_identity_rule_tag` is the one authority in the document whose whole job is to say WHICH derivation reconstructed these identities, and it asserted something false about its own subject the moment the object table grew an authored-source arm -- a meaning fork in the worst possible place. It is now `repository_object_identity_rule_tag`, and its value names the rule rather than one branch's hash function: `scm-object-identity-v1`, a total law over the object coproduct whose semantic-node branch delegates to v2.std.node content_hash. A manifest branch joins this tag when it lands rather than minting a third. THE OLD-WRITER EVIDENCE, RESHAPED BY THE VERSIONING RATHER THAN DROPPED. The re-review asked for a frozen predecessor document -- bytes not produced by the current encoder -- to prove old documents still read. Bumping the tag changed what that evidence has to show: a v2 document is now a version this build does not implement, so the required behaviour is a REFUSAL. Specifically the unsupported-protocol one and never malformed, because those have opposite remedies and malformed is the ONE standing that permits supersession (gunbc.scm.supersession standing_permission_refusal) -- a reader that filed a predecessor document as damaged could discard a generation it merely cannot read. So three claims, not one: the cause is the format's own, the STANDING is the protocol gap, and the identical document under this build's tag loads. The document is a hand-authored literal precisely so nothing in the file can quietly re-derive it from the encoder under test, which is what made the existing round trips unable to answer this question. Instrument: claim_batch over every dag/test/claim/scm/*_witness_test.dag with each file's own `test fn` roster. 302 pass, 0 fail, exit 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9 * The module names the codec and the tag names the version: retire a suffix that had started to fork meaning `gunbc.scm.commit_closure_json_v2` announced v2 in its name while emitting `gunbc-scm-commit-closure-v3` in its bytes. A reader has no way to decide which of the two is the contract, and that is the meaning fork DESIGN section 3 forbids sitting in the one authority whose whole job is to say which schema this build writes. The suffix was not wrong when it was authored: it named the second CODEC GENERATION, distinguishing this module from the predecessor that encoded the root positionally. That predecessor is deleted, so the suffix had already stopped distinguishing anything, and bumping the format tag turned a harmless fossil into an active contradiction. The fix is the ordinary one -- one fact per authority: the module names the codec, the tag names the version, and the version lives in a value the encoder actually emits rather than in a spelling in a path. Renamed with it: the module, its file, the witness module and file, and every prose citation across the corpus. A citation naming a symbol that no longer exists is the positional-citation failure by another route -- it decays silently and nothing refuses. Found by the designated SCM reviewer while tracing whether retired version names still fork meaning after the tag bump. They did. Instrument: claim_batch over every dag/test/claim/scm/*_witness_test.dag with each file's own `test fn` roster. 302 pass, 0 fail, exit 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9 * Restore the rung-drop row an auto-merge deleted, and revert a module rename whose admission wave every other lane would have paid for Three CI findings, and the first is the one that matters. A DECLARED SAFETY REGRESSION DISAPPEARED IN A MERGE, WITH NO CONFLICT. `direct_call_arg_seam_v2_exemption` landed on main today: a §4b(3) row declaring that one of the two direct-call argument judgments is switched off for every `v2.*` module. Taking main into this branch deleted it -- the declaration and its roster entry -- and git reported a clean auto-merge. That is exactly the class main filed this morning as `merge_region_excludes_shared_tail`, whose own text names `gunbc.rung_drop` as the second carrier with the vulnerable shape: multi-line rows sharing a `}` tail, so the three-way merge factors the tail out of the conflict region and the region's natural resolution drops what lived in it. I resolved the file git DID conflict on and never looked at the one it resolved for me. The recognition rule that row states is about the shared suffix; the operational half I learned here is that the same carrier class also produces SILENT deletions on the files that never conflict at all. `gunbc.rung_drop` and `docs/design-ledgers.md` are restored to main's content, and the ledger is REGENERATED from the authorities rather than hand-restored, so what is committed is what the projection derives. THE MODULE RENAME IS REVERTED, AND THE REASON IS AN EXTERNALITY. `commit_closure_json_v2` -> `commit_closure_json` was right on its merits: after the tag bump the module announced v2 in its name and v3 in its bytes. It is reverted because of what it costs everyone else. A rename re-targets every binding naming it, which the required namespace-wave-admission phase classifies `TargetChanged` -- not auto-admitted, one roster row each, 41 here. That roster's own header records the receipt: `stale_admissions` is computed PER RUN, so rows sitting on main are inherited by every open pull request, and a change touching no namespace can never match them; the phase then refuses every unrelated lane until they are dissolved. Spending every other lane's CI to fix one module's name is the externalized cost DESIGN §5 names, and doing it inside a pull request about the object model compounds it. So the fork is REPORTED where a reader meets it: the module header now says which of the two versions is the contract (the emitted tag), that the suffix is a dead codec-generation fossil, what the rename costs, and a dissolve-on naming the shape that can pay it -- its own change, rows authored and deleted in one wave, at a moment the open-PR population can absorb. A comment does not make the name honest and this one does not claim to; it stops the two from looking equally authoritative. AND ONE REAL DEFECT THE FLOOR CAUGHT THAT LOCAL RUNS DID NOT. `NewUnresolvedness binding gunbc.scm.log::CommitLogRow ObjectId` -- the row still named `ObjectId` after that import was dropped, resolving locally through bare-reference closure while the namespace wall measured it against the base and saw a name that had lost its declarer. The field is now `SemanticNodeObjectRef`, which is what it actually holds: `RepositoryCommit.root` is branded, so the log row carries the reference rather than a bare address. Instrument: claim_batch over every dag/test/claim/scm/*_witness_test.dag with each file's own `test fn` roster. 302 pass, 0 fail, exit 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9 * Execute the authored-source wire round trip in both arms The branded-reference cut gave AuthoredSourceContent two arms and gave the codec two encode branches, but nothing executed the claim that the arms survive the wire and stay distinct. A codec branch with no discriminating consumer is specification-without-execution (DESIGN.md §5). Two claims now encode a closure holding an empty and a non-empty authored source, decode it, and read the content back off the rebuilt store: one asserts each arm round-trips, the other asserts the two do not collapse into each other. Proven discriminating rather than decorative by mutation: collapsing the text branch of encode_authored_source_content to json_string(s: "") takes both claims red (PASS 0 FAIL 2, exit 1); the codec was restored byte-clean afterwards. Full SCM sweep: 304 pass, 0 fail. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9 * Make a node requirement a proposition, and let only the store answer it The branded-reference recut left StoredEdge.target a two-armed sum, Resolved beside Unresolved, on the reasoning that a partial closure must reference an object it does not carry. The premise was right and the carrier was wrong, and the designated SCM reviewer is what made the difference visible. A child edge states a PROPOSITION -- a semantic node is required at locator L. Whether a store satisfies it is an OBSERVATION, and observations do not belong inside an object whose identity is derived from its content. The four facts offered as evidence the resolved arm was harmless are the proof it never belonged: the hash ignores the arm, records_equal must ignore it, the wire has no spelling for it, and every consumer re-resolves it anyway. Carrying it there was not merely redundant. store_contains_node_target answered Bool, and "false" meant BOTH "nothing is here" and "a file is here" -- states whose remedies are incompatible, since no fetch repairs an occupied locator. Completeness put wrong-kind locators into the fetchable population, and the encoder's position index was over OBJECTS, so it found the file and emitted the CONTAINED spelling. An admitted partial closure produced bytes this same build refuses at decode. - SemanticNodeTarget { locator } is canonical requirement content, plain and publicly constructible, because stating a requirement forges nothing. The sum is deleted, not deprecated: keeping both would leave a second authoring route that bypasses the census. - resolve_node_target projects it into find_node_record, which ALREADY carries the three arms. No peer NodeTargetResolution type is minted; one concept keeps one authority. - The Bool is deleted rather than kept beside the fix. - ClosureCensus separates absent from source_occupied in one traversal and keeps them apart all the way up. unresolved_identities projects absent only; closure_is_complete requires both empty; admission grew ClosureRequiresNodeAtAuthoredSource naming the whole population, so a caller repairing one does not discover the rest one round at a time. - The encoder now carries TWO indexes. Targets resolve against a node-only map, so a contained reference to a file has no entry to find. This is the part that makes the class unwritable rather than checked: .dag has no module privacy, so guarding the two intended entry points would leave the bad publication representable through the importable raw encoder. Red first, and the claim FORBIDS the state rather than describing the emit-then-refuse contradiction, which would have been green on the defect. Three cells one fact apart: source-occupied FAILED while both controls passed; all three pass now. Mutation: routing NodeRecordIsAuthoredSource back into absent takes exactly one claim red. The two authored-source claims now run through serialize_json and parse_json_document, reading content only from the reparsed value. The reviewer proposed a newline payload; mutating the newline escape left both GREEN, because this parser tolerates a raw newline in a string -- the durable leg would have looked rigorous and discriminated nothing. With a quote-bearing payload, mutating the cp==34 escape takes both red. An unreadable parse answers Absent rather than a manufactured ClosureDocRefusal, which would report a decoder verdict for something the decoder never saw. SCM suite 307 pass, 0 fail. Still open on the reviewer's bar: records_equal compares labeled children in supplied order while content_hash_of_children canonicalizes, so the permutation collision is LIVE; parent-first/child-later graft ordering; repository-v1 names under the v2 tag; a frozen v1 document refusing as unsupported protocol. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9 * Canonicalize on the other route too, and stop fabricating a collision object_store already records why the stored record must be the canonical one: content_hash_of_children SORTS named edges, so keying on the canonical identity while persisting the authored order stores an arbitrary first-observed representative of an equivalence class. store_node does that. insert_from_ structure, the other public way to make a record, did not -- it derived the locator from the canonicalized children and then persisted the children AS SUPPLIED. The consequence was a FABRICATED REFUSAL, which is why it survived: writing [alpha, beta] and then [beta, alpha] derives ONE locator, so the second write found the first record, records_equal compared labeled children in supplied order, they differed, and insert_admission answered LocatorCollision. Nothing had collided. The store refused a write it should have accepted, and every existing claim stayed green because none of them wrote one object two ways. Found by external review reading the identity rule against the persistence, then executed here rather than argued: re_writing_the_same_bindings_in_another_order_is_idempotent FAIL -> PASS swapping_which_target_each_label_names_is_a_different_object PASS throughout The control is the half that matters: without it the claim is satisfied by collapsing genuinely different label-to-target associations into one object, which would trade a fabricated refusal for a fabricated equality. The canonicalization reuses std's canonicalize_labeled_for_content_hash rather than sorting locally. The ordering rule belongs to the identity authority, and a second sort in this module would be that rule written twice, free to drift from the one the hash actually uses. SCM suite 309 pass, 0 fail. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9 * Derive dependency order instead of trusting insertion order store_records_in_dependency_order returned store.objects, and the note beside it argued that insertion order IS dependency order because store_node recurses into a target before appending its parent. True for store_node, false for the graft route: copy_object appends one record without its children, so copying a parent and then its child leaves the parent EARLIER than the object it references. Completeness never noticed -- both objects are present. The encoder did. It assigns positions in this order, so it reached the parent before the child had one and spelled the target UNCONTAINED while the document carried that very object a few entries later. That contradicts the declared wire meaning of the arm and let a complete closure emit a partial spelling. A post-order walk emits every referenced record before its referent, for any insertion history. The store is acyclic by construction -- a locator is derived from content that already includes its children's locators, so a cycle would have to hash itself -- which is what makes the walk terminate without a separate descent argument. THE FIRST VERSION OF THE CLAIM WAS NOT DISCRIMINATING, and the comment says so because the failure mode is instructive. Encoding, round-tripping through text and requiring the reloaded closure to be complete PASSES ON THE DEFECT: decode re-stores every object in the table, so the child arrives whichever spelling its parent used. The defect is in the BYTES, and an assertion that reads only the rebuilt store cannot see it. The claim now compares IMAGES. The grafted-then-completed store and the naturally-built store are the same closure, so they must serialize to the same document; under the insertion-order encoder they did not. one_closure_has_one_image_however_its_store_was_assembled FAIL -> PASS a_genuinely_partial_store_does_not_encode_as_the_complete_one PASS throughout The control is load-bearing: without it the claim is satisfied by an encoder that never spells a target uncontained at all, which would delete the partial-closure format this branch exists to add. ON COST, since DESIGN section 6 refuses "n is small here": emitted_holds is a linear scan, so the walk is quadratic in object count. That is the same shape find_object already has -- the carrier is a List -- and is bounded by the same dissolve-on already recorded for that carrier. A keyed collection retires both at once; fixing it here alone would leave the store's own lookup quadratic while adding a second index to maintain. SCM suite 311 pass, 0 fail. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9 * Delete the two node-presence predicates the census replaced Review 58294 found store_contains_node has no caller anywhere in the tree. That is correct and it is mine: this PR added it, it is absent from main, and the census split then removed the need for it without removing it. The same deletion catches a second one the finding did not name. store_holds_node_at's ONLY caller was store_contains_node, so with that gone it is dead too. Deleting just the reported symbol would have left a dead function whose sole consumer had been deleted in the same commit -- fixing the finding rather than the fact. Both are superseded by resolve_node_target, which answers the same question three ways instead of two. That is the whole point of the census: a Bool at a node position cannot separate "nothing is here" from "a file is here", so nothing should be asking for one. I do not accept the rule the finding cites. There is no corpus-wide prohibition on Boolean projections over a coproduct in DESIGN.md -- the same claim was made in review 58182 and answered there with a census of 2,523 such functions across 908 files. What makes these two deletable is that they are UNUSED, which is DESIGN section 2: unnecessary work is redundant work, and dead API surface on a sole_constructor carrier is surface a later caller can reach for. SCM suite 311 pass, 0 fail. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9 * Name the repository codec for the tag it implements, and freeze a real v1 document TWO ITEMS FROM THE SCM REVIEWER'S BAR, and they are the same seam from opposite sides: what this build CALLS its repository format, and what it DOES with the format it replaced. repository_envelope_format_tag has read "gunbc-scm-repository-v2" since the protocol cut, while the dispatch arm was RepositoryFormatV1 and the codec functions were encode_repository_v1 and decode_repository_v1. That is the meaning fork DESIGN section 3 names: one spelling over two materially different contracts, with the wrong one standing where a reader looks first. Nothing about the schema changes here -- the names were lying about which schema they were. The frozen-predecessor claim needed a REAL document. The existing unknown-format claim uses "gunbc-scm-repository-v99", a version that never existed: it proves the dispatch refuses a tag it does not know, which is a different question from whether this build handles the format it actually superseded. This branch moved the tag from -v1 to -v2, so every repository written before it is a real artifact a real caller can hand to this build today. The fixture is main's v1 document verbatim, carrying both v1 axes -- the schema tag and the fnv1a64-structural-canonical-v1 identity rule this branch also replaced. THE ASSERTION IS THE STANDING, NOT THE REFUSAL. Refusing is easy and the wrong refusal is worse than none: "these bytes are damaged" sends a reader hunting corruption in a perfectly intact file, while "I do not speak this protocol" names a version gap with a real remedy. A refusal-only assertion cannot separate them, because both arms refuse. Mutation receipt -- mapping RepositoryFormatUnrecognized to LoadDocumentMalformed: scm_rl_a_frozen_v1_document_is_a_protocol_gap_not_damage FAIL scm_rl_a_damaged_document_is_not_a_protocol_gap PASS (control held) The control is what stops a classifier that answers UnsupportedProtocol for everything from satisfying the claim. SCM suite 313 pass, 0 fail. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9 * Read both census populations everywhere, not just the absent one The SCM reviewer re-anchored at 837115a and found the previous fix had MOVED the leak rather than closed it. They are right on all three counts. Splitting the census into absent and source_occupied is only worth what its consumers read. Three of them read the absent half alone: - encode_repository_checked guarded on an_uncontained_target and NOTHING else, so a requirement whose locator is occupied by an authored source walked through and got PUBLISHED. It now refuses first, with its own cause: RepositoryEncodeTargetIsAuthoredSource. The two refusals stay separate because their remedies are -- one says fetch an object that is not here, the other says something IS here and is the wrong kind, which no fetch repairs. - loaded_closure_standing answered LoadPartial for every not-complete closure. LoadPartial promises "fetch the named identities and this completes", which is FALSE for an occupied locator. LoadStanding grows LoadRequiresNodeAtAuthoredSource, and standing_loaded answers false for it: the document read fine, but what it asserts is unsatisfiable. - The raw encoder spells a source-occupied target uncontained, which is indistinguishable from absent on the wire. Both callers now refuse before reaching it. THE THIRD FINDING IS THE ONE I MOST NEEDED. My byte-equality claim -- one_closure_has_one_image_however_its_store_was_assembled -- catches a DISAGREEMENT between assembly routes but not a wrong spelling both routes share. Proven, not conceded: under an encoder that spells EVERY target uncontained, one_closure_has_one_image_however_its_store_was_assembled PASS a_complete_closure_spells_its_child_contained FAIL a_partial_closure_spells_its_missing_child_uncontained PASS Agreement is consistency, not correctness. The new claims read the arm out of the document through decode_target_reference -- the module's own reader, not a second parser written to grade the first. Item 3 also lacked its positional control. Canonicalizing named children is satisfied by sorting every child list unconditionally, which is wrong under Cardinality and Instantiation where PositionalEdges makes position the meaning. The canonicalizer already dispatches correctly on kind_edge_discipline; nothing executed it, so the rule was true and unwitnessed -- and an unwitnessed rule is one a later local sort can quietly replace. SCM suite 317 pass, 0 fail. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9 * Seal the raw closure encoder behind a carrier the bad state cannot inhabit The SCM reviewer's exact-head ruling on 837115a listed five required closures. 54cd2e4 had already landed three of them (the repository source-occupied refusal, LoadRequiresNodeAtAuthoredSource, and the contained-spelling witness with the mutation receipt they asked for). These are the remaining two, plus the part of the first that their reading was right about and mine was not. encode_complete_closure_document asked unresolved_identities, which projects the ABSENT population. For a requirement whose locator is occupied by an authored source that population is EMPTY -- so the FRONT DOOR returned bytes for the identical state partial admission refused at the side door. My previous claim that "both entry points are closed" was true of admission and false of this one. THE REPAIR IS A CARRIER, NOT A THIRD CHECK. Writing the judgment at each entry point is what produced this bug twice: `.dag` has no module privacy, so encode_closure_document stayed importable and the wrong-kind state stayed representable through it no matter how many front doors were guarded. It now takes a WellKindedClosure -- sole_constructor, mintable only through admit_well_kinded_closure, which runs the census. There is no argument a caller can build that carries the state, so the raw writer needs no claim of its own. An ABSENT population is fine in that carrier and is the partial-closure case the format exists for: it seals KIND, not completeness. encode_admitted_partial_closure_document re-seals rather than re-judges, because admit_partial_commit_closure already refused source_occupied before minting. Also: unresolved_commit_root_step asked position_of, which is the GENERIC object index, so a commit root pointing at a file found a position and passed. It asks node_position_of now. Repository roots are established against node positions, not object occupancy. The seal is not decorative -- it broke three fixture call sites that had been handing the raw encoder a store and a root, which is exactly the shape a production caller could have used. the_complete_writer_refuses_a_wrong_kind_closure_and_names_it the_complete_writer_still_encodes_a_complete_closure (control) SCM suite 319 pass, 0 fail. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9 * Separate a wrong-kind commit root from an absent one, and execute the new standing Four blockers from the SCM reviewer's exact-head ruling on 9bdd976. 1. COMMIT ROOT. Asking node_position_of was the right first question, but every node-index miss became RepositoryCommitRootNotInStore -- reporting ABSENCE for an object that is present and wrong-kind, the same conflation one layer later. commit_root_standing now answers three ways and the encoder grew RepositoryEncodeCommitRootIsAuthoredSource. HONESTY NOTE ON ITS EVIDENCE, because the arm ships without a discriminating RED and that must not pass silently. I could not author one. RepositoryCommit.root is a SemanticNodeObjectRef, sole_constructor, obtainable only by storing a node; reaching this arm needs a store where that same LOCATOR holds a file, which is a 64-bit digest collision. No fixture can construct it without the forge factory this module refuses to export. Per DESIGN section 4b the arm is therefore mitigatable-with-unauthorable-RED, and its next-rung trigger is a fixture capability that can seed a store at a chosen locator. I am not claiming coverage for it. 2. THE NEW STANDING IS NOW EXECUTED. LoadRequiresNodeAtAuthoredSource had an implementation, exhaustive plumbing, and no evidence any external document reaches it rather than LoadPartial -- a typed carrier and a tag are not a consumer. The document is authored BY HAND because the encoders now refuse to produce it, which is exactly section 4b's case for authoring a RED as source handed to the decoder. scm_image_a_requirement_over_a_carried_file_is_not_partial scm_image_a_requirement_over_nothing_is_partial_and_loaded (control) Mutation, the one the reviewer named: routing source_occupied back through the absent branch takes the first red and leaves the control green. 3. THE MANIFEST TRANSITION RULE was wrong. It said a manifest branch "joins this same tag; it does not mint a third one". The first half is right, the second does not follow: joining one authority is the section 3 point, but adding an arm changes the authority's TOTAL LAW, and this tag names the law. The tag is not pre-bumped for a branch that does not exist; the rule is corrected so the next author does not read it as permission to grow the law under a fixed name. 4. TWO SOURCE COMMENTS ASSERTED THINGS THAT ARE FALSE. object_store said "every node-requiring position takes the node brand; a file in a child edge is then not refused, it is UNWRITABLE." That was true of a design this module no longer has. It is the worst kind of stale comment -- one asserting a SAFETY property, which a reader plans against -- and an external review restated it back as the module's guarantee, which is how a false comment becomes a false shared premise. The honest statement is rung 3, not rung 4: the state is writable and every consumer refuses it by name. Three real defects were found in this seam AFTER the brands landed; had it been unwritable, none could have existed. commit_closure_json_v2 still called the schema addition "a strict extension, not a format version". The module's own policy refutes it -- a schema that grows a member grows a new format tag -- and this codec emits v3 and refuses the frozen predecessor outright. SCM suite 321 pass, 0 fail. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9 * Make the commit-root fold exhaustive instead of rostering its wildcard CI red on 103f2d5, reproduced locally rather than assumed flaky: nfr_roster_receipt: unrostered=1 stale=0 live=187 unrostered: ["gunbc.scm.repository_envelope::unresolved_commit_root_step"] The new fold carried `_ => found` over CommitRootStanding, a closed coproduct. The roster exists to make exactly that visible, and the lens caught it on the first commit that introduced one. There are two ways to satisfy it and only one is right. Rostering the wildcard would record a residue that does not need to exist; the arms are enumerable, so the fold is written out. That also restores what the coproduct is for: a fourth standing would now break this match rather than being silently absorbed by the wildcard into "keep whatever we had" -- which, for a fold whose whole job is to report the FIRST bad root, would have quietly kept reporting the wrong one. nfr_roster_receipt 1 passed SCM suite 321 pass, 0 fail Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9 * Carry the standing to its destination, scope the image claim, sweep the stale text Three of the four items from the SCM reviewer's ruling on 103f2d5. The fourth is terminal exact-head CI, which follows this push. They accepted the unwitnessed commit-root arm's disposition: keep it, export no forge, claim no coverage. 1. THE NEW STANDING NOW REACHES AN ACTUATOR. The hand-authored document proved LoadRequiresNodeAtAuthoredSource is produced; nothing proved it CHANGED anything. A standing no actuator consumes is a value with a name and no consequence, and the exhaustive arm in supersession was plumbing, not execution. The claim now drives the standing THAT DOCUMENT ACTUALLY PRODUCES -- not a literal handed to the actuator -- through observe_generation into supersede_head_slot with every other condition admitting, so the only thing that can refuse is the standing, and requires exactly StandingDoesNotPermit { standing: LoadRequiresNodeAtAuthoredSource }. The third one-fact cell was also missing: a SEMANTIC NODE carried at that locator must resolve the SAME uncontained spelling to LoadComplete. Without it the pair is about presence rather than kind, and satisfiable by a decoder that treats every uncontained target as unresolved -- which would make LoadComplete unreachable through that spelling. 2. THE IMAGE CLAIM IS RENAMED to a_completed_parent_first_graft_has_the_naturally_ built_image. The old name claimed GLOBAL image invariance across every assembly history -- more than the fixture builds and more than the repository authority grants, since insertion history still decides tie order among INDEPENDENT components. A claim whose name promises more than its body executes is the same inflation as a rung reported above its evidence. 3. THE STALE AUTHORITY TEXT IS ONE SWEEP, not another round of single-paragraph repairs, because that is how the previous two got missed: - repository_envelope said emission order is INSERTION order. It is a derived post-order walk; what survives is the narrower true claim that insertion history decides TIE ORDER among independent components. - "THIS FABRICATES NO v2" sat above an encoder emitting v2. The number moved and the prose did not; it is the v3 statement now. - The repository encode header and its witness header both said "the three causes" / "one claim per cause". The coproduct is larger and one cause is deliberately unwitnessed, so both now say which one and why. - commit_closure_json_v2 credited the index split alone with closing the raw route. It never did -- the carrier does. Both facts are needed and the note now says which does what. - "A missing position is UNREACHABLE" was true only while every closure was complete. For an ADMITTED PARTIAL closure a missing node position IS the uncontained arm, and reading it as unreachable would invite an author to delete the format's reason for existing. - supersession's producer census said loaded_closure_standing answers "only LoadComplete or LoadPartial", and that repository_envelope classifies NOTHING into LoadStanding. Both premises are obsolete; a stale count is exactly what corrupts a census. The conclusion is unchanged and broader. SCM suite 323 pass, 0 fail. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9 * State the populations that exist, not the ones these notes were written against The reviewer's hold on 689a2b5 was text-only but authority-bearing: three source notes described populations their own subjects had outgrown, and one asserted a rule in the present tense directly above its retraction. The supersession census is recomputed from the live tree rather than patched. It said "PRODUCERS, TOTAL: two" beside "the producer count grew"; both cannot hold at the declared denominator. There are five, across three modules. Recounting surfaced a second, load-bearing falsehood the review did not name. The census also claimed closure_document_load_standing was the ONLY declaration able to return LoadDocumentMalformed, and concluded that the producer count grew while the permitting surface did not. LoadDocumentMalformed is the sole permitting standing, and repository_envelope_load_standing returns it for its member-read and target-reference modes while object_table_load_standing returns it for six of its seven causes. The permitting surface grew with the producer count. Recording the growth of one while denying the other is exactly the stale count this census exists to catch, so the conclusion is replaced rather than the number. What survives is stated narrowly: the success path stays outside the relation by construction, because none of loaded_closure_standing's three arms permits. RepositoryEncodeRefusal's note described "the single producible cause" and "the two sibling arms" above an eight-arm coproduct, and the checked encoder's note named three ways a repository can be unrepresentable above the same eight. Both now enumerate what exists, grouped by remedy, keeping the domain rule that was each paragraph's actual content. The witness header opened by asserting one claim per cause and retracted it two lines later. The heading now carries the exception it always had. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9 * Exclude witness helpers from the census denominator, by name The recount stated its denominator as "declarations whose return type is LoadStanding". That is too wide: witness modules declare their own, and test.claim.scm_commit_closure_json_v2_witness scm_image_standing_of is one. A fixture helper is not a producer this destination can be reached from, so the denominator is now production classifiers under gunbc.scm.*, and the witness helper is excluded by name rather than by being overlooked. The withdrawn claim -- that the producer count grew while the permitting surface did not -- is removed rather than qualified. The account states positively what holds: LoadDocumentMalformed is the one permitting standing, three of the five producers can answer with it, and no declaration or module owns it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9 * Regenerate the Rust source-type bindings from their authority main carries an updated `proof` row in gunbc.rust_source_type_bindings whose committed projection was never regenerated, so origin/main is itself in a drifted state and every branch that merges it inherits a failing regen phase. Measured here rather than assumed: both the .dag authority and the .rs projection on this branch are byte-identical to origin/main, and regen still reports first_generation_equal=false for this one file. The fix is the projection, not the authority: the generated file now carries what the row says. The build lane is green at this head -- regen first_generation_equal=true, generated-artifact 35/35 matched, 0 drifted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9 * Two claims quantified over a population they did not check The census repair left its neighbours stating the population as it stood when each sentence was written. Two of those neighbours are claims, and that is the part that matters: their names quantify universally while their bodies enumerated a subset, so the standing this branch introduces could have collapsed into the permitting arm with both green. every_non_permitting_standing_refuses_and_says_which checked four of the five non-permitting standings. malformed_is_the_only_standing_that_permits_replacement checked two of the three it owns (LoadComplete and LoadPartial belong to a_partial_load_is_never_supersedable, so the five are covered between the pair). Both now check the whole population. Executed rather than argued: routing LoadRequiresNodeAtAuthoredSource to the permitting arm takes BOTH claims red, and both are green with the arm restored. Before this change that mutation was invisible to both. The prose repairs are the same defect without the teeth. load_standing said "four refusals ... for four different reasons" and listed four of five, so the new standing had no stated reason; it has one now, and it is its own -- the document decoded and what it asserts is unsatisfiable, so no fetch repairs it and superseding would destroy a generation over a defect not in its bytes. object_table_json called collision "the five"; it is the seventh cause against the other six. The census said "one producer today" after counting five, and "a sixth standing" above a six-arm coproduct. The census partition is restated at the grain the review asked for: three declarations DECIDE LoadDocumentMalformed from a refusal cause, one RELAYS it without deciding, and one cannot return it -- a fourth return route is not a fourth decision authority. The surviving consequence is stated at arm grain (the ClosureDocLoaded branch) rather than as "no load that succeeded", because this module deliberately separates a document that decoded from one whose standing answers standing_loaded true. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9 --------- Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Worker attestation
Summary
SPARK-PAIR-0 P0-C3a makes one sealed observation transaction own every Spark serving probe capture, typed acquisition identity, provenance, projection, and acquisition-coverage receipt. Planning privilege is separated from pure row computation: only an acquired host observation may mint a bound Spark axis, while hermetic projections may exercise computation without crossing into an executable plan.
The generic FleetConverge artifact now consumes that bound axis and is the sole artifact assembler. Its rendered body carries the Spark typed-action marker required by its own apply reader. Post-apply verification acquires a distinct PostApply transaction and refuses unobserved linger rather than planning from a fabricated Boolean.
Two production defects the cut surfaced, both repaired here:
spark_serving_fleet_converge_plan_artifact_from_rowsassembled a wholeFleetConvergePlanArtifactbeside the genericfleet_converge_plan_artifact. The only thing it did that the generic one did not was write the# spark-serving-typed-actions:line thatfleet_converge_plan_spark_typed_actions_consistency_admitsreads andspark_serving_apply_reads_frozen_typed_rowsdepends on. Once production moved to the generic route, the shipping path emitted plans its own apply reader refuses whenever a Spark row existed. Nothing was red, because every claim reading the marker entered through the Spark-shaped door that had it. The line now lives in the generic assembler; the second assembler is deleted with no callers left.The hermetic acquisition fixture is transitively sealed: every function returning a production-accepted capture, request, transaction, provenance or observation carries
admit_callersnaming its exact consumers. Sealing proved eight functions dead and they are deleted. The wall is proven by a control pair that makes the same call with the same arguments and differs only in whether the roster names the caller.Fixture states are earned from probe output rather than declared: the unit read echoes the desired unit wire so the content member converges by bytes, the runtime receipt echoes the identity digest the projector compares, the required model digest is the joint across four store legs, and linger's third state comes from a
loginctlleg that ran and exited nonzero.Test plan
Candidate head:
3add770ce1418c530c064b5d5f2d7f7f4155d5eeTree:
60f44f57ae343f9f227a22621f676a9547f72009Eight-entry srv6 matrix, run on the earlier head
a44a6a1b(tree29b5962a): 305/305 PASS. That matrix has NOT been re-run on the current head; what covers the current head is CI, which executed all five jobs on the composed merge-preview tree (below). The matrix is recorded as the historical targeted run it was, not as evidence about744d69e9.Each entry is scored as PASS-count against that file's own declared
test fnroster, so a truncated or killed entry reads asPASS < rosterrather than as silence.rc=0on all eight.Generated-artifact assessor (
generated_artifact_gate.main): clean,rc=0. The zero is discriminating, not assumed: a control in an isolated detached worktree that perturbs.githooks/pre-commitreturnsrc=1with the located findingcommitted content differs from authority.This PR performs no fleet mutation. Its canary is one read-only observation of srv5 and srv6, re-executed on this exact head
744d69e9, and again on the composed merge-preview tree, rather than carried. The34de783execution below is retained as the historical record it is; the applicability section that follows states why its carry was withdrawn.The observation itself: srv5 and srv6 each observed exactly once with no foreign host; acquired provenance and
phase=reporton both; one shared fleet attempt with distinct observation-attempt identities; a single credential identity and locus across both hosts;required=23 captured=23 missing=0 duplicated=0;executed=23,transport-refused=0,prerequisite-refused=0; process exit 0.Second receipt: the COMPOSED tree, not just the head. A head-only receipt
proves the observe route for the branch, not for what actually lands. The wet
observation was therefore re-run a second time against GitHub's merge preview —
the same tree CI adjudicated, confirmed from the job log's checkout line
(
HEAD is now at 59cd9388d6 Merge 744d69e9fda... into 0b4366efb8f...).SUPERSEDED — the composed-tree receipt below was taken against merge preview
59cd9388whose head-parent was the previous head. main advanced toeced9d24,which conflicted
DESIGN.md; that merge is now in this branch and the head moved,so the preview changed and this block is retained as history rather than as current
evidence. The exact-head receipt above IS current. Re-running the composed-tree
observation costs no head move and no CI restart, so it can be re-taken on request
against whatever preview is current at the moment of merge.
Same predeclared criteria, all passed: srv5=1 srv6=1 with 0 foreign; acquired and
phase=reporton both;required=23 captured=23 missing=0 duplicated=0andexecuted=23 transport-refused=0 prerequisite-refused=0on both; one fleet-attemptwith distinct per-host observation-attempt; one credential identity;
model_tag=20bdigest
17052f91…on both. The harness additionally asserts both parents, so arefreshed merge preview refuses rather than silently scoring a different tree.
This closes the gap a head-only receipt leaves: main commits reachable only through
the base parent — including #9813, which touches the evaluation substrate that runs
this entry — are inside the tree this observation executed.
The honest boundary, stated rather than implied. The composed tree is a function
of main, so this receipt is exact for
4440edfaand is retired by any main advancethat changes the merge preview. It is not, and cannot be, a claim about a tree that
does not exist yet. A per-merge-preview wet receipt cannot be made permanently
current while main advances faster than the merge cycle; what it can do, and does
here, is pair the wet evidence to the exact tree the dry evidence adjudicated.
This receipt is bound to the head this PR currently carries, so it is not a carry
argument and has no intersection premise to be invalidated by a moving main. Any
further merge from main produces a new head and a new tree, which retires this
receipt and re-opens the same obligation.
Withdrawn: the previous applicability block. It asserted
canary_execution_component_intersection = []andverdict = CarriedByStructuralIdentityfor candidatea44a6a1b. Measured at thecurrent head, that intersection is not empty. Changed since
a44a6a1band on orbeside the wet route:
dag/extdeps/posix/shell_command_language.dag(remote-commandquoting),
src/v2/std/text.dag,src/v2/extdeps/languages/bash.dagandbash_command_fold.dag,dag/extdeps/ollama/capability.dag(support catalog[20b]to[20b, 120b]),dag/gunbc/ollama_model_resolution.dag, and in the seedsrc/v1/stage0/src/cli_run/entry_resolve.rsandresolved_graph_cache.rs. Theblock is replaced rather than corrected, because re-running removes the need for a
carry instead of arguing one.
Withdrawn: the grain label. That block named
dag_comparison_grain = ResolvedDeclarationDependencyClosure. The instrument wasnot a resolved dependency closure — it was a conservative lexical declaration
reachability over-approximation over
fn/func/data/typewith anidentifier-names-declaration edge rule. The label overstated the instrument. It is
recorded here as withdrawn rather than silently dropped, since the mislabel is
independent of the staleness and would otherwise recur.
Subject unchanged, checked at this head.
spark_serving_desired_model_refis stillollama_gpt_oss_20b_model_ref, andollama_model_support_row_forselects by model identity rather than catalogposition, so appending the 120b row cannot move the selection. The appended row is
therefore not a subject change. A later change that rewires the desired ref or its
resolution to 120b would be one, and would require its own wet execution.
Acceptance, measured on this receipt — every criterion below was written down
before the run produced output, and a partial pass was pre-committed to count as a
failure:
acquiredx2phase=reportx2required=23 captured=23 missing=0 duplicated=0executed=23 transport-refused=0 prerequisite-refused=001c65fe99aeb7b24model_tag=20b digest=17052f91…on bothThis PR still performs no fleet mutation; the canary remains one read-only
observation of srv5 and srv6.
SupersededHistoricalExecution: an earlier canary ran on05c0dddaac400d49ef78ea677634cc50564344b9(treebcb150be…, attemptcanary-20260831T075026Z-cab81106e375,binary-sha256=45ab012e…,report-body-sha256=b7ba142f…) and passed, as did one on689a3c38ce2787c28a9aaf892303d132c1c89fe5. None of them is the authoritative receipt for this candidate, and the34de783tuple is now superseded too: the authoritative receipts are the exact-head executioncanary-20260831T202616Z-47796f96a950on744d69e9and the composed-tree executioncanary-20260831T195020Z-7083b85f17f9on merge-preview59cd9388, both recorded above. Full receipt and ephemeral harness text for the earlier run: issue comment 5475049785.