# CI Requirements — FabledScribe

> Spec lives in [`docs/process.md`](https://git.fabledsword.com/bvandeusen/CI-runner/src/branch/main/docs/process.md)
> in the CI-Runner repo.

## Runtime image

```
git.fabledsword.com/bvandeusen/ci-python:3.14
```

Used by all six jobs in `.forgejo/workflows/ci.yml`: typecheck (Vue/TS),
plugin (hook checks), lint (ruff), test (pytest), integration (pytest +
real Postgres), build (docker buildx).

## Image deps used

- python 3.14
- node 24 (used for `npm ci` + `vue-tsc` in the typecheck job, and as the
  frontend builder stage inside the production `Dockerfile`)
- ruff (lint job runs `ruff check src/` with zero install overhead)
- uv (test + integration jobs run `uv sync --locked`; installed in the
  image since the ci-python Dockerfile started pip-installing it)
- docker CLI + buildx (build job pushes the production image to the
  Fabled-Git registry)

## Per-job tool installs

Anything CI installs at job time that isn't in the image. Promotion
candidates if more than one project needs them.

- `jq` + `shellcheck` — apt-installed in the **plugin** job, which lints
  the four Claude Code hook scripts and runs their fail-open smoke test.
  Per `docs/process.md`'s decision checkpoint, single-consumer deps stay
  per-job until a second consumer wants them; Scribe is the only one so
  far. Both are small (jq ~1 MB, shellcheck ~20 MB) and would be
  promotion candidates the moment another project lints shell.
  **jq is load-bearing for the smoke test specifically**: every hook
  starts with `command -v jq || exit 0`, so without it the test passes
  while exercising nothing.

## Notes

- Production runtime image (`Dockerfile`) also tracks Python 3.14 — the
  CI image and runtime image stay aligned by design so test results are
  representative.
- Build wall time: dominated by `pytest` (full async test suite). Cold
  ci-python pulls add ~30s; not a blocker.
- Registry-backed BuildKit layer cache (`type=registry,ref=…:cache,mode=max`)
  gives ~80% speedup on warm builds — see the build job comment.
- `pyproject.toml` pins `requires-python = ">=3.14"` to match the CI +
  runtime target; lockfile (`uv.lock`) is committed and resolves against
  Python 3.14.
