Files
peltier.io/.github/workflows/ci.yml
T
benbrownandClaude Fable 5 b9fa4932b9 ci: the Windows refusal test failed because the refusal succeeded
GitHub's PowerShell step wrapper appends 'exit $LASTEXITCODE', so the
inner preflight's deliberate exit 1 — the exact outcome the step had just
asserted — leaked out as the step's own failure. Clear it with an explicit
exit 0 once the refusal is verified. The 5.1 log from the failing run shows
the ps1 behaving perfectly: STATUS=refuse, correctly composed em dash,
'unsupported host Windows' reason.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 08:20:49 -04:00

271 lines
13 KiB
YAML

name: ci
on:
pull_request:
push:
branches: [main]
jobs:
trust-layer:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo test --workspace
- run: cargo clippy --workspace --all-targets -- -D warnings
- run: cargo fmt --all --check
# Phase 1: target gates run mechanically, and the bench workload
# executes inside a NO-NETWORK container (SPEC §3.1/§10). The runner
# is a shared VM — fine for gates; bench numbers for verdicts come
# from calibrated hardware, not this job.
target-gates-no-net:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Toolchains for C/C++ targets
run: sudo apt-get update -qq && sudo apt-get install -y -qq clang cmake
- name: Fetch pinned targets
run: |
sh targets/fetch.sh comrak
sh targets/fetch.sh tokei
sh targets/fetch.sh cjson
- name: Generate + verify pinned corpora
run: |
sh corpora/comrak/gen-corpus.sh
sh corpora/tokei/gen-corpus.sh
sh corpora/cjson/gen-corpus.sh
- name: Build tokei baseline
run: cargo build --release --manifest-path targets/tokei/workspace/Cargo.toml
- name: Gates — tokei (Rust; corpus pin -> upstream tests -> golden replay)
run: cargo run -p diff-test -- tokei
- name: Gates — cjson (C; clang build -> Unity/CTest -> golden replay)
run: cargo run -p diff-test -- cjson
- name: Bench workload inside no-network container
run: |
docker run --rm --network=none \
-v "$PWD:/w" -w /w \
debian:bookworm-slim \
sh -c "targets/tokei/workspace/target/release/tokei --sort code corpora/tokei/input | sha256sum"
# Informational: measure THIS runner's noise floor with the A/A +
# injection harness and archive the evidence. A shared VM failing the
# bars is expected — the artifact is the point (trust layer judges
# environments empirically; verdict benches belong on bench-metal).
runner-calibration:
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Calibrate (reduced sessions, synthetic CPU-bound workload)
run: |
mkdir -p calib
cargo run -p bench-runner -- calibrate \
--cmd "head -c 3000000 /dev/urandom | sha256sum" \
--sessions 20 --out calib/gh-runner-calibration.json \
| tee calib/summary.txt
- uses: actions/upload-artifact@v4
if: always()
with:
name: runner-calibration
path: calib/
# Informational: does perf work here, and with which event sources?
perf-probe:
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4
- name: Probe perf availability
run: |
sudo apt-get -qq update && sudo apt-get -qq install -y linux-tools-common linux-tools-generic || true
PERF=$(ls /usr/lib/linux-tools/*/perf 2>/dev/null | head -1 || command -v perf || true)
echo "perf binary: ${PERF:-none}"
[ -n "$PERF" ] || exit 0
sudo sysctl -w kernel.perf_event_paranoid=1 || true
echo "--- software clock sampling:"
$PERF record -e cpu-clock -o /tmp/p.data -- sh -c 'i=0; while [ $i -lt 2000000 ]; do i=$((i+1)); done' && $PERF report -i /tmp/p.data --stdio | head -5 || echo "cpu-clock sampling unavailable"
echo "--- hardware cycle counter:"
$PERF stat -e cycles true 2>&1 | tail -3 || true
# The peltier skill's contract: preflight must resolve the trust layer when
# one is reachable, and REFUSE when it is not. The refusal is the
# safety-critical path — a skill that silently degrades to a hand-rolled
# timing loop is worse than no skill — so it is asserted here, not assumed.
# Statistical outcomes are NOT asserted: a shared runner is too noisy for
# that, which is the whole reason the skill calibrates before it claims.
skill-preflight:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: SKILL.md conforms to the Agent Skills spec (both copies)
run: |
python3 - <<'PY'
import re, sys, yaml, pathlib
for skill in (".claude/skills/peltier", ".agents/skills/peltier"):
p = pathlib.Path(skill) / "SKILL.md"
text = p.read_text(encoding="utf-8")
if not text.startswith("---\n"):
sys.exit(f"{p}: must open with a --- frontmatter block")
fm = yaml.safe_load(text.split("---", 2)[1]) # raises on malformed YAML
body = text.split("---", 2)[2]
for key in ("name", "description"):
if not isinstance(fm.get(key), str) or not fm[key].strip():
sys.exit(f"{p}: frontmatter '{key}' must be a non-empty string")
# agentskills.io spec: 1-64 chars, [a-z0-9-], no edge/double hyphens,
# name == parent dir; description <= 1024 chars; body <= 500 lines.
n = fm["name"]
if not re.fullmatch(r"[a-z0-9]+(-[a-z0-9]+)*", n) or len(n) > 64:
sys.exit(f"{p}: name '{n}' violates the Agent Skills name rules")
if n != p.parent.name:
sys.exit(f"{p}: name '{n}' != directory '{p.parent.name}'")
if len(fm["description"]) > 1024:
sys.exit(f"{p}: description exceeds 1024 chars")
if len(body.splitlines()) > 500:
sys.exit(f"{p}: body exceeds 500 lines")
if "metadata" in fm and not (
isinstance(fm["metadata"], dict)
and all(isinstance(k, str) and isinstance(v, str) for k, v in fm["metadata"].items())
):
sys.exit(f"{p}: metadata must be a string-to-string map")
print(f"{p}: spec OK (name={n}, description={len(fm['description'])} chars)")
PY
- name: .agents mirror is byte-identical to .claude canonical
run: |
diff -r .claude/skills/peltier .agents/skills/peltier
echo "mirror identical"
- name: Installer stamps both layouts + the ZeroClaw variant correctly
run: |
if sh scripts/install-skill.sh /tmp/no-such-dir 2>/dev/null; then
echo "FAIL: installer accepted a nonexistent target"; exit 1
fi
mkdir -p /tmp/consumer && sh scripts/install-skill.sh /tmp/consumer
diff -r .claude/skills/peltier /tmp/consumer/.claude/skills/peltier
diff -r .claude/skills/peltier /tmp/consumer/.agents/skills/peltier
sh scripts/install-skill.sh --zeroclaw-variant /tmp/zc-variant
test ! -e /tmp/zc-variant/scripts # script-less: ZeroClaw blocks script files
test -f /tmp/zc-variant/SKILL.md
diff -r .claude/skills/peltier/references /tmp/zc-variant/references
echo "installer OK: both layouts identical to canonical; zeroclaw variant has no scripts/"
- name: Preflight resolves inside a peltier checkout
run: |
sh .claude/skills/peltier/scripts/preflight.sh | tee /tmp/pf.txt
grep -q '^STATUS=ok$' /tmp/pf.txt
grep -q "^PELTIER_HOME=$PWD$" /tmp/pf.txt
BR=$(sed -n 's/^BENCH_RUNNER=//p' /tmp/pf.txt); test -x "$BR"
- name: Preflight REFUSES with no trust layer reachable
run: |
# The skill must be copied OUT of the checkout: preflight also walks
# up from its own location, so running it in-place would find this
# very repo and (correctly) resolve — testing nothing.
cp -r .claude/skills/peltier /tmp/skillcopy
cd /tmp && mkdir -p not-a-repo && cd not-a-repo
set +e
env -u PELTIER_HOME sh /tmp/skillcopy/scripts/preflight.sh > out.txt 2>&1
rc=$?
set -e
cat out.txt
test "$rc" -ne 0 || { echo "FAIL: preflight did not refuse"; exit 1; }
grep -q '^STATUS=refuse$' out.txt
- name: Preflight works when consumed from another repo (PELTIER_HOME)
run: |
cd /tmp && mkdir -p other-project && cd other-project
PELTIER_HOME="$GITHUB_WORKSPACE" \
sh "$GITHUB_WORKSPACE/.claude/skills/peltier/scripts/preflight.sh" | tee pf.txt
grep -q '^STATUS=ok$' pf.txt
- name: sh and ps1 preflights are byte-equivalent (two entry points, one contract)
run: |
# preflight.ps1 exists for hosts with no POSIX shell. Two
# implementations of the same contract can drift, so both run here
# on the same host and their stdout is byte-diffed on every path.
# 1. in-checkout resolve
sh .claude/skills/peltier/scripts/preflight.sh > /tmp/eq-sh.txt
pwsh -NoProfile -File .claude/skills/peltier/scripts/preflight.ps1 > /tmp/eq-ps.txt
diff /tmp/eq-sh.txt /tmp/eq-ps.txt
# 2. refusal with no checkout reachable (copied out of the repo)
cp -r .claude/skills/peltier /tmp/eqskill
mkdir -p /tmp/eq-not-a-repo && cd /tmp/eq-not-a-repo
set +e
env -u PELTIER_HOME sh /tmp/eqskill/scripts/preflight.sh > sh.txt 2>&1; shrc=$?
env -u PELTIER_HOME pwsh -NoProfile -File /tmp/eqskill/scripts/preflight.ps1 > ps.txt 2>&1; psrc=$?
set -e
test "$shrc" -ne 0 && test "$psrc" -ne 0
diff sh.txt ps.txt
# 3. resolve via PELTIER_HOME from a foreign repo
mkdir -p /tmp/eq-foreign && cd /tmp/eq-foreign
PELTIER_HOME="$GITHUB_WORKSPACE" sh /tmp/eqskill/scripts/preflight.sh > sh2.txt
PELTIER_HOME="$GITHUB_WORKSPACE" pwsh -NoProfile -File /tmp/eqskill/scripts/preflight.ps1 > ps2.txt
diff sh2.txt ps2.txt
echo "equivalence holds on all three paths"
- name: The harness the skill drives actually runs (plumbing, not verdict)
run: |
BR=$(sed -n 's/^BENCH_RUNNER=//p' /tmp/pf.txt)
cp .claude/skills/peltier/assets/accept.template.toml /tmp/accept.toml
"$BR" --config /tmp/accept.toml compare \
--baseline 'sleep 0.02' --candidate 'sleep 0.02' | tee /tmp/cmp.txt
grep -q 'speedup (baseline/candidate): median .*95% CI \[' /tmp/cmp.txt
grep -q '^verdict: ' /tmp/cmp.txt
- name: Noise floor of this runner (informational — A/A must find nothing)
continue-on-error: true
run: |
BR=$(sed -n 's/^BENCH_RUNNER=//p' /tmp/pf.txt)
"$BR" --config /tmp/accept.toml aa --cmd 'sleep 0.02'
# The refusal doctrine must survive hosts with no POSIX shell: on a real
# Windows runner, BOTH preflight entry points must refuse with a structured
# STATUS=refuse — never a launch failure an agent could misread as license
# to hand-roll a timing loop. Windows PowerShell 5.1 is tested separately
# from pwsh because 5.1 misdecodes BOM-less UTF-8 (the ps1 source is pure
# ASCII for exactly that reason).
skill-preflight-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: ps1 refuses natively (Windows PowerShell 5.1)
shell: powershell
run: |
$out = & powershell -NoProfile -ExecutionPolicy Bypass -File .claude\skills\peltier\scripts\preflight.ps1
$out | Write-Output
if ($LASTEXITCODE -eq 0) { throw "preflight did not refuse on Windows" }
$joined = $out -join "`n"
if ($joined -notlike "*STATUS=refuse*") { throw "no STATUS=refuse in output" }
if ($joined -notlike "*unsupported host 'Windows'*") { throw "missing refusal reason" }
Write-Output "native refusal verified (5.1)"
# the runner appends `exit $LASTEXITCODE`; the refusal's exit 1 is
# the EXPECTED outcome we just asserted, so clear it explicitly
exit 0
- name: ps1 refuses natively (pwsh)
shell: pwsh
run: |
$out = & pwsh -NoProfile -File .claude\skills\peltier\scripts\preflight.ps1
$out | Write-Output
if ($LASTEXITCODE -eq 0) { throw "preflight did not refuse under pwsh" }
if (($out -join "`n") -notlike "*STATUS=refuse*") { throw "no STATUS=refuse in output" }
Write-Output "native refusal verified (pwsh)"
exit 0 # clear the asserted refusal's exit 1 (runner appends exit $LASTEXITCODE)
- name: sh entry point also refuses here (Git Bash)
shell: bash
run: |
set +e
sh .claude/skills/peltier/scripts/preflight.sh > out.txt 2>&1
rc=$?
set -e
cat out.txt
test "$rc" -ne 0
grep -q '^STATUS=refuse$' out.txt