cyberine
UsageRun recipes

Security-gate drift check — npm run selftest:gate-list-drift (T707, retargeted 2026-09-07)

The security/data-integrity smoke list froze on 2026-08-22 in two hand-maintained copies (the CI gate loop and the git-workflow rule's precondition 3), and ten security smokes shipped afterwards without entering either — including selftest:daily-cap-reservation, which guards the fix for a real $12.18-of-a-$10-cap overshoot.

npm run selftest:gate-list-drift

scripts/selftest-manifest.json is the single declaration. Since the 2026-09-07 code-only CI cut the smoke asserts:

  1. every declared name is a registered selftest:* script;
  2. precondition 3 in .claude/rules/push-gates.md (tracked) equals the declared set;
  3. .github/workflows/ci.yml runs NO runtime smoke — no for t in loop, no npm run sweep, and every selftest:<name> it invokes is one of the static drift checks (gate-list-drift, claude-md-drift, dep-audit);
  4. every expectedSkips entry names a real script;
  5. the local wiring exists: scripts/selftest-sweep.mjs writes the per-SHA stamp, .githooks/pre-push is executable, guards refs/heads/main against a skip-ci token and no longer reads the stamp, and package.json's prepare script points core.hooksPath at .githooks.

CI runs this smoke in its gate job, so a runtime smoke added back to the workflow, or a security smoke added to the manifest but not to the rule, goes red on the next push.

Extend recipe — adding a security smoke: add its bare name to security in scripts/selftest-manifest.json (keep the array sorted) and to precondition 3 in .claude/rules/push-gates.md — both in one commit. npm run sweep -- --security runs exactly that class locally.

Known limitations and deferred work: the check compares NAMES, not behaviour — it cannot tell you that a smoke in the list has stopped guarding anything. It also only inspects run: lines of ci.yml with comments stripped, so a runtime smoke hidden behind an indirection (a script CI calls that itself runs npm run selftest:...) would not be caught; keep CI steps as direct npm run invocations.