From e3fcd27b803f3cc6e446916674bc64a7f4b24541 Mon Sep 17 00:00:00 2001 From: Ralf Anton Beier Date: Sat, 27 Jun 2026 06:52:16 +0200 Subject: [PATCH 1/2] =?UTF-8?q?test(e2e):=20Tier=20C=20=E2=80=94=20meld?= =?UTF-8?q?=E2=86=92kiln=20execution=20seam=20(#297),=20#[ignore]d=20on=20?= =?UTF-8?q?kiln#364?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit golden_e2e Tier A/B prove behavioural equivalence under wasmtime but disclose the boundary "NOT on the synth/kiln MCU target". #297 asks to close it. Adds Tier C: fuse a wasi:cli command fixture (--output component), execute it on kilnd, and assert it runs — with the unfused original as the on-kiln baseline so a fused failure is attributable to fusion. The seam is currently BROKEN: kiln's component executor requires a core-instance _start, which meld's multi-core-module wrap (stubs, fused, fixup, caller modules with a deferred start) doesn't expose — it fails with "No core instance exports _start". The same artifact runs under wasmtime via wasi:cli/run (Tier A/B green), so meld's output is spec-valid. Filed kiln#364. So the test is #[ignore]d with that reason; verified it fails for exactly the documented cause (baseline ran on kiln, fused did not). Un-ignore when kiln#364 lands and it pins the meld-to-kiln seam green. kilnd is located via MELD_KILND or the conventional ../../kiln debug build; absent -> skip. Refs: #297, kiln#364. Co-Authored-By: Claude Opus 4.8 (1M context) --- meld-core/tests/golden_e2e.rs | 105 ++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/meld-core/tests/golden_e2e.rs b/meld-core/tests/golden_e2e.rs index b93d049..3f83d55 100644 --- a/meld-core/tests/golden_e2e.rs +++ b/meld-core/tests/golden_e2e.rs @@ -412,3 +412,108 @@ fn tier_b_separate_inputs_internalise_link() { fn ls_cp_6_separate_inputs_internalise_link() { tier_b_separate_inputs_internalise_link(); } + +// --------------------------------------------------------------------------- +// Tier C (#297): execution on kiln — the safety-critical MCU-target runtime, +// not just wasmtime. This is the "honest boundary" Tier A/B disclose. +// --------------------------------------------------------------------------- + +/// Locate the `kilnd` binary: `MELD_KILND` override, else the conventional +/// sibling-repo debug build. `None` (→ skip) when unavailable, mirroring the +/// fixture-absent skips above. +fn kilnd_path() -> Option { + if let Ok(p) = std::env::var("MELD_KILND") { + let p = std::path::PathBuf::from(p); + if p.exists() { + return Some(p); + } + } + let conv = + std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../kiln/target/debug/kilnd"); + conv.exists().then_some(conv) +} + +/// Run a `--component` wasm on `kilnd` and report whether kiln executed it +/// successfully. `None` when kilnd is unavailable (→ skip). `tag` only +/// disambiguates the temp file (no `Date`/rand in tests — the caller-supplied +/// tag is unique per fixture+role). Uses `std::process::Command` fully +/// qualified to avoid the `wasmtime_wasi::...::Command` import above. +fn run_on_kiln(wasm: &[u8], tag: &str) -> Option { + let kilnd = kilnd_path()?; + let tmp = std::env::temp_dir().join(format!("meld_tierc_{tag}.wasm")); + std::fs::write(&tmp, wasm).ok()?; + let out = std::process::Command::new(&kilnd) + .arg(&tmp) + .arg("--component") + .arg("--wasi") + .output() + .ok()?; + let _ = std::fs::remove_file(&tmp); + let combined = format!( + "{}{}", + String::from_utf8_lossy(&out.stdout), + String::from_utf8_lossy(&out.stderr) + ); + // kilnd prints an explicit success/failure line; exit code alone is + // unreliable across its run modes. + Some(combined.contains("executed successfully") && !combined.contains("Execution failed")) +} + +/// Tier C (#297): a meld-fused component must EXECUTE on kiln — the +/// safety-critical MCU-target runtime — not only under wasmtime (Tier A/B). +/// +/// `#[ignore]`d: today kiln's component executor looks for a core-instance +/// `_start` and cannot run meld's multi-core-module `--component` wrap (the +/// stubs, fused, fixup and caller modules, with a deferred start); it fails +/// with "No core instance exports _start". Filed as kiln#364. wasmtime runs +/// the same artifact via `wasi:cli/run` (Tier A/B green), so meld's output is +/// spec-valid. Un-ignore when kiln#364 lands; it then pins the meld-to-kiln +/// behavioural seam green. +#[test] +#[ignore = "blocked on kiln#364: kilnd requires a core-instance _start; can't run meld's multi-core-module fused component"] +fn tier_c_fused_executes_on_kiln() { + if kilnd_path().is_none() { + eprintln!("skipping Tier C: kilnd not found (set MELD_KILND or build ../../kiln)"); + return; + } + let mut ran = 0usize; + for &name in &[ + "release-0.2.0/hello_c_cli", + "release-0.2.0/hello_rust", + "release-0.2.0/hello_cpp_cli", + ] { + let Some(orig) = fixture_bytes(name) else { + continue; + }; + let tag = name.replace('/', "_"); + // Baseline: the unfused original must run on kiln, else a fused + // failure can't be attributed to fusion. + match run_on_kiln(&orig, &format!("{tag}_orig")) { + Some(true) => {} + Some(false) => { + eprintln!("[{name}] unfused original did not execute on kiln; skipping"); + continue; + } + None => return, // kilnd vanished mid-run + } + let fused = fuse( + &orig, + name, + OutputFormat::Component, + MemoryStrategy::MultiMemory, + ) + .expect("fusing a single command component must succeed"); + let ok = run_on_kiln(&fused, &format!("{tag}_fused")).expect("kilnd available (checked)"); + assert!( + ok, + "[{name}] meld-fused component must execute on kiln (kiln#364): \ + the unfused original ran but the fused one did not" + ); + ran += 1; + } + assert!( + ran > 0, + "Tier C exercised no command fixtures (corpus missing in {FIXTURES_DIR}?)" + ); + eprintln!("Tier C: {ran} fused components executed on kiln ✓"); +} From c2c6cf971022c0041a97f6812fd6ff97ac7622a0 Mon Sep 17 00:00:00 2001 From: Ralf Anton Beier Date: Sat, 27 Jun 2026 11:10:09 +0200 Subject: [PATCH 2/2] chore(release): adopt rivet v0.22.0 release-handling as the readiness gate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rivet v0.22.0 turns the release: field into a queryable readiness gate (rivet release status — per-status burn-down, "cuttable" verdict, non-zero exit when not, --format json for CI). - Bump the compliance.yml rivet pin v0.19.0 -> v0.22.0. - Scope SR-45 (the v0.37.0-delivered fusion-premise req) with the first-class release: v0.37.0 via `rivet release move`, and move it implemented -> verified (its V is closed: SWV-45's SCPV v3 codec + golden-fusion tests pass), matching the 28-verified convention. - Document the release-readiness query as a mandatory pre-release checklist step in AGENTS.md (scope artifacts with release:, gate on rivet release status). Historical per-release vX.Y *tags* stay as shipped-in markers; the release: field is the single forward scope the query reads. Tested: `rivet release status v0.37.0` -> "Cuttable — every artifact is verified/accepted", exit 0; JSON {cuttable:true, not_verified:[]}. rivet validate still PASS (70 pre-existing warnings). Refs: #302 (Track E release-gating), rivet #516. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/compliance.yml | 4 ++-- AGENTS.md | 6 ++++++ safety/requirements/safety-requirements.yaml | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/compliance.yml b/.github/workflows/compliance.yml index e019fb4..e17e59d 100644 --- a/.github/workflows/compliance.yml +++ b/.github/workflows/compliance.yml @@ -43,10 +43,10 @@ jobs: - name: Generate compliance report id: report - uses: pulseengine/rivet/.github/actions/compliance@v0.19.0 + uses: pulseengine/rivet/.github/actions/compliance@v0.22.0 with: theme: dark - rivet-version: v0.19.0 + rivet-version: v0.22.0 include-data-formats: true report-label: ${{ steps.tag.outputs.tag }} archive: 'true' diff --git a/AGENTS.md b/AGENTS.md index d111c97..eec57ca 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -777,6 +777,12 @@ GitHub REST API). 2. **CI must pass completely**: Wait for ALL CI jobs to succeed before merging 3. **Watch the full CI run**: Do not assume CI passes — verify it 4. **Mythos delta pass**: Run per `### Pre-Release Mythos delta pass` above. Zero `confirmed` findings, OR every `confirmed` finding maps to an `approved LS-N` in `safety/stpa/loss-scenarios.yaml` with a shipped fix +5. **Release-readiness query (rivet ≥ v0.22.0)**: readiness is a query, not an opinion. Scope this release's requirement artifacts with the first-class `release:` field (`rivet release move vX.Y.Z`), then gate on: + ```bash + rivet release status vX.Y.Z # human burn-down; exits non-zero when not cuttable + rivet release status vX.Y.Z --format json # CI-consumable: {"cuttable": bool, "not_verified": [...]} + ``` + The release is **cuttable** only when every scoped artifact is `verified`/`accepted` (an artifact whose V is closed — verification passing at the right levels — moves `implemented`→`verified`). The `compliance.yml` rivet pin is also v0.22.0. (Historical per-release `vX.Y` *tags* on artifacts are "shipped-in" markers and stay; the `release:` field is the single forward scope `rivet release status` queries.) #### Release Steps diff --git a/safety/requirements/safety-requirements.yaml b/safety/requirements/safety-requirements.yaml index 39350fb..d42e4ce 100644 --- a/safety/requirements/safety-requirements.yaml +++ b/safety/requirements/safety-requirements.yaml @@ -1523,7 +1523,7 @@ artifacts: (scry#63); meld is the producer, scry-provenance owns the format (DD-002). meld shall NOT compute value ranges / constant args / dead params — those are scry's abstract interpretation, fed by these premises (#313). - status: implemented + status: verified tags: [feature, provenance, specialization, v0.37.0] links: - type: derives-from @@ -1535,6 +1535,7 @@ artifacts: - uri: "https://github.com/pulseengine/scry/issues/63" kind: github last-checked: 2026-06-26 + release: v0.37.0 fields: implementation: - meld-core/src/provenance.rs