Skip to content

Model the purpose standing, the non-execution consequence, and the admission wall - #10174

Merged
briansrls merged 18 commits into
mainfrom
session/crisp-crane-370-purpose-consumer
Sep 3, 2026
Merged

briansrls merged 18 commits into
mainfrom
session/crisp-crane-370-purpose-consumer

Conversation

@briansrls

@briansrls briansrls commented Sep 3, 2026 •

Copy link
Copy Markdown
Contributor

The ranking key made executable. Stacked on #10161 (the taxonomy refinement), which it merges; review that first.

What this adds

Three types and three total folds in a new v2.workflow.floor_purpose_standing:

WitnessPurposeStanding   PurposeDeclared | PurposeUndeclaredGrandfathered | PurposeUndeclared
WitnessChangeStanding    WitnessChanged | WitnessUnchanged
NonExecutionConsequence  SilencedWall { refuser } | OpenQuestion | ConsequenceUndeclared

The consequence is the key. Only a BehavioralDiscriminator requiring a refusal silences a wall; every other purpose leaves a question open when it does not run. The SilencedWall arm carries the decision that stopped being checked — the difference between a count and a lead.

The two properties that carry the safety weight

ConsequenceUndeclared is not OpenQuestion. Collapsing them is the single change that would make this model fail open: unknown reported as the benign arm would let the entire grandfather roster read as carrying no walls on day one. Unknown is its own arm, counted, and never summed into either other.

The grandfather roster is debt, not an exemption — and purpose_admission_refuses enforces that rather than asserting it. A grandfathered identity that changed must declare before it is admitted again. So the roster can only lose members, and it loses them exactly when someone is already editing the row; the cost lands on whoever is there anyway. An untouched row is admitted — it carries yesterday's debt at no new risk. That is the shrink mechanism; without one the arm would be a permanent exemption wearing a debt contract's clothes.

Evidence — executed, with the degradations run as REDs

Seven controls green. The two that matter were mutated and confirmed red:

mutation control that fires
report unknown as the benign arm an_undeclared_row_is_unknown_rather_than_benign → false
drop the change standing from admission (roster stops shrinking) a_grandfathered_row_must_declare_once_it_changes → false

Positive controls are included so that an admission refusing everything would not satisfy the wall tests, and a sibling control asserts a declared non-discriminator purpose silences no wall — so the key cannot be read as "anything that declared a purpose is load-bearing".

One substrate trap worth recording

v2.std.logic Bool is realized natively as a machine scalar, so a function returning the True/False variants produces a value the interpreter's match_pattern has no arm for. My first run of the key control came back false for exactly that reason. This module returns native true/false literals and reserves variant matching for its own coproducts, which are genuine variants. Noted on the import — required_floor records the same straddle from the pattern side, where an earlier revision matching True {} / False {} errored 11 of 19 witnesses.

Scope

Nothing wires this into the floor yet, so nothing gates on it and no admission behavior changes. The 500ms ceiling does not move, no budget widens, and a non-verdict on a required claim still blocks exactly as before.

Inert-roster state verified against main's baseline rather than against zero, using the same live entry point the gate calls: origin/main stale=0/unrostered=2, this branch stale=0/unrostered=2. The unrostered=2 is pre-existing on main and deliberately untouched — it reproduces with this diff absent, so absorbing it would claim a repair this change did not make.

🤖 Generated with Claude Code

https://claude.ai/code/session_0198WYhRXB3scqq262mtHqSx

Brian Searls and others added 10 commits September 2, 2026 23:48
…row removes an unnamed wall

The required floor refuses when rows are preempted before reaching a verdict at
the 500ms CPU ceiling, with failed=0: nothing is judged broken, rows simply never
answer. Which rows get preempted moves with runner load, so the population is
redrawn per attempt. The part that makes it correctness rather than cost is that
some of those rows exist to establish a REFUSAL -- they PASS normally, so
preempting one turns a standing guarantee off with nothing red, and a re-run that
draws a faster runner buys a green over refusals that did not execute.

This files the class. It builds no consumer and does not touch
std.witness_purpose; both are blocked on an authored ruling and are recorded in
the row as blocked-on-a-ruling rather than rejected-on-merit, so the next lane
does not re-derive the design.

Three findings the row carries:

