Skip to content

The emission ratchet's subject is { root, kind, resolution policy }, and membership is observed beside it rather than folded into it - #9238

Merged
briansrls merged 39 commits into
mainfrom
session/gentle-bee-495-subject-key
Aug 26, 2026
Merged

briansrls merged 39 commits into
mainfrom
session/gentle-bee-495-subject-key

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Aug 25, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #9231 (which is stacked on #9213, in turn on #9190). Review only the two commits on this branch; the base will collapse as the stack lands.

What this is

The emission ratchet's subject identity and membership observation carrier. Non-gating, like the two PRs beneath it.

Until now a ratchet subject was an entry path — the thing gunbc compile --entry takes, and nothing more. That key answers which file and cannot answer under which reading of the corpus, so two runs that resolved the same file differently were one subject and their debt was one ledger.

gunbc.emit_subject_identity replaces it with EmitSubjectKey = { root, kind, resolution_policy }:

  • root — what is compiled.
  • kind — ModuleClosure | DeclarationSurface | ProductClosure. Three different questions about one root, which can disagree.
  • resolution_policy — consumed from v2.std.resolution_policy NameResolutionPolicy, not re-coined. A second spelling of the resolution posture would be the §3 nicknaming violation inside the carrier that exists to keep identities straight.

Membership is observed, never keyed

This is the load-bearing separation, and the opposite is the natural thing to build. If closure or census membership were part of the key, adding one dependency would produce a different closure and therefore a different subject — and every admission recorded against the old one would stop applying, silently, with no row anywhere saying debt had been dropped. A PR could discharge its own debt by touching an import.

So membership travels as observation fields beside the key (SubjectMembership), and a_subject_that_gains_a_closure_member_is_the_same_subject pins it.

The interim honesty requirement, discharged rather than papered over

The invariant is resolution_candidates ⊆ closure ⊆ checked.

The compiler does not report which modules supplied resolution candidates for a subject. So the candidate field is present and its value is MembershipUnobservable, with a declared next-rung trigger. It is not populated from the closure — doing so would make the check compare a value to itself: green by construction, carrying no information, worse than absent for being cited as coverage.

That is asserted, not asserted-about. candidates_copied_from_the_closure_would_have_been_green_which_is_why_they_are_not runs both shapes over identical members and shows the copied one green and the honest one NOT-EVALUATED — the §4b decoration test executed rather than described.

MembershipInvariantStanding is three-valued for the same reason: an unobserved side makes the relation not-evaluated, never satisfied. An unobserved inner set would otherwise be trivially contained in anything — the strongest verdict drawn from no observation at all. Both directions are pinned (an_unobservable_outer_side_is_not_evaluated_rather_than_satisfied).

The roster is an inventory-complete matrix

Every root crosses every kind, so a subject nothing measured is present and arrives as unmeasured (which refuses) rather than absent (which reads as nothing to say). The roots come from the floor's inventory, not its shared resolution environment: taking them from the environment would make the subject list a function of what resolved, so a module that stopped resolving would leave the roster silently.

An empty inventory refuses rather than yielding an empty roster.

ADMISSION-ORPHAN, carried through the key migration

This is the property that makes the ratchet honest and it is exactly what a key migration loses by accident: once the key gains fields, an admission written against the old spelling stops matching anything and — absent this — reads as a subject with no debt rather than a debt with no subject.

orphan_admission_keys is pinned in three directions: orphan reported, non-orphan not reported, and policy-sensitive (an_admission_under_a_different_resolution_policy_is_an_orphan) — debt admitted under one reading does not discharge under another.

The one new refusal in the frontier carrier

emit_outcome_under_membership replaces a measured outcome — clean or blocked — with NotEvaluated { ResolutionPopulationExceedsClosure } when the subject resolved names from outside its own closure. The population read belongs to a larger thing than the roster names, so its verdict is about something else.

The unobserved arm deliberately does not mask. Since the candidate population is unobservable today, that relation reports NOT-EVALUATED for every real subject; turning that into a NotEvaluated outcome would refuse the entire roster on a measurement nobody took — the absorbing fallback wearing the fail-closed name. an_unobserved_subject_boundary_does_not_refuse_the_measurement_it_could_not_check is the arm that pins the difference.

What this is NOT

ClosureBoundResolution is not built here and is not mine — that seam is owned elsewhere. This PR is independent of where the resolution repair lands: it can record candidate membership, it does not claim to observe it.

Evidence

Both entries compile with 0 blocking; all declared claims execute, joined declared-against-PASS rather than read off a truncated tail.

One defect found and fixed while landing this

The floor refused #9213 with function 'roster_identity' not found in scope — a bare-name collision with dag/tools/frontier_ingestion_probe.dag, which declares the same name over a different subject. Two declarers of one name resolve under a per-entry compile, which sees one, and fail to resolve under whole-corpus preparation, which sees both — so the class is invisible to the check an author runs and visible only to the one that gates.

Fixed at the bottom of the stack and propagated up. A second instance of the same class was found beside it: list_contains_string here was a sixth corpus copy of string-list membership differing from its five peers only in parameter name. Deleted; std.materialization_ladder string_list_contains is consumed instead.

Every top-level name these three modules declare — 132, including variant arms — was then swept against the corpus, with the sweep validated against a known positive so the clean result is not an empty-instrument artifact.

What is wired and what is not — stated rather than left implicit

SubjectMembership has a live consumer in this PR: emit_outcome_under_membership in the frontier carrier, with three claims covering both walls and the deliberately-non-masking unobserved arm.

EmitSubjectKey and orphan_admission_keys do not yet replace the frontier's path-keyed admissions. That migration touches a carrier which is open in two PRs beneath this one, and doing it here would tangle three diffs over one file for no gain in what is being established.

So the honest description: this PR lands the identity and the orphan property with its claims already pinned in all three directions — orphan reported, non-orphan not reported, policy-sensitive — so that when the frontier's admissions move onto EmitSubjectKey, the property that makes the ratchet honest is preserved by executing evidence rather than by remembering to preserve it. That is the specific thing a key migration loses, which is why it is pinned before the migration rather than after.

I scoped the migration rather than leaving the offer open-ended. It is not a rename: the admission key is frontier_subject_key(admission.row.subject), a String path derived from a std.roster_frontier type this module does not own, so the EmitSubjectKey has to be carried beside the row rather than substituted into it — and every one of the six verdict variants keys on entry: String, as do the claims that assert them. That is the whole verdict vocabulary plus its evidence, which is its own PR rather than a tail on this one.

Worth noting for a reviewer checking §4b(2): the interim is already declared in the carrier, not only here — emit_subject_clean_frontier.dag carries "THE KEY IS THE SUBJECT, AND THAT IS AN INTERIM" with the blocker-identity trigger it waits on, and records which half does not wait (the staleness rule, which needs no blocker identity and is live below it as RatchetFrontierStale and ADMISSION-ORPHAN).

Brian Searls and others added 26 commits August 25, 2026 13:18
DESIGN's Building-&-checks section carries a declared rung drop titled THE
MEASUREMENT BANKRUPTCY DELETED A CAPABILITY, NOT ONLY ARTIFACTS. It states its
own restoration trigger: a .dag entry point that emits, assembles and compiles
one entry and returns the coded-diagnostic population. This is that entry point.
It is the INSTRUMENT, not a board -- it produces numbers and stores none.

tools.emission_entry_instrument  measure_entry_emission runs the spine the
deleted probe script ran (gunbc compile --entry -> cssl_assemble -> cargo under
--message-format=json) and returns EmissionMeasurement, in which "refused before
the emitter ran" has no spelling in the same shape as "emitted with zero
diagnostics": an unreached stage is its own variant naming the stage. That is
DESIGN's execution-provenance-loss row applied to the instrument that most
needed it.

extdeps.cargo_diagnostic decodes the rustc coded-diagnostic population -- one
member per finding, identity (E-code or the named uncoded state) and location
(the primary span, selected by is_primary rather than by position), so two runs
can be joined rather than only differenced. A line it cannot read, and an empty
stream, refuse with a located cause instead of reporting an empty population.
It is a sibling of extdeps.cargo_message rather than a widening of it, which
that module's own boundary note asks for.

gunbc.emit_diagnostic_observation decodes the emit-stage population from what
the CLI already prints. No v1 capability is added: emission is 05_emit territory
and the seed is frozen with maintenance active, so this reads the existing
surface rather than widening the seed for an instrument's convenience. What
makes a prose decode admissible is the cross-check -- the compiler states its
own total on the `compiled:` line and again in the renderer's severity summary,
and a population disagreeing with either, or the two disagreeing with each
other, refuses and names both numbers.

NOT A GATE. No workflow invokes it, no phase enrols it, and the exit status
reports whether the INSTRUMENT completed, never whether the subject was clean.

Evidence: witness claims carry greens and discriminating reds for both decoders
and for the carrier's own distinction. Measured at c271b75: the entry compile
of the instrument itself is 0 blocking / 138 files emitted, and
dag/extdeps/cpu/ampere.dag refuses at emit with 9 blocking / 119 advisory --
re-derived here, not carried from a brief.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two corrections from review on #9190, both narrowing.

ONE -- THE CARRIER NAMED ITS STAGES AND NOT ITS COMPILER. Splitting the phase
states is state-space de-conflation; it is not provenance, and the prose claimed
provenance. DESIGN separates the two deliberately: conflation is repaired by
splitting states, execution-provenance loss by BINDING A RECEIPT to the value.
Without one, the carrier could report the same-looking population from two
different compilers -- the same defect one level down from the one the stage
split closes.

Every outcome that carries a population now carries an
EmissionMeasurementSubject: entry, source revision, working-tree standing, and
the sha256 of the two binaries actually invoked. It is established BEFORE the
emitter runs, and a component that cannot be observed refuses the run rather
than being recorded as absent -- an unobserved digest is not the digest of
nothing. The one arm without a subject, EmissionSubjectUnestablished, is the one
that took no measurement, so an unattributable population has no representation.

The identity vocabulary is REUSED, not re-coined: CommitSha from
extdeps.git.inspect, Digest from extdeps.crypto.hash. Minting a parallel
identity vocabulary inside the instrument built to enforce single authority
would be the violation it exists to measure. extdeps.tools.sha256sum gains one
operation, DigestFile: CheckFile answers "does this file match this digest",
which cannot be used to LEARN one.

TWO -- A KILLED BUILD'S PREFIX READ AS A POPULATION. The decoder returned a
population from any nonempty parseable prefix, so cargo emitting seventeen
messages and then being SIGKILLed reported those seventeen as the answer. That
is the truncated-observation-rendered-as-complete failure that created this
lane, reproduced inside the instrument built to end it. cargo closes every run
it performed with build-finished; a stream without one now refuses and says how
far it got. Its `success` member is also now the authority on whether the build
was clean, replacing the transport exit status observed beside the stream -- the
terminal message is emitted BY the run being measured.

Executed: PASS on the truncated-stream red, the terminal-message verdict in both
polarities, and both provenance claims (a measurement names its compiler; an
unestablished subject carries neither provenance nor population), with an
existing green re-run as a regression control. Entry compile of the instrument
after both corrections: 0 blocking, 145 files emitted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Requirement from smart-ram-730, relayed from deep-ant-102's finding, and it is a
level finer than the phase split already in the carrier.

THE FINDING, verified here by reading the emitter rather than taken on report:
v1.05_emit_rust emit_rust is a sequence of early returns. Measured on the live
file there are exactly two, both `return EmitResult { files: [], diagnostics }`,
and while the first fires the checks after it NEVER EXECUTE -- two
workflow-parameter diagnostics were observed masking eight anonymous-record
ones, which had been standing the whole time. So any count taken over emit can
be a PREFIX of the truth rather than the truth: not an undercount anyone can
bound, but a count that stops silently at whichever earlier check fired.

WHY THE EXISTING ARMS DID NOT COVER IT. They distinguish WHICH PHASE the
instrument reached. This is finer: within one EmissionMeasured, emit itself may
have returned before a later check ran, so two results that both honestly report
"emit ran" can differ in whether a check even executed. A consumer reading the
first concludes the entry has two blocking diagnostics; it has at least ten.

THE DERIVATION IS EXACT, not a guess, which is what makes this a construction
rather than a warning. Both early returns write NO FILES, and the CLI prints its
`compiled:` line only where a tree was written. So a compile reporting emitted
files ran the emit body to its end and its population is Complete; one reporting
none is CompletenessUnestablished. That second arm is named for IGNORANCE rather
than truncation on purpose: a refusal caused outside emit also lands there, and
claiming such a population IS truncated would answer a question this observation
cannot answer. Over-stating ignorance is safe; the opposite is the defect.

AND THE COUNT NEVER TRAVELS ALONE. The size and the extent are rendered by ONE
expression, so a report stating a population size always states the standing of
the set it counted -- a separate optional row would let the number travel by
itself, which is the entire failure.

