Checkwright v0.36.0

2026-11-14

Checkwright catches reproducible classes of unsupported claims in coding-agent delivery before merge, and this release installs one session-hook arm for you rather than a repository, lets any hook run as a trial that enforces nothing, and runs the turn-end liveness hook outside any repository.

In brief

Section Entries Who acts
New and tightened gates 6 everyone: these may red on upgrade
Knob changes 5 if you set knobs
Gate-authoring changes 0 if you write or shadow gates
Platforms 0 if you install on a listed platform
Behavior changes 13 if you depend on a listed behavior
  • You can now install one session-hook arm for yourself with init --user <arm>, from any directory, without vendoring a kit into a repository.
  • Any hook can run as a trial first: it logs what it would have decided and blocks nothing.
  • The turn-end liveness hook now guards turn ends fired outside a repository, and each tree-free hook arm has its own page with a generated knob table.
  • You must act on one red: register the new gates in your gates.list or declare them unregistered. Both read clean until you set their knobs.

New and tightened gates

  • check-settings-paths and check-plugin-parity — red a hook registration whose tokens after the member leave --hook <member> [--observe] [--binding <name>] [--knobs <absolute file>], which they read as clean before; the arm now refuses such a registration at run time. Fix the misspelled or misordered token.
  • check-settings-paths — reds a hook registration whose --knobs names a relative path, which it read as clean before; the arm declines such a registration on every firing. Write the file’s absolute path.
  • check-hook-knobs-fresh — new, off until GATE_SDK_HOOK_PAGES_DIR is set: holds each hook-arm page’s knob block to its member’s knob table, and reds a page naming no member and a tree-free member with no page. Nothing to do unless you publish a page per hook arm; register it with the knob, then run --emit hook-knobs --write.
  • check-gate-substrate-parity — assertion I reds on check-hook-knobs-fresh, a new gate-sdk gate the binary carries, until your gates.list registers it or declares # unregistered: check-hook-knobs-fresh — <reason>. It reads clean while GATE_SDK_HOOK_PAGES_DIR is empty, so register it.
  • check-docs-since — new, off until SITE_KIT_SINCE_PAGES is set: holds each declared page to a since: front-matter key, unreleased or vX.Y.Z. With SITE_KIT_RELEASE_NOTES_DIR set, it also reds a page saying unreleased while a release note is untagged. Nothing to do unless your pages state a release status; register it with the knobs, then run --emit docs-since --write in each note-authoring commit.
  • check-gate-substrate-parity — assertion I reds on check-docs-since, a new site-kit gate the binary carries, until your gates.list registers it or declares # unregistered: check-docs-since — <reason>. It reads clean while SITE_KIT_SINCE_PAGES is empty, so register it.

Knob changes

  • GATE_SDK_HOOK_OBSERVE_LOG — new: the log an observed hook appends its would-be decision to, default <GATE_SDK_TMP_DIR>/hook-observe.log. Nothing to do unless you register a hook under --observe; outside any repository, set it absolute.
  • GATE_SDK_HOOK_PAGES_DIR — new: a word list of the directories whose hook-arm pages --emit hook-knobs writes and check-hook-knobs-fresh holds, default empty, which governs no page. Nothing to do unless you publish a page per hook arm.
  • SITE_KIT_SINCE_PAGES — new: the page globs check-docs-since and --emit docs-since govern, default empty, which governs no page. Nothing to do unless your pages state a release status.
  • SITE_KIT_RELEASE_NOTES_DIR — new: the directory of release notes the since: note rule and its rewrite read for the untagged note, default empty, which turns both off. Nothing to do unless you set SITE_KIT_SINCE_PAGES.
  • DELEGATION_KIT_STOP_WAIT — new: the turn-end hook’s waiting policy, refuse or resume-wait, whose default member is the hook’s behavior unchanged. Any other value fails the knob table at exit 2 and makes the hook decline with an operator notice. Nothing to do; bind resume-wait only where your harness gives a session a new turn when its own background task completes.

Gate-authoring changes

None.

Platforms

None.

