Skip to content

fix(governance): package policy — cite the canonical ruling, detect sealed containers - #534

Merged
hyperpolymath merged 1 commit into
mainfrom
fix/package-policy-guix-only
Jul 27, 2026
Merged

fix(governance): package policy — cite the canonical ruling, detect sealed containers#534
hyperpolymath merged 1 commit into
mainfrom
fix/package-policy-guix-only

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Fixes check-package-policy.sh, which encoded the superseded packaging rule.

Three defects

  1. 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.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 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* and Dockerfile* now satisfy it.

  3. 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 ship flake.nix to 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:

state repos
Nix-only → RED on merge 22
guix only 160
both 18
neither (already covered by the 2026-08-21 cutoff) 33

If 2027-06-01 was 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.scm that is a non-functional scaffold stub. For those, deleting the flake means having no working packaging. The error output says so explicitly and points at spec/scaffold-stub-debt.adoc step 3: make the Guix side real first, then retire the mirror.

Red-teamed — all nine branches measured

case exit
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 0 guix wins, no nix warning
malformed date 1 refuses to run

bash -n clean.

🤖 Generated with Claude Code

…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>
@sonarqubecloud

Copy link
Copy Markdown

@hyperpolymath
hyperpolymath marked this pull request as ready for review July 27, 2026 13:34
@hyperpolymath
hyperpolymath merged commit c598fbf into main Jul 27, 2026
23 of 25 checks passed
@hyperpolymath
hyperpolymath deleted the fix/package-policy-guix-only branch July 27, 2026 13:35
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>
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