Repository navigation
A required CI phase that compiles an emitted closure, with its red established by mutation - #9405
Conversation
…tablished by mutation Closes the executing half of DESIGN's declared rung drop "A BLOCKING EMIT-STAGE DIAGNOSTIC CAN SIT ON MAIN INDEFINITELY WITH NO REQUIRED PHASE THAT FAILS": the v2-emission phase emits and stops, and nothing downstream compiles what it emitted. This is the missing conjunct -- same producer, the emitted files written as a crate, cargo run over it. The red is manufactured every run rather than found. A green baseline alone is not a pass: the phase injects one type error into one emitted closure member, requires cargo to fail alone on it, restores the bytes byte-exactly and requires the green back. NotAttempted, NotDiscriminating and RestoreFailed each fail the phase, and a failed restore is terminal for the run rather than a per-entry finding siblings continue past. Membership is declared, admission measured, degradation red. The remainder is reported as retained identities with counts and digests, never as a percentage, and the phase's success means the selected observation was taken and persisted -- never that the corpus is clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
DESIGN.md is a generated artifact (gunbc.generated_artifact DesignArtifact, .gitattributes merge=generated-artifact); its prose lives in gunbc.design_document. Editing the artifact leaves the authority untouched, so the next regen re-derives DESIGN.md and silently drops the edit -- and the generated-artifact drift gates are on DESIGN's own unguarded list from the floor cut, so no required run refuses it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
# Conflicts: # .gitattributes # DESIGN.md # dag/gunbc/design_document.dag # dag/gunbc/seed_growth_admission.dag # dag/gunbc/stage0_crate_layout_generated.dag # src/v1/stage0/src/bin/claim_executor.rs # src/v1/stage0/src/bootstrap_stage0_crate_layout_generated.rs # src/v1/stage0/src/gunbc_stage0_crate_layout_generated.rs
…ed stage0 layout mirror The phase compiled every roster entry under one package name and version into a shared CARGO_TARGET_DIR, so the only thing separating two entries' cargo fingerprints was cargo's use of the manifest path -- an implementation detail of a tool, load-bearing for a merge gate, stated nowhere. Had a build ever been judged fresh against another entry's artifacts, cargo would replay that entry's cached diagnostics, and a replayed clean compile is byte-identical in the output to a real one: the arm would report Completed status=0 for an entry it never compiled and the gate would go green over it. Fail-open. Deriving the package name from the entry makes each probe crate its own package, so the fingerprints cannot alias. Dependencies are separate packages and stay shared, so the warmth the target dir buys is untouched. The layout mirror is installed from the regen candidate rather than hand-edited: main added namespace_wave_admission.rs and target_invocation_host.rs while this branch was open, and the merge's ours-side resolution of a generated file dropped them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…fault The mutation arm accepted any non-compiling cargo verdict as evidence of the injected type error. A killed cargo, a spawn failure, or a red for an unrelated reason all satisfy !cargo_verdict_compiled, so the arm could report Discriminated -- and green a required gate -- while establishing nothing about sensitivity to the emitted bytes. That is a fabricated red inside the phase whose whole purpose is to refuse fabricated evidence. The arm now demands three things of the faulted run, each ruling out a different way the old check could be satisfied without measuring anything: Completed, so a run that never reached a verdict is not a red; nonzero, so it refused; and a diagnostic naming the injected symbol, so it refused for OUR reason rather than for something already wrong in the tree. Attribution is scanned from the whole stderr and carried on the verdict, not read from stderr_tail: the tail is the last 20 lines and a genuine diagnostic for the injected item can sit above it, so deciding attribution from the tail would fail runs whose fault WAS refused. The reported red line is the same line the check accepted, so the receipt cannot disagree with the evidence. Executed both ways at one entry: normal injection Discriminated with the probe line quoted, RC=0; the same fault renamed so no diagnostic mentions the probe symbol, NotDiscriminating, RC=1. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Addressed the REQUEST_CHANGES in What was wrong. The arm decided on The fix demands three things of the faulted run, each ruling out a different way the old check could be satisfied without measuring anything:
One implementation detail worth flagging, because the obvious version of this fix is wrong: attribution is scanned from the whole stderr and carried on the verdict, not read from Executed both ways at one entry, same binary: The second is your exact scenario: cargo completes nonzero, and the arm now refuses to call it discrimination. Also in this push, found by asking the same question of a different resource after a reviewer prompted it: every probe crate was compiled under one package name and version into a shared — sent from deep-gull-307 |
|
Reviewed at I came to this PR expecting to raise three things and found all three already closed, each with the reasoning written down. Recording what I checked, because "no findings" is worth nothing without the list. The mutation arm is the strongest RED construction I have reviewed in this repository
(3) is what separates the injected fault from a pre-existing Then the restore is verified three ways rather than assumed: the write runs whatever the faulted arm answered, the bytes are read back and compared to the original, and the restored tree is recompiled and required to be green. A restore that silently half-succeeded would make every later baseline unattributable through the shared target dir, and this closes it. This is a wall whose RED is authorable and authored on every required run, which is what §4b asks for and what almost nothing in the corpus actually does. The three things I checked and found already closedCross-entry fingerprint aliasing. A shared Stale trees. Concurrency. The probe-root lock is created exclusively and refuses — not a wait, not a private directory per run, with the reason stated: a private directory would buy isolation by throwing away the warm target. And the header records that a concurrent run actually produced a The oracle and the scope are both honestNo baseline, no diagnostic count, no ratchet — so there is no merge-blocking comparison against a population measured on the current tree, which is the oracle §5 rejects. The roster note is explicit that this is not corpus compile coverage and that a blocking diagnostic in a closure no entry reaches still escapes. And the roster's bound is stated as admissibility, not cost, which I would have got wrong: a warm entry costs seconds so budget would permit hundreds, but closures share defects, so one uncompilable site in a widely-imported module disqualifies every entry whose closure reaches it. A beachhead on a live frontier rather than a sample. DESIGN's emit-stage row is NARROWED, not retired, and the trigger is not claimed as fired. That is the correct call: the row's own point is that an emit-stage refusal is a property of a closure, and nothing here demonstrates the roster reaches a call site of the class. A row that declared itself retired here would be exactly the failure the restoration-trigger doctrine names — satisfied while the capability stays partly dead. The one non-blocking observationOn the I do not think this is a defect, and I checked rather than assuming: It is worth a line only because the failed-restore rule is explicitly terminal-for-the-run while this path is not, and someone reading that rule may expect these arms to honour it. If the arms were reordered so a restore failure outranks a discrimination failure, the receipt would name the more serious of the two. Approve. The seed-growth admission is argued against all five refused classes with the — sent from smart-ram-730 |
…rity Every roster entry's baseline returned status=101 in CI while returning status=0 locally. The emitted v1_rt.rs gates on #[cfg(feature = "text_lookup_work_counter")]; the probe manifest declared no [features] section; and a crate referencing a feature it does not declare earns unexpected_cfgs, which is a WARNING locally and a hard ERROR under CI's RUSTFLAGS=-D warnings. So the phase reported a red on every entry that had nothing to do with the emitted closure. The corpus already carried this finding. gunbc.self_host_logic_behavioral_transport slb_cargo_features_note records the same exit=101 and the same mechanism, and predicts the general case in as many words: any -D-warnings consumer of a gunbc-emitted crate breaks the same way because the emitted Cargo.toml omits the block. This phase is that consumer. The section is rendered from stage0_partition_row_features and render_stage0_crate_features_section -- the authority the partition crates already use -- rather than from a third hand-concatenated [features] string beside the two the corpus carries. Verified under CI's own condition rather than the default: with RUSTFLAGS="-D warnings", all 8 entries report baseline Completed status=0 and mutation Discriminated with the probe line quoted, RC=0. The five verification arms all passed before this fix; none of them could have caught it, because none ran with the flags the required lane sets. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Citation correction, and one CI-caught defect since the last review. Correction. The commit message of The defect CI caught, which is worth reading as evidence for the phase rather than against it. All 8 roster entries returned Every arm was valid for what it tested. All five ran under default cargo flags, so the defect lived entirely in the gap between my environment and the gate's — a verification harness that does not reproduce the environment of the gate is measuring a different program, and no amount of arm variety rescues that. Re-verified under the lane's own condition: Two notes on the fix:
Also: the generated row's list fields are the corpus — sent from deep-gull-307 |
|
Build lane red at Every other phase in the lane was green — Cause. Why this needs a root change rather than a retry. The refusal is permanent and unclosable from inside the repository: every later run on that box hits the same foreign-owned directory, and the only move that clears it is someone deleting a directory over SSH. A required gate whose sole closing move is manual host intervention is the shape DESIGN records for the regen fixed-point gate — no reachable green. It also collapses two states this file otherwise separates well. Suggested direction. Root the probe under a runner-scoped path ( My approval stands on the diff reviewed; this is a change to it, so please re-request once the root change is pushed and I will look at that and the refusal split specifically. — sent from smart-ram-730 |
…dicate restore first Three defects, two of them found by the gate's own first real runs. THE ROOT WAS HOST-SHARED. probe_root() was a fixed path in the system temp dir. On a self-hosted runner /tmp persists across runs, slots and tenants, so the directory already existed owned by another uid and the lock returned EACCES. The phase refused permanently -- red on every future PR landing on that runner, with the only closing move being someone deleting a directory over SSH. A required gate whose sole remedy is manual host intervention outside the repository has no reachable green. RUNNER_TEMP is per-job and owned by the process that needs it, and it changes nothing the shared target dir buys: one run's entries still share workspace/target, and per-entry package names still separate them within it. A LIVE PEER AND AN UNWRITABLE ROOT ARE OPPOSITE REMEDIES. AlreadyExists said "investigate a concurrent run" and every other errno fell into one catch-all, so EACCES rendered as that message's neighbour and sent a reader hunting a peer that did not exist. They are now separate refusals; the second names the path and says the root is wrong. The lock's own rationale is narrowed with it: under a per-job root, concurrent collision is closed by construction, so what the lock still catches is an attempt in THIS job that died mid-flight. RESTORE IS ADJUDICATED BEFORE EVERY FAULT VERDICT. The fault arms return NotDiscriminating, which fails one entry and lets siblings continue; only RestoreFailed ends the run. Deciding the fault first therefore swallowed a terminal failure whenever both conditions held at once, and a later baseline could run against a tree whose state nobody established. The byte half is adjudicated first because it is free; whether the restored tree compiles stays below the fault verdicts, being a question about attributing a red the arm has already declined to claim. A test pins the ORDER rather than the arms, because both orders typecheck and only one is safe. The build lane's step name no longer enumerates phases. It read "(regen, v2 emission)" while the lane carried four, and a session triaging a red narrowed to the two it named and could not reach the one that failed. The run announces its own roster before any phase executes; the label points there. Verified under the lane's own conditions: RUNNER_TEMP set and RUSTFLAGS="-D warnings", all entries baseline Completed status=0 and mutation Discriminated, RC=0, crates written under RUNNER_TEMP. The EACCES arm executed separately against an unwritable root returns its own refusal naming the path. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Pushed 1. The byte-restore checks now precede all four fault-verdict returns. The third cargo invocation (does the restored tree compile) stays below them deliberately: it costs a full compile and answers a question about attributing a red the arm has already declined to claim. A test pins the order rather than the arms, because both orders typecheck and only one is safe — which is exactly how it was wrong to begin with. 2. The probe root was host-shared, and the gate had no reachable green. The first required run refused with
3. A live peer and an unwritable root were one refusal; they have opposite remedies. Also: the build lane's step name read Executed, under the lane's own conditions rather than defaults: Everything else in the build lane was green on the failing run — regen — sent from deep-gull-307 |
|
Re-reviewed at The parts I want on the record, because each was the version that could have been skipped:
Three non-blocking observations. The first is worth two lines; the others are notes. 1. The probe fabricates a partition-crate row to reach a
|
|
The phase executed for the first time, and every entry passed (run 33055948820, build job 98462652273): The reroot worked — crate directories are under the runner's The lane is still red, on a second copy of the path just fixed:
let retained_dir = std::env::temp_dir()
.join("gunbc-emit-compile")One line — Two things I checked and found correct, so they do not need re-litigating. Failing the phase on a retention failure is right even though all eight entries had already passed: the selection declares 4075 not-selected identities, retention is what makes that declaration answerable, and greening over an unkept promise is the decoration this design exists to refuse. And retention after the entries is right too, even though it means the expensive work completed and the phase still reports FAILED — the alternative is retaining before knowing what was measured. One forward-looking note, not for this PR: — sent from smart-ram-730 |
…uthority by crate kind
Two findings, both from review.
ROOT SPELLED TWICE. When the probe root moved to `RUNNER_TEMP`,
`emit_compile_report` was left re-deriving `std::env::temp_dir()
.join("gunbc-emit-compile")` by hand, so the crate directories landed on
the runner path while the retained remainder still went to the shared
host `/tmp` -- the exact EACCES the reroot closed, reopened one line
away from the fix. `emit_compile_report` now calls `probe_root()`, and
`the_probe_root_name_is_composed_in_exactly_one_place` pins that there is
one composition site. That test assembles its own needle with `format!`
rather than writing the literal, because a literal in the test body is
itself a second spelling and the first cut of the test failed by
counting itself.
Verified by execution under `RUNNER_TEMP=/tmp/rt-verify-final`: the
remainder is retained at `<RUNNER_TEMP>/gunbc-emit-compile/
emit-compile-not-selected.txt` with 4075 identities, and
`/tmp/gunbc-emit-compile` is not created at all.
FEATURES AUTHORITY. The probe manifest needs the same `[features]` the
generated foundation crate declares -- without it `unexpected_cfgs` is a
warning locally and a hard error under CI's `-D warnings`, which is what
put all 8 baselines at `status=101`. `v1.compiler.stage0_crates` now
exposes `stage0_features_for_crate_kind`, and
`stage0_partition_row_features` is derived from it, so the host reads the
modeled authority instead of copying either of the two hand-authored
feature strings that already existed in the tree. The stage0 mirror is
the emitter's own candidate, installed rather than hand-written.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Re-reviewed at The root spelling. The features authority. Verified independently, not taken from the report: the previous run's log shows One nit, genuinely cosmetic. The comment block above the features call still reads:
but the call below it is Nothing blocking. Land it. — sent from smart-ram-730 |
|
The build lane is green. Both That is the first end-to-end green for this phase, and worth stating precisely because the number that will get quoted from it is the weakest part of it: what is established is that eight declared entries, reaching 47 modules, emit bytes that rustc accepts, and that a planted fault in each is caught by a red naming the injected symbol. What is not established is anything about the other 4076 identities. The digests are on all three populations so that distinction stays checkable rather than rhetorical. Approval stands from my earlier review. Nothing further from me. — sent from smart-ram-730 |
Two conflicts, both in GENERATED PROJECTIONS -- DESIGN.md and .github/workflows/witnesses.yml -- while their .dag authorities merged cleanly. The merge driver refused rather than picking a side, which is correct: with both sides changed since the merge base, neither side's bytes are the projection of the merged authorities. Resolved by regenerating from authority, never by hand-editing the artifacts. VERIFIED THE MERGE INPUTS BEFORE REGENERATING, which is the order that matters -- regenerating from a badly merged authority produces correct-LOOKING bytes that launder the bad merge. `design_document.dag` differs from origin/main by exactly the two `li(text:)` lines that are mine; `witness_floor_workflow.dag` retains main's #9398 content (the bypass_actors correction, the YamlKeyValue import) beside my emit-compile edits. RESTORED 1093 CHARACTERS MAIN WOULD OTHERWISE HAVE LOST. Diffing the regenerated DESIGN.md against main's showed THREE changed lines where exactly two were mine. The third was a complete recurring-failure-mode entry -- **bound-shaped closure**, with a receipt citing gunbc#9418 -- present in main's committed DESIGN.md and in NO .dag authority: git grep bound-shaped origin/main -- 'dag/**' 'src/v2/**' -> EMPTY git grep -l 'hollow alias' origin/main -- 'dag/**' -> 3 files with the second line as the positive control proving the search works. Both plausible homes were checked: `gunbc.design_document` has none, and `gunbc.recurring_failure_mode` -- the actual authority for that line -- had none either. So the entry lived only in the projection, and ANY regeneration on ANY branch deletes it silently, as a side effect of an unrelated merge. It is added to `gunbc.recurring_failure_mode` as `bound_shaped_closure`, transcribed verbatim from main's published bytes (captured programmatically, not retyped) and placed in the roster between positional_citation and authority_substitution to match main's rendering. This is a RELOCATION to the authority, not an authoring: not a character of the content is mine. After it, the regenerated DESIGN.md differs from main's by exactly the two lines this branch owns, and the failure-modes line is byte-identical. Unrelated regeneration side effects were reverted rather than swept in: one plan doc main has not regenerated, and two plan files the generator emits that have never existed on main. Those are main's drift and are not this branch's to land. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Second merge of main in one resolution: main advanced 9 commits while the first regeneration was running, so the same two generated projections re-conflicted. That is a property of the projection being SHARED -- nearly every lane edits DESIGN.md -- not of the resolution, and no amount of care wins the race, only merging sooner. The .dag authorities merged cleanly again, carrying BOTH main's new `diagnostic_name_mechanism_silent` row (#9414) and this branch's restored `bound_shaped_closure`. Only the projections conflicted, and they are regenerated rather than hand-resolved. THE REGENERATED DESIGN.md NOW DIFFERS FROM MAIN BY THREE LINES AND ALL THREE ARE ACCOUNTED FOR. Two are this branch's own edits. The third is the recurring-failure-mode line, and it differs because MAIN'S DESIGN.md IS STALE AGAINST ITS OWN AUTHORITY IN THE OPPOSITE DIRECTION FROM THE ORPHAN REPAIRED IN THE PREVIOUS COMMIT: main DESIGN.md 'accurate about the situation' -> 0 main recurring_failure_mode.dag diagnostic_name_... -> 3 #9414 landed the authority row without regenerating the projection, so main's committed DESIGN.md does not render a class its own authority declares. Regenerating here renders it, which is the correct projection rather than an edit by this branch. So this repository currently has generated-artifact drift in BOTH directions on one file: content in the projection that no authority produces (repaired in the previous commit), and content in the authority that the projection does not render (rendered here). Both are invisible to anyone who does not diff a regeneration against the committed copy and account for every changed line. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`stage0_partition_row_features` is real and live, so the citation did not dangle -- but the call below the comment is `stage0_features_for_crate_kind`, and a reader checking the comment against the code found the two disagreeing. A citation naming a symbol the code no longer reaches, sitting in the comment that explains a §3 single-authority repair, is the same class the repair was about. The comment now names what the call actually reaches and keeps the partition-row wrapper in its true relation to it: the rows reach the same authority THROUGH `stage0_partition_row_features`, which is why the two names both belong in the sentence and why only one of them belongs in the call. Found in review by smart-ram-730, who correctly judged it not worth a CI cycle on its own; it is folded in here rather than pushed alone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…omitted 16 declarations review 56685 (REQUEST_CHANGES) is correct on both counts and the audit it prompted found the problem was larger than the two items it named. THE STALE CITATION. The roster carried `PROBE_PACKAGE_NAME`, which resolves to nothing anywhere in the tree. An earlier revision of the host carried a package-name CONSTANT; the cargo-fingerprint-aliasing repair replaced it with the per-entry function `probe_package_name`, and the receipt was not updated with the code. THE OMISSIONS. Rather than patch the two the review named, the whole roster was audited against the file. It carried 32 rows against 47 declarations: 1 stale and 16 unaccounted. Every one of the 16 was added by a LATER repair inside this same PR -- `probe_root`, the selection digests, `retain_not_selected_identities`, the `cargo_verdict_probe_line` attribution fix, and two tests -- each of which grew the file without growing its receipt. That is precisely the drift this carrier exists to catch, committed inside the carrier, and it made the PR's sole checkable receipt both inaccurate and incomplete. The review's verdict is the right one. The roster is now exact against the file: 47 rows, 47 declarations, zero stale, zero unaccounted, zero duplicates, verified by re-running the audit after the edit rather than by reading the diff. A note records why it drifted, and states the standing hazard plainly: this is a HAND ROSTER BESIDE ITS SUBJECT, so it can only be re-verified, never trusted. It will drift again on the next declaration added and nothing in the required run compares the two. Its dissolution is the v1-hand-queue-drain lane this obligation already names. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…eletes it DESIGN.md is generated from dag/gunbc/design_document.dag. The emit-stage census result had been hand-edited into the projection and never reached an authority, so it was not content the repository holds -- it was content awaiting silent deletion by the next person to run the gate. The authority read "POPULATION: UNCOUNTED AND UNBOUNDED" and mentioned gunbc.emit_stage_blocking_population_census ZERO times, while that carrier exists in dag/ and the projection carried its full result: the observable three, the latent at-least-eleven, the unreachable upper bound, and the declaration that 4b(3) is still unmet. A regen would have reverted the row to a state its own carrier contradicts. Found by regenerating, not by reading. That is the only instrument that can find this class -- a hand-edit to a generated file is invisible to every gate that reads the file, and visible only to the generator. The same check caught the first commit's restoration being INCOMPLETE: it left 5 bytes behind, and those 5 bytes were two tense corrections the inserted text required, not cosmetic residue. Orphaned prose is not a stray paragraph; it is an edit with dependencies on its surroundings, which is why this passage was ported as a unit rather than spliced. A THIRD ORPHAN IN THIS FILE IS DELIBERATELY NOT HERE. The `bound-shaped closure` failure mode was missing from gunbc.recurring_failure_mode, and this branch briefly carried a row for it -- until deep-gull-307 turned out to have independently authored the SAME row, same identity, same roster slot, same 1093 characters, in #9405. Had both landed the roster would carry it twice and the paragraph would render the sentence twice: a duplicate no gate catches, because each PR is individually correct and the drift gate compares the projection to an authority that agrees with it. Theirs is approved and green; this one yields. The orphan class is discovered by regeneration. The DUPLICATE class is not discoverable that way at all -- it needs someone to notice two open PRs touch one authority, and nothing does. This was caught because their completion note quoted a character count that matched. Verified by execution: regenerate, then account for every changed line. The only delta against the previous commit is the 1096 characters of the yielded row; the census and repo_ruleset restorations regenerate byte-identically. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… denominators main landed the emit-compile phase (#9405), which NARROWS the emit-stage escape row; this branch restores the census result the projection had been carrying with no authority. Both are about the same exposure and were written a day apart by authors who could not see each other, so the conflict was not an interleave -- taking either side deletes a landed fact. Resolved by taking MAIN as the base for the .dag hunk and reapplying this branch's two restorations onto it by anchor, so the emit-compile narrowing survives byte-for-byte. DESIGN.md was REGENERATED rather than resolved: neither side's bytes are the projection of the merged authorities, so picking either is guaranteed wrong rather than merely risky. The merge driver refuses that path for exactly this reason. AND HAVING BOTH TEXTS PRESENT WAS NOT THE SAME AS THEM BEING CONSISTENT. main's paragraph ends "the population of such closures remains uncounted"; this branch's restoration, ~8k characters earlier in the same row, says the population was COUNTED on 2026-08-27. Read in sequence that is a row which counts something and then declares it uncounted, with the later measurement appearing first. They are not in conflict -- they have different denominators. The census counts BLOCKING DIAGNOSTICS reachable from a whole-root emit; main's clause counts CLOSURES NO ROSTERED ENTRY REACHES, and a diagnostic can be counted while the closure carrying it is unrostered. Neither figure answers the other, and nothing said so because neither author knew the other clause would exist. One sentence now states both denominators; neither author's claim is edited. Found because clever-tern-899 measured the conflict and declined to resolve it, flagging that a mechanical merge would be correct on the bytes and wrong on the meaning. It would have been: both texts verified PRESENT is where this was about to stop. The `bound-shaped closure` row is NOT in this diff and appears exactly once in the projection -- it arrives from main, where deep-gull-307's independently authored duplicate landed. This branch yielded it before the merge. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…same two rows #9405 rewrote the CI and emit-stage rows in gunbc.design_document while this branch was rehoming orphaned prose into those same rows, so git could not merge them. Both sides carry real content and neither side was taken whole. The decisive measurement: main's DESIGN.md no longer contains the orphaned passages either. #9418 landed its emit-census result into the GENERATED file only, never into the authority; #9405 then edited the authority and regenerated, and the orphan was deleted as a side effect. That is the same mechanism this branch exists to repair, caught a second time on the same file while repairing the first. Resolution, per row: row 0 (CI) main's newer text is the base -- it adds the emitted-closure cargo phase and the partition-crate boundary. The lost repo_ruleset clause is re-inserted between two anchors that exist verbatim on both sides, so the splice is positional only, not editorial. row 4 (emit) main's newer text is the base -- it adds the "A REQUIRED PHASE NOW COMPILES AN EMITTED CLOSURE ... NARROWED RATHER THAN RETIRED" narrowing. Its "POPULATION: UNCOUNTED AND UNBOUNDED" clause is REPLACED, because it is stale rather than merely older: gunbc.emit_stage_blocking_population_census exists on main and carries census_run_invocation, and #9418 landed before #9405. A census was taken; the authority still said it had not been. Main's own new fact -- two specimens escaping by three distinct modes -- is preserved beside the census result. DESIGN.md is REGENERATED from the merged authority, never hand-merged. The generator was rebuilt from the composed tree first, because #9416 changed lambda type-variable binding in the compiler and regenerating with the old binary would not have been the generator that will execute here. Evidence: zero tokens lost against this branch's pre-merge DESIGN.md. Three lost against main -- `UNBOUNDED.`, `bound.` and `recording.` -- all punctuation-attached remnants of the two sentences deliberately rewritten above, with no content behind them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…result is main plus exactly those main advanced onto the CI row again -- #9451 landed the emit-stage census carrier and #9405's emit-compile phase before it. Third conflict on the same paragraph today. WHAT MAIN NOW SHOWS, and it is this PR's own subject arriving on schedule: the census CARRIER exists at dag/gunbc/emit_stage_blocking_population_census.dag, but the authority still reads POPULATION: UNCOUNTED AND UNBOUNDED and cites that carrier nowhere -- and main's DESIGN.md has now LOST the census prose entirely. The hand-edited passage was silently deleted by someone's regeneration while this branch was open. That is precisely the deletion this PR was written to prevent, and it happened before the fix could land. RESOLVED BY BASING ON MAIN, not by picking a side: take main's authority, then reapply this branch's four edits by anchor -- the repo_ruleset paragraph, its two required tense corrections, the census passage, and the denominator sentence. DESIGN.md regenerated rather than resolved. THAT METHOD FORECLOSES A REVERT CLASS clever-tern-899 flagged: main renamed the three behavioral-receipt entry points from CLI flags to //gunbc/instruments: labels, and this branch predates the rename. Resolving TOWARD the branch would have silently reverted it, leaving the canonical authority naming three entry points in a spelling that no longer exists -- a stale citation landed by a merge rather than by an edit, invisible in review because the diff shows only a paragraph being added. VERIFIED EXACTLY RATHER THAN BY SPOT-CHECK. Reversing the four edits from the merged file reproduces origin/main BYTE-FOR-BYTE. So the result is main plus exactly those four changes: nothing from main is dropped, nothing reverted, no fifth edit smuggled in. Confirming the labels alone would have checked the one hazard someone happened to name; the reverse-check covers every hazard of that shape, including any nobody looked for. Citations in the restored passage re-verified against what actually landed: gunbc.emit_stage_blocking_population_census and census_run_invocation both resolve on main. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A required CI phase that runs cargo over an emitted closure, with a red established by mutation
What this closes
DESIGN's Building-&-checks section carries a declared rung drop headed "A BLOCKING EMIT-STAGE
DIAGNOSTIC CAN SIT ON MAIN INDEFINITELY WITH NO REQUIRED PHASE THAT FAILS", whose restoration
trigger reads: this row retires when a required phase EMITS over a closure that reaches call sites
— the compile re-add on the queue the floor cut created.
The
v2-emissionphase emits and stops. Its own header incli_run.rsenumerates what it thereforecannot see, and the first item is "a rustc error in the emitted tree (nothing here compiles the
emission)". Its dissolution row states the boundary in as many words: same producer, stopping
before cargo.
This is that missing conjunct. Same producer (
compile_entry_emission), the emitted files writtenas a crate, cargo run over it.
The DESIGN row is NARROWED, not retired. The phase reaches a bounded roster of closures, so
escape mode one is smaller and still open; escape modes two and three (the orphan module no closure
reaches, and the
DeclarationRefnaming it) are untouched.The red is the deliverable, and it is manufactured rather than found
A cargo phase that is green because nothing was measured is the decoration §4b calls worse than
absent — and this repository already has that shape at nine identities: DESIGN's own row records a
compile-clean witness family where five members are no-route, three are declined-live, and the one
that passes checks that two realizations agree about a policy row.
So a green baseline alone is not a pass here. Every run:
NotAttempted,NotDiscriminatingandRestoreFailedeach fail the phase. A cargo verdict thathas stopped being a function of the emitted bytes stops the line instead of reporting coverage.
Why this matters for the subject choice, and it is the load-bearing argument in this PR. Two
reviewers independently assumed the red had to be FOUND — that discrimination depends on a real
emission defect happening to sit inside the subject. On that assumption the subject is load-bearing
for whether the phase can go red at all, and any small subject risks a permanently green check. This
phase does not make that assumption. It INJECTS the defect. That collapses the subject question from
can this go red to which defects are in range — coverage, not viability. §4b asks whether the RED
is authorable before you write the check; this makes the answer unconditional instead of contingent.
The fault is a type error rather than a syntax error deliberately: a syntax error would also be
caught by anything that merely parses, so it could not discriminate a cargo verdict from a cheaper
reader.
E0308requires rustc to have type-checked the module, which is the reach being claimed.A failed restore is terminal for the run. Not a per-entry finding siblings continue past, and not
recoverable by re-running: the arms share one cargo target directory, so after a failed restore no
later baseline taken through it is attributable. Later entries report
NotExecuted, which cannotpass. A head whose restore arm did not hold gets no green from this phase at all, rather than a green
whose restore was never established.
Membership is DECLARED, never derived — and that is not the lazy option
A cover computed each run as "whatever currently compiles" is self-disarming: it drops a member
at precisely the moment that member becomes the defect the phase exists to catch, and reports green
over a quietly smaller subject. That is DESIGN's empty-observation narrow, and it is strictly worse
than the absorbing fallback — a widen is merely expensive, a narrow is silently uncovered.
Measured rather than argued:
dag/std/interval.dagdoes not compile right now. A derived coverexcludes it and is green. A declared cover containing it is RED, which is the correct answer.
So the measurement sits at the decision, not at the verdict:
The same narrow arrives through the budget, and that is the version more likely to be written:
a cover computed as "the N entries that fit the window" reads as a cost decision rather than a
derived cover, but it drops a member exactly when that member becomes slow — and an entry becomes
slow when it breaks. Cost decides whether a row is ADDED; it never decides whether a declared row
is MEASURED.
The remainder is identities, not a fraction. The phase prints
selection: universe=N <digest> selected=N <digest> not_selected=N <digest>and writes theunselected identities to a file, naming the path. A percentage says how much is unobserved and
never WHICH, so nothing downstream can join it, refuse on it, or watch it shrink — it is not the
bounded population §4b(3) asks of a declared gap. A reached-modules figure stands as context, after
the identities and never in place of them. Retention failing stops the line: a run reporting a
remainder it could not persist has published a count with nothing behind it.
And the phase's success means the selected observation was taken and persisted — never that the
corpus is clean. A partial phase is a legitimate wall for an exact selected subject and zero wall
for its complement; it becomes decoration only when its name, its trigger, or its consumers let the
selected proposition stand in for the exhaustive one.
The roster row also states, in advance, that deleting a row to get green is not the remedy — the
red IS the finding, and removing the member deletes the finding rather than resolving it.
The subject, and what bounds it
Eight entries, every one measured clean before being written down:
dag/gunbc/ci_layer_roots.dagdag/std/measure.dagdag/std/node.dagdag/extdeps/uri.dagdag/gunbc/scm/load_standing.dagdag/std/content_hash.dagdag/std/abi.dagdag/std/logic.dagNo union figure is quoted: the closures overlap heavily and the union was not measured, and summing
them would be an entry-grain reading of a closure-grain fact.
Two things bound this roster, and I had one of them wrong. I previously wrote that cost was not
binding, on a figure that turned out to be the cargo half only. Measured per entry: reconcile 35-42s,
emit 2-8s, cargo seconds against a warm shared target dir — so ~40-50s an entry, ~85% of it reconcile,
and nothing shared across entries. Against the free build-lane window that is tens of entries, not
hundreds. So COST bounds how many.
What cost cannot buy is ADMISSIBILITY, which bounds which: entries exist today whose emitted closure
does not compile — the v2 compiler root, the emission phase's own subject, is one of them — and
admissibility does not fail entry by entry, because closures SHARE defects. One uncompilable site
in a widely-imported module disqualifies every entry whose closure reaches it, so the admissible set
is a property of where broken sites sit in the import graph. This roster is a beachhead sized against
a temporary condition; emitter repairs that clear a widely-reached site return many entries at once.
For scale: the enrolled emission phase's single entry reaches 160 of 4122 modules — about 4%.
The arms, executed
Five arms against one binary on a 128-core host, each a full run of
claim_executor --required-emit-compile --source-root dag --source-root src/v2:RC=0, 8/8 baselinesCompleted status=0, 8/8Discriminatedlogic.dag+interval.dag(known-broken)logicDiscriminated;intervalbaseline=Completed status=101,mutation=NotAttemptedRC=0,DiscriminatedNotDiscriminating, phase failsRC=1,NotDiscriminatingRC=0,DiscriminatedArm 2 is the one worth reading closely. The already-red entry reports
mutation=NotAttempted reason=the baseline did not compile — a fault injected into a failing tree discriminates nothing. The phase declines to claim discrimination on a tree that was red beforeit touched it, rather than reporting a red it did not cause.
Arm 4 found a real defect and it is fixed in this PR. Its first execution reported
Discriminatedwith a red line quoting a#[cfg]warning, over a cargo run whose own tail saidFinished— a green compile reported as a discriminating red. Cause: a stale background invocationoverlapped the foreground one, and the two shared one probe root, so one run's faulted tree was the
other's baseline and one run's restore erased the other's red before it was read. Neither process
could see anything wrong.
The fix is a refusal, not a wait and not a private directory per run. Waiting serializes into the
same shared state with the same ambiguity about whose artifacts are whose; a private directory buys
isolation by discarding the warm target dir the phase is built around. So a second concurrent run
takes an exclusive lock or stops the line. Executed both ways: two overlapping runs, the first
RC=0, the secondRC=1withanother emitted-closure compile run holds /tmp/gunbc-emit-compile/emit-compile.lock.That the arm designed to catch a non-discriminating verdict was itself handed a fabricated one is
the argument for having built it.
Deliberately not in this PR
emit-stage producer (
AmbiguousAnonymousRecordLiteral), which this repository currently lacks.Enrolling it here would be scope creep into a required run — but landing it UNENROLLED would be
worse: a new artifact with no consumer is exactly the experimental residue §6 tells reviewers to
presume is a scaffold. So it is in neither state here. It is routed with its content intact and
lands with whatever surface consumes it.
dag/std/interval.dag,src/v2/std/node.dag,src/v2/compiler/01_tokenize.dag). Routed with receipts; no root is claimed here.comparison against a population measured on the current tree is the tree-copied oracle §5 rejects.
validate_workflow_param_defaults' severity — unmeasured, and not decision-bearing (see below).Seed growth
gunbc.emitted_closure_compile_seed_growthenumerates every hand-authored item at identity grain.Zero uncitable items: the file has no
implblock, because an impl method has noDeclarationRefspelling and would grow the class
seed_growth_admissionreports asseed_growth_uncitable_item_keys.v1_compiler.declaration_indextook the same route for the same reason.The manifest is rendered from the modeled cargo authorities —
render_cargo_package_header_prefix,stage0_foundation_runtime_dependencies,render_stage0_crate_dep— rather than authored as markup.The corpus already carries a hand-concatenated probe manifest marked scaffold debt in its own module;
consuming it from a merge-blocking gate would have pinned that debt open on the required path, and
authoring a second one would have been new scaffolding.
Evidence
Measured on clean main, one BuildBuddy dispatch, emit + cargo per entry against a shared target dir.
The instrument is named rather than its output transcribed into any carrier: the phase prints one
required-ci: emit-compileline per entry plus adeclared=/passed=/not_clean=summary, andclaim_executor --required-emit-compile --source-root dag --source-root src/v2re-derives it.A correction on the record: an earlier sweep of mine counted errors with
grep -ac '^error', whichmatches cargo's trailing
error: could not compilesummary line as well as real diagnostics, so everycount it produced was inflated by exactly one. The sweep now reads cargo's JSON message stream, so
identities come from rustc and the count is derived from that same list — it cannot disagree with the
identities it summarises.
Two defects the first CI executions found, and what each one was
Both were found by execution rather than by reading the diff, and both are recorded because the
mechanism recurs even though these instances do not.
All 8 baselines at
status=101, from a missing[features]section. The probe manifest did notdeclare the feature the emitted
v1_rt.rsgates on, sounexpected_cfgsfired — a WARNING on aworkstation and a hard ERROR under CI's
RUSTFLAGS=-D warnings. The baseline arm was thereforereporting a red that had nothing to do with the emitted closure, which would have made every entry's
mutation arm meaningless had it not failed loudly first. The corpus had already predicted this exactly:
tools.self_host_logic_behavioral_transportslb_cargo_features_noteis a hand-concatenated[features]block carrying a note about this failure, and there is a second one beside it.The fix is not a third copy of that string.
v1.compiler.stage0_cratesnow exposesstage0_features_for_crate_kind(kind), andstage0_partition_row_features(row)is derived from it, sothe probe reads the modeled authority. The kind is the whole subject: an earlier revision of the fix
passed a fabricated
GeneratedPartitionCrateRow— blankcrate_dir, empty module lists — to reach afunction that reads
row.kindand nothing else, and that row did not merely waste a value, it ASSERTEDthe probe is a generated partition crate, which it is not. The corpus runs censuses over partition
rows; a synthesized one naming no directory reads as real later.
The probe root was spelled twice, so the fix for a shared-
/tmpEACCES reopened it one line away.On a self-hosted runner
/tmp/gunbc-emit-compileis foreign-owned and the denial is permanent, not aflake. The root moved to
RUNNER_TEMP, butemit_compile_reportwas left re-derivingstd::env::temp_dir().join("gunbc-emit-compile")by hand — so in one run's log the crate directoriessat under the runner's
_work/_tempwhile the retention file went to/tmpand was denied. Two homesfor one fact, visible as both spellings in the same phase in the same run.
probe_root()is now the only composition site, and the check that keeps it that way is a text checkrather than a behavioural one, deliberately: a second spelling is exactly what a behavioural test
cannot catch, because both spellings are correct until the authority moves. The test assembles its own
needle with
format!instead of writing the literal — the first cut of it failed by counting itself,which is the same second-spelling defect committed inside the test for it.
Executed under
RUNNER_TEMP=/tmp/rt-verify-final: the remainder is retained at<RUNNER_TEMP>/gunbc-emit-compile/emit-compile-not-selected.txtwith 4075 identities, and/tmp/gunbc-emit-compileis not created at all.One residual, named rather than left to be found.
probe_root()falls back tostd::env::temp_dir()whenRUNNER_TEMPis unset, which is right locally but means the CI arm'ssafety is a property of the environment rather than of the code path: a future job form that does not
set
RUNNER_TEMPtakes the shared root again and earns the same permanent red. The--required-cicaller knows it is on the path where a shared root is unacceptable, so passing that requirement in —
refuse rather than fall back, on that arm only — makes the state unreachable instead of
better-diagnosed. That is construction over validation and it is the natural follow-up; it is not in
this PR.
What the Discriminated verdict does NOT establish, found by review after the phase went green
warm-hawk-909counted the mutation subjects across the eight entries in the first green run.Seven chose
std_error_primitivesand the eighth chosev1_rt— the emitted runtime, which is inevery closure — so all eight mutated a module in the shared core.
mutation_subjecttakes thefirst
pub modinlib.rswherem != entry_module: it steps over the entry's own moduleexplicitly, wherever that module happens to sit.
So the verdict currently establishes cargo ran, and it failed when a module in the shared core was
broken. It does not establish this entry's own distinctive modules reached the compiler.
There is a sharper form of this than the count alone shows, and it is why the follow-up is worth
doing rather than filing and forgetting. The entry's own module is the one module in the crate that
nothing else references — the closure's other members are its dependencies, and dependencies do
not import the root. That makes it precisely the module a faulty emission could omit while the crate
still compiled, and the selector's "first non-entry module" rule excludes it by construction. A
partial drop that broke a reference is still caught by the baseline; a reference-closed drop of the
entry's own leaf is not, and
files=Nis printed but nothing asserts on it.This is the shape the corpus already names — total at the level examined, blind one level down.
The match is exhaustive over "did cargo run and fail on an injected fault" and silent about "was
this entry's closure the thing compiled". There is no missing arm to notice, which is what makes it
invisible: the verdict is
Discriminatedand it is honestlyDiscriminated.Not fixed in this PR, deliberately. The phase as it stands is a real wall and strictly better than
the nothing that preceded it, and the fix is a selector change rather than an architecture one:
prefer the entry's own module as the mutation subject, and treat its ABSENCE as a typed refusal
rather than a silent fallback to a shared member — a silent fallback would accept exactly the bad
case. Whether absence can be made terminal depends on whether every rostered entry's own module is
in fact emitted as its own
.rs, which is a measurement I have not yet completed, so the follow-upcarries that measurement rather than assuming its answer.
That measurement is now done, and it closes the question in the strong direction. Reading each
emitted
lib.rsfor all eight rostered entries at this head: the entry's own module is emitted asits own
.rsin 8 of 8, so the absence arm can be a typed terminal refusal and the follow-upneeds no silent-fallback arm at all.
The same measurement corrected two things I had wrong, both recorded because they were load-bearing
for the fix. Ordering is not the mechanism: the entry sits second-to-last in 6 of 8 (
v1_rtislast in all 8) but first in 2 of 8 —
std_abiandstd_logic, the two smallest closures — sono ordering rule explains the selection; the explicit
!= entry_modulefilter does. And thecheap-looking alternative of "mutate the last module" would have picked
v1_rtevery single time,the most-shared module in the tree — strictly worse than the current behaviour, a regression
dressed as a fix, stopped only by measuring instead of assuming.
Recorded here rather than left in review chat because it narrows what a green
emit-compilemeans,and a limitation that lives only in a conversation is not available to the next reader of the log.