ci(plugin): add shellcheck + jq, and the fail-open smoke test they unlock
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Failing after 7s
CI & Build / TypeScript typecheck (push) Successful in 35s
CI & Build / integration (push) Successful in 39s
CI & Build / Python tests (push) Successful in 47s
CI & Build / Build & push image (push) Successful in 21s

Per-job installs, not an image change. CI-runner's docs/process.md decision
checkpoint is explicit: "If only one project needs the dep, prefer that
project installing it per-job in their workflow — at least until a second
consumer arrives." Scribe is the only consumer, and shellcheck is not a
natural extension of a Python image's purpose. Promotion into ci-python is
filed as an issue on CI-runner rather than assumed here — same doc, step 1:
the maintainer's call goes in the issue, then the PR.

This also corrects something I got wrong earlier in this work: I cited rule
#5 as blocking a per-job install. Rule #5 is about language TOOLCHAINS via
setup-* actions, not small lint utilities, and CI-runner's own process doc
positively recommends per-job installs in exactly this case.

jq is load-bearing rather than convenient. Every hook opens with
`command -v jq || exit 0`, so without it a "runs and stays silent" smoke
test passes while exercising nothing — a green tick proving less than no
test at all. That is why the smoke test didn't ship with the first cut.

The smoke test pins the fail-open contract: each hook, with no credentials
and then against a refused connection, must exit 0. Three must also stay
silent; scribe_session_context.sh must NOT, because its static behavioural
floor is meant to survive having no credentials and no network — asserting
silence there would encode the opposite of the design.

Verified it can actually fail, rather than assuming: injected a non-zero
exit and separately a stray stdout write, and confirmed each is caught.

shellcheck and jq are both optional at runtime — missing either SKIPs its
check loudly rather than passing. A check that quietly no-ops is the exact
failure mode this file exists to prevent.

ci-requirements.md updated: jq + shellcheck recorded under per-job installs
(the input CI-runner's maintainer uses for the next promotion decision),
plus two stale entries corrected — the sheet claimed four jobs when there
are six, and listed `uv` as a per-job install when it has been in the image
since the ci-python Dockerfile started pip-installing it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs
This commit is contained in:
2026-07-28 22:39:55 -04:00
parent 1feef179d2
commit dd878bc498
3 changed files with 157 additions and 17 deletions
+13
View File
@@ -129,6 +129,19 @@ jobs:
# of main's tip is enough, and cheaper.
- uses: actions/checkout@v6
# Per-job, not in the image, per CI-runner's docs/process.md: "If only one
# project needs the dep, prefer that project installing it per-job in
# their workflow — at least until a second consumer arrives." Scribe is
# the only consumer today. Promotion into ci-python is filed as an issue
# on CI-runner rather than assumed here.
#
# jq is not optional for the smoke test: every hook exits at line 1
# without it, so the check would pass while exercising nothing.
- name: Install shell tooling
run: |
apt-get update -qq
apt-get install -y -qq --no-install-recommends jq shellcheck
# On main the comparison would be against itself, so only the syntax and
# pattern checks mean anything there.
- name: Check plugin hooks and manifest