diff --git a/DESIGN.md b/DESIGN.md index d3c4256fac0..6eaffd31939 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -174,4 +174,4 @@ hollow alias (minimality ≠ grounding) · state-space conflation (an `Option`/` - `cargo test --workspace` · `cargo clippy --all-targets -- -D warnings` · `cargo fmt --all --check` - one-time per clone: `git config core.hooksPath .githooks` — the only documented manual seed; generated pre-commit/pre-push hooks then idempotently converge `merge.generated-artifact.driver` and re-assert `core.hooksPath` via argv derived from `gunbc.repo_local_git_config` (clones that skip hooksPath degrade to vanilla text-merge for generated-artifact paths; drift gate still guards at CI). The driver REFUSES rather than answering `true`: git reaches a low-level merge driver only when both sides changed the path since the merge base — measured on a four-case matrix, one-sided and identical changes never reach it — and taking the ours side there dropped the other side's authority-derived bytes with no conflict, twice on #7836 against the stage0 seed. It now leaves the ours side in the worktree with no conflict markers, marks the path unmerged, and prints the regeneration recipe; the class is mechanically preventable, not structural, and its next-rung trigger is the commit-writer binding rows in `gunbc.commit_workflow` - explicit actuator (CI / tooling): `gunbc run --source-root dag --source-root src/v2 --entry dag/gunbc/repo_local_git_config.dag --function converge` -- CI (`gunbc ci` generates `.github/workflows/ci.yml`; all `v1-compiler` seed bins, no shell gate): **one** composed floor pass — `claim_executor --source-root src/v2 --source-root dag --plan-entry src/v2/workflow/ci_floor_plan.dag --plan-function gunbc_ci_floor_plan`. The v2 scheduler decides the batches from the single-authority spec (`gunbc.ci_spec`); the only structural fact the plan adds is *compile-clean gates the rest* (batch-1 `dag_compile_clean_gate` → batch-2 everything else, a dependency edge). Witness enrollment: **discovery shrunk by the affected set** (operator acceptance 2026-07-09, firing the 2026-07-04 opt-in inversion's dissolve-on — see `gunbc.ci_spec` `ci_spec_discovery_flip_note`): the corpus batch scans `CiSpec.discovery_scan_dirs` (plus the source-root `*_test.dag` walk) with `SelectionApplied`, so a PR runs the tree-wide witness corpus (~1,721 rows at flip time) shrunk to the diff's affected set; selection is fail-closed (a provenance gap refuses, never widens; host-scaffold/live-tree rows never predict-skip) and the scheduled `affected-set-falsifier` (every 4 hours) runs the corpus cold with predictions recorded, so a missing selection edge surfaces as a counted divergence within one cadence window. `CiSpec.witness_entries` (`CommitWitnessClaim` rows on the `GithubActionsCiJob` surface, projected by `project_ci_floor_witness_entries`) remains the explicit-entry roster — execution-kind rows with their own resource profile, and any row that must run as declared. Empty entries AND empty discovery dirs = zero witness-corpus nodes (the regen spec's shape). Naming hygiene stays fail-closed (a `test fn` outside `*_test.dag` is still a violation) but is **demand-directed, not unconditional** (#8140, 2026-08-11, superseding the prior clause 'the executor runs the zero-enrollment naming walk when no discovery batch is scheduled'): `claim_executor` ran the whole-source-root roster walk BEFORE resolving the plan on every invocation, on the stated ground that a naming violation should be 'the cheapest possible failure'; measured, it was the most expensive phase in the process — 5.9 min of a 56.5-min ordinary floor (run 31477894666) and ~6 min of a ~15-min regen whose plan has two nodes and no roster — because the producer it reaches also builds module-graph facts, runs a second strict reference-resolution pass, and runs inert-lens reachability plus the construction-justification census. The walk now runs where the demand is structural (`schedules_discovery`: the plan carries a discovery or scoped-witness batch), at unchanged whole-source-root scope and unchanged cost — the roster is memoized by request digest, so the corpus batch hits the memo this call fills, and the coordinator's expected pre-plan digest is byte-identical. TWO CONSEQUENCES, both deliberate. (a) SCOPE: discovery-free plans (regen, plan-artifact) no longer run the corpus-wide nameability rules at all. Per-PR coverage is unchanged because every PR runs the `ci` job, whose plan schedules discovery and walks the full tree; what is lost is a *second* redundant walk on regen-only and plan-artifact-only runs. This is a scope narrowing declared here rather than silently taken, and it is NOT backstopped by the affected-set falsifier — that cadence has produced no green verdict since 2026-08-03. (b) ORDERING: for plans that DO schedule discovery the walk now runs after plan resolve/eval, since batches are known only then, so a naming violation pays full plan resolution (~0.5 min) before refusing. The 'cheapest possible failure' ordering is knowingly traded for deleting the same walk from every plan that never needed it; the trade is ~0.5 min later on the refusing path against ~6 min saved on every regen. Dissolve-on: the placement rules move to canonical source ingestion and test identities derive from parser-produced declarations, at which point the separate walk, this clause, and the `__`-basename rule all retire. Tree-wide unselected discovery remains the **local** path (`claim_batch --roster-from-discovery --source-root dag --source-root src/v2 --scan-dir dag/test/claim --scan-dir src/v2/test/claim/manual`). Batch-2 also carries the effectful gates: the rust fmt gate (when a `.rs` changes; nextest was removed from CI 2026-07-11 — operator ruling recorded in `gunbc.commit_workflow` `commit_gate_rust_suite_removed_disposition`, the suite runs locally only; clippy removed from CI 2026-07-08 — crate-wide `#![allow(clippy::all)]` made it zero-signal over ~44m/run, still available as a local dev check), emit-host MVP smokes, source-root-ingest, and the `ci.yml` drift+parse gate (ci.yml == `gunbc ci` output). The compile-clean gate is `--target dag`, and its per-PR scope is the same import-closure authority (2026-07-16, channel 2 of the 2026-07-10 grain fork): an all-`.dag`/docs diff compiles only the affected shard-entry closures (`tools.dag_compile_clean_scope`, host fast path `entry_file_touched_via_import_closure`); any non-selectable touched path (`.rs`, workflow yml, manifests), any non-docs departed path, or any selection refusal keeps the whole-tree baseline, loudly; the falsifier cadence carries the deterministic whole-tree cold control (`GUNBC_CI_COMPILE_CLEAN_COLD_CONTROL=1`, widen-only). The regen step (self-host fixed-point) is scoped the same way at its own closure (#6732 + this change's departed-path guard): `regen_floor_skip_witness` skips the pull_request step only when a non-empty merge-base diff is provably disjoint from the regen input set (`cli_run::regen_input_sources` — the SAME closure authority `regen_stage0` compiles — plus the src/v1/** prefix and Cargo/toolchain config); empty diffs, departed non-docs paths, and every failure arm run regen, and the skip is shell-gated to pull_request events so main pushes stay the unconditional cold control. Parse is grammar-owned: `ingest_yaml_source` (`dag/extdeps/languages/yaml/ingest.dag`), no shell/host parser. +- CI (`gunbc ci` generates `.github/workflows/ci.yml`; all `v1-compiler` seed bins, no shell gate): **one** composed floor pass — `claim_executor --source-root src/v2 --source-root dag --plan-entry src/v2/workflow/ci_floor_plan.dag --plan-function gunbc_ci_floor_plan`. The v2 scheduler decides the batches from the single-authority spec (`gunbc.ci_spec`); the only structural fact the plan adds is *compile-clean gates the rest* (batch-1 `dag_compile_clean_gate` → batch-2 everything else, a dependency edge). Witness enrollment: **discovery shrunk by the affected set** (operator acceptance 2026-07-09, firing the 2026-07-04 opt-in inversion's dissolve-on — see `gunbc.ci_spec` `ci_spec_discovery_flip_note`): the corpus batch scans `CiSpec.discovery_scan_dirs` (plus the source-root `*_test.dag` walk) with `SelectionApplied`, so a PR runs the tree-wide witness corpus (~1,721 rows at flip time) shrunk to the diff's affected set; selection is fail-closed (a provenance gap refuses, never widens; host-scaffold/live-tree rows never predict-skip) and the scheduled `affected-set-falsifier` (every 4 hours) runs the corpus cold with predictions recorded, so a missing selection edge surfaces as a counted divergence within one cadence window. `CiSpec.witness_entries` (`CommitWitnessClaim` rows on the `GithubActionsCiJob` surface, projected by `project_ci_floor_witness_entries`) remains the explicit-entry roster — execution-kind rows with their own resource profile, and any row that must run as declared. Empty entries AND empty discovery dirs = zero witness-corpus nodes (the regen spec's shape). Naming hygiene stays fail-closed (a `test fn` outside `*_test.dag` is still a violation) but is **demand-directed, not unconditional** (#8140, 2026-08-11, superseding the prior clause 'the executor runs the zero-enrollment naming walk when no discovery batch is scheduled'): `claim_executor` ran the whole-source-root roster walk BEFORE resolving the plan on every invocation, on the stated ground that a naming violation should be 'the cheapest possible failure'; measured, it was the most expensive phase in the process — 5.9 min of a 56.5-min ordinary floor (run 31477894666) and ~6 min of a ~15-min regen whose plan has two nodes and no roster — because the producer it reaches also builds module-graph facts, runs a second strict reference-resolution pass, and — until gunbc#8141 deleted them — ran inert-lens reachability plus the construction-justification census. The walk now runs where the demand is structural (`schedules_discovery`: the plan carries a discovery or scoped-witness batch), at unchanged whole-source-root scope and unchanged cost — the roster is memoized by request digest, so the corpus batch hits the memo this call fills, and the coordinator's expected pre-plan digest is byte-identical. TWO CONSEQUENCES, both deliberate. (a) SCOPE: discovery-free plans (regen, plan-artifact) no longer run the corpus-wide nameability rules at all. Per-PR coverage is unchanged because every PR runs the `ci` job, whose plan schedules discovery and walks the full tree; what is lost is a *second* redundant walk on regen-only and plan-artifact-only runs. This is a scope narrowing declared here rather than silently taken, and it is NOT backstopped by the affected-set falsifier — that cadence has produced no green verdict since 2026-08-03. (b) ORDERING: for plans that DO schedule discovery the walk now runs after plan resolve/eval, since batches are known only then, so a naming violation pays full plan resolution (~0.5 min) before refusing. The 'cheapest possible failure' ordering is knowingly traded for deleting the same walk from every plan that never needed it; the trade is ~0.5 min later on the refusing path against ~6 min saved on every regen. Dissolve-on: the placement rules move to canonical source ingestion and test identities derive from parser-produced declarations, at which point the separate walk and this clause retire. **TWO OF THAT WALK'S THREE JOBS ARE ALREADY GONE (#8155, 2026-08-11), by deletion rather than by migration.** The `__`-basename rule is deleted: a census found ZERO offending basenames in the corpus and no stated rationale anywhere in the tree, so it guarded an empty population — and reaching it cost a whole-corpus path walk plus a SEPARATE `.dag` entry resolve (`FLOOR_NAMING_HYGIENE_ENTRY`) on every discovery-bearing run, which is why a zero-population style rule was not free. The orphan-helper census is deleted: it walked every `*_test.dag`, parsed each, projected `ModuleSurface` values, resolved a second interpreter context, and ran a fuelled reachability fixpoint against a hand-authored cross-module export roster, to decide whether a plain helper in a test file was referenced. An unreferenced test helper is dead-code hygiene, not evidence the compiler or the tests are correct, and it did not justify a recurring whole-corpus traversal on a required path. **Scope narrowing, declared not implied:** an unreferenced test helper and a `__` basename are both writable again and nothing detects either; the orphan class had fourteen enrolled witnesses and they are deleted with it, which §4b permits only because the class is ABANDONED rather than climbing — there is no higher rung for that evidence to guard. What survives is the `test fn` placement rule and the file-grain expand half. If the dead-helper inspection is wanted back it belongs as a demand-pulled query, never a required floor phase. Tree-wide unselected discovery remains the **local** path (`claim_batch --roster-from-discovery --source-root dag --source-root src/v2 --scan-dir dag/test/claim --scan-dir src/v2/test/claim/manual`). Batch-2 also carries the effectful gates: the rust fmt gate (when a `.rs` changes; nextest was removed from CI 2026-07-11 — operator ruling recorded in `gunbc.commit_workflow` `commit_gate_rust_suite_removed_disposition`, the suite runs locally only; clippy removed from CI 2026-07-08 — crate-wide `#![allow(clippy::all)]` made it zero-signal over ~44m/run, still available as a local dev check), emit-host MVP smokes, source-root-ingest, and the `ci.yml` drift+parse gate (ci.yml == `gunbc ci` output). The compile-clean gate is `--target dag`, and its per-PR scope is the same import-closure authority (2026-07-16, channel 2 of the 2026-07-10 grain fork): an all-`.dag`/docs diff compiles only the affected shard-entry closures (`tools.dag_compile_clean_scope`, host fast path `entry_file_touched_via_import_closure`); any non-selectable touched path (`.rs`, workflow yml, manifests), any non-docs departed path, or any selection refusal keeps the whole-tree baseline, loudly; the falsifier cadence carries the deterministic whole-tree cold control (`GUNBC_CI_COMPILE_CLEAN_COLD_CONTROL=1`, widen-only). The regen step (self-host fixed-point) is scoped the same way at its own closure (#6732 + this change's departed-path guard): `regen_floor_skip_witness` skips the pull_request step only when a non-empty merge-base diff is provably disjoint from the regen input set (`cli_run::regen_input_sources` — the SAME closure authority `regen_stage0` compiles — plus the src/v1/** prefix and Cargo/toolchain config); empty diffs, departed non-docs paths, and every failure arm run regen, and the skip is shell-gated to pull_request events so main pushes stay the unconditional cold control. Parse is grammar-owned: `ingest_yaml_source` (`dag/extdeps/languages/yaml/ingest.dag`), no shell/host parser. diff --git a/dag/gunbc/design_document.dag b/dag/gunbc/design_document.dag index efa2e1b4590..997da210572 100644 --- a/dag/gunbc/design_document.dag +++ b/dag/gunbc/design_document.dag @@ -229,7 +229,7 @@ fn building_checks_blocks() -> List { li(text: "`cargo test --workspace` · `cargo clippy --all-targets -- -D warnings` · `cargo fmt --all --check`"), li(text: "one-time per clone: `git config core.hooksPath .githooks` — the only documented manual seed; generated pre-commit/pre-push hooks then idempotently converge `merge.generated-artifact.driver` and re-assert `core.hooksPath` via argv derived from `gunbc.repo_local_git_config` (clones that skip hooksPath degrade to vanilla text-merge for generated-artifact paths; drift gate still guards at CI). The driver REFUSES rather than answering `true`: git reaches a low-level merge driver only when both sides changed the path since the merge base — measured on a four-case matrix, one-sided and identical changes never reach it — and taking the ours side there dropped the other side's authority-derived bytes with no conflict, twice on #7836 against the stage0 seed. It now leaves the ours side in the worktree with no conflict markers, marks the path unmerged, and prints the regeneration recipe; the class is mechanically preventable, not structural, and its next-rung trigger is the commit-writer binding rows in `gunbc.commit_workflow`"), li(text: "explicit actuator (CI / tooling): `gunbc run --source-root dag --source-root src/v2 --entry dag/gunbc/repo_local_git_config.dag --function converge`"), - li(text: "CI (`gunbc ci` generates `.github/workflows/ci.yml`; all `v1-compiler` seed bins, no shell gate): **one** composed floor pass — `claim_executor --source-root src/v2 --source-root dag --plan-entry src/v2/workflow/ci_floor_plan.dag --plan-function gunbc_ci_floor_plan`. The v2 scheduler decides the batches from the single-authority spec (`gunbc.ci_spec`); the only structural fact the plan adds is *compile-clean gates the rest* (batch-1 `dag_compile_clean_gate` → batch-2 everything else, a dependency edge). Witness enrollment: **discovery shrunk by the affected set** (operator acceptance 2026-07-09, firing the 2026-07-04 opt-in inversion's dissolve-on — see `gunbc.ci_spec` `ci_spec_discovery_flip_note`): the corpus batch scans `CiSpec.discovery_scan_dirs` (plus the source-root `*_test.dag` walk) with `SelectionApplied`, so a PR runs the tree-wide witness corpus (~1,721 rows at flip time) shrunk to the diff's affected set; selection is fail-closed (a provenance gap refuses, never widens; host-scaffold/live-tree rows never predict-skip) and the scheduled `affected-set-falsifier` (every 4 hours) runs the corpus cold with predictions recorded, so a missing selection edge surfaces as a counted divergence within one cadence window. `CiSpec.witness_entries` (`CommitWitnessClaim` rows on the `GithubActionsCiJob` surface, projected by `project_ci_floor_witness_entries`) remains the explicit-entry roster — execution-kind rows with their own resource profile, and any row that must run as declared. Empty entries AND empty discovery dirs = zero witness-corpus nodes (the regen spec's shape). Naming hygiene stays fail-closed (a `test fn` outside `*_test.dag` is still a violation) but is **demand-directed, not unconditional** (#8140, 2026-08-11, superseding the prior clause 'the executor runs the zero-enrollment naming walk when no discovery batch is scheduled'): `claim_executor` ran the whole-source-root roster walk BEFORE resolving the plan on every invocation, on the stated ground that a naming violation should be 'the cheapest possible failure'; measured, it was the most expensive phase in the process — 5.9 min of a 56.5-min ordinary floor (run 31477894666) and ~6 min of a ~15-min regen whose plan has two nodes and no roster — because the producer it reaches also builds module-graph facts, runs a second strict reference-resolution pass, and runs inert-lens reachability plus the construction-justification census. The walk now runs where the demand is structural (`schedules_discovery`: the plan carries a discovery or scoped-witness batch), at unchanged whole-source-root scope and unchanged cost — the roster is memoized by request digest, so the corpus batch hits the memo this call fills, and the coordinator's expected pre-plan digest is byte-identical. TWO CONSEQUENCES, both deliberate. (a) SCOPE: discovery-free plans (regen, plan-artifact) no longer run the corpus-wide nameability rules at all. Per-PR coverage is unchanged because every PR runs the `ci` job, whose plan schedules discovery and walks the full tree; what is lost is a *second* redundant walk on regen-only and plan-artifact-only runs. This is a scope narrowing declared here rather than silently taken, and it is NOT backstopped by the affected-set falsifier — that cadence has produced no green verdict since 2026-08-03. (b) ORDERING: for plans that DO schedule discovery the walk now runs after plan resolve/eval, since batches are known only then, so a naming violation pays full plan resolution (~0.5 min) before refusing. The 'cheapest possible failure' ordering is knowingly traded for deleting the same walk from every plan that never needed it; the trade is ~0.5 min later on the refusing path against ~6 min saved on every regen. Dissolve-on: the placement rules move to canonical source ingestion and test identities derive from parser-produced declarations, at which point the separate walk, this clause, and the `__`-basename rule all retire. Tree-wide unselected discovery remains the **local** path (`claim_batch --roster-from-discovery --source-root dag --source-root src/v2 --scan-dir dag/test/claim --scan-dir src/v2/test/claim/manual`). Batch-2 also carries the effectful gates: the rust fmt gate (when a `.rs` changes; nextest was removed from CI 2026-07-11 — operator ruling recorded in `gunbc.commit_workflow` `commit_gate_rust_suite_removed_disposition`, the suite runs locally only; clippy removed from CI 2026-07-08 — crate-wide `#![allow(clippy::all)]` made it zero-signal over ~44m/run, still available as a local dev check), emit-host MVP smokes, source-root-ingest, and the `ci.yml` drift+parse gate (ci.yml == `gunbc ci` output). The compile-clean gate is `--target dag`, and its per-PR scope is the same import-closure authority (2026-07-16, channel 2 of the 2026-07-10 grain fork): an all-`.dag`/docs diff compiles only the affected shard-entry closures (`tools.dag_compile_clean_scope`, host fast path `entry_file_touched_via_import_closure`); any non-selectable touched path (`.rs`, workflow yml, manifests), any non-docs departed path, or any selection refusal keeps the whole-tree baseline, loudly; the falsifier cadence carries the deterministic whole-tree cold control (`GUNBC_CI_COMPILE_CLEAN_COLD_CONTROL=1`, widen-only). The regen step (self-host fixed-point) is scoped the same way at its own closure (#6732 + this change's departed-path guard): `regen_floor_skip_witness` skips the pull_request step only when a non-empty merge-base diff is provably disjoint from the regen input set (`cli_run::regen_input_sources` — the SAME closure authority `regen_stage0` compiles — plus the src/v1/** prefix and Cargo/toolchain config); empty diffs, departed non-docs paths, and every failure arm run regen, and the skip is shell-gated to pull_request events so main pushes stay the unconditional cold control. Parse is grammar-owned: `ingest_yaml_source` (`dag/extdeps/languages/yaml/ingest.dag`), no shell/host parser."), + li(text: "CI (`gunbc ci` generates `.github/workflows/ci.yml`; all `v1-compiler` seed bins, no shell gate): **one** composed floor pass — `claim_executor --source-root src/v2 --source-root dag --plan-entry src/v2/workflow/ci_floor_plan.dag --plan-function gunbc_ci_floor_plan`. The v2 scheduler decides the batches from the single-authority spec (`gunbc.ci_spec`); the only structural fact the plan adds is *compile-clean gates the rest* (batch-1 `dag_compile_clean_gate` → batch-2 everything else, a dependency edge). Witness enrollment: **discovery shrunk by the affected set** (operator acceptance 2026-07-09, firing the 2026-07-04 opt-in inversion's dissolve-on — see `gunbc.ci_spec` `ci_spec_discovery_flip_note`): the corpus batch scans `CiSpec.discovery_scan_dirs` (plus the source-root `*_test.dag` walk) with `SelectionApplied`, so a PR runs the tree-wide witness corpus (~1,721 rows at flip time) shrunk to the diff's affected set; selection is fail-closed (a provenance gap refuses, never widens; host-scaffold/live-tree rows never predict-skip) and the scheduled `affected-set-falsifier` (every 4 hours) runs the corpus cold with predictions recorded, so a missing selection edge surfaces as a counted divergence within one cadence window. `CiSpec.witness_entries` (`CommitWitnessClaim` rows on the `GithubActionsCiJob` surface, projected by `project_ci_floor_witness_entries`) remains the explicit-entry roster — execution-kind rows with their own resource profile, and any row that must run as declared. Empty entries AND empty discovery dirs = zero witness-corpus nodes (the regen spec's shape). Naming hygiene stays fail-closed (a `test fn` outside `*_test.dag` is still a violation) but is **demand-directed, not unconditional** (#8140, 2026-08-11, superseding the prior clause 'the executor runs the zero-enrollment naming walk when no discovery batch is scheduled'): `claim_executor` ran the whole-source-root roster walk BEFORE resolving the plan on every invocation, on the stated ground that a naming violation should be 'the cheapest possible failure'; measured, it was the most expensive phase in the process — 5.9 min of a 56.5-min ordinary floor (run 31477894666) and ~6 min of a ~15-min regen whose plan has two nodes and no roster — because the producer it reaches also builds module-graph facts, runs a second strict reference-resolution pass, and — until gunbc#8141 deleted them — ran inert-lens reachability plus the construction-justification census. The walk now runs where the demand is structural (`schedules_discovery`: the plan carries a discovery or scoped-witness batch), at unchanged whole-source-root scope and unchanged cost — the roster is memoized by request digest, so the corpus batch hits the memo this call fills, and the coordinator's expected pre-plan digest is byte-identical. TWO CONSEQUENCES, both deliberate. (a) SCOPE: discovery-free plans (regen, plan-artifact) no longer run the corpus-wide nameability rules at all. Per-PR coverage is unchanged because every PR runs the `ci` job, whose plan schedules discovery and walks the full tree; what is lost is a *second* redundant walk on regen-only and plan-artifact-only runs. This is a scope narrowing declared here rather than silently taken, and it is NOT backstopped by the affected-set falsifier — that cadence has produced no green verdict since 2026-08-03. (b) ORDERING: for plans that DO schedule discovery the walk now runs after plan resolve/eval, since batches are known only then, so a naming violation pays full plan resolution (~0.5 min) before refusing. The 'cheapest possible failure' ordering is knowingly traded for deleting the same walk from every plan that never needed it; the trade is ~0.5 min later on the refusing path against ~6 min saved on every regen. Dissolve-on: the placement rules move to canonical source ingestion and test identities derive from parser-produced declarations, at which point the separate walk and this clause retire. **TWO OF THAT WALK'S THREE JOBS ARE ALREADY GONE (#8155, 2026-08-11), by deletion rather than by migration.** The `__`-basename rule is deleted: a census found ZERO offending basenames in the corpus and no stated rationale anywhere in the tree, so it guarded an empty population — and reaching it cost a whole-corpus path walk plus a SEPARATE `.dag` entry resolve (`FLOOR_NAMING_HYGIENE_ENTRY`) on every discovery-bearing run, which is why a zero-population style rule was not free. The orphan-helper census is deleted: it walked every `*_test.dag`, parsed each, projected `ModuleSurface` values, resolved a second interpreter context, and ran a fuelled reachability fixpoint against a hand-authored cross-module export roster, to decide whether a plain helper in a test file was referenced. An unreferenced test helper is dead-code hygiene, not evidence the compiler or the tests are correct, and it did not justify a recurring whole-corpus traversal on a required path. **Scope narrowing, declared not implied:** an unreferenced test helper and a `__` basename are both writable again and nothing detects either; the orphan class had fourteen enrolled witnesses and they are deleted with it, which §4b permits only because the class is ABANDONED rather than climbing — there is no higher rung for that evidence to guard. What survives is the `test fn` placement rule and the file-grain expand half. If the dead-helper inspection is wanted back it belongs as a demand-pulled query, never a required floor phase. Tree-wide unselected discovery remains the **local** path (`claim_batch --roster-from-discovery --source-root dag --source-root src/v2 --scan-dir dag/test/claim --scan-dir src/v2/test/claim/manual`). Batch-2 also carries the effectful gates: the rust fmt gate (when a `.rs` changes; nextest was removed from CI 2026-07-11 — operator ruling recorded in `gunbc.commit_workflow` `commit_gate_rust_suite_removed_disposition`, the suite runs locally only; clippy removed from CI 2026-07-08 — crate-wide `#![allow(clippy::all)]` made it zero-signal over ~44m/run, still available as a local dev check), emit-host MVP smokes, source-root-ingest, and the `ci.yml` drift+parse gate (ci.yml == `gunbc ci` output). The compile-clean gate is `--target dag`, and its per-PR scope is the same import-closure authority (2026-07-16, channel 2 of the 2026-07-10 grain fork): an all-`.dag`/docs diff compiles only the affected shard-entry closures (`tools.dag_compile_clean_scope`, host fast path `entry_file_touched_via_import_closure`); any non-selectable touched path (`.rs`, workflow yml, manifests), any non-docs departed path, or any selection refusal keeps the whole-tree baseline, loudly; the falsifier cadence carries the deterministic whole-tree cold control (`GUNBC_CI_COMPILE_CLEAN_COLD_CONTROL=1`, widen-only). The regen step (self-host fixed-point) is scoped the same way at its own closure (#6732 + this change's departed-path guard): `regen_floor_skip_witness` skips the pull_request step only when a non-empty merge-base diff is provably disjoint from the regen input set (`cli_run::regen_input_sources` — the SAME closure authority `regen_stage0` compiles — plus the src/v1/** prefix and Cargo/toolchain config); empty diffs, departed non-docs paths, and every failure arm run regen, and the skip is shell-gated to pull_request events so main pushes stay the unconditional cold control. Parse is grammar-owned: `ingest_yaml_source` (`dag/extdeps/languages/yaml/ingest.dag`), no shell/host parser."), ]), ] } diff --git a/dag/gunbc/floor_discovery_scaffold.dag b/dag/gunbc/floor_discovery_scaffold.dag index 95a57ff492c..5e75041499d 100644 --- a/dag/gunbc/floor_discovery_scaffold.dag +++ b/dag/gunbc/floor_discovery_scaffold.dag @@ -15,9 +15,9 @@ data floor_discovery_hand_rust_scaffold: Disposition = Scaffold { } } -data floor_discovery_dissolve_trigger: DissolutionCondition = unbound_dissolution(description: "🟡 dissolve-on: floor_discovery_hand_rust_bridge — floor_discovery_producer.dag is roster authority; Rust host still realizes effect-reach reclassification (apply_effect_reach_derived_reads_live_tree), parallel tree walks for build_floor_lens_import_graph, and invoke_floor_discovery_producer interpreter transport via discover_floor_witness_roster; DISSOLVES WHEN discovery admission consumes v2.lens.effect_reach directly and lens-graph feed the .dag producer closure (witness: dag/test/claim/floor_discovery_hand_rust_equivalence_witness_test.dag). Legacy discover_floor_corpus_rows_inner_rust_legacy deleted; discover_floor_corpus_rows / check_floor_filename_hygiene / _inner / _scoped arms deleted. Transport surface enumerated for this receipt (review 44710): invoke_floor_discovery_producer delegates to invoke_floor_discovery_producer_over_corpus(producer_roots, corpus_roots, ...), which de-fuses the two roles the single source_roots list carried — producer_roots locates the MACHINERY (this module's producer entry), corpus_roots is the SUBJECT walked and judged. Production passes one list for both, so the split is role-naming with no behaviour change, and it dissolves with the transport it parameterizes. The producer and filename-hygiene entries are typed WorkspaceRootRelativeEntry values resolved through cli_run.rs resolve_workspace_entry, which anchors them against process_workspace_root before the shared resolver observes them.") +data floor_discovery_dissolve_trigger: DissolutionCondition = unbound_dissolution(description: "🟡 dissolve-on: floor_discovery_hand_rust_bridge — floor_discovery_producer.dag is roster authority; Rust host still realizes effect-reach reclassification (apply_effect_reach_derived_reads_live_tree), parallel tree walks for build_floor_lens_import_graph, and invoke_floor_discovery_producer interpreter transport via discover_floor_witness_roster; DISSOLVES WHEN discovery admission consumes v2.lens.effect_reach directly and lens-graph feed the .dag producer closure (witness: dag/test/claim/floor_discovery_hand_rust_equivalence_witness_test.dag). Legacy discover_floor_corpus_rows_inner_rust_legacy deleted; discover_floor_corpus_rows / check_floor_filename_hygiene / _inner / _scoped arms deleted. Transport surface enumerated for this receipt (review 44710): invoke_floor_discovery_producer delegates to invoke_floor_discovery_producer_over_corpus(producer_roots, corpus_roots, ...), which de-fuses the two roles the single source_roots list carried — producer_roots locates the MACHINERY (this module's producer entry), corpus_roots is the SUBJECT walked and judged. Production passes one list for both, so the split is role-naming with no behaviour change, and it dissolves with the transport it parameterizes. The producer entry is a typed WorkspaceRootRelativeEntry value resolved through cli_run.rs resolve_workspace_entry, which anchors it against process_workspace_root before the shared resolver observes it. The filename-hygiene entry was the second such value until gunbc#8155 deleted the `__`-basename rule it resolved for; that rule guarded a zero-row population, and reaching it cost a whole-corpus path walk plus this separate entry resolve on every discovery-bearing run.") -data floor_entry_resolution_host_boundary_note: String = "ENTRY-ANCHOR host boundary: cli_run.rs WorkspaceRootRelativeEntry names the family intent and resolve_workspace_entry is its single host realization. Both FLOOR_DISCOVERY_PRODUCER_ENTRY and FLOOR_NAMING_HYGIENE_ENTRY flow through it; no per-target anchor helper or manually maintained hand-Rust count remains. GUARANTEE RUNG: documentary brand plus one anchoring site, not a construction wall — WorkspaceRootRelativeEntry remains directly constructible inside cli_run.rs, so the type alone does not make an absolute or otherwise invalid entry unwritable. NEXT TRIGGER: move the type behind a separate-module opaque constructor that accepts only validated workspace-root-relative paths. DISSOLVE-ON DEBT REMAINING: this change satisfies the typed-entry half of the former trigger but resolve_workspace_entry still joins against process_workspace_root in hand Rust; the hand-Rust anchor dissolves only when the shared resolver or a modeled host realization owns workspace-relative entry semantics. Concrete ROADMAP row: gunbc.roadmap_authority v1-hand-queue-drain (Work through the remaining hand-written files the products need)." +data floor_entry_resolution_host_boundary_note: String = "ENTRY-ANCHOR host boundary: cli_run.rs WorkspaceRootRelativeEntry names the family intent and resolve_workspace_entry is its single host realization. FLOOR_DISCOVERY_PRODUCER_ENTRY flows through it; no per-target anchor helper or manually maintained hand-Rust count remains. FLOOR_NAMING_HYGIENE_ENTRY was the second such entry until gunbc#8155 deleted the `__`-basename rule it existed to resolve — that rule guarded a population of zero with no stated rationale, and reaching it cost a whole-corpus path walk plus a separate entry resolve on every discovery-bearing run. GUARANTEE RUNG: documentary brand plus one anchoring site, not a construction wall — WorkspaceRootRelativeEntry remains directly constructible inside cli_run.rs, so the type alone does not make an absolute or otherwise invalid entry unwritable. NEXT TRIGGER: move the type behind a separate-module opaque constructor that accepts only validated workspace-root-relative paths. DISSOLVE-ON DEBT REMAINING: this change satisfies the typed-entry half of the former trigger but resolve_workspace_entry still joins against process_workspace_root in hand Rust; the hand-Rust anchor dissolves only when the shared resolver or a modeled host realization owns workspace-relative entry semantics. Concrete ROADMAP row: gunbc.roadmap_authority v1-hand-queue-drain (Work through the remaining hand-written files the products need)." data floor_entry_resolution_host_boundary_discriminator: String = "resolve_workspace_entry" diff --git a/dag/gunbc/test_module_hygiene.dag b/dag/gunbc/test_module_hygiene.dag index 65316228053..a49ba6fe7ed 100644 --- a/dag/gunbc/test_module_hygiene.dag +++ b/dag/gunbc/test_module_hygiene.dag @@ -4,323 +4,12 @@ import std.types { String, Bool, List } import v2.std.optional { Absent, Optional, Present, optional_absent, optional_present } import v2.workflow.floor_naming_hygiene { floor_discovery_scan_test_decl_names } -data test_module_hygiene_authority_note: String = "U2 orphan enroll-or-refuse + U3 file-grain expand authority (proud-wren-892). Pure fold over ModuleSurface — refs are already-extracted local names (call + bare-var). Host realizes parse Node→surface, FS walk, and interpreter transport (cli_run/test_module_hygiene_bridge.rs). Equivalence: dag/test/claim/test_module_hygiene_hand_rust_equivalence_witness_test.dag + cli_run.rs test_module_hygiene_bridge_equivalence_tests. ROADMAP node v1-test-hygiene-producer-retirement: seed producer deleted; bind is gunbc.test_module_hygiene.orphan_plain_names_or_refuse." - -type DeclSurface { - name: String - refs: List -} - -type ModuleSurface { - entry: String - test_fns: List - plain_fns: List - test_data: List - plain_data: List -} - -type CrossModuleExportRow { - entry_path: String - exports: List -} - -data cross_module_exported_plains_note: String = "Declaration-grain seeds (review 43604) — single authority for the interim roster. Dissolve-on: rename each file off *_test.dag, or import-graph reachability supersedes." - -data cross_module_exported_plains: List = [ - CrossModuleExportRow { - entry_path: "src/v2/lens/no_dual_representation_test.dag", - exports: [ - "no_dual_representation_test_clean_holds", - "no_dual_representation_test_coverage_honesty_holds", - ] - }, - CrossModuleExportRow { - entry_path: "src/v2/lens/affected_set/edit_locus_resolver_test.dag", - exports: [ - "edit_locus_source_provenance_affected_set_wire_holds", - "edit_locus_source_provenance_producer_rejects_malformed_source_holds", - "edit_locus_source_provenance_producer_holds", - "edit_locus_source_provenance_parse_root_span_holds", - ] - }, -] - +data test_module_hygiene_authority_note: String = "U3 file-grain expand authority, plus the failure-receipt companion naming convention claim_executor invokes. Host realizes the interpreter transport (cli_run/test_module_hygiene_bridge.rs).\n\nWHAT THIS MODULE NO LONGER DOES (gunbc#8155): the U2 orphan enroll-or-refuse census is DELETED. It walked every *_test.dag in the corpus, parsed each one, projected DeclSurface/ModuleSurface values, resolved a second interpreter context, and ran a fuelled reachability fixpoint with a hand-authored cross-module export exception roster — all to decide whether a plain helper in a test file was referenced. An unreferenced helper is dead-code hygiene; it is not evidence that the compiler or the tests are correct, and it did not justify a recurring whole-corpus traversal on every discovery-bearing run. Nothing replaces it: an unreferenced test helper is writable again and nothing detects it. If that inspection is wanted back it belongs as a demand-pulled query, not a required floor phase." fn is_file_grain_function(function: String) -> Bool { function == "" } -fn name_in(xs: List, needle: String) -> Bool { - any(xs: xs, predicate: fn(x) { x == needle }) -} - -fn names_set_eq(left: List, right: List) -> Bool { - (length(xs: left) == length(xs: right)) - && (left |> all(x => name_in(xs: right, needle: x))) - && (right |> all(x => name_in(xs: left, needle: x))) -} - -fn unique_snoc(xs: List, item: String) -> List { - if name_in(xs: xs, needle: item) { - xs - } else { - concat(xs, [item]) - } -} - -fn entry_path_matches(entry: String, auth: String) -> Bool { - (entry == auth) || ends_with(s: entry, suffix: concat("/", auth)) -} - -fn cross_module_exports_for(entry: String) -> List { - fold( - cross_module_exported_plains, - init: [], - f: fn(acc, row) { - if length(xs: acc) > 0 { - acc - } else if entry_path_matches(entry: entry, auth: row.entry_path) { - row.exports - } else { - acc - } - } - ) -} - -fn has_enrollment_surface(module: ModuleSurface) -> Bool { - (length(xs: module.test_fns) > 0) || (length(xs: module.test_data) > 0) -} - -fn plain_names(module: ModuleSurface) -> List { - fold( - module.plain_fns, - init: [], - f: fn(acc, d) { - concat(acc, [d.name]) - } - ) -} - -fn all_decl_bodies(module: ModuleSurface) -> List { - concat( - concat(concat(module.test_fns, module.plain_fns), module.test_data), - module.plain_data - ) -} - -fn refs_of(bodies: List, name: String) -> List { - fold( - bodies, - init: [], - f: fn(acc, d) { - if length(xs: acc) > 0 { - acc - } else if d.name == name { - d.refs - } else { - acc - } - } - ) -} - -type ReachState { - reachable_plains: List - seen_data: List - frontier: List -} - -fn plain_already_queued_or_reached(name: String, state: ReachState) -> Bool { - name_in(xs: state.reachable_plains, needle: name) || name_in(xs: state.frontier, needle: name) -} - -fn data_already_queued_or_seen(name: String, state: ReachState) -> Bool { - name_in(xs: state.seen_data, needle: name) || name_in(xs: state.frontier, needle: name) -} - -fn expand_refs_into( - refs: List, - plains: List, - data_keys: List, - state: ReachState -) -> ReachState { - fold( - refs, - init: state, - f: fn(st, r) { - if name_in(xs: plains, needle: r) { - if plain_already_queued_or_reached(name: r, state: st) { - st - } else { - ReachState { - reachable_plains: st.reachable_plains, - seen_data: st.seen_data, - frontier: unique_snoc(xs: st.frontier, item: r) - } - } - } else if name_in(xs: data_keys, needle: r) && (data_already_queued_or_seen(name: r, state: st) == false) { - ReachState { - reachable_plains: st.reachable_plains, - seen_data: unique_snoc(xs: st.seen_data, item: r), - frontier: unique_snoc(xs: st.frontier, item: r) - } - } else { - st - } - } - ) -} - -fn reach_step( - module: ModuleSurface, - plains: List, - data_keys: List, - bodies: List, - state: ReachState -) -> ReachState { - if length(xs: state.frontier) == 0 { - state - } else { - let name = state.frontier.first() - let rest = state.frontier.skip(n: 1) - let is_plain = name_in(xs: plains, needle: name) - let already = name_in(xs: state.reachable_plains, needle: name) - if is_plain && already { - ReachState { - reachable_plains: state.reachable_plains, - seen_data: state.seen_data, - frontier: rest - } - } else { - let next_reachable = if is_plain { - unique_snoc(xs: state.reachable_plains, item: name) - } else { - state.reachable_plains - } - let seeded = ReachState { - reachable_plains: next_reachable, - seen_data: state.seen_data, - frontier: rest - } - expand_refs_into( - refs: refs_of(bodies: bodies, name: name), - plains: plains, - data_keys: data_keys, - state: seeded - ) - } - } -} - -fn reach_fixpoint_pass( - module: ModuleSurface, - plains: List, - data_keys: List, - bodies: List, - state: ReachState -) -> ReachState { - reach_step( - module: module, - plains: plains, - data_keys: data_keys, - bodies: bodies, - state: state - ) -} - -fn reach_convergence_fuel(module: ModuleSurface) -> List { - let names = fold( - all_decl_bodies(module: module), - init: [], - f: fn(acc, d) { - concat(acc, [d.name]) - } - ) - concat(names, names) -} - -fn seed_reach_state(module: ModuleSurface, plains: List, exports: List) -> ReachState { - let data_keys = fold( - concat(module.test_data, module.plain_data), - init: [], - f: fn(acc, d) { - concat(acc, [d.name]) - } - ) - let from_tests = fold( - module.test_fns, - init: ReachState { - reachable_plains: [], - seen_data: [], - frontier: [] - }, - f: fn(st, d) { - expand_refs_into( - refs: d.refs, - plains: plains, - data_keys: data_keys, - state: st - ) - } - ) - let from_test_data = fold( - module.test_data, - init: from_tests, - f: fn(st, d) { - let marked = ReachState { - reachable_plains: st.reachable_plains, - seen_data: unique_snoc(xs: st.seen_data, item: d.name), - frontier: st.frontier - } - expand_refs_into( - refs: d.refs, - plains: plains, - data_keys: data_keys, - state: marked - ) - } - ) - fold( - exports, - init: from_test_data, - f: fn(st, name) { - if name_in(xs: plains, needle: name) { - ReachState { - reachable_plains: st.reachable_plains, - seen_data: st.seen_data, - frontier: unique_snoc(xs: st.frontier, item: name) - } - } else { - st - } - } - ) -} - -type OrphanPlainNamesOutcome = Orphans { names: List } | ReachBudgetRefused { reason: String } - -fn format_reach_budget_refusal(entry: String, frontier: List) -> String { - concat( - "test-module orphan reachability (DESIGN §5): convergence budget exhausted for ", - concat(entry, concat(" with ", concat(to_string(value: length(xs: frontier)), " frontier item(s) remaining — refuse rather than fabricate orphan results"))) - ) -} - -fn orphan_names_from_reachable(plains: List, reachable_plains: List) -> List { - fold( - plains, - init: [], - f: fn(acc, name) { - if name_in(xs: reachable_plains, needle: name) { - acc - } else { - concat(acc, [name]) - } - } - ) -} - -data failure_receipt_companion_note: String = "claim_executor invokes _failure_receipt on Bool(false) via the _holds/_passes → _failure_receipt naming convention (v2.std.native_agreement / gunbc#7644). Orphan reachability treats the companion as reachable from its witness even though no .dag decl references it — the executor is the caller. Single authority for the suffix derivation: Rust cli_run::failure_receipt_companion delegates here." +data failure_receipt_companion_note: String = "claim_executor invokes _failure_receipt on Bool(false) via the _holds/_passes → _failure_receipt naming convention (v2.std.native_agreement / gunbc#7644). Single authority for the suffix derivation: Rust cli_run::failure_receipt_companion delegates here. The clause that used to sit here — that orphan reachability treats the companion as reachable from its witness even though no .dag decl references it — described the census deleted in gunbc#8155 and is removed with it rather than left describing machinery that is gone." fn without_suffix(s: String, suffix: String) -> String { if ends_with(s: s, suffix: suffix) { @@ -348,196 +37,6 @@ fn failure_receipt_companion(function: String) -> Optional { } } -fn failure_receipt_companion_plain_names(module: ModuleSurface, plains: List) -> List { - fold( - module.test_fns, - init: [], - f: fn(acc, test_fn) { - match failure_receipt_companion(function: test_fn.name) { - Present { value: companion } => - if name_in(xs: plains, needle: companion) { - unique_snoc(xs: acc, item: companion) - } else { - acc - } - Absent => acc - } - } - ) -} - -fn reachable_plains_with_failure_receipt_companions( - module: ModuleSurface, - plains: List, - reachable_plains: List -) -> List { - fold( - failure_receipt_companion_plain_names(module: module, plains: plains), - init: reachable_plains, - f: fn(acc, companion) { - unique_snoc(xs: acc, item: companion) - } - ) -} - -fn orphan_plain_names_or_refuse(module: ModuleSurface) -> OrphanPlainNamesOutcome { - let plains = plain_names(module: module) - if length(xs: plains) == 0 { - Orphans { names: [] } - } else { - let exports = cross_module_exports_for(entry: module.entry) - if (has_enrollment_surface(module: module) == false) && (length(xs: exports) == 0) { - Orphans { names: plains } - } else { - let data_keys = fold( - concat(module.test_data, module.plain_data), - init: [], - f: fn(acc, d) { - concat(acc, [d.name]) - } - ) - let bodies = all_decl_bodies(module: module) - let seeded = seed_reach_state(module: module, plains: plains, exports: exports) - let closed = fold( - reach_convergence_fuel(module: module), - init: seeded, - f: fn(st, _) { - if length(xs: st.frontier) == 0 { - st - } else { - reach_fixpoint_pass( - module: module, - plains: plains, - data_keys: data_keys, - bodies: bodies, - state: st - ) - } - } - ) - if length(xs: closed.frontier) > 0 { - ReachBudgetRefused { - reason: format_reach_budget_refusal(entry: module.entry, frontier: closed.frontier) - } - } else { - Orphans { - names: orphan_names_from_reachable( - plains: plains, - reachable_plains: reachable_plains_with_failure_receipt_companions( - module: module, - plains: plains, - reachable_plains: closed.reachable_plains - ) - ) - } - } - } - } -} - -// Synthetic fixtures — same spellings as cli_run/test_module_hygiene_bridge.rs equivalence tests. -data fixture_zero_enrolled_demotion: ModuleSurface = ModuleSurface { - entry: "demoted_witness_test.dag", - test_fns: [], - plain_fns: [DeclSurface { name: "formerly_test_holds", refs: [] }], - test_data: [], - plain_data: [] -} - -data fixture_directory_prefix_bypass: ModuleSurface = ModuleSurface { - entry: "extdeps/languages/not_on_allowlist_test.dag", - test_fns: [], - plain_fns: [DeclSurface { name: "export_for_importers", refs: [] }], - test_data: [], - plain_data: [] -} - -data fixture_cross_module_export_partial: ModuleSurface = ModuleSurface { - entry: "src/v2/lens/no_dual_representation_test.dag", - test_fns: [], - plain_fns: [ - DeclSurface { name: "no_dual_representation_test_clean_holds", refs: [] }, - DeclSurface { name: "accidental_demotion_holds", refs: [] }, - ], - test_data: [], - plain_data: [] -} - -data fixture_cross_module_export_empty_file: ModuleSurface = ModuleSurface { - entry: "src/v2/lens/no_dual_representation_test.dag", - test_fns: [], - plain_fns: [DeclSurface { name: "unrelated_dark_holds", refs: [] }], - test_data: [], - plain_data: [] -} - -data fixture_test_data_only: ModuleSurface = ModuleSurface { - entry: "test_data_only_test.dag", - test_fns: [], - plain_fns: [ - DeclSurface { name: "used_by_test_data", refs: [] }, - DeclSurface { name: "dark_plain", refs: [] }, - ], - test_data: [DeclSurface { name: "enrolled_row", refs: ["used_by_test_data"] }], - plain_data: [] -} - -data fixture_plain_data_not_enrollment: ModuleSurface = ModuleSurface { - entry: "plain_data_demotion_test.dag", - test_fns: [], - plain_fns: [DeclSurface { name: "formerly_test_holds", refs: [] }], - test_data: [], - plain_data: [DeclSurface { name: "fixture", refs: ["formerly_test_holds"] }] -} - -data fixture_dead_plain_data: ModuleSurface = ModuleSurface { - entry: "dead_plain_data_test.dag", - test_fns: [DeclSurface { name: "unrelated_holds", refs: [] }], - plain_fns: [DeclSurface { name: "demoted_holds", refs: [] }], - test_data: [], - plain_data: [DeclSurface { name: "fixture", refs: ["demoted_holds"] }] -} - -data fixture_plain_data_chain: ModuleSurface = ModuleSurface { - entry: "plain_data_chain_test.dag", - test_fns: [], - plain_fns: [ - DeclSurface { name: "helper_holds", refs: [] }, - DeclSurface { name: "dark_plain", refs: [] }, - ], - test_data: [DeclSurface { name: "enrolled_run", refs: ["claim_row"] }], - plain_data: [DeclSurface { name: "claim_row", refs: ["helper_holds"] }] -} - -data fixture_orphan_plant: ModuleSurface = ModuleSurface { - entry: "orphan_plant_test.dag", - test_fns: [DeclSurface { name: "live_holds", refs: [] }], - plain_fns: [DeclSurface { name: "dark_unreachable_check", refs: [] }], - test_data: [], - plain_data: [] -} - -data fixture_failure_receipt_companion_pair: ModuleSurface = ModuleSurface { - entry: "failure_receipt_companion_test.dag", - test_fns: [DeclSurface { name: "witness_holds", refs: [] }], - plain_fns: [DeclSurface { name: "witness_failure_receipt", refs: [] }], - test_data: [], - plain_data: [] -} - -data fixture_failure_receipt_orphan: ModuleSurface = ModuleSurface { - entry: "failure_receipt_orphan_test.dag", - test_fns: [DeclSurface { name: "witness_holds", refs: [] }], - plain_fns: [DeclSurface { name: "orphan_failure_receipt", refs: [] }], - test_data: [], - plain_data: [] -} - -type OrphanRecord { - entry: String - name: String -} - type ExplicitExpandInput { entry: String function: String @@ -549,60 +48,6 @@ type ExplicitEntryPair { function: String } -type CollectOrphanOutcome = CollectRefused { reason: String } | CollectOrphans { records: List } - -fn collect_orphan_outcome(modules: List) -> CollectOrphanOutcome { - fold( - modules, - init: CollectOrphans { records: [] }, - f: fn(acc, surf) { - match acc { - CollectRefused { reason: reason } => CollectRefused { reason: reason } - CollectOrphans { records: recs } => - match orphan_plain_names_or_refuse(module: surf) { - ReachBudgetRefused { reason: reason } => CollectRefused { reason: reason } - Orphans { names: names } => CollectOrphans { - records: fold( - names, - init: recs, - f: fn(inner, name) { - concat(inner, [OrphanRecord { entry: surf.entry, name: name }]) - } - ) - } - } - } - } - ) -} - -fn format_orphan_refusal(orphans: List) -> String { - let header = concat( - "test-module orphan-helper hygiene (DESIGN §5/§6 enroll-or-refuse): ", - to_string(value: length(xs: orphans)), - " plain fn(s) in `*_test.dag` are unreachable from every `test fn` / `test data` (fixture data only when those exist) — silent de-enrollment. Promote to `test fn`/`test data` or delete:" - ) - fold( - orphans, - init: header, - f: fn(acc, o) { - concat(acc, "\n ", o.entry, "::", o.name) - } - ) -} - -fn check_orphan_surfaces_or_refuse(modules: List) -> Optional { - match collect_orphan_outcome(modules: modules) { - CollectRefused { reason: reason } => optional_present(value: reason) - CollectOrphans { records: orphans } => - if length(xs: orphans) == 0 { - optional_absent() - } else { - optional_present(value: format_orphan_refusal(orphans: orphans)) - } - } -} - fn pair_key(entry: String, function: String) -> String { concat(entry, "\0", function) } diff --git a/dag/gunbc/test_module_hygiene_scaffold.dag b/dag/gunbc/test_module_hygiene_scaffold.dag deleted file mode 100644 index b36bfc50a93..00000000000 --- a/dag/gunbc/test_module_hygiene_scaffold.dag +++ /dev/null @@ -1,46 +0,0 @@ -module gunbc.test_module_hygiene_scaffold - -import std.dissolution { DissolutionCondition, dissolution_description, unbound_dissolution } - -import std.disposition { Disposition, Scaffold, SingleAuthority } -import std.decl_ref { DeclarationRef, WholeDeclaration } -import std.types { String } - -data test_module_hygiene_orphan_gate_scaffold: Disposition = Scaffold { - dissolves_to: SingleAuthority, - bind: DeclarationRef { - module_path: "gunbc.test_module_hygiene", - decl_name: "orphan_plain_names_or_refuse", - field: WholeDeclaration - } -} - -data test_module_hygiene_orphan_gate_dissolve_trigger: DissolutionCondition = unbound_dissolution(description: "🟡 dissolve-on: test_module_hygiene — U2 orphan enroll-or-refuse + U3 file-grain expand live in gunbc.test_module_hygiene.dag; host transport in cli_run/test_module_hygiene_bridge.rs (parse→ModuleSurface projection only). LANDED: src/v1/stage0/src/test_module_hygiene.rs deleted; SeedRetainedIntrinsicRegistration row removed from v2.compiler.self_host.frontier. ROADMAP node v1-test-hygiene-producer-retirement. Discriminating receipt: dag/test/claim/test_module_hygiene_hand_rust_equivalence_witness_test.dag + cli_run.rs test_module_hygiene_bridge_equivalence_tests (not a marker-count rg).") - -data test_module_hygiene_orphan_gate_receipt_lane_deferral: String = "gunbc.roadmap_authority node v1-test-hygiene-producer-retirement / DESIGN §7 hand-Rust shrink-to-zero — seed producer deleted; orphan fold authority is gunbc.test_module_hygiene; host bridge is transport-only interim (review 43601 pattern), not unmarked workaround." - -data test_module_hygiene_orphan_gate_receipt_plan_anchor: String = "gunbc.roadmap_authority v1-test-hygiene-producer-retirement" - -data test_module_hygiene_authority_entry: String = "dag/gunbc/test_module_hygiene.dag" - -data test_module_hygiene_equivalence_witness_entry: String = "dag/test/claim/test_module_hygiene_hand_rust_equivalence_witness_test.dag" - -data test_module_hygiene_bridge_transport_entry: String = "src/v1/stage0/src/cli_run/test_module_hygiene_bridge.rs" - -data test_module_hygiene_orphan_gate_test_discriminator_demotion: String = "zero_enrolled_plain_helpers_refuse_demotion" - -data test_module_hygiene_orphan_gate_test_discriminator_fixture_allowlist: String = "cross_module_export_roster_seeds_named_plain_not_whole_file" - -data test_module_hygiene_orphan_gate_test_discriminator_directory_bypass_refused: String = "directory_prefix_alone_does_not_exempt_zero_enrolled_plains" - -data test_module_hygiene_orphan_gate_test_discriminator_export_roster_no_whole_file_green: String = "cross_module_export_roster_alone_does_not_green_empty_file" - -data test_module_hygiene_orphan_gate_test_discriminator_unparsable_refuse: String = "orphan_collect_refuses_unparsable_test_dag" - -data test_module_hygiene_orphan_gate_test_discriminator_test_data: String = "test_data_only_module_still_orphans_unreachable_plains" - -data test_module_hygiene_orphan_gate_test_discriminator_plain_data_not_enrollment: String = "plain_data_alone_does_not_enroll_demoted_holds" - -data test_module_hygiene_orphan_gate_test_discriminator_dead_plain_data: String = "unrelated_test_plus_dead_plain_data_does_not_enroll" - -data test_module_hygiene_orphan_gate_test_discriminator_plain_data_chain: String = "plain_data_reached_from_test_data_still_covers_helpers" diff --git a/dag/test/claim/floor_discovery_hand_rust_equivalence_witness_test.dag b/dag/test/claim/floor_discovery_hand_rust_equivalence_witness_test.dag index f0ecacc5f1d..54c36d62966 100644 --- a/dag/test/claim/floor_discovery_hand_rust_equivalence_witness_test.dag +++ b/dag/test/claim/floor_discovery_hand_rust_equivalence_witness_test.dag @@ -34,7 +34,6 @@ import v2.workflow.floor_discovery_producer { floor_discovery_surface_text_scan_scaffold, } import v2.workflow.floor_naming_hygiene { - floor_filename_hygiene_refusal_for_paths, floor_discovery_scan_test_decl_names, floor_test_sidecar_suffix, floor_wire_contract_sidecar_suffix @@ -69,10 +68,6 @@ test fn floor_entry_resolution_host_boundary_is_declared() -> Bool { s: floor_entry_resolution_host_boundary_note, pattern: "FLOOR_DISCOVERY_PRODUCER_ENTRY", ) - && string_contains( - s: floor_entry_resolution_host_boundary_note, - pattern: "FLOOR_NAMING_HYGIENE_ENTRY", - ) && string_contains( s: floor_entry_resolution_host_boundary_note, pattern: "v1-hand-queue-drain", @@ -84,8 +79,6 @@ data fixture_witness_function: String = "floor_discovery_roster_fixture_witness" data fixture_test_fn_content: String = "test fn floor_discovery_roster_fixture_witness() -> Bool { true }\n" data fixture_test_data_content: String = "test data floor_discovery_data_fixture: Bool = true\n" data fixture_misplaced_entry: String = "src/v2/lens/example.dag" -data fixture_hygiene_clean_path: String = "dag/test/claim/floor_discovery_hand_rust_equivalence_witness_test.dag" -data fixture_hygiene_bad_path: String = "dag/test/claim/bad__hygiene.dag" data fixture_misplaced_wire_contract_entry: String = "dag/test/fixture/floor_discovery/misplaced_wire_contract.dag" data fixture_live_tree_entry: String = "dag/test/claim/floor_discovery_roster_fixture_test.dag" @@ -258,22 +251,6 @@ test fn floor_discovery_producer_empty_transport_red_control() -> Bool { } } -test fn floor_discovery_filename_hygiene_clean_holds() -> Bool { - match floor_filename_hygiene_refusal_for_paths(dag_paths: [fixture_hygiene_clean_path]) { - Absent => true - Present { value: _ } => false - } -} - -test fn floor_discovery_filename_hygiene_double_underscore_red_control() -> Bool { - match floor_filename_hygiene_refusal_for_paths(dag_paths: [fixture_hygiene_bad_path]) { - Present { value: msg } => - string_contains(s: msg, pattern: "__") - && string_contains(s: msg, pattern: fixture_hygiene_bad_path) - Absent => false - } -} - test fn floor_discovery_scan_test_fn_decl_holds() -> Bool { list_contains_string( xs: floor_discovery_scan_test_decl_names(content: fixture_test_fn_content), diff --git a/dag/test/claim/test_module_hygiene_hand_rust_equivalence_witness_test.dag b/dag/test/claim/test_module_hygiene_hand_rust_equivalence_witness_test.dag index 1226ec0810e..b1607a75e15 100644 --- a/dag/test/claim/test_module_hygiene_hand_rust_equivalence_witness_test.dag +++ b/dag/test/claim/test_module_hygiene_hand_rust_equivalence_witness_test.dag @@ -1,79 +1,30 @@ module test.claim.test_module_hygiene_hand_rust_equivalence_witness -import std.dissolution { dissolution_description } - -import gunbc.test_module_hygiene_scaffold { - test_module_hygiene_orphan_gate_scaffold, - test_module_hygiene_orphan_gate_dissolve_trigger, - test_module_hygiene_orphan_gate_receipt_plan_anchor, - test_module_hygiene_orphan_gate_receipt_lane_deferral, - test_module_hygiene_orphan_gate_test_discriminator_demotion, - test_module_hygiene_orphan_gate_test_discriminator_fixture_allowlist, - test_module_hygiene_orphan_gate_test_discriminator_directory_bypass_refused, - test_module_hygiene_orphan_gate_test_discriminator_export_roster_no_whole_file_green, - test_module_hygiene_orphan_gate_test_discriminator_test_data, - test_module_hygiene_orphan_gate_test_discriminator_plain_data_not_enrollment, - test_module_hygiene_orphan_gate_test_discriminator_dead_plain_data, - test_module_hygiene_orphan_gate_test_discriminator_plain_data_chain, - test_module_hygiene_orphan_gate_test_discriminator_unparsable_refuse, - test_module_hygiene_authority_entry, - test_module_hygiene_equivalence_witness_entry, - test_module_hygiene_bridge_transport_entry, -} import gunbc.test_module_hygiene { - ModuleSurface, - orphan_plain_names_or_refuse, - names_set_eq, is_file_grain_function, ExplicitExpandInput, expand_explicit_pairs_or_refuse, failure_receipt_companion, - fixture_zero_enrolled_demotion, - fixture_directory_prefix_bypass, - fixture_cross_module_export_partial, - fixture_cross_module_export_empty_file, - fixture_test_data_only, - fixture_plain_data_not_enrollment, - fixture_dead_plain_data, - fixture_plain_data_chain, - fixture_orphan_plant, - fixture_failure_receipt_companion_pair, - fixture_failure_receipt_orphan, } import v2.std.optional { Present, Absent } -import std.disposition { Scaffold, SingleAuthority } -import std.types { Bool, String, List } +import std.types { Bool, String } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly -// Pins gunbc.test_module_hygiene.orphan_plain_names_or_refuse against synthetic ModuleSurface fixtures matching cli_run/test_module_hygiene_bridge.rs equivalence tests (parse→surface→producer). Seed producer deleted; bridge is transport-only. RED: plain_data_not_enrollment must stay refused. -fn orphans_are(module: ModuleSurface, expected: List) -> Bool { - match orphan_plain_names_or_refuse(module: module) { - Orphans { names: names } => names_set_eq(left: names, right: expected) - ReachBudgetRefused { reason: _ } => false - } -} - -test fn test_module_hygiene_scaffold_is_single_authority_target() -> Bool { - match test_module_hygiene_orphan_gate_scaffold { - Scaffold { dissolves_to: SingleAuthority, bind: _ } => true - _ => false - } -} - -test fn test_module_hygiene_dissolve_trigger_names_retirement_lane() -> Bool { - string_contains(s: dissolution_description(condition: test_module_hygiene_orphan_gate_dissolve_trigger), pattern: "v1-test-hygiene-producer-retirement") - && string_contains(s: test_module_hygiene_orphan_gate_receipt_plan_anchor, pattern: "v1-test-hygiene-producer-retirement") - && string_contains(s: test_module_hygiene_orphan_gate_receipt_lane_deferral, pattern: "v1-test-hygiene-producer-retirement") -} - -test fn test_module_hygiene_authority_paths_named() -> Bool { - string_contains(s: test_module_hygiene_authority_entry, pattern: "test_module_hygiene.dag") - && string_contains(s: test_module_hygiene_equivalence_witness_entry, pattern: "equivalence_witness") - && string_contains(s: test_module_hygiene_bridge_transport_entry, pattern: "test_module_hygiene_bridge.rs") -} - +// WHAT THIS FILE USED TO CARRY, and why it does not. Fourteen of the eighteen witnesses here +// pinned gunbc.test_module_hygiene orphan_plain_names_or_refuse against synthetic ModuleSurface +// fixtures — zero-enrolled demotion, directory-prefix bypass, cross-module export partial/empty, +// test-data-only, plain-data enrollment, dead plain data, plain-data chains, a planted orphan, and +// an unparsable refusal. Every one of them tested the orphan census, which is deleted along with +// its fixtures, its scaffold carrier, and its hand-authored cross-module export exception roster. +// Deleting the machinery deletes its evidence, which is normally forbidden — DESIGN §4b's +// dissolution-on-climb rule keeps the discriminating RED enrolled when a class CLIMBS. This is not +// a climb: the class is abandoned, not made unwritable, so there is no higher rung for the evidence +// to guard, and retaining fixtures for a fold nothing calls would be specification-without- +// execution. The scope loss is declared in the module authority note on gunbc.test_module_hygiene +// rather than implied here. WHAT SURVIVES BELOW is the file-grain expand half plus the +// failure-receipt companion convention, both of which have live consumers in claim_executor. test fn test_module_hygiene_file_grain_empty_function_holds() -> Bool { is_file_grain_function(function: "") && (is_file_grain_function(function: "alpha_holds") == false) @@ -94,103 +45,16 @@ test fn test_module_hygiene_file_grain_expand_refuses_empty_test_decls() -> Bool } } -test fn test_module_hygiene_equivalence_zero_enrolled_demotion_holds() -> Bool { - orphans_are(module: fixture_zero_enrolled_demotion, expected: ["formerly_test_holds"]) - && string_contains( - s: test_module_hygiene_orphan_gate_test_discriminator_demotion, - pattern: "zero_enrolled_plain_helpers_refuse_demotion" - ) -} - -test fn test_module_hygiene_equivalence_directory_prefix_bypass_refused_holds() -> Bool { - orphans_are(module: fixture_directory_prefix_bypass, expected: ["export_for_importers"]) - && string_contains( - s: test_module_hygiene_orphan_gate_test_discriminator_directory_bypass_refused, - pattern: "directory_prefix_alone_does_not_exempt" - ) -} - -test fn test_module_hygiene_equivalence_cross_module_export_partial_holds() -> Bool { - orphans_are(module: fixture_cross_module_export_partial, expected: ["accidental_demotion_holds"]) - && string_contains( - s: test_module_hygiene_orphan_gate_test_discriminator_fixture_allowlist, - pattern: "cross_module_export_roster_seeds_named_plain" - ) -} - -test fn test_module_hygiene_equivalence_cross_module_export_no_whole_file_holds() -> Bool { - orphans_are(module: fixture_cross_module_export_empty_file, expected: ["unrelated_dark_holds"]) - && string_contains( - s: test_module_hygiene_orphan_gate_test_discriminator_export_roster_no_whole_file_green, - pattern: "cross_module_export_roster_alone_does_not_green" - ) -} - -test fn test_module_hygiene_equivalence_test_data_only_holds() -> Bool { - orphans_are(module: fixture_test_data_only, expected: ["dark_plain"]) - && string_contains( - s: test_module_hygiene_orphan_gate_test_discriminator_test_data, - pattern: "test_data_only_module_still_orphans" - ) -} - -test fn test_module_hygiene_equivalence_plain_data_not_enrollment_holds() -> Bool { - orphans_are(module: fixture_plain_data_not_enrollment, expected: ["formerly_test_holds"]) - && string_contains( - s: test_module_hygiene_orphan_gate_test_discriminator_plain_data_not_enrollment, - pattern: "plain_data_alone_does_not_enroll" - ) -} - -test fn test_module_hygiene_equivalence_dead_plain_data_holds() -> Bool { - orphans_are(module: fixture_dead_plain_data, expected: ["demoted_holds"]) - && string_contains( - s: test_module_hygiene_orphan_gate_test_discriminator_dead_plain_data, - pattern: "unrelated_test_plus_dead_plain_data" - ) -} - -test fn test_module_hygiene_equivalence_plain_data_chain_holds() -> Bool { - orphans_are(module: fixture_plain_data_chain, expected: ["dark_plain"]) - && string_contains( - s: test_module_hygiene_orphan_gate_test_discriminator_plain_data_chain, - pattern: "plain_data_reached_from_test_data_still_covers" - ) -} - -test fn test_module_hygiene_equivalence_orphan_plant_holds() -> Bool { - orphans_are(module: fixture_orphan_plant, expected: ["dark_unreachable_check"]) -} - -test fn test_module_hygiene_failure_receipt_companion_pair_holds() -> Bool { - orphans_are(module: fixture_failure_receipt_companion_pair, expected: []) - && match failure_receipt_companion(function: "witness_holds") { - Present { value: companion } => companion == "witness_failure_receipt" - Absent => false - } -} - -test fn test_module_hygiene_failure_receipt_without_witness_refuses() -> Bool { - orphans_are(module: fixture_failure_receipt_orphan, expected: ["orphan_failure_receipt"]) -} - -data test_module_hygiene_unparsable_refuse_bridge_note: String = "Parse failure on a *_test.dag must refuse via check_orphan_helpers_or_err — executing receipt: cli_run.rs test_module_hygiene_bridge_equivalence_tests::orphan_collect_refuses_unparsable_test_dag (re-homed from deleted test_module_hygiene.rs)." - -test fn test_module_hygiene_equivalence_unparsable_refuse_discriminator_holds() -> Bool { - string_contains( - s: test_module_hygiene_orphan_gate_test_discriminator_unparsable_refuse, - pattern: "orphan_collect_refuses_unparsable_test_dag" - ) - && string_contains( - s: test_module_hygiene_unparsable_refuse_bridge_note, - pattern: "orphan_collect_refuses_unparsable_test_dag" - ) +test fn test_module_hygiene_failure_receipt_companion_derives_suffix() -> Bool { + match failure_receipt_companion(function: "witness_holds") { + Present { value: companion } => companion == "witness_failure_receipt" + Absent => false + } } -// RED control: if plain data were wrongly treated as enrollment, fixture_plain_data_not_enrollment would go green with zero orphans — keep the refuse countable. -test fn test_module_hygiene_red_plain_data_must_not_green_demotion() -> Bool { - match orphan_plain_names_or_refuse(module: fixture_plain_data_not_enrollment) { - Orphans { names: names } => length(xs: names) > 0 - ReachBudgetRefused { reason: _ } => false +test fn test_module_hygiene_failure_receipt_companion_absent_on_non_witness() -> Bool { + match failure_receipt_companion(function: "ordinary_fn") { + Absent => true + Present { value: _ } => false } } diff --git a/src/v1/stage0/src/bin/claim_executor.rs b/src/v1/stage0/src/bin/claim_executor.rs index 2e3cb38acde..25be16873b2 100644 --- a/src/v1/stage0/src/bin/claim_executor.rs +++ b/src/v1/stage0/src/bin/claim_executor.rs @@ -10429,7 +10429,8 @@ fn run() -> Result { Runnable::DiscoveryBatch { .. } | Runnable::ScopedWitnessBatch { .. } ) }); - // Witness naming hygiene (`test fn` outside `*_test.dag`, `__` basenames) is a + // Witness naming hygiene (`test fn` outside `*_test.dag`; the `__`-basename rule and + // the orphan-helper census were deleted in gunbc#8155) is a // property of the witness ROSTER, so it is paid by the plans that have one. It ran // unconditionally before plan evaluation until this change, on the stated ground // that a naming violation should be "the cheapest possible failure"; measured, the diff --git a/src/v1/stage0/src/cli_run.rs b/src/v1/stage0/src/cli_run.rs index 7a7f3d2db72..f60c9aa07ef 100644 --- a/src/v1/stage0/src/cli_run.rs +++ b/src/v1/stage0/src/cli_run.rs @@ -20183,73 +20183,6 @@ fn scan_test_decl_lines(content: &str) -> Vec<(String, i64)> { out } -pub(crate) fn floor_filename_hygiene_refusal_via_producer( - source_roots: &[String], -) -> Result<(), String> { - let mut dag_paths: Vec = Vec::new(); - for root in source_roots { - let mut dag_files: Vec = Vec::new(); - collect_dag_files_tolerant(Path::new(root), &mut dag_files); - for path in dag_files { - dag_paths.push(path.to_string_lossy().into_owned()); - } - } - if dag_paths.is_empty() { - return Ok(()); - } - let (graph, indices) = resolve_workspace_entry(source_roots, FLOOR_NAMING_HYGIENE_ENTRY) - .map_err(|e| format!("floor_naming_hygiene resolve for filename hygiene: {e}"))?; - let ctx = make_eval_context(&graph, indices, v1_interpreter::ExecutionMode::Wet); - let path_values: Vec = dag_paths - .iter() - .map(|p| v1_interpreter::Value::Str(p.clone())) - .collect(); - let args = [( - Some("dag_paths".to_string()), - list_value_from_vec(path_values), - )]; - let result = v1_interpreter::run_in_context_with_args( - &ctx, - "floor_filename_hygiene_refusal_for_paths", - &args, - false, - ) - .map_err(|e| format!("floor_filename_hygiene_refusal_for_paths: {e}"))?; - match &result { - v1_interpreter::Value::Variant { - type_name, - variant_name, - fields, - .. - } if ctx.sym_eq(*type_name, "Optional") && ctx.sym_eq(*variant_name, "Present") => { - match ctx.field(fields, "value") { - Some(v1_interpreter::Value::Str(reason)) => Err(reason.clone()), - _ => Err( - "floor_filename_hygiene_refusal_for_paths Present missing reason string" - .to_string(), - ), - } - } - v1_interpreter::Value::Variant { - type_name, - variant_name, - .. - } if ctx.sym_eq(*type_name, "Optional") && ctx.sym_eq(*variant_name, "Absent") => Ok(()), - other => Err(format!( - "floor_filename_hygiene_refusal_for_paths returned `{}`, expected Optional", - ctx.format_value(other) - )), - } -} - -#[derive(Clone, Copy)] -struct WorkspaceRootRelativeEntry(&'static str); - -const FLOOR_DISCOVERY_PRODUCER_ENTRY: WorkspaceRootRelativeEntry = - WorkspaceRootRelativeEntry("src/v2/workflow/floor_discovery_producer.dag"); -const FLOOR_NAMING_HYGIENE_ENTRY: WorkspaceRootRelativeEntry = - WorkspaceRootRelativeEntry("src/v2/workflow/floor_naming_hygiene.dag"); - /// The producer entry above is repo-relative, and every resolve of it ultimately reads the /// path **cwd-relative** (`entry_source_from_index_or_disk` does a bare `Path::is_file`). /// The floor runs from the repo root, so that ambient dependency is invisible in production @@ -20267,6 +20200,12 @@ const FLOOR_NAMING_HYGIENE_ENTRY: WorkspaceRootRelativeEntry = /// Part of the `cli_run_runtime_workspace_root_plumbing` scaffold (see /// `CLI_RUN_RUNTIME_WORKSPACE_ROOT_SCAFFOLD_MARKER`): it dissolves with the rest of the /// anchoring plumbing when entry resolution stops reading cwd. +#[derive(Clone, Copy)] +struct WorkspaceRootRelativeEntry(&'static str); + +const FLOOR_DISCOVERY_PRODUCER_ENTRY: WorkspaceRootRelativeEntry = + WorkspaceRootRelativeEntry("src/v2/workflow/floor_discovery_producer.dag"); + fn resolve_workspace_entry( source_roots: &[String], entry: WorkspaceRootRelativeEntry, @@ -20942,10 +20881,6 @@ pub fn discover_floor_witness_roster( exclude_substrings: &[String], discovery_scope_dirs: &[String], ) -> Result, String> { - floor_filename_hygiene_refusal_via_producer(source_roots)?; - // U2 — orphan plain fns in *_test.dag (enroll-or-refuse). Lives in the naming walk, - // not a new lens (umbrella-dissolution fence). - test_module_hygiene_bridge::check_orphan_helpers_or_err(source_roots)?; let mut rows = invoke_floor_discovery_producer(source_roots, scan_dirs, exclude_substrings)?; rows = apply_discovery_scope_dirs_filter(rows, discovery_scope_dirs); // The module-graph facts build now serves exactly two consumers on this path: @@ -29521,9 +29456,10 @@ mod sidecar_placement_hygiene_tests { // asserted twice — `floor_discovery_hand_rust_equivalence_witness_test.dag` // (`floor_discovery_equivalence_misplaced_wire_contract_refuses_holds`) already owns // it content-side, so re-deriving it here would be a second representation (§2/§3). - // Hence the call is at the producer seam and not at - // `floor_filename_hygiene_refusal_for_paths`, which decides only the `__`-in-basename - // rule and can never report a wire-contract violation. + // Hence the call is at the producer seam, where a wire-contract violation is + // observable at all. (This clause used to contrast the seam against + // `floor_filename_hygiene_refusal_for_paths`; that decided only the `__`-in-basename + // rule, which is deleted, so the contrast named nothing.) // // Nothing here chdirs, and that is load-bearing: the producer entry is located by // `resolve_workspace_entry` (git-toplevel, not cwd), so this test diff --git a/src/v1/stage0/src/cli_run/floor_discovery_snapshot.rs b/src/v1/stage0/src/cli_run/floor_discovery_snapshot.rs index 1b63e199c01..e980f02aedc 100644 --- a/src/v1/stage0/src/cli_run/floor_discovery_snapshot.rs +++ b/src/v1/stage0/src/cli_run/floor_discovery_snapshot.rs @@ -4,7 +4,7 @@ //! //! | Consumer site | What it reads from the discovery walk | Closed projection | //! |---|---|---| -//! | Demand-directed `discover_floor_witness_roster([], [])` (runs only when the plan schedules a discovery or scoped-witness batch, gunbc#8140) | Naming hygiene, orphan/helpers, producer roster, module-graph facts, effect-reach derivation | Full snapshot payload + installed module-graph cache | +//! | Demand-directed `discover_floor_witness_roster([], [])` (runs only when the plan schedules a discovery or scoped-witness batch, gunbc#8140) | `test fn` placement hygiene, producer roster, module-graph facts, effect-reach derivation (the orphan-helper census and the `__`-basename rule were deleted, gunbc#8155) | Full snapshot payload + installed module-graph cache | //! | Discovery corpus with `scan_dirs=[]` + explicit entries | Skips roster walk; still calls `build_module_graph_facts_live` on selection/skip paths | Module-graph facts bytes in snapshot (cache install) | //! | Discovery corpus with non-empty `scan_dirs` | Full roster walk for that scan shape | Not covered by pre-plan snapshot (distinct request identity) | //! @@ -85,8 +85,6 @@ pub struct FloorDiscoverySnapshot { pub request_identity_digest: String, pub roster: Vec, pub roster_digest: String, - pub naming_hygiene_refusal: Option, - pub orphan_helper_refusal: Option, pub module_graph_facts: ModuleGraphFactsSnapshot, pub module_graph_facts_digest: String, pub payload_digest: String, @@ -500,8 +498,6 @@ struct FloorDiscoveryPayloadDigestView<'a> { request_identity_digest: &'a str, roster: &'a [DiscoveryRowSnapshot], roster_digest: &'a str, - naming_hygiene_refusal: &'a Option, - orphan_helper_refusal: &'a Option, module_graph_facts: &'a ModuleGraphFactsSnapshot, module_graph_facts_digest: &'a str, } @@ -512,8 +508,6 @@ fn digest_payload(snapshot: &FloorDiscoverySnapshot) -> String { request_identity_digest: &snapshot.request_identity_digest, roster: &snapshot.roster, roster_digest: &snapshot.roster_digest, - naming_hygiene_refusal: &snapshot.naming_hygiene_refusal, - orphan_helper_refusal: &snapshot.orphan_helper_refusal, module_graph_facts: &snapshot.module_graph_facts, module_graph_facts_digest: &snapshot.module_graph_facts_digest, }; @@ -570,23 +564,6 @@ pub fn produce_floor_discovery_snapshot( request: &FloorDiscoveryRequest, ) -> Result { COORDINATED_DISCOVERY_COMPUTE_COUNT.fetch_add(1, Ordering::SeqCst); - let naming_hygiene_refusal = - match super::floor_filename_hygiene_refusal_via_producer(&request.source_roots) { - Ok(()) => None, - Err(msg) => Some(msg), - }; - if naming_hygiene_refusal.is_some() { - return Err(naming_hygiene_refusal.clone().unwrap()); - } - let orphan_helper_refusal = - match super::test_module_hygiene_bridge::check_orphan_helpers_or_err(&request.source_roots) - { - Ok(()) => None, - Err(msg) => Some(msg), - }; - if orphan_helper_refusal.is_some() { - return Err(orphan_helper_refusal.clone().unwrap()); - } let mut rows = super::invoke_floor_discovery_producer( &request.source_roots, &request.scan_dirs, @@ -606,8 +583,6 @@ pub fn produce_floor_discovery_snapshot( request_identity_digest, roster, roster_digest, - naming_hygiene_refusal, - orphan_helper_refusal, module_graph_facts: facts_snapshot, module_graph_facts_digest, payload_digest: String::new(), @@ -882,8 +857,6 @@ mod tests { request, roster: vec![], roster_digest: digest_rows(&[]), - naming_hygiene_refusal: None, - orphan_helper_refusal: None, module_graph_facts_digest: digest_facts_snapshot(&facts), module_graph_facts: facts, payload_digest: String::new(), @@ -1005,8 +978,6 @@ mod tests { request_identity_digest: request_identity_digest(&request).expect("request identity"), roster: vec![], roster_digest: digest_rows(&[]), - naming_hygiene_refusal: None, - orphan_helper_refusal: None, module_graph_facts: ModuleGraphFactsSnapshot { edges: vec![], nodes: vec![], @@ -1060,8 +1031,6 @@ mod tests { request_identity_digest: request_identity_digest(&request).expect("request identity"), roster: vec![], roster_digest: digest_rows(&[]), - naming_hygiene_refusal: None, - orphan_helper_refusal: None, module_graph_facts: ModuleGraphFactsSnapshot { edges: vec![], nodes: vec![], diff --git a/src/v1/stage0/src/cli_run/test_module_hygiene_bridge.rs b/src/v1/stage0/src/cli_run/test_module_hygiene_bridge.rs index 3287a69a190..ee802a045c5 100644 --- a/src/v1/stage0/src/cli_run/test_module_hygiene_bridge.rs +++ b/src/v1/stage0/src/cli_run/test_module_hygiene_bridge.rs @@ -21,61 +21,10 @@ fn hygiene_entry_file() -> String { TEST_MODULE_HYGIENE_ENTRY.to_string() } -#[derive(Debug, Clone)] -pub(crate) struct DeclSurface { - pub name: String, - pub refs: Vec, -} - -#[derive(Debug, Clone)] -pub(crate) struct ModuleSurface { - pub entry: String, - pub test_fns: Vec, - pub plain_fns: Vec, - pub test_data: Vec, - pub plain_data: Vec, -} - pub(crate) fn is_file_grain_function(function: &str) -> bool { function.is_empty() } -fn walk_refs( - node: &Rc, - si: &Rc>>, - out: &mut HashSet, -) { - match node.expr_data.as_ref() { - ExprData::ExprCall { .. } => { - let name = expr_call_func_at(node.clone(), si.clone()); - if !name.is_empty() { - out.insert(name); - } - } - ExprData::ExprVar { .. } => { - let name = expr_var_name_at(node.clone(), si.clone()); - if !name.is_empty() { - out.insert(name); - } - } - _ => {} - } - for child in node.children.iter() { - walk_refs(child, si, out); - } -} - -fn refs_from_body( - body: &Rc, - si: &Rc>>, -) -> Vec { - let mut set = HashSet::new(); - walk_refs(body, si, &mut set); - let mut out: Vec = set.into_iter().collect(); - out.sort(); - out -} - fn test_decl_names_from_content(content: &str) -> Result, String> { let roots = super::default_source_roots(); let ctx = resolve_hygiene_ctx(&roots)?; @@ -101,96 +50,6 @@ fn test_decl_names_from_content(content: &str) -> Result, String> { .collect() } -fn analyze_to_surface(path: &Path, content: &str, entry: &str) -> Option { - let parsed = parse_dag_file(path)?; - let test_names: HashSet = test_decl_names_from_content(content) - .ok()? - .into_iter() - .collect(); - let si = parsed.source_indices.clone(); - let mut test_fns = Vec::new(); - let mut plain_fns = Vec::new(); - let mut test_data = Vec::new(); - let mut plain_data = Vec::new(); - for item in parsed.items.iter() { - let name = authored_name_at(si.clone(), item.clone()); - if name.is_empty() { - continue; - } - let kind = item_kind(item.clone()); - match kind { - ItemKind::FnItem | ItemKind::FuncItem => { - let is_test = test_names.contains(&name); - let refs = item - .body - .as_ref() - .map(|b| refs_from_body(b, &si)) - .unwrap_or_default(); - let decl = DeclSurface { name, refs }; - if is_test { - test_fns.push(decl); - } else { - plain_fns.push(decl); - } - } - ItemKind::DataItem => { - let is_test_data = test_names.contains(&name); - let refs = item - .body - .as_ref() - .map(|b| refs_from_body(b, &si)) - .unwrap_or_default(); - let decl = DeclSurface { name, refs }; - if is_test_data { - test_data.push(decl); - } else { - plain_data.push(decl); - } - } - _ => {} - } - } - Some(ModuleSurface { - entry: entry.to_string(), - test_fns, - plain_fns, - test_data, - plain_data, - }) -} - -fn is_test_dag_path(path: &str) -> bool { - Path::new(path) - .file_name() - .and_then(|s| s.to_str()) - .map(|n| n.ends_with("_test.dag")) - .unwrap_or(false) -} - -fn collect_dag_files(root: &Path, out: &mut Vec) -> Result<(), String> { - let entries = std::fs::read_dir(root).map_err(|e| { - format!( - "orphan/umbrella hygiene: cannot read_dir {}: {e}", - root.display() - ) - })?; - for ent in entries { - let ent = ent.map_err(|e| { - format!( - "orphan/umbrella hygiene: read_dir entry under {}: {e}", - root.display() - ) - })?; - let path = ent.path(); - if path.is_dir() { - collect_dag_files(&path, out)?; - } else if path.extension().and_then(|e| e.to_str()) == Some("dag") { - out.push(path); - } - } - Ok(()) -} - fn repo_relative(path: &Path, ws: &Path) -> String { path.strip_prefix(ws) .unwrap_or(path) @@ -198,53 +57,6 @@ fn repo_relative(path: &Path, ws: &Path) -> String { .into_owned() } -fn decl_surface_to_value(decl: &DeclSurface, ctx: &InterpContext) -> Value { - let refs: Vec = decl.refs.iter().map(|r| Value::Str(r.clone())).collect(); - Value::Record { - type_name: ctx.sym("DeclSurface"), - fields: Rc::new(sorted_fields(vec![ - (ctx.sym("name"), Value::Str(decl.name.clone())), - (ctx.sym("refs"), v1_interpreter::list_value(refs)), - ])), - } -} - -fn module_surface_to_value(surface: &ModuleSurface, ctx: &InterpContext) -> Value { - let test_fns: Vec = surface - .test_fns - .iter() - .map(|d| decl_surface_to_value(d, ctx)) - .collect(); - let plain_fns: Vec = surface - .plain_fns - .iter() - .map(|d| decl_surface_to_value(d, ctx)) - .collect(); - let test_data: Vec = surface - .test_data - .iter() - .map(|d| decl_surface_to_value(d, ctx)) - .collect(); - let plain_data: Vec = surface - .plain_data - .iter() - .map(|d| decl_surface_to_value(d, ctx)) - .collect(); - Value::Record { - type_name: ctx.sym("ModuleSurface"), - fields: Rc::new(sorted_fields(vec![ - (ctx.sym("entry"), Value::Str(surface.entry.clone())), - (ctx.sym("test_fns"), v1_interpreter::list_value(test_fns)), - (ctx.sym("plain_fns"), v1_interpreter::list_value(plain_fns)), - (ctx.sym("test_data"), v1_interpreter::list_value(test_data)), - ( - ctx.sym("plain_data"), - v1_interpreter::list_value(plain_data), - ), - ])), - } -} - fn resolve_hygiene_ctx(source_roots: &[String]) -> Result { let entry = hygiene_entry_file(); let (graph, indices) = super::resolve_entry_graph_shared(source_roots, &entry) @@ -256,50 +68,6 @@ fn resolve_hygiene_ctx(source_roots: &[String]) -> Result )) } -fn collect_module_surfaces(roots: &[String]) -> Result, String> { - let ws = std::env::current_dir().map_err(|e| format!("cwd: {e}"))?; - let mut files = Vec::new(); - for root in roots { - let root_path = Path::new(root); - let abs = if root_path.is_absolute() { - root_path.to_path_buf() - } else { - ws.join(root_path) - }; - collect_dag_files(&abs, &mut files)?; - } - files.sort(); - let mut surfaces = Vec::new(); - let mut failures = Vec::new(); - for path in files { - let rel = repo_relative(&path, &ws); - if !is_test_dag_path(&rel) { - continue; - } - let content = match std::fs::read_to_string(&path) { - Ok(c) => c, - Err(e) => { - failures.push(format!("read {rel}: {e}")); - continue; - } - }; - let Some(surface) = analyze_to_surface(&path, &content, &rel) else { - failures.push(format!("parse failed: {rel}")); - continue; - }; - surfaces.push(surface); - } - if !failures.is_empty() { - return Err(format!( - "test-module orphan-helper hygiene: {} read/parse failure(s) — refuse (DESIGN §5), \ - never fabricate a clean result:\n {}", - failures.len(), - failures.join("\n ") - )); - } - Ok(surfaces) -} - #[derive(Debug, Clone, PartialEq, Eq)] pub enum FailureReceiptCompanionLookup { NotDeclared, @@ -371,50 +139,6 @@ pub(crate) fn failure_receipt_companion_from_authority( } } -pub(crate) fn check_orphan_helpers_or_err(source_roots: &[String]) -> Result<(), String> { - let surfaces = collect_module_surfaces(source_roots)?; - let ctx = resolve_hygiene_ctx(source_roots)?; - let module_values: Vec = surfaces - .iter() - .map(|s| module_surface_to_value(s, &ctx)) - .collect(); - let args = [( - Some("modules".to_string()), - v1_interpreter::list_value(module_values), - )]; - let result = v1_interpreter::run_in_context_with_args( - &ctx, - "check_orphan_surfaces_or_refuse", - &args, - false, - ) - .map_err(|e| format!("check_orphan_surfaces_or_refuse: {e}"))?; - match &result { - Value::Variant { - type_name, - variant_name, - fields, - .. - } if ctx.sym_eq(*type_name, "Optional") && ctx.sym_eq(*variant_name, "Present") => { - match ctx.field(&fields, "value") { - Some(Value::Str(reason)) => Err(reason.clone()), - _ => { - Err("check_orphan_surfaces_or_refuse Present missing reason string".to_string()) - } - } - } - Value::Variant { - type_name, - variant_name, - .. - } if ctx.sym_eq(*type_name, "Optional") && ctx.sym_eq(*variant_name, "Absent") => Ok(()), - other => Err(format!( - "check_orphan_surfaces_or_refuse returned `{}`, expected Optional", - ctx.format_value(other) - )), - } -} - pub(crate) fn enumerate_entry_test_fns(entry_path: &str) -> Result, String> { let content = std::fs::read_to_string(entry_path) .map_err(|e| format!("file-grain enumerate: read {entry_path}: {e}"))?; @@ -515,71 +239,6 @@ pub(crate) fn expand_explicit_entries( } } -pub(crate) fn module_surface_for_test( - path: &Path, - content: &str, - entry: &str, -) -> Option { - analyze_to_surface(path, content, entry) -} - -pub(crate) fn invoke_orphan_plain_names( - ctx: &InterpContext, - surface: &ModuleSurface, -) -> Result, String> { - let args = [( - Some("module".to_string()), - module_surface_to_value(surface, ctx), - )]; - let result = - v1_interpreter::run_in_context_with_args(ctx, "orphan_plain_names_or_refuse", &args, false) - .map_err(|e| format!("orphan_plain_names_or_refuse: {e}"))?; - match &result { - Value::Variant { - type_name, - variant_name, - fields, - .. - } if ctx.sym_eq(*type_name, "OrphanPlainNamesOutcome") - && ctx.sym_eq(*variant_name, "ReachBudgetRefused") => - { - match ctx.field(fields, "reason") { - Some(Value::Str(reason)) => Err(reason.clone()), - _ => Err("OrphanPlainNamesOutcome.ReachBudgetRefused missing reason".to_string()), - } - } - Value::Variant { - type_name, - variant_name, - fields, - .. - } if ctx.sym_eq(*type_name, "OrphanPlainNamesOutcome") - && ctx.sym_eq(*variant_name, "Orphans") => - { - let Value::List(items) = ctx - .field(fields, "names") - .ok_or_else(|| "OrphanPlainNamesOutcome.Orphans missing names".to_string())? - else { - return Err("OrphanPlainNamesOutcome.Orphans names is not List".to_string()); - }; - items - .iter() - .map(|item| match item { - Value::Str(s) => Ok(s.clone()), - other => Err(format!( - "orphan_plain_names_or_refuse element `{}` is not String", - ctx.format_value(other) - )), - }) - .collect() - } - other => Err(format!( - "orphan_plain_names_or_refuse returned `{}`, expected OrphanPlainNamesOutcome", - ctx.format_value(other) - )), - } -} - #[cfg(test)] mod test_module_hygiene_bridge_equivalence_tests { use super::*; @@ -597,139 +256,6 @@ mod test_module_hygiene_bridge_equivalence_tests { std::env::temp_dir().join(format!("gunbc_tmh_bridge_{n}_{id}")) } - fn check_entries_or_err(entry_paths: &[String]) -> Result<(), String> { - let mut surfaces = Vec::new(); - for entry in entry_paths { - if !is_test_dag_path(entry) { - continue; - } - let path = Path::new(entry); - let content = std::fs::read_to_string(path) - .map_err(|e| format!("orphan check read {entry}: {e}"))?; - let Some(surface) = analyze_to_surface(path, &content, entry) else { - return Err(format!("orphan check: parse failed for {entry}")); - }; - surfaces.push(surface); - } - let roots = super::super::default_source_roots(); - let ctx = resolve_hygiene_ctx(&roots)?; - let module_values: Vec = surfaces - .iter() - .map(|s| module_surface_to_value(s, &ctx)) - .collect(); - let args = [( - Some("modules".to_string()), - v1_interpreter::list_value(module_values), - )]; - let result = v1_interpreter::run_in_context_with_args( - &ctx, - "check_orphan_surfaces_or_refuse", - &args, - false, - ) - .map_err(|e| format!("check_orphan_surfaces_or_refuse: {e}"))?; - match &result { - Value::Variant { - type_name, - variant_name, - fields, - .. - } if ctx.sym_eq(*type_name, "Optional") && ctx.sym_eq(*variant_name, "Present") => { - match ctx.field(&fields, "value") { - Some(Value::Str(reason)) => Err(reason.clone()), - _ => Err("Present missing reason".to_string()), - } - } - Value::Variant { - type_name, - variant_name, - .. - } if ctx.sym_eq(*type_name, "Optional") && ctx.sym_eq(*variant_name, "Absent") => { - Ok(()) - } - other => Err(ctx.format_value(other)), - } - } - - #[test] - fn orphan_plant_reds_then_removing_greens() { - let dir = tmp_dir(); - std::fs::create_dir_all(&dir).unwrap(); - let file = dir.join("orphan_plant_test.dag"); - std::fs::write( - &file, - r#"module orphan_plant_fixture - -test fn live_holds() -> Bool { - true -} - -fn dark_unreachable_check() -> Bool { - false -} -"#, - ) - .unwrap(); - let entry = file.to_string_lossy().into_owned(); - let err = check_entries_or_err(&[entry.clone()]).expect_err("planted dark helper must red"); - assert!( - err.contains("dark_unreachable_check"), - "must name the orphan fn: {err}" - ); - std::fs::write( - &file, - r#"module orphan_plant_fixture - -test fn live_holds() -> Bool { - true -} -"#, - ) - .unwrap(); - check_entries_or_err(&[entry]).expect("removing plant must green"); - let _ = std::fs::remove_dir_all(&dir); - } - - #[test] - fn zero_enrolled_plain_helpers_refuse_demotion() { - let dir = tmp_dir(); - std::fs::create_dir_all(&dir).unwrap(); - let file = dir.join("demoted_witness_test.dag"); - std::fs::write( - &file, - r#"module demoted_witness - -fn formerly_test_holds() -> Bool { - true -} -"#, - ) - .unwrap(); - let entry = file.to_string_lossy().into_owned(); - let err = check_entries_or_err(&[entry]).expect_err("zero enrolled must refuse"); - assert!( - err.contains("formerly_test_holds"), - "must name plain: {err}" - ); - let _ = std::fs::remove_dir_all(&dir); - } - - #[test] - fn orphan_collect_refuses_unparsable_test_dag() { - let dir = tmp_dir(); - std::fs::create_dir_all(&dir).unwrap(); - let file = dir.join("garbage_unparsable_test.dag"); - std::fs::write(&file, "this is not valid dag {{{{").unwrap(); - let root = dir.to_string_lossy().into_owned(); - let err = - check_orphan_helpers_or_err(&[root]).expect_err("unparsable *_test.dag must refuse"); - assert!( - err.contains("parse failed") || err.contains("garbage_unparsable_test.dag"), - "must locate the parse failure: {err}" - ); - let _ = std::fs::remove_dir_all(&dir); - } - #[test] fn file_grain_expand_enumerates_test_decls() { let dir = tmp_dir(); @@ -781,55 +307,22 @@ fn plain_only() -> Bool { } #[test] - fn failure_receipt_companion_reachable_from_holds_witness() { - let dir = tmp_dir(); - std::fs::create_dir_all(&dir).unwrap(); - let file = dir.join("failure_receipt_companion_test.dag"); - std::fs::write( - &file, - r#"module failure_receipt_companion_fixture - -test fn witness_holds() -> Bool { - true -} - -func witness_failure_receipt() -> String { - "detail" -} -"#, - ) - .unwrap(); - let entry = file.to_string_lossy().into_owned(); - check_entries_or_err(&[entry]).expect("companion must be reachable from holds witness"); - let _ = std::fs::remove_dir_all(&dir); - } - - #[test] - fn failure_receipt_without_matching_witness_still_refuses() { - let dir = tmp_dir(); - std::fs::create_dir_all(&dir).unwrap(); - let file = dir.join("failure_receipt_orphan_test.dag"); - std::fs::write( - &file, - r#"module failure_receipt_orphan_fixture - -test fn witness_holds() -> Bool { - true -} - -func orphan_failure_receipt() -> String { - "detail" -} -"#, - ) - .unwrap(); - let entry = file.to_string_lossy().into_owned(); - let err = check_entries_or_err(&[entry]) - .expect_err("companion without matching witness must refuse"); - assert!( - err.contains("orphan_failure_receipt"), - "must name the orphan companion: {err}" + fn failure_receipt_companion_derives_the_suffix_for_both_witness_forms() { + // Direct coverage for the one convention that survived the orphan-census + // deletion. It used to be exercised only through the whole-corpus walk in + // `check_orphan_helpers_or_err`, so without this the fn would have lost its + // executing consumer along with the machinery it was tested inside. + assert_eq!( + failure_receipt_companion_from_authority("w_thing_holds"), + FailureReceiptCompanionLookup::Declared("w_thing_failure_receipt".to_string()), + ); + assert_eq!( + failure_receipt_companion_from_authority("w_thing_passes"), + FailureReceiptCompanionLookup::Declared("w_thing_failure_receipt".to_string()), + ); + assert_eq!( + failure_receipt_companion_from_authority("ordinary_fn"), + FailureReceiptCompanionLookup::NotDeclared, ); - let _ = std::fs::remove_dir_all(&dir); } } diff --git a/src/v2/workflow/floor_discovery_producer.dag b/src/v2/workflow/floor_discovery_producer.dag index fc5332b0c27..b8a2866a563 100644 --- a/src/v2/workflow/floor_discovery_producer.dag +++ b/src/v2/workflow/floor_discovery_producer.dag @@ -17,7 +17,6 @@ import v2.workflow.floor_discovery { FloorDiscoverySidecarViolation } import v2.workflow.floor_naming_hygiene { - floor_filename_contains_double_underscore, floor_test_marked_decl_allowed_in_entry, floor_wire_contract_decl_allowed_in_entry, floor_test_sidecar_suffix, @@ -471,8 +470,6 @@ fn floor_discovery_process_dag_file( ) -> FloorDiscoveryWalkState { if floor_discovery_path_excluded(path: repo_path, exclude_substrings: exclude_substrings) { state - } else if floor_filename_contains_double_underscore(path: repo_path) { - state } else { let read_out = Filesystem.Read(path: repo_path) if !read_out.success { diff --git a/src/v2/workflow/floor_naming_hygiene.dag b/src/v2/workflow/floor_naming_hygiene.dag index f09494d8ae5..8b7ff140e08 100644 --- a/src/v2/workflow/floor_naming_hygiene.dag +++ b/src/v2/workflow/floor_naming_hygiene.dag @@ -31,13 +31,6 @@ fn floor_path_strip_dot_slash_prefix(path: String) -> String { } } -fn floor_filename_contains_double_underscore(path: String) -> Bool { - string_contains( - s: floor_path_basename(path: floor_path_strip_dot_slash_prefix(path: path)), - pattern: "__" - ) -} - fn floor_entry_requires_test_sidecar(entry_path: String) -> Bool { ends_with( s: floor_path_strip_dot_slash_prefix(path: entry_path), @@ -142,28 +135,4 @@ fn floor_discovery_scan_test_decl_names(content: String) -> List { ) } -fn floor_filename_hygiene_refusal_for_paths( - dag_paths: List -) -> Optional { - fold_list( - xs: dag_paths, - empty: optional_absent(), - cons: fn(found, path) { - match found { - Present { value: _ } => found - Absent => if floor_filename_contains_double_underscore(path: path) { - optional_present( - value: concat( - "filename hygiene: `.dag` basenames must not contain `__`; offending: ", - path - ) - ) - } else { - optional_absent() - } - } - } - ) -} - -data floor_naming_hygiene_note: String = "Single authority for floor witness filename + sidecar placement + test-decl name scan predicates. Host discovery walks files and projects rows; these pure fns decide whether a path or entry placement is admissible and which decls a *_test.dag enrolls. Lives here (not in floor_discovery_producer) so orphan/filename hygiene resolve stays free of the Filesystem service closure — filesystem_io's stripped ExternalAuthority/Uri imports are Class-B accidental coverage and refuse when resolved as a small entry. Dissolves HAND-Rust mirrors in cli_run.rs check_floor_filename_hygiene and SIDECAR_PLACEMENT_RULES suffix checks once dual-run equivalence receipts are green." +data floor_naming_hygiene_note: String = "Single authority for floor witness sidecar placement + test-decl name scan predicates. Host discovery walks files and projects rows; these pure fns decide whether an entry placement is admissible and which decls a *_test.dag enrolls.\n\nCORRECTED WITH THE DELETION IT DESCRIBES (gunbc#8155): this note previously claimed two things that stopped being true in the same change that edited this file. It said the module lives here rather than in floor_discovery_producer so that \"orphan/filename hygiene resolve stays free of the Filesystem service closure\" -- there is no longer an orphan census OR a filename-hygiene resolve to keep free of anything, since the `__`-basename rule and the whole-corpus orphan-helper walk are both deleted. It also declared it would dissolve the HAND-Rust mirrors in cli_run.rs check_floor_filename_hygiene and the SIDECAR_PLACEMENT_RULES suffix checks once dual-run equivalence receipts went green -- the filename half of that obligation is discharged by deletion rather than by dissolution, and no receipt was ever needed for a rule with a zero-row population.\n\nWHAT REMAINS TRUE: the sidecar placement predicates and the test-decl line scan are still the single authority for their own questions, and the line scan is still a SECOND PARSER. Its replacement is not a refactor of this module: it needs the canonical parser to retain the authored `test` marker, which v1_compiler_parse.rs drop_leading_test_marker discards, and `test` cannot become a lex keyword because it is a live module-path segment (test.claim.*, extdeps.test.*). The prerequisite is named by v2.workflow.realization_attempt test_marker_capture_note: a GrammarExpr contextual-keyword terminal, which does not exist yet."