From a5dac72ac9147fdeb50e61db5cbf52ed140697cb Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Wed, 19 Aug 2026 02:01:44 +0000 Subject: [PATCH] =?UTF-8?q?Witness=20cost=20class:=20import-closure=20poll?= =?UTF-8?q?ution=20=E2=80=94=20zero=20confirmed=20exemplars?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Investigated three candidates for the witness-cost-class lane (dashboard node://adhoc-0e6f8ea5-158): the extdeps_scope_placement_gate loudness witness (PR #8432's 718→62 module import-closure narrowing), the Class B rust_test_fixtures rows 1-2, and the dissolution_census empty-literal witness. All three were falsified as import-closure pollution, each against the required floor's own [floor-witness-slow] line rather than a standalone harness run: - extdeps_scope_placement_gate_loudness_witness: the closure narrowing is real (718/10.1MB -> 62/0.49MB modules) and standalone resolve time dropped, but the floor's own last post-repair line for red_seed_runner_failure_detail_projects_located_receipt is 75926ms (BUDGET-REFUSED against 1552ms, +1.00GB RSS, worst claim in the run) — unchanged in order of magnitude from its pre-repair floor history. Right repair, wrong quantity measured. - Class B rows 1-2 (declared_import_closure_lists_rust_import, ambient_pool_provider_preserves_listed_import): a paired same-host, same-dispatch comparison found the narrow probe-backed entry and the heavy transport-backed entry statistically indistinguishable (~70s/~80s both). The floor's own line (24674ms/31603ms) confirms the same non-divergence at its own (lower, amortized) magnitude. - dissolution_census unbound-literal witnesses: root-caused to a different mechanism entirely — compile_dag_rust_emit_check's uncached path rebuilds a full corpus-rooted module index per call, memoized only under a real --required-floor run, producing a first-toucher shared-fill signature (42129ms vs 825ms) with no import-closure difference between the two rows. Class-2 per-claim memoization, not closure-narrowing scope. All three point at the same underlying object: a fixed corpus-wide load/index cost paid once per MultiEntryIndex rather than once per entry's import closure — corroborating, not re-deriving, DESIGN.md's floor-shared-compute-memoization thread on build_both_closure_edge_index. The class_b_import_closure_probe split (mirroring the already-landed inferred_tree split) is retained as a DESIGN.md §3 relocation-not- refork dependency correctness improvement, not a cost repair. The CLASS_B_GATE_INPUT_ENTRIES roster fix in cli_run.rs is a genuine collateral correctness fix for that split. Deliverable: the paired same-process, same-host narrow-vs-heavy comparison methodology, baselined against the floor's own CI job log (gh api .../jobs//logs --allow-escape-sequences) rather than a standalone run — the reusable diagnostic for any future import- closure-pollution candidate in this population. Co-Authored-By: Claude Sonnet 5 --- .../long/dissolution_census_witness_test.dag | 2 + ...e_placement_gate_loudness_witness_test.dag | 2 +- ...t_fixtures_import_closure_witness_test.dag | 4 +- src/v1/stage0/src/cli_run.rs | 7 +++ src/v2/compiler/04_infer.dag | 27 ++++-------- src/v2/compiler/inferred_tree.dag | 40 +++++++++++++++++ .../long/bind_eval_by_execution_test.dag | 2 +- .../long/loop_eval_by_execution_test.dag | 2 +- .../workflow/class_b_import_closure_probe.dag | 44 +++++++++++++++++++ .../class_b_import_closure_transport.dag | 39 +++------------- 10 files changed, 113 insertions(+), 56 deletions(-) create mode 100644 src/v2/compiler/inferred_tree.dag create mode 100644 src/v2/workflow/class_b_import_closure_probe.dag diff --git a/dag/test/claim/long/dissolution_census_witness_test.dag b/dag/test/claim/long/dissolution_census_witness_test.dag index 535b0a8979a..711a1f8a36c 100644 --- a/dag/test/claim/long/dissolution_census_witness_test.dag +++ b/dag/test/claim/long/dissolution_census_witness_test.dag @@ -14,6 +14,8 @@ import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly +data dissolution_census_cost_class_note: String = "unbound_dissolution_empty_literal_refuses (below) was investigated as a possible third import-closure-pollution exemplar for the witness-cost-class lane (gunbc.roadmap_authority adhoc-0e6f8ea5-158) and FALSIFIED -- NOT import-closure pollution, and not fixable by closure-narrowing. w_unbound_dissolution_empty_literal_red / w_unbound_dissolution_nonempty_literal_green both call compile_dag_rust_emit_check, whose uncached path (compile_dag_rust_emit_check_uncached, src/v1/stage0/src/cli_run.rs) rebuilds a FULL corpus-rooted module index (build_module_path_index_from_witness_roots) and compiles the supplied virtual source against it on every call -- a cost driven entirely by that helper's own corpus-wide work, unrelated to this witness ENTRY's own transitive import closure. The helper is memoized (COMPILE_DAG_RUST_EMIT_CHECK_MEMO) but the memo is only consulted when floor_prepared_inventory_digest() returns Some, i.e. only under a real claim_executor --required-floor run; a standalone claim run never populates or reads it. This produces a first-toucher shared-fill signature within the same module: whichever of this module's compile_dag_rust_emit_check-calling rows the floor visits first under a given inventory pays the uncached full rebuild, and its sibling(s) hit the warm memo -- confirmed against the floor's own [floor-witness-slow] line, 42129ms vs 825ms for two same-module sibling rows, a ~51x gap with no import-closure difference between them. This is the same underlying mechanism as the Class B and loop/bind falsifications in spirit (a fixed corpus-wide cost masquerading as an entry-closure cost) but a DIFFERENT mechanism in kind -- a per-call memo gated on the floor guard, not a witness entry's own static import graph -- so it belongs to the class-2 per-claim deterministic-reconstruction/memoization bucket, not this lane's closure-narrowing scope. No repair attempted here; this module is left as-is." + test fn census_counters_exhaustively_partition_the_rows() -> Bool { let r = dissolution_census_report() (r.unbound_count + r.pending_count + r.fired_still_present_count) == length(xs: census_closure_frontier_rows()) diff --git a/dag/test/claim/long/extdeps_scope_placement_gate_loudness_witness_test.dag b/dag/test/claim/long/extdeps_scope_placement_gate_loudness_witness_test.dag index 4b17f62d57d..fe85cc17905 100644 --- a/dag/test/claim/long/extdeps_scope_placement_gate_loudness_witness_test.dag +++ b/dag/test/claim/long/extdeps_scope_placement_gate_loudness_witness_test.dag @@ -17,7 +17,7 @@ import gunbc.test_module_hygiene { failure_receipt_companion } import std.process { ProcessExit, ExitSuccess } import std.types { String, Bool, List } -data loudness_witness_note: String = "Floor loudness for tools.extdeps_scope_placement_gate (gunbc#7644): lands the seed-runner loudness path + extdeps_scope_placement_gate production companion only; six other production _passes gates remain mute (counted frontier in tools.floor_effect_gate_witness floor_gate_failure_receipt_note). Per-arm fixtures exercise gate REASON GENERATION with synthetic observations (supporting evidence). Executed RED: red_seed_runner_failure_detail_projects_located_receipt calls production seed_runner_bool_false_failure_detail against seed_runner_loudness_probe_passes, an always-false _passes probe declared IN THIS ENTRY beside its _failure_receipt companion. THE PROBE PAIR MOVED HERE (2026-08-18) AND THAT MOVE IS THE POINT: claim_executor evaluates the derived companion in the WITNESS entry's context, so the pair has to be reachable from here — and reaching it by importing tools.floor_effect_gate_witness dragged that entry's whole gate closure (ci_gates, the compile-clean, generated-artifact and regen-verify gates) into a witness that asserts none of them. Measured import closure: 718 modules / 10.1 MB of source before, 62 modules / 0.49 MB after; the witness was the floor's second most expensive row at 78.6s. Nothing about the claim changed — the same production fn is called against the same located receipt text, and the probe pair is deleted from tools.floor_effect_gate_witness rather than duplicated. It is a func (not test fn), so floor discovery, which selects TestMarkedDecl only, does not enroll it. MUTATION CONTROL RE-EXECUTED AGAINST THE MOVED PAIR (2026-08-18, remote sandbox at 3abc17b54c9): renaming seed_runner_loudness_probe_failure_receipt so the companion derivation misses it makes this row FAIL (rc=1) while all seven rows of this entry pass at head (rc=0) — so the claim still discriminates on the production path after the move, and does not merely assert its own literal. Mutation control executed 2026-08-05 at b0a17f608a, when the probe pair still lived on tools.floor_effect_gate_witness: disposable detached worktree, removed _passes handling from production failure_receipt_companion (holds-only), rebuilt claim_batch, re-ran witness — FAIL red_seed_runner_failure_detail_projects_located_receipt (eval_self 0ms, resolved; not resolution); detail collapsed to bare returned Bool(false) without THIS CHANGE or dag/extdeps/browser/opera.dag. Current head PASS; head detail includes returned Bool(false) | extdeps scope placement gate: THIS CHANGE ... dag/extdeps/browser/opera.dag. Guarantee rung: observed validation on the production seed_runner path; no construction wall claimed for seed/.dag mirror parity (DESIGN section 7 seed retention)." +data loudness_witness_note: String = "Floor loudness for tools.extdeps_scope_placement_gate (gunbc#7644): lands the seed-runner loudness path + extdeps_scope_placement_gate production companion only; six other production _passes gates remain mute (counted frontier in tools.floor_effect_gate_witness floor_gate_failure_receipt_note). Per-arm fixtures exercise gate REASON GENERATION with synthetic observations (supporting evidence). Executed RED: red_seed_runner_failure_detail_projects_located_receipt calls production seed_runner_bool_false_failure_detail against seed_runner_loudness_probe_passes, an always-false _passes probe declared IN THIS ENTRY beside its _failure_receipt companion. THE PROBE PAIR MOVED HERE (2026-08-18) AND THAT MOVE IS THE POINT: claim_executor evaluates the derived companion in the WITNESS entry's context, so the pair has to be reachable from here — and reaching it by importing tools.floor_effect_gate_witness dragged that entry's whole gate closure (ci_gates, the compile-clean, generated-artifact and regen-verify gates) into a witness that asserts none of them. Measured import closure: 718 modules / 10.1 MB of source before, 62 modules / 0.49 MB after; the witness was the floor's second most expensive row at 78.6s. Nothing about the claim changed — the same production fn is called against the same located receipt text, and the probe pair is deleted from tools.floor_effect_gate_witness rather than duplicated. It is a func (not test fn), so floor discovery, which selects TestMarkedDecl only, does not enroll it. MUTATION CONTROL RE-EXECUTED AGAINST THE MOVED PAIR (2026-08-18, remote sandbox at 3abc17b54c9): renaming seed_runner_loudness_probe_failure_receipt so the companion derivation misses it makes this row FAIL (rc=1) while all seven rows of this entry pass at head (rc=0) — so the claim still discriminates on the production path after the move, and does not merely assert its own literal. Mutation control executed 2026-08-05 at b0a17f608a, when the probe pair still lived on tools.floor_effect_gate_witness: disposable detached worktree, removed _passes handling from production failure_receipt_companion (holds-only), rebuilt claim_batch, re-ran witness — FAIL red_seed_runner_failure_detail_projects_located_receipt (eval_self 0ms, resolved; not resolution); detail collapsed to bare returned Bool(false) without THIS CHANGE or dag/extdeps/browser/opera.dag. Current head PASS; head detail includes returned Bool(false) | extdeps scope placement gate: THIS CHANGE ... dag/extdeps/browser/opera.dag. Guarantee rung: observed validation on the production seed_runner path; no construction wall claimed for seed/.dag mirror parity (DESIGN section 7 seed retention). REMEASURED AGAINST THE FLOOR'S OWN LINE (witness-cost-class lane, gunbc.roadmap_authority adhoc-0e6f8ea5-158, 2026-08-19) AND FALSIFIED AS A WITNESS-COST REPAIR: the narrowing's own commit measured import closure (718 modules/10.1MB -> 62/0.49MB) and standalone entry-resolve time (30.726s -> 1.005s), but standalone resolve time is not the number the floor's ceiling judges. Run 32177951514 (head 59653f569e, which git merge-base --is-ancestor confirms is a descendant of this repair's ff56305b731) is the last floor observation of this identity before #8457 quarantined the module by name (test.claim.long. prefix) out of required_floor's discovery entirely -- there is no floor line for it after that quarantine, and none will exist until the module returns to floor scope. That last, post-repair line: [floor-witness-slow] red_seed_runner_failure_detail_projects_located_receipt 75926ms, BUDGET-REFUSED against the 1552ms ceiling (an exact measured cost, not a bound -- the witness ran to completion), and [floor-claim-memory] recorded this as the single worst claim in that run, growing RSS by 1.00GB to 10.12GB. 75926ms is statistically the same order as this identity's own PRE-repair floor history (42983-97260ms across four earlier CI runs, docs/plans/measurements/floor-slow-rows-2026-08-17.tsv) -- the 718-to-62-module import-closure narrowing left the floor cost effectively unchanged. EXEMPLAR 1 IS THEREFORE UNCONFIRMED, joining the Class B and loop/bind candidates as this lane's third falsification: import-closure pollution, as a witness-cost class with a demonstrated fixable member, tested empty across all three candidates this lane investigated. What is NOT retracted: the closure narrowing itself is real and DESIGN.md §3-legitimate (fewer modules genuinely load faster in isolation), it is retained as a correctness improvement, and it may have cut cold standalone resolve time as measured -- what is refuted is that it moved the FLOOR's own per-row cost, which is the only number the 1552ms ceiling enforces. Same shape as this lane's Class B correction: the right repair, the wrong quantity measured. All three falsifications point at the same underlying object: a fixed, corpus-wide load/index cost paid once per MultiEntryIndex rather than once per entry's import closure (DESIGN.md's floor-shared-compute-memoization open thread on build_both_closure_edge_index, whose closure-size-independence claim this lane's measurements corroborate rather than re-derive)." fn read_manifest_rows() -> List { parse_frontier_manifest( diff --git a/dag/test/claim/long/rust_test_fixtures_import_closure_witness_test.dag b/dag/test/claim/long/rust_test_fixtures_import_closure_witness_test.dag index ec9fe79da76..7f50a35e92f 100644 --- a/dag/test/claim/long/rust_test_fixtures_import_closure_witness_test.dag +++ b/dag/test/claim/long/rust_test_fixtures_import_closure_witness_test.dag @@ -1,12 +1,12 @@ module test.claim.long.rust_test_fixtures_import_closure_witness -import v2.workflow.class_b_import_closure_transport { +import v2.workflow.class_b_import_closure_probe { class_b_declared_import_closure_lists_rust_import, class_b_ambient_pool_provider_preserves_listed_import } import std.types { Bool } -data class_b_witness_note: String = "Rows 1-2 of the Class B rust_test_fixtures control (#6985), delegated to v2.workflow.class_b_import_closure_transport for single authority. QUARANTINED, not path-excluded: this module is excluded from the required floor by its AUTHORED NAME — required_floor.dag matches long_home_prefixes against the module name, and test.claim.long. is one — so the exclusion is the rename #8457 performed, and the directory is for humans. The FalsifierSubstrateLongLane WitnessExclusionRow that this note once cited is deleted with it: the required floor never consulted witness_exclusion_frontier at all (floor_discovery_path_excluded is reached only from claim_batch and a cfg(test) module), and the falsifier cadence those long-lane rows enrolled was deleted outright in the floor cut, so neither mechanism had an executing consumer. WHY IT IS HERE: both rows PASS and exceed the 1552ms ceiling (Cpu 25816ms and 36536ms, run 32179249889), so they landed STALE-QUARANTINE plus BUDGET-REFUSED and no enrollment setting clears a row that is true on both axes. The rows are NOT executing anywhere while they sit here — that coverage loss is #8457's declared bounded rung drop, and it restores when the cost is fixed. Root cause is what class_b_import_closure_transport reaches for, not this file; splitting a witness to move it under the ceiling is refused as a measurement artifact (gunbc.witness_row_cost witness_decomposition_does_not_reduce_entry_cost_note). Rows 3-4 stay in class_b_import_closure_binding_refusal_witness_test.dag and class_b_strip_receipt_witness_test.dag; wet integration remains run_class_b_import_closure_gate on source_root_ingest_gate." +data class_b_witness_note: String = "Rows 1-2 of the Class B rust_test_fixtures control (#6985), delegated to v2.workflow.class_b_import_closure_probe for single authority. QUARANTINED, not path-excluded: this module is excluded from the required floor by its AUTHORED NAME — required_floor.dag matches long_home_prefixes against the module name, and test.claim.long. is one — so the exclusion is the rename #8457 performed, and the directory is for humans. The FalsifierSubstrateLongLane WitnessExclusionRow that this note once cited is deleted with it: the required floor never consulted witness_exclusion_frontier at all (floor_discovery_path_excluded is reached only from claim_batch and a cfg(test) module), and the falsifier cadence those long-lane rows enrolled was deleted outright in the floor cut, so neither mechanism had an executing consumer. WHY IT WAS HERE: both rows PASSED and exceeded the 1552ms ceiling (Cpu 25816ms and 36536ms, run 32179249889) while this file imported v2.workflow.class_b_import_closure_transport, whose module-level extdeps.git / extdeps.git.inspect / gunbc.ci_layer_roots imports (needed only by that module's separate rows 3-4) inflated this entry's resolve closure — the same signature extdeps_scope_placement_gate_loudness_witness's 718-to-62-module repair fixed. This module now imports v2.workflow.class_b_import_closure_probe instead, which carries only what rows 1-2 need (gunbc.class_b_import_closure_overlay's class_b_entry_rel/class_b_declared_import_pool_roots and gunbc.declared_import_closure_binding), never the heavy transport module. The narrowing does not change what class_b_declared_import_closure_lists_rust_import/class_b_ambient_pool_provider_preserves_listed_import compute — same fn bodies, same DeclaredImportClosureBindingObservation calls, only the entry's own import graph shrank. Splitting a witness's own test file to dodge the ceiling is refused as a measurement artifact (gunbc.witness_row_cost witness_decomposition_does_not_reduce_entry_cost_note); this is not that — the fix narrows what the SAME entry's assertions actually depend on, by fixing the imported dependency's shape (relocation-not-refork per DESIGN.md §3), not by moving these test fns to a new file. Rows 3-4 stay in class_b_import_closure_binding_refusal_witness_test.dag and class_b_strip_receipt_witness_test.dag; wet integration remains run_class_b_import_closure_gate on source_root_ingest_gate. MEASURED 2026-08-19, and the result is NEGATIVE — this narrowing is FALSIFIED as an import-closure-pollution repair, not confirmed. The two-sided test (a paired same-host comparison, per the standing acceptance bar: cost must collapse when the closure narrows AND not collapse under mere repetition) was run via gunbc run --claim-run against a fresh release build in one remote dispatch: declared_import_closure_lists_rust_import through this narrow probe-backed entry measured 70.0s and 69.4s wall (two independent process calls, back to back); the SAME function reached through v2.workflow.class_b_import_closure_transport directly (the heavy module, still carrying its module-level extdeps.git / extdeps.git.inspect / gunbc.ci_layer_roots imports) measured 69.98s in the identical dispatch, on the identical host, moments apart. Narrow and heavy are statistically indistinguishable. ambient_pool_provider_preserves_listed_import (which itself calls the observe function twice in one process) measured 80.8s, consistent with the same fixed floor rather than a per-import-closure charge. CORRECTED MAGNITUDE (per-row floor line, supplied against the required floor's own [floor-witness-slow] output for these same two identities, not the standalone harness): declared_import_closure_lists_rust_import 24674ms and ambient_pool_provider_preserves_listed_import 31603ms -- the standalone --claim-run figures above (70.0s/80.8s) are ~2-3x inflated relative to the floor's own amortized accounting, because a standalone claim run pays the corpus-wide load/index term once per claim while the floor pays that same fixed term once per MultiEntryIndex, amortized across the whole discovered roster (see DESIGN.md's floor-shared-compute-memoization open thread on build_both_closure_edge_index, whose central size-independence claim these figures corroborate on a different subject rather than re-derive). The paired-comparison DESIGN of the standalone measurement is still the right instrument for detecting a closure-size effect (narrow vs heavy, same process shape) -- only its absolute magnitude needs the floor's own line as the citable number. Both the standalone pair and the floor's per-row numbers agree on the same verdict: narrow and heavy did not diverge, and both sit far over the 1552ms ceiling. None of these approach the printed compile.frontend/normalize/reconcile/analyses phase timings (~1-2s total each), meaning the ~25-32s floor-line cost is spent somewhere the entry-closure resolution does not reach -- most plausibly the corpus-wide load/index cost named above. CONCLUSION: the module split (v2.workflow.class_b_import_closure_probe) is a legitimate DESIGN.md §3 relocation-not-refork decomposition and stays -- rows 1-2's shape genuinely does not need git/git.inspect/ci_layer_roots -- but it is NOT a witness-cost fix and this file does NOT leave long/ on its account; 24.7-31.6s (floor line) / 70-80s (standalone) both remain far over the 1552ms ceiling. This is the second candidate this lane has falsified as import-closure pollution (after the loop/bind witnesses), not confirmed as a second exemplar; see the lane's PR description for the full delta." test fn declared_import_closure_lists_rust_import() -> Bool { class_b_declared_import_closure_lists_rust_import() diff --git a/src/v1/stage0/src/cli_run.rs b/src/v1/stage0/src/cli_run.rs index f1b711fd9cd..ea20dc1b4b0 100644 --- a/src/v1/stage0/src/cli_run.rs +++ b/src/v1/stage0/src/cli_run.rs @@ -3821,6 +3821,12 @@ pub fn regen_floor_skip_label_for_ci() -> String { pub const CLASS_B_ENTRY_REL: &str = "src/v2/extdeps/languages/rust_test_fixtures.dag"; pub const CLASS_B_TRANSPORT_REL: &str = "src/v2/workflow/class_b_import_closure_transport.dag"; +/// Rows 1-2's shape+call, relocated out of CLASS_B_TRANSPORT_REL so a witness reaching only +/// rows 1-2 does not inherit the transport module's extdeps.git / extdeps.git.inspect / +/// gunbc.ci_layer_roots closure (DESIGN.md §3 relocation-not-refork; the same signature +/// extdeps_scope_placement_gate_loudness_witness's repair fixed). The transport module imports +/// this one back for rows 1-2's own use, so it is part of the gate's input closure too. +pub const CLASS_B_PROBE_REL: &str = "src/v2/workflow/class_b_import_closure_probe.dag"; pub const CLASS_B_BINDING_REL: &str = "dag/gunbc/declared_import_closure_binding.dag"; pub const CLASS_B_OVERLAY_REL: &str = "dag/gunbc/class_b_import_closure_overlay.dag"; pub const CLASS_B_FIXTURES_PREFIX: &str = "fixtures/class_b_import_closure"; @@ -3829,6 +3835,7 @@ const CLASS_B_DECLARED_POOL_ROOTS_DATA_NAME: &str = "class_b_declared_import_poo pub const CLASS_B_GATE_INPUT_ENTRIES: &[&str] = &[ CLASS_B_ENTRY_REL, CLASS_B_TRANSPORT_REL, + CLASS_B_PROBE_REL, CLASS_B_BINDING_REL, CLASS_B_OVERLAY_REL, ]; diff --git a/src/v2/compiler/04_infer.dag b/src/v2/compiler/04_infer.dag index 1d387918c1c..18360abeecb 100644 --- a/src/v2/compiler/04_infer.dag +++ b/src/v2/compiler/04_infer.dag @@ -11,6 +11,14 @@ import v2.std.compilers.target_model { canonical_operation_wire_matches_operation } import v2.compiler.resolve { ResolvedTree } +import v2.compiler.inferred_tree { + DerivedGrounding, + GroundingNotDerived, + InferredFacts, + InferredFactsEntry, + InferredTree, + NodeGrounding +} import v2.extdeps.languages.dag { DagCanonicalBoolLiteral, DagCanonicalIntLiteral, @@ -92,15 +100,6 @@ type AlgebraRef { data node_grounding_frontier_note: String = "GroundingNotDerived is v2's typed inference frontier, not a failure arm and not an escape hatch. v2 derives a node's type for exactly four of the closed vocabulary's twelve node kinds (Branch, Match, Loop, Transform add-shape only — 6 connectives + 6 behaviors = 12); the other eight kinds, and every other Transform shape, have no derivation rule yet. Before this carrier existed the nine were routed through solve_constraints with the singleton candidate set [root] under a predicate that reduces to root == root, and the resulting grounding carried the SOURCE NODE as its structural evidence — i.e. as the node's resolved type (inferred_facts_resolved_type reads that field), so every literal, binding and operation in v2 was typed as itself and every downstream consumer read that as an established judgment. Each frontier node now carries a typed, located, counted diagnostic (infer_grounding_not_derived) on the Accepted path, so the deficit's frequency is observable and prioritizable rather than zeroed by construction (DESIGN §5). dissolve-on: a behavior-specific derivation rule lands for a kind, at which point that kind moves to DerivedGrounding and its frontier count drops; the carrier deletes when all twelve kinds derive." -type NodeGrounding - = DerivedGrounding { grounding: CanonicalGrounding } - | GroundingNotDerived { node: Node } - -type InferredFacts { - grounding: NodeGrounding - descent: Witness -} - fn infer_grounding_not_derived_diagnostic(at: Locus) -> Diagnostic { Diagnostic { reason: ^infer_grounding_not_derived, @@ -171,11 +170,6 @@ fn inferred_facts_descent(facts: InferredFacts) -> Witness -} - data infer_emit_shape_frontier_note: String = "Test-only emit-shape frontier (review 47246): empty facts map for orch-emit-compatible mock proofs (`orch_emit_facts_lookup`, `ingested_emit_shape_frontier_tree_from_source` in emit-host witnesses). Not a production carrier — production emit paths pass canonical infer output facts-flow-forward; erasing or relabeling infer judgments is forbidden (review 47234, review 47246). dissolve-on: v2-inferred-tree-completeness makes incomplete InferredTree unwritable on the production emit path." fn infer_emit_shape_frontier_inferred_tree(root: Node) -> InferredTree { @@ -189,11 +183,6 @@ fn infer_emit_shape_frontier_inferred_tree(root: Node) -> InferredTree { } } -type InferredFactsEntry { - node: Node - facts: InferredFacts -} - type InferGatherFoldAcc { node: Node entries: List diff --git a/src/v2/compiler/inferred_tree.dag b/src/v2/compiler/inferred_tree.dag new file mode 100644 index 00000000000..0e20caefc06 --- /dev/null +++ b/src/v2/compiler/inferred_tree.dag @@ -0,0 +1,40 @@ +module v2.compiler.inferred_tree + +import v2.std.cardinality { TerminationProof } +import v2.std.collection { Map } +import v2.std.constraints { CanonicalGrounding } +import v2.std.node { Node } +import v2.std.witness { Witness } + +// InferredFacts/InferredTree/InferredFactsEntry are the SHAPE infer's pass +// produces and consumers hold -- not the pass itself. They lived inside +// v2.compiler.infer (the 04_infer.dag fold implementation), which imports +// v2.compiler.resolve for its own logic; any consumer that wanted only these +// record shapes (to hand-construct a fixture InferredTree, never calling an +// infer-stage fold) inherited that resolve coupling for free, inflating its +// import closure past what it actually uses -- exactly the module-grain +// type-vs-fold conflation witness_cost_locality_precision_frontier names as +// a known LayerDepth false-positive source (src/v2/lens/witness_cost_locality.dag). +// Split out per the v2.compiler.normalized_tree precedent (03_resolve.dag's +// pass is v2.compiler.resolve; ResolvedTree's own shape carrier is separate). +// v2.compiler.infer imports these back; this is a relocation of one +// authority, never a second declaration of it (DESIGN.md §3). + +type NodeGrounding + = DerivedGrounding { grounding: CanonicalGrounding } + | GroundingNotDerived { node: Node } + +type InferredFacts { + grounding: NodeGrounding + descent: Witness +} + +type InferredTree { + root: Node + facts: Map +} + +type InferredFactsEntry { + node: Node + facts: InferredFacts +} diff --git a/src/v2/test/claim/execution/long/bind_eval_by_execution_test.dag b/src/v2/test/claim/execution/long/bind_eval_by_execution_test.dag index 36976d72ae1..0935eddec41 100644 --- a/src/v2/test/claim/execution/long/bind_eval_by_execution_test.dag +++ b/src/v2/test/claim/execution/long/bind_eval_by_execution_test.dag @@ -19,7 +19,7 @@ import v2.compiler.eval { test_claim_run_verdict, test_claim_typed_input_node } -import v2.compiler.infer { InferredFacts, InferredTree } +import v2.compiler.inferred_tree { InferredFacts, InferredTree } import v2.std.collection { List, Map, diff --git a/src/v2/test/claim/execution/long/loop_eval_by_execution_test.dag b/src/v2/test/claim/execution/long/loop_eval_by_execution_test.dag index 38fddae0d6f..1ee7a4f495b 100644 --- a/src/v2/test/claim/execution/long/loop_eval_by_execution_test.dag +++ b/src/v2/test/claim/execution/long/loop_eval_by_execution_test.dag @@ -19,7 +19,7 @@ import v2.compiler.eval { test_claim_run_verdict, test_claim_typed_input_node } -import v2.compiler.infer { InferredFacts, InferredTree } +import v2.compiler.inferred_tree { InferredFacts, InferredTree } import v2.std.collection { List, Map, diff --git a/src/v2/workflow/class_b_import_closure_probe.dag b/src/v2/workflow/class_b_import_closure_probe.dag new file mode 100644 index 00000000000..cd116f4f1db --- /dev/null +++ b/src/v2/workflow/class_b_import_closure_probe.dag @@ -0,0 +1,44 @@ +module v2.workflow.class_b_import_closure_probe + +import gunbc.class_b_import_closure_overlay { + class_b_entry_rel, + class_b_declared_import_pool_roots +} +import gunbc.declared_import_closure_binding { + DeclaredImportClosureBindingObservation, + binding_source_is_listed_import +} +import std.logic { Bool } +import std.types { String, List } + +data class_b_probe_note: String = "Rows 1-2 of the Class B rust_test_fixtures control (#6985) -- the SHAPE and single call rows 1-2 need, split out of v2.workflow.class_b_import_closure_transport per the same relocation-not-refork discipline as v2.compiler.inferred_tree (DESIGN.md §3). class_b_import_closure_transport also imports extdeps.git, extdeps.git.inspect and gunbc.ci_layer_roots at module level, but ONLY its rows 3-4 (class_b_observe_with_derived_stripped_overlay and callers) ever touch them -- rows 1-2 use only class_b_declared_import_pool_roots (class_b_import_closure_transport_note already says so). Because a witness entry's resolve context is driven by its ENTRY MODULE's whole transitive import closure, not by which functions its test bodies actually call, rust_test_fixtures_import_closure_witness_test.dag inherited git/git.inspect/ci_layer_roots's closures for free every time it imported the transport module to reach these two functions -- the same signature as extdeps_scope_placement_gate_loudness_witness's 718-to-62-module repair. class_b_import_closure_transport imports this module back for rows 1-2's own use (the relocated authority, not a second declaration of it); rows 3-4 and the gate stay on the heavy module, which is where their own git/shell/ci_layer_roots need actually lives. MEASURED 2026-08-19: this narrowing does NOT reduce witness wall-clock cost for rows 1-2 (a same-dispatch, same-host paired comparison found this narrow entry statistically indistinguishable from the heavy transport entry, ~70s both standalone; the floor's own [floor-witness-slow] line agrees at 24674ms/31603ms, ~2-3x lower than the standalone figure because the floor amortizes its corpus-wide load term once per MultiEntryIndex rather than once per claim, but still shows no narrow-vs-heavy divergence) -- neither figure is attributable to entry-closure resolution at all, so this split is retained purely as a DESIGN.md §3 dependency-correctness improvement, not as a witness-cost repair; see class_b_import_closure_transport's class_b_transport_note and the witness's class_b_witness_note for the full measurement." + +data class_b_consumer_module: String = "v2.extdeps.languages.rust_test" +data class_b_symbol: String = "rust_selection_policy_node" +data class_b_perturbation_overlay_rel: String = "fixtures/class_b_import_closure/perturbation_overlay" + +fn class_b_observe_with_overlay( + pool_roots: List +) -> DeclaredImportClosureBindingObservation { + observe_declared_import_closure_symbol_binding( + pool_roots: pool_roots, + entry_path: class_b_entry_rel, + consumer_module: class_b_consumer_module, + symbol: class_b_symbol + ) +} + +func class_b_declared_import_closure_lists_rust_import() -> Bool { + binding_source_is_listed_import( + obs: class_b_observe_with_overlay(pool_roots: class_b_declared_import_pool_roots) + ) +} + +func class_b_ambient_pool_provider_preserves_listed_import() -> Bool { + let baseline = class_b_observe_with_overlay(pool_roots: class_b_declared_import_pool_roots) + let perturbed = class_b_observe_with_overlay( + pool_roots: concat([class_b_perturbation_overlay_rel], class_b_declared_import_pool_roots) + ) + binding_source_is_listed_import(obs: baseline) + && binding_source_is_listed_import(obs: perturbed) +} diff --git a/src/v2/workflow/class_b_import_closure_transport.dag b/src/v2/workflow/class_b_import_closure_transport.dag index 944928e33ea..4eadeb36939 100644 --- a/src/v2/workflow/class_b_import_closure_transport.dag +++ b/src/v2/workflow/class_b_import_closure_transport.dag @@ -5,7 +5,6 @@ import extdeps.git.inspect import extdeps.shell import gunbc.ci_layer_roots { witness_layer_roots } import gunbc.class_b_import_closure_overlay { - class_b_entry_rel, class_b_materialize_stripped_entry_overlay, class_b_declared_import_pool_roots, OverlayMaterialized, @@ -17,31 +16,22 @@ import gunbc.declared_import_closure_binding { BindingNotRunnable, ObservationProduced, ObservationProductionRefused, - binding_source_is_listed_import, row3_stripped_refuses_binding, row4_stripped_pool_coincidence_or_refuse } +import v2.workflow.class_b_import_closure_probe { + class_b_perturbation_overlay_rel, + class_b_observe_with_overlay, + class_b_declared_import_closure_lists_rust_import, + class_b_ambient_pool_provider_preserves_listed_import +} import std.logic { Bool } import std.types { String, List, NonEmptyStr } -data class_b_transport_note: String = "Class B import-closure transport (#6985). All four rows are ordinary funcs on this module. NO REQUIRED-FLOOR CONSUMER TODAY: rows 1-2 sit in test.claim.long.rust_test_fixtures_import_closure_witness, quarantined by #8457 because they PASS over the 1552ms ceiling (Cpu 25816ms and 36536ms); required_floor.dag excludes them by authored module name via long_home_prefixes. Their FalsifierSubstrateLongLane exclusion and long-lane enrollment rows are deleted (2026-08-18): the floor never consulted witness_exclusion_frontier, and the falsifier cadence died in the floor cut, so neither had an executing consumer. Rows 1-2 are unwitnessed until the cost is fixed and they leave long/. WET INTEGRATION CONSUMER (not on required floor yet): run_class_b_import_closure_gate on source_root_ingest_gate_passes — rows 1-4 together when that gate is re-enrolled on claim_executor --required-floor. Rows 1-2 use class_b_declared_import_pool_roots (minimal declared-import footprint). Rows 3-4 use witness_layer_roots for stripped-entry pool-coincidence pressure. Rows 3-4 accept ONLY ObservationProduced carrying a successfully ingested BindingObserved outcome; every ObservationProductionRefused (overlay read/strip/mkdir/mktemp/write/cleanup/compile) reds the gate — compile graph:None is P1(b), not a seam refusal. Predicate unit controls stay in class_b_import_closure_binding_refusal_witness_test.dag and class_b_strip_receipt_witness_test.dag. Affected-set skip: class_b_import_closure_gate_not_affected_skip (cli_run::class_b_import_closure_gate_skip_label_for_ci) skips the ~2.3min gate on pull_request when the merge-base diff is provably disjoint from the gate input closure; empty diff / departed non-docs / observation or closure failure runs the full gate (fail-closed); push-to-main always runs the full gate as cold control." +data class_b_transport_note: String = "Class B import-closure transport (#6985). All four rows are ordinary funcs on this module or its v2.workflow.class_b_import_closure_probe companion. NO REQUIRED-FLOOR CONSUMER TODAY: rows 1-2 sit in test.claim.long.rust_test_fixtures_import_closure_witness, quarantined by #8457 because they PASS over the 1552ms ceiling (Cpu 25816ms and 36536ms); required_floor.dag excludes them by authored module name via long_home_prefixes. Their FalsifierSubstrateLongLane exclusion and long-lane enrollment rows are deleted (2026-08-18): the floor never consulted witness_exclusion_frontier, and the falsifier cadence died in the floor cut, so neither had an executing consumer. Rows 1-2 SHAPE AND CALL are relocated to class_b_import_closure_probe (this module imports them back) because their own resolve closure does not need extdeps.git / extdeps.git.inspect / gunbc.ci_layer_roots -- only rows 3-4 below do, for stripped-entry pool-coincidence pressure -- and a witness entry's resolve context is driven by its whole transitive import closure, not by which of a module's functions its test bodies call (the same signature extdeps_scope_placement_gate_loudness_witness's 718-to-62-module repair fixed). Rows 1-2's shape/call were relocated for genuine dependency-narrowing reasons (relocation-not-refork, DESIGN.md §3) but the relocation was MEASURED 2026-08-19 to NOT reduce witness wall-clock cost: a paired same-dispatch, same-host comparison found declared_import_closure_lists_rust_import through the narrow probe entry (70.0s/69.4s) statistically indistinguishable from the identical function reached directly through this heavy module (69.98s) -- see rust_test_fixtures_import_closure_witness_test.dag's class_b_witness_note for the full measurement. The floor's own [floor-witness-slow] line for these same two identities (24674ms declared_import_closure_lists_rust_import, 31603ms ambient_pool_provider_preserves_listed_import) is the citable magnitude -- the standalone 70.0-80.8s figures above are ~2-3x inflated because a standalone claim run pays the corpus-wide load/index term once per claim rather than once per MultiEntryIndex, but both sources agree narrow and heavy did not diverge. Neither figure is attributable to entry-closure resolution (compile.frontend/normalize/reconcile/analyses total ~1-2s in the same run) and most plausibly reflects a fixed corpus-wide load cost independent of entry closure size (see DESIGN.md's floor-shared-compute-memoization open thread on build_both_closure_edge_index). Rows 1-2 therefore stay in long/, and this split is retained as a correctness/modeling improvement, not claimed as a witness-cost repair. WET INTEGRATION CONSUMER (not on required floor yet): run_class_b_import_closure_gate on source_root_ingest_gate_passes — rows 1-4 together when that gate is re-enrolled on claim_executor --required-floor. Rows 1-2 use class_b_declared_import_pool_roots (minimal declared-import footprint). Rows 3-4 use witness_layer_roots for stripped-entry pool-coincidence pressure. Rows 3-4 accept ONLY ObservationProduced carrying a successfully ingested BindingObserved outcome; every ObservationProductionRefused (overlay read/strip/mkdir/mktemp/write/cleanup/compile) reds the gate — compile graph:None is P1(b), not a seam refusal. Predicate unit controls stay in class_b_import_closure_binding_refusal_witness_test.dag and class_b_strip_receipt_witness_test.dag. Affected-set skip: class_b_import_closure_gate_not_affected_skip (cli_run::class_b_import_closure_gate_skip_label_for_ci) skips the ~2.3min gate on pull_request when the merge-base diff is provably disjoint from the gate input closure; empty diff / departed non-docs / observation or closure failure runs the full gate (fail-closed); push-to-main always runs the full gate as cold control." -data class_b_consumer_module: String = "v2.extdeps.languages.rust_test" -data class_b_symbol: String = "rust_selection_policy_node" -data class_b_perturbation_overlay_rel: String = "fixtures/class_b_import_closure/perturbation_overlay" data class_b_overlay_template: String = "/target/class_b_stripped_XXXXXX" -fn class_b_observe_with_overlay( - pool_roots: List -) -> DeclaredImportClosureBindingObservation { - observe_declared_import_closure_symbol_binding( - pool_roots: pool_roots, - entry_path: class_b_entry_rel, - consumer_module: class_b_consumer_module, - symbol: class_b_symbol - ) -} - fn class_b_outcome_from_observation( obs: DeclaredImportClosureBindingObservation ) -> DeclaredImportClosureBindingObservationOutcome { @@ -54,21 +44,6 @@ fn class_b_outcome_from_observation( } } -func class_b_declared_import_closure_lists_rust_import() -> Bool { - binding_source_is_listed_import( - obs: class_b_observe_with_overlay(pool_roots: class_b_declared_import_pool_roots) - ) -} - -func class_b_ambient_pool_provider_preserves_listed_import() -> Bool { - let baseline = class_b_observe_with_overlay(pool_roots: class_b_declared_import_pool_roots) - let perturbed = class_b_observe_with_overlay( - pool_roots: concat([class_b_perturbation_overlay_rel], class_b_declared_import_pool_roots) - ) - binding_source_is_listed_import(obs: baseline) - && binding_source_is_listed_import(obs: perturbed) -} - func class_b_observe_with_derived_stripped_overlay( include_perturbation: Bool ) -> DeclaredImportClosureBindingObservationOutcome {