ci: fail when a test or crate is reached by no runner (#194) - #195
Conversation
npm test is `npm run -ws --if-present test` and cargo test is `--workspace`, so two things are currently indistinguishable from a pass: a workspace with no test script, and a crate outside the members list. Both runners then report green and complete. draco.yml already carries a note from the last time this bit -- "An explicit list silently drifts: arm tests existed but CI never ran them" -- and this generalises that fix instead of repeating it per-workflow. scripts/check-runner-coverage.mjs walks every test file, package.json and Cargo.toml and asserts each is either reached by a runner or recorded as a deliberate exclusion with a reason. Zero dependencies, static, <1s, no network. It counts three invocation forms as "reached", all of which are in use here: - a workspace matched by the root `workspaces` globs that has a test script - a file named explicitly in a workflow, as `vitest run <path>` OR `node <path>` - a workflow job with `working-directory: <dir>` plus its own `npm test` The second and third were added after the first draft produced FALSE POSITIVES on this repo: ci.yml's "SOTA integrity-attestation gate" runs its suites under plain `node`, and pages.yml tests apps/web-ui via working-directory. Those 11 findings were wrong, and a checker that cries wolf gets deleted. The working-directory match is deliberately coarse (workflow-level, not job-level): a missed gap costs less than a false alarm that blocks CI on correct code. The allowlist is a RATCHET, not a suppression list -- seeded with what is already unreached today so this passes on main unchanged, and anything NEW fails. Entries require a non-empty reason; an allowlist without reasons is a mute button and the next reader cannot tell a decision from an oversight. A stale entry also fails, so it cannot rot into blanket permission. Deleting entries is the point. Seeded state (measured on 1f3b870, all cross-checked against the workflows): 56 test files -- 45 root __tests__/ (npm run -ws excludes the root package), 9 services/apicompletions (outside packages/*), 2 kimi-k3-harness (outside packages/*, has its own "test": "vitest run" that nothing invokes) 4 crates -- oo-agents 20, horizon 14, k3rs 9, k3-kernel-bench 1 (#[test] fns; each declares its own [workspace], so cargo is behaving correctly -- nothing else runs them either) I am not asserting these should run: kimi-k3-harness reads like a self-contained experiment and nested [workspace] crates are a normal way to isolate a dependency graph. The claim is only that nothing currently distinguishes "deliberately not run" from "accidentally not run". Proved able to fail, four ways, each verified to die for the right reason: new unreached test file -> red, names the file and why allowlist entry with no reason -> red stale allowlist entry -> red drop the `node <path>` form -> the 2 SOTA suites reappear as findings (no syntax error; the fix is load-bearing) actionlint clean. The added CI step uses no ${{ }} expressions.
|
@ruvnet — this one needs an Approve and run workflows click when you get a chance; all three runs (CI / Security / Real Tools) are sitting at Low-risk to approve, for what it's worth: the check is a static, zero-dependency Node script — no network, no install, <1s — and the allowlist is seeded with the current state so it passes on If you'd rather sanity-check it locally before spending CI minutes: git fetch origin pull/195/head:pr-195 && git checkout pr-195
node scripts/check-runner-coverage.mjs # exits 0 todayAnd to see it bite, add any test file nothing runs — e.g. No rush, and happy to adjust the approach if you'd prefer this as a warning rather than a gate, or scoped to fewer directories to start. |
|
@ruvnet — thanks for #193, that closes the scope gap cleanly. I checked it against the original defect rather than assuming: the new pattern matches the real pre-fix root Nudging #195 one last time: it's still sitting at Genuinely fine to close it if it's not wanted — I'd rather that than have it linger, and I won't keep bumping either way. |
…on fix (#212) Ships #212's shellDq()/yamlCommentSafe() + the move from a plain `run: echo` scalar to a `|` block literal, across both published copies. metaharness needs its own bump because 0.4.9 was published BEFORE #212 merged: its dist/host-config.js has no block literal, so the fix was absent from the shipped CLI. Verified by unpacking the published 0.4.9 tarball. Note: the third byte-parity copy, apps/web-ui/src/generator/scaffold.ts, is private:true and unpublished — but it is also outside CI (root workspaces is ["packages/*"] and ci.yml never references web-ui), so its 71 tests guarding this fix run on no runner. See #195/#194. Lockfile resynced in the same commit. Co-Authored-By: claude-flow <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01G2iAKhp4FGq2Ty5CFnj8mT
|
Reviewed and independently verified. This is a well-built guard and the PR description is unusually honest about its own limits — the false-positive story ( Verified all four failure modes myself, checking real exit codes (a pipe to
It also still passes against It corrected me. While reviewing #212 I claimed The residual gap there is narrower than I first said and this PR doesn't claim to cover it:
|
Two defects, both caught by __tests__/adr-index.test.ts -- which never runs in CI (root suite, allowlisted as unreached by #195/#194), so neither would have surfaced on merge. 1. ADR NUMBER COLLISION. ADR-251 is already taken on main by ADR-251-governed-autonomous-variation-runtime.md (@metaharness/avo, Accepted and shipped). This PR added a SECOND ADR-251, and INDEX.md carried two rows with the same number. ADR-252 (LatentMesh) already cites "ADR-251 capability policy", meaning the AVO one -- so the collision made an existing cross-reference ambiguous. Renumbered to 273 (272 is the current high-water mark); the AVO reference is left pointing where it always did. 2. NON-CONFORMING HEADINGS. The ADR used numbered sections ("## 1. Context"). 235 of the repo's 236 ADRs use the unnumbered form and the test asserts it; this file was the sole exception. Stripped the numeric prefixes. Also corrected the INDEX status, which claimed "routine created; first live run pending" while LEDGER.md records 10 completed runs through 2026-08-29. Not fixed here (pre-existing on main, unrelated to this PR): ADR-253 has no Consequences section and fails the same test. It has no section that could be renamed into one, so writing it is the author's call, not a mechanical fix. Co-Authored-By: claude-flow <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01G2iAKhp4FGq2Ty5CFnj8mT
…d evolution routine) (#179) * docs: add ADR-251 nightly dream machine (cloud-scheduled dream cycle) Co-Authored-By: claude-flow <ruv@ruv.net> * docs: ADR-251 — record created routine id trig_01T9FVWfahfGrkK9E9eCsvyw Co-Authored-By: claude-flow <ruv@ruv.net> * docs(dream-cycle): prompt v2 — port Ruflo Dream Cycle v3.1 research-depth protections Operator feedback on the first Ruflo v3 nights: research had visibly thinned (all-None competitor rows, thin justification) because the budget language treated research as the compressible phase. v2 flips that: - STEP 0.6: research phase is protected; budget pressure cuts from the END of the pipeline (Darwin generations, corpus size, adversarial depth), and a shortened research pass must be disclosed, never silent - STEP 1: sparse-ledger caution — verify dream/* branches + PRs before reporting a prior night as having skipped a step - STEP 3: minimum research depth — all-None rows need a WHY, fixed lists are a floor not a ceiling, prefer >=1 source <12 months old, no same-shaped tables night after night - STEP 7: note that a fresh corpus is graded by its own author-session, pointing at STEP 10's fairness check - GLOBAL INVARIANTS: leads with trustworthy-improvement-over-activity - ADR-251: Updated stamp + status reflects two completed runs (#181, #188) Scheduler copy updated in the same piece of work per the mirror rule. Co-Authored-By: claude-flow <ruv@ruv.net> * docs: renumber the Dream Machine ADR 251 -> 273 and conform its headings Two defects, both caught by __tests__/adr-index.test.ts -- which never runs in CI (root suite, allowlisted as unreached by #195/#194), so neither would have surfaced on merge. 1. ADR NUMBER COLLISION. ADR-251 is already taken on main by ADR-251-governed-autonomous-variation-runtime.md (@metaharness/avo, Accepted and shipped). This PR added a SECOND ADR-251, and INDEX.md carried two rows with the same number. ADR-252 (LatentMesh) already cites "ADR-251 capability policy", meaning the AVO one -- so the collision made an existing cross-reference ambiguous. Renumbered to 273 (272 is the current high-water mark); the AVO reference is left pointing where it always did. 2. NON-CONFORMING HEADINGS. The ADR used numbered sections ("## 1. Context"). 235 of the repo's 236 ADRs use the unnumbered form and the test asserts it; this file was the sole exception. Stripped the numeric prefixes. Also corrected the INDEX status, which claimed "routine created; first live run pending" while LEDGER.md records 10 completed runs through 2026-08-29. Not fixed here (pre-existing on main, unrelated to this PR): ADR-253 has no Consequences section and fails the same test. It has no section that could be renamed into one, so writing it is the author's call, not a mechanical fix. Co-Authored-By: claude-flow <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01G2iAKhp4FGq2Ty5CFnj8mT --------- Co-authored-by: ruv <ruvnet@gmail.com>
…red on Windows main has been failing on Node 20/windows and Node 22/windows since #178 merged: FAIL reads the pinned Ed25519 key out of the real meta-proxy.ts: The input did not match the regular expression /^-----BEGIN PUBLIC KEY-----\n/ meta-proxy.ts is not pinned to `eol=lf` in .gitattributes (which covers only the draco corpus and a few golden fixtures), so a Windows checkout produces a CRLF PEM and the LF-anchored assertion fails. Production is NOT affected, verified rather than assumed: the PEM only ever reaches crypto.createPublicKey(), is never string-compared or hashed, and createPublicKey() accepts CRLF and LF alike (checked both). So the fix belongs in the test's platform assumption, not in readPinnedPublicKey(). simulated CRLF PEM: OLD -> false, NEW -> true LF PEM: NEW -> true (unchanged) Root cause of it reaching main: #178 came from a fork, and fork PRs report no checks on this repo, so its CI never ran. I merged it on local (Linux) verification alone. The same blind spot applies to #195. Worth a follow-up on fork-PR CI. Co-Authored-By: claude-flow <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01G2iAKhp4FGq2Ty5CFnj8mT
ci: fail when a test or crate is reached by no runner (#194)
npm test is
npm run -ws --if-present testand cargo test is--workspace, sotwo things are currently indistinguishable from a pass: a workspace with no test
script, and a crate outside the members list. Both runners then report green and
complete. draco.yml already carries a note from the last time this bit --
"An explicit list silently drifts: arm tests existed but CI never ran them" --
and this generalises that fix instead of repeating it per-workflow.
scripts/check-runner-coverage.mjs walks every test file, package.json and
Cargo.toml and asserts each is either reached by a runner or recorded as a
deliberate exclusion with a reason. Zero dependencies, static, <1s, no network.
It counts three invocation forms as "reached", all of which are in use here:
workspacesglobs that has a test scriptvitest run <path>ORnode <path>working-directory: <dir>plus its ownnpm testThe second and third were added after the first draft produced FALSE POSITIVES
on this repo: ci.yml's "SOTA integrity-attestation gate" runs its suites under
plain
node, and pages.yml tests apps/web-ui via working-directory. Those 11findings were wrong, and a checker that cries wolf gets deleted. The
working-directory match is deliberately coarse (workflow-level, not job-level):
a missed gap costs less than a false alarm that blocks CI on correct code.
The allowlist is a RATCHET, not a suppression list -- seeded with what is
already unreached today so this passes on main unchanged, and anything NEW
fails. Entries require a non-empty reason; an allowlist without reasons is a
mute button and the next reader cannot tell a decision from an oversight. A
stale entry also fails, so it cannot rot into blanket permission. Deleting
entries is the point.
Seeded state (measured on 1f3b870, all cross-checked against the workflows):
56 test files -- 45 root tests/ (npm run -ws excludes the root package),
9 services/apicompletions (outside packages/),
2 kimi-k3-harness (outside packages/, has its own
"test": "vitest run" that nothing invokes)
4 crates -- oo-agents 20, horizon 14, k3rs 9, k3-kernel-bench 1
(#[test] fns; each declares its own [workspace], so cargo is
behaving correctly -- nothing else runs them either)
I am not asserting these should run: kimi-k3-harness reads like a self-contained
experiment and nested [workspace] crates are a normal way to isolate a
dependency graph. The claim is only that nothing currently distinguishes
"deliberately not run" from "accidentally not run".
Proved able to fail, four ways, each verified to die for the right reason:
new unreached test file -> red, names the file and why
allowlist entry with no reason -> red
stale allowlist entry -> red
drop the
node <path>form -> the 2 SOTA suites reappear as findings(no syntax error; the fix is load-bearing)
actionlint clean. The added CI step uses no ${{ }} expressions.