Skip to content
Merged
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
58 changes: 26 additions & 32 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,38 +58,32 @@ jobs:
key: cargo-ci-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock', 'rust-toolchain.toml') }}
restore-keys: |
cargo-ci-${{ runner.os }}-${{ runner.arch }}-
# CI floor — one step, nine `.dag` evaluations:
# (1) claim_batch: the always-on src/v2 witness corpus (the compiler-seed
# `claim_batch` bin, --roster-from-discovery). Discovery is a build-time
# lens over parsed source (NOT runtime reflection), so it is a Rust host today;
# it dissolves when witness discovery moves to gunbc.
# (2) gunbc: the rust-monolith SELECTOR witnesses (tools.rust_stage0_gates) — keeps the
# selector's edge cases (suffix-not-substring, dag-only, fail-closed) gated.
# (3) gunbc: the rust-monolith GATE (tools.rust_gates_ci) — git diff -> selector ->
# cargo fmt + clippy, returning ProcessExit (fail-closed). The gate logic is `.dag`
# authority; the only Rust is the gunbc interpreter seed (DESIGN §7). A `.dag`-only
# diff fires neither cargo gate; an undeterminable diff fail-closes (runs them).
# (4) gunbc: emit-host gate WITNESSES (tools.emit_host_fixtures contract pins).
# (5) gunbc: emit-host GATE (tools.emit_host_gate) — compiles+runs MVP fixtures per
# target row; replaces deleted `tools/emit_host_runner` (DESIGN §7).
# (6) gunbc: layering-imports coverage witness (pure roster pins, src/v2).
# (7) gunbc: layering-imports GATE (tools.layering_imports_gate) — host scan +
# clean_tree + scanner-execution perturb receipts; replaces deleted
# layering_imports_scan + scripts/v4-layering-imports-gate.sh (DESIGN §7).
# (8) gunbc: filesystem entry-kind witness (test.claim.filesystem_entry_kind_witness) —
# by-execution proof that the cited posix/ntfs reductions map to the agnostic
# std.filesystem.types.EntryKind (socket/junction → Other, not nicknamed Symlink).
# (9) gunbc: std.measure Power/Watt consuming-fold witness (computes a wattage total).
# The git fetch makes `origin/main...HEAD` resolvable.
# CI floor — two composed passes (was nine separate cargo-run lines on main):
# (1) claim_batch: discovery over src/v2 (test fn / unified_claim_* corpus)
# PLUS hand-enrolled Bool witnesses (rust_stage0, emit_host, layering coverage,
# filesystem entry-kind, power/watt consuming-fold) that lived on separate
# `gunbc run --claim-run` lines. One module index, one resolve per distinct
# entry — not one cold resolve per witness.
# SCAFFOLD — dissolve when the hand-enrolled witnesses enroll as discoverable
# `test fn` rows under a scanned dsl root (then drop the explicit --entry lines).
# (2) gunbc: tools.ci_floor_gates — the three effectful ProcessExit gates
# (rust fmt/clippy selector, emit-host MVP smokes, layering-imports
# host scan + perturb receipts) composed behind one entry resolve.
# The git fetch makes `origin/main...HEAD` resolvable for the rust gate.
- name: CI floor (.dag witness corpus + gates via gunbc)
run: |
git fetch --no-tags origin main 2>/dev/null || true
cargo run -p v1-compiler --release --bin claim_batch -- --source-root src/v2 --roster-from-discovery --scan-dir src/v2/test --scan-dir src/v2/compiler/extdeps_shape_transport_policy --notice-title "v2 claim corpus"
cargo run -p v1-compiler --release --bin gunbc -- run --source-root dsl --entry dsl/test/claim/rust_stage0_gates_witness.dag --function rust_stage0_gates_witnesses --claim-run
cargo run -p v1-compiler --release --bin gunbc -- run --source-root dsl --entry dsl/tools/rust_gates_ci.dag --function main
cargo run -p v1-compiler --release --bin gunbc -- run --source-root dsl --entry dsl/test/claim/emit_host_gate_witness.dag --function emit_host_gate_witnesses --claim-run
cargo run -p v1-compiler --release --bin gunbc -- run --source-root dsl --entry dsl/tools/emit_host_gate.dag --function main
cargo run -p v1-compiler --release --bin gunbc -- run --source-root src/v2 --entry src/v2/test/claim/layering_imports/coverage_domain_equivalence.dag --function coverage_domain_equivalence_argument_holds --claim-run
cargo run -p v1-compiler --release --bin gunbc -- run --source-root dsl --entry dsl/tools/layering_imports_gate.dag --function main
cargo run -p v1-compiler --release --bin gunbc -- run --source-root dsl --entry dsl/test/claim/filesystem_entry_kind_witness.dag --function filesystem_entry_kind_witnesses --claim-run
cargo run -p v1-compiler --release --bin gunbc -- run --source-root dsl --entry dsl/test/claim/power_watt_witness.dag --function power_watt_witnesses --claim-run
cargo run -p v1-compiler --release --bin claim_batch -- \
--source-root src/v2 --source-root dsl \
--roster-from-discovery \
--scan-dir src/v2/test \
--scan-dir src/v2/compiler/extdeps_shape_transport_policy \
--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 \
--notice-title "v2 claim corpus"
cargo run -p v1-compiler --release --bin gunbc -- run \
--source-root dsl --source-root src/v2 \
--entry dsl/tools/ci_floor_gates.dag --function main
2 changes: 1 addition & 1 deletion DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,4 +192,4 @@ still a violation) · internal review finds missing tests, external review finds

