Skip to content

Delete world_converge at the root: replace it with std.goal_assessment - #10814

Merged
briansrls merged 29 commits into
mainfrom
session/sleek-newt-698
Sep 9, 2026
Merged

briansrls merged 29 commits into
mainfrom
session/sleek-newt-698

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

What this is

The first cut of the convergence replacement migration: gunbc.world_converge is deleted at the root and replaced by std.goal_assessment, a pure, subject-agnostic goal-assessment algebra. Its one binding, gunbc.repo_ruleset, cuts over to inspect_goal in the same motion.

The corpus search surface for the replaced authority was exactly four files — the module, repo_ruleset, and two witnesses — so this is a §3 atomic root cut, not a staged one. No shadow structure, no compatibility arm, no consumer that understands both representations.

Why the shape it replaces could not be fixed in place

WorldConvergenceHandler fused four separable facts into one record with an apply stage. Three consequences, all measured rather than argued:

  1. The successful actuation path had never executed anywhere in the corpus. The generic's own fixture bound apply to FixtureApplyShouldNotRun, and the sole production binding is read-only. There was no green to preserve.
  2. Read-only policy was modelled as an actuator that always refuses. Three of repo_ruleset's seven refusal causes described a write that does not exist. One of them — RepositoryRulesetUnexpectedLiveRule — was a second representation of a fact the divergence list already carried as RulesetRuleUnexpected, free to disagree, with only the second reachable once a hold fired.
  3. That cost a real finding. Because the holds were ordered above the divergence step, an unreadable bypass roster returned the hold and discarded the drift list. Under this repository's credential the roster is unreadable on every live read — so verify reported "the roster could not be read" unconditionally and never reported that the enforcement, required contexts or ruleset name had also drifted.

What lands

dag/std/goal_assessment.dag — two layers kept as two layers:

  • ObservationAttempt — did a usable snapshot exist at all. An established absence is a fact inside Observed, not a refusal.
  • GoalAssessment — GoalSatisfied | GoalDiverged | GoalIndeterminate | GoalAssessmentRefused. GoalDiverged and GoalIndeterminate carry FreeSemigroup, so a divergence naming nothing has no constructor (§4b rung four).
  • inspect_goal — guards observation first; the observer never receives the goal, so it cannot narrow what it looks at to what the desire mentions.
  • assess_by_deviations / assess_by_deviations_and_unknowns — the mints. The list is destructured, so agreement and divergence are decided by the same match that supplies the payload; unknowns outrank deviations and the deviations survive into GoalIndeterminate.

No std.nonempty_list was minted. std.algebra already owns the free semigroup over T, and its grounding note explicitly withholds the NonEmptyList alias so one name never resolves to two declarations. Using anything else here would have been the §3 nickname this module exists to argue against.

gunbc.repo_ruleset cuts to inspect_goal. The five held standings redistribute onto the three facts they always were, and two causes are deleted rather than moved (RepositoryRulesetActuationRealizationUnbound, RepositoryRulesetUnexpectedLiveRule). RulesetBypassRosterUnobserved leaves the divergence coproduct and becomes RulesetUnknown — an unread fact was being filed as a proved mismatch. Two helpers that already recorded having no production consumer (bypass_roster_projection_refusal, merge_queue_projection_refusal) are deleted with the last thing that could be mistaken for one; the DELETE-hazard they carried is restated as an obligation on the writer that does not exist yet.

Every refusal is preserved. Unreadable read, proved drift, an undeclared bypass actor, an undeclared live rule, an unreadable roster, an unprojectable signed policy — all still exit non-zero. What changed is that the drift list is no longer discarded when a hold fires.

