Repository navigation
v1 emit_rust: deep type-surface collection + compile-time method-call refusal - #8614
Merged
Merged
Conversation
…l guard Two defect repairs in the v1 seed's Rust emitter, root-caused while investigating why v2.std.compilers.target_model emits badly (20 of 26 target_model-reaching module boards share this root, E0063=16 constant across all of them). 1. collect_value_emit_type_surface_names's default match arm called the shallow emit_inferred_type_leaf_name instead of the deep collect_type_node_import_surface_names, under-collecting import surface names for a resolved inferred type (peeling optionality first, matching the deep collector's contract). This clears missing-import E0425s in the target_model boards' generated `use` lists at the sites it covers. 2. emit_rust_generic_method_call's bridge-lowering branch previously fell through unconditionally for any method neither a resolved callable receiver field nor a registered rt_functions() bridge, and rust_runtime_bridge_name's Absent-arm identity fallback silently emitted a fabricated v1_rt::<name>(...) call to a function that may not exist (specimen: v1_rt::keys, no such function in v1_rt.rs). This is a DESIGN.md Sec5 fail-open: a bad state was writable and passed silently. Closed with a guard that refuses loudly via emit_error_expr instead of falling through to the fabricated call. Verified against freshly-rebuilt gunbc/cssl_assemble (this fix's src/v1/05_emit_rust.dag change synced into its committed mirror v1_compiler_emit_rust.rs by function-level splice + rustfmt, not full regen -- full regen is blocked by 8 pre-existing unrelated drift files, out of scope here and covered by CLAUDE.md's documented rung-drop note): - The 16 E0063 sites in v2_std_compilers_target_model.rs are byte- identical (same line:col) before and after this change -- an unrelated construction-site class, confirmed untouched. - Defect 2's guard fires at exactly one site across the whole reachable corpus (06_translate.dag 89-file and emit_module.dag 94-file closures both show exactly one hit, method "keys", same file; self_host.dag's 58-file closure doesn't reach it). A handful, not a rung climb: a clean local repair. - Three residual E0425s (Edge, NonEmptyDiagnostics) remain in v2_compiler_target_carriers.rs and v2_compiler_normalized_tree.rs, confirmed pre-existing and unchanged by this change (same line:col before/after) -- a different, uncovered call pattern, not a regression and not claimed as fixed here. Admitted under v1_maintenance_standing (src/v1 is frozen-semantics, active-maintenance per CLAUDE.md Sec3): this is defect repair that changes observable diagnostics (a fabricated call becomes a located refusal), not BehaviorPreservingCorrectness -- none of that carrier's three admitted classes currently names this shape; a carrier amendment naming it is in flight separately. Also adds a probe-methodology note on a related trap hit during verification: src/v1/*.dag edits have no effect on the standard probe route until the corresponding generated src/v1/stage0/src/*.rs mirror is resynced and gunbc/cssl_assemble are rebuilt, since the probe's --source-root list never includes src/v1. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…me; drop the spliced mirror
The prior commit's defect-2 guard rendered via emit_error_expr, which lowers
to panic!(...) at Rust expression position. That is a DESIGN.md §4b rung
DROP relative to the defect it replaced: the unresolved v1_rt::keys call
previously failed rustc compilation outright (E0425, mechanically
preventable at compile time); panic! only fails if that codepath executes at
runtime (mitigatable at best). The 268->267 error-count improvement this
produced was the class-conversion trap described in §4b's rung-inflation
warning, arriving in the direction nobody watches for: a smaller count while
the guarantee weakened.
Fix: reuse the existing spec.error_type_template authority (already
"compile_error!(\"{0}\")", used for the UNRESOLVED_<label> and
ANONYMOUS_COPRODUCT type-position markers in 05_emit.dag) at this one call
site via apply_type_template1, instead of emit_error_expr's
rust_error_expr_template (panic!({0})). Verified directly against rustc
(2021 edition) that compile_error! is valid in Rust expression position and
fires unconditionally at compile time regardless of runtime reachability —
placed in a structurally-present-but-dynamically-unreached branch, it still
aborts compilation, unlike panic!.
Deliberately scoped to this one site rather than swapping the shared
rust_error_expr_template globally: ~50 other emit_error_expr call sites in
05_emit.dag/05_emit_rust.dag are internal AST-shape-invariant refusals whose
individual runtime reachability has not been audited. compile_error!'s
unconditional firing means a global template swap risks turning any
currently-clean compile at one of those sites into a hard compile failure;
this commit does not carry that audit, so it reuses the existing
error_type_template authority at this single call site rather than
widening it.
Also: error_type_template's {0} substitution is raw/unescaped (unlike
error_expr_template, which pre-escapes via emit_string_literal), so the
refusal message drops the embedded escaped double-quotes around the method
name to stay a well-formed Rust string literal.
Verified end-to-end via a temporary, uncommitted mirror splice (per the
src/v1-regen trap documented in
docs/probes/probe_methodology_rustc_wrapper_cache_impurity_2026-08-19.md):
rebuilt gunbc/cssl_assemble from the spliced mirror, re-ran the
06_translate.dag closure with RUSTC_WRAPPER cleared. Result: 268 total
rustc errors (267 -> 268, correctly — the count goes back up because the
refusal is a real compiler error again), the new compile_error! diagnostic
fires at the exact "keys" call site with no v1_rt::keys emitted, the 16 x
E0063 sites are byte-identical line:col to every prior measurement (same
sixteen, unaffected), and the 3 pre-existing unrelated E0425s
(Edge/NonEmptyDiagnostics) are unchanged.
That splice is NOT included in this commit. Per explicit ruling: a
hand-spliced generated .rs file committed as source is manual application
committed as source (DESIGN.md §6), the same class that produced the
fleet's mirror-drift incident this session. src/v1/stage0/src/
v1_compiler_emit_rust.rs is reverted to its pre-splice, currently-committed
state in this commit. The .dag authority now carries both fixes
(deep type-surface collection, compile-time method-call refusal); the
generated mirror does not, and a gunbc/cssl_assemble built from committed
stage0 sources will not carry either fix until a regen runs. That regen is
out of scope here — claim_executor --required-regen currently refuses on
unrelated pre-existing drift elsewhere in the stage0 population — and is
tracked as the open writer-hole, not silently implied closed by this
commit.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This was referenced Aug 20, 2026
Merged
gunbai-bot Bot
pushed a commit
that referenced
this pull request
Aug 20, 2026
… three defects the repair itself caused
RUN 32345571818 REFUSED DURING PREPARATION with 21 errors -- a regression from run D, which reached
the fold. All 21 were mine and all were located, so this is the ordinary loop and not a mystery.
Twenty are fixed here; the twenty-first is diagnosed below and is not the same kind of thing.
THE 20, IN THREE CLASSES, each decided by asking the CORPUS rather than the import that arrived:
FilePath and NonEmptyStr are qualified 891 and 8196 times against a bare residue of 11 and 25.
The residue was the failure. Qualified: 15 and 43 sites.
std.types.String does not exist. `String` is declared in std.string_type and spelled BARE 16,054
times -- it is a kernel type. I wrote std.types.String in four places because main's own import
block says `import std.types { String }`, which is the lesson I have now been taught three
times on this branch: AN IMPORT IS EVIDENCE OF WHERE THE AUTHOR THOUGHT A NAME LIVED, NEVER
AUTHORITY FOR WHERE IT DOES. De-qualified to bare.
Two Step/RunStep sites in witness_floor_workflow that my line-anchored sed did not reach, because
main added a second pair of step functions below the ones I had matched.
AND THE REPAIR BROKE THREE THINGS ON ITS WAY, which is the part worth keeping:
It qualified DECLARATION HEADS -- `type NonEmptyStr` became `type std.types.NonEmptyStr` in
std/types.dag, the authority for the very name being qualified. I had already fixed exactly this
for `fn` and `func` earlier on this branch and did not carry the guard forward.
Un-qualifying those heads then STRIPPED LEGITIMATE DOTTED HEADS: `service shell.Find` is correctly
dotted, and a rule that removes a qualifier after a declaration keyword cannot tell it from the
damage. 16 restored by joining against HEAD rather than by re-deriving.
And it qualified a coproduct ARM declaration, `| FilePath { file_id, index }` in openai.dag -- an
arm is a declaration, not a reference to the type whose name it borrows.
All three were caught by PARSING THE TREE, not by reading the diff. Three unparseable files.
THE INSTRUMENT DID NOT SEE ANY OF IT, AND THAT IS A REAL FINDING. badqual reads 0 both before and
after, because it excludes a KERNEL set -- String, Int, Bool, Map, List, Unit -- from checking at
all. Every name in that set can therefore be qualified WRONGLY and my "0 bad qualifiers" stays 0.
That is how std.types.String reached CI behind a green check. An exclusion list in a checker is a
silent hole exactly the size of the list, and the names on it are the most-referenced in the corpus.
THE 21st IS NOT MINE TO FIX HERE. mandatory_tag_gate_witness_test's `lens.gate(inferred)` refuses
with "no declared argument contract is available for this method call" -- main's new #8614 refusal
meeting this corpus. Main's own floor is green at 4cec10f (only its regen step is red, which is
the drift deep-ant-102 broadcast as inherited and NOT this branch's), so the interaction is real and
needs a binary built from the merged seed to iterate against. Named rather than guessed at.
Tree parses; badqual 0; armtype 0; the seed-mirror witness still returns true.
This was referenced Aug 20, 2026
Merged
Merged
Merged
gunbai-bot Bot
pushed a commit
that referenced
this pull request
Aug 20, 2026
The rule was specified and not executed: #8619 added where_predicate_{guaranteed,required}_min_length and where_refinement_predicate_satisfied_by to the .dag authority, and the generated mirror the compiler is actually built from did not carry them. A measurement with this PR in-tree was byte-identical to one without it. Not hand-written. Produced by claim_executor --required-regen --source-root dag --source-root src/v2 run at this branch head (the script asserted HEAD == 53df763 and would have refused to measure at any other sha), and taken verbatim from target/stage0-regen-candidate/src/v1_compiler_infer.rs. +78/-1 across two hunks, both adjacent to where_refinement_predicates_equivalent and inside where_refinement_predicates_covered -- the shape a two-function addition plus its routing should have. Control that this is the right file and the right change: the identical run against origin/main produces a candidate byte-identical to main's committed mirror (1047709 bytes, 0 added, 0 removed), so infer.rs drift exists only on this branch and is mine. v1_compiler_emit_rust.rs remains drifted and is NOT touched here -- that one is inherited from main (#8614's spliced mirror) and is owned by #8652. This commit does not make CI green; it removes one of the two named files.
gunbai-bot Bot
pushed a commit
that referenced
this pull request
Aug 20, 2026
The first CI execution of the composed step found two real defects and named both, which is the mechanism working: `phases_run=4 failed=2`. THIS COMMIT FIXES THE ONE THAT IS MINE. `gunbc.fabric_witness_run` is a second model of the same job shape — the floor's priced demand on the compute fabric — and the consolidation left it describing the ladder I deleted: floor_work_contract steps: ["v1-dag-parse", "required-floor"] two steps floor_run_command argv: [..., "--required-floor"] old mode so `fabric_argv_and_workflow_step_agree_on_source_roots` correctly went red: the two representations no longer agreed. Both re-pointed at the one composed step. THE OLD COMMENT'S CONCERN WAS RIGHT AND IS NOW BETTER SERVED, which is why the row moved rather than the concern being dropped. It read that collapsing the two steps "would make a parse failure and a floor failure indistinguishable in the receipt, which is the distinction gunbc#8466 -> #8519 was paid to learn." The receipt now distinguishes FOUR phases, not two steps, and prints `FAILED PHASE <name>` per failure — demonstrated by the very run that caught this, which named a regen drift AND a floor failure where a ladder would have surfaced them one merge at a time. WHAT IT COSTS, stated rather than glossed: resumability was per-step, so a green parse could be receipt-satisfied and skipped on rerun. One step means one receipt and the whole run repeats. Real consequence of the consolidation; phase-grain resumability belongs with the cost basis, not here. A GAP FOUND WHILE FIXING IT. The witness is named "argv and workflow step AGREE" but only compared source roots and that the SCRIPT names the mode — it never checked the ARGV names the same mode. So the two could drift on the one flag that decides what runs, and stay green. Found by execution: after re-pointing the script, the argv still said `--required-floor` and this witness passed. It now asserts the argv carries `--required-ci` and does NOT carry `--required-floor`. Mutation-tested with the restore and the re-verification in ONE command, per the lesson from the previous commit: flipping the argv flag turns it false, restoring turns it true, and the restored line is printed. NOT FIXED HERE, because it is not mine: `regen FAIL generated surface drift: v1_compiler_emit_rust.rs`. Main is ALREADY RED with the identical failure at 4cec10f (run 32343207326). Bisected to #8614, which changed the authority `src/v1/05_emit_rust.dag` without regenerating its mirror `src/v1/stage0/src/v1_compiler_emit_rust.rs`. My PR inherits it because PR runs check out the merge ref. It is reported separately rather than bundled here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot
pushed a commit
that referenced
this pull request
Aug 20, 2026
… converge collect_value_emit_type_surface_names computes the use-import surface for every module gunbc emits, including v1_compiler_emit_rust.rs itself -- so correcting it changes the compiler that computes its own import surface, making it a fixed point of a function of itself, unlike the other 128 mirrors in the prior commit which stabilize after one round. CI (run 32348717736, step "Regen fixed point") caught this: --required-regen still reported single-file drift on v1_compiler_emit_rust.rs after the prior commit, naming two missing use blocks (NamingCase, EdgeKind). Reproduced locally, applied the delta, rebuilt gunbc, ran --required-regen again: first_generation_equal=true, planned=129 executed=129, zero drift, candidate byte-identical to committed -- a genuine A->B->B convergence, not a 2-cycle (ruled out by an independent digest-sequence measurement from this file's sole owner, and by reproducing CI's red locally, which rules out the environment/ rustfmt-divergence hypothesis that was raised alongside the 2-cycle one: if this box and CI disagreed on the fixed point, this box would have stayed green on the prior commit instead of reproducing the red). Of the two added blocks, only one is semantically live: NamingCase's variants (SnakeCase, CamelCase, AsAuthored) are referenced in the body; EdgeKind's variants are not (the one "Read" hit in the file is prose inside a string literal, not EdgeKind::Read). That's expected, not a regression: the deep type-surface walk that #8614 corrected keys a variant glob on the enum's type name reaching the import surface, not on any variant actually being referenced, so a more complete walk necessarily emits more dead-but-harmless glob imports alongside the genuinely missing ones. Pre-existing on main at a smaller rate; the general over-emission is tracked as a separate row against 05_emit_rust.dag by that module's owner. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
gunbai-bot Bot
pushed a commit
that referenced
this pull request
Aug 20, 2026
…lan it ran was not the plan it checked Two defects on the pushed head, both found by review rather than by anything in the tree. FIRST: spark_grant_install_administrator_standing returned CredentialMaterializationPending. An earlier commit imported spark_administrator_password_secret_ref and wired the CI credential step, but never changed the function body -- so run_privileged_step, which matches the lease standing BEFORE reading the credential file, would have refused every privileged step for "no credential enrolled". IAM was not the only blocker on that path; it was merely the one failing loudly enough to hide this. The standing now names the carried reference, which is honest because it is only half the fact: the standing declares WHICH secret, the file read establishes whether THIS RUN holds its bytes. That split had nowhere to land, so it gets one. BootstrapCredentialNotMaterialized discarded its cause and collapsed into CredentialNotEnrolled -- fine while the standing was pending, because that arm was unreachable, and wrong the moment it became the live one: the receipt would have told a reader to enrol a secret that was already enrolled. CredentialNotMaterialized carries the reader's own cause, names the enrolled resource, and says the remedy is this run's materialization. SECOND: the install plan froze four steps -- stage, validate, install, unstage -- with `sudo -n` argvs, and the executor destructured `more: _` and threw three of them away, building its own credential-bound commands instead. The consequences compounded. The ordering guard scanned "validate-"/"install-" identities of the list that never ran, so the whole safety argument was asserted about a shadow. `unstage` never executed, leaving a readable copy of the sudoers content in the installer's home after every run. And the discarded argvs could not have succeeded anyway -- no NOPASSWD exists for them; they were the exact spelling the credential cutover replaced. The four phases are now FIELDS of the plan record the executor destructures. Order is not checked because there is no sequence to permute, so the forward scan dissolved with the list it read; per the guarantee ladder the check goes and its control stays, one rung up. Cleanup runs on every path that reached the far side, and is reported beside the install rather than folded into it: standings feed spark_grant_install_succeeded, so a failed `rm` in that list would have unsaid a grant the host demonstrably holds. The witnesses that asserted staged-copy validation, 0440/root ownership and no-password-prompt were green against argvs nothing sent. They now read admitted_root_argv_words of the plan's own fields, and the prompt property is stated as the truth it became: `-S` with a suppressed prompt and the credential never an argv word. Both new controls verified by mutation, each beside a control that stayed green in the same run: regressing the standing to pending reds the credential witness (1 control green); swapping validate and install reds the ordering witness (2 controls green). 63/63 across the four affected files. Also merges origin/main, whose #8618 and #8614 fix the regen drift that reddened this PR. Regen after the merge writes every artifact and produces no diff. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Aug 20, 2026
briansrls
pushed a commit
that referenced
this pull request
Aug 20, 2026
…8614's emit_rust fix (#8652) * Regen 17 stage0 mirrors: self-hosting import-surface propagation from #8614's emit_rust fix #8614 fixed v1.compiler.emit_rust collect_value_emit_type_surface_names (the `_` catch-all arm: peel Present{value: Resolved{node: rt}}, drop optional cardinality, and collect the resolved node's import surface, instead of the prior emit_inferred_type_leaf_name call) and emit_rust_generic_method_call (a new else-if refusal branch for an unresolved receiver method name with no registered v1_rt bridge). That commit hand-spliced only the touched function bodies into the committed v1_compiler_emit_rust.rs mirror without a full corpus regen. collect_value_emit_type_surface_names is shared self-hosting infrastructure: it computes the use-import surface for every module gunbc emits, not just target_model. Before this commit all 129 stage0 mirrors were mutually self-consistent under the OLD, under-collecting version of that function -- a fixed point that happened to be wrong. Once gunbc is rebuilt from the corrected mirror and used to regenerate the rest of stage0, its emitter produces more complete use-import lists for 16 other previously-self-consistent mirrors too. The 16 are not new damage -- they are the corpus catching up to a collector that is now correct. CI re-enrolled `claim_executor --required-regen` in #8618 (merged before #8614), which caught this: main has been red since 5a71831 (#8614's merge), step "Regen fixed point: first generation matches committed candidate" failing with generated surface drift named on v1_compiler_emit_rust.rs (CI run 32343044158 and 32343207326, corroborated independently by deep-ant-102's run-history bisection and swift-moth-294's commit-window check). Landing v1_compiler_emit_rust.rs alone was considered and withdrawn: CI's single cargo build compiles gunbc from the committed mirror, so a lone-file regen would only postpone the other 16 files' drift to the next run. The 17-file closure is not a larger fix than the 1-file fix -- it is the only correct one. Every diff across all 17 files is confirmed pure `use`-line churn (no logic changed). Verified via the two-generation fixed-point protocol: build gunbc from the OLD committed mirrors, regen to a candidate, apply it, rebuild gunbc from the NEW mirrors, regen again -- the second pass gives first_generation_equal=true, planned=129 executed=129, zero drift across the full stage0 population, confirmed on a fully clean (rm -rf target/release) rebuild. Two of the 17 files (v1_compiler_emit_rust.rs, v1_compiler_trait_derive_emit.rs) are owned by 05_emit_rust.dag / trait_derive_emit.dag's sole-write authority; that owner reviewed both diffs and gave explicit go-ahead, on the grounds that a tool-generated regen from unchanged authority is not an exercise of write ownership. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * v1_compiler_emit_rust.rs is self-referential: one more regen round to converge collect_value_emit_type_surface_names computes the use-import surface for every module gunbc emits, including v1_compiler_emit_rust.rs itself -- so correcting it changes the compiler that computes its own import surface, making it a fixed point of a function of itself, unlike the other 128 mirrors in the prior commit which stabilize after one round. CI (run 32348717736, step "Regen fixed point") caught this: --required-regen still reported single-file drift on v1_compiler_emit_rust.rs after the prior commit, naming two missing use blocks (NamingCase, EdgeKind). Reproduced locally, applied the delta, rebuilt gunbc, ran --required-regen again: first_generation_equal=true, planned=129 executed=129, zero drift, candidate byte-identical to committed -- a genuine A->B->B convergence, not a 2-cycle (ruled out by an independent digest-sequence measurement from this file's sole owner, and by reproducing CI's red locally, which rules out the environment/ rustfmt-divergence hypothesis that was raised alongside the 2-cycle one: if this box and CI disagreed on the fixed point, this box would have stayed green on the prior commit instead of reproducing the red). Of the two added blocks, only one is semantically live: NamingCase's variants (SnakeCase, CamelCase, AsAuthored) are referenced in the body; EdgeKind's variants are not (the one "Read" hit in the file is prose inside a string literal, not EdgeKind::Read). That's expected, not a regression: the deep type-surface walk that #8614 corrected keys a variant glob on the enum's type name reaching the import surface, not on any variant actually being referenced, so a more complete walk necessarily emits more dead-but-harmless glob imports alongside the genuinely missing ones. Pre-existing on main at a smaller rate; the general over-emission is tracked as a separate row against 05_emit_rust.dag by that module's owner. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
6 tasks
briansrls
pushed a commit
that referenced
this pull request
Aug 20, 2026
* CI: four job steps become one invocation, four in-process phases Operator directive, 2026-08-20, on the merged #8618: "the regen steps seem to share a compile with all three steps - we basically need to consolidate ALL the work in there now - we added 2 more steps, but they are not properly managed (between github actions job steps) - i would much prefer if it was all handled within the witnesses step and within the gunbc binary, not at a github actions job level". WHAT THE STEP LADDER WAS. Four steps — parse, regen, regen-fixed-point, floor — each its own process; the ORDER a YAML list; each precondition an `if:` naming another step's `outcome`; and the fixed-point step receiving pass 1's digest by READING THE RECEIPT FILE the regen process had just written. That last one is the tell: `run_required_regen_fixed_point` has taken `pass1_digest: Option<String>` all along and CI passed it `None`. A process boundary sat where a function call belonged. WHAT RUNS NOW. One step, `claim_executor --required-ci`, four phases in one process, the digest handed over in memory. ONLY ONE REAL DEPENDENCY EXISTS, and the rest is the behavioural change worth reading closely: fixed-point needs regen's pass-1 digest, so it is skipped — visibly, as its own reported state — when there is none. Every other phase RUNS EVEN AFTER AN EARLIER FAILURE, so the run reports the complete ledger instead of letting the first defect hide the rest. The line still stops (nonzero exit on any failed phase); it stops with every deficit named. Skipped is never silence and never a pass. The digest is handed over even when regen's comparison DISAGREED: pass 1 emitted a tree either way, and "does the emitter reproduce itself" is a separate question from "does it match what is committed". Skipping determinism on a regen mismatch would conflate them and lose the signal exactly when drift makes it interesting. NOT CLAIMED: no compile is shared. Regen and its fixed point each call `compile_stage0` and the second call STAYS — re-emitting and comparing digests is what the fixed point measures, so collapsing it would delete the measurement. The floor's preparation is a different computation again. What this removes is process startup, the receipt round-trip, and the job-level orchestration. ONE DEFECT I INTRODUCED AND CAUGHT, recorded because the shape matters more than the fix: extracting the parse walk from its bin, I dropped the `tests/fixtures/` exclusion. The first local run duly reported a parse FAILURE in `fact_cardinality_split_brace.dag` — a headerless fragment that is on main, where the parse step is green. The "finding" was my extraction having silently widened its own subject. Restored verbatim, and the subject is now provably identical to main's: 50 files parse-clean here, 50 in run 32341236470. One deliberate difference does remain, stated rather than smuggled: the bin used `read_dir.flatten()`, which silently DISCARDS an unreadable entry, so a walk that never saw a file was indistinguishable from a file that parsed. The error now propagates. SHARED, NOT DUPLICATED: `report_required_floor_outcome` and `required_floor_outcome_is_clean` are extracted so `--required-floor` and `--required-ci` cannot drift into reporting one outcome two ways, and the five-cause conjunction is written once (§3). STALE RECITALS UPDATED, because a knowingly-false present-tense claim in an authority is premise contamination: `gunbc.design_document` (twice), `gunbc.ci_layer_roots` `witness_fold_src_v1_coverage_gap_note`, and `tools.extdeps_scope_placement_gate`. Two other `--required-floor` mentions in `ci_layer_roots` are DATED MEASUREMENTS naming the command as run; they stay true and are untouched. EXECUTED: all four phases sequenced correctly in one local run — `first_generation_equal=true`, `fixed_point_equal=true` with no receipt round-trip, floor entered. Four new witnesses in `witness_floor_workflow_consolidation_witness_test.dag` assert one composed invocation, no cross-step outcome precondition, the parse sweep surviving, and the retired step NAMES not returning (a different axis from the commands, so the two can disagree). Mutation-tested: pointing the step back at `--required-floor` turns the first false. `cargo check --all-targets` and `cargo fmt --all --check` clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Restore --required-ci: a mutation test shipped its own mutation (review 54012) The emitter and the generated workflow were calling `--required-floor`, so the composed four-phase run this PR introduces never executed, while DESIGN.md asserted it did. Blocking, and correct. HOW IT HAPPENED, because the mechanism is more useful than the fix. I mutation-tested the new witness by pointing the step back at `--required-floor` and confirming `w_ci_invokes_one_composed_mode_not_a_step_ladder` went false. The wall worked. The restore did not: the command ran the mutation, the witness, and `cp /tmp/wf.bak` back — and the shell TIMED OUT mid-loop, before the restore. The "restored" echo never printed and I did not notice its absence. Then I verified the wrong thing. `grep -c 'required-ci'` returned 1 and I read that as restored. It was matching ONE PROSE LINE — the comment block explaining the consolidation — not the emitted script. A corpus grep for a symbol finds the documentation about the symbol first, and this file is mostly documentation. The witness would have caught it. It had already TOLD me, returning false as the mutation intended; I attributed that to the mutation and never re-ran it after the supposed restore. A mutation test's last step is not observing red — it is re-observing green afterwards, and that step has to be in the same command as the restore or it does not reliably happen. FIXED: emitter emits `--required-ci`, yml regenerated (drift was a symptom, not a second defect), and all four witnesses re-run AGAINST THE FINAL STATE — all true. ALSO FIXED, same review: the SKIPPED eprintln carried a runaway indentation blob. `cargo fmt` had collapsed a `\` continuation into one literal with the source indentation baked in. Re-broken with an escaped continuation, and re-checked that fmt does not re-collapse it. NOT FIXED, named rather than swept in: three pre-existing strings of the same shape at claim_executor.rs:405, :7998 and :8028 (FLOOR-COMPILE-CLEAN-OVER-BUDGET, FLOOR-BATCH-CLAMP-REFUSED, FLOOR-BATCH-OVER-BUDGET). Same fmt-collapse class, none of them this PR's, and widening the diff to unrelated lines is how a focused change stops being reviewable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * The fabric model still described the two-step job (CI run 32347573121) The first CI execution of the composed step found two real defects and named both, which is the mechanism working: `phases_run=4 failed=2`. THIS COMMIT FIXES THE ONE THAT IS MINE. `gunbc.fabric_witness_run` is a second model of the same job shape — the floor's priced demand on the compute fabric — and the consolidation left it describing the ladder I deleted: floor_work_contract steps: ["v1-dag-parse", "required-floor"] two steps floor_run_command argv: [..., "--required-floor"] old mode so `fabric_argv_and_workflow_step_agree_on_source_roots` correctly went red: the two representations no longer agreed. Both re-pointed at the one composed step. THE OLD COMMENT'S CONCERN WAS RIGHT AND IS NOW BETTER SERVED, which is why the row moved rather than the concern being dropped. It read that collapsing the two steps "would make a parse failure and a floor failure indistinguishable in the receipt, which is the distinction gunbc#8466 -> #8519 was paid to learn." The receipt now distinguishes FOUR phases, not two steps, and prints `FAILED PHASE <name>` per failure — demonstrated by the very run that caught this, which named a regen drift AND a floor failure where a ladder would have surfaced them one merge at a time. WHAT IT COSTS, stated rather than glossed: resumability was per-step, so a green parse could be receipt-satisfied and skipped on rerun. One step means one receipt and the whole run repeats. Real consequence of the consolidation; phase-grain resumability belongs with the cost basis, not here. A GAP FOUND WHILE FIXING IT. The witness is named "argv and workflow step AGREE" but only compared source roots and that the SCRIPT names the mode — it never checked the ARGV names the same mode. So the two could drift on the one flag that decides what runs, and stay green. Found by execution: after re-pointing the script, the argv still said `--required-floor` and this witness passed. It now asserts the argv carries `--required-ci` and does NOT carry `--required-floor`. Mutation-tested with the restore and the re-verification in ONE command, per the lesson from the previous commit: flipping the argv flag turns it false, restoring turns it true, and the restored line is printed. NOT FIXED HERE, because it is not mine: `regen FAIL generated surface drift: v1_compiler_emit_rust.rs`. Main is ALREADY RED with the identical failure at 4cec10f (run 32343207326). Bisected to #8614, which changed the authority `src/v1/05_emit_rust.dag` without regenerating its mirror `src/v1/stage0/src/v1_compiler_emit_rust.rs`. My PR inherits it because PR runs check out the merge ref. It is reported separately rather than bundled here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Stop building the parse binary CI no longer runs — and unpin it from my witness Found by the side thread reviewing #8647 for surplus work. CI still compiled `v1_src_dag_parse` after the consolidation removed the only step that invoked it. THE AUTHORITY ALREADY STATED THE RULE, two lines above the row I left stale: "naming a binary that no step runs buys nothing and costs a compile." The row's own comment said the bin was added for the step below it — the step this PR deleted. So this is not a new principle, it is the consolidation failing to carry its own deletion through to the build list. WORSE, AND THE PART WORTH RECORDING: my consolidation witness ASSERTED the surplus. `w_the_parse_sweep_survives_the_fold` required the yml to contain `--bin v1_src_dag_parse`, using "CI compiles the parse binary" as a proxy for "the parse sweep survives". The two came apart the moment the sweep moved INTO the composed run and the binary stopped being invoked — so the witness was pinning a surplus compile in place as a requirement, which is the opposite of what its name promised. A green witness protecting waste is worse than no witness, because the roster reads as coverage. REPLACED by `w_the_retired_parse_binary_is_no_longer_built`, which asserts what its subject can actually decide: the emitted yml invokes `--required-ci`, builds `claim_executor`, and does NOT build the retired bin. The comment names the boundary explicitly — this file reads emitted workflow text and CANNOT see that the parse phase runs. That is established by execution (`required-ci: parse OK 50 file(s) parse-clean`, run 32371293567), and a static witness claiming it would be asserting something its subject does not contain. THE BINARY STAYS IN THE TREE. Running the parse sweep alone is the cheapest check available while editing src/v1, and it is a thin caller of the same `cli_run` walk rather than a second implementation. What it stops being is CI's business. Mutation-tested: putting the bin back in `witness_floor_required_bins` turns the new witness false; restoring turns it true. The restore was verified by reading the row and the emitted yml directly, not by a symbol count — the timeout ate the in-command re-verify again, which is exactly why the file state is checked explicitly now. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Fix the duplicated memo receipt (review 54101); close the pass-1 dual input TWO FINDINGS, ONE FROM REVIEW AND ONE FROM THE SIDE THREAD. 1. THE MEMO RECEIPT PRINTED TWICE, and my own comment caused it. The previous commit re-derives `report_required_floor_outcome` from main's inline block on every merge that touches it. Main's block ALREADY carried #8642's memo line — #8642 is merged — and I grafted a second copy on top, so both `--required-floor` and `--required-ci` emitted the receipt twice. That degrades the exact "one receipt, both numbers" property #8642 introduced: two lines reporting one pair is the second-representation shape the receipt existed to remove. The instruction that caused it is deleted with the duplicate. It read "each merge has to graft it back deliberately" — an unconditional re-add with no check for what re-derivation already brought. Re-derivation copies main's block wholesale, so the line arrives WITH it and needs no grafting. The surviving comment now says so, and says that exactly one may exist. 2. THE PASS-1 DIGEST HAD TWO SOURCES AND A SILENT PRECEDENCE RULE. The receipt is read unconditionally — the cross-tree refusal and `PriorReceiptRef` are provenance facts only the file carries — so when a caller ALSO supplies the digest in memory it exists twice, and `pass1_digest.unwrap_or(prior)` silently preferred the argument. A disagreement decided nothing and reported nothing. WHOSE DEFECT IT IS: mine. Until the phases shared a process every caller passed `None`, so the file was the only source and `unwrap_or` had one arm in practice. The composed run is what supplies the argument, so the change creating the second source is the change that closes it. WHAT IT IS NOT, stated because the side thread called it a hard blocker and it is weaker than that: it does not guard an active defect on the composed path. There `run_required_regen` writes the receipt and returns the same digest in one pass, so the two agree BY CONSTRUCTION and the arm is unreachable. I tried to exercise it end-to-end by corrupting the receipt and re-running `--required-ci`, and the test was void — regen rewrites the receipt before the fixed point reads it. The refusal guards the FUNCTION's contract, for a caller supplying a digest against a receipt written by some other run at this commit. That unreachability is why the decision is EXTRACTED as `reconcile_pass1_digest`: reaching the arm through the real function needs a seven-minute emit, and a wall no test can reach is a wall nobody knows works. `pass1_digest_disagreement_refuses_rather_than_preferring_one` asserts the refusal names BOTH values, plus two positive controls (agreeing, and None) without which a function that refused everything would also pass. Mutation-tested with the restore and re-verification in ONE command: disarming the guard makes it FAILED, restoring makes it ok, and the restored source line is counted rather than assumed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Baseline the floor heartbeat's cpu_ms, as wall_s already was Composing the CI phases into one process changed what a process-cumulative counter means. floor_resource_sample() read /proc/self/stat utime+stime absolutely, so regen's multi-threaded compile now landed on the floor's line: the floor's FIRST heartbeat reported cpu_ms=59830 with its own process (run 32341236470) and cpu_ms=786650 without one (run 32371293567). wall_s was already relative to heartbeat spawn; cpu_ms now is too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Refusal has no digest to hand the fixed point (review finding) A population refusal returns Ok with a receipt whose digest fields hold the sentinel `refused:population` — the receipt's fields are String and there is nowhere else to put "there was no measurement". The composed coordinator read that receipt, so a refusal handed the sentinel to phase three, which compared it against a real pass-two digest and reported fixed-point refused: pass-1 digest refused:population != pass-2 digest <real> a determinism failure nobody measured, wearing the shape of a real one (§5 fabricated plausible output). The sentinel was documented as known residue; what was missed is that consolidation gave it a route out. RequiredRegenOutcome now carries FirstGeneration = Measured(digest) | NotMeasured(reason), and pass1_digest_for_fixed_point is the only route to the digest — a refusal has no digest field to read, so phase three reports its existing SKIPPED state. Drift still runs the fixed point; drift and refusal were never the same thing. Three premise-accuracy edits from the same review: FIVE CAUSES -> SEVEN (main added route_gap and stale_route_gap and the sentence kept saying five); the dual-input control said ENROLLED RED when the Rust suite has been out of CI since 2026-07-11, so it says LOCAL; and the workflow witness said "the retired parse binary is no longer built" when fleet-converge still builds it — scoped to the required workflow, which is what its subject can decide. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Brian Searls <briansearls1@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
briansrls
added a commit
that referenced
this pull request
Aug 20, 2026
…a min-length demand (#8619) * where-refinement: credit a guaranteed minimum length as evidence for a min-length demand The wall compared predicate NAMES for equality, so a value whose own declared refinement already proves the property was not credited: lower_hex_40 != non_empty, and a 40-hex-digit string flowing into a NonEmptyStr position carried a WhereRefinementUnenforced advisory for REFUSED evidence rather than absent evidence. Decides one closed relation and refuses outside it, as two SEPARATE partial functions over the predicate vocabulary. The asymmetry is the soundness argument: collapsing them into a single min-length table fails open, because a formal lower_hex_64 would then be satisfied by an actual lower_hex_128 (128 >= 64) and a 128-digit string is not a valid sha256 hex. lower_hex_N is a provider only; non_empty is the vocabulary's only pure length lower bound and so its only demander. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Enroll the discriminating RED as known-red: it is red because the harness runs the mirror, not because the wall is wrong Measured at 0456098 (BuildBuddy ba6c4598), claim_batch over the witness entry: FAIL where_refinement_lower_hex_40_implies_non_empty_credits_evidence PASS where_refinement_bare_string_at_non_empty_stays_advisory PASS where_refinement_exact_length_predicate_is_not_a_min_length_demander PASS where_refinement_min_length_implication_does_not_relax_literal_refusal Exactly one of the four is red, and it is the only one whose assertion needs the fix PRESENT. Both assertions that the wall must not OVER-credit hold either way, which is what keeps this quarantine narrow. The same run corroborates the cause without relying on the diagnosis: the only other reds are the two witnesses for the other .dag-only fix to this wall, red for the identical unmirrored reason. Three reds, one cause. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * State the demander-table enrollment condition at its honest rung: diligence, not structure fierce-ant-91 asked whether anything structural stops a predicate being enrolled as provider AND demander at once. Nothing does -- and membership in both is not the fail-open: non_empty is deliberately in both and is sound there, because it demands exactly what it guarantees. The enrollment condition is narrower than the question assumed. A REQUIRED row is sound only if the predicate's entire semantics is a length lower bound. lower_hex_64 in both tables would be unsound not for appearing twice but for demanding an exact length and a charset a lower bound does not establish. Nothing enforces that condition -- two hand-written name-keyed partial functions. guaranteed >= required is mechanically enforced; the discipline populating the demander table is diligence, rung mitigatable, contained only by the vocabulary being closed and small. Next-rung trigger is the dissolve-on already recorded: bounds as fields on the predicate's own declaration leave mis-enrollment nowhere to be written. No behavior change -- prose row only. * Regenerate the v1_compiler_infer.rs mirror from 04_infer.dag The rule was specified and not executed: #8619 added where_predicate_{guaranteed,required}_min_length and where_refinement_predicate_satisfied_by to the .dag authority, and the generated mirror the compiler is actually built from did not carry them. A measurement with this PR in-tree was byte-identical to one without it. Not hand-written. Produced by claim_executor --required-regen --source-root dag --source-root src/v2 run at this branch head (the script asserted HEAD == 53df763 and would have refused to measure at any other sha), and taken verbatim from target/stage0-regen-candidate/src/v1_compiler_infer.rs. +78/-1 across two hunks, both adjacent to where_refinement_predicates_equivalent and inside where_refinement_predicates_covered -- the shape a two-function addition plus its routing should have. Control that this is the right file and the right change: the identical run against origin/main produces a candidate byte-identical to main's committed mirror (1047709 bytes, 0 added, 0 removed), so infer.rs drift exists only on this branch and is mine. v1_compiler_emit_rust.rs remains drifted and is NOT touched here -- that one is inherited from main (#8614's spliced mirror) and is owned by #8652. This commit does not make CI green; it removes one of the two named files. * Dissolve the min-length known-red row: its own trigger is now satisfied The row's dissolution condition read: 'the 00_core and 04_infer stage0 mirrors are regenerated so the compiled harness contains the wall -- this row deletes in that same change'. Both halves now hold at this head: v1_std_core.rs carries expr_is_any_literal + expr_literal_symbol_optional v1_compiler_infer.rs carries where_predicate_guaranteed_min_length (regenerated in 79196a7) So the quarantine is stale, and the module's own known_red_class_note says a row whose witness runs green is stale and deletes. The witness itself is UNCHANGED and promotes to ordinary DiscoverySelection as the permanent regression control -- DESIGN 4b(4): the climb deletes the quarantine machinery, never the evidence. I am one commit late doing this; the trigger said 'in that same change' and the regen was the change. Also repairs a citation my deletion invalidated: the #8592 row cited this row by name as precedent, which would have become a symbol no reader can resolve. It now records the precedent as a dissolved instance rather than pointing at a live row. If the witness is still red, CI says so by name -- which is the correct outcome and better than a quarantine that hides it. * Require a dissolve_on to be discriminating, on the receipt of two that were not A trigger keyed on a condition that is ALREADY TRUE is not a trigger, it is a deletion licence with a date on it -- the next reader dissolves a quarantine standing in for a live defect, and the row reads as dissolved-per-its-own-terms while the wall it substituted for does not exist. Two receipts, one night, and the second is mine: #8592's row keys on the harness CONTAINING declared_arg_types_for_method. That symbol has always been there; the defect (the function takes no TypeEnv) is live on main right now. Satisfiable from the moment it was written. Reported by me, ruled on by deep-ant-102, left standing -- its repair is still-pike-216's and its trigger needs rewriting, not firing. My own min-length row keyed on the harness containing 'the wall'. Measured: where_refinement_predicates_covered = 2 in that mirror on main and always was. A reader checking 'the wall' could have dissolved the row any time in the preceding weeks. I dissolved it correctly only because I happened to grep where_predicate_guaranteed_min_length (0 on main, 2 after the regen). The right answer came from the reader, not the row. The rule and its reviewer test are now in the class note: name the symbol, signature or behaviour whose ABSENCE is the gap; never a category word; run the check against main today, and if it passes the trigger is defective and the row is unprotected. * Correct my own promotion claim: this file's home means the witness executes NOWHERE #8619 deleted the known-red row once its trigger fired and claimed the witness thereby promotes to ordinary DiscoverySelection as a permanent regression control. The first half was right; the second is false for a file under dag/test/claim/long/, which is excluded from witness discovery at dir grain (ci_layer_roots long_lane_exclusion_note). So deleting the row removed the only thing naming these witnesses, and the home ensures nothing else does. The honest state is EXECUTED NOWHERE -- strictly worse than the quarantine it replaced, because a known-red row is at least counted. The row was still right to delete: it asserted a RED that is no longer red. The defect is that promotion presumes a discovering consumer this file does not have. Rung stated at mitigatable with the local recipe, and the next-rung trigger named as a decision NOT taken here: either the file leaves the long home (pushing its eval cost into the fast lane, the exact thing that home exists to prevent) or the long home gets an executing cadence (deleted with falsifier.yml in the 2026-08-15 floor cut, not this lane's to re-add). Found while resolving a merge conflict with #8625, whose own coverage paragraph states the same fact about this file from the outside. * Correct my correction: these identities ARE counted, and the remedy is a module rename not a file move Two errors in the note I landed an hour ago, both found by reading the mechanism instead of reasoning about it, and both changing what is owed: 1. I wrote that deleting the known-red row left the witnesses in an uncounted silence, 'strictly worse than the quarantine it replaced'. False. v2.workflow.required_floor gives EVERY DISCOVERED SITE exactly one RequiredFloorDisposition keyed by qualified module.function (operator ruling 2026-08-19), and the long home is a Declined ARM of that receipt, not an absence. These identities are discovered, counted at identity grain, and aggregate into declined_long. What the deleted row actually cost is its reason string and its named owner -- not counting. Counted-and-declined is weaker than executed and better than silence. 2. I wrote the remedy as moving the file out of the long home. Also false. Admission tests the module's AUTHORED NAME against long_home_prefixes() = 'test.claim.long.'; this module declares test.claim.long.where_refinement_enforcement_witness on line 1. Moving the file while keeping the module name changes nothing. The remedy is a module rename, file following. Also records why floor_route_gap (merged today) is not the route: it is scoped to identities that EXECUTE and reach an unrouted host effect, and its reverse join reds the build on an enrolled identity that did not execute. Enrolling there would be a false claim, not a shortcut. * Name the lesson in the note's own words, and mark one claim as read-not-observed 1. required_floor's header records that the previous host tested a PATH, which 'made a directory the admission authority', and names that as the root cause of the 2026-08-04 ruling. I proposed to fix this by moving a directory -- reproducing the exact mistake the mechanism was built to stop, inside the mechanism that stops it, while reading the file that says so. 2. The floor_route_gap claim is read from that module's contract prose, not observed. Today produced a gate that never executed and a lens reading a file that exists nowhere, both fully described in prose, so 'the contract states the outcome' is the class of claim that has failed most often here. Accepted on two narrow grounds, both now recorded: it is a claim about a REFUSAL (trusting it wrongly yields a weaker refusal, never a silent accept), and exercising it would require authoring a knowingly-false row to red a build. --------- Co-authored-by: Brian Searls <briansearls1@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two defects in the v1 seed's Rust emitter (
src/v1/05_emit_rust.dag), found while investigating whyv2.std.compilers.target_model(v2_std_compilers_target_model.rs) emits badly under a 20-of-26-module-board census.Root cause of the census pattern: the 16 x E0063 diagnostics shared across those boards are all in
v2_std_compilers_target_model.rsand are an unrelated, orthogonal construction-site class — confirmed byte-identical line:col across every measurement in this PR, before and after both fixes below. Neither fix touches or resolves that class; it is a separate, still-open defect.Defect 1 —
collect_value_emit_type_surface_namesdefault match arm used a shallowemit_inferred_type_leaf_namewhere a deepcollect_type_node_import_surface_names(peeling optionality first) was needed, causing under-collected import surfaces for inferred-typed expressions.Defect 2 —
emit_rust_generic_method_callcould emit a call tov1_rt::<unregistered-name>for a method neither resolvable as a receiver field nor registered inrt_functions(), producing an unresolved-symbol Rust compile error (E0425) with no located diagnostic pointing at the actual .dag-level cause.Defect 2's fix, and the compile_error! question
My first pass at defect 2 (superseded, see history) guarded the bad call with
emit_error_expr, which lowers topanic!(...)in Rust expression position. That is a DESIGN.md §4b guarantee-ladder rung drop: the previous behavior (emittingv1_rt::keys) failed to compile (E0425, mechanically preventable at compile time);panic!only fails if that codepath executes at runtime (mitigatable at best, and only if reached). The naive diagnostic count improved (268 -> 267) while the guarantee weakened — the rung-inflation trap's mirror image, a smaller count hiding a downgrade.Read on compile_error!, stated explicitly per request: it works here, and this PR uses it. Verified directly against
rustc --edition 2021:compile_error!is valid in Rust expression position and fires unconditionally at compile time regardless of runtime reachability (confirmed by placing it in a structurally-present-but-dynamically-unreached branch — it still aborted compilation). The fix reuses the existingspec.error_type_templateauthority (alreadycompile_error!("{0}"), used for theUNRESOLVED_<label>/ANONYMOUS_COPRODUCTtype-position markers in05_emit.dag) at this one call site viaapply_type_template1, rather thanemit_error_expr'srust_error_expr_template(panic!({0})).This is deliberately a scoped, single-site fix, not a global template swap:
rust_error_expr_templatebacks ~50 otheremit_error_exprcall sites across05_emit.dag/05_emit_rust.dag, mostly internal AST-shape-invariant refusals ("expected ExprXxx") whose individual runtime reachability has not been audited. Becausecompile_error!fires unconditionally, swapping the shared template globally risks turning a currently-clean compile at any of those unaudited sites into a hard failure. That audit is out of scope for this PR, so it reuses the existing type-position authority at this one site instead of widening the shared expression template.One related correctness fix:
error_type_template's{0}substitution is raw/unescaped (unlikeerror_expr_template, which pre-escapes viaemit_string_literal), so the refusal message had to drop the embedded escaped double-quotes around the method name to stay a well-formed Rust string literal.Verified end-to-end, do not let net-minus-one stand as the headline: the count goes back UP, correctly. Rebuilt
gunbc/cssl_assemblefrom a temporary (uncommitted, measurement-only) mirror splice and re-ran the06_translate.dagclosure withRUSTC_WRAPPERcleared (see the cache-impurity trap in the probe doc below):compile_error!diagnostic fires at the exactkeyscall site, withv1_rt::keysno longer emitted anywhere.Edge,NonEmptyDiagnosticsinv2_compiler_target_carriers.rs/v2_compiler_normalized_tree.rs) are unchanged.06_translate.dag89 files,emit_module.dag94 files, both hitting the same single "keys" site;self_host.dag58 files, 0 sites, doesn't reach it). A clean local repair, not a §4b rung climb.What is NOT in this commit, and why
The mirror splice used for verification is not committed. Per explicit ruling: a hand-edited generated
.rsfile committed as source is "manual application committed as source" (DESIGN.md §6) — the exact class behind this session's fleet-wide mirror-drift incident (#8592).src/v1/stage0/src/v1_compiler_emit_rust.rsis unchanged by this PR.Honest state: the
.dagauthority (src/v1/05_emit_rust.dag) now carries both fixes. The generated mirror does not. Agunbc/cssl_assemblebuilt from committedstage0sources will not carry either fix until a regen runs. That regen is out of scope here —claim_executor --required-regencurrently refuses on unrelated, pre-existing drift elsewhere in thestage0population (8 unrelated basenames) — and is an open writer-hole, not silently implied closed by this PR.v1_maintenance_standing classification
Both changes are within v1's frozen-semantics/active-maintenance envelope per
gunbc.v1_maintenance_standingv1_seed_standing(DESIGN.md §3's standing rule): scoped correctness repairs to the existing Rust-emission behavior, not new growth surfaces.Test plan
.dag(gunbc compile --source-root src/v1 --source-root dag --entry src/v1/05_emit_rust.dag ...) to confirm the edit compiles through v1's own pipeline and renders the intendedapply_type_template1(spec.error_type_template, ...)call shape.cargo build --release -p v1-compiler --bin gunbc --bin cssl_assemble, then06_translate.dag->cssl_assemble->cargo build --release --libwithRUSTC_WRAPPER=cleared andCTRL_BUILD_MODE=local(seedocs/probes/probe_methodology_rustc_wrapper_cache_impurity_2026-08-19.mdfor why the wrapper must be cleared, and the newly-added src/v1-mirror-regen-trap section for why a.dag-only edit otherwise has zero visible effect on this probe route).Co-authored-by findings from
deep-ant-102(v2 self host), who caught the rung-drop in review and asked for the compile_error! judgment above.