Files
hanzo-devandzeekay 13e8668940 ci: one trigger publishes, and the gate belongs to the recipe
docs.hanzo.ai could not be republished. This makes every path that could do it
real or explicitly named, and moves the one check that matters somewhere every
path passes through.

The export gate is now in the Dockerfile. This site fails by exporting NOTHING:
the build succeeds, the layer is valid, the push succeeds, the host answers 404,
and no builder notices because from a builder's point of view nothing went wrong.
It used to be gated between build and push in deploy.yml, which only works for
deploy.yml — buildx builds and pushes in a single invocation with no step in
between to hold, and hanzo.yml's `test:` block runs AFTER that push, so neither
could have held it back. scripts/check-export.sh states what a site is, once;
apps/<app>/export.require names the sections that vanish without breaking
anything else. A failed gate means no image exists, so no lane can push past it.
deploy.yml keeps its own call of the same script against the finished image,
which answers the one question the build stage cannot: did the export land in the
IMAGE. Tested against seven exports — healthy, empty, nav-less, under the page
floor, section missing, absent directory, default require-file resolution — under
both dash and busybox ash.

docs/studio/ is the case that motivates the require file. It is a submodule, and
hanzoai/ci checks out without recursing, so on that lane the section silently
disappears while page count and nav both stay green.

.github/workflows/cicd.yml is seven lines importing hanzoai/ci, configured by the
root hanzo.yml. It is workflow_dispatch-only: give this repo two push-triggered
builders for one image and one commit yields two images under two tag schemes,
and the pin names one of them by luck. It names our own runner pool, the same
label the forge lane answers to. It cannot schedule yet, and the reason is not
Actions — Actions is on and the repo is public; every runner we own is registered
to git.hanzo.ai, and GitHub-hosted runners are not something we build on.

RELEASE.md is the runbook: four builders, the credential each needs, and the
exact edit that makes an image live. The finding worth repeating here is that
universe pins a digest beside the tag and the chart renders repo:tag@digest — so
the digest is what gets pulled, and moving the tag alone is a silent no-op.

Verified against the live systems rather than the docs: the pin resolves to the
digest the values file names; no image exists for current main in ANY of the
three tag shapes, so all lanes are cold rather than one quietly working; the
forge repo answers 404 on /actions while another repo on the same forge answers
200; /v1/runner answers 401 with the token configured on the far side, so it is
armed and one credential away.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-31 17:45:47 -07:00
..