Skip to content

A machine scalar is never raised to a reference layer: one decision, both positions - #8384

Merged
briansrls merged 2 commits into
mainfrom
rcwrap/flow-decision
Aug 18, 2026
Merged

briansrls merged 2 commits into
mainfrom
rcwrap/flow-decision

Conversation

@briansrls

@briansrls briansrls commented Aug 17, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The v1 Rust emitter wrapped machine scalars in Rc, emitting Rc<i64> and Rc<Nat> where a bare
i64 was owed. The cause is that the wrap decision consulted two facts, neither of which is about
the carrier: leaf-name membership in a shared_types set, and a string-prefix test for "Rc<"
on the already-rendered text. A name and a rendered prefix cannot answer whether a thing is a
reference layer.

This routes both rendering positions through the authority that already knows —
rust_seed_host_numeric_alias, which keys on declaration identity (name and declaring file, via
numeric_realization_declaring_modules). No second roster is minted.

Two positions, because fixing one alone creates an asymmetry rather than a repair — my first attempt
patched only the parameter path and produced a new error class (cannot multiply Rc<i64> by i64),
which is why both land together:

  • the parameter / alias path — render_rust_shared_type_with_optional
  • the applied-type-argument path — rust_render_checkpoint_scalar_bare

Authority is src/v1/05_emit_rust.dag; the generated src/v1/stage0/src/v1_compiler_emit_rust.rs
is produced by regen_stage0, never hand-edited.

Relationship to this session's work item

The work item is delete v1_interpreter.rs, and this PR does not delete it — that file is
still present on HEAD. Flagged by cursor/composer-2.5 in review 53031, and the observation is
correct; the title has been corrected to describe the diff.

What this is instead: the terminal direction for that lane is native, never a rebuilt
mini-interpreter and never wholesale restoration. Going native requires the emitted evaluator to
compile, and it did not. The 483-site wall was censused by mechanism rather than by count and
resolved to roughly four model↔realization forks; this PR removes the largest, on the v1 emitter —
the sanctioned bootstrap producer, not the interpreter being deleted. The remaining forks are
separate cuts.

Measurement

Both arms measured on merged main (7c1c633be7), keyed on (file, code, normalized message) for
causes and (file, line, col) for sites. Line-keyed diffing is invalid here — emitted line numbers
move when the emitter changes, which made an early A/B read meaningless (461 added / 430 removed)
until it was re-keyed.

arm A (main) arm B (this PR) delta
distinct causes 159 148 −11
error messages 539 417 −122
distinct sites 483 361 −122
scalar sites 151 29 −122
emitted Rc<i64> 12 0 −12
emitted Rc<Nat> 160 40 −120

122 sites removed, 0 added causes. Arm A measures identically on merged main and on the
pre-merge base, so the delta is attributable to this change rather than to anything squashed
alongside it.

Test plan

  • regen_stage0 on merged base → wrote 145 files, 0 stage0 drift; the committed artifact is
    byte-identical to what merged main produces.
  • regen_stage0 --verify → regen_divergence_count=0, two-generation fixed point green.
  • Control: Option<Absent> in emitted eval → 0, both arms.
  • Emit control: 92 files emitted, emit_rc=0.
  • Corpus CI on this head.

Scope and rung — stated as limits, not achievements

Numeric only. String checkpoint rows are deliberately untouched; widening the guard to
lookup_checkpoint rows is a separate cut.

29 scalar sites survive and are not claimed fixed.

Rung: mitigatable. The guard inherits rust_seed_host_numeric_alias's name-and-file keying
ceiling — the same ceiling recorded on checkpoint_table_bypasses_identity_note. The next rung is
the modeled layer transition in src/v2/compiler/wrap_decision.dag, whose wrap_decision_flow_gate
exists and is witnessed but has zero production callers today; its own note says it is not wired
into 06_translate. Calling this structural would be rung inflation.

Also in this branch

dag/tools/p1_emit_probe.dag was added as a branch-local experiment and is deleted in
02ba2c1c76
before merge. It answered one question by execution — can v2's emitter semantics
serve as the bootstrap producer? P1 refuses at the first bridge
(parse_tree_to_emitted_node_no_matching_row, v2.std.grammar) while P0 emits 92 files, so P0
remains the measured producer
. That selection is the durable fact; the transport that produced it
had no remaining consumer. Deleted rather than registered as a scaffold, per DESIGN §5 — a
dissolution condition describes how debt ends and does not authorize creating it. Raised by
cursor/composer-2.5 in review 53026.

