Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions dsl/gunbc/gunbc_test_manual_corpus_harness.dag
Original file line number Diff line number Diff line change
@@ -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")
}
}
30 changes: 30 additions & 0 deletions scripts/v4-testclaim-corpus-eval.sh
Original file line number Diff line number Diff line change
@@ -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
37 changes: 33 additions & 4 deletions src/v2/05_emit_rust.dag
Original file line number Diff line number Diff line change
Expand Up @@ -1231,7 +1231,7 @@ fn emit_lib_rs_from_files(all_module_files: List<TextFile>, 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 {
""
}
Expand Down Expand Up @@ -7491,7 +7491,8 @@ fn emit_main_mod_uses(workflow_funcs: List<WorkflowFunc>, 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
}
Expand Down Expand Up @@ -7545,7 +7546,16 @@ fn emit_subcommand_enum(workflow_funcs: List<WorkflowFunc>, 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",
Expand Down Expand Up @@ -7616,7 +7626,12 @@ fn emit_main_fn(workflow_funcs: List<WorkflowFunc>, 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 {
Expand Down Expand Up @@ -7754,6 +7769,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
Expand Down
1 change: 1 addition & 0 deletions src/v2/stage0/src/bin/regen_stage0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
26 changes: 26 additions & 0 deletions src/v2/stage0/src/cli_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// 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;

/// 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 `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";

/// 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 {})",
HARNESS_SOURCE_ROOTS.join(" "),
HARNESS_ENTRY_FN
);
cli_run::handle_run_with_options(
HARNESS_SOURCE_ROOTS.iter().map(|s| s.to_string()).collect(),
HARNESS_ENTRY_FN.to_string(),
dry_run,
);
}
1 change: 1 addition & 0 deletions src/v2/stage0/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
7 changes: 7 additions & 0 deletions src/v2/stage0/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -383,6 +386,10 @@ fn main() {
} => {
cli_run::handle_run(source_roots, function);
}

Commands::Test => {
cli_test::handle_test_with_options(cli.dry_run);
}
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
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";

Check failure on line 47 in src/v3/compiler/tests/integration/v4_test_bootstrap_infra_closeout_test.rs

View workflow job for this annotation

GitHub Actions / ci_floor

constant `CI_PATH` is never used

Check failure on line 47 in src/v3/compiler/tests/integration/v4_test_bootstrap_infra_closeout_test.rs

View workflow job for this annotation

GitHub Actions / ci_floor

constant `CI_PATH` is never used
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";
Expand Down Expand Up @@ -977,6 +979,83 @@
);
}

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).
#[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_body.contains("\"dsl\""),
"cli_test.rs must target the ProcessExit harness entry with dsl+src/v4 source roots"
);
assert!(
GUNBC_TEST_MANUAL_CORPUS_HARNESS_DAG
.contains("fn gunbc_test_manual_corpus_harness_exit() -> ProcessExit"),
"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\"")
&& 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)"),
"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();
!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
/// variant to the `anchor` field (not wrapped in `manual_claim_anchor`).
/// Helper-function bodies (arity_rejection_claim, testgen_emit_*, etc.) are Call
Expand Down
6 changes: 6 additions & 0 deletions src/v4/test/claim/workflow/testclaim_corpus_runner.dag
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,12 @@ fn run_manual_testclaim_corpus_eval() -> CorpusEvalReport {
}


// Stage0 / `gunbc test` acceptance path (RR-A Rung-1; NOT emit_host):
// 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)
TestClaimSubjectRosterFamilyReceipt {
Expand Down
12 changes: 12 additions & 0 deletions src/v4/workflow/ci.dag
Original file line number Diff line number Diff line change
Expand Up @@ -1483,6 +1483,18 @@ 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) → 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 {
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<UpsertInputRef> = [
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"),
Expand Down
Loading