Checkwright v0.24.0
2026-08-21
Checkwright is the verification layer under agent orchestration, and this release is the one where the last takeable gate leaves shell and a consumer configuration seam stops being executable code. It batches fourteen iterations behind a deferred tag, so it is wide — and unlike the last one it carries a seam that is genuinely retired rather than merely relocated, which is the single thing to read for before you upgrade.
In brief
- If you theme the graph artifact, this release breaks that and tells you so.
The
check-graphtheme seam was a set of bash functions you wrote and the kit sourced; it is now a directory of plain files the kit reads. The gate refuses outright rather than silently ignoring your old file, so you will find out at the battery and not from a differently-styled page weeks later. - If you script against kit paths, check them. Six kit command-line tools and a long tail of gate implementation files no longer exist under the names they had; every capability survives, but the spelling that reaches it changed.
- If you run the lifecycle stages, one recorded line gained a field. Stage stamps now record the commit they were taken at. Old four-field stamps are refused, not tolerated.
- Everything else is additive or invisible. The bulk of this window is the gate battery finishing its move to the compiled binary, which asserts exactly what it asserted before.
- You must re-run
initafter replacing the kit directories, before anything else. That order is load-bearing for the same reason it was last release.
Tightened gates
check-settings-pins— its left side narrowed from an arbitrary jq filter to a documented path grammar, and it now refuses an out-of-subset pin at exit 2 rather than accepting whatever the filter returned. A pin outside the documented subset is the red.check-gate-substrate-parity— assertion B gained a second scope clause and the owner column gained a consumer sentinel, so a consumer-declared ported member now has a representation it previously had none for. Before this, such a member fell outside the parity statement rather than passing it.check-reads-couples— its--readschannel gained an optional filter-knob name on a root line, carried as a name rather than a literal so the value stays single-sourced. The gate’s couples widen to cover the amendment and roster corpora that assertion C genuinely reads, which the shell form was exempt from asserting. Its counted zero is now a live instance.check-gate-exemption-tasks— the# port-until: <slug>descriptor field is minted and this gate holds it: a temporary hold must name the work owed and resolve to a live owning entry. The field landed with its five day-one holders rather than empty, so the gate has something to check from the first run.check-graph— ported to the binary, and it now refuses at exit 2 on a setGATE_SDK_GRAPH_THEMEor a stale<gates-dir>/graph-theme.sh. That refusal is the migration path for the Behavior change below, and it is deliberately loud: the alternative was emitting an unthemed artifact and letting you notice. Its fixture corpus also widened from one of its nine assertions to all nine.check-stage-evidence— the stage-stamp grammar requires a fifth<head>field, so a four-field stamp is now malformed rather than merely terse. If you carry a.workflow/WORKFLOW-STATE.txt, its existing data lines are the red.
Renamed knobs
GATE_SDK_GRAPH_THEME→ ∅
That is a removal rather than a rename, and the replacement is not a new spelling
of the same thing — see the first Behavior change. GATE_SDK_GRAPH_THEME_DIR is
a new knob taking a directory of data, not a renamed knob taking a script.
No other knob was added, renamed or removed across the fourteen iterations this note batches. Twenty-nine knobs were added in that window; additions are not declarations here, and none of them changes a default you already rely on.
Behavior changes
The graph theme seam is retired, and this is the one that can cost you work.
check-graph’s consumer theme was four executable bash hooks — graph_theme_css,
graph_theme_header, graph_theme_footer from GATE_SDK_GRAPH_THEME, plus
graph_surface_layer from GATE_SDK_GRAPH_VOCAB — each dispatched by the shell
gate through declare -F. A compiled binary cannot be handed a bash function, so
porting this member meant deciding what replaces the hooks rather than preserving
them. The replacement is data: GATE_SDK_GRAPH_THEME_DIR (default
<gates-dir>/graph-theme/) is a directory whose part files are read verbatim,
and the layer rule is now an ordered <path-prefix>:<layer-id> roster rather
than a shell case. Your vocabulary and your theme stay yours — only their
form moves from executable to declarative, which is what keeps the provenance
seam intact. Two alternatives were costed and refused: keeping a shell shim for
theme emission alone, which preserves the seam and adds a shell-only install
step; and shipping the artifact unthemed, which is cheapest and regresses a
shipped capability. Migrating is mechanical: split what your three theme
functions printed into header.html, footer.html and theme.css under the
theme directory, and rewrite each graph_surface_layer branch as one
<prefix>:<layer> line. The prefix test is deliberately narrower than the shell
pattern it replaces — it anchors on path segments where the old case matched
across / — and it expresses every rule the live consumer hooks expressed.
Stage stamps record the commit they were taken at. The grammar is now
<iteration> <stage> <session-id> <date> <head> — five fields, the fifth
required. If you vendor lifecycle-kit and carry a state file, check-stage-evidence
reds on every existing line until they carry a head field; none is the legal
value for a stamp taken where no commit could be read. Reconciliation is a
rewrite of the file, not a code change.
Kit gate implementation files are gone from the vendored tree, replaced by
.gate descriptors that dispatch to the binary. This is the same shape across
the whole window and it accumulates: roughly forty gates lost their
<kit>/checks/check-<name>.sh file, check-graph being the last takeable one.
Every gate keeps its name, its gates.list registration and its behavior —
nothing is deprecated and removed, which is why this rides a minor. What breaks
is any consumer allow-list entry or document that names one of those .sh paths;
check-settings-paths and check-docs-cmd are shaped to red on exactly that, so
your migration worklist finds you.
Six kit command-line tools were deleted and their derivations are now non-gate arms of the shared front-end. This one owes its own bullet because it is not the shape above: a gate script deletion swaps an implementation behind a stable gate name, while these were tools you may have invoked directly, and a consumer scripting the old path gets file-not-found. The replacements:
queue-kit/bin/queue-index.sh→bash gate-sdk/bin/run-gates.sh --emit queue-indexlifecycle-kit/bin/close-surfaces.sh→… --emit close-surfacesdrift-kit/bin/trajectory.sh→… --emit trajectoryqueue-kit/bin/roadmap.sh→… --emit roadmapcontext-kit/bin/footprint.sh→… --emit footprintgate-sdk/bin/enforcement-map.sh→… --emit enforcement-map
The documented regen command for two published projections was respelled onto
that front-end at the same time. This project had to rewrite its own
CONTEXT_KIT_HOOK_CMD to the new spelling, so the stranded-path case is attested
rather than hypothetical. One stated consequence rides along: the footprint
emitter’s advisory bare mode lost its human header.
Two kit library functions were removed. context_memory_dir_default() from
context-kit/lib/context.sh and queue_roadmap_entries() from
queue-kit/lib/queue.sh. A consumer that sources either library and calls one
breaks; nothing in either kit’s own surface calls them any more.
The lifecycle boundary softened in one direction. A gap bullet filed after the closing stage ran is now ADMITTED by the next first-stage entry and carried into that session’s intake, where the entry previously refused a non-empty inbox outright. If you relied on that refusal as a hard boundary, it is now a disposition obligation instead — the reconciliation is to read the advisory, not to change anything.
Three new blocking guard rules land in guard-kit, including rule 14: a tracked-tree mutation under a recorded live producer is refused. A vendoring consumer will meet these as new refusals rather than as new checks.
Smaller consumer-reachable changes, each additive or narrowing in a way a clean tree will not feel:
evidence-kit’scheck-producer-livenessgained a set mode, accepting a directory and globbing*.run, kept beside the lock-pointed form rather than replacing it.gate-sdkmints the# no-port:descriptor field, andport-blockers --group’s trailer changed shape to a three-way split — a consumer parsing that trailer sees a new sentence.- The config bridge carries an associative knob across the wire keyed rather than in hash order, so a consumer declaring one gets a different environment than it did.
upgrade-smoke’s phase-A determinism check reads the staged set before regen instead of exempting named paths after it. This can change your smoke verdict, and it is the repair of a defect that made it red on a correct tree.- The installer’s JSON-reading verbs now refuse by name on a machine without
jq, where they previously misdiagnosed. check-queue-entry-budget’s clean output gained a per-entry headroom field, andenter-stage.sh --simulatenow relays each refusal’s recovery help line.- The
recurrence:declaration is a recorded judgment whose direct, out-of-channel stamp is both sanctioned and obliged, ruled prospective-only.
Upgrading
Replace the vendored kit directories wholesale at v0.24.0, then re-run init
before anything else — the same order last release required, and for the same
reason: registered members now resolve to descriptors, and a descriptor with no
binary present refuses to certify rather than skipping. Then regenerate the
generated artifacts — the pre-commit hook and the graph projection — and run the
full battery.
The allowed reds. Most of the Tightened-gates list cannot red a synced-and-reinstalled tree: the ports assert exactly what they asserted before. Three can reach you, and each names the surface that moved.
check-graphreds at exit 2 ifGATE_SDK_GRAPH_THEMEis set or a stale<gates-dir>/graph-theme.shis on disk. Clear the knob, convert the file to theme parts underGATE_SDK_GRAPH_THEME_DIR, delete the old script. The gate refuses rather than ignoring precisely so this cannot pass silently.check-stage-evidencereds on every existing four-field stamp in.workflow/WORKFLOW-STATE.txt. Append a head field — a commit-ish, or the literalnone— to each data line.check-settings-pinsreds on a pin outside the documented path grammar. Rewrite the pin; the refusal names the offending token.
Separately, and not a gate: if you invoke a kit bin/ tool or source a kit
library function by name, read the Behavior changes above before you upgrade
rather than after. Those are the two surfaces this release moves that no red will
find for you, because a call into a deleted file is your script’s error and not
the battery’s.
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.