Skip to content

first(): construct the Optional std.algebra declares, migrate the field-access population, and declare the parameter coercion - #9785

Closed
gunbai-bot[bot] wants to merge 39 commits into
mainfrom
session/still-swift-363
Closed

gunbai-bot[bot] wants to merge 39 commits into
mainfrom
session/still-swift-363

Conversation

@gunbai-bot

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

Copy link
Copy Markdown
Contributor

Draft. One hold remains; the ordering hold is DISSOLVED.

  1. Ordering (from the BT-N brief): PR BT-0: gunbc build gunbc — commanded exact build through the emitted CLI #9775 enrolls this exact divergence as a KNOWN-RED. DISSOLVED — confirmed by the BT-N lane: nothing in this PR waits on BT-0: gunbc build gunbc — commanded exact build through the emitted CLI #9775 or on BT-0 any longer. The title carried that hold longer than the fact did.
  2. Grounding: the repair here is deliberately incomplete, and the census explains why (below). The builtin parameter-signature grounding is a genuine independent blocker and is being authored on session/still-swift-363-builtin-sigs.

Review corrections carried (review at head 4ed8c14f71)

  • The bounded comparison population is 14, not nine. Ten are merge-admission sites — four in gunbc.merge_admission_produce and six in gunbc.merge_admission_subject, per Merge admission: eliminate the ten first() comparisons before the Optional repair turns them silently false #9912 — plus four elsewhere. An earlier draft said "nine merge-admission sites"; that number was carried, not counted.
  • The Optional/Null wall was over-broad and is narrowed. It refused every constructed Optional against the host Null carrier, so Present { value: x } == none — whose correct answer is ordinary false — was unwritable, and the diagnostic called any constructed Optional a representation of absence, which is false for Present. Only Absent-vs-none is a straddle now. Five controls measured with claim_batch: Present == none is false and Present != none is true (both enrolled), Absent == none and Present == bare still refuse with their own messages (fixture-measured — a test fn returns Bool and a refusal aborts evaluation), Optional-vs-Optional unchanged.
  • The optionality judgement selected rows by method spelling alone. The algebra is receiver-keyed and a spelling is not: 23 spellings are declared in more than one profile and three of them shape their returns differently (join, member, get). It now derives the profile from the receiver value's own carrier through kernel_algebra_profile and takes that profile's single row — the same fork resolution the get row needed.
  • The judgement is now two-directional: a row declaring a concrete non-optional return refuses an Optional result. The naive symmetric rule was deliberately not implemented, because ReceiverSelf, ReceiverElement, ReceiverKey, ReceiverValue and AlgebraTypeVariable are all satisfiable by an Optional at runtime — implementing it blindly would have repeated the over-refusal above in a new place.
  • Neither half has an authorable red, and that is measured: zero spellings in dag/std/algebra.dag declare mixed optionality across profiles, so no accepted program distinguishes the two selections. Discrimination is mutation-measured — with the join arm altered to return optional_present(..), ["a", "b"] |> join(",") refuses; unaltered it passes. The next-rung trigger is recorded in the witness.

The divergence

dag/std/algebra.dag declares first/last/get/lookup/map_get with return_type: OptionalOf { inner: ReceiverElement }. The Rust emit arm realizes that row as Option<T>. The interpreter arm answered the same question by hand and answered it differently — items.front().cloned().unwrap_or(Value::Null), the raw element. DESIGN §5 silent wrongness: it typechecks, returns a wrong answer, and warns nobody.

Executed, not reasoned (gunbc run, dag + src/v2 roots, unmodified seed):

probe interpreted declared
[] |> first OUTER-ABSENT OUTER-ABSENT agrees
[Absent] |> first OUTER-ABSENT OUTER-PRESENT-INNER-ABSENT diverges
[Present { value: "x" }] |> first x x agrees (control)
(["x"] |> filter(n => true) |> first) == Present { value: "x" } false true diverges
... == "x" true not well-typed diverges

Enrolled as dag/test/claim/first_optional_construction_witness_test.dag: 7/7 green with this change, 4 red against the unmodified arm, three green positive controls. branded_list_first_optional_witness stays 8/8 green.

Census — docs/plans/first-optional-divergence-census.md

186 terminal |> first sites / 81 files on main, rostered at identity grain in three shapes (143 match-eliminated, 37 returned onward as T?, 6 value-position). The parent lane's 187/82 differs by one row not present on main. The roster, not the count, is the deliverable.

The census kept going past the pipeline spelling, and that is the finding: the METHOD form .first() is a separate population — 646 occurrences over 180 files — dominated by the value position (parse_int(s: fields.first()), trim(tokens.first()), percent(scalars.first())). Those work today only because the emitted arm inserts rust_call_arg_fail_closed_unwrap's .expect(..) while the interpreter needs no coercion at all, having never wrapped. The raw-element arm is the compensation the interpreter's MISSING argument coercion has been leaning on corpus-wide.

Measured: with only the first construction in place, bmc_capability_solve_witness_test's firmware_wire_version_is_parsed_before_track_matching — a witness that names nothing about optionals — flips PASS → FAIL (parse_int expects a string argument, got Variant).

What is repaired, and what blocks the rest

Repaired: the four arms construct their declared Optional (call-site decided, per map_lookup_as_optional's stated rule); eval_algebra_method_inner refuses when an arm's result does not inhabit the optionality all_algebra_field_templates() declares; .value on an absent Optional refuses instead of returning Value::Null; call_function_inner gains the optional-into-required-parameter coercion the emitter already had.

Blocked: a builtin call never reaches call_function_inner, and builtin_function_registry maps a builtin name to a RETURN TYPE only (04_sigs says so). Argument cardinality cannot be derived for a builtin. Deciding it from the argument's value shape is validation standing where construction was available, and is exactly the inference map_lookup_as_optional's doc comment refuses. So this lane stopped rather than shipping it.

The grounding this class waits on: builtin PARAMETER signatures, modelled beside the return type the registry already carries. That is model-before-implement work in std/ ahead of any further pipeline edit — a routing decision, not something to improvise inside this repair.

RETRACTED: the generated-doc drift was not deliberate, it was a defect of mine

This section previously argued that the DESIGN.md / docs/design-ledgers.md drift was
deliberate and must not be regenerated. That was wrong and I am retracting it, not editing it
quietly.
I had carried drifted=2 for days as an inherent property of the branch. It was a
consequence of a defect I introduced, and it could not have been otherwise, because the regen
actuator itself was dying on that defect.

The control makes the attribution unambiguous. main run 33481211724, same phase, same
denominator: rostered=35 adjudicated=35 matches=35 drifted=0 unadjudicated=0. This branch, before
the repair: adjudicated=32 matches=30 drifted=2 unadjudicated=3. All five were mine.

The root, one root under all five. tools.generated_artifact_gate main_wet refused with
CallContractMismatch { callee: "outcome_accepted", ... } — this branch's own coercion arm, firing
on a call that is correct. v2.std.diagnostic outcome_accepted is fn outcome_accepted<T>(value: T) -> Outcome<T> with T free, so Optional<Node> is a legitimate instantiation and not a
cardinality escape. param_declares_required_value asked three questions of the declared type — is
it spelled Optional, does it carry CardOptional, is it the parameter's own name — and none of
them can see a free type variable.

Both arms were wrong, and the quiet one was the dangerous one. Absent was refused: loud and
false. Present was silently unwrapped into the callee, so a caller whose T = Optional<Int>
had the callee receive Int — the same silent semantic divergence this PR exists to remove,
reintroduced at a new seam by the removal itself. My first probe missed it because it matched on the
callee's return value and that match was lenient enough to accept a bare value against a Present
pattern; the probe that found it observes the arrival inside the callee and reported
UNWRAPPED - arrived as a bare value.

The fix reads the declaration, not the spelling. v1.compiler.parse parse_fn_body_from_prefix
builds a fn node's params as concat(type_params, value_params), and a type parameter is exactly
the entry whose declared type is its own name — the shape the positional-parameter filter beside it
already uses. Two discriminating REDs are enrolled in
test.claim.first_optional_construction_witness, each measured red on the defective head and green
now, beside the existing positive control; they stay enrolled rather than retiring with the climb
(§4b(4)).

Which path this was measured on. The interpreter path, and only that one.
gunbc.recurring_failure_mode mitigation_injected_where_judgment_declined was filed against the
Rust emitter for this same seam and this same callee, and its stated trigger is a producer that
can distinguish a free type variable from a declared non-optional formal. This PR builds that
producer on the interpreter side. §4b(1) measures source→interpretation and source→each emission
target independently and takes the minimum, so this does not retire or weaken that row: it
stands at the emitter's rung. I have not run the emitter path at this seam and claim nothing about
it.

Why this lane is red, and why that is the measurement

required-witnesses-floor refuses: unexpected_failures=427 verdict_incomplete=5 non_verdict_unenrolled=5. Measured against a trunk control — the same artifact on main at
b41d5648 reports 0 errored, 0 failed, 3065 passed — the entire population is caused by this
partial repair and none of it is inherited.

The 5 non_verdict_unenrolled are known-reds that now error instead of returning their known-red
verdict. v2.workflow.floor_non_verdict leaves one direction unconfined: nothing compares the
roster at HEAD against the roster at the merge base, so those 5 could be rostered in this same
change and both arms would pass. They are not being rostered. They are caused by this branch, so
enrolling them would use the wall's one unconfined direction to launder a regression past the wall
that exists to refuse it. The disposition is to finish the repair.

The 448 at identity grain, and which failures are load-bearing

Enumerated from required-floor-disposition (run 33362356177), not from the run log — the log's
failed= folds the distinction this table splits.

outcome count
runtime-errored-before-verdict 442
failed (reached an assertion, answered false) 6
known-red-held 15
route-gap-before-verdict 47
passed 2638

The 442 are one population, not a spread. Every one is v2.test.* — 157 v2.test.claim, 132
v2.test.manual, 71 v2.test.emit, 52 v2.test.execution, and 28 across nine smaller segments.
Zero are dag/test/claim witnesses. That is the self-host coupling and not a corpus-wide
breakage: the v2 compiler is .dag interpreted by the v1 seed, so changing the interpreter's
optional projections changes v2's behaviour as it runs. The concentration is consistent with it —
45 in bash_command_fold, 24 in trait_derive_supplemental_generic_bound_contract, 19 in
symbol_index.containment, 18 in sg2_type_expression_projection.

Honest limit on this half: the disposition artifact carries identity / disposition / matched_prefix / outcome and no cause column, so the 442 are grouped by identity here, not by
error. Claiming they share one cause would need per-claim error text, which the log folds. What is
established is the population and its shape, not a single root for it.

All six assertion failures are load-bearing. None is incidental. Each was read at its assertion
rather than inferred from its name — which matters, because two of the names actively mislead:

claim what it asserts class
self_host_symbol_identity_binding_witness.w_next_rung_representations_have_no_realization_yet rust_representation_realization_for(...) == none Phase D — none-literal straddle
self_host_symbol_identity_binding_witness.w_symbol_ruling_names_a_realizable_representation ... != none and ... == none Phase D — same
emit_host_shell_exec_run_equals_eval.shell_exec_run_eval_argv_is_bash_dash_s argv().skip(n: 0).first() == "bash" value-position first()
emit_host_shell_exec_run_equals_eval.emit_host_shell_exec_run_equals_eval_holds composes the above value-position first()
cargo_build_run_argv_witness.cargo_build_run_argv_holds argv.skip(n: index).first() returned as a bare String value-position first()
value_null_split_witness.raw_get_miss_differs_from_optional_absent raw get miss ≠ optional_absent() the enrolled discriminator

The two self_host_symbol_identity claims are named for symbol-identity binding and are nothing of
the kind here: they fail on == none against an Optional-returning function. none evaluates to
Value::Null while the repaired arm produces Optional::Absent, so the comparison is false. That is
Phase D's class — the ~218 == None sites over 66 files — surfacing in a witness whose title gives
no hint of it. Reading these two by name is what earlier led this lane to attribute them to #9741,
which a trunk control refuted.

So the six split 2 / 3 / 1 across the three gates the census already named: Phase D's none
migration, the value-position first() sites the emitter's rust_call_arg_fail_closed_unwrap has
been compensating for, and the discriminator that is red by design because Phase B landed. There
is no fourth cause among those six — the failures are the repair's own predicted surface.

RETRACTED, and by measurement rather than by scruple: an earlier draft of this paragraph ended
"which is the evidence that the census closed rather than merely counted." That claim is FALSIFIED.
Three unmigrated sites were found at head fa11d87da0e — roadmap_forecast.dag lines 329, 352 and
354 — in a file this very census covered, and confirmed by two independent routes. The census
counted; it did not close. No sentence in this body claims a closed population, and where a
population is stated it is stated against its base.

Two roster admissions were available and both were refused

Recorded because a reader cannot otherwise tell that a choice was made — in both cases the roster
was the cheaper arm, the mechanism would have accepted it, and the check would have gone green.

1. floor_non_verdict — the 5 non_verdict_unenrolled. Five known-reds error under this branch
instead of returning their known-red verdict, and that is what turns the lane red. That module
declares one direction unconfined: nothing compares the roster at HEAD against the roster at the
merge base, so those 5 could be enrolled in this same change and both arms would pass. They are
not enrolled. They are caused by this branch, so rostering them would use the wall's one unconfined
direction to launder a regression past the wall that exists to refuse it. The roster stays
Empty {}; the diff on that module is comments only. The disposition is to finish the repair.

2. nfr_roster_receipt — value_null_phase_eq. rust-unit-tests refused this branch with one
unrostered non-fold residue site, mine, added by the plan amendment: a match on a whose seven
arms each matched b with a _ => false wildcard over a closed coproduct. non_fold_residue_roster
would have taken the entry and greened the job. Instead the function is now derived from one
exhaustive value_null_phase_ordinal (no wildcard), following std.fermi fermi_ordinal. The
safety difference is the whole point: under the old form an eighth phase silently takes the _ arm
at seven sites and compiles clean; under the new one it makes the ordinal non-exhaustive and the
compiler refuses. Verified with the nfr_ suite at 14/14 including
red_control_wildcard_over_closed_coproduct_is_residue, so the detector still discriminates — the
site is gone rather than the check blunted. rust-unit-tests passes in CI at abee235.

A roster entry is a real mechanism and neither of these refusals says otherwise. But both sites were
decidable and had a construction available, and §4b puts construction over validation: below the
attainable ceiling is a correctness gap, not optional elegance.

Rung

Below the ladder (silent wrongness) → mechanically preventable once both halves land. Ceiling is structural impossibility, reached when interpreter arm bodies are projected from the same rows the emit arm reads (the §7 self-host frontier for v1_interpreter); the next-rung trigger names that capability.

🤖 Generated with Claude Code

https://claude.ai/code/session_01N8xvN1T1NKiJqCUqwEmDgK


Since the exact-head REQUEST_CHANGES at 4ed8c14f71

All three semantic-authority blockers are repaired, and the two record corrections stand in this body above.

1. The Optional-vs-Null wall is narrowed to the case that is actually a straddle. Only Absent against the host Null carrier reaches the migration refusal; a constructed Present against none answers ordinary false, because present-vs-none inequality is legitimate and calling every constructed Optional a representation of absence was false for Present. The narrowing is by variant (is_absent_variant), not by another spelling-based carve-out — which is the refusing mirror of the spared-case failure this lane's own ledger row names.

2. The optionality judgment runs in both directions. The local DeclaredOptionality is deleted and the authority is std.algebra algebra_result_optionality, a three-armed OptionalityRequired | OptionalityForbidden | OptionalityUnconstrained. OptionalityForbidden refuses an Optional result from a row declaring a non-Optional one, so the validator no longer admits in one direction by omission. The third arm exists because a receiver-relative return constrains neither direction, and it is witnessed rather than asserted.

3. The judgment is keyed by the receiver-selected row, not by method spelling. algebra_row_returns_optional(method) is gone. receiver_algebra_profile derives the profile from the receiver value and algebra_row_for_receiver(method, profile) selects the row, so the exact selected row is the authority. The discriminating fixture is a planted same-spelling fork whose two profiles declare opposite optionality, proving the receiver-selected row wins rather than a global same-name scan.

What this head adds beyond the blockers

The field-access population is migrated: every .first().<field> site in the corpus, 22 production across eight files plus the witness sites. Each production Absent arm is derived from the sibling refusal the site already sat beside — the count() == 0 arm — and where that guard became redundant it is dissolved rather than left standing beside the match. The one site with no sibling to derive from, product.pcb.copper nth_layer_function, returns Optional so its single caller answers with the LayerOutsideStackup refusal it already declares for the out-of-range case. Witness sites answer Absent => false.

The parameter coercion is declared, not commented: gunbc.rung_drop optional_into_declared_nonoptional_parameter. It claims no previous rung at its subject grain, because the emit arm carries the coercion on main in six places across five emitted files — this PR gave the second arm the same seam and thereby made a corpus-wide coercion visible at one site instead of invisible at two. Temporary rung is mitigatable: Absent refuses with a typed, located call-contract-mismatch; only Present is unwrapped. The trigger is named at capability grain — the compile seam refuses an Optional flowing into a parameter declared non-optional, at both arms, proven by a discriminating pair — and the row states explicitly that a site census does not retire it, because a census retires the corpus and never the coercion.

What is verified, and what is not

Verified by execution: 4487 file(s) parse-clean from the corpus walk, and dag/test/claim/commit_writer_heal_admission_real_execution_witness_test.dag resolves with no NoSuchField. The pre-migration head's floor red was exactly NoSuchField: no field 'oid' on type 'Optional', three LocalRepoWetTerminalVerdictNotExpected refusals in that file's local-repo-wet lane — the discriminating red this change is meant to clear.

Not verified here: the wet-lane verdict itself. claim_batch reaches those four witnesses only hermetically and refuses them as a route gap rather than a verdict, so a typecheck is not the floor verdict and this body does not claim it is. CI's floor lane is the arm that decides.

Every population in this body is stated against a base, because a census has a freshness window

A census reported as a structural property — "the population is closed", "N sites, all migrated" — is
a measurement with its base stripped off. It is the same defect as a green CI run cited for a tree
that no longer exists, aimed at a census instead of a build. On a repo merging every few minutes the
window between the base a census was taken at and the head it is cited on is never empty.

This is not hypothetical here, it is what happened. Two CrossRepresentationEquality floor refusals
appeared on this branch at a head whose census had already returned zero. The cause was not a missed
site: extdeps.languages.yaml.ingest yaml_lines_from_source compares raw.skip(n: n - 1).first()
against a bare "", and it landed on main in #9875 after my census ran. The census was correct
about its base and wrong about the head it was being read on.

So, stated at grain. At merge base 2b56084270d (merged into this branch at 76e41fa29cc),
these four shapes each return zero over dag + src/v2:

  1. field access on the result — xs.first().<field>
  2. an element-variant match — match xs.first() { <element constructor> => .. }
  3. let bound then read — let e = xs.first() … e.<field>
  4. comparison against a bare value — xs.first() == <non-optional>, excluding none, a
    Present { .. } literal, and another optional, all three of which are legitimate

A fifth shape was then found and migrated: the prefix-call form with a field read,
first(gaps).nodes, which none of the four greps could express because they all keyed on the method
spelling. That is the point:

Five shapes is a FLOOR on the shape count, not a total. Each of the five was invisible to every
grep that preceded it, and the fifth was found by asking what spelling the previous four assumed
rather than by extending them. The honest claim this PR makes is that five shapes return zero at
this merge base — not that the population is closed. The mechanical closure of the whole class is
the compile-seam refusal named in the optional_into_declared_nonoptional_parameter rung-drop row's
restoration trigger, and until that lands, a grep census is a floor with a timestamp.

gunbc-ci-auto-heal and others added 3 commits August 31, 2026 02:30
…f that has an authority

`dag/std/algebra.dag` declares `first`/`last`/`get`/`lookup`/`map_get` with
`return_type: OptionalOf { inner: ReceiverElement }`. The Rust emit arm realizes that row
(`{recv}.first().cloned()` -> `Option<T>`); the interpreter answered the same question by hand and
answered it differently -- `items.front().cloned().unwrap_or(Value::Null)`, the RAW element. Two
realizations of one declared signature that disagree are DESIGN.md section 5 silent wrongness,
outside the guarantee ladder rather than low on it.

CENSUS (docs/plans/first-optional-divergence-census.md). 186 terminal `|> first` sites over 81
files in dag/ + src/v2 on main, rostered at identity grain in three shapes: 143 eliminated by
`match` (the population the interpreter's compensating raw-unwrap arms already made agree, and the
one the repair must not break), 37 returned onward as the enclosing function's `T?`, 6 flowing into
a value position. The count is for reconciliation with the parent lane's 187/82 only; the roster is
the deliverable.

The census did not stop at the pipeline spelling, and that is where it earned its keep. The METHOD
form `.first()` is a separate population of 646 occurrences over 180 files whose dominant idiom is
the value position -- `parse_int(s: fields.first())`, `trim(tokens.first())`,
`percent(scalars.first())`. Those work today because the emitted arm inserts
`rust_call_arg_fail_closed_unwrap`'s `.expect(..)` while the interpreter needs no coercion at all,
having never wrapped in the first place. So the raw-element arm is not one bad handler: it is the
compensation the interpreter's MISSING argument coercion has been leaning on corpus-wide, and
repairing `first` alone converts a silent agreement into a silent disagreement.

MEASURED, not argued. A five-case probe run through `gunbc run` fixes the divergence (`[Absent] |>
first` read as an empty list; `(["x"] |> filter(..) |> first) == Present { value: "x" }` false
interpreted and true emitted). Those five rows are enrolled in
dag/test/claim/first_optional_construction_witness_test.dag, 7/7 green with this change and 4 red
against the unmodified arm, with three green positive controls separating "constructs the Optional"
from "refuses everything". branded_list_first_optional_witness stays 8/8 green.

REPAIRED HERE: `first`/`last`/`get`/`lookup` construct the Optional their roster row declares,
decided by call site rather than value shape (the rule `map_lookup_as_optional` already states);
`eval_algebra_method_inner` refuses when an arm's result does not inhabit the optionality
`all_algebra_field_templates()` declares for it; `.value` on an absent Optional refuses instead of
returning `Value::Null`; and `call_function_inner` gains the optional-into-required-parameter
coercion the Rust emitter already had, unwrapping `Present` and stopping the line on `Absent`.

NOT CLOSED, and the census says why: a builtin call never reaches `call_function_inner`, and
`builtin_function_registry` maps a builtin to a RETURN TYPE only, so argument cardinality cannot be
derived for one. Deciding it from the argument's value shape is validation standing where
construction was available. The grounding this class waits on is builtin PARAMETER signatures; the
doc names it as the blocker rather than working around it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N8xvN1T1NKiJqCUqwEmDgK
The parent lane's read of the first draft is right — a shape says where the value goes, only a
disposition says whether the two realizations answer differently on an input the corpus can reach,
and this document is about to be cited instead of re-derived.

186 occurrences resolve to 181 real sites over 81 files plus 5 non-sites (4 inside `//`
annotations, 1 inside a string literal carrying a probe program), which reconciles exactly with the
parent's 187/82 as that count minus #9775's own known-red row.

Dispositions, each measured rather than asserted:

- AgreesUnderCompensation, 142. Its failure condition is an element type that is itself `Optional`,
  and the corpus declares five list-of-optional carriers in total, all in witness tests, none
  reaching a `first`. Zero harmed today — which is exactly why the class stayed invisible: the shape
  that dominates the corpus is the one the compensation covers.
- Propagates, 36. Resolved one level out by following all 36 functions to their call sites: 72
  callers eliminate by `match`, 2 tail-propagate into another `T?`, and 4 compare `== none`, which
  agrees only because a miss is `Null` on one side and `Absent` on the other and both compare equal
  to that one constructor. Zero harmed today, by a margin one constructor wide.
- HarmedNow, 3, listed in full: `cache_facts_for_id` declaring `-> CacheInterfaceFacts` over a
  `first()` (with `cache_layer_plan_primary`/`_fallback` as sibling defects in the same module), and
  two `measure_count(m: .. |> first)` argument sites that agree while non-empty and diverge on empty.

Three of 181 read alone argues the class is not worth repairing. It is the wrong denominator, and
the method-spelling section already says why. Both filters over that population are now named
beside their producers — 646/180 here, 655/178 by the parent's independent filter — because they
disagree, and a disagreement is the reason to cite the producer rather than the figure.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N8xvN1T1NKiJqCUqwEmDgK
…ts denominator

Reading std/ before authoring turned the routed step-1 task into a different one, and the
difference is the second time on this class that the obvious repair was the wrong one.

dag/std/primitive_identity.dag ALREADY models the callable-signature grounding and already
executes: PrimitiveSignatureGrounding, PrimitiveSemanticContract, PrimitiveSignatureResolution
(SignatureResolved carries `parameters: List<AlgebraTypeTemplate>`), primitive_signature and
primitive_arity, green in primitive_signature_grounding_witness_test. Its own doc comment refuses
the fork this lane was about to commit -- "the contract carries a KEY into the one authority, never
its contents" -- and it keys on (canonical_name, profile) rather than name alone precisely because
`get` reads differently on the List and Map profiles. So there is no carrier to mint, and authoring
one would have been the section 3 nickname the routing message warned about.

The gap is coverage, and it is measured: of builtin_function_registry's 131 names, 20 resolve
through primitive_signature and 111 answer SignatureNotGrounded. parse_int -- the name that reds the
corpus control -- is one of the 111.

The 111 are two populations and nothing separates them: language primitives that should carry a
signature, and host/lens transports whose parameter shape is a Realization fact and belongs with
the transport. The obvious discriminator fails, measured rather than assumed:
gunbc.v1_interpreter_primitive_surface enumerates an arm for both by construction, so joining on it
classifies doc_graph_orphan_count and parse_int identically. Splitting on a naming convention would
be the smuggled heuristic section 5 names, so this lane raises the question instead of picking.

Also recorded, independent of this class: the 20 overlapping names are two authorities that already
DISAGREE. The registry is receiver-blind -- `reverse` types as List where algebra says ReceiverSelf
(so a String reverse), `map_keys` and `map_values` share one element type variable where algebra
distinguishes ReceiverKey from ReceiverValue, `concat` types as String where algebra says
ReceiverSelf. Same fork as the first() divergence, one layer up: not two realizations of one
declaration, but two declarations of one operation.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N8xvN1T1NKiJqCUqwEmDgK
@gunbai-bot

gunbai-bot Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor Author

The red floor is the measurement, not a defect to fix. Nothing will be pushed to make it green.

This PR is a held draft carrying a deliberately incomplete repair — the first/last/get/lookup Optional construction without the optional-into-required argument coercion that the emitted arm already has and the interpreter does not. The census in docs/plans/first-optional-divergence-census.md says in advance that landing one without the other converts a silent agreement into a silent disagreement across the method-spelling population (~650 sites). The floor is that prediction being measured.

Correcting my own earlier number. I previously reported this as flipping one witness. That was wrong in a way worth naming: I ran claim_batch against two witness files, one of ten witnesses flipped, and I reported a two-file sample as though it bounded the corpus. The measured figure from the required-witnesses-floor run is planned=3141 executed=3141 passed=2630 known_red_held=15 failed=442. Against a ~0 baseline that is ~440 claims turned red, two orders of magnitude larger than I said, and it strengthens rather than weakens the case for the sequencing: it is the blast radius of landing the construction alone.

A full enumeration of the 442 at identity grain is running, with the failing-line count computed on the runner rather than grepped from the streamed log, so a truncated stream cannot be mistaken for a short failing set. It will be posted here, split into the value-position argument sites the census predicted versus anything it did not — the second group being the part that matters.

Two holds remain, and they are different objects:

Neither is the dissolution trigger. The trigger is semantic: when the interpreted and emitted realizations of first agree on the optional result shape. No PR number is load-bearing anywhere in this branch.

gunbc-ci-auto-heal and others added 2 commits August 31, 2026 04:20
…ss is Phase B of an open lane

THE NUMBER, CORRECTED AND THEN ENUMERATED. An earlier revision reported the partial repair as
flipping ONE witness. That was a two-file claim_batch sample reported as a corpus bound, and worse:
the witness it named, bmc_capability firmware_wire_version_is_parsed_before_track_matching, has
disposition declined_outside_gate_closure / not_executed, so the floor never runs it. The sample was
drawn from outside the population the floor measures.

The floor reports failed=442 of 3141. The job log prints only six per-claim lines, which reads as
truncation and is not -- the required-floor-disposition ARTIFACT separates the outcomes the summary
folds: 442 runtime-errored-before-verdict, 6 failed (assertion), 1 budget-refused, 47 route-gap, 15
known-red-held. The 442 errored before reaching a verdict; they did not assert and fail.

ALL 442 ARE v2.test.* and none is a dag/test/claim witness. The v2 compiler is .dag interpreted by
the v1 seed, so changing the interpreter's projections changes v2's own behaviour as it runs. The
blast radius is the interpreted v2 compiler, which is a different shape from the value-position
argument sites this census predicted.

A MECHANISM CORRECTION, which matters more than the verdict it supported. This document said the
four `== none` sites agree "because a miss is Null on one side and Absent on the other and both
compare equal to none". Wrong: in the interpreter `none` EVALUATES TO Value::Null, so the raw side
compares equal because it IS Null, and a constructed Absent variant does not compare equal at all.
Those sites agree BEFORE the construction and break after it; two are among the six assertion
failures. The verdict was right about the pre-change state by the wrong route, and the wrong route
is what hid the none-literal migration from the first draft.

THE CLASS ALREADY HAS AN AUTHORED PROGRAM. gunbc.plans.value_null_split (lane keen-ferret-250)
models Value::Null's four overloaded meanings and phases the repair A-E. This branch is its Phase B,
built without knowing the plan existed. Its Phase-A witness predicted this branch's failure BY NAME:
"raw_get_miss_differs_from_optional_absent .. flips RED in Phase B when get+Optional routes through
map_lookup_as_optional" -- and it is one of the six. That is the enrolled signal Phase B landed, not
a defect. Section 0 of that plan also pre-refutes a blanket cross-representation equality guard,
because present == None -> false is legitimate at ~218 sites.

So the completion has THREE gates: the argument coercion (needs the primitive denominator), Phase D's
none-literal migration over ~218 sites in 66 files, and Phase C's bridge deletion.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N8xvN1T1NKiJqCUqwEmDgK
Conflict in dag/gunbc/recurring_failure_mode.dag: main's #9769 appended
surface_shorthand_preempts_resolved_identity while this branch appended
coarser_parallel_authority; both also appended to the roster. Kept both,
main's first, in the declaration region and the roster.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N8xvN1T1NKiJqCUqwEmDgK
gunbc-ci-auto-heal and others added 9 commits August 31, 2026 05:08
…e field that carries known-red discrimination

The census gains the post-merge re-measurement with a trunk control (main at
b41d564 is 0 errored / 0 failed / 3065 passed against this branch's 426/6/2619),
which is what makes the attribution a measurement rather than a reading of the
diff. It also refuted an attribution this document would otherwise have carried:
two failures name self_host_symbol_identity_binding_witness, merged in from main
the same hour, and the clean trunk says they are this branch's.

main_wet is added as a measured victim outside the floor -- it refuses under this
branch's own coercion arm -- which is why the DESIGN.md and design-ledgers.md
projections are deliberately left inconsistent rather than regenerated from a
stock-interpreter seed.

floor_non_verdict gains two sentences naming which field carries the property
readers cite it for: non_verdict_unenrolled, not known_red_now_passing. An
earlier draft of this lane proposed a 4b class row asserting an unguarded
conflation there; that was wrong -- the wall exists and fired -- and a row
claiming a missing guarantee over a working wall is rung deflation. Annotation
only, semantically inert.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N8xvN1T1NKiJqCUqwEmDgK
Same conflict shape as the previous merge: main's #9786 appended meaning_fork and
externalized_degradation while this branch carries coarser_parallel_authority, and
all three also append to recurring_failure_mode_roster. Kept all three, main's
first, in both the declaration region and the roster.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N8xvN1T1NKiJqCUqwEmDgK
…ldcard comparison

rust-unit-tests nfr_roster_receipt refused this branch with one unrostered
non-fold residue site: value_null_phase_eq, added by the plan amendment. It
matched a, then matched b inside each of seven arms with a '_ => false'
wildcard, which is a wildcard over a CLOSED coproduct -- un-migrated modeling
under DESIGN section 6, and the detector is right to flag it.

Fixed by construction rather than by rostering it. The roster entry was
available and would have greened the test, but registering the site admits debt
where a fold was available. Equality now derives from value_null_phase_ordinal,
one exhaustive 7-arm projection with no wildcard, following std.fermi
fermi_ordinal.

The safety difference is why the detector exists: under the old form an eighth
phase would silently take the '_' arm at seven sites and compile clean; under
the new one it makes the ordinal non-exhaustive and the compiler refuses.

Verified: nfr_ suite 14/14, including red_control_wildcard_over_closed_coproduct
_is_residue -- the detector still discriminates, so the site is gone rather than
the check blunted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N8xvN1T1NKiJqCUqwEmDgK
…ior instrument

Added by 32606e4 as a one-afternoon probe and never removed. It is the §6
experimental-residue tell: a hand-authored root-level shell script with no final
consumer, raw shell implementing semantics expressible in .dag, nothing calling
it and no CI reference. It does not survive the terminal architecture.

The reason it must not merge is stronger than hygiene. Its roster comes from
grep -E '^FAILED|FAILED in' over the run log, and this branch established that
the log FOLDS distinctions the required-floor-disposition artifact splits --
log failed=427 against the artifact's 6 failed plus 421 runtime-errored. Worse,
runtime-errored claims emit no FAILED line at all, so this script reports them
as absent. That is exactly how this lane first reported the blast radius as one
witness when it was two orders of magnitude larger. Checking it in would hand
the next reader the instrument that caused that error, with the repo's implicit
sanction, at the moment the better instrument was proven.

If a standing floor probe is worth having it is a modeled entry point reading
required_floor_disposition.tsv, authored as its own change rather than as cargo
on a semantic repair. Local probes belong in the scratchpad.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N8xvN1T1NKiJqCUqwEmDgK
…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
…ate drift, not the bmc.types field read

The previous commit recorded NoSuchField at extdeps.bmc.types:183 as the cause of
the required-witnesses-build red and explicitly ruled out stale artifacts. Both
halves were wrong. The CI log reports drifted=2 (DESIGN.md, docs/design-ledgers.md)
and unadjudicated=3 (three workflow yml artifacts refusing with the same
CallContractMismatch on outcome_accepted that main_wet gives) -- so the ruled-out
hypothesis was half the answer and the coercion arm is the other half.

The error was method, not arithmetic: I reproduced A failure locally and treated
it as THE failure. The local entry evaluates the gate body as one expression and
dies at its first refusal; CI adjudicates per artifact path and records an outcome
for all 35. Same subject, two routes, different first failures.

Also records what made the lane blocking: main #9814 restored generated-artifact
drift to required CI, so the deliberate projection drift is no longer latent debt.
The refusal to fabricate those bytes from a stock-interpreter seed stands.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N8xvN1T1NKiJqCUqwEmDgK
Third consecutive integration conflicting on dag/gunbc/recurring_failure_mode.dag
and always the same way: main appended instrument_output_read_as_subject_content
and executed_conjunct_discriminates_nothing while this branch carries
coarser_parallel_authority, and all three also append to the roster. Kept all
three, main's first, in both regions.

The resolution again orphaned the closing 'evidence: [],' and '}' of the LAST
appended block -- the known append-vs-append class, where git's conflict region
ends before the shared tail so the block at the boundary loses it with no marker
to signal the loss, and a name-based declared-vs-rostered join passes anyway. The
repair is now generic (any 'authored:' line not followed by 'evidence:') rather
than a hand fix per instance. Verified structurally at 27/27 well-formed with the
roster joining both ways, and by execution: the module evaluates and returns all
27 rows.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N8xvN1T1NKiJqCUqwEmDgK
Fourth consecutive integration conflicting on dag/gunbc/recurring_failure_mode.dag,
identically each time: main appends rows and roster entries at the same two append
points this branch's coarser_parallel_authority uses. Kept both sides, main's
first, in both regions -- 30 declarations, 30 roster entries, joining both ways.

The resolution again orphaned the last block's closing 'evidence: [],' and '}'.
That is structural, not incidental: git's conflict region ends before the shared
tail, so whichever side's final block sits at the boundary loses it, no markers
survive to signal the loss, and a declared-vs-rostered NAME join passes over a
file that no longer parses. Verified structurally (30/30 well-formed, no identity
mismatch, no duplicates) and by execution.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N8xvN1T1NKiJqCUqwEmDgK
@briansrls
briansrls marked this pull request as ready for review September 1, 2026 05:47
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 1, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-01T05:52:18.454659Z 2201637 Draft marked ready
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@gunbai-bot
gunbai-bot Bot marked this pull request as draft September 1, 2026 05:48

@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: 22016377f0

ℹ️ 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".

Comment thread src/v1/stage0/src/v1_interpreter.rs Outdated
Comment on lines +4915 to +4917
for (i, param) in fn_node.params.iter().enumerate() {
let pname = &all_param_names[i];
if !param_declares_required_value(param, pname, ctx) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Apply optional coercion to builtin calls before landing

This coercion only runs in call_function_inner, so required-argument builtins bypass it. After the new first arm wraps a nonempty result in Present, existing calls such as parse_int(s: fields.first()) in dag/extdeps/bmc/capability.dag pass a Variant directly to expect_str and fail with a type error; this breaks dag/test/claim/bmc/bmc_capability_solve_witness_test.dag and the required floor. The optional-to-required conversion must also be derived and applied by builtin dispatch before these method results are wrapped.

Useful? React with 👍 / 👎.

gunbc-ci-auto-heal and others added 5 commits September 1, 2026 07:19
# Conflicts:
#	dag/gunbc/recurring_failure_mode.dag
…nt and refusing Absent

Constructing the Optional for `first`/`last`/`get`/`lookup` created a second
obligation -- an `Optional<T>` argument now meets a parameter declared `T` --
and the coercion this branch added to satisfy it was wrong on GENERIC formals.

`param_declares_required_value` asked three questions of a parameter's declared
type and none of them could see a free type variable. For
`fn outcome_accepted<T>(value: T)`, `T` is not spelled `Optional`, carries no
`CardOptional` flag, and is not the parameter's own name, so the predicate
answered "required" for a formal that declares nothing about cardinality at all.
`Optional<Node>` is a legitimate instantiation of `T`, not a cardinality escape.

BOTH ARMS WERE WRONG, and the quiet one is the worse one. `Present` was
UNWRAPPED into the callee: a caller whose `T = Optional<Int>` had the callee
receive `Int`, which is precisely the silent semantic divergence this branch
exists to remove, reintroduced at a new seam by the repair itself. `Absent` was
REFUSED with a located `CallContractMismatch`, which is loud but equally false,
and it took down the generated-artifact regen actuator -- three workflow
projections reached no verdict in the required build lane for this reason.

THE FIX READS THE DECLARATION, NOT THE SPELLING. `v1.compiler.parse`
`parse_fn_body_from_prefix` builds a fn node's `params` as
`concat(type_params, value_params)`, and a type parameter is exactly the entry
whose declared type is its own name -- the same shape the positional-parameter
filter beside it already uses. Naming that set is what lets the predicate tell a
free type variable from a declared non-optional formal, so the coercion declines
where the declaration is silent instead of fabricating a requirement.

This is the producer that `gunbc.recurring_failure_mode`
`mitigation_injected_where_judgment_declined` names as its trigger, arrived at
from the interpreter side: that row was filed against the Rust emitter for the
same seam and the same callee.

EVIDENCE, both enrolled and both discriminating. Measured on the head that
carried the defect, a `Present` into a free type variable reported "UNWRAPPED --
arrived as a bare value" and an `Absent` raised CallContractMismatch; both now
report the Optional whole. They stay enrolled as regression controls beside the
existing positive control rather than retiring with the climb.

NOT FIXED HERE, and unchanged: builtin calls bypass this path entirely, so
`parse_int(s: fields.first())` still receives the Variant raw. That is the
declared grounding hold -- the builtin registry maps a name to a return type
with no parameter list -- and it has 9 located sites inside the regen actuator's
own import closure.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
… argument, got Variant` said nothing about where

`eval_builtin` receives a name and a value list and has no span, so an
argument-shape refusal reached the operator as a typed sentence with no file and
no line. DESIGN section 5 admits typed AND LOCATED; typed alone is half of it,
and the half that is missing is the one a reader needs to act.

It became load-bearing in this branch. Constructing the `Optional` for
`first`/`last`/`get` started routing Optionals into builtins, and a builtin
carries a return type with no declared parameter list, so no coercion can be
derived for it and the refusal is the ONLY signal the reader gets. An unlocated
one hands them a corpus to search.

The call node is in scope at the builtin dispatch site and carries the span, so
the location is attached at the one seam that knows it, in the `file:offset`
form the interpreter's other located diagnostics already use.

NARROW BY CONSTRUCTION, and deliberately so: this locates refusals raised by
builtin dispatch and nothing else. Interpreter-wide diagnostic location is a
separate class with its own trigger and is not absorbed into this change.

RECEIPT, and it corrects something I would otherwise have reported wrongly. The
same actuator run, twice, same binary and same arguments, refuses in two
DIFFERENT places: once `NoSuchField { type_name: "Optional", field: "shape" }`
and once `dag/extdeps/ollama/capability.dag:4740: parse_int expects a string
argument, got Variant`. The population of un-migrated consumers is walked in a
map order that is not stable across runs, so any single run names one member of
it. Without the location I would have read the second run as the first defect
having moved.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
… of the operation was never measured

The census declared its population as "every terminal `|> first` occurrence".
That is a SYNTAX, not the operation. The corpus writes the same declared
operation two ways and the pipe form is the smaller one: 224 pipe-form
occurrences against 661 `.first()`, 19 `.last()`, 41 `.lookup()` and 7 `.get()`
across 183 files.

Every consumer that has actually failed in production is in the half the census
never looked at -- `parse_int(s: fields.first())`, `matches.first().shape`,
`lines.first() == schema`.

RE-DERIVED AGAINST THE POPULATION THAT ACTUALLY GATES LANDING: the 944-module
import closure of `tools.generated_artifact_gate`, classified by what CONSUMES
the result. 270 consumer sites -- 137 match-eliminated, 96 propagated, 14
compared to a bare value, 11 declared-fn arguments, 5 field accesses on the
result, 3 builtin arguments, 1 method call on the result. Twenty-three are
un-migrated and are why the closure will not load.

THE CLASS THE CENSUS NEVER NAMED IS THE LARGEST AND THE WORST. Fourteen sites
compare a `first()` result to a bare value. Comparison is the one shape with no
pattern for a compensation arm to intercept -- which the census's own probe
table establishes from the other side -- and nine of the fourteen are
merge-admission receipt parsing, where a schema check that silently answers
false is exactly the failure this branch exists to remove.

The mechanism, root cause, two-sided argument and probe table are unaffected;
they are about the mechanism, not the population. The disposition counts must
not be cited as a population or as completeness, and the document now says so at
the head of the section that carries them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
…fabricated `false` instead of refusing

Constructing the `Optional` for `first`/`last`/`get`/`lookup` made every consumer
that COMPARES the result to a bare value compare across two representations.
`Value::eq` cannot decide those, so the comparison silently answered `false`.
DESIGN §5 forbids exactly that: a failure arm must refuse, never fabricate a
plausible answer. This is the second silent arm this repair produced -- the
free-type-variable unwrap was the first -- and the reason is structural: a change
to what values ARE turns every consumer of the old representation into a seam
that must be made loud.

MEASURED BEFORE THE WALL, both shapes silent:
  `["schema-v2", "body"].first() == "schema-v2"`  ->  false
  `[] |> first == none`                            ->  false

Nine of the fourteen comparison sites in the generated-artifact gate's 944-module
import closure are merge-admission receipt schema checks, where a quiet `false`
rejects a valid receipt with no diagnostic -- on the path every other lane merges
through. The wall makes them loud, and it makes the remaining population
self-announcing rather than something a textual census has to find.

`CrossRepresentationEquality` already existed and did not fire here. It does now.

THE `x == none` CARVE-OUT WAS DELETED AFTER MEASURING IT. The first version of
this wall spared `Optional` against `Value::Null`, on the reasoning that `none`
evaluates to the Null carrier and refusing it would break the corpus's emptiness
idiom rather than the bug. The control said otherwise: `[] |> first == none`
already answered `false`, so the carve-out was preserving a silent false rather
than a working test. It now refuses with its own sentence naming the two carriers
of absence. It stays narrow by construction, not by exception: a declared `T?`
whose absent state IS `Value::Null` still compares `Null == Null` and never
reaches the check, so only a CONSTRUCTED `Optional` meeting the Null carrier
fires -- exactly the un-migrated population.

EVIDENCE. Three fixture arms, each discriminating: the bare-value straddle
refuses, the Null straddle refuses with the distinct sentence, and the positive
control -- `Optional` against `Optional` -- still compares and answers true.
The positive control is ENROLLED here. The two REDs are fixture-measured and NOT
enrolled, which the witness module states rather than glosses: a `test fn`
returns `Bool` and an interpreter refusal aborts evaluation, so this harness
cannot express "this expression refuses". A harness that can catch a refusal and
assert its reason is this seam's next-rung trigger.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
gunbai-bot Bot pushed a commit that referenced this pull request Sep 1, 2026
…hree silent arms from one repair, two authored while fixing the previous one

When a change alters what a value IS, every consumer that reads the OLD
representation becomes a seam, and at each seam the author's instinct is to keep
the observable behaviour the same. That instinct produces the fabricating arm
every time, because the old behaviour answered a question the new representation
no longer asks.

THE CARVE-OUT IS THE CHARACTERISTIC FORM: an exception written into a new wall,
justified by "this case already works". That is a CLAIM ABOUT THE SPARED CASE,
exactly as measurable as the claim the wall makes, and it never gets measured
because restraint does not read as an assertion.

Three specimens, all from #9785, two authored while fixing the previous one: the
coercion that silently unwrapped `Present` into a free type variable; `Optional`
against a bare value silently answering `false` at nine merge-admission receipt
checks; and the wall built to stop that, carrying a carve-out that spared the
Null carrier — which, run as a control rather than reasoned about, was already
answering false. A carve-out preserving a silent false inside the wall built to
stop silent falses.

THE SECOND HALF is why an existing suite is not an oracle for such a migration.
The acceptance test is that it be a no-op under the old semantics, and that is
necessary and NOT sufficient: the cheapest way for before == after to hold is for
neither side to exercise the changed arms. Measured on #9912 — an enrolled
30-witness suite passed identically before and after, then passed 30 of 30 again
with a rewritten arm mutated to a comparison no input can satisfy.

Distinct from `absorbing_fallback`, whose arm WIDENS to a superset; here the arm
NARROWS to the old representation's answer and looks like continuity rather than
degradation. Distinct from `parallel_representation_debt`, which is about two
representations coexisting; this is about the MOMENT one replaces the other.

Projections regenerated by the actuator (`main_wet_one` for `DESIGN.md` and
`docs/design-ledgers.md`) rather than hand-edited.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
gunbc-ci-auto-heal and others added 2 commits September 1, 2026 10:36
… closure LOADS

The re-derivation's largest non-`match` bucket is 31 sites where a one-line
textual reader cannot decide whether `algorithm: parts.first(),` is a
record-field assignment or a function argument, because the opening brace is on a
previous line. That bucket names its own undecidability rather than being guessed
into whichever class looked likelier — a guessed split would have produced a
tidier table no reader could question.

Record-field assignment into a declared non-optional field is a real consumer
class, it is somewhere inside those 31, and it cannot be counted from source
text. So 23 is what is KNOWN to block the closure, never the population, and this
document now says so where the number appears.

The consequence is a better completion criterion than any count: THE CLOSURE
LOADS. It is self-verifying, needs no population known in advance, and each fix
lets the closure load further so the next refusal names the next site —
exhaustive by construction and terminating exactly when the property we want is
true. "The known set is repaired" and "the migration is complete" are two
different assertions, and the document now requires reports to say which.

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

A catch-all is only visible once something has fallen out of it, so the first
census of anything should be assumed to have one. It goes here because it is what
a reader needs in order to weigh every number below it, and because this document
is its own two receipts.

First layer: the population was a SPELLING — "every terminal `|> first`
occurrence" is a syntax, not the operation, and the method-call form is the
larger by far. Second layer: the re-derivation that fixed that produced a tidy
table with six named classes and a 96-site bucket called `propagated / returned
onward, declared type honest`, which is not a class but what was left after five
were named, described in a way that reassures. It had already eaten
record-field assignment into a declared non-optional field.

THE CORRECTION WAS NOT RESTRAINT, and recording it as restraint would leave a
virtue nobody can act on. Three classifiers were written and the first two both
produced the tidy table; the bucket that now names its own undecidability
appeared only after a SPECIMEN fell out of the catch-all and showed what it was
hiding. Without it the tidy table would have shipped a third time.

The three operative rules are stated in the document: hunt the catch-all before a
reader finds it, with the tell being a bucket defined by what it is NOT or
described with a reassurance; name undecidability rather than the likelier class
and say what would decide it; and report a lower bound with a self-verifying
completion criterion rather than a number.

The class also belongs in `gunbc.recurring_failure_mode`, and is deliberately NOT
filed there yet: that carrier is mid-merge-forward under another lane, and a
third lane appending to it today would be an instance of the failure it rosters.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
`true` and `false`, zero bytes each, added in 62f11ae. They are shell
redirect residue -- not on `main`, not referenced by anything, and they reached
the branch because I staged with `git add -A` without reading what it had picked
up. Nothing to preserve; the fix is the deletion.

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

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

REQUEST_CHANGES at exact head 4ed8c14f71c18e3b52b9da978efcd3c102e23534.

This remains a draft/incomplete repair, and the exact-head required floor is red. Three semantic-authority blockers must be repaired before this head can become a landing candidate.

1. The Optional-vs-Null equality wall is overbroad. optional_against_bare_straddle currently refuses every constructed Optional against Value::Null. That includes Present { value: "x" } == none, whose correct answer is ordinary false, not a cross-representation refusal; the existing value-null authority explicitly records that blanket Optional/Null refusal is invalid because present-vs-none inequality is legitimate. The current diagnostic also calls any constructed Optional a representation of absence, which is false for Present.

Required controls: Present("x") == none returns false; Present("x") != none returns true; Absent == none reaches the named migration refusal; Present("x") == "x" reaches the Optional-vs-bare refusal; Optional-vs-Optional equality is unchanged. Do not restore green with another spelling-based carve-out.

2. The optionality validator checks only one direction. Some(Ok(false)) => Ok(value) admits an Optional result for an algebra row declaring a non-Optional result. A function named and used as declared-optionality validation must enforce both directions. Add a discriminating control in which a selected non-Optional algebra row returns optional_present(...) and require refusal.

3. The validator is keyed at the wrong grain. algebra_row_returns_optional(method) flattens all_algebra_field_templates() and selects by method spelling alone. Algebra rows are receiver-profile-relative; the exact selected row/profile is the authority. A global same-name scan can either reject a valid selected row because another profile gives the same spelling a different shape, or validate against the wrong row. Thread the selected AlgebraFieldTemplate/profile from dispatch into the judgment, and add a same-spelling/opposite-optionality fixture proving the receiver-selected row wins.

Two record corrections are also required on the next head:

  • The bounded comparison population is 14 = 10 merge-admission + 4 other, not nine merge-admission sites. #9912 established four sites in merge_admission_produce and six in merge_admission_subject.
  • The title/body still carry the dissolved BT ordering hold. #9785 is no longer ordered behind #9775; its live hold is its own grounding/completion and red floor.

The builtin parameter-signature grounding remains a valid independent blocker. No roster admission is accepted as a substitute for completing these semantics.

gunbc-ci-auto-heal added 3 commits September 1, 2026 14:27
…sent-vs-none is ordinary false

The first arm refused every constructed Optional against the host Null carrier, so
Present { value: x } == none was unwritable and the diagnostic called a Present a
representation of absence. Scope decided by reasoning rather than by running the case
-- the refusing mirror of the spared-case failure this branch's own row names.

Five controls measured with claim_batch: Present == none is false and Present != none
is true (both enrolled here); Absent == none and Present == bare still refuse with
their own messages (fixture-measured, since a test fn cannot express a refusal); an
Optional against an Optional is unchanged.

Also caches Optional/Present/Absent as interned Symbols on the context: is_optional_value
sits on the Eq/Ne chokepoint and reached them through ctx.sym(), a RefCell borrow_mut
plus a hash lookup, up to six per comparison.
…both directions

The old scan keyed on method spelling over every profile. 23 spellings are declared in
more than one profile and three shape their returns differently (join, member, get), so
a spelling scan can judge against a row the receiver never selected. The profile now
comes from the receiver value's own carrier via kernel_algebra_profile.

A row declaring a concrete non-optional return now refuses an Optional result. The
naive symmetric rule is NOT implemented: ReceiverSelf, ReceiverElement, ReceiverKey,
ReceiverValue and AlgebraTypeVariable are all satisfiable by an Optional at runtime.

Zero spellings declare mixed optionality today, so neither half has an authorable red;
discrimination is mutation-measured on join and the trigger is recorded in the witness.
@gunbai-bot gunbai-bot Bot changed the title first() divergence: census the 186 sites, repair the half that has an authority (BLOCKED on #9775 + builtin param signatures) first() divergence: census the 186 sites, repair the half that has an authority (blocked on the builtin param-signature grounding) Sep 1, 2026
@gunbai-bot

gunbai-bot Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

All three semantic findings are repaired at df356789675, and the two record corrections are in the body. Thank you — the first one was right in a way I would not have found, because the case it names is one I decided by reasoning rather than by running.

1. The Optional/Null wall over-refused. Present { value: "x" } == none is ordinary false, and the arm refused it; the diagnostic even said out loud that any constructed Optional is a representation of absence, which is false for Present. Only Absent-vs-none is a straddle now. I ran all five named controls with claim_batch before touching the arm and again after, and I did not restore green with another spelling-based carve-out — the narrowing is on the variant, not on a name:

  • Present("x") == none → false (enrolled, a_present_optional_against_none_is_ordinary_false)
  • Present("x") != none → true (enrolled, a_present_optional_is_unequal_to_none)
  • Absent == none → refuses, "a constructed Absent and the host Null carrier are two representations of ABSENCE" (fixture-measured)
  • Present("x") == "x" → refuses, "an Optional<T> and a bare T are two representations of one value" (fixture-measured)
  • Optional-vs-Optional → unchanged (an_optional_compared_against_an_optional_is_not_a_straddle)

The two refusals are fixture-measured rather than enrolled for the reason the file already states: a test fn returns Bool and a refusal aborts evaluation, so this harness cannot express "this expression refuses". That is the seam's standing next-rung trigger.

3, taken before 2, because 2 depends on it. algebra_row_returns_optional selected by method spelling over all_algebra_field_templates(). Measured: 23 spellings are declared in more than one profile, and three shape their returns differently — join is ReceiverSelf in two profiles and NamedTemplate { name: "String" } in the collection profile; member and get likewise. The judgement now derives the profile from the receiver value's own carrier through kernel_algebra_profile and takes that profile's single row.

2. Both directions, but not the naive symmetric rule. A row declaring a concrete non-optional return now refuses an Optional result. I did not implement "any row not declaring OptionalOf must not return an Optional", because ReceiverSelf, ReceiverElement, ReceiverKey, ReceiverValue and AlgebraTypeVariable are all satisfiable by an Optional at runtime — [Absent] |> reverse is a list of Optionals, and fold over an Optional accumulator returns one. Implementing the symmetric rule blindly would have been finding 1 committed a second time.

On the fixture you asked for, and this is the part I cannot deliver as specified. A same-spelling/opposite-optionality fixture is not authorable: zero spellings in dag/std/algebra.dag declare mixed optionality across profiles, so no accepted program distinguishes the old selection from the new one, and the old (true, true) => Err arm could never fire — it was permanently green by construction. The discrimination is therefore mutation-measured and recorded as such: with the join arm altered to return optional_present(..), ["a", "b"] |> join(",") refuses with the new negative-direction message; restored, it passes. The next-rung trigger is written into the witness — a spelling whose profiles disagree about optionality entering the algebra, or a fixture harness able to present synthetic algebra rows.

Record corrections. The bounded comparison population is 14, not nine: ten merge-admission sites (four in gunbc.merge_admission_produce, six in gunbc.merge_admission_subject, per #9912) plus four elsewhere. And the BT ordering hold is dissolved — the title carried it longer than the fact did, and both are now updated.

The builtin parameter-signature grounding remains a real independent blocker and is being authored on session/still-swift-363-builtin-sigs; it is not a route around these semantics.

— sent from still-swift-363

gunbc-ci-auto-heal and others added 6 commits September 1, 2026 15:26
… planted red

algebra_result_optionality now declares, in the roster's own module, what a return
template says about a result's Optional-ness -- three arms, because a receiver-relative
template constrains neither direction. The interpreter calls the generated function
instead of carrying a second hand-written answer.

The discriminating red IS authorable: I recorded it as unauthorable on the strength of
the accepted corpus containing no mixed-optionality spelling, which is the wrong
boundary. Both halves take their input as a parameter, so a planted same-spelling
opposite-optionality pair is expressible and is now enrolled, with a receiver-relative
control beside it. Each reds under a different mutation of the .dag.
…enerated pair

A self-inverting instrument. The control stays green and reads as evidence that it
discriminates nothing, so a false negative argues for deleting a check that was sound.
Both directions receipted from this branch: the emitted mirror was the wrong target for
an interpreted witness, and the interpreter arm was the right one for the join control.
…the parameter coercion

Every `.first().<field>` site in the corpus read an Optional's payload as a
record. The 22 production sites are migrated to `match X.first()` with the
Absent arm DERIVED from the sibling guard the site already carried -- the
`count == 0` refusal it sat beside -- rather than fabricated; where no sibling
existed (`nth_layer_function`) the partial function now returns Optional and
its single caller answers with the `LayerOutsideStackup` refusal it already
declares. The witness sites answer `Absent => false`, which is the honest
verdict for a claim about a head that does not exist.

Also declares `gunbc.rung_drop optional_into_declared_nonoptional_parameter`:
an Optional actual flowing into a parameter declared non-optional is unwrapped
rather than refused, in BOTH arms. The emit arm carries that coercion on main
in six places; documenting it under a comment was not declaring it. Trigger is
named at capability grain -- the compile seam refuses the flow, at both arms,
proven by a discriminating pair -- and explicitly is NOT a site census.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
The mechanical first() migration rewrote three sites whose conjunct spanned a
match arm header or a call's argument list, splicing a match into positions the
grammar does not admit. CI's parse phase caught two files; the local corpus
parse caught a third that CI never reached, because the first refusal stopped
the phase. All three are re-derived by hand from the pre-migration source and
carry the same Absent => false verdict as their neighbours.

The instrument that should have run before the push is target/release/
v1_src_dag_parse: it parses all 4483 corpus files in seconds and names the file,
line and column. Pushing a corpus-wide mechanical rewrite without it spent a
27-minute CI lane to learn what a local second would have said.

Also regenerates DESIGN.md and docs/design-ledgers.md for the rung-drop row
declared in the previous commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
Files the check never opened. An --entry run is evidence about the files its
import closure OPENS and about nothing else, so a corpus-wide mechanical rewrite
breaks, by construction, exactly what no single closure reaches -- and the green
is honest about its own subject while reading as coverage of the population.
Specimen is this PR: one clean claim_batch resolve, then a CI parse refusal in
files that run never opened.

The row's second half is the enumeration property, because it decides which
instrument to reach for: a phase that aborts on first refusal cannot enumerate,
so its failure list is a prefix and a green after fixing that list is not a
green. CI named two files; the corpus walk named a third it had never reached.

The row records that v1_src_dag_parse's own source already advertises itself as
the cheapest check in the tree for exactly this, which makes the specimen an
unread instrument rather than a missing one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
@gunbai-bot gunbai-bot Bot changed the title first() divergence: census the 186 sites, repair the half that has an authority (blocked on the builtin param-signature grounding) first(): construct the Optional std.algebra declares, migrate the field-access population, and declare the parameter coercion Sep 1, 2026
gunbc-ci-auto-heal and others added 3 commits September 1, 2026 19:44
The `.first().<field>` grep bounded one shape. Two more were invisible to it and
both were silent, not loud:

MATCHING AN OPTIONAL AGAINST ELEMENT VARIANTS. `match steps.first() { Actuator..
=> .. _ => false }` typechecks and answers false forever. Probed directly: the
checker validates arm constructors against the ELEMENT type while checking
exhaustiveness against the Optional -- two answers for one scrutinee in one
match -- so where the element type is a coproduct the arms resolve, the wildcard
satisfies exhaustiveness, and nothing refuses. 17 sites, one of them production
(`extdeps.bmc.pid_control_decode` `decode_curve_side`).

LET-THEN-MEMBER. `let entry = matched.first()` on one line and `entry.magnitude`
on the next reads the OPTIONAL's payload, not the entry's field -- the exact
confusion this module's own annotation records. 11 sites across 8 files,
including three folds in `std/effect_axes.dag`.

`indexed_decimal_value_at` now declares `Optional<ExactDecimal>`, which made its
one caller partial: a `map` has no arm for a missing output, so it could only
fabricate a magnitude or let an Optional reach a declared non-optional field.
The point set is built by a fold that refuses instead, with the Absent arm
DERIVED -- an indexed reading whose output the join cannot supply is exactly the
`CurveReadingWithoutOutput` the caller already raises. It accumulates by prepend
so the fold stays linear (DESIGN 6 bare-minimum-cost), and the existing sort_by
on the point index restores order.

Every Absent arm here is derived from the guard it replaces: the `if length ==
0` sibling is dissolved into the match rather than answered twice.

Verified by execution: 4483 files parse-clean, and the curve, machine-shape,
NBD, websocat, runner, effect-axes, WIF, cpu-cache, roadmap-contract and
browser-observation witnesses PASS. `enforcement_consistency_gate_holds` fails
both with and without the schedule_lens edit on this tree, so it is not this
change -- measured by swapping that one file, not inferred.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
MERGE. main and this branch each appended one row to gunbc.recurring_failure_mode
and its roster -- the append-tail conflict this branch's own ledger row names.
Resolved as a union in the .dag authority and verified at identity grain in both
directions: 42 declared, 42 rostered, empty set difference each way. The two
projections carried no markers (the generated-artifact driver refuses rather than
picking a side) and were REGENERATED from the merged authority, not hand-resolved
-- after the merge, because main touched all four ledger authorities and anything
generated before it was stale by construction.

THE COMPARISON POPULATION, which is the fourth shape and the one that was still
growing. main #9875 landed `raw.skip(n: n - 1).first() == ""` in
extdeps.languages.yaml.ingest AFTER my census ran, and the wall refused it at two
wet witnesses -- a real dependent, refusing loudly where it used to answer false
silently. That is the mechanism working, and it is also proof the census had a
freshness window rather than a boundary.

Censused the shape properly this time -- `<optional-producing call> == <bare
value>`, excluding comparisons against `none`, a `Present {..}` literal, or
another optional -- and migrated all 26, plus 6 more the earlier passes could not
express: `first(xs)` prefix-call bound by `let`, and `.last().<field>`.

Production Absent arms stay derived. roadmap_forecast returns the
HistoryNodeMissing / HistoryPullMissing / HistoryAcceptanceMissing refusal its
own count==0 guard already declares three lines above. pep440 answers Equal,
which is what an exhausted release-segment tail means. parse_tmux_pane_line
answers none. dag_compile_clean_shard_totality and the witnesses answer false.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
The four shapes censused so far all keyed on the METHOD spelling `.first()`.
`gunbc.roadmap.roadmap_forecast_witness` reads `first(gaps).nodes` in prefix-call
form inside a match-arm conjunct, which every one of those greps was structurally
unable to express. Migrated to a nested match with an `Absent => false` arm, which
is the honest answer for a conjunct asserting a property of the first element:
there is no first element, so the property does not hold.

Five shapes is therefore a FLOOR on the shape count, not a total. Each of the five
was invisible to every grep that preceded it, and this one was found by asking what
spelling the previous four assumed rather than by extending them.

NOT VALIDATED BY TYPECHECK, and the reason is measured rather than assumed.
`claim_batch` cannot resolve this entry at all: `gunbc.roadmap.roadmap_forecast`
`path_to_node` and `node_forecast_build` bind a `let` from a match whose other arm
`return`s, and the checker takes the let's type as the coproduct union, so three
direct-call arguments fail inhabitance. A controlled one-file swap to main's version
of that file reproduces all three at the same sites (lines shifted only by the count
of lines this branch adds), so the errors are pre-existing on main and not this
branch's. The edit is verified by parse only -- 4493 file(s) parse-clean.

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

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

REQUEST_CHANGES at exact head fa11d87da0e12893d180e88dc123ee9c68592915.

The prior review at 4ed8c14f71 is partly superseded: the Optional/Null over-refusal, the missing reverse-direction check, the count/title corrections, and the dissolved #9775 ordering hold have been addressed. The live body’s replacement census claim — five observed syntactic shapes are a floor at this merge base, not a closed population — is honest. Three semantic/evidence blockers remain.

1. The exact selected algebra row is already carried by inference, but interpretation discards it and reconstructs a coarser answer from the runtime value.

MethodSemantics::AlgebraMethodSemantics carries algebra_template: AlgebraFieldTemplate?, and resolve_known_method_node fills it from the receiver-selected MethodFieldResult. But eval_method_call destructures only method_def, calls eval_algebra_method by spelling, and the new validator then re-derives a profile through a handwritten runtime map (Value::List -> "List", Map, Set, Str) before looking the row up again.

That is the same authority-substitution class as the old global spelling scan, one layer later: the selected row exists and is erased, then a receiver profile is reconstructed after declaration identity has collapsed into Value. Thread the carried algebra_template into the interpreter dispatch/result validator and make absence explicit; do not re-resolve it from runtime carrier spelling.

The planted forked witness does not close this. It hands optionality_of two already-separated candidate lists and proves the vocabulary can answer them differently. It never executes eval_method_call, receiver_algebra_profile, or the carried algebra_template, so breaking the runtime routing can leave it green. Add a discriminator over the real selected-row path.

2. ReceiverSelf is misclassified as OptionalityUnconstrained; the justification confuses outer optionality with element optionality.

[Absent] |> reverse is a List whose element is Optional, not an outer Optional<List<...>>. On the path the validator actually handles, receiver_algebra_profile admits List/Map/Set/String receivers, so a ReceiverSelf result must preserve that non-Optional outer carrier. The present rule would admit a mutated reverse, skip, filter, concat, or map update arm returning optional_present(receiver) because ReceiverSelf declines both directions.

At minimum, ReceiverSelf must forbid an outer Optional on this path, with a mutation control that wraps a selected ReceiverSelf result. The terminal shape is stronger: validate the produced value against the instantiated exact selected result type rather than reducing the template to one optionality bit.

3. Exact head is red, and the one-file control did not establish that the three roadmap failures were inherited from main.

CI checked synthetic merge eaa5842174348222a1e2f879e3b66c775386d353 — this head over exact main 28a34baf7527c16f5ef965af8fc2d8cd66cc290e — and required-witnesses-floor failed on the three roadmap_forecast argument-inhabitance errors. Exact main 28a34baf75 separately completed the full witnesses workflow with the floor green. Replacing only roadmap_forecast_witness_test.dag with main’s version proves the newest fifth-shape edit is not the cause; every earlier #9785 semantic change remains, so it cannot prove the errors existed on main.

The live PR body now correctly retracts the closed-census/inherited-error reading. Repair or migrate those three branch-induced sites and obtain a green exact-head run before re-review.

Evidence wording correction. The document/body says the five probe rows are enrolled, but the Optional-vs-bare refusal is not directly expressed by the Bool witness harness. first_result_does_not_compare_equal_to_the_bare_element does not compare an Optional to a bare element; it repeats Optional-to-Present and then compares the matched payload to "y". Keep the refusal explicitly fixture-measured, rename/correct that test and the census statement, and do not cite it as enrolled evidence for CrossRepresentationEquality.

The 26 comparison migrations are admissible as a base-stamped migration plus runtime ratchet, not as closure of the source class. CrossRepresentationEquality prevents an executed straddle from fabricating false, so this is more than an unguarded sweep; a new source site can still be authored and will fail later until the compile-seam Optional-to-non-Optional wall lands. Preserve that distinction in the committed census with the exact base/selector/instrument identity.

…_forecast

int_at and median_int now return Optional<Int> and propagate; calibrate_cell's
`count(samples) == 0` guard is dissolved into `match first(samples)`, deriving the
CalibrationRefusedNoSample it already declared from the same call that decides it.

HELD LOCAL AND NOT PUSHED. This does NOT clear the floor's three errors on this
branch, which is measured rather than assumed: after this migration the three
errors survive verbatim at the same sites. Their cause is a checker hole -- a `let`
bound from a match whose sibling arm `return`s is typed as the union of the binding
and the function's return coproduct, because a diverging arm is not modelled as
contributing nothing to the join. That is fixed in a separate PR; this branch waits
for it rather than routing around it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
briansrls pushed a commit that referenced this pull request Sep 2, 2026
…and the parse_int argument site (#9926)

* Merge admission: eliminate the ten `first()` comparisons, and cover the guard arms the suite could not see

Ten sites in `gunbc.merge_admission_produce` and `gunbc.merge_admission_subject`
compare a `first()` result to a bare value. `dag/std/algebra.dag` declares
`first` as returning `Optional<..>`, so once the interpreter constructs what that
row declares, every one of these compares across two representations and
`Value::eq` cannot decide them -- measured on the branch that constructs it,
`["schema-v2", "body"].first() == "schema-v2"` answers FALSE. These are the
receipt schema and blank-required-field checks on the path every lane merges
through, so a quiet `false` rejects a valid receipt with no diagnostic.

Each site moves from the COMPARED class into the MATCH-ELIMINATED class, which is
the class that already agrees under both semantics. The `Absent` arm is DERIVED,
not decided: `parse_receipt_wire_v2` declares `-> MergeAdmissionReceiptV2?` and
already answers `none` for every malformed case it handles -- wrong line count,
wrong schema line, blank field, unparseable attempt id, conclusion, roster hash
or PR number -- and `parse_tested_subject_wire` and `parse_git_object_id_wire`
are the same shape. `receipt_wire_v2_pr_number` already carries the exact target
form. So "the receipt has no first line" is an instance of an answer these
modules already give, and no new refusal vocabulary is minted.

IT IS A NO-OP TODAY, AND THAT IS THE OBLIGATION THIS CHANGE HAS TO MEET. It lands
on `main`, where `first` returns the raw element. The interpreter's `match_pattern`
binds a `Present { value: v }` pattern to a raw value, so each rewritten site
binds the same string it compared before and answers the same verdict; the
`Absent` arm is unreachable under the length guard each function already applies
before these checks.

MEASURED, ONE BINARY, THREE ARMS -- the change is `.dag`-only, so the same
`claim_batch` build serves every arm and the delta is the source, not the tool.

  pristine main, 36 witnesses   36 PASS
  migrated,      36 witnesses   36 PASS, verdict-for-verdict identical
  mutation control              34 PASS, 2 named FAIL

THE SUITE COULD NOT SEE THESE ARMS BEFORE, WHICH IS WHY SIX WITNESSES ARE ADDED.
Mutating the blank-field comparison to a string no field can equal left the
existing 30 witnesses ALL PASSING: they reach `parse_gate_roster_hash_wire` and
`compose_walk_attempt_id` directly and feed the wire parsers only well-formed
text, a trailing line and a malformed PR line. An uncovered guard reads as a
covered one, and without these rows the rewrite would have been "verified" by a
suite blind to it. Under the same mutation the new rows go red by name.

ONE HONEST LIMIT ON THAT COVERAGE. The mutation flips the blank-head and
blank-base rows and NOT the blank-roster row, because a blank roster line is
independently rejected downstream by `parse_gate_roster_hash_wire`. That guard is
therefore shadowed rather than discriminated, and this states it instead of
claiming three for three.

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

* Cover the five comparisons the first cut left blind: the subject parser's four guards and a valid sha256 wire

Review of #9912 found the rows added in the previous commit exercised only
`parse_receipt_wire_v2` while the comment claimed both parsers. Five of the ten
rewritten comparisons therefore had no discriminating evidence at all -- the
subject parser's schema guard and its three blank-field guards, and the `sha256`
algorithm prefix, whose row accepted a valid sha1 wire and rejected md5 and so
never supplied a valid sha256 at all.

The finding is right and it is the same defect one parser over from the one the
mutation control caught. The lesson had been learned about the receipt parser and
then not carried across the file, which is what the comment's overclaim recorded.

SEVEN ROWS ADDED. Four for `parse_tested_subject_wire` over its own five-line
wire -- wrong schema line, blank base_ref, blank head_sha, blank base_commit_sha
-- plus a positive control that the all-correct builder parses, without which
every refusal row could be satisfied by a wire malformed for some other reason.
The object-id row is split into three: a valid sha1 wire, a valid sha256 wire,
and an undeclared algorithm refused. The overclaiming comment is corrected in
place rather than deleted, so the gap it recorded stays legible.

MUTATION CONTROL, ON EXACTLY THE FIVE THE REVIEW NAMED. Mutating the subject
parser's blank guards, its schema comparison and the sha256 prefix turns five
rows red BY NAME:

  object_id_wire_accepts_a_valid_sha256_wire
  subject_wire_refuses_a_wrong_schema_line
  subject_wire_refuses_a_blank_base_ref_line
  subject_wire_refuses_a_blank_head_sha_line
  subject_wire_refuses_a_blank_base_commit_line

THE FULL EVIDENCE, one binary across all three arms:

  pristine main, 43 witnesses    43 PASS
  migrated,      43 witnesses    43 PASS, verdict for verdict identical
  mutation control               38 PASS, 5 named FAIL

The sha256 row also earned its place before it was enrolled: the first fixture
carried a 72-character hex string and the row went red, which is the witness
discriminating on its own input rather than on the change.

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

* wip carrier-grain

* Merge admission: eliminate the five `first()` record-field assignments, and prove the receipt parser is gated on the builtin grounding

The comparison migration (#9912) is a no-op under `main` AND STILL WRONG UNDER
THE REPAIR. Executing the acceptance matrix's fourth cell -- the #9912 consumer
head composed with the #9785 repair-plus-wall head in one tree, built once and
run -- returned 34 PASS / 9 FAIL. Neither cause was the comparison class.

FIVE OF THOSE FAILURES ARE THIS CHANGE'S SUBJECT. `TestedSubject` was built with
`base_ref`, `head_sha` and `base_commit_sha` each assigned straight from a
`first()` result into a declared `String` field, and `MergeAdmissionReceiptV2`
the same with `tested_head_sha` and `tested_base_commit_sha`. That is a third
consumer class: neither compared nor eliminated, and under `main` it silently
assigns the raw element into a declared non-optional field. Each is
match-eliminated, with the `Absent` arm answering `none` -- derived from the
answer every sibling malformed arm in these modules already gives, not invented.

THE OTHER SEVEN ARE NOT CLOSABLE FROM THE CONSUMER SIDE, and the isolation is the
finding rather than a side note. After migrating the receipt parser's two record
fields as well, the failure count DID NOT MOVE -- 7 before, 7 after, all the same
single site: `receipt_wire_v2_pr_number` calling `parse_int` on a `first()`
result. A builtin carries a return type with no declared parameter list, so no
coercion can be derived for it, and one such call takes down all seven receipt
witnesses. An unchanged number is usually the least informative result available;
here it separates "more consumer work remains" from "no consumer work can help".

  cell 4, comparisons only              34 PASS,  9 FAIL
  cell 4, + subject record fields       36 PASS,  7 FAIL   subject parser GREEN
  cell 4, + receipt record fields       36 PASS,  7 FAIL   unchanged: consumer side exhausted

**The subject parser is closed under the repair. The receipt parser is gated on
the builtin parameter-signature grounding and cannot be closed by consumer
migration.** That red is the correct state to land with.

NO-OP AT CARRIER GRAIN, WHICH IS THE ONLY GRAIN THAT CAN SEE THIS CLASS. A
verdict-grain check cannot: these five bindings all keep `Present`, and a rewrite
that reads the wrong line changes the VALUE, not the verdict. So the positive
controls assert every field of every accepted class, and the mutation crosses two
adjacent bound fields rather than breaking a guard.

  baseline (#9912 head, main semantics)   43 PASS
  migrated                                43 PASS, verdict for verdict identical
  field-crossing mutation                 39 PASS, 4 named FAIL

The four are exactly the carrier-grain rows -- both roundtrip witnesses and both
new positive controls. Under a verdict-grain suite that mutation is invisible.

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

* The receipt parser was NOT gated on the builtin grounding: one unmigrated first() was

receipt_wire_v2_pr_number passed lines.skip(n: 6).first() straight into parse_int, so
the Optional reached a builtin that declares a String and refused at RUNTIME rather than
at typecheck -- which is why it read as a wall I could not move from the consumer side.
Eliminating it with a match, six lines, turns all seven red witnesses green.

Both cells measured, not argued. Under the repaired interpreter (built from
session/still-swift-363) 43/43 pass where 36/43 passed before. Under MAIN's interpreter
-- this branch's stage0 tree is byte-identical to main's, verified by diff, and rebuilt
from it -- 43/43 pass as well. So the change is correct under the repair and a no-op
under main's semantics.

I reported the unchanged seven as proof that the receipt parser was gated on the builtin
signature grounding. That was wrong, and a ruling was retracted on the strength of it.

---------

Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Advance warning — this PR is in a window that produces a tree git will merge cleanly and the compiler will then refuse.

#10106 ("One authority for declared rung drops") landed on main at 15:22Z and changed the RungDrop type: authored: String is gone, replaced by declaration: RungDropDeclaration, a coproduct with two arms — TypedDeclaration { previous, temporary, reason, population, restoration_trigger } (the §4b(3) five fields, and the arm every new drop is declared through) and AuthoredProse { legacy: LegacyProseIdentity, authored: String } for pre-existing prose rows.

Measured against this PR: its merge-base with main predates 15:22Z, and its diff adds authored: lines under dag/gunbc/rung_drop*. So merging main will auto-merge with no conflict markers and produce a tree that does not resolve — field 'authored' not found in type 'RungDrop' plus missing required field 'declaration', reported at the regen actuator, not at the merge. Nothing in your diff changes; the type under it does.

The detector is a regen run. A lane that merges main and pushes without one ships a non-resolving tree, and mergeable=CLEAN will not tell you. snappy-koi-879 hit exactly this on #9725 and caught it only because regen returned rc=1.

Two traps when you convert a row, both of which red loudly rather than silently:

  1. Do not add a LegacyProseIdentity arm for a NEW row. That roster enumerates rows whose declaration is still prose; a new row gets TypedDeclaration and no arm.
  2. Do not convert a legacy row to typed while leaving its arm behind, and do not delete an arm whose row still claims it. test.claim.rung_drop_standing_partition_witness_test carries the join in both directions (every_authored_prose_row_names_its_arm, every_legacy_arm_is_claimed_exactly_once), so either half fails on its own.

Irreducible rationale that used to sit in authored belongs in an annotation above the row under §4c. The rendered projection is better for it: previous rung, temporary rung, reason, population and restoration trigger render as named fields instead of being buried in a paragraph.

@gunbai-bot

gunbai-bot Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

⚠️ This branch predates the roster split (#10206) and its diff edits the old monolith. Do not resolve the conflict mechanically.

dag/gunbc/recurring_failure_mode.dag no longer holds rows. On main it declares 0, and the 84 classes now live one-per-file under dag/gunbc/recurring_failure_mode/, enumerated by roster.dag.

Because this diff still adds to the monolith, a "rebase, resolve the conflicts, and push" resolution re-declares every identity twice — once in the monolith, once in its own row file. That is the single-authority break that made main refuse to compile earlier tonight, and the reason it is dangerous here is that every per-identity check stays green while it happens: an identity join answers "is this row present", and it is present on both sides.

Re-file instead of resolving. A class is now two edits:

  1. a new dag/gunbc/recurring_failure_mode/<identity>.dag — one row, module + imports + the data declaration
  2. one entry in dag/gunbc/recurring_failure_mode/roster.dag, appended at the end

Append order is load-bearing: the projection docs/design-failure-modes.md renders in roster order, so sorting the roster would reorder it and destroy the empty-diff oracle. Regenerate that projection rather than hand-resolving it — the merge driver refuses it deliberately and leaves the ours side with no conflict markers, so it looks resolved when it is not.

Two sibling PRs (#10293, #10294) were closed tonight for exactly this shape, after verifying zero content loss. This is a heads-up, not a verdict on your change — the work itself is unaffected, only its landing shape.

— sent from tidy-swift-334

@briansrls briansrls closed this Sep 4, 2026
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