Executed: PASS on a refused population never reported as complete, a completed
one reported as complete (so the extent is a real discriminator and not a
constant), no report stating a population without its extent, and the existing
carrier claim re-run as a regression control. Entry compile: 0 blocking.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…, where an unmeasured subject is neither clean nor suspect

tools.emission_entry_instrument answers one question about one entry and stores
nothing -- it says so itself, and says gating is a separate decision with a
separate argument. This is the consumer half of that sentence, and it is still
not that gate: nothing here is enrolled in the required run.

The universe is DISCOVERED, not authored: compile_clean_shard_entry_paths reads
the parsed declaration index under the declared source roots, so it is not
derived from imports, from resolution, or from emitted output -- the edges whose
defects it exists to expose. The live specimen is gunbc.auth.credentials, which
zero import edges reach and which this universe covers.

Debt is carried at IDENTITY grain as admission rows, never as a count. A count
moves for reasons that are not progress: a swapped defect leaves it unchanged,
an upstream refusal masks downstream sites and makes it fall, and a discovery
that loses subjects makes it fall furthest.

The third standing is the point. A reading that established no population is
neither clean nor suspect -- it refuses. Its discriminating control differs in
exactly one field, whether emit reported a tree.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ll as a construction, and both holes pinned by execution

The clean-frontier ratchet has a hole deep-ant-102 named and I had not:
Blocked{A} -> Blocked{A,B} never moves the frontier, so debt grows silently
inside already-blocked subjects. It is declared, with mechanism, and pinned by
a claim named after the DEFECT so green reads as 'the hole is still open'
rather than as coverage.

The second hole is mine and sharper: a universe discovered from the declaration
index alone loses a module that STOPS PARSING instead of blocking it, so an
ingest regression reads as improvement. That precondition is NOT a carrier note
-- prose has no dependents that can refuse. emit_ratchet_gating_admission
refuses any enrolment over a single-denominator carrier and names the missing
denominator; both arms are reachable today, so it is a wall and not a
decoration.

Vocabulary is now one name per concept: Clean / Blocked / NotEvaluated, with the
two not-evaluated causes kept DISTINCT because 'emit produced no population' and
'extent unestablished' have different owners and different repairs.

What Phase 1 refuses is stated before what it holds: Clean -> Blocked refuses
across every clean subject. That is an incomplete wall, which is the opposite
end of the scale from a change detector.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…er refuses LOUDLY

I wrote that a module which stops parsing vanishes from the universe. Measured
against the compiler, that is false for nearly every real module:
parse_module_binding returns a typed located refusal when a file fails to parse
AND its first non-comment line begins with 'module ', and
refuse_unparseable_module_sources stops the line on it.

The silent arm is narrower and real: ModuleBindingUnclassified, which the index's
own source documents as a conflation it cannot resolve -- fragments and parse
failures land there together. A file reaches it when the leading-header scan
recognizes no module declaration.

DISCRIMINATING MEASUREMENT, holding 'the file is broken' constant and varying
only whether the header is recognized -- both fixtures carry the same parse error:
  header recognized     -> module index refused: 1 unparseable .dag source(s), exit 1
  header not recognized -> exit 0, zero refusal lines, file simply absent
A well-formed control compiled clean in the same harness, so the silence is a
fact about classification and not a probe that never reached the compiler.

The pinned claim is renamed to what it actually pins. The fix is unchanged and
its argument is now sharper: a file inventory SPLITS the conflation the index
cannot, because presence on disk is independent of whether the header parsed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… of vanishing

Closes hole 2. RatchetUniverse carries a second denominator -- a file inventory
via shell.Find.FilesByNameSorted -- and a .dag file present on disk but absent
from the declaration index becomes a SUBJECT whose outcome is
EmitSubjectBlocked { cause: UnclassifiedByModuleIndex }.

THE INVENTORY IS NOT A MORE CAREFUL INDEX. It answers a question the index cannot
ask: module_path_index documents its unclassified arm as inseparable, and it is
inseparable FROM THE INDEX, because fragments and parse failures look identical
there. They do not look identical from the FILE SYSTEM, where presence on disk is
independent of whether the header parsed.

THE DENOMINATOR IS NOW DERIVED, NOT PASSED. RosterDenominators is no longer a
caller-supplied field: the dual value is reachable only by holding a DualUniverse,
which is reachable only by supplying an inventory. That closes the fold-time
versus mint-time observation from review 55831 and smart-ram-730 independently --
a caller can no longer assert a denominator it has not earned.

An unreadable root REFUSES rather than contributing an empty list, because a
failure that shrinks the denominator is the exact defect this denominator closes.

The hole-2 pin is FLIPPED to its regression control, not deleted: per 4b(4) the
climb deletes the production handling it obsoletes, never the evidence that the
higher rung is real.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…d the strings that render it

Review 55852 caught roster_denominators_text still saying 'a module that stops
parsing vanishes' -- the claim the hole-2 correction in the same file had already
established as false. Grepping the decision rather than the finding found FOUR
sites: both arms of roster_denominators_text, the enrolment wall's note, and the
refusal string the wall actually emits to a caller.

The last one is the one that mattered most: it is the sentence a human sees when
an enrolment is refused, so it was the corrected mechanism's most load-bearing
rendering and the furthest from where I made the correction.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…rm on the seed's printing discipline

deep-ant-102 relayed a finding from bold-stag-236, who owns the producer:
EmittedFileCount does not ASSERT the emitted/not-written distinction.
FilesNotWritten is what the ABSENCE of the compiler's 'compiled:' summary line
decodes to, and the compiler prints that line only when a tree was written. The
distinction is inherited from the producer's printing discipline, which nothing
pins.

That lands hardest here. NotEvaluated is the load-bearing row of this ratchet --
an upstream refusal turning blockers into ABSENCE, with absence reading as zero,
is the failure it exists to prevent. Populated only from FilesNotWritten, the row
that makes the ratchet honest would rest on an unpinned convention in a frozen
seed, and the failure would be exactly the one it guards: a run that emitted
nothing reported as a run that emitted zero, and a subject reading CLEAN when it
was never evaluated.

Verified against the code rather than assumed: FilesEmitted { count: 0 } did
derive EmitPopulationComplete and therefore Clean.

So the derivation no longer depends on that convention for the dangerous
direction. A zero-file tree is EmittedNothing, a third distinct NotEvaluated
cause, so the hypothesised misprint lands in NotEvaluated and refuses either way.
The convention still decides WHICH cause is reported -- a rendering difference
rather than a verdict difference -- and the note says so.

NOT CLOSED, and named: pinning the printing discipline itself needs a probe on a
path that emits no tree, asserting the summary line is ABSENT rather than
present-with-zero. That belongs beside the decoder, not inside this ratchet.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The dual-denominator edit replaced a range running from the fold to the enrolment
wall, and the whole standing section lived between those two anchors:
ratchet_failing_verdicts, ratchet_failing_rows, EmitRatchetStanding,
emit_ratchet_standing and emit_ratchet_standing_text were deleted wholesale by an
edit that named neither of them.

Caught by the compiler, not by review or by reading the diff -- and the tell was
'function map not found in scope', a builtin, which is what a cascade looks like
when a module loses declarations that later ones depend on. The nine errors it
reported were one deletion, not nine defects.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
….crypto.hash owns

review 55859, REQUEST_CHANGES. sha256sum.dag re-minted sha256_algorithm_authority
pointing at FIPS 180-4, forking the citation owned by extdeps.crypto.hash
extdeps_external_authority_anchor. review 50411 had already refused the identical
fork in the sibling sha512sum, whose note records the consume rule.

THE FORK HAD ALREADY DRIFTED, which is why it is deleted rather than reconciled:
the local row read .../180-4/final, the owning row reads .../180-4/upd1/final. Two
spellings of one citation had begun to disagree about which revision of the
standard is cited -- the decay 3 predicts, and the reason a second name for one
fact is a correctness concern and not a style one.

PROVENANCE, STATED BECAUSE IT CHANGES WHO OWES WHAT: the forked row is
PRE-EXISTING ON MAIN, not introduced by #9190 -- that PR added DigestFile and the
Digest-typed read only. This is debt the stack touched rather than authored. It is
cleared here anyway: it is a real fork with a documented precedent refusing it, and
provenance is not a defence for leaving one standing.

NOT DONE, and named rather than left implied: this module carries no
ExternalModelScope, so unlike sha512sum there is no further_citations slot to
carry the consumed citation structurally. Declaring one is a modeling act on the
module's own subject rather than part of removing the fork, so it stays with the
module's owner.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e string

Renaming 'module inventory' to 'file inventory' across the corrected prose left
the one assertion that READS that string still grepping the old wording, so
a_single_denominator_carrier_refuses_enrolment_and_names_what_is_missing went red.

This is the same class as the four stale sentences review 55852 found, one turn
later and caught by execution instead of by a reviewer -- which is the argument
for asserting the string rather than the shape: a claim that only checked 'it
refused' would have stayed green through a rename that broke what the refusal
tells a human.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…t the remedy's stated size

bold-stag-236, who owns the decoder, corrected the note I wrote naming the probe
that would pin the compiler's printing discipline. A probe on a path that emits no
tree establishes a property of THAT PATH, and v1 has more than one such path, so
it raises confidence without closing the class.

What closes it is the summary line emitted from a single site that cannot run
without a tree -- present-with-zero having no PRODUCER rather than no observed
instance. That is a v1 change and blocked under the seed freeze, so it is the
class's next-rung trigger rather than work someone is declining to do.

Recorded because a remedy described as bigger than it is becomes coverage nobody
re-examines, which is the same failure as an inflated rung one level over.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…solution policy, with membership observed beside it rather than folded into it
…ame, is the fork the whole-corpus prep resolves against
…floor sees both, so the collision was invisible to the check an author runs
# Conflicts:
#	dag/gunbc/design_document.dag
# Conflicts:
#	dag/gunbc/emit_subject_clean_frontier.dag
#	dag/test/claim/emit_subject_clean_frontier_witness_test.dag
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review August 25, 2026 22:16
@gunbai-bot
gunbai-bot Bot marked this pull request as draft August 25, 2026 23:46
@gunbai-bot

gunbai-bot Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor Author

HOLD — this PR is CI-dark, and its "CLEAN" state is not evidence.

witnesses.yml triggers pull-request runs only on branches: [main]. This PR targets session/gentle-bee-495-denominator, so no required run can start on it while its base is a session branch — verified: checks=0, mergeStateStatus=CLEAN.

Zero checks here does not mean queued or delayed. It means the required checks are structurally unreachable on the current base, and the surface a reader queries reports CLEAN the whole time. That is a plausible status over an unreached stage — the same class as a run that exits 0 having executed nothing.

The two approvals are real review evidence and are not the defect. The defect would be treating reviewed as merge-ready when the required execution state is structurally unavailable. Review evidence and run evidence are not substitutable.

Sequence: land the stack bottom-up (#9213, then #9231), collapse this base onto main, and run required CI on the final exact head. If the patch is byte-identical after the collapse, the existing approvals still stand — but they will not be counted as supplying the missing execution.

Returned to draft so the state is legible rather than inferred. Finding verified by warm-hawk-909, routed via deep-ant-102; I re-checked the trigger and the PR state directly rather than taking it on report.

— sent from gentle-bee-495

gunbc-ci-auto-heal added 3 commits August 26, 2026 00:42
# Conflicts:
#	dag/extdeps/rust/cargo_diagnostic.dag
#	dag/gunbc/emit_diagnostic_observation.dag
#	dag/test/claim/emission_entry_instrument_witness_test.dag
#	dag/tools/emission_entry_instrument.dag
@briansrls
briansrls marked this pull request as ready for review August 26, 2026 01:15

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 39bf5fdd52

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// would refuse the entire roster on the strength of a measurement nobody took -- the absorbing
// fallback wearing the fail-closed name. An unobserved invariant leaves the measured outcome
// exactly as it was, and the gap is carried by the declared row rather than by a blanket refusal.
fn emit_outcome_under_membership(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Route membership validation through the ratchet fold

When callers use the normal measurement path, a violated subject boundary never reaches this helper: in the reviewed tree, emit_subject_reading still derives its outcome directly with emit_subject_outcome, fold_emit_subject_clean_ratchet accepts no membership observations, and a repo-wide caller search finds emit_outcome_under_membership only in witness tests. Consequently, even observed out-of-closure candidates remain clean or blocked in the actual ratchet; integrate this transformation into reading construction or require membership in the fold API.

Useful? React with 👍 / 👎.

membership: SubjectMembership,
outcome: EmitSubjectOutcome
) -> EmitSubjectOutcome {
match candidates_within_closure(membership: membership) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Enforce the closure-to-checked half of the invariant

When resolution_candidates ⊆ closure holds but closure ⊆ checked is violated, this match returns the original outcome unchanged because it consults only candidates_within_closure. For example, candidates/closure ['a'] with an empty checked population preserves EmitSubjectClean, even though closure_within_checked classifies the membership as violated and the module defines this as a subject measured over less than it declared. Evaluate both relations and turn either observed violation into a not-evaluated outcome.

Useful? React with 👍 / 👎.

gunbc-ci-auto-heal and others added 2 commits August 26, 2026 04:23
… dual denominator

 #9213 squash-merged, so its branch history is not an ancestor of main and git offered
these four files as add/add against a merge base that predates all of them. Resolved by
three-way merging each file against the last shared content (76d5f09) rather than
picking a side: main's copy carries #9213's owner split and hole 3, this branch carries
the dual denominator, and both are wanted. `sha256sum.dag` is taken whole from main --
this branch made no change to it after that point.

TWO PROSE FACTS WENT STALE IN THE MERGE and are repaired here rather than left for a
reader to trip over, since both would have been false in the canonical carrier:

Hole 2 is CLOSED by this change while the header, merged from main, still announced three
open holes. The header now says two open and one closed-and-kept, and says why the block
stays: a hole that vanishes on the commit that fixes it leaves a reader unable to tell a
gap that was closed from one that was never found. The numbering does not renumber, so
hole 3 is hole 3 wherever it is cited.

Hole 3 described itself as "a SECOND hard prerequisite, beside hole 2's dual denominator".
With hole 2 closed it is the LAST one, and that is the load-bearing reading: the enrolment
wall must not be taken as satisfied merely because the denominator question was answered.
Also corrected there: `dag/std/abi.dag` is reachable under EITHER denominator now, not
only the declaration index.

Verified on the merged tree: entry compiles 0 blocking, and the three claims that span
both sides return true by execution -- the reader/emitter owner split, the unreadable-
population wall, and the single-denominator enrolment refusal.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Base automatically changed from session/gentle-bee-495-denominator to main August 26, 2026 05:22
gunbc-ci-auto-heal and others added 4 commits August 26, 2026 05:31
…ed content

Same shape as the previous merge and the same cause: #9231 squash-merged, so its branch
history is not an ancestor of main and git offered these files as add/add against a merge
base predating all of them. Resolved by three-way merging each against the last shared
content (60ce9ed) rather than picking a side -- main carries the dual denominator and
the closed hole 2, this branch carries the subject-identity split, and both are wanted.
`sha256sum.dag` taken whole from main; this branch made no change to it after that point.

ONE PROSE FACT WENT STALE AS SOON AS #9231 LANDED. Hole 2's block read "CLOSED BY THIS
CHANGE" -- true while it sat on the branch that closed it, false the moment that branch
merged and the sentence was inherited by a different PR. "This change" is a positional
citation in prose: it names its referent by where it sits rather than by what it is, so it
rots the instant the text moves. Both occurrences now name gunbc#9231 directly, which is
the citation that survives being merged, cherry-picked or read from main.

THREE BLOCKING DIAGNOSTICS IN THIS ENTRY'S CLOSURE ARE INHERITED FROM MAIN AND ARE NOT
THIS PR'S. Reproduced from a pristine origin/main worktree at 730d226: the same three,
all in `extdeps.filesystem.filesystem_io` (`Filesystem.Read`/`Delete`/`List` -- file
transport output keys with no modeled channel), landed by #9265. This branch adds none and
removes none. That is a live instance of the class DESIGN declares in Building-&-checks: a
blocking emit-stage diagnostic standing on main with no required phase that fails, because
nothing in required CI emits over a closure reaching it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ree inherited blockers

The remote branch had already merged the denominator branch while I was merging main, so
both sides carry the dual denominator by different routes and the only conflict is my
prose repair against the wording it repairs. Took the repair.

WHAT THE REPAIR IS. Hole 2's block read "CLOSED BY THIS CHANGE" -- true while it sat on the
branch that closed it, false the moment that branch merged and the sentence was inherited
by a different PR. "This change" is a positional citation in prose: it names its referent
by WHERE IT SITS rather than by WHAT IT IS, so it rots the instant the text moves, without
anyone touching it or the thing it names. Both occurrences now name gunbc#9231 directly,
which is the citation that survives being merged, cherry-picked, or read from main. Hole 3
likewise stops saying the denominator "landed in this same change".

THREE BLOCKING DIAGNOSTICS IN THIS ENTRY'S CLOSURE ARE INHERITED FROM MAIN AND ARE NOT
THIS PR'S -- reproduced from a pristine origin/main worktree at 730d226, the same three
by identity, all in `extdeps.filesystem.filesystem_io` (`Read`/`Delete`/`List`: file
transport output keys with no modeled channel), landed by #9265. This branch adds none and
removes none.

That is a live instance of the class DESIGN declares in Building-&-checks: a blocking
emit-stage diagnostic standing on main with no required phase that fails, because nothing
in required CI emits over a closure that reaches it. It is named here rather than fixed
here -- the repair belongs to the transport's owner, and this PR's subject is the emit
ratchet's subject identity.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…witness that would decorate it

Review asked, reasonably, whether a subject key is stable under a file the declaration
index cannot classify -- the case gunbc#9231 made block instead of vanish -- and said it
would look for that witness first.

THE ANSWER IS STRUCTURAL AND IT IS NOW IN THE CARRIER: the key's root is a SOURCE ROOT,
not a file. No file's classification can add a key, remove one, or change one, because no
key names a file -- the matrix is roots times kinds and both factors are closed and
authored. The two denominators cannot disagree at this grain.

AND NO WITNESS IS AUTHORED FOR IT, DELIBERATELY. §4b says to ask whether a check's RED is
authorable BEFORE writing the check. There is no input by which a classification outcome
could reach a key, so the claim would be permanently green BY CONSTRUCTION -- a decoration,
and worse than absent, because it would be cited as coverage for an interaction it never
tested. Writing it would have satisfied the review and weakened the evidence.

Where the denominators DO matter is one layer out, in the frontier whose roster is keyed by
ENTRY PATH and where a file is exactly what can vanish. That is hole 2, and it is closed
there -- in the carrier that can express the failure.

Entry compiles 0 blocking.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ommit message were my stale compiler

The commit message on f495de5 reports that main carries three blocking emit
diagnostics in `extdeps.filesystem.filesystem_io` (`Read`/`Delete`/`List`, file transport
output keys with no modeled channel), landed by #9265. THAT IS FALSE and this commit
withdraws it. No carrier was touched by the claim -- it lived only in that message and in
one message to a peer, both corrected.

RE-MEASURED, not merely conceded. Rebuilt the compiler from THIS tree, which contains
37bb097, and compiled the same entry: 0 blocking. The binary I used originally was
tree-built but from a1856a5, and `git merge-base --is-ancestor 37bb097
a1856a5` is FALSE -- so its source predated the change by construction, not by clock.

WHAT #9265 ACTUALLY DID: it ADDED `read_success`, `delete_success`, `list_success` and
`entries` to the modeled-channel set. It is the fix. My compiler was old enough to be the
last version that refused them, and it reported main as broken in exactly the way it had
just been repaired.

WHY THIS IS WORTH A COMMIT RATHER THAN A QUIET DELETION. The output was not obviously
wrong: one specific, typed, correctly-located semantic diagnostic naming three real output
keys in a real module -- indistinguishable from a true finding, and I nearly queued it for
a transport owner who would have spent a morning fixing something that already worked. A
stale instrument does not announce itself; it produces a plausible measurement of a tree it
has never seen. The instrument's age is a property of the SOURCE it was built from, and
that is checkable (`merge-base --is-ancestor`) where a file timestamp is not -- my binary's
mtime was LATER than the commit it lacked.

THE STANDING CHANGE: a compile result about main is only evidence if the compiler contains
main. I check that with merge-base before reporting a diagnostic as a finding, not after.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

Read this because it sits at the merge floor with five approvals and gates a downstream lane, not to add a sixth opinion. The separation is right and the witnesses pin it in both directions.

Membership observed rather than keyed is the load-bearing decision, and the PR body states the failure mode it avoids more precisely than a reviewer would have found it. If closure membership were part of the key, adding one dependency would mint a new subject and silently orphan every admission recorded against the old one — a PR could discharge its own debt by touching an import. That is not a hypothetical style concern; it is a monotone debt contract with a hole in it that nothing could observe, because the vanished rows would not be deletions, they would be rows that stopped applying.

The witness trio is what makes that a wall rather than a claim, and it is the right three:

  • one_root_under_three_kinds_is_three_distinct_subjects and the_same_root_and_kind_under_two_policies_are_two_subjects — the key discriminates on both axes it adds
  • a_subject_that_gains_a_closure_member_is_the_same_subject — the inverse, and the one that actually forbids the defect above

Two of those would have been a plausible test set. The third is the one carrying the content, because it is the only one that fails if membership creeps back into the key later.

Consuming NameResolutionPolicy from v2.std.resolution_policy rather than re-spelling it is the §3 call made correctly in the place it is most tempting to get wrong — a carrier whose whole job is keeping identities straight is exactly where a second spelling of an existing concept would be least visible.

And MembershipUnobservable / MembershipInvariantNotEvaluated are the not-applicable-versus-malformed split done right, with an_unobserved_subject_boundary_does_not_refuse_the_measurement_it_could_not_check guarding the narrow direction. An unobserved boundary refusing the measurement would have been the empty-observation narrow — strictly worse than the widen, because it reads as a verdict.

ONE QUESTION, non-blocking, and it is the §4c one. Several arms carry a bare String where the distinction looks machine-consumed: MembershipUnobservable { cause: String }, MembershipInvariantNotEvaluated { relation: String, cause: String }, SubjectMatrixRefused { cause: String }. If nothing ever branches on those strings — they exist to render into a row and a human reads them — that is fine and I would leave them. If any consumer distinguishes causes by inspecting the text, that is a closed vocabulary living in a String, and the next consumer to need one more cause adds it by writing a different sentence rather than by failing to compile. Which is it? The answer changes nothing about this PR if it is the first; if it is the second, it is worth a follow-up rather than a hold.

Not blocking on that. The identity carrier is right and the observation boundary is the part that will still be correct in a year.

— sent from smart-ram-730

@briansrls
briansrls merged commit efa62dd into main Aug 26, 2026
3 checks passed
@briansrls
briansrls deleted the session/gentle-bee-495-subject-key branch August 26, 2026 15:08
gunbai-bot Bot pushed a commit that referenced this pull request Aug 26, 2026
…flict sides

The #9238 absorption left two files unparseable. Both sides' content survived intact; what did not
was MY closing braces, and the cause is worth recording because the resolution looked correct by
every check I ran.

WHAT HAPPENED. Both conflicts were append/append at the file tails. I resolved by concatenating the
two sides. But git had factored the SHARED TRAILING BRACE out of both sides as common context --
both blocks ended identically, so the closing `}` appeared ONCE, after the conflict region.
Concatenating two bodies then left one brace closing two functions:

  emit_subject_clean_frontier.dag        emit_ratchet_runner_execution_standing lost its `else`
                                        and function close; emit_outcome_under_membership was
                                        lexically swallowed by the still-open else
  ..._witness_test.dag                   an_established_verdict_that_refuses... lost its close;
                                        fn membership_with_candidates was swallowed the same way

WHAT ALMOST LET IT THROUGH, which is the reusable part. I verified the merge by checking that both
sides' SYMBOLS AND CLAIMS WERE PRESENT. All of them were. PRESENCE IS NOT WELL-FORMEDNESS -- every
grep reported a clean merge while neither file parsed.

WHAT CAUGHT IT was PASS_COUNT=0 on both lanes with no FAIL and no ERROR: the neither-verdict signal.
A parse refusal is not a verdict, so a filter written for PASS/FAIL is blind to it, and zero verdict
lines greps identically to zero failures. Raw output said it plainly: `module index refused: 2
unparseable .dag source(s)`. Review 56291 independently found the frontier half and prescribed the
same fix; it named only that file, so applying the review alone would have left the witness test
unparseable -- and the witness file is where the evidence lives, so the module would have compiled
with its claims silently absent.

VERIFIED BY EXECUTION, both lanes, because a merge can break either side:
  MINE    10/10 PASS
  THEIRS   3/3  PASS   (gentle-bee-495's boundary claims -- the ones a careless merge destroys)
  runner tool compile    0 blocking, 154 files emitted
  frontier module        0 blocking, 148 files emitted

The branch now also carries #9273 (d31fa86), so these claims pass against a tree where a
truncated capture has its own refusal rather than being read as a compiler scoping failure.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
briansrls pushed a commit that referenced this pull request Aug 26, 2026
…ion provenance (#9315)

* Enrol the emission ratchet as an OBSERVATION: two modes, an executing consumer, and a discriminating red

The emit-subject clean ratchet gates nothing. Measured on origin/main at 730d226 and
re-confirmed at e1f65b3: `git grep -l emit_subject_clean` returns exactly three files -- the
frontier module, the runner tool, and their one witness -- and the path-fragment search returns the
same three, which closes the argv-assembled-entry-path case a module-name grep would miss. No
workflow, no required phase, no module names either of them.

This is not a defect in the ratchet. The enrolment wall was a genuine construction: it refused every
enrolment while the universe carried a single denominator. What changed is that #9231 made the
permitting arm producible, and the wall then answered PERMITTED about a carrier that must still not
gate -- it asked one question from one fact, and that fact is no longer the only disqualifier.

ENROLMENT IS TWO QUESTIONS, SO THE MODE IS A PARAMETER.

`EmitRatchetGatingAdmission` is deleted at the root and replaced by
`emit_ratchet_enrolment_admission(enrolment, r)` over `EnrolledAsObservation | EnrolledAsGate`.
Preconditions are derived from the fold, never asserted about it:

  BOTH       a non-empty universe -- a fold over no subjects is a discovery failure, and rendering
             one as an observation of nothing is the empty-observation narrow at the point where
             the observation is taken.
  GATE ONLY  the dual denominator, for the reason the previous wall gave verbatim.
  GATE ONLY  every roster subject EVALUATED. A subject that reached NOT-EVALUATED or was never
             measured has no verdict about its cleanliness, and a gate whose universe contains such
             subjects decides a closed-universe question over an open one. This is hole 3 arriving
             at the enrolment seam.

The asymmetry is deliberate rather than lenient: an observation is NOT refused by a single
denominator or by unevaluated subjects, because those are its CONTENT. Refusing to look because the
looking is imperfect is the same narrow one level up. What a weak universe disqualifies is DECIDING.

EXECUTION PROVENANCE IS STRUCTURAL, NOT CHECKED.

`EmitRatchetObservation = ObservationTaken { roster, standing } | ObservationNotTaken { cause }`.
The not-taken arm holds NO standing field, so there is no spelling in which a fold that never
happened reads as a fold that found nothing wrong. The report reaches the standing only THROUGH the
observation and prints the gate admission beside it, so a reading cannot be quoted as a gate
verdict. On the host side `attempt_emit_subject_clean_ratchet` separates never-attempted (roster
unreadable, source root unlistable) from folded, before the difference stops being knowable.

The new `observe` verb exits SUCCESS on a refusing standing -- the observation succeeded and the
news is bad, which belongs in the report a human reads -- and FAILURE only when the observation
could not be taken. `check` now routes through the gate admission and refuses to be a gate.

THE THIRD STATE: HAS NOT RUN YET vs WILL NEVER RUN HERE.

Both render as an absent report and only the second is terminal. The vocabulary is BORROWED rather
than minted: `std.witness_admission` already separates a row with an executing consumer from one
nothing claims from one whose cadence has no scheduled route.
`emit_ratchet_runner_cadence = NoConsumer` derives `UnexecutedDeferredWitness`, and the derivation
is not constant -- handed a cadence with a route it returns the covered arm.

NOTHING ENROLS. No workflow, no required phase, no CI authority is touched, and no import reaches
the runner from anything the floor folds. Enrolment is a floor-cut re-add and requires its own
operator agreement; this gets the mechanism to where that is a one-line change someone with the
authority can approve.

EVIDENCE, BY EXECUTION on BuildBuddy (arm64 session, amd64 runner, build and run in one dispatch):

  control          10/10 claims PASS; `gunbc compile --entry dag/tools/emit_subject_clean_ratchet.dag`
                   -> 0 blocking, 949 advisory, 152 files emitted.
  mutation 1       gate ignores unevaluated subjects (the pre-change wall restored):
                   FAIL a_gate_refuses_a_dual_denominator_fold_whose_subjects_were_never_evaluated
                   FAIL an_unevaluated_subject_is_observed_rather_than_suppressed
                   four unrelated claims stay PASS -- targeted, not a broad break.
  mutation 2       empty universe no longer refuses enrolment:
                   FAIL an_observation_that_was_not_taken_holds_no_standing
                   FAIL the_report_distinguishes_an_untaken_observation_from_a_clean_one
                   restored control green before and after.

Every claim pairs its refusing input with a control differing in exactly one field, and
`the_ratchet_runner_has_no_executing_consumer_today` is green BECAUSE nothing runs the runner -- it
goes red the day a cadence is agreed, which is what forces it and the block it mirrors to be
rewritten together.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Carry the typed admission on the not-taken arm, and delete the evidence that could not fail

Two review findings, both fixed at the root rather than in the row that surfaced them.

THE NOT-TAKEN ARM HELD A RENDERING WHERE IT SHOULD HAVE HELD THE FACT.

`ObservationNotTaken { cause: String }` flattened the typed refusal into prose at the moment it was
stored -- the anemic leaf DESIGN §2 names, a String hiding parts that already existed one function
away. The cost landed exactly where it hurts most: every claim about WHY an observation was not
taken had to be a substring match, so the one artifact a reader would cite as proof of this
carrier's strongest property was a change detector -- red on a wording edit, green on any text
carrying the phrase. It now carries `admission: EmitRatchetEnrolmentAdmission`, and the rendering
becomes a projection derived at the edge, never the record.

AND THE ROW THAT COULD NOT FAIL IS DELETED, NOT REPAIRED.

`an_observation_that_was_not_taken_holds_no_standing` had four conjuncts: one structural, three
substring matches. Worse, the property its NAME asserted is unauthorable at BOTH §4b boundaries --
no fixture can construct a variant field that does not exist -- which is precisely the case where
the right answer is no check at all, because a permanently-green check is worse than absent for
being cited as coverage. It is replaced by `an_empty_dual_fold_yields_no_observation`, which asserts
only what can fail for the right reason: which ARM an unobservable fold dispatches to, and which
REFUSAL it carries, both as variant matches. The structural property survives in the type and is
stated in the module header, where the header also records that no witness asserts it and why.

The report row IS legitimately a renderer claim, so it stays -- but its expected rows are now
COMPOSED from the renderer instead of hand-written. A literal fragment there pins one authority's
wording into another's claim, which is §3's fork arriving as a test fixture.

THE QUADRATIC FOLD IS FIXED, AND NOT AS A NIT.

`ratchet_unevaluated_subjects` accumulated with `concat(acc, [e])` per hit. DESIGN §6's standing
bare-minimum-cost ruling is explicit that a copied accumulator or a quadratic fold is ALWAYS fixed
regardless of the realized n, because "n is small here" is not a time-stable fact -- and this
roster is the whole discovered corpus, so the mitigating premise was weak on its own terms. It is
now a total `emit_ratchet_verdict_evaluated` predicate plus filter-then-map, which also states the
question the whole not-evaluated wall turns on ONCE instead of inlining it in a selection loop.
This module's peer accumulators predate the change and are untouched; pricing this cut against
pre-existing corpus defects would be the wrong denominator.

EVIDENCE, BY EXECUTION (BuildBuddy, build and run in one dispatch), controls green either side:

  filter rewrite   control 8/8 PASS. Mutating the EXTRACTED predicate (NotEvaluated reads as
                   evaluated) reds exactly the two gate claims -- so the wall moved with the code
                   rather than out from under it, which is the specific risk in extracting it.
  typed admission  control 8/8 PASS.
                   mutation A, empty universe permits so the fold routes to TAKEN
                     -> FAIL an_empty_dual_fold_yields_no_observation
                   mutation B, the not-taken arm carries the WRONG refusal identity
                     -> FAIL an_empty_dual_fold_yields_no_observation
                   Both conjuncts load-bearing; six unrelated claims PASS throughout.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Dissolve the roster predicate into a direct match, and repair a stale citation to a symbol this branch deletes

REVIEW 56173, FINDING 1 -- FIXED. `ratchet_roster_is_empty` is gone; `emit_ratchet_enrolment_admission`
matches `r.roster` directly, as the wall it replaced did. The reviewer's instinct is right even though
the citation is not: `grep -c` for the named predicate-dissolution rule in DESIGN.md returns 0. The
real precedent is in the corpus -- `std.witness_admission` `witness_admission_predicate_dissolution_note`
records two predicates dissolved (review 39760) BY MAKING CONSUMERS MATCH THE COPRODUCT, and
`v2.std.witness_execution_routing` records three more. Same direction, so the change stands on its
merits rather than on the rule as cited.

REVIEW 56173, FINDING 2 -- DECLINED, three reasons, the third deciding.
  1. `filter` takes a Bool by construction, so dissolving `emit_ratchet_verdict_evaluated` means
     returning to a fold that appends -- the quadratic accumulator review 56160 and DESIGN §6's
     bare-minimum-cost ruling had just removed. The two findings would cancel.
  2. `emit_ratchet_verdict_holds` sits ten lines above it: same coproduct, same `-> Bool`, same total
     match, pre-existing and untouched here. That IS the module's idiom.
  3. It is not a second accessor for one answer, which is what the duplicated-shape objection needs.
     `RatchetRegressed` discriminates them: holds=false, evaluated=TRUE. A regression is a verdict the
     fold ESTABLISHED and then refused. Collapsing the two would make an evaluated failure
     indistinguishable from a subject nobody could measure -- the distinction this carrier exists for.

A STALE CITATION TO A SYMBOL THIS BRANCH DELETES, found via gentle-bee-495 rather than by review.
The `RosterDenominators` note cited `emit_ratchet_gating_admission` -- deleted by this very PR -- and
it would have shipped, in the module that argues for cited-symbol hygiene. Repaired, and widened,
because the sentence carried a second defect of the same family: it ended "when the file inventory
lands, the field changes and enrolment becomes possible", true of the wall as it stood and falsified
the moment gunbc#9231 landed. A dual denominator now makes a gate ELIGIBLE, not admissible, and an
observation does not consult the field at all. A stale citation wrapped around a stale claim.

THE GATE'S PERMIT IS UNOCCUPIED ON THE LIVE CORPUS, AND THAT IS NOT UNREACHABLE (gentle-bee-495,
2026-08-26). Hole 3's bounded-tail reader means a talkative subject arrives NOT-EVALUATED, so over a
realistic roster the any-unevaluated condition refuses essentially always and nothing RECEIVES the
permit. Recorded as occupancy rather than reachability, because only the second reading licenses
deleting the arm: DESIGN puts that test at the FIXTURE boundary, and the witness authors the permit
today as the one-field control beside the unevaluated refusal. Yes / yes / zero is a healthy guard
being quiet. The arm is NOT relaxed to tolerate unevaluated subjects -- that converts the
instrument's blindness into a green, the absorbing fallback arriving as a kindness to a roster that
is not ready. The relayed sample is carried as a SHAPE and not a rate; no proportion is stated.

EVIDENCE, controls green either side, every arm printing an explicit VOID fallback:
  control after dissolution            8/8 PASS
  C: gate permits without consulting   FAIL ×3, including the substring-free `_permits_none`
     denominator or verdicts           PASS an_empty_dual_fold (a different question, correctly green)
  E: empty-roster arm permits          FAIL an_empty_dual_fold_yields_no_observation
  restored                             PASS

Two earlier attempts at C were VOID rather than passing: forcing the arm by inventing a variant name
does not compile, and zero verdict lines greps identically to zero failures.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Carry the two-arm measurement behind the surviving predicate, not the one-arm version

smart-ram-730 re-derived the holds/evaluated disagreement against this branch and found TWO arms
disagreeing, not one. Re-measured here and confirmed:

  variant                    holds    evaluated
  RatchetCleanHeld           true     true
  RatchetDebtHeld            true     true
  RatchetRegressed           FALSE    TRUE       <-- disagree
  RatchetFrontierStale       FALSE    TRUE       <-- disagree
  RatchetNotEvaluated        false    false
  RatchetSubjectUnmeasured   false    false

WHY TWO IS STRONGER THAN ONE, and it is the reason this note changed rather than a citation being
added to it: a single disagreeing variant is dismissible as an accident of how that one case is
treated, which is exactly how the declined review finding would have read it. Two disagreeing in the
SAME DIRECTION show the functions answer different questions by construction.

AND THE READING IS THE PART THAT SURVIVES. Both disagreeing arms are cases where the fold DID
establish a verdict and then refused it -- a regression is a subject measured and found dirty, a
stale frontier row is a subject measured and found CLEAN while its admission row says otherwise.
Neither is a subject nobody could measure. That is the entire content of the split, and collapsing
the two questions would make an evaluated failure indistinguishable from an unevaluated subject:
the state-space conflation this carrier exists to refuse, committed inside the machinery refusing it.

Comment-only, and verified anyway rather than assumed -- this corpus refuses annotations in the
wrong position, so "it is only a comment" is not evidence. PASS_COUNT=8 over the eight claims.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Give both verdict questions one authority: a canonical classification, with the divergence class named

Review 56204 raised the predicate duplication a second time and rejected both my reasons. It is right,
and the part I got wrong is worth stating exactly: I argued the two predicates have DISTINCT SEMANTICS
and that `filter` needs a `Bool`. Both true, and neither answers the objection. Two total matches over
six variants are two places holding the coproduct's shape, and the compiler forcing both to be total
does not make them ONE AUTHORITY. Distinct semantics and duplicated shape knowledge can both be true
at once; my reply treated the first as refuting the second.

THE FIX GIVES BOTH QUESTIONS ONE SOURCE RATHER THAN DELETING A PREDICATE. `VerdictStanding` is the
real semantic axis and `emit_ratchet_verdict_standing` is now the only function reading the verdict
vocabulary's shape for classification:

  VerdictHeld                   established, and the ratchet holds
  VerdictEstablishedAndRefused  established, and then REFUSED
  VerdictUnestablished          no verdict about this subject exists at all

`holds` and `evaluated` are projections over that, not over storage. Measured after the change --
variant mentions: standing 6, holds 0, evaluated 0. One place fails to compile when a variant lands.
(`emit_ratchet_verdict_entry` and `_row` still match six: they extract per-variant PAYLOADS, a field
and a rendering, which is not classification knowledge.)

AND IT IS A BETTER MODEL THAN EITHER POSITION IN THE ARGUMENT. The divergence between the two
questions used to be EMERGENT -- two independent matches that happened to disagree on two arms,
discoverable only by measuring them against each other, which is literally how it surfaced. Now it
is a named class. `VerdictEstablishedAndRefused` IS the population where the fold established a
verdict and then refused it: a regression is a subject measured and found dirty, a stale frontier row
is a subject measured and found CLEAN while its admission row disagrees.

TWO NEW CLAIMS PIN THE RELATION, and they fail for different reasons:
  holding_a_verdict_entails_having_established_it -- the ratchet cannot hold over a subject nobody
    measured; a failure means some arm reports a verdict it never established.
  an_established_verdict_that_refuses_is_what_separates_the_two_questions -- the converse set is
    NON-EMPTY (2). If it were empty the split would be a decoration and `evaluated` deletable.

EVIDENCE: control 10/10 PASS. Mutating the classifier to call a regression UNESTABLISHED reds exactly
the divergence claim and nothing else -- the entailment claim correctly stays green, since collapsing
that class creates no holds-without-evaluated case. Restored 10/10.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Repair the merge: git factored a shared closing brace out of both conflict sides

The #9238 absorption left two files unparseable. Both sides' content survived intact; what did not
was MY closing braces, and the cause is worth recording because the resolution looked correct by
every check I ran.

WHAT HAPPENED. Both conflicts were append/append at the file tails. I resolved by concatenating the
two sides. But git had factored the SHARED TRAILING BRACE out of both sides as common context --
both blocks ended identically, so the closing `}` appeared ONCE, after the conflict region.
Concatenating two bodies then left one brace closing two functions:

  emit_subject_clean_frontier.dag        emit_ratchet_runner_execution_standing lost its `else`
                                        and function close; emit_outcome_under_membership was
                                        lexically swallowed by the still-open else
  ..._witness_test.dag                   an_established_verdict_that_refuses... lost its close;
                                        fn membership_with_candidates was swallowed the same way

WHAT ALMOST LET IT THROUGH, which is the reusable part. I verified the merge by checking that both
sides' SYMBOLS AND CLAIMS WERE PRESENT. All of them were. PRESENCE IS NOT WELL-FORMEDNESS -- every
grep reported a clean merge while neither file parsed.

WHAT CAUGHT IT was PASS_COUNT=0 on both lanes with no FAIL and no ERROR: the neither-verdict signal.
A parse refusal is not a verdict, so a filter written for PASS/FAIL is blind to it, and zero verdict
lines greps identically to zero failures. Raw output said it plainly: `module index refused: 2
unparseable .dag source(s)`. Review 56291 independently found the frontier half and prescribed the
same fix; it named only that file, so applying the review alone would have left the witness test
unparseable -- and the witness file is where the evidence lives, so the module would have compiled
with its claims silently absent.

VERIFIED BY EXECUTION, both lanes, because a merge can break either side:
  MINE    10/10 PASS
  THEIRS   3/3  PASS   (gentle-bee-495's boundary claims -- the ones a careless merge destroys)
  runner tool compile    0 blocking, 154 files emitted
  frontier module        0 blocking, 148 files emitted

The branch now also carries #9273 (d31fa86), so these claims pass against a tree where a
truncated capture has its own refusal rather than being read as a compiler scoping failure.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
briansrls pushed a commit that referenced this pull request Aug 26, 2026
…t the instrument failed on (#9346)

* Enrol the emission ratchet as an OBSERVATION: two modes, an executing consumer, and a discriminating red

The emit-subject clean ratchet gates nothing. Measured on origin/main at 730d226 and
re-confirmed at e1f65b3: `git grep -l emit_subject_clean` returns exactly three files -- the
frontier module, the runner tool, and their one witness -- and the path-fragment search returns the
same three, which closes the argv-assembled-entry-path case a module-name grep would miss. No
workflow, no required phase, no module names either of them.

This is not a defect in the ratchet. The enrolment wall was a genuine construction: it refused every
enrolment while the universe carried a single denominator. What changed is that #9231 made the
permitting arm producible, and the wall then answered PERMITTED about a carrier that must still not
gate -- it asked one question from one fact, and that fact is no longer the only disqualifier.

ENROLMENT IS TWO QUESTIONS, SO THE MODE IS A PARAMETER.

`EmitRatchetGatingAdmission` is deleted at the root and replaced by
`emit_ratchet_enrolment_admission(enrolment, r)` over `EnrolledAsObservation | EnrolledAsGate`.
Preconditions are derived from the fold, never asserted about it:

  BOTH       a non-empty universe -- a fold over no subjects is a discovery failure, and rendering
             one as an observation of nothing is the empty-observation narrow at the point where
             the observation is taken.
  GATE ONLY  the dual denominator, for the reason the previous wall gave verbatim.
  GATE ONLY  every roster subject EVALUATED. A subject that reached NOT-EVALUATED or was never
             measured has no verdict about its cleanliness, and a gate whose universe contains such
             subjects decides a closed-universe question over an open one. This is hole 3 arriving
             at the enrolment seam.

The asymmetry is deliberate rather than lenient: an observation is NOT refused by a single
denominator or by unevaluated subjects, because those are its CONTENT. Refusing to look because the
looking is imperfect is the same narrow one level up. What a weak universe disqualifies is DECIDING.

EXECUTION PROVENANCE IS STRUCTURAL, NOT CHECKED.

`EmitRatchetObservation = ObservationTaken { roster, standing } | ObservationNotTaken { cause }`.
The not-taken arm holds NO standing field, so there is no spelling in which a fold that never
happened reads as a fold that found nothing wrong. The report reaches the standing only THROUGH the
observation and prints the gate admission beside it, so a reading cannot be quoted as a gate
verdict. On the host side `attempt_emit_subject_clean_ratchet` separates never-attempted (roster
unreadable, source root unlistable) from folded, before the difference stops being knowable.

The new `observe` verb exits SUCCESS on a refusing standing -- the observation succeeded and the
news is bad, which belongs in the report a human reads -- and FAILURE only when the observation
could not be taken. `check` now routes through the gate admission and refuses to be a gate.

THE THIRD STATE: HAS NOT RUN YET vs WILL NEVER RUN HERE.

Both render as an absent report and only the second is terminal. The vocabulary is BORROWED rather
than minted: `std.witness_admission` already separates a row with an executing consumer from one
nothing claims from one whose cadence has no scheduled route.
`emit_ratchet_runner_cadence = NoConsumer` derives `UnexecutedDeferredWitness`, and the derivation
is not constant -- handed a cadence with a route it returns the covered arm.

NOTHING ENROLS. No workflow, no required phase, no CI authority is touched, and no import reaches
the runner from anything the floor folds. Enrolment is a floor-cut re-add and requires its own
operator agreement; this gets the mechanism to where that is a one-line change someone with the
authority can approve.

EVIDENCE, BY EXECUTION on BuildBuddy (arm64 session, amd64 runner, build and run in one dispatch):

  control          10/10 claims PASS; `gunbc compile --entry dag/tools/emit_subject_clean_ratchet.dag`
                   -> 0 blocking, 949 advisory, 152 files emitted.
  mutation 1       gate ignores unevaluated subjects (the pre-change wall restored):
                   FAIL a_gate_refuses_a_dual_denominator_fold_whose_subjects_were_never_evaluated
                   FAIL an_unevaluated_subject_is_observed_rather_than_suppressed
                   four unrelated claims stay PASS -- targeted, not a broad break.
  mutation 2       empty universe no longer refuses enrolment:
                   FAIL an_observation_that_was_not_taken_holds_no_standing
                   FAIL the_report_distinguishes_an_untaken_observation_from_a_clean_one
                   restored control green before and after.

Every claim pairs its refusing input with a control differing in exactly one field, and
`the_ratchet_runner_has_no_executing_consumer_today` is green BECAUSE nothing runs the runner -- it
goes red the day a cadence is agreed, which is what forces it and the block it mirrors to be
rewritten together.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Carry the typed admission on the not-taken arm, and delete the evidence that could not fail

Two review findings, both fixed at the root rather than in the row that surfaced them.

THE NOT-TAKEN ARM HELD A RENDERING WHERE IT SHOULD HAVE HELD THE FACT.

`ObservationNotTaken { cause: String }` flattened the typed refusal into prose at the moment it was
stored -- the anemic leaf DESIGN §2 names, a String hiding parts that already existed one function
away. The cost landed exactly where it hurts most: every claim about WHY an observation was not
taken had to be a substring match, so the one artifact a reader would cite as proof of this
carrier's strongest property was a change detector -- red on a wording edit, green on any text
carrying the phrase. It now carries `admission: EmitRatchetEnrolmentAdmission`, and the rendering
becomes a projection derived at the edge, never the record.

AND THE ROW THAT COULD NOT FAIL IS DELETED, NOT REPAIRED.

`an_observation_that_was_not_taken_holds_no_standing` had four conjuncts: one structural, three
substring matches. Worse, the property its NAME asserted is unauthorable at BOTH §4b boundaries --
no fixture can construct a variant field that does not exist -- which is precisely the case where
the right answer is no check at all, because a permanently-green check is worse than absent for
being cited as coverage. It is replaced by `an_empty_dual_fold_yields_no_observation`, which asserts
only what can fail for the right reason: which ARM an unobservable fold dispatches to, and which
REFUSAL it carries, both as variant matches. The structural property survives in the type and is
stated in the module header, where the header also records that no witness asserts it and why.

The report row IS legitimately a renderer claim, so it stays -- but its expected rows are now
COMPOSED from the renderer instead of hand-written. A literal fragment there pins one authority's
wording into another's claim, which is §3's fork arriving as a test fixture.

THE QUADRATIC FOLD IS FIXED, AND NOT AS A NIT.

`ratchet_unevaluated_subjects` accumulated with `concat(acc, [e])` per hit. DESIGN §6's standing
bare-minimum-cost ruling is explicit that a copied accumulator or a quadratic fold is ALWAYS fixed
regardless of the realized n, because "n is small here" is not a time-stable fact -- and this
roster is the whole discovered corpus, so the mitigating premise was weak on its own terms. It is
now a total `emit_ratchet_verdict_evaluated` predicate plus filter-then-map, which also states the
question the whole not-evaluated wall turns on ONCE instead of inlining it in a selection loop.
This module's peer accumulators predate the change and are untouched; pricing this cut against
pre-existing corpus defects would be the wrong denominator.

EVIDENCE, BY EXECUTION (BuildBuddy, build and run in one dispatch), controls green either side:

  filter rewrite   control 8/8 PASS. Mutating the EXTRACTED predicate (NotEvaluated reads as
                   evaluated) reds exactly the two gate claims -- so the wall moved with the code
                   rather than out from under it, which is the specific risk in extracting it.
  typed admission  control 8/8 PASS.
                   mutation A, empty universe permits so the fold routes to TAKEN
                     -> FAIL an_empty_dual_fold_yields_no_observation
                   mutation B, the not-taken arm carries the WRONG refusal identity
                     -> FAIL an_empty_dual_fold_yields_no_observation
                   Both conjuncts load-bearing; six unrelated claims PASS throughout.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Dissolve the roster predicate into a direct match, and repair a stale citation to a symbol this branch deletes

REVIEW 56173, FINDING 1 -- FIXED. `ratchet_roster_is_empty` is gone; `emit_ratchet_enrolment_admission`
matches `r.roster` directly, as the wall it replaced did. The reviewer's instinct is right even though
the citation is not: `grep -c` for the named predicate-dissolution rule in DESIGN.md returns 0. The
real precedent is in the corpus -- `std.witness_admission` `witness_admission_predicate_dissolution_note`
records two predicates dissolved (review 39760) BY MAKING CONSUMERS MATCH THE COPRODUCT, and
`v2.std.witness_execution_routing` records three more. Same direction, so the change stands on its
merits rather than on the rule as cited.

REVIEW 56173, FINDING 2 -- DECLINED, three reasons, the third deciding.
  1. `filter` takes a Bool by construction, so dissolving `emit_ratchet_verdict_evaluated` means
     returning to a fold that appends -- the quadratic accumulator review 56160 and DESIGN §6's
     bare-minimum-cost ruling had just removed. The two findings would cancel.
  2. `emit_ratchet_verdict_holds` sits ten lines above it: same coproduct, same `-> Bool`, same total
     match, pre-existing and untouched here. That IS the module's idiom.
  3. It is not a second accessor for one answer, which is what the duplicated-shape objection needs.
     `RatchetRegressed` discriminates them: holds=false, evaluated=TRUE. A regression is a verdict the
     fold ESTABLISHED and then refused. Collapsing the two would make an evaluated failure
     indistinguishable from a subject nobody could measure -- the distinction this carrier exists for.

A STALE CITATION TO A SYMBOL THIS BRANCH DELETES, found via gentle-bee-495 rather than by review.
The `RosterDenominators` note cited `emit_ratchet_gating_admission` -- deleted by this very PR -- and
it would have shipped, in the module that argues for cited-symbol hygiene. Repaired, and widened,
because the sentence carried a second defect of the same family: it ended "when the file inventory
lands, the field changes and enrolment becomes possible", true of the wall as it stood and falsified
the moment gunbc#9231 landed. A dual denominator now makes a gate ELIGIBLE, not admissible, and an
observation does not consult the field at all. A stale citation wrapped around a stale claim.

THE GATE'S PERMIT IS UNOCCUPIED ON THE LIVE CORPUS, AND THAT IS NOT UNREACHABLE (gentle-bee-495,
2026-08-26). Hole 3's bounded-tail reader means a talkative subject arrives NOT-EVALUATED, so over a
realistic roster the any-unevaluated condition refuses essentially always and nothing RECEIVES the
permit. Recorded as occupancy rather than reachability, because only the second reading licenses
deleting the arm: DESIGN puts that test at the FIXTURE boundary, and the witness authors the permit
today as the one-field control beside the unevaluated refusal. Yes / yes / zero is a healthy guard
being quiet. The arm is NOT relaxed to tolerate unevaluated subjects -- that converts the
instrument's blindness into a green, the absorbing fallback arriving as a kindness to a roster that
is not ready. The relayed sample is carried as a SHAPE and not a rate; no proportion is stated.

EVIDENCE, controls green either side, every arm printing an explicit VOID fallback:
  control after dissolution            8/8 PASS
  C: gate permits without consulting   FAIL ×3, including the substring-free `_permits_none`
     denominator or verdicts           PASS an_empty_dual_fold (a different question, correctly green)
  E: empty-roster arm permits          FAIL an_empty_dual_fold_yields_no_observation
  restored                             PASS

Two earlier attempts at C were VOID rather than passing: forcing the arm by inventing a variant name
does not compile, and zero verdict lines greps identically to zero failures.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Carry the two-arm measurement behind the surviving predicate, not the one-arm version

smart-ram-730 re-derived the holds/evaluated disagreement against this branch and found TWO arms
disagreeing, not one. Re-measured here and confirmed:

  variant                    holds    evaluated
  RatchetCleanHeld           true     true
  RatchetDebtHeld            true     true
  RatchetRegressed           FALSE    TRUE       <-- disagree
  RatchetFrontierStale       FALSE    TRUE       <-- disagree
  RatchetNotEvaluated        false    false
  RatchetSubjectUnmeasured   false    false

WHY TWO IS STRONGER THAN ONE, and it is the reason this note changed rather than a citation being
added to it: a single disagreeing variant is dismissible as an accident of how that one case is
treated, which is exactly how the declined review finding would have read it. Two disagreeing in the
SAME DIRECTION show the functions answer different questions by construction.

AND THE READING IS THE PART THAT SURVIVES. Both disagreeing arms are cases where the fold DID
establish a verdict and then refused it -- a regression is a subject measured and found dirty, a
stale frontier row is a subject measured and found CLEAN while its admission row says otherwise.
Neither is a subject nobody could measure. That is the entire content of the split, and collapsing
the two questions would make an evaluated failure indistinguishable from an unevaluated subject:
the state-space conflation this carrier exists to refuse, committed inside the machinery refusing it.

Comment-only, and verified anyway rather than assumed -- this corpus refuses annotations in the
wrong position, so "it is only a comment" is not evidence. PASS_COUNT=8 over the eight claims.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Give both verdict questions one authority: a canonical classification, with the divergence class named

Review 56204 raised the predicate duplication a second time and rejected both my reasons. It is right,
and the part I got wrong is worth stating exactly: I argued the two predicates have DISTINCT SEMANTICS
and that `filter` needs a `Bool`. Both true, and neither answers the objection. Two total matches over
six variants are two places holding the coproduct's shape, and the compiler forcing both to be total
does not make them ONE AUTHORITY. Distinct semantics and duplicated shape knowledge can both be true
at once; my reply treated the first as refuting the second.

THE FIX GIVES BOTH QUESTIONS ONE SOURCE RATHER THAN DELETING A PREDICATE. `VerdictStanding` is the
real semantic axis and `emit_ratchet_verdict_standing` is now the only function reading the verdict
vocabulary's shape for classification:

  VerdictHeld                   established, and the ratchet holds
  VerdictEstablishedAndRefused  established, and then REFUSED
  VerdictUnestablished          no verdict about this subject exists at all

`holds` and `evaluated` are projections over that, not over storage. Measured after the change --
variant mentions: standing 6, holds 0, evaluated 0. One place fails to compile when a variant lands.
(`emit_ratchet_verdict_entry` and `_row` still match six: they extract per-variant PAYLOADS, a field
and a rendering, which is not classification knowledge.)

AND IT IS A BETTER MODEL THAN EITHER POSITION IN THE ARGUMENT. The divergence between the two
questions used to be EMERGENT -- two independent matches that happened to disagree on two arms,
discoverable only by measuring them against each other, which is literally how it surfaced. Now it
is a named class. `VerdictEstablishedAndRefused` IS the population where the fold established a
verdict and then refused it: a regression is a subject measured and found dirty, a stale frontier row
is a subject measured and found CLEAN while its admission row disagrees.

TWO NEW CLAIMS PIN THE RELATION, and they fail for different reasons:
  holding_a_verdict_entails_having_established_it -- the ratchet cannot hold over a subject nobody
    measured; a failure means some arm reports a verdict it never established.
  an_established_verdict_that_refuses_is_what_separates_the_two_questions -- the converse set is
    NON-EMPTY (2). If it were empty the split would be a decoration and `evaluated` deletable.

EVIDENCE: control 10/10 PASS. Mutating the classifier to call a regression UNESTABLISHED reds exactly
the divergence claim and nothing else -- the entailment claim correctly stays green, since collapsing
that class creates no holds-without-evaluated case. Restored 10/10.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Repair the merge: git factored a shared closing brace out of both conflict sides

The #9238 absorption left two files unparseable. Both sides' content survived intact; what did not
was MY closing braces, and the cause is worth recording because the resolution looked correct by
every check I ran.

WHAT HAPPENED. Both conflicts were append/append at the file tails. I resolved by concatenating the
two sides. But git had factored the SHARED TRAILING BRACE out of both sides as common context --
both blocks ended identically, so the closing `}` appeared ONCE, after the conflict region.
Concatenating two bodies then left one brace closing two functions:

  emit_subject_clean_frontier.dag        emit_ratchet_runner_execution_standing lost its `else`
                                        and function close; emit_outcome_under_membership was
                                        lexically swallowed by the still-open else
  ..._witness_test.dag                   an_established_verdict_that_refuses... lost its close;
                                        fn membership_with_candidates was swallowed the same way

WHAT ALMOST LET IT THROUGH, which is the reusable part. I verified the merge by checking that both
sides' SYMBOLS AND CLAIMS WERE PRESENT. All of them were. PRESENCE IS NOT WELL-FORMEDNESS -- every
grep reported a clean merge while neither file parsed.

WHAT CAUGHT IT was PASS_COUNT=0 on both lanes with no FAIL and no ERROR: the neither-verdict signal.
A parse refusal is not a verdict, so a filter written for PASS/FAIL is blind to it, and zero verdict
lines greps identically to zero failures. Raw output said it plainly: `module index refused: 2
unparseable .dag source(s)`. Review 56291 independently found the frontier half and prescribed the
same fix; it named only that file, so applying the review alone would have left the witness test
unparseable -- and the witness file is where the evidence lives, so the module would have compiled
with its claims silently absent.

VERIFIED BY EXECUTION, both lanes, because a merge can break either side:
  MINE    10/10 PASS
  THEIRS   3/3  PASS   (gentle-bee-495's boundary claims -- the ones a careless merge destroys)
  runner tool compile    0 blocking, 154 files emitted
  frontier module        0 blocking, 148 files emitted

The branch now also carries #9273 (d31fa86), so these claims pass against a tree where a
truncated capture has its own refusal rather than being read as a compiler scoping failure.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* One silence, two owners: split a subject nobody asked about from one the instrument failed on

A roster subject arriving at the fold with no reading was RatchetSubjectUnmeasured whether the
run had asked about it or not. The two facts share nothing but their silence and have opposite
remedies -- fix the instrument, or widen the run -- so a reader handed one UNMEASURED count
could not tell which they were looking at.

This module already draws exactly this distinction correctly one axis over: an admission row
naming a subject the roster lacks is reported as an ORPHAN rather than as a missing measurement,
in its own words "because the two have opposite owners". The selection axis is the same shape and
did not get the same treatment.

WHAT LANDS

  SubjectSelection = SelectionWholeRoster | SelectionSubset { entries }

A coproduct rather than a bare List<String>, deliberately: an empty list would mean both "a subset
naming nothing" and "no subsetting at all", which is the state-space conflation the type exists to
remove, reintroduced in the type that removes it.

RatchetSubjectNotSelected joins the verdict vocabulary and is threaded through the four total
matches over it. Both silences remain VerdictUnestablished, so a gate still refuses over either --
this splits OWNERS without promoting scope into evidence. The runner derives one selection and
reads it twice (the measured list and the classified selection come from the same value), because
two independent derivations could disagree and the disagreement would render as an UNMEASURED
subject the run had in fact measured.

EVIDENCE, and the reds are the point

The pair is a ONE-FIELD control: identical roster, admissions and readings, only the selection
differs. Mutated on BuildBuddy in both directions:

  A, selection ignored (the pre-change behavior): a_subject_the_run_never_selected... FAILS alone
  B, selection inverted:                          the two selected-side claims FAIL, that one passes
  restored:                                       all four pass

So claim A is the regression control for precisely the defect repaired here.

Stated rather than counted: neither_silence_establishes_a_verdict passes in every arm. It guards a
different property -- that neither silence is promoted to evidence -- and is NOT discriminating on
the selection axis.

a_partial_run_refuses_and_names_the_subjects_it_did_not_measure pinned the old UNMEASURED row text
and is updated with it; it stays red under both mutations.

Measured: 4 new/changed claims pass, the 12 pre-existing pass unchanged, and the runner compiles
0 blocking / 951 advisory.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
briansrls added a commit that referenced this pull request Aug 26, 2026
…reement, and a pricing receipt that cannot be unattached (#9348)

* Enrol the emission ratchet as an OBSERVATION: two modes, an executing consumer, and a discriminating red

The emit-subject clean ratchet gates nothing. Measured on origin/main at 730d226 and
re-confirmed at e1f65b3: `git grep -l emit_subject_clean` returns exactly three files -- the
frontier module, the runner tool, and their one witness -- and the path-fragment search returns the
same three, which closes the argv-assembled-entry-path case a module-name grep would miss. No
workflow, no required phase, no module names either of them.

This is not a defect in the ratchet. The enrolment wall was a genuine construction: it refused every
enrolment while the universe carried a single denominator. What changed is that #9231 made the
permitting arm producible, and the wall then answered PERMITTED about a carrier that must still not
gate -- it asked one question from one fact, and that fact is no longer the only disqualifier.

ENROLMENT IS TWO QUESTIONS, SO THE MODE IS A PARAMETER.

`EmitRatchetGatingAdmission` is deleted at the root and replaced by
`emit_ratchet_enrolment_admission(enrolment, r)` over `EnrolledAsObservation | EnrolledAsGate`.
Preconditions are derived from the fold, never asserted about it:

  BOTH       a non-empty universe -- a fold over no subjects is a discovery failure, and rendering
             one as an observation of nothing is the empty-observation narrow at the point where
             the observation is taken.
  GATE ONLY  the dual denominator, for the reason the previous wall gave verbatim.
  GATE ONLY  every roster subject EVALUATED. A subject that reached NOT-EVALUATED or was never
             measured has no verdict about its cleanliness, and a gate whose universe contains such
             subjects decides a closed-universe question over an open one. This is hole 3 arriving
             at the enrolment seam.

The asymmetry is deliberate rather than lenient: an observation is NOT refused by a single
denominator or by unevaluated subjects, because those are its CONTENT. Refusing to look because the
looking is imperfect is the same narrow one level up. What a weak universe disqualifies is DECIDING.

EXECUTION PROVENANCE IS STRUCTURAL, NOT CHECKED.

`EmitRatchetObservation = ObservationTaken { roster, standing } | ObservationNotTaken { cause }`.
The not-taken arm holds NO standing field, so there is no spelling in which a fold that never
happened reads as a fold that found nothing wrong. The report reaches the standing only THROUGH the
observation and prints the gate admission beside it, so a reading cannot be quoted as a gate
verdict. On the host side `attempt_emit_subject_clean_ratchet` separates never-attempted (roster
unreadable, source root unlistable) from folded, before the difference stops being knowable.

The new `observe` verb exits SUCCESS on a refusing standing -- the observation succeeded and the
news is bad, which belongs in the report a human reads -- and FAILURE only when the observation
could not be taken. `check` now routes through the gate admission and refuses to be a gate.

THE THIRD STATE: HAS NOT RUN YET vs WILL NEVER RUN HERE.

Both render as an absent report and only the second is terminal. The vocabulary is BORROWED rather
than minted: `std.witness_admission` already separates a row with an executing consumer from one
nothing claims from one whose cadence has no scheduled route.
`emit_ratchet_runner_cadence = NoConsumer` derives `UnexecutedDeferredWitness`, and the derivation
is not constant -- handed a cadence with a route it returns the covered arm.

NOTHING ENROLS. No workflow, no required phase, no CI authority is touched, and no import reaches
the runner from anything the floor folds. Enrolment is a floor-cut re-add and requires its own
operator agreement; this gets the mechanism to where that is a one-line change someone with the
authority can approve.

EVIDENCE, BY EXECUTION on BuildBuddy (arm64 session, amd64 runner, build and run in one dispatch):

  control          10/10 claims PASS; `gunbc compile --entry dag/tools/emit_subject_clean_ratchet.dag`
                   -> 0 blocking, 949 advisory, 152 files emitted.
  mutation 1       gate ignores unevaluated subjects (the pre-change wall restored):
                   FAIL a_gate_refuses_a_dual_denominator_fold_whose_subjects_were_never_evaluated
                   FAIL an_unevaluated_subject_is_observed_rather_than_suppressed
                   four unrelated claims stay PASS -- targeted, not a broad break.
  mutation 2       empty universe no longer refuses enrolment:
                   FAIL an_observation_that_was_not_taken_holds_no_standing
                   FAIL the_report_distinguishes_an_untaken_observation_from_a_clean_one
                   restored control green before and after.

Every claim pairs its refusing input with a control differing in exactly one field, and
`the_ratchet_runner_has_no_executing_consumer_today` is green BECAUSE nothing runs the runner -- it
goes red the day a cadence is agreed, which is what forces it and the block it mirrors to be
rewritten together.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Carry the typed admission on the not-taken arm, and delete the evidence that could not fail

Two review findings, both fixed at the root rather than in the row that surfaced them.

THE NOT-TAKEN ARM HELD A RENDERING WHERE IT SHOULD HAVE HELD THE FACT.

`ObservationNotTaken { cause: String }` flattened the typed refusal into prose at the moment it was
stored -- the anemic leaf DESIGN §2 names, a String hiding parts that already existed one function
away. The cost landed exactly where it hurts most: every claim about WHY an observation was not
taken had to be a substring match, so the one artifact a reader would cite as proof of this
carrier's strongest property was a change detector -- red on a wording edit, green on any text
carrying the phrase. It now carries `admission: EmitRatchetEnrolmentAdmission`, and the rendering
becomes a projection derived at the edge, never the record.

AND THE ROW THAT COULD NOT FAIL IS DELETED, NOT REPAIRED.

`an_observation_that_was_not_taken_holds_no_standing` had four conjuncts: one structural, three
substring matches. Worse, the property its NAME asserted is unauthorable at BOTH §4b boundaries --
no fixture can construct a variant field that does not exist -- which is precisely the case where
the right answer is no check at all, because a permanently-green check is worse than absent for
being cited as coverage. It is replaced by `an_empty_dual_fold_yields_no_observation`, which asserts
only what can fail for the right reason: which ARM an unobservable fold dispatches to, and which
REFUSAL it carries, both as variant matches. The structural property survives in the type and is
stated in the module header, where the header also records that no witness asserts it and why.

The report row IS legitimately a renderer claim, so it stays -- but its expected rows are now
COMPOSED from the renderer instead of hand-written. A literal fragment there pins one authority's
wording into another's claim, which is §3's fork arriving as a test fixture.

THE QUADRATIC FOLD IS FIXED, AND NOT AS A NIT.

`ratchet_unevaluated_subjects` accumulated with `concat(acc, [e])` per hit. DESIGN §6's standing
bare-minimum-cost ruling is explicit that a copied accumulator or a quadratic fold is ALWAYS fixed
regardless of the realized n, because "n is small here" is not a time-stable fact -- and this
roster is the whole discovered corpus, so the mitigating premise was weak on its own terms. It is
now a total `emit_ratchet_verdict_evaluated` predicate plus filter-then-map, which also states the
question the whole not-evaluated wall turns on ONCE instead of inlining it in a selection loop.
This module's peer accumulators predate the change and are untouched; pricing this cut against
pre-existing corpus defects would be the wrong denominator.

EVIDENCE, BY EXECUTION (BuildBuddy, build and run in one dispatch), controls green either side:

  filter rewrite   control 8/8 PASS. Mutating the EXTRACTED predicate (NotEvaluated reads as
                   evaluated) reds exactly the two gate claims -- so the wall moved with the code
                   rather than out from under it, which is the specific risk in extracting it.
  typed admission  control 8/8 PASS.
                   mutation A, empty universe permits so the fold routes to TAKEN
                     -> FAIL an_empty_dual_fold_yields_no_observation
                   mutation B, the not-taken arm carries the WRONG refusal identity
                     -> FAIL an_empty_dual_fold_yields_no_observation
                   Both conjuncts load-bearing; six unrelated claims PASS throughout.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Dissolve the roster predicate into a direct match, and repair a stale citation to a symbol this branch deletes

REVIEW 56173, FINDING 1 -- FIXED. `ratchet_roster_is_empty` is gone; `emit_ratchet_enrolment_admission`
matches `r.roster` directly, as the wall it replaced did. The reviewer's instinct is right even though
the citation is not: `grep -c` for the named predicate-dissolution rule in DESIGN.md returns 0. The
real precedent is in the corpus -- `std.witness_admission` `witness_admission_predicate_dissolution_note`
records two predicates dissolved (review 39760) BY MAKING CONSUMERS MATCH THE COPRODUCT, and
`v2.std.witness_execution_routing` records three more. Same direction, so the change stands on its
merits rather than on the rule as cited.

REVIEW 56173, FINDING 2 -- DECLINED, three reasons, the third deciding.
  1. `filter` takes a Bool by construction, so dissolving `emit_ratchet_verdict_evaluated` means
     returning to a fold that appends -- the quadratic accumulator review 56160 and DESIGN §6's
     bare-minimum-cost ruling had just removed. The two findings would cancel.
  2. `emit_ratchet_verdict_holds` sits ten lines above it: same coproduct, same `-> Bool`, same total
     match, pre-existing and untouched here. That IS the module's idiom.
  3. It is not a second accessor for one answer, which is what the duplicated-shape objection needs.
     `RatchetRegressed` discriminates them: holds=false, evaluated=TRUE. A regression is a verdict the
     fold ESTABLISHED and then refused. Collapsing the two would make an evaluated failure
     indistinguishable from a subject nobody could measure -- the distinction this carrier exists for.

A STALE CITATION TO A SYMBOL THIS BRANCH DELETES, found via gentle-bee-495 rather than by review.
The `RosterDenominators` note cited `emit_ratchet_gating_admission` -- deleted by this very PR -- and
it would have shipped, in the module that argues for cited-symbol hygiene. Repaired, and widened,
because the sentence carried a second defect of the same family: it ended "when the file inventory
lands, the field changes and enrolment becomes possible", true of the wall as it stood and falsified
the moment gunbc#9231 landed. A dual denominator now makes a gate ELIGIBLE, not admissible, and an
observation does not consult the field at all. A stale citation wrapped around a stale claim.

THE GATE'S PERMIT IS UNOCCUPIED ON THE LIVE CORPUS, AND THAT IS NOT UNREACHABLE (gentle-bee-495,
2026-08-26). Hole 3's bounded-tail reader means a talkative subject arrives NOT-EVALUATED, so over a
realistic roster the any-unevaluated condition refuses essentially always and nothing RECEIVES the
permit. Recorded as occupancy rather than reachability, because only the second reading licenses
deleting the arm: DESIGN puts that test at the FIXTURE boundary, and the witness authors the permit
today as the one-field control beside the unevaluated refusal. Yes / yes / zero is a healthy guard
being quiet. The arm is NOT relaxed to tolerate unevaluated subjects -- that converts the
instrument's blindness into a green, the absorbing fallback arriving as a kindness to a roster that
is not ready. The relayed sample is carried as a SHAPE and not a rate; no proportion is stated.

EVIDENCE, controls green either side, every arm printing an explicit VOID fallback:
  control after dissolution            8/8 PASS
  C: gate permits without consulting   FAIL ×3, including the substring-free `_permits_none`
     denominator or verdicts           PASS an_empty_dual_fold (a different question, correctly green)
  E: empty-roster arm permits          FAIL an_empty_dual_fold_yields_no_observation
  restored                             PASS

Two earlier attempts at C were VOID rather than passing: forcing the arm by inventing a variant name
does not compile, and zero verdict lines greps identically to zero failures.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Carry the two-arm measurement behind the surviving predicate, not the one-arm version

smart-ram-730 re-derived the holds/evaluated disagreement against this branch and found TWO arms
disagreeing, not one. Re-measured here and confirmed:

  variant                    holds    evaluated
  RatchetCleanHeld           true     true
  RatchetDebtHeld            true     true
  RatchetRegressed           FALSE    TRUE       <-- disagree
  RatchetFrontierStale       FALSE    TRUE       <-- disagree
  RatchetNotEvaluated        false    false
  RatchetSubjectUnmeasured   false    false

WHY TWO IS STRONGER THAN ONE, and it is the reason this note changed rather than a citation being
added to it: a single disagreeing variant is dismissible as an accident of how that one case is
treated, which is exactly how the declined review finding would have read it. Two disagreeing in the
SAME DIRECTION show the functions answer different questions by construction.

AND THE READING IS THE PART THAT SURVIVES. Both disagreeing arms are cases where the fold DID
establish a verdict and then refused it -- a regression is a subject measured and found dirty, a
stale frontier row is a subject measured and found CLEAN while its admission row says otherwise.
Neither is a subject nobody could measure. That is the entire content of the split, and collapsing
the two questions would make an evaluated failure indistinguishable from an unevaluated subject:
the state-space conflation this carrier exists to refuse, committed inside the machinery refusing it.

Comment-only, and verified anyway rather than assumed -- this corpus refuses annotations in the
wrong position, so "it is only a comment" is not evidence. PASS_COUNT=8 over the eight claims.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Give both verdict questions one authority: a canonical classification, with the divergence class named

Review 56204 raised the predicate duplication a second time and rejected both my reasons. It is right,
and the part I got wrong is worth stating exactly: I argued the two predicates have DISTINCT SEMANTICS
and that `filter` needs a `Bool`. Both true, and neither answers the objection. Two total matches over
six variants are two places holding the coproduct's shape, and the compiler forcing both to be total
does not make them ONE AUTHORITY. Distinct semantics and duplicated shape knowledge can both be true
at once; my reply treated the first as refuting the second.

THE FIX GIVES BOTH QUESTIONS ONE SOURCE RATHER THAN DELETING A PREDICATE. `VerdictStanding` is the
real semantic axis and `emit_ratchet_verdict_standing` is now the only function reading the verdict
vocabulary's shape for classification:

  VerdictHeld                   established, and the ratchet holds
  VerdictEstablishedAndRefused  established, and then REFUSED
  VerdictUnestablished          no verdict about this subject exists at all

`holds` and `evaluated` are projections over that, not over storage. Measured after the change --
variant mentions: standing 6, holds 0, evaluated 0. One place fails to compile when a variant lands.
(`emit_ratchet_verdict_entry` and `_row` still match six: they extract per-variant PAYLOADS, a field
and a rendering, which is not classification knowledge.)

AND IT IS A BETTER MODEL THAN EITHER POSITION IN THE ARGUMENT. The divergence between the two
questions used to be EMERGENT -- two independent matches that happened to disagree on two arms,
discoverable only by measuring them against each other, which is literally how it surfaced. Now it
is a named class. `VerdictEstablishedAndRefused` IS the population where the fold established a
verdict and then refused it: a regression is a subject measured and found dirty, a stale frontier row
is a subject measured and found CLEAN while its admission row disagrees.

TWO NEW CLAIMS PIN THE RELATION, and they fail for different reasons:
  holding_a_verdict_entails_having_established_it -- the ratchet cannot hold over a subject nobody
    measured; a failure means some arm reports a verdict it never established.
  an_established_verdict_that_refuses_is_what_separates_the_two_questions -- the converse set is
    NON-EMPTY (2). If it were empty the split would be a decoration and `evaluated` deletable.

EVIDENCE: control 10/10 PASS. Mutating the classifier to call a regression UNESTABLISHED reds exactly
the divergence claim and nothing else -- the entailment claim correctly stays green, since collapsing
that class creates no holds-without-evaluated case. Restored 10/10.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Repair the merge: git factored a shared closing brace out of both conflict sides

The #9238 absorption left two files unparseable. Both sides' content survived intact; what did not
was MY closing braces, and the cause is worth recording because the resolution looked correct by
every check I ran.

WHAT HAPPENED. Both conflicts were append/append at the file tails. I resolved by concatenating the
two sides. But git had factored the SHARED TRAILING BRACE out of both sides as common context --
both blocks ended identically, so the closing `}` appeared ONCE, after the conflict region.
Concatenating two bodies then left one brace closing two functions:

  emit_subject_clean_frontier.dag        emit_ratchet_runner_execution_standing lost its `else`
                                        and function close; emit_outcome_under_membership was
                                        lexically swallowed by the still-open else
  ..._witness_test.dag                   an_established_verdict_that_refuses... lost its close;
                                        fn membership_with_candidates was swallowed the same way

WHAT ALMOST LET IT THROUGH, which is the reusable part. I verified the merge by checking that both
sides' SYMBOLS AND CLAIMS WERE PRESENT. All of them were. PRESENCE IS NOT WELL-FORMEDNESS -- every
grep reported a clean merge while neither file parsed.

WHAT CAUGHT IT was PASS_COUNT=0 on both lanes with no FAIL and no ERROR: the neither-verdict signal.
A parse refusal is not a verdict, so a filter written for PASS/FAIL is blind to it, and zero verdict
lines greps identically to zero failures. Raw output said it plainly: `module index refused: 2
unparseable .dag source(s)`. Review 56291 independently found the frontier half and prescribed the
same fix; it named only that file, so applying the review alone would have left the witness test
unparseable -- and the witness file is where the evidence lives, so the module would have compiled
with its claims silently absent.

VERIFIED BY EXECUTION, both lanes, because a merge can break either side:
  MINE    10/10 PASS
  THEIRS   3/3  PASS   (gentle-bee-495's boundary claims -- the ones a careless merge destroys)
  runner tool compile    0 blocking, 154 files emitted
  frontier module        0 blocking, 148 files emitted

The branch now also carries #9273 (d31fa86), so these claims pass against a tree where a
truncated capture has its own refusal rather than being read as a compiler scoping failure.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* One silence, two owners: split a subject nobody asked about from one the instrument failed on

A roster subject arriving at the fold with no reading was RatchetSubjectUnmeasured whether the
run had asked about it or not. The two facts share nothing but their silence and have opposite
remedies -- fix the instrument, or widen the run -- so a reader handed one UNMEASURED count
could not tell which they were looking at.

This module already draws exactly this distinction correctly one axis over: an admission row
naming a subject the roster lacks is reported as an ORPHAN rather than as a missing measurement,
in its own words "because the two have opposite owners". The selection axis is the same shape and
did not get the same treatment.

WHAT LANDS

  SubjectSelection = SelectionWholeRoster | SelectionSubset { entries }

A coproduct rather than a bare List<String>, deliberately: an empty list would mean both "a subset
naming nothing" and "no subsetting at all", which is the state-space conflation the type exists to
remove, reintroduced in the type that removes it.

RatchetSubjectNotSelected joins the verdict vocabulary and is threaded through the four total
matches over it. Both silences remain VerdictUnestablished, so a gate still refuses over either --
this splits OWNERS without promoting scope into evidence. The runner derives one selection and
reads it twice (the measured list and the classified selection come from the same value), because
two independent derivations could disagree and the disagreement would render as an UNMEASURED
subject the run had in fact measured.

EVIDENCE, and the reds are the point

The pair is a ONE-FIELD control: identical roster, admissions and readings, only the selection
differs. Mutated on BuildBuddy in both directions:

  A, selection ignored (the pre-change behavior): a_subject_the_run_never_selected... FAILS alone
  B, selection inverted:                          the two selected-side claims FAIL, that one passes
  restored:                                       all four pass

So claim A is the regression control for precisely the defect repaired here.

Stated rather than counted: neither_silence_establishes_a_verdict passes in every arm. It guards a
different property -- that neither silence is promoted to evidence -- and is NOT discriminating on
the selection axis.

a_partial_run_refuses_and_names_the_subjects_it_did_not_measure pinned the old UNMEASURED row text
and is updated with it; it stays red under both mutations.

Measured: 4 new/changed claims pass, the 12 pre-existing pass unchanged, and the runner compiles
0 blocking / 951 advisory.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* WIP: observation context carriers (parked to unblock 9315/9346)

* Observation context, binding agreement, persistence and pricing receipt (WIP: claims not yet executed)

* Construct the fixture digest through sha256_digest rather than a bare record literal

* Render the pricing receipt, and report what it does not speak for beside what it cost

The unevaluated-subject count was a first-class field on EmitPricingDimensions and nothing
rendered it, so it was carried and unreadable. A field nobody prints is a field nobody reads.

WHY THIS ONE IS NOT BOOKKEEPING. Per-entry measurement mapped across a roster covers the union
of those entries' closures, which is not the corpus -- a narrow run is on record reporting clean
while twelve real sites sat outside what it looked at. So the number of roster subjects the
receipt establishes nothing about is exactly the number saying how much it does not speak for.
It is rendered BESIDE the five cost figures rather than below them, because a clean board printed
without it reads as coverage while being precisely the shape that is not.

A refused receipt renders as a named refusal and carries NO dimensions at all. Zeroed cost figures
beside a refusal would read as a measured cheap run, which is the fabricated-plausible-output
failure in the artifact whose whole job is provenance. Every refusal cause names which precondition
failed, because a divergent binding, a dirty tree, an unbound coordinate and a persistence failure
have four different owners.

EVIDENCE: 13 claims pass, 8 prior claims unchanged, runner compiles 0 blocking. Mutation G drops
the unevaluated count from the renderer and fails a_bound_receipt_reports_what_it_does_not_speak_for
ALONE -- so the claim responds to that field and to nothing else. Restored: 13.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Ground the pricing quantities in std.measure: Nanosecond and ByteSize, not bare Int

Review 56351, and the finding is this change's own thesis applied where I missed it. The PR
grounds every provenance coordinate meticulously -- CommitSha, Digest, run coordinates, all
through authorities that already own them -- and then minted wall_ms, cpu_ms, peak_rss_bytes and
report_bytes as bare Int in the same file. Grounding provenance while re-minting the pricing
scalars is inconsistent on its face, and gunbc.emit_diagnostic_observation had already recorded
the identical finding one module over: "THE SIZES ARE ByteSize AND NOT Int".

THREE DEPARTURES FROM THE REVIEW AS WRITTEN, each measured rather than assumed.

NANOSECOND, NOT Duration. std.measure declares no Duration type; its carriers are Nanosecond,
Millisecond and Second. Which one is not a preference -- the module states that Nanosecond is the
canonical exact elapsed-time carrier, that Millisecond "remains a policy and presentation scale",
and that measurement, ordering, joining and attribution must retain Nanosecond. A pricing receipt
is measurement that later runs are joined against, so a millisecond field would have recorded a
floor-rounded reading as though it were exact.

ONE LINE THE REVIEW DID NOT NAME. ObservationPersisted.report_bytes was the same class on an added
line and is now ByteSize. Fixing the flagged lines and leaving its sibling would have repaired the
report and not the defect.

TWO FIELDS DELIBERATELY LEFT Int. roster_size and unevaluated_subject_count are CARDINALS, not
quantities in a unit system: no scale to convert, no dimension to check. Wrapping them in a measure
would assert a structure they do not have.

The _ms suffixes went with the types -- a unit spelled in the field name beside a unit spelled in
the type is the same fact twice, and the two disagree the day the scale changes.

MEASURED: 13 claims pass with the typed carriers, 6 prior claims unchanged, runner compiles
0 blocking. The seven mutation arms behind those claims are unaffected: none of them touched a
unit field, so the discrimination they established still holds.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* The gate refusal must say WHY, not only WHICH -- and derive each component's values once

TWO FIXES, ONE OF THEM A DEFECT AND ONE A COST SHAPE.

THE DEFECT. ratchet_unevaluated_subjects mapped the filtered verdicts to their entries and dropped
the variant. I reported that as a convenience projection losing information; smart-ram-730 verified
it in the tree and established it is worse: THAT LIST IS THE GATE REFUSAL'S PAYLOAD.
EnrolmentRefusedUnevaluated.subjects carried entries with no reasons, so the refusal could name
which subjects blocked enrolment and could not say why any of them did. DESIGN requires a refusal to
be typed AND located AND to carry its cause; this one carried three causes as one.

The three have different owners and different repairs -- a reading that established no population, an
instrument asked about a subject that produced nothing, and a subject the run never asked about --
so a reader had to go find r.verdicts to learn which they were holding.

It is the corpus's "total at the level examined, blind one level down", with the named tell present
exactly as described: the payload-carrying position kept the entry and discarded the variant. The
filter is exhaustive over evaluated-versus-not and says nothing about the distinction that decides
who fixes it.

WHERE THE FIX WENT, and it is the part worth arguing. The reason rides on the canonical
classification's own VerdictUnestablished arm rather than in a second total match beside it. Review
56204 dissolved exactly that duplication once already, and recovering the reason through a parallel
classifier would have reintroduced it. One authority over the verdict vocabulary, and the arm gains
the payload it was missing.

THE COST SHAPE. Review 56364 approved and called the double evaluation in component_agreement a
non-blocking nit because the denominator is a handful of components. DESIGN's bare-minimum-cost
ruling says a proven cost-shape defect is ALWAYS fixed regardless of the realized n, and names "n is
small here" as not a time-stable fact. Same rule already applied to review 56160 this session.
distinct_component_values evaluated subject_component_value twice per subject; component_agreement
built the distinct values twice per component. Now: project once then deduplicate, and one extracted
helper derives a component's values a single time.

EVIDENCE: 19 claims pass. Mutation H flattens the reason back to one arm and fails BOTH new reason
claims while leaving the pre-existing gate claim passing, since that one pins a reason H did not
touch. Mutation I makes the extracted divergence helper always agree: 19 to 15. Mutation J makes the
dedup keep duplicates: 19 to 16, and it fails the AGREEING control -- duplicates inflate the distinct
count past one, so identical subjects read as divergent. Restored 19 after every arm, so neither
extraction became decorative.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Aug 31, 2026
…row, prose leaves the data plane (review 57758)

Three of the four findings, verified against the tree first:

- src/v2/std/nat.dag carried a String data row of commentary. Converted to a
  module-scope // annotation on nat_max, which is the DESIGN section 4c
  quarantine boundary; the row predates this lane (#9238) but the file is this
  lane's to fix.
- The same note narrated the two-Nat fork without scheduling its end. The fork
  is now gunbc.guarantee_rung_drop two_nat_authorities_stall, with ceiling
  StructurallyImpossible and a trigger naming the CAPABILITY (one Nat
  declaration with the other derived), not an artifact. This lane did not
  create the fork but made it load-bearing by giving the Peano side real
  operations.
- OperatorRealizationRefusal typed its operator as String, a closed vocabulary
  flattened to text. Both variants now carry BinOp itself, producers pass the
  variant, and the single glyph spelling happens once at the message boundary.
  binop_label survives as that rendering fact with an annotation recording the
  corpus check: no BinOp-to-glyph producer exists elsewhere, so it is the first
  authority for the mapping rather than a second one. The rendered message text
  is unchanged, so the emitted-path witness pins the same bytes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013koFunEtpLQCnvUiz85k7Y
gunbai-bot Bot pushed a commit that referenced this pull request Aug 31, 2026
…on is reachability not a new call

required-witnesses-build fails after refreshing onto main (42 commits) where it
passed at abee235. Not stale artifacts -- the obvious hypothesis and the wrong
one. run_generated_artifact_drift_gate_body refuses with NoSuchField { Optional,
shape } at extdeps.bmc.types:183, 'matches.first().shape', a field read straight
off a first() result.

The site predates this branch (#9238) and was present at abee235 under a green
build lane. Nothing in main is defective and nothing here changed to reach it;
main widened generated_artifact_gate by 98 lines and the site entered the gate's
evaluation closure. Neither side is broken alone.

In-class rather than a census miss: it is a value-position method-form site, a
member of the 646-occurrence population this document names and deliberately does
not roster at identity grain.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N8xvN1T1NKiJqCUqwEmDgK
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