Skip to content

Commit 2a2cb33

Browse files
authored
Merge pull request #64 from ScriptKittyOS/chore/gate-prod-check
The gate checks the release (scripts/prod_check.sh: prod compile, headless release assemble, runtime config eval) and packaging inputs are packaged on the branch. Signed-off-by: Ayla Croft <aylacroft@proton.me>
2 parents 4950f41 + 3bad82e commit 2a2cb33

5 files changed

Lines changed: 79 additions & 1 deletion

File tree

‎.github/workflows/package.yml‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,16 @@ name: package
1919
# slice 001 had already proven. It now runs when a slice or release tag is pushed, on a
2020
# change to something packaging actually depends on, or by hand. A slice that touches
2121
# packaging asks for the run with `workflow_dispatch` and cites the run id in its proof.
22+
# Corrected again after slice 062. The tags-only trigger meant a change to a packaging input
23+
# (a dependency, the toolchain, the Tauri shell, config) was first packaged at the tag, which is
24+
# after the owner has approved the slice: slice 062 added `jose` and the first packaging run of
25+
# it was the tag's, which failed. Branches are added beside tags, with the same path filter, so
26+
# a change to those inputs is packaged on the branch that makes it. This is not the guard for a
27+
# prod-only code defect: the filter below does not match `lib/`, and `scripts/prod_check.sh` in
28+
# `mix gate` is what catches that class on every push.
2229
on:
2330
push:
31+
branches: ['**']
2432
tags: ['slice/**', 'v*']
2533
paths:
2634
- mix.exs

‎CLAUDE.md‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@ If ROADMAP.md shows no slice `in_progress` and the previous slice is not `approv
3030

3131
A slice is done only when ALL of these are true:
3232

33-
1. `mix gate` passes (format check, compile with warnings-as-errors, credo --strict, boundary, tests, audits).
33+
1. `mix gate` passes (format check, compile with warnings-as-errors, boundary, **the release check**,
34+
credo --strict, sobelow, tests, audits, plan check). The release check is `scripts/prod_check.sh`:
35+
the tree compiles under `MIX_ENV=prod`, the headless release assembles, and it evaluates
36+
`config/runtime.exs`'s prod branch. It exists because `MIX_ENV=test` says nothing about what
37+
ships: a plug's `init/1` is called at compile time under prod, and slice 062 shipped a closure
38+
there that broke the release while every gate stayed green (slice 062 NOTES, F5).
3439
2. Every acceptance criterion has a corresponding proof entry (command + output, or screenshot for UI).
3540
3. New behaviours/public modules have `@moduledoc` and `@doc`; new behaviours have typespecs.
3641
4. Tests exist for new logic (unit for pure code, process tests for GenServers, LiveView tests for UI).
@@ -43,6 +48,7 @@ A slice is done only when ALL of these are true:
4348

4449
```
4550
mix gate # the full quality gate (defined in Slice 000). Must pass before every commit.
51+
./scripts/prod_check.sh # the release half of the gate, on its own: prod compile, release assemble, config eval
4652
mix test # tests
4753
mix test --cover # coverage (report the line in PROOF.md)
4854
mix credo --strict

‎ROADMAP.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,4 @@ After 012, multiple branches are independent. If running more than one coding ag
124124
| 2026-09-20 | The MCP phase replanned under owner decisions of 2026-09-08, recorded in ADR-0007 decisions 5 to 8: beam_mcp 0.8.0 is the server core; 059 measures the capability gap and probes the `:server` seam (S/M); 060 is Trinity's thin driver (L, the M/L condition decided); the OAuth client role moves from 060 to 062; 061 carries a named blocker on the MRTR wrapper; the four `M5 Always-on` headers on 059 to 062 read M5a Automates, as this file has since 2026-09-08. R14 re-scoped, R15 closed. Re-derived this date: `find slices -name SLICE.md | wc -l` → **36**. |
125125
| 2026-09-20 | Four slices added under the accepted 2026-09-20 plan: 002 (supply chain, early: SBOM, provenance, TLS floor; counted in M9), 003 (FIPS build leg from source; counted in M2 because it runs 024's FIPS properties), 025 (encryption at rest and the key-custody seam) and 026 (store-and-forward receipts; blocked on the external plane's answer). Re-derived this date: `find slices -name SLICE.md | wc -l` → **40**. |
126126
| 2026-09-22 | Slice 062 amended under the owner's decision: the production profile is an OAuth client and resource server against an external authorization server, the embedded authorization server is the non-default personal profile refused under an external authority adapter, and Enterprise Managed Authorization (ID-JAG redemption at Trinity's own server) leaves the slice as a deferral with a lift condition (slice 062 NOTES, "Amendment"). AC3 and AC6 removed; the row's title follows. |
127+
| 2026-09-22 | The gate gained a release check (`scripts/prod_check.sh`: prod compile, headless release assemble, runtime config eval) and `package.yml` gained a `branches:` trigger beside its tags one. Both follow slice 062's F5: a plug's `init/1` is escaped at compile time under prod, so a closure there broke the release build while `mix gate`, which runs in `:test`, stayed green on every leg, and the tags-only packaging trigger meant the first packaging run of the slice was the one after approval. Measured before adopting: the release check costs 52 s cold and 2 s warm, and it is red on the original defect. |

‎mix.exs‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,21 @@ defmodule Trinity.MixProject do
284284
gate: [
285285
"format --check-formatted",
286286
"compile --warnings-as-errors --force",
287+
# The release half, added after slice 062: MIX_ENV=test says nothing about what ships.
288+
# `Plug.Builder` escapes a plug's `init/1` result at compile time under prod, so a
289+
# closure in those options broke the release while every gate stayed green, and the
290+
# three-OS `package` workflow only found it at the tag, after approval (062 NOTES, F5).
291+
# The script compiles prod, assembles the headless release and evaluates its runtime
292+
# config, which is the only thing in the gate that executes runtime.exs's prod branch.
293+
# `cmd`, like the two steps below: its own OS process and its own exit code.
294+
# `env ERL_AFLAGS=` for the same reason `hex.audit` below carries it: on the FIPS leg
295+
# this step runs outside FIPS mode. Compiling prod builds the dependencies in that
296+
# environment, and `tokenizers` fetches a precompiled NIF over TLS, which OTP's ssl
297+
# cannot do in the mode (docs/fips-leg.md, finding 1: the same HelloRetryRequest
298+
# problem that stops Hex reaching hex.pm). Whether the tree compiles for release is not
299+
# a FIPS property, so measuring it outside the mode loses nothing; 024's FIPS
300+
# properties are measured by `mix test --trace test/fips`, which stays in the mode.
301+
"cmd env ERL_AFLAGS= ./scripts/prod_check.sh",
287302
"credo --strict",
288303
"sobelow --exit --skip",
289304
# `cmd` runs it as its own OS process: Hex's tasks are not reliably resolvable from

‎scripts/prod_check.sh‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/usr/bin/env sh
2+
# SPDX-FileCopyrightText: Sudo Apt Holdings LLC
3+
# SPDX-License-Identifier: Apache-2.0
4+
#
5+
# The release half of the gate. Added after slice 062, for a defect the gate could not see:
6+
# `Plug.Builder` calls a plug's `init/1` at COMPILE TIME when MIX_ENV=prod and escapes what it
7+
# returns into the endpoint, so an anonymous function there breaks the release build and nothing
8+
# else. `mix gate` runs in MIX_ENV=test, where `init/1` is called per request, so every gate was
9+
# green over a tree whose release could not compile; the three-OS `package` workflow found it at
10+
# the tag, which is after approval (slice 062 NOTES, F5).
11+
#
12+
# Three commands, each for a class the one before it cannot see:
13+
#
14+
# 1. the tree compiles in prod — compile-time differences (the defect above)
15+
# 2. the release assembles — the release's application list and boot scripts
16+
# 3. the release evaluates its config — config/runtime.exs's `config_env() == :prod`
17+
# branch, 116 lines that nothing else in the gate
18+
# or in CI ever executes
19+
#
20+
# What it does NOT prove, said plainly: nothing here builds a Burrito binary, loads a NIF under
21+
# musl (slice 013's mdex finding), builds the Tauri shell, or runs on macOS or Windows. That is
22+
# the `package` workflow's job and it stays the `package` workflow's job.
23+
#
24+
# POSIX sh, like scripts/plan_check.sh, and like it not run on a Windows developer's machine.
25+
# The gate invokes it as `cmd env ERL_AFLAGS= ./scripts/prod_check.sh`: on the FIPS leg it runs
26+
# outside FIPS mode, because compiling prod builds the dependencies and `tokenizers` fetches a
27+
# precompiled NIF over TLS, which OTP's ssl cannot do in the mode. Compiling for release is not
28+
# a FIPS property; the FIPS properties are `mix test --trace test/fips`, which stays in the mode.
29+
set -eu
30+
cd "$(dirname "$0")/.."
31+
32+
build_root="${MIX_BUILD_ROOT:-_build}"
33+
rel="$build_root/prod/rel/headless/bin/headless"
34+
35+
echo "== 1. the tree compiles in prod =="
36+
MIX_ENV=prod mix compile --warnings-as-errors
37+
38+
echo "== 2. the headless release assembles =="
39+
# Assets are not deployed here: this step is about the release's own assembly, and
40+
# `mix assets.deploy` is the packaging workflow's concern.
41+
MIX_ENV=prod mix release headless --overwrite --quiet
42+
43+
echo "== 3. the release evaluates its runtime configuration =="
44+
# `eval` runs the release's config providers, so config/runtime.exs's prod branch is executed
45+
# with no environment prepared for it, which is how a packaged binary is first started.
46+
"$rel" eval 'IO.puts("prod boot config ok")'
47+
48+
echo "prod_check: PASS"

0 commit comments

Comments
 (0)