05_eval.dag under P1 timed out at 1500s with no verdict. Not extrapolated from the nat.dag
control.

…both positions

The emitted 05_eval closure carried 151 of 483 distinct rustc sites in which a
numeric scalar sat behind Rc. `Rc<i64>` is not a sharing decision, it is a
category error: every arithmetic and comparison operator refuses on it, which is
why one wrong decision produced many sites.

THE RULE THAT PRODUCED THEM decides from two facts, neither about the carrier:
membership by leaf NAME in shared_types, guarded by a string PREFIX test for
"Rc<" against already-rendered text. So one carrier reached by two spellings is
decided twice, and the prefix guard cannot see a layer through an alias.
Measured on this base, where Nat realizes natively as i64: 172 scalar-wrap
spellings, 12 literal `Rc<i64>` and 160 `Rc<Nat>` -- one category error in two
spellings, both appearing on a single emitted line:

  measure_scale_fraction_floor<Q, S>(m: Rc<Measure<Q, S, Rc<i64>>>, num: Rc<Nat>, ...)

the type argument rendering the primitive, the parameter rendering the alias.

BOTH POSITIONS NOW CONSULT ONE AUTHORITY (rust_seed_host_numeric_alias, via the
declaring file) instead of re-deciding from rendered text. Fixing only the
parameter half first was measured and REJECTED: it left `Rc<i64>` in argument
position and produced `Rc<i64> * i64`, an operator refusal neither uniform
answer produces -- a mixed layer is worse than either. The applied-argument half
sits in rust_render_checkpoint_scalar_bare, which despite its name applied the
shared wrap after the scalar authority had already answered.

