v0.10.0 — SessionStart injection slimming + 3000-token cap
MINOR release per SemVer: a new hook capability (cpp-gated, slimmed SessionStart injection with authoring-time flatness validation) plus a raised injection budget — design invariant #2 changes from ≤ 2000 to ≤ 3000 tokens (user-approved). The warn-only hook contract and JSON shape are unchanged; hooks still never write. No migration: existing projects benefit at the next session start (generic projects now spend ~39% of the cap instead of 94%, and the freed budget flows to the project-specific PROJECT-TOC view — repos up to ~150 files get the full file index injected). Re-running /anchor refreshes the scaffolded context-budget.md numbers. Also ships two long-standing hook fixes (watchdog SIGTERM deferral; quadratic JSON escaping) surfaced while chasing CI on the rescaled deep-repo fixture.
Added
- cpp-gated, slimmed SessionStart injection. The meta-skill body is now injected as a
pure filter ofusing-harness-anchor/SKILL.md: YAML frontmatter stripped, and
<!-- cpp-only-start -->/<!-- cpp-only-end -->regions (the fourcpp-*sibling
skills,/cpp-init,/sanitize) dropped in non-C/C++ projects — catching invariant #5
up at the injection layer. The file itself is untouched for the Skill-tool path. New
contract test pins both modes plus the skip-leak guard;validate-anchorchecks the
regions are flat — sequenced, non-nested, closed (a nested pair would leak past the
filter's single skip boolean with start/end counts still equal) — and that every
cpp-onlyline is exactly one of the two markers. measure-context.shsecond pass on a bare generic fixture, so the generic fixed-cost
baseline (the common case) is measured alongside the C/C++ e2e fixture.
Fixed
- SessionStart watchdog: kills are now SIGKILL and the watchdog's stdio is detached.
Measured on macOS bash 3.2: a subshell blocked onsleep 5defers SIGTERM until the
sleep completes, so (a) the parent'swait $watchdog_pidburned the full 5-second
window on every session start even thoughmain()finished in ~0.4s — any consumer
waiting on the hook saw ~5s of wall per invocation — and (b) a genuinely runaway
main()was never actually killed on that bash (the deferred TERM aborts the subshell
before itskillline runs), leaving invariant #7's enforcement partly fictional.
Exposed on CI by the rescaled 600-dir deep-repo fixture: slow runners pushedmain()
past 5s and the still-armed watchdog hard-killed it ("no output emitted"). Deep-repo
hook wall: 5047ms → 502ms; the timeout contract (genuine overrun → silent, exit 0)
re-verified at 5054ms. - SessionStart JSON escaping is O(n) via python3 (pure-bash fallback retained). The
${var//…}escaper is quadratic in matches×length: a ~12KB, ~700-line payload (a
deep-repo directory map at the raised cap) burned the remaining watchdog window in
this one step on pessimal macOS CI runners (~0.15s on a fast machine — which masked
it).json.dumpsis linear and also escapes control characters the bash path misses;
environments without python3 keep the old escaper. Deep-repo hook wall: 502ms → 165ms.
Changed
- Injection budget raised: ≤ 2000 → ≤ 3000 tokens (8000 → 12000 chars) — invariant #2.
The old cap was 94% consumed and the squeeze fell entirely on the project-specific
<project-toc>block (the banner never truncates). With the slimmed body a generic
project's fixed cost drops to ~4.6KB (≈1160 tokens measured) and the TOC budget grows
~5×, so repos up to ~150 files get the full## Filesview instead of the degraded
directory map. All cap reference points (hook, measure script, three test files,
CLAUDE.md, README, both context-budget references) moved in lockstep; the deep-repo
contract fixture rescaled 200→600 dirs so the degradation path stays exercised. - Meta-skill body compressed ~5.9KB → ~5.0KB — packaging only: rules, trigger keywords,
read order, and command timing preserved item-for-item (contract-suite verified; a live
three-scenario spot-check — scope-jump, TOC-before-Glob, no-done-without-evidence — gates
the merge).