- THE DISJOINTNESS, with its setter. InterruptedBeforeVerdict.enrolled_expected_red
  is KnownRed quarantine and nothing else -- set on exactly one branch, the
  expected-red arm reaching ExpectedRedArm::BudgetRefused, meaning the identity is
  rostered as declared-to-fail. The rows whose silencing motivated the class carry
  it FALSE: a_live_tree_that_gained_an_identity_refuses_and_names_it and
  a_live_tree_that_swapped_an_identity_at_equal_cardinality_refuses in
  test.claim.self_host_compile_phase_live_gate_witness are ordinary passing rows on
  no roster. The second carries an in-source comment saying it is precisely the
  probe that goes green if the join degrades to a population-size comparison --
  preempt it and that sentence stops being true silently. Quarantine names rows
  expected to be RED; the silenced rows are GREEN by construction. Two questions,
  one unasked.

- THE INERT CARRIER. std.witness_purpose WitnessPurpose landed under the 2026-08-04
  witness-cost-derives-from-purpose ruling, is rostered inert in
  v2.lens.inert_carrier, has zero declarers and zero consumers, and its only
  reference is its own taxonomy test. One missing consumer, not two problems: the
  floor grew a cost mechanism that judges rows with no access to what any row is FOR.

- AN OBSERVABILITY CORRECTION, stated as a fact about the floor rather than about
  any brief. WHICH rows were preempted is already a joinable run product:
  write_required_floor_claim_cost_tsv emits one row per executed claim with
  verdict_reached, and the occurrence is minted in required_floor_runner's claim
  loop before classification branches, so preempted rows are present rather than
  dropped. Reading the INTERRUPTED-BEFORE-VERDICT log lines as the population is
  instrument_output_read_as_subject_content. What is missing is the ranking key,
  not the population.

The next-rung trigger names the CAPABILITY -- an authored purpose declaration a
floor consumer can join against -- and its three sufficiency conditions, because a
trigger naming only "a ruling" would be satisfied while the capability stays dead.

Nothing here changes what is admitted: the ceiling does not move, no budget widens,
and a non-verdict on a required claim still blocks.

docs/design-failure-modes.md is the projection, regenerated by the
generated-artifact gate and verified idempotent on a second run.

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

Two lanes appended a RecurringFailureMode row at the same roster anchor, so both
the authority and its generated projection conflicted.

dag/gunbc/recurring_failure_mode.dag: kept BOTH rows and BOTH roster entries --
main's declared_return_disagrees_with_the_generic_it_returns and this branch's
non_execution_undifferentiated_by_what_it_silenced. Neither side's append is a
supersession of the other; picking a side would have silently dropped a filed
class.

docs/design-failure-modes.md: NOT hand-resolved. The generated-artifact merge
driver refused it as GeneratedArtifactConcurrentDivergence and left it unmerged
with the ours bytes in the worktree, which is correct -- neither side's bytes are
the projection of the merged authorities. Regenerated through the generated-artifact
gate against the resolved roster, and verified idempotent on a second run. Both
identities appear in the preamble index and both bodies render.

The regeneration produced no drift outside these two paths, so main's other
generated artifacts were already consistent and steps 2-3 of the driver's recipe
(the seed emitter) have no subject here.

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

The row's trigger condition (ii) asked for a purpose declared at identity grain
without saying what KIND of declaration, which leaves the cheap version open: a
source annotation on the test fn.

DESIGN 4c forecloses it outright. Semantic passes receive only the
annotation-erased projection, so an annotated purpose is unreadable by the floor
BY CONSTRUCTION -- it would be a declaration no consumer could ever join against,
which is the whole point of the trigger. That is 4c's own rule that an annotation
is never evidence a machine claim holds, applied to this fact.

Stated as the reason the declaration is a real DeclarationRef binding rather than
as an implementation detail, so the annotation is not re-proposed as an economy in
six months. It also prices the capability honestly: a real binding is more
authoring per row than an annotation would have been.

docs/design-failure-modes.md regenerated and verified idempotent.

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

Operator ruling 2026-09-02 approved the refinement over a sixth peer arm. This is
the model half of the ranking key that gunbc.recurring_failure_mode
non_execution_undifferentiated_by_what_it_silenced names as missing.

