ci(governance): ADR swap gate — Firewall #1 blocks new Nix under the active Guix migration - #325
Merged
Conversation
…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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this closes
The percolation failure (retro 2026-08-04): ADR-0001 (Nix→Guix) was recorded but nothing stopped new
.nixbeing authored under the active swap. An agent added one and nothing objected. This wires Firewall #1 intosource-osCI 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..nixduring the parity phase is untouched. Only new Nix surface is refused.--selftestpasses; verified: it blockspackages/sourceos-shell/default.nix(the exact file that started this), and clears.scmand 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.nixvs 3.scmon main. Flipstatus→cutover→doneto 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 awaiversentry to the ADR — reviewed like any change, not a silent bypass.Action for a maintainer
Make
adr-swap-gatea required status check onmainso 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).