RECEIPT, entry src/v2/compiler/05_eval.dag, base 0ce4ce6, site identity
(file, code, normalized message) because emitted line numbers move:

                    arm A -> arm B
  distinct causes     159 -> 148
  error messages      539 -> 417
  distinct sites      483 -> 361     (-122)
  scalar sites        151 ->  29     (-122)
  ADDED causes          0            (removal, not movement)
  emitted Rc<i64>      12 ->   0
  Option<Absent>        0 ->   0     (merged #8376 not regressed)
  regen --verify            divergence_count=0

SCOPE, stated rather than implied: this closes the NUMERIC scalar case, the whole
of the measured Rc<primitive> population. It deliberately does not consult
lookup_checkpoint, whose rows include non-scalar carriers such as String that may
legitimately be shared; widening there is a separate cut with its own evidence.
29 scalar sites survive and are not claimed fixed.

RUNG (DESIGN 4b): MITIGATABLE, and the ceiling is inherited rather than novel.
rust_seed_host_numeric_alias keys on a NAME plus a declaring-file check, not on
resolved declaration identity -- the same ceiling checkpoint_table_bypasses_identity_note
records for the neighbouring table. This makes the numeric-scalar wrap not happen;
it does not make a wrapped scalar unwritable. Next rung is the modeled layer
transition wrap_decision.dag already names, at which point an agreeing flow is
DERIVED rather than guarded.

Also adds dag/tools/p1_emit_probe.dag, the producer experiment that settled which
emitter to repair: the v2 emitter run under the v1 interpreter refuses at the
FIRST bridge on a real module (parse_tree_to_emitted_node_no_matching_row -- zero
grammar-relation rows match a real .dag token-class frontier), while the v1
emitter produces 92 files. So P0 remains the measured bootstrap producer and this
repair is on the selected producer, not on the interpreter being deleted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot
gunbai-bot Bot force-pushed the rcwrap/flow-decision branch from 31e5837 to 09fe505 Compare August 17, 2026 21:47
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review August 17, 2026 21:47
…ntation goes

dag/tools/p1_emit_probe.dag was branch-local experiment tooling. It asked one
question -- can v2's emitter semantics serve as the bootstrap producer? -- and
answered it by execution: P1 refuses at the first bridge
(parse_tree_to_emitted_node_no_matching_row, v2.std.grammar), while P0 emits 92
files. That measurement selected P0 and is recorded in the PR receipt; the
transport that produced it has no remaining consumer.

Zero references to p1_emit_probe / p1_probe_eval / p1_probe_small_control exist
outside the deleted module itself.

Deleted rather than registered as a scaffold. DESIGN §5 rules that a dissolution
condition describes how admitted debt ends and does not authorize creating it,
so marking it would have required an operator verdict on this exact population
-- and buying that verdict for tooling whose question is already answered spends
review and maintenance time against a benefit of zero. The same section names
the correct arm directly: the canary may run, produce measurements, and be
deleted before merge, leaving the modeled facts the final route consumes.

The emit-side repair is unaffected: the probe never entered any production path,
and the fix's authority is src/v1/05_emit_rust.dag.

Raised by cursor/composer-2.5 in review 53026 on #8384.
@gunbai-bot

gunbai-bot Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Fixed in 02ba2c1c76 — the probe is deleted, not marked.

Verified the finding against the tree before acting: dag/tools/p1_emit_probe.dag carried only a // dissolve-on: comment and a LiveTreeDisposition field, no typed Disposition = Scaffold / DissolutionCondition like the sibling in tools.self_host_curated_probe_cargo, and a corpus grep found zero references to p1_emit_probe / p1_probe_eval / p1_probe_small_control outside the module itself. Accurate on both halves.

I took the deletion arm rather than the scaffold-registration arm, and the reason is worth stating because the review offered both as equivalent. DESIGN §5 rules that a dissolution condition describes how admitted debt ends and does not authorize creating it — the marker is a lifecycle fact, not permission — so registering this would have required an explicit operator verdict on this exact population. Buying that verdict for tooling whose question is already answered spends operator, reviewer and maintainer time against a benefit of zero. The same section names the arm that actually applies: the canary may run, produce measurements, and be deleted before merge, leaving the modeled facts the final route consumes.

What the probe bought is kept; only its implementation goes. It asked whether v2 emitter semantics could serve as the bootstrap producer and answered by execution — P1 refuses at the first bridge (parse_tree_to_emitted_node_no_matching_row, v2.std.grammar) while P0 emits 92 files — so P0 remains the measured bootstrap producer. That selection is the durable fact and it is in the receipt; the transport that produced it has no remaining consumer.

The emit-side repair is unaffected: the probe never entered a production path, and the fix's authority is src/v1/05_emit_rust.dag. Confirmed post-deletion: zero stage0 drift, and the commit touches no file outside the probe.

Also on the record for this PR, unchanged and not claimed away by the fix above: 29 scalar sites survive and are not claimed fixed, and the rung is mitigatable — the guard inherits rust_seed_host_numeric_alias's name-and-file keying ceiling. The modeled climb is routing the decision through wrap_decision.dag, which exists and is witnessed but today has zero production callers.

— sent from gentle-dove-833

@gunbai-bot gunbai-bot Bot changed the title delete v1_interpreter.rs (eval emit) A machine scalar is never raised to a reference layer: one decision, both positions Aug 17, 2026
@gunbai-bot

gunbai-bot Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Title and body corrected — the remark was right and the defect was bigger than the title.

v1_interpreter.rs is indeed still on HEAD; I confirmed with git ls-tree. The title was the auto-opened work-item name, not a description of the diff, and checking it surfaced that the body was still the unedited template with TODO placeholders. Every receipt I produced had gone into commit messages and PR comments, so the one document reviewers read first carried none of it. Fixed both: the title now describes the diff, and the body carries the summary, the two-arm measurement, the test plan, and the scope/rung limits.

On the relationship the review flagged, stated in the body rather than left implicit: the work item is to delete v1_interpreter.rs, this PR does not delete it, and the terminal direction for that lane is native — never a rebuilt mini-interpreter, never wholesale restoration. Going native needs the emitted evaluator to compile, and it did not. The 483-site wall censuses by mechanism to roughly four model↔realization forks; this removes the largest of them, and it lands on the v1 emitter — the sanctioned bootstrap producer — not on the interpreter being deleted. Those are different files and conflating them is what makes "fixing v1" look like work on the thing being deleted. The remaining forks are separate cuts.

Nothing in the code changed for this; it is a title/body correction only, so the approvals on 02ba2c1c76 stand against unchanged content.

Restating the two limits that both approvals correctly declined to launder: 29 scalar sites survive and are not claimed fixed, and the rung is mitigatable, inheriting rust_seed_host_numeric_alias's name-and-file keying ceiling. The next rung is wrap_decision.dag, which exists and is witnessed but has zero production callers — its own note says it is not wired into 06_translate.

— sent from gentle-dove-833

@briansrls
briansrls merged commit 0cd36b5 into main Aug 18, 2026
4 of 5 checks passed
@briansrls
briansrls deleted the rcwrap/flow-decision branch August 18, 2026 00:03
gunbai-bot Bot pushed a commit that referenced this pull request Aug 18, 2026
Three conflicts, resolved by rule rather than by side:

- runner_slot_provision_witness_test.dag: main still carries the import block this
  branch deleted. Took HEAD (import-free). One-sided re-add of a deleted class.
- 05_emit_rust.dag: took main's semantic content for both hunks -- #8384's
  rust_seed_host_numeric_alias arm, which stops a machine scalar being raised to a
  reference layer -- then re-applied the cut's qualification to code positions.
- v1_compiler_emit_rust.rs: the generated-artifact merge driver refused, as designed
  (unmerged, no markers, regeneration recipe). Took main's generated bytes: this
  branch's .dag now carries main's semantics for that file, and qualification does not
  change emitted output. Owed a regenerate once the tree is green.

The string-literal control caught a real defect in the qualification pass: it had
rewritten names inside quoted regions, including three lines of EMITTED RUST SOURCE
(Rc<v1.std.core.NewlineIndex>), which would have emitted invalid Rust. Four in-string
occurrences reverted by a quote-aware pass; control re-run clean.

Import discipline verified after merge: zero .dag files carry an import statement.
gunbai-bot Bot pushed a commit that referenced this pull request Aug 18, 2026
…the import era

The merge resolution took main's v1_compiler_emit_rust.rs on the reasoning that this
branch's .dag now carried main's semantics and qualification does not change emitted
output. That was wrong, and CI caught it: 9 build errors across four classes, including

    unresolved import crate::v1_std_core::CompilerDiagnostic::UnlistedImportUse
    no variant named UnlistedImportUse found for enum CompilerDiagnostic

main's generated bytes still carry import-era machinery this cut deleted, so taking them
re-admitted the class into the seed -- the same trap recorded two commits ago and then
walked into anyway. The other errors (missing corpus_repr, build_emit_graph_info arity)
are the same era skew: main's emitter compiled against main's 04_infer, not this branch's.

Restored to dd598f3, byte-identical to the seed that built successfully at fd9aaea
(the witnesses job there reached the claim_executor invocation, so the build passed).

#8384's rust_seed_host_numeric_alias fix is NOT lost: it lives in src/v1/05_emit_rust.dag,
which is the authority. It reaches the generated .rs at the next regen. The .dag and the
.rs are deliberately allowed to drift until then.

Rule for this branch, stated so it is not relearned a third time: a generated artifact
must be taken from the side whose SOURCE this branch carries, never from the side whose
semantics are wanted. Semantics are merged in the .dag; bytes follow at regen.
gunbai-bot Bot pushed a commit that referenced this pull request Aug 18, 2026
…ted bytes

7 x E0308 'expected RustCorpusRepr, found String', all in the module added to lib.rs by
the previous merge. Era skew, and the direction matters: this branch's .dag SOURCE already
carries main's rust_scalar_checkpoint_render_base(dag_name, decl_file) signature, taken
with #8384's hunks; only the generated .rs still carries the old corpus_repr form, which is
the drift deliberately accepted under the source-follows rule.

So the witness is compatible with this branch's source and incompatible only with its stale
bytes. Removed from lib.rs; file RETAINED and re-declaration owed once regen makes the
generated signature match -- deleting it would discard evidence that becomes valid at regen,
and this witness exists precisely to repair a rung-inflation defect where seven assertions
were authored, type-correct and never executed.

Records the authoring error: the module was declared because the file existed. Unioning two
module lists is not a merge resolution -- added members must typecheck against the side whose
generated bytes are kept.
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