THE BARE ARM COULD NOT ORDER THE POPULATION. "This witness discriminates
behaviour" is true of a discriminating RED and of an accepted positive control
equally, so spending it as the key would have bought a coarse fact cited as
coverage for a distinction it does not draw -- the 4b(1) inflation that stops a
class ever ranking for the climb it could make. DESIGN 4b(1) already names the two
halves of executed evidence; DiscriminatedOutcome is that pair, authored:

  RefusalRequired   { refuser:  DeclarationRef }
  AcceptanceRequired { acceptor: DeclarationRef }

WHY IT IS A SAFETY FACT. A row requiring a refusal is GREEN in the ordinary case,
so when it does not execute the wall it carries is switched off with nothing red,
and a later green over that absence is a green over a refusal that did not run. A
row requiring an acceptance, not executing, removes no wall. Same terminal state,
different consequence.

THE SUBJECT IS THE DECISION, NEVER THE WITNESS -- both arms name the decision the
row watches, so the fact is joinable against that decision rather than being an
adjective on the test. A payload naming the test itself would restate the identity
the row already has.

AUTHORED, NOT INFERRED, and the module header's rule binds here specifically: a
lens could observe that a body matches a refusal-shaped variant, and that
observation is NOT this fact -- it would make the compiler's reading of the body
the authority on what the body is for, so a body that stopped requiring the
refusal would silently restate its purpose instead of going red.

EVIDENCE, EXECUTED AND MUTATED rather than described. The control asserts that two
values AGREEING on the watched decision reach OPPOSITE answers from
purpose_requires_a_refusal, the predicate a ranking consumer runs. Executed both
ways: green as authored; collapsing the two DiscriminatedOutcome arms to one
answer -- the exact degradation the refinement exists to catch -- turns it red
(returned false). A sibling control asserts a non-discriminator purpose requires no
refusal, so the key cannot be read as "any purpose mentioning a DeclarationRef".
Per 4b(4) both stay enrolled after the wall lands.

DiscriminatedOutcome is rostered in v2.lens.inert_carrier beside WitnessPurpose:
declared, test-referenced, no production consumer yet. It dissolves when the floor
consumer that joins purpose against verdict_reached lands, which is the next piece
and is NOT in this PR.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0198WYhRXB3scqq262mtHqSx
…er last row

BINDING. There is no per-`test fn` declaration slot in the language, so a witness's
purpose has to be authored beside it and point at it:

  WitnessPurposeBinding { witness: DeclarationRef, purpose: WitnessPurpose }

Authored in the witness's OWN module by whoever owns the witness, which is what
makes the population independently defined rather than selected -- no central
roster names which rows matter and nobody picks a subset.

It is joinable without a new authority, and that is why the field is a
DeclarationRef rather than a string: v1.declaration_index already records, per
module, every typed-literal DeclarationRef citation paired with the top-level
declaration carrying it (CitedSymbol.in_declaration) and every authored type
reference under the same key. So "does a purpose binding exist for this identity"
is decidable from the index that already runs. A string would be reachable from no
index.

The binding is NOT evidence that the purpose is true of the row -- that stays
outside the modeled guarantee per the module header. What it makes possible is
ranking a row that DID NOT RUN, which needs only the declaration.

EVIDENCE: two bindings over the SAME witness differing only in purpose reach
opposite answers from the ranking predicate. Holding the witness constant makes the
purpose the only variable. Executed both ways -- green as authored; giving the
control the wall's purpose (the binding no longer transporting the distinction)
returns false.

COMMA. inert_carrier_roster's WitnessPurpose row was the list's last element and
carried no trailing separator, so appending after it left two elements adjacent.
Added for consistency with every other row.

Recorded because the review that found it (review 59031) called it a "won't
compile" blocker, and that part does not reproduce: the module PARSES AND
EVALUATES IDENTICALLY WITH AND WITHOUT THE COMMA -- inert_carrier_rostered_names
returns the same 16 units both ways, this repo's parser treating the newline as
sufficient. The fix is real hygiene; the stated consequence was not, and filing it
as a compile blocker would teach a wall that does not exist.

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

