MINOR. Watchdog coverage on two hooks that never had it is a new backward-compatible capability, and it ships with new gate checks and new tests.
Two hooks ran unbounded
hooks/stop and hooks/user-prompt-submit had no timeout guard, although both fork a JSON engine: ha_json_engine_init probes by running python3 -c 'print(1)', and both then call ha_flist_active. A wedged interpreter — antivirus scanning a first-run interpreter, a Microsoft Store python3 alias, an unreachable network share on PATH — hung them with nothing to intervene.
Measured against stub engines that sleep 30s:
| before | after | |
|---|---|---|
hooks/stop |
still running at 25s | 5s, emits nothing, exit 0 |
hooks/user-prompt-submit |
still running at 25s | 5s, emits nothing, exit 0 |
The three peer hooks have had this since v0.10.0. These two were missed for eight releases, while the README asserted every hook had one.
ha_json_engine_init now sits inside main(): outside it, the probe's own hang would sit in front of the watchdog rather than behind it.
Supporting work: a contract test that wedges every engine in the chain, a validate-anchor assertion so a new hook cannot ship without a watchdog, pre-compact added to the timing benchmark with a coverage guard, and troubleshooting entry #16 for the symptom this creates — a hook that goes completely silent.
README rewritten
It opened with a component inventory; the first concrete piece of value was at line 118. Now ordered problem → artifact → components → credibility, led by a real session-start banner. The long-form rationale moved to a new docs/design.md, with the alternative rejected in each case.
Four claims were wrong and are corrected: the hook count (four claimed, five shipping), a read-only assertion about index-curator — which carries Write, and needs it — done_criteria described as booleans when the enforced rule is evidence: null blocking status: pass, and release-note provenance that had leaked in from CHANGELOG entries.
All ten design invariants are now enforced
CLAUDE.md states that the invariant list is the part a test mechanically enforces. That was untrue of three entries.
- #5 asserted that
cpp-detect.shgates skill loading and that C/C++ skills reference the detected build system. Neither holds. Reworded to state both real gates, with the requirement that does the work —skills/cpp-*scoping to C/C++ inside the first 80 characters — now asserted. The skills themselves are deliberately untouched. - #7 — see above.
- #9 needed only the check, not new wording.
- #6 turned out never to have been unenforced; its eval is the skill-triggering harness. It now says so and warns against replacing that eval with a static check.
docs/commands.md was also re-verified end to end. Its doc-align marker had stood at v0.12.0 through six releases, hiding five stale claims — including a /verify report documented with five sections when nine are emitted.
Verification
| Gate | Result |
|---|---|
validate-anchor |
176 / 0 (was 158) |
validate-manifests (+ negative fixtures) |
3 / 0 |
windows-compat · posix-compat |
24 / 0 · 4 / 0 |
| skill-triggering coverage | 44 / 0 (was 28) |
hook-timing |
5 / 5 hooks, 930–2443 ms vs the 5000 ms budget |
| unit · hook-contracts | 19 / 19 · 16 / 16 |
measure-context |
6807 / 5153 chars, cap 12000 |
ShellCheck (--severity=warning, 68 files) |
exit 0 |
CI on the merged head 2664c77 |
4 / 4 green (ubuntu, macOS, Windows, lint) |
The merge commit's tree was verified byte-identical to the reviewed branch tip. Every new check was verified red before green by mutation, including the non-vacuity guards.
Not run for this release: tests/skill-triggering/run-all.sh, the live-session tier of #6's eval. No skill description and no triggering prompt changed here — what changed is the structural guard around them — so its outcome is unaffected. Recorded rather than implied.
Full detail in CHANGELOG.md · PR #21