Behavior changes

  • --hook — takes --observe after the member, under which any member’s would-be decision is logged to GATE_SDK_HOOK_OBSERVE_LOG and the call proceeds at exit 0 with nothing said; an observed shell-guard grants nothing, so a trial may show permission prompts. It refuses at exit 2 a trailing token outside --hook <member> [--observe] [--binding <name>] [--knobs <absolute file>], where it ignored the token and ran the member enforcing. Fix any such registration; add --observe to trial a hook before it enforces.
  • subagent-stop-liveness — under the resume-wait member of DELEGATION_KIT_STOP_WAIT, allows a turn end over a live producer while a running shell task of the session’s own will resume it, and refuses one whose launch record declares a hand-back. The launch record takes an optional third field, handoff=<path>, which check-producer-liveness and the shell guard read as the same live producer where they read such a record as corrupt or skipped it. Its log line gains a wait= field before decision=. Nothing to do under the default.
  • --install-hooks — refuses at exit 2, placing nothing, a core.hooksPath that is not UTF-8 at the effective or the local scope, or a set value whose prior hooks directory resolves to a path that is not UTF-8, as through a link, where it chained that directory as one holding no hook and advised a restore line naming another value. It also keeps a line break the value ends in, so the line-break refusal now meets it, where it trimmed the break and chained a different directory. Rename the directory or respell the value, then re-run.
  • --emit env-probe — refuses at exit 2 a profile file whose markers do not pair around an unchanged block, where it reported unchanged at exit 0; the file is untouched either way. Repair the markers, then re-run.
  • init, update and uninstall — refuse at exit 1 before any write where git has no identity to commit with, the author or the committer, where they wrote and staged everything first and left git’s failed commit as the only account. A run that would commit nothing refuses too, and --no-commit, a personal install and --dry-run waive the check. Set user.name and user.email, or pass --no-commit and commit yourself.
  • --hook subagent-stop-liveness — runs outside any repository when its scratch dir and log are absolute, and otherwise declines naming the knob, where it declined with the no-tree notice. Set GATE_SDK_TMP_DIR and DELEGATION_KIT_STOP_LOG absolute to guard turn ends fired outside a repository.
  • --install-hooks and uninstall — refuse at exit 2 a common git directory that is not UTF-8, where --install-hooks placed hooks under a replaced spelling. Rename the directory, then re-run.
  • --hook <member> --knobs <absolute file> — new: runs a tree-free member under the named knob file as its only file layer, inside a repository or not, entering no toplevel and reading no project knob file or <KIT>_KNOB_FILE. A file it cannot use declines at exit 0 naming the file and line; the token on a member that reads a repository is refused at exit 2. Nothing to do unless you register a hook for a user rather than a repository.
  • init --user <arm>, update --user and uninstall --user — new: install one session-hook arm for the user rather than a repository, from any directory and with no repository precondition. The gate binary and the arm’s knob file go in the per-user data dir ($XDG_DATA_HOME/checkwright, else ~/.local/share/checkwright; %LOCALAPPDATA%\checkwright on Windows), recorded in user.lock there, and the arm’s scratch and log in the per-user state dir. init --user prints a settings: block carrying the harness entry, --hook <arm> --knobs <knob file> under the absolute binary, for you to merge into your user settings; nothing writes it. update --user re-places every recorded arm and reprints each block; uninstall --user removes the binary and every knob file you have not edited, keeps the state dir, and reminds you to remove the entry. Nothing to do unless you want one arm without vendoring a kit.
  • The gate binary — refuses an argument that is not UTF-8 at exit 2, naming its position, where it panicked.
  • uninstall — prints the line restoring core.hooksPath on every exit after it removes the placed hooks directory, a failed commit included, where a refusal there lost it.
  • init, uninstall and prove — read a personal install as personal when its exclude file also holds a line that is not UTF-8, where they read it as a tracked install: init ran the committing install and uninstall left the block.
  • --usage-poll — writes a snapshot whose five-hour pair is empty beside the weekly pair when the source carries no five-hour window, which the verdict judges on the weekly axis, where it refused the payload as unparseable and left the snapshot to go STALE. A payload with neither window, or with half of the five-hour one, still refuses. Nothing to do.

Upgrading

Replace the vendored kit directories wholesale at v0.36.0, or run checkwright update. Regenerate your generated artifacts; each freshness gate that reds prints its own regen command. Then run the full battery.

The allowed reds. Every gate under New and tightened gates above may red, and its bullet names what clears it. On a tree that sets none of the new knobs, expect only check-gate-substrate-parity’s assertion I, naming check-hook-knobs-fresh and check-docs-since: add both to your gates.list, where they read clean until you set their knobs, or declare each # unregistered: <gate> — <reason>. check-settings-paths and check-plugin-parity red only a hook registration with a misordered token or a relative --knobs path; fix the registration.

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.