Checkwright v0.16.0

2026-07-26

Checkwright is the verification layer under agent orchestration, and this release gives it an activation path: npx checkwright init vendors pinned kit source into your repository, registers the gates each kit can run with no configuration, writes a checkwright.lock manifest, and commits the result. checkwright doctor reports whether your toolchain clears the floor the battery needs. Nothing about the verification model changes — this is how you get it into a tree.

Tightened gates

None. Gates did land and tighten in this repository’s own scripts/ gate directory this release — check-installer-no-deps is new, and check-install-toolchain moved from name-set to whole-element parity — but neither ships inside a kit, so neither can appear in a vendored tree. A reader watching this repository’s battery grow would otherwise expect a red that cannot reach them. The clean upgrade’s allowed-red set is empty.

Renamed knobs

None. This release adds knobs (GATE_SDK_LINT_EXTRA_DIRS, and the installer’s INSTALLER_PACK_TMP_DIR) but renames and removes none.

Behavior changes

  • installer/ — new. A published npm package whose payload is this repository’s kit source, assembled at pack time and stamped from the release tag. checkwright init resolves a profile (starter, delegation, full), copies the kits that profile names, registers each kit’s zero-config gates, writes checkwright.lock, and commits. checkwright doctor renders the toolchain verdict in its exit status. The package is a delivery vehicle only: the payload is bash, and nothing resolves, compiles, or downloads at your build time — Node is used to fetch and unpack, never to run what governs your tree. What a consumer reconciles: nothing. An existing vendored tree is untouched by this release, and the wholesale-sync upgrade path in §The upgrade contract remains exactly as it was; init is an alternative to doing that sync by hand, not a replacement for it.
  • context-kit/lib/toolfloor.sh — new library, and the PROBE_SET roster moved into it out of context-kit/bin/env-probe.sh. The roster also gained a grammar — name:min-version:impl-token, so bash:4.0 carries a version floor and awk::GNU / sort::coreutils carry an implementation requirement — and gained sort as a member. What a consumer reconciles: running env-probe.sh is unchanged. A tree that parsed env-probe.sh for the PROBE_SET array reads the library instead, and must expect roster elements to carry colon-separated fields rather than being bare tool names.
  • context-kit/bin/env-probe.sh — both version probes now read stdin from /dev/null. -V prints a version banner for most tools but is an ordinary flag for some (GNU sort’s version-sort), so a tool that rejected --version could fall through to a -V that consumed inherited stdin and hung the probe. What a consumer reconciles: nothing — a hang becomes a result.
  • gate-sdk/checks/check-shellcheck.sh — a new GATE_SDK_LINT_EXTRA_DIRS knob appends consumer-named directories to the derived default lint set. It is append-only, never substituted for the default, so a consumer naming a shipped script that sits under no kit root can only widen coverage. What a consumer reconciles: nothing — the knob defaults to empty and the default lint set is unchanged, so this gate cannot newly red on upgrade. Set it if you ship shell outside your kit roots.
  • delegation-kit/SPEC.md §Layout and configuration — the DELEGATION_KIT_GATE_FILES description is corrected. It had said a consumer’s config widens the default; the loader guards the array with declare -p … ||, so a consumer declaration replaces the default outright. The mechanism did not change — the documentation was the inverse of it. What a consumer reconciles: read your own declaration. If you declare DELEGATION_KIT_GATE_FILES and expected the default’s <gates-dir>/check-*.sh glob to still apply, it does not, and those gates are outside check-gate-tamper’s coverage. Restate every glob you want covered. This repository had exactly that hole and fixed it in this release.

Upgrading

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

No allowed reds. No kit-shipped gate landed or tightened this release.

If you are installing for the first time

Erratum, added 2026-07-26. The pinned command below does not resolve, and will not. This release’s tag fired the publish workflow, but the job passed npm a package spec npm read as a GitHub owner/repo shorthand rather than as a path, so it exited before contacting the registry and 0.16.0 was never published. Use the unpinned npx checkwright init, which resolves whatever version is current. Everything below this paragraph is the note as shipped.

npx checkwright@0.16.0 init does the vendoring, gate registration, and commit for you. Two limits are worth knowing before you rely on it, because this release ships init and doctor only — update, diff, and uninstall are deferred:

  • Upgrading is supported, but it is spelled init. Re-run npx checkwright@<newer> init from the newer package: the version check refuses only the downgrade direction, so an upgrade passes through, the profile is re-read from your checkwright.lock, and files you have modified are preserved and reported rather than overwritten. You would not guess that verb, which is the honest cost of update being deferred. --force is a deliberate-rollback override, not the upgrade path.

    Erratum, added 2026-08-05. The preservation half of that promise is wrong, and it was wrong on the day this note was published. The profile re-read is accurate; files you have modified are preserved and reported rather than overwritten was not. Three classes of file were overwritten instead, and none of them needs an upgrade or a --force to lose your edits — a bare init at the same version is enough. Each kit’s templates/*-config.sh was copied into your gates directory before the hash comparison ran, so the comparison was made against the copy that had just replaced your edit and reported nothing; the affected set is every kit that ships such a template, which is most of them. gate-sdk’s msg-patterns.list was copied the same way, and it reaches the starter profile, so the smallest install is affected too. And a <!-- doctrine-digest-trim: … --> line — the sanctioned way to drop a doctrine rule you do not keep resident — was discarded and its rule restored, silently, because the resulting file hashed the same as what the installer had recorded. All three predate this note: the first two landed in this release itself, the trim marker just over two weeks earlier. A fourth route reaches the same false sentence but is narrower — a release that stops shipping a file init created, followed by a later one that re-adds it — so it needs that window rather than firing on any re-run.

    A fix is in progress. It is in no published release, so until one ships, treat that sentence as false for those files: commit your tree before you re-run init, and read the diff afterwards.

    Update, later the same day. The fix has landed: init now claims every file it rewrites before writing it, so a config template or msg-patterns.list you have edited is reported rather than silently replaced, and a declared doctrine-digest-trim is re-emitted in its rule’s position instead of being discarded. It is still in no published release. The advice above therefore stands unchanged until one ships — this records that the work is done, not that you can stop reading the diff.

  • Previewing is supported. init --dry-run prints the file plan and the manifest that would be written, which covers most of what a diff verb would add.
  • uninstall is genuinely absent. Backing out means deleting files by hand. checkwright.lock records every installed path with its hash, so the work is mechanical — but there is no verb for it, and that is a real gap rather than a discoverability one.
  • The cross-version upgrade path is implemented but not yet smoke-covered. The consumer smoke packs a single version and asserts a same-version re-run leaves the tree unchanged; it does not yet exercise a version bump. If you upgrade from v0.16.0 to the next release, you are the first execution of that path. It was read against the source, not run.

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.