status: accepted; supersedes 0028
Run each CI pipeline in its own jail, with act and its log inside
ADR-0028 decided two separable things: an *authority boundary* for CI, and an *executor* for it. This ADR supersedes it to change the executor only. The authority boundary is restated here unchanged, because it is correct and nothing below weakens it.
Retained from ADR-0028, verbatim in force. CI runs secretless checks. The only mutation CI can request is a single, pre-promoted Level 2 deployment profile, authorized by an opaque capability bound to owner, repository, ref, exact commit, required checks, destination, deployment profile, and TTL — never a raw command, inventory path, Ansible flag, or Slurm flag, and never Level 1 under any circumstance. Events deduplicate on (repository, ref, commit digest, workflow/profile) and survive a controller outage rather than being dropped or replayed twice.
Changed: the executor. ADR-0028 routed CI onto neotek-sandbox Slurm (ADR-0025). Each pipeline instead runs in its own FreeBSD jail with Nektos act inside it, and the run's act log is retained in that jail for inspection after the fact.
The argument for the change, stated rather than assumed. First, the debugging surface: what this repository actually runs are act workflows, and a Slurm executor turns each into a batch job whose output must be lifted off the cluster to be read, at exactly the moment something has gone wrong. A jail keeps the log where the run happened. Second, coupling: sandbox-Slurm CI makes CI availability a function of compute-cluster availability, so a drained, re-homed, or upgraded cluster takes CI with it — and this estate has spent I15–I19 doing precisely that kind of cluster surgery. Third, the primitive is already here: jails, their PF discipline, and their teardown path are the repository's existing isolation mechanism, exercised by vpn-gateway, nexus-gateway, agent-hub, and the I14 disposable Slurm candidate.
The cost is real and is accepted, not argued away. A FreeBSD jail on NeoTek is a weaker boundary than a separate Slurm trust domain on separate hardware: ADR-0025 split trusted from sandbox precisely so that a compromised CI workload sits on the far side of a machine boundary, and a jail does not give that. Three things carry the weight instead — secretless remains mandatory and is not relaxed by one word; the capability boundary above is unchanged, so a compromised pipeline still cannot express a Level 1 action; and the isolation fixture ADR-0028 required of I18 is re-pointed at the jail boundary rather than dropped, and must still prove that a compromised CI run cannot reach trusted controllers, workers, vaults, or management endpoints.
Nothing in ADR-0025 is affected. catalog_cli/slurm_adapter.cljc, the ci-sandbox Slurm profile in ops/catalog.edn, and its read-only rendering path are retained, not deleted: they remain the tested description of a secretless sandbox submission, and reverting to a Slurm executor stays available without rebuilding it.
Consequences
I22 provisions one jail per pipeline, keeps its act log in-jail under a declared retention bound, and gets its parallelism from concurrent jails rather than from Slurm job steps. I22 remains the only increment allowed to switch a repository's real CI executor and to enable bounded auto-deploy for an allowlisted ref, and only after the re-pointed I18 isolation fixture passes. The existing act-runner bhyve VM stays authoritative until that switch. Log retention inside a jail is new state on NeoTek and needs a bound and a place in the backup/criticality classification I36 introduces — an unbounded CI log directory is a disk-exhaustion path into a host that also serves Soft Serve.