fix(governance): package policy — cite the canonical ruling, detect sealed containers - #534
Merged
Merged
Conversation
…ontainers
Three defects in check-package-policy.sh, all stemming from it encoding a
superseded rule.
1. Cited the WRONG SOURCE. It read "POLICY (CLAUDE.md, 'Package Management')".
CLAUDE.md's packaging section is stale — it still calls Nix a fallback, in
472 copies estate-wide — and CLAUDE.md itself defers to
spec/LANGUAGE-POLICY.adoc as canonical. Now quotes the actual ruling:
RULED 2026-05-18 (estate-wide): Guix primary + sealed-container escape;
NO Nix mirror. ... Supersedes the prior "Nix fallback everywhere" rule.
2. NO sealed-container detection. The policy names a sealed container
(Podman/Containerfile, Svalinn-sealed) as the escape hatch for the
not-in-Guix / non-free tail, but the script only looked for Guix and Nix.
A repo doing exactly the right thing was reported as having no packaging.
Containerfile* and Dockerfile* now satisfy the policy.
3. Printed `✅ Nix package management detected (fallback)`. Nix is not a tier.
Blessing a flake as compliant is what let the 2026-07-21 sweep ship
flake.nix to 59 repos that should have received Guix or a container.
Nix-only now warns, then fails on ENFORCE_NIX_RETIREMENT_FROM. It never
prints a pass.
Retirement date set to 2026-06-01 per owner decision.
⚠ That date is in the PAST (today is 2026-07-27), so Nix-only fails
IMMEDIATELY with no grace window — unlike every other gate in this suite.
Measured over the local estate checkout, 22 repos are Nix-only and go red on
merge. Raised as a draft for confirmation in case 2027-06-01 was intended;
it is one constant to change either way.
The failure message explicitly warns against simply deleting the flake:
campaign #102 closed COMPLETED having hand-diffed 277 candidates and removed
exactly ONE, and ~270 repos carry a guix.scm that is a non-functional scaffold
stub — for them, deleting the flake means having no working packaging. Make
the Guix side real first (spec/scaffold-stub-debt.adoc, step 3).
Red-teamed — all nine branches measured, not asserted:
guix.scm -> 0 ✅ primary
Containerfile -> 0 ✅ escape hatch
Dockerfile -> 0 ✅ escape hatch
nix-only, pre-retirement -> 0 warning + NOT YET ENFORCED
nix-only, post-retirement -> 1 ::error::
no packaging, pre-cutoff -> 0 warning + NOT YET ENFORCED
no packaging, post-cutoff -> 1 ::error::
guix + nix both present -> 0 guix wins, no nix warning
malformed date -> 1 refuses to run
`bash -n` clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
hyperpolymath
marked this pull request as ready for review
July 27, 2026 13:34
hyperpolymath
added a commit
that referenced
this pull request
Jul 27, 2026
Follow-up to #534, which added sealed-container detection but accepted it on **presence alone**. That reproduces the very fault this script exists to remove: #505 s predecessor accepted *any* `*.scm` as "Guix detected", and the Guix predicate was tightened for exactly that reason. The container branch was left untightened. ## The problem The estate scaffold ships a Containerfile template whose every install and build line is a **commented `# TODO:` example** — no active `RUN`, no `ENTRYPOINT`. It provides no environment at all, yet satisfied the gate. Measured across `hyper-repos` + `meta-repos`: | | count | |---|---| | **stub** Containerfiles (no active RUN/ENTRYPOINT/CMD) | **17** | | real Containerfiles | 43 | So 17 repos were being reported compliant on the strength of an untouched template. **How it was found:** while preparing to add Containerfiles to the 29 Nix-only repos that the 2026-06-01 retirement made non-compliant — i.e. that sweep was about to mass-produce artefacts satisfying a gate I already knew was weak. Better to tighten first. ## The predicate Deliberately cheap and syntactic: at least one **active** `RUN` / `ENTRYPOINT` / `CMD`. It cannot prove an image is *useful*, but it separates "someone filled this in" from "this is the untouched template" — the distinction actually available at gate time. A stub now warns and is treated as no packaging. ## Red-teamed — six cases | case | exit | | |---|---|---| | stub Containerfile, post-cutoff | **1** | warns, does not pass | | real Containerfile (`RUN`) | 0 | ✅ | | real Containerfile (`ENTRYPOINT` only) | 0 | ✅ | | stub + `guix.scm` | 0 | guix wins, no container warning | | stub + `flake.nix`, nix retired | **1** | correctly non-compliant | | only a **commented-out** `RUN` | **1** | not fooled by `# RUN ...` | `bash -n` clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
hyperpolymath
added a commit
that referenced
this pull request
Jul 29, 2026
…te (#563) `scripts/check-package-policy.sh` names `LANGUAGE-POLICY.adoc` as its authority in **three places**, including the runtime error message it prints on failure — and **the file did not exist**. That dangling citation came in with #534; this corrects it. The contents are the rules already in force, written down for the first time, plus one genuinely new section: **§1 — JS/TS runtime ordering: Bun > Deno > pnpm > npm.** This *inverts* what the estate currently enforces. `npm-bun-blocker.yml`, in **55 repositories**, fails any build carrying `bun.lockb` with "Use Deno instead" — blocking the new first choice and mandating the second. That gate must change next. **§1.2** — TypeScript permitted under Bun; the "use ReScript instead" rule retired. Records that `ts-blocker.yml` (also 55 repos) **cannot fail**: it diffs `HEAD~1` on a depth-1 checkout and swallows the error. **20/20 runs green, never once fired.** **§2** — packaging (Guix primary, sealed-container escape, Nix retired 2026-06-01), including the standing *never mass-delete `flake.nix`* constraint and the measured `guix.scm` identity/licence clobber. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.



Fixes
check-package-policy.sh, which encoded the superseded packaging rule.Three defects
Cited the wrong source. It read "POLICY (CLAUDE.md, Package Management)". That section of CLAUDE.md is stale — it still calls Nix a fallback, in 472 copies estate-wide — and CLAUDE.md itself defers to
spec/LANGUAGE-POLICY.adocas canonical. Now quotes the actual ruling:No sealed-container detection at all. The policy names a sealed container (Podman/Containerfile, Svalinn-sealed) as the escape hatch for the not-in-Guix / non-free tail — but the script only looked for Guix and Nix, so a repo doing exactly the right thing was told it had no packaging.
Containerfile*andDockerfile*now satisfy it.Printed
✅ Nix package management detected (fallback). Nix is not a tier. Blessing a flake as compliant is precisely what let the 2026-07-21 sweep shipflake.nixto 59 repos that should have received Guix or a container. Nix-only now warns, then fails — it never prints a pass.⚠ Draft — one thing to confirm: the retirement date
Set to 2026-06-01 per your decision. That date is in the past (today is 2026-07-27), so Nix-only fails immediately, with no grace window — unlike every other gate in this suite.
Measured over the local estate checkout:
If
2027-06-01was intended, it is one constant to change. Left as a draft rather than guessing.(This also corrects my own earlier estimate — I had warned this would "red hundreds of repos". Measured, it is 22.)
Sequencing warning is built into the failure message
Campaign #102 closed COMPLETED having hand-diffed 277 candidates and removed exactly one flake; ~270 repos carry a
guix.scmthat is a non-functional scaffold stub. For those, deleting the flake means having no working packaging. The error output says so explicitly and points atspec/scaffold-stub-debt.adocstep 3: make the Guix side real first, then retire the mirror.Red-teamed — all nine branches measured
guix.scmContainerfileDockerfileNOT YET ENFORCED::error::NOT YET ENFORCED::error::bash -nclean.🤖 Generated with Claude Code