Repository navigation
first()/last(): construct the declared Optional, repair the corpus that consumes it, and withdraw the decline candidate - #10187
gunbai-bot[bot] wants to merge 31 commits into
Conversation
std.algebra free_monoid_collection_templates declares
first [ReceiverSelf] -> OptionalOf { inner: ReceiverElement }
last [ReceiverSelf] -> OptionalOf { inner: ReceiverElement }
and that declaration is mirrored into the seed as std_algebra.rs and READ by the
type checker (v1_compiler_infer_method walks algebra_templates_for_profile).
The evaluator did not read it: the arm bodies returned
items.front().cloned().unwrap_or(Value::Null), collapsing "the collection had no
first element" and "the first element is itself absent" into one sentinel.
This is a CONFORMANCE repair, not a modeling one. The declaration already
existed; the evaluator contradicted it.
fn f() -> Int? over [none, Present{value:2}] -- TWO elements, first absent
interpreter before 9 ("empty") after 7 ("present, and absent")
emitted Rust 7 7
positive control [5,6].first() 5 5
The construction mirrors map_lookup_as_optional, which already performs exactly
this for maps by call site rather than value shape.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
The row said 'REPAIRED BY CONSTRUCTION (gunbc#10187): the two arms now build optional_present/optional_absent' and then, several hundred words later, explained that the repair is deferred. A reader grepping the first sentence reads a class that climbed. That is DESIGN 4b(1) rung inflation, and an inflated class never ranks for climbing -- which this row is the ledger for, so it cannot carry an instance of it. Now reads: a repair EXISTS and is DELIBERATELY UNLANDED (#10187, draft, not merged), the arms WOULD build the constructors, the reason it is withheld is the prior filing, and NO CLIMB IS CLAIMED BY THIS ROW. The prior-filing paragraph is now the cause of the deferral rather than the reversal of a claim already made. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
Census of
|
| class (what immediately follows the call) | sites | meaning |
|---|---|---|
match X.first() { |
248 | already destructures the Optional explicitly |
.first().<member> |
118 | member access straight off the call — the auto-unwrap population |
| end of line | 106 | result returned or bound |
| argument in a list | 80 | passed onward |
| closes enclosing call | 64 | passed onward |
compared (==) |
31 | |
| other | 21 | |
| total code call sites | 668 | across 181 files |
28 further matches were excluded as prose or string content — several .dag annotations discuss .first().tag and .first().value in text. Counting those as call sites is what inflated my earlier figure of 130 for the member-access class to a wrong number; the corrected class is 118 sites across 33 files.
What this decomposition is for
The two large classes are governed by different decisions, which is the point of separating them:
- The 248
matchsites already expectOptional. They are the consumers that the interpreter's oldValue::Nullreturn contradicted, and they are conformant by construction once the evaluator honours the declaration — the repair in this PR. - The 118 member-access sites are the ones §11 item 0(1) governs. Whether
xs.first().fieldauto-unwraps, refuses, or requires an explicit match is exactly the member-selection policy question, and it cannot be settled by this PR — which is why this PR is deliberately the narrow half (item 0(2), type conformance).
What this is NOT
This is a static shape census keyed by site identity — a strictly better instrument than a count, and still not the executed population. Whether a given site observes the divergence depends on whether the collection's element type is itself Optional and whether the arm is reached; neither is decidable from the syntax. The executed population remains CI's floor run, for the reasons stated in the PR body.
I also cannot reconcile these figures against the "187 candidate sites" in my original brief: I do not have that number's producer or its selection rule, so I am reporting what I measured and how, rather than asserting agreement or disagreement with a figure whose provenance I cannot inspect.
— sent from still-swift-363
Narrowing the 118 against the code path item 0(1) actually governsMeasured at
Both unwrap. The first reads
The single ambiguous siteThe parameter it feeds is named Why this matters for the rulingThe two arms carry very different migration costs, and they separate the same way 0(2) separated from 0(1):
No recommendation is offered among these — it is a language-semantics decision. The measurable point is that the option most would assume expensive (killing the ambiguous Same two limits as the census above: a static shape keyed by site identity, not the executed population; whether any site observes a divergence still requires element types and reachability, neither decidable from syntax. — sent from still-swift-363 |
…63-first-conformance
…ms from field_summary_for_type #10187 alone BREAKS THE CORPUS, and the control says so rather than the argument: same tree, same binary vintage, only the two `first`/`last` arms differing. Repair present -> NoSuchField { type_name: "Optional", field: "shape" } at extdeps/bmc/types.dag. Repair absent -> evaluates through. CI found a second site with the same root and a different symptom (`cannot concat on Variant` in floor_observe_git_diff_unified_for_ci), which is why one reproduction was not the population. THE ROOT WAS NOT WHAT I FIRST REPORTED. I described the checker as auto-unwrapping member access through an Optional. It does not: the `else if normed_opt` arm of `field_summary_for_type` recurses on the inner type and returns `value_shape: OptionalValue`. That is a FUNCTOR LIFT -- field access through an Optional yields an Optional, PRESERVING the empty case rather than erasing it. So the declaration and the checker already agreed and the interpreter was the sole divergent arm, before this repair and after it, in opposite directions. There was no semantics decision over the 122 member-access sites to take: they are on green main today, typed under OptionalValue, and every consumer downstream is already written against that reading. THE DEFECT, STATED AS A COUNT: `value_shape` appeared 0 times in v1_interpreter.rs. `field_summary_for_type` decides field access in TWO parts; the evaluator consumed `access_style` and silently dropped the other half, then re-decided by direct lookup. One authority, both directions -- the checker's decision is now consumed rather than re-derived. THREE ARMS, EACH WITH A CONTROL THAT DISCRIMINATES: 1. first/last construct a real Optional (unchanged from #10187). 2. value_shape: OptionalValue lifts through the wrapper. Fixture: two-element list -> 7; EMPTY list -> 9, the Absent case preserved rather than fabricated. Both mutate to red when the expected value is changed, so the green is not vacuous. 3. OptionalUnwrap projects the payload. NOT IN THE PLAN -- it surfaced because arm 1 RETIRED AN ENCODING. That arm returned `base_val`, which WAS the unwrap while an absent optional was Value::Null and a present one the bare element; against a real Variant it hands back the wrapper. Control: the witness at dag/test/claim/roadmap/roadmap_execution_contract_witness_test returns true with arm 3 and fails PatternMatchFailure { value: "Present { value: GunbcClaimRunnerCapability }" } without it. WHAT IS DELIBERATELY NOT REPAIRED. Arm 3 keeps the pre-existing Value::Null result for Absent. The unwrap style has no carrier position for absence, and `LiteralValue::LitNull => Ok(Value::Null)` makes `null` an authorable .dag literal -- so that Null is not a lookalike but THE SAME VALUE a true answer uses, indistinguishable to every downstream consumer. It is not fixable inside the arm: the arm cannot tell the two Nulls apart because the information was destroyed upstream, where the `field == "value"` arm claimed PlainValue for a spelling it never verified. Repairing it here would mean GUESSING which Null, which is the absorbing fallback in a different hat. That class is decidable (inspect the inner type), reached independently three times -- this change, tidy-lynx-804's read, and a pre-existing minimal reproduction already in dag/extdeps/bmc/pid_control_decode.dag -- and is owed its own state_space_conflation form, whose rung is the OPPOSITE of the fourth form's: that one refuses, this one answers. MEASURED: cargo test --release -p v1-compiler --lib -> 705 passed, 0 failed. The count moved from 689 because the merge of main brought 16 tests, not because this change added any. NOT MEASURED HERE: the emitted-Rust arm, and the .dag corpus beyond the entries named above -- CI is the instrument for the corpus and its verdict on this head is not yet in. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
…r to audit-queue item 0 (#10192) * state_space_conflation: file the first() receipt as a fifth form — the declaration already existed The four preceding forms are all missing-constructor cases. This one is not: std.algebra declares first/last as OptionalOf { inner: ReceiverElement }, the declaration is mirrored into the seed, the inference path reads it, and the evaluator in the same binary contradicted it. Records the three-column realization picture deliberately, so a future reader cannot flatten it: VERIFIED BY EXECUTION (rust nests; python's and typescript's type constructors cannot carry the nesting), REFUTED BY EXECUTION (go looked faithful on its row and emitted *int64 for a declared Optional<Optional<Int>> -- the row predicts capability, not behaviour), and UNOBSERVED (what python and go emission answer end-to-end, because neither artifact runs). No refusal is filed for those paths, deliberately: it would be a wall in front of a hole, and for typescript unreachable in any case since the CLI parses only rust | python | go | dag. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23 * state_space_conflation receipt: cite audit-queue item 0 as the prior filing, and record that the repair is deferred Found after the repair was written and before it landed: this seam is already filed as item 0 of docs/plans/compiler-guarantee-recovery-gap-analysis.md (2026-08-27, gunbc#9491), with extdeps.bmc.pid_control_decode carrying the minimal reproduction and an admitted workaround. That filing reclassifies the repair. The type path ALREADY treats first() as optional and auto-unwraps field access through it (04_lookup.dag field_summary_for_type unwraps for every spelling except 'value'); the evaluator returned a bare element, so the unwrap was a no-op. Making the evaluator construct Present{...} therefore makes it inconsistent with an auto-unwrap the type checker still performs -- the opposite direction from where the halves started, which is not a climb. So the interpreter/emitted divergence and item 0(1) are ONE seam approached from two sides, and whoever settles the member-selection policy owns the evaluator repair too. The four-arm return-check control is recorded as CORROBORATION of item 0(2), not as a new finding: a second row for a class filed five days ago is the nicknaming refusal at the ledger layer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23 * receipt: state the repair as written-and-withheld, not as landed The row said 'REPAIRED BY CONSTRUCTION (gunbc#10187): the two arms now build optional_present/optional_absent' and then, several hundred words later, explained that the repair is deferred. A reader grepping the first sentence reads a class that climbed. That is DESIGN 4b(1) rung inflation, and an inflated class never ranks for climbing -- which this row is the ledger for, so it cannot carry an instance of it. Now reads: a repair EXISTS and is DELIBERATELY UNLANDED (#10187, draft, not merged), the arms WOULD build the constructors, the reason it is withheld is the prior filing, and NO CLIMB IS CLAIMED BY THIS ROW. The prior-filing paragraph is now the cause of the deferral rather than the reversal of a claim already made. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23 * chore: regenerate drifted generated artifacts (ci auto-heal) --------- Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…normalizing it away
WHAT WAS OPEN. DESIGN 4b names "values inhabit declared types" as part of the
ORDINARY COMPILER FLOOR. Two seams in src/v1/04_infer.dag judge type
compatibility -- direct_call_arg_type_mismatch (argument position) and
field_type_diags (record-literal field init) -- and BOTH already compared the
right operands at the right positions. Each first applies
with_required_cardinality to the DECLARED side and then compares KERNELS, so the
cardinality dimension was normalized out of the operands BEFORE the comparison
ran. A field declared String accepted a value typed Optional<String> with no
diagnostic.
THIS IS WHY NOBODY FOUND IT BY READING THE CHECK: reading the logic of either
seam shows a correct comparison. The check ran, matched, and answered on a
projection that had the answer removed from it first.
WHAT IT COST. It made a method's declared return type NON-LOAD-BEARING at every
assignment and argument position. gunbc#10187 changed first()'s evaluator to
honour its declared Optional return, and sites then broke at RUNTIME instead of
at the check -- executed twice, two sites, two different symptoms from one root:
extdeps/git/git.dag:629 "cannot concat on Variant", two calls downstream
extdeps/bmc/types.dag:183 NoSuchField { "Optional", "shape" }
THE REPAIR IS ONE-DIRECTIONAL AND THAT IS DELIBERATE. Required-into-optional is
WIDENING and stays legal: an Optional<T> position accepting a T loses nothing.
Optional-into-required is the narrowing that drops the absent case, and it is the
only case decided. That is narrower than "enforce assignment and argument
positions", which would have pulled in every kernel and brand judgment those
seams already make.
At the field seam the predicate reads `expected_node` rather than the existing
`expected_required` local, so declared optionality survives into the comparison
instead of being erased by the very call that hid this. At the argument seam it
reads `actual_raw` rather than the peeled `actual`, because peeling to the kernel
can drop the cardinality being judged.
THE DIAGNOSTIC NAMES ITS OWN CAUSE, and it has to. Reusing type_mismatch_error
produced
type mismatch: expected 'Primitive(String)', got 'Primitive(String)'
-- a REAL refusal that is unreadable, because node_type_shape renders optionality
only after the primitive branch, so an optional and its payload print identically
at a primitive kernel. A located refusal whose message cannot distinguish the two
states it is refusing over is not a typed diagnostic (DESIGN 5), and it would
also have made the population unfilterable. OptionalValueInRequiredPosition
carries the declared type and a live remedy, and is blocking via the existing
`_ => true`.
SCOPE HELD DELIBERATELY NARROW. The predicate is NOT wired into the shared
direct_call_arg_type_mismatch helper: its other two callers are Bool predicates
whose diagnostics are emitted elsewhere and would have printed the unreadable
message. Argument coverage is therefore the emitting seam only, and the
structured-record-literal predicate path is NOT covered by this change.
EVIDENCE, three-way and discriminating:
declared String <- Optional<String> REDS, named diagnostic
declared String? <- Optional<String> does not fire (widening stays legal)
declared String <- String does not fire
Measured on a binary built from this tree, 1 blocking error and no others.
AUTHORITY, NOT MIRROR. Authored in src/v1/04_infer.dag and src/v1/00_core.dag;
src/v1/stage0/src/v1_compiler_infer.rs and v1_std_core.rs are the EMITTER'S OWN
BYTES installed from target/stage0-regen-candidate, not hand-shaped. The two arms
in cli_run/compile_clean.rs are hand-Rust: that file's matches are deliberately
total ("no silent widening"), so the new variant made them fail to compile, which
is the exhaustiveness wall working rather than an inconvenience.
WHAT IS NOT MEASURED YET, AND IS THE REASON THIS IS PUSHED. The corpus
population. A whole-tree compile needs 16GiB and is a CI job, not a session job;
run remotely it exceeded the 45-minute window and was KILLED, and an earlier
attempt that reported "0 type mismatches" was that same dead run reporting its
own death as a clean corpus. CI's compile-clean gate is the census, and it is the
SAME instrument that will gate -- so the measurement cannot drift from the
enforcement. The count is CI's to produce; it is not asserted here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
…nformance' into session/still-swift-363-first-conformance
…its owner
MIRROR NOTE, FIRST BECAUSE IT IS THE ONLY UNSOUND THING HERE: the .rs mirrors in
this commit are from the PREVIOUS regen round and are one cycle behind
src/v1/*.dag. The regen that reconciles them is running as this lands. Pushed
anyway, deliberately: unpushed work in a long-running session is one container
restart from gone, and a transiently drifted pair on a DRAFT branch is
recoverable where lost work is not. The next commit carries the reconciled
mirror; do not read this one as a fixed point.
THE CHECK HAD THE WRONG SUBJECT TWICE, AND BOTH TIMES THE SUBJECT WAS NOWHERE
WRITTEN DOWN:
1. it tested cardinality only, so an explicitly-spelled Optional<T> DECLARATION
read as required -- refusing `redirect: Present { value: DiscardAll }`
against a field declared Optional<Redirect>;
2. it read a bare `none` as a PRODUCED optional, when `none` is a literal whose
type is decided BY the position it lands in -- refusing `takes_opt(x: none)`
against `fn takes_opt(x: Thing?)`, and with it 146 corpus sites reaching
through one helper's `else_stmt: TsStmt?` calls.
Both times the predicate was clean, total over its own coproduct, and asking a
question nobody had stated it was asking. That is not a bug review can catch: a
check whose subject lives only in its author's head has no scope to be reviewed
against. So the SUBJECT IS NOW DECLARED beside the predicate in 00_core.dag -- a
COMPUTED optional reaching a position DECLARED required -- and everything outside
it is an explicit exclusion carrying its reason and its measurement. A future red
outside that sentence is a scope violation to be read, not output to be
explained.
THE EXCLUSION IS DERIVED, NOT SPELLED. Excluding bare `none` at both seams
removed the disagreement and left a COUPLING in its place: two sites that must
keep agreeing with field_type_admits_bare_none about what a bare none IS, with
nothing joining them, failing silently in the expensive direction if that notion
widens. So expr_is_bare_none_reference is narrowed to take source_indices -- all
it ever read -- which is what lets the direct-call argument seam, which has no
InferScope, call the REAL authority instead of a copy. One definition, three
callers, the duplicate deleted. Replacing a fork with a hand-kept agreement is
the version of the defect that survives review because both sides are
individually correct today.
ALSO IN THIS COMMIT, from the earlier round: node_carries_optional consolidated
into 00_core.dag as the single authority for "is this node optional", with
04_infer's equality_operand_admission and 04_types's extract_optional_inner_node
routed through it. Its annotation states that it is NOT the fix for the
two-spellings class -- the construction move is canonicalization at
normalization, which would make all seventeen cardinality-only tests in
04_infer.dag correct without being touched -- because a shared predicate callers
must REMEMBER is validation standing where construction was available, and
without that sentence the seventeen read as unmigrated rather than defective.
extract_optional_inner_node keeps two arms deliberately: one DECISION, two PEELS,
and collapsing the peel would have been a semantic error wearing
deduplication's clothes.
NOT MEASURED IN THIS COMMIT: the corrected traversal. Every population figure so
far is contaminated -- an early-refused corpus walk containing my own false
positives -- and two of my three estimates about it have been refuted by
execution. No total is asserted here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
The seam fix is one line and it is the whole finding: the direct-call argument seam judged app.formal, which is peel_nominal_alias_identity(formal_subst) and therefore has cardinality stripped. A check written to judge the cardinality dimension was comparing a declared operand with that dimension already normalized out of it -- an instance of comparison_discards_the_dimension_it_judges, the class filed in this same commit, arriving in its own author's hands. It now judges app.formal_subst, symmetric with the actual_raw already used on the produced side. Measured by a four-cell fixture rather than by a count, because the pairing is what discriminates a broken operand from a vacuous check: argument seam, formal ProbeS?, bare none -> was FIRED, now silent argument seam, formal ProbeS, optional -> fires (unchanged) field seam, field ProbeS?, bare none -> silent (unchanged) field seam, field ProbeS, optional -> fires (unchanged) Both true-positive cells still fire, so the false positive was removed without buying silence. Effect on the reported population: 1,292 -> 11 mentions in the regen closure and 419 -> 118 sites in the projection closure. Neither closure is the corpus, so both are counts over an unstated denominator and neither is offered as a population figure. Two failure-mode rows, in the one-row-per-file layout #10206 introduced, with roster entries appended in source order because sorting would destroy that cut's empty-diff oracle. evidence stays empty on both, per that carrier's own recorded reasoning that an unresolved, unrendered citation is decoration rather than coverage. comparison_discards_the_dimension_it_judges -- new class, argued against both total_at_the_level_examined_blind (there the deciding distinction is PRESENT and the match declines to descend; here it is REMOVED from the operands, so descending finds nothing) and check_subject_narrower_than_its_declared_claim (whose recognition rule asks which sites are VISITED, which cannot find this). state_space_conflation -- nine receipts for the field_summary_for_type form, which has the OPPOSITE rung from the fourth form: that one refuses, this one ANSWERS with Value::Null, and null is an authorable literal, so the fabricated result is not a similar spelling but the same value. The merge also carries #10206's relocation of the failure-mode rows one-per-file. A branch behind that relocation resurrects a declaration as a duplicate rather than as a conflict, so nothing marks it; audited, and this tree carries each name in exactly one file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
…63-first-conformance # Conflicts: # dag/gunbc/recurring_failure_mode/roster.dag
Four functions in std.realization_schedule -- string_list_eq, schedule_witness_entry_list_eq, runnable_batch_eq, schedule_eq -- were the same recursion modulo the element comparison. Each opened with a count()-pair guard and then called first() on both sides, so each independently reproduced the shape that puts an optional in a required position: first() is total and returns an Optional, while the emptiness proof sits in a branch the type checker cannot read. Repairing them site-by-site would have minted four repairs for one fact, so the section 2 duplication fix and the section 4b safety fix are the same edit. Doing the safety repair per-site would have cemented the duplication it should dissolve. list_eq_by<T>(left, right, eq: fn(T, T) -> Bool) MATCHES on first() rather than asserting past it. That is the difference between the two repair kinds: unwrapping would discard the author's emptiness proof four times over, whereas matching means no proof is needed, because the absent case is decided rather than assumed. It also removes a cost defect that section 6's bare-minimum-cost rule says is always fixed regardless of realized n: the old shape recomputed left.count() and right.count() at every recursion level, paying a linear scan per element. Measured on the projection closure: 118 -> 112 sites, 35 -> 34 files, with realization_schedule going 6 -> 0. The delta is exactly the six diagnostics that file carried and nothing else moved. Also files source_text_census_counts_its_own_documentation, which is a standing property of this repository rather than a fact about this census: DESIGN 4c requires prose to live in typed carriers, those carriers are .dag data rows, so a source-text census counts receipts QUOTING a construct as instances of it. Measured at 19 of 660 matches inside string literals. The failure-mode ledger is the densest contaminator precisely because its job is to quote defects verbatim, the floor GROWS as the ledger grows, and two of the contaminating receipts were filed by this lane on the night of the census they inflated. It surfaced only as a 3-occurrence disagreement between two independently built instruments and was attributed to merge drift first; a single instrument publishes the inflated number with nothing to investigate. The census trigger is filed as ONE capability -- an AST-grain reader over the resolved program -- because the enclosing-span attribution, the guard-detector bracket and the prose contamination are one root: source text standing where the resolved tree was available. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
The enforcing check now accepts the corpus it runs over, which is what dissolves
the bootstrap deadlock: while any true positive remained, regen refused before
emit, so the change could not be regenerated through the seed at all.
Three repairs, one shape, applied where a guard existed that the type checker
cannot read:
extdeps.version.semver semver_compare_identifiers -- a count()==0 cascade
becomes nested matches on both first() values. Empty/empty Equal,
empty/non-empty Less, non-empty/empty Greater, exactly as before, and four
count() scans per recursion level go away.
extdeps.container.oci.digest oci_wire_digest_parts -- matches both first()
values and answers none on either absent. The length(parts) != 2 guard is
KEPT deliberately: it is not made redundant by the matches, because it is what
rejects three-or-more parts while the matches prove only non-emptiness.
std.occurrence_identity occurrence_id_list_is_prefix_of -- the Absent arm keeps
path_remaining: [], which is equivalent because the list is already empty on
that branch.
Each CONSUMES the optional rather than unwrapping it. The distinction is the
point: unwrapping discards a proof the author wrote, while matching means no proof
is needed, because the absent case is decided rather than assumed.
Measured, regen closure: 11 -> 0 optional-into-required diagnostics.
Measured, projection closure: 118 -> 112 sites after the fold dissolution alone.
FIXED POINT, which is what makes the bootstrap a round trip rather than a one-way
patch: rebuilt from the installed seed, required-regen exits 0 with
first_generation_equal=true, 156 planned / 156 executed / 156 adjudicated, one
pre-existing declared divergence (main.rs), and the rebuilt emitter reproduces
v1_compiler_infer.rs byte-for-byte (sha 1436bbdbf3bff127 both sides).
The install was verified at manifest grain, not file grain: exactly the four named
mirrors changed and zero other candidate files differed from their installed
copies. That check exists because installing a stale emitter's lib.rs earlier
dropped the mod declarations for three files it did not know about -- a refusal
naming N files implicates every generated file that DECLARES those N.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
The repair is to make the proof UNNECESSARY, not to make it visible -- matching consumes the optional so the absent case is decided rather than assumed. And a repair that obviates some checks must prove subsumption PER CHECK, because adjacency is not subsumption: oci_wire_digest_parts' length(parts) != 2 rejects three-or-more parts, which the non-emptiness matches do not subsume. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
…63-first-conformance # Conflicts: # dag/gunbc/recurring_failure_mode/roster.dag
…accusing it optional_into_required_mismatch returned Bool, which has nowhere to put "could not tell". node_carries_optional answers false for a declared position whose type is still an unsubstituted type parameter -- the identical false it gives a genuinely required position -- so the unknowable case took the ACCUSING arm. That is DESIGN section 5's absorbing fallback in its most deceptive form: the arm that could not compute its answer substituted the widest one, and because the widening emitted a located, typed diagnostic it read as the check working. The judgment is now three-valued -- NarrowsToRequired | ConformsOrWidens | NarrowingUndetermined -- and the undetermined case declines to judge. MEASURED EXTENT, STATED HONESTLY: one site. Over the dag + src/v2 corpus this clears exactly dag/std/serialize.dag:19:29 and introduces no new diagnostics (103 -> 102 unique sites, 0 newly-appearing). That site is a false positive: value_quote is String?, and the null/q match binds q to the PRESENT payload, so the formal of concat was an unsubstituted T from which the check concluded "required". No real defect is hidden by the decline. So this is a construction change with n=1, not a repair of a population. It lands because the two-valued type made the wrong answer REPRESENTABLE, which is the thing section 5 forbids, and not because it fixed 103 things. Corollary worth keeping: the 102 surviving sites are NOT contaminated by this defect, so the repair population is trustworthy. Ceiling and next-rung trigger: substitution of builtin generic formals at the argument seam. Until that lands, an unsubstituted formal is honestly unjudged rather than falsely judged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
An instrument that RENDERS one scope while ADJUDICATING another reports confidently about a subject it never measured. Filed as its own row with its receipts; enrolled on the roster beside the existing rows. Carries the regenerated stage0 mirror for the three-valued optionality judgment in the preceding commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
…63-first-conformance # Conflicts: # dag/gunbc/recurring_failure_mode/roster.dag
…trust first Three consecutive runs reported CLEARED=103 of 103 with NEWLY-APPEARING=0 -- a perfect clearance matching the hypothesis -- and all three were instrument failures with three different causes: a hyphenated subcommand that does not exist, an underscored one that also does not (it is a separate binary), and grep silently switching to binary mode on a NUL byte while grep -c kept counting. The true answer was 1. The three share no mechanism. What makes them one class is that each produced ZERO EXTRACTED SITES, and zero extracted sites renders as total success for a repair -- every instrument in the chain fails toward the answer the author wants. The discriminator is a positive control counting the subject at its coarsest grain: at 0 it exposes a run that never compiled anything, and at 206 beside an extracted 0 it exposes an EXTRACTION fault rather than a compiler result. Records the mirror-image instance too, which is subtler: a small believable delta is more dangerous than a perfect one. A .dag edit measured without regenerating the seed cargo compiles attributed a 103 -> 102 change to a construct the binary did not contain. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
… it replaced Review blocker, verified against abff82c and correct. The three-valued judgment was introduced and then DISCARDED at its only consumer: NarrowingUndetermined => false Both seams went through that one predicate, so the whole behaviour of the undetermined arm was to admit the position with no diagnostic, no count and no trace. That trades a LOUD false positive for a SILENT one, which is worse on the axis that matters: the false positive accused correct code but announced itself and was found in one read, whereas a silent admission leaves no artifact to notice, so the deficit stops ranking for repair. Below the ladder, not low on it. It is the absorbing fallback arriving inside the fix for the absorbing fallback -- the mechanism that could not compute its answer still substituted a weaker one, and only the direction changed. The annotation twelve lines above claimed the opposite ("keeps the deficit visible as an unjudged position"), which is the review tell: the comment asserted the property the code omitted, about the very defect the change exists to repair. Repaired by deletion rather than by patching the arm: - OptionalNarrowingUndetermined { declared, span } is a NEW diagnostic variant, deliberately distinct from OptionalValueInRequiredPosition, so a census can separate the seam's own deficit from a defect in the authored code by VARIANT rather than by inference. Its text says so explicitly. - optional_narrowing_refuses is DELETED. The predicate that collapsed three values into two no longer exists, so the collapse cannot be reintroduced by a later hand adding a fourth case to a Bool. - optional_narrowing_diags is total over the three values and returns the diagnostics directly; both seams emit from that single authority, bound once per site rather than evaluated twice. - optional_into_required_error is deleted as production machinery the climb obsoleted (4b(4) dissolution-on-climb); its one caller now routes through the total emitter. Expected and not a regression: the next census will show a new diagnostic class with a non-zero count that did not exist before. That is the deficit becoming countable, and it will be reported as its own number, not folded into the 102. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
briansrls
left a comment
There was a problem hiding this comment.
REQUEST_CHANGES on exact head b812bd4. DO NOT MERGE.
The b812 repair is directionally correct and closes one real blocker from the preceding head: NarrowingUndetermined => false is deleted, the third judgment now reaches a distinct OptionalNarrowingUndetermined diagnostic, and both position seams consume the total emitter. A could-not-tell is no longer silently admitted.
Three source findings remain.
- THE RECORD-LITERAL INSTANTIATION FALLBACK IS STILL NOT TOTAL.
record_lit_instantiated_fieldsreturnsList<Node>?and answersnonefor materially different states: instantiation is inapplicable, expected evidence is absent, lookup fails, generic arity disagrees, or variant-template selection fails.infer_record_lit_structuraltreats everynoneas permission to proceed throughrecord_lit_fields_from_expected, the expected variant, and ultimately the raw declaration fields. The comment aboverecord_lit_instantiation_template_fieldssays a miss must refuse rather than answer rawT; the caller still does the latter. The new optionality diagnostic catches one downstream manifestation when the raw DECLARED operand is a recognized type variable and the produced value is alreadyCardOptional; it does not make this fallback honest for the other consumers ofstruct_fields.
Split the result at the source: at minimum InstantiationNotApplicable | Instantiated { fields } | InstantiationUnavailable { cause }. Only NotApplicable may enter the legacy fallback. Unavailable must emit a typed refusal/decline or otherwise remain a first-class unjudged disposition; it must not supply unsubstituted fields as if they were the instantiated answer.
The new diagnostic also demonstrates why the cause must be carried. OptionalNarrowingUndetermined { declared, span } renders a dissolution trigger specific to “builtin generic formals ... at the argument seam”, but infer_record_lit_structural emits that same variant at the record-field seam, where the observed cause was record/variant instantiation fallback. The variant has no seam or cause field, so one of its consumers necessarily receives a false remedy. Make the message cause-neutral or carry a typed cause/seam; do not let one argument-seam trigger speak for both.
- THE PRODUCED-SIDE FALSE NEGATIVE SURVIVES.
optional_into_required_mismatchfirst matchesproduced.return_cardinality;RequiredreturnsConformsOrWidensimmediately. An unsubstituted produced type parameter therefore never reaches any unknown judgment. When thatTsubstitutes to an optional type at a required destination, the check stays silent. b812 detects only an unsubstituted DECLARED operand.
That mechanism is source-established, but this PR does not yet carry discriminating execution evidence for its reachability. Add the mirrored produced-side matrix at both seams: produced T instantiated to Optional at a required destination must refuse or emit a typed undetermined disposition; the same path instantiated to a required type must remain admitted. Then either derive the produced substitution before comparison or add a produced-side unknown arm that is consumed, not collapsed. The recurring-failure-mode prose correctly predicts newly appearing findings, but prose is not the execution receipt.
- DO NOT NARROW THE CHECK TO MAKE THE CORPUS GREEN. On that policy I agree. A use-site diagnostic whose repair authority is the binding is evidence that the diagnostic lacks an origin join, not evidence that the semantic population should shrink. The atomic landing shape—wall plus demanded corpus repairs in one PR, with commits separated by repair mechanism—is sound.
I disagree only with the proposed carrier name for any irreducible residue. This is not a DESIGN §4b(3) RungDrop unless an exact population previously held a rung and then lost it. This PR is establishing the ordinary-floor wall for the first time; an unresolved identity never had that wall. The honest carrier is a §4b(2) GuaranteeStall, or an exact typed unjudged/debt roster consumed by the check, over the affected occurrence identities. It should name the missing resolved-AST capability that joins each use occurrence to its producer/binding and dominating proof; membership must be an identity join with a reverse check, and an unrostered unknown must refuse rather than disappear. Counts may churn without affecting that contract.
No arithmetic finding is made. CI has not run on this head, and a future green cannot discharge these source-level distinctions.
…63-first-conformance # Conflicts: # dag/gunbc/recurring_failure_mode/roster.dag
The seam had three states collapsed into two. `optional_into_required_mismatch`
returned a Bool, so "the declared type is an unsubstituted type parameter" --
which establishes optionality NEITHER way -- had to pick an arm, and picking
either one is wrong: `true` accuses 219 sites of ordinary generic code, `false`
is a silent admission on a wall that previously held.
The judgment is now three-valued with a typed cause (OptionalNarrowing =
NarrowsToRequired | ConformsOrWidens | NarrowingUndetermined { cause }), and the
undetermined arm emits its own located diagnostic rather than falling through.
Per DESIGN 4b(4) the two functions the Bool supported are deleted, not kept
beside it.
`record_lit_instantiated_fields` carried the same collapse one layer down: an
optional whose Absent meant both nothing-to-report and could-not-tell. Split
into RecordLitInstantiation, with two corrections found by execution:
- a record-literal type name that does not resolve on the TYPE axis is
NotApplicable, not Unavailable. Record literals name VARIANTS far more often
than generic types, so classifying that Unavailable withdrew the fallback
from the ordinary variant route and ate 15 true positives.
- a declaration with ZERO generic params has nothing to instantiate.
`exp.children` is "the supplied type arguments" only for a generic type; on
a resolved non-generic coproduct the children are its VARIANTS, so the arity
comparison was reading two different axes and declined on 16 ordinary
zero-field constructions.
Real source repair: dag/std/effect_grant.dag, 5 sites across 3 functions, every
`count() == 0` guard dissolved into a match on `first()`. This turns
host_built_receipt_renders_through_the_model green.
Measured, predicted before each run and hit exactly on two independent runs:
population 105 cleared 5 (all five effect_grant) newly-appearing 8
102 - 5 + 8 = 105, zero unexplained movement
The 8 newly-appearing are all in src/v2 -- positions the instantiation split had
been swallowing. `required-regen` reports REGEN_RC=0, so the .dag authority and
the seed agree. 737 lib tests pass, 0 failed.
Evidence, enrolled and executing: compiler_tests_rust.dag adds a fifth fixture
cell whose assertion is three-way -- it fails on the accusation AND on silence,
with two positive controls (a concrete required type still narrows; optional
into optional still conforms). Corpus declines are 0 after the zero-param fix,
so the arm's RED is the fixture, NOT corpus coverage, and this commit does not
claim otherwise.
What is NOT fixed: the produced operand is still read pre-substitution, so a
call site whose produced type should have been substituted and was not is still
admitted silently. That is filed as gunbc.guarantee_stall
produced_operand_substitution_stall -- a 4b(2) stall, not a 4b(3) drop, because
nothing ever held that wall. Its trigger names the capability (a substituting
read CONSUMED BY BOTH SEAMS), not an artifact.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
Four conflicts, none resolved by picking a side. roster.dag (recurring_failure_mode): union of both sides, verified by identity join rather than by reading the hunk -- origin/main 96 imports/96 members all present, our branch 97/97 all present, union 100 == result 100, import/member parity exact. A whole-side take here is green and silently drops rows. guarantee_stall.dag: not a content conflict. main split it one-file-per-row in #10328, so our row re-homed to dag/gunbc/guarantee_stall/produced_operand_substitution_stall.dag with its import and all_guarantee_stalls entry (29/29 bijection). restored_stalls is left untouched: its own header states a new stall row must be admitted without editing that pinned list. The row's prose was re-read for claims the relocation falsified -- it cites resolved_type, formal_subst and the two seams, all of which live in 04_infer.dag and were never in the stall module, so nothing rotted. compiler_tests.rs and v1_compiler_compiler_tests_rust.rs: regenerated from the merged authority, not hand-resolved, and verified BY CONTENT rather than by a clean merge or an rc. That check earned its keep: the first candidate carried our fifth fixture cell but was MISSING two of main's tests (a_corpus_with_no_entry_point_emits_a_refusing_main_and_declares_no_clap and the_clap_dependency_follows_the_emitted_cli_demand_in_both_directions), because v1_compiler_compiler_tests_rust.rs is the EMITTER and the installed one predated them. Two-generation regen: install the emitter, rebuild, re-emit. Gen 2 carries all three, and required-regen then reports REGEN_RC=0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
Response to REQUEST_CHANGES on
|
| predicted | measured | |
|---|---|---|
| population | 105 | 105 |
| cleared | 5, only effect_grant |
5 |
| newly-appearing | 8 | 8 |
102 − 5 + 8 = 105, zero unexplained movement. The 8 newly-appearing are all in src/v2/. required-regen reports REGEN_RC=0; 737 lib tests pass, 0 failed, with the new fixture cell confirmed executed (not ignored).
The one real source repair is dag/std/effect_grant.dag — 5 sites, 3 functions, every count() == 0 guard dissolved into a match on first() — which turns host_built_receipt_renders_through_the_model green.
Two things I will not let read as stronger than they are
declines 0is not a wall. Zero corpus declines is an absence of observation. The arm's only executing evidence is the fifth fixture cell — §4b's authorable-RED-in-a-fixture case, which is fixture-grain, not corpus-grain.- This PR repairs ONE ARM of the divergence.
emit_typed_field_accessbranches onaccess_stylewhile the repaired interpreter consumesvalue_shape; the emitted arm has no discriminating test. The half that ships is the executing half, which can make the pair look converged. This is stated at the top of the PR body.
You are correct that a future green cannot discharge source-level distinctions, and I am not offering CI as an answer to any of the three.
Your CHANGES_REQUESTED is not sha-scoped, so it stands at cdbdbdc and no other reviewer can clear it — requesting your superseding verdict on that head.
— sent from still-swift-363
briansrls
left a comment
There was a problem hiding this comment.
REQUEST_CHANGES on exact head cdbdbdc. DO NOT MERGE. This supersedes my review 5108590638 on b812bd4, but it does not clear that disposition: finding 1 remains live in the exact source.
InstantiationUnavailableSTILL ENTERS THE LEGACY FALLBACK. The source commentary and the author's response both say onlyInstantiationNotApplicablemay fall back. The implementation says otherwise:
fn record_lit_instantiation_may_fall_back(v: RecordLitInstantiation) -> Bool {
match v {
InstantiationNotApplicable => true
InstantiationUnavailable { cause: _ } => true
Instantiated { fields: _ } => false
}
}
That true is load-bearing. infer_record_lit_structural uses it to enter record_lit_fields_from_expected, expected-variant selection, alias expansion and finally record_lit_expected_fields. If any such route yields fields, struct_fields |> count > 0 is passed as judged: true, and record_lit_instantiation_undetermined_diags suppresses the unavailable diagnostic. Therefore the exact states the split calls unknowable—generic arity disagreement and failed template selection—may still be judged from fallback/raw fields. That is the original absorbing fallback, now behind a three-arm type.
Make Unavailable unable to enter those routes. Prefer matching the coproduct directly rather than retaining a Bool whose implementation contradicts its name and annotation. An unavailable instantiation must remain a typed unjudged/refusal regardless of whether the legacy lookup could find raw fields; fallback success cannot count as judged for that disposition.
There is a related source-boundary collapse. lookup_type_for returning Absent is always classified InstantiationNotApplicable. That is right only when another axis positively establishes that the literal names a variant or otherwise proves instantiation inapplicable. A genuine lookup failure is unavailable, not inapplicable. Preserve that distinction rather than repairing the fifteen variant false negatives by mapping every type-axis miss to the non-applicable arm.
Finally, InstantiationUnavailable { cause: String } is not a closed typed cause, and the record-field diagnostic puts that free-form cause string in OptionalNarrowingUndetermined.declared. The rendered declared field should describe the declared type/position; the cause should be a separate closed cause. Do not make a sentence such as “declared generic arity does not match…” render as though it were the position's declared type.
- THE FIFTH FIXTURE CELL DOES NOT ESTABLISH EITHER PRODUCTION SEAM.
ct_optional_narrowing_undetermined_testconstructsNodes directly and callsoptional_into_required_mismatchplusoptional_narrowing_diagsdirectly. It proves that the third value and diagnostic helper are constructible. It remains green if the direct-call seam, the record-literal seam, or both stop consuming them. With corpus declines = 0, there is no production-path execution evidence to fill that gap.
Add authored-source integration fixtures that compile through both actual consumers. The record-literal fixture must force an InstantiationUnavailable state and require exactly the typed undetermined disposition—not the accusation and not silence—with a variant/ordinary NotApplicable control. The direct-call fixture must likewise exercise the formal-unsubstituted arm through the call application path. Mutation of either consumer to drop the result, and mutation of Unavailable back into the fallback, must make the corresponding fixture red. Fixture grain is sufficient for this wall only when the fixture traverses the seam whose wiring it claims.
- THE PRODUCED-SIDE RESIDUE IS HONESTLY CARRIED, BUT TWO SOURCE CLAIMS ARE STALE. I accept
gunbc.guarantee_stall.produced_operand_substitution_stallas the disposition of the original produced-Tfinding. The attempted 219-site type-variable rule was non-discriminating; the stall correctly waits on a substituting read consumed by both seams and does not misstate those 219 sites as its population. I do not require a decorative matrix that the current substrate cannot make discriminating.
However, src/v1/00_core.dag and the preceding optionality commentary in 04_infer.dag still say the produced side tests cardinality only and that an explicitly spelled Optional<T> value is not covered. The current implementation calls node_carries_optional on the produced operand, which recognizes both cardinality and nominal Optional spelling. Correct those stale scope claims; they now deny a repair the function performs.
- THE UNTESTED EMITTED ARM NEEDS A DURABLE CARRIER. The PR body is commendably explicit that the interpreter now consumes
value_shape,emit_typed_field_accessstill decides fromaccess_style, and the emitted arm has no discriminating test. But a PR body is not a repository carrier and is consumed by no corpus fold. Once merged, that warning becomes external history while the executing arm can make the pair look converged.
This does not force the emitted repair into this PR. Either add the same discriminating emitted test now, or declare an enrolled typed GuaranteeStall for the unestablished emitted-arm consumption/parity, with a trigger requiring the emitted path to consume the same field-access semantics and pass present/absent controls. The existing produced-operand stall and state-space-conflation receipt are different subjects; neither carries this backend-parity obligation.
- EXACT-HEAD CI IS RED. On cdbdbdc the
required-witnesses-floorandheal-generated-artifactschecks have failed. The uploaded required-CI receipt ismeasurement_unreached: changed-witness observation stopped during resolve on 96 blockingOptionalValueInRequiredPositiondiagnostics, so it could not observe or attribute the CI diff. The 737 library tests and localREGEN_RC=0are useful, but they are not the integrated acceptance path and do not override this red. The wall and the corpus repairs still need to land atomically; do not narrow the check to obtain green.
What is accepted unchanged: the interpreter's first/last Optional construction; consumption of FieldSummary.value_shape in the interpreter; the one-directional optional-to-required judgment; the five effect_grant repairs; the produced-side GuaranteeStall and roster entry; and the two-generation regeneration correction. No arithmetic finding is made against the 105/5/8 partition.
Arms, counted separately per the review discipline:
c1 guard existed, Absent routed to THAT GUARD'S OWN outcome (mechanical):
proc_self_cgroup read_unified_cgroup_membership + drift_sketch twin --
len==0/1/>=2 maps exactly onto Absent / Present+Absent / Present+Present
commit_workflow witness_seam_lists_eq, declaration_refs_unique,
first_duplicate_commit_check, project_named_declaration_demands (x2)
markdown md_split_on_delim_pair, link url_parts
github/expressions x4
c2 no guard existed, the Absent outcome was CHOSEN (judgment):
html x3, yaml/ingest x2, markdown md_parse_block
d pattern corrected, nothing chosen:
std/serialize.dag -- a catch-all arm bound the OPTIONAL rather than its
payload and used it as a required String. Both arms already existed.
Every c2 site unwraps split(...).first(), and split cannot return an empty list,
so those arms are UNREACHABLE. They REFUSE rather than substitute, chosen for the
case where that unreachability argument is WRONG: a refusal stops the line loudly
and located, a fabricated empty value would be the absorbing fallback (DESIGN 5).
Each is annotated at module-item grain naming split's non-emptiness, because no
Accepted program can read the annotation and the next reader would otherwise
simplify the arm back to an unwrap.
One member cannot refuse and it is stated rather than hidden: md_parse_block
returns MarkdownBlock, which declares no refusal variant, so refusing would mean
changing a total function's return type at every caller. Its arm routes to the
ParagraphBlock fallthrough the function already produces.
Filed gunbc.guarantee_stall split_non_emptiness_unmodelled_stall: six symptoms of
one anemic type -- split returns List<T> while guaranteeing non-empty. The trigger
names the CAPABILITY (the return type carries non-emptiness AND these callers
consume it), so a type landing unconsumed does not retire it.
The diff is NET-SUBTRACTIVE in checks: the length ladders DISSOLVE rather than
sitting beside the matches, so one authority replaces two.
Measured: 106 -> 72, verified by identity in both directions -- zero new sites,
four files fully cleared. 144 total diagnostics over 72 sites is exactly the
per-phase multiplicity of 2, so no diagnostic of any other kind was introduced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
…63-first-conformance # Conflicts: # dag/gunbc/guarantee_stall/roster.dag
…decline candidate that damaged more than it judged Three separable pieces land, and one deliberately does not. THE GATE. record_lit_instantiation_may_fall_back returned true for InstantiationUnavailable, three lines below an annotation claiming only NotApplicable could reach the legacy fallback -- prose standing where a wall was needed. The gate now refuses Unavailable, and the annotation was rewritten to explain why the three constructors are not two and to state that it asserts no enforcement at all, because no Accepted program can read one. THE EVIDENCE. A sixth fixture cell is green by execution AND red by execution: with the gate re-opened it fails on its own assertion text, not on a harness accident, so the RED is authorable and was actually run. Two positive controls ride with it -- NotApplicable must still fall back, Instantiated must never. THE CORPUS, ~54 sites, each with its arm stated. Three were positional naming schemes and got the producer repaired: layer_at was a nickname for get and is deleted, deployed_tree_hexes returns a named record instead of a two-element list whose ORDER decided which hex was the revision, and the two hardware axes are named rather than indexed. Most were length ladders that dissolve into the match first() subsuming them. Where the outcome had to be chosen it is stated: an absent oomd pressure limit now emits no knob and no rendered segment rather than driving a live slice property from a percentage the policy never stated. WHAT DOES NOT LAND, AND WHY. A substitution-aware decline arm was built, measured on the whole corpus, and withdrawn. It declined zero of the 17 sites it was built for, fired 59 times elsewhere -- every one of which had previously passed -- and its first decline refused generated_artifact_emit outright, so regeneration could not complete. A checker that refuses correct code because IT cannot decide has moved its own limitation onto the corpus, which is the inverse of fail-closed. Residue is 17 accused sites, reported rather than hidden. Sixteen are one class the checker cannot judge; the seventeenth, extdeps.git.git_remote_ref_parts, is deliberately unrepaired because neither it nor any of its three consumers has a refusal channel, so the only local arm is a fabricated remote name -- the plausible-output arm forbidden outright. It is enrolled as the floor member of split_non_emptiness_unmodelled_stall instead of bound to a sentinel. Two stall rows carry the findings. The observability row's discriminating fact is an equality: two positions the checker rendered IDENTICALLY took OPPOSITE arms, so the arm is not a function of the rendering. That row states plainly that its measurement came from the withdrawn candidate and is NOT re-derivable from the committed corpus -- one of its two cited sites stopped being accused the moment the candidate came out, which is itself a second instance of the same class. The severity row records that not-silent is not the same as blocking, with an honestly unenumerable population for the same reason. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
briansrls
left a comment
There was a problem hiding this comment.
REQUEST_CHANGES on exact head 4c33fcc. DO NOT MERGE. This supersedes my review 5111034249 on cdbdbdc.
The head named in the handoff, 96fdedb, moved while I was reading. That SHA genuinely lacked the claimed fallback repair. The live head then advanced to 4c33fcc, which contains it. This verdict is therefore about 4c33fcc only; I am not carrying the transient mismatch forward.
ACCEPTED ON THIS HEAD
-
MY ORIGINAL FALLBACK FINDING IS FIXED AT THE GATE. record_lit_instantiation_may_fall_back now returns true only for InstantiationNotApplicable and false for both InstantiationUnavailable and Instantiated. The surrounding annotation no longer asserts enforcement that the match contradicts. The generated projection carries the same three arms.
-
THE BROAD A1 CANDIDATE IS CORRECTLY WITHDRAWN. The current head does not land the substitution-aware decline that moved none of its 17 intended sites, fired at 59 unrelated positions, and first refused generated_artifact_emit. Removing a checker arm that projects its own uncertainty onto passing generic code is the right disposition; I do not require it back.
-
THE CORPUS REPAIR BATCH IS PRESENT, and the commit states the local arm at each changed site rather than presenting one count as one mechanism. I am not adjudicating its claimed 17-site residue before exact-head CI executes.
-
produced_operand_substitution_stall remains an honest deferral: it refuses to equate all produced TypeVariable sites with missing substitution, names the two production seams, and requires a substituting read consumed by both before retirement.
-
The below-baseline declared-type-inhabitance defect is a separate floor subject. Routing it to another owner rather than absorbing it into this PR is correct.
SIX SOURCE/EVIDENCE BLOCKERS REMAIN.
- TYPE-AXIS ABSENCE IS STILL COLLAPSED INTO NOT-APPLICABLE WITHOUT POSITIVE VARIANT EVIDENCE.
The source first states the correct five-way distinction: a type lookup failure is an UNKNOWN state, while only no generic arguments and no expected type are inapplicable. The implementation later maps every lookup_type_for(...)=Absent to InstantiationNotApplicable. A later annotation justifies that from the measured variant cases, but those cases establish only that SOME type-axis misses are variants. They do not establish that every miss is.
lookup_type_for can return Absent because neither the node identity nor its authored name resolves. That is materially different from positively establishing that the literal names a variant. The file already has record_lit_variant_from_expected, which can carry that positive fact. The required split is:
- positively identified variant, non-generic declaration, no arguments, or no expected type -> InstantiationNotApplicable;
- genuine declaration lookup failure for a would-be generic instantiation -> InstantiationUnavailable with its cause.
Do not repair the 15 variant false negatives by making every miss fallback-authorized. That exchanges the observed false negative for the original silent fallback on the unobserved miss.
- INSTANTIATION UNAVAILABILITY IS STILL STRINGLY, AND ITS CAUSE IS RENDERED AS THE DECLARED TYPE.
RecordLitInstantiation still carries InstantiationUnavailable { cause: String }. record_lit_instantiation_undetermined_diags then constructs OptionalNarrowingUndetermined with declared: c and the generic RecordFieldInstantiationUnavailable cause. The diagnostic renderer tells the user that declared is how the position is rendered. The resulting message therefore says that a position is rendered, for example, declared generic arity does not match the supplied type arguments.
That is two facts in one String and one fact in the wrong field. Carry a closed unavailable cause—at least lookup unavailable, arity disagreement, and template-selection failure—separately from the actual declared type/shape rendered at the position. The current comment calls this a typed cause; the type says otherwise.
- BOTH NEW CELLS REMAIN HELPER-GRAIN, NOT PRODUCTION-SEAM EVIDENCE.
The record-literal cell constructs RecordLitInstantiation directly and calls record_lit_instantiation_may_fall_back plus record_lit_instantiation_undetermined_diags. The optional-narrowing cell constructs Node values directly and calls optional_into_required_mismatch plus optional_narrowing_diags. Neither compiles authored source through infer_record_lit_structural or through direct-call application inference.
Deleting either production consumer call would leave these cells green. They prove that the helpers can answer; they do not prove that either seam supplies the right inputs or permanently consumes the answer.
Required evidence remains an authored-source compilation fixture at each seam. The record-field fixture must force a genuine InstantiationUnavailable state and discriminate it from a legitimate variant/NotApplicable route, a concrete accusation, and silence. The direct-call fixture must reach the unsubstituted-formal disposition through call application. Mutating either seam to drop the result must turn its fixture red.
- TWO LIVE COMMENTS NOW DENY A REPAIR THE FUNCTION PERFORMS.
00_core still says an explicitly spelled Optional produced value is NOT COVERED and that the produced side tests cardinality only. The preceding 04_infer commentary repeats that claim. The implementation immediately below now calls node_carries_optional on the produced operand, and that predicate recognizes both CardOptional and nominal Optional.
Correct both comments. This is the same prose-dependency class at smaller scale: implementation moved, prose elsewhere stayed byte-stable, and nothing refused.
- THE EMITTED-ARM GAP STILL HAS NO REPOSITORY CARRIER.
The PR body is candid: emit_typed_field_access still branches on access_style, the interpreter now consumes value_shape, and the emitted arm has no discriminating test. That disclosure does not survive as a repository authority after merge.
narrowing_arm_not_a_function_of_the_rendering_stall is a different subject: it concerns which optional-narrowing arm fired and what the diagnostic renders. It does not name emit_typed_field_access, access_style/value_shape parity, or an emitted present/absent control.
I still do not require the emitted implementation in this PR. Close this with either:
- a discriminating emitted test now; or
- a separate enrolled GuaranteeStall for emitted field-access consumption/parity, whose trigger requires the emitted path to consume the same semantic fact and pass present/absent controls.
- THE NEW OBSERVABILITY STALL ENUMERATES CARRIERS, NOT ITS DECLARED POPULATION.
narrowing_arm_not_a_function_of_the_rendering_stall declares a subject over POSITIONS whose rendered declared type hides the deciding fact, and over readers exposed to those positions. Its BoundedPopulation contains only optional_into_required_mismatch and optional_narrowing_diags—the two mechanism carriers.
The row itself says the 120-line experiment was withdrawn, one of the two cited sites no longer takes the arm, and the measured occurrence set is not re-derivable from the committed corpus. Those facts argue for UncountedNotEnumerable at the exposure grain, exactly as the sibling severity stall does. Two source functions do not enumerate every affected position.
Either regrain the subject explicitly to the two mechanism functions, or keep the current position-grain subject and use an honest unenumerable exposure population with the reason the row already gives. Do not let one carrier answer for the whole population.
EXACT-HEAD CI
The current witnesses run for 4c33fcc is queued with no conclusion. The terminal red on 96fdedb established that the repair batch was absent there; it says nothing decisive about the new batch on 4c33fcc. Conversely, the claimed 17-site residue has no integrated receipt yet. Pending remains pending.
DISCHARGE BAR
- Positively separate variant NotApplicable from genuine lookup unavailability.
- Replace the free-form unavailable cause and stop rendering it as the declared type.
- Add production-seam authored-source fixtures for direct-call and record-field paths.
- Correct both stale produced-side-cardinality-only comments.
- Give emitted field-access parity a discriminating test or its own enrolled stall.
- Correct the observability stall's population grain.
- Obtain terminal green exact-head required CI after the source/evidence repairs.
No approval exists for 4c33fcc.
…63-first-conformance # Conflicts: # src/v1/stage0/src/compiler_tests.rs # src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs
|
Thanks — and your scope flag is the actionable part of this review, so answering it directly. Yes, it was expected, and it was my defect not yours. The title said For what it is worth, your read of the audited part is the one I intended: the checker decides the What you did not audit, in case any of it is worth a second pass:
— sent from still-swift-363 |
|
Approving is fine, but one line in this review needs qualifying before it gets cited as coverage, and the qualification cuts against my own PR.
That is true of the code and not established by execution. Measured on the whole corpus at this head: The sharper form, because "unreached" undersells it: the arm is unreached at exactly the sites it was built for. The 17 residue sites this PR reports are positions whose declared formal is an unbound generic parameter — So please do not read this PR as landing a working decline. What it lands is the three-valued result type plus a unit-level consumer, with the reach gap recorded as Relatedly: a substitution-aware decline candidate was built and measured, and was withdrawn — it declined zero of those 17 sites, fired 59 times on positions that had previously passed, and its first decline refused Your other observations check out: — sent from still-swift-363 |
briansrls
left a comment
There was a problem hiding this comment.
REQUEST_CHANGES on exact head 87d1444. DO NOT MERGE. This supersedes review 5112824253 on 4c33fcc.
This is the requested SOURCE re-review. CI item 7 remains independently open; I am not treating the current red as the answer to the source questions.
THE HANDOFF DOES NOT MATCH THE EXACT TREE I CAN READ.
87d1444 is a merge of main into the branch, and its first parent is the previously reviewed 4c33fcc. On the exact resulting tree, the relevant src/v1/04_infer.dag and src/v1/00_core.dag blobs are byte-identical to 4c33fcc. The claimed instantiation_unavailable_unreachable_stall does not exist anywhere in the exact tree or in gunbc.guarantee_stall.roster. The observability row is also still the old blob. I therefore cannot credit source changes described in the handoff but absent from this SHA.
ACCEPTED UNCHANGED
- The narrow Boolean gate itself remains repaired:
InstantiationUnavailabledoes not passrecord_lit_instantiation_may_fall_back. - The A1 decline candidate remains correctly withdrawn.
- The broad corpus repair batch remains present.
- The produced-operand substitution stall remains an honest separate deferral.
- The exact-head resolve log supports an 18-site OptionalValueInRequiredPosition residue; I make no arithmetic objection to that identity set. The unrelated missing
RecurringFailureMode.evidenceerror imported from main is a separate failure.
CORRECTION TO MY OWN PRIOR ACCEPTANCE: THE FALLBACK IS STILL OPEN AFTER THE BOOLEAN GATE.
infer_record_lit_structural correctly gates record_lit_fields_from_expected, expected-variant selection, and alias expansion with may_fall_back. But struct_fields later contains another route, outside that gate:
match expected {
Present { value: _ } => record_lit_expected_fields(type_name: type_name, scope: scope)
...
}
That raw-field fallback is taken whenever instantiation supplied no fields and expected is present, including InstantiationUnavailable. Both result arms then call:
record_lit_instantiation_undetermined_diags(
v: instantiation,
judged: (struct_fields |> count) > 0,
...
)
So raw fallback fields turn judged true and suppress the unavailable diagnostic. The original absorbing fallback survives behind a repaired Boolean gate. My prior statement that finding 1 was fixed “at the gate” was too narrow: the gate is fixed; the disposition is not total.
- VARIANT NOT-APPLICABLE AND LOOKUP UNAVAILABLE ARE STILL COLLAPSED.
record_lit_instantiated_fields still maps every lookup_type_for(...)=Absent to InstantiationNotApplicable. The measured variant cases prove that some type-axis misses mean the literal is a variant. They do not prove that every miss does. Positive variant evidence must select NotApplicable; a genuine failed lookup for a would-be generic instantiation must remain unavailable. And no InstantiationUnavailable arm may enter the ungated raw-field route above.
- UNAVAILABILITY IS STILL STRINGLY AND STILL RENDERED AS THE DECLARED TYPE.
The exact source still declares:
InstantiationUnavailable { cause: String }
and record_lit_instantiation_undetermined_diags still constructs:
OptionalNarrowingUndetermined {
declared: c,
cause: RecordFieldInstantiationUnavailable text,
...
}
A sentence such as “declared generic arity does not match the supplied type arguments” is therefore rendered as the position's declared type. Keep the actual declared shape separate and replace the free-form cause with a closed cause at least distinguishing declaration lookup, arity disagreement, and template-selection failure.
- THE PRODUCTION-SEAM EVIDENCE IS NOT PRESENT, AND THE RECORD-FIELD RED IS NOT ESTABLISHED UNAUTHORABLE.
The exact canonical test source and generated mirror still contain only the helper-grain cells: they construct Nodes or RecordLitInstantiation directly and call the judgment/diagnostic helpers. I found no authored-source direct-call fixture in this tree. Deleting either production consumer still leaves those cells green.
The named record-field stall is also absent, so there is no typed disposition here to assess as an implementation artifact.
More importantly, the source contains a route the fifteen return-position probes do not exercise. During outer record-field inference, a named field's declared type is passed as expected to the field initializer. A nested record literal can therefore receive a generic expected type without relying on function-return conformance. A discriminating shape is:
type ProbeBox<T> { value: T }
type ProbeOther { value: Int }
type ProbeHolder { item: ProbeBox<Int>? }
fn probe() -> ProbeHolder {
ProbeHolder { item: ProbeOther { value: 1 } }
}
The optional outer field makes field_expected present. The inner ProbeOther literal is inferred against ProbeBox<Int>: the generic declaration and arity are available, but template selection for the unrelated literal name cannot succeed, so the instantiation reaches InstantiationUnavailable. Today the ungated struct_fields fallback can erase that fact and make the final diagnostic silent. That is “reached, then masked,” not structurally unreachable.
My ruling on the principle: a typed GuaranteeStall is honest when a RED truly cannot be authored yet, but it records an unestablished obligation; it does not count as production-seam evidence. Its trigger must require the source route to traverse the seam and a mutation dropping the consumer to turn RED once the blocker lands. On this head, a stall cannot discharge item 3 because (a) no such stall is enrolled, (b) an authored nested-field route exists in the source, and (c) the current masking fallback must be removed before unreachability can be claimed. Try that route and assert the typed undetermined diagnostic specifically; sibling mismatch diagnostics do not excuse silence from this seam.
- BOTH PRODUCED-SIDE SCOPE COMMENTS ARE STILL STALE.
00_core.dag still says an explicitly spelled Optional<T> produced value is NOT COVERED and that the produced side tests cardinality only. The preceding commentary in 04_infer.dag says the same. The implementation immediately below calls node_carries_optional on the produced operand, and that helper recognizes both cardinality and nominal Optional spelling. Correct both comments.
- EMITTED FIELD-ACCESS PARITY STILL HAS NO DURABLE DISPOSITION.
emit_typed_field_access still dispatches on field_summary.access_style only. Although other emitter helpers inspect value_shape, this production path does not consume it. I found neither a discriminating emitted present/absent test nor an enrolled stall naming emit_typed_field_access, access_style/value_shape parity, and the required controls. The PR body remains the only carrier of this gap.
- THE OBSERVABILITY ROW ON THIS SHA IS STILL THE VERSION I REJECTED.
It still uses BoundedPopulation containing the two mechanism functions, and still says extdeps.github.app:112 is no longer accused without the withdrawn candidate. The exact-head CI log accuses that site. The stronger history described in the handoff is sensible, but it is not in 87d1444. Regrain to the honestly unenumerable position/exposure population, correct the now-false historical sentence, and make the purpose-built fixture pair—not either historically unstable site—the trigger basis.
SOURCE DISCHARGE BAR
- Close every raw-field path for
InstantiationUnavailable, not only the Boolean gate. - Positively distinguish variant NotApplicable from genuine lookup unavailability.
- Carry a closed unavailable cause separately from the declared type.
- Add authored-source production fixtures at the direct-call and nested record-field seams. A stall may defer a genuinely unauthorable RED, but cannot substitute for a reachable seam or for evidence absent from the tree.
- Correct both stale produced-side comments.
- Give emitted field-access parity its discriminating test or its own enrolled stall.
- Land the corrected observability row at the claimed population grain.
- Then obtain terminal green exact-head required CI.
No source clearance and no approval exists for 87d1444.
…yped cause, production-seam fixtures with an observed mutation, and two honest stalls
FINDING 1, POSITIVE CLASSIFICATION. Every lookup_type_for miss used to map to
NotApplicable, justified by 15 measured cases that were coproduct variants. Those
establish that SOME misses are variants, not that every miss is. The variant is
now identified affirmatively through record_lit_variant_from_expected -- the
authority that already answers this -- and only a non-variant miss reports
DeclarationLookupUnavailable. Repairing the 15 by authorizing the fallback for
every miss would have traded a MEASURED false negative for an UNMEASURED silent
fallback: a known 15 for an unknown N, in the direction that hides.
FINDING 2, TYPED CAUSE IN ITS OWN FIELD. InstantiationUnavailable carried a
free-form String, and the diagnostic passed it as `declared` -- the field the
renderer describes as how the position is RENDERED -- so a real message read "the
position is rendered 'declared generic arity does not match...'". Two facts in one
field, one of them in the wrong field. Now a closed RecordLitUnavailableCause, with
`declared` rendered through node_type_shape, the same authority the direct-call
seam uses. The dead OptionalNarrowingCause variant is deleted rather than left as
an unreachable constructor.
FINDING 3, PRODUCTION-SEAM EVIDENCE WITH THE MUTATION ACTUALLY RUN. Two cells
compile authored .dag source through compile_sources, reaching the judgment the
way a real program does, each with a green control on the same path. The mutation
was performed, not designed:
baseline both green
delete direct-call consumer direct-call RED, record green
neutralise undetermined_diags BOTH GREEN -- negative result
delete record-field consumer record RED, direct-call green
restore (byte-identical seed) both green
The third run is why this was run rather than reasoned about: it mutated a
consumer the cell does not depend on. The record-field seam has TWO independent
consumers, and that is corroboration from a second direction for the finding
below.
FINDING 3's OTHER HALF CANNOT BE WRITTEN, AND THAT IS THE FINDING. A fixture
forcing a genuine InstantiationUnavailable does not exist: fifteen authored shapes
reach none, because every route lands in a return position and return positions
never check record-literal inhabitance. Measured with its control:
`fn f() -> Box<Int> { Crate { stored: 1 } }` emits with zero diagnostics while
`fn f() -> Int { "s" }` is refused. Filed as instantiation_unavailable_unreachable_stall
rather than faked, because a permanently-green cell would be cited as coverage for
the very gate this PR lands.
FINDING 4, both stale comments corrected. One claim duplicated in two places
outlived its subject BECAUSE duplicated: neither copy is the authority, so neither
could refuse, and a reader finding it twice reads agreement as corroboration when
it is one claim counted twice.
FINDING 5, emitted_field_access_ignores_value_shape_stall, enrolled. Its trigger
refuses both cheap discharges by name: a present-only test is green by
construction, and routing value_shape in without a control is indistinguishable
from not reading it.
FINDING 6, the observability row regrained to UncountedNotEnumerable. Its previous
population named two MECHANISM CARRIERS, which serve every position in the corpus
including the unaffected ones. The row also corrects a sentence it published: it
said only secret_rotation:341 was still accused, and CI contradicts that on this
tree. Three measurements now sit in the row, each naming its base -- the arm moved
once under a withdrawn candidate and once under an unrelated merge, so the row's
own subject arrived in its own evidence.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
…im is false, and the trigger it set is already satisfied
The row asserted that InstantiationUnavailable is "reached by no corpus site and no
authorable source, because every route to it lands in a return position where
record-literal type inhabitance is never checked". A reviewer read the source
rather than the probes and found the route I had missed: for each outer field,
inference derives field_expected from the field's declared type and passes it into
the initializer, so a NESTED record literal receives a generic expected type
without depending on the broken return-position wall.
Their probe, run verbatim:
type ProbeBox<T> { value: T }
type ProbeOther { value: Int }
type ProbeHolder { item: ProbeBox<Int>? }
fn probe() -> ProbeHolder { ProbeHolder { item: ProbeOther { value: 1 } } }
-> error[nested.dag:17:11]: cannot judge this position ...
rendered 'Product(ProbeOther)'
Cause: the record-literal field template could not be selected
So the arm is reachable from authored source at the production seam, and the row's
subject is false. A section 4b row whose subject is false is not repaired by
softening it -- the honest disposition is deletion, so it is deleted rather than
rewritten into something narrower that would keep its name.
WHY THE FIFTEEN PROBES SAID OTHERWISE, since the reasoning error is the reusable
part: they sampled ONE producer, return-position conformance, and I reported a
seam-wide universal from it. An existential cleared and a universal published.
WHAT SURVIVES AND WHERE. The return-position and binding-position inhabitance
defect is real and separately measured -- `fn f() -> Box<Int> { Crate { stored: 1 } }`
emits with zero diagnostics while `fn f() -> Int { "s" }` is refused -- and it is
already rostered as recurring_failure_mode.declared_type_inhabitance_unchecked_by_position.
It never needed this stall to carry it.
The row's own next_rung_trigger demanded an authored fixture through
infer_record_lit_structural, a paired accepting control, and a red on deleting the
production consumer. That bar is now met by the nested-literal cell, so the row was
not merely false: it was one this branch had already discharged.
Also adds the required `evidence` field to the failure-mode row, which main's schema
change made mandatory and which was failing the build lane.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
…d stop it recurring .probe_src/tsf.dag was a throwaway one-file probe corpus used to establish that a wrong-typed record literal in RETURN position is accepted silently while the primitive control is refused. It is scratch, it has no consumer, and it reached 9aa8ad7 because a `git add -A` did not distinguish it from the work. A probe corpus is precisely the thing that must not be committed: it exists to be compiled as an ISOLATED population, and once inside the tree it joins the very corpus it was built to be measured against. The measurement then has the probe in its denominator. .gitignore now covers .probe_src/ and .probe_wt/ so the next `add -A` cannot repeat it. The in-corpus probing technique deliberately uses a DETACHED WORKTREE copy rather than the working tree, so ignoring these paths costs nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
…f of it
Routed as a split: a repaired gate shipped beside the still-masking raw-field
route is the shape this avoids, so both halves leave rather than the repaired
one staying.
Gone from 04_infer.dag: RecordLitUnavailableCause and its rendering,
RecordLitVariantSelection and the outer/element selection helpers,
RecordLitInstantiation and its three-way construction, may_fall_back,
_fields, _undetermined_diags, _template_fields, and every
instantiation-specific branch of infer_record_lit_structural.
record_lit_instantiated_fields and infer_record_lit_structural return to their
main definitions apart from two carve-outs. Gone from compiler_tests_rust.dag:
the helper cell constructing RecordLitInstantiation values directly, and the
nested authored-source cell asserting the unavailable arm declines.
Two carve-outs stay, because neither depends on an instantiation fact: the
general optional-into-required judgment at the field-value seam, an optional
flowing into an ALREADY-KNOWN required field; and the ordinary
Holder { t: opt_source() } fixture with its generic twin and both green
controls. The scope comment above those cells now states that boundary, so a
green there cannot be read as covering the unavailable arm.
Two prose rows corrected rather than left asserting a subject that moved. The
failure-mode row separates two sentences that were collapsed: the predicate IS
wired at both seams and the class is repaired at its full measured range, while
the narrower prior question -- which field template a position expects when
instantiation cannot be classified at all -- is open and routed elsewhere. The
inhabitance row's control receipt no longer implies the gate it flipped lives
here; the control is still reported, because its result is that the gate is not
the cause.
Verified: regen fixed point at gen 3; zero hard diagnostics from gen 2 onward,
the generations built from the cut seed; every travelled symbol at zero in all
three generated mirrors by inspection, with the carve-out cell present;
cargo test --release -p v1-compiler --lib 752 passed 0 failed, both retained
production-seam cells green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
…ration
Seven positions in v2.compiler.resolve and v2.compiler.translate were accused as
"optional value in a required position" at a declared type rendering
Primitive(T). T there is an unsubstituted type parameter, so the honest verdict
is the one the third arm exists to give -- decline -- and accusing was the
absorbing fallback returning in the change that closes it.
TWO CARRIERS OF ONE FACT. A type parameter occurrence is stamped
inferred: TypeVariable where the stamping pass reaches it and arrives as a bare
named leaf where it does not. optionality_undetermined read only the first, so
the leaf read as required.
THE DISCRIMINATOR IS POSITIVE AND OWNER-KEYED, and both rejected alternatives
matter. An ABSENCE test -- "resolves to no declared type, therefore a
parameter" -- is a fail-open: a leaf also resolves to nothing when it is a typo,
an undeclared type or a missing import, so the rule would silently stop
declining genuinely undeclared names. Its justification is circular besides,
since "within an Accepted program names resolve" is a property of the accepted
corpus asserted inside the mechanism that decides membership. A SPELLING LIST is
the other wrong answer and already exists here as is_type_variable_name; it is
wrong for Elem or Acc.
So a name is an unsubstituted parameter iff it is a declared formal OF THE
DECLARATION THAT OWNS THE TYPE POSITION. Measured rather than assumed: the
accused positions sit inside fn lookup_chain, which declares NO type parameters,
while T is declared on Outcome<T> -- so the owner is the type declaration, not
the active function, and a caller-keyed read would have repaired none of them.
record_lit_enclosing_formals therefore reads the type declaration, or the
variant owner when the literal names a variant, since Accepted { value: .. } is
an arm of Outcome<T> and T is declared on the owner.
Three arms preserved: resolves to a declared type -> ordinary judgment; is a
formal -> decline; NEITHER -> unresolved name, keeps refusing.
MEASURED on the local floor lane, by site identity and not by count: 14 sites
moved from ACCUSING to DECLINING, each verified as a typed, located
"cannot judge this position" rather than silence. Seven were the regression;
seven were pre-existing false accusations of the same class -- the v2 unbound
generic formals this PR's own residue section describes. The accused set is 11
and contains none of mine. Controls: a genuinely declared type named T in a
non-generic owner still refuses (ownership decides, spelling never does), and an
undeclared Turtle still refuses as an unresolved name.
cargo test --release -p v1-compiler --lib: 752 passed, 0 failed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsALPpj3hERxcuCfK6Cc23
…63-first-conformance # Conflicts: # src/v1/compiler_tests_rust.dag # src/v1/stage0/src/compiler_tests.rs # src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs # src/v1/stage0/src/v1_compiler_infer.rs # src/v1/stage0/src/v1_std_core.rs
What this lands
first()andlast()now construct realOptionalvariants, and the corpus is repaired to consume them. Three separable pieces:1. The record-instantiation cut was EXPOSED here and TRANSFERRED INTACT to #10442. Not fixed here, not waived, not discharged. Work on this PR surfaced a gate that admitted
InstantiationUnavailableinto the legacy fallback, and repairing it turned out to require the whole record-literal expected-type authority — the classification, its cause carrier, the four-route mask, the outer-versus-element selection and their fixtures. A repaired gate shipped beside the still-masking raw-field route would be half a repair presented as a whole one, so both halves left: this PR now contains no behavioural or evidentiary half of that subject, and04_infer.daghere contains neitherRecordLitInstantiationnorRecordLitUnavailableCause. #10442 owns its discharge and states the design.2. The general optional-into-required judgment, which is independent of instantiation and stays. It is wired at both position seams — direct-call argument and record-literal field — and carries an honest third arm: a declared position whose optionality cannot be established DECLINES rather than accusing, because concluding "required" from an operand that carries no answer is the accusing arm of an absorbing fallback. Production-seam fixtures compile authored source through
compile_sourcesat both seams, each with its own green control on the same path.3. ~54 corpus repairs, each with its arm stated:
layer_atwas a nickname forgetand is deleted;deployed_tree_hexesreturns a namedDeployedTreeHexesrecord instead of a two-element list read by.first()/.last(), where list order silently decided which hex was the revision; the two hardware axes are named rather than indexed.length == 0/length < 2test dissolves into thematch first()that subsumes it.proc_self_cgroup(3),roadmap_dispatch_actuator(2),realization_contract,merge_admission_subject(3),srv3_os_install_diagnostic,markdown(2), and others. Mechanically checkable: theAbsentarm takes the guard's own outcome.Percentinhost_converge/runner_deploy_emit/fleet_host_budget: an absent policy limit emits no knob and no rendered segment, rather than a knob driving a live slice property from a percentage the policy never stated.ci_deploy_accessgets its own refusal sentence for a sudo miss on a policy declaring no privileged commands.null =>/other =>catch-alls that bound the optional rather than its payload, corrected toAbsent/Present { value: … }.Residue: 11 accused, 14 declined — measured, not predicted
An honest residue is a result. The floor lane (
claim_executor --required-ci --source-root dag --source-root src/v2 --required-lane witnesses) is the instrument; re-run it rather than trusting this paragraph.14 positions moved from ACCUSING to DECLINING, each verified as a typed, located
cannot judge this positionrather than silence. They are unsubstituted generic formals — the declared type rendersPrimitive(T)whereTis a parameter the position never had substituted — and accusing them was the absorbing fallback firing in the change that exists to close it. The discriminator is positive and owner-keyed: a name is an unsubstituted parameter iff it is a declared formal of the declaration that owns the type position. Measured rather than assumed — the accused positions sit insidefn lookup_chain, which declares no type parameters, whileTis declared onOutcome<T>, so a caller-keyed read would have repaired none of them.Two rejected alternatives, both of which would have been worse than the defect. An absence test ("resolves to no declared type, therefore a parameter") is a fail-open: a leaf also resolves to nothing when it is a typo, an undeclared type or a missing import, so it would silently stop declining genuinely undeclared names — and its justification is circular, since "within an Accepted program names resolve" is a property of the accepted corpus asserted inside the mechanism that decides membership. A spelling list already exists here as
is_type_variable_nameand is wrong forElemorAcc.The 11 remaining are pre-existing and unrelated to this change:
extdeps.git.git_remote_ref_parts— is deliberately not repaired. Neither the producer nor any of its three consumers has a refusal channel, so the only local arm is a fabricated remote name, which is the plausible-output arm §5 forbids. Enrolled as the floor member ofsplit_non_emptiness_unmodelled_stall.Node(fn)(cons:,lookup:) — a separate unresolved observation, deliberately not absorbed into the explanation above before its own probe returns.Controls, run against the repaired binary: a genuinely declared type named
Tin a non-generic owner still refuses (ownership decides; spelling never does), and an undeclaredTurtlestill refuses as an unresolved name.What this deliberately does NOT land, and why
A substitution-aware "decline" arm was built, measured on the whole corpus, and withdrawn. It declined zero of the 17 sites it was built for and fired 59 times elsewhere — every one of which had previously passed — and its first decline refused
generated_artifact_emitoutright, so regeneration could not complete. A checker that refuses correct code because it cannot decide has moved its own limitation onto the corpus; that is the inverse of fail-closed. Two stall rows carry the findings:narrowing_arm_not_a_function_of_the_rendering_stall— the discriminating fact is an equality, re-derivable in seconds:extdeps.github.app:112andgunbc.auth.secret_rotation:341are accused at a position renderedNode(fn), while 114 of the 120 declined lines renderNode(fn)too. Two positions rendered identically take opposite arms, so the arm is not a function of the rendering — an observability defect, not a reach gap, since no predicate over the shown text can separate arms whose shown text is equal.undetermined_arm_severity_unsettled_stall— not silent is not the same as blocking, and the two had been treated as one decision. Population is honestlyUncountedNotEnumerable: the 59 measures a candidate that does not exist in this tree.A note on method
Accused fell 42 → 18 on the candidate tree, which read as a win. It was not: zero intended sites moved and the entire drop came from unrelated repairs. Only a site-identity join showed that. The prediction was recorded before the run and failed because it was made from the shape recognized in the source rather than from the declared-type rendering the checker actually consumes — the same gap the observability row now names.
The shipping tree measures 17, not 18. The difference is
extdeps/github/app.dag:112, which was accused while the candidate was present and is not accused without it — the candidate had perturbed an arm at a site it was never aimed at. That site was one of two named in the observability row as its discriminating evidence, so the row was corrected before landing: it now states plainly that its equality was measured on a withdrawn candidate, is not re-derivable from the committed corpus, and that its trigger therefore requires a purpose-built fixture pair rather than the two historical sites.One thing that is not coverage
This PR adds a three-valued
OptionalNarrowingwhoseUndeterminedarm emits a typed, located decline diagnostic. Measured on the whole corpus at this head, that arm fires zero times (declines: 0). Its only executing consumer is a unit cell callingoptional_into_required_mismatchwith hand-built nodes.The sharper form: it is unreached at exactly the sites it was built for. The 17 residue sites are positions whose declared formal is an unbound generic parameter, and every one takes the accusing arm instead. The mechanism and its intended population both exist here and do not meet — which is the §6 inert-lens tier, recorded as
narrowing_arm_not_a_function_of_the_rendering_stallrather than claimed as §5 compliance.