- `cargo test --workspace` · `cargo clippy --all-targets -- -D warnings` · `cargo fmt --all --check`
- one-time: `git config core.hooksPath .githooks` (pre-push runs `cargo fmt`)
- CI floor (all `v1-compiler` seed bins, no shell gate): `cargo run -p v1-compiler --release --bin claim_batch -- --source-root src/v2 --roster-from-discovery --scan-dir src/v2/test` discovers + evaluates the witness corpus and enforces v2 `.dag` filename hygiene (no `__` in basenames; use subdirectories); the Rust fmt/clippy gate runs via `gunbc run --source-root dsl --entry dsl/tools/rust_gates_ci.dag --function main`
- CI floor (all `v1-compiler` seed bins, no shell gate): two composed passes in `.github/workflows/ci.yml` — (1) `claim_batch --roster-from-discovery` over `src/v2` plus four hand-enrolled Bool witnesses (`--source-root dsl`, shared module index); (2) `gunbc run --entry dsl/tools/ci_floor_gates.dag` sequences the three effectful ProcessExit gates (rust fmt/clippy selector, emit-host MVP smokes, layering-imports host scan + perturb receipts) behind one resolve
38 changes: 38 additions & 0 deletions dsl/tools/ci_floor_gates.dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// tools/ci_floor_gates.dag — composed effectful CI floor gates (one resolve, one process).
//
// Sequences the three ProcessExit gates previously invoked as separate `gunbc run`
// lines in `.github/workflows/ci.yml`: rust-monolith fmt/clippy, emit-host MVP smokes,
// and layering-imports host scan + perturb receipts. Bool witnesses stay in
// `claim_batch --roster-from-discovery` (same process, shared module index).
//
// Run in CI:
// cargo run -p v1-compiler --release --bin gunbc -- run \
// --source-root dsl --source-root src/v2 \
// --entry dsl/tools/ci_floor_gates.dag --function main
//
// SCAFFOLD — dissolves when the Rust seed reaches zero (DESIGN §7).

module tools.ci_floor_gates

import std.process { ProcessExit, ExitSuccess, exit_failure }
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 }

fn exit_ok(e: ProcessExit) -> Bool {
match e { ExitSuccess => true _ => false }
}

// Run all three gates (no short-circuit — surface every transport failure in one pass).
func main() -> ProcessExit {
rust = run_rust_monolith_gate()
emit_host = run_emit_host_ci_gate()
layering = run_layering_imports_ci_gate()
return if exit_ok(rust) && exit_ok(emit_host) && exit_ok(layering) {
ExitSuccess
} else {
exit_failure(
reason: "ci floor gates failed: rust_monolith and/or emit_host and/or layering_imports"
)
}
}
5 changes: 5 additions & 0 deletions dsl/tools/emit_host_gate.dag
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,8 @@ func run_gate() -> ProcessExit {
func main() -> ProcessExit {
run_gate()
}

// Composed CI floor entry point (tools.ci_floor_gates imports this name).
func run_emit_host_ci_gate() -> ProcessExit {
run_gate()
}
5 changes: 5 additions & 0 deletions dsl/tools/layering_imports_gate.dag
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,8 @@ func run_gate() -> ProcessExit {
func main() -> ProcessExit {
run_gate()
}

// Composed CI floor entry point (tools.ci_floor_gates imports this name).
func run_layering_imports_ci_gate() -> ProcessExit {
run_gate()
}
6 changes: 5 additions & 1 deletion dsl/tools/rust_gates_ci.dag
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,15 @@ func run_gates() -> ProcessExit {
}

// Entry: consult the diff, fire the gates only when the Rust monolith is touched.
func main() -> ProcessExit {
func run_rust_monolith_gate() -> ProcessExit {
changed = git.Core.ChangedFiles()
return if should_run_gates(paths: changed.paths, git_success: changed.success) {
run_gates()
} else {
ExitSuccess
}
}

func main() -> ProcessExit {
run_rust_monolith_gate()
}
44 changes: 34 additions & 10 deletions src/v1/stage0/src/bin/claim_batch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,17 @@
//! `ci_claim_gate` carried was flag-gated (`--perturb-check`) and never run
//! in CI, so it is intentionally dropped with that binary.
//!
//! 4. **Discovery + explicit append** (consolidated CI floor witnesses):
//! `--roster-from-discovery` may be combined with explicit `--entry`/
//! `--function` rows. Discovered rows run first; explicit rows are appended
//! with (entry, function) dedup. `--scan-dir` is optional when explicit rows
//! are given (the `test fn` walk still runs over all `--source-root`s).
//!
//! Usage (discovery — the CI floor shape):
//! claim_batch --source-root <dir> [--source-root <dir> ...] \
//! --roster-from-discovery \
//! --scan-dir <dir> [--scan-dir <dir> ...] \
//! [--scan-dir <dir> ...] \
//! [--entry <file.dag> --function <fn> ...] \
//! [--notice-title <title>] [--wet] [--hermetic]
//!
//! Exit codes: 0 = all witnesses returned Bool(true); 1 = any witness failed,
Expand Down Expand Up @@ -484,16 +491,12 @@ fn parse_args(args: &[String]) -> Result<ParsedArgs, ExitCode> {
}

let discovery = if roster_from_discovery {
if !entry_groups.is_empty() {
if scan_dirs.is_empty() && entry_groups.is_empty() {
eprintln!(
"claim_batch: --roster-from-discovery is exclusive with --entry/--function/--functions"
"claim_batch: --roster-from-discovery requires at least one --scan-dir and/or explicit --entry row"
);
return Err(ExitCode::from(2));
}
if scan_dirs.is_empty() {
eprintln!("claim_batch: --roster-from-discovery requires at least one --scan-dir");
return Err(ExitCode::from(2));
}
Some(DiscoveryConfig {
scan_dirs,
notice_title,
Expand Down Expand Up @@ -694,8 +697,9 @@ fn run() -> Result<ExitCode, ExitCode> {
return Err(ExitCode::from(2));
}

// Discovery mode: reflect over the scan dirs for the roster, group by entry,
// and run through the SAME batch loop the explicit path uses.
// Discovery mode: reflect over the scan dirs for the roster, optionally append
// explicit `--entry` rows, group by entry, and run through the SAME batch
// loop the explicit-only path uses.
let (entry_groups, discovery_notice) = if let Some(disc) = parsed.discovery {
// Filename hygiene (fail-closed): no `__` in any `.dag` basename under the
// source roots — folder-based naming, no legacy flat-dir encoding (#5051,
Expand All @@ -704,17 +708,37 @@ fn run() -> Result<ExitCode, ExitCode> {
eprintln!("claim_batch: {e}");
return Err(ExitCode::from(2));
}
let rows = match discover_roster(&source_roots, &disc.scan_dirs) {
let mut rows = match discover_roster(&source_roots, &disc.scan_dirs) {
Ok(r) => r,
Err(e) => {
eprintln!("claim_batch: discovery roster failed: {e}");
return Err(ExitCode::from(2));
}
};
let mut seen: std::collections::BTreeSet<(String, String)> = rows
.iter()
.map(|r| (r.entry.clone(), r.function.clone()))
.collect();
for group in &parsed.entry_groups {
for function in &group.functions {
if seen.insert((group.entry.clone(), function.clone())) {
rows.push(GateRow {
label: function.clone(),
entry: group.entry.clone(),
function: function.clone(),
});
}
}
}
if rows.is_empty() {
eprintln!("claim_batch: roster produced no rows (empty corpus → fail closed)");
return Err(ExitCode::from(2));
}
rows.sort_by(|a, b| {
a.entry
.cmp(&b.entry)
.then_with(|| a.function.cmp(&b.function))
});
(group_discovered_rows(rows), Some(disc.notice_title))
} else {
(parsed.entry_groups, None)
Expand Down