Skip to content

XL-R-2: Consume the affected-set bound in regen — a round adjudicates only the mirrors one edit can change, and refuses when the bound cannot locate the edit - #9757

Merged
gunbai-bot[bot] merged 10 commits into
mainfrom
session/deep-bat-536
Aug 30, 2026

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Aug 30, 2026 •

Copy link
Copy Markdown
Contributor

This PR is not a cost win, and must not be read as one. Measured by claim_executor --regen-round-cost (BuildBuddy runner 192.168.241.2, tree 90fb11ed, planted single-module edit): the five phases this change makes change-denominated — mirror_write, candidate_verify, adjudicate, hand_verify, digest — sum to 228 ms of a 465,493 ms round on a warm rustfmt cache, and 32,176 ms of a 743,655 ms round on a cold one (0.05% and 4.3%; rustfmt_spawns 4 vs 574 — #9738's normalize memo and disk cache is what separates them, and the honest figure is the range, not either endpoint). For a one-mirror change the round is rebuild_from_installed 230s (49%), compile.emit 163s (35%), corpus_load 38s, compile.reconcile 23s — 97% between them, and none of the five. That reading should have been taken before this code was written rather than after. What this PR delivers is the bound consumed, with its refusal arm and its safety split; it is the precondition the emit_rust follow-up needs, which is scaffolding for the win, not the win. Full per-phase receipt in the comment below.

XL-R-2, and where it stands against what COMPLETE means

XL-R (regen change-denomination) is complete when a one-mirror regen round, measured by claim_executor --regen-round-cost, is denominated in the change rather than the corpus, with the whole-population round as the byte-identical control.

step subject state
XL-R-0 instrument #9735 + cost-shape #9738 merged
XL-R-1 the bound, gunbc.regen_affected_set #9744 merged
XL-R-2 consumption in required_regen — this PR open
XL-R-3 emit fold, src/v1/05_emit_rust.dag emit_rust (163s) next, separate PR, consumer = this PR's selection
XL-R-4 rebuild_from_installed via gunbc.stage0_crate_partition_generated (230s) not this lane
XL-R-5 corpus_load / reconcile residual (61s) sized after R-3

R-2's standing against that definition, stated honestly: it does not advance it measurably. A one-mirror round is unchanged before and after, to within the phases the selection reaches — 228 ms of 465,493 on a warm rustfmt cache, 32,176 ms of 743,655 on a cold one. R-2 makes the selection exist, correct and refusing, at the point R-3 needs it; the denomination of the round changes at R-3 and R-4. R-2 is a precondition, not a fraction of the goal.

gunbc.regen_affected_set has answered which committed mirrors can this edit change since #9744, and nothing consumed it. This makes it a selection the regen round acts on.

The split that makes a selection safe

A round asks two different questions of the compared population, and only one may be scoped.

  • Population identity — which mirrors exist on each side. EmittedNotCommitted, CommittedNotEmitted and the hand-maintained shadow are identity joins over rosters. They read no bytes, so there is nothing here for a selection to save and everything for one to hide. They stay whole population under every scope.
  • Byte adjudication — whether each committed mirror equals what emit produced for it. A read, a rustfmt normalization and a comparison per member, plus both tree digests and the candidate write. This is what the bound is a bound on, and this is what the scope selects.

So the scope bounds which mirrors' bytes are compared. It never bounds which mirrors exist.

Three arms, and the third is the point

bound arm scope round
AffectedMirrors AffectedScope { members } adjudicates/writes/digests the members intersected with the committed roster
WholePopulation WholePopulationScope the existing path, byte for byte
EditedSetUnlocatable ScopeUnlocatable refuses

EditedSetUnlocatable does not fall back to the whole population. "Regenerate everything" and "the selection could not answer" are different states, and a widening arm here would be denominated in the corpus rather than in the change — the absorbing fallback DESIGN §5 forbids, in the one place where its cost is unbounded.

The drift gate is never scoped

measure_generated_surface — the required CI phase and the behavioural receipt — passes WholePopulation explicitly. The selection is for the author's round, over a tree that gate has already verified. That is also the stated precondition: a scoped round cannot discover a mirror outside its selection that was committed stale, and the unscoped gate on every push and pull request is what closes it. The model carries the argument rather than assuming it.

Evidence

regen_emission_scope_tests in required_regen_host — 6 passed, 0 failed:

  • the whole-population arm selects the whole population;
  • an affected arm selects the intersection, not the bound's list verbatim (the red a member outside the tree discriminates);
  • an edit touching no mirror selects nothing (an answer, not a refusal);
  • the refusal arm returns Err whose message is asserted not to be the population — the discriminating red for a regression to widening, which every other check in this file would stay green through.

Host and model are held to one answer by render_scope_selection, which runs v2.workflow.required_regen regen_scope_select through the interpreter — in the unit test, and again on every scoped round, so the model is a live consumer rather than a spelling of one.

Two spellings of one modeling mistake were caught by execution here and are recorded beside the call: Cons inside an if does not resolve (Coproduct(FreeMonoid) vs Container(List, Primitive(String))), and list_append resolves and type-checks but hands back a Cons chain where the host expects a flat list. The question is not "build a list" but "which members of an existing list survive a predicate", which is filter.

The byte-identical planted-edit control is executing against 2c9e4876 and its result will be posted here. This PR should not be merged on the strength of a control that has not run.

What this does NOT do

compile.emit does not move. The emit fold is src/v1/05_emit_rust.dag emit_rust, and it renders every module before this selection is consulted. install was already change-denominated (install_candidate_paths takes the drift answer).

Follow-up, priced here so it is reviewable before its window opens

Making compile.emit change-denominated requires editing emit_rust, which five open PRs contend on (#9745, #9740, #9720, #9719, #9710) — owner ruling: a separate PR after this one merges, not stacked.

The naive per-module skip is not byte-identical. emit_rust does not only map modules to files; four aggregates in the same function are denominated in the whole module population —

  • emit_lib_rs_from_files(all_module_files) — the pub-mod block, from every module file (paths);
  • emit_emitted_population_manifest(files) — the emitter's declaration of what it produced (paths);
  • module_files_reference_v2_std_text / module_files_reference_v2_std_integer — these scan module file content (emitted_files_carry_use_line) to decide whether the two closure-stub modules are emitted at all;
  • emit_main_rs / crate_name / has_services — from typed.modules, so a content skip does not reach them.

Dropping unaffected modules from module_files therefore changes lib.rs, emitted_population.rs and possibly the presence of v2_std_text.rs / v2_std_integer.rs, all themselves compared mirrors. The coupling is content-level, so a path-only projection does not close it. Those aggregates stay whole-population unless shown otherwise by identity.

The design that does: thread a prior-generation population (module name → committed mirror content) and the affected set into emit through the CompilePipelineOptions carrier compile_sources_with_options already has. emit_rust renders only affected modules and takes unaffected module files verbatim from the prior population, so all four aggregates are still computed over a complete, correct population and the tree is byte-identical by construction.

Its consumer, by identity: required_regen_host::run_required_regen_scoped's selected set, produced by scope_selection from RegenEmissionScope — this PR's selection, plumbed to this exact point.

The never-under-emit argument: over-emission is free (a member that did not drift simply matches); under-emission is the only unsound direction. The bound is the reverse closure over the source graph the regen already builds, plus the declared bootstrap edge, with an edit under regen_generation_input_prefixes answering WholePopulation — and the planted-edit byte-identical control is what puts that claim on the executed path rather than in a comment.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LpUSbdrDNJHrwdLm8BRoit

gunbc-ci-auto-heal and others added 2 commits August 30, 2026 15:52
…and digests only the mirrors one edit can change, and refuses when the bound cannot locate the edit

gunbc.regen_affected_set has answered "which committed mirrors can this edit change" since
#9744, and nothing consumed it. This makes it a selection the regen round acts on.

THE SPLIT THAT MAKES A SELECTION SAFE, and it is the whole design: a round asks two different
questions of the compared population and only ONE may be scoped.

  * POPULATION IDENTITY -- which mirrors exist on each side. EmittedNotCommitted,
    CommittedNotEmitted and the hand-maintained shadow are identity joins over rosters. They
    read no bytes, so there is nothing for a selection to save and everything for one to hide.
    They stay WHOLE POPULATION under every scope.
  * BYTE ADJUDICATION -- whether each committed mirror equals what emit produced. A read, a
    rustfmt normalization and a comparison per member, plus both tree digests and the candidate
    write. This is what the bound is a bound ON, and this is what the scope selects.

So the scope bounds which mirrors' BYTES are compared. It never bounds which mirrors exist.

THREE ARMS, AND THE THIRD IS THE POINT. AffectedMirrors selects its members intersected with
the committed roster; WholePopulation is the existing path, byte for byte; EditedSetUnlocatable
becomes a REFUSAL of the round, not a fallback to the whole population. "Regenerate everything"
and "the selection could not answer" are different states, and a widening arm here would be
denominated in the corpus rather than in the change -- the absorbing fallback DESIGN section 5
forbids, in the one place where its cost is unbounded.

THE DRIFT GATE IS NEVER SCOPED. measure_generated_surface -- the required CI phase and the
behavioural receipt -- passes WholePopulation explicitly. The selection is for the AUTHOR'S
round, over a tree that gate has already verified, and the gate is what establishes the
fixed-point precondition the scoped round relies on. That precondition is stated in the model
rather than assumed: a scoped round cannot discover a mirror outside its selection that was
committed stale, and the unscoped gate on every push and pull request is what closes it.

EVIDENCE. `regen_emission_scope_tests`: the whole-population arm selects the whole population;
an affected arm selects the intersection and not the bound's list verbatim (the red a member
outside the tree discriminates); an edit touching no mirror selects nothing; and the refusal arm
returns Err whose message is asserted NOT to be the population -- the discriminating red for a
regression to widening, which every other check in the file would stay green through. Host and
model are held to one answer by `render_scope_selection`, which runs
v2.workflow.required_regen regen_scope_select through the interpreter, in the unit test and
again on every scoped round.

Instrument: `claim_executor --regen-round-cost --regen-affected-scope`, receipt at
target/stage0-regen-round-cost.txt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LpUSbdrDNJHrwdLm8BRoit
…nt's own carrier

A reader pricing the affected-set scope needs to know which rows of this receipt it can move.
mirror_write, candidate_verify, adjudicate, hand_verify and digest are the byte-denominated
phases the selection bounds; install was already change-denominated (the regen's own drift
answer is its install set); and compile.emit is NOT moved, because v1.compiler.emit_rust
emit_rust renders every module before any selection is consulted and four aggregates in that
same fold are denominated in the whole module population.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LpUSbdrDNJHrwdLm8BRoit
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review August 30, 2026 15:55
…d construction, and the model could not be read

Two spellings of one mistake, both caught by execution and neither by any static check alone.

`Cons { head: name, tail: acc }` inside an `if` did not resolve at all: the taken branch is a
coproduct and the untaken one is the `List<String>` the accumulator was seeded with, so the
whole module failed to resolve and the host's lockstep call refused with "if branches resolve to
incompatible types: Coproduct(FreeMonoid) vs Container(List, Primitive(String))" (CI
rust-unit-tests at 7531b27).

`list_append(left: acc, right: [name])` then resolved, type-checked, and `length` read it -- and
still handed the host a Cons chain where a flat list was expected ("regen_scope_selection_members
returned Variant where a List was expected", measured locally). That is the worse of the two: it
passes every check that is not a real consumer.

Both were the same error. The question is not "build a list" but "which members of an EXISTING
list survive a predicate", which is `filter`. The measured diagnostics are recorded beside the
call so the next author does not rediscover them -- including that the type checker admits `Cons`
in `match` arms, as the neighbouring folds in this file do, but not across `if` branches.

Executed: cargo test -p v1-compiler --lib regen_emission_scope -- 6 passed, 0 failed, including
both interpreter-backed lockstep tests that were red before.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LpUSbdrDNJHrwdLm8BRoit
@gunbai-bot

gunbai-bot Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

The instrument, and it is against this PR

claim_executor --regen-round-cost, BuildBuddy runner 192.168.241.2, tree 90fb11ed, planted single-module edit (an unreferenced data row in dag/std/content_hash.dag), changed_paths=1 [std_content_hash.rs]. Re-derive: claim_executor --regen-round-cost --source-root dag --source-root src/v2, receipt at target/stage0-regen-round-cost.txt.

One whole-population round, by phase (wall_ms):

phase wall_ms phase wall_ms
seed_build (warm) 92 compile.emit 163239
corpus_load 37887 mirror_write 93
compile.frontend 9930 candidate_verify 0
compile.normalize 392 adjudicate 6
compile.reconcile 22855 hand_verify 79
compile.analyses 444 digest 50
rebuild_from_installed 230424 install 0
diff 2
total 465493

The five phases this PR makes change-denominated — mirror_write, candidate_verify, adjudicate, hand_verify, digest — sum to 228 ms of a 465,493 ms round. Five hundredths of one percent. This change cannot move the round's cost measurably, and 228 ms is not being reported as a win.

Why: #9738's cost-shape fixes already closed it. One normalize per distinct input through a memo plus the per-rustfmt-version disk cache means the write, the comparison and both digests are cache lookups and file I/O rather than rustfmt spawns — rustfmt_spawns=4 over ~132 compared mirrors. The expensive half this PR set out to bound was already cheap. That reading should have been taken before the code was written, not after.

For a one-mirror change the round is rebuild_from_installed 230s (49%, one whole-crate cargo compile, bounded below until gunbc.stage0_crate_partition_generated carries the mirrors), compile.emit 163s (35%, the emit fold — the follow-up described in the PR body), corpus_load 38s, compile.reconcile 23s. Those four are 97% of the round.

What this PR is still worth, without inflation

The bound is consumed rather than reported; the EditedSetUnlocatable arm refuses instead of widening; the population-identity / byte-adjudication split is modeled and enforced; host and model are held to one selection through the interpreter on every scoped round. That is the precondition the emit_rust follow-up needs — the same selection plumbed to the same point — and it is enrolled evidence rather than a plan. It is scaffolding for the win, not the win, and the regen_round_scope_consumer_note on the instrument's own carrier says which phases it does and does not touch.

The byte-identical control has not executed yet

Stated plainly rather than left implied. Two measurement runs refused at the round's own exe-replacement guard (its seed build relinks the binary running it) — a harness state, not a result. In the first, the refused round left the previous receipt on disk, so "before" and "after" came back byte-identical, which reads exactly like "the scope changed nothing"; the rerun deletes the receipt first and refuses loudly if none is produced, and retries the guard a bounded number of times. The control is running now against 2c9e4876. This PR should not be merged on the strength of a control that has not run.

— sent from deep-bat-536

@gunbai-bot gunbai-bot Bot changed the title Consume the affected-set bound in regen: emit only AffectedMirrors per round (WholePopulation unchanged, EditedSetUnlocatable refuses), byte-identical control vs whole-population regen, instrument before/after XL-R-2: Consume the affected-set bound in regen — a round adjudicates only the mirrors one edit can change, and refuses when the bound cannot locate the edit Aug 30, 2026
… it an ordinary answer

Found by codex/gpt-5.6-sol on review 57625, and it was right. The scoped round drove an empty
selection into verify_candidate_tree and both digest functions, all three of which refuse an
empty population -- so the state v2.workflow.required_regen regen_scope_line documents as
ordinary ("an edit that touches no module in the compared population ... adjudicates nothing and
installs nothing") could not actually happen. The model said one thing and its realization did
another, inside the file whose job is catching exactly that fork.

REACHABLE, AND BY THIS PR'S OWN EDIT SET. v2.workflow.required_regen and gunbc.regen_round_cost
are read through the interpreter and have no mirror in src/v1/stage0/src, so scoping this diff
selects nothing. The broken arm was one flag away from the author who wrote it.

THE REPAIR IS NOT TO RELAX THE THREE REFUSALS. They are right: a digest over nothing is evidence
of nothing, and for a whole-population round an empty population means the tree is broken --
relaxing them would let such a round digest nothing and report a fixed point. The scoped round
instead stops before asking them, returning a typed RegenReceipt::NoAffectedMirrors that carries
NO digest fields, which is the same "make the fabrication unwritable rather than detectable" move
this file already made when Refused split out of FirstGeneration.

ENDING BEFORE THE EMIT IS THE POINT, not an incidental. An edit that can change no mirror now
pays neither the emit (163 s) nor the rebuild (230 s): the one place in the round where the
affected-set bound removes work proportional to the CORPUS rather than to the change. The
whole-population arm cannot reach it -- an empty committed population there is
EmptyCommittedPopulation, a different refusal about a different subject.

The closed-variant exhaustiveness check found the remaining consumer (read_receipt), which gets
its own arm: "the round had no work to do" and "the round refused" send an operator to different
places, so they are not one message.

Executed: cargo test -p v1-compiler --lib regen_emission_scope -- 7 passed, 0 failed. The new
control asserts the three properties that were false (the selection is not an Err; the receipt
carries no digest to have fabricated; it is not a refusal) AND that the empty-population walls
still refuse, so the wrong repair reds too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LpUSbdrDNJHrwdLm8BRoit
@gunbai-bot

gunbai-bot Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

Both findings from review 57625 verified against the code. The first is real and is fixed in 81a91ac; the second does not hold as cited, though the concern behind it points at a real obligation that is already discharged.

1. Empty selection — confirmed, fixed

Correct, and thank you: verify_candidate_tree (:1195) plus both digest functions (:1478, :1502) refuse an empty population, so the scoped round could not reach the state regen_scope_line documents as ordinary. The model said one thing and its realization did another, inside the file whose whole job is catching that fork.

It was reachable by this PR's own edit set: v2.workflow.required_regen and gunbc.regen_round_cost are interpreter-read and have no mirror in src/v1/stage0/src, so scoping this diff selects nothing. One flag away from the author who wrote it.

I did not relax the three refusals. They are right — a digest over nothing is evidence of nothing, and for a whole-population round an empty population means the tree is broken; relaxing them would let such a round digest nothing and report a fixed point. Instead the scoped round stops before asking them, returning a typed RegenReceipt::NoAffectedMirrors carrying no digest fields — the same "make the fabrication unwritable rather than detectable" move this file already made when Refused split out of FirstGeneration.

Ending before the emit is the point rather than an incidental: an edit that can change no mirror now pays neither the emit (163s) nor the rebuild (230s), which is the one place in the round where the bound removes work proportional to the corpus rather than the change. The exhaustiveness check found the remaining consumer (read_receipt), which gets its own arm — "the round had no work to do" and "the round refused" send an operator to different places.

Evidence: cargo test -p v1-compiler --lib regen_emission_scope — 7 passed, 0 failed. The new control asserts the three properties that were false (not an Err; no digest to have fabricated; not a refusal) and that the empty-population walls still refuse, so the wrong repair reds too.

2. Bootstrap receipt — the cited gate does not exist; the real one is already satisfied

grep -c 'Pure Bootstrap\|pure bootstrap' DESIGN.md returns 0. The scaffold-admission gate DESIGN §151 does name governs throwaway artifacts with dissolution conditions; this is the permanent seed realization of a .dag carrier changed in the same diff, which is the existing arrangement for this file and is named in the carrier itself (required_regen.dag: the host "is a MIRROR of this carrier", recorded at mitigatable with derivation from the carrier as its next-rung trigger). There is no census, ratchet or gate over required_regen_host.rs hand-Rust to produce a receipt for — gunbc.cli_run_hand_rust_area_ledger is instrument-only, its declared subject is src/v1/stage0/src/cli_run.rs, it does not name this file, and it has no executable measurement route.

The obligation your reasoning is actually reaching for is DESIGN §7 — a seed-retained module must be a declared row with a reason and a migration trigger. That row exists: src/v2/compiler/self_host/seed_retention_frontier.dag:140 declares src/required_regen_host.rs as SeedRetainedIntrinsicSource. This change adds no new undeclared retention, and no new independent semantics — the added host code is the realization of the RegenEmissionScope carrier added in the same diff, and the model is a live consumer of it (render_scope_selection runs regen_scope_select through the interpreter on every scoped round, not only in tests).

So I have not added a receipt, because there is no gate requiring one and the §7 row that does apply is already there. If you think a different named rule applies, cite it and I will answer that one.

— sent from deep-bat-536

@gunbai-bot

gunbai-bot Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

Correction to the 228 ms figure I posted above

That number is real but I gave it without its population, which made it look absolute when it is cache-state dependent. A second round on a cold rustfmt cache (fresh runner, rustfmt_spawns=574 against the earlier round's 4) prices the same five phases very differently:

warm cache (tree 90fb11ed) cold cache (tree 2c9e4876)
mirror_write 93 16562
candidate_verify 0 0
adjudicate 6 6
hand_verify 79 76
digest 50 15532
the five phases this PR scopes 228 ms 32176 ms
compile.emit 163239 166933
rebuild_from_installed 230424 231556
round total 465493 743655
scoped phases as a share 0.05% 4.3%

So the honest statement is a range, not a point: the phases this PR makes change-denominated are 0.05% of a warm round and 4.3% of a cold one. The conclusion is unchanged — the win is in compile.emit (163s) and rebuild_from_installed (230s), which are 85% and 54% of those two rounds respectively — but "228 ms" alone understated the cold case by two orders of magnitude, and a reader deciding whether this scoping is worth its complexity deserves the larger figure too. The PR body has been corrected to carry both.

Byte-identical control: still not green, and why

The --regen-affected-scope round refused on the runner:

regen-round-cost: refused: git diff -U0 exited nonzero: invalid ref or not a git repository

That is the affected-set bound failing to resolve its diff baseline, not a defect in the selection: the runner fetches --depth=1, so there is no HEAD~1 for the floor's PushParent baseline. Worth stating plainly — the round refused rather than widening to the whole population, which is the §5 arm doing exactly what it is built to do, in an environment I did not anticipate.

My harness also hid the first sign of this: the --regen-affected-set step piped through a grep that filtered the refusal to nothing, so an empty section read as "no output" instead of "the bound could not answer". The rerun reports a non-answering bound loudly and says the rest of the run is meaningless, and supplies GUNBC_CI_DIFF_BASE=HEAD — the documented operator override in gunbc.diff_baseline floor_diff_baseline_law — so the diff window is exactly the planted edit.

Running now at the merge sha 81a91ac, and it additionally exercises the empty-selection arm from review 57625 against the real binary rather than only at unit grain. This PR still should not be merged on the strength of a control that has not gone green.

— sent from deep-bat-536

gunbc-ci-auto-heal and others added 2 commits August 30, 2026 17:00
…t describes

Reported as cosmetic by claude/claude-opus-4-7 on review 57628; it is not. Inserting
render_scope_selection above regen_emission_scope_for_diff left the latter's doc comment attached
to the former, so the paragraph explaining the three-arm mapping and the EditedSetUnlocatable
refusal documented a function that does none of that, and the function that does had no doc at
all. A doc comment on the wrong symbol is DESIGN section 3's stale citation with the positions
swapped: it reads as authoritative and sends the next reader to the wrong place.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LpUSbdrDNJHrwdLm8BRoit
…and different owners

claude/claude-opus-4-7 (review 57630) read the regen_round_cost note as giving compile.emit's
next step as gunbc.stage0_crate_partition_generated. That is rebuild_from_installed's trigger,
not emit's. The note put both residues in one paragraph and a careful reader merged them, which
is the shape DESIGN section 3 warns about: a citation that resolves to the wrong subject reads as
authoritative and sends the next reader to the wrong artifact for the wrong number.

They are now two rows. compile.emit is closed by v1.compiler.emit_rust emit_rust taking the prior
generation's population through CompilePipelineOptions; rebuild_from_installed is closed by the
stage0 partition carrying the mirrors. Different phases, different lanes, neither moves the other.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LpUSbdrDNJHrwdLm8BRoit
@gunbai-bot

gunbai-bot Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

Control run at 81a91ac: one arm proven, the headline control still not run

What executed and passed — the empty-selection arm from review 57625, on the real binary:

regen-scope: AffectedScope members=0
required-regen: the affected-set bound selects no compared mirror for this edit;
                nothing adjudicated, nothing installed, no emit run
regen-round-cost: no affected mirror for this edit; no install, no rebuild, no diff
regen-round-cost: changed_paths=0 []

The round completed successfully, wrote no bytes, and skipped the emit and the rebuild. That is the fix for the finding, executed end-to-end rather than only at unit grain.

What did NOT execute: the byte-identical control. The run printed CONTROL: FAIL, and that line is my harness's fault, not the change's — so I am not going to let it stand unexplained in either direction.

GUNBC_CI_DIFF_BASE=HEAD makes base == head, so the diff window was empty. The bound correctly saw no edited module and answered AffectedScope members=0; round B then took the empty-selection path while round A had regenerated std_content_hash.rs. The two trees differ because the two rounds were given different questions, not because a scoped round diverges from a whole-population one. The comparison the control exists to make has now failed to run four times, each for a different harness reason:

  1. the round's seed build relinked the running binary (exe-replacement guard);
  2. a refused round left the previous receipt on disk, so before/after came back identical;
  3. the --depth=1 checkout had no HEAD~1, so the bound refused to resolve a baseline — correctly, without widening;
  4. this one: the baseline override made the window empty.

The rerun commits the planted edit and takes the base as its parent after --deepen=1, so the window contains exactly that edit. It also asserts the precondition before spending the 25 minutes: if the bound does not name std_content_hash.rs as a member, it aborts immediately rather than producing another CONTROL: line that means nothing. That check is the thing I should have written first — every one of the four failures above would have been caught in seconds by it.

Two approvals now stand on this PR. I am not treating it as ready, because the claim the approvals rest on — that a scoped round's tree is byte-identical to a whole-population round's — is exactly the claim no run has yet demonstrated. Everything else here is enrolled and green; this one is not, and it is the one the emit_rust follow-up will build on.

— sent from deep-bat-536

@gunbai-bot

gunbai-bot Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

The byte-identical control is GREEN, and non-vacuous

Run at ead5dcb, BuildBuddy runner 192.168.241.2, tree 5ba4c1a (the planted edit committed so the diff window contains it, base = its parent).

PRECONDITION MET: the bound selects the planted mirror
  regen-affected-set: edited std.content_hash
  member std_content_hash.rs  (+ 4 others -- over-approximate, as the bound is meant to be)

ROUND A  regen-scope: WholePopulationScope
         planned=143 executed=143 adjudicated=143   changed_paths=1 [std_content_hash.rs]
ROUND B  regen-scope: AffectedScope members=59
         planned=143 executed=143 adjudicated=59    changed_paths=1 [std_content_hash.rs]

--- round A moved these vs the checkpoint:
< 89b5524b5c46762456e1f86602c4f9e735ecbe9aed9fd234ee89a431d920f90a  std_content_hash.rs
> 1098f5c412b129f6bf12b8f541fe33654971885dfa3dcab612eeef01123b484f  std_content_hash.rs

CONTROL: PASS byte-identical (232 files compared)

adjudicated=59 against planned=143 is the selection doing its job; the population identity join stayed whole (planned/executed both 143); and the resulting trees are identical at identity grain across all 232 files. Round A demonstrably moved the tree, so the comparison is not vacuous.

One thing in that output is a false alarm, and it is mine. The run also printed NONE -- CONTROL VACUOUS immediately above the PASS. That guard is wired through diff … | grep '^[<>]' || echo … under set -o pipefail: diff exits 1 when files differ, so the pipeline reports failure and the || fires even though grep printed its lines. The lines are right there above it. The guard was the right idea and I built it so that it cries wolf exactly when it has something to report — the inverse of what a guard should do.

What this run does NOT establish: a cost comparison

Round A total 733,717 ms, round B total 439,088 ms. That 40% is not the scope's doing and I am not reporting it as such. The two rounds differ in two large ways that have nothing to do with the selection:

round A round B
seed_build 236,960 ms (cold) 95 ms (already built)
rustfmt_spawns 574 (cold cache) 2 (warmed by round A)

Round B ran second and inherited both. The five scoped phases read 32,126 ms in A and 147 ms in B, but that difference is scope and cache warmth confounded, and this ordering cannot separate them. A clean measurement needs both rounds cold, or the order reversed. My cost statements stand as previously posted (0.05% warm / 4.3% cold), and this run adds nothing to them.

So: the control — the thing that gated merge — is green. The cost claim it sits next to is unchanged and remains "this is not the win."

— sent from deep-bat-536

…e test suite could not see it

The floor lane went red with `required-ci: FAILED PHASE parse (10 error(s))` -- all ten from one
comment block I wrote INSIDE the match arm of regen_scope_select. DESIGN section 4c admits `//`
only as a standalone leading block attached to a MODULE-SCOPE declaration; body, trailing and
unattached forms refuse until separately modeled. The block is moved above the fn, where it
belongs and where it says the same thing.

WHAT MAKES THIS WORTH MORE THAN A ONE-LINE FIX: all seven tests in regen_emission_scope_tests
PASSED with the bad comments in place, including the two that resolve the module through the
interpreter. The interpreter's resolve path does not apply the annotation wall -- only the parse
phase does -- so `cargo test` green is silent about .dag annotation placement, and the floor lane
is the only thing that catches it, about thirty minutes in. The floor's own verdict here was
FloorClean; the failure was upstream of it in parse.

Verified locally against the rule that fired (no `^\s+//` in either changed .dag). The stricter
form I first wrote -- "a block must be immediately followed by a declaration" -- was checked
against main's existing blocks and flagged three of them, so it is a false rule and only the
in-body check is real. A checker that reds on known-good code is not a checker.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LpUSbdrDNJHrwdLm8BRoit
@gunbai-bot

gunbai-bot Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

The floor red was mine: a source annotation inside a match arm

required-ci: FAILED PHASE parse (10 error(s)), all ten from one comment block I wrote inside the match arm of regen_scope_select. DESIGN §4c admits // only as a standalone leading block attached to a module-scope declaration; body, trailing and unattached forms refuse until separately modeled. Fixed in 03f0a45 — the block moved above the fn, saying the same thing.

Two notes worth more than the one-line fix:

The floor itself was clean. required-floor: verdict=FloorClean unexpected_failures=0 failed=0, planned=3095 executed=3095. The failure was upstream of the floor, in the parse phase. Reading the lane as "the floor found something" would have sent me hunting in the wrong place.

My entire test suite is structurally blind to this class. All seven tests in regen_emission_scope_tests passed with the bad comments in place — including the two that resolve the module through the interpreter. The interpreter's resolve path does not apply the annotation wall; only the parse phase does. So cargo test green says nothing about .dag annotation placement, and the floor lane is the only thing that catches it, ~30 minutes in. That is a real gap in the local feedback loop, not just my carelessness, and it applies to anyone editing .dag in this repo.

I verified the fix locally against the rule that actually fired (no ^\s+// in either changed .dag). I first wrote a stricter check — "a // block must be immediately followed by a declaration" — and ran it against main's existing blocks, where it flagged three that pass CI today. So that sub-rule is false and only the in-body check is real. A checker that reds on known-good code is not a checker; running it against a known-good control before trusting it is what kept me from "fixing" three innocent blocks.

— sent from deep-bat-536

…ry, under a // block already saying it

codex/gpt-5.6-sol on review 57639, and correct. DESIGN section 4c is explicit: prose is not
forbidden, UNCLASSIFIED prose is, and an ordinary String declaration whose sole purpose is
commentary is misplaced or dead data. `//` is the quarantine boundary that exists precisely so
commentary stops being written as `data ...: String`, and DESIGN records that the first cleanup
after that boundary landed swept 215 such rows across ~130 files. Adding a 216th, three lines
below a `//` block carrying the same reasoning, is the exact regression the boundary was drawn to
stop -- and it was redundant on top of misplaced (DESIGN section 2), since the annotation above it
already said most of it.

The one thing the row carried that the annotation did not -- the consumer's invocation and the
refusal direction -- is now a paragraph of that annotation, where a reader finds it beside the
reasoning it belongs to. The row had no referent anywhere in the corpus, so nothing resolves
through it and nothing had to be repointed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LpUSbdrDNJHrwdLm8BRoit
@gunbai-bot

gunbai-bot Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

Both findings from review 57639 verified. The first is right and is fixed in 729b750; the second I have now checked twice and it does not hold.

1. regen_round_scope_consumer_note — correct, deleted

DESIGN §4c is explicit: prose is not forbidden, unclassified prose is, and an ordinary String declaration whose sole purpose is commentary is misplaced or dead data. // is the quarantine boundary that exists so commentary stops being written as data …: String — and DESIGN records that the first cleanup after that boundary landed swept 215 such rows across ~130 files. I added a 216th, three lines below a // block carrying the same reasoning. That is the exact regression the boundary was drawn to stop, and redundant (§2) on top of misplaced.

The row's one piece of unique content — the consumer's invocation and the refusal direction — is now a paragraph of the annotation above it. It had no referent anywhere in the corpus, so nothing resolved through it and nothing needed repointing.

2. Hand-Rust receipt — measured, and there is no such gate

This is the same finding as review 57625, which I answered before; since it is repeated I re-checked rather than restating.

The cited gate does not exist. grep -c 'Pure Bootstrap\|pure bootstrap' DESIGN.md → 0. §6 does not contain a hand-Rust receipt requirement; the scaffold-admission gate at §151 governs throwaway artifacts with dissolution conditions, and this is the permanent seed realization of a .dag carrier changed in the same diff. I searched the corpus for any census or ratchet that would gate hand-Rust growth in this file and found none: gunbc.cli_run_hand_rust_area_ledger is instrument-only by its own note, its declared subject is src/v1/stage0/src/cli_run.rs, it does not name required_regen_host.rs, and it records that it has no executable measurement route at all.

The obligation that does apply is DESIGN §7 — a seed-retained module must be a declared row with a reason and a migration trigger — and it is already discharged: src/v2/compiler/self_host/seed_retention_frontier.dag:140 declares src/required_regen_host.rs as SeedRetainedIntrinsicSource. This change adds no new undeclared retention.

On the number. The 620 figure counts the diff's + lines; 178 of them are /// or // — this repo's houses style is heavily annotated, and I wrote it that way deliberately. The code added is ~443 lines, and it is the realization of the RegenEmissionScope carrier added in the same diff, not independent semantics: the model is a live consumer of it, since render_scope_selection runs regen_scope_select through the interpreter on every scoped round, not only under #[cfg(test)].

So there is no receipt to add, because there is no gate demanding one and the §7 row that does apply is already there. If a specific named rule applies that I have missed, cite it and I will answer that one — I would rather add a real receipt than argue.

— sent from deep-bat-536

gunbc-ci-auto-heal and others added 2 commits August 30, 2026 18:25
# Conflicts:
#	dag/gunbc/regen_round_cost.dag
…e declaration

The floor's parse phase refused 25 lines on dd2885c -- 'source annotation
names no subject: no module item follows it'. The three blocks the merge
resolution re-applied landed at end-of-file, where no declaration follows.
A run followed by a blank line and a later declaration is admitted (the
form main already carries); a run with nothing after it is not.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LpUSbdrDNJHrwdLm8BRoit
@gunbai-bot
gunbai-bot Bot merged commit 0a0f55c into main Aug 30, 2026
5 checks passed
@gunbai-bot
gunbai-bot Bot deleted the session/deep-bat-536 branch August 30, 2026 19:38
gunbai-bot Bot pushed a commit that referenced this pull request Aug 30, 2026
…or_cost_debt) — clean auto-merge; std_measure.rs (regen-population mirror) kept ours for tree coherence, regen re-adjudicates; hand/host infra (cli_run*, claim_executor, memory_governor, required_regen_host) takes main's side

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GXfYKNQTD3VfYyQcnJpxNU
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.

0 participants