An operating protocol for Claude Code, for work that is hard, long, or multi-step — debugging, multi-file refactors, long documents, messy data analysis, open-ended builds.
It targets two failure modes that cause most wasted turns:
- Spending tokens in the wrong place — long prose on easy problems, thin reasoning on hard ones.
- Holding state in context instead of in files, where it decays across a long session and vanishes between sessions.
Nothing here raises the capability ceiling. It reduces self-inflicted loss and shortens the correction loop.
- Triage first — trivial tasks get answered, not processed. Firing the protocol on a one-line question is itself a failure.
- Think in proportion to difficulty, write in inverse proportion. A hard problem well understood produces a shorter answer.
- Batch independent tool calls instead of chaining them — usually the biggest wall-clock cost in agentic work.
- Externalize state to
CONTEXT.md/CHECKLIST.mdonce a task outgrows the context window. - Never ship code that wasn't run. Unrunnable code gets labelled
UNVERIFIED (reason)with the exact command to verify it. - Hold position when challenged — re-derive from evidence before conceding, so agreement stays informative.
- Stop conditions — escalate after three failed attempts rather than circling.
Per-task-class rules load only when they match: debugging, multi-file changes, long documents, data analysis, open-ended builds.
git clone https://github.com/SPOTWHALE/working-protocol
cp -r working-protocol/skills/working-protocol ~/.claude/skills/
cp working-protocol/hooks/protocol-tracker.js ~/.claude/hooks/Windows PowerShell:
Copy-Item -Recurse skills\working-protocol "$env:USERPROFILE\.claude\skills\"
Copy-Item hooks\protocol-tracker.js "$env:USERPROFILE\.claude\hooks\"The skill works on its own — invoke it with /working-protocol. The hook is optional but recommended: it makes the protocol persist automatically across /clear, compaction, and new sessions, instead of being loaded once and forgotten.
Add to ~/.claude/settings.json:
{
"hooks": {
"SessionStart": [
{ "hooks": [{ "type": "command", "command": "node \"<HOME>/.claude/hooks/protocol-tracker.js\" session-start", "timeout": 5 }] }
],
"UserPromptSubmit": [
{ "hooks": [{ "type": "command", "command": "node \"<HOME>/.claude/hooks/protocol-tracker.js\" prompt", "timeout": 5 }] }
],
"PostToolUse": [
{ "matcher": "Skill",
"hooks": [{ "type": "command", "command": "node \"<HOME>/.claude/hooks/protocol-tracker.js\" skill", "timeout": 5 }] }
]
}
}Replace <HOME> with your home directory (on Windows use escaped backslashes and the full path to node.exe if it isn't on PATH).
What each mode does:
| Mode | Event | Behaviour |
|---|---|---|
session-start |
SessionStart | Re-arms from the persisted preference and injects the full rules. Fires on startup, resume, /clear and compact. |
prompt |
UserPromptSubmit | Detects /protocol on / stop protocol and persists the choice. Injects the digest only at the moment of activation. |
skill |
PostToolUse(Skill) | Marks the protocol active when the model invokes the skill itself. |
The preference persists across sessions, so stop protocol stays off until you turn it back on.
- On:
/protocol,/working-protocol, or "use the working protocol" - Off:
/protocol off, "stop protocol", "disable working protocol"
Reported honestly, because a protocol that only grows gets skimmed:
- The externalization threshold (~3 files / ~5 steps) is tuned for editing existing code, where you need an inventory of call sites you might otherwise miss. On authoring tasks — writing new files from an outline you already hold — it fires early and gets ignored without any loss. Treat it as a rule about inventories, not about file counts.
FAILURES.mdcompetes with Claude Code's own memory system. If you have file-based memory configured, that's the better home for a durable correction, because it loads automatically.- The debugging and data-analysis classes are the highest-value sections. The rest earns its place less often.
If a rule stops earning its place, delete it. That instruction is in the protocol itself and it's meant literally.
MIT