docs: propose ADR 0047, a published harness every module runs in CI #59

Merged
Zayin_Admin merged 2 commits from docs/adr-0047-module-harness into main 2026-10-06 02:00:39 +00:00
Showing only changes of commit 3a2f5d68f6 - Show all commits
@@ -154,8 +154,9 @@ it.
- checks out the module with `persist-credentials: false` and pinned actions; - checks out the module with `persist-credentials: false` and pinned actions;
- runs the module's own unit tests, typecheck, and lint; - runs the module's own unit tests, typecheck, and lint;
- runs the harness, once per pinned platform version, with one `docker run` of the harness image - sweeps stale CI stacks from the runner, then runs the harness, once per pinned platform version,
**by digest**. The module directory is mounted read-only, and the runner's Docker is used exactly with one `docker run` of the harness image **by digest**, and finally, always, removes this job's
own stacks. The module directory is mounted read-only, and the runner's Docker is used exactly
as the platform's smoke uses it. as the platform's smoke uses it.
It references no secret, needs no registry credential (the harness image is public within the It references no secret, needs no registry credential (the harness image is public within the
@@ -176,6 +177,22 @@ An image digest is the one reference that is equally immutable, equally reviewab
unchanged on Gitea 1.24, Gitea 1.26, and GitHub Actions. A composite action can be added later as a unchanged on Gitea 1.24, Gitea 1.26, and GitHub Actions. A composite action can be added later as a
convenience over the same image, without changing what a module pins. convenience over the same image, without changing what a module pins.
### Nothing the harness starts outlives the job that started it
The harness starts Docker stacks on a shared runner, and a runner can kill a job outright: a
cancelled job, or the runner's own time limit, since Gitea 1.24 ignores `timeout-minutes`. A script's
own cleanup trap does not run then. The platform's CI leaked a whole migration stack that way, and it
ran until someone removed it by hand. So the harness follows the platform's rule
(`scripts/ci-sweep.sh`, `docs/development/security.md`):
- everything it starts is named and labeled as a CI stack of its run, attempt, and job key;
- it removes its own stacks in a trap;
- the template workflow runs the harness's **stale sweep** before the harness starts, and its
**job sweep** in a final `if: always()` step, so a killed run's stacks are removed by that run's
last step, or failing that by the next job on the runner.
The sweep ships inside the harness image, so a module never copies it.
### Publishing follows the instance's release pattern ### Publishing follows the instance's release pattern
The harness image is pushed only by a `publish` job that runs on `v*` tags, after the platform's CI The harness image is pushed only by a `publish` job that runs on `v*` tags, after the platform's CI