agentleFS
yagizdoyagizdo/quiver/CLAUDE.md

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

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
  1. Distinctive:

    Every proposed feature must pass both conditions before design begins:

    line 31
  2. Very specific:

    Create skills/<name>/SKILL.md with YAML front-matter containing name and description fields.

    line 40
  3. Very specific:

    Do not use $() command substitution, variable assignment, if/else, or logic-bearing pipes in !… blocks — Claude Code blocks these in …

    line 42
  4. Very specific:

    Do not reference CLAUDE_PLUGIN_ROOT from inside SKILL.md — it is unavailable in skill prompts.

    line 43
  5. Very specific:

    If the agent searches the broader codebase (beyond files it already knows about), reference the code-navigation skill and include the Code …

    line 56
  6. Very specific:

    Place the script in hooks/scripts/.

    line 63

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
CLAUDE.md

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 contains SKILL.md and TEST-PLAN.md). Each SKILL.md carries YAML front-matter (name, description) and is a self-contained prompt; the name field doubles as the slash invocation (/handover, /quiver:review, etc.). Exception: visual-companion also contains server.py, a runtime executable -- this is the only skill with non-prompt code. verification, tdd, code-navigation, and local-files are internal reference skills read by other skills, not slash invocations. /ship implements nothing itself: it invokes /work through the Skill tool for the build and again for the fix round, and quiver:review for 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.json registers event hooks; scripts/ holds implementations. Three hooks: PreToolUse (matcher Bash, 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's when-to-use). The SessionStart matcher is startup|resume|clear|compact, not startup alone -- 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 inside targets(); adding a runtime is one row and no other line. It symlinks into the user's clone (so git pull is 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 -- .gitignore drops /scripts/.
  • Storage — Handover files are written to <project>/.claude/handovers/. The orchestration workspace -- the ledger, task briefs, and task reports written by skills/work/orchestrator.md -- is written to <project>/.claude/work/<plan-basename>/. /ship keeps its state in <project>/.claude/ship/: roadmap.md (one status per phase), decisions.md (numbered project decisions), and one phases/<id>-<slug>.md per 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>.md in the /brainstorm format, and a plan and a fix plan to <project>/.claude/plans/<date>-<project>-<phase-id>-plan.md and -fix-plan.md in the /plan format (suffixed -2, -3 when the path is taken; never overwritten); the review report lands in <project>/.claude/reports/ because ship passes --output .claude/reports. /brainstorm writes 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 in skills/local-files/SKILL.md before 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 --output or --set-output is not covered; /ship applies 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/ contains skill-rules.md (hard rules and learned lessons for skills, formerly command-rules.md), review-agent-rules.md, cli-overlay-rules.md, agent-capability-rules.md, and readme-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_path from stdin JSON (via sed), pipes the transcript to claude -p for summarization, and writes the result to the handovers directory. The PreToolUse hook reads tool_name and tool_input.command from the same stdin JSON, splits the command on shell separators, and classifies each segment by its first word; a match prints a hookSpecificOutput object with a permissionDecision of deny or ask, 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 its SYNC: marker comment) and the PROMPT_PREFIX string in hooks/scripts/pre-compact-handover.sh. Both files contain a SYNC: comment pointing to the other. If you change headings, change both -- tests/hooks/test-handover-sync-contract.sh compares 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:

  1. Standalone: It must be useful when invoked in isolation, without any other Quiver skill having been run.
  2. 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

  1. Create skills/<name>/SKILL.md with YAML front-matter containing name and description fields. The name field must match the directory (and filename without .md) — it enables prefix-free access (e.g., /handover instead of /quiver:handover).
  2. 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.
  3. Do not use $() command substitution, variable assignment, if/else, or logic-bearing pipes in !`…` blocks — Claude Code blocks these in marketplace plugins.
  4. Do not reference CLAUDE_PLUGIN_ROOT from inside SKILL.md — it is unavailable in skill prompts. (CLAUDE_PLUGIN_ROOT is still available inside hooks.json and hook scripts.)
  5. Follow the hard rules and learned lessons in .claude/rules/skill-rules.md. R12 is the one most easily missed on a refactor: every AskUserQuestion question, 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.
  6. 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 || true only when no downstream logic needs to know the path was missing.
  7. Give every migrated or new skill a TEST-PLAN.md beside its SKILL.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.md excludes from user-facing docs -- drop the slash: # Test Plan -- <name>. skills/<name>/TEST-PLAN.md is the merge gate -- a skill without one is incomplete. It is a sibling rather than a section because SKILL.md loads on every invocation and the test instructions do not need to.

Adding an Agent

  1. Create agents/<category>/<name>.md by hand, using an existing agent in the same category as the pattern. The maintainer's /create-agent skill 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.
  2. 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.
  3. Agents live in agents/<category>/<name>.md with YAML front-matter fields: name, description, model.
  4. Category directories: review/, research/, workflow/, design/, docs/, or custom.
  5. The agents/ directory is registered in plugin.json's agents array.
  6. Assign the agent a capability profile and effort tier, add it to the ## Assignments table in .claude/rules/agent-capability-rules.md, and copy the profile's canonical disallowedTools string into the agent's frontmatter. By hand that is two edits; the maintainer's local /create-agent does both.
  7. If the agent searches the broader codebase (beyond files it already knows about), reference the code-navigation skill and include the Code Navigation Strategy block from skills/code-navigation/SKILL.md. The dispatching skill must pass lsp_available context.
  8. If the agent will be dispatched by /quiver:review (anything under agents/review/ or any cross-category agent listed in skills/review/SKILL.md Step 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 like stress-tester and security-audit).
  9. If the agent is a /quiver:review Step 2 participant, give it a row in the ## Dispatch Gates table in .claude/rules/review-agent-rules.md and restate that row in skills/review/SKILL.md Step 2b. The split is the one the skill's "Adding future agents" block already draws: review-scoped agents under agents/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, and tests/skills/test-review-dispatch-contract.sh fails on a row naming an agent /quiver:review never dispatches exactly as it fails on a participant with no row.

Adding or Modifying a Hook

  1. Register the event in hooks/hooks.json with "type": "command" (the only supported hook type).
  2. Place the script in hooks/scripts/. Use $CLAUDE_PROJECT_DIR (falls back to pwd) for the project root.
  3. $CLAUDE_PLUGIN_ROOT is available in hooks.json and hook scripts.

Invariants

  • Retention policy — Exactly the 3 most recent .md files 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.sh runs every tests/**/test-*.sh and exits nonzero if any failed. .github/workflows/tests.yml runs it on every pull request and on every push to master. 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.sh discovers it with no edit. The contract is the one all existing tests honor: runnable under bash from any working directory, exit 0 on pass and nonzero on fail. A shared helper must not match test-*.sh -- name it lib-*.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.sh checks every agent's disallowedTools and effort frontmatter 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.sh feeds real PreToolUse payloads to hooks/scripts/pre-tool-use-guard.sh and 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 in hooks/hooks.json.
  • Review dispatch contract -- bash tests/skills/test-review-dispatch-contract.sh binds the canonical ## Dispatch Gates table in .claude/rules/review-agent-rules.md to its runtime copy: the Step 2b prose in skills/review/SKILL.md. It fails when that copy's class list or mode list drifts from the canonical row, when a file under agents/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.sh reads the real skill files and asserts that skills/plan/SKILL.md still carries a line reading exactly ## Global Constraints, that skills/work/orchestrator.md, skills/work/SKILL.md, skills/review/SKILL.md, and skills/ship/SKILL.md still name the section, and that both degrade-cleanly clauses survive -- the orchestrator's omits sentence in the same paragraph as the block, and no note inside a ## Step 1.8 heading 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 --plan alone, 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.sh runs install.sh under a throwaway HOME from mktemp -d and asserts the symlink is created, a second run changes nothing, a real directory at a destination is skipped with the run exiting nonzero, --uninstall removes only links resolving into the repo, XDG_CONFIG_HOME relocates 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.sh binds .claude-plugin, .cursor-plugin, and .codex-plugin to one version string, asserts the Cursor agents array equals the Claude one and that Claude registers every file under agents/, and checks the README version badge. scripts/release.sh applies 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.sh asserts that every git log gather block under skills/ falls back to NO_COMMITS. In a repository made with git init and no commit yet, every other gather block exits 0 and git log exits 128, so a git log block falling back to NO_GIT made Step 0 report "No git repository detected" inside a real repository -- /commit could 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 a NO_COMMITS run 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 with git commit --allow-empty --only before branching, and on a non-default branch creates the main branch the user picks with git branch <name>: git checkout -b on 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. --only with no paths keeps anything already staged out of that commit. Finding no git log block is a failure, so a reworded block form cannot leave the test asserting nothing.
  • Component count contract -- bash tests/inventory/test-component-count-contract.sh binds the tree to every hand-written component count and component list: the three rows of README.md's inventory table and its ## Hooks table, the skills/ and agents/ bullets in this file's Architecture section, .opencode/README.md's skill sentence, its #### Available skills table, its agent sentence and its @name list, 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.md excludes 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 inside readme-structure.md is 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.sh extracts the 8 H2 headings under the SYNC: marker in skills/handover/SKILL.md and the 8 inside PROMPT_PREFIX in hooks/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 a SYNC: comment naming the other.
  • Handover keep-local behavior -- bash tests/hooks/test-handover-local-exclude.sh runs hooks/scripts/pre-compact-handover.sh against throwaway repositories, with claude and timeout replaced by stubs first on PATH so 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/exclude exactly once, writes a handover through the stub, and leaves git check-ignore reporting 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 gets app/.claude/handovers/ exactly once after two runs, since an entry without the prefix never matches and every compaction would append again; that a .gitignore carrying !.claude/handovers/ still leaves exactly one entry after two runs, because that negation outranks the exclude file and git check-ignore keeps reporting the directory not ignored; that a .claude reached through a symlink, where git check-ignore exits 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.sh binds the **Provides:** field in skills/plan/SKILL.md to 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 stale 7 checks because 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 ## Interfaces verbatim, and that its degrade-cleanly clause still says it omits the ## Interfaces heading. That clause is matched on a phrase naming the heading rather than on the paragraph, because Section 2 Part 1 item 1 already carries an omits clause 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.sh binds R10 in .claude/rules/skill-rules.md to every skill's frontmatter and to the block hooks/scripts/session-start-auto-dispatch.sh builds 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 own EXEMPT list removes its assertion instead of failing it -- that every non-exempt skill's when-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 nor disable-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 for handover would wire its --clear and --clear-all deletes into silent auto-invocation. The two exclusions are different failures: an exempt skill has no when-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: a handover that lost disable-model-invocation: true falls 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.sh binds skills/verification/SKILL.md to its six consumers: it asserts the reference skill is user-invocable: false with no shell block, that every consumer names the file, that the subagent restatement in skills/work/orchestrator.md is byte-identical to the canonical line -- /ship hands execution to /work, dispatches no implementing subagent, and carries no copy -- that the orchestrator declares the TESTS evidence grammar, that /ship's metadata names test_command and build_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, asserts skills/verification/TEST-PLAN.md is 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.md names the skill; the R10 exemption is enforced by tests/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.sh binds agents/review/finding-verifier.md to its one consumer, /quiver:review Step 3.3. It asserts the agent's frontmatter carries isolation: worktree, that both files carry the three verdict tokens -- CONFIRMED, -- REFUTED, and -- UNVERIFIABLE, that skills/review/SKILL.md names the report lines Verification: confirmed -- and Verification: unverified -- and the refuted-by-verification classification and has exactly one ## Step 3.3 -- Verify Findings line, and that the agent's hypothetical-language ban hashes equal to the copy in agents/review/logic-reviewer.md. It also binds the report lines to their two readers: skills/work/SKILL.md must name Verification: 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 skips finding-verifier, and skills/report-check/SKILL.md must name Verification: (a rewrite keeps the line). It pins --run-checks in 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 /work passing 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.sh binds skills/tdd/SKILL.md to its five consumers: it asserts the reference skill is named tdd, is user-invocable: false with no shell block, that its four body headings and its restatement heading each appear exactly once and that skills/tdd/TEST-PLAN.md is present, non-empty and carries a Trigger line, and that it carries both evidence line forms and the no red evidence reason; that the orchestrator, /ship, and /hypothesis-debugging name the file anywhere, and that /plan and /work name it in the build-step sentence itself -- both carry a second mention in a TEST-PLAN.md checklist or a gotchas line, so a whole-file grep stays green after the pointer is deleted; that the restatement copy in skills/work/orchestrator.md is byte-identical to the canonical line -- /ship dispatches no implementing subagent and carries none; that the orchestrator's return contract carries the TDD | red and skipped forms, that the orchestrator names no red evidence and 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 /plan that put exact test code in the plan; and that the readme-structure.md exclusion list and the R10 test's EXEMPT list both name tdd; the when-to-use absence and R10's own text are enforced by tests/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.sh scans every markdown file under skills/ 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-fix shipped 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.sh extracts every `skills/<name>/SKILL.md` Step <N> pointer in skills/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 is skills/plan/SKILL.md Steps 2.5, 3, 4.5, 5, 6, 6.5 and 7, skills/brainstorm/SKILL.md Step 5, and skills/review/SKILL.md Steps 1 and 4. The step number stops at the first character that is not a digit or a decimal part, so ship's Step 4a item 3 is checked as Step 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 --, so Step 6 is 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 /plan would otherwise pass the real file forever. SHIP_FILE overrides the ship path for running the rule against a fixture.
  • Ship contract -- bash tests/skills/test-ship-contract.sh binds skills/ship/SKILL.md to the files it writes and the skills it invokes. It asserts that /work's Phase 5d in skills/work/SKILL.md no longer says continues into its verification (the finding verification contract pins the follow its continuation step that 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 /work twice, 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/, and skills/local-files/SKILL.md; that its # State Files section, 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 ## Goal exactly twice because the roadmap and the phase file both carry it, each of the eleven phase-file frontmatter fields from phase: to fix_rounds: at the start of a line, and the D<n>. <decision> -- <reason> (<user|agent>, <date>, phase <id>) entry format of decisions.md; and that the table in skills/local-files/SKILL.md has 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} and cache_read_input_tokens for the context measurement, /work <plan path> --auto and Phase 5d for the build, with /work still carrying a #### 5d -- heading; quiver:review and --output .claude/reports for the review, which must never be passed --terminal, since that writes no report for the fix stage to read; and Verification: confirmed, review_source, and fix_rounds for the fix round, with /work still naming `review_source`. It pins the done stage's two reads -- review_source:, through which it finds the first report, and the fix: not fixed: Log line -- inside ## Stage: done, the backticked fix: not fixed: writer inside ## Stage: fix, and /work's still failing after 3 attempts -- accepted line: 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 go done with it open. It pins the two lines that keep one /ship going -- Init 4 and the done stage each print a Starting phase line and re-enter ## Step 1 -- Route as a bare /ship -- and fails when Init still ends on Run /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.sh binds the # Commit Message Generation section of skills/commit/SKILL.md to its two consumers, skills/work/SKILL.md and skills/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 says subject-only and still carries the no-attribution rule; that each consumer names skills/commit/SKILL.md and the heading and no longer says Conventional 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 starting 6. -- 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.sh asserts that no file under skills/ names docs/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 that skills/local-files/SKILL.md, the one home of the keep-local rule, is user-invocable: false, carries no inline shell block, and has the ## Keep-Local Rule heading 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 in skills/local-files/SKILL.md; and that hooks/scripts/pre-compact-handover.sh still names info/exclude and ls-files, the commands of its bash copy. It does not run the hook; tests/hooks/test-handover-local-exclude.sh does. A new writer of a development directory goes into the test's CONSUMERS list 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.sh builds a throwaway git repo from tests/eval/review-golden/fixture/, runs a real /quiver:review --base main --run-checks against it through claude -p, and grades the report's ## Findings section against tests/eval/review-golden/expectations.txt. The fixture plants three defects a fast review should find -- an off-by-one in bulk_discount, a shell=True command injection in export_snapshot, a helper that reimplements utils.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's DECISIONS.md, named in its CLAUDE.md, requires word for word, and a comment in app/ledger.py whose byte count tools/measure_ledger.py reproduces. Their ~ expectations fail when the regex appears under ## Findings and print, as information only, whether it appears on a refuted-by-verification line 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-usd is 8 rather than 5; a --deep probe 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 in agents/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 the test-*.sh glob and neither tests/run-all.sh nor .github/workflows/tests.yml can discover it. Run it before a release and after any change to the review agents, the dispatch gates, or the model /quiver:review runs 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] runs claude -p with --plugin-dir pointing at this repo, in an empty temp project, and has it dispatch quiver:stack-researcher once 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 names maxBuffer, states its 1 MiB default (a product such as 64 * 1024 * 1024 does not count), sets encoding to buffer, and uses exec-out; Q2 quotes the typescript-eslint peer range for typescript; Q3, which asks what a customer's own head unit build prints, comes back needs-measurement; and the project root is still empty after the run. The Q2 expectation is read from the npm registry at run time with npm 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 when npm, claude, or python3 is not on PATH. It does not look at adb devices: no maintainer has the Q3 head unit, so an attached phone or emulator cannot answer it, and the run's allowlist keeps adb out of the agent's reach. It exits nonzero on any failed check, keeps the workspace on a failure (--keep keeps it on a pass too), and prints one cost line. It runs in dontAsk mode with an allowlist of Agent, WebSearch, WebFetch, context7 and npm view: the agent reads open-web pages, and curl and gh stay 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 denied curl under ## Gaps. dontAsk also 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 as Bash(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 two curl calls 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 the test-*.sh glob. Its free counterpart, tests/eval/test-stack-researcher-eval.sh, is inside the glob and runs in tests/run-all.sh and CI: it drives the script against fake claude, npm, and adb executables and asserts that the run uses dontAsk with 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 missing npm stops the run before claude is called, and that an attached device does not. Run the paid eval after any edit to agents/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.sh and bash -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 its TEST-PLAN.md.

Known Gotchas

  • .gitignore's docs/ entry has no leading slash, so it matches a docs/ directory at any depth. A test placed at tests/docs/ is silently untracked: tests/run-all.sh finds 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 check git check-ignore before writing the file.
  • /quiver:review and /quiver:design are written namespaced in user-facing docs on purpose, and a "cleanup" pass that shortens them to /review and /design breaks both commands silently. Claude Code bundles a code-review skill whose documented alias is /review, and the docs state that a skill "replaces the bundled command, but not its aliases" -- so a plugin skill named review is unreachable as /review by design. Measured on 2.1.267 in a throwaway directory with --plugin-dir pointing at this repo: /review ran the bundled reviewer (its own "Phase 0" wording, none of this skill's !git rev-parse blocks, 0 subagents where fast mode fans out to 5), /design ran 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 its No git repository detected line. /design was the surprise: it is not on any published bundled-alias list, and the claude-code-guide agent asserted no bundled design exists. 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 -- review alone 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, and tests/opencode/test-bootstrap-injection.sh fails when the workflow chain there is namespaced because it parses those entries as skill names.
  • The PreCompact hook pipes transcript + prompt to claude -p via stdin to avoid ARG_MAX limits -- do not refactor to use command-line arguments.
  • Hook timeout is 180s (hooks.json), but the inner claude -p call has timeout 150 -- the 30s gap prevents zombie processes.
  • ls -1r in 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.sh points 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 -uf without set -e (the file header), and masking JSON escapes before extracting the command (above json_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.md followed by a line reading rm -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 wrong deny, which is the one decision a user cannot override.
  • Lesson LO3 in .claude/rules/cli-overlay-rules.md says to prefer no automatic hook over a frequently-firing one with a fragile guard. The PreToolUse guard fires more often than the Stop hook that lesson killed and is still the right call: the killed hook ran claude -p on every fire behind a cooldown that only reset on success, while this one is sed plus tr, 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, and git pull there is the update. Cursor's loadUserLocalPlugins reads 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, and install.sh, which only ever symlinks, still has no cursor row. Measured on Cursor 3.17.21 against an isolated HOME: the clone logs loadUserLocalPlugin quiver loaded and registers the plugin's context7 MCP server, the symlink logs rejected: symlink target ... is outside ... and 0 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 is master needs nothing special.
  • .cursor-plugin/plugin.json still matters: it is the first manifest path Cursor looks for (.cursor-plugin/plugin.json, then .claude-plugin/plugin.json, then plugin.json), for the local clone and for the Plugins panel's git import alike. The panel also accepts .cursor-plugin/marketplace.json or .claude-plugin/marketplace.json as 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.json ships to Cursor as well (.cursor-plugin/plugin.json sets "hooks": "./hooks/hooks.json"), so Cursor receives the PreToolUse block whether or not it implements permissionDecision. Codex has no hooks key in its manifest and is unaffected.
  • claude plugin validate ./ --strict fails in plugin-manifest mode (the mode chosen when .claude-plugin/marketplace.json is absent) on a warning that CLAUDE.md at the plugin root is not loaded as project context. This predates the guard -- it reproduces on master at 6f74e11 -- and marketplace-manifest mode, which is how the plugin actually ships, passes clean.
  • The ## Global Constraints heading is one hand-synced string across four files: skills/plan/SKILL.md writes the section, skills/work/orchestrator.md reproduces it into every task brief, skills/review/SKILL.md Step 1.8 extracts it, and the plan stage of skills/ship/SKILL.md writes it into every phase plan and checks for it on read-back. Renaming it in one file and not the others raises no error -- /work writes briefs carrying no constraints, /quiver:review reports N/A, /ship writes a heading /work no longer extracts -- and each is indistinguishable from a plan that derived none. tests/skills/test-global-constraints-contract.sh is the only gate on that. It pins exactly the files listed above, so a fifth consumer -- /design-build or anything else that reads the block -- must be added to that test in the same change that adds the consumer, exactly as tests/skills/test-review-dispatch-contract.sh requires a row per dispatched agent. A consumer with no assertion is a consumer the rename case silently skips.
  • **Provides:** and ## Interfaces are 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.md writes the label into the plan and skills/work/orchestrator.md extracts on it. ## Interfaces is 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: /work then 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.sh is 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 Constraints gotcha 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 /work names skills/work/orchestrator.md. The only text copied out of it is the one-line ### Subagent restatement, pasted verbatim into the subagent prompt in skills/work/orchestrator.md -- the only implementing-subagent prompt in the tree, since /ship hands execution to /work and 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-debugging read it at their build step by naming that path, the same way they read skills/verification/SKILL.md; /ship names 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 in skills/work/orchestrator.md -- the only implementing-subagent prompt in the tree, since /ship hands execution to /work and 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 -- and tests/skills/test-tdd-contract.sh is the only thing that catches it. A return with no TDD line is skipped: no red evidence: recorded, counted in the /work 5d 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.

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.