diff --git a/dsl/gunbc/ci_layer_roots.dag b/dsl/gunbc/ci_layer_roots.dag new file mode 100644 index 00000000000..d5e27be849a --- /dev/null +++ b/dsl/gunbc/ci_layer_roots.dag @@ -0,0 +1,20 @@ +// gunbc/ci_layer_roots.dag β€” single authority for CI witness layer roots (Β§3). +// +// Intentionally v2-import-free so dsl-only `gunbc compile` and transport scripts +// can read roots without pulling the ci_workflow projection graph. + +module gunbc.ci_layer_roots + +// Single authority for CI witness layer roots (replaces triple-fork in ci.yml / transport scripts). +data witness_layer_roots: List = ["src/v2", "dsl"] + +// Satisfies claim_batch --roster-from-discovery scan-dir gate (test-fn discovery needs no unified_claim resolve). +data witness_discovery_scan_dirs: List = ["dsl/test/claim"] + +fn witness_layer_source_flags(roots: List) -> String { + fold(roots, init: "", f: (acc, r) => concat(acc, concat(" --source-root ", r))) +} + +fn witness_layer_scan_flags() -> String { + fold(witness_discovery_scan_dirs, init: "", f: (acc, d) => concat(acc, concat(" --scan-dir ", d))) +} diff --git a/dsl/gunbc/ci_spec.dag b/dsl/gunbc/ci_spec.dag index 7e2a17e9c37..85c43b4bbc3 100644 --- a/dsl/gunbc/ci_spec.dag +++ b/dsl/gunbc/ci_spec.dag @@ -1,15 +1,10 @@ -// gunbc/ci_spec.dag β€” CI intent as data (witness roots, gates, diff policy). +// gunbc/ci_spec.dag β€” CI intent as data (gates, diff policy, transport scripts). module gunbc.ci_spec -type WitnessRoot { - root: String - scan_subtrees: List -} - -type WitnessEntry { - entry: String - function: String +import gunbc.ci_layer_roots { + witness_layer_roots, + witness_layer_source_flags } type Gate @@ -29,42 +24,25 @@ type DiffPolicy { } type CiSpec { - witness_roots: List - witness_entries: List gates: List diff_policy: DiffPolicy notice_title: String } +// 🟑 SCAFFOLD β€” feature:gate-coproduct-inhabitant-bridge β€” bind gunbc#4872 β€” +// dissolve-on-arrival: generic Symbolβ†’inhabitant replaces exhaustive match roster; +// roster completeness is witness-checked in src/v2/test/claim/ci_spec_witness_test.dag. +data gunbc_ci_gates: List = [ + RustMonolithGate, + EmitHostGate, + LayeringImportsGate, + DslCompileCleanGate, + CiYamlGate, + SourceRootIngestGate +] + data gunbc_ci_spec: CiSpec = { - witness_roots: [ - { root: "src/v2", scan_subtrees: [] }, - { root: "dsl", scan_subtrees: [] } - ], - witness_entries: [ - { entry: "dsl/test/claim/rust_stage0_gates_witness.dag", function: "rust_stage0_gates_witnesses" }, - { entry: "dsl/test/claim/emit_host_gate_witness.dag", function: "emit_host_gate_witnesses" }, - { entry: "src/v2/test/claim/layering_imports/coverage_domain_equivalence.dag", function: "coverage_domain_equivalence_argument_holds" }, - { entry: "dsl/test/claim/filesystem_entry_kind_witness.dag", function: "filesystem_entry_kind_witnesses" }, - { entry: "dsl/test/claim/power_watt_witness.dag", function: "power_watt_witnesses" }, - { entry: "dsl/test/claim/cargo_target_path_witness.dag", function: "cargo_target_path_witnesses" }, - { entry: "dsl/test/claim/dsl_compile_clean_witness.dag", function: "dsl_compile_clean_witnesses" }, - { entry: "dsl/test/claim/ci_spec_witness.dag", function: "ci_spec_witnesses" }, - { entry: "src/v2/test/claim/ci_floor_plan_witness.dag", function: "ci_floor_plan_witnesses" }, - { entry: "dsl/test/claim/ci_yaml_serializer_witness.dag", function: "ci_yaml_serializer_keystone_holds" }, - { entry: "dsl/test/claim/pareto_dominance_witness.dag", function: "pareto_dominance_holds" }, - { entry: "dsl/test/claim/hardware_selection_witness.dag", function: "hardware_selection_holds" }, - { entry: "dsl/test/claim/realization_witness.dag", function: "realization_witnesses" }, - { entry: "dsl/test/claim/realization_schedule_witness.dag", function: "realization_schedule_witnesses" } - ], - gates: [ - RustMonolithGate, - EmitHostGate, - LayeringImportsGate, - DslCompileCleanGate, - CiYamlGate, - SourceRootIngestGate - ], + gates: gunbc_ci_gates, diff_policy: { base: "origin/main", head: "HEAD", @@ -164,23 +142,19 @@ fn git_fetch_script(policy: DiffPolicy) -> String { ) } -fn witness_root_source_flag(wr: WitnessRoot) -> String { - concat(" --source-root ", wr.root) -} - // `claim_executor` takes ONLY `--source-root` (to resolve the plan) β€” it has no // `--scan-dir` flag and hard-errors on any unknown argument. Scan dirs are carried -// SOLELY by the plan (`ci_floor_plan.corpus_scan_dirs` β†’ `RunnableDiscoveryBatch. +// SOLELY by the plan (`ci_floor_plan` discovery batch `scan_dirs` from +// `gunbc.ci_layer_roots.witness_discovery_scan_dirs` β†’ `RunnableDiscoveryBatch. // scan_dirs`), the single authority for that derivation (DESIGN Β§3); emitting them // on the argv too would be a redundant, fail-closed mis-wire (DESIGN Β§5). -fn witness_roots_source_flags(roots: List) -> String { - fold(roots, init: "", f: (acc, wr) => concat(acc, witness_root_source_flag(wr: wr))) -} +// +// Layer roots: `gunbc.ci_layer_roots.witness_layer_roots` (Β§3 single authority). // The dependency-ordered scheduler plan (the single floor topology, DESIGN Β§3) and // the host entry the executor evaluates. The scheduler β€” NOT a flat claim_batch + // gates pair β€” is the default CI runtime: both the discovery corpus and the gates -// are scheduler plan nodes (`dsl/gunbc/ci_floor_plan.dag`), run dependency-ordered +// are scheduler plan nodes (`src/v2/workflow/ci_floor_plan.dag`), run dependency-ordered // and parallel-where-independent, fail-closed (DESIGN Β§4/Β§5). data floor_plan_entry: String = "src/v2/workflow/ci_floor_plan.dag" data floor_plan_function: String = "gunbc_ci_floor_batches" @@ -189,7 +163,7 @@ fn scheduler_invoke(spec: CiSpec) -> String { concat( concat( concat( - concat(concat(claim_executor_bin_shell(), witness_roots_source_flags(roots: spec.witness_roots)), concat(" --plan-entry ", floor_plan_entry)), + concat(concat(claim_executor_bin_shell(), witness_layer_source_flags(roots: witness_layer_roots)), concat(" --plan-entry ", floor_plan_entry)), concat(" --plan-function ", floor_plan_function) ), concat(concat(" --notice-title \"", spec.notice_title), "\"") diff --git a/dsl/test/claim/cargo_target_path_witness.dag b/dsl/test/claim/cargo_target_path_witness_test.dag similarity index 98% rename from dsl/test/claim/cargo_target_path_witness.dag rename to dsl/test/claim/cargo_target_path_witness_test.dag index c26a19a1dfa..ca43f5f18b9 100644 --- a/dsl/test/claim/cargo_target_path_witness.dag +++ b/dsl/test/claim/cargo_target_path_witness_test.dag @@ -88,7 +88,7 @@ fn edition_tokens_render() -> Bool { && rust_edition_str(edition: default_rust_edition) == "2021" } -fn cargo_target_path_witnesses() -> Bool { +test fn cargo_target_path_witnesses() -> Bool { lib_lands_on_src_lib() && package_bin_collapses_to_main() && other_bin_lands_under_src_bin() diff --git a/dsl/test/claim/ci_yaml_serializer_witness.dag b/dsl/test/claim/ci_yaml_serializer_witness_test.dag similarity index 96% rename from dsl/test/claim/ci_yaml_serializer_witness.dag rename to dsl/test/claim/ci_yaml_serializer_witness_test.dag index 73d6bdbe615..e351e5e3ced 100644 --- a/dsl/test/claim/ci_yaml_serializer_witness.dag +++ b/dsl/test/claim/ci_yaml_serializer_witness_test.dag @@ -169,8 +169,6 @@ fn witness_sccache_wired_when_fleet_daemon_up() -> Bool { !string_contains(s: yaml, pattern: "SCCACHE_DIR=$RUNNER_TEMP/sccache") } -// Full-file drift gate (`ci_yml_drifted` == byte-identical to `expected_ci_yml()`); these -// pin distinctive `gunbc_ci_workflow_run` projections in the committed step body. fn witness_gunbc_ci_step_projects_run_script_markers() -> Bool { let yaml = expected_ci_yml() let script = gunbc_ci_workflow_run(spec: gunbc_ci_spec) @@ -179,7 +177,7 @@ fn witness_gunbc_ci_step_projects_run_script_markers() -> Bool { string_contains(s: script, pattern: "env -u RUSTC_WRAPPER") } -fn ci_yaml_serializer_keystone_holds() -> Bool { +test fn ci_yaml_serializer_keystone_holds() -> Bool { witness_fetch_depth_is_int_not_string() && witness_rustflags_empty_string_quoted() && witness_steps_sequence_elements_are_yaml_values() && diff --git a/dsl/test/claim/dsl_compile_clean_witness.dag b/dsl/test/claim/dsl_compile_clean_witness_test.dag similarity index 94% rename from dsl/test/claim/dsl_compile_clean_witness.dag rename to dsl/test/claim/dsl_compile_clean_witness_test.dag index ec20a28f1f2..e4c88739182 100644 --- a/dsl/test/claim/dsl_compile_clean_witness.dag +++ b/dsl/test/claim/dsl_compile_clean_witness_test.dag @@ -20,6 +20,6 @@ fn witness_perturb_optional_skew_goes_red() -> Bool { run_perturb_optional_skew_red_receipt() } -fn dsl_compile_clean_witnesses() -> Bool { +test fn dsl_compile_clean_witnesses() -> Bool { witness_clean_tree_compile_holds() && witness_perturb_optional_skew_goes_red() } diff --git a/dsl/test/claim/emit_host_gate_witness.dag b/dsl/test/claim/emit_host_gate_witness_test.dag similarity index 97% rename from dsl/test/claim/emit_host_gate_witness.dag rename to dsl/test/claim/emit_host_gate_witness_test.dag index 8d847d56fcf..088fc7f3da2 100644 --- a/dsl/test/claim/emit_host_gate_witness.dag +++ b/dsl/test/claim/emit_host_gate_witness_test.dag @@ -47,7 +47,7 @@ fn w_ts_byte_count_is_four() -> Bool { } // Conjunction keystone β€” CI runs with --claim-run (false β†’ exit 1). -fn emit_host_gate_witnesses() -> Bool { +test fn emit_host_gate_witnesses() -> Bool { w_rust_fixture_nonempty() && w_python_fixture_nonempty() && w_go_fixture_nonempty() && diff --git a/dsl/test/claim/filesystem_entry_kind_witness.dag b/dsl/test/claim/filesystem_entry_kind_witness_test.dag similarity index 97% rename from dsl/test/claim/filesystem_entry_kind_witness.dag rename to dsl/test/claim/filesystem_entry_kind_witness_test.dag index cfb185616e5..7e5e965dd2a 100644 --- a/dsl/test/claim/filesystem_entry_kind_witness.dag +++ b/dsl/test/claim/filesystem_entry_kind_witness_test.dag @@ -54,7 +54,7 @@ fn w_ntfs_normal_is_regular() -> Bool { } // Conjunction keystone β€” CI runs with --claim-run (false β†’ exit 1). -fn filesystem_entry_kind_witnesses() -> Bool { +test fn filesystem_entry_kind_witnesses() -> Bool { w_posix_socket_is_other() && w_posix_regular_is_regular() && w_posix_symlink_is_symlink() && diff --git a/dsl/test/claim/hardware_selection_witness.dag b/dsl/test/claim/hardware_selection_witness_test.dag similarity index 99% rename from dsl/test/claim/hardware_selection_witness.dag rename to dsl/test/claim/hardware_selection_witness_test.dag index 6d295c17f5d..86e31efed16 100644 --- a/dsl/test/claim/hardware_selection_witness.dag +++ b/dsl/test/claim/hardware_selection_witness_test.dag @@ -244,7 +244,7 @@ fn derived_ratios_are_computed() -> Bool { && bandwidth_per_watt(a: cfg_a_axes) > bandwidth_per_watt(a: cfg_b_axes) } -fn hardware_selection_holds() -> Bool { +test fn hardware_selection_holds() -> Bool { downclock_is_rank_dependent() && psu_sizing_is_computed() && rank_tradeoff_is_incomparable() diff --git a/dsl/test/claim/pareto_dominance_witness.dag b/dsl/test/claim/pareto_dominance_witness_test.dag similarity index 98% rename from dsl/test/claim/pareto_dominance_witness.dag rename to dsl/test/claim/pareto_dominance_witness_test.dag index 3352819e724..4a5a6ed83b2 100644 --- a/dsl/test/claim/pareto_dominance_witness.dag +++ b/dsl/test/claim/pareto_dominance_witness_test.dag @@ -88,7 +88,7 @@ fn undominated_candidate_stays_on_front() -> Bool { on_pareto_front(verdicts: pool) && !any_dominates(verdicts: pool) } -fn pareto_dominance_holds() -> Bool { +test fn pareto_dominance_holds() -> Bool { dominates_when_better_on_all_axes() && dominated_is_the_mirror() && equivalent_when_identical() diff --git a/dsl/test/claim/power_watt_witness.dag b/dsl/test/claim/power_watt_witness_test.dag similarity index 98% rename from dsl/test/claim/power_watt_witness.dag rename to dsl/test/claim/power_watt_witness_test.dag index 68bfbda80e1..e7b1331a77c 100644 --- a/dsl/test/claim/power_watt_witness.dag +++ b/dsl/test/claim/power_watt_witness_test.dag @@ -44,7 +44,7 @@ fn watt_roundtrips() -> Bool { watt_count(w: watt(count: 183)) == 183 } -fn power_watt_witnesses() -> Bool { +test fn power_watt_witnesses() -> Bool { watt_roundtrips() && empty_budget_is_zero_watts() && total_power_is_computed_sum() diff --git a/dsl/test/claim/realization_schedule_witness.dag b/dsl/test/claim/realization_schedule_witness_test.dag similarity index 98% rename from dsl/test/claim/realization_schedule_witness.dag rename to dsl/test/claim/realization_schedule_witness_test.dag index b4d6bb06dc9..45ed2e80f24 100644 --- a/dsl/test/claim/realization_schedule_witness.dag +++ b/dsl/test/claim/realization_schedule_witness_test.dag @@ -79,7 +79,7 @@ fn witness_schedule_lens_red_when_corpus_runs_first_on_synthetic() -> Bool { } } -fn realization_schedule_witnesses() -> Bool { +test fn realization_schedule_witnesses() -> Bool { witness_schedule_lens_green_on_synthetic_plan() && witness_schedule_lens_red_when_corpus_runs_first_on_synthetic() } diff --git a/dsl/test/claim/realization_witness.dag b/dsl/test/claim/realization_witness_test.dag similarity index 97% rename from dsl/test/claim/realization_witness.dag rename to dsl/test/claim/realization_witness_test.dag index ff61f082cee..9cbc9602f8a 100644 --- a/dsl/test/claim/realization_witness.dag +++ b/dsl/test/claim/realization_witness_test.dag @@ -52,7 +52,7 @@ fn witness_upsert_different_keys_independent() -> Bool { ) == Independent } -fn realization_witnesses() -> Bool { +test fn realization_witnesses() -> Bool { witness_unknown_independence_never_reorders() && witness_read_read_independent() && witness_create_unknown_not_independent() diff --git a/dsl/test/claim/rust_stage0_gates_witness.dag b/dsl/test/claim/rust_stage0_gates_witness_test.dag similarity index 94% rename from dsl/test/claim/rust_stage0_gates_witness.dag rename to dsl/test/claim/rust_stage0_gates_witness_test.dag index f9f31e1ea28..7fecb35bdf1 100644 --- a/dsl/test/claim/rust_stage0_gates_witness.dag +++ b/dsl/test/claim/rust_stage0_gates_witness_test.dag @@ -47,8 +47,8 @@ fn w_git_fail_fires() -> Bool { should_run_gates(paths: [], git_success: false) == true } -// Conjunction keystone β€” the single fn the CI corpus runs (--claim-run; false β†’ exit 1). -fn rust_stage0_gates_witnesses() -> Bool { +// Conjunction keystone β€” the single test fn the CI corpus runs (--claim-run; false β†’ exit 1). +test fn rust_stage0_gates_witnesses() -> Bool { w_rs_selects() && w_cargo_toml_selects() && w_toolchain_selects() && diff --git a/dsl/tools/floor_effect_gate_witness.dag b/dsl/tools/floor_effect_gate_witness.dag index 55f1788d36e..529815994c0 100644 --- a/dsl/tools/floor_effect_gate_witness.dag +++ b/dsl/tools/floor_effect_gate_witness.dag @@ -2,45 +2,40 @@ // CI gates, so the dependency-ordered scheduler (claim_executor) can run each gate // as a plan node (`RunnableSingleClaim` is nullary β€” one `(entry, function)`). // -// REUSE, not re-coin (DESIGN Β§2/Β§3): each adapter pins one closed `Gate` variant and -// calls that gate's composed CI entry (`run_*_ci_gate`). Do NOT import -// `tools.ci_gates` here β€” its composed `main`/`run_ci_gates` fold loops under the -// v1 interpreter (#5138); production CI runs one gate per witness adapter. +// REUSE, not re-coin (DESIGN Β§2/Β§3): the Gateβ†’effect mapping stays the single +// authority `tools.ci_gates.run_spec_gate` (itself driven by `gunbc_ci_spec.gates`). +// Each adapter just pins one closed `Gate` variant and maps its ProcessExit to the +// Bool the scheduler's per-node verdict needs. These are `func` (effectful) β€” the +// host runs them in Wet mode so the transports fire. module tools.floor_effect_gate_witness -import std.process { ProcessExit, ExitSuccess } -import tools.rust_gates_ci { run_rust_monolith_gate } -import tools.emit_host_gate { run_emit_host_ci_gate } -import tools.layering_imports_gate { run_layering_imports_ci_gate } -import tools.dsl_compile_clean_gate { run_dsl_compile_clean_gate } -import tools.ci_yaml_gate { run_ci_yaml_gate } -import tools.source_root_ingest_gate { run_source_root_ingest_ci_gate } - -fn exit_ok(e: ProcessExit) -> Bool { - match e { ExitSuccess => true _ => false } +import gunbc.ci_spec { + RustMonolithGate, EmitHostGate, LayeringImportsGate, DslCompileCleanGate, CiYamlGate, + SourceRootIngestGate } +import tools.ci_gates { exit_ok, run_spec_gate } func rust_monolith_gate_passes() -> Bool { - exit_ok(e: run_rust_monolith_gate()) + exit_ok(e: run_spec_gate(g: RustMonolithGate)) } func emit_host_gate_passes() -> Bool { - exit_ok(e: run_emit_host_ci_gate()) + exit_ok(e: run_spec_gate(g: EmitHostGate)) } func layering_imports_gate_passes() -> Bool { - exit_ok(e: run_layering_imports_ci_gate()) + exit_ok(e: run_spec_gate(g: LayeringImportsGate)) } func dsl_compile_clean_gate_passes() -> Bool { - exit_ok(e: run_dsl_compile_clean_gate()) + exit_ok(e: run_spec_gate(g: DslCompileCleanGate)) } func ci_yaml_gate_passes() -> Bool { - exit_ok(e: run_ci_yaml_gate()) + exit_ok(e: run_spec_gate(g: CiYamlGate)) } func source_root_ingest_gate_passes() -> Bool { - exit_ok(e: run_source_root_ingest_ci_gate()) + exit_ok(e: run_spec_gate(g: SourceRootIngestGate)) } diff --git a/dsl/tools/layering_imports_transport.dag b/dsl/tools/layering_imports_transport.dag index 792ffee34cf..3f131539e85 100644 --- a/dsl/tools/layering_imports_transport.dag +++ b/dsl/tools/layering_imports_transport.dag @@ -7,35 +7,128 @@ module tools.layering_imports_transport import extdeps.shell +import gunbc.ci_layer_roots { witness_layer_roots, witness_layer_source_flags } import std.logic { Bool } +fn gunbc_run_with_manifest_overlay() -> String { + concat( + concat("\"$GUNBC\" run", witness_layer_source_flags(roots: witness_layer_roots)), + " --source-root \"$MANIFEST_DIR\"" + ) +} + // Green pass: scan live repo, assert clean_tree witness holds. fn run_clean_tree_gate() -> Bool { - let script = "set -e\nROOT=\"$(git rev-parse --show-toplevel 2>/dev/null || pwd)\"\ncd \"$ROOT\"\nMANIFEST=\"$ROOT/target/v2-layering-imports-manifest.dag\"\nMANIFEST_DIR=\"$(dirname \"$MANIFEST\")\"\nbash scripts/layering-imports-scan.sh \"$ROOT\" \"$MANIFEST\"\nGUNBC=\"$ROOT/target/release/gunbc\"\nif [ ! -x \"$GUNBC\" ]; then cargo build -p v1-compiler --release --bin gunbc || exit 1; fi\n\"$GUNBC\" run --source-root src/v2 --source-root \"$MANIFEST_DIR\" --entry src/v2/test/claim/layering_imports/clean_tree.dag --function clean_tree_no_wrong_direction_imports_holds --claim-run" + let script = concat( + concat( + concat( + "set -e\nROOT=\"$(git rev-parse --show-toplevel 2>/dev/null || pwd)\"\ncd \"$ROOT\"\nMANIFEST=\"$ROOT/target/v2-layering-imports-manifest.dag\"\nMANIFEST_DIR=\"$(dirname \"$MANIFEST\")\"\nbash scripts/layering-imports-scan.sh \"$ROOT\" \"$MANIFEST\"\nGUNBC=\"$ROOT/target/release/gunbc\"\nif [ ! -x \"$GUNBC\" ]; then cargo build -p v1-compiler --release --bin gunbc || exit 1; fi\n", + gunbc_run_with_manifest_overlay() + ), + " --entry src/v2/test/claim/layering_imports/clean_tree.dag --function clean_tree_no_wrong_direction_imports_holds --claim-run" + ), + "" + ) let result = shell.Exec.Run(script: script) result.success } fn run_perturb_std_v4_prefix() -> Bool { - let script = "set -e\nROOT=\"$(git rev-parse --show-toplevel 2>/dev/null || pwd)\"\nGUNBC=\"$ROOT/target/release/gunbc\"\nif [ ! -x \"$GUNBC\" ]; then (cd \"$ROOT\" && cargo build -p v1-compiler --release --bin gunbc) || exit 1; fi\nTMP=$(mktemp -d)\nMANIFEST=\"$TMP/v2-layering-imports-manifest.dag\"\nMANIFEST_DIR=\"$TMP\"\nmkdir -p \"$TMP/src/v2/std\"\ncat >\"$TMP/src/v2/std/_perturb_scanner_std_v4_prefix.dag\" <<'PLANT'\nmodule v2.std._perturb_scanner_std_v4_prefix\nimport v2.compiler.tokenize\ndata perturb: Bool = true\nPLANT\nbash \"$ROOT/scripts/layering-imports-scan.sh\" \"$TMP\" \"$MANIFEST\"\n\"$GUNBC\" run --source-root src/v2 --source-root \"$MANIFEST_DIR\" --entry src/v2/test/claim/layering_imports/scanner/std_v4_prefix.dag --function scanner_std_v4_prefix_detects_holds --claim-run\nif \"$GUNBC\" run --source-root src/v2 --source-root \"$MANIFEST_DIR\" --entry src/v2/test/claim/layering_imports/clean_tree.dag --function clean_tree_no_wrong_direction_imports_holds --claim-run 2>/dev/null; then echo 'clean_tree should be red' >&2; rm -rf \"$TMP\"; exit 1; fi\nrm -f \"$TMP/src/v2/std/_perturb_scanner_std_v4_prefix.dag\"\nbash \"$ROOT/scripts/layering-imports-scan.sh\" \"$TMP\" \"$MANIFEST\"\n\"$GUNBC\" run --source-root src/v2 --source-root \"$MANIFEST_DIR\" --entry src/v2/test/claim/layering_imports/clean_tree.dag --function clean_tree_no_wrong_direction_imports_holds --claim-run\nrm -rf \"$TMP\"" + let run = gunbc_run_with_manifest_overlay() + let script = concat( + concat( + concat( + concat( + "set -e\nROOT=\"$(git rev-parse --show-toplevel 2>/dev/null || pwd)\"\nGUNBC=\"$ROOT/target/release/gunbc\"\nif [ ! -x \"$GUNBC\" ]; then (cd \"$ROOT\" && cargo build -p v1-compiler --release --bin gunbc) || exit 1; fi\nTMP=$(mktemp -d)\nMANIFEST=\"$TMP/v2-layering-imports-manifest.dag\"\nMANIFEST_DIR=\"$TMP\"\nmkdir -p \"$TMP/src/v2/std\"\ncat >\"$TMP/src/v2/std/_perturb_scanner_std_v4_prefix.dag\" <<'PLANT'\nmodule v2.std._perturb_scanner_std_v4_prefix\nimport v2.compiler.tokenize\ndata perturb: Bool = true\nPLANT\nbash \"$ROOT/scripts/layering-imports-scan.sh\" \"$TMP\" \"$MANIFEST\"\n", + concat(run, " --entry src/v2/test/claim/layering_imports/scanner/std_v4_prefix.dag --function scanner_std_v4_prefix_detects_holds --claim-run\n") + ), + concat("if ", run, " --entry src/v2/test/claim/layering_imports/clean_tree.dag --function clean_tree_no_wrong_direction_imports_holds --claim-run 2>/dev/null; then echo 'clean_tree should be red' >&2; rm -rf \"$TMP\"; exit 1; fi\n") + ), + concat( + concat( + "rm -f \"$TMP/src/v2/std/_perturb_scanner_std_v4_prefix.dag\"\nbash \"$ROOT/scripts/layering-imports-scan.sh\" \"$TMP\" \"$MANIFEST\"\n", + concat(run, " --entry src/v2/test/claim/layering_imports/clean_tree.dag --function clean_tree_no_wrong_direction_imports_holds --claim-run\nrm -rf \"$TMP\"") + ), + "" + ) + ), + "" + ) let result = shell.Exec.Run(script: script) result.success } fn run_perturb_extdeps_v4_prefix() -> Bool { - let script = "set -e\nROOT=\"$(git rev-parse --show-toplevel 2>/dev/null || pwd)\"\nGUNBC=\"$ROOT/target/release/gunbc\"\nif [ ! -x \"$GUNBC\" ]; then (cd \"$ROOT\" && cargo build -p v1-compiler --release --bin gunbc) || exit 1; fi\nTMP=$(mktemp -d)\nMANIFEST=\"$TMP/v2-layering-imports-manifest.dag\"\nMANIFEST_DIR=\"$TMP\"\nmkdir -p \"$TMP/src/v2/extdeps\"\ncat >\"$TMP/src/v2/extdeps/_perturb_scanner_extdeps_v4_prefix.dag\" <<'PLANT'\nmodule v2.extdeps._perturb_scanner_extdeps_v4_prefix\nimport v2.compiler.parse\ndata perturb: Bool = true\nPLANT\nbash \"$ROOT/scripts/layering-imports-scan.sh\" \"$TMP\" \"$MANIFEST\"\n\"$GUNBC\" run --source-root src/v2 --source-root \"$MANIFEST_DIR\" --entry src/v2/test/claim/layering_imports/scanner/extdeps_v4_prefix.dag --function scanner_extdeps_v4_prefix_detects_holds --claim-run\nif \"$GUNBC\" run --source-root src/v2 --source-root \"$MANIFEST_DIR\" --entry src/v2/test/claim/layering_imports/clean_tree.dag --function clean_tree_no_wrong_direction_imports_holds --claim-run 2>/dev/null; then echo 'clean_tree should be red' >&2; rm -rf \"$TMP\"; exit 1; fi\nrm -f \"$TMP/src/v2/extdeps/_perturb_scanner_extdeps_v4_prefix.dag\"\nbash \"$ROOT/scripts/layering-imports-scan.sh\" \"$TMP\" \"$MANIFEST\"\n\"$GUNBC\" run --source-root src/v2 --source-root \"$MANIFEST_DIR\" --entry src/v2/test/claim/layering_imports/clean_tree.dag --function clean_tree_no_wrong_direction_imports_holds --claim-run\nrm -rf \"$TMP\"" + let run = gunbc_run_with_manifest_overlay() + let script = concat( + concat( + concat( + concat( + "set -e\nROOT=\"$(git rev-parse --show-toplevel 2>/dev/null || pwd)\"\nGUNBC=\"$ROOT/target/release/gunbc\"\nif [ ! -x \"$GUNBC\" ]; then (cd \"$ROOT\" && cargo build -p v1-compiler --release --bin gunbc) || exit 1; fi\nTMP=$(mktemp -d)\nMANIFEST=\"$TMP/v2-layering-imports-manifest.dag\"\nMANIFEST_DIR=\"$TMP\"\nmkdir -p \"$TMP/src/v2/extdeps\"\ncat >\"$TMP/src/v2/extdeps/_perturb_scanner_extdeps_v4_prefix.dag\" <<'PLANT'\nmodule v2.extdeps._perturb_scanner_extdeps_v4_prefix\nimport v2.compiler.parse\ndata perturb: Bool = true\nPLANT\nbash \"$ROOT/scripts/layering-imports-scan.sh\" \"$TMP\" \"$MANIFEST\"\n", + concat(run, " --entry src/v2/test/claim/layering_imports/scanner/extdeps_v4_prefix.dag --function scanner_extdeps_v4_prefix_detects_holds --claim-run\n") + ), + concat("if ", run, " --entry src/v2/test/claim/layering_imports/clean_tree.dag --function clean_tree_no_wrong_direction_imports_holds --claim-run 2>/dev/null; then echo 'clean_tree should be red' >&2; rm -rf \"$TMP\"; exit 1; fi\n") + ), + concat( + concat( + "rm -f \"$TMP/src/v2/extdeps/_perturb_scanner_extdeps_v4_prefix.dag\"\nbash \"$ROOT/scripts/layering-imports-scan.sh\" \"$TMP\" \"$MANIFEST\"\n", + concat(run, " --entry src/v2/test/claim/layering_imports/clean_tree.dag --function clean_tree_no_wrong_direction_imports_holds --claim-run\nrm -rf \"$TMP\"") + ), + "" + ) + ), + "" + ) let result = shell.Exec.Run(script: script) result.success } fn run_perturb_dsl_extdeps_v4_exact() -> Bool { - let script = "set -e\nROOT=\"$(git rev-parse --show-toplevel 2>/dev/null || pwd)\"\nGUNBC=\"$ROOT/target/release/gunbc\"\nif [ ! -x \"$GUNBC\" ]; then (cd \"$ROOT\" && cargo build -p v1-compiler --release --bin gunbc) || exit 1; fi\nTMP=$(mktemp -d)\nMANIFEST=\"$TMP/v2-layering-imports-manifest.dag\"\nMANIFEST_DIR=\"$TMP\"\nmkdir -p \"$TMP/dsl/extdeps\"\ncat >\"$TMP/dsl/extdeps/_perturb_scanner_dsl_extdeps_v4_exact.dag\" <<'PLANT'\nmodule dsl.extdeps._perturb_scanner_dsl_extdeps_v4_exact\nimport v2.compiler\ndata perturb: Bool = true\nPLANT\nbash \"$ROOT/scripts/layering-imports-scan.sh\" \"$TMP\" \"$MANIFEST\"\n\"$GUNBC\" run --source-root src/v2 --source-root \"$MANIFEST_DIR\" --entry src/v2/test/claim/layering_imports/scanner/dsl_extdeps_v4_exact.dag --function scanner_dsl_extdeps_v4_exact_detects_holds --claim-run\nif \"$GUNBC\" run --source-root src/v2 --source-root \"$MANIFEST_DIR\" --entry src/v2/test/claim/layering_imports/clean_tree.dag --function clean_tree_no_wrong_direction_imports_holds --claim-run 2>/dev/null; then echo 'clean_tree should be red' >&2; rm -rf \"$TMP\"; exit 1; fi\nrm -f \"$TMP/dsl/extdeps/_perturb_scanner_dsl_extdeps_v4_exact.dag\"\nbash \"$ROOT/scripts/layering-imports-scan.sh\" \"$TMP\" \"$MANIFEST\"\n\"$GUNBC\" run --source-root src/v2 --source-root \"$MANIFEST_DIR\" --entry src/v2/test/claim/layering_imports/clean_tree.dag --function clean_tree_no_wrong_direction_imports_holds --claim-run\nrm -rf \"$TMP\"" + let run = gunbc_run_with_manifest_overlay() + let script = concat( + concat( + concat( + concat( + "set -e\nROOT=\"$(git rev-parse --show-toplevel 2>/dev/null || pwd)\"\nGUNBC=\"$ROOT/target/release/gunbc\"\nif [ ! -x \"$GUNBC\" ]; then (cd \"$ROOT\" && cargo build -p v1-compiler --release --bin gunbc) || exit 1; fi\nTMP=$(mktemp -d)\nMANIFEST=\"$TMP/v2-layering-imports-manifest.dag\"\nMANIFEST_DIR=\"$TMP\"\nmkdir -p \"$TMP/dsl/extdeps\"\ncat >\"$TMP/dsl/extdeps/_perturb_scanner_dsl_extdeps_v4_exact.dag\" <<'PLANT'\nmodule dsl.extdeps._perturb_scanner_dsl_extdeps_v4_exact\nimport v2.compiler\ndata perturb: Bool = true\nPLANT\nbash \"$ROOT/scripts/layering-imports-scan.sh\" \"$TMP\" \"$MANIFEST\"\n", + concat(run, " --entry src/v2/test/claim/layering_imports/scanner/dsl_extdeps_v4_exact.dag --function scanner_dsl_extdeps_v4_exact_detects_holds --claim-run\n") + ), + concat("if ", run, " --entry src/v2/test/claim/layering_imports/clean_tree.dag --function clean_tree_no_wrong_direction_imports_holds --claim-run 2>/dev/null; then echo 'clean_tree should be red' >&2; rm -rf \"$TMP\"; exit 1; fi\n") + ), + concat( + concat( + "rm -f \"$TMP/dsl/extdeps/_perturb_scanner_dsl_extdeps_v4_exact.dag\"\nbash \"$ROOT/scripts/layering-imports-scan.sh\" \"$TMP\" \"$MANIFEST\"\n", + concat(run, " --entry src/v2/test/claim/layering_imports/clean_tree.dag --function clean_tree_no_wrong_direction_imports_holds --claim-run\nrm -rf \"$TMP\"") + ), + "" + ) + ), + "" + ) let result = shell.Exec.Run(script: script) result.success } fn run_perturb_parse_level_pre_resolve() -> Bool { - let script = "set -e\nROOT=\"$(git rev-parse --show-toplevel 2>/dev/null || pwd)\"\nGUNBC=\"$ROOT/target/release/gunbc\"\nif [ ! -x \"$GUNBC\" ]; then (cd \"$ROOT\" && cargo build -p v1-compiler --release --bin gunbc) || exit 1; fi\nTMP=$(mktemp -d)\nMANIFEST=\"$TMP/v2-layering-imports-manifest.dag\"\nMANIFEST_DIR=\"$TMP\"\nmkdir -p \"$TMP/src/v2/std\"\ncat >\"$TMP/src/v2/std/_perturb_scanner_parse_level_pre_resolve.dag\" <<'PLANT'\nmodule v2.std._perturb_scanner_parse_level_pre_resolve\nimport v2.compiler.tokenize\nimport totally.broken.unresolved.module\ndata x: String = \"unresolved\"\nPLANT\nbash \"$ROOT/scripts/layering-imports-scan.sh\" \"$TMP\" \"$MANIFEST\"\n\"$GUNBC\" run --source-root src/v2 --source-root \"$MANIFEST_DIR\" --entry src/v2/test/claim/layering_imports/scanner/parse_level_pre_resolve.dag --function scanner_parse_level_pre_resolve_detects_holds --claim-run\nif \"$GUNBC\" run --source-root src/v2 --source-root \"$MANIFEST_DIR\" --entry src/v2/test/claim/layering_imports/clean_tree.dag --function clean_tree_no_wrong_direction_imports_holds --claim-run 2>/dev/null; then echo 'clean_tree should be red' >&2; rm -rf \"$TMP\"; exit 1; fi\nrm -f \"$TMP/src/v2/std/_perturb_scanner_parse_level_pre_resolve.dag\"\nbash \"$ROOT/scripts/layering-imports-scan.sh\" \"$TMP\" \"$MANIFEST\"\n\"$GUNBC\" run --source-root src/v2 --source-root \"$MANIFEST_DIR\" --entry src/v2/test/claim/layering_imports/clean_tree.dag --function clean_tree_no_wrong_direction_imports_holds --claim-run\nrm -rf \"$TMP\"" + let run = gunbc_run_with_manifest_overlay() + let script = concat( + concat( + concat( + concat( + "set -e\nROOT=\"$(git rev-parse --show-toplevel 2>/dev/null || pwd)\"\nGUNBC=\"$ROOT/target/release/gunbc\"\nif [ ! -x \"$GUNBC\" ]; then (cd \"$ROOT\" && cargo build -p v1-compiler --release --bin gunbc) || exit 1; fi\nTMP=$(mktemp -d)\nMANIFEST=\"$TMP/v2-layering-imports-manifest.dag\"\nMANIFEST_DIR=\"$TMP\"\nmkdir -p \"$TMP/src/v2/std\"\ncat >\"$TMP/src/v2/std/_perturb_scanner_parse_level_pre_resolve.dag\" <<'PLANT'\nmodule v2.std._perturb_scanner_parse_level_pre_resolve\nimport v2.compiler.tokenize\nimport totally.broken.unresolved.module\ndata x: String = \"unresolved\"\nPLANT\nbash \"$ROOT/scripts/layering-imports-scan.sh\" \"$TMP\" \"$MANIFEST\"\n", + concat(run, " --entry src/v2/test/claim/layering_imports/scanner/parse_level_pre_resolve.dag --function scanner_parse_level_pre_resolve_detects_holds --claim-run\n") + ), + concat("if ", run, " --entry src/v2/test/claim/layering_imports/clean_tree.dag --function clean_tree_no_wrong_direction_imports_holds --claim-run 2>/dev/null; then echo 'clean_tree should be red' >&2; rm -rf \"$TMP\"; exit 1; fi\n") + ), + concat( + concat( + "rm -f \"$TMP/src/v2/std/_perturb_scanner_parse_level_pre_resolve.dag\"\nbash \"$ROOT/scripts/layering-imports-scan.sh\" \"$TMP\" \"$MANIFEST\"\n", + concat(run, " --entry src/v2/test/claim/layering_imports/clean_tree.dag --function clean_tree_no_wrong_direction_imports_holds --claim-run\nrm -rf \"$TMP\"") + ), + "" + ) + ), + "" + ) let result = shell.Exec.Run(script: script) result.success } diff --git a/src/v2/test/claim/ci_floor_plan_witness.dag b/src/v2/test/claim/ci_floor_plan_witness_test.dag similarity index 61% rename from src/v2/test/claim/ci_floor_plan_witness.dag rename to src/v2/test/claim/ci_floor_plan_witness_test.dag index 1518a2a55b3..c8bb25505b2 100644 --- a/src/v2/test/claim/ci_floor_plan_witness.dag +++ b/src/v2/test/claim/ci_floor_plan_witness_test.dag @@ -1,4 +1,4 @@ -// v2/test/claim/ci_floor_plan_witness.dag +// v2/test/claim/ci_floor_plan_witness_test.dag // // Model witnesses for the spec-derived CI floor plan (`v2.workflow.ci_floor_plan`) β€” the // topology the scheduler runs as the default CI runtime. These prove the ORDER and @@ -14,23 +14,12 @@ import std.realization { Runnable, RunnableSingleClaim, RunnableDiscoveryBatch, } import v2.workflow.ci_floor_plan { - gunbc_ci_floor_batches, corpus_scan_dirs + gunbc_ci_floor_batches, corpus_runnable } -import gunbc.ci_spec { CiSpec, WitnessRoot, DiffPolicy, DiffMergeBase } -import v2.std.algebra { list_snoc_item } +import gunbc.ci_layer_roots { witness_discovery_scan_dirs, witness_layer_roots } +import v2.std.algebra { length, list_snoc_item } import v2.std.collection { List } -// A spec whose witness root carries a `scan_subtrees` β€” the scan dir must surface -// in the PLAN (`corpus_scan_dirs`), the single authority for that derivation, NOT -// on the `claim_executor` argv (proven absent in `ci_spec_witness`). -data spec_with_scan_subtree: CiSpec = { - witness_roots: [{ root: "src/v2", scan_subtrees: ["test"] }], - witness_entries: [], - gates: [], - diff_policy: { base: "origin/main", head: "HEAD", mode: DiffMergeBase }, - notice_title: "scan-test" -} - data corpus_sentinel: String = "__corpus__" fn runnable_function(r: Runnable) -> String { @@ -73,18 +62,42 @@ fn witness_independent_jobs_parallelize() -> Bool { gunbc_ci_floor_batches().skip(n: 1).first().length() > 1 } -// Scan dirs are carried by the plan (the corpus DiscoveryBatch), the single -// authority β€” NOT by the scheduler argv. A spec with `scan_subtrees: ["test"]` -// under root `src/v2` yields `src/v2/test`; a derivation that dropped the subtree -// would go RED here. -fn witness_scan_subtree_flows_through_plan() -> Bool { - list_contains_str(xs: corpus_scan_dirs(spec: spec_with_scan_subtree), target: "src/v2/test") +// Scan dirs are carried by the plan discovery batch β€” authority from ci_layer_roots. +fn witness_discovery_scan_dirs_in_plan() -> Bool { + match corpus_runnable() { + RunnableDiscoveryBatch { source_roots: _, scan_dirs: dirs, explicit_entries: _ } => + list_contains_str(xs: dirs, target: "dsl/test/claim") + _ => false + } +} + +fn witness_scan_dirs_match_layer_authority() -> Bool { + match corpus_runnable() { + RunnableDiscoveryBatch { source_roots: _, scan_dirs: dirs, explicit_entries: _ } => + list_contains_str(xs: dirs, target: witness_discovery_scan_dirs.first()) + _ => false + } +} + +// Dissolution relocated from CiSpec.witness_entries: the plan's discovery batch +// is marker-driven only β€” no hand-listed explicit_entries (Β§3/Β§5). +fn witness_corpus_has_no_explicit_entries() -> Bool { + match corpus_runnable() { + RunnableDiscoveryBatch { source_roots: roots, scan_dirs: _, explicit_entries: entries } => + (length(xs: entries) == 0) && + list_contains_str(xs: roots, target: "src/v2") && + list_contains_str(xs: roots, target: "dsl") && + list_contains_str(xs: roots, target: witness_layer_roots.first()) + _ => false + } } -fn ci_floor_plan_witnesses() -> Bool { +test fn ci_floor_plan_witnesses() -> Bool { witness_two_readiness_layers() && witness_compile_root_runs_first() && witness_corpus_not_before_compile() && witness_independent_jobs_parallelize() && - witness_scan_subtree_flows_through_plan() + witness_discovery_scan_dirs_in_plan() && + witness_scan_dirs_match_layer_authority() && + witness_corpus_has_no_explicit_entries() } diff --git a/dsl/test/claim/ci_spec_witness.dag b/src/v2/test/claim/ci_spec_witness_test.dag similarity index 52% rename from dsl/test/claim/ci_spec_witness.dag rename to src/v2/test/claim/ci_spec_witness_test.dag index 60c77d25e59..c352d50801c 100644 --- a/dsl/test/claim/ci_spec_witness.dag +++ b/src/v2/test/claim/ci_spec_witness_test.dag @@ -1,22 +1,33 @@ -// dsl/test/claim/ci_spec_witness.dag +// src/v2/test/claim/ci_spec_witness_test.dag // // Prove CiSpec fields drive the scheduler invocation `gunbc ci` runs (not // decorative literals), and that the run script invokes the dependency-ordered -// scheduler β€” NOT a flat claim_batch + gates lane (DESIGN Β§3, the operator's -// "default runtime, not a parallel dogfood lane"). +// scheduler β€” NOT a flat claim_batch + gates lane (DESIGN Β§3). Witness-entry +// dissolution is re-homed on the plan's discovery batch (explicit_entries: [], +// marker-driven roster) β€” proven in ci_floor_plan_witness_test. +// Gate coproduct equivalence lives here β€” NOT in ci_spec.dag +// (gunbc ci runtime closure must stay import-light; see qualified_name.dag:25). +// Lives under src/v2 so dsl-only `gunbc compile` (compile-clean gate) stays v2-import-free. -module test.claim.ci_spec_witness +module v2.test.claim.ci_spec_witness +import v2.std.algebra { bag_eq, list_snoc_item } +import v2.std.collection { List } +import v2.std.diagnostic { Accepted, Rejected } +import v2.std.logic { Bool } +import v2.std.node { Behavior, Symbol } +import v2.std.node_query { coproduct_arm_keys, coproduct_nullary_inhabitants } +import gunbc.ci_layer_roots { witness_layer_roots, witness_layer_source_flags } import gunbc.ci_spec { - CiSpec, WitnessRoot, DiffPolicy, DiffMergeBase, - gunbc_ci_spec, ci_release_features, + CiSpec, DiffPolicy, DiffMergeBase, Gate, + gunbc_ci_spec, gunbc_ci_gates, ci_release_features, scheduler_invoke, gunbc_ci_run_script, gunbc_ci_workflow_run, git_fetch_script, ci_release_build_script } -data spec_alternate_root: CiSpec = { - witness_roots: [{ root: "Z9Z9Z9-witness-root", scan_subtrees: [] }], - witness_entries: [], +data reflect_gate_type: Symbol = ^Gate + +data spec_alternate_notice: CiSpec = { gates: [], diff_policy: { base: "upstream/dev", @@ -26,12 +37,43 @@ data spec_alternate_root: CiSpec = { notice_title: "alt-notice" } -data spec_with_scan_subtree: CiSpec = { - witness_roots: [{ root: "src/v2", scan_subtrees: ["test"] }], - witness_entries: [], - gates: [], - diff_policy: gunbc_ci_spec.diff_policy, - notice_title: "scan-test" +data alternate_witness_layer_roots: List = ["Z9Z9Z9-witness-root"] + +fn symbol_eq(a: Symbol, b: Symbol) -> Bool { + a == b +} + +fn gate_arm_symbol(g: Gate) -> Symbol { + discriminant(v: g) +} + +fn gate_arm_symbols(gates: List) -> List { + fold(gates, init: [], f: fn(acc, g) { + list_snoc_item(xs: acc, item: gate_arm_symbol(g: g)) + }) +} + +fn nullary_inhabitant_discriminants(inhabitants: List) -> List { + fold(inhabitants, init: [], f: fn(acc, v) { + list_snoc_item(xs: acc, item: discriminant(v: v)) + }) +} + +fn witness_gate_roster_matches_coproduct_arms() -> Bool { + let arm_keys = coproduct_arm_keys(type_name: reflect_gate_type) + match coproduct_nullary_inhabitants(type_name: reflect_gate_type) { + Accepted { value: inhabitants, diagnostics: _ } => + bag_eq( + xs: gate_arm_symbols(gates: gunbc_ci_gates), + ys: arm_keys, + eq: symbol_eq + ) && bag_eq( + xs: nullary_inhabitant_discriminants(inhabitants: inhabitants), + ys: arm_keys, + eq: symbol_eq + ) + Rejected { diagnostics: _ } => false + } } fn witness_roots_fold_into_scheduler() -> Bool { @@ -40,21 +82,18 @@ fn witness_roots_fold_into_scheduler() -> Bool { string_contains(s: script, pattern: " --source-root dsl") } -fn witness_edit_witness_root_changes_scheduler() -> Bool { - let base = scheduler_invoke(spec: gunbc_ci_spec) - let alt = scheduler_invoke(spec: spec_alternate_root) - (base != alt) && - string_contains(s: alt, pattern: "Z9Z9Z9-witness-root") && - !string_contains(s: alt, pattern: " --source-root src/v2") +fn witness_layer_authority_drives_source_flags() -> Bool { + let flags = witness_layer_source_flags(roots: witness_layer_roots) + (flags != witness_layer_source_flags(roots: alternate_witness_layer_roots)) && + string_contains(s: flags, pattern: " --source-root src/v2") && + !string_contains(s: flags, pattern: "Z9Z9Z9-witness-root") } -// `claim_executor` has no `--scan-dir` flag (it hard-errors on unknown args), so a -// spec with `scan_subtrees` must NOT leak `--scan-dir` onto the scheduler argv β€” -// the scan dir is carried by the plan (`corpus_scan_dirs`, proven in -// `ci_floor_plan_witness`). Discriminating: the source-root is still emitted; only -// the harmful scan-dir flag is gone. +// `claim_executor` has no `--scan-dir` flag (it hard-errors on unknown args), so +// scan dirs must NOT leak onto the scheduler argv β€” they are carried by the plan +// (`corpus_scan_dirs`, proven in `ci_floor_plan_witness_test`). fn witness_scan_subtree_not_in_scheduler_argv() -> Bool { - let script = scheduler_invoke(spec: spec_with_scan_subtree) + let script = scheduler_invoke(spec: gunbc_ci_spec) !string_contains(s: script, pattern: " --scan-dir") && string_contains(s: script, pattern: " --source-root src/v2") } @@ -62,9 +101,9 @@ fn witness_scan_subtree_not_in_scheduler_argv() -> Bool { fn witness_diff_policy_drives_fetch() -> Bool { let fetch = git_fetch_script(policy: gunbc_ci_spec.diff_policy) string_contains(s: fetch, pattern: "git fetch --no-tags origin main") && - (git_fetch_script(policy: spec_alternate_root.diff_policy) != fetch) && + (git_fetch_script(policy: spec_alternate_notice.diff_policy) != fetch) && string_contains( - s: git_fetch_script(policy: spec_alternate_root.diff_policy), + s: git_fetch_script(policy: spec_alternate_notice.diff_policy), pattern: "git fetch --no-tags upstream dev" ) } @@ -73,11 +112,12 @@ fn witness_notice_title_from_spec() -> Bool { string_contains( s: scheduler_invoke(spec: gunbc_ci_spec), pattern: concat(concat(" --notice-title \"", gunbc_ci_spec.notice_title), "\"") + ) && string_contains( + s: scheduler_invoke(spec: spec_alternate_notice), + pattern: concat(concat(" --notice-title \"", spec_alternate_notice.notice_title), "\"") ) } -// The run script invokes the scheduler over the floor plan β€” and does NOT -// flat-invoke claim_batch (the parallel lane is gone, not shadowed). fn witness_run_script_invokes_scheduler() -> Bool { let script = gunbc_ci_run_script(spec: gunbc_ci_spec) string_contains(s: script, pattern: git_fetch_script(policy: gunbc_ci_spec.diff_policy)) && @@ -116,9 +156,9 @@ fn witness_workflow_run_is_collapsed_script() -> Bool { gunbc_ci_workflow_run(spec: gunbc_ci_spec) == gunbc_ci_run_script(spec: gunbc_ci_spec) } -fn ci_spec_witnesses() -> Bool { +test fn ci_spec_witnesses() -> Bool { witness_roots_fold_into_scheduler() && - witness_edit_witness_root_changes_scheduler() && + witness_layer_authority_drives_source_flags() && witness_scan_subtree_not_in_scheduler_argv() && witness_diff_policy_drives_fetch() && witness_notice_title_from_spec() && @@ -126,5 +166,6 @@ fn ci_spec_witnesses() -> Bool { witness_release_build_unifies_features() && witness_release_build_eagain_cold_retry() && witness_scheduler_uses_release_binary() && - witness_workflow_run_is_collapsed_script() + witness_workflow_run_is_collapsed_script() && + witness_gate_roster_matches_coproduct_arms() } diff --git a/src/v2/test/claim/diagnostic_sequence_outcome_witness.dag b/src/v2/test/claim/diagnostic_sequence_outcome_witness_test.dag similarity index 98% rename from src/v2/test/claim/diagnostic_sequence_outcome_witness.dag rename to src/v2/test/claim/diagnostic_sequence_outcome_witness_test.dag index 54c8af6e0c1..73bad0106b7 100644 --- a/src/v2/test/claim/diagnostic_sequence_outcome_witness.dag +++ b/src/v2/test/claim/diagnostic_sequence_outcome_witness_test.dag @@ -107,7 +107,7 @@ fn witness_sequence_outcome_short_circuit_does_not_merge_second_reject() -> Bool } } -fn diagnostic_sequence_outcome_witnesses() -> Bool { +test fn diagnostic_sequence_outcome_witnesses() -> Bool { witness_sequence_outcome_empty_accepts() && witness_sequence_outcome_collects_values() && witness_sequence_outcome_short_circuits_on_reject() diff --git a/src/v2/test/claim/layering_imports/coverage_domain_equivalence.dag b/src/v2/test/claim/layering_imports/coverage_domain_equivalence_test.dag similarity index 98% rename from src/v2/test/claim/layering_imports/coverage_domain_equivalence.dag rename to src/v2/test/claim/layering_imports/coverage_domain_equivalence_test.dag index 1272348bb94..dde5d1f36db 100644 --- a/src/v2/test/claim/layering_imports/coverage_domain_equivalence.dag +++ b/src/v2/test/claim/layering_imports/coverage_domain_equivalence_test.dag @@ -104,7 +104,7 @@ fn rows_cover_form(form: String) -> Bool { ) } -fn coverage_domain_equivalence_argument_holds() -> Bool { +test fn coverage_domain_equivalence_argument_holds() -> Bool { rows_cover_root(root: layer_root_src_v2_std) && rows_cover_root(root: layer_root_src_v2_extdeps) && rows_cover_root(root: layer_root_dsl_extdeps) diff --git a/src/v2/workflow/ci_floor_plan.dag b/src/v2/workflow/ci_floor_plan.dag index 2580c128613..edd1565d991 100644 --- a/src/v2/workflow/ci_floor_plan.dag +++ b/src/v2/workflow/ci_floor_plan.dag @@ -4,8 +4,8 @@ // This is the default CI runtime: `gunbc ci` (via `gunbc.ci_spec.gunbc_ci_run_script`) // invokes `claim_executor --plan-entry … --plan-function gunbc_ci_floor_batches`, and // the v2.workflow scheduler decides the batches. There is NO parallel hardcoded floor -// list β€” the gates come from `gunbc_ci_spec.gates`, the corpus roots/witnesses from -// `gunbc_ci_spec.witness_roots`/`witness_entries`. The ONLY fact added here is the +// list β€” the gates come from `gunbc_ci_spec.gates`, the corpus roots/scan dirs from +// `gunbc.ci_layer_roots` (discovery roster is marker-driven, not hand-listed). The ONLY fact added here is the // dependency TOPOLOGY: `DslCompileCleanGate` (whole-tree compile) is the fail-fast // root, and every other gate + the discovery corpus sit in a later readiness layer // behind it, so they run dependency-ordered and parallelize where independent @@ -26,8 +26,9 @@ module v2.workflow.ci_floor_plan +import gunbc.ci_layer_roots { witness_layer_roots, witness_discovery_scan_dirs } import gunbc.ci_spec { - gunbc_ci_spec, CiSpec, WitnessRoot, WitnessEntry, + gunbc_ci_spec, CiSpec, Gate, RustMonolithGate, EmitHostGate, LayeringImportsGate, DslCompileCleanGate, CiYamlGate, SourceRootIngestGate } @@ -43,7 +44,6 @@ import std.realization_schedule { Runnable, RunnableSingleClaim, RunnableDiscoveryBatch, - ScheduleWitnessEntry, cost_account_predicted_zero, schedule_lens_verdict_for_ci_floor } @@ -98,37 +98,16 @@ fn floor_compile_root_node() -> Node { gate_node(g: DslCompileCleanGate) } -// ── corpus runnable fields, derived from the spec witness roots/entries ────── -fn corpus_source_roots(spec: CiSpec) -> List { - fold(spec.witness_roots, init: [], f: fn(acc, wr) { - list_snoc_item(xs: acc, item: wr.root) - }) -} - -fn corpus_scan_dirs(spec: CiSpec) -> List { - fold(spec.witness_roots, init: [], f: fn(acc, wr) { - fold(wr.scan_subtrees, init: acc, f: fn(inner, sub) { - let dir = if starts_with(s: sub, prefix: wr.root) { sub } else { concat(concat(wr.root, "/"), sub) } - list_snoc_item(xs: inner, item: dir) - }) - }) -} - -fn schedule_witness_from_entry(e: WitnessEntry) -> ScheduleWitnessEntry { - ScheduleWitnessEntry { entry: e.entry, function: e.function } -} - -fn corpus_explicit_schedule_entries(spec: CiSpec) -> List { - fold(spec.witness_entries, init: [], f: fn(acc, we) { - list_snoc_item(xs: acc, item: schedule_witness_from_entry(e: we)) - }) +// ── corpus runnable fields, derived from ci_layer_roots (Β§3 single authority) ─ +fn corpus_source_roots() -> List { + witness_layer_roots } -fn corpus_runnable(spec: CiSpec) -> Runnable { +fn corpus_runnable() -> Runnable { RunnableDiscoveryBatch { - source_roots: corpus_source_roots(spec: spec), - scan_dirs: corpus_scan_dirs(spec: spec), - explicit_entries: corpus_explicit_schedule_entries(spec: spec) + source_roots: corpus_source_roots(), + scan_dirs: witness_discovery_scan_dirs, + explicit_entries: [] } } @@ -167,7 +146,7 @@ fn floor_unrunnable_sentinel() -> Runnable { fn runnable_for_node(node: Node, spec: CiSpec) -> Runnable { if node == floor_corpus_node { - corpus_runnable(spec: spec) + corpus_runnable() } else { fold(spec.gates, init: floor_unrunnable_sentinel(), f: fn(acc, g) { if node == gate_node(g: g) { gate_runnable(g: g) } else { acc }