From bafe22f6c74bff1c927e916c0675bdd8bba88a5c Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Tue, 1 Sep 2026 09:36:42 +0000 Subject: [PATCH 1/6] Fixture-callable emitted-crate compilation: a route that reaches rustc over an emitted fixture closure A witness can already compile an in-memory .dag source and assert on the emitted TEXT (compile_dag_rust_emit_check). That is a SPELLING oracle: a meaning-level emitter defect is invisible to it whenever the wrong bytes still contain the right substring, and it is exactly what rustc's type checker refuses. emitted_closure_compile_host does reach rustc, but only over the entry files its roster names, so no fixture-authorable subject could be posed to it at all. fixture_closure_rustc_verdict closes that: the same in-memory source shape, emitted through the same compile_sources, written by the SAME crate writer (write_probe_crate_files, factored out of write_probe_crate) and handed to the SAME run_cargo -- so a green here and a green in the required phase cannot be two facts about two crates. run_cargo's attribution symbol becomes the caller's, because the required phase attributes its own injected fault while this route attributes a red to the fixture's own emitted module. Executed, both directions, on the real path: control fixtures/fixture_closure_rustc/green_probe.dag cargo status=0 red fixtures/fixture_closure_rustc/text_nonliteral_probe.dag cargo status=101 E0308 expected `Rc>` found `std::string::String` at src/fixture_closure_rustc_text_nonliteral_probe.rs:13:5 The red is the fixture gunbc.rung_drop text_boundary_identity_wall names; that row's emitted-Rust claim now has an executing probe rather than a sentence. No rung is claimed here and no class changes rung. NOT A GATE, stated positively: --required-emit-compile is not among REQUIRED_CI_PHASES and no workflow invokes it, so what this enrolls is green by execution and blocks nothing. v1 growth admitted under the DESIGN section 3 PURPOSE test by the owning manager: it adds no new host CAUSE, only a second entry point over a source shape the tree already accepts. Dissolution is the trigger emitted_closure_compile_seed_growth already carries -- a modeled write-file-set and invoke-cargo actuation -- which does not exist yet, and that finding is the more important half. Files: fixture_closure_rustc_verdict + FixtureClosureOutcome (gunbc's own refusal stays separate from a rustc verdict), per-arm diagnostics, the enrolled pair, and claim_executor's --fixture-closure-compile (report, do not adjudicate: a red is one arm of someone else's discriminator) and --fixture-closure-pair (adjudicate: here the pair is the subject). Seed-growth roster re-derived exact at 74/74. Class row filed: instrument_reenters_its_own_build_tool. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HoAvoacTJ3UVqBhBSfCYZh --- DESIGN.md | 1 + .../emitted_closure_compile_seed_growth.dag | 90 ++++- dag/gunbc/recurring_failure_mode.dag | 7 + docs/design-ledgers.md | 1 + .../fixture_closure_rustc/green_probe.dag | 8 + .../text_nonliteral_probe.dag | 11 + src/v1/stage0/src/bin/claim_executor.rs | 126 ++++++- src/v1/stage0/src/cli_run.rs | 14 +- .../src/emitted_closure_compile_host.rs | 355 +++++++++++++++++- 9 files changed, 596 insertions(+), 17 deletions(-) create mode 100644 fixtures/fixture_closure_rustc/green_probe.dag create mode 100644 fixtures/fixture_closure_rustc/text_nonliteral_probe.dag diff --git a/DESIGN.md b/DESIGN.md index e0acd8598b1..a6924f7fc9e 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -212,6 +212,7 @@ One row per class, each carrying its recognition rule and its receipts, in [docs - `instrument_output_read_as_subject_content` - `executed_conjunct_discriminates_nothing` - `unbacked_execution_claim` +- `instrument_reenters_its_own_build_tool` - `mitigation_injected_where_judgment_declined` ## Building & checks diff --git a/dag/gunbc/emitted_closure_compile_seed_growth.dag b/dag/gunbc/emitted_closure_compile_seed_growth.dag index 3ba6959de97..7f0128f24d0 100644 --- a/dag/gunbc/emitted_closure_compile_seed_growth.dag +++ b/dag/gunbc/emitted_closure_compile_seed_growth.dag @@ -87,6 +87,79 @@ import gunbc.seed_growth { SeedGrowthJustification } // answered by execution rather than by inspection, and it is why a green from this phase carries // information that a bare cargo-exit-status phase would not. +// THE SECOND ROUTE INTO THIS HOST: FIXTURE-CALLABLE EMITTED-CRATE COMPILATION (2026-09-01). +// +// WHAT WAS MISSING, AND IT WAS A REACH GAP RATHER THAN A MISSING CHECK. A witness can already +// compile an in-memory .dag source and assert on the emitted TEXT +// (v1_compiler.cli_run.emit_host compile_dag_rust_emit_check: substring includes and excludes). +// That is a SPELLING oracle. A meaning-level emitter defect -- a host representation emitted +// where a structural carrier was declared, a dropped type argument, a reference emitted past its +// resolved binding -- is invisible to it whenever the wrong bytes still contain the right +// substring, and it is exactly what rustc's type checker refuses. Meanwhile the phase above DOES +// reach rustc, but only over the entry files gunbc.ci_layer_roots required_emit_compile_entries +// names, so no fixture-authorable subject could be posed to it at all. Fixture-authorable +// subjects with no compiler behind them on one side; a real compiler with no fixture-authorable +// subjects on the other. +// +// WHAT LANDED: fixture_closure_rustc_verdict, taking the SAME in-memory source shape +// compile_dag_rust_emit_check takes, emitted through the same compile_sources, written by the +// SAME crate writer (write_probe_crate_files, factored out of write_probe_crate for this) and +// handed to the SAME run_cargo. Sharing those three is the whole design: a separately authored +// fixture harness would have made a green here and a green in the required phase two facts about +// two crates, which is the property this file's header already refuses for the emission producer. +// +// GUNBC'S OWN REFUSAL IS A SEPARATE ARM AND NEVER A RUSTC VERDICT. FixtureClosureOutcome +// separates SourceRefused (v1 refused the fixture; nothing was emitted, so rustc had no subject), +// CrateNotWritten and Measured. Collapsing the first into a red would report a fixture gunbc +// rejected as 'rustc refused the emitted bytes' -- a claim about bytes that never existed. +// +// THE ROUTE SHIPS WITH ITS OWN DISCRIMINATING PAIR, run_fixture_closure_discrimination, because a +// route that reaches rustc proves nothing on its own: a green says only that SOME crate compiled. +// The control is a self-contained fixture whose closure must compile. The RED is meaning-level +// and is the fixture gunbc.rung_drop text_boundary_identity_wall names -- a NON-LITERAL +// kernel-String value at an exact v2.std.text.String boundary, admitted by leaf-name compatibility +// with no homomorphism row. That row's temporary rung claims this class is mechanically +// preventable ON THE EMITTED-RUST PATH because such a value 'renders as a Rust type mismatch and +// the mandatory emitted-crate cargo check refuses it' -- a claim about a fixture the entry roster +// cannot reach. This arm is where that claim EXECUTES instead of being asserted. +// +// ATTRIBUTION IS PART OF THE RED. The red fixture's closure is hundreds of modules and a bare +// non-zero status could come from any of them, so the pair passes only when a diagnostic line +// NAMES the fixture's own emitted module; otherwise the report reads 'unattributed' and the pair +// FAILS rather than crediting a red the fixture did not cause. run_cargo's attribution symbol is +// therefore a parameter of the caller rather than the injected mutation's constant: the required +// phase attributes its own fault, the fixture route attributes the fixture. +// +// WHERE IT EXECUTES, STATED HONESTLY BECAUSE IT IS NOT THE MERGE PATH. The pair runs inside +// claim_executor --required-emit-compile, in the same process and after the entry roster so the +// shared target directory is warm, and its verdict JOINS that mode's exit status rather than +// being printed beside it. That mode is not a phase of --required-ci and no workflow invokes it, +// which gunbc.rung_drop required_gate_bankruptcy and emit_stage_blocking already declare for the +// phase this route joins. So: green by execution, and NOT a merge gate. Nothing here claims a +// rung for the text-boundary class, which stays exactly where its own row puts it -- what changes +// is that the row's emitted-Rust half now has an executing probe instead of a sentence. +// +// WHY IT IS NOT A cargo test. The route spawns cargo against the workspace target directory, and +// a test binary launched BY cargo would be a nested cargo over the directory the outer one holds. +// It does not fail there, it BLOCKS, which is worse: a lock wait is indistinguishable from a slow +// legitimate build. Measured, at the cost of a thirty-minute remote run terminated rather than +// concluded, and filed as gunbc.recurring_failure_mode instrument_reenters_its_own_build_tool -- +// an instrument may not be hosted by its own subject's actuator. The executing consumer is +// therefore the binary, exactly as the entry roster's is. +// +// HAND-ITEM DELTA FOR THIS LANE: fifteen declarations, every one enumerated in the roster below +// rather than counted -- write_probe_crate_files, FixtureClosureOutcome, fixture_closure_compiled, +// fixture_closure_attributed_line, fixture_closure_summary, fixture_rust_module, +// fixture_closure_rustc_verdict, fixture_arm_diagnostic_lines, FixtureDiscrimination, +// FIXTURE_GREEN_PATH, FIXTURE_RED_PATH, fixture_arm_verdict, run_fixture_closure_discrimination, +// fixture_discrimination_passed, fixture_discrimination_report. run_cargo, write_probe_crate and +// the cli_run re-export list are MODIFIED rather than added, and claim_executor gains argv arms +// and two mode bodies inside an existing function -- ExistingSeedItemModified in both files, so +// listing them would net a modification into an addition census. THE ROSTER IS EXACT AGAINST THE +// FILE AS OF THIS LANE: 74 declarations counted at every indent by the rule stated above, 74 +// rows, zero stale, zero unaccounted. Re-derive it rather than trusting it; the paragraph above +// records what happens when nobody does. + // HAND-RUST RECEIPT FOR THE REQUIRED-PATH ROOT CORRECTION. Additions: PROBE_ROOT_DIR_NAME, // required_ci_probe_root_from_runner_temp, required_ci_emit_compile_probe_root, // local_emit_compile_probe_root, and the_probe_root_does_not_exist_without_runner_temp -- five @@ -135,6 +208,21 @@ data emitted_closure_compile_seed_growth_justification: SeedGrowthJustification DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "probe_crate_dir", field: WholeDeclaration }, DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "write_probe_crate", field: WholeDeclaration }, DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "run_cargo", field: WholeDeclaration }, + DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "write_probe_crate_files", field: WholeDeclaration }, + DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "FixtureClosureOutcome", field: WholeDeclaration }, + DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "fixture_closure_compiled", field: WholeDeclaration }, + DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "fixture_closure_attributed_line", field: WholeDeclaration }, + DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "fixture_closure_summary", field: WholeDeclaration }, + DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "fixture_rust_module", field: WholeDeclaration }, + DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "fixture_closure_rustc_verdict", field: WholeDeclaration }, + DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "fixture_arm_diagnostic_lines", field: WholeDeclaration }, + DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "FixtureDiscrimination", field: WholeDeclaration }, + DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "FIXTURE_GREEN_PATH", field: WholeDeclaration }, + DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "FIXTURE_RED_PATH", field: WholeDeclaration }, + DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "fixture_arm_verdict", field: WholeDeclaration }, + DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "run_fixture_closure_discrimination", field: WholeDeclaration }, + DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "fixture_discrimination_passed", field: WholeDeclaration }, + DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "fixture_discrimination_report", field: WholeDeclaration }, DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "closure_modules", field: WholeDeclaration }, DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "mutation_subject", field: WholeDeclaration }, DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "establish_discriminating_red", field: WholeDeclaration }, @@ -167,5 +255,5 @@ data emitted_closure_compile_seed_growth_justification: SeedGrowthJustification reason: "WHY RUST IS STILL NEEDED, and it is a REACHABILITY limit rather than a modeling gap. The subject is a required CI phase: it must run inside claim_executor, which is the only witness-executing consumer in the tree, and its stages are host effects -- writing an emitted tree to disk and spawning cargo over it. The required floor's hermetic envelope REFUSES host effects during preparation, so a .dag witness whose subject is a subprocess exit status is counted executed while its assertion never runs; DESIGN's Building-&-checks section records that boundary in as many words, and records that mocking the refusal would pass the witness against a fabricated exit status. So the phase has to live where the effects do, exactly as required_regen_host and partition_crate_boundary_host do.\n\nWHY IT IS ADMITTED AGAINST THE v1 FREEZE, ON PRECEDENT RATHER THAN ANALOGY. gunbc.v1_maintenance_standing v1_seed_standing admits a change by PURPOSE -- does it serve the v2 self-host program -- and the purpose test reaches the INSTRUMENT that measures that program, not only the program. This is that instrument in the most direct available sense: the self-host claim IS that the emitted tree compiles and behaves, and until now nothing on the merge path compiled an emitted tree at all. gunbc.floor_non_verdict_enrollment and gunbc.declaration_index_seed_growth are both admitted on that same footing. Classified against the five refused classes: NewLanguageBehavior -- no, the compile pipeline is byte-untouched, no diagnostic is added, moved or removed. NewCompatibilityObligation -- no, nothing is kept working for an old caller. NewEscapeHatchOrAdmissionRow -- no, and the direction is opposite: this adds a wall and adds no flag, env var or mode that skips it. SeedFeatureCompletion -- no, the seed compiler gains no capability; it compiles exactly what it compiled before. PublicSurfaceGrowth -- the nearest, and the test is a diff over THE EMITTED SEED'S EXPORTED DECLARATIONS: the file is wired by #[path] mod inside cli_run.rs exactly as declaration_index.rs, phase_profile.rs and partition_crate_boundary_host.rs are, so it contributes no pub mod line to the emitted lib.rs and its SeedRetainedIntrinsicRegistration carries has_pub_mod: false.\n\nWHAT IS NOT CLAIMED. This phase is not a compile of the corpus and must not be read as one: gunbc.whole_corpus_compile_admission refuses a whole-bundle compile on the default runner and records two EXIT=137 kills behind that refusal, so the subject is a bounded roster of entries in gunbc.ci_layer_roots required_emit_compile_entries. It carries no diagnostic count, no baseline and no ratchet -- a merge-blocking comparison against a population measured on the current tree is the tree-copied oracle DESIGN 5 rejects.", owning_dissolution_lane: "v1-hand-queue-drain" as RoadmapNodeId, trigger: "Delete this host when the phase's two host effects are modeled and a modeled actuator can perform them: writing an emitted file set to a directory, and invoking cargo over a manifest with a typed outcome. extdeps.cargo already models the manifest, the dependency rows and the target kinds, and extdeps.filesystem.filesystem_io models the write, so what is missing is the ACTUATION -- a modeled operation the required run can execute without the hermetic envelope refusing it. When that exists, run_emit_compile_entry becomes a .dag fold over those two operations and every item here is deleted, not re-homed. A PARTIAL migration is admissible and is the expected shape: the moment the cargo invocation is a modeled transport, run_cargo and the CargoVerdict vocabulary go first and the emission-to-disk half stays behind. What does NOT dissolve these items is the phase merely changing its roster, and what does NOT dissolve them is a hermetic witness asserting a fabricated exit status -- that would retire the evidence while retiring nothing it guards.", - current_boundary: "src/v1/stage0/src/emitted_closure_compile_host.rs; src/v1/stage0/src/cli_run.rs (the #[path] mod line and the re-export); src/v1/stage0/src/bin/claim_executor.rs (RequiredCiPhase::EmitCompile and its phase body); dag/gunbc/ci/ci_layer_roots.dag required_emit_compile_entries; src/v2/compiler/self_host/stage0_crate_layout.dag; dag/gunbc/emitted_closure_compile_seed_growth.dag" + current_boundary: "src/v1/stage0/src/emitted_closure_compile_host.rs; src/v1/stage0/src/cli_run.rs (the #[path] mod line and the re-export); src/v1/stage0/src/bin/claim_executor.rs (RequiredCiPhase::EmitCompile and its phase body, plus the --fixture-closure-compile and --fixture-closure-pair modes); fixtures/fixture_closure_rustc/ (the pair's two arms, which are files rather than string constants so that the arm this pair adjudicates is the arm any caller runs); dag/gunbc/ci/ci_layer_roots.dag required_emit_compile_entries; src/v2/compiler/self_host/stage0_crate_layout.dag; dag/gunbc/emitted_closure_compile_seed_growth.dag" } diff --git a/dag/gunbc/recurring_failure_mode.dag b/dag/gunbc/recurring_failure_mode.dag index 1ac1f81384d..5533578394f 100644 --- a/dag/gunbc/recurring_failure_mode.dag +++ b/dag/gunbc/recurring_failure_mode.dag @@ -228,6 +228,12 @@ data unbacked_execution_claim: RecurringFailureMode = RecurringFailureMode { evidence: [], } +data instrument_reenters_its_own_build_tool: RecurringFailureMode = RecurringFailureMode { + identity: "instrument_reenters_its_own_build_tool" as NonEmptyStr, + authored: "**instrument re-enters its own build tool** (a check whose SUBJECT is the build -- does this emitted crate compile, does this generated tree link, does this artifact still build under the real toolchain -- is hosted inside a process that the same build tool launched, so the check spawns the tool that is already holding the lock on the directory it needs. It does not fail; it BLOCKS, which is the whole reason the class earns a row: a refusal names itself in one line, and a lock wait is indistinguishable from a slow legitimate build for as long as anyone is willing to watch. Measured here rather than reasoned about (gunbc, 2026-09-01, node adhoc-da8c65c0-b3b): a `#[cfg(test)]` consumer of a new emit-then-rustc route, run under `cargo test --release -p v1-compiler --lib`, spawning `cargo build` with `CARGO_TARGET_DIR` pointing at the same workspace `target/` the outer `cargo test` holds -- thirty minutes of remote runner time producing no output at all, terminated rather than concluded, which is why the cost is recorded: the failure mode's signature is that it charges you the timeout to learn anything. **RECOGNITION RULE, and it is available BEFORE the run rather than after: name the process that will launch your check, name the directory your check's subprocess will lock, and refuse the pairing if they are the same tool over the same directory.** The two escapes differ in kind and only one is usually right. Giving the inner invocation its OWN target directory removes the contention and pays a full cold dependency rebuild for every run, which for a check hosted in a unit-test suite is a cost the suite cannot carry. Hosting the check in a BINARY the build tool has already finished producing -- built, then executed as its own process -- keeps the warm shared directory and is what the existing required emit-compile phase already does; the new route simply had to be homed the same way. **THE GENERALIZATION IS THE POINT AND IT IS NOT ABOUT CARGO:** any instrument whose subject is a build reaches for the build tool, and the most convenient host for an instrument is the test harness that same tool runs, so the pairing is what a reasonable author reaches for first. It recurs wherever the two facts are true together -- a generated-artifact check under `npm test` shelling out to `npm run build`, a bazel-hosted test invoking bazel, a make-driven check calling make -- and the guard is the same: **an instrument may not be hosted by its own subject's actuator.** Neighbouring but distinct from `absorbing_fallback`: nothing widens and nothing is fabricated here, the check is simply never able to answer, so the harm is an unanswerable instrument rather than a wrong answer -- and an instrument that hangs will, under any timeout that reports leniently, eventually be read as one that passed.)", + evidence: [], +} + data recurring_failure_mode_roster: List = [ hollow_alias, state_space_conflation, @@ -258,5 +264,6 @@ data recurring_failure_mode_roster: List = [ instrument_output_read_as_subject_content, executed_conjunct_discriminates_nothing, unbacked_execution_claim, + instrument_reenters_its_own_build_tool, mitigation_injected_where_judgment_declined, ] diff --git a/docs/design-ledgers.md b/docs/design-ledgers.md index 6abd1cc41ec..e7611542a65 100644 --- a/docs/design-ledgers.md +++ b/docs/design-ledgers.md @@ -41,6 +41,7 @@ The landing measurement partitions the 31 parser-visible identities into **2 cit - **instrument output read as subject content** (a consumer derives a SET from a report whose completeness is a property of the REPORTING TOOL rather than of the subject. **The boundary against `execution_provenance_loss` is the whole justification for a separate row, because the two look identical at the call site and have opposite repairs**: there a value is consumed as though an observation produced it while nothing proves the observation path RAN, and the fix is to bind an execution receipt; here the observation ran and reported FAITHFULLY — the instrument answered its own question correctly — and what is wrong is that its output was complete for the REPORTER s purpose and was read as complete for the CONSUMER s. Provenance is intact; the SUBJECT is substituted, so no receipt binding can detect it and the repair is instead to derive the set from the subject itself. Distinct from `total_at_the_level_examined_blind`, which is a judgment shape (a match total over the type it examines, blind inside an arm), and from `reachability_read_as_occupancy`, which asks whether a quiet guard should exist; this is neither a match nor a guard. Specimen with a receipt, repaired on main by gunbc#9829: the required floor asked `extdeps.git` `git.Core.DiffUnified0` (`git diff -U0 `) which lines a change touched, and `floor_diff_edits_from_line_ranges` attributed those lines to enclosing declarations to decide which witness identities the change established. Git prints only the hunks that DIFFER, so on a rename it prints the module line and whatever else moved — while every identity at the destination is a newly qualified `module.function` that has never executed under that spelling, because the authored module name moved with the file. **THE REASON IT SURVIVED IS THAT THE TWO ANSWERS COINCIDE BY ACCIDENT IN THE COMMON CASE**: for a wholly-added file every line is a `+` line, so line attribution reaches every declaration and the defect is invisible; it appears only when the instrument gets TERSE, which rename detection is. Measured over the window reachable in a shallow session clone — a floor on the population, not the population, since older renames are unmeasured — 8 of 8 rename-destination `.dag` files carrying test decls were under-enrolled and at least 87 of their 103 identities were never selected; a PURE MOVE with no content edit enrolled NOTHING. It was not merely a reporting gap: changed-witness membership is what OVERRIDES the cost-debt withhold and the outside-gate suppression, so the same miss on a rostered identity is a silent decline of a witness whose author is present, which is the state `v2.workflow.floor_changed_witness` `changed_witness_execution_standing` exists to red. The repair derives the set from the subject: a path `parse_unified_diff_added_paths` already rules new-at-path contributes the declarations THE FILE ITSELF PARSES TO, bounded to that file s own decl list and never the corpus, so it is the precise answer rather than an absorbing widen. Its discriminating red is `rename_destination_enrolls_every_test_decl_not_only_the_diff_touched_one`, which feeds the specimen s verbatim diff and FAILS when the rule is disabled, paired with `in_place_modify_enrolls_only_the_touched_test_decl`, which passes under both states so the first cannot be satisfied by widening. **Recognition rule, and it predicts well beyond git**: whenever a set is read out of a tool s output, ask whether that tool promised completeness over YOUR question or over its own — `--stat`, any de-duplicating or truncating reporter, and any porcelain that elides for human brevity all report faithfully and none of them promise the consumer s set. **Its near twin is `executed_conjunct_discriminates_nothing`, approached from the other side**: there the satisfied signal is EXECUTION -- the conjunct genuinely runs and is genuinely gated on -- while here it is COMPLETENESS RELATIVE TO THE REPORTER, which is why this class is not a reachability problem and looking for an unexecuted path finds everything healthy. In both, the axis the diligent reader checks already reads green, and checking it harder returns the same green.) - **a conjunct that executes, is reached, is gated on, and still discriminates nothing** (a predicate wired into a live check runs on every call, sits inside a gate that really blocks, and yet deletes cleanly: remove it and every witness stays green, because no fixture ever constructs the population that would make it false. **It is invisible precisely BECAUSE reachability is satisfied.** The usual signature of an unenforced mechanism -- nothing calls it -- is absent, so every reachability-based tell reports health, and review reads the conjunct as a wall because it genuinely is evaluated. Executed and enforcing are different properties, and no amount of coverage over the calling path distinguishes them. **It is bounded against three neighbours and none of their recognition rules will find it.** `reachability_read_as_occupancy` asks whether to DELETE a guard that is quiet, and warns that mechanism-yes / join-yes / occupancy-zero is a healthy guard; this class is not about deletion and not about occupancy -- the conjunct here should stay, and the defect is that it was believed to be doing work it was not. Section 6's inert-lens tell is machinery that nothing GATES on; this is gated on. Section 4b's decoration case is a check whose RED cannot be authored ANYWHERE; this RED can be authored -- it simply never was, which is the whole difference, and it is the difference between a permanent ceiling and one missing fixture. **THE REMEDY HAS TWO HALVES AND THE SECOND IS WHAT MAKES THE FIRST SOUND.** First: for each wall, run the DELETE arm and confirm EXACTLY ONE witness reddens -- not that a witness reddens. Second: the subject that isolates the conjunct must be chosen with care, because a red for the wrong reason is how this class survives its own test. **SPECIMEN, with both halves executed** (gunbc#9838, `gunbc.self_host_compile_phase_frontier`): `census_population_is_duplicate_free` was added as a conjunct of `receipt_population_coherent`, evaluated on every coherence check and reachable from the live path; deleting it left the whole witness module green. The fixture that isolates it is a receipt whose `refused_files` REPEATS A PATH -- enrolled as `a_receipt_whose_refused_population_repeats_a_path_is_refused_by_coherence` in `test.claim.self_host_compile_phase_frontier_witness`, with `the_same_receipt_without_the_repeat_is_coherent` as its positive control. The subject that looks equally good and is not: a repeated ERROR DIAGNOSTIC IDENTITY also reddens the module, but it breaks a count invariant one line over rather than this conjunct, so it would have scored the DEAD conjunct as LIVE and closed the investigation with the wall still inert. **RUNG: mechanically preventable, and the honest ceiling is the same rung rather than higher.** The unenforcing conjunct stays writable; what exists is an executed falsifier per wall, not a construction that forbids authoring one. The next-rung trigger is a CAPABILITY and not an artifact: a mechanism that derives, from the check itself, the population its removal would change -- so that a conjunct with an empty such population fails to compile rather than waiting for an author to think of the delete arm. Enumerating delete arms by hand does not retire it; it only executes this row's recognition rule once per wall. **Its near twin is `instrument_output_read_as_subject_content`, and they meet from opposite sides**: there a report is complete for the REPORTER and read as complete for the SUBJECT; here a conjunct is genuinely executed and read as genuinely discriminating. Both are the shape where the health signal the reader actually checks -- completeness there, execution here -- is fully satisfied while the property they believe it establishes does not hold. **Neither is found by checking harder along the axis that already reads green.**) - **unbacked execution claim** (prose asserts a live executing relation — this runs on X, X reads these fields, Y already resolves Z — that no authority backs, and a reader forms the premise and plans against it. §4b's CI rung drop names the harm PREMISE CONTAMINATION in its own words; this row is that harm as a general class, since the drop is about one paragraph in the canonical authority and the class is about every carrier. **THE ORIGINS ARE A FIELD OF THIS ONE ROW RATHER THAN SEPARATE ROWS, because the recognition rule is identical across them** — resolve the claim against the authority that owns the relation — **but the REMEDY BRANCHES ON THE ORIGIN, and a remedy applied without reading the origin manufactures new false statements.** Three origins are ATTESTED here and a fourth is conceivable but was NOT found. ORIGIN 1, EXECUTOR DELETED: the claim was true and its executor was removed. Specimen: `gunbc.floor_component_receipt_document` recited that an alert downloads an artifact named `floor-component-receipt` from a run id it is given and reads four named fields; that alert was `falsifier-alert.yml`, deleted 2026-08-15, and a lane read the module, believed the transport live, planned against it, and was corrected by its reviewing authority — the cost is measured, not hypothetical. Second specimen, same origin: `gunbc.ci_failure_class` stated its dissolution trigger as calling `classify_failure_reason` *the way claim_executor already resolves* `gunbc.floor_component_receipt`; `write_floor_component_receipt_at` did exactly that from 2026-07-30 (gunbc#7467) until gunbc#9228 deleted it on 2026-08-25. ORIGIN 2, THE CITED SYMBOL DIED WHILE ITS CONTENT SURVIVED AS AN ANNOTATION, and this one has a corpus-wide producer: `v1_compiler.cli_run` twice cited `floor_component_resource_checkpoint_note` and `floor_component_phase_journal_scaffold_note`, which were real `data …: String` declarations in `gunbc.floor_component_receipt` until the 2026-08-30 prose-bankruptcy sweep (gunbc#9752) converted module-scope commentary rows into §4c annotations corpus-wide. §4c makes an annotation uncitable by construction — `v1_compiler.cli_run` `annotation_erased_scan_text` feeds the semantic passes, so annotation text is not present in the tree the citation harvester walks. **THE CONSEQUENCE IS THE OPPOSITE OF THE ONE THIS ROW FIRST DREW, AND THE CORRECTION IS THE INSTRUCTIVE PART.** The first revision reasoned that the sweep therefore broke every citation of a converted row, making the population the sweep's own diff. Measured by a separate lane (gunbc XL-0-CITE, 2026-09-01) against `v1_compiler.declaration_index`, whose only citation producers are `citation_from_record_literal` and `citation_from_constructor_call`: the typed-citation breakage is **ZERO**, computed both today and as of the sweep commit, on a control that discriminates in both directions. A plain name in annotation prose was never a citation, so the sweep could not break one. What that leaves is a REAL class with an honest ceiling rather than a defect population: these citations bind READERS and no mechanism, so they sit in §4b's *outside the modeled guarantee* column — observed and repaired by reading, never gated — and calling them a dangling-citation population would have been rung inflation about a check that by construction cannot see them. The two `cli_run` sites are genuine instances of the harm and there is no mechanical census that would have found them. ORIGIN 3, WRONG AUTHORITY FOR SOMETHING THAT STILL EXECUTES: `dag/test/claim/instrument_sandbox_witness_test` says its live half `runs on the falsifier lane`; that half sits on `FalsifierSubstrateLongLane`, which `std.witness_admission` `witness_cadence_has_scheduled_route` reports routeless — but it also carries a local recipe, and `OfflineLocalRecipe` HAS a route, so the relation is live and only the named authority is wrong. THE FOURTH, NEVER BACKED AT ALL, IS NOT ATTESTED IN THIS CORPUS and is recorded as unmeasured rather than as a population. **THIS ROW IS NOT THE AUTHORITY ON ANY POPULATION, and saying so is the point:** `gunbc.deleted_cadence_reference_census` already owns the falsifier-cadence instance of origin 1 — twelve sites, a `DeletedCadenceReferenceStanding` vocabulary whose `PremiseInvalidated` arm is exactly this class, and one `GuaranteeRungDrop` filed once for the shared executor rather than per site. A pattern row that restated its population would be a second authority for a fact that already has one, which is the §3 violation this row's own remedy is meant to prevent; so the census is cited here and nothing about its contents is copied. What belongs HERE is only the recognition rule and the remedy arms, which generalize past the falsifier. **THE MECHANICAL TEST for finding one is sharper than asking whether the verb is runs or is enrolled: resolve the claim against the authority that owns the relation, and ask whether the prose implies a live route for something that authority says has none.** The naive runs/enrolled discriminator loses one arm and it is the common one — `Enrolled on falsifier_rehomed_bin_wet_entries (nightly batch 5)` is nominally an enrollment claim, which is TRUE, but `nightly` asserts a cadence that executes and `FalsifierRehomedBinWet` is routeless, so the reader forms the false premise anyway. Three outcomes per line: asserts execution (contaminating), asserts enrollment only (true, leave it), asserts enrollment while implying a live cadence (contaminating). **THE REMEDY HAS THREE ARMS, KEYED TO WHAT THE AUTHORITY SAYS NOW, and picking the wrong one is itself an instance of this class.** Origin 1 → PAST TENSE WITH THE DATE, keeping the reasoning, because the reasoning usually outlives its executor and a reader who finds a dangling claim silently deleted learns nothing. Origin 2 → name the module and the FACT rather than the dead symbol, since §4c left no symbol to cite; past-tensing here would wrongly report the content as gone when only its citability is. Origin 3 → CORRECT IT to the authority that does back it, NOT past tense: past-tensing a live relation records it as dead and is a new contamination in the opposite direction, produced by the remedy itself. Origin 4 → say it was never backed, or delete the assertion while keeping any surviving reasoning; NEVER past tense, because there is no past to record, and `X USED TO resolve Y` asserts an execution that also never happened. **The default against deletion is a default and not an absolute: a sentence whose entire content is a false execution claim has no reasoning to preserve.** **THE SELF-RECEIPT, and it is the most useful thing in this row (2026-09-01):** the first revision of this entry asserted the `ci_failure_class` clause and the two `cli_run` notes as never-true, on the strength of a grep showing they resolve nowhere TODAY. **Both were true when written**, and the row about unbacked claims therefore contained two of them. It was caught by a reviewing authority asking for verification rather than inheritance, not by any check. **RECOGNITION RULE EARNED: `resolves nowhere now` is not `never resolved`, the distinction is decided by history and not by the working tree, and the instrument is `git log --all -S` over the DECLARATION FORM** — which also separates origin 1 from origin 2 by showing WHAT deleted the referent. The neighbouring rule is `positional_citation`'s: a citation left naming a deleted symbol fakes a grep hit, so every arm above records the change rather than quietly removing the name.) +- **instrument re-enters its own build tool** (a check whose SUBJECT is the build -- does this emitted crate compile, does this generated tree link, does this artifact still build under the real toolchain -- is hosted inside a process that the same build tool launched, so the check spawns the tool that is already holding the lock on the directory it needs. It does not fail; it BLOCKS, which is the whole reason the class earns a row: a refusal names itself in one line, and a lock wait is indistinguishable from a slow legitimate build for as long as anyone is willing to watch. Measured here rather than reasoned about (gunbc, 2026-09-01, node adhoc-da8c65c0-b3b): a `#[cfg(test)]` consumer of a new emit-then-rustc route, run under `cargo test --release -p v1-compiler --lib`, spawning `cargo build` with `CARGO_TARGET_DIR` pointing at the same workspace `target/` the outer `cargo test` holds -- thirty minutes of remote runner time producing no output at all, terminated rather than concluded, which is why the cost is recorded: the failure mode's signature is that it charges you the timeout to learn anything. **RECOGNITION RULE, and it is available BEFORE the run rather than after: name the process that will launch your check, name the directory your check's subprocess will lock, and refuse the pairing if they are the same tool over the same directory.** The two escapes differ in kind and only one is usually right. Giving the inner invocation its OWN target directory removes the contention and pays a full cold dependency rebuild for every run, which for a check hosted in a unit-test suite is a cost the suite cannot carry. Hosting the check in a BINARY the build tool has already finished producing -- built, then executed as its own process -- keeps the warm shared directory and is what the existing required emit-compile phase already does; the new route simply had to be homed the same way. **THE GENERALIZATION IS THE POINT AND IT IS NOT ABOUT CARGO:** any instrument whose subject is a build reaches for the build tool, and the most convenient host for an instrument is the test harness that same tool runs, so the pairing is what a reasonable author reaches for first. It recurs wherever the two facts are true together -- a generated-artifact check under `npm test` shelling out to `npm run build`, a bazel-hosted test invoking bazel, a make-driven check calling make -- and the guard is the same: **an instrument may not be hosted by its own subject's actuator.** Neighbouring but distinct from `absorbing_fallback`: nothing widens and nothing is fabricated here, the check is simply never able to answer, so the harm is an unanswerable instrument rather than a wrong answer -- and an instrument that hangs will, under any timeout that reports leniently, eventually be read as one that passed.) - **mitigation injected where the judgment declined** (a downstream stage injects a RUNTIME mitigation at exactly the seam where an upstream judgment returned Undecidable, so a 4b rung-1 fallback comes to occupy the place the wall belongs and the declined seam stops looking empty. **THE LOAD-BEARING HALF IS WHAT THE COINCIDENCE PROVES ABOUT THE DECLINE**: an Undecidable verdict is normally read as benign conservatism — the facts did not settle it, nothing is claimed, no harm asserted — and a mitigation landing on the SAME LINE is the first evidence that a real defect was sitting under that silence. The decline is therefore load-bearing AT THIS SITE, which refutes reading an Undecidable verdict as evidence of ABSENCE. **IT DOES NOT MAKE THE DECLINE CENSUS PREDICTIVE, and this row carries that limit because the author first wrote the stronger claim and then measured it FALSE** on the same board that produced the specimen: 259 of 272 declines sit in modules with ZERO blocking errors, only 6 of 28 decline-bearing modules carry any error at all, and the module holding 42 of the 63 errors carries ZERO declines and 105 ACCEPTS. Declines and defects are ANTI-CORRELATED at module grain, so this specimen is ONE co-location out of 272 and a walk of the decline census would be walking sites that are ~95% empty. The class is about a mitigation landing on a declined seam; it is NOT a claim that declines predict defects. Distinct from `absorbing_fallback`, whose arm WIDENS to a superset and destroys the precise mechanism s signal; here the arm NARROWS to a panic and destroys nothing except the evidence that a judgment was owed. Distinct from `reflection_evidence_structural_proof`, which mistakes an observation for a proof; here nothing is proven or observed — a decision was declined and then papered. Specimen with a receipt, measured 2026-09-01 on gunbc 0e8c49d and unrepaired at authoring: `v2.std.diagnostic outcome_accepted` is `fn outcome_accepted(value: T) -> Outcome` with T FREE; `v2.compiler.07_target_carriers target_fidelity_quotient_optional` declares `-> Outcome>` and calls `outcome_accepted(Present { value: child })`, so T instantiates to `Optional` and THE SOURCE IS CORRECT. The Rust emitter nonetheless rendered `Some(child).expect("fail-closed: an optional value flowed into non-optional parameter 0 of outcome_accepted (empty Optional at runtime)")`. FOUR DEFECTS IN ONE LINE: it fires on a GENERIC formal where `Optional` is a legitimate instantiation rather than a cardinality escape, so the check does not know T is free; it substitutes a runtime panic for a compile-time judgment; the emitted code does NOT typecheck anyway (rustc E0308 expected `Option>` found `Rc`), so the mitigation buys nothing while standing where the wall belongs; and its own panic string calls itself `fail-closed`, which is 4b rung inflation inside a string literal — a diagnostic naming the discipline it violates is worse than a silent one because it will be cited as evidence the discipline holds. The coincidence that makes this a class and not a bug: `v1.compiler.infer declared_type_inhabitance` returned InhabitanceUndecidable{UndecidableOptionalCarrier} at that module s ONLY optional-carrier decline, 07_target_carriers.dag:128:24 parameter `value` — the same line the emitter mitigated. RUNG FOUND AT: 1 (mitigatable) on a seam whose CEILING is 3 (structurally guaranteed), since generic-formal instantiation is decidable from modeled structure. CONSEQUENCE WITH ITS OWN TRIGGER, deliberately NOT folded into this row: wiring the optional-carrier judgment is argued by this specimen but is a separate change, and its trigger is a producer at that seam that can distinguish a FREE type variable from a declared non-optional formal — until that exists the judgment would decline for the same reason it declines today.) ## Declared rung drops (§4b(3)) diff --git a/fixtures/fixture_closure_rustc/green_probe.dag b/fixtures/fixture_closure_rustc/green_probe.dag new file mode 100644 index 00000000000..6ba9ba7a4c9 --- /dev/null +++ b/fixtures/fixture_closure_rustc/green_probe.dag @@ -0,0 +1,8 @@ +module fixture.closure.rustc_green_probe + +// The positive control for the fixture-callable emitted-crate compilation route +// (v1_compiler.emitted_closure_compile_host fixture_closure_rustc_verdict). No imports, so its +// emitted closure is its own module plus the runtime -- the smallest subject that still proves +// the crate writer, the manifest and the emitter reached a crate rustc accepts. Without it the +// red arm beside it could pass for a reason that has nothing to do with its fixture. +data rustc_green_probe_value: Int = 424242 diff --git a/fixtures/fixture_closure_rustc/text_nonliteral_probe.dag b/fixtures/fixture_closure_rustc/text_nonliteral_probe.dag new file mode 100644 index 00000000000..7c5a80730e8 --- /dev/null +++ b/fixtures/fixture_closure_rustc/text_nonliteral_probe.dag @@ -0,0 +1,11 @@ +module fixture.closure.rustc_text_nonliteral_probe + +import v2.std.text { string_is_empty } + +// The MEANING-LEVEL red arm, and the fixture gunbc.rung_drop text_boundary_identity_wall names. +// `concat` answers the kernel string; the return boundary declares the structural +// v2.std.text.String; no homomorphism row stands between them, and the corpus-wide compatibility +// relation admits the pairing by leaf-name spelling. A substring oracle over the emitted text +// cannot see this -- the emitted bytes contain every substring a spelling assertion would look +// for -- which is why the subject needs rustc rather than a text check. +fn rustc_text_nonliteral_probe() -> v2.std.text.String { concat("x", "y") } diff --git a/src/v1/stage0/src/bin/claim_executor.rs b/src/v1/stage0/src/bin/claim_executor.rs index 3b0843cc13c..56433294db6 100644 --- a/src/v1/stage0/src/bin/claim_executor.rs +++ b/src/v1/stage0/src/bin/claim_executor.rs @@ -158,6 +158,9 @@ fn run() -> Result { let mut required_ci_lane: Option = None; let mut required_v2_emission_mode = false; let mut required_emit_compile_mode = false; + let mut fixture_closure_compile_mode = false; + let mut fixture_closure_pair_mode = false; + let mut fixture_closure_paths: Vec = Vec::new(); let mut required_regen_mode = false; let mut emit_partition_crates_mode = false; let mut emit_partition_crates_write = false; @@ -211,6 +214,41 @@ fn run() -> Result { // THE PHASE AS ITS OWN ENTRY POINT, for the reason `--required-v2-emission` is one: // running this alone is a real local action, and it runs the SAME producer the // required phase runs, so a green here and a green there cannot be two facts. + // THE FIXTURE-CALLABLE ROUTE AS ITS OWN ENTRY POINT. + // + // WHO THIS IS FOR, because it is not the required phase's caller. A lane holding a + // two-arm emitter discriminator -- one accepted `.dag` source, two emitter + // behaviours, the arms separated only by what rustc says about the EMITTED crate -- + // has nowhere to run it: the text oracles stop at emitted bytes, and the phase above + // takes only the entries its roster names. This mode takes `--fixture ` as many + // times as there are arms and reports EACH ARM'S DIAGNOSTIC. + // + // IT REPORTS; IT DOES NOT ADJUDICATE, and the distinction is the whole contract. A + // red arm is the EXPECTED result for half of a discriminator, so a red may not be + // this mode's failure -- the caller compares the arms, and a mode that exited + // nonzero on a red would make the discriminator's own passing state look like a + // broken run. What DOES stop the line is the route failing to reach a verdict at + // all (`CrateNotWritten`): then no arm was measured and there is nothing to compare. + "--fixture-closure-compile" => { + fixture_closure_compile_mode = true; + } + // THE ROUTE'S OWN ENROLLED DISCRIMINATION, RUNNABLE ON ITS OWN. + // + // It is also a conjunct of `--required-emit-compile`, which is where it gates. It has + // its own flag because that mode walks the whole entry roster first, and evidence + // nobody can afford to run is evidence that stops being run. This form compiles two + // closures and nothing else. + // + // IT IS AN EXPLICIT FLAG AND NOT THE NO-ARGUMENT DEFAULT OF THE MODE ABOVE. A default + // that silently swaps in a different subject when the caller passed no fixture is how + // a run reports on arms nobody asked for. + "--fixture-closure-pair" => { + fixture_closure_pair_mode = true; + } + "--fixture" => { + i += 1; + fixture_closure_paths.push(require_path_value(&args, i, "--fixture")?); + } "--required-emit-compile" => { required_emit_compile_mode = true; } @@ -326,6 +364,81 @@ fn run() -> Result { } } + // ORDERED AHEAD OF THE SOURCE-ROOT REQUIREMENT, for the reason `--emit-partition-crates` + // is: this mode takes NO roots. The route resolves a fixture's imports against the live tree + // through the witness roots the emit host already uses, so the generic guard below would + // refuse the only invocation this mode has. + if fixture_closure_pair_mode { + let probe_root = v1_compiler::cli_run::local_emit_compile_probe_root(); + let pair = v1_compiler::cli_run::run_fixture_closure_discrimination(&probe_root); + for line in v1_compiler::cli_run::fixture_discrimination_report(&pair) { + eprintln!("{line}"); + } + // HERE THE PAIR IS THE SUBJECT, so a red arm that failed to be red IS a failure -- the + // opposite contract from `--fixture-closure-compile`, where a red is one arm of someone + // else's discriminator and this binary is not the one comparing them. + return if v1_compiler::cli_run::fixture_discrimination_passed(&pair) { + Ok(ExitCode::SUCCESS) + } else { + Err(ExitCode::from(1)) + }; + } + + if fixture_closure_compile_mode { + // AN EMPTY FIXTURE LIST REFUSES. Zero arms measured is the mode reaching no subject, not + // zero failures, and reporting it clean is the empty-observation narrow. + if fixture_closure_paths.is_empty() { + eprintln!( + "fixture-closure-compile: no --fixture given; the mode has no subject and \ + refuses rather than reporting a clean run over nothing" + ); + return Err(ExitCode::from(2)); + } + let probe_root = v1_compiler::cli_run::local_emit_compile_probe_root(); + let mut unanswered = 0usize; + for path in &fixture_closure_paths { + let source = match std::fs::read_to_string(path) { + Ok(text) => text, + Err(e) => { + eprintln!("fixture-closure-compile: {path} unreadable: {e}"); + unanswered += 1; + continue; + } + }; + let outcome = v1_compiler::cli_run::fixture_closure_rustc_verdict(&source, &probe_root); + eprintln!( + "fixture-closure-compile: {path} {}", + v1_compiler::cli_run::fixture_closure_summary(&outcome) + ); + for line in v1_compiler::cli_run::fixture_arm_diagnostic_lines(path, &outcome) { + eprintln!("fixture-closure-compile: {line}"); + } + if let Some(attributed) = + v1_compiler::cli_run::fixture_closure_attributed_line(&outcome) + { + eprintln!("fixture-closure-compile: {path} attributed {attributed}"); + } + if matches!( + outcome, + v1_compiler::cli_run::FixtureClosureOutcome::CrateNotWritten { .. } + ) { + unanswered += 1; + } + } + eprintln!( + "fixture-closure-compile: arms={} unanswered={}", + fixture_closure_paths.len(), + unanswered + ); + // A RED ARM IS NOT THIS MODE'S FAILURE -- it is half of what a discriminator is for. Only + // an arm the route could not answer stops the line. + return if unanswered == 0 { + Ok(ExitCode::SUCCESS) + } else { + Err(ExitCode::from(1)) + }; + } + if source_roots.is_empty() { eprintln!("claim_executor: provide at least one --source-root"); return Err(ExitCode::from(2)); @@ -796,6 +909,17 @@ fn run() -> Result { } } } + // THE FIXTURE ROUTE'S DISCRIMINATING PAIR RUNS IN THE SAME MODE AND THE SAME + // PROCESS. It is not a second gate: the entry roster and the fixture pair are two + // subjects of ONE question -- does an emitted closure survive rustc -- and running + // them apart would let one green stand for the other. It runs after the roster so + // the shared target directory is already warm, and its verdict joins this mode's + // exit status rather than being printed beside it. + let pair = v1_compiler::cli_run::run_fixture_closure_discrimination(&probe_root); + for line in v1_compiler::cli_run::fixture_discrimination_report(&pair) { + eprintln!("required-emit-compile: {line}"); + } + let pair_passed = v1_compiler::cli_run::fixture_discrimination_passed(&pair); let (report, retention_error) = v1_compiler::cli_run::emit_compile_report( &outcomes, &roots, @@ -805,7 +929,7 @@ fn run() -> Result { for line in report { eprintln!("{line}"); } - return if not_passed == 0 && retention_error.is_none() { + return if not_passed == 0 && retention_error.is_none() && pair_passed { Ok(ExitCode::SUCCESS) } else { Err(ExitCode::from(1)) diff --git a/src/v1/stage0/src/cli_run.rs b/src/v1/stage0/src/cli_run.rs index 5706e63d1ec..cac406bc20c 100644 --- a/src/v1/stage0/src/cli_run.rs +++ b/src/v1/stage0/src/cli_run.rs @@ -1893,7 +1893,7 @@ fn collect_module_binding_manifest_rows(source_roots: &[String]) -> Vec, @@ -38756,10 +38756,14 @@ pub use emitted_closure_compile_host::{ cargo_verdict_stderr_tail, emit_compile_modules_reached, emit_compile_outcome_passed, emit_compile_outcome_summary, emit_compile_report, emit_compile_selection, emit_compile_selection_not_selected_digest, emit_compile_selection_selected_digest, - emit_compile_selection_universe_digest, local_emit_compile_probe_root, - required_ci_emit_compile_probe_root, required_emit_compile_entries, - retain_not_selected_identities, run_required_emit_compile, CargoVerdict, EmitCompileOutcome, - EmitCompileSelection, MutationVerdict, + emit_compile_selection_universe_digest, fixture_arm_diagnostic_lines, + fixture_closure_attributed_line, fixture_closure_compiled, fixture_closure_rustc_verdict, + fixture_closure_summary, fixture_discrimination_passed, fixture_discrimination_report, + local_emit_compile_probe_root, required_ci_emit_compile_probe_root, + required_emit_compile_entries, retain_not_selected_identities, + run_fixture_closure_discrimination, run_required_emit_compile, CargoVerdict, + EmitCompileOutcome, EmitCompileSelection, FixtureClosureOutcome, FixtureDiscrimination, + MutationVerdict, }; /// The authority's own declared module path, for consumers outside this module. diff --git a/src/v1/stage0/src/emitted_closure_compile_host.rs b/src/v1/stage0/src/emitted_closure_compile_host.rs index f7edff00ed1..4ab7734b146 100644 --- a/src/v1/stage0/src/emitted_closure_compile_host.rs +++ b/src/v1/stage0/src/emitted_closure_compile_host.rs @@ -24,6 +24,16 @@ //! `run_required_emit_compile`, which stops there and reports every later entry as `NotExecuted` //! rather than measuring through a target directory of unknown state. //! +//! TWO ROUTES REACH RUSTC FROM HERE, AND THE SECOND IS WHY THE HEADER ABOVE IS NOT THE WHOLE +//! FILE. The required phase's subject is a rostered `.dag` ENTRY FILE, so nothing a fixture can +//! author is a subject for it. `fixture_closure_rustc_verdict` is the second route: the same +//! in-memory `.dag` source shape a witness already hands `compile_dag_rust_emit_check`, emitted +//! through the same `compile_sources`, written by the same crate writer, handed to the same cargo +//! invocation. It exists because a substring oracle over emitted TEXT cannot see a meaning-level +//! emitter defect, and rustc can. The two routes share `write_probe_crate_files`, `probe_manifest` +//! and `run_cargo` deliberately: a separately authored fixture harness would make a green here and +//! a green there two facts about two crates. +//! //! WHAT THIS PHASE IS NOT. No baseline, no diagnostic count, no ratchet. Cargo's exit status is //! the whole verdict; warnings are not errors here. Pinning a diagnostic population measured on //! the current tree would be the tree-copied oracle DESIGN 5 rejects; an identity-grain debt @@ -105,11 +115,17 @@ pub enum CargoVerdict { DidNotComplete { detail: String }, /// Ran to completion and reported its own exit status. /// - /// `probe_line` is the first diagnostic line naming the injected probe symbol, scanned from - /// the WHOLE stderr, not `stderr_tail`: the tail is the last 20 lines kept for a human - /// reader, and a genuine `E0308` for the injected item can sit above it when other + /// `probe_line` is the first diagnostic line naming the CALLER'S ATTRIBUTION SYMBOL, scanned + /// from the WHOLE stderr, not `stderr_tail`: the tail is the last 20 lines kept for a human + /// reader, and a genuine diagnostic for the attributed item can sit above it when other /// diagnostics follow. Attributing from the tail would fail a run whose fault WAS refused /// because the receipt was short. + /// + /// THE SYMBOL IS THE CALLER'S BECAUSE THE TWO ROUTES ATTRIBUTE DIFFERENT THINGS AND MUST NOT + /// SHARE ONE ANSWER: the required phase attributes its own injected fault + /// (`MUTATION_PROBE_SYMBOL`), while the fixture route attributes a red to the FIXTURE'S OWN + /// emitted module. A red naming neither is a red about something else in the closure, and + /// both callers need to be able to say so. Completed { status: i32, stderr_tail: String, @@ -136,7 +152,7 @@ pub fn cargo_verdict_summary(verdict: &CargoVerdict) -> String { } } -/// The diagnostic line naming the injected probe symbol, if the run produced one. +/// The diagnostic line naming the caller's attribution symbol, if the run produced one. pub fn cargo_verdict_probe_line(verdict: &CargoVerdict) -> Option<&str> { match verdict { CargoVerdict::Completed { probe_line, .. } => probe_line.as_deref(), @@ -544,6 +560,21 @@ fn write_probe_crate( .iter() .find(|emission| emission.target_name == "rust") .ok_or_else(|| "the emission carries no rust target".to_string())?; + write_probe_crate_files(&emission.result.files, probe_root, entry) +} + +/// The crate writer both routes share: emitted files in, a written crate directory out. +/// +/// ONE WRITER, TWO CALLERS, AND THE SHARING IS THE POINT (DESIGN §2). The required phase reaches +/// it through a `CompileRun`'s rust emission; the fixture route reaches it with the files a +/// virtual source's `compile_sources` produced. A second writer beside this one would let the two +/// routes disagree about what "the emitted crate" is -- manifest, stale-tree removal, or the +/// `src/lib.rs` requirement -- so a green on one would stop being evidence about the other. +fn write_probe_crate_files( + files: &im::Vector>, + probe_root: &Path, + entry: &str, +) -> Result<(PathBuf, usize), String> { let dir = probe_crate_dir(probe_root, entry); // A STALE TREE IS NOT A SUBJECT. A previous run's bytes under the same slug would let a module // deleted from the closure keep compiling, so the directory is removed, not written over. @@ -551,7 +582,7 @@ fn write_probe_crate( std::fs::create_dir_all(dir.join("src")) .map_err(|e| format!("creating {}: {e}", dir.display()))?; let mut written = 0usize; - for file in emission.result.files.iter() { + for file in files.iter() { let path = dir.join(&*file.path); if let Some(parent) = path.parent() { std::fs::create_dir_all(parent) @@ -585,7 +616,7 @@ fn write_probe_crate( /// /// Phases within one required run are sequential in one process, so nothing else holds cargo's /// lock on that directory. -fn run_cargo(crate_dir: &Path, workspace: &Path) -> CargoVerdict { +fn run_cargo(crate_dir: &Path, workspace: &Path, attribution_symbol: &str) -> CargoVerdict { let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); let mut command = std::process::Command::new(&cargo); command @@ -608,7 +639,7 @@ fn run_cargo(crate_dir: &Path, workspace: &Path) -> CargoVerdict { let tail: Vec<&str> = stderr.lines().rev().take(20).collect(); let probe_line = stderr .lines() - .find(|line| line.contains(MUTATION_PROBE_SYMBOL)) + .find(|line| line.contains(attribution_symbol)) .map(|line| line.trim().to_string()); CargoVerdict::Completed { status, @@ -671,7 +702,7 @@ fn establish_discriminating_red( }; } - let red = run_cargo(crate_dir, workspace); + let red = run_cargo(crate_dir, workspace, MUTATION_PROBE_SYMBOL); // THE RESTORE RUNS WHATEVER THE FAULTED ARM ANSWERED, or the next run's baseline goes red for // a reason unrelated to the corpus. @@ -765,7 +796,7 @@ fn establish_discriminating_red( }; let attributed = attributed.to_string(); - let restored = run_cargo(crate_dir, workspace); + let restored = run_cargo(crate_dir, workspace, MUTATION_PROBE_SYMBOL); if !cargo_verdict_compiled(&restored) { return MutationVerdict::RestoreFailed { detail: format!( @@ -861,7 +892,7 @@ pub fn run_emit_compile_entry( "emit-compile: {entry} emitted {emitted_files} file(s) into {} — cargo baseline", crate_dir.display() ); - let baseline = run_cargo(&crate_dir, &workspace); + let baseline = run_cargo(&crate_dir, &workspace, MUTATION_PROBE_SYMBOL); eprintln!( "emit-compile: {entry} baseline {} — mutation", cargo_verdict_summary(&baseline) @@ -890,6 +921,310 @@ pub fn run_emit_compile_entry( } } +/// WHAT A FIXTURE CAN ASK RUSTC, AND WHY NOTHING COULD ASK IT BEFORE. +/// +/// `compile_dag_rust_emit_check` compiles an in-memory `.dag` source and lets a fixture assert on +/// the emitted TEXT — substring includes and excludes. That is a SPELLING oracle: it answers +/// "does this byte sequence occur", never "does the emitted program MEAN what the source said". +/// The required `emit-compile` phase does reach rustc, but only over the eight `.dag` ENTRY files +/// its roster names, so a fixture cannot pose a subject to it at all. +/// +/// So the two capabilities sat on opposite sides of a gap: fixture-authorable subjects with no +/// compiler behind them, and a real compiler with no fixture-authorable subjects. This function +/// closes it — the SAME in-memory source shape `compile_dag_rust_emit_check` takes, emitted +/// through the same `compile_sources`, written by the same crate writer the required phase uses, +/// and handed to the same cargo invocation. +/// +/// WHY THAT MATTERS FOR SAFETY RATHER THAN CONVENIENCE. A meaning-level emitter defect —— a host +/// representation emitted where a structural carrier was declared, a dropped type argument, a +/// reference emitted past its resolved binding —— is invisible to a substring oracle whenever the +/// wrong bytes happen to contain the right substring, and it is exactly what rustc's type checker +/// refuses. `gunbc.rung_drop` `text_boundary_identity_wall` already CLAIMS that rung ("on the +/// emitted-Rust self-host path the class is mechanically preventable — ... renders as a Rust type +/// mismatch and the mandatory emitted-crate cargo check refuses it") for a fixture the required +/// phase's roster cannot reach. DESIGN §4b(1) says a rung is established by evidence executing on +/// the acceptance path; this is the route that lets such a claim execute over its own fixture +/// instead of being asserted about one. +/// +/// GUNBC'S OWN REFUSAL IS A DISTINCT ARM AND NEVER A RUSTC VERDICT. If the fixture does not reach +/// emission compile-clean, the answer is `SourceRefused` carrying the count and the first hard +/// diagnostic — not a red. Collapsing the two would let a fixture that gunbc rejected be reported +/// as "rustc refused the emitted bytes", which is a claim about bytes that were never emitted. +#[derive(Debug, Clone)] +pub enum FixtureClosureOutcome { + /// The v1 compiler refused the fixture itself: emission never happened, so rustc has no + /// subject. `first` names one hard diagnostic so the caller can say WHICH refusal. + SourceRefused { + hard_diagnostics: usize, + first: String, + }, + /// Emission completed and the crate could not be written — a filesystem or closure-shape + /// fact, again not a rustc verdict. + CrateNotWritten { cause: String }, + /// The emitted closure was written and cargo answered over it. + Measured { + crate_dir: String, + emitted_files: usize, + cargo: CargoVerdict, + }, +} + +/// True only when cargo COMPLETED with status zero over the written closure. Every other arm — +/// a gunbc refusal, an unwritten crate, a killed toolchain — answers false, because none of them +/// is evidence the emitted program type-checks. +pub fn fixture_closure_compiled(outcome: &FixtureClosureOutcome) -> bool { + match outcome { + FixtureClosureOutcome::Measured { cargo, .. } => cargo_verdict_compiled(cargo), + _ => false, + } +} + +/// The diagnostic line attributing a red to the fixture's OWN emitted module, if there is one. +/// +/// A CALLER MUST BE ABLE TO SEPARATE "RUSTC REFUSED THIS FIXTURE" FROM "THE CLOSURE WAS ALREADY +/// RED". The subject is one module inside a closure of hundreds; a bare non-zero status says the +/// crate did not build and says nothing about which member broke. So the red direction is only +/// claimable when a diagnostic line names the fixture's emitted file, which is what this returns. +pub fn fixture_closure_attributed_line(outcome: &FixtureClosureOutcome) -> Option<&str> { + match outcome { + FixtureClosureOutcome::Measured { cargo, .. } => cargo_verdict_probe_line(cargo), + _ => None, + } +} + +pub fn fixture_closure_summary(outcome: &FixtureClosureOutcome) -> String { + match outcome { + FixtureClosureOutcome::SourceRefused { + hard_diagnostics, + first, + } => format!("SourceRefused hard={hard_diagnostics} first={first}"), + FixtureClosureOutcome::CrateNotWritten { cause } => { + format!("CrateNotWritten cause={cause}") + } + FixtureClosureOutcome::Measured { + crate_dir, + emitted_files, + cargo, + } => format!( + "Measured files={emitted_files} dir={crate_dir} cargo={}", + cargo_verdict_summary(cargo) + ), + } +} + +/// The emitted Rust module basename for a `.dag` module path — `a.b.c` emits `src/a_b_c.rs`. +/// +/// It is the ATTRIBUTION SYMBOL for the fixture route, and it is derived from the fixture's own +/// module line rather than passed in beside it: a caller-supplied symbol could name a module the +/// fixture does not declare, and every red would then be reported as unattributed while the +/// fixture's real diagnostics sat in the same stderr. +fn fixture_rust_module(source: &str) -> Result { + source + .lines() + .find(|line| line.starts_with("module ")) + .map(|line| line.trim_start_matches("module ").trim().replace('.', "_")) + .ok_or_else(|| "the fixture source declares no module line".to_string()) +} + +/// Emit one in-memory `.dag` fixture's closure and hand it to rustc. +/// +/// The fixture's imports are resolved transitively against the live tree by +/// `resolve_virtual_source_with_imports` — the same resolver `compile_dag_rust_emit_check` uses, +/// so a fixture that compiles under one reaches the same closure under the other. +pub fn fixture_closure_rustc_verdict(source: &str, probe_root: &Path) -> FixtureClosureOutcome { + let rust_module = match fixture_rust_module(source) { + Ok(module) => module, + Err(cause) => return FixtureClosureOutcome::CrateNotWritten { cause }, + }; + eprintln!("fixture-closure: {rust_module} emitting"); + let module_index = crate::cli_run::build_module_path_index_from_witness_roots(); + let sources = + crate::cli_run::resolve_virtual_source_with_imports("fixture.dag", source, &module_index); + let result = crate::v1_compiler_compile::compile_sources( + std::rc::Rc::new(sources.into()), + crate::v1_compiler_artifact::RenderTarget::Rust, + ); + let hard: Vec = result + .diagnostics + .iter() + .filter(|d| crate::cli_run::compile_clean_diagnostic_is_hard(d)) + .map(|d| format!("{d:?}")) + .collect(); + if !hard.is_empty() { + return FixtureClosureOutcome::SourceRefused { + hard_diagnostics: hard.len(), + first: hard[0].chars().take(400).collect(), + }; + } + // The crate slug is the fixture's own emitted module, so two fixtures never share a package + // name in the shared target directory — the fingerprint-aliasing fail-open `probe_package_name` + // records for the entry route applies identically here. + let (crate_dir, emitted_files) = + match write_probe_crate_files(&result.files, probe_root, &rust_module) { + Ok(pair) => pair, + Err(cause) => return FixtureClosureOutcome::CrateNotWritten { cause }, + }; + eprintln!( + "fixture-closure: {rust_module} emitted {emitted_files} file(s) into {} — cargo", + crate_dir.display() + ); + let cargo = run_cargo( + &crate_dir, + &process_workspace_root(), + &format!("{rust_module}.rs"), + ); + eprintln!( + "fixture-closure: {rust_module} {}", + cargo_verdict_summary(&cargo) + ); + FixtureClosureOutcome::Measured { + crate_dir: crate_dir.display().to_string(), + emitted_files, + cargo, + } +} + +/// THE FIXTURE ROUTE'S OWN DISCRIMINATING PAIR, EXECUTED RATHER THAN DESCRIBED. +/// +/// A route that reaches rustc proves nothing on its own: a green says only that SOME crate +/// compiled, and DESIGN §4b calls a check that cannot go red worse than absent because it is +/// cited as coverage. So the route ships with the two arms that make its answer information — +/// run together, in one report, because either alone is satisfiable by an instrument that ignores +/// its input. +/// +/// THE POSITIVE CONTROL is a self-contained fixture whose emitted closure must compile. It fails +/// if the crate writer, the manifest or the emitter regresses, and it is what stops the red arm +/// below from passing for an unrelated reason. +/// +/// THE RED IS MEANING-LEVEL, WHICH IS THE WHOLE POINT OF REACHING RUSTC. Its fixture is the one +/// `gunbc.rung_drop` `text_boundary_identity_wall` names: a NON-LITERAL kernel-String value at an +/// exact `v2.std.text.String` boundary, which the corpus-wide compatibility relation admits by +/// leaf-name spelling and which therefore emits a host representation where the structural +/// carrier was declared. A substring oracle over the emitted text cannot see it — the emitted +/// bytes contain every substring a spelling assertion would look for. rustc's type checker does, +/// and that row's temporary rung ("on the emitted-Rust self-host path the class is mechanically +/// preventable — ... renders as a Rust type mismatch and the mandatory emitted-crate cargo check +/// refuses it") is a claim about exactly this, made about a fixture the required phase's entry +/// roster cannot reach. This arm is where that claim executes. +/// +/// ATTRIBUTION IS PART OF THE RED AND NOT A NICETY. The red fixture's closure is hundreds of +/// modules; a bare non-zero status could come from any of them. So the arm passes only when a +/// diagnostic line NAMES the fixture's own emitted module — otherwise the report says +/// `unattributed` and the pair fails, rather than crediting a red the fixture did not cause. +#[derive(Debug, Clone)] +pub struct FixtureDiscrimination { + pub green: FixtureClosureOutcome, + pub red: FixtureClosureOutcome, +} + +/// THE TWO ARMS ARE FIXTURE FILES, NOT STRING CONSTANTS IN THIS FILE, and the difference is +/// §3 rather than taste. The same arms are what a caller passes to `--fixture-closure-compile` +/// by path; carrying a second copy of their source here would be one fixture with two authorities +/// that drift the first time either is edited, and the arm this pair adjudicates would stop being +/// the arm anyone else runs. +/// +/// The positive control has no imports, so its closure is its own emitted module plus the runtime. +const FIXTURE_GREEN_PATH: &str = "fixtures/fixture_closure_rustc/green_probe.dag"; + +/// The meaning-level red: `concat` answers the kernel string, the boundary declares the +/// structural `v2.std.text.String`, and no homomorphism row stands between them. +const FIXTURE_RED_PATH: &str = "fixtures/fixture_closure_rustc/text_nonliteral_probe.dag"; + +/// AN UNREADABLE ARM IS `CrateNotWritten`, NAMING THE PATH -- never an empty source quietly +/// compiled. An empty `.dag` text emits a crate that builds, so substituting one would turn a +/// missing fixture into a GREEN control and a red arm that stopped discriminating. +fn fixture_arm_verdict(rel_path: &str, probe_root: &Path) -> FixtureClosureOutcome { + let path = process_workspace_root().join(rel_path); + match std::fs::read_to_string(&path) { + Ok(source) => fixture_closure_rustc_verdict(&source, probe_root), + Err(e) => FixtureClosureOutcome::CrateNotWritten { + cause: format!("reading the fixture arm {}: {e}", path.display()), + }, + } +} + +pub fn run_fixture_closure_discrimination(probe_root: &Path) -> FixtureDiscrimination { + FixtureDiscrimination { + green: fixture_arm_verdict(FIXTURE_GREEN_PATH, probe_root), + red: fixture_arm_verdict(FIXTURE_RED_PATH, probe_root), + } +} + +/// The pair passes only when BOTH directions hold: the control compiled, and the meaning-level +/// fixture was refused BY RUSTC with a diagnostic naming its own emitted module. +/// +/// A `SourceRefused` red does NOT pass. gunbc refusing the fixture is a different and better +/// outcome — it means the wall climbed — but it is not this pair's subject, and reporting it as +/// a pass here would let the route's evidence green while nothing reached rustc at all. +pub fn fixture_discrimination_passed(pair: &FixtureDiscrimination) -> bool { + fixture_closure_compiled(&pair.green) + && !fixture_closure_compiled(&pair.red) + && matches!(&pair.red, FixtureClosureOutcome::Measured { .. }) + && fixture_closure_attributed_line(&pair.red).is_some() +} + +/// EACH ARM REPORTS ITS OWN DIAGNOSTIC, NOT A VERDICT ABOUT ITSELF. +/// +/// A pass/fail per arm is not enough for the thing this route exists to carry. A two-arm emitter +/// discriminator separates its arms BY WHAT RUSTC SAID -- one arm `E0308 expected Quantity found +/// Time`, the other `E0573 expected type found variant` -- and a route reporting only that both +/// arms were red cannot tell those two apart, so it cannot discriminate the emitter behaviours +/// that produced them. So every arm's diagnostic lines are reported, red or green, and the +/// summary sits beside them rather than replacing them. +pub fn fixture_discrimination_report(pair: &FixtureDiscrimination) -> Vec { + let mut lines = vec![format!( + "fixture-closure: control {}", + fixture_closure_summary(&pair.green) + )]; + lines.extend( + fixture_arm_diagnostic_lines("control", &pair.green) + .into_iter() + .map(|line| format!("fixture-closure: {line}")), + ); + lines.push(format!( + "fixture-closure: red {}", + fixture_closure_summary(&pair.red) + )); + lines.extend( + fixture_arm_diagnostic_lines("red", &pair.red) + .into_iter() + .map(|line| format!("fixture-closure: {line}")), + ); + lines.push(format!( + "fixture-closure: red attribution {}", + fixture_closure_attributed_line(&pair.red).unwrap_or("unattributed") + )); + lines.push(format!( + "fixture-closure: pair {}", + if fixture_discrimination_passed(pair) { + "PASSED" + } else { + "FAILED" + } + )); + lines +} + +/// One arm's diagnostics, whatever arm the outcome took. +/// +/// A gunbc refusal reports the diagnostic gunbc produced; a cargo run reports what cargo said. +/// Neither is silently rendered as the other, because the reader's next move differs: one names +/// a fixture the compiler rejected, the other names emitted bytes rustc rejected. +pub fn fixture_arm_diagnostic_lines(label: &str, outcome: &FixtureClosureOutcome) -> Vec { + match outcome { + FixtureClosureOutcome::SourceRefused { first, .. } => { + vec![format!("{label}| gunbc {first}")] + } + FixtureClosureOutcome::CrateNotWritten { cause } => { + vec![format!("{label}| not-written {cause}")] + } + FixtureClosureOutcome::Measured { cargo, .. } => cargo_verdict_stderr_tail(cargo) + .lines() + .map(|line| format!("{label}| cargo {line}")) + .collect(), + } +} + /// Every configured entry, each run whatever the previous one did -- the stopped-line audit /// shape: report everything, green nothing. /// From 5414dad9869d8f197ff9fea0e3deac9da15387ff Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Tue, 1 Sep 2026 09:56:55 +0000 Subject: [PATCH 2/6] Fail closed when an arm never reached rustc (review 58120) --fixture-closure-compile counted only CrateNotWritten as unanswered, so it exited SUCCESSFULLY on a fixture gunbc refused (SourceRefused) and on a cargo run that never finished (DidNotComplete). Neither carries a rustc verdict, so the mode could report success without reaching rustc -- the fail-open DESIGN section 5 forbids, and a direct contradiction of the contract written three lines above it: "only an arm the route could not answer stops the line". The code enumerated one of the three arms that cannot answer. THE REPAIR IS A PREDICATE BESIDE THE CARRIER, not a widened matches! at the call site: the defect was a caller enumerating a subset of the arms, and a second caller would enumerate a different subset. fixture_closure_reached_rustc is true only for a cargo run that COMPLETED and reported its own exit status. A red is reached -- rustc ran and refused, which is half of what a discriminator is for. SourceRefused, CrateNotWritten, NotAttempted and DidNotComplete are not. fixture_discrimination_passed's red arm now goes through the same predicate. EXECUTED, BOTH DIRECTIONS, in one run: arm gunbc refuses SourceRefused hard=1 ... 'definitely_not_a_declared_callable' REFUSED ARM EXIT=1 (exited 0 before this commit) enrolled pair control status=0, red status=101, E0308 attributed pair PASSED, PAIR EXIT=0 So the refusal is real and was not bought by breaking the route. cargo clippy --all-targets -- -D warnings exits 0. Seed-growth roster re-derived exact at 75 declarations / 75 rows, zero stale, zero unaccounted. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HoAvoacTJ3UVqBhBSfCYZh --- .../emitted_closure_compile_seed_growth.dag | 6 ++-- src/v1/stage0/src/bin/claim_executor.rs | 8 ++--- src/v1/stage0/src/cli_run.rs | 14 ++++----- .../src/emitted_closure_compile_host.rs | 30 ++++++++++++++++++- 4 files changed, 44 insertions(+), 14 deletions(-) diff --git a/dag/gunbc/emitted_closure_compile_seed_growth.dag b/dag/gunbc/emitted_closure_compile_seed_growth.dag index 7f0128f24d0..ba26fe77668 100644 --- a/dag/gunbc/emitted_closure_compile_seed_growth.dag +++ b/dag/gunbc/emitted_closure_compile_seed_growth.dag @@ -156,8 +156,9 @@ import gunbc.seed_growth { SeedGrowthJustification } // the cli_run re-export list are MODIFIED rather than added, and claim_executor gains argv arms // and two mode bodies inside an existing function -- ExistingSeedItemModified in both files, so // listing them would net a modification into an addition census. THE ROSTER IS EXACT AGAINST THE -// FILE AS OF THIS LANE: 74 declarations counted at every indent by the rule stated above, 74 -// rows, zero stale, zero unaccounted. Re-derive it rather than trusting it; the paragraph above +// FILE AS OF THIS LANE: 75 declarations counted at every indent by the rule stated above, 75 +// rows, zero stale, zero unaccounted (74 at first push; fixture_closure_reached_rustc is the +// fail-closed repair review 58120 required). Re-derive it rather than trusting it; the paragraph above // records what happens when nobody does. // HAND-RUST RECEIPT FOR THE REQUIRED-PATH ROOT CORRECTION. Additions: PROBE_ROOT_DIR_NAME, @@ -211,6 +212,7 @@ data emitted_closure_compile_seed_growth_justification: SeedGrowthJustification DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "write_probe_crate_files", field: WholeDeclaration }, DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "FixtureClosureOutcome", field: WholeDeclaration }, DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "fixture_closure_compiled", field: WholeDeclaration }, + DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "fixture_closure_reached_rustc", field: WholeDeclaration }, DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "fixture_closure_attributed_line", field: WholeDeclaration }, DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "fixture_closure_summary", field: WholeDeclaration }, DeclarationRef { module_path: "v1_compiler.emitted_closure_compile_host", decl_name: "fixture_rust_module", field: WholeDeclaration }, diff --git a/src/v1/stage0/src/bin/claim_executor.rs b/src/v1/stage0/src/bin/claim_executor.rs index 56433294db6..266f398c8a1 100644 --- a/src/v1/stage0/src/bin/claim_executor.rs +++ b/src/v1/stage0/src/bin/claim_executor.rs @@ -418,10 +418,10 @@ fn run() -> Result { { eprintln!("fixture-closure-compile: {path} attributed {attributed}"); } - if matches!( - outcome, - v1_compiler::cli_run::FixtureClosureOutcome::CrateNotWritten { .. } - ) { + // AN ARM IS ANSWERED ONLY IF RUSTC REACHED A VERDICT ON IT. A red is answered; a + // fixture gunbc refused, an unwritten crate and a cargo run that never completed are + // not, and this mode must not exit successfully over any of them (review 58120). + if !v1_compiler::cli_run::fixture_closure_reached_rustc(&outcome) { unanswered += 1; } } diff --git a/src/v1/stage0/src/cli_run.rs b/src/v1/stage0/src/cli_run.rs index cac406bc20c..59c55811bd0 100644 --- a/src/v1/stage0/src/cli_run.rs +++ b/src/v1/stage0/src/cli_run.rs @@ -38757,13 +38757,13 @@ pub use emitted_closure_compile_host::{ emit_compile_outcome_summary, emit_compile_report, emit_compile_selection, emit_compile_selection_not_selected_digest, emit_compile_selection_selected_digest, emit_compile_selection_universe_digest, fixture_arm_diagnostic_lines, - fixture_closure_attributed_line, fixture_closure_compiled, fixture_closure_rustc_verdict, - fixture_closure_summary, fixture_discrimination_passed, fixture_discrimination_report, - local_emit_compile_probe_root, required_ci_emit_compile_probe_root, - required_emit_compile_entries, retain_not_selected_identities, - run_fixture_closure_discrimination, run_required_emit_compile, CargoVerdict, - EmitCompileOutcome, EmitCompileSelection, FixtureClosureOutcome, FixtureDiscrimination, - MutationVerdict, + fixture_closure_attributed_line, fixture_closure_compiled, fixture_closure_reached_rustc, + fixture_closure_rustc_verdict, fixture_closure_summary, fixture_discrimination_passed, + fixture_discrimination_report, local_emit_compile_probe_root, + required_ci_emit_compile_probe_root, required_emit_compile_entries, + retain_not_selected_identities, run_fixture_closure_discrimination, run_required_emit_compile, + CargoVerdict, EmitCompileOutcome, EmitCompileSelection, FixtureClosureOutcome, + FixtureDiscrimination, MutationVerdict, }; /// The authority's own declared module path, for consumers outside this module. diff --git a/src/v1/stage0/src/emitted_closure_compile_host.rs b/src/v1/stage0/src/emitted_closure_compile_host.rs index 4ab7734b146..1040e26ce06 100644 --- a/src/v1/stage0/src/emitted_closure_compile_host.rs +++ b/src/v1/stage0/src/emitted_closure_compile_host.rs @@ -979,6 +979,34 @@ pub fn fixture_closure_compiled(outcome: &FixtureClosureOutcome) -> bool { } } +/// DID THIS ARM REACH A RUSTC VERDICT AT ALL -- which is a different question from whether it +/// compiled, and the one a caller must ask before reading a red as evidence. +/// +/// TRUE only for a cargo run that COMPLETED and reported its own exit status. A red (`status` +/// non-zero) is reached: rustc ran and refused, which is half of what a discriminator is for. +/// Everything else answered nothing about the emitted bytes and must not be counted as if it had: +/// +/// - `SourceRefused` -- gunbc refused the fixture, so nothing was emitted and rustc had no subject; +/// - `CrateNotWritten` -- the closure never reached disk; +/// - `NotAttempted` -- the toolchain was never invoked; +/// - `DidNotComplete` -- cargo was killed or failed to spawn, reporting no status of its own. +/// +/// THIS EXISTS BECAUSE THE CALL SITE GOT IT WRONG (review 58120). `--fixture-closure-compile` +/// counted only `CrateNotWritten` as unanswered and therefore exited SUCCESSFULLY on a fixture +/// gunbc rejected and on a cargo run that never finished -- a mode reporting success without +/// reaching rustc, which is the fail-open §5 forbids and which contradicted this module's own +/// outcome separation. A predicate beside the carrier, rather than a `matches!` at each caller, +/// is what stops the next caller from enumerating a different subset of the same arms. +pub fn fixture_closure_reached_rustc(outcome: &FixtureClosureOutcome) -> bool { + match outcome { + FixtureClosureOutcome::Measured { cargo, .. } => { + matches!(cargo, CargoVerdict::Completed { .. }) + } + FixtureClosureOutcome::SourceRefused { .. } + | FixtureClosureOutcome::CrateNotWritten { .. } => false, + } +} + /// The diagnostic line attributing a red to the fixture's OWN emitted module, if there is one. /// /// A CALLER MUST BE ABLE TO SEPARATE "RUSTC REFUSED THIS FIXTURE" FROM "THE CLOSURE WAS ALREADY @@ -1158,8 +1186,8 @@ pub fn run_fixture_closure_discrimination(probe_root: &Path) -> FixtureDiscrimin /// a pass here would let the route's evidence green while nothing reached rustc at all. pub fn fixture_discrimination_passed(pair: &FixtureDiscrimination) -> bool { fixture_closure_compiled(&pair.green) + && fixture_closure_reached_rustc(&pair.red) && !fixture_closure_compiled(&pair.red) - && matches!(&pair.red, FixtureClosureOutcome::Measured { .. }) && fixture_closure_attributed_line(&pair.red).is_some() } From a11237c5f1900e036e0f4aaf52975941c50ee4cb Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Tue, 1 Sep 2026 10:55:39 +0000 Subject: [PATCH 3/6] Rework onto test-facing visibility; retract a falsified deadlock claim and the ledger row built on it TWO THINGS: a retraction, and the rework the approval's conditions required. THE RETRACTION. This branch asserted that a cargo-test-hosted consumer DEADLOCKS -- that a test binary launched by cargo, spawning cargo at the same target directory, blocks on the outer lock. THAT IS FALSE AND WAS NEVER MEASURED. It was inferred from a 30-minute run that produced no output and was terminated. Measured properly, from inside a cargo-launched test: elapsed=215.076672126s status=Some(0) blocking_line=None It COMPLETED. Cargo never emitted a Blocking line because it never waited for one. The original silence was an ordinary rebuild: an inner `cargo build --release` from an outer `cargo test --release` does not share fingerprints, so it recompiles part of the dependency graph before reaching the emitted closure. Two long builds in series, working throughout. The run was piped through `tail`, which destroyed the interim visibility that would have shown it. WHAT THAT FALSIFIES, AND WHAT IS DONE ABOUT EACH: - gunbc.recurring_failure_mode instrument_reenters_its_own_build_tool -- a class row filed and pushed on this specimen, claiming 'it does not fail; it BLOCKS'. DELETED, not amended: per unbacked_execution_claim's own remedy arms, an assertion that was NEVER BACKED is deleted rather than past-tensed, because there is no past to record. A recognition rule firing on an unreproducible mechanism routes every future reader to the wrong repair, in a ledger whose entire purpose is rules that hold. Both projections drop it. - The seed-growth paragraph asserting it: replaced by the falsification IN THE PAST TENSE with the numbers, so the next author cannot re-infer it from a deleted sentence. THE REWORK, to the approval's bounded conditions: - Both CLI flags (--fixture-closure-compile, --fixture-closure-pair) and the --required-emit-compile conjunct are REMOVED. No new CLI surface, no phase variant. - Every route item is #[cfg(test)] pub(crate) behind its own `use`, kept OUT of the production re-export: a production re-export would put these on the emitted seed's exported surface, which is PublicSurfaceGrowth against a seed frozen for growth. - The discriminator is authored in v1.compiler.compiler_tests_rust and emitted into compiler_tests.rs through the seed's own generator -- not hand-written into the projection. required-regen reaches its fixed point (first_generation_equal=true). - The carrier's known-FALSE claim that nothing under #[cfg(test)] executes on the merge path is corrected: rust-unit-tests runs repo_self_test_command on every push and PR. THE HONEST GRAIN OF THE EVIDENCE, stated identically in carrier, PR and rung claim: the discriminator is ENROLLED AND AVAILABLE via `-- --ignored`; it DOES NOT EXECUTE BY DEFAULT (it spawns cargo and compiles two emitted crates); rust-unit-tests is not a `needs` of the required aggregate, so even un-ignored a red there would not block through the required context. Candidate evidence, reviewable on demand, NO WALL. An #[ignore] is a cost decision and NOT a rung, and nothing here may be cited as merge-path coverage. It therefore does not discharge any next-rung trigger naming this capability. Seed-growth roster re-derived after the visibility change rather than carried forward: 75 declarations, 75 rows, zero stale, zero unaccounted. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HoAvoacTJ3UVqBhBSfCYZh --- DESIGN.md | 1 - .../emitted_closure_compile_seed_growth.dag | 48 +++++-- dag/gunbc/recurring_failure_mode.dag | 7 - docs/design-ledgers.md | 1 - src/v1/compiler_tests_rust.dag | 44 ++++++ src/v1/stage0/src/bin/claim_executor.rs | 126 +----------------- src/v1/stage0/src/cli_run.rs | 25 +++- src/v1/stage0/src/compiler_tests.rs | 40 ++++++ .../src/emitted_closure_compile_host.rs | 47 +++++-- .../src/v1_compiler_compiler_tests_rust.rs | 6 +- 10 files changed, 180 insertions(+), 165 deletions(-) diff --git a/DESIGN.md b/DESIGN.md index a6924f7fc9e..e0acd8598b1 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -212,7 +212,6 @@ One row per class, each carrying its recognition rule and its receipts, in [docs - `instrument_output_read_as_subject_content` - `executed_conjunct_discriminates_nothing` - `unbacked_execution_claim` -- `instrument_reenters_its_own_build_tool` - `mitigation_injected_where_judgment_declined` ## Building & checks diff --git a/dag/gunbc/emitted_closure_compile_seed_growth.dag b/dag/gunbc/emitted_closure_compile_seed_growth.dag index ba26fe77668..cd7e41ee9cd 100644 --- a/dag/gunbc/emitted_closure_compile_seed_growth.dag +++ b/dag/gunbc/emitted_closure_compile_seed_growth.dag @@ -78,9 +78,30 @@ import gunbc.seed_growth { SeedGrowthJustification } // silently on the next declaration added, and nothing in the required run compares the two. Its // dissolution is the same v1-hand-queue-drain lane this obligation already names. // -// WHAT THE EXECUTED EVIDENCE IS, because the seed's own unit tests are not it. The Rust suite was -// removed from CI on 2026-07-11, so nothing under #[cfg(test)] executes on the merge path and none -// of it may be cited as coverage. The executed evidence is the phase itself: +// WHAT THE EXECUTED EVIDENCE IS, because the seed's own unit tests are not it. THE SENTENCE THAT +// STOOD HERE UNTIL 2026-09-01 SAID THE RUST SUITE WAS REMOVED FROM CI ON 2026-07-11 AND THAT +// NOTHING UNDER #[cfg(test)] EXECUTES ON THE MERGE PATH. THAT IS FALSE AND IS CORRECTED RATHER +// THAN LEFT STANDING: `gunbc.witness_floor_workflow` runs a `rust-unit-tests` job executing +// `repo_self_test_command` (`cargo test --release -p v1-compiler --lib`) on every push and pull +// request. A known-false paragraph does not get to survive because it predates the change that +// noticed it -- it is premise contamination, and a reader planning against it plans against a +// CI shape that has not existed for six weeks. +// +// WHAT IS TRUE, AT THE GRAIN THE DISTINCTION ACTUALLY HAS, because the corrected sentence is +// weaker than it first looks and the weakness is the point. The fixture discriminator +// (`fixture_closure_rustc_discrimination`, authored in `v1.compiler.compiler_tests_rust` and +// emitted into `compiler_tests.rs`) is ENROLLED AND AVAILABLE in `rust-unit-tests` via +// `cargo test --release -p v1-compiler --lib fixture_closure_rustc_discrimination -- --ignored`. +// It DOES NOT EXECUTE BY DEFAULT on push or pull request: it is `#[ignore]`d because it spawns +// cargo and compiles two emitted crates, which is minutes inside a suite that runs on every push. +// And `rust-unit-tests` is not a `needs` of the required aggregate, so even un-ignored a red +// there would be visible and would not block through the required context. THE EVIDENCE IS +// THEREFORE CANDIDATE EVIDENCE -- reviewable on demand, no wall -- and an `#[ignore]` is a COST +// DECISION AND NOT A RUNG. Nothing here may be cited as coverage that executes on the merge path, +// which is what the false sentence above got right for the wrong reason. +// +// The older reason still stands, and it is a different claim from the paragraph above: the +// phase's OWN executed evidence is the phase itself, not any unit test. // establish_discriminating_red injects one type error into one emitted file on EVERY required run, // requires it to fail alone, restores the bytes and requires the green back -- and a mutation that // fails to go red is a PHASE FAILURE, not a note. That is DESIGN 4b's authorable-RED question @@ -139,13 +160,20 @@ import gunbc.seed_growth { SeedGrowthJustification } // rung for the text-boundary class, which stays exactly where its own row puts it -- what changes // is that the row's emitted-Rust half now has an executing probe instead of a sentence. // -// WHY IT IS NOT A cargo test. The route spawns cargo against the workspace target directory, and -// a test binary launched BY cargo would be a nested cargo over the directory the outer one holds. -// It does not fail there, it BLOCKS, which is worse: a lock wait is indistinguishable from a slow -// legitimate build. Measured, at the cost of a thirty-minute remote run terminated rather than -// concluded, and filed as gunbc.recurring_failure_mode instrument_reenters_its_own_build_tool -- -// an instrument may not be hosted by its own subject's actuator. The executing consumer is -// therefore the binary, exactly as the entry roster's is. +// WHERE THE CONSUMER LIVES, AND A FALSIFIED CLAIM RECORDED RATHER THAN QUIETLY DROPPED. An +// earlier revision of this lane asserted that a cargo-test-hosted consumer DEADLOCKS -- that a +// test binary launched by cargo, spawning cargo at the same target directory, blocks on the outer +// lock. THAT IS FALSE AND WAS NEVER MEASURED: it was inferred from a thirty-minute run that +// produced no output and was terminated. Measured properly, from inside a cargo-launched test +// binary: elapsed 215s, exit status 0, and cargo emitted no `Blocking waiting for file lock` +// line at all. The run completed. What the original silence actually was is an ordinary REBUILD -- +// an inner `cargo build --release` from an outer `cargo test --release` does not share +// fingerprints, because a test build resolves features differently, so the inner invocation +// recompiles part of the dependency graph before it reaches the emitted closure. Two long builds +// in series, working the whole time. The claim is recorded here in the past tense, with what +// replaced it, because a mechanism asserted from a silence is exactly what this file's own +// evidence discipline exists to prevent, and deleting the sentence would leave the next author +// free to infer it again. // // HAND-ITEM DELTA FOR THIS LANE: fifteen declarations, every one enumerated in the roster below // rather than counted -- write_probe_crate_files, FixtureClosureOutcome, fixture_closure_compiled, diff --git a/dag/gunbc/recurring_failure_mode.dag b/dag/gunbc/recurring_failure_mode.dag index 5533578394f..1ac1f81384d 100644 --- a/dag/gunbc/recurring_failure_mode.dag +++ b/dag/gunbc/recurring_failure_mode.dag @@ -228,12 +228,6 @@ data unbacked_execution_claim: RecurringFailureMode = RecurringFailureMode { evidence: [], } -data instrument_reenters_its_own_build_tool: RecurringFailureMode = RecurringFailureMode { - identity: "instrument_reenters_its_own_build_tool" as NonEmptyStr, - authored: "**instrument re-enters its own build tool** (a check whose SUBJECT is the build -- does this emitted crate compile, does this generated tree link, does this artifact still build under the real toolchain -- is hosted inside a process that the same build tool launched, so the check spawns the tool that is already holding the lock on the directory it needs. It does not fail; it BLOCKS, which is the whole reason the class earns a row: a refusal names itself in one line, and a lock wait is indistinguishable from a slow legitimate build for as long as anyone is willing to watch. Measured here rather than reasoned about (gunbc, 2026-09-01, node adhoc-da8c65c0-b3b): a `#[cfg(test)]` consumer of a new emit-then-rustc route, run under `cargo test --release -p v1-compiler --lib`, spawning `cargo build` with `CARGO_TARGET_DIR` pointing at the same workspace `target/` the outer `cargo test` holds -- thirty minutes of remote runner time producing no output at all, terminated rather than concluded, which is why the cost is recorded: the failure mode's signature is that it charges you the timeout to learn anything. **RECOGNITION RULE, and it is available BEFORE the run rather than after: name the process that will launch your check, name the directory your check's subprocess will lock, and refuse the pairing if they are the same tool over the same directory.** The two escapes differ in kind and only one is usually right. Giving the inner invocation its OWN target directory removes the contention and pays a full cold dependency rebuild for every run, which for a check hosted in a unit-test suite is a cost the suite cannot carry. Hosting the check in a BINARY the build tool has already finished producing -- built, then executed as its own process -- keeps the warm shared directory and is what the existing required emit-compile phase already does; the new route simply had to be homed the same way. **THE GENERALIZATION IS THE POINT AND IT IS NOT ABOUT CARGO:** any instrument whose subject is a build reaches for the build tool, and the most convenient host for an instrument is the test harness that same tool runs, so the pairing is what a reasonable author reaches for first. It recurs wherever the two facts are true together -- a generated-artifact check under `npm test` shelling out to `npm run build`, a bazel-hosted test invoking bazel, a make-driven check calling make -- and the guard is the same: **an instrument may not be hosted by its own subject's actuator.** Neighbouring but distinct from `absorbing_fallback`: nothing widens and nothing is fabricated here, the check is simply never able to answer, so the harm is an unanswerable instrument rather than a wrong answer -- and an instrument that hangs will, under any timeout that reports leniently, eventually be read as one that passed.)", - evidence: [], -} - data recurring_failure_mode_roster: List = [ hollow_alias, state_space_conflation, @@ -264,6 +258,5 @@ data recurring_failure_mode_roster: List = [ instrument_output_read_as_subject_content, executed_conjunct_discriminates_nothing, unbacked_execution_claim, - instrument_reenters_its_own_build_tool, mitigation_injected_where_judgment_declined, ] diff --git a/docs/design-ledgers.md b/docs/design-ledgers.md index e7611542a65..6abd1cc41ec 100644 --- a/docs/design-ledgers.md +++ b/docs/design-ledgers.md @@ -41,7 +41,6 @@ The landing measurement partitions the 31 parser-visible identities into **2 cit - **instrument output read as subject content** (a consumer derives a SET from a report whose completeness is a property of the REPORTING TOOL rather than of the subject. **The boundary against `execution_provenance_loss` is the whole justification for a separate row, because the two look identical at the call site and have opposite repairs**: there a value is consumed as though an observation produced it while nothing proves the observation path RAN, and the fix is to bind an execution receipt; here the observation ran and reported FAITHFULLY — the instrument answered its own question correctly — and what is wrong is that its output was complete for the REPORTER s purpose and was read as complete for the CONSUMER s. Provenance is intact; the SUBJECT is substituted, so no receipt binding can detect it and the repair is instead to derive the set from the subject itself. Distinct from `total_at_the_level_examined_blind`, which is a judgment shape (a match total over the type it examines, blind inside an arm), and from `reachability_read_as_occupancy`, which asks whether a quiet guard should exist; this is neither a match nor a guard. Specimen with a receipt, repaired on main by gunbc#9829: the required floor asked `extdeps.git` `git.Core.DiffUnified0` (`git diff -U0 `) which lines a change touched, and `floor_diff_edits_from_line_ranges` attributed those lines to enclosing declarations to decide which witness identities the change established. Git prints only the hunks that DIFFER, so on a rename it prints the module line and whatever else moved — while every identity at the destination is a newly qualified `module.function` that has never executed under that spelling, because the authored module name moved with the file. **THE REASON IT SURVIVED IS THAT THE TWO ANSWERS COINCIDE BY ACCIDENT IN THE COMMON CASE**: for a wholly-added file every line is a `+` line, so line attribution reaches every declaration and the defect is invisible; it appears only when the instrument gets TERSE, which rename detection is. Measured over the window reachable in a shallow session clone — a floor on the population, not the population, since older renames are unmeasured — 8 of 8 rename-destination `.dag` files carrying test decls were under-enrolled and at least 87 of their 103 identities were never selected; a PURE MOVE with no content edit enrolled NOTHING. It was not merely a reporting gap: changed-witness membership is what OVERRIDES the cost-debt withhold and the outside-gate suppression, so the same miss on a rostered identity is a silent decline of a witness whose author is present, which is the state `v2.workflow.floor_changed_witness` `changed_witness_execution_standing` exists to red. The repair derives the set from the subject: a path `parse_unified_diff_added_paths` already rules new-at-path contributes the declarations THE FILE ITSELF PARSES TO, bounded to that file s own decl list and never the corpus, so it is the precise answer rather than an absorbing widen. Its discriminating red is `rename_destination_enrolls_every_test_decl_not_only_the_diff_touched_one`, which feeds the specimen s verbatim diff and FAILS when the rule is disabled, paired with `in_place_modify_enrolls_only_the_touched_test_decl`, which passes under both states so the first cannot be satisfied by widening. **Recognition rule, and it predicts well beyond git**: whenever a set is read out of a tool s output, ask whether that tool promised completeness over YOUR question or over its own — `--stat`, any de-duplicating or truncating reporter, and any porcelain that elides for human brevity all report faithfully and none of them promise the consumer s set. **Its near twin is `executed_conjunct_discriminates_nothing`, approached from the other side**: there the satisfied signal is EXECUTION -- the conjunct genuinely runs and is genuinely gated on -- while here it is COMPLETENESS RELATIVE TO THE REPORTER, which is why this class is not a reachability problem and looking for an unexecuted path finds everything healthy. In both, the axis the diligent reader checks already reads green, and checking it harder returns the same green.) - **a conjunct that executes, is reached, is gated on, and still discriminates nothing** (a predicate wired into a live check runs on every call, sits inside a gate that really blocks, and yet deletes cleanly: remove it and every witness stays green, because no fixture ever constructs the population that would make it false. **It is invisible precisely BECAUSE reachability is satisfied.** The usual signature of an unenforced mechanism -- nothing calls it -- is absent, so every reachability-based tell reports health, and review reads the conjunct as a wall because it genuinely is evaluated. Executed and enforcing are different properties, and no amount of coverage over the calling path distinguishes them. **It is bounded against three neighbours and none of their recognition rules will find it.** `reachability_read_as_occupancy` asks whether to DELETE a guard that is quiet, and warns that mechanism-yes / join-yes / occupancy-zero is a healthy guard; this class is not about deletion and not about occupancy -- the conjunct here should stay, and the defect is that it was believed to be doing work it was not. Section 6's inert-lens tell is machinery that nothing GATES on; this is gated on. Section 4b's decoration case is a check whose RED cannot be authored ANYWHERE; this RED can be authored -- it simply never was, which is the whole difference, and it is the difference between a permanent ceiling and one missing fixture. **THE REMEDY HAS TWO HALVES AND THE SECOND IS WHAT MAKES THE FIRST SOUND.** First: for each wall, run the DELETE arm and confirm EXACTLY ONE witness reddens -- not that a witness reddens. Second: the subject that isolates the conjunct must be chosen with care, because a red for the wrong reason is how this class survives its own test. **SPECIMEN, with both halves executed** (gunbc#9838, `gunbc.self_host_compile_phase_frontier`): `census_population_is_duplicate_free` was added as a conjunct of `receipt_population_coherent`, evaluated on every coherence check and reachable from the live path; deleting it left the whole witness module green. The fixture that isolates it is a receipt whose `refused_files` REPEATS A PATH -- enrolled as `a_receipt_whose_refused_population_repeats_a_path_is_refused_by_coherence` in `test.claim.self_host_compile_phase_frontier_witness`, with `the_same_receipt_without_the_repeat_is_coherent` as its positive control. The subject that looks equally good and is not: a repeated ERROR DIAGNOSTIC IDENTITY also reddens the module, but it breaks a count invariant one line over rather than this conjunct, so it would have scored the DEAD conjunct as LIVE and closed the investigation with the wall still inert. **RUNG: mechanically preventable, and the honest ceiling is the same rung rather than higher.** The unenforcing conjunct stays writable; what exists is an executed falsifier per wall, not a construction that forbids authoring one. The next-rung trigger is a CAPABILITY and not an artifact: a mechanism that derives, from the check itself, the population its removal would change -- so that a conjunct with an empty such population fails to compile rather than waiting for an author to think of the delete arm. Enumerating delete arms by hand does not retire it; it only executes this row's recognition rule once per wall. **Its near twin is `instrument_output_read_as_subject_content`, and they meet from opposite sides**: there a report is complete for the REPORTER and read as complete for the SUBJECT; here a conjunct is genuinely executed and read as genuinely discriminating. Both are the shape where the health signal the reader actually checks -- completeness there, execution here -- is fully satisfied while the property they believe it establishes does not hold. **Neither is found by checking harder along the axis that already reads green.**) - **unbacked execution claim** (prose asserts a live executing relation — this runs on X, X reads these fields, Y already resolves Z — that no authority backs, and a reader forms the premise and plans against it. §4b's CI rung drop names the harm PREMISE CONTAMINATION in its own words; this row is that harm as a general class, since the drop is about one paragraph in the canonical authority and the class is about every carrier. **THE ORIGINS ARE A FIELD OF THIS ONE ROW RATHER THAN SEPARATE ROWS, because the recognition rule is identical across them** — resolve the claim against the authority that owns the relation — **but the REMEDY BRANCHES ON THE ORIGIN, and a remedy applied without reading the origin manufactures new false statements.** Three origins are ATTESTED here and a fourth is conceivable but was NOT found. ORIGIN 1, EXECUTOR DELETED: the claim was true and its executor was removed. Specimen: `gunbc.floor_component_receipt_document` recited that an alert downloads an artifact named `floor-component-receipt` from a run id it is given and reads four named fields; that alert was `falsifier-alert.yml`, deleted 2026-08-15, and a lane read the module, believed the transport live, planned against it, and was corrected by its reviewing authority — the cost is measured, not hypothetical. Second specimen, same origin: `gunbc.ci_failure_class` stated its dissolution trigger as calling `classify_failure_reason` *the way claim_executor already resolves* `gunbc.floor_component_receipt`; `write_floor_component_receipt_at` did exactly that from 2026-07-30 (gunbc#7467) until gunbc#9228 deleted it on 2026-08-25. ORIGIN 2, THE CITED SYMBOL DIED WHILE ITS CONTENT SURVIVED AS AN ANNOTATION, and this one has a corpus-wide producer: `v1_compiler.cli_run` twice cited `floor_component_resource_checkpoint_note` and `floor_component_phase_journal_scaffold_note`, which were real `data …: String` declarations in `gunbc.floor_component_receipt` until the 2026-08-30 prose-bankruptcy sweep (gunbc#9752) converted module-scope commentary rows into §4c annotations corpus-wide. §4c makes an annotation uncitable by construction — `v1_compiler.cli_run` `annotation_erased_scan_text` feeds the semantic passes, so annotation text is not present in the tree the citation harvester walks. **THE CONSEQUENCE IS THE OPPOSITE OF THE ONE THIS ROW FIRST DREW, AND THE CORRECTION IS THE INSTRUCTIVE PART.** The first revision reasoned that the sweep therefore broke every citation of a converted row, making the population the sweep's own diff. Measured by a separate lane (gunbc XL-0-CITE, 2026-09-01) against `v1_compiler.declaration_index`, whose only citation producers are `citation_from_record_literal` and `citation_from_constructor_call`: the typed-citation breakage is **ZERO**, computed both today and as of the sweep commit, on a control that discriminates in both directions. A plain name in annotation prose was never a citation, so the sweep could not break one. What that leaves is a REAL class with an honest ceiling rather than a defect population: these citations bind READERS and no mechanism, so they sit in §4b's *outside the modeled guarantee* column — observed and repaired by reading, never gated — and calling them a dangling-citation population would have been rung inflation about a check that by construction cannot see them. The two `cli_run` sites are genuine instances of the harm and there is no mechanical census that would have found them. ORIGIN 3, WRONG AUTHORITY FOR SOMETHING THAT STILL EXECUTES: `dag/test/claim/instrument_sandbox_witness_test` says its live half `runs on the falsifier lane`; that half sits on `FalsifierSubstrateLongLane`, which `std.witness_admission` `witness_cadence_has_scheduled_route` reports routeless — but it also carries a local recipe, and `OfflineLocalRecipe` HAS a route, so the relation is live and only the named authority is wrong. THE FOURTH, NEVER BACKED AT ALL, IS NOT ATTESTED IN THIS CORPUS and is recorded as unmeasured rather than as a population. **THIS ROW IS NOT THE AUTHORITY ON ANY POPULATION, and saying so is the point:** `gunbc.deleted_cadence_reference_census` already owns the falsifier-cadence instance of origin 1 — twelve sites, a `DeletedCadenceReferenceStanding` vocabulary whose `PremiseInvalidated` arm is exactly this class, and one `GuaranteeRungDrop` filed once for the shared executor rather than per site. A pattern row that restated its population would be a second authority for a fact that already has one, which is the §3 violation this row's own remedy is meant to prevent; so the census is cited here and nothing about its contents is copied. What belongs HERE is only the recognition rule and the remedy arms, which generalize past the falsifier. **THE MECHANICAL TEST for finding one is sharper than asking whether the verb is runs or is enrolled: resolve the claim against the authority that owns the relation, and ask whether the prose implies a live route for something that authority says has none.** The naive runs/enrolled discriminator loses one arm and it is the common one — `Enrolled on falsifier_rehomed_bin_wet_entries (nightly batch 5)` is nominally an enrollment claim, which is TRUE, but `nightly` asserts a cadence that executes and `FalsifierRehomedBinWet` is routeless, so the reader forms the false premise anyway. Three outcomes per line: asserts execution (contaminating), asserts enrollment only (true, leave it), asserts enrollment while implying a live cadence (contaminating). **THE REMEDY HAS THREE ARMS, KEYED TO WHAT THE AUTHORITY SAYS NOW, and picking the wrong one is itself an instance of this class.** Origin 1 → PAST TENSE WITH THE DATE, keeping the reasoning, because the reasoning usually outlives its executor and a reader who finds a dangling claim silently deleted learns nothing. Origin 2 → name the module and the FACT rather than the dead symbol, since §4c left no symbol to cite; past-tensing here would wrongly report the content as gone when only its citability is. Origin 3 → CORRECT IT to the authority that does back it, NOT past tense: past-tensing a live relation records it as dead and is a new contamination in the opposite direction, produced by the remedy itself. Origin 4 → say it was never backed, or delete the assertion while keeping any surviving reasoning; NEVER past tense, because there is no past to record, and `X USED TO resolve Y` asserts an execution that also never happened. **The default against deletion is a default and not an absolute: a sentence whose entire content is a false execution claim has no reasoning to preserve.** **THE SELF-RECEIPT, and it is the most useful thing in this row (2026-09-01):** the first revision of this entry asserted the `ci_failure_class` clause and the two `cli_run` notes as never-true, on the strength of a grep showing they resolve nowhere TODAY. **Both were true when written**, and the row about unbacked claims therefore contained two of them. It was caught by a reviewing authority asking for verification rather than inheritance, not by any check. **RECOGNITION RULE EARNED: `resolves nowhere now` is not `never resolved`, the distinction is decided by history and not by the working tree, and the instrument is `git log --all -S` over the DECLARATION FORM** — which also separates origin 1 from origin 2 by showing WHAT deleted the referent. The neighbouring rule is `positional_citation`'s: a citation left naming a deleted symbol fakes a grep hit, so every arm above records the change rather than quietly removing the name.) -- **instrument re-enters its own build tool** (a check whose SUBJECT is the build -- does this emitted crate compile, does this generated tree link, does this artifact still build under the real toolchain -- is hosted inside a process that the same build tool launched, so the check spawns the tool that is already holding the lock on the directory it needs. It does not fail; it BLOCKS, which is the whole reason the class earns a row: a refusal names itself in one line, and a lock wait is indistinguishable from a slow legitimate build for as long as anyone is willing to watch. Measured here rather than reasoned about (gunbc, 2026-09-01, node adhoc-da8c65c0-b3b): a `#[cfg(test)]` consumer of a new emit-then-rustc route, run under `cargo test --release -p v1-compiler --lib`, spawning `cargo build` with `CARGO_TARGET_DIR` pointing at the same workspace `target/` the outer `cargo test` holds -- thirty minutes of remote runner time producing no output at all, terminated rather than concluded, which is why the cost is recorded: the failure mode's signature is that it charges you the timeout to learn anything. **RECOGNITION RULE, and it is available BEFORE the run rather than after: name the process that will launch your check, name the directory your check's subprocess will lock, and refuse the pairing if they are the same tool over the same directory.** The two escapes differ in kind and only one is usually right. Giving the inner invocation its OWN target directory removes the contention and pays a full cold dependency rebuild for every run, which for a check hosted in a unit-test suite is a cost the suite cannot carry. Hosting the check in a BINARY the build tool has already finished producing -- built, then executed as its own process -- keeps the warm shared directory and is what the existing required emit-compile phase already does; the new route simply had to be homed the same way. **THE GENERALIZATION IS THE POINT AND IT IS NOT ABOUT CARGO:** any instrument whose subject is a build reaches for the build tool, and the most convenient host for an instrument is the test harness that same tool runs, so the pairing is what a reasonable author reaches for first. It recurs wherever the two facts are true together -- a generated-artifact check under `npm test` shelling out to `npm run build`, a bazel-hosted test invoking bazel, a make-driven check calling make -- and the guard is the same: **an instrument may not be hosted by its own subject's actuator.** Neighbouring but distinct from `absorbing_fallback`: nothing widens and nothing is fabricated here, the check is simply never able to answer, so the harm is an unanswerable instrument rather than a wrong answer -- and an instrument that hangs will, under any timeout that reports leniently, eventually be read as one that passed.) - **mitigation injected where the judgment declined** (a downstream stage injects a RUNTIME mitigation at exactly the seam where an upstream judgment returned Undecidable, so a 4b rung-1 fallback comes to occupy the place the wall belongs and the declined seam stops looking empty. **THE LOAD-BEARING HALF IS WHAT THE COINCIDENCE PROVES ABOUT THE DECLINE**: an Undecidable verdict is normally read as benign conservatism — the facts did not settle it, nothing is claimed, no harm asserted — and a mitigation landing on the SAME LINE is the first evidence that a real defect was sitting under that silence. The decline is therefore load-bearing AT THIS SITE, which refutes reading an Undecidable verdict as evidence of ABSENCE. **IT DOES NOT MAKE THE DECLINE CENSUS PREDICTIVE, and this row carries that limit because the author first wrote the stronger claim and then measured it FALSE** on the same board that produced the specimen: 259 of 272 declines sit in modules with ZERO blocking errors, only 6 of 28 decline-bearing modules carry any error at all, and the module holding 42 of the 63 errors carries ZERO declines and 105 ACCEPTS. Declines and defects are ANTI-CORRELATED at module grain, so this specimen is ONE co-location out of 272 and a walk of the decline census would be walking sites that are ~95% empty. The class is about a mitigation landing on a declined seam; it is NOT a claim that declines predict defects. Distinct from `absorbing_fallback`, whose arm WIDENS to a superset and destroys the precise mechanism s signal; here the arm NARROWS to a panic and destroys nothing except the evidence that a judgment was owed. Distinct from `reflection_evidence_structural_proof`, which mistakes an observation for a proof; here nothing is proven or observed — a decision was declined and then papered. Specimen with a receipt, measured 2026-09-01 on gunbc 0e8c49d and unrepaired at authoring: `v2.std.diagnostic outcome_accepted` is `fn outcome_accepted(value: T) -> Outcome` with T FREE; `v2.compiler.07_target_carriers target_fidelity_quotient_optional` declares `-> Outcome>` and calls `outcome_accepted(Present { value: child })`, so T instantiates to `Optional` and THE SOURCE IS CORRECT. The Rust emitter nonetheless rendered `Some(child).expect("fail-closed: an optional value flowed into non-optional parameter 0 of outcome_accepted (empty Optional at runtime)")`. FOUR DEFECTS IN ONE LINE: it fires on a GENERIC formal where `Optional` is a legitimate instantiation rather than a cardinality escape, so the check does not know T is free; it substitutes a runtime panic for a compile-time judgment; the emitted code does NOT typecheck anyway (rustc E0308 expected `Option>` found `Rc`), so the mitigation buys nothing while standing where the wall belongs; and its own panic string calls itself `fail-closed`, which is 4b rung inflation inside a string literal — a diagnostic naming the discipline it violates is worse than a silent one because it will be cited as evidence the discipline holds. The coincidence that makes this a class and not a bug: `v1.compiler.infer declared_type_inhabitance` returned InhabitanceUndecidable{UndecidableOptionalCarrier} at that module s ONLY optional-carrier decline, 07_target_carriers.dag:128:24 parameter `value` — the same line the emitter mitigated. RUNG FOUND AT: 1 (mitigatable) on a seam whose CEILING is 3 (structurally guaranteed), since generic-formal instantiation is decidable from modeled structure. CONSEQUENCE WITH ITS OWN TRIGGER, deliberately NOT folded into this row: wiring the optional-carrier judgment is argued by this specimen but is a separate change, and its trigger is a producer at that seam that can distinguish a FREE type variable from a declared non-optional formal — until that exists the judgment would decline for the same reason it declines today.) ## Declared rung drops (§4b(3)) diff --git a/src/v1/compiler_tests_rust.dag b/src/v1/compiler_tests_rust.dag index 3a5dd2399eb..857a5f1a1be 100644 --- a/src/v1/compiler_tests_rust.dag +++ b/src/v1/compiler_tests_rust.dag @@ -293,6 +293,49 @@ fn ct_parse_tests() -> String { " }\n\n") } +fn ct_fixture_closure_rustc_discrimination_test() -> String { + concat( + " /// THE EMITTED CLOSURE, HANDED TO RUSTC, OVER FIXTURES A TEST CAN AUTHOR.\n", + " ///\n", + " /// Every other emitted-bytes assertion in this file is a SPELLING oracle: it reads the\n", + " /// emitted text and asserts on substrings. A meaning-level emitter defect is invisible to\n", + " /// that whenever the wrong bytes still contain the right substring -- and it is exactly\n", + " /// what rustc's type checker refuses. This arm is the one that asks rustc.\n", + " ///\n", + " /// THE PAIR IS THE SUBJECT, NOT EITHER ARM. The control must COMPILE, or a red below\n", + " /// proves only that something in the tree is broken. The red must be refused BY RUSTC and\n", + " /// ATTRIBUTED to the fixture's own emitted module, or a non-zero status from any of the\n", + " /// hundreds of modules in its closure would pass for the fixture's own defect.\n", + " ///\n", + " /// #[ignore] AND WHY, STATED RATHER THAN LEFT TO BE DISCOVERED: this arm spawns cargo and\n", + " /// compiles two emitted crates, which is minutes rather than milliseconds, and\n", + " /// `repo_self_test_command` runs the whole --lib suite on every push and pull request. It\n", + " /// is therefore ENROLLED AND OPT-IN: `cargo test --release -p v1-compiler --lib\n", + " /// fixture_closure_rustc_discrimination -- --ignored`. An #[ignore] is a cost decision and\n", + " /// NOT a rung: nothing here may be cited as coverage that executes on the merge path.\n", + " #[test]\n", + " #[ignore]\n", + " fn fixture_closure_rustc_discrimination() {\n", + " let probe_root = crate::cli_run::local_emit_compile_probe_root();\n", + " let pair = crate::cli_run::run_fixture_closure_discrimination(&probe_root);\n", + " for line in crate::cli_run::fixture_discrimination_report(&pair) {\n", + " eprintln!(\"{}\", line);\n", + " }\n", + " assert!(\n", + " crate::cli_run::fixture_closure_reached_rustc(&pair.red),\n", + " \"the red arm never reached a rustc verdict, so nothing about the emitted bytes was measured: {}\",\n", + " crate::cli_run::fixture_closure_summary(&pair.red)\n", + " );\n", + " assert!(\n", + " crate::cli_run::fixture_discrimination_passed(&pair),\n", + " \"the control must compile and the meaning-level red must be refused by rustc and attributed to its own emitted module; control={} red={} attribution={:?}\",\n", + " crate::cli_run::fixture_closure_summary(&pair.green),\n", + " crate::cli_run::fixture_closure_summary(&pair.red),\n", + " crate::cli_run::fixture_closure_attributed_line(&pair.red)\n", + " );\n", + " }\n\n") +} + fn ct_import_lines_follow_resolved_binding_identity_test() -> String { concat( " #[test]\n", @@ -3079,6 +3122,7 @@ fn compiler_tests_source() -> String { ct_parse_tests(), ct_pipeline_test(), ct_import_lines_follow_resolved_binding_identity_test(), + ct_fixture_closure_rustc_discrimination_test(), ct_unlisted_import_use_witness_test(), ct_call_shape_wall_witness_test(), ct_call_deficit_red_witness_test(), diff --git a/src/v1/stage0/src/bin/claim_executor.rs b/src/v1/stage0/src/bin/claim_executor.rs index 266f398c8a1..3b0843cc13c 100644 --- a/src/v1/stage0/src/bin/claim_executor.rs +++ b/src/v1/stage0/src/bin/claim_executor.rs @@ -158,9 +158,6 @@ fn run() -> Result { let mut required_ci_lane: Option = None; let mut required_v2_emission_mode = false; let mut required_emit_compile_mode = false; - let mut fixture_closure_compile_mode = false; - let mut fixture_closure_pair_mode = false; - let mut fixture_closure_paths: Vec = Vec::new(); let mut required_regen_mode = false; let mut emit_partition_crates_mode = false; let mut emit_partition_crates_write = false; @@ -214,41 +211,6 @@ fn run() -> Result { // THE PHASE AS ITS OWN ENTRY POINT, for the reason `--required-v2-emission` is one: // running this alone is a real local action, and it runs the SAME producer the // required phase runs, so a green here and a green there cannot be two facts. - // THE FIXTURE-CALLABLE ROUTE AS ITS OWN ENTRY POINT. - // - // WHO THIS IS FOR, because it is not the required phase's caller. A lane holding a - // two-arm emitter discriminator -- one accepted `.dag` source, two emitter - // behaviours, the arms separated only by what rustc says about the EMITTED crate -- - // has nowhere to run it: the text oracles stop at emitted bytes, and the phase above - // takes only the entries its roster names. This mode takes `--fixture ` as many - // times as there are arms and reports EACH ARM'S DIAGNOSTIC. - // - // IT REPORTS; IT DOES NOT ADJUDICATE, and the distinction is the whole contract. A - // red arm is the EXPECTED result for half of a discriminator, so a red may not be - // this mode's failure -- the caller compares the arms, and a mode that exited - // nonzero on a red would make the discriminator's own passing state look like a - // broken run. What DOES stop the line is the route failing to reach a verdict at - // all (`CrateNotWritten`): then no arm was measured and there is nothing to compare. - "--fixture-closure-compile" => { - fixture_closure_compile_mode = true; - } - // THE ROUTE'S OWN ENROLLED DISCRIMINATION, RUNNABLE ON ITS OWN. - // - // It is also a conjunct of `--required-emit-compile`, which is where it gates. It has - // its own flag because that mode walks the whole entry roster first, and evidence - // nobody can afford to run is evidence that stops being run. This form compiles two - // closures and nothing else. - // - // IT IS AN EXPLICIT FLAG AND NOT THE NO-ARGUMENT DEFAULT OF THE MODE ABOVE. A default - // that silently swaps in a different subject when the caller passed no fixture is how - // a run reports on arms nobody asked for. - "--fixture-closure-pair" => { - fixture_closure_pair_mode = true; - } - "--fixture" => { - i += 1; - fixture_closure_paths.push(require_path_value(&args, i, "--fixture")?); - } "--required-emit-compile" => { required_emit_compile_mode = true; } @@ -364,81 +326,6 @@ fn run() -> Result { } } - // ORDERED AHEAD OF THE SOURCE-ROOT REQUIREMENT, for the reason `--emit-partition-crates` - // is: this mode takes NO roots. The route resolves a fixture's imports against the live tree - // through the witness roots the emit host already uses, so the generic guard below would - // refuse the only invocation this mode has. - if fixture_closure_pair_mode { - let probe_root = v1_compiler::cli_run::local_emit_compile_probe_root(); - let pair = v1_compiler::cli_run::run_fixture_closure_discrimination(&probe_root); - for line in v1_compiler::cli_run::fixture_discrimination_report(&pair) { - eprintln!("{line}"); - } - // HERE THE PAIR IS THE SUBJECT, so a red arm that failed to be red IS a failure -- the - // opposite contract from `--fixture-closure-compile`, where a red is one arm of someone - // else's discriminator and this binary is not the one comparing them. - return if v1_compiler::cli_run::fixture_discrimination_passed(&pair) { - Ok(ExitCode::SUCCESS) - } else { - Err(ExitCode::from(1)) - }; - } - - if fixture_closure_compile_mode { - // AN EMPTY FIXTURE LIST REFUSES. Zero arms measured is the mode reaching no subject, not - // zero failures, and reporting it clean is the empty-observation narrow. - if fixture_closure_paths.is_empty() { - eprintln!( - "fixture-closure-compile: no --fixture given; the mode has no subject and \ - refuses rather than reporting a clean run over nothing" - ); - return Err(ExitCode::from(2)); - } - let probe_root = v1_compiler::cli_run::local_emit_compile_probe_root(); - let mut unanswered = 0usize; - for path in &fixture_closure_paths { - let source = match std::fs::read_to_string(path) { - Ok(text) => text, - Err(e) => { - eprintln!("fixture-closure-compile: {path} unreadable: {e}"); - unanswered += 1; - continue; - } - }; - let outcome = v1_compiler::cli_run::fixture_closure_rustc_verdict(&source, &probe_root); - eprintln!( - "fixture-closure-compile: {path} {}", - v1_compiler::cli_run::fixture_closure_summary(&outcome) - ); - for line in v1_compiler::cli_run::fixture_arm_diagnostic_lines(path, &outcome) { - eprintln!("fixture-closure-compile: {line}"); - } - if let Some(attributed) = - v1_compiler::cli_run::fixture_closure_attributed_line(&outcome) - { - eprintln!("fixture-closure-compile: {path} attributed {attributed}"); - } - // AN ARM IS ANSWERED ONLY IF RUSTC REACHED A VERDICT ON IT. A red is answered; a - // fixture gunbc refused, an unwritten crate and a cargo run that never completed are - // not, and this mode must not exit successfully over any of them (review 58120). - if !v1_compiler::cli_run::fixture_closure_reached_rustc(&outcome) { - unanswered += 1; - } - } - eprintln!( - "fixture-closure-compile: arms={} unanswered={}", - fixture_closure_paths.len(), - unanswered - ); - // A RED ARM IS NOT THIS MODE'S FAILURE -- it is half of what a discriminator is for. Only - // an arm the route could not answer stops the line. - return if unanswered == 0 { - Ok(ExitCode::SUCCESS) - } else { - Err(ExitCode::from(1)) - }; - } - if source_roots.is_empty() { eprintln!("claim_executor: provide at least one --source-root"); return Err(ExitCode::from(2)); @@ -909,17 +796,6 @@ fn run() -> Result { } } } - // THE FIXTURE ROUTE'S DISCRIMINATING PAIR RUNS IN THE SAME MODE AND THE SAME - // PROCESS. It is not a second gate: the entry roster and the fixture pair are two - // subjects of ONE question -- does an emitted closure survive rustc -- and running - // them apart would let one green stand for the other. It runs after the roster so - // the shared target directory is already warm, and its verdict joins this mode's - // exit status rather than being printed beside it. - let pair = v1_compiler::cli_run::run_fixture_closure_discrimination(&probe_root); - for line in v1_compiler::cli_run::fixture_discrimination_report(&pair) { - eprintln!("required-emit-compile: {line}"); - } - let pair_passed = v1_compiler::cli_run::fixture_discrimination_passed(&pair); let (report, retention_error) = v1_compiler::cli_run::emit_compile_report( &outcomes, &roots, @@ -929,7 +805,7 @@ fn run() -> Result { for line in report { eprintln!("{line}"); } - return if not_passed == 0 && retention_error.is_none() && pair_passed { + return if not_passed == 0 && retention_error.is_none() { Ok(ExitCode::SUCCESS) } else { Err(ExitCode::from(1)) diff --git a/src/v1/stage0/src/cli_run.rs b/src/v1/stage0/src/cli_run.rs index 59c55811bd0..e4549ed19cc 100644 --- a/src/v1/stage0/src/cli_run.rs +++ b/src/v1/stage0/src/cli_run.rs @@ -38756,14 +38756,25 @@ pub use emitted_closure_compile_host::{ cargo_verdict_stderr_tail, emit_compile_modules_reached, emit_compile_outcome_passed, emit_compile_outcome_summary, emit_compile_report, emit_compile_selection, emit_compile_selection_not_selected_digest, emit_compile_selection_selected_digest, - emit_compile_selection_universe_digest, fixture_arm_diagnostic_lines, - fixture_closure_attributed_line, fixture_closure_compiled, fixture_closure_reached_rustc, - fixture_closure_rustc_verdict, fixture_closure_summary, fixture_discrimination_passed, - fixture_discrimination_report, local_emit_compile_probe_root, + emit_compile_selection_universe_digest, local_emit_compile_probe_root, required_ci_emit_compile_probe_root, required_emit_compile_entries, - retain_not_selected_identities, run_fixture_closure_discrimination, run_required_emit_compile, - CargoVerdict, EmitCompileOutcome, EmitCompileSelection, FixtureClosureOutcome, - FixtureDiscrimination, MutationVerdict, + retain_not_selected_identities, run_required_emit_compile, CargoVerdict, EmitCompileOutcome, + EmitCompileSelection, MutationVerdict, +}; + +/// THE FIXTURE ROUTE IS TEST-FACING ONLY, AND THAT IS WHY IT HAS ITS OWN `use` RATHER THAN A LINE +/// IN THE LIST ABOVE. +/// +/// `pub(crate)` under `#[cfg(test)]`: the generated `compiler_tests` module is a sibling module in +/// this crate, so it needs crate visibility to reach the route and needs nothing wider. Putting +/// these names in the production list would place them on the emitted seed's exported surface -- +/// PublicSurfaceGrowth against a seed frozen for growth. An internal test function does not become +/// public surface merely because Rust needs a path to it. +#[cfg(test)] +pub(crate) use emitted_closure_compile_host::{ + fixture_arm_diagnostic_lines, fixture_closure_attributed_line, fixture_closure_reached_rustc, + fixture_closure_rustc_verdict, fixture_closure_summary, fixture_discrimination_passed, + fixture_discrimination_report, run_fixture_closure_discrimination, FixtureClosureOutcome, }; /// The authority's own declared module path, for consumers outside this module. diff --git a/src/v1/stage0/src/compiler_tests.rs b/src/v1/stage0/src/compiler_tests.rs index 32a964a88be..5469b774325 100644 --- a/src/v1/stage0/src/compiler_tests.rs +++ b/src/v1/stage0/src/compiler_tests.rs @@ -459,6 +459,46 @@ mod compiler_tests { result.expect("emit_import_lines_follow_resolved_binding_identity panicked"); } + /// THE EMITTED CLOSURE, HANDED TO RUSTC, OVER FIXTURES A TEST CAN AUTHOR. + /// + /// Every other emitted-bytes assertion in this file is a SPELLING oracle: it reads the + /// emitted text and asserts on substrings. A meaning-level emitter defect is invisible to + /// that whenever the wrong bytes still contain the right substring -- and it is exactly + /// what rustc's type checker refuses. This arm is the one that asks rustc. + /// + /// THE PAIR IS THE SUBJECT, NOT EITHER ARM. The control must COMPILE, or a red below + /// proves only that something in the tree is broken. The red must be refused BY RUSTC and + /// ATTRIBUTED to the fixture's own emitted module, or a non-zero status from any of the + /// hundreds of modules in its closure would pass for the fixture's own defect. + /// + /// #[ignore] AND WHY, STATED RATHER THAN LEFT TO BE DISCOVERED: this arm spawns cargo and + /// compiles two emitted crates, which is minutes rather than milliseconds, and + /// `repo_self_test_command` runs the whole --lib suite on every push and pull request. It + /// is therefore ENROLLED AND OPT-IN: `cargo test --release -p v1-compiler --lib + /// fixture_closure_rustc_discrimination -- --ignored`. An #[ignore] is a cost decision and + /// NOT a rung: nothing here may be cited as coverage that executes on the merge path. + #[test] + #[ignore] + fn fixture_closure_rustc_discrimination() { + let probe_root = crate::cli_run::local_emit_compile_probe_root(); + let pair = crate::cli_run::run_fixture_closure_discrimination(&probe_root); + for line in crate::cli_run::fixture_discrimination_report(&pair) { + eprintln!("{}", line); + } + assert!( + crate::cli_run::fixture_closure_reached_rustc(&pair.red), + "the red arm never reached a rustc verdict, so nothing about the emitted bytes was measured: {}", + crate::cli_run::fixture_closure_summary(&pair.red) + ); + assert!( + crate::cli_run::fixture_discrimination_passed(&pair), + "the control must compile and the meaning-level red must be refused by rustc and attributed to its own emitted module; control={} red={} attribution={:?}", + crate::cli_run::fixture_closure_summary(&pair.green), + crate::cli_run::fixture_closure_summary(&pair.red), + crate::cli_run::fixture_closure_attributed_line(&pair.red) + ); + } + #[test] fn unlisted_import_use_witness() { // Discriminating witness for the selective-import fail-closed mask diff --git a/src/v1/stage0/src/emitted_closure_compile_host.rs b/src/v1/stage0/src/emitted_closure_compile_host.rs index 1040e26ce06..e6430dd673f 100644 --- a/src/v1/stage0/src/emitted_closure_compile_host.rs +++ b/src/v1/stage0/src/emitted_closure_compile_host.rs @@ -951,7 +951,8 @@ pub fn run_emit_compile_entry( /// diagnostic — not a red. Collapsing the two would let a fixture that gunbc rejected be reported /// as "rustc refused the emitted bytes", which is a claim about bytes that were never emitted. #[derive(Debug, Clone)] -pub enum FixtureClosureOutcome { +#[cfg(test)] +pub(crate) enum FixtureClosureOutcome { /// The v1 compiler refused the fixture itself: emission never happened, so rustc has no /// subject. `first` names one hard diagnostic so the caller can say WHICH refusal. SourceRefused { @@ -972,7 +973,8 @@ pub enum FixtureClosureOutcome { /// True only when cargo COMPLETED with status zero over the written closure. Every other arm — /// a gunbc refusal, an unwritten crate, a killed toolchain — answers false, because none of them /// is evidence the emitted program type-checks. -pub fn fixture_closure_compiled(outcome: &FixtureClosureOutcome) -> bool { +#[cfg(test)] +pub(crate) fn fixture_closure_compiled(outcome: &FixtureClosureOutcome) -> bool { match outcome { FixtureClosureOutcome::Measured { cargo, .. } => cargo_verdict_compiled(cargo), _ => false, @@ -997,7 +999,8 @@ pub fn fixture_closure_compiled(outcome: &FixtureClosureOutcome) -> bool { /// reaching rustc, which is the fail-open §5 forbids and which contradicted this module's own /// outcome separation. A predicate beside the carrier, rather than a `matches!` at each caller, /// is what stops the next caller from enumerating a different subset of the same arms. -pub fn fixture_closure_reached_rustc(outcome: &FixtureClosureOutcome) -> bool { +#[cfg(test)] +pub(crate) fn fixture_closure_reached_rustc(outcome: &FixtureClosureOutcome) -> bool { match outcome { FixtureClosureOutcome::Measured { cargo, .. } => { matches!(cargo, CargoVerdict::Completed { .. }) @@ -1013,14 +1016,16 @@ pub fn fixture_closure_reached_rustc(outcome: &FixtureClosureOutcome) -> bool { /// RED". The subject is one module inside a closure of hundreds; a bare non-zero status says the /// crate did not build and says nothing about which member broke. So the red direction is only /// claimable when a diagnostic line names the fixture's emitted file, which is what this returns. -pub fn fixture_closure_attributed_line(outcome: &FixtureClosureOutcome) -> Option<&str> { +#[cfg(test)] +pub(crate) fn fixture_closure_attributed_line(outcome: &FixtureClosureOutcome) -> Option<&str> { match outcome { FixtureClosureOutcome::Measured { cargo, .. } => cargo_verdict_probe_line(cargo), _ => None, } } -pub fn fixture_closure_summary(outcome: &FixtureClosureOutcome) -> String { +#[cfg(test)] +pub(crate) fn fixture_closure_summary(outcome: &FixtureClosureOutcome) -> String { match outcome { FixtureClosureOutcome::SourceRefused { hard_diagnostics, @@ -1046,6 +1051,7 @@ pub fn fixture_closure_summary(outcome: &FixtureClosureOutcome) -> String { /// module line rather than passed in beside it: a caller-supplied symbol could name a module the /// fixture does not declare, and every red would then be reported as unattributed while the /// fixture's real diagnostics sat in the same stderr. +#[cfg(test)] fn fixture_rust_module(source: &str) -> Result { source .lines() @@ -1059,7 +1065,11 @@ fn fixture_rust_module(source: &str) -> Result { /// The fixture's imports are resolved transitively against the live tree by /// `resolve_virtual_source_with_imports` — the same resolver `compile_dag_rust_emit_check` uses, /// so a fixture that compiles under one reaches the same closure under the other. -pub fn fixture_closure_rustc_verdict(source: &str, probe_root: &Path) -> FixtureClosureOutcome { +#[cfg(test)] +pub(crate) fn fixture_closure_rustc_verdict( + source: &str, + probe_root: &Path, +) -> FixtureClosureOutcome { let rust_module = match fixture_rust_module(source) { Ok(module) => module, Err(cause) => return FixtureClosureOutcome::CrateNotWritten { cause }, @@ -1140,9 +1150,10 @@ pub fn fixture_closure_rustc_verdict(source: &str, probe_root: &Path) -> Fixture /// diagnostic line NAMES the fixture's own emitted module — otherwise the report says /// `unattributed` and the pair fails, rather than crediting a red the fixture did not cause. #[derive(Debug, Clone)] -pub struct FixtureDiscrimination { - pub green: FixtureClosureOutcome, - pub red: FixtureClosureOutcome, +#[cfg(test)] +pub(crate) struct FixtureDiscrimination { + pub(crate) green: FixtureClosureOutcome, + pub(crate) red: FixtureClosureOutcome, } /// THE TWO ARMS ARE FIXTURE FILES, NOT STRING CONSTANTS IN THIS FILE, and the difference is @@ -1152,15 +1163,18 @@ pub struct FixtureDiscrimination { /// the arm anyone else runs. /// /// The positive control has no imports, so its closure is its own emitted module plus the runtime. +#[cfg(test)] const FIXTURE_GREEN_PATH: &str = "fixtures/fixture_closure_rustc/green_probe.dag"; /// The meaning-level red: `concat` answers the kernel string, the boundary declares the /// structural `v2.std.text.String`, and no homomorphism row stands between them. +#[cfg(test)] const FIXTURE_RED_PATH: &str = "fixtures/fixture_closure_rustc/text_nonliteral_probe.dag"; /// AN UNREADABLE ARM IS `CrateNotWritten`, NAMING THE PATH -- never an empty source quietly /// compiled. An empty `.dag` text emits a crate that builds, so substituting one would turn a /// missing fixture into a GREEN control and a red arm that stopped discriminating. +#[cfg(test)] fn fixture_arm_verdict(rel_path: &str, probe_root: &Path) -> FixtureClosureOutcome { let path = process_workspace_root().join(rel_path); match std::fs::read_to_string(&path) { @@ -1171,7 +1185,8 @@ fn fixture_arm_verdict(rel_path: &str, probe_root: &Path) -> FixtureClosureOutco } } -pub fn run_fixture_closure_discrimination(probe_root: &Path) -> FixtureDiscrimination { +#[cfg(test)] +pub(crate) fn run_fixture_closure_discrimination(probe_root: &Path) -> FixtureDiscrimination { FixtureDiscrimination { green: fixture_arm_verdict(FIXTURE_GREEN_PATH, probe_root), red: fixture_arm_verdict(FIXTURE_RED_PATH, probe_root), @@ -1184,7 +1199,8 @@ pub fn run_fixture_closure_discrimination(probe_root: &Path) -> FixtureDiscrimin /// A `SourceRefused` red does NOT pass. gunbc refusing the fixture is a different and better /// outcome — it means the wall climbed — but it is not this pair's subject, and reporting it as /// a pass here would let the route's evidence green while nothing reached rustc at all. -pub fn fixture_discrimination_passed(pair: &FixtureDiscrimination) -> bool { +#[cfg(test)] +pub(crate) fn fixture_discrimination_passed(pair: &FixtureDiscrimination) -> bool { fixture_closure_compiled(&pair.green) && fixture_closure_reached_rustc(&pair.red) && !fixture_closure_compiled(&pair.red) @@ -1199,7 +1215,8 @@ pub fn fixture_discrimination_passed(pair: &FixtureDiscrimination) -> bool { /// arms were red cannot tell those two apart, so it cannot discriminate the emitter behaviours /// that produced them. So every arm's diagnostic lines are reported, red or green, and the /// summary sits beside them rather than replacing them. -pub fn fixture_discrimination_report(pair: &FixtureDiscrimination) -> Vec { +#[cfg(test)] +pub(crate) fn fixture_discrimination_report(pair: &FixtureDiscrimination) -> Vec { let mut lines = vec![format!( "fixture-closure: control {}", fixture_closure_summary(&pair.green) @@ -1238,7 +1255,11 @@ pub fn fixture_discrimination_report(pair: &FixtureDiscrimination) -> Vec Vec { +#[cfg(test)] +pub(crate) fn fixture_arm_diagnostic_lines( + label: &str, + outcome: &FixtureClosureOutcome, +) -> Vec { match outcome { FixtureClosureOutcome::SourceRefused { first, .. } => { vec![format!("{label}| gunbc {first}")] diff --git a/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs b/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs index 4fec99157be..ecd61324b23 100644 --- a/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs +++ b/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs @@ -52,6 +52,10 @@ pub fn ct_parse_tests() -> String { v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(" #[test]\n".to_string(), " fn parse_trivial_module() {\n".to_string()), " let tokens = tokenize(\n".to_string()), " \"module test\\ntype Foo { x: Int }\\n\".to_string(),\n".to_string()), " \"test.dag\".to_string(),\n".to_string()), " );\n".to_string()), " let result = crate::v1_compiler_parse::parse(tokens, std::rc::Rc::new(im::HashMap::new()));\n".to_string()), " assert!(\n".to_string()), " result.module.is_some(),\n".to_string()), " \"valid module should parse successfully\"\n".to_string()), " );\n".to_string()), " }\n\n".to_string()), " #[test]\n".to_string()), " fn self_parse_tokenize_dag() {\n".to_string()), " let result = std::thread::Builder::new()\n".to_string()), " .stack_size(16 * 1024 * 1024)\n".to_string()), " .spawn(|| {\n".to_string()), " let source = read_dag(\"src/v1/01_tokenize.dag\");\n".to_string()), " let tokens = tokenize(source, \"src/v1/01_tokenize.dag\".to_string());\n\n".to_string()), " assert!(\n".to_string()), " !tokens.is_empty(),\n".to_string()), " \"tokenizing 01_tokenize.dag should produce tokens\"\n".to_string()), " );\n\n".to_string()), " let last = tokens.last().expect(\"should have tokens\");\n".to_string()), " assert!(\n".to_string()), " matches!(last.shape, crate::v1_std_core::TokenShape::ShEof),\n".to_string()), " \"last token should be Eof, got {:?}\",\n".to_string()), " last.shape\n".to_string()), " );\n\n".to_string()), " let result = crate::v1_compiler_parse::parse(tokens, std::rc::Rc::new(im::HashMap::new()));\n\n".to_string()), " assert!(\n".to_string()), " result.module.is_some(),\n".to_string()), " \"parsing 01_tokenize.dag should produce a module\"\n".to_string()), " );\n\n".to_string()), " let module = result.module.as_ref().unwrap();\n".to_string()), " assert_eq!(\n".to_string()), " module.name, \"v1.compiler.tokenize\",\n".to_string()), " \"module name should be v1.compiler.tokenize, got {}\",\n".to_string()), " module.name\n".to_string()), " );\n".to_string()), " })\n".to_string()), " .expect(\"failed to spawn thread\")\n".to_string()), " .join();\n".to_string()), " result.expect(\"self-parse test panicked\");\n".to_string()), " }\n\n".to_string()) } +pub fn ct_fixture_closure_rustc_discrimination_test() -> String { + v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(" /// THE EMITTED CLOSURE, HANDED TO RUSTC, OVER FIXTURES A TEST CAN AUTHOR.\n".to_string(), " ///\n".to_string()), " /// Every other emitted-bytes assertion in this file is a SPELLING oracle: it reads the\n".to_string()), " /// emitted text and asserts on substrings. A meaning-level emitter defect is invisible to\n".to_string()), " /// that whenever the wrong bytes still contain the right substring -- and it is exactly\n".to_string()), " /// what rustc's type checker refuses. This arm is the one that asks rustc.\n".to_string()), " ///\n".to_string()), " /// THE PAIR IS THE SUBJECT, NOT EITHER ARM. The control must COMPILE, or a red below\n".to_string()), " /// proves only that something in the tree is broken. The red must be refused BY RUSTC and\n".to_string()), " /// ATTRIBUTED to the fixture's own emitted module, or a non-zero status from any of the\n".to_string()), " /// hundreds of modules in its closure would pass for the fixture's own defect.\n".to_string()), " ///\n".to_string()), " /// #[ignore] AND WHY, STATED RATHER THAN LEFT TO BE DISCOVERED: this arm spawns cargo and\n".to_string()), " /// compiles two emitted crates, which is minutes rather than milliseconds, and\n".to_string()), " /// `repo_self_test_command` runs the whole --lib suite on every push and pull request. It\n".to_string()), " /// is therefore ENROLLED AND OPT-IN: `cargo test --release -p v1-compiler --lib\n".to_string()), " /// fixture_closure_rustc_discrimination -- --ignored`. An #[ignore] is a cost decision and\n".to_string()), " /// NOT a rung: nothing here may be cited as coverage that executes on the merge path.\n".to_string()), " #[test]\n".to_string()), " #[ignore]\n".to_string()), " fn fixture_closure_rustc_discrimination() {\n".to_string()), " let probe_root = crate::cli_run::local_emit_compile_probe_root();\n".to_string()), " let pair = crate::cli_run::run_fixture_closure_discrimination(&probe_root);\n".to_string()), " for line in crate::cli_run::fixture_discrimination_report(&pair) {\n".to_string()), " eprintln!(\"{}\", line);\n".to_string()), " }\n".to_string()), " assert!(\n".to_string()), " crate::cli_run::fixture_closure_reached_rustc(&pair.red),\n".to_string()), " \"the red arm never reached a rustc verdict, so nothing about the emitted bytes was measured: {}\",\n".to_string()), " crate::cli_run::fixture_closure_summary(&pair.red)\n".to_string()), " );\n".to_string()), " assert!(\n".to_string()), " crate::cli_run::fixture_discrimination_passed(&pair),\n".to_string()), " \"the control must compile and the meaning-level red must be refused by rustc and attributed to its own emitted module; control={} red={} attribution={:?}\",\n".to_string()), " crate::cli_run::fixture_closure_summary(&pair.green),\n".to_string()), " crate::cli_run::fixture_closure_summary(&pair.red),\n".to_string()), " crate::cli_run::fixture_closure_attributed_line(&pair.red)\n".to_string()), " );\n".to_string()), " }\n\n".to_string()) +} + pub fn ct_import_lines_follow_resolved_binding_identity_test() -> String { v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(" #[test]\n".to_string(), " fn emit_import_lines_follow_resolved_binding_identity() {\n".to_string()), " let result = std::thread::Builder::new()\n".to_string()), " .stack_size(16 * 1024 * 1024)\n".to_string()), " .spawn(|| {\n".to_string()), " let textlike = std::rc::Rc::new(crate::v1_compiler_compile::SourceFile {\n".to_string()), " path: \"probe_textlike.dag\".to_string(),\n".to_string()), " content: \"module probe.textlike\\ntype String = StringLeaf | StringNode { next: String }\\ntype Carrier = CarrierOne | CarrierTwo\\n\".to_string(),\n".to_string()), " });\n".to_string()), " let logiclike = std::rc::Rc::new(crate::v1_compiler_compile::SourceFile {\n".to_string()), " path: \"probe_logiclike.dag\".to_string(),\n".to_string()), " content: \"module probe.logiclike\\ntype Bool = ProbeTrue | ProbeFalse\\n\".to_string(),\n".to_string()), " });\n".to_string()), " let victim = std::rc::Rc::new(crate::v1_compiler_compile::SourceFile {\n".to_string()), " path: \"probe_victim.dag\".to_string(),\n".to_string()), " content: \"module probe.victim\\nimport probe.textlike { String, Carrier }\\nimport probe.logiclike { Bool }\\ntype Holder { tag: String, load: Carrier, flag: Bool }\\nfn keep(h: Holder) -> String { h.tag }\\n\".to_string(),\n".to_string()), " });\n".to_string()), " let result = crate::v1_compiler_compile::compile_sources(std::rc::Rc::new(im::vector![textlike, logiclike, victim]), crate::v1_compiler_artifact::RenderTarget::Rust);\n".to_string()), " let victim_out = result.files.iter().find(|f| f.path.contains(\"probe_victim\")).expect(\"victim module must emit\");\n".to_string()), " assert!(\n".to_string()), " !victim_out.content.lines().any(|l| l.contains(\"pub use\") && l.contains(\"String\")),\n".to_string()), " \"a name the resolver answers with the structureless kernel scalar must not emit a structural use-line shadow (the 139-row std::string::String vs Rc> family); emitted:\\n{}\",\n".to_string()), " victim_out.content\n".to_string()), " );\n".to_string()), " assert!(\n".to_string()), " victim_out.content.lines().any(|l| l.contains(\"pub use\") && l.contains(\"Carrier\")),\n".to_string()), " \"a non-kernel imported type must keep its use-line -- positive control that the drop is keyed on the resolved kernel identity, not on the import list; emitted:\\n{}\",\n".to_string()), " victim_out.content\n".to_string()), " );\n".to_string()), " assert!(\n".to_string()), " victim_out.content.lines().any(|l| l.contains(\"pub use\") && l.contains(\"Bool\")),\n".to_string()), " \"a kernel-SPELLED name whose kernel binding is structural (Bool = True | False) must keep its use-line -- the control that stops a blanket kernel-name drop; emitted:\\n{}\",\n".to_string()), " victim_out.content\n".to_string()), " );\n".to_string()), " assert!(\n".to_string()), " victim_out.content.contains(\"pub tag:\") && victim_out.content.contains(\"pub load:\"),\n".to_string()), " \"the probe fields must still emit at all; emitted:\\n{}\",\n".to_string()), " victim_out.content\n".to_string()), " );\n".to_string()), " })\n".to_string()), " .expect(\"failed to spawn thread\")\n".to_string()), " .join();\n".to_string()), " result.expect(\"emit_import_lines_follow_resolved_binding_identity panicked\");\n".to_string()), " }\n\n".to_string()) } @@ -381,5 +385,5 @@ pub fn ct_shell_service_output_projection_known_hole_probe_test() -> String { } pub fn compiler_tests_source() -> String { - v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(ct_module_header(), ct_workspace_helpers()), ct_file_discovery_helpers()), ct_source_builders()), ct_tokenizer_tests()), ct_parse_tests()), ct_pipeline_test()), ct_import_lines_follow_resolved_binding_identity_test()), ct_unlisted_import_use_witness_test()), ct_call_shape_wall_witness_test()), ct_call_deficit_red_witness_test()), ct_call_shape_duplicate_wall_witness_test()), ct_function_value_named_application_controls_witness_test()), ct_function_value_field_method_known_hole_probe_test()), ct_shell_service_output_projection_known_hole_probe_test()), ct_method_existence_wall_witness_test()), ct_declared_type_conformance_witness_test()), ct_sole_constructor_test()), ct_constructor_call_admission_test()), ct_constructor_call_admission_same_module_sibling_test()), ct_constructor_call_admission_function_value_test()), ct_constructor_call_admission_zero_arity_test()), ct_constructor_call_admission_shadowing_test()), ct_constructor_call_admission_qualified_caller_test()), ct_sole_constructor_fieldless_witness_test()), ct_contracts_sidecar_witness_test()), ct_self_parse_all_test()), ct_self_resolve_test()), ct_self_compile_test()), ct_self_compile_cargo_check_test()), ct_type_size_test()), ct_coercion_tests()), ct_profile_helpers()), ct_profile_self_compile_test()), ct_profile_full_pipeline_test()), ct_profile_reconcile_test()), ct_module_footer()) + v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(ct_module_header(), ct_workspace_helpers()), ct_file_discovery_helpers()), ct_source_builders()), ct_tokenizer_tests()), ct_parse_tests()), ct_pipeline_test()), ct_import_lines_follow_resolved_binding_identity_test()), ct_fixture_closure_rustc_discrimination_test()), ct_unlisted_import_use_witness_test()), ct_call_shape_wall_witness_test()), ct_call_deficit_red_witness_test()), ct_call_shape_duplicate_wall_witness_test()), ct_function_value_named_application_controls_witness_test()), ct_function_value_field_method_known_hole_probe_test()), ct_shell_service_output_projection_known_hole_probe_test()), ct_method_existence_wall_witness_test()), ct_declared_type_conformance_witness_test()), ct_sole_constructor_test()), ct_constructor_call_admission_test()), ct_constructor_call_admission_same_module_sibling_test()), ct_constructor_call_admission_function_value_test()), ct_constructor_call_admission_zero_arity_test()), ct_constructor_call_admission_shadowing_test()), ct_constructor_call_admission_qualified_caller_test()), ct_sole_constructor_fieldless_witness_test()), ct_contracts_sidecar_witness_test()), ct_self_parse_all_test()), ct_self_resolve_test()), ct_self_compile_test()), ct_self_compile_cargo_check_test()), ct_type_size_test()), ct_coercion_tests()), ct_profile_helpers()), ct_profile_self_compile_test()), ct_profile_full_pipeline_test()), ct_profile_reconcile_test()), ct_module_footer()) } From 9c3c86a1875bafa952856248b397bf482d394ece Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Tue, 1 Sep 2026 11:18:31 +0000 Subject: [PATCH 4/6] Point the boundary and the docstrings at the code that landed (review 58151) The rework withdrew the --fixture-closure-compile and --fixture-closure-pair modes under the approval's test-facing-visibility condition. THE PROSE DESCRIBING THEM SURVIVED THE CODE BY ONE REVISION: current_boundary still listed both modes, the carrier still said the pair "runs inside claim_executor --required-emit-compile ... and its verdict JOINS that mode's exit status", and fixture_closure_reached_rustc's docstring still attributed its fail-open repair to a --fixture-closure-compile call site. None of that is in the tree -- `grep fixture-closure src/v1/stage0/src/bin/claim_executor.rs` returns nothing. Specification pointing at wiring that does not exist is exactly the class this file's receipts are supposed to prevent. THE CITATIONS ARE CORRECTED RATHER THAN THE WIRING ADDED, because the modes are forbidden by the condition that removed them: - current_boundary now names the real consumer: v1.compiler.compiler_tests_rust ct_fixture_closure_rustc_discrimination_test and its emitted form in compiler_tests.rs. - The WHERE IT EXECUTES paragraph now states the actual grain -- one #[ignore]d consumer, available via `-- --ignored`, not executing by default, rust-unit-tests outside the required aggregate, candidate evidence and no wall, discharging no next-rung trigger. - fixture_closure_reached_rustc's docstring says the specimen caller was a CLI mode SINCE WITHDRAWN and keeps the reason the predicate stays: the defect was a caller enumerating a SUBSET of the arms that cannot answer, and the next caller would enumerate a different subset. - The module header states the route has exactly one consumer, reaches no CLI flag and no required phase, and is not on the emitted seed's public surface. The withdrawal is RECORDED in the carrier rather than silently rewritten, naming both modes and review 58151, because a receipt that quietly changes what it certifies is worse than one that was wrong once -- which is this file's own standing argument. Seed-growth roster re-derived: 75 declarations, 75 rows, zero stale, zero unaccounted. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HoAvoacTJ3UVqBhBSfCYZh --- .../emitted_closure_compile_seed_growth.dag | 29 +++++++++++++------ .../src/emitted_closure_compile_host.rs | 23 ++++++++++----- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/dag/gunbc/emitted_closure_compile_seed_growth.dag b/dag/gunbc/emitted_closure_compile_seed_growth.dag index cd7e41ee9cd..73a7b0aca79 100644 --- a/dag/gunbc/emitted_closure_compile_seed_growth.dag +++ b/dag/gunbc/emitted_closure_compile_seed_growth.dag @@ -151,14 +151,25 @@ import gunbc.seed_growth { SeedGrowthJustification } // therefore a parameter of the caller rather than the injected mutation's constant: the required // phase attributes its own fault, the fixture route attributes the fixture. // -// WHERE IT EXECUTES, STATED HONESTLY BECAUSE IT IS NOT THE MERGE PATH. The pair runs inside -// claim_executor --required-emit-compile, in the same process and after the entry roster so the -// shared target directory is warm, and its verdict JOINS that mode's exit status rather than -// being printed beside it. That mode is not a phase of --required-ci and no workflow invokes it, -// which gunbc.rung_drop required_gate_bankruptcy and emit_stage_blocking already declare for the -// phase this route joins. So: green by execution, and NOT a merge gate. Nothing here claims a -// rung for the text-boundary class, which stays exactly where its own row puts it -- what changes -// is that the row's emitted-Rust half now has an executing probe instead of a sentence. +// WHERE IT EXECUTES, STATED HONESTLY BECAUSE IT IS NOT THE MERGE PATH. The pair's ONE consumer is +// `v1.compiler.compiler_tests_rust` `fixture_closure_rustc_discrimination`, emitted into +// `compiler_tests.rs` and run by `repo_self_test_command` -- and it is `#[ignore]`d, so it is +// ENROLLED AND AVAILABLE via `cargo test --release -p v1-compiler --lib +// fixture_closure_rustc_discrimination -- --ignored` and DOES NOT EXECUTE BY DEFAULT on push or +// pull request. `rust-unit-tests` is additionally not a `needs` of the required aggregate, so even +// un-ignored a red there would be visible and would not block through the required context. The +// evidence is therefore CANDIDATE EVIDENCE -- reviewable on demand, NO WALL -- and an `#[ignore]` +// is a cost decision and NOT a rung. It discharges no next-rung trigger naming this capability: +// the capability exists, the evidence does not execute. Nothing here claims a rung for the +// text-boundary class, which stays exactly where its own row puts it. +// +// AN EARLIER REVISION OF THIS PARAGRAPH CITED TWO `claim_executor` MODES THAT NO LONGER EXIST +// (`--fixture-closure-compile`, `--fixture-closure-pair`) AND SAID THE PAIR'S VERDICT JOINED +// `--required-emit-compile`'s EXIT STATUS. Those modes were withdrawn under the approval's +// test-facing-visibility condition before this landed, and the sentence describing them survived +// the code by one revision -- specification pointing at wiring that is not in the tree, found by +// review 58151. Recorded rather than silently rewritten, because this file's whole subject is +// receipts that outlive what they certify. // // WHERE THE CONSUMER LIVES, AND A FALSIFIED CLAIM RECORDED RATHER THAN QUIETLY DROPPED. An // earlier revision of this lane asserted that a cargo-test-hosted consumer DEADLOCKS -- that a @@ -285,5 +296,5 @@ data emitted_closure_compile_seed_growth_justification: SeedGrowthJustification reason: "WHY RUST IS STILL NEEDED, and it is a REACHABILITY limit rather than a modeling gap. The subject is a required CI phase: it must run inside claim_executor, which is the only witness-executing consumer in the tree, and its stages are host effects -- writing an emitted tree to disk and spawning cargo over it. The required floor's hermetic envelope REFUSES host effects during preparation, so a .dag witness whose subject is a subprocess exit status is counted executed while its assertion never runs; DESIGN's Building-&-checks section records that boundary in as many words, and records that mocking the refusal would pass the witness against a fabricated exit status. So the phase has to live where the effects do, exactly as required_regen_host and partition_crate_boundary_host do.\n\nWHY IT IS ADMITTED AGAINST THE v1 FREEZE, ON PRECEDENT RATHER THAN ANALOGY. gunbc.v1_maintenance_standing v1_seed_standing admits a change by PURPOSE -- does it serve the v2 self-host program -- and the purpose test reaches the INSTRUMENT that measures that program, not only the program. This is that instrument in the most direct available sense: the self-host claim IS that the emitted tree compiles and behaves, and until now nothing on the merge path compiled an emitted tree at all. gunbc.floor_non_verdict_enrollment and gunbc.declaration_index_seed_growth are both admitted on that same footing. Classified against the five refused classes: NewLanguageBehavior -- no, the compile pipeline is byte-untouched, no diagnostic is added, moved or removed. NewCompatibilityObligation -- no, nothing is kept working for an old caller. NewEscapeHatchOrAdmissionRow -- no, and the direction is opposite: this adds a wall and adds no flag, env var or mode that skips it. SeedFeatureCompletion -- no, the seed compiler gains no capability; it compiles exactly what it compiled before. PublicSurfaceGrowth -- the nearest, and the test is a diff over THE EMITTED SEED'S EXPORTED DECLARATIONS: the file is wired by #[path] mod inside cli_run.rs exactly as declaration_index.rs, phase_profile.rs and partition_crate_boundary_host.rs are, so it contributes no pub mod line to the emitted lib.rs and its SeedRetainedIntrinsicRegistration carries has_pub_mod: false.\n\nWHAT IS NOT CLAIMED. This phase is not a compile of the corpus and must not be read as one: gunbc.whole_corpus_compile_admission refuses a whole-bundle compile on the default runner and records two EXIT=137 kills behind that refusal, so the subject is a bounded roster of entries in gunbc.ci_layer_roots required_emit_compile_entries. It carries no diagnostic count, no baseline and no ratchet -- a merge-blocking comparison against a population measured on the current tree is the tree-copied oracle DESIGN 5 rejects.", owning_dissolution_lane: "v1-hand-queue-drain" as RoadmapNodeId, trigger: "Delete this host when the phase's two host effects are modeled and a modeled actuator can perform them: writing an emitted file set to a directory, and invoking cargo over a manifest with a typed outcome. extdeps.cargo already models the manifest, the dependency rows and the target kinds, and extdeps.filesystem.filesystem_io models the write, so what is missing is the ACTUATION -- a modeled operation the required run can execute without the hermetic envelope refusing it. When that exists, run_emit_compile_entry becomes a .dag fold over those two operations and every item here is deleted, not re-homed. A PARTIAL migration is admissible and is the expected shape: the moment the cargo invocation is a modeled transport, run_cargo and the CargoVerdict vocabulary go first and the emission-to-disk half stays behind. What does NOT dissolve these items is the phase merely changing its roster, and what does NOT dissolve them is a hermetic witness asserting a fabricated exit status -- that would retire the evidence while retiring nothing it guards.", - current_boundary: "src/v1/stage0/src/emitted_closure_compile_host.rs; src/v1/stage0/src/cli_run.rs (the #[path] mod line and the re-export); src/v1/stage0/src/bin/claim_executor.rs (RequiredCiPhase::EmitCompile and its phase body, plus the --fixture-closure-compile and --fixture-closure-pair modes); fixtures/fixture_closure_rustc/ (the pair's two arms, which are files rather than string constants so that the arm this pair adjudicates is the arm any caller runs); dag/gunbc/ci/ci_layer_roots.dag required_emit_compile_entries; src/v2/compiler/self_host/stage0_crate_layout.dag; dag/gunbc/emitted_closure_compile_seed_growth.dag" + current_boundary: "src/v1/stage0/src/emitted_closure_compile_host.rs; src/v1/stage0/src/cli_run.rs (the #[path] mod line and the re-export); src/v1/stage0/src/bin/claim_executor.rs (RequiredCiPhase::EmitCompile and its phase body); src/v1/compiler_tests_rust.dag ct_fixture_closure_rustc_discrimination_test and its emitted form in src/v1/stage0/src/compiler_tests.rs, the route's only consumer; fixtures/fixture_closure_rustc/ (the pair's two arms, which are files rather than string constants so that the arm this pair adjudicates is the arm any caller runs); dag/gunbc/ci/ci_layer_roots.dag required_emit_compile_entries; src/v2/compiler/self_host/stage0_crate_layout.dag; dag/gunbc/emitted_closure_compile_seed_growth.dag" } diff --git a/src/v1/stage0/src/emitted_closure_compile_host.rs b/src/v1/stage0/src/emitted_closure_compile_host.rs index e6430dd673f..bfa61500d83 100644 --- a/src/v1/stage0/src/emitted_closure_compile_host.rs +++ b/src/v1/stage0/src/emitted_closure_compile_host.rs @@ -34,6 +34,11 @@ //! and `run_cargo` deliberately: a separately authored fixture harness would make a green here and //! a green there two facts about two crates. //! +//! THE FIXTURE ROUTE IS `#[cfg(test)]` AND HAS EXACTLY ONE CONSUMER: the generated +//! `fixture_closure_rustc_discrimination` (authored in `v1.compiler.compiler_tests_rust`), which +//! is `#[ignore]`d and therefore available on demand rather than executing on push. It reaches no +//! CLI flag and no required phase, and it is not on the emitted seed's public surface. +//! //! WHAT THIS PHASE IS NOT. No baseline, no diagnostic count, no ratchet. Cargo's exit status is //! the whole verdict; warnings are not errors here. Pinning a diagnostic population measured on //! the current tree would be the tree-copied oracle DESIGN 5 rejects; an identity-grain debt @@ -993,12 +998,14 @@ pub(crate) fn fixture_closure_compiled(outcome: &FixtureClosureOutcome) -> bool /// - `NotAttempted` -- the toolchain was never invoked; /// - `DidNotComplete` -- cargo was killed or failed to spawn, reporting no status of its own. /// -/// THIS EXISTS BECAUSE THE CALL SITE GOT IT WRONG (review 58120). `--fixture-closure-compile` -/// counted only `CrateNotWritten` as unanswered and therefore exited SUCCESSFULLY on a fixture -/// gunbc rejected and on a cargo run that never finished -- a mode reporting success without -/// reaching rustc, which is the fail-open §5 forbids and which contradicted this module's own -/// outcome separation. A predicate beside the carrier, rather than a `matches!` at each caller, -/// is what stops the next caller from enumerating a different subset of the same arms. +/// THIS EXISTS BECAUSE A CALL SITE GOT IT WRONG (review 58120). A caller counted only +/// `CrateNotWritten` as unanswered and therefore treated a fixture gunbc REJECTED, and a cargo run +/// that never FINISHED, as successfully measured -- reporting success without reaching rustc, +/// which is the fail-open §5 forbids and which contradicted this module's own outcome separation. +/// That caller was a CLI mode since withdrawn, so the specimen no longer exists in the tree; the +/// predicate stays because the defect was a caller enumerating a SUBSET of the arms that cannot +/// answer, and the next caller would enumerate a different subset. A predicate beside the carrier +/// is what makes that unavailable, rather than a `matches!` repeated at each call site. #[cfg(test)] pub(crate) fn fixture_closure_reached_rustc(outcome: &FixtureClosureOutcome) -> bool { match outcome { @@ -1157,8 +1164,8 @@ pub(crate) struct FixtureDiscrimination { } /// THE TWO ARMS ARE FIXTURE FILES, NOT STRING CONSTANTS IN THIS FILE, and the difference is -/// §3 rather than taste. The same arms are what a caller passes to `--fixture-closure-compile` -/// by path; carrying a second copy of their source here would be one fixture with two authorities +/// §3 rather than taste. The same arms are files any caller can hand this route by path; carrying +/// a second copy of their source here would be one fixture with two authorities /// that drift the first time either is edited, and the arm this pair adjudicates would stop being /// the arm anyone else runs. /// From 5b0199216024751a3a01bcf5288b09b641e1d479 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Tue, 1 Sep 2026 11:39:49 +0000 Subject: [PATCH 5/6] Record why the red fixture's import is load-bearing (review 58161) Review 58161 read `import v2.std.text { string_is_empty }` in the red fixture as dead weight: the binding is never mentioned again, and the red is `concat("x","y")` at the return boundary. Every reader will have that instinct, so the answer is measured and written down rather than argued. THE IMPORT IS THE MECHANISM. `v1_compiler.cli_run` `resolve_virtual_source_with_imports` builds this fixture's source set ONLY by following import statements transitively, so the import is the single thing that puts v2.std.text into the resolved closure. Run the route on this file with the import stripped: WITHOUT IMPORT => SourceRefused hard=1 UnresolvedType { name: "v2.std.text.String" } | reached_rustc=false So deleting it does not tidy the fixture -- it converts the red arm from a RUSTC VERDICT into a GUNBC REFUSAL and destroys the discrimination, while looking like cleanup. That is the failure shape this lane keeps meeting: a change that reads as obviously right and inverts what the code does. THE WALL HOLDS IF ANYONE DOES IT ANYWAY, which is worth stating because it is the fail-closed repair from review 58120 doing its job: fixture_closure_reached_rustc refuses to count an arm that never reached rustc, so the pair goes red rather than passing on a refusal. The annotation exists so the wall is not hit in the first place. No code change; the fixture gains the note and the measurement that produced it. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HoAvoacTJ3UVqBhBSfCYZh --- .../text_nonliteral_probe.dag | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/fixtures/fixture_closure_rustc/text_nonliteral_probe.dag b/fixtures/fixture_closure_rustc/text_nonliteral_probe.dag index 7c5a80730e8..676b3b0b634 100644 --- a/fixtures/fixture_closure_rustc/text_nonliteral_probe.dag +++ b/fixtures/fixture_closure_rustc/text_nonliteral_probe.dag @@ -8,4 +8,19 @@ import v2.std.text { string_is_empty } // relation admits the pairing by leaf-name spelling. A substring oracle over the emitted text // cannot see this -- the emitted bytes contain every substring a spelling assertion would look // for -- which is why the subject needs rustc rather than a text check. +// +// THE IMPORT ABOVE IS LOAD-BEARING AND MUST NOT BE DELETED AS UNUSED. It binds a name this module +// never mentions again, so every reader -- and review 58161, which is why this note exists -- sees +// dead weight. It is the opposite: `v1_compiler.cli_run` `resolve_virtual_source_with_imports` +// builds this fixture's source set ONLY by following import statements transitively, so the import +// is the single mechanism that puts v2.std.text into the resolved closure at all. Without it the +// qualified return type has nothing to resolve against. +// +// MEASURED RATHER THAN ASSERTED, by running the route on this file with the import stripped: +// WITHOUT IMPORT => SourceRefused hard=1 +// UnresolvedType { name: "v2.std.text.String" } | reached_rustc=false +// So deleting it does not tidy the fixture; it converts the red arm from a RUSTC VERDICT into a +// GUNBC REFUSAL and destroys the discrimination, while looking like cleanup. The pair does go red +// if anyone does it -- `fixture_closure_reached_rustc` refuses to count an arm that never reached +// rustc -- so the wall holds; this note exists so the wall is not hit in the first place. fn rustc_text_nonliteral_probe() -> v2.std.text.String { concat("x", "y") } From 7be507bf28cfc4bff3186d5c9a82dc3be2050dc0 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Tue, 1 Sep 2026 11:59:41 +0000 Subject: [PATCH 6/6] Record the ignore decision with its measurement and its reversal condition A decision to NOT RUN something goes silently stale: read in six months, an #[ignore] with no reasoning beside it is indistinguishable from neglect. So the carrier now carries the number, the argument and the condition that re-opens it. MEASURED, one runner, one run, warm target dir so the figures are comparable: discriminator alone 184.65s (test result: ok, pair PASSED) default --lib suite 456.28s (what runs on every push and PR) => un-ignoring is ~+40% on that suite, fleet-wide, per push. NOT measured cold, stated rather than estimated. The 185s is two emitted-crate cargo builds -- 6-file and 17-file closures, both linking the seed crate -- so it is rustc doing the work that makes the arm mean anything, not test logic, and it cannot be optimised away without removing the subject. THE ARGUMENT IS A RUNG ARGUMENT, NOT A COST ONE. Un-ignoring moves this from AVAILABLE to EXECUTES-ON-A-NON-BLOCKING-JOB, which is a real honesty improvement and still not an acceptance path, because rust-unit-tests is not a `needs` of the required aggregate. DESIGN 4b(1) establishes a rung by evidence executing on the real acceptance path, so BOTH STATES LEAVE A TRIGGER NAMING THIS CAPABILITY UNFIRED. The ignore costs nothing in rung terms and saves 40% of a per-push suite. REVERSAL CONDITION, named so this is re-decided rather than inherited: when rust-unit-tests is promoted into the required aggregate, cost and WALL arrive together and this trade no longer holds. Nothing else reverses it -- not a faster suite, not a bigger roster, and not a lane wanting to cite this as coverage. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HoAvoacTJ3UVqBhBSfCYZh --- .../emitted_closure_compile_seed_growth.dag | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/dag/gunbc/emitted_closure_compile_seed_growth.dag b/dag/gunbc/emitted_closure_compile_seed_growth.dag index 73a7b0aca79..19a771466ca 100644 --- a/dag/gunbc/emitted_closure_compile_seed_growth.dag +++ b/dag/gunbc/emitted_closure_compile_seed_growth.dag @@ -163,6 +163,32 @@ import gunbc.seed_growth { SeedGrowthJustification } // the capability exists, the evidence does not execute. Nothing here claims a rung for the // text-boundary class, which stays exactly where its own row puts it. // +// THE #[ignore] IS A MEASURED DECISION WITH A REVERSAL CONDITION, NOT NEGLECT, and it is written +// down because a decision to NOT RUN something is exactly the kind that goes silently stale -- read +// in six months, an ignore with no reasoning beside it is indistinguishable from an oversight. +// +// THE MEASUREMENT, one BuildBuddy runner, one run, warm target directory, so the two figures are +// comparable: the discriminator alone is 184.65s (`test result: ok`, pair PASSED); the default +// `--lib` suite that runs on every push and pull request is 456.28s. Un-ignoring it is therefore +// roughly +40% on that suite, fleet-wide, per push. NOT MEASURED COLD, and stated rather than +// estimated: a cold runner pays the dependency graph first. WHAT THE 185s IS, so nobody prices a +// black box: two emitted-crate cargo builds -- the control's closure is 6 files, the red's is 17, +// both linking the seed crate. It is rustc doing the work that makes the arm mean anything, not +// test logic, so it cannot be optimised away without removing the subject. +// +// THE ARGUMENT THAT DECIDES IT IS A RUNG ARGUMENT AND NOT A COST ONE. Un-ignoring would move this +// evidence from AVAILABLE to EXECUTES-ON-A-NON-BLOCKING-JOB. That is a real honesty improvement and +// it is still not an acceptance path, because `rust-unit-tests` is not a `needs` of the required +// aggregate. DESIGN 4b(1) establishes a rung by evidence executing on the REAL ACCEPTANCE PATH, so +// BOTH STATES LEAVE A NEXT-RUNG TRIGGER NAMING THIS CAPABILITY UNFIRED. The ignore therefore costs +// NOTHING in rung terms and saves 40% of a per-push suite, which is the whole argument. +// +// THE CONDITION THAT REVERSES IT, named so this is re-decided rather than inherited: when +// `rust-unit-tests` is promoted into the required aggregate. At that point the cost and the WALL +// arrive together -- the evidence would execute on an acceptance path and could block a merge -- +// and the trade this row prices no longer holds. Nothing else reverses it: not the suite getting +// faster, not the roster growing, and not a lane wanting to cite this as coverage. +// // AN EARLIER REVISION OF THIS PARAGRAPH CITED TWO `claim_executor` MODES THAT NO LONGER EXIST // (`--fixture-closure-compile`, `--fixture-closure-pair`) AND SAID THE PAIR'S VERDICT JOINED // `--required-emit-compile`'s EXIT STATUS. Those modes were withdrawn under the approval's