Checkwright v0.20.0

2026-08-01

Checkwright is the verification layer under agent orchestration, and this release puts the release pointer itself under a probe. A GitHub Release body lives on the host, out of every gate’s reach, so the one artifact that tells a reader where a release is documented was governed by nothing but the hand that wrote it — and it had already shipped empty once, and shipped a URL that 404s once. The daily site-health monitor now reads every published Release body and asserts both that it points at its note and that the URLs it carries actually resolve.

Tightened gates

None. The release-body arm is a monitor arm, not a battery gate: it joins no gates.list, reds only its own scheduled run, and signals by filing a site-health issue. Nothing in a clean upgrade turns red because of it.

Renamed knobs

None. The template gains three RELEASE_NOTE_* values, added rather than renamed or removed. They are step-level workflow env in a file you copy and edit, not SITE_KIT_* kit knobs — deliberately, since a copied workflow must not reach into a vendored gates directory to resolve its own configuration.

Behavior changes

  • site-kit/templates/site-health.yml — the scheduled probe gains a sixth arm over release-note pointers, and the workflow’s permissions: block gains contents: read to feed it. For every tracked note whose front-matter tag key names a tag that exists on the remote, the arm reads that tag’s GitHub Release body and makes two assertions over two different strings: presence, that the body contains the note’s apex URL as derived from the note’s filename, and resolution, that every apex URL literally written in the body answers 200. They are not collapsible — a trailing-slash URL contains its own bare form, so a presence check alone passes on a link that 404s live, which is one of the two defects the arm exists for. Three refusals ride with it, each a silent-green path closed: the tag list comes from the API rather than git tag, because a depth-1 checkout fetches no tags and a git-driven arm would report zero released notes forever; the gh call is asserted on its own exit status rather than inferred from the resulting count, because the step runs under set +e and a failed call otherwise yields an empty tag list and a clean run; and pagination is consumed by streaming names rather than by a length query, which returns the last page’s count. A census line states which zero-case a reader is in. Reconcile if you have copied this template out: re-copy it, then either set RELEASE_NOTE_GLOB / RELEASE_NOTE_TAG_KEY / RELEASE_NOTE_URL_PATH and keep the contents: read scope, or delete the arm together with its three env if you publish no release notes. RELEASE_NOTE_URL_PATH is the value worth checking twice — it must match the URL form your host actually serves and the form your Release bodies are written to carry. A permissions: block is an allowlist rather than an addition, so omitting contents: read makes the arm’s reads return 404, which reads like “no such Release” rather than “not permitted”.

Upgrading

Replace the vendored kit directories wholesale at v0.20.0, then regenerate the generated artifacts — the pre-commit hook and the graph projection. Then run the full battery.

No allowed reds. This release tightens no gate; the new assertion is a monitor arm and cannot red your battery. The work, if any, is the template re-copy named above, and it is yours only if you had copied the workflow out.

The behavior changes above are declared for reading, not a mechanical scan. If a gate reds that this note does not name, the upgrade smoke was supposed to catch it first — open an issue, because that is a defect in the release rather than work for you.