Skip to content

ci(governance): ADR swap gate — Firewall #1 blocks new Nix under the active Guix migration - #325

Merged
mdheller merged 2 commits into
mainfrom
feat/adr-swap-gate
Aug 4, 2026
Merged

ci(governance): ADR swap gate — Firewall #1 blocks new Nix under the active Guix migration#325
mdheller merged 2 commits into
mainfrom
feat/adr-swap-gate

Conversation

@mdheller

@mdheller mdheller commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

What this closes

The percolation failure (retro 2026-08-04): ADR-0001 (Nix→Guix) was recorded but nothing stopped new .nix being authored under the active swap. An agent added one and nothing objected. This wires Firewall #1 into source-os CI as a fail-closed, required gate — the enforcement that was missing.

How it works

  • scripts/adr_swap_gate.py (stdlib, self-contained): on a PR it flags any newly-ADDED file (git diff --diff-filter=A) that uses the FROM toolchain of an active swap ADR, unwaived → exit 1 with a GitHub ::error:: annotation.
    • Blocks only added files — ongoing maintenance of existing .nix during the parity phase is untouched. Only new Nix surface is refused.
    • --selftest passes; verified: it blocks packages/sourceos-shell/default.nix (the exact file that started this), and clears .scm and docs.
  • governance/adr/ADR-0001-nix-to-guix.json: the swap decision as in-repo canon the gate reads (status: parity). Records the honest state — 63 .nix vs 3 .scm on main. Flip statuscutoverdone to relax the gate as the migration completes.
  • .github/workflows/adr-swap-gate.yml: runs on every PR.

Governed escape hatch: need a legitimate new .nix? Add a waivers entry to the ADR — reviewed like any change, not a silent bypass.

Action for a maintainer

Make adr-swap-gate a required status check on main so it actually gates merges. (It's a workflow addition, so this PR is for human review — I'm not auto-merging.)

The generalization

This is instance one of a reusable pattern: any swap/deprecation ADR (library A→B, tool A→B) gets the same gate. The reasoning/graph/two-firewall model lives in prophet-platform#1392 (the ADR dependency graph, Firewall #2 control-of-controls, and the RCA→HellGraph + Resolution-asset pipeline).

…active Guix migration

Closes the percolation failure at the merge boundary (retro 2026-08-04): the Nix→Guix ADR was recorded
but nothing stopped new .nix being authored under the swap. Now a required, fail-closed CI check does.

- scripts/adr_swap_gate.py — stdlib, self-contained. Flags any newly-ADDED file (git --diff-filter=A)
  that uses the FROM toolchain of an active swap ADR, unwaived → exit 1 with a GH ::error:: annotation.
  Blocks only ADDED files, so maintenance of existing .nix during the parity phase is untouched.
  `--selftest` passes; blocks a new packages/*.nix, clears .scm and docs.
- governance/adr/ADR-0001-nix-to-guix.json — the swap decision as in-repo canon the gate reads
  (status=parity; flip to cutover/done to relax). Records the real state: 63 .nix vs 3 .scm on main.
- .github/workflows/adr-swap-gate.yml — runs on every PR.

Governed escape hatch: add a `waivers` entry to the ADR (reviewed like any change). ACTION FOR ADMIN:
make `adr-swap-gate` a REQUIRED status check so it actually gates merges. Workflow change → human review.
…#1 + sealed-receipt stream

Two-layer trigger: the CI gate (adr_swap_gate) is authoritative and blocks merges; this is its
inner-loop companion. scripts/adr_watch.py watches the working tree and, the moment a new FROM-toolchain
file appears (a .nix under the active Guix swap), warns at author-time — advisory only (exit 0; a local
watcher is racy and must never gate). Its durable output is a SEALED receipt per event
(sourceos.adr_watch.advisory.v1) — the stream a later pump feeds into the always-on HellGraph service
for graph-native RCA. `--watch` polls (stdlib, no watchdog); `--once` checks the PR's added files.
+4 tests; verified it warns on packages/sourceos-shell/default.nix and stays exit 0.
@mdheller
mdheller merged commit d3bf207 into main Aug 4, 2026
2 checks passed
@mdheller
mdheller deleted the feat/adr-swap-gate branch August 4, 2026 10:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant