Skip to content

Native diagnostics through the result boundary: the verdict carries the located chain, ambiguity names its lookup class and competing declarations - #11965

Merged
briansrls merged 2 commits into
mainfrom
session/swift-newt-233
Sep 21, 2026
Merged

briansrls merged 2 commits into
mainfrom
session/swift-newt-233

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Subject

The row the native test route persists for a refused identity, and the line the operator reads beside it. Before this change a refused row was {identity, verdict: NativeTestRefused { stage, reason }} — one symbol copied out of a located, typed cause the failing stage had already computed. The persisted output, the admission authority and the operator command all read that symbol; none could say where.

Implementation boundary

  • v2.compiler.native_test_vocabulary NativeTestVerdict — the refused arm carries the stage's NonEmptyDiagnostics unfolded (the same stance native_module_resolve_verdict already takes at the census grain). The reason every roster keys on is derived at the consumer, so admission verdicts do not change.
  • v2.std.diagnostic diagnostics_fatal / diagnostics_fatal_reason — "the fatal is the last diagnostic" is created by rejected_with_pending and was spelled twice by consumers (cli_fatal_diagnostic, native_test_fatal_reason). Hoisted to the home that creates the ordering; both consumer spellings deleted.
  • v2.compiler.resolve AmbiguousLookupClass — SymbolIndexAtomAmbiguous was a bare variant standing where the lookup had answered LexicalAmbiguous { candidates }. It now carries which lookup was ambiguous (lexical chain / global bare / qualified head shadowing an absolute hit), and ambiguous_symbol_diagnostics emits the class row and one resolve_ambiguous_competing_declaration row per binder in front of the unchanged fatal resolve_reason_ambiguous_symbol. "Where available" is honest: the global-bare index collapsed its binders at fill time, so that class names itself and carries zero candidates rather than a fabricated list.
  • v2.std.diagnostic Locus gains DeclarationLocus { declaration: QualifiedName } — DESIGN §3's cite-the-symbol form. The corpus faked it (resolve_module_not_found puts a module QN into a Textual.file); a competing binder is now named by identity rather than dragged in as its declaration node (which is its whole body, and would size every persisted row by the binder). All exhaustive Locus matches extended; the frontier probe's serializable projection gains CauseLocationDeclaration.
  • v2.std.lens_verdict — a NodeLocus renders as its occurrence identity (<node occurrence #N>) instead of <node locus, no file>; new lens_verdict_diagnostics_located_chain_text renders each link located.
  • Operator command — the emitted eval driver (v1.compiler.emit_rust emit_source_root_eval_driver_main_rs) prints one [native-verdict] stderr line per non-passing row beside the JSON it persists, rendered by gunbc.witness_v2_native_route native_route_member_row_text; the seed runner already relays that stderr. Declared on the driver contract in std.compiler_entry. The stage0 mirror was regenerated by the documented recipe (claim_executor --required-regen, first pass) and the delta is exactly the template insertion — the two other drifts the regen reports (std_integer.rs, gunbc_cli_dispatch_surface.rs) pre-exist on main and are not installed here.
  • Route-minted entry refusals (declaration not found / not an arrow) are located at DeclarationLocus(<module>.<decl>); native_test_refused_located is the only reason-first constructor and it demands a locus.

The fatal pick, and why the specimen's fatal LOOKED like an advisory (manager direction, 2026-09-21)

The built CLI's specimen read infer_grounding_not_derived ×35 | FATAL AT <node locus, no file>. The last-link pick (#11507) was choosing the refusing link — but that link was spelled with the advisory's name: canonical_grounding_from_inferred_facts (04_infer) and translate's grounding gate both refused with infer_grounding_not_derived, the same symbol infer carries as a frontier advisory on its Accepted path (review 46789 did that on purpose). One name, two contracts — a §3 meaning fork — so no reader of the flat chain could tell the refusal from the rows riding in front of it. eval's gate had already drawn the line (eval_rejected_grounding_not_derived).

  • Root: canonical_grounding_from_inferred_facts now refuses with infer_grounding_demanded_not_derived (the earliest unjustified boundary; coercion and translate's coerce fold reach it).
  • Translate's own gate refuses with translate_rejected_grounding_not_derived, mirroring eval.
  • The CLI's CliRunRefused.reason was d.head.reason — the first pending advisory (the grammar-global overlap residue, identical on every broken entry). It is now diagnostics_fatal_reason.
  • Both new reasons are owned at FatalGrain in compile_door_cause_ownership (the former population was counted under the advisory's row).

Controls: v2.test.cli.v2_native_cli a_trailing_token_refusal_names_the_refusing_link_as_its_reason_holds (supplied two-file ingest through the real v2_cli_run; reason == parse_g0_tokens_remain, with the overlap-residue advisory in front) and …_locates_its_fatal_in_the_file_holds (FATAL AT <compilation unit> bytes …); translate_underived_refusal_test translate_refused_canonical_grounding asserts the fatal is translate's own and is not the advisory; infer_self_grounding_wall_test / ingest_bridge_test retargeted from the head to the fatal with the root's new name. The claims that assert d.head.reason == infer_grounding_not_derived on paths where a real infer runs in front (cross-language compile, compile Eval mode) stay green — there the head really is the advisory.

Positive control

v2.test.claim.native_route.native_refusal_detail — six claims over supplied sources reaching the real native_test_context_from_ingest → native_lane_module_resolution fold (the producer of the persisted rows), warm producer ndp_resolutions enrolled in floor_cross_claim_pure_producers_warm:

  • unbound_reference_row_locates_its_source_occurrence_holds — fatal reason + a minted node occurrence
  • shadowed_reference_row_names_lookup_class_and_both_competing_declarations_holds — lexical class + both binders on DeclarationLocus, exactly two
  • global_bare_collision_row_names_its_lookup_class_without_fabricating_candidates_holds
  • operator_line_names_subject_stage_cause_and_competing_declaration_holds — the consumer-level control over native_route_member_row_text
  • operator_line_renders_the_source_occurrence_holds
  • clean_module_resolves_holds — the denominator

10/10 PASS locally (these six plus the four touched existing witnesses) via claim_batch; the fatal-pick claims and every retargeted witness PASS at the current head (see the commit list).

Failure / mutation controls (executed)

  • Mutation A — ambiguous_symbol_diagnostics returns the fatal singleton (drops class + candidates): the three ambiguity claims go FAIL, everything else PASS.
  • Mutation B — native_lane_module_resolution folds the verdict back to reason with a port locus: all five detail claims go FAIL; clean_module_resolves_holds and the pre-existing walls PASS.
  • Under both mutations namespace_only_policy_chain_shadow_ambiguous_refuses_holds and shared_root_validation_refusal_stays_at_prepare_for_each_identity stay green — the measurement that the pre-existing walls read only the reason and never covered the detail.

Exact-head handback

the branch head (final sha in the last commit). Interpreted evidence is at this head; the [native-verdict] driver line is prepared, not qualified — the native lane is a declared rung drop until Pkg1 restores native artifact production, and a run of the built driver is the qualification this PR cannot perform.

Disposition of what it replaces

  • NativeTestRefused.reason deleted; native_test_fatal_reason, cli_fatal_diagnostic deleted in favour of v2.std.diagnostic diagnostics_fatal(_reason).
  • native_test_observation_refused now takes diagnostics; supplied-verdict tests use native_test_refused_located.
  • The class is rostered: gunbc.recurring_failure_mode.located_cause_folded_to_its_reason_at_a_result_boundary (receipts: gunbc#11507's CLI, this route; next-rung trigger named).

Named remainders

  1. NativeTestFileRefusal (Context-stage rows) keeps head_reason/fatal_reason only, although front-end diagnostics carry the most actionable loci (Textual file + byte range). Widening it is a change to the seed host runner's TSV decoder as well; Context rows are located at Textual { file, WholeFile } for now.
  2. A node-anchored fatal renders its occurrence, not a file. Live specimen (manager, from the built native CLI at 7d3a2578): FATAL AT <node locus, no file> with infer_grounding_not_derived ×35 — after this change it reads FATAL AT <node occurrence #N> and the chain renders each link's occurrence. A file is not derivable at that boundary: the parse SpanIndex (occurrence → file+extent) is dropped inside program_assembly before normalize, and occurrence ids are allocated per file from 0, so an id cannot be joined to a file inside a grafted tree. Attributing by searching the tree for an equal node would be a plausible guess, not a fact (§5). Trigger: carry the per-file SpanIndex (or ingest-unique occurrence ids via OccurrenceIdAllocator's min-next seeding) through normalize/resolve on the context, so the renderer's join is a lookup. That is a program_assembly change and its own package.
  3. resolve_module_not_found's QN-as-Textual.file encoding is left in place; it now has a home to migrate to.

Ownership vs. the original session's PRs

Checked against #11919 (native-route universe selection) and #11952 (eval driver verb/plan surface): no hunk overlap. All three touch src/v1/05_emit_rust.dag in different regions; the one-line stage0 mirror will need a regen round after whichever lands second.

🤖 Generated with Claude Code

…he failing stage's located chain, ambiguity names its lookup class and competing declarations

The native test route's refused verdict was `NativeTestRefused { stage, reason: Symbol }`,
filled everywhere by folding the failing stage's NonEmptyDiagnostics to its last reason. The
stage had computed a located, typed cause (v2.std.diagnostic Diagnostic: reason, locus,
correction) and the boundary kept one field of it, so every persisted population row and every
line the operator read said WHAT refused and could not say WHERE. One seam earlier the resolver
did the same to itself: `SymbolIndexAtomAmbiguous` was a bare variant standing where the lookup
had answered `LexicalAmbiguous { candidates }`, so an ambiguous reference reported one symbol
while the resolver had known which lookup was ambiguous and which declarations competed.

Subject: the NativeRouteMemberRow the native lane persists and the operator line beside it.
Implementation boundary: v2.compiler.native_test_vocabulary NativeTestVerdict (refused arm now
carries the stage's NonEmptyDiagnostics; reason is DERIVED at the consumer through the new
v2.std.diagnostic diagnostics_fatal_reason, which also dissolves the two per-consumer spellings of
"the fatal is the last" in the CLI and the compile fold); v2.compiler.resolve
AmbiguousLookupClass (lexical chain with candidates / global bare / qualified head shadowing an
absolute hit) carried as class + competing-declaration diagnostics in front of the fatal;
v2.std.diagnostic Locus gains DeclarationLocus { declaration: QualifiedName } -- DESIGN section
3's cite-the-symbol form, which the corpus had faked by putting a module QN into a Textual file
-- so a competing binder is named by identity rather than dragged in as its body node; the
emitted eval driver prints one `[native-verdict]` stderr line per non-passing row, rendered by
gunbc.witness_v2_native_route native_route_member_row_text; lens_verdict renders a NodeLocus as
its occurrence identity instead of "<node locus, no file>".

Positive control and failure controls: v2.test.claim.native_route.native_refusal_detail, six
claims over supplied sources through the real native_test_context_from_ingest and
native_lane_module_resolution fold (warm producer enrolled). Mutation A (resolver folds the
ambiguity back to a singleton) reds exactly the three ambiguity claims; mutation B (the verdict
boundary folds back to the reason) reds all five detail claims; the pre-existing reason-only
walls stay green under both, which is the measurement that they never covered the detail.

Disposition: `native_test_fatal_reason` and `cli_fatal_diagnostic` deleted in favour of the std
fold; the reason-only verdict arm has no constructor left (native_test_refused_located demands a
locus). Declared remainder: NativeTestFileRefusal (Context-stage rows) keeps head/fatal reasons
because the seed host runner's TSV decoder reads that shape; and a node-anchored fatal renders
its occurrence but not a file, because the parse SpanIndex is dropped before normalize and
occurrence ids are per-file -- rostered in the new recurring_failure_mode row.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot gunbai-bot Bot changed the title Pkg2: native diagnostics through result boundary Native diagnostics through the result boundary: the verdict carries the located chain, ambiguity names its lookup class and competing declarations Sep 21, 2026
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review September 21, 2026 14:21
… wearing the advisory's name

The built CLI's specimen read `infer_grounding_not_derived x35 | FATAL AT <node locus, no file>`.
The last-link pick was choosing the refusing link -- but that link was spelled with the ADVISORY's
symbol: canonical_grounding_from_inferred_facts (v2.compiler.infer) and translate's grounding
gate both REFUSED with `infer_grounding_not_derived`, the symbol infer carries as a frontier
advisory on its Accepted path (review 46789 routed the gate through the shared fn so it would).
One name, two contracts (DESIGN section 3), so no reader of the chain could tell the refusal from
the rows in front of it; eval's gate had already drawn the line with its own reason.

- root: canonical_grounding_from_inferred_facts refuses with infer_grounding_demanded_not_derived
  (coercion and translate's coerce fold reach it) -- the earliest unjustified boundary;
- translate's gate refuses with translate_rejected_grounding_not_derived, mirroring eval;
- v2.cli.compile_cli's CliRunRefused.reason was d.head.reason -- the first pending advisory, the
  grammar-global overlap residue on every broken entry -- and is now the fatal; its chain renders
  each link located (lens_verdict_diagnostics_located_chain_text; cli_diagnostic_chain deleted);
- both new reasons owned at FatalGrain in compile_door_cause_ownership.

Controls: v2.test.cli.v2_native_cli runs the real v2_cli_run over a supplied two-file ingest whose
second file leaves tokens after its module -- reason == parse_g0_tokens_remain with the overlap
advisory in front, detail `FATAL AT <compilation unit> bytes ...` (warm producer enrolled);
translate_underived_refusal_test asserts the fatal is translate's own AND is not the advisory;
infer_self_grounding_wall_test and ingest_bridge_test retarget from head to fatal under the root's
name. Head-reason claims on paths where a real infer runs in front stay green -- there the head is
the advisory. Recurring-failure-mode row extended with this receipt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@briansrls
briansrls added this pull request to the merge queue Sep 21, 2026
@gunbai-bot

gunbai-bot Bot commented Sep 21, 2026

Copy link
Copy Markdown
Contributor Author

Side-chat review approved merging this head (67ac2bb) as an increment. That approval is under the operator's delegation. Pkg2 stays OPEN for native qualification: the emitted driver must preserve a located refusal, an advisory before the fatal, and an ambiguity naming the competing declarations. Note: the witness-floor job 106378500935 declined its phases (the dependency pool did not resolve), so its green is not counted as coverage for this change.

Merged via the queue into main with commit 619dab7 Sep 21, 2026
4 checks passed
@briansrls
briansrls deleted the session/swift-newt-233 branch September 21, 2026 16:08
@briansrls
briansrls restored the session/swift-newt-233 branch September 21, 2026 16:11
gunbai-bot Bot pushed a commit that referenced this pull request Sep 21, 2026
…used assembly

Reconciles with #11965 (merged) and addresses review 69607.

- lens_verdict: ONE `match d.at`. The index-free door is now a projection of
  the index-taking one with an empty index, so the two cannot disagree about
  a locus -- there is only one set of arms. #11965's
  lens_verdict_node_occurrence_text stays the single answer for every case
  the index cannot resolve, and is called rather than restated. Its own note
  said resolving the ordinal "needs that graph's SpanIndex, which the stages
  past parse do not carry"; program_assembly now carries it, so this is that
  sentence's other half and not a second renderer.

- review 69607 finding 1: the refused arm returned an empty index while the
  annotation above it claimed the opposite -- prose asserting behavior the
  code did not have (DESIGN 4c). Fixed functionally, not by trimming the
  prose: ProgramAssemblyLocatedFoldFailed carries spans, and the per-read
  result carries them across its own refusal, so a file that parsed and then
  failed to normalize or graft stays locatable. That is the arm where the
  occurrence carry matters most. A grammar refusal reports empty honestly --
  nothing was parsed.

- review 69607 finding 2: attribution_assembly() had no caller. Deleted.

The whole chain is now located per link, each with its own span:
  infer_grounding_not_derived @ fixtures/native_cli_door/door_probe.dag bytes 89..90
  -> ... -> FATAL AT fixtures/native_cli_door/door_probe.dag bytes 0..6

The earlier mangled rendering was the probe supplying a caret literal where
the emitted main supplies `path.clone()`; the claim now interns the path, as
production does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant