harness-anchor
A warn-only runtime layer for Claude Code agents.
superpowers gives an agent process
(brainstorm → plan → TDD → review). harness-anchor gives it environment and
state discipline: where am I, what is the active feature, what counts as
"done", and what to do when stuck.
It never blocks a tool call. It keeps state on disk, injects it back at session start, and makes the agent show its evidence before it claims anything.
What the agent sees
Every session opens with this — real output, from an anchored project:
<harness-anchor-state>
Plugin: harness-anchor v0.18.0
Project root: /home/you/invoice-service
Active feature: feat-3: PDF export pipeline
Project type: generic
Platform: linux
TOC freshness: fresh (8243585b3b97b927cd16672b6095d9270ccc3a3f)
Golden rules: 3 rule(s) — /gc scans changed code against them
Last session handoff (first 10 lines):
# Session Handoff
Last updated: 2026-07-30
Active feature: feat-3
Next action: wire the renderer into the CLI, then run scripts/lint.sh
</harness-anchor-state>
The failure modes it's built around
"Done." — with nothing run. Every criterion starts false. A feature cannot
reach status: pass while its evidence is null, and feature_list.schema.json
rejects that combination — the gate is a schema, not a good intention. Evidence
means a path you can open: a build log, test output, a lint report.
"That's not installed." — after checking one place. The same contract runs
in reverse. Asserting absence requires the scope searched and the date checked —
searched PATH + VS-bundled LLVM, not found (as of 2026-07-31). An empty
command -v proves nothing: VS-bundled LLVM needs vcvars, and Homebrew's is
keg-only.
State that lived in the chat, and a compaction ate it. Feature status, progress and handoff live in files, not scrollback. Four warn-only sentinels fire at the danger moments: context filling, compaction imminent, just-compacted, session stopping.
The change that quietly grew. One active feature at a time. Scope jumps are caught on the prompt side ("also, could you…") and on the action side — a new module written while a feature is still in progress.
What it deliberately does not do
- Never blocks. No hook returns
denyorblock. They inject context the agent can correct itself with — feedback loops, not gates. - Never auto-refactors.
/gcreports drift and stops there. - Never installs anything. bash + git. JSON parsing walks a python3 → python → py -3 → node → pure-bash chain, and degrades honestly when none of them exist.
- Never floods your context. SessionStart injects ≤3000 tokens — banner, project index, meta-skill; C/C++ and per-OS sections are dropped when they don't apply, and depth loads on demand.
Install
claude /plugin marketplace add Redtropig/harness-anchor
claude /plugin install harness-anchor@harness-anchor-local
Start a session; the banner above should appear in the agent's context.
(harness-anchor-local is the marketplace id, not a dev build.)
Quick start
/anchor # scaffold the state files
/index-project # build PROJECT-TOC.md from git-tracked files
bash init.sh # health-check the environment
# ...work...
/session-end # write handoff, append progress.md, offer archival + commit
Commands
| Command | What it does |
|---|---|
/anchor | Scaffold the state files (never overwrites without explicit approval) |
/index-project | (Re)build PROJECT-TOC.md from git-tracked sources |
/status | Read-only snapshot: active feature, counts, git tree, TOC freshness, handoff head |
/verify | Fresh-context subagent runs build + lint + tests, reports evidence paths (--fix = bounded ≤2-cycle auto-fix) |
/test-plan | Fresh-context coverage-gap analysis: obligations from code + spec, plus paths outside the runner's scope |
/gc | Fresh-context drift scan against golden-rules.md + slop heuristics. Report-only (not git gc) |
/session-end | Write handoff, append progress.md, offer archival + commit |
/cpp-init | C/C++: tune init.sh, drop .clang-format / .clang-tidy / lint.sh / sanitizer-build.sh |
/sanitize | C/C++: build under ASan+UBSan, run tests, report with a log evidence path |
Full manual — arguments, prerequisites, outputs: docs/commands.md.
Skills (14, auto-triggered — you never invoke these)
- State & navigation —
project-indexing,feature-state-keeper,init-verification - Evidence & verification —
anti-hallucination-gates,self-correction-loop,test-coverage-design - Governance & context —
capturing-golden-rules,context-budget-discipline,docs-lookup - C/C++ —
cpp-build-systems,cpp-static-analysis,cpp-formatting,cpp-sanitizers - Meta —
using-harness-anchor, injected at every session start
Subagents (5)
verification-runner (/verify), coverage-analyst (/test-plan) and
drift-analyst (/gc) are fresh-context evaluators — separating who writes the
code from who grades it is what keeps the grade honest. coverage-analyst is
the code-aware pass TDD deliberately cannot do: it derives test obligations from
the code as written, and flags paths the test runner never reaches.
cpp-build-doctor diagnoses build failures from compiler output.
Those four are read-only by frontmatter, not by convention — an evaluator that
can edit what it grades has an incentive to make the finding go away.
index-curator is the one that can write, and only to PROJECT-TOC.md.
Hooks (5, all warn-only)
| Event | What it does |
|---|---|
SessionStart | Injects the state banner and a slimmed meta-skill (≤3000 tokens; C/C++ and per-OS sections dropped when they don't apply) |
PostToolUse | Nudges on repeated identical calls, error streaks, context watermarks, scope creep and feature checkpoints; runs clang-tidy on C/C++ edits when compile_commands.json exists. One nudge per call, with a cooldown |
UserPromptSubmit | Flags scope-jump phrasing, surfaces the active feature for confirmation |
PreCompact | Records compaction forensics for the post-compaction notice |
Stop | Nudges a progress.md update, a handoff refresh, and flushing chat-only memory |
Every hook fails silent on missing tools and carries a ≤5s watchdog — on timeout
it emits nothing and exits 0. scripts/validate-anchor.sh asserts the watchdog
is present in each hook the manifest registers, so a new one cannot ship without it.
Design notes
Grounded in Anthropic's Effective Harnesses for Long-Running Agents and the learn-harness-engineering five-subsystem model: harness-anchor supplies State, Verification, Scope and Lifecycle; superpowers supplies process.
docs/design.md carries the full mapping and the reasoning behind each choice — warn-only hooks, the two-directional evidence contract, state budgets and archival, why heavy operations are commands rather than hooks — with the alternative that was rejected in each case.
C/C++ projects
The build system is auto-detected (CMake / Meson / Make / Bazel); nothing to
configure. /cpp-init drops .clang-format, .clang-tidy, scripts/lint.sh
and scripts/sanitizer-build.sh. PostToolUse runs clang-tidy on edited files
when compile_commands.json exists. /sanitize builds under ASan+UBSan and
runs the tests; cpp-build-doctor reads compiler output when a build breaks.
Sanitizer coverage is platform-bound, and the skill says so instead of assuming:
TSan and LSan do not exist on Windows — skills/cpp-sanitizers/platform/windows.md
carries the substitute table.
Platforms
Linux, macOS and Windows; the self-tests run on all three in CI.
Windows runs on the Git-Bash baseline — Git for Windows, which Claude Code
already requires, supplies the bash and coreutils the hooks need, and
hooks/run-hook.cmd dispatches to it (WSL bash is deliberately excluded: hooks
must see Windows paths). LF checkout is pinned by .gitattributes, System32's
incompatible find / sort / timeout are shielded by a PATH prepend, and
C:\ paths are normalized at hook entry.
With no JSON engine at all, the banner, project typing and version stay real — plugin-controlled formats parse in pure bash — while ledger-derived checks skip and say so rather than reporting a false zero.
Verifying the plugin
bash scripts/validate-anchor.sh # plugin self-consistency
bash scripts/validate-manifests.sh # manifest name/version sync
bash tests/windows-compat.sh # static Windows invariants (runs anywhere)
bash scripts/measure-context.sh # SessionStart injection vs the 12000-char cap
CI runs these on every push and PR across Linux, macOS and Windows — hook contracts, unit tests, compatibility gates, and a ShellCheck gate that fails on warning severity. Test scripts are discovered by glob, not from a hard-coded list. (The Windows leg deliberately runs a curated core subset.)
Stuck? docs/troubleshooting.md.
Works alongside superpowers (recommended,
not required) and any docs MCP — docs-lookup prefers first-party sources over
general search.
MIT — see LICENSE. Release history: CHANGELOG.md.