quiver
CLAUDE.md16 starsChanged 57 days agoMITView on GitHub
Setup, tests, and PR expectations for contributors are in CONTRIBUTING.md; AGENTS.md is the short form for other coding agents. Keep the three in agreement. Composable development lifecycle plugin for AI coding CLIs. Expert tools for every development moment -- independently useful, optionally combined. Dependencies: bash, claude CLI.
- Deletes or force-pushes
In this repository23 other files it gives its agents.
- AGENTS.mdAGENTS.md
- adviseSkill
- brainstormSkill
- code-navigationSkill
- commitSkill
- create-agents-mdSkill
- create-prSkill
- design-buildSkill
- design-fixSkill
- designSkill
- handoverSkill
- hypothesis-debuggingSkill
- load-handoverSkill
- local-filesSkill
- planSkill
- report-checkSkill
- reviewSkill
- senior-reviewSkill
- shipSkill
- tddSkill
- verificationSkill
- visual-companionSkill
- workSkill
The file
We split this CLAUDE.md into the rules it gives an agent
6 rules. About 0 are common, 1 is distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 31
Every proposed feature must pass both conditions before design begins:
- Very specific: line 40
Create skills/<name>/SKILL.md with YAML front-matter containing name and description fields.
- Very specific: line 42
Do not use $() command substitution, variable assignment, if/else, or logic-bearing pipes in
!…blocks — Claude Code blocks these in … - Very specific: line 43
Do not reference CLAUDE_PLUGIN_ROOT from inside SKILL.md — it is unavailable in skill prompts.
- Very specific: line 56
If the agent searches the broader codebase (beyond files it already knows about), reference the code-navigation skill and include the Code …
- Very specific: line 63
Place the script in hooks/scripts/.
≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. Setup, tests, and PR expectations for contributors are in CONTRIBUTING.md; AGENTS.md is the short form for other coding agents. Keep the three in agreement.
Quiver
Composable development lifecycle plugin for AI coding CLIs. Expert tools for every development moment -- independently useful, optionally combined.
Dependencies: bash, claude CLI.
Architecture
.claude-plugin/— Plugin manifest (plugin.json) and marketplace listing (marketplace.json). Defines name, version, hook/skill/agent registration, and MCP servers.skills/— 22 skill directories (each containsSKILL.mdandTEST-PLAN.md). EachSKILL.mdcarries YAML front-matter (name,description) and is a self-contained prompt; thenamefield doubles as the slash invocation (/handover,/quiver:review, etc.). Exception:visual-companionalso containsserver.py, a runtime executable -- this is the only skill with non-prompt code.verification,tdd,code-navigation, andlocal-filesare internal reference skills read by other skills, not slash invocations./shipimplements nothing itself: it invokes/workthrough the Skill tool for the build and again for the fix round, andquiver:reviewfor the review, and continues when each one's last step tells it to follow the caller's continuation step.agents/— 22 agent definitions organized by category (review/,research/,debug/,workflow/). Agents are persona prompts spawned as subagents.hooks/—hooks.jsonregisters event hooks;scripts/holds implementations. Three hooks: PreToolUse (matcherBash, classifies a command as deny/ask/silence before it runs), PreCompact (fires before context compaction, saves a handover) and SessionStart (emits the skill routing block from every skill'swhen-to-use). The SessionStart matcher isstartup|resume|clear|compact, notstartupalone -- a session that began with/clear,/resume, or a compaction otherwise carries no routing block, and natural-language triggers stop reaching Quiver skills for the rest of that session.install.sh— Repo-root install script for the runtimes with no plugin manager. Its install targets are a six-column TSV heredoc insidetargets(); adding a runtime is one row and no other line. It symlinks into the user's clone (sogit pullis the update mechanism) and never removes or overwrites a path that is not a symlink resolving into the repo. It must stay at the repo root --.gitignoredrops/scripts/.- Storage — Handover files are written to
<project>/.claude/handovers/. The orchestration workspace -- the ledger, task briefs, and task reports written byskills/work/orchestrator.md-- is written to<project>/.claude/work/<plan-basename>/./shipkeeps its state in<project>/.claude/ship/:roadmap.md(one status per phase),decisions.md(numbered project decisions), and onephases/<id>-<slug>.mdper phase holding its stage, its pointers to the other skills' files, its measurements, its log, and its final report. Per phase it writes a spec to<project>/.claude/specs/<date>-<project>-<phase-id>-<slug>.mdin the/brainstormformat, and a plan and a fix plan to<project>/.claude/plans/<date>-<project>-<phase-id>-plan.mdand-fix-plan.mdin the/planformat (suffixed-2,-3when the path is taken; never overwritten); the review report lands in<project>/.claude/reports/because ship passes--output .claude/reports./brainstormwrites specs to<project>/.claude/specs/. Each skill that creates or first writes.claude/plans/,.claude/reports/,.claude/specs/,.claude/handovers/,.claude/work/, or.claude/ship/applies the keep-local rule inskills/local-files/SKILL.mdbefore its first write into the directory: unless the directory has tracked files or the project's instructions say to track it, the directory is appended to.git/info/exclude, which keeps it out of git in every worktree and changes no tracked file. A review written to a path the user chose with--outputor--set-outputis not covered;/shipapplies the rule to.claude/reports/itself before the review it starts with--output. The PreCompact hook carries the one bash copy of the rule; it cannot read project instructions, so tracked files under.claude/handovers/are its only signal to leave the directory alone. - Rules —
.claude/rules/containsskill-rules.md(hard rules and learned lessons for skills, formerlycommand-rules.md),review-agent-rules.md,cli-overlay-rules.md,agent-capability-rules.md, andreadme-structure.md. - External MCP — Context7 MCP server (
plugin.json>mcpServers) provides real-time library documentation lookups for review agents.
System Behavior
- Skills — Markdown files executed by Claude Code. Shell blocks (
!`…`) run inline and inject output into the prompt. Skills cannot call hook scripts directly; they duplicate save/prune logic as Claude instructions. - Hooks — Bash scripts invoked by Claude Code on lifecycle events. The PreCompact hook reads
transcript_pathfrom stdin JSON (viased), pipes the transcript toclaude -pfor summarization, and writes the result to the handovers directory. The PreToolUse hook readstool_nameandtool_input.commandfrom the same stdin JSON, splits the command on shell separators, and classifies each segment by its first word; a match prints ahookSpecificOutputobject with apermissionDecisionofdenyorask, and everything else prints nothing. - SYNC contract — The 8 handover section headings are defined in two places that must stay identical:
skills/handover/SKILL.md(the headings under itsSYNC:marker comment) and thePROMPT_PREFIXstring inhooks/scripts/pre-compact-handover.sh. Both files contain aSYNC:comment pointing to the other. If you change headings, change both --tests/hooks/test-handover-sync-contract.shcompares the two lists and fails on any difference in text, count, or order. Neither comment cites line numbers any more: they were hand-maintained, both had already drifted by one line, and a pointer that rots on every unrelated edit above it is worse than the path alone.
Development Standards
Feature Admission Test
Every proposed feature must pass both conditions before design begins:
- Standalone: It must be useful when invoked in isolation, without any other Quiver skill having been run.
- Composable: It must accept input from other skills and/or produce output that other skills can consume.
If a feature only works inside a pipeline (requires prior skill X to have run), redesign it to accept equivalent input from any source. If it produces no output that other skills can use and accepts no input from them, it is a utility -- still valid, but verify it belongs in Quiver rather than in the user's project.
Adding a Skill
- Create
skills/<name>/SKILL.mdwith YAML front-matter containingnameanddescriptionfields. Thenamefield must match the directory (and filename without.md) — it enables prefix-free access (e.g.,/handoverinstead of/quiver:handover). - Skills are prompts, not scripts.
!`…`blocks gather raw data; the rest of the file is a prompt that tells Claude how to interpret the data, make decisions, and take actions with its own tools. Never write a bare code block without accompanying prompt guidance — marketplace users need skills that work out of the box. - Do not use
$()command substitution, variable assignment,if/else, or logic-bearing pipes in!`…`blocks — Claude Code blocks these in marketplace plugins. - Do not reference
CLAUDE_PLUGIN_ROOTfrom insideSKILL.md— it is unavailable in skill prompts. (CLAUDE_PLUGIN_ROOTis still available insidehooks.jsonand hook scripts.) - Follow the hard rules and learned lessons in
.claude/rules/skill-rules.md. R12 is the one most easily missed on a refactor: everyAskUserQuestionquestion, button label, and option description, and every status line the skill prints, is written for a user who has never opened the file -- no frontmatter field names, no plugin vocabulary (gate, verdict, ledger, manifest), and one plain-language description per option. For structural patterns (role framing, decision trees, output format), read existing skills (skills/review/SKILL.md,skills/work/SKILL.md,skills/commit/SKILL.md) as examples -- adapt to the new skill's purpose, don't copy mechanically. - Every
!`…`block must exit 0 even when the target file or directory does not exist. Use the|| echo "NOT_FOUND: <path>"pattern (like the existing|| echo "NO_GIT"pattern for git commands) so the prompt receives actionable context instead of a silent failure. Reserve|| trueonly when no downstream logic needs to know the path was missing. - Give every migrated or new skill a
TEST-PLAN.mdbeside itsSKILL.md, holding the same five parts (Trigger / Setup / Expected behavior / Verification checklist / Known gotchas) under a# Test Plan -- /<name>heading. The five skills that are not slash-invoked --code-navigation,tdd,verification,visual-companion,local-files, the same list.claude/rules/readme-structure.mdexcludes from user-facing docs -- drop the slash:# Test Plan -- <name>.skills/<name>/TEST-PLAN.mdis the merge gate -- a skill without one is incomplete. It is a sibling rather than a section becauseSKILL.mdloads on every invocation and the test instructions do not need to.
Adding an Agent
- Create
agents/<category>/<name>.mdby hand, using an existing agent in the same category as the pattern. The maintainer's/create-agentskill scaffolds this interactively, but it lives in.claude/skills/, which is gitignored maintainer tooling like.claude/commands/and/scripts/-- it is on the maintainer's machines only, never in the repo, and nothing tracked may depend on it. - Agents are persona prompts, not skills. They define a specialist role that gets spawned as a subagent by skills or directly via the Agent tool.
- Agents live in
agents/<category>/<name>.mdwith YAML front-matter fields:name,description,model. - Category directories:
review/,research/,workflow/,design/,docs/, or custom. - The
agents/directory is registered inplugin.json'sagentsarray. - Assign the agent a capability profile and effort tier, add it to the
## Assignmentstable in.claude/rules/agent-capability-rules.md, and copy the profile's canonicaldisallowedToolsstring into the agent's frontmatter. By hand that is two edits; the maintainer's local/create-agentdoes both. - If the agent searches the broader codebase (beyond files it already knows about), reference the
code-navigationskill and include the Code Navigation Strategy block fromskills/code-navigation/SKILL.md. The dispatching skill must passlsp_availablecontext. - If the agent will be dispatched by
/quiver:review(anything underagents/review/or any cross-category agent listed inskills/review/SKILL.mdStep 2a Tier 2), follow the hard rules in.claude/rules/review-agent-rules.md. In particular, every review agent must carry the hypothetical-language ban rule (canonical text, or a domain-specific exemption variant for adversarial agents likestress-testerandsecurity-audit). - If the agent is a
/quiver:reviewStep 2 participant, give it a row in the## Dispatch Gatestable in.claude/rules/review-agent-rules.mdand restate that row inskills/review/SKILL.mdStep 2b. The split is the one the skill's "Adding future agents" block already draws: review-scoped agents underagents/review/are discovered automatically and always need the row, while a cross-category agent needs it only when you also add its path to the Tier 2 list in Step 2a. Agents outside that set --agents/debug/,agents/workflow/, and any research agent not on the Tier 2 list -- take no row, andtests/skills/test-review-dispatch-contract.shfails on a row naming an agent/quiver:reviewnever dispatches exactly as it fails on a participant with no row.
Adding or Modifying a Hook
- Register the event in
hooks/hooks.jsonwith"type": "command"(the only supported hook type). - Place the script in
hooks/scripts/. Use$CLAUDE_PROJECT_DIR(falls back topwd) for the project root. $CLAUDE_PLUGIN_ROOTis available inhooks.jsonand hook scripts.
Invariants
- Retention policy — Exactly the 3 most recent
.mdfiles in.claude/handovers/are kept. Both the handover skill and hook implement pruning independently. - Timestamp format — Filenames use
date '+%Y-%m-%d_%H-%M-%S'. This format is lexicographically sortable.
Testing
- All tests --
bash tests/run-all.shruns everytests/**/test-*.shand exits nonzero if any failed..github/workflows/tests.ymlruns it on every pull request and on every push tomaster. A failing test does not stop the run, so one invocation reports every contract that drifted. - Adding a test -- put it at
tests/<area>/test-<name>.sh;run-all.shdiscovers it with no edit. The contract is the one all existing tests honor: runnable underbashfrom any working directory, exit 0 on pass and nonzero on fail. A shared helper must not matchtest-*.sh-- name itlib-*.sh. - Skills — Run the slash invocation in a Claude Code session and follow
skills/<name>/TEST-PLAN.md. - Hook — Pipe test JSON to the script:
echo '{"transcript_path":"/path/to/transcript.json"}' | bash hooks/scripts/pre-compact-handover.sh - Agent capability contract —
bash tests/agents/test-capability-profile-contract.shchecks every agent'sdisallowedToolsandeffortfrontmatter against the profiles and assignments in.claude/rules/agent-capability-rules.md, and fails when a copy drifts, an agent is missing from the table, or a table row has no file. - Destructive command guard —
bash tests/hooks/test-destructive-guard.shfeeds real PreToolUse payloads tohooks/scripts/pre-tool-use-guard.shand asserts the decision each one should produce, including the cases that must stay silent. Beyond the tier, it asserts the reason text (an empty or wrong target makes the prompt worthless), byte-exact silence rather than trimmed silence, that a target carrying quotes still emits parseable JSON, and that a 1000-target delete stays inside the 5s timeout inhooks/hooks.json. - Review dispatch contract --
bash tests/skills/test-review-dispatch-contract.shbinds the canonical## Dispatch Gatestable in.claude/rules/review-agent-rules.mdto its runtime copy: the Step 2b prose inskills/review/SKILL.md. It fails when that copy's class list or mode list drifts from the canonical row, when a file underagents/review/or either Tier 2 research agent has no row, and when a canonical row names no agent file in dispatch scope. - Global Constraints contract --
bash tests/skills/test-global-constraints-contract.shreads the real skill files and asserts thatskills/plan/SKILL.mdstill carries a line reading exactly## Global Constraints, thatskills/work/orchestrator.md,skills/work/SKILL.md,skills/review/SKILL.md, andskills/ship/SKILL.mdstill name the section, and that both degrade-cleanly clauses survive -- the orchestrator'somitssentence in the same paragraph as the block, andno noteinside a## Step 1.8heading it also pins to exactly one occurrence. It is the only thing that catches a heading rename in the producer that was not mirrored into the consumers. It also fails when Step 1.8 names a plans directory (plans/, "plans directory") or a recency pick ("newest", "most recent", "latest") on any line, a negation included: constraints come from--planalone, because a plan file carries no branch and the newest one can belong to another open branch. Each term is matched alone, since requiring the directory and the pick on one line let a reworded rule through. - Install script --
bash tests/install/test-install-script.shrunsinstall.shunder a throwawayHOMEfrommktemp -dand asserts the symlink is created, a second run changes nothing, a real directory at a destination is skipped with the run exiting nonzero,--uninstallremoves only links resolving into the repo,XDG_CONFIG_HOMErelocates the OpenCode destination, and every target row has exactly six tab-separated columns. That last assertion is what catches an editor turning the table's tabs into spaces. - Manifest parity --
bash tests/manifests/test-manifest-parity.shbinds.claude-plugin,.cursor-plugin, and.codex-pluginto one version string, asserts the Cursoragentsarray equals the Claude one and that Claude registers every file underagents/, and checks the README version badge.scripts/release.shapplies the bump, this test enforces it -- the helper is gitignored and cannot be the gate. - Git log fallback contract --
bash tests/skills/test-git-log-fallback.shasserts that everygit loggather block underskills/falls back toNO_COMMITS. In a repository made withgit initand no commit yet, every other gather block exits 0 andgit logexits 128, so agit logblock falling back toNO_GITmade Step 0 report "No git repository detected" inside a real repository --/commitcould not make a new project's first commit. It also pins the token's two readers, both in/work. The Phase 2.5 line sends aNO_COMMITSrun that Phase 2 gave no first commit down the sequential path: the working branch does not resolve before a first commit, so every worktree subagent would come back BLOCKED. The Phase 2 section makes an empty first commit withgit commit --allow-empty --onlybefore branching, and on a non-default branch creates the main branch the user picks withgit branch <name>:git checkout -bon a branch with no commit only renames it, the default branch never exists, and/ship's review against it stopped on a base branch that was not found.--onlywith no paths keeps anything already staged out of that commit. Finding nogit logblock is a failure, so a reworded block form cannot leave the test asserting nothing. - Component count contract --
bash tests/inventory/test-component-count-contract.shbinds the tree to every hand-written component count and component list: the three rows ofREADME.md's inventory table and its## Hookstable, theskills/andagents/bullets in this file's Architecture section,.opencode/README.md's skill sentence, its#### Available skillstable, its agent sentence and its@namelist, and the scope note in.claude/rules/agent-capability-rules.md. The user-facing skill number is derived rather than pinned -- the directory count minus the names.claude/rules/readme-structure.mdexcludes from user-facing docs -- and the test binds its own copy of that exclusion list back to the rule text in both directions, so a name dropped locally shrinks the expectation to match whatever the docs say instead of failing. Each number is asserted to occur exactly once: a reworded sentence leaves the grep testing nothing, and a second copy is a drift partner no assertion covers. Each list is compared by name as well as by length, since a length check alone stays green when one entry replaces another. The inventory table insidereadme-structure.mdis a formatting example, not a count of this repo, and is deliberately not a site. - Handover SYNC contract --
bash tests/hooks/test-handover-sync-contract.shextracts the 8 H2 headings under theSYNC:marker inskills/handover/SKILL.mdand the 8 insidePROMPT_PREFIXinhooks/scripts/pre-compact-handover.sh, then asserts both lists are 8 long and identical in order. The hook side is scoped to the single-quoted string so a##shell comment cannot join the list, and the skill side stops at the---rule that closes the block so the file's own later headings stay out. Both sides are scoped by a terminator rather than by a count on purpose: an extractor that stops after 8 matches cannot report a 9th, so a section appended to the skill alone would pass green against a hook still carrying 8. It also asserts each file still carries aSYNC:comment naming the other. - Handover keep-local behavior --
bash tests/hooks/test-handover-local-exclude.shrunshooks/scripts/pre-compact-handover.shagainst throwaway repositories, withclaudeandtimeoutreplaced by stubs first onPATHso no model call is made, and with global and system git config switched off so a developer's own excludes file cannot pre-ignore the directory. It asserts that a first run in a repository with no tracked handovers adds.claude/handovers/to.git/info/excludeexactly once, writes a handover through the stub, and leavesgit check-ignorereporting the directory ignored; that a second run adds nothing; that a repository with a committed file under.claude/handovers/gains no entry; that an exclude file whose last line has no newline keeps that line intact and gets the entry on its own line; that a project directory below the repo top getsapp/.claude/handovers/exactly once after two runs, since an entry without the prefix never matches and every compaction would append again; that a.gitignorecarrying!.claude/handovers/still leaves exactly one entry after two runs, because that negation outranks the exclude file andgit check-ignorekeeps reporting the directory not ignored; that a.claudereached through a symlink, wheregit check-ignoreexits 128, gains no entry; and that outside a git repository the hook exits 0 and creates no.git. The hook runs from a working directory other than the project, so a git call that reads the cwd instead of the project dir fails the assertions. - Task interfaces contract --
bash tests/skills/test-task-interfaces-contract.shbinds the**Provides:**field inskills/plan/SKILL.mdto its one consumer,skills/work/orchestrator.md. On the producer side it asserts the label is still written at the start of a line -- the consumer extracts on a line-anchored label, so the same text buried mid-sentence documents the field without making it extractable -- that Step 6 still carries a### Check 8:heading, and that both of Step 6's check-count strings read 8, with a separate tripwire for a stale7 checksbecause a half-finished renumber leaves one count behind while both positive assertions still pass. On the consumer side it asserts the orchestrator still names**Provides:**and## Interfacesverbatim, and that its degrade-cleanly clause still says it omits the## Interfacesheading. That clause is matched on a phrase naming the heading rather than on the paragraph, because Section 2 Part 1 item 1 already carries anomitsclause for Global Constraints -- a looser match is satisfied by that one and stays green after the interface clause is deleted. - R10 routing contract --
bash tests/skills/test-when-to-use-contract.shbinds R10 in.claude/rules/skill-rules.mdto every skill's frontmatter and to the blockhooks/scripts/session-start-auto-dispatch.shbuilds from it. It asserts the four R10 exemptions are still named in the rule text -- by name and by count, because dropping a name from the test's ownEXEMPTlist removes its assertion instead of failing it -- that every non-exempt skill'swhen-to-use:is a single-line double-quoted string carrying its own slash name and a quoted user utterance, and that every exempt skill omits the field. Then it runs the real hook and asserts the routing block has a non-empty entry for each skill that is neither exempt nordisable-model-invocation: true, and none for either excluded set -- a block scalar parses to nothing there, which silently drops a skill from routing, and a routing entry forhandoverwould wire its--clearand--clear-alldeletes into silent auto-invocation. The two exclusions are different failures: an exempt skill has nowhen-to-use:at all, while a disabled skill keeps one and stays under R10's format rules, so the hook filters it out at the block instead. That branch also fails when no disabled skill is left to check, since an assertion about an empty set passes on a hook that dropped the filter. That branch cannot assert the field it filters on: ahandoverthat lostdisable-model-invocation: truefalls through to the entry assertion and passes, so the flag is pinned by name in a separate check after the loop. - Verification contract --
bash tests/skills/test-verification-contract.shbindsskills/verification/SKILL.mdto its six consumers: it asserts the reference skill isuser-invocable: falsewith no shell block, that every consumer names the file, that the subagent restatement inskills/work/orchestrator.mdis byte-identical to the canonical line --/shiphands execution to/work, dispatches no implementing subagent, and carries no copy -- that the orchestrator declares theTESTSevidence grammar, that/ship's metadata namestest_commandandbuild_command, and that neither of/ship's former stack tables has returned. It also pins the producer's shape the consumers read -- each of its four headings exactly once and exactly six stack rows, assertsskills/verification/TEST-PLAN.mdis present, non-empty and carries a Trigger line -- sweeps all six consumers for a re-grown table, and checks that the exclusion list in.claude/rules/readme-structure.mdnames the skill; the R10 exemption is enforced bytests/skills/test-when-to-use-contract.sh, not here. Every assertion reads the live skill files; no fixture is written. - Finding verification contract --
bash tests/skills/test-finding-verification-contract.shbindsagents/review/finding-verifier.mdto its one consumer,/quiver:reviewStep 3.3. It asserts the agent's frontmatter carriesisolation: worktree, that both files carry the three verdict tokens-- CONFIRMED,-- REFUTED, and-- UNVERIFIABLE, thatskills/review/SKILL.mdnames the report linesVerification: confirmed --andVerification: unverified --and therefuted-by-verificationclassification and has exactly one## Step 3.3 -- Verify Findingsline, and that the agent's hypothetical-language ban hashes equal to the copy inagents/review/logic-reviewer.md. It also binds the report lines to their two readers:skills/work/SKILL.mdmust nameVerification: confirmed(when the report carries the lines, 4c matches any other finding to a plan task by its ID or title only, never by file path) and must say 4d skipsfinding-verifier, andskills/report-check/SKILL.mdmust nameVerification:(a rewrite keeps the line). It pins--run-checksin the review's argument hint, its Step 0.5 parse, and the Step 3.3 line that allows a branch review to run project code only with that flag, and on/work's printed review line and/ship's printed review line and review invocation: a rename on one side leaves/workpassing a flag the review ignores, and every behavior check silently goes read-only. A verdict token renamed on one side raises no error: Step 3.3 recognizes no verdict, every finding stays in the report unmarked, and the report reads like one whose findings all held up. Every assertion reads the live files; no fixture is written. - TDD contract --
bash tests/skills/test-tdd-contract.shbindsskills/tdd/SKILL.mdto its five consumers: it asserts the reference skill is namedtdd, isuser-invocable: falsewith no shell block, that its four body headings and its restatement heading each appear exactly once and thatskills/tdd/TEST-PLAN.mdis present, non-empty and carries a Trigger line, and that it carries both evidence line forms and theno red evidencereason; that the orchestrator,/ship, and/hypothesis-debuggingname the file anywhere, and that/planand/workname it in the build-step sentence itself -- both carry a second mention in aTEST-PLAN.mdchecklist or a gotchas line, so a whole-file grep stays green after the pointer is deleted; that the restatement copy inskills/work/orchestrator.mdis byte-identical to the canonical line --/shipdispatches no implementing subagent and carries none; that the orchestrator's return contract carries theTDD |red and skipped forms, that the orchestrator namesno red evidenceand tells the subagent the red run is not one of the three attempts, and that/work's 5d summary carries the tally; that the replaced text has not grown back -- the five-step list in/planthat put exact test code in the plan; and that thereadme-structure.mdexclusion list and the R10 test'sEXEMPTlist both nametdd; thewhen-to-useabsence and R10's own text are enforced bytests/skills/test-when-to-use-contract.sh, not here. It also holds the producer to ASCII. Every assertion reads the live skill files; no fixture is written. - Shell-block syntax contract --
bash tests/skills/test-shell-block-syntax.shscans every markdown file underskills/for the inline shell-block pattern written where the loader runs it by accident. The loader executes a!followed by a backtick-delimited span before any step logic, and markdown nesting does not hide it, so a sentence documenting the syntax executes it --/design-fixshipped that way in v1.24.0 and aborted before Step 0. The rule is occurrence-scoped, not line-scoped: the safe prose form is stripped first, then any remaining occurrence with a non-whitespace prefix fails, as does a second occurrence on one line. That last clause is the only thing covering a line that carries both forms, which is the shape a line-scoped filter lets through. A fourth section runs the same rule over two inputs it must accept and two it must reject, because a rule that stopped matching would report a clean tree forever. It does not catch a prose mention that markdown rewrapping pushed to column 0; no grep separates that from the real blocks that legitimately start a line, and the test's header says so. - Ship pointer contract --
bash tests/skills/test-ship-plan-pointers.shextracts every`skills/<name>/SKILL.md` Step <N>pointer inskills/ship/SKILL.md, for any skill ship points into, and asserts that the named skill file exists and has a line starting## Step <N> --for each one; today that isskills/plan/SKILL.mdSteps 2.5, 3, 4.5, 5, 6, 6.5 and 7,skills/brainstorm/SKILL.mdStep 5, andskills/review/SKILL.mdSteps 1 and 4. The step number stops at the first character that is not a digit or a decimal part, so ship'sStep 4a item 3is checked asStep 4. Ship does not copy another skill's formats, checks, or dispatch prompts. It names the step, and the model reads that section when it gets there, so the pointer is the whole interface. Before this test nothing pinned those headings: a step renamed or renumbered with no edit in ship leaves ship naming a section that does not exist, and the model then improvises the format or skips the check with no error anywhere. The heading match keeps the trailing--, soStep 6is not satisfied by## Step 6.5 --. Finding no pointer at all is a failure, because a reworded pointer form would otherwise leave the test asserting nothing, and every dangling step is reported with each ship line that names it. Its first section runs the rule on a throwaway fixture naming`skills/brainstorm/SKILL.md` Step 6, a step that file does not have, and fails unless that run fails on that pointer: a rule that stopped matching pointers outside/planwould otherwise pass the real file forever.SHIP_FILEoverrides the ship path for running the rule against a fixture. - Ship contract --
bash tests/skills/test-ship-contract.shbindsskills/ship/SKILL.mdto the files it writes and the skills it invokes. It asserts that/work's Phase 5d inskills/work/SKILL.mdno longer sayscontinues into its verification(the finding verification contract pins thefollow its continuation stepthat replaced it): the Skill tool does not return, so that line is the only thing that carries the turn back into the skill that loaded/work, and ship loads/worktwice, for the build and for the fix round, so a line naming one caller's step sends the fix round into the wrong stage. It asserts that ship names.claude/ship/roadmap.md,.claude/ship/decisions.md,.claude/ship/phases/,.claude/specs/, andskills/local-files/SKILL.md; that its# State Filessection, read to the next H1 outside a fence so a template line cannot end it early, carries each of the eleven roadmap and phase-file headings as a line of its own, with## Goalexactly twice because the roadmap and the phase file both carry it, each of the eleven phase-file frontmatter fields fromphase:tofix_rounds:at the start of a line, and theD<n>. <decision> -- <reason> (<user|agent>, <date>, phase <id>)entry format ofdecisions.md; and that the table inskills/local-files/SKILL.mdhas a.claude/ship/row naming/ship. The next/ship, often in a fresh session, resumes from those files alone, so a heading renamed in one sentence and not in the template leaves a resume reading a section nothing writes. It pins the strings the stage handoffs run on:${CLAUDE_SESSION_ID}andcache_read_input_tokensfor the context measurement,/work <plan path> --autoandPhase 5dfor the build, with/workstill carrying a#### 5d --heading;quiver:reviewand--output .claude/reportsfor the review, which must never be passed--terminal, since that writes no report for the fix stage to read; andVerification: confirmed,review_source, andfix_roundsfor the fix round, with/workstill naming`review_source`. It pins the done stage's two reads --review_source:, through which it finds the first report, and thefix: not fixed:Log line -- inside## Stage: done, the backtickedfix: not fixed:writer inside## Stage: fix, and/work'sstill failing after 3 attempts -- acceptedline: the review after the fix round covers only the changes since the first report, so a Critical the fix round left unfixed is only in that report, and a rename on either side lets the phase godonewith it open. It pins the two lines that keep one/shipgoing -- Init 4 and the done stage each print aStarting phaseline and re-enter## Step 1 -- Routeas a bare/ship-- and fails when Init still ends onRun /ship to start it.: reworded into a printed command, either line ends the run after every phase with no error, and re-entering with the run's arguments reads the project description as an unknown phase id. It also fails when ship names.claude/reports/ship-, the separate report the one-run ship wrote, because the phase file now holds the final report. Every assertion reads the live files; no fixture is written. - Commit rules contract --
bash tests/skills/test-commit-rules-contract.shbinds the# Commit Message Generationsection ofskills/commit/SKILL.mdto its two consumers,skills/work/SKILL.mdandskills/work/orchestrator.md. It asserts the producer carries that heading on exactly one line and that the section, read from the heading to the next---rule, still sayssubject-onlyand still carries the no-attribution rule; that each consumer namesskills/commit/SKILL.mdand the heading and no longer saysConventional Commits message, the instruction it carried before, because a consumer with its own rule set drifts from the producer; and that the orchestrator still names## Commit Messages, the heading it copies the section under into every task brief, and that a line starting6.-- subagent prompt item 6 -- names it too. A heading renamed in the producer, or a rule moved out of the section, raises no error: the subagents commit without it, since the brief is the only text a worktree subagent has. Every assertion reads the live files; no fixture is written. - Local files contract --
bash tests/skills/test-local-files-contract.shasserts that no file underskills/namesdocs/brainstorms, the spec path before specs moved to.claude/specs/: a skill still naming it writes a spec nothing reads, or looks for one where nothing writes, and neither raises an error. It asserts thatskills/local-files/SKILL.md, the one home of the keep-local rule, isuser-invocable: false, carries no inline shell block, and has the## Keep-Local Ruleheading on exactly one line; that each of the eight writers --/plan,/brainstorm,/quiver:review,/senior-review,/handover,/quiver:design,skills/work/orchestrator.md, and/ship-- carries the sentence telling the model to apply the keep-local rule inskills/local-files/SKILL.md; and thathooks/scripts/pre-compact-handover.shstill namesinfo/excludeandls-files, the commands of its bash copy. It does not run the hook;tests/hooks/test-handover-local-exclude.shdoes. A new writer of a development directory goes into the test'sCONSUMERSlist in the same change. Every assertion reads the live files; no fixture is written. - Review behavior eval (costs money, on demand) --
bash tests/eval/run-review-golden.shbuilds a throwaway git repo fromtests/eval/review-golden/fixture/, runs a real/quiver:review --base main --run-checksagainst it throughclaude -p, and grades the report's## Findingssection againsttests/eval/review-golden/expectations.txt. The fixture plants three defects a fast review should find -- an off-by-one inbulk_discount, ashell=Truecommand injection inexport_snapshot, a helper that reimplementsutils.chunked-- and three baits it must not report: a real defect in a file the diff does not touch, a bounded loop carrying a "could be slow for large N" comment, and correct-but-odd tested code. Two more baits are cleared only by checking the code: a README sentence that the fixture'sDECISIONS.md, named in itsCLAUDE.md, requires word for word, and a comment inapp/ledger.pywhose byte counttools/measure_ledger.pyreproduces. Their~expectations fail when the regex appears under## Findingsand print, as information only, whether it appears on arefuted-by-verificationline under## Filtered Findings. Two fast-mode runs measured $3.66 and $4.34 on 2026-09-13, which is why the runner's--max-budget-usdis 8 rather than 5; a--deepprobe would need more again. Those two runs graded the three planted defects identically and their severities differently -- the injection came back High once and Critical once, neither time with a stated reason. The cause was inagents/review/security-audit.md: its Critical line mixed a category test ("Injection, auth bypass, ...") with a reachability test ("Actively exploitable"), and a sink nothing calls satisfies one and fails the other. The reachability paragraph added below that rubric settled it -- the two runs after it both returned High and both named the reason, the second one correctly pointing out that an operator who can invoke the CLI already has a shell. One expectation now pins that tier, because a pinned tier is the only thing that catches a return to flapping. Everything else still pins file and function names: run-to-run the agent credited with a finding moves, the heading case moves, and the count of unplanned-but-real findings moves. It spends real API credit on every run, so it is named outside thetest-*.shglob and neithertests/run-all.shnor.github/workflows/tests.ymlcan discover it. Run it before a release and after any change to the review agents, the dispatch gates, or the model/quiver:reviewruns on. A missed planted defect is a result to report, not a fixture bug to patch. - Stack researcher eval (costs money, on demand) --
bash tests/eval/run-stack-researcher-eval.sh [--keep]runsclaude -pwith--plugin-dirpointing at this repo, in an empty temp project, and has it dispatchquiver:stack-researcheronce with three pre-build questions for a TypeScript CLI that captures a screenshot from an Android Automotive head unit through adb. It grades ten checks: each### Q<n> -- <status>heading uses a status from the agent's grammar; Q1 namesmaxBuffer, states its 1 MiB default (a product such as64 * 1024 * 1024does not count), setsencodingto buffer, and usesexec-out; Q2 quotes the typescript-eslint peer range fortypescript; Q3, which asks what a customer's own head unit build prints, comes backneeds-measurement; and the project root is still empty after the run. The Q2 expectation is read from the npm registry at run time withnpm view typescript-eslint@latest peerDependencies.typescript, not pinned, because the range moves with every typescript-eslint release and a pinned copy would fail a correct answer. The script refuses to start whennpm,claude, orpython3is not on PATH. It does not look atadb devices: no maintainer has the Q3 head unit, so an attached phone or emulator cannot answer it, and the run's allowlist keepsadbout of the agent's reach. It exits nonzero on any failed check, keeps the workspace on a failure (--keepkeeps it on a pass too), and prints one cost line. It runs indontAskmode with an allowlist of Agent, WebSearch, WebFetch, context7 andnpm view: the agent reads open-web pages, andcurlandghstay off the list because one can post a local file to any host and the other can call a write endpoint with the maintainer's token. The agent reads pages through WebFetch instead and names the deniedcurlunder## Gaps.dontAskalso runs whatever a loaded settings file allows, so the run passes--setting-sources project,local: user settings stay unloaded and a maintainer's own allow rules, such asBash(curl *), cannot widen the list, while project and local settings come from the empty temp root and add nothing. The 2026-10-03 run with these flags scored 10 passed, 0 failed at 2 turns, 60.0s and $0.4566, under the runner's--max-budget-usd 5, with an Android phone and an emulator attached; its twocurlcalls were denied and named under## Gaps. No call in that run matched a user allow rule, so it does not show the user-settings exclusion at work; that rests on the permissions docs. It spends API credit on every run, so like the review eval it is named outside thetest-*.shglob. Its free counterpart,tests/eval/test-stack-researcher-eval.sh, is inside the glob and runs intests/run-all.shand CI: it drives the script against fakeclaude,npm, andadbexecutables and asserts that the run usesdontAskwith exactly that allowlist and without user settings, that a clean report passes every check, that a flawed report fails exactly the checks it breaks, that an empty result is reported as blocked, that a missingnpmstops the run beforeclaudeis called, and that an attached device does not. Run the paid eval after any edit toagents/research/stack-researcher.md. A failed check is a measurement of the agent: fix the agent file, never the check. - Syntax check —
bash -n hooks/scripts/pre-compact-handover.shandbash -n hooks/scripts/pre-tool-use-guard.sh - All skills — Run each
/quiver:*slash invocation in a Claude Code session and verify the expected output/side-effects defined in itsTEST-PLAN.md.
Known Gotchas
.gitignore'sdocs/entry has no leading slash, so it matches adocs/directory at any depth. A test placed attests/docs/is silently untracked:tests/run-all.shfinds it locally and CI never sees it, which is green-on-nothing rather than a failure. The/scripts/entry two lines below carries a comment about its leading slash for the opposite reason. Name a test directory for its subject --tests/inventory/,tests/skills/-- and checkgit check-ignorebefore writing the file./quiver:reviewand/quiver:designare written namespaced in user-facing docs on purpose, and a "cleanup" pass that shortens them to/reviewand/designbreaks both commands silently. Claude Code bundles acode-reviewskill whose documented alias is/review, and the docs state that a skill "replaces the bundled command, but not its aliases" -- so a plugin skill namedreviewis unreachable as/reviewby design. Measured on 2.1.267 in a throwaway directory with--plugin-dirpointing at this repo:/reviewran the bundled reviewer (its own "Phase 0" wording, none of this skill's!git rev-parseblocks, 0 subagents where fast mode fans out to 5),/designran Claude Design's canvas skill ("/design consent", "/design revoke"), and/work-- the control, which collides with nothing -- ran Quiver's own gather-context blocks and printed itsNo git repository detectedline./designwas the surprise: it is not on any published bundled-alias list, and theclaude-code-guideagent asserted no bundleddesignexists. The probe is the reason that claim did not ship. The other 19 skill names collide with nothing. Renaming the two skills was considered and declined --reviewalone is 21 files and 57 user-facing mentions, and/quiver:<name>always resolves -- so the namespace is the fix, and these two names must stay namespaced wherever a user is told what to type. Overlay docs are the exception:.opencode/and.codex-plugin/target CLIs with no bundled/review, they keep the bare form, andtests/opencode/test-bootstrap-injection.shfails when the workflow chain there is namespaced because it parses those entries as skill names.- The PreCompact hook pipes transcript + prompt to
claude -pvia stdin to avoidARG_MAXlimits -- do not refactor to use command-line arguments. - Hook timeout is 180s (
hooks.json), but the innerclaude -pcall hastimeout 150-- the 30s gap prevents zombie processes. ls -1rin the prune loop relies on filenames being timestamps for correct sort order -- non-timestamp filenames break pruning.- The hook silently exits 0 on any failure (missing transcript, empty claude output) -- check handover directory contents to verify it ran.
- Changing the timestamp format (
date '+%Y-%m-%d_%H-%M-%S') breaks lexicographic sort ordering of existing handover files. - The PreToolUse guard is an accident brake, not a security boundary. It reads the literal command string before the shell ever sees it, so shell escapes, variable expansion, aliases, base64, and any deliberate obfuscation are outside its threat model by construction. It exists to catch a slip, and a determined bypass is not a bug in it. This bullet is the one home for that fact;
hooks/scripts/pre-tool-use-guard.shpoints here rather than repeating it. - Three invariants inside the guard are documented at the code that depends on them rather than restated here: first-word segment anchoring (the Segmentation block),
set -ufwithoutset -e(the file header), and masking JSON escapes before extracting the command (abovejson_field). Read those comments before editing the classifier -- none of the three survives a "simplify this" pass that has not read them, and the first two each protect against a specific defect this repository has already shipped once. - The guard stops classifying at the first heredoc or herestring opener, and drops any double-quoted span that contains a newline, before it segments. Both are text being written rather than commands being run, and the first-word anchoring cannot tell them apart once a newline is a separator --
cat <<EOF > notes.mdfollowed by a line readingrm -rf /is a documentation edit. The concession is one-directional on purpose: it costs a missed prompt for anything destructive that follows a heredoc in the same command, and it removes a wrongdeny, which is the one decision a user cannot override. - Lesson LO3 in
.claude/rules/cli-overlay-rules.mdsays to prefer no automatic hook over a frequently-firing one with a fragile guard. The PreToolUse guard fires more often than theStophook that lesson killed and is still the right call: the killed hook ranclaude -pon every fire behind a cooldown that only reset on success, while this one issedplustr, holds no state, has no cooldown to be fragile, and exits 0 on every path. LO3 constrains expensive hooks with stateful guards, not cheap stateless ones. - Cursor needs no Quiver install of its own when Claude Code has one. It discovers skills by scanning a fixed root list --
.cursor/skills/,.cursor/skills-cursor/,.cursor/cloud-skills/,.cursor/plugins/,.claude/skills/,.claude/plugins/,.codex/skills/,.agents/skills/-- hardcoded in the app bundle with no setting to disable it. A Claude Code install lands in~/.claude/plugins/and is therefore visible in Cursor; a Codex install lands in~/.codex/plugins/, which is NOT on that list, so it is not. - The Cursor-only install is a clone at
~/.cursor/plugins/local/quiver, andgit pullthere is the update. Cursor'sloadUserLocalPluginsreads every entry of~/.cursor/plugins/local/, and for a symlink it resolves the target and rejects it when the target sits outside that directory -- so a link into a clone kept elsewhere loads nothing, andinstall.sh, which only ever symlinks, still has no cursor row. Measured on Cursor 3.17.21 against an isolatedHOME: the clone logsloadUserLocalPlugin quiver loadedand registers the plugin's context7 MCP server, the symlink logsrejected: symlink target ... is outside ...and0 plugins loaded. The log is<user-data-dir>/logs/*/window1/exthost/anysphere.cursor-agent-exec/Cursor Plugins.log. This route needs no marketplace and no sign-in, and takes the branch from the remote's HEAD, so a repo whose default branch ismasterneeds nothing special. .cursor-plugin/plugin.jsonstill matters: it is the first manifest path Cursor looks for (.cursor-plugin/plugin.json, then.claude-plugin/plugin.json, thenplugin.json), for the local clone and for the Plugins panel's git import alike. The panel also accepts.cursor-plugin/marketplace.jsonor.claude-plugin/marketplace.jsonas a marketplace manifest, but that route indexes the repo through Cursor's servers, so the clone is the one documented in the README.hooks/hooks.jsonships to Cursor as well (.cursor-plugin/plugin.jsonsets"hooks": "./hooks/hooks.json"), so Cursor receives the PreToolUse block whether or not it implementspermissionDecision. Codex has nohookskey in its manifest and is unaffected.claude plugin validate ./ --strictfails in plugin-manifest mode (the mode chosen when.claude-plugin/marketplace.jsonis absent) on a warning thatCLAUDE.mdat the plugin root is not loaded as project context. This predates the guard -- it reproduces onmasterat6f74e11-- and marketplace-manifest mode, which is how the plugin actually ships, passes clean.- The
## Global Constraintsheading is one hand-synced string across four files:skills/plan/SKILL.mdwrites the section,skills/work/orchestrator.mdreproduces it into every task brief,skills/review/SKILL.mdStep 1.8 extracts it, and the plan stage ofskills/ship/SKILL.mdwrites it into every phase plan and checks for it on read-back. Renaming it in one file and not the others raises no error --/workwrites briefs carrying no constraints,/quiver:reviewreportsN/A,/shipwrites a heading/workno longer extracts -- and each is indistinguishable from a plan that derived none.tests/skills/test-global-constraints-contract.shis the only gate on that. It pins exactly the files listed above, so a fifth consumer --/design-buildor anything else that reads the block -- must be added to that test in the same change that adds the consumer, exactly astests/skills/test-review-dispatch-contract.shrequires a row per dispatched agent. A consumer with no assertion is a consumer the rename case silently skips. **Provides:**and## Interfacesare not the same kind of string, and only one of them is a cross-file contract.**Provides:**is hand-synced across two files:skills/plan/SKILL.mdwrites the label into the plan andskills/work/orchestrator.mdextracts on it.## Interfacesis written by the orchestrator into task briefs and read by nothing else in the tree, so it has no drift partner -- it is pinned only so a rename does not silently change what briefs look like. Renaming**Provides:**in one file and not the other raises no error:/workthen writes briefs with no interface block, and that is indistinguishable from a plan whose tasks share no symbol and so declared none.tests/skills/test-task-interfaces-contract.shis the only gate on that, and it pins exactly those two files, so a future third consumer -- anything else that reads the declarations -- must be added to that test in the same change that adds the consumer, the same rule the## Global Constraintsgotcha above states.- The resolution table and the evidence rule have one home,
skills/verification/SKILL.md, and/work,/ship,/design-build,/hypothesis-debugging, and/quiver:review(Step 3.3) read it at runtime by naming that path at their verification step, the same way/worknamesskills/work/orchestrator.md. The only text copied out of it is the one-line### Subagent restatement, pasted verbatim into the subagent prompt inskills/work/orchestrator.md-- the only implementing-subagent prompt in the tree, since/shiphands execution to/workand itself dispatches only read-only agents, researchers before the plan is written and a plan reviewer after -- because a subagent working in a worktree cannot read the plugin's skill files and has only the prompt it was given. A copy that drifts raises no error -- the subagent is simply no longer told that a zero-test run, an unreturned command, or a remembered result is not a pass -- and the verification contract test is the only thing that catches it. The same test is the only gate in the other direction: a consumer that pastes the stack table back in passes every other test, and its copy then drifts from the producer's with nothing raising an error. - The test-first cycle and the
red:evidence form have one home,skills/tdd/SKILL.md, and/plan,/work, and/hypothesis-debuggingread it at their build step by naming that path, the same way they readskills/verification/SKILL.md;/shipnames it where it hands the build to/work. The only text copied out of it is the one-line### Subagent restatement, pasted verbatim into the subagent prompt inskills/work/orchestrator.md-- the only implementing-subagent prompt in the tree, since/shiphands execution to/workand itself dispatches only read-only agents -- because a worktree subagent cannot read the plugin's skill files. A copy that drifts raises no error -- the subagent is simply no longer told that a test it never saw failing is not red evidence -- andtests/skills/test-tdd-contract.shis the only thing that catches it. A return with noTDDline isskipped: no red evidence: recorded, counted in the/work5d tally, and never a merge block. That is deliberate -- no run has yet shown an implementer skipping the test, and the count is the measurement that would justify a gate.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