Evidence, by execution

  • dag/test/claim/goal_assessment_witness_test.dag — 6 witnesses, all PASS: the observation guard (with a distinct refusal type per layer, so a root reaching the assessor could not return the observer's refusal), its positive control, both directions of the nonempty mint, unknowns-outrank-and-still-carry, and the fixed-point predicate answering false for indeterminate and for refused.
  • dag/test/claim/repo_ruleset_goal_assessment_witness_test.dag — the binding's own witnesses, including the discriminating RED for the regression above: an unreadable roster must be GoalIndeterminate and must still carry the proved name drift. A model answering GoalDiverged has lost the first distinction; one answering GoalIndeterminate with empty known_deviations has reproduced the original defect exactly.
  • dag/test/claim/repo_ruleset_witness_test.dag — retargeted; the unobserved-roster claim now asserts both halves (the unknown is produced and the divergence fold does not also carry it).

Deliberately not in this PR

reconcile_once — the effectful half (planner, admission boundary, ActuationCompletionUnknown, independent post-apply re-observation). Its only honest first consumer is the fleet admission spine (gunbc.fleet_converge_plan: generation, held lease, observed baseline, compare-and-set), and shipping an effectful protocol around one convenient adapter is precisely how the shape being deleted acquired an apply stage nothing ever ran. Trigger: that spine binding it.

A goal revision in the result. The law is real — a receipt should say which revision it assessed — but both consumers today declare their goal as corpus data, so the parameter would be a placeholder at every call site. Trigger: the plan basis, where a revision is what the compare-and-set is fenced against.

The §3b conformance row and the corpus fork gate. Deferred, and now for a measured reason rather than a cautious one: live_deploy is ~14,700 lines across 22 files and fleet_converge_plan.dag is 2,607. There is not yet one effectful authority to conform to, and adding the row before those land would canonize the missing post-observation boundary.

Finding, not fixed here

Four modules carry annotations claiming std has no generic nonempty carrier, which is false since FreeSemigroup landed: extdeps.external_authority, gunbc.scm.commit_closure, gunbc.machine_intake.subject, gunbc.host_assimilation_program. commit_closure's is the sharpest — its stated next-rung trigger was "a NonEmptyList authority earning its place from more than one consumer", and that trigger has fired without retiring anything. They are not repaired here because std.algebra's own convergence-debt note rules that the corpus-wide de-fork is not carried by the lane that landed the authority; the same reasoning applies to the lane that merely became its fifth consumer. Worth a follow-up work item.

Failure-mode row

read_only_policy_bound_as_an_actuator_that_only_refuses — the class this migration discovered, with its recognition rule (if every arm a binding supplies for a stage is a refusal, the binding does not inhabit that stage), the ordering harm, and its separation from three near neighbours.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg

gunbc-ci-auto-heal and others added 3 commits September 8, 2026 00:32
…ctuator that only refuses

gunbc.world_converge fused observation, comparison and actuation into one
handler with an apply stage. Three measured consequences, none of which a
refinement could have reached:

The successful actuation path had never executed anywhere in the corpus --
the generic's own fixture bound apply to FixtureApplyShouldNotRun, and the
sole production binding is read-only. There was no green to preserve.

Read-only policy was therefore modelled as an actuator that always refuses,
so three of repo_ruleset's seven refusal causes described a write that does
not exist. One of them, RepositoryRulesetUnexpectedLiveRule, was a second
representation of a fact the divergence list already carried.

And that cost a finding: because the holds were ordered above the divergence
step, an unreadable bypass roster -- which is EVERY live read under this
repository's credential -- returned the hold and DISCARDED the drift list.
verify reported that the roster could not be read and never reported that
the enforcement, required contexts or ruleset name had also drifted.

std.goal_assessment replaces it, keeping the two layers as two layers and
making the empty divergence unconstructible: GoalDiverged and
GoalIndeterminate carry std.algebra FreeSemigroup, which has no empty
inhabitant. No NonEmptyList was minted -- FreeSemigroup is the corpus's
nonempty carrier and its own grounding note withholds that alias.

repo_ruleset cuts to inspect_goal in the same motion. Two causes are deleted
rather than moved, RulesetBypassRosterUnobserved leaves the DIVERGENCE
coproduct to become an unknown, and two helpers that already recorded having
no production consumer go with the last thing that could be mistaken for one.
Every refusal that made verify exit non-zero still does; what changed is that
the drift survives a hold.

Not landed, with triggers stated in the module: reconcile_once, whose only
honest first consumer is the fleet admission spine; and a goal revision,
whose inhabitant is the plan basis.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg
# Conflicts:
#	dag/gunbc/recurring_failure_mode/roster.dag
#	docs/design-failure-modes.md
…consumed by nothing

Review 62257. Verified: `git grep goal_is_satisfied` returned its own
definition and the witness asserting it, and nothing else. The one real
binding this branch lands, gunbc.repo_ruleset verify, matches GoalSatisfied
directly -- correctly, because the other three arms carry payloads it has to
report. So the declaration's only consumer was a test, which DESIGN section
3c and section 5 both red: a witness is not a consumer, and its own
annotation named a caller that did not exist.

It is the same test this branch already applied when it deleted
bypass_roster_projection_refusal and merge_queue_projection_refusal, which
had recorded the identical standing. Missing it on a declaration I had just
written is the reason the reviewer's question is asked from the consumers
inward rather than from the declaration outward.

The law the deleted witness asserted -- that indeterminate must not read as
agreement -- is not weakened by losing it. It is strengthened: with no
one-bit summary in the module, the conflation has no spelling to be written
in, which is construction where there was validation. An annotation now
stands where the predicate did, saying why the convenience is refused, so
the next author does not restore it as a kindness.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg
@gunbai-bot

gunbai-bot Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Fixed in 5ec3876 — review 62257 was right, and I verified it before acting rather than taking it on faith.

git grep -n goal_is_satisfied -- dag src returned exactly two sites: the definition and the witness asserting it. gunbc.repo_ruleset verify matches GoalSatisfied directly, which is the correct route precisely because the other three arms carry payloads it has to report. So the only consumer was a test — §3c and §5, and the function’s own annotation named a caller that did not exist.

The sharper part: this branch had already applied that exact test two files over, deleting bypass_roster_projection_refusal and merge_queue_projection_refusal because they recorded the same standing. I ran the reviewer’s question outward from the declarations I was deleting and never turned it back on the one I had just written. That asymmetry is the argument for §3c reading from consumers inward.

Deleted: goal_is_satisfied, its witness only_a_satisfied_goal_reads_as_a_fixed_point, the three fixture builders and the FixtureAssessment alias that existed only to feed it, and the now-unused GoalAssessmentRefused import.

On the law that witness carried — that indeterminate must not read as agreement. It is not weakened by the deletion, it is strengthened. With no one-bit summary in the module the conflation has no spelling to be written in: construction where there was validation, which is the direction §5 asks for. An annotation now stands where the predicate did, stating why the convenience is refused, so the next author does not restore it as a kindness.

Re-ran the generic-law witnesses after the deletion: 5/5 PASS (the sixth was the deleted one). The binding witnesses and the 29 pre-existing repo_ruleset witnesses are untouched by this commit.

— sent from sleek-newt-698

gunbc-ci-auto-heal and others added 4 commits September 8, 2026 01:16
Ledger-Repair-Judged: docs/design-failure-modes.md
Ledger-Rows-Repaired: docs/design-failure-modes.md read_only_policy_bound_as_an_actuator_that_only_refuses
Ledger-Repair-Judged: docs/design-rung-drops.md
…onale to a declaration

TWO DEFECTS, ONE OF THEM THE REASON THE OTHER WAS NOT CAUGHT.

Review 62263: every assertion in the two new witness files was declared
`fn`, not `test fn`. Verified against the modelled rule rather than by
inspection -- v2.workflow.floor_naming_hygiene enrolls a decl only when its
line starts with `test fn ` or `test data `, and
floor_discovery_barren_test_sidecar_reason_prefix refuses a `*_test.dag`
entry declaring none. So floor discovery enrolled NOTHING from either file.
The whole evidence set for the new std.goal_assessment root -- the layer
guard, the nonempty-divergence law, the unknowns-outrank-deviations
precedence -- was inert.

That is section 4b(4) inverted: a climb deletes the lower-rung PRODUCTION
machinery and its discriminating RED stays enrolled. This migration deleted
the enrolled world_converge witnesses and put unenrolled functions in their
place. All ten are now `test fn`.

The second defect is the annotation this branch put where goal_is_satisfied
had been: a trailing `//` block at end of file, which section 4c refuses
because an annotation must attach to a module-scope declaration and that one
named no subject. Ten located parse errors, and they are what refused the
floor phase in CI. The rationale now sits above `type GoalAssessment`, which
is the carrier it is actually about.

WHY MY OWN VERIFICATION MISSED BOTH, recorded because the gap is the useful
part: `claim_batch --functions <names>` runs whatever it is handed, so it
proves the assertions HOLD and says nothing about whether anything would
ever ask them; and it resolves an entry closure rather than running the
parse phase over the corpus, so the annotation refusal never reached it.
Naming functions to a runner is not evidence that a lane runs them.
`gunbc compile --source-root dag --source-root src/v2` is the instrument for
the second, and it now reports zero errors where CI reported ten.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg
@gunbai-bot

gunbai-bot Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

CI failure fixed in eb5dd6c9729, plus review 62263 addressed in the same commit. Both were real, and one caused the other.

Review 62263 — the witnesses were not enrolled. Verified against the modelled rule rather than by eye: v2.workflow.floor_naming_hygiene enrolls a decl only when its line starts with test fn or test data , and floor_discovery_barren_test_sidecar_reason_prefix refuses a *_test.dag entry that declares none. Both new files had grep -c "test fn" = 0, so floor discovery enrolled nothing from either. The whole evidence set for the new root — layer guard, nonempty-divergence law, unknowns-outrank-deviations — was inert. That is §4b(4) inverted: a climb deletes the lower-rung production machinery while its discriminating RED stays enrolled, and this migration deleted the enrolled world_converge witnesses and put unenrolled functions in their place. All ten are now test fn.

The CI failure was my previous fix. The annotation I put where goal_is_satisfied had been was a trailing // block at end of file. §4c admits only leading blocks attached to a module-scope declaration, so it named no subject — ten located parse errors, and they are what refused the floor phase (adjudication BLOCKING phase=parse ... (10 error(s))). The rationale now sits above type GoalAssessment, the carrier it is actually about. gunbc compile --source-root dag --source-root src/v2 reports 0 errors where CI reported ten.

Why my own verification missed both, since that is the transferable part. claim_batch --functions <names> runs whatever it is handed — it proves the assertions hold and says nothing about whether any lane would ever ask them. Naming functions to a runner is not evidence of enrollment. And it resolves an entry closure rather than running the parse phase over the corpus, so the annotation refusal could not reach it. I was using a runner as though it were the floor.

Also merged current main (the branch was 2 commits behind). On the regenerated ledger I ran the driver’s prescribed set-difference check rather than a count: no row from the base projection is missing, and admission_roster_read_as_the_whole_membership_rule — which briefly looked dark — is simply a row main added after my earlier merge, now present.

Not claiming green: the floor lane is the instrument for enrollment, and I want its run on this head to say so rather than assert it from the source.

— sent from sleek-newt-698

@gunbai-bot

gunbai-bot Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Correction to my previous comment: I said gunbc compile --source-root dag --source-root src/v2 reports 0 errors. That was read off an incomplete log. The finished run reports 21 blocking error(s), 26010 advisory diagnostic(s) under gunbc.compile_clean_diagnostic_policy, exit 1.

What I actually verified, stated at the grain it holds:

  • The class CI refused on — source annotation names no subject — is 0 occurrences in the completed compile, down from 10. That is the fix, and it is measured.
  • None of the 21 blocking errors is in a file this branch changes. Checked by joining the error locations against git diff --name-only origin/main: zero intersection. No error file imports std.goal_assessment or gunbc.repo_ruleset either, so there is no coupling by which this branch could have caused one. They are inherited from main, which this branch merged.
  • I am not claiming the corpus is compile-clean, and gunbc compile is evidently not the gate the floor lane runs — main carries these 21 while its floor lane is green.

The overstatement mattered because "0 errors" is the kind of sentence a reviewer relies on instead of re-running, so it is corrected here rather than left to be discovered. The narrow claim it should have been: the annotation refusal that failed the floor phase is gone, and nothing else in this branch’s files is red.

— sent from sleek-newt-698

…nsumer

Review 62280, all four findings verified against the tree and all four real:
`fixture_subject` in the binding witness had no caller anywhere; the
`Bool` import in std.goal_assessment outlived the deleted goal_is_satisfied;
and RepositoryRulesetConvergenceSubject, ObservationRefused,
ObservationEstablished and RulesetBypassRosterUnobservable were imported and
never referenced.

RATHER THAN FIX THE FOUR THAT WERE NAMED, THE SAME QUESTION WAS ASKED OF
EVERY DECLARATION AND EVERY IMPORT THESE FILES CARRY, which found three more
the review did not reach: `List` and `GoalInspection` in the generic witness,
`NoRequiredStatusChecksRule` in the binding witness -- and `gunbc_repository`,
which became unused only once the dead fixture was deleted. A reviewer
finding four instances of one class is evidence about the class, not a list
of four repairs, and the fourth of those would have survived a
finding-by-finding fix.

That is the third time this branch has landed an unconsumed declaration:
goal_is_satisfied, then this fixture, both mine, both after the branch had
already deleted two such helpers for exactly that reason. The pattern is that
section 3c is easy to apply to code being removed and easy to forget on code
being written, so it now runs as a sweep over the diff rather than a
judgement at each site.

Ten witnesses re-run after the deletions: 10 PASS, 0 FAIL.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg
@gunbai-bot

gunbai-bot Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Fixed in 18dfb429b6f. All four findings in review 62280 verified against the tree first — all four real.

  • fixture_subject() in the binding witness: no caller anywhere (git grep over the tree returns only the definition). Deleted.
  • Bool in dag/std/goal_assessment.dag: residue of the deleted goal_is_satisfied; the only other occurrence is the word inside the annotation explaining why the predicate was refused. Removed.
  • RepositoryRulesetConvergenceSubject, ObservationRefused, ObservationEstablished, RulesetBypassRosterUnobservable: imported, never referenced. Removed.

I did not fix only the four that were named. Four instances of one class is evidence about the class, not a list of four repairs, so I ran the same question over every import and declaration in all four files this branch adds. That found three more the review did not reach — List and GoalInspection in the generic witness, NoRequiredStatusChecksRule in the binding witness — plus gunbc_repository, which became unused only after the dead fixture was deleted and would have survived a finding-by-finding fix entirely.

Worth naming plainly: this is the third unconsumed declaration this branch has landed — goal_is_satisfied, then this fixture — both mine, both after the branch had already deleted two such helpers for that exact reason. §3c is easy to apply to code being removed and easy to forget on code being written. It now runs as a sweep over the diff rather than a judgement at each site, which is what caught the extra three.

Also confirmed while I was in there, since they read as unused by an in-file check and are not: inspect_goal and assess_by_deviations_and_unknowns have no second reference inside std.goal_assessment because their consumer is gunbc.repo_ruleset (repo_ruleset.dag:1190,1253).

Ten witnesses re-run after the deletions: 10 PASS, 0 FAIL.

Thanks for the non-findings you recorded as cleared — the FreeSemigroup-is-one-declaration check and the refusal-preservation check are the two I would most want a second reader on, and knowing they were examined is worth more than the findings.

— sent from sleek-newt-698

…efect suppressed every comparison

Review 62287, verified and correct. After the actuation holds were deleted,
`ruleset_assess` still answered `signed_desire_projection_refusal()` FIRST
and returned a whole-assessment refusal, never reaching the divergence fold.
So a merge-queue projection gap suppressed live enforcement, target, name and
required-context drift that have nothing to do with it -- the same ordering
harm this branch deleted the holds to fix, reproduced one layer down on the
arm that was left, and an instance of the class this same branch rosters as
read_only_policy_bound_as_an_actuator_that_only_refuses. The helper's own
prose still reads "Refusing rather than actuating", which is what it was for:
a refusal on behalf of a writer this module does not bind.

THE DISPOSITION IS NOT THE MINIMUM THE REVIEW ASKED FOR, because the honest
one is available. The gap is not merely "do not let it suppress the rest": the
goal states the merge-queue desire TWICE -- as the signed policy and as the
rules wire the projection carries -- and when those disagree, WHICH ONE IS THE
GOAL is undetermined. merge_queue_divergences can still compare the live queue
against the signed policy, so the dimension is not unassessable for want of a
comparison; it is unassessable for want of an unambiguous goal. That is an
UNKNOWN on one dimension, RulesetMergeQueueDesireSelfInconsistent, carried
beside every other dimension's proved drift.

With that arm gone, no state of this subject leaves its inputs incomparable,
and the type now says so rather than leaving an unreachable variant to be
mistaken for a live one: AssessRefusal is v2.std.cardinality Never. The arm
survives in verify's match because the coproduct is closed -- which is the
property that makes a fifth assessment arm a compile error rather than a
silent fall-through -- and it is unreachable by construction.

Two witnesses added: the desire is self-consistent today and contributes no
unknown, and a name drift is still reported with the desire self-consistent,
which a reinstated short-circuit anywhere above the comparison fails.
36 witnesses over both repo_ruleset entries: 36 PASS, 0 FAIL.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg
@gunbai-bot

gunbai-bot Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Fixed in 8255cea132f. Review 62287 is correct on every point, and it found the sharpest defect on this branch so far.

Verified before acting. ruleset_assess did answer signed_desire_projection_refusal() first and return a whole-assessment refusal, never reaching observation_divergences. So a merge-queue projection gap suppressed live enforcement, target, name and required-context drift that have nothing to do with it. That is the same ordering harm this branch deleted the actuation holds to fix, reproduced one layer down on the arm that was left — and an instance of the class this same branch rosters. The helper’s own prose still reads “Refusing rather than actuating”, which is exactly what it was: a refusal on behalf of a writer this module does not bind.

I did not take the minimum the review offered, because a stronger disposition is available and honest. The gap is not merely "don’t let it suppress the rest". The goal states the merge-queue desire twice — as the signed policy, and as the rules wire the projection carries — and when those two disagree, which one is the goal is undetermined. merge_queue_divergences can still compare the live queue against the signed policy, so the dimension is not unassessable for want of a comparison; it is unassessable for want of an unambiguous goal. That is an unknown on one dimension (RulesetMergeQueueDesireSelfInconsistent), carried beside every other dimension’s proved drift — the same shape the bypass roster got.

The type now states the consequence. With that arm gone, no state of this subject leaves its inputs incomparable, so AssessRefusal is v2.std.cardinality Never, which has no constructor. That is the checked claim, not a placeholder. The arm survives in verify’s match because the coproduct is closed — the property that makes a fifth assessment arm a compile error rather than a silent fall-through — and it is unreachable by construction rather than by convention.

Two witnesses added, both enrolled as test fn: the desire is self-consistent today and contributes no unknown, and a name drift is still reported with the desire self-consistent — which a short-circuit reinstated anywhere above the comparison fails. 36 witnesses across both repo_ruleset entries: 36 PASS, 0 FAIL.

Worth stating plainly, since it is the pattern rather than the instance: I deleted three refusals that spoke for an absent writer and left a fourth standing, in the same function, having just written a failure-mode row whose recognition rule names it. Finding four of a class and repairing three is how a class survives its own repair.

— sent from sleek-newt-698

gunbc-ci-auto-heal and others added 3 commits September 8, 2026 02:29
# Conflicts:
#	dag/gunbc/recurring_failure_mode/roster.dag
# Conflicts:
#	dag/gunbc/recurring_failure_mode/roster.dag
…t deletion time

Review 62304. Verified and correct, and the CAUSE is the part worth keeping:
when this branch deleted gunbc.world_converge the census was clean -- four
referents, all handled. Main then landed #10712 (std.decision, "selection
precedes convergence"), which added a NEW world_converge consumer while this
branch was deleting it. Merging main was textually clean, so the break
arrived silently: no conflict, no marker, an unresolved import on a claim
witness. A textual merge cannot see a symbol deleted on one side and newly
referenced on the other.

DESIGN section 3 says the deletion IS the census and what breaks is exactly
what was load-bearing. That holds -- but only against the tree at deletion
time. Against a moving main the census must be RE-RUN after every merge, and
this branch had merged four times without doing so.

Fixed forward rather than restored, per the same section:

- dag/test/claim/realization_selection_witness_test.dag migrates to
  std.goal_assessment. Two stages disappear rather than being rewritten: it
  supplied WorldApplied over a context it threaded through, asserting nothing
  about actuation and existing only because the interface demanded a stage.
  There is now nothing to supply. The section 3d claim it exists for is
  unchanged -- a SelectedWithin candidate feeds the convergence root as its
  SUBJECT and the root reaches a satisfied assessment -- and the receipt was
  returned unchanged by the old handler too, so nothing asserted was dropped.
  Its two test names named the deleted symbol and are renamed with it.

- gunbc.design_document, gunbc.design_argument and std.decision cited
  world_converge in the prose and in the section 3d reviewer question. That
  is the stale-name defect section 3's standing rule names, sitting in the
  document that GOVERNS the criterion, so the rename lands with the deletion.
  DESIGN.md regenerated: zero occurrences.

18 witnesses on the migrated entry: 18 PASS, 0 FAIL.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg
@gunbai-bot

gunbai-bot Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Fixed in a14a66d032e. Review 62304 is correct on both findings, and the cause is worth more than the fix.

When this branch deleted gunbc.world_converge, the census was clean — four referents, all handled. Main then landed #10712 (std.decision, "selection precedes convergence"), which added a new world_converge consumer while this branch was deleting it. Merging main was textually clean, so the break arrived silently: no conflict, no marker, an unresolved import on a claim-witness path. A textual merge cannot see a symbol deleted on one side and newly referenced on the other.

DESIGN §3 says the deletion is the census and what breaks is exactly what was load-bearing. That holds — but only against the tree at deletion time. Against a moving main the census has to be re-run after every merge, and this branch had merged four times without doing so. That is the generalizable lesson here, not the two files.

Fixed forward rather than restored, per the same section:

  • dag/test/claim/realization_selection_witness_test.dag migrates to std.goal_assessment. Two stages disappear rather than being rewritten: it supplied WorldApplied over a context it threaded straight through, which asserted nothing about actuation and existed only because the interface demanded a stage. There is now nothing to supply and nothing to mistake for a proven write. The §3d claim it exists for is unchanged — a SelectedWithin candidate feeds the convergence root as its subject, and the root reaches a satisfied assessment — and the old handler returned that receipt unchanged too, so nothing asserted was dropped. Its two test fn names named the deleted symbol and are renamed with it.
  • gunbc.design_document, gunbc.design_argument, std.decision cited world_converge in prose and in the §3d reviewer question. That is precisely the stale-name defect §3’s standing rule names, sitting in the document that governs the criterion — so the rename lands with the deletion rather than after it. DESIGN.md regenerated: zero occurrences.

git grep world_converge across dag/ and src/ now returns only prose about the deletion (the failure-mode row, the new module’s note, repo_ruleset’s migration note). 18 witnesses on the migrated entry: 18 PASS, 0 FAIL.

— sent from sleek-newt-698

gunbc-ci-auto-heal and others added 4 commits September 8, 2026 03:12
… an unconsumed arm

Found by re-reading DESIGN against the diff rather than by a reviewer, which
is the only reason it is worth recording separately.

GoalAssessmentRefused has no constructor anywhere in the corpus. Both
bindings reach it never: repo_ruleset instantiates AssessRefusal at
v2.std.cardinality Never because no state of that subject leaves its inputs
incomparable, and the realization-selection witness never mints it. Under
section 3c that is a DECLARED FRONTIER, admissible only with its trigger
stated beside it, and no trigger was stated.

The trigger is a NAMED CONSUMER rather than a date, because section 4b(3)'s
rule that a trigger must name the capability applies to a frontier the same
way: product.fan_tach_health already computes exactly this fact --
FanTachRefused over DuplicateExpectedHeader, DuplicateObservedHeader and
HostIdentityMismatch, each an ill-formed QUESTION SET rather than an unhealthy
fan -- and the arm discharges when that module binds to this algebra.

DELETING THE ARM WAS THE OTHER CANDIDATE AND IS THE WRONG ONE. The
distinction between "the world differs" and "the question was malformed" is
one this module exists to keep; a coproduct that cannot express the second
forces the first binding with an ill-formed input to file it as a divergence,
which is the conflation section 5 forbids. The frontier is the honest state.

This is the fifth unconsumed declaration on this branch. The first four were
found by review; this one by reading the authority. Both are cheaper than the
sixth.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg
…nnotation is a parse refusal

CI on c6a2626: required-witnesses-floor refused with four parse errors, all
mine, all one class -- "source annotation sits inside a declaration body.
Only module-item grain is modeled." The note explaining why verify's
GoalAssessmentRefused arm is unreachable sat INSIDE the match. It now sits
above verify, where DESIGN section 4c says an annotation attaches, and it
records that fact so the next author does not rediscover it the same way.

WHY THIS SHIPPED, WHICH IS THE PART WORTH KEEPING. This branch already broke
CI once on section 4c, with a TRAILING annotation that named no subject. I
fixed that and then verified this round with the check I had built for it:
"does the file end on a declaration". That check is structurally incapable of
seeing a body-level comment. I tested the previous BUG instead of the RULE,
which is the same shape as a regression control that greens on everything
except the one input it was written for.

The rule is one line of DESIGN and decides both variants at once: annotations
attach at module-item grain. The check that follows from the rule rather than
from the last failure is `^[[:space:]]+//` over the changed files, and it now
returns empty across all of them.

Verified with the instrument rather than a proxy this time: `gunbc compile
--source-root dag --source-root src/v2` completes its frontend phase -- the
phase that refused -- with zero occurrences of the annotation class and zero
errors in any file this branch touches.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg
… neighbours

The world_converge deletion removed the tests around them, and their leading
annotation blocks went with the wrong neighbour. Both survivors were left bare:
w_RED_an_incomplete_merge_queue_rule_refuses_and_names_the_missing_fields and
w_RED_verify_reports_an_undeclared_bypass_actor.

The restored text is rewritten rather than reverted, because the original prose
described a shape this branch removed: one half cited a type-tag guard test that
no longer exists, and the other contrasted verify against a converge actuation
path that is gone. What survives is the irreducible rationale DESIGN section 4c
asks for -- absent and zero are different observations and only the optional
carries that difference; a refusal that names nothing tells the operator that
something is wrong without telling them what.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg
@gunbai-bot

gunbai-bot Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Both findings in review 62345 verified against the tree. One fixed, one is in-flight by the driver's own prescribed route.

Annotation loss in repo_ruleset_witness_test.dag — real, fixed in b2a2e1e01f1.
Confirmed exactly as described: w_RED_an_incomplete_merge_queue_rule_refuses_and_names_the_missing_fields and w_RED_verify_reports_an_undeclared_bypass_actor both survived with their leading blocks taken by the neighbouring deletions. An off-by-one, not a decision — agreed.

I restored the rationale rather than reverting the text, because the original prose described a shape this branch removed. One block called itself "the second half of the type-tag guard," and that guard's test no longer exists; the other contrasted verify against a converge actuation path that is gone. Reverting would have reinstated two stale citations. What is restored is the part §4c actually asks for — the irreducible why: absent and zero are different observations and only the optional carries that difference; and a refusal that names nothing tells the operator something is wrong without telling them what.

Missing read_only_policy_bound_as_an_actuator_that_only_refuses row in docs/design-failure-modes.md — real observation, but regenerating it here is the wrong move.
The row was in the projection on an earlier head. It went dark in the origin/main merge at 4978326, where that path hit GeneratedArtifactConcurrentDivergence: both sides had changed the projection since the merge base, so the driver refused, left the ours-side bytes unmerged, and printed its repair route. Step 1 of that route is to take the base side verbatim; step 4 is "do not regenerate this projection locally"; step 5 is that heal-generated-artifacts derives it from the merged authorities and pushes the healed head.

I followed that, and verified the resolution by set difference in both directions rather than by count, per step 2: zero rows from main went dark, and the single row missing from the artifact is mine — whose authority is merged and intact (dag/gunbc/recurring_failure_mode/read_only_policy_bound_as_an_actuator_that_only_refuses.dag exists, and roster.dag carries both its import and its enumeration entry; I identity-joined that file in both directions, 178 = 178, no orphans).

So the roster and its rendering do disagree right now, and that disagreement is what heal-generated-artifacts exists to close on this head. Hand-regenerating would put me back on the exact path the driver refused. If heal runs and the row is still absent, that is a real defect in the heal route and I will say so rather than paper over it.

— sent from sleek-newt-698

gunbc-ci-auto-heal and others added 2 commits September 8, 2026 05:13
Ledger-Repair-Judged: docs/design-failure-modes.md
Ledger-Rows-Repaired: docs/design-failure-modes.md read_only_policy_bound_as_an_actuator_that_only_refuses
Ledger-Repair-Judged: docs/design-rung-drops.md
merge_queue_desire_unknowns read the declared rows through a nullary wrapper,
so with this repository's merge queue unsigned the fold answered Absent on
every input the corpus can produce: the Present arm and the
RulesetMergeQueueDesireSelfInconsistent renderer it feeds were unreachable, and
the only claim over them was a positive control. DESIGN section 4b asks whether
a check's RED is authorable before the check is written, and settles it on
whether the forbidden state is authorable AS A FIXTURE rather than whether it
exists in the accepted corpus. It is.

So the seam is a parameter, as signed_desire_projection_refusal_for already was
and as the deleted repo_ruleset_observed_actuation_admission carried it for this
same reason -- it was unreachability that let an inversion sit in that arm
undetected. The nullary wrapper is deleted rather than kept beside the
parameterized one.

The RED is discriminating by execution, not by inspection: with the operand set
back to what the nullary wrapper supplied, claim_batch reports FAIL on
RED_a_signed_policy_the_projection_does_not_carry_is_an_unknown_on_that_dimension.
Restored, all 7 witnesses in the file pass.

Also fixes prose the actuation deletion left stale: the refusal string still
read "converge would send a ruleset that cannot converge. Refusing rather than
actuating" in a value that is now an unknown and not a refusal, and an
annotation cited that sentence as present tense.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg
@gunbai-bot

gunbai-bot Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Review 62379 verified and fixed in f54790be214. The finding was correct and it was the sharpest one on this PR: the migration kept the arm and dropped the seam that made it reachable.

What was wrong. merge_queue_desire_unknowns read the declared rows through a nullary signed_desire_projection_refusal(). This repository's merge queue is unsigned, so that fold answers Absent on every input the corpus can produce — the Present arm and the whole RulesetMergeQueueDesireSelfInconsistent branch of render_unknown were unreachable, and the only claim over them was a positive control. §4b's test is whether the check's RED is authorable before the check is written, and its stated boundary is whether the forbidden state is authorable as a fixture, not whether it exists in the accepted corpus. It is.

The fix is the one the module already knew, as the review said. merge_queue_desire_unknowns now takes policy / rules and calls the already-parameterized signed_desire_projection_refusal_for; the nullary wrapper is deleted rather than left standing beside it. That is the same seam the deleted repo_ruleset_observed_actuation_admission carried, for the reason its own removed note gave — it was unreachability that let the inversion sit.

The RED is discriminating by execution, not by inspection. I did not want to assert that from the source, having been wrong that way before, so I falsified it: with the operand set back to what the nullary wrapper supplied, claim_batch reports

FAIL RED_a_signed_policy_the_projection_does_not_carry_is_an_unknown_on_that_dimension

Restored, all 7 witnesses in the file pass. The mutation was reverted before the commit.

One thing the review did not name, found while fixing it. The refusal string still read "converge would send a ruleset that cannot converge. Refusing rather than actuating" — actuation prose surviving the actuation deletion, sitting inside a value that is now an unknown and not a refusal. Rewritten, along with an annotation that cited that sentence in the present tense.

— sent from sleek-newt-698

# Conflicts:
#	DESIGN.md
#	dag/gunbc/recurring_failure_mode/roster.dag
#	docs/design-failure-modes.md
@gunbai-bot

gunbai-bot Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Blocked, and deliberately not worked around. required-witnesses-floor fails on 1020adf with

required-ci: adjudication BLOCKING phase=namespace-wave-admission
  cause=namespace-wave-admission (1 unadjudicated delta(s), 0 stale, 0 consumed)

witnesses is the aggregator reading that lane, so this is one cause and not two. required-witnesses-build and heal-generated-artifacts both pass on this head.

The delta. Eight were reported; seven auto-classified. The one that did not:

NewPoolCoincidenceResolution binding
  gunbc.recurring_failure_mode.roster::recurring_failure_mode_roster
  `read_only_policy_bound_as_an_actuator_that_only_refuses`
  base {} -> head {gunbc.recurring_failure_mode.read_only_policy_bound_as_an_actuator_that_only_refuses}

Cause, and it is not this PR. Merging main brought in #10822, which took roster.dag out of version control and derives it instead. Before that, adding a failure-mode row edited roster.dag in the same commit, so the reference was authored — AuthoredReferenceResolution, auto-admitted. Now the roster is generated and gitignored, so the reference exists only in generated bytes, and disposition_auto_admitted does not admit NewPoolCoincidenceResolution.

This PR carries the first failure-mode row to land after #10822, which is the only reason it surfaced here. It will recur for every future row.

Why I have not unblocked myself. A row in NAMESPACE_TRANSITION_ADMISSIONS clears this in minutes. That array is currently empty — every prior admission has been dissolved — and adding one would be a standing permission papering over a substrate deficit, paid again by every later author. DESIGN §5: "noticing you are implementing a workaround IS the line-stop signal." The root fix is in namespace_wave_admission.rs, load-bearing seed code, under a brief that predates #10822.

Escalated to the operator with three options: fix the classifier (my recommendation, and it belongs in #10822's lane rather than this one), add the seed admission row, or drop the failure-mode row from this PR — the last of which I do not favour, since it would drop a §4b obligation for a class that was genuinely discovered.

Holding here rather than pushing.

— sent from sleek-newt-698

…ng its head record

The namespace wave-admission baseline is the head index with the diff applied
in reverse at FILE grain, which assumes one thing about every file the diff did
not name: that its base text equals its head text. That holds for a tracked
file. It is false for a source generated before compile and gitignored, because
such a file never appears in `git diff` no matter how far its content moved --
so its head record was carried into the BASE index and the wall compared the
head against itself.

#10822 made the failure-mode roster derived and gitignored. This branch carries
the first row to land after it, and had its own authored addition reported as
NewPoolCoincidenceResolution -- a name resolving from a pool with nobody
authoring a reference -- when the reference was authored by this very change.
That disposition is not auto-admitted, so a required lane blocked, and it would
have blocked for every later row. The binding delta was right; the authorship
evidence under it was poisoned.

The carry now requires the base tree to contain the path, which is what makes
unchangedness proof of byte identity. A derived path is re-rendered from the
base revision's row files through the SAME renderer the writer uses and parsed
by base_records, so no hand-built record and no second derivation authority
exist. Any other untouched head-index record absent from the base tree is
NotEvaluated: a path listing supplies existence, never content, and an untracked
path may equally be local state, a scratch fixture or a dirty worktree file --
inheriting or silently dropping it fabricates a baseline either way.

binding_disposition, locally_authored_claim_added and disposition_auto_admitted
are untouched, and NAMESPACE_TRANSITION_ADMISSIONS stays empty: nothing is
admitted by permission. Measured on this head, the wall goes from 1 unadjudicated
delta to ADMITTED with 7 real deltas, binding_rows_compared 759867 -> 759866 --
the one row that should never have been compared, because with a correct base
the name does not exist there.

Evidence: the wall's own run on this base/head reports
`namespace-wave-admission ADMITTED`. Two unit tests enrolled in the deriver's
seed-growth population cover the derivation, and the one encoding the defect is
discriminating -- it fails under a mutation that renders the base as the head
while its three siblings stay green. Those tests sit under the declared drop
rust_unit_tests_off_the_merge_path and are local diligence, not enrolled evidence.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg
@gunbai-bot

gunbai-bot Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Substrate deficit fixed at the root, not admitted around. d8f366ce7e0. Operator authorized the fix; the side-chat ruling shaped it.

What was wrong. The wave-admission baseline is the head index with the diff applied in reverse at file grain, which assumes one thing about every file the diff did not name: that its base text equals its head text. True for a tracked file. False for a source generated before compile and gitignored, because that file never appears in git diff no matter how far its content moved — so its head record was carried into the base index and the wall compared the head against itself. The binding delta was right (base {} is real); the authorship evidence under it was poisoned.

#10822 made the roster derived and gitignored. This branch carries the first row to land after it. It would have blocked every later row too.

The fix. The ordinary carry now requires the base tree to contain the path — unchangedness is only proof of byte identity for a file git actually tracks. A derived path is re-rendered from the base revision's row files through the same renderer the writer uses and parsed by base_records. Any other untouched head-index record absent from the base tree is NotEvaluated.

Three things I did not do, each deliberately:

  • No NAMESPACE_TRANSITION_ADMISSIONS row. That array is still empty. Nothing is admitted by permission.
  • No widening of disposition_auto_admitted. That would say "whenever authorship evidence is missing, treat the coincidence as though authorship had been established" — turning poisoned evidence into permission, and destroying the negative control that justified splitting the two 0 -> 1 variants.
  • No hand-built record. My first attempt cloned the head ModuleDeclarationRecord and filtered selected collections. That produces facts no source ever produced, and the next field added to that struct would silently keep its head value on the base side. Rendering source and re-parsing it keeps one derivation authority and one parser.

Measured, not asserted. The wall's own run on this base/head:

namespace-wave-admission ... deltas=7   (was 8)
namespace-wave-admission ADMITTED — every delta is auto-admitted or named by a transition admission

binding_rows_compared 759867 -> 759866: exactly the one row that should never have been compared, because with a correct base that name does not exist there. Note this differs from the predicted outcome — the delta does not reclassify to AuthoredReferenceResolution, it disappears, since binding_rows keys on the base record's own occurrences and a correct base roster has none. Same result by a stricter route.

Two unit tests cover the derivation, both enrolled in gunbc.derived_row_roster_seed_growth (that receipt is exhaustive — it lists even write_atomically). The one encoding the defect is discriminating: it fails under a mutation that renders the base as the head, while its three siblings stay green. Honest caveat: these sit under the declared drop rust_unit_tests_off_the_merge_path, so they are local diligence, not enrolled evidence.

cargo clippy --all-targets -- -D warnings clean. One local wet-lane witness pair (self_host_logic_*) fails in my session; both passed in CI on the identical head 1020adf, and they compile and run binaries, so I read that as my environment and am letting CI adjudicate rather than asserting it.

— sent from sleek-newt-698

gunbc-ci-auto-heal and others added 2 commits September 8, 2026 18:34
…s this touch brings due

TWO OBLIGATIONS, ONE FILE.

Review 62579 found a promise standing where a check belongs. The base-side
reconstruction renders base inputs with the HEAD renderer, which is sound only
while both revisions carry the same one -- and the diff said so in a comment
while nothing enforced it. A later edit to render_roster would have had the wall
render a base with head logic, compare it against a head rendered the same way,
and report the renderer's own change as zero: the very class this branch repairs
one revision earlier, re-admitted as an author obligation. DESIGN section 5 asks
for the wrong state to be unwritable, not described.

The check compares the text of render_roster on both sides and refuses with
NotEvaluated when it differs. The subject is the RENDERER, not its file: whole-
file bytes were measured first and refuse too much -- they would refuse this very
change, and every later one adding a doc comment beside the renderer, for a
reason unrelated to the hazard. A line stopped where nothing is wrong teaches
authors to route around the wall. The membership rule is a stated residual, not a
covered case, because this change moves it into a declaration the base lacks so
no text comparison spans both sides.

Verified both directions against the real base blob: the texts are equal today,
so the guard passes rather than blocking this branch, and a mutation changing the
rendered import syntax makes it refuse.

THIRTY-FIFTH DISSOLUTION. Touching this file brings the two gunbc#10688 rows due,
and CI said so: `0 unadjudicated delta(s) ... 2 consumed admission(s) due for
deletion on this roster-touching change`. Those rows demanded their own
adjudication -- "not by trusting this sentence" -- so the join was run: on main
v1.std.core DECLARES call_semantics_target while v1.compiler.emit_rust references
it four times and declares it none, so both sides resolve the spelling to
v1.std.core and the TargetChanged delta is no longer producible. Rows and label
go together, as the two label deletions before them did, and the roster is empty
again. gunbc.namespace_wave_admission drops the label from its enumerated
declarations and re-tenses the prose that claimed it was listed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg
@gunbai-bot

gunbai-bot Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Two things in fd0098e5887, on top of the merge at 3af78c573a4.

Review 62579 — the promise is now a check. Correct finding: I wrote the hazard into a comment and left nothing enforcing it, which is validation standing exactly where §5 asks for construction — and the hazard is the same class this PR repairs one revision earlier, re-admitted as an author obligation.

I did not take the proposed construction (compare the base blob of DERIVATION_CARRIER_REL_PATH against head bytes), because I measured it first and it refuses this very PR: this diff edits that file. It would also refuse every later change to it, including one adding a doc comment — a line stopped where nothing is wrong, which teaches authors to route around the wall rather than through it.

So the guard's subject is the renderer, not its file: it compares the text of render_roster, the declaration whose bytes decide the output and which exists on both revisions so the comparison actually spans the boundary. Verified both directions against the real base blob — identical today (so it passes rather than blocking), and refusing under a mutation that changes the rendered import syntax. One residual is stated in the code rather than papered over: the membership predicate is not covered, because this change moves it into a declaration the base does not have, so no text comparison spans both sides.

The substrate fix is confirmed on the real instrument. CI on d8f366ce reported deltas=7, 0 unadjudicated delta(s) — the false NewPoolCoincidenceResolution is gone on the runner, not just locally, and NAMESPACE_TRANSITION_ADMISSIONS was still empty when it said so.

What blocked instead was a rule I triggered, and it is now paid. That same run said 2 consumed admission(s) due for deletion on this roster-touching change. Main had added the two gunbc#10688 rows admitting the call_semantics_target re-home; my change touches namespace_wave_admission.rs, which is the roster path, so they came due. Those rows demand their own adjudication in as many words — "not by trusting this sentence" — so I ran the join rather than trusting either the sentence or CI: on main v1.std.core declares call_semantics_target, while v1.compiler.emit_rust references it four times and declares it none. Both sides resolve the spelling to v1.std.core, the TargetChanged delta is no longer producible, both rows are consumed.

Rows and label deleted together, as the two label deletions before them were, recorded as the Thirty-Fifth Dissolution. gunbc.namespace_wave_admission drops the label from its enumerated declarations and re-tenses the prose that claimed it was listed. The roster is empty again — still nothing admitted by permission.

cargo clippy --all-targets -- -D warnings clean; cargo fmt clean via the hooks.

— sent from sleek-newt-698

… actuation claims

TWO FIXES.

The first is mine to own: deleting CALL_SEMANTICS_TARGET_REHOME_LABEL from the
enumerated declarations dropped only the `decl_name` line and left the enclosing
DeclarationRef standing, so gunbc.namespace_wave_admission carried a record with
no name. Every required lane parses the corpus, so one malformed literal failed
all four on fd0098e. The compiler named it exactly -- missing required field
'decl_name' in literal of type 'DeclarationRef' -- which is the fail-closed
behaviour working; the generated-artifact gate now resolves again.

The second is review 62605, which found the sign-off standing still asserting the
world this branch deleted: a WHOLE-RULESET PUT, a converge run that would delete
an out-of-band rule, and "the actuation refusal below" -- a positional pointer to
deleted code -- beside a dissolution string promising "a divergence converge
repairs" two sentences before saying SIGNING THIS ROW ACTUATES NOTHING. A meaning
fork inside one operator-facing obligation.

Sweeping the module found seven such sites, not one, and they are fixed as a
class because DESIGN section 3 is explicit that a dead assumption is an attractor:
while it stands, nearby questions keep being answered in its vocabulary.

The UI-flip warning gets the sharpest correction, because the hazard did not
soften -- it INVERTED. It read: a rule enabled in the web form is deleted by the
next run, a scheduled silent self-reversal. With no writer, a flip is not
reversed at all; it PERSISTS, unmodeled, reported as a divergence on every
assessment forever, with nothing in this repository able to clear it. The
operator sees a configured queue and the assessment sees drift it can never
repair, so signing the row is now the only route rather than merely the best one.

Where a hazard survives as an obligation on a future writer rather than a current
fact -- the whole-ruleset replace, the unexpected-rule refusal, the signed desire
the projection cannot build -- it is stated that way rather than deleted, because
the reason those checks exist does not go away with the writer that consumed them.

All 7 witnesses in repo_ruleset_goal_assessment_witness pass, including the
discriminating RED.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg
@gunbai-bot

gunbai-bot Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

43b78562bf5 — two fixes.

Mine to own: the four-lane failure on fd0098e5887 was my error. Deleting CALL_SEMANTICS_TARGET_REHOME_LABEL from the enumerated declarations dropped only the decl_name line and left the enclosing DeclarationRef { … } standing, so the module carried a record with no name. Every required lane parses the corpus, so one malformed literal failed all four — this was one cause, not four. The compiler named it precisely (missing required field 'decl_name' in literal of type 'DeclarationRef'), which is the fail-closed behaviour doing its job. The generated-artifact gate resolves again locally.

Review 62605 — correct, and it was one instance of a class. The sign-off standing did still assert the world this branch deleted: a WHOLE-RULESET PUT, a converge run that would delete an out-of-band rule, "a divergence converge repairs" two sentences before SIGNING THIS ROW ACTUATES NOTHING, and "the actuation refusal below" — a positional pointer to deleted code, which is the §3 citation defect the review also named.

Sweeping the module found seven such sites. I fixed them as a class rather than the one line, because §3 is explicit that a dead assumption is an attractor: while it stands, nearby questions keep being answered in its vocabulary.

The UI-flip warning got the sharpest correction, because the hazard did not soften — it inverted. It read: a rule enabled in the web form is deleted by the next run, a scheduled silent self-reversal. With no writer, a flip is not reversed at all. It persists, unmodeled, reported as a divergence on every assessment forever, with nothing in this repository able to clear it. The operator sees a configured queue; the assessment sees drift it can never repair. So signing the row is now the only route rather than merely the best one, and the note says that.

On the review's parenthetical — "drop 'converge repairs' unless a writer actually exists" — I dropped the claim of repair, but kept the hazards that survive as obligations on a future writer (the whole-ruleset replace, the unexpected-rule refusal, the signed desire the projection cannot build), stated as obligations rather than as current facts. The reason those checks exist does not disappear with the writer that used to consume them, and deleting the rationale would lose a correctness distinction rather than update it.

All 7 witnesses in repo_ruleset_goal_assessment_witness pass, including the discriminating RED.

— sent from sleek-newt-698

…ntry it left standing

Review 62621 found ~90 lines of the module's dissolution record present twice.
It is not a merge artifact: my scripted replacement of the SIXTH TRANSITION block
computed the block's END offset with an index search from position 0, which
matched an EARLIER occurrence of the same trigger sentence in a preceding entry.
With end < start, concatenating the two slices duplicated everything between them.

Deleting the span turned up more than the review reported. The original SIXTH
TRANSITION block was still standing as well, so the file at once documented the
two gunbc#10688 rows as live and recorded their dissolution -- two sources free to
disagree about exactly the rows this roster adjudicates, which is the section 3
violation the review named, in a sharper form than it found.

Verified by counting occurrences against origin/main rather than by reading the
diff, because this is the second time a scripted text edit of mine damaged this
file: every pre-existing record now appears exactly once as it does on main, the
THIRTY-FIFTH DISSOLUTION is the only addition, and SIXTH TRANSITION is gone.
A delta scan across every changed file finds no other block duplicated relative
to main.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg
@gunbai-bot

gunbai-bot Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Review 62621 verified and fixed in 26f123aab26. The finding was real, and the cause is mine rather than the merge.

What happened. My scripted replacement of the SIXTH TRANSITION block computed the block's END offset with an index search from position 0, which matched an earlier occurrence of the same trigger sentence in a preceding entry. With end < start, concatenating the two slices duplicated everything between them. The review's reading of it as a merge-resolution artifact was the natural one, but the merges are innocent here.

Deleting the span turned up more than the review reported. The original SIXTH TRANSITION block was still standing too, at :1692. So the file simultaneously documented the two gunbc#10688 rows as live and recorded their dissolution — two sources free to disagree about exactly the rows this roster adjudicates. That is the §3 violation the review named, in a sharper form than it found: not just a duplicated record, but a duplicated record contradicting itself.

Verified by counting against origin/main, not by reading the diff, because this is the second time a scripted text edit of mine has damaged this file (the headless DeclarationRef was the first). Per-pattern occurrence counts, head vs main:

THE gunbc#10671 ROWS DISSOLVED HERE     head=1  main=1
THE gunbc#10602 ROW DISSOLVED HERE      head=1  main=1
SIXTH TRANSITION (2026-09-07            head=0  main=1   (replaced by the dissolution)
THIRTY-FIFTH DISSOLUTION                head=1  main=0   (the only addition)
CALL_SEMANTICS_TARGET_REHOME_LABEL      head=1  main=3   (const + 2 rows gone; 1 mention in the record)

I also ran a delta scan across every changed file — repeated long lines in head vs the same file on main — to catch any other block my edits duplicated. One hit, in derived_row_roster.rs, and it is legitimate: the same fixture path appears in both of my new tests. No other file carries duplication relative to main.

cargo clippy --all-targets -- -D warnings clean.

— sent from sleek-newt-698

gunbc-ci-auto-heal and others added 2 commits September 8, 2026 20:31
# Conflicts:
#	src/v1/stage0/src/namespace_wave_admission.rs
Kept separate from the merge commit so the reasoning is reviewable on its own.

Main's gunbc#10813 hit the defect this branch repairs. Adding one
recurring-failure-mode class made the roster's existing declaration resolve a new
name, the wall reported NewPoolCoincidenceResolution -- a name resolving from a
pool with nobody authoring a reference -- and since that disposition does not
auto-admit, the class could not land without a permission row. Its entry called
that "the honest cost of the pool being adjudicated rather than assumed".

It was not the pool's cost. It was a wrong baseline's. The roster is derived
before compile and gitignored, so it never appears in git diff, and the
reconstruction carried its head record into the BASE index -- the wall compared
the head roster against itself, so a reference authored by the change looked
authored by nobody. Every future class would have paid the same row.

With the base re-derived from the base revision's own row files, the class is
absent from the base roster, the binding row exists on one side only, and the
comparison skips it under the rule the wall already states: only rows present on
both sides, because a newly authored name has no prior denotation to change. No
delta is produced, so the row matches nothing -- and a row matching no delta is
STALE, which refuses. Deleting it is that rule firing, not a preference of mine.

Adjudicated by measurement: the class file is ABSENT at this change's merge base
and present at its head, the exact shape that produced the delta before and
produces none after.

My own THIRTY-FIFTH DISSOLUTION entry is dropped in the same resolution. Main
independently recorded the same gunbc#10688 deletion while this branch did, and
keeping both would be two sources free to disagree about which rows were
adjudicated -- the duplicated-record fork review 62621 found here one head ago.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg
@gunbai-bot

gunbai-bot Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Merge conflict resolved in d24dc450338, with the reasoning recorded separately in 99a213ae38d so it is reviewable on its own. This one is worth reading rather than skimming, because main and this branch collided on the same defect.

Main's #10813 hit exactly the defect this PR repairs. Adding one recurring-failure-mode class made the roster's existing declaration resolve a new name; the wall reported NewPoolCoincidenceResolution; that disposition does not auto-admit; so the class could not land without a permission row. #10813 added one, plus a label constant and a transition entry describing it as "the honest cost of the pool being adjudicated rather than assumed".

It was not the pool's cost. It was a wrong baseline's. The roster is derived before compile and gitignored, so it never appears in git diff, and the reconstruction carried its head record into the BASE index — the wall compared the head roster against itself, so a reference authored by the change looked authored by nobody. Every future class would have paid the same row. That is the same finding I escalated on this branch, arrived at independently from the other side.

So the row is deleted, and that is main's own rule firing rather than a preference of mine. With the base re-derived from the base revision's row files, the class is absent from the base roster, the binding row exists on one side only, and the comparison skips it under the rule the wall already states — only rows present on both sides, because a newly authored name has no prior denotation to change. No delta is produced, so the row matches nothing, and a row matching no delta is stale, which refuses. Keeping it would have blocked CI on a stale admission.

Adjudicated by measurement, not by that paragraph: the class file is absent at this change's merge base and present at its head — precisely the shape that produced the delta before and produces none after. The required run on this head is the check.

I also dropped my own THIRTY-FIFTH DISSOLUTION entry. Main independently recorded the same #10688 row deletion while this branch did. Keeping both would be two sources free to disagree about which rows were adjudicated and why — the duplicated-record fork review 62621 caught here one head ago, so I took main's entry as the single authority and deleted mine.

Verified the file by occurrence counts against origin/main rather than by reading the diff (the method I switched to after damaging this file twice): every pre-existing record appears exactly once, SEVENTH TRANSITION is replaced by its dissolution, and the two extra repeated long lines relative to main are NotEvaluated arms in the renderer guard, not duplication. NAMESPACE_TRANSITION_ADMISSIONS is empty again.

cargo clippy --all-targets -- -D warnings clean.

— sent from sleek-newt-698

gunbc-ci-auto-heal and others added 2 commits September 8, 2026 22:56
# Conflicts:
#	dag/gunbc/derived_row_roster_seed_growth.dag
#	dag/gunbc/namespace/namespace_wave_admission.dag
#	src/v1/stage0/src/cli_run/derived_row_roster.rs
#	src/v1/stage0/src/namespace_wave_admission.rs
…e cut

Main's gunbc#10856 implemented the repair this branch carried: a derived roster
has no base side in a diff, so a baseline that drops only the paths the diff
TOUCHED inherits the head's roster as the base's, the module's own source reads
as unchanged, the authorship discriminator answers false, and an ordinary append
classifies NewPoolCoincidenceResolution. Same diagnosis, same cut, arrived at
independently from the other side.

Two implementations of one fact is the section 3 fork, so this branch takes
main's and deletes its own. All four files now diff to zero against main.

MAIN'S IS ALSO BETTER IN A PLACE MINE WAS WRONG. roster_from_path_listing
returns None when the base tree carries no row files, keeping "the roster module
was genuinely absent at this revision" distinct from "present and empty". Mine
rendered an empty roster for both, which fabricates a module the base never had
-- the empty-observation narrow, in the code written to remove one. Main also
generalizes over sweep roots rather than hardcoding the dag/ prefix, and it
retired the gunbc#10813 admission row on the same reasoning this branch reached.

WHAT IS LOST AND WHERE IT SHOULD LAND: the renderer-sameness guard from review
62579. Main's version renders base inputs with the HEAD renderer, so a change to
render_roster would have the wall compare a head-rendered base against a
head-rendered head and report that renderer's own change as zero. The finding is
moot against the code it was written about, since that code is gone; the hazard
is not, and it now sits in main's implementation rather than this PR's. It wants
its own change against main's shape, not a rushed transplant here.

This PR is now the convergence consolidation alone: 14 files, no Rust, no
seed-growth roster, no wall.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg
@gunbai-bot

gunbai-bot Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Conflict resolved in fe50f1d181a; reasoning recorded separately in 6f90b0d37f0. The resolution is a yield, and it shrinks this PR back to its original subject.

Main's #10856 landed the same fix this branch was carrying. Same diagnosis, same cut, arrived at independently from the other side: a derived roster has no base side in a diff, so a baseline that drops only the paths the diff touched inherits the head's roster as the base's, the module's own source reads as unchanged, the authorship discriminator answers false, and an ordinary append classifies NewPoolCoincidenceResolution. Main's comment describes the mechanism in nearly the same words mine did.

Two implementations of one fact is the §3 fork, so this branch takes main's and deletes its own. All four files now diff to zero against main.

Main's is also better in a place mine was wrong. roster_from_path_listing returns None when the base tree carries no row files, keeping "the roster module was genuinely absent at this revision" distinct from "present and empty". Mine rendered an empty roster for both — which fabricates a module the base never had, the empty-observation narrow, inside the code written to remove one. Main also generalizes over sweep roots instead of hardcoding the dag/ prefix, and it retired the #10813 admission row on the same reasoning I reached here.

What is lost, and where it should land: the renderer-sameness guard from review 62579. Main's version renders base inputs with the HEAD renderer, so a change to render_roster would have the wall compare a head-rendered base against a head-rendered head and report that renderer's own change as zero. The finding is moot against the code it was written about — that code no longer exists — but the hazard is not, and it now lives in main's implementation rather than in this PR. It deserves its own change against main's shape rather than a rushed transplant at the end of this one. Flagging it rather than silently dropping it.

This PR is now the convergence consolidation alone: 14 files, no Rust, no seed-growth roster, no wall changes. That also resolves the two-subject scope concern I raised earlier — main landing the wall fix independently removed the coupling that justified carrying it here.

— sent from sleek-newt-698

…h was open

Review 62717 is right that the root cut was unfinished. The fe50f1d merge
brought gunbc#10683's consumers of a module this branch deletes, the textual
merge was clean, and the break arrived silently -- the same gap a14a66d already
named once. The census is the only thing that finds this, which is why the
deletion IS the census.

Migrated onto std.goal_assessment rather than restoring world_converge, because
each of these is the failure class this PR rosters. Both carried an `apply` that
could only refuse, and that refusal had to inhabit the same coproduct as the
causes meaning the subject could not be OBSERVED -- so an unread leg and a
miscoded leg, an uncaptured lane and a misconfigured one, arrived as one verdict
answering two questions. PlantCodingCannotBeActuatedHere and
SwitchCannotBeActuatedHere are deleted, not ported: each restated as a REFUSAL
what the difference list already carried as a VALUE, and the weaker copy was the
arm a reader reached first.

fabric_switch_desired carried the same conflation one layer out. Its
DesiredSpeedUndecidedCause had a single PlantDidNotConverge arm holding a plant
refusal, so a caller could not tell "the plant was never read" from "the plant
was read and diverges" -- go read the plant versus go change the copper, two
remedies behind one name. It is now PlantNotObservable and
PlantDivergedFromRequirement.

EVERY DEFECT IN THE FIRST PASS WAS CAUGHT BY EXECUTION, NOT BY READING. The
corpus compile found a stale import rename and two matches where switch refusal
causes were left standing as arms of an assessment; the witness run then found
two more, where the list-and-unwrap ceremony of the old List shape was carried
into a FreeSemigroup that answers with .head directly. All 13 witnesses across
both files now pass, and the compile reports zero errors in any file this branch
touches.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg
@gunbai-bot

gunbai-bot Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Review 62717 verified and fixed in c5ea5027df2. The finding was correct: the root cut was unfinished, and your diagnosis of the cause matches mine — those consumers arrived via the fe50f1d181a merge of #10683, the textual merge was clean, and the break was silent. The census is the only thing that finds this, which is exactly why the deletion is the census.

Migrated, not restored — and you were right that restore is the wrong arm. All five files you named are done: cable_plant_converge, fabric_switch_converge, fabric_switch_desired, and both witnesses. git grep world_converge now returns four files, all annotations naming it as deleted.

Your observation about the refuse-only apply arms was the useful part. Both bindings carried one, and in each case that refusal had to inhabit the same coproduct as the causes meaning the subject could not be observed — so an unread leg and a miscoded leg, an uncaptured lane and a misconfigured one, arrived as a single verdict answering two different questions. PlantCodingCannotBeActuatedHere and SwitchCannotBeActuatedHere are therefore deleted rather than ported: each restated as a refusal what the difference list already carried as a value, and the weaker copy was the arm a reader reached first.

fabric_switch_desired had the same conflation one layer out, which the review did not reach. Its DesiredSpeedUndecidedCause held a single PlantDidNotConverge arm carrying a plant refusal, so a caller could not distinguish the plant was never read from the plant was read and diverges — two different remedies (go read the plant, versus go change the copper) behind one name. Now PlantNotObservable and PlantDivergedFromRequirement.

Every defect in my first migration pass was caught by execution, not by reading, which is worth stating plainly given how much of this PR has been scripted text edits. The corpus compile found a stale import rename and two matches where switch-refusal causes were left standing as arms of an assessment. The witness run then found two more, where I had carried the old List shape's list-and-unwrap ceremony into a FreeSemigroup that answers with .head directly. All 13 witnesses across both files now pass, and the compile reports zero errors in any file this branch touches (the 34 corpus-wide blocking errors are inherited from main and unrelated).

— sent from sleek-newt-698

@briansrls
briansrls merged commit fe83b41 into main Sep 9, 2026
4 checks passed
@briansrls
briansrls deleted the session/sleek-newt-698 branch September 9, 2026 01:51
@briansrls
briansrls restored the session/sleek-newt-698 branch September 9, 2026 01:54
gunbai-bot Bot pushed a commit that referenced this pull request Sep 9, 2026
…nd retract the stale plant claim

CONFLICT RESOLUTION. main deleted gunbc.world_converge at the root and replaced
it with std.goal_assessment, carrying all three modules from #10683 across with
it. The single conflict was one import block where this branch adds LegNeverRead
(the six-unread-legs repair) while main renames converge_cable_plant to
inspect_cable_plant and CablePlantConvergence to CablePlantInspection. Resolved
to main's vocabulary PLUS this branch's addition -- neither side wholesale.

The migration is an improvement on what it replaced, and worth naming: the
actuation arms are Never in GoalInspection<...>, so "this subject cannot be
actuated from here" is now STRUCTURAL rather than my always-refusing apply
function. That is section 4b's climb from a runtime refusal to a state with no
constructor, in a place I had left at the lower rung.

REVIEW 62808. The block heading the plant still said "every leg was read to the
same bytes, so the plant is the lane roster joined to the one coding
observation" -- the exact claim the previous commit was written to retract,
sitting five lines above the retraction. Two annotations, one file, materially
different answers to the same question. It now says the plant joins the roster
to what was READ of each, which is not the same for every lane, and that a lane
enters as unread until someone dumps it. Section title follows main's vocabulary
from convergence to inspection.

This is the third stale-prose finding on this PR and the mechanism has been
identical each time: I rewrite the block I am editing and do not re-read its
neighbour. The declaration changes, nothing forces the adjacent comment to move
with it, and section 4c guarantees no Accepted program will ever notice.

NOT CHANGED, deliberately: fabric_cable_plant_convergence still says
"convergence" while returning a CablePlantInspection. That is a nickname against
the new authority, but main authored it in the migration, and renaming a
migrated symbol is not conflict resolution. Worth a follow-up.

Typecheck: 0 blocking errors across all five affected closures on the merged
tree.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AigrH3JpxgSJBmqHMBAr6W
gunbai-bot Bot pushed a commit that referenced this pull request Sep 9, 2026
`dag/gunbc/superseded_run_starvation_census.dag:696`, from #10742
(9b33fd6), was authored against pre-cut main and refused the parse phase
with `expected item declaration`. It carries no `uses` row, so the migration is
the keyword and nothing else.

This is the SECOND such delivery on this branch -- #10814 supplied the first,
in gunbc.repo.repo_ruleset -- and the two arrived by different routes: that one
surfaced as a merge conflict because it rewrote a hunk this branch had touched,
while this one merged cleanly and was caught only by the required parse sweep.
The sweep is what makes the class fail-closed rather than silent, and it is the
reason the corpus residual is checked immediately before merge rather than once
at the start: every branch authored against pre-cut main can add one, and the
window closes only when this lands.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZZi3QsDvibJqy8k8PwzZ9
briansrls pushed a commit that referenced this pull request Sep 10, 2026
…ntence (#10883)

* The observer could still see the goal, because the guarantee was a sentence

std.goal_assessment said "the observer cannot see the goal" while `observe`
took `(Subject, Scope)` -- and a subject is exactly where a goal travels. The
sentence was true when written and false one binding later:
fabric_switch matched on `subject.intent`, so the desired mode selected which
lanes were read. Two of the three bindings broke the law the module advertised.

inspect_goal now takes subject, goal and request as three arguments and hands
the observer `(Subject, ObservationRequest)`. The observer is goal-blind
because it has no goal-typed parameter, not because a note asks it to be. It
is NOT subject-blind: handing it only the request would make `subject` a label
on the result, free to name A while the read went to B. Both result arms carry
the request, so a refusal says which read was attempted.

Three defects this fixes were shipped by #10814 and ratified by its witnesses.

MEMBER-LEVEL UNKNOWNS WERE PROMOTED TO WHOLE-OBSERVATION REFUSAL. The first
unread lane short-circuited the observation and discarded every deviation
proved on lanes that were read -- an unknown fact suppressing a known one,
which is the ordering harm #10814 removed from repo_ruleset, reintroduced one
module over. They are unknowns now, and the mixed case reports both.

THE DENOMINATOR CAME FROM THE OBSERVATION. Unknowns were built from
`LaneNeverRead` rows a caller authored, so a lane omitted from the readings
entirely vanished from the population it was supposed to be measured against.
The switch joins `SwitchIntent.lanes` -- the declared roster -- against what
was read, so the denominator is the goal's. An intent naming lanes none of
which were read is fully UNKNOWN, not empty; only an intent naming no lanes is
the malformed question. cable_plant cannot do this join (it has no declared
leg roster) and now states that ceiling with its next-rung trigger rather than
implying the guarantee.

THE COLLAPSE SURVIVED ONE MODULE DOWNSTREAM. fabric_switch_desired mapped
divergence, indeterminacy AND assessment refusal onto one
`PlantDivergedFromRequirement { legs: Nat }`, discarding every unknown and
reporting a malformed assessment as `legs: 0`. It carries the rows now.

The inspect-only modules stop advertising an actuator they never had:
*_converge -> *_assessment, Crs812ConvergenceSubject -> Crs812SwitchSubject,
*_convergence -> *_inspection. No alias or compatibility shim.

EVIDENCE. 41 witnesses green, including four new controls: unknown is not a
failed read; a proved deviation and an unread member are reported together; an
unread roster is not an empty one; and a lane the readings omit ENTIRELY is
still an unknown. The last two are proved discriminating by mutation --
reverting the join to the reading-derived shape fails both while the positive
control stays green. A new generic control fixes the module's central claim:
two goals over one request produce one observation.

NOT VERIFIED BY EXECUTION: repo_ruleset. Its closure reaches
gunbc.roadmap.roadmap_belt_actuate through extdeps.transports.rest, and that
module fails to resolve on main (`if branches resolve to incompatible types`)
byte-identically to this branch. Its changes typecheck with no errors
attributed to them, which is not the same as green by execution.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg

* The request repeated the subject, and the goal-blindness check could not go red

Two review findings, both confirmed, and the first was worse than reported.

THE OBSERVER TOOK ONE ARGUMENT WHERE THE ROOT PASSES TWO.
`observe_repo_ruleset_attempt` kept the arity it had before the recut while
`inspect_goal` calls `observe(subject, request)`. Nothing else repaired it,
and it sits in the one module whose witnesses cannot execute -- so no green
run of mine would ever have found it.

Underneath it was a second defect the finding implies but does not name: the
`request` passed at the call site was a VERBATIM DUPLICATE of the subject,
the same RepositoryRulesetConvergenceSubject built twice. Re-typing the
parameter would have compiled and left this binding's request carrying nothing
about the read, making the module's own claim -- a refusal says which read was
attempted -- false here while the receipt still reported one.

So the repair is the split rather than the arity: subject is the `Repository`
(WHO), request is `RulesetReadRequest { ruleset_id }` (WHAT TO READ). The
fused RepositoryRulesetConvergenceSubject is deleted; it existed only to carry
both facts at once, which is the fusion this whole change is about.

THE GOAL-BLINDNESS CONTROL WAS PERMANENTLY GREEN.
`two_goals_over_one_request_produce_one_observation` passed the SAME
`fixture_observe` to both calls, and that function has no goal parameter, so
the equality could not go red for any authorable input. DESIGN section 4b asks
whether a check's RED is authorable BEFORE writing it; this one's was not,
which makes it worse than absent because it would have been cited as coverage
for the module's central claim. Deleted, not repaired.

The RED that IS authorable replaces it. `inspect_goal` closes the PARAMETER
route; it does not close LEXICAL CAPTURE -- a lambda may close over the goal
and shape the read by it. The new control holds that as an executing fact and
goes red if capture ever stops being expressible.

That forces an honest downgrade this change had claimed too strongly:
goal-blindness is STRUCTURAL against the parameter and MITIGATED BY REVIEW
against capture. Both the module and the witness now say so, and the
next-rung trigger is named: a lens refusing an `observe` argument whose free
variables include the goal bound at the same call site.

Also removes fixture_assess_echoing_goal, which the deletion left unconsumed.

EVIDENCE. 41 witnesses green across four entries, post-merge with main.

STILL NOT VERIFIED BY EXECUTION: repo_ruleset, and the cause is now located.
Its closure reaches gunbc.roadmap.roadmap_belt_actuate, where two coproducts
in the corpus declare an arm named `Observed` -- BeltObserve's and a nullary
one in gunbc.self_host_compile_phase_frontier. Resolution picks the nullary,
so a branch building `Observed { live: ... }` types as Primitive rather than
Coproduct(BeltObserve). That is a DESIGN section 3 collision on main: one
name, two declarations. It is unrelated to this subject and is not fixed here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg

* Prose that was true before the cut and false in the same diff, twice

Three findings, all confirmed, and the first is the failure this change exists
to delete -- reintroduced by the change itself.

THE FLEET ENTRY POINT'S ANNOTATION DESCRIBED A REFUSAL STAGE THAT NO LONGER
EXISTS. It claimed the entry "refuses at the observe stage today, and it
refuses with the ONE fact that is missing -- the interface nobody read". The
observer's refusal type is `Never` and it always establishes; the refusal comes
from the ASSESSMENT and carries the CABLE cause, which this module's own
witness asserts while returning false on GoalObservationRefused. The sentence
was true of the shape BEFORE this cut and false of the one in the same diff.

It is recorded rather than quietly swapped, because the recurrence is the
point: a guarantee carried only by prose surviving one edit past the code it
described is precisely what this change removes from the type, and it
reappeared in the module while that removal was being written.

A SIBLING SWEEP FOUND A SECOND SITE THE REVIEW DID NOT CITE. The module header
said the "observe stage inherits that refusal rather than re-deciding it" --
same class, same edit, one paragraph away. The ASSESSMENT inherits it; the
observe stage cannot refuse at all. Fixed, and the reason is now stated.

Annotations are the one part of this change no witness can guard: no Accepted
program reads one, so 41 green tests say nothing about whether the prose beside
them is true. Review and a targeted sweep are the only mechanisms, and the
sweep only worked because the specific false claim was known.

MEMBERSHIP WAS A FAILED DECOMPOSITION, AND THE COST SHAPE COMPOUNDED IT.
`interface_absent_from` minted `list_length(flat_map(...)) == 0` beside the
corpus-canonical `any(xs, pred)` -- section 2's net-concepts test. Worse,
`observed_lane_interfaces` was called INSIDE the fold, so n intent lanes and m
read lanes did n*m work and allocated n copies of one list to answer a single
membership question each, and `lane_intent_interface` computed twice per lane.
The projection is hoisted once and the fold uses `any`. Section 6's standing
rule: a proven cost-shape defect is fixed regardless of the realized n, because
n is not a time-stable fact.

A merged paragraph is also repaired: the new assessment block had been spliced
into the tail of an unrelated one and lost its heading.

EVIDENCE. 17 witnesses green across the switch and cable entries.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg

* Point the new spark witness at the renamed cable-plant module

main added test.claim.spark.spark_fabric_switch_witness while this branch was
open, importing product.cable_plant_converge. The merge is textually clean --
an import naming a module that no longer exists is not a conflict -- so only
the census finds it. Third time this cutover has been overtaken by new
authorship against a module scheduled to move.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg

* Adjudicate the nine bindings the cable-plant rename retargets

The floor refused this head: `namespace-wave-admission (9 unadjudicated
delta(s))`. All nine are `TargetChanged binding` -- a spelling authored on both
sides that now resolves to `product.cable_plant_assessment` instead of
`product.cable_plant_converge`, because this cut renames the module. That is
the exact subject NAMESPACE_TRANSITION_ADMISSIONS exists for, and the roster's
own note already states the claim these rows make.

THE ARITHMETIC IS THE CHECK, not the green. The wave measured eighteen deltas.
Nine auto-admit as the membership half of one motion: the old edge reports
SameDeclarationIdentityRebind (every name it supplied still denotes the same
declaration) and the new edge ExplicitlyEvaluatedZeroDelta (reached by a name
the module authors). The nine below are the binding half. Consumers whose OWN
module was renamed produce no binding delta at all -- a delta needs the module
present on both sides -- which is why the two witness files this cut renamed
are absent from the roster and `fabric_switch_observed` is present. Rows for
the renamed modules would have meant I had misread what the wall measures.

ENUMERATED BY EXACT IDENTITY, never by a pattern over the renamed module pair:
a pattern would admit a genuine rebind that happened to land in the same pair,
which is the absorbing fallback section 5 forbids. None of these nine changes
WHICH DECLARATION the spelling denotes; a binding whose meaning had moved would
refuse on its own row rather than be covered here.

TRIGGER: removed when this PR merges. The base then binds each spelling to
`product.cable_plant_assessment`, every row reports consumed-at-base, and
leaving them would refuse every unrelated PR -- the cost the roster's own
history records from the first fifty-three rows.

This is the mechanism I declined earlier in this session for a different
subject. There the row would have papered over a baseline-reconstruction
defect, so writing it was a workaround. Here it adjudicates a rename that was
intended, which is what the roster is for. Same mechanism, opposite standing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg

* The cable module still called its two stages four

The switch module's heading was corrected when the handler record was deleted;
the cable module's was not, so it kept announcing an actuation stage and an
unchanged-receipt stage that no longer exist and never ran for this subject.

Found by re-reading the request against the code rather than the review tally:
renaming the stale stage headings was an explicit item on the list this PR is
answering, and it survived two approving reviews. An approval is 'no blocking
defect found', which is not the same as 'this is what was asked'.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg

* The goal still chose what was read, one level above the observer

REVIEW FINDING, CONFIRMED, AND IT IS RUNG INFLATION IN THE MODULE ABOUT RUNG
HONESTY. `fabric_switch_subject()` built its observation REQUEST by mapping
over the intent's own interfaces, so an undecided intent produced an empty
request and therefore an empty snapshot BECAUSE OF THE GOAL. Closing the
parameter route while the request stayed goal-derived renamed the mechanism
and removed nothing. This change had been reporting goal-blindness as
structural against the parameter while the live binding routed the goal into
the read anyway -- section 4b(1): citing the strongest path while another
stays silent.

CLOSED RATHER THAN DECLARED. No RouterOS capture of this switch exists, so the
honest request is empty whatever anyone wants. The unknowns are still one per
intent lane because `switch_unknowns` joins the intent's declared roster on the
ASSESSMENT side, which is the stage allowed to know the goal. Same reported
answer; the goal no longer reaches the read. The goal-derived roster function
is deleted -- nothing consumed it once the request stopped calling it.

THE RESIDUAL DISCLOSURE WAS WRONG BY OMISSION. It named two routes; there are
three. A `request` is caller-constructed, so no type can stop a caller
computing it from the goal -- and this module's own binding was the proof. All
three are now enumerated, with this binding recorded as the worked example, and
ONE next-rung trigger covers both review-mitigated residuals because they are
the same shape: a goal reaching the read through a VALUE rather than a
parameter.

"THE OBSERVER RECEIVES AN ObservationRequest AND NOTHING ELSE" WAS FALSE. It
was written before an earlier review restored the subject to the observer, and
went stale in that edit. Corrected in std.goal_assessment and in the switch
module's own copy of the claim.

AND THE FIRST CONTROL FOR THIS WAS PERMANENTLY GREEN -- the same fault this
change deleted a check for, committed while correcting it. It compared the
fleet subject against an undecided-intent subject and asserted both requests
were empty; both are empty UNDER THE DEFECT too, because the fleet's real
intent is undecided today. Mutation caught it: restoring the defect left the
check PASS. The property was unobservable because the goal never varied, so
`fabric_switch_subject_under(intent)` takes it as a parameter now and the
control supplies a DECIDED intent naming two lanes. Under the defect that
request carries two entries. Mutation-confirmed: FAIL on the defect, positive
control green. The dead first version is recorded in the witness rather than
swapped out, because it is the reason the mutation step is not optional.

EVIDENCE. 12 witnesses green, including the new control, proved discriminating.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg

* The assessor's subject had five signatures and no readers

REVIEW FINDING, CONFIRMED ON ALL FIVE BINDINGS. `assess` took a `Subject` that
`assess_cable_plant`, `assess_fabric_switch`, `ruleset_assess`,
`fixture_assess` and `selection_assess` all ignore -- five signatures, zero
uses -- while an annotation beside it claimed "a refusal cause that has to name
what it could not assess should not have to be handed the identity through a
closure". Section 3c dangling, and section 4c prose asserting a use that does
not exist.

DROPPED RATHER THAN CONSUMED, which was the other option offered. Locating
`PlantHoldsNoLegs` or `SwitchIntentNamesNoLanes` by their subject would put a
second copy of the identity beside the one `GoalInspection` already carries in
BOTH arms -- the exact redundancy this change removed when it dropped `switch`
from `SwitchHoldsNoLanes`. Consuming the parameter would have undone that
reasoning to justify a parameter that should not have been added.

THE ASYMMETRY THAT REMAINS IS REAL AND IS NOW STATED. The OBSERVER keeps its
subject: gunbc.repo_ruleset's observer builds its request path from
`subject.owner` and `subject.name`, which is the one consumer that earns it.
A sweep for signature-only parameters confirms the two corpus-data observers
ignore theirs -- legitimate, since their read is a projection of declared data
and one genuine consumer is what section 3c asks of a generic parameter. The
assessor had none anywhere, which is the difference.

Dropping it cascaded: `witness_subject` in the repo-ruleset witness lost its
only reader, so the fixture and its two imports went with it. The section 3c
question asked one level down rather than stopped at the first answer.

I INTRODUCED THIS WHILE FIXING THE PREVIOUS INSTANCE OF IT. An earlier review
established that the OBSERVER needs its subject; I extended that to the
assessor by symmetry without checking whether any assessor would read it. That
is the fourth claim in this change to outlive the code it described, and the
correction is recorded in the module rather than silently reverted.

EVIDENCE. 41 witnesses green across four entries.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg

* Main's five admission rows were consumed the moment #10692 landed

The merge of origin/main kept its five #10692 rows verbatim alongside my
nine. Additive-only was the right instinct for my rows and the wrong one
for rows whose trigger had already fired in the commits being merged in:
main now binds each of those spellings to its own target, so the rows
report consumed-at-base, and the floor refused with

  namespace-wave-admission (0 unadjudicated delta(s), 0 stale
  admission(s), 5 consumed admission(s) due for deletion on this
  roster-touching change)

Leaving them is not harmless. A consumed row matches nothing, so it would
refuse every unrelated PR until someone else deleted it -- the standing
cost the roster's first 53 rows were written down to record.

The deletion is adjudicated by the instrument, not by the trigger
sentence: main's own block warned that "a trigger sentence is not
evidence that the trigger fired", and claim_executor performed the
(module, in_declaration, spelling, target) join against the base tree and
returned consumed. The rows, their label const, and their doc block go
together; nine rows remain and the wave reports ADMITTED.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUtSkZTNvpWQv58Lk9cGzg

---------

Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant