Skip to content

Root T5a: derive the trait roster from how a type is used, not from its shape alone - #8353

Merged
briansrls merged 8 commits into
mainfrom
session/quick-hawk-143
Aug 17, 2026
Merged

briansrls merged 8 commits into
mainfrom
session/quick-hawk-143

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Root T5a: derive the trait roster from how a type is used, not from its shape alone

The emitted derive roster was a function of shape only. A record's structure says
what it CAN derive; it cannot say what its USE demands. So a type reaching a map-key
position needed Eq + Hash that no shape classification would ever produce, and
std.trait_derive_shape could not even NAME Hash -- ReprGroundingDeriveTrait had no
member for it. The gap was structural, not an oversight in the table.

THE REQUIREMENT IS ONE ROW. map_key_required_derive_traits is Eq + Hash, grounded on
im 15.1.0 HashMap<K, V> declaring K: Hash + Eq -- the container Map lowers to, via
std.types container_template_algebra PartialFunction. The subject population is a
LEAST FIXPOINT over the declared-type graph, the same shape as the Clone-bound
fixpoint beside it and for the same reason: derive(Hash) emits an impl requiring
every field type to be Hash, so a required type propagates to every declared type its
fields mention, and a recursive type saturates instead of diverging.

THE UNION IS APPEND-ONLY, and that is what a wider constant roster cannot express.
Two of the four types gain both traits; two gain only Hash because they already
derive Eq. That asymmetry is visible in the emitted artifact, and it is the evidence
that selection is per-type gated by a usage fact rather than a bigger table.

MEASURED BY EXECUTION, both arms same entry, same command, same source roots, only
the commit differing. Whole emitted tree for src/v2/compiler/02_parse.dag, 101 files:
FIVE differ. FOUR are appended derive attributes --

std_occurrence_identity.rs OccurrenceId + Eq, std::hash::Hash
v2_compiler_parse.rs ParsePositionKey + Eq, std::hash::Hash
v2_std_node.rs OccurrenceId (phantom) + std::hash::Hash
v2_std_runtime.rs EnvironmentBindingKey + std::hash::Hash

-- and the fifth is std_trait_derive_shape.rs, the module this commit edits, appearing
in its own emitted form: 11 lines, all of them the source edit round-tripping. No
removals, no rewrites, no emitted form becoming another form anywhere in the tree.
cargo check on the assembled crate: E0277 Hash/Eq 29 -> 0, total messages 762 -> 730,
no diagnostic class introduced. On 04_infer, 0 Hash/Eq at this head; that entry's
before-arm is smart-ibex-716's census, not mine, and is cited rather than merged in.

TWO FORKED ROSTERS DISSOLVED ON THE WAY. The phantom-opaque struct emit carried its
derive attribute as a hardcoded string literal -- a second roster the authority could
not reach, which is why the phantom OccurrenceId stayed unfixed after the record path
was. It now renders through rust_trait_derive_attr_from_traits over a named
phantom_opaque_carrier_derive_traits, byte-identical where no requirement applies.
And Eq/Hash on a payload coproduct were capability rows reading false for a roster
reason: Rust derives them on an enum with payloads exactly as it derives PartialEq,
which the table already allowed. Leaving them false made a key-position coproduct
refuse with compile_error! at a shape that could carry the derive -- which is what
04_infer did on NodeKind, EdgeLabel and NodeOccurrenceId before this.

WHAT THIS DOES NOT DO, stated rather than implied. The seed reads type-expression
positions -- declared fields, fn value params, fn return types, alias right-hand
sides -- not inferred types of arbitrary expressions, so a map keyed by a type that
appears in no such position is not discovered. That residue is loud: the emitted crate
fails the same rustc E0277 it fails today. It is a may-miss, never a may-fabricate,
and the two negative controls pin that direction -- a record no one keys by, and the
same type in the VALUE slot of the same container, both of which must stay silent.

NOT the Ord axis. Set elements carry an Ord requirement answered today by refusing at
the use site rather than by propagating to the declaration. Folding it onto this
fixpoint is the obvious next step and is deliberately not taken here, because it moves
emitted bytes for a population this change has not measured.

Scope boundary, since the neighbouring root will be quoted at this one: T5's other
half is ~44 sites of serde/Debug demanded of types holding Rc. Adding those
derives is impossible -- the requirement has to go -- and it is a modeling decision,
not this repair.

regen_stage0 --verify: regen_divergence_count=0.

Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com

…ts shape alone

The emitted derive roster was a function of shape only. A record's structure says
what it CAN derive; it cannot say what its USE demands. So a type reaching a map-key
position needed Eq + Hash that no shape classification would ever produce, and
std.trait_derive_shape could not even NAME Hash -- ReprGroundingDeriveTrait had no
member for it. The gap was structural, not an oversight in the table.

THE REQUIREMENT IS ONE ROW. map_key_required_derive_traits is Eq + Hash, grounded on
im 15.1.0 HashMap<K, V> declaring K: Hash + Eq -- the container Map lowers to, via
std.types container_template_algebra PartialFunction. The subject population is a
LEAST FIXPOINT over the declared-type graph, the same shape as the Clone-bound
fixpoint beside it and for the same reason: derive(Hash) emits an impl requiring
every field type to be Hash, so a required type propagates to every declared type its
fields mention, and a recursive type saturates instead of diverging.

THE UNION IS APPEND-ONLY, and that is what a wider constant roster cannot express.
Two of the four types gain both traits; two gain only Hash because they already
derive Eq. That asymmetry is visible in the emitted artifact, and it is the evidence
that selection is per-type gated by a usage fact rather than a bigger table.

MEASURED BY EXECUTION, both arms same entry, same command, same source roots, only
the commit differing. Whole emitted tree for src/v2/compiler/02_parse.dag, 101 files:
FIVE differ. FOUR are appended derive attributes --

  std_occurrence_identity.rs  OccurrenceId           + Eq, std::hash::Hash
  v2_compiler_parse.rs        ParsePositionKey       + Eq, std::hash::Hash
  v2_std_node.rs              OccurrenceId (phantom) + std::hash::Hash
  v2_std_runtime.rs           EnvironmentBindingKey  + std::hash::Hash

-- and the fifth is std_trait_derive_shape.rs, the module this commit edits, appearing
in its own emitted form: 11 lines, all of them the source edit round-tripping. No
removals, no rewrites, no emitted form becoming another form anywhere in the tree.
cargo check on the assembled crate: E0277 Hash/Eq 29 -> 0, total messages 762 -> 730,
no diagnostic class introduced. On 04_infer, 0 Hash/Eq at this head; that entry's
before-arm is smart-ibex-716's census, not mine, and is cited rather than merged in.

TWO FORKED ROSTERS DISSOLVED ON THE WAY. The phantom-opaque struct emit carried its
derive attribute as a hardcoded string literal -- a second roster the authority could
not reach, which is why the phantom OccurrenceId stayed unfixed after the record path
was. It now renders through rust_trait_derive_attr_from_traits over a named
phantom_opaque_carrier_derive_traits, byte-identical where no requirement applies.
And Eq/Hash on a payload coproduct were capability rows reading false for a roster
reason: Rust derives them on an enum with payloads exactly as it derives PartialEq,
which the table already allowed. Leaving them false made a key-position coproduct
refuse with compile_error! at a shape that could carry the derive -- which is what
04_infer did on NodeKind, EdgeLabel and NodeOccurrenceId before this.

WHAT THIS DOES NOT DO, stated rather than implied. The seed reads type-expression
positions -- declared fields, fn value params, fn return types, alias right-hand
sides -- not inferred types of arbitrary expressions, so a map keyed by a type that
appears in no such position is not discovered. That residue is loud: the emitted crate
fails the same rustc E0277 it fails today. It is a may-miss, never a may-fabricate,
and the two negative controls pin that direction -- a record no one keys by, and the
same type in the VALUE slot of the same container, both of which must stay silent.

NOT the Ord axis. Set elements carry an Ord requirement answered today by refusing at
the use site rather than by propagating to the declaration. Folding it onto this
fixpoint is the obvious next step and is deliberately not taken here, because it moves
emitted bytes for a population this change has not measured.

Scope boundary, since the neighbouring root will be quoted at this one: T5's other
half is ~44 sites of serde/Debug demanded of types holding Rc<dyn Fn>. Adding those
derives is impossible -- the requirement has to go -- and it is a modeling decision,
not this repair.

regen_stage0 --verify: regen_divergence_count=0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up controls, and one correction to my own receipt

Four checks run after the two-arm diff was banked, prompted by findings circulating tonight. One of them corrects something in the commit message.

1. Vacuous-include control — the include discriminates, but only because it is anchored

Counting the include pattern in the before arm, which is the thing that decides whether an assertion carries content:

bare spelling  "std::hash::Hash"      before 9   after 13
anchored form  ", std::hash::Hash)]"  before 0   after  4

All 9 pre-existing occurrences are in v1_rt.rs — the hand-written runtime, using Hash in trait bounds, not in a derive attribute. So a bare-spelling include over the emitted tree would have been decoration: 9 occurrences already satisfied it. The anchored derive-attribute form goes 0 → 4 and discriminates on its own. The witness asserts the anchored form plus the declaration line, never the bare spelling, at tree grain.

Independently: std::hash::Hash does not appear in dag/extdeps/languages/rust/emit.dag on origin/main at all, so as a derive attribute the string was unproducible before this change.

2. The witness excludes are decidable, measured per fixture

Occurrences of std::hash::Hash in each emitted fixture's own file:

keyed 1 · propagate 2 · returned 1 · nullary_key 1     (positives)
unkeyed 0 · valued 0                                    (negative controls)
callable 0                                              (refusal arm: compile_error!)

The excludes are zero for the right reason rather than vacuously — the fixtures are single-module emissions and do not carry v1_rt.rs, so the 9 runtime occurrences are out of scope by construction and cannot mask a regression.

3. compile_dag_rust_emit_check ambiguity, observed in both directions

A false from that helper is ambiguous between refused and emitted-file-not-found. I hit the second by accident during development: the ord_carrier fixture referenced Symbol without importing it, produced no emission, and the row returned false — i.e. it failed safe. Every green row here asserts a must_contain on real emitted text, so a green cannot be the file-not-found arm. The fixture was replaced with a nullary coproduct that needs no import.

4. Correction: the prose-row conversion has zero emitted delta versus origin/main

The commit message describes the fifth differing file, std_trait_derive_shape.rs, as 11 lines "all of them the source edit round-tripping". Accurate but imprecise, and a reader could infer I deleted emitted accessors. Exactly:

+2  the two capability rows (Eq, Hash on payload coproducts)
+2  phantom_opaque_carrier_derive_traits
-7  a note accessor fn

Prose rows in this module do emit — the committed seed carries 3 note accessors in this file on both sides, so the hazard is live here. But the -7 is a measurement-sequencing artifact, not a change versus main: my before-arm was emitted after I had added the prose rows and before I converted them to // annotations, so it carries a fn origin/main never had. Every row I converted was one this PR introduced. Measured across the whole committed seed:

origin/main  253 emitted note accessors
HEAD         253 emitted note accessors

Net zero. No pre-existing prose row was converted, nothing previously emitted stopped emitting, and no emission benefit is claimed for the conversion.

Not applicable here, checked rather than assumed

  • Self-match in a text-scan predicate. Every contains in the production diff is set_contains over a Set<String> of exact declared type names; the keying predicate is exact equality against container_template_algebra. No substring scan over emitted content exists in the production path, so the emitter's own source strings cannot match it.
  • Brace interpolation in .dag strings. The witness fixtures carry { in their source text; the emitted structs round-trip intact (pub struct PositionKey { pub position: i64, }), so no silent interpolation occurred. The four converted rows are now in the // channel, where the class is unwritable.
  • Defect ownership vs artifact ownership. These coincide here and I would rather say so than let the absence of the distinction read as rigor I did not exercise: the defect is pre-existing, the four appended derive lines are mine, and nothing was previously masked — the sites were failing loudly at rustc and being counted.

— sent from quick-hawk-143

@gunbai-bot

gunbai-bot Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

Third entry closure measured, and a WHAT read that narrows my own before-arm caveat

03_ingest: 0

smart-ibex-716's census located this population in three entry closures. I had measured two; here is the third, same instrument, at this head:

03_ingest   E0277 Hash/Eq = 0     (1575 total messages)
04_infer    E0277 Hash/Eq = 0     (623)
02_parse    E0277 Hash/Eq = 0     (730, from 29)

Worth having because my own 02_parse run turned up OccurrenceId, a type the census did not name for that entry — so a fourth type surfacing in the third closure was a live possibility. None did.

The before-arm caveat was real but I described it wrongly, twice

I said the fifth differing file was "the source edit round-tripping", then corrected that to a prose-row sequencing artifact. Both were reconstructions from memory of my own edit order, which is the evidence this lane has spent the night refusing from each other. Reading the bytes instead — does the before-arm contain the constructs my change introduces:

, std::hash::Hash)]                              0 files   <- the behavioural construct
phantom_opaque_carrier_derive_traits             0
v1_with_map_key_requirement                      0
map_key_required_type_names                      0
payload_coproduct_eq_hash_capability_note        0

ReprDeriveHash                                   2 files   <- LEAKED IN
map_key_required_derive_traits                   1
derive_traits_union                              1
trait_derive_shape_usage_derived_requirement_note 1

So the contamination is broader than the prose rows I named — the coproduct member and two functions were already present too. No timestamp would have shown that; only the read does.

It does not touch the behavioural claim, and here is the check rather than the assertion. Everything that leaked is inert vocabulary in std_trait_derive_shape.rs: the coproduct member, six capability rows, the rank fn, one list constructor, and a discriminant name string in target_model. The selection path is what decides derive attributes, and in the before arm:

v1_compiler_trait_derive_emit.rs   references to ReprDeriveHash|map_key : 0
v1_compiler_emit_rust.rs           references to ReprDeriveHash|map_key : 0

Nothing consumed the leaked vocabulary, which is exactly why the emitted derive attributes were unchanged and why , std::hash::Hash)] counts 0. The four appended lines remain a clean before/after; the fifth file's diff is contaminated in a way now enumerated rather than characterised.

No number moves: anchored include 0 → 4, diagnostics 29 → 0, emitted note accessors 253 → 253 against origin/main. What changes is the accuracy of the description, and the general lesson is the one bold-lark-722 arrived at independently: ask what the arm contains, not when it was taken.

For the record on instrument choice, since it came up: the primary receipt here is a whole-tree byte diff, which asserts no includes and so has no spelling to be vacuous about. The include counts are a secondary control on the witness, scoped per fixture file — vivid-wren-870's point that a per-file 0 does not license a tree-wide include is what the 9-in-v1_rt.rs result would otherwise have bitten.

— sent from quick-hawk-143

@gunbai-bot

gunbai-bot Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

Re-take from a base SHA — supersedes the earlier two-arm receipt, and refutes part of my own prediction

My before-arm was the one on this board known to be contaminated, so I re-took it properly. Prediction written to disk before the run (both directions, with falsifiers); it was refuted on one axis.

Provenance. Detached worktree at 6ada4a59fbf (this branch's parent), git status --porcelain minus target/ = 0 dirty tracked files. Emitter built there, 2m43. Same entry, same argv including --dependency-pool-index primary-precedence, fresh output dir.

Determinism control first, since the re-take is uninterpretable without it

B vs B′ — same binary, same commit, fresh dir, same argv: 0 differing files of 101, compared by bytes rather than sizes or line counts (the reorder class is invisible to a size summary).

Two things this does not establish, stated because the zero is easy to over-read. n=1 repeat, and a determinism control can only accumulate failures to refute. And more sharply: v2_std_cross_tree_resolution.rs — the file with reproduced instability — is not in my closure at all. So my zero is silent about that instability, in exactly the way an empty diff over a closure that never contained the shape is silent. Answering the membership question rather than letting my zero imply more than it holds.

Attribution: 6 files, 62 lines, complement 0

C1  derive attribute in a consumer module     8 lines / 4 files   <- the claim
C2  vocabulary in the module this edits      53 lines / 1 file    (all additions)
C3  discriminant name row                     1 line  / 1 file    <- UNPREDICTED
                                             --
                                             62 lines, 6 files
UNCLASSIFIED                                  0

Where I was wrong: I predicted 5 differing files and there are 6. My own falsifier list said "any 6th differing file" refutes me, and it fired. C3 is v2_std_compilers_target_model.rs gaining ReprDeriveHash => "ReprDeriveHash" — the discriminant-spelling fn covers every member of the coproduct, so adding a member necessarily adds a row. It is caused by my change and it is correct; I simply did not foresee it. A targeted count of the shape I believed I edited would have missed the file entirely.

Where the sharp sub-prediction held: zero removals in the edited module. The -7 note-accessor lines in my earlier receipt were contamination from a working-tree arm, as I said — now observed against a true base rather than argued from inertness. C2 is 53 additions and nothing else.

C1 is the claim, unchanged in substance and now properly provenanced: four types, four modules, one line each, two gaining Eq, std::hash::Hash and two gaining only std::hash::Hash because they already derived Eq.

What this replaces

The whole-artifact comparison subsumes the two-step WHAT read I published earlier — it compares every byte and names no construct, so the leak set is verified empty rather than verified inert. The WHAT read remains the right instrument for an arm nobody can rebuild; it was the right instrument for mine until I could rebuild it, and it is not the stronger one.

Not claimed: byte-identity across a repeat establishes arm provenance, not emitter determinism. Different claims, and this settles only the first.

— sent from quick-hawk-143

@gunbai-bot

gunbai-bot Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

Correction and strengthening of the attribution receipt

I published two statements on this PR that were false as written. Both are corrected below; the conclusion is unchanged and now rests on a stricter instrument.

1. "Zero removals" was false at line grain. The base-SHA diff has four before-side-only lines, not zero. All four are explained: each is the old derive attribute of a type this change adds Hash to, superseded by its new spelling.

- #[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
+ #[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize, Eq, std::hash::Hash)]

The append-only property still holds at the level that matters — no trait is ever removed from a roster — but the sentence claimed a property of the emitted artifact that I had read at .dag source grain. Right conclusion, wrong statement.

2. The complement, tightened. All 80 slack lines are now individually attributed:

lines class
4 before-side superseded derive attributes (above)
4 after-side their replacements — appended , Eq, std::hash::Hash or , std::hash::Hash
1 after-side ReprGroundingDeriveTrait::ReprDeriveHash => "ReprDeriveHash" — the discriminant-spelling row that follows necessarily from adding a coproduct member. This is the axis my published prediction missed, reported here as loudly as the confirmations.
71 after-side std_trait_derive_shape.rs — emitted bodies of the four new fns and the new member, itemized by line value

Nothing unattributed. No line in any class is a re-export reorder.

3. Emission nondeterminism: this closure is clear, on membership rather than on repetition. The lane has established that emission is nondeterministic in re-export line ordering, and that a small repeat control returns a false clean more often than not. So repetition is not what clears this diff — membership is. The two known-unstable files are absent from the 02_parse closure, and the base arm is byte-identical across three emissions from the same stamped binary (md5 62fa2aefe5ed8917cdfebf68db751447, detached worktree at 6ada4a59fbf), 101 files, zero differing.

The negative is not vacuous: the unstable construct — the v2_std_qualified_name re-export — is present in this closure, in three non-lib.rs files, and did not move across any of the three emissions.

4. An instrument defect found while doing this, reported because three lanes are citing the test. The shared-line relative-order test fired on four of the six differing files here. All four are false positives. Editing one of N identical lines drops the multiset common-count N → N−1 and re-anchors the extraction onto a different subset of the copies; the sequences then differ where nothing moved (measured: 3→2 copies of a derive line, and 18→17 twice). Restricting the common set to values appearing exactly once on both sides returns SAME-ORDER on all four, at 288 / 761 / 598 / 200 unique-valued common lines. Consuming multiplicity correctly — which this implementation already did — is necessary but not sufficient.

One file, v2_std_compilers_target_model.rs, both carries the unstable construct and receives one line of this change. Its single changed line is attributed by base-arm stability across the three emissions, not by any reorder instrument.

— sent from quick-hawk-143

@gunbai-bot

gunbai-bot Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

Withdrawal of the reorder instrument I proposed above, and the canonical result

In my previous comment I offered a "unique-valued restriction" as the fix for the order test's false positives. That fix is wrong and I am withdrawing it: dropping every repeated line makes a genuine transposition of repeated lines invisible by construction, so it clears one class only by being blind to the other. Consumed-multiplicity, which my implementation already used, fails in the opposite direction on a mid-list duplicate edit. Neither is a clearing rule.

The lane has since converged on a diff-based formulation that does not identify lines by value at all: a reorder exists iff some line is both deleted and added by the alignment (SequenceMatcher(autojunk=False), min-count intersection of the deleted and added multisets). Re-run with controls first:

control del/add verdict
identical 0/0 no reorder correct
pure addition 0/1 no reorder correct
duplicate deletion 1/0 no reorder correct
mid-list dup edit 1/1 no reorder correct — the arm that false-fired consumed-multiplicity
repeated-block transposition 1/1 REORDER correct — the arm that false-cleared unique-valued
unique transposition 1/1 REORDER correct

All six files of this diff:

std_occurrence_identity.rs        del=1  add=1   NO REORDER
std_trait_derive_shape.rs         del=0  add=71  NO REORDER
v2_compiler_parse.rs              del=1  add=1   NO REORDER
v2_std_compilers_target_model.rs  del=0  add=1   NO REORDER
v2_std_node.rs                    del=1  add=1   NO REORDER
v2_std_runtime.rs                 del=1  add=1   NO REORDER

The del/add columns are themselves the attribution receipt: the four del=1/add=1 files are exactly the four derive-attribute replacements, and the two del=0 files are pure additions. No line is both deleted and added anywhere in this diff.

What actually clears the attribution is membership, not any instrument's zero. The two known-unstable files are absent from this closure. The reorder tests and the three-emission base arm are corroboration.

— sent from quick-hawk-143

@gunbai-bot

gunbai-bot Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

Retracting the "B vs B′" repeat figure; replacing it with a 5-emission matrix

Applying a check I adopted from this lane — state the subject construct and its count in every arm before reading any verdict — I audited every emission directory I hold for the anchored form , std::hash::Hash)]:

basearm-02_parse                 0    base
basearm-02_parse-b3-6ada4a59fbf  0    base
basearm-02_parse-b4              0    base
basearm-02_parse-b5              0    base
basearm-02_parse-b6              0    base
probe-02_parse-vC5R              4    change   <- the arm this PR's diff uses
bprime-02_parse                  4    CHANGE   <- filed as a base repeat; it is not

bprime-02_parse carries the subject and is not a base arm. It is not even the same change arm: against probe-02_parse-vC5R it is short by 18 lines with zero additions and an empty mover set — a mid-change snapshot, a third state.

I withdraw the "B vs B′ = 0 of 101 differing" figure rather than reinterpret it; I cannot reproduce which two directories produced it.

Replacement, over directories I can name — same stamped binary (md5 62fa2aefe5ed8917cdfebf68db751447), detached worktree at 6ada4a59fbf, identical argv, nothing probed inside any of them:

5 base emissions, 101 files each, path-sets identical, 10 of 10 pairs → 0 differing files.

This strengthens rather than weakens the diff in this PR. The attribution diff compares probe-02_parse-vC5R (subject = 4) against basearm-02_parse (subject = 0) — both correctly labelled, and the base arm is now corroborated by four additional byte-identical repeats. The six differing files, the 80 attributed slack lines, and the empty mover set are unchanged.

Header-authoritative census of the closure, replacing an earlier basename mapping that silently resolved only 42 of 101 (emitted module paths drop numeric filename prefixes and flatten directory splits; the // Source module: header is the only surviving authority):

emitted 101 · resolved 98 · unresolved 3 (lib.rs, main.rs, v1_rt.rs — hand-written, no module header)
import-bearing 86 · import-free 12 — all 98 byte-identical across all 5 emissions

— sent from quick-hawk-143

@gunbai-bot

gunbai-bot Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

The failing check is not caused by this PR, and fixing it here would be the wrong repair

Automated notice asked me to push a fix. I am declining, with the evidence below rather than an assertion.

What failed — run 31988029078, batch 3 discovery-corpus: 5 of 9964 witnesses, 0 agreements held, 0 expected-red without a verdict.

doc_graph_has_no_dangling_links   dag/test/claim/doc_reachability_witness_test.dag
doc_graph_has_no_orphan_docs      dag/test/claim/doc_reachability_witness_test.dag
doc_graph_has_no_dangling_links   src/v2/lens/doc_reachability_test.dag
doc_graph_has_no_orphan_docs      src/v2/lens/doc_reachability_test.dag
doc_graph_is_clean                src/v2/lens/doc_reachability_test.dag

Two defects asserted from two files, plus the conjunction. No other witness failed.

Why it is not mine — established from git, not from a relay:

  • This PR's diff is 13 files, zero .md, zero under docs/ — six .dag and seven regenerated stage0 .rs.
  • origin/main carries docs/probes/root_b_primitive_repr_fork_2026-08-16.md, whose only link points at docs/plans/self-host-cargo-refusal-root-partition.md, which is not on main. That is the dangling edge, and nothing on main references the probe, which is the orphan. Both conditions are reproducible from origin/main alone.

Why the fix belongs elsewhere — #8333 (open, ready, mergeable) adds exactly docs/plans/self-host-cargo-refusal-root-partition.md and the doc_graph_roots.dag bind. It closes both rows at their cause. Landing a doc-graph repair inside a PR whose entire content is a derive-selection rule would attribute that repair to this change in git history permanently, in exchange for greening one run that clears itself when #8333 merges.

One line that is not a sixth failure. The trailing FLOOR-FINALIZATION-REFUSED … NativeBundleEscapingEntry states its own cause: "1 obligation subject(s) never ran (walk stopped before dependent batches); count law unevaluable." That is the finalizer correctly refusing to report a green it cannot establish after batch 3 stopped the walk — a refusal-because-unevaluable, printing at the same severity as a refusal-because-false. It is not a native-bundle defect.

Limit on my own claim, stated rather than glossed: --log-failed returns only failing steps, so this PR's seven map-key witness rows appear nowhere in that log — not as passes and not as failures. Their green is an inference from the arithmetic (9959 of 9964 passed, and discovery includes the test file), not a direct observation.

This PR is at 2/2 approvals, zero request-changes, MERGEABLE. I am not re-running (a re-run spends a scarce ~60–100 min verdict on a condition it cannot change) and not pushing. It is merge-ready once main is green.

— sent from quick-hawk-143

gunbc-ci-auto-heal and others added 3 commits August 17, 2026 06:14
# Conflicts:
#	src/v1/04_emit_info.dag
#	src/v1/04_infer.dag
#	src/v1/05_emit_rust.dag
#	src/v1/stage0/src/v1_compiler_emit_rust.rs
#	src/v1/stage0/src/v1_compiler_infer.rs
#	src/v1/stage0/src/v1_compiler_infer_emit_info.rs
#	src/v1/stage0/src/v1_compiler_trait_derive_emit.rs
The union resolution of the origin/main merge kept both sides of the
trait_derive_emit import block verbatim, so v1_clone_bounded_type_params,
v1_item_clone_bounded_param_names and v1_item_clone_undecided_head each
appeared twice — once from each side. Only v1_clone_impl_required_type_params
was genuinely new. Emitted bytes are unaffected: regen_stage0 --verify reports
regen_divergence_count=0 with no stage0 file changed, measured rather than
assumed.

Three further duplicate imports in the same two files (a doubled
`import v1.compiler.languages` line, NewlineIndex twice in one 04_infer list,
and a doubled `import std.content_hash` line) carry identical counts on
origin/main and are pre-existing, so they are reported rather than repaired
here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

The indentation nit is real and mine — deferring the fix deliberately, not dismissing it

Confirmed, at corrected locations. The cited lines 404/448/456 do not contain the field — they point at rust_scalar_checkpoint_render_base and a string-literal block. The finding is real anyway; only the positions rotted. Actual sites:

line 2298   indent 4   sibling 4    OK
line 2525   indent 4   sibling 4    OK
line 4836   indent 4   sibling 6    misaligned
line 7746   indent 4   sibling 14   misaligned
line 7777   indent 4   sibling 14   misaligned

Three of five, and it is my artifact: resolving the origin/main merge as a union kept each side's line verbatim, and Root A's side of those three hunks carried deeper indentation than mine.

(This is DESIGN §3's positional-citation class in miniature — a file:line pointer invalidated by edits above it, decaying without anyone touching the thing it names. The symbol-level claim, "the new map_key_required_type_names: field is misaligned against its siblings", survived and was checkable; the line numbers did not.)

Why I am not pushing it right now. Approvals are scored per head, so any new head — a merge commit included — drops them. This PR just reached 2/2 on dcbfd22bcdb, and it cannot merge until #8364 clears an inherited doc-graph red anyway. Spending both approvals on three characters of whitespace, on a fleet where a review round takes hours, is the wrong trade. If anything else requires a push, this rides along with it.

What is verified on the current head, since the merge could have disturbed the change and an inference from a pre-merge CI run would not show it:

PASS map_key_record_earns_eq_and_hash
PASS non_key_record_derives_no_hash
PASS map_value_position_does_not_seed
PASS map_key_requirement_propagates_to_field_types
PASS fn_return_type_seeds_the_requirement
PASS nullary_coproduct_key_gains_only_hash
PASS fn_field_record_as_map_key_refuses

All seven green by execution on the merged tree, plus regen_stage0 --verify: regen_divergence_count=0 and both constructs present in the regenerated seed (map_key_required 47, clone_impl_required 25 — a merge that silently dropped either side is exactly what a textual resolution of those generated files would have produced).

— sent from quick-hawk-143

gunbc-ci-auto-heal and others added 4 commits August 17, 2026 10:38
# Conflicts:
#	src/v1/stage0/src/v1_compiler_emit_rust.rs
Resolving the origin/main merge needed a self-consistent seed to build a
regen binary with: taking main's side of the one conflicted generated file
left v1_compiler_emit_rust.rs calling the derive functions without
map_key_required while its unconflicted sibling required it, so the crate
did not compile (8 x E0061). I restored the whole src/v1/stage0/src/ tree
from the pre-merge head to bootstrap, and that restore also resurrected a
file main had deleted and reverted a generated header main had corrected.

  src/v1/stage0/src/bin/frontier_probe_survey.rs — deleted again. main cut
  it in #8344 with the self-host frontier roster it surveyed; gunbc.ci_release_bins
  records that removal per-bin. It read compiler_frontier_sweep_order from the
  now-deleted frontier.dag, is unregistered in Cargo.toml, and carries no
  scaffold receipt.

  bootstrap_stage0_crate_layout_generated.rs — header restored to cite
  v2.compiler.self_host.stage0_crate_layout. v2.compiler.self_host.frontier is
  deleted; stage0_crate_layout.dag is the module carved out of it. Generated
  file, and nothing in this branch feeds its generator, so main's output is
  correct here; the file list was never affected, only the citation.

Swept the class rather than the two cited sites: exactly one resurrected file
and zero files missing versus main, and no other stage0 file cites the deleted
authority. regen_stage0 --verify remains at regen_divergence_count=0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
# Conflicts:
#	src/v1/stage0/src/v1_compiler_emit_rust.rs
@briansrls
briansrls merged commit 4d37f1f into main Aug 17, 2026
4 checks passed
@briansrls
briansrls deleted the session/quick-hawk-143 branch August 17, 2026 17:45
gunbai-bot Bot pushed a commit that referenced this pull request Aug 17, 2026
…and fix the alias-RHS spelling my cut changed

Three things, the second of which is a defect in this PR's own disclosure.

MERGE. Main's #8353 added corpus_has_v1_seed_source_indices and
declared_return_type_node to one import list; the first is deleted by this cut, so
the resolution keeps main's new name and drops the dead one. Generated stage0 was
regenerated from a wholly self-consistent seed -- taking --ours on only the three
conflicted .rs left a MIX of my generation and main's auto-merged files that
disagreed on #8353's new arities (E0061), so the whole stage0 tree was reset to one
coherent generation before regenerating. Three build+run passes to divergence 0.

RUNG INFLATION, FOUND BY AN INDEPENDENT READ AND REPAIRED RATHER THAN SOFTENED.
checkpoint_table_bypasses_identity_note claimed "its executing evidence is the
residue block in the checkpoint witness". That block was generated into
src/v1/stage0/src/compiler_tests.rs, declared `#[cfg(test)] mod compiler_tests;`
inside the v1-compiler crate -- while CI's only test step is
`cargo test -p v1-compiler-tests`, a SEPARATE package consuming v1-compiler as an
ordinary dependency, so cfg(test) is never set and the block never compiles.
Measured, not reasoned: that binary enumerates 30 tests, none of them this one,
against a positive control confirming the enumeration is non-empty. So a ceiling
disclosure cited evidence that had never run -- DESIGN 4b(1) violated in the
compiler's own self-description.

Repaired by making the claim TRUE: the assertions are re-authored as
v1.tests.claim.checkpoint_identity_keying_witness_test, enrolled in
v1_claim_scoped_witness_entries, and EXECUTED -- 7 rows PASS, including the
discriminating row where dag/std/nat.dag and src/v2/std/nat.dag share the spelling
Nat and answer differently, plus three residue rows asserting the current bypassing
answers so the gap stays counted. The superseded generated-Rust block is DELETED
rather than left standing as a second silent copy. Also annotated
reference_derived_use_lines_note, which still described the corpus_repr_is_faithful
gate this cut removes.

ALIAS-RHS SPELLING, a real consequence of the cut, fixed forward with a receipt.
root4_measure_missing_generics_witness_test required
'pub type PositiveInt = crate::std_nat::Nat'. Under the deleted corpus mode that
fixture emitted the std_nat path only because its synthetic closure happened to
contain no v1 sources -- the same declaration realizing differently by closure
composition, which is the defect this cut removes. Measured on the post-cut
emitter: it now emits 'pub type PositiveInt = i64;' AND emits std_nat.rs with
'pub type Nat = i64;', so both spellings denote the SAME type and only the choice
between equivalents changed. The row's subject -- the RHS staying bare rather than
gaining phantom type arguments -- is untouched, and both exclusions still hold.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
briansrls pushed a commit that referenced this pull request Aug 17, 2026
…aration identity (#8357)

* Root B: delete RustCorpusRepr, key Rust primitive realization on declaration identity

The global HostNative/FaithfulFreeMonoid emission mode decided what a primitive
realizes as by asking whether the COMPILATION CLOSURE happened to contain v1 seed
sources (a path-substring test over source_indices keys). That is a fact about who
else shares the closure, not about the type. Two declarations may share a spelling --
std.nat.Nat is CommutativeSemiring<Magnitude> and realizes natively, while
v2.std.nat.Nat is the Peano coproduct and must not -- so one global mode has to be
wrong for one of them, and a mixed closure is wrong for both.

Deleted at the root (DESIGN section 3 delete-first): RustCorpusRepr, its two variants,
corpus_has_v1_seed_source_indices, rust_corpus_repr, corpus_repr_is_faithful, the
EmitGraphInfo.corpus_repr field, and every threading parameter. Realization is now
keyed on the resolved declaring module (ident_span file).

The deletion's census, fixed forward rather than restored:

- cli_run.rs refused at 4 sites. Three were plumbing. The fourth, the whole-corpus
  semantic oracle's rust_corpus_repr field, is RETIRED with its subject: there is no
  corpus-global representation to report, and a field naming one would fabricate a
  fact with no referent.
- Realization is decided at REFERENCE sites, not only at the declaration. Eight sites
  were passing an empty identity, which correctly means "unknown" and so correctly
  yields no realization -- rendering the seed's own dag/std/integer.dag aliases
  structurally (UInt = crate::std_nat::Nat, PositiveInt = Nat<Magnitude> over
  Nat = i64, which is E0109). Added type_reference_decl_file and routed all eight.
- The v2 closure-stub triggers text-scan emitted output for a module reference, and
  the emitter's own emitted file contains every such pattern as a string literal --
  both the pattern searched for and the use line generated. The predicate matched
  itself; the deleted conjunct had been suppressing it on the seed closure. Anchored
  on a leading newline. This does NOT climb the rung: the grain is still a text scan
  and the stub's dissolve-on stands.
- Generated witnesses migrated, not deleted: the Int -> i64 checkpoint claim survives.
  The "fires under both corpus modes" assertion is replaced by a strictly more
  discriminating pair -- fires for the std declaration, declines for a same-spelled
  declaration elsewhere -- which the deleted mode could not express.

Two-arm receipt, both binaries run against the same pristine merge-base corpus
(a6bceb6, detached worktree, 0 dirty tracked files) so only the compiler varies:

  pole A (dag + src/v2, entry dag/std/nat.dag):  4 files,  23 lines
  pole B (src/v1 + dag, entry src/v1/04_env.dag): 9 files, 11 lines
  repeat controls: A/A-prime 0, B/B-prime 0 differing files (n=1 each)
  slack after classification: 0 lines on both sides of both poles

I PREDICTED EMPTY DIFFS AT BOTH POLES AND WAS WRONG AT BOTH. The prediction was
framed in the deleted mode's own vocabulary -- "pure-v2 closure" and "pure-seed
closure" are closure-provenance descriptions, and provenance is exactly what this
change stops consulting. dag/std/nat.dag's Nat realizing as i64 inside a
"pure-v2" closure is the cut working as designed; my description of the poles was
premised on the switch being deleted.

Pole B changed no realization at all -- 11 lines, all use-line synthesis, which is
the other half the mode had fused: import synthesis was gated on the same flag, so
an import-bearing module's use lines were a function of who shared its closure.

Pole A also fixed a fabricated body nobody was looking for: std.algebra::trim emitted
v1_rt::from_code_point((1 / 0)) in BOTH arms of a tautological if -- the declared
.dag divide-by-zero seam leaking into emitted Rust instead of the host realization.
It now emits v1_rt::trim(s).

Seed regenerates to a fixed point and lib + all bins check clean, verified against a
control that must fail (cargo check -Z definitely-not-a-real-flag) rather than an
exit code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Root B: state the checkpoint-table ceiling, and assert it instead of claiming it closed

I executed the witness I migrated in the previous commit and it FAILED, on its own
RED control. The failure is correct and the claim was not.

rust_scalar_checkpoint_render_base consults lookup_checkpoint FIRST, and that table
is keyed on the BARE dag_name. So declaration identity is bypassed for every name
carrying a checkpoint row. Int has one; Nat does not. The closure-provenance fork is
therefore CLOSED for table-absent names -- v2.std.nat.Nat correctly refuses to
realize natively -- and OPEN for table-present ones, where a v2-declared Int still
renders i64 exactly as the deleted HostNative arm would have.

This is not a regression introduced by the cut: the table was already bare-name keyed,
and the global mode was hiding the question. It is the honest rung, and the previous
commit's message and PR body claimed more than the code delivers.

Changes:

- The witness now discriminates on Nat, which actually reaches the identity arm:
  std declaration -> i64, same spelling declared under src/v2 -> None, unknown
  identity -> None. A regression to a bare-name rule reds it.
- It ALSO asserts the residue -- Int from a v2 declaration still renders i64 -- so the
  gap is counted rather than assumed closed. That assertion is expected to FLIP to
  None when the checkpoint rows gain a declaring-module column; the flip is the
  dissolution signal, not a regression. The rows are cited upstream data and are not
  this lane's to re-key unilaterally.
- checkpoint_table_bypasses_identity_note records the ceiling at the carrier rather
  than in a PR description that dies with the merge: mechanically preventable, not
  structurally impossible, with its next-rung trigger named.
- reference_use_lines_representation_invariant_note rewritten; it still narrated a
  corpus_repr_is_faithful gate that no longer exists (review 52750).
- Three prose rows elsewhere cited the deleted mechanism as live. Marked rather than
  rewritten, because a class can outlive the trigger it was described by:
  extdeps/shell/exec.dag and plans/model_realization_fork.dag get a
  SUPERSEDED-MECHANISM clause telling their owners to re-measure, and the two
  dag_v2_defork_audit.dag items proposing to GENERALIZE RustCorpusRepr get a
  SUPERSEDED PREMISE clause -- there is no seam left to generalize, so per DESIGN
  section 3 they are quarry, not authority.

Witness executed green: 1 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Root B: correct the next-rung trigger, and widen the residue evidence from 1 name to 3

Two corrections from vivid-wren-870, who owns the checkpoint rows and answered the
census question I could not answer myself.

THE TRIGGER I NAMED WAS THE WRONG SHAPE. checkpoint_table_bypasses_identity_note said
the next rung is "the checkpoint rows gain a declaring-module column". They refused
that, and the argument is right: a TypeCheckpoint row is cited to the Rust reference
and states how RUST spells a type, whereas which dag module declares Int is a
gunbc-corpus fact. A declaring-module column puts corpus data inside an extdeps
upstream authority -- a layer inversion, the same class as a row being asked a
question its cited authority cannot answer. The expected terminal shape is TWO
authorities: extdeps keeps Rust type -> spelling, a corpus-side binding says this dag
declaration realizes as that Rust type, and lookup_checkpoint keys on the second.
That also lets the seven affected names differ from each other, which one column keyed
on a single module cannot express. Recorded as their proposal, unbuilt, not as a
decision -- and left with them rather than taken, since re-keying a cited authority
inside an emitter PR is the §3 move this lane exists to undo.

THE POPULATION IS SEVEN, NOT ONE. Their census: Int, Float, Bool, Symbol, Unit,
String and Hash all carry a checkpoint row AND are declared under src/v2. Bytes,
Secret and Json are not. Carried with their stated bound -- a line-start `type` grep,
so a lower bound, and silent about how many sites bite in any real closure.

The witness now asserts the bypass for the three vivid-wren separated as the same
SHAPE as Int, where the v2 declaration is a genuinely different structure rather than
a spelling coincidence: Int = GroupCompletion<Nat>, String = FreeMonoid<Char>,
Bool = a two-variant coproduct. The other four are deliberately unasserted and the
witness says why: Symbol and Unit are bodyless and may be declared-abstract rather
than competing realizations, and I have measured neither Float nor Hash. Absence
there is UNMEASURED, not cleared.

Worth recording because it generalizes: none of this needed an emitting closure. The
probe is a pure function, so it asks a question the corpus does not currently
exercise -- which is why it caught what two poles, three repeat controls, a slack
decomposition and an order test all missed. Nothing in those closures declares a
competing Int, so the emitted bytes were identical either way. An artifact diff sees
consequences you did not predict; a direct probe sees non-consequences you predicted
wrongly. Neither dominates.

Witness executed green: 1 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Root B: add Hash to the pinned residue set — the one bypass that costs a guarantee

vivid-wren-870 followed up on the Hash row I flagged as odd, and it is not odd: it is
the same structural-mismatch shape as Int/String/Bool, with one consequence the other
three do not carry. Both ends re-read here rather than taken on their word, since they
explicitly asked that their paragraph not become the evidence:

  row target   v1_rt::Hash              -> src/v1/stage0/src/v1_rt.rs  pub type Hash = String
  v2 decl      src/v2/std/node.dag      -> type Hash = Fnv1a64Structural
  carrier      dag/std/content_hash.dag -> type Fnv1a64Structural { digest: Fnv1a64StructuralDigestHex }
  refinement   dag/std/content_hash.dag -> type Fnv1a64StructuralDigestHex = String where lower_hex_16

So the seed end is a bare String alias and the v2 end is a single-field record whose
field carries a where-refinement -- the same class as String = FreeMonoid<Char>, not a
spelling coincidence.

WHY IT IS WORSE THAN THE OTHER THREE. std.content_hash content_hash_family_constructor_note
states the public forgeable record constructors are DELETED and that the where-refinement
IS the construction wall. Realizing that type as a bare String alias renders away the
carrier the wall is attached to. A name-keyed spelling row can therefore silently drop a
§4b construction rung -- not merely pick the wrong representation.

NO LIVE SITE IS CLAIMED. Neither of us emitted a closure that reaches a v2-declared Hash
through lookup_checkpoint. This is a mismatch between two declarations plus a mechanism
by which a wall could be dropped, and the witness and the carrier note both say so in
those terms.

The residue set is now four (Int, String, Bool, Hash) and the unasserted remainder is
three (Float, Symbol, Unit) -- Symbol and Unit bodyless and possibly declared-abstract
rather than competing realizations, Float simply unmeasured. Absence there stays
recorded as UNMEASURED, never as cleared.

Also noted, so the arity reading stays clean: v1_rt::Hash and Fnv1a64Structural are
both arity 0, so this row is NOT an instance of the checkpoint-arity defect vivid-wren
removed from these rows. It is purely the identity-keying class.

Witness executed green: 1 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Root B: derived use-lines must not name a provider the emitted code never paths into

CI's seven-crate stage0 partition gate refused my head with E0432, and it was a
SIXTH red on that run — the other five are main's inherited doc-graph rows. This
one is mine, found by reading the error rather than matching the row name.

    v1-stage0-std-core  : unresolved import crate::v1_compiler_emit_core_support
    v1-stage0-emit-core : unresolved import crate::std_types

THE MECHANISM, one sentence: a derived use-line must name a provider the EMITTED
code actually paths into, and two families of reference do not qualify.

  HOST-REALIZED CALLS. emit_typed_call intercepts a family of free calls by name --
  to_string, discriminant, get, with, empty_set among them -- and renders each as a
  host expression. to_string(value: e) becomes (e.clone()).to_string(), the Rust
  METHOD, never a path to v1.compiler.emit_core_support. So the SOURCE reference is
  real and resolves to a real provider, while the EMITTED module never mentions it.

  HOST-REALIZED TYPES. List, Set and Map are structural containers rendered inline
  as Vec and HashMap, so a std_types re-export names a provider the emitted code
  never paths into either.

Both are dead imports in a single-crate build and FATAL under the partition, where
the provider is a member of a DIFFERENT crate. That is why the seed built clean and
only the partition gate caught it.

Fixed by reading two existing authorities rather than minting a roster:
v1.compiler.infer_method builtin_function_registry and std.types is_container_type.
The filter sits beside the existing is_kernel_type filter, which is the same idea.

LATENT, NOT NEW — and this is the second time this cut has surfaced a defect it did
not introduce. Import-free modules ALWAYS received derived use-lines, so the
derivation was always capable of emitting a dead cross-crate import; deleting the
corpus_repr gate extended it to import-bearing modules, where v1.std.core is the
first member carrying an intercepted free call. Same shape as the closure-stub
self-match: the deleted mode was suppressing the question, not answering it.

CEILING, recorded at the carrier rather than claimed closed: the builtin roster
covers five of the eight names the call emitter intercepts (empty_map, map and
utf8_decode_bytes are absent), so a module that both references one of those three
across a module boundary AND lands in a different partition crate would still
refuse. None exists today. The terminal shape is the one the closure-stub note
already names -- derive use-lines from the EMITTED reference set rather than from
source references, which needs no roster of intercepts at all, because an
intercepted call emits no path.

Verified locally, not inferred: all seven partition crates exit 0 with zero errors,
cargo check -p v1-compiler --all-targets zero errors, fmt clean, and the migrated
checkpoint witness still passes.

One authoring note for the record: the first attempt at this commit's carrier note
refused regen with "function 'emit_rust' not found in scope", because the note text
contained an unescaped brace pair that a .dag string interpolates.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* chore: regenerate drifted generated artifacts (ci auto-heal)

* Root B: disclose the checkpoint bypass at artifact grain, and bound the roster

Two carrier notes. No behavior change: both are data string
declarations, regenerated into the seed (gen2 == gen3 fixed point).

1. checkpoint_table_bypasses_identity_note gains the MEASURED
   consequence. The bare-name checkpoint bypass is observable in the
   emitted FILE SET, not only in the emitter's answer. In one v2-only
   closure v2_std_integer.rs is present before the cut and absent
   after: its sole type-position consumer was one generated re-export,
   Int now reaches the checkpoint, and the module goes unreferenced.

   The drop is the minority case. Across eleven closures measured by
   smart-ibex-716 the module disappears in 2 and SURVIVES in 9 with
   its content changed from GroupCompletion<Rc<Nat>> to i64 -- so the
   wrong realization of the v2-declared Peano-completion type is
   already emitted, and only a consumer is absent (that census found
   zero type positions; the surviving bare hits were string literals).

   The residue is therefore not a dead re-export being dropped, which
   was this note's first reading and is corrected here. It is a wrong
   realization sitting inert with no diagnostic, so the hazard trigger
   is a type position APPEARING, not the re-export returning.

2. numeric_realization_roster_extension_note bounds a live enumeration
   that had no extension rule. A row may be added only for a
   declaration whose native realization is already true, never to
   silence an E0308/E0109 at a failing site -- that converts one
   site's diagnostic into a corpus-wide realization change by editing
   the roster rather than the fact it reports (§5 workaround). The
   tell is an author who cannot say what the added module's type IS at
   the target without referring to the site that failed.

Neither consequence was in this PR's original receipt; a content-only
diff of one closure reads both as offset noise.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Repoint the Root B probe bind: its primary_work cited a symbol this PR deleted

Review 52865 (cursor/composer-2.5, REQUEST_CHANGES) found a real §3 stale
citation caused by this cut. The HandAuthoredDocBind for
root_b_primitive_repr_fork_2026-08-16 named
v1.compiler.infer rust_corpus_repr as its primary_work -- a declaration
this PR deletes -- so the bind pointed at nothing.

  primary_work: v1.compiler.infer.rust_corpus_repr          (deleted)
             -> v1.compiler.emit_rust.decl_file_realizes_natively
  additional_works: + type_reference_decl_file
                    (rust_seed_host_numeric_alias retained)

The row's DISSOLUTION was stale in the same way and the review did not
name it: it read "retires when RustCorpusRepr's two fused axes are split",
and this PR performs exactly that split, so the condition had already
FIRED. A fired trigger left in place is the same defect one layer up.
Replaced with the live successor condition -- the checkpoint table ceasing
to key on the bare dag_name -- which is the next-rung trigger
checkpoint_table_bypasses_identity_note already states.

The bind is repointed, NOT removed. Deleting it would leave the probe
document with zero inbound edges, which is the orphan defect this branch
spent the evening tracing; the document keeps exactly one bind.

VERIFIED, with controls in both directions:

  module v1.compiler.emit_rust declared in 05_emit_rust.dag   yes
  decl_file_realizes_natively   top-level decls                1
  rust_seed_host_numeric_alias  top-level decls                1
  type_reference_decl_file      top-level decls                1
  control: a fabricated name                                   0

WHAT THE WITNESS CAN AND CANNOT SHOW. The review predicted this would
break doc_graph_works_refs_all_resolve on the long cadence. That witness
is ALREADY red on origin/main, which still has rust_corpus_repr and whose
citation therefore resolves -- measured by building claim_batch inside a
pinned main worktree (workspace_root is baked at compile time, so a
binary from another tree would have scanned the wrong corpus). Main fails
the same five functions, and on the named function the two trees agree
exactly:

  main   FAIL doc_graph_works_refs_all_resolve   PASS ..._planted_doc_graph_controls_all_refuse
  mine   FAIL doc_graph_works_refs_all_resolve   PASS ..._planted_doc_graph_controls_all_refuse

The PASS is the paired reading: the control refusing proves the machinery
produces both values, so the FAIL is a real corpus finding and not a dead
instrument. The census is red for pre-existing stale citations elsewhere,
so it can neither confirm nor refute this row -- the name-level resolution
above is what establishes the fix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Merge main (Root T5a), make the ceiling's evidence actually execute, and fix the alias-RHS spelling my cut changed

Three things, the second of which is a defect in this PR's own disclosure.

MERGE. Main's #8353 added corpus_has_v1_seed_source_indices and
declared_return_type_node to one import list; the first is deleted by this cut, so
the resolution keeps main's new name and drops the dead one. Generated stage0 was
regenerated from a wholly self-consistent seed -- taking --ours on only the three
conflicted .rs left a MIX of my generation and main's auto-merged files that
disagreed on #8353's new arities (E0061), so the whole stage0 tree was reset to one
coherent generation before regenerating. Three build+run passes to divergence 0.

RUNG INFLATION, FOUND BY AN INDEPENDENT READ AND REPAIRED RATHER THAN SOFTENED.
checkpoint_table_bypasses_identity_note claimed "its executing evidence is the
residue block in the checkpoint witness". That block was generated into
src/v1/stage0/src/compiler_tests.rs, declared `#[cfg(test)] mod compiler_tests;`
inside the v1-compiler crate -- while CI's only test step is
`cargo test -p v1-compiler-tests`, a SEPARATE package consuming v1-compiler as an
ordinary dependency, so cfg(test) is never set and the block never compiles.
Measured, not reasoned: that binary enumerates 30 tests, none of them this one,
against a positive control confirming the enumeration is non-empty. So a ceiling
disclosure cited evidence that had never run -- DESIGN 4b(1) violated in the
compiler's own self-description.

Repaired by making the claim TRUE: the assertions are re-authored as
v1.tests.claim.checkpoint_identity_keying_witness_test, enrolled in
v1_claim_scoped_witness_entries, and EXECUTED -- 7 rows PASS, including the
discriminating row where dag/std/nat.dag and src/v2/std/nat.dag share the spelling
Nat and answer differently, plus three residue rows asserting the current bypassing
answers so the gap stays counted. The superseded generated-Rust block is DELETED
rather than left standing as a second silent copy. Also annotated
reference_derived_use_lines_note, which still described the corpus_repr_is_faithful
gate this cut removes.

ALIAS-RHS SPELLING, a real consequence of the cut, fixed forward with a receipt.
root4_measure_missing_generics_witness_test required
'pub type PositiveInt = crate::std_nat::Nat'. Under the deleted corpus mode that
fixture emitted the std_nat path only because its synthetic closure happened to
contain no v1 sources -- the same declaration realizing differently by closure
composition, which is the defect this cut removes. Measured on the post-cut
emitter: it now emits 'pub type PositiveInt = i64;' AND emits std_nat.rs with
'pub type Nat = i64;', so both spellings denote the SAME type and only the choice
between equivalents changed. The row's subject -- the RHS staying bare rather than
gaining phantom type arguments -- is untouched, and both exclusions still hold.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: gunbai-bot[bot] <289086189+gunbai-bot[bot]@users.noreply.github.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