From db2076d6da39dbe432193c0be49fa73c4d9c3b79 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 3 Jun 2026 01:47:40 +0000 Subject: [PATCH 1/4] feat(runtime): gunbc test stage0 handler + corpus-eval CI host transport (Rung 1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RR-A §5.2 step-3 on #4335 substrate: ProcessExit harness entry, `gunbc test` cli_test handler, thin scripts/v4-testclaim-corpus-eval.sh, and ci.dag host-script witness. Rebased onto main (drops duplicate #4335 history). Co-authored-by: Cursor --- scripts/v4-testclaim-corpus-eval.sh | 30 +++++++++++++ src/v2/05_emit_rust.dag | 37 +++++++++++++-- src/v2/stage0/src/bin/regen_stage0.rs | 1 + src/v2/stage0/src/cli_test.rs | 30 +++++++++++++ src/v2/stage0/src/lib.rs | 1 + src/v2/stage0/src/main.rs | 7 +++ .../v4_test_bootstrap_infra_closeout_test.rs | 45 +++++++++++++++++++ .../workflow/testclaim_corpus_runner.dag | 23 ++++++++++ src/v4/workflow/ci.dag | 10 +++++ 9 files changed, 180 insertions(+), 4 deletions(-) create mode 100755 scripts/v4-testclaim-corpus-eval.sh create mode 100644 src/v2/stage0/src/cli_test.rs diff --git a/scripts/v4-testclaim-corpus-eval.sh b/scripts/v4-testclaim-corpus-eval.sh new file mode 100755 index 00000000000..5be6bed3562 --- /dev/null +++ b/scripts/v4-testclaim-corpus-eval.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# scripts/v4-testclaim-corpus-eval.sh +# +# T-38 TestClaim manual-corpus eval host transport (RR-A §5.2 / A.3b step-3). +# Invokes the stage0 `gunbc test` harness — no host-owned compile loop (#4091 §1.2). +# Authority: v4.workflow.ci `TestClaimCorpusEvalCommand` + `ci_upsert_testclaim_corpus_eval_*`. +# +# Env: +# V2_COMPILER — gunbc binary (default: target/release/gunbc) +# V4_TESTCLAIM_CORPUS_EVAL_STRICT — if 1, exit non-zero when gunbc is missing (default: 0) + +set -euo pipefail + +root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)" +cd "$root" + +bin="${V2_COMPILER:-target/release/gunbc}" +strict="${V4_TESTCLAIM_CORPUS_EVAL_STRICT:-0}" + +if [[ ! -x "$bin" ]]; then + echo "error: gunbc (v2 stage0 binary) not found at $bin" >&2 + if [[ "$strict" == "1" ]]; then + echo "::error title=testclaim corpus eval setup::gunbc missing at $bin" >&2 + exit 2 + fi + echo "::notice title=testclaim corpus eval::skipped — gunbc missing at $bin" + exit 0 +fi + +exec "$bin" test diff --git a/src/v2/05_emit_rust.dag b/src/v2/05_emit_rust.dag index 45bcef5bf1c..ea8d9577e13 100644 --- a/src/v2/05_emit_rust.dag +++ b/src/v2/05_emit_rust.dag @@ -1230,7 +1230,7 @@ fn emit_lib_rs_from_files(all_module_files: List, has_compiler_tests: // rest_transport_facts.rs and INVARIANTS bounded substrate seed). // These are not generated — they survive regen as non-generated files. let hand_maintained_mods = if has_compiler_tests { - "\npub mod v2_interpreter;\npub mod cli_run;\npub mod rest_transport_facts;" + "\npub mod v2_interpreter;\npub mod cli_run;\npub mod cli_test;\npub mod rest_transport_facts;" } else { "" } @@ -7330,7 +7330,8 @@ fn emit_main_mod_uses(workflow_funcs: List, has_pipeline: Bool, cr " byte_to_line_col, source_line_at, NewlineIndex,\n", " CompilerDiagnostic,\n", "}", ";\n", - "use ", crate_name, "::cli_run;\n") + "use ", crate_name, "::cli_run;\n", + "use ", crate_name, "::cli_test;\n") } else { base } @@ -7384,7 +7385,16 @@ fn emit_subcommand_enum(workflow_funcs: List, has_pipeline: Bool) make_indent(level: depth + 2), "function: String,\n", make_indent(level: depth + 1), "},") } else { "" } - let all_variants = if has_pipeline { concat(variants, [compile_variant, run_variant]) } else { variants } + let test_variant = if has_pipeline { + concat( + "/// Execute the T-38 manual TestClaim corpus harness (RR-A §5.2; `gunbc test`)\n", + make_indent(level: depth + 1), "Test,") + } else { "" } + let all_variants = if has_pipeline { + concat(variants, [compile_variant, run_variant, test_variant]) + } else { + variants + } let variants_str = all_variants |> join(separator: "\n") concat( "#[derive(Subcommand)]\n", @@ -7455,7 +7465,12 @@ fn emit_main_fn(workflow_funcs: List, has_services: Bool, has_pipe let match_arms = workflow_funcs |> map(wf => emit_main_match_arm(wf: wf, has_services: has_services)) let compile_arm = if has_pipeline { emit_compile_match_arm(crate_name: crate_name) } else { "" } let run_arm = if has_pipeline { emit_run_match_arm(crate_name: crate_name) } else { "" } - let all_arms = if has_pipeline { concat(match_arms, [compile_arm, run_arm]) } else { match_arms } + let test_arm = if has_pipeline { emit_test_match_arm(has_services: has_services) } else { "" } + let all_arms = if has_pipeline { + concat(match_arms, [compile_arm, run_arm, test_arm]) + } else { + match_arms + } let arms_str = all_arms |> join(separator: "\n") let match_block = concat("match cli.command {\n", make_indent(level: depth + 2),arms_str, "\n }") let result_handling = if has_services { @@ -7593,6 +7608,20 @@ fn emit_run_match_arm(crate_name: String) -> String { " },") } +fn emit_test_match_arm(has_services: Bool) -> String { + if has_services { + concat( + "\n Commands::Test => {\n", + " cli_test::handle_test_with_options(dry_run.0);\n", + " },") + } else { + concat( + "\n Commands::Test => {\n", + " cli_test::handle_test_with_options(cli.dry_run);\n", + " },") + } +} + // ========================================================================= // Pipeline helper functions emitted at module level in main.rs // FF-9: import-driven source resolution + diagnostic rendering diff --git a/src/v2/stage0/src/bin/regen_stage0.rs b/src/v2/stage0/src/bin/regen_stage0.rs index 94e11ae9ce5..bb724a5faa8 100644 --- a/src/v2/stage0/src/bin/regen_stage0.rs +++ b/src/v2/stage0/src/bin/regen_stage0.rs @@ -92,6 +92,7 @@ const GENERATED_STAGE0_FILES: &[&str] = &[ const HAND_MAINTAINED_STAGE0_FILES: &[&str] = &[ "cli_run.rs", + "cli_test.rs", "rest_transport_facts.rs", "v2_compiler_dag_collect.rs", "v2_interpreter.rs", diff --git a/src/v2/stage0/src/cli_test.rs b/src/v2/stage0/src/cli_test.rs new file mode 100644 index 00000000000..32a306ebe67 --- /dev/null +++ b/src/v2/stage0/src/cli_test.rs @@ -0,0 +1,30 @@ +// cli_test.rs — Hand-maintained `gunbc test` subcommand handler. +// Not generated — survives stage0 regeneration. +// RR-A §5.2: runs the manual corpus harness through `cli_run` / `dag run` machinery. + +use crate::cli_run; + +/// Mirrors `bootstrap_manual_corpus_harness.source_root` (v4_dag_source convention). +const HARNESS_SOURCE_ROOT: &str = "src/v4"; + +/// ProcessExit entry in `v4.test.claim.workflow.testclaim_corpus_runner` — not +/// `run_manual_testclaim_corpus_eval` (returns `CorpusEvalReport`, not `ProcessExit`). +const HARNESS_ENTRY_FN: &str = "gunbc_test_manual_corpus_harness_exit"; + +/// Entry point for `gunbc test`. Called from the generated main.rs. +pub fn handle_test() { + handle_test_with_options(false); +} + +/// Entry point with options for dry-run mode (global `--dry-run` on the CLI). +pub fn handle_test_with_options(dry_run: bool) { + eprintln!( + "gunbc test: manual corpus harness (--source-root {} --function {})", + HARNESS_SOURCE_ROOT, HARNESS_ENTRY_FN + ); + cli_run::handle_run_with_options( + vec![HARNESS_SOURCE_ROOT.to_string()], + HARNESS_ENTRY_FN.to_string(), + dry_run, + ); +} diff --git a/src/v2/stage0/src/lib.rs b/src/v2/stage0/src/lib.rs index 3492a5f9783..c7a761936c5 100644 --- a/src/v2/stage0/src/lib.rs +++ b/src/v2/stage0/src/lib.rs @@ -13,6 +13,7 @@ )] pub mod cli_run; +pub mod cli_test; pub mod extdeps_languages_dag_emit; pub mod extdeps_languages_dag_syntax; pub mod extdeps_languages_dag_types; diff --git a/src/v2/stage0/src/main.rs b/src/v2/stage0/src/main.rs index 47af681e9df..a077b940749 100644 --- a/src/v2/stage0/src/main.rs +++ b/src/v2/stage0/src/main.rs @@ -7,6 +7,7 @@ use clap::{Parser, Subcommand}; use std::collections::HashMap; use std::rc::Rc; use v2_compiler::cli_run; +use v2_compiler::cli_test; use v2_compiler::v2_compiler_compile; use v2_compiler::v2_compiler_compile::PipelineResult; use v2_compiler::v2_std_core::{ @@ -53,6 +54,8 @@ enum Commands { #[arg(long, default_value = "main")] function: String, }, + /// Execute the T-38 manual TestClaim corpus harness (RR-A §5.2; `gunbc test`) + Test, } /// Recursively find all .dag files under a directory. @@ -383,6 +386,10 @@ fn main() { } => { cli_run::handle_run(source_roots, function); } + + Commands::Test => { + cli_test::handle_test_with_options(cli.dry_run); + } }; } diff --git a/src/v3/compiler/tests/integration/v4_test_bootstrap_infra_closeout_test.rs b/src/v3/compiler/tests/integration/v4_test_bootstrap_infra_closeout_test.rs index ac4f1d1053e..195a6bd5ff7 100644 --- a/src/v3/compiler/tests/integration/v4_test_bootstrap_infra_closeout_test.rs +++ b/src/v3/compiler/tests/integration/v4_test_bootstrap_infra_closeout_test.rs @@ -43,6 +43,8 @@ const BOOTSTRAP_DAG: &str = include_str!("../../../../v4/workflow/bootstrap.dag" const BOOTSTRAP_PATH: &str = "src/v4/workflow/bootstrap.dag"; const CLI_DAG: &str = include_str!("../../../../v4/workflow/cli.dag"); const CLI_PATH: &str = "src/v4/workflow/cli.dag"; +const CI_DAG: &str = include_str!("../../../../v4/workflow/ci.dag"); +const CI_PATH: &str = "src/v4/workflow/ci.dag"; const CONNECTIVE_ANCHORS_DAG: &str = include_str!("../../../../v4/test/claim/manual/connective_anchors.dag"); const CONNECTIVE_ANCHORS_PATH: &str = "src/v4/test/claim/manual/connective_anchors.dag"; @@ -977,6 +979,49 @@ fn rr_a_step2_bootstrap_evaluator_corpus_harness_entry() { ); } +const TESTCLAIM_CORPUS_RUNNER_DAG: &str = + include_str!("../../../../v4/test/claim/workflow/testclaim_corpus_runner.dag"); +const CORPUS_EVAL_SCRIPT: &str = "scripts/v4-testclaim-corpus-eval.sh"; + +/// Rung 1: stage0 `gunbc test` handler + thin CI host script (execution-runnable transport). +#[test] +fn rung1_gunbc_test_stage0_handler_and_corpus_eval_host_script() { + let repo_root = workspace_root(); + let script = repo_root.join(CORPUS_EVAL_SCRIPT); + assert!( + script.is_file(), + "missing {CORPUS_EVAL_SCRIPT} — RR-A §5.2 host transport must be re-landed" + ); + let script_body = std::fs::read_to_string(&script).expect("read corpus eval script"); + assert!( + script_body.contains("\"$bin\" test") || script_body.contains("$bin test"), + "{CORPUS_EVAL_SCRIPT}: must invoke `gunbc test`, not a host-owned compile loop" + ); + assert!( + !script_body.lines().any(|line| { + let t = line.trim(); + !t.starts_with('#') && t.contains("gunbc") && t.contains("compile") + }), + "{CORPUS_EVAL_SCRIPT}: executable lines must not invoke host-owned gunbc compile (#4091 §1.2)" + ); + let cli_test = repo_root.join("src/v2/stage0/src/cli_test.rs"); + let cli_test_body = std::fs::read_to_string(&cli_test).expect("read cli_test.rs"); + assert!( + cli_test_body.contains("gunbc_test_manual_corpus_harness_exit"), + "cli_test.rs must target the ProcessExit harness entry (not CorpusEvalReport-returning run_manual_testclaim_corpus_eval)" + ); + assert!( + TESTCLAIM_CORPUS_RUNNER_DAG + .contains("fn gunbc_test_manual_corpus_harness_exit() -> ProcessExit"), + "testclaim_corpus_runner.dag must expose ProcessExit harness entry for stage0 dag run" + ); + assert!( + CI_DAG.contains("data ci_testclaim_corpus_eval_host_script: String = \"scripts/v4-testclaim-corpus-eval.sh\"") + && CI_DAG.contains("witness_ci_testclaim_corpus_eval_host_transport"), + "ci.dag must model corpus-eval host script path for execution-runnable transport" + ); +} + /// Returns true when a `data x: TestClaim = …` body directly assigns a ManualAnchorKey /// variant to the `anchor` field (not wrapped in `manual_claim_anchor`). /// Helper-function bodies (arity_rejection_claim, testgen_emit_*, etc.) are Call diff --git a/src/v4/test/claim/workflow/testclaim_corpus_runner.dag b/src/v4/test/claim/workflow/testclaim_corpus_runner.dag index 8a40a6d7b07..ab0113f4ec7 100644 --- a/src/v4/test/claim/workflow/testclaim_corpus_runner.dag +++ b/src/v4/test/claim/workflow/testclaim_corpus_runner.dag @@ -25,9 +25,17 @@ import v4.std.verdict { verdict_tally_total } import v4.std.verification { test_claim_label } +import std.process { + ProcessExit, + ExitSuccess, + exit_failure +} import v4.test.claim.manual.manual_corpus_roster { manual_corpus_node_subject_rows } +import v4.test.claim.workflow.manual_corpus_eval { + manual_corpus_gate +} type CorpusEvalEntry { @@ -137,6 +145,21 @@ fn run_manual_testclaim_corpus_eval() -> CorpusEvalReport { } +// Stage0 / `gunbc test` harness entry — wraps the corpus eval report in `ProcessExit` so +// `cli_run` can map success/failure to a host exit code (std/process.dag contract). +// Authority: v4.workflow.bootstrap `bootstrap_manual_corpus_harness` is invoked via this +// symbol from `cli_test.rs`, not via `run_manual_testclaim_corpus_eval` (CorpusEvalReport). +data gunbc_test_manual_corpus_harness_exit: Symbol = gunbc_test_manual_corpus_harness_exit + +fn gunbc_test_manual_corpus_harness_exit() -> ProcessExit { + if manual_corpus_gate(report: run_manual_testclaim_corpus_eval()) { + ExitSuccess + } else { + exit_failure(reason: "manual_testclaim_corpus_eval gate not closed") + } +} + + fn manual_testclaim_subject_roster_family_receipt() -> TestClaimSubjectRosterFamilyReceipt { let runs = run_node_runtime_value_subjects(subjects: manual_corpus_node_subject_rows) TestClaimSubjectRosterFamilyReceipt { diff --git a/src/v4/workflow/ci.dag b/src/v4/workflow/ci.dag index 0bab424fd23..75518bd7524 100644 --- a/src/v4/workflow/ci.dag +++ b/src/v4/workflow/ci.dag @@ -1396,6 +1396,16 @@ fn ci_testclaim_corpus_eval_command() -> CiCommand { } } +// RR-A §5.2 / A.3b step-3 — host transport for `testclaim_corpus_eval_execution` when scheduled. +// Invokes stage0 `gunbc test` (scripts/v4-testclaim-corpus-eval.sh); demoted from Wave-1 required path. +data ci_testclaim_corpus_eval_host_script: String = "scripts/v4-testclaim-corpus-eval.sh" + +fn ci_testclaim_corpus_eval_host_transport_well_formed() -> Bool { + ci_testclaim_corpus_eval_host_script == "scripts/v4-testclaim-corpus-eval.sh" +} + +data witness_ci_testclaim_corpus_eval_host_transport: Bool = ci_testclaim_corpus_eval_host_transport_well_formed() + data ci_upsert_testclaim_corpus_eval_file_set_inputs: List = [ ci_upsert_file_set_input(segment: "src/v4/test/claim/manual/**"), ci_upsert_file_set_input(segment: "src/v4/test/claim/workflow/testclaim_corpus_runner.dag"), From 848e7ca9fce80d711665fa8bbe5f274822adda91 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 3 Jun 2026 01:51:23 +0000 Subject: [PATCH 2/4] fix(runtime): address review + document gunbc test verdict acceptance path Remove unused handle_test() (main always passes global --dry-run via handle_test_with_options). Pin run_test_claim verdict chain in closeout test; document acceptance path and ci.yml Class-C demotion in substrate. Co-authored-by: Cursor --- src/v2/stage0/src/cli_test.rs | 7 +---- .../v4_test_bootstrap_infra_closeout_test.rs | 28 +++++++++++++++++++ .../workflow/testclaim_corpus_runner.dag | 9 ++++-- src/v4/workflow/ci.dag | 4 ++- 4 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/v2/stage0/src/cli_test.rs b/src/v2/stage0/src/cli_test.rs index 32a306ebe67..8df7b5c2083 100644 --- a/src/v2/stage0/src/cli_test.rs +++ b/src/v2/stage0/src/cli_test.rs @@ -11,12 +11,7 @@ const HARNESS_SOURCE_ROOT: &str = "src/v4"; /// `run_manual_testclaim_corpus_eval` (returns `CorpusEvalReport`, not `ProcessExit`). const HARNESS_ENTRY_FN: &str = "gunbc_test_manual_corpus_harness_exit"; -/// Entry point for `gunbc test`. Called from the generated main.rs. -pub fn handle_test() { - handle_test_with_options(false); -} - -/// Entry point with options for dry-run mode (global `--dry-run` on the CLI). +/// Entry point for `gunbc test`. Called from the generated main.rs (honors global `--dry-run`). pub fn handle_test_with_options(dry_run: bool) { eprintln!( "gunbc test: manual corpus harness (--source-root {} --function {})", diff --git a/src/v3/compiler/tests/integration/v4_test_bootstrap_infra_closeout_test.rs b/src/v3/compiler/tests/integration/v4_test_bootstrap_infra_closeout_test.rs index 195a6bd5ff7..e72c6111e1e 100644 --- a/src/v3/compiler/tests/integration/v4_test_bootstrap_infra_closeout_test.rs +++ b/src/v3/compiler/tests/integration/v4_test_bootstrap_infra_closeout_test.rs @@ -1020,6 +1020,34 @@ fn rung1_gunbc_test_stage0_handler_and_corpus_eval_host_script() { && CI_DAG.contains("witness_ci_testclaim_corpus_eval_host_transport"), "ci.dag must model corpus-eval host script path for execution-runnable transport" ); + // Acceptance path: >=1 real Verdict via gunbc test → run_test_claim (not emit_host). + assert!( + TESTCLAIM_CORPUS_RUNNER_DAG + .contains("map(subjects, fn(subject) { run_test_claim(subject: subject) })") + && TESTCLAIM_CORPUS_RUNNER_DAG.contains("test_claim_run_verdict(run: e.run)") + && TESTCLAIM_CORPUS_RUNNER_DAG + .contains("manual_corpus_gate(report: run_manual_testclaim_corpus_eval())"), + "acceptance path must fold per-row TestClaimRun verdicts from run_test_claim (T-38)" + ); + assert!( + !TESTCLAIM_CORPUS_RUNNER_DAG.lines().any(|line| { + let t = line.trim(); + !t.starts_with("//") + && (t.contains("import") && t.contains("emit_host") + || t.contains("emit_host_")) + }), + "manual corpus runner must not import or call emit_host (gunbc test / stage0 interpreter only)" + ); + assert!( + !cli_test_body.contains("emit_host"), + "cli_test.rs must dispatch gunbc test via cli_run only, not emit_host" + ); + // ci.yml: corpus-eval job is modeled in ci.dag but demoted from Wave-1 required path (Class C). + const CI_YML: &str = include_str!("../../../../../.github/workflows/ci.yml"); + assert!( + !CI_YML.contains("v4-testclaim-corpus-eval.sh"), + "ci.yml required path must not invoke corpus-eval script until live-workflow projection lands (shadow/demoted OK per brief)" + ); } /// Returns true when a `data x: TestClaim = …` body directly assigns a ManualAnchorKey diff --git a/src/v4/test/claim/workflow/testclaim_corpus_runner.dag b/src/v4/test/claim/workflow/testclaim_corpus_runner.dag index ab0113f4ec7..29aa44134bc 100644 --- a/src/v4/test/claim/workflow/testclaim_corpus_runner.dag +++ b/src/v4/test/claim/workflow/testclaim_corpus_runner.dag @@ -145,8 +145,13 @@ fn run_manual_testclaim_corpus_eval() -> CorpusEvalReport { } -// Stage0 / `gunbc test` harness entry — wraps the corpus eval report in `ProcessExit` so -// `cli_run` can map success/failure to a host exit code (std/process.dag contract). +// Stage0 / `gunbc test` acceptance path (RR-A Rung-1; NOT emit_host): +// gunbc test → cli_run → gunbc_test_manual_corpus_harness_exit → run_manual_testclaim_corpus_eval +// → run_node_runtime_value_subjects → run_test_claim → TestClaimRun verdict per row +// → corpus_report_tally / test_claim_run_verdict → manual_corpus_gate → ProcessExit. +// At least one real Verdict fact flows through run_test_claim on the manual roster (F5 gate: +// zero Fail + zero Deferred). Forbidden: emit_host transport as terminal pass authority. +// Wraps the corpus report in `ProcessExit` so `cli_run` maps pass/fail to a host exit code. // Authority: v4.workflow.bootstrap `bootstrap_manual_corpus_harness` is invoked via this // symbol from `cli_test.rs`, not via `run_manual_testclaim_corpus_eval` (CorpusEvalReport). data gunbc_test_manual_corpus_harness_exit: Symbol = gunbc_test_manual_corpus_harness_exit diff --git a/src/v4/workflow/ci.dag b/src/v4/workflow/ci.dag index 75518bd7524..f4a8571ce59 100644 --- a/src/v4/workflow/ci.dag +++ b/src/v4/workflow/ci.dag @@ -1397,7 +1397,9 @@ fn ci_testclaim_corpus_eval_command() -> CiCommand { } // RR-A §5.2 / A.3b step-3 — host transport for `testclaim_corpus_eval_execution` when scheduled. -// Invokes stage0 `gunbc test` (scripts/v4-testclaim-corpus-eval.sh); demoted from Wave-1 required path. +// Invokes stage0 `gunbc test` (scripts/v4-testclaim-corpus-eval.sh) → run_test_claim verdict chain. +// Wave-1 §11.7.1: NOT on ci.yml required path yet (Class C demoted); execution-runnable when +// affected-set schedules the job — shadow / non-fail-closed posture OK until live projection. data ci_testclaim_corpus_eval_host_script: String = "scripts/v4-testclaim-corpus-eval.sh" fn ci_testclaim_corpus_eval_host_transport_well_formed() -> Bool { From ae3fa2d5b19423bd8e974c3dd3045bccd632de9f Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 3 Jun 2026 02:27:40 +0000 Subject: [PATCH 3/4] WIP: Rung 1: gunbc test stage0 handler + ci corpus-eval step (execution-runna --- .../gunbc_test_manual_corpus_harness.dag | 22 ++++++++++++++ src/v2/stage0/src/cli_test.rs | 14 +++++---- .../v4_test_bootstrap_infra_closeout_test.rs | 20 ++++++++----- .../workflow/testclaim_corpus_runner.dag | 30 +++---------------- 4 files changed, 48 insertions(+), 38 deletions(-) create mode 100644 dsl/gunbc/gunbc_test_manual_corpus_harness.dag diff --git a/dsl/gunbc/gunbc_test_manual_corpus_harness.dag b/dsl/gunbc/gunbc_test_manual_corpus_harness.dag new file mode 100644 index 00000000000..bea08bc7688 --- /dev/null +++ b/dsl/gunbc/gunbc_test_manual_corpus_harness.dag @@ -0,0 +1,22 @@ +// dsl/gunbc/gunbc_test_manual_corpus_harness.dag +// Stage0 `gunbc test` ProcessExit entry (RR-A §5.2 Rung-1). +// Lives under dsl/ so M1 v4-only rust emit never resolves `std.process` from src/v4. +// Run via: gunbc test (cli_test.rs passes --source-root src/v4 --source-root dsl). + +module gunbc.gunbc_test_manual_corpus_harness + +import std.process { ProcessExit, ExitSuccess, exit_failure } +import v4.test.claim.workflow.manual_corpus_eval { manual_corpus_gate } +import v4.test.claim.workflow.testclaim_corpus_runner { + run_manual_testclaim_corpus_eval +} + +// gunbc test → cli_run → this entry → run_manual_testclaim_corpus_eval → run_test_claim +// verdicts → manual_corpus_gate → ProcessExit (not emit_host). +fn gunbc_test_manual_corpus_harness_exit() -> ProcessExit { + if manual_corpus_gate(report: run_manual_testclaim_corpus_eval()) { + ExitSuccess + } else { + exit_failure(reason: "manual_testclaim_corpus_eval gate not closed") + } +} diff --git a/src/v2/stage0/src/cli_test.rs b/src/v2/stage0/src/cli_test.rs index 8df7b5c2083..9a35bda4e87 100644 --- a/src/v2/stage0/src/cli_test.rs +++ b/src/v2/stage0/src/cli_test.rs @@ -4,10 +4,10 @@ use crate::cli_run; -/// Mirrors `bootstrap_manual_corpus_harness.source_root` (v4_dag_source convention). -const HARNESS_SOURCE_ROOT: &str = "src/v4"; +/// v4 corpus + dsl std.process harness (see `dsl/gunbc/gunbc_test_manual_corpus_harness.dag`). +const HARNESS_SOURCE_ROOTS: &[&str] = &["src/v4", "dsl"]; -/// ProcessExit entry in `v4.test.claim.workflow.testclaim_corpus_runner` — not +/// ProcessExit entry in `gunbc.gunbc_test_manual_corpus_harness` — not /// `run_manual_testclaim_corpus_eval` (returns `CorpusEvalReport`, not `ProcessExit`). const HARNESS_ENTRY_FN: &str = "gunbc_test_manual_corpus_harness_exit"; @@ -15,10 +15,14 @@ const HARNESS_ENTRY_FN: &str = "gunbc_test_manual_corpus_harness_exit"; pub fn handle_test_with_options(dry_run: bool) { eprintln!( "gunbc test: manual corpus harness (--source-root {} --function {})", - HARNESS_SOURCE_ROOT, HARNESS_ENTRY_FN + HARNESS_SOURCE_ROOTS.join(" "), + HARNESS_ENTRY_FN ); cli_run::handle_run_with_options( - vec![HARNESS_SOURCE_ROOT.to_string()], + HARNESS_SOURCE_ROOTS + .iter() + .map(|s| s.to_string()) + .collect(), HARNESS_ENTRY_FN.to_string(), dry_run, ); diff --git a/src/v3/compiler/tests/integration/v4_test_bootstrap_infra_closeout_test.rs b/src/v3/compiler/tests/integration/v4_test_bootstrap_infra_closeout_test.rs index e72c6111e1e..2fd785fe551 100644 --- a/src/v3/compiler/tests/integration/v4_test_bootstrap_infra_closeout_test.rs +++ b/src/v3/compiler/tests/integration/v4_test_bootstrap_infra_closeout_test.rs @@ -981,6 +981,8 @@ fn rr_a_step2_bootstrap_evaluator_corpus_harness_entry() { const TESTCLAIM_CORPUS_RUNNER_DAG: &str = include_str!("../../../../v4/test/claim/workflow/testclaim_corpus_runner.dag"); +const GUNBC_TEST_MANUAL_CORPUS_HARNESS_DAG: &str = + include_str!("../../../../../dsl/gunbc/gunbc_test_manual_corpus_harness.dag"); const CORPUS_EVAL_SCRIPT: &str = "scripts/v4-testclaim-corpus-eval.sh"; /// Rung 1: stage0 `gunbc test` handler + thin CI host script (execution-runnable transport). @@ -1007,13 +1009,14 @@ fn rung1_gunbc_test_stage0_handler_and_corpus_eval_host_script() { let cli_test = repo_root.join("src/v2/stage0/src/cli_test.rs"); let cli_test_body = std::fs::read_to_string(&cli_test).expect("read cli_test.rs"); assert!( - cli_test_body.contains("gunbc_test_manual_corpus_harness_exit"), - "cli_test.rs must target the ProcessExit harness entry (not CorpusEvalReport-returning run_manual_testclaim_corpus_eval)" + cli_test_body.contains("gunbc_test_manual_corpus_harness_exit") + && cli_test_body.contains("\"dsl\""), + "cli_test.rs must target the ProcessExit harness entry with dsl+src/v4 source roots" ); assert!( - TESTCLAIM_CORPUS_RUNNER_DAG + GUNBC_TEST_MANUAL_CORPUS_HARNESS_DAG .contains("fn gunbc_test_manual_corpus_harness_exit() -> ProcessExit"), - "testclaim_corpus_runner.dag must expose ProcessExit harness entry for stage0 dag run" + "dsl/gunbc/gunbc_test_manual_corpus_harness.dag must expose ProcessExit harness for stage0 dag run" ); assert!( CI_DAG.contains("data ci_testclaim_corpus_eval_host_script: String = \"scripts/v4-testclaim-corpus-eval.sh\"") @@ -1024,11 +1027,14 @@ fn rung1_gunbc_test_stage0_handler_and_corpus_eval_host_script() { assert!( TESTCLAIM_CORPUS_RUNNER_DAG .contains("map(subjects, fn(subject) { run_test_claim(subject: subject) })") - && TESTCLAIM_CORPUS_RUNNER_DAG.contains("test_claim_run_verdict(run: e.run)") - && TESTCLAIM_CORPUS_RUNNER_DAG - .contains("manual_corpus_gate(report: run_manual_testclaim_corpus_eval())"), + && TESTCLAIM_CORPUS_RUNNER_DAG.contains("test_claim_run_verdict(run: e.run)"), "acceptance path must fold per-row TestClaimRun verdicts from run_test_claim (T-38)" ); + assert!( + GUNBC_TEST_MANUAL_CORPUS_HARNESS_DAG + .contains("manual_corpus_gate(report: run_manual_testclaim_corpus_eval())"), + "ProcessExit harness must gate the runtime corpus report (not emit_host)" + ); assert!( !TESTCLAIM_CORPUS_RUNNER_DAG.lines().any(|line| { let t = line.trim(); diff --git a/src/v4/test/claim/workflow/testclaim_corpus_runner.dag b/src/v4/test/claim/workflow/testclaim_corpus_runner.dag index 29aa44134bc..6538c7ff02e 100644 --- a/src/v4/test/claim/workflow/testclaim_corpus_runner.dag +++ b/src/v4/test/claim/workflow/testclaim_corpus_runner.dag @@ -25,17 +25,9 @@ import v4.std.verdict { verdict_tally_total } import v4.std.verification { test_claim_label } -import std.process { - ProcessExit, - ExitSuccess, - exit_failure -} import v4.test.claim.manual.manual_corpus_roster { manual_corpus_node_subject_rows } -import v4.test.claim.workflow.manual_corpus_eval { - manual_corpus_gate -} type CorpusEvalEntry { @@ -146,24 +138,10 @@ fn run_manual_testclaim_corpus_eval() -> CorpusEvalReport { // Stage0 / `gunbc test` acceptance path (RR-A Rung-1; NOT emit_host): -// gunbc test → cli_run → gunbc_test_manual_corpus_harness_exit → run_manual_testclaim_corpus_eval -// → run_node_runtime_value_subjects → run_test_claim → TestClaimRun verdict per row -// → corpus_report_tally / test_claim_run_verdict → manual_corpus_gate → ProcessExit. -// At least one real Verdict fact flows through run_test_claim on the manual roster (F5 gate: -// zero Fail + zero Deferred). Forbidden: emit_host transport as terminal pass authority. -// Wraps the corpus report in `ProcessExit` so `cli_run` maps pass/fail to a host exit code. -// Authority: v4.workflow.bootstrap `bootstrap_manual_corpus_harness` is invoked via this -// symbol from `cli_test.rs`, not via `run_manual_testclaim_corpus_eval` (CorpusEvalReport). -data gunbc_test_manual_corpus_harness_exit: Symbol = gunbc_test_manual_corpus_harness_exit - -fn gunbc_test_manual_corpus_harness_exit() -> ProcessExit { - if manual_corpus_gate(report: run_manual_testclaim_corpus_eval()) { - ExitSuccess - } else { - exit_failure(reason: "manual_testclaim_corpus_eval gate not closed") - } -} - +// gunbc test → cli_run → gunbc.gunbc_test_manual_corpus_harness.gunbc_test_manual_corpus_harness_exit +// → run_manual_testclaim_corpus_eval → run_test_claim → test_claim_run_verdict per row +// → manual_corpus_gate (manual_corpus_eval.dag). ProcessExit wrapper lives in dsl/gunbc/ +// (std.process is not in the v4-only M1 emit closure). Forbidden: emit_host as pass authority. fn manual_testclaim_subject_roster_family_receipt() -> TestClaimSubjectRosterFamilyReceipt { let runs = run_node_runtime_value_subjects(subjects: manual_corpus_node_subject_rows) From ab8647dc6bb671a4d784bb843546959b06e31a2c Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 3 Jun 2026 02:31:25 +0000 Subject: [PATCH 4/4] chore: apply cargo fmt for cli_test harness roots Co-authored-by: Cursor --- src/v2/stage0/src/cli_test.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/v2/stage0/src/cli_test.rs b/src/v2/stage0/src/cli_test.rs index 9a35bda4e87..14164781fe8 100644 --- a/src/v2/stage0/src/cli_test.rs +++ b/src/v2/stage0/src/cli_test.rs @@ -19,10 +19,7 @@ pub fn handle_test_with_options(dry_run: bool) { HARNESS_ENTRY_FN ); cli_run::handle_run_with_options( - HARNESS_SOURCE_ROOTS - .iter() - .map(|s| s.to_string()) - .collect(), + HARNESS_SOURCE_ROOTS.iter().map(|s| s.to_string()).collect(), HARNESS_ENTRY_FN.to_string(), dry_run, );