Files
peltier.io/.github/workflows/ci.yml
T
benbrownandClaude Fable 5 477c5472dd skill: make the peltier skill consumable across agent harnesses
The SKILL.md Agent Skills standard (agentskills.io) is now read natively by
Codex, Copilot, VS Code, Cursor, Gemini CLI, opencode, Goose, Amp, Zed,
Factory, and ZeroClaw — so the portable move is not per-harness adapter
files (forked content that drifts) but one canonical skill placed where
each harness actually looks.

- Spec-compliant frontmatter: license, compatibility (the Linux-only
  measurement story, stated up front), version/author/tags top-level
  (ZeroClaw's runtime reads those; the spec ignores unknown fields).
  reference/ renamed to references/ per the spec's conventional layout.
- .agents/skills/peltier/ mirror: the cross-tool convergence path (Codex,
  Zed, Cursor, Gemini CLI, Copilot, opencode, Goose, Amp). Byte-identical
  to the .claude canonical, enforced by CI — drift between the two copies
  is a red build, not a footnote.
- AGENTS.md at the repo root for the ~20 harnesses that read it (Claude
  Code does not; CLAUDE.md remains its file). Deliberately thin: it points
  at CLAUDE.md and the skill instead of restating rules.
- scripts/install-skill.sh: stamps a consumer repo at both discovery paths
  (--layout claude|agents|both), or emits a script-less --zeroclaw-variant.
  ZeroClaw's skill security policy blocks script files by default
  (skills.allow_scripts off) — its audit rejected the full skill and passes
  the variant, verified against the real zeroclaw binary. The variant loses
  nothing: preflight.sh still exists exactly once, in the peltier checkout
  the skill already requires, and SKILL.md now documents running it from
  there. Re-implementing preflight inline is named as a refusal condition.
- CI (skill-preflight): frontmatter upgraded to a full Agent Skills spec
  conformance check on BOTH copies (name rules, dir match, description and
  body limits, metadata string-map), plus mirror equality and a functional
  installer test (both layouts identical to canonical, variant carries no
  scripts/, nonexistent target refused).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 11:59:26 -04:00

203 lines
9.3 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: 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'