ci: add GitHub Actions workflow encoding the CLAUDE.md gates#169
Conversation
kaish had no CI — every gate (tests, clippy, the capability-feature sandbox build, the WASI target) ran only on contributor machines, so nothing stopped a red suite or a new lint from landing via a PR that skipped the runbook. Modeled on kaibo's ci.yml (pinned-SHA actions, minimal permissions, per-ref concurrency) but encoding kaish's own gates as three parallel jobs: test+clippy (-D warnings, --all-targets), kernel no-default-features tests, and the wasm32-wasip1 build. kaish releases to crates.io rather than binaries and publishing stays manual (/release), so there is no release workflow; kaibo's TLS-invariant guard was dropped as kaibo-specific. Instead of a second cargo-insta run, a tripwire step fails on any committed *.snap.new — under CI insta fails tests without writing files, so any found were committed locally. A kaibo review caught the matching gap on the local side: .gitignore had no *.snap.new entry, added here. All three legs verified locally (test suite, no-default-features tests, wasi build all green) before encoding. Co-Authored-By: Claude Fable 5 <[email protected]>
First live run: two failures, both realThe first CI run on this PR failed two of three jobs — and both failures were
|
The first run of the new workflow failed two of three jobs; both were pre-existing repo bugs, not workflow bugs. The stdin test's comment said "/bin/readlink to bypass the builtin" but the code ran bare `readlink` — the kaish builtin, which resolves the *test process's* fd/0, not a spawned child's. It only passed where the test runner itself had stdin=/dev/null; GitHub's runner hands the step a pipe. Reproduced red locally with piped stdin, fixed by making the code match the comment, verified green the same way. The ignored interactive twin got the same treatment. The no-default-features leg was written as `cargo test`, which has never compiled: KernelConfig::repl() is #[cfg(feature = "localfs")] and ~25 integration-test files call it. The earlier local "verification" piped cargo through tail and read tail's exit code — properly rerun it exits 101. The documented invariant is "sandbox-mode compiles", so the leg is now `cargo check`; upgrading it to `cargo test` via feature-gated test crates is GH #170. Co-Authored-By: Claude Fable 5 <[email protected]>
Third CI finding on PR #169: clippy passes locally on stable 1.96 but the runner's stable is 1.97, whose question_mark lint now catches a strip_prefix match that an early-return `?` expresses directly. Toolchain drift between contributor machines and CI is precisely what the workflow is for — the fix is the suggested `?`, not a pinned toolchain. Co-Authored-By: Claude Fable 5 <[email protected]>
Follow-up to #169: the workflow exists; this wires it into the docs contributors actually read, and serves as the final validation pass — this PR itself exercises the `pull_request` trigger on a fresh branch, and its merge will light the README badge from the `push`-to-main leg. - **README**: ci badge + crates.io badge (kaish-kernel, the flagship embeddable crate). The `Status:` line stops hand-maintaining a version number — it had already drifted (said 0.11, we're on 0.12); the badge carries it now. "Building from Source" shows the clippy gate and points at `ci.yml`; "Contributing" notes CI must be green and warns that runners track current stable Rust, so CI clippy may know lints your local toolchain doesn't (this bit us live: 1.97's `question_mark` failed a run 1.96 passed). - **CLAUDE.md**: Build Commands documents what CI runs, the keep-`ci.yml`-in-sync rule, and the fix-the-code-don't-pin-the-toolchain stance; the pre-commit gate list notes CI now enforces it. - **docs/devlog.md**: the full arc of #169 — first run failed two of three jobs, every failure a pre-existing repo bug (builtin-shadowed `readlink` stdin test; a no-default-features test leg that never compiled → #170; toolchain-drift clippy). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Follow-up to tobert#169 (the CI workflow itself): wire the new CI into the docs that tell people how to work here. README gains the ci badge and a crates.io badge for kaish-kernel, and the "Status:" line stops hand-maintaining a version number that had already drifted (said 0.11, we're on 0.12) — the badge carries it now. Building from Source shows the clippy gate and names ci.yml as the enforcement point; Contributing notes CI must be green and that runners track current stable Rust, so CI clippy can know lints a local toolchain doesn't (discovered live when 1.97's question_mark lint failed a run that 1.96 passed). CLAUDE.md's Build Commands section now documents what CI runs and the keep-ci.yml-in-sync rule; the pre-commit gate list notes CI enforces it. Devlog entry covers the whole arc: first run failed two of three jobs, every failure a pre-existing repo bug (builtin-shadowed readlink stdin test, a no-default-features test leg that never compiled → GH tobert#170, toolchain-drift clippy). Co-Authored-By: Claude Fable 5 <[email protected]>
kaish has had no CI since the repo began — every gate in CLAUDE.md (tests, clippy,
the capability-feature sandbox build, the WASI target) ran only on contributor
machines. This PR adds the repo's first GitHub Actions workflow, modeled on
kaibo's
ci.ymlbut encoding kaish's own gates. kaish releases to crates.iorather than shipping binaries, and publishing stays deliberately manual (the
/releaserunbook), so there is no release workflow —ci.ymlis the whole story.Three parallel jobs, all with pinned-by-SHA actions (a tag can be moved, a digest
can't):
cargo test --all --locked, thencargo clippy --all --all-targets --locked -- -D warnings, then a tripwire thatfails if any insta
*.snap.newpending snapshot was committed (under CI instafails-without-writing, so any
.snap.newfound was committed from a local run).cargo test -p kaish-kernel --no-default-features --locked, the gate that catches OS-touching code landing outside its capabilityaxis.
cargo build -p kaish-wasi --target wasm32-wasip1 --locked, soembedders on that target don't discover breakage at release time.
Decisions along the way:
--lockedeverywhere:Cargo.lockis tracked, and CI drifting to newer depsthan contributors test against is a silent-fallback class of surprise.
cargo insta test --checkjob:cargo test --allalready fails on snapshotmismatch under CI, and the
.snap.newtripwire covers the committed-pendingcase — same coverage without installing cargo-insta or running the suite twice.
builds honest, and kaish has no TLS or binary-release surface.
.gitignorehad no*.snap.newentry, so the very mistake the CI tripwirecatches could be staged by
git add .locally. Belt added; CI stays suspenders.All three legs were verified locally before encoding. This PR's own checks are the
live validation that the workflow runs on
pull_request; a follow-up doc-sweep PR(README badge + CI mentions in CLAUDE.md/README) validates the on-push-to-main leg
and the badge.
🤖 Generated with Claude Code