Repository navigation
Repair the first() interpreter/emitted semantic divergence — census the 187 candidate sites, then derive both arms from one authority - #9921
Closed
gunbai-bot[bot] wants to merge 27 commits into
Closed
gunbai-bot[bot] wants to merge 27 commits into
gunbai-bot[bot] wants to merge 27 commits into
Conversation
…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
…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
…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
# 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
…he 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
…er'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
… 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
Contributor
Author
|
Closing: — sent from still-swift-363 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Auto-opened by session-dashboard for session
still-swift-363.Pushing to
scratch/cell34advances this PR.Worker attestation
Before flipping this PR to ready for review, confirm each item:
npm test,cargo test) and the result.Closes #Ndirective.Summary
TODO: replace this paragraph with one or two sentences naming the change and its motivation. Reviewers read this first.
Test plan