ci-requirements.md is the document that drives promoting a per-job dep into
the ci-python image, and it still recorded jq as installed in the plugin job
and "load-bearing for the smoke test specifically", on the grounds that every
hook opened `command -v jq || exit 0` and the test would otherwise pass while
exercising nothing.
That was the tail wagging the dog. The hooks ship to users; jq is absent by
default on macOS, the Debian/Ubuntu slim images, Alpine and most CI
containers, and a machine without it got no context, no rules, no prior art
and no process sync in silence. The answer was to remove the dependency, not
to install it harder.
Recorded as a promotion the entry now argues AGAINST rather than deleted: the
next person to read this file should find out why jq is absent, not merely
that it is.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
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
Match CI + runtime target exactly — both run Python 3.14, so the
package metadata signals consumers that we don't test against 3.12/3.13.
uv.lock is tracked so the test job's `uv venv` resolution is
reproducible (currently the test job installs the editable package
without consulting the lockfile; future work could wire `uv sync` in).
Lockfile resolves 179 packages against Python 3.14.4.
ci-requirements.md updated to drop the prior "permissive lower bound"
caveat.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Migrate to the FabledRulebook CI-Runner contract:
- .forgejo/workflows/ci.yml: all four jobs (typecheck/lint/test/build)
now schedule on the `python-ci` runner label and run inside
container.image: git.fabledsword.com/bvandeusen/ci-python:3.14
(Python 3.14 + Node 24 + ruff + uv + Docker CLI). Dropped the inline
uv install in the test job — uv is now baked into the image.
- Dockerfile: production runtime bumped to python:3.14-slim so test
results stay representative against what we ship.
- ci-requirements.md: new file at repo root declaring image deps and
per-job installs (per FabledRulebook ci-runners.md).
- infra/Dockerfile.runner-base: deleted. The in-repo runner base
(Ubuntu 24.04 + Python 3.12 + Node 22) is superseded by the shared
ci-python image. The runner-host deployment files
(runner-compose.yml + act-runner-config.yml) stay as deployment-shape
documentation; source of truth is the deployed config.
- docs/development.md: CI/CD + Runner sections refreshed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>