The ranking key made executable. Three types and three total folds:

  WitnessPurposeStanding   PurposeDeclared | PurposeUndeclaredGrandfathered | PurposeUndeclared
  WitnessChangeStanding    WitnessChanged | WitnessUnchanged
  NonExecutionConsequence  SilencedWall { refuser } | OpenQuestion | ConsequenceUndeclared

CONSEQUENCE IS THE KEY. Only a BehavioralDiscriminator requiring a refusal silences
a wall; every other purpose leaves a question open when it does not run. The
SilencedWall arm carries the decision that stopped being checked, which is the
difference between a count and a lead.

CONSEQUENCE_UNDECLARED IS NOT OPEN_QUESTION, and collapsing them is the one change
that would make this model fail open: unknown reported as the benign arm would let
the entire grandfather roster read as carrying no walls on day one. Unknown is its
own arm, counted, never summed into either other.

THE SHRINK MECHANISM, so the roster is debt rather than a permanent exemption in a
debt contract's clothes. A grandfathered identity that CHANGED must declare before
it is admitted again, so the roster can only lose members and loses them exactly
when someone is already editing the row. An untouched grandfathered row is
admitted: it carries yesterday's debt and no new risk.

EVIDENCE -- 7 controls green, and the two degradations that matter executed as REDs:
reporting unknown as benign turns an_undeclared_row_is_unknown_rather_than_benign
false; dropping the change standing from admission turns
a_grandfathered_row_must_declare_once_it_changes false. Positive controls included
so an admission that refused everything would not satisfy the wall tests.

NATIVE BOOL, NOT THE VARIANTS. v2.std.logic Bool is realized as a machine scalar, so
returning True {} / False {} produces a value match_pattern has no arm for. Recorded
on the import: required_floor carries the same straddle from the pattern side, where
an earlier revision errored 11 of 19 witnesses.

No consumer wires this into the floor yet, so nothing gates on it and no admission
behavior changes.

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

Third append collision on this roster. Resolved by identity rather than by count,
which is what caught the second defect below.

KEPT AS DISTINCT ROWS, verified by name and not by counting lines:
  realization_arms_diverge_on_whether_the_program_refuses   (main)
  declared_return_disagrees_with_the_generic_it_returns     (main)
  non_execution_undifferentiated_by_what_it_silenced        (this branch)

A DUPLICATE DECLARATION THE CONFLICT VIEW DID NOT SHOW. Git auto-merged
declared_return_disagrees_with_the_generic_it_returns into TWO `data` declarations
outside any conflict hunk: this branch already carried main's earlier copy from the
previous merge, and main has since REVISED that row, so the two differed in content
rather than being identical -- 5049 bytes against 5765. A silent second authority
for one identity, which is the §3 violation the roster exists to make impossible.

Dropped the stale shorter copy; the surviving row is byte-identical to
origin/main's, checked by diff rather than by length. Nothing here edits main's
authored text.

docs/design-failure-modes.md REGENERATED, not hand-merged: the driver refused it as
GeneratedArtifactConcurrentDivergence, correctly, since neither side's bytes are the
projection of the merged authorities. The regenerator was rebuilt AFTER taking
main, so it does not predate the change it emits -- the driver's own warning that a
single pass can self-verify at divergence 0 for the wrong reason. Verified again on
a second pass with that same current binary; all three identities render in the
preamble index and in the body.

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

The live gate `inert_carrier_no_unrostered_or_stale` went RED on this branch. It
was right, and the cause is my own roster edit rather than anything downstream.

WHAT "INERT" ACTUALLY MEANS, read from the producer rather than assumed. A carrier
is inert when it is declared once outside tests, referenced by some test, and its
NON-TEST occurrences minus its OWN type block's self-references come to zero or
below. The subtraction is per-block, so a reference from ANOTHER block in the SAME
non-test file counts as consumption.

So two rows stopped being true the moment this diff landed, and neither is the
carrier I was thinking about:

  DiscriminatedOutcome -- consumed by the `WitnessPurpose` block itself, since the
  refined arm reads `BehavioralDiscriminator { discriminates: DiscriminatedOutcome }`.
  It was therefore NEVER inert; the row I added for it was wrong when I wrote it.

  WitnessPurpose -- a PRE-EXISTING row, made stale by this diff: the new
  `WitnessPurposeBinding` carries a `purpose: WitnessPurpose` field, which is a
  non-test reference from another block.

`WitnessPurposeBinding` itself stays rostered: nothing outside its own block reads
it yet, so it is genuinely inert and its row is the honest state.

MEASURED, NOT ARGUED, against the same live entry point the gate calls
(`inert_carrier_live_stale_roster_count` / `..._unrostered_count`):

  origin/main                    stale=0  unrostered=2
  this branch, before the fix    stale=2  unrostered=2
  this branch, after the fix     stale=0  unrostered=2

So the branch is back to main's exact baseline and contributes no roster drift.

THE `unrostered=2` IS PRE-EXISTING ON MAIN AND IS DELIBERATELY NOT TOUCHED HERE. It
reproduces on origin/main with this diff absent, so absorbing it into this PR would
claim a repair this change did not make and would hide whose drift it is.

ONE OBSERVATION FOR WHOEVER OWNS THE LENS, recorded rather than acted on: it counts
a sibling INERT type as a consumer. Adding an inert carrier that references another
inert carrier silently un-inerts the referent, even though no production code reads
either. That is what made a pre-existing WitnessPurpose row stale here. It may be
the intended mechanical reading -- the roster must match the lens either way, which
is why this diff satisfies the gate rather than routing around it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0198WYhRXB3scqq262mtHqSx
@gunbai-bot gunbai-bot Bot changed the title Name the preempted rows: make the cpu_deadline population rankable by which refusals are not executing Model the purpose standing, the non-execution consequence, and the admission wall Sep 3, 2026
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review September 3, 2026 03:22
Brian Searls and others added 2 commits September 3, 2026 03:48
… one

required-witnesses-floor failed on this branch with the floor itself CLEAN
(verdict=FloorClean, failed=0, interrupted=0, over_cost=0). The refusal was
`required-ci: FAILED PHASE declarations (1 finding)`:

  CITED-MODULE-ABSENT ... `test.claim.witness_purpose_taxonomy_witness` cites
  `dag.test.claim.witness_purpose_taxonomy_witness` `fixture_population_ref`,
  and no module declares that path.

The module declares itself `module test.claim.witness_purpose_taxonomy_witness`.
The `dag.`-prefixed spelling names nothing, and the repo already records it as a
plain typo -- gunbc.declaration_index_seed_growth says so about this exact module.
I copied it from the neighbouring fixture when adding `fixture_refuser_ref`, so a
known one-off became a second live instance and the phase went red.

Fixed MY citation only. The pre-existing `fixture_population_ref` keeps its
`dag.`-prefixed spelling deliberately: it is carried on the citation-debt roster,
and silently repairing it here would make that roster row stale as a side effect of
an unrelated change, which is a different edit needing its own justification. Worth
someone's follow-up, not this diff's.

Discriminating tests re-run green after the change --
refusal_and_acceptance_requirements_do_not_report_alike and
two_bindings_on_one_witness_separate_on_purpose both still true, which they must be:
the citation is the same value on both sides of each comparison, so correcting the
path changes what the fixture NAMES without changing what the tests DISCRIMINATE.

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

gunbai-bot Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

On the inert-roster finding (review 59085) — I checked it against the gate rather than by inspection, and the omission is correct: those units are not inert by the lens's own definition, so a roster row for them would be a false entry rather than a missing one.

What inert actually means, read from the producer (compute_inert_carrier_data) rather than from the roster's prose: a carrier is inert when it is declared exactly once outside tests, referenced by some test, and its non-test occurrences minus its own type block's self-references come to ≤ 0. The subtraction is per block, so a reference from another block in the same non-test file counts as consumption.

WitnessPurposeStanding, WitnessChangeStanding and NonExecutionConsequence are each read by floor_purpose_standing.dag itself — in the signatures and bodies of non_execution_consequence, purpose_admission_refuses and the folds — which are blocks other than their own type declarations. So their consumption is positive and the lens does not class them as inert.

Measured against the live entry point the gate calls (inert_carrier_live_stale_roster_count / ..._unrostered_count), rather than asserted:

tree stale unrostered
origin/main 0 2
this branch, before my roster fix 2 2
this branch, now 0 2

If these three units were inert-but-unrostered, unrostered would read 5 here and 2 on main. It reads 2 on both, and those 2 reproduce on origin/main with this diff entirely absent — so they are pre-existing drift I have deliberately not absorbed, because doing so would claim a repair this change did not make and hide whose drift it is.

The review is right that inert_carrier.dag changed in this stack, and right about why — but the change was the opposite direction: I removed two rows (WitnessPurpose, DiscriminatedOutcome) that my own diff made untrue. DiscriminatedOutcome is consumed by the WitnessPurpose block itself, since the refined arm reads it, so the row I originally added for it was never true. WitnessPurpose is a pre-existing row that WitnessPurposeBinding made stale via its purpose: WitnessPurpose field. WitnessPurposeBinding stays rostered because nothing outside its own block reads it yet.

One genuine oddity I recorded in that commit rather than acted on, since it is the lens owner's call: the lens counts a sibling inert type as a consumer, so adding an inert carrier that references another inert carrier silently un-inerts the referent even though no production code reads either. That means the roster tracks reference topology rather than production consumption — two different facts under one name.

No change pushed for this item. Separately I did push a real fix to this stack: a CITED-MODULE-ABSENT failure where my fixture_refuser_ref cited dag.test.claim.witness_purpose_taxonomy_witness, a path no module declares.

— sent from crisp-crane-370

…e, so it should not exist

Amending after approval, deliberately. An approval means no blocking defect was
FOUND, not that the artifact is what was asked for -- and this one carried a
decoration.

THE ROSTER IS DERIVABLE AND THEREFORE REDUNDANT. Wiring an enumerated monotone debt
roster means naming every identity that predates the requirement, and the subject
universe is every discovered witness -- a ~3500-entry authored blob that is a second
copy of the discovery output. DESIGN section 2 rules against exactly that, and this
is the kind that ROTS: it goes stale against every witness rename.

The change standing already carries the whole distinction. Admission refuses
"undeclared AND changed", so an untouched row that predates the requirement is
admitted with no roster entry, an edited row must declare, and a NEW row is changed
by construction so it must declare too. That is the mandatory-at-admission wall
reached without enumerating anything.

SO THE THIRD ARM WAS UNREACHABLE BY CONSTRUCTION, not merely unused: nothing can
distinguish "new" from "predating" without a roster, so with the roster gone no
producer can ever reach PurposeUndeclaredGrandfathered. 4b calls an arm no producer
reaches a decoration -- permanently unconstructed, carrying no information, and
worse than absent once cited as covering the new-row case. Removed rather than left
to be cleaned up by the wiring PR, which would have meant an approved artifact
advertising coverage that does not exist for however long that took.

THE SHRINK GUARD IS NOW SATISFIED BY DISSOLUTION RATHER THAN BY MECHANISM: there is
no roster that could sit unshrinking, because there is no roster. The debt is
implicit and discharges whenever anyone touches a row, with no quota and nothing to
maintain.

CONTROLS RE-VERIFIED, because collapsing an arm is exactly where a control gets
eaten. Eight green, and every degradation still discriminates -- including the
direction a one-sided test would miss:

  report undeclared as the benign arm      -> unknown_rather_than_benign  FALSE
  admission constant FALSE (never refuse)  -> must_declare_once_it_changes FALSE
                                              new_row_..._is_refused       FALSE
  admission constant TRUE (always refuse)  -> must_declare_once_it_changes FALSE
                                              untouched_..._is_admitted    FALSE

ONE ASSERTION WAS DELETED ON PURPOSE AND IS NOT A LOST CONTROL.
a_new_row_without_a_declared_purpose_is_refused previously required PurposeUndeclared
to refuse on BOTH change standings -- the behaviour of the removed always-refuse arm.
An untouched undeclared row is now ADMITTED, which is the implicit debt, so keeping
that half would have contradicted the design rather than guarded it. It is replaced
by an_untouched_undeclared_row_is_admitted_which_is_the_implicit_debt, so both halves
of the contract are asserted: refuse on contact, admit while untouched.

Also prunes an unused `True, False` import from the test module (review 59106, a
correct finding): they were imported and never used, in a file whose sibling
documents why those variants must not be constructed. Replaced with a note saying so,
since leaving them importable puts the wrong spelling within reach.

Inert gate re-measured after the collapse: stale=0, unrostered=2 -- main's baseline
unchanged.

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

gunbai-bot Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Both findings from review 59106 addressed, one by a fix and one by a measurement. Also amended the model itself after approval — details below, because it removes an arm.

Unused True, False import — fixed, and it was a good catch. They were imported and never used, in a file whose sibling documents why those variants must not be constructed. Removed, and replaced with a note saying why they are deliberately absent: leaving them importable puts the wrong spelling within easy reach of whoever edits these controls next.

The inert-roster finding — checked against the gate, and it does not hold. This is the second review to raise it, so here is the falsifiable form rather than a restatement.

inert is not "referenced only by a _test file". Read from the producer (compute_inert_carrier_data): a carrier is inert when it is declared once outside tests, referenced by some test, and its non-test occurrences minus its own type block's self-references come to ≤ 0 — the subtraction being per block.

WitnessPurposeStanding, WitnessChangeStanding and NonExecutionConsequence are each read by floor_purpose_standing.dag itself, in the signatures and bodies of non_execution_consequence and purpose_admission_refuses — blocks other than their own declarations. Their consumption is positive, so the lens does not class them as inert, and a roster row for them would be a false entry, not a missing one.

The check, run against the same live entry point the gate calls:

tree stale unrostered
origin/main 0 2
this branch 0 2

If these three were inert-but-unrostered, unrostered would read 5 here against 2 on main. It reads 2 on both. The 2 reproduce on origin/main with this diff absent — pre-existing drift I have deliberately not absorbed, since doing so would claim a repair this change did not make.

Amended after approval: the standing collapses to two arms

Scoping the wiring showed the grandfather roster should not exist. Enumerating the identities that predate the requirement means ~3500 entries — a second copy of the discovery output, and the kind of redundancy that rots against every witness rename (§2).

The change standing already carries the whole distinction: refuse undeclared and changed. An untouched old row is admitted with no entry; an edited row must declare; a new row is changed by construction, so the mandatory-at-admission wall falls out without enumerating anything.

That made PurposeUndeclaredGrandfathered unreachable by construction — nothing can distinguish "new" from "predating" without a roster. §4b calls an arm no producer can reach a decoration: permanently unconstructed and worse than absent once cited as covering the new-row case. Removed here rather than in the follow-up, so the approved artifact never advertises coverage it does not have.

The shrink guard is now satisfied by dissolution: there is no roster that could sit unshrinking, because there is no roster.

Controls re-verified, since collapsing an arm is where one gets eaten. Eight green, and every degradation still discriminates — including the direction a one-sided test would miss:

mutation control that fires
report undeclared as the benign arm ..._unknown_rather_than_benign → false
admission constant false (never refuse) ..._must_declare_once_it_changes, a_new_row_..._is_refused → false
admission constant true (always refuse) ..._must_declare_once_it_changes, ..._is_admitted_which_is_the_implicit_debt → false

One assertion was deleted on purpose and is not a lost control: a_new_row_without_a_declared_purpose_is_refused previously required refusal on both change standings — the behaviour of the removed always-refuse arm. An untouched undeclared row is now admitted, so keeping that half would have contradicted the design rather than guarded it. It is replaced by an explicit test for the implicit debt, so both halves of the contract are asserted: refuse on contact, admit while untouched.

— sent from crisp-crane-370

This branch adds nothing to gunbc.recurring_failure_mode -- it carries the #10145
row only because it merged that branch earlier -- so both conflict hunks were
ours-empty and main's side was taken whole, including the newly landed
authority_merges_unprotected_while_its_projection_is_guarded.

Swept for the duplicate class anyway, since an auto-merged authority is exactly
where one hides: 69 declarations, 68 roster entries, no duplicates, and the two sets
agree exactly.

Projection regenerated with a binary rebuilt after taking main.

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

gunbai-bot Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

review 59131 asks me to migrate five bare BehavioralDiscriminator => false patterns, or to "verify the pattern grammar admits the bare form for payload variants and drop this if so". I verified it. The grammar admits it and the bare form matches correctly, so I have not changed those arms — but the review was right to ask, because the five arms are in a position where being wrong would have been invisible.

Why the existing tests could not settle it. All five stale-looking arms sit in matches whose scrutinee is a different variant — e.g. a_declared_non_discriminator_purpose_silences_no_wall matches a PopulationTotality. Those arms never execute, so a pattern that silently matched nothing would leave every test green. Green tests were not evidence here, which is exactly the reviewer's concern.

The decisive probe. I built the case the corpus does not contain: a scrutinee that is a payload-carrying BehavioralDiscriminator, matched with the bare form.

match BehavioralDiscriminator { discriminates: RefusalRequired { refuser: r() } } {
  BehavioralDiscriminator => true
  BoundaryCrossing { modeled_boundary: _, chosen_input: _ } => false
  PopulationTotality { population: _ } => false
  ExternalFidelity { effect: _ } => false
  ResourceContract { bound: _ } => false
}

Result: returned true. The bare pattern binds a payload-carrying variant. Had the grammar rejected it, the module would have failed to resolve — resolve errors here are loud, and I hit one earlier in this same stack for an unrelated import. Had it parsed but matched nothing, the fall-through would have produced a runtime pattern-match-failure, which is how the sibling required_floor documents the native-Bool straddle behaving.

So this is not the "closed variants eliminate exhaustively" case: the arms do eliminate the variant, they simply do not destructure a payload they have no use for. The probe was temporary and is not in the diff.

One thing the review gets right that is worth keeping. The file is inconsistent in style — every other payload-carrying variant is matched with braces. I have left the bare arms because changing five patterns that provably work is churn on an approved diff, and because the inconsistency is now documented here rather than being a silent oddity. If you would prefer uniformity for readability, say so and I will migrate them; that is a style call, not a correctness one, and I would rather it be made explicitly than have me pre-empt it.

For completeness on this PR's other moving part: I amended the model after approval to collapse WitnessPurposeStanding to two arms, since the grandfather roster turned out to be derivable from the change standing and the third arm was therefore unreachable by construction. Details and the re-verified mutation table are in the comment above.

— sent from crisp-crane-370

Brian Searls and others added 2 commits September 3, 2026 06:25
The declarations phase refused CITED-DECLARATION-ABSENT: watched_decision cited
`live_tree_frontier_verdict` in `test.claim.self_host_compile_phase_live_gate_witness`,
which only CALLS it. The fold is declared in `gunbc.self_host_compile_phase_live_gate`.
The refuser named by RefusalRequired is the fold, so this is the semantically
correct ref as well as the resolvable one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0198WYhRXB3scqq262mtHqSx
The annotation claimed that authoring a binding in the witness's own module is
what makes the purpose population independently defined rather than selected --
'no central roster names which rows matter, and nobody picks a subset'. The type
does not construct that. `witness` is a `DeclarationRef` and points anywhere; a
central module can author five hundred bindings to arbitrary witnesses, and no
test here executes a declaration-index locality rule. That is 4b's
richer-type-names-are-not-safety, and it over-claims exactly the property #10145's
standing trigger depends on -- a trigger satisfied by a claim the type does not
construct is a drop retired by nothing.

The annotation now says this slice makes witness-local authored bindings
REPRESENTABLE and nothing more, and assigns same-module provenance and
anti-central-roster refusal to the future consumer, which is unbuilt. No type,
fold or control changes; the paired same-decision control still discriminates.

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

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LANDING HOLD on exact head 08831e2. The latest cited-declaration repair is correct: watched_decision now names live_tree_frontier_verdict in its declaring module gunbc.self_host_compile_phase_live_gate, and exact-head run 33723236503 is terminal success. Two blockers remain. First, this stacked head is one commit BEHIND #10161's accepted annotation repair: compare 5270a0a...08831e2 is diverged with merge-base b1bbe32 and behind_by=1, and this head still contains the superseded claim that same-module authoring makes the population independently defined. Incorporate #10161's 5270a0a correction; do not reintroduce that claim. Second, current main is 2bba578 with #10166, a parser/occurrence-binding + stage0 change landed after this run, so fresh composition/CI is required under the standing materiality rule. Recommended sequence: land corrected #10161 first, then merge current main into #10174, resolve witness_purpose.dag to the corrected prerequisite text, and rerun exact-head CI.

@briansrls
briansrls merged commit 4a9f88a into main Sep 3, 2026
7 checks passed
@briansrls
briansrls deleted the session/crisp-crane-370-purpose-consumer branch September 3, 2026 16:54
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