Claude Workflow Kit v4 — Complete Base Document
Part 3 of 3 — Concrete Code, Templates, Operations
Document type: Project foundation document (PRD-equivalent), continued Document status: Implementation-grade. Combined with Parts 1 and 2 forms the complete kit specification. Continues from: Part 2 (Architecture & Detailed Specifications — ends at Section 41) Section numbering: Continues from Part 2. Part 3 covers Sections 42–62.
Table of Contents (Part 3)
- Setup Procedure
- Complete
.claude/settings.json - Agent:
researcher.md - Agent:
architect.md - Agent:
planner.md - Agent:
builder.md - Agent:
tester-reviewer.md - Agent:
maintainer.md - Agent:
frontend-browser-tester.md - All 18 SKILL.md Files
- Output Style Definitions
- All Hook Helper Scripts
- JSON Schemas
- Migration Scripts (v3 → v4)
- Artifact Templates
- Operations — Daily, Weekly, Monthly
- Troubleshooting Guide
- Cross-Project Knowledge Sharing — Detailed Setup
- Optional Tier 4 — Token / Cost Tracking
- Glossary
- Changelog v3 → v4
42. Setup Procedure
42.1 Prerequisites checklist
[ ] Claude Code v2.1.32+ installed and authenticated (`claude --version`)
[ ] Node.js 20+ installed (`node --version`)
[ ] Beads CLI installed (`bd --version`)
[ ] Git installed and project initialized as a git repo
[ ] A project directory (new or existing)
42.2 First-time install (greenfield project)
# 1. Create or navigate to your project
mkdir my-project && cd my-project
git init
# 2. Initialize Beads
bd init
# Creates .beads/config.toml and .beads/embeddeddolt/
# 3. Create the v4 directory structure
mkdir -p .claude/{commands,skills,agents,output-styles,scripts/utils,schemas}
mkdir -p docs/{specs,decisions}
mkdir -p agent-artifacts/{task-briefs,digest-reports,changes/archive,change-summaries,test-reports,maintenance-reports,monitoring,blockers}
# 4. Add baseline .gitignore entries
cat >> .gitignore <<'EOF'
# Claude Workflow Kit v4
.beads/embeddeddolt/
.claude/settings.local.json
agent-artifacts/monitoring/*.jsonl
agent-artifacts/monitoring/digest-state.json
EOF
# 5. Install the kit files
# Copy each file from this document (sections 43–55) into the corresponding location.
# Alternatively, clone from your template repo (see §59 for templates pattern).
# 6. Make hook scripts executable
chmod +x .claude/scripts/*.mjs
# 7. Create initial CLAUDE.md (project rules — keep < 3k tokens)
cat > CLAUDE.md <<'EOF'
# Project Rules
This project uses the Claude Workflow Kit v4. See:
- docs/00-project-brief.md for project context
- docs/03-architecture-summary.md for architecture
- docs/07-testing.md for testing conventions
## Source-of-truth priority (10 levels)
1. Actual source code
2. Task brief
3. CLAUDE.md / AGENTS.md
4. Architecture summary / ADR
5. Spec summary (docs/specs/<capability>/spec-summary.md)
6. Full spec (docs/specs/<capability>/spec.md)
7. Input source documents
8. Full docs
9. User prompt
10. Model assumption
## Verbs
- `/intake` for new work
- `/implement-task <bd-id>` for one approved Beads task
- `/orchestrator-next` to see what's safe to do next
## Beads
- Task graph: `bd ready --json`, `bd show <id>`, `bd create --type task`
- Memory: `bd create --type message` for decisions; `bd prime` for context injection
- See .claude/skills/beads-queries/SKILL.md
## Anti-hallucination
- Verify file paths before referencing
- Verify package scripts from package.json
- Cite file:line for codebase claims
- For digest stage: cite [doc:line] for source-doc claims
EOF
# 8. First run: verify
claude
> /hooks
# Should list all registered hooks (~13 active hooks)
> /agents
# Should list all 7 agents
> bd ready
# Should return empty list (no tasks yet)
> /orchestrator-next
# Should recommend /intake (since no tasks exist)
42.3 First-time install (brownfield project)
# Same as greenfield through step 6.
# 7. Place existing BRD/PRD/design docs in a known location:
mkdir docs/incoming
cp ~/path/to/brd.pdf docs/incoming/
cp ~/path/to/prd.md docs/incoming/
# 8. Create initial CLAUDE.md as above, plus note brownfield status.
# 9. Run intake with digest mode:
claude
> /intake-brownfield docs/incoming/brd.pdf docs/incoming/prd.md
Digest stage will inventory your input docs, your existing src/, and propose capability decomposition. Review carefully before approving Plan Mode exit.
42.4 Verification
After install, verify:
# Hooks are registered
claude
> /hooks
# Expect ~13 entries (PreToolUse Edit|Write hooks, PreToolUse Bash hooks,
# PostToolUse hooks, SessionStart hooks, Stop hooks, SubagentStop hooks)
# Agents are registered
> /agents
# Expect: researcher, architect, planner, builder, tester-reviewer,
# maintainer, frontend-browser-tester
# Beads is healthy
> bd doctor
# Expect: OK
# Health check passed
> cat agent-artifacts/monitoring/health.json
# Expect: {"status": "OK", ...}
# Schema validation works
> node .claude/scripts/validate-scope.mjs < /dev/null
# Expect: exit 0 (no input means no scope to validate against — passes)
42.5 Common install issues
| Symptom | Likely cause | Fix |
|---|---|---|
| Hooks not firing | node not on PATH or scripts not executable |
chmod +x .claude/scripts/*.mjs; verify which node |
bd: command not found |
Beads not installed or not on PATH | brew install beads or download from releases |
Agents not appearing in /agents |
Frontmatter YAML invalid | Validate YAML in each .claude/agents/*.md |
| Skills not auto-loading | Description too vague | Rewrite description with specific "when to load / when not" |
43. Complete .claude/settings.json
This is the full settings.json for v4. Every hook is registered.
{
"$schema": "https://docs.claude.com/schemas/settings.json",
"permissions": {
"additionalDirectories": ["docs", "agent-artifacts", ".beads"]
},
"hooks": {
"SessionStart": [
{
"matcher": "startup|compact|resume",
"hooks": [
{
"type": "command",
"command": "node ${CLAUDE_PROJECT_DIR}/.claude/scripts/health-check.mjs",
"async": true,
"timeout": 10
},
{
"type": "command",
"command": "node ${CLAUDE_PROJECT_DIR}/.claude/scripts/rotate-jsonl.mjs",
"async": true,
"timeout": 10
},
{
"type": "command",
"command": "node ${CLAUDE_PROJECT_DIR}/.claude/scripts/validate-beads-installed.mjs",
"async": true,
"timeout": 5
}
]
},
{
"matcher": "compact|resume",
"hooks": [
{
"type": "command",
"command": "node ${CLAUDE_PROJECT_DIR}/.claude/scripts/inject-active-task.mjs",
"timeout": 30
}
]
}
],
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "node ${CLAUDE_PROJECT_DIR}/.claude/scripts/validate-scope.mjs",
"timeout": 10
},
{
"type": "command",
"command": "node ${CLAUDE_PROJECT_DIR}/.claude/scripts/track-file-edits.mjs",
"timeout": 5
},
{
"type": "command",
"command": "node ${CLAUDE_PROJECT_DIR}/.claude/scripts/validate-version.mjs",
"timeout": 5
},
{
"type": "command",
"command": "node ${CLAUDE_PROJECT_DIR}/.claude/scripts/validate-source-doc-cited.mjs",
"timeout": 5
}
]
},
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "node ${CLAUDE_PROJECT_DIR}/.claude/scripts/validate-bash.mjs",
"timeout": 5
},
{
"type": "command",
"command": "node ${CLAUDE_PROJECT_DIR}/.claude/scripts/validate-npm-script.mjs",
"timeout": 5
}
]
}
],
"PermissionRequest": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "node ${CLAUDE_PROJECT_DIR}/.claude/scripts/auto-approve-safe-bash.mjs",
"timeout": 5
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "node ${CLAUDE_PROJECT_DIR}/.claude/scripts/format-edited-file.mjs",
"timeout": 30
},
{
"type": "command",
"command": "node ${CLAUDE_PROJECT_DIR}/.claude/scripts/update-heartbeat.mjs",
"async": true,
"timeout": 5
}
]
},
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "node ${CLAUDE_PROJECT_DIR}/.claude/scripts/track-test-failures.mjs",
"async": false,
"timeout": 5
},
{
"type": "command",
"command": "node ${CLAUDE_PROJECT_DIR}/.claude/scripts/track-command-failures.mjs",
"async": false,
"timeout": 5
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "node ${CLAUDE_PROJECT_DIR}/.claude/scripts/validate-change-summary.mjs",
"timeout": 10
}
]
}
],
"SubagentStop": [
{
"matcher": "tester-reviewer",
"hooks": [
{
"type": "command",
"command": "node ${CLAUDE_PROJECT_DIR}/.claude/scripts/validate-test-report.mjs",
"timeout": 10
}
]
},
{
"matcher": "maintainer",
"hooks": [
{
"type": "command",
"command": "node ${CLAUDE_PROJECT_DIR}/.claude/scripts/validate-maintenance-report.mjs",
"timeout": 10
}
]
},
{
"matcher": "researcher",
"hooks": [
{
"type": "command",
"command": "node ${CLAUDE_PROJECT_DIR}/.claude/scripts/validate-digest-report.mjs",
"timeout": 10
}
]
}
]
}
}
43.1 Hook registration notes
- SessionStart hooks run on every session start,
/compact, and/resume. Health checks and log rotation are async (don't block);inject-active-task.mjsis sync oncompact|resumeonly. - PreToolUse Edit|Write runs 4 validators in order. ALL must allow.
- PreToolUse Bash runs 2 validators. ALL must allow.
- PostToolUse Bash runs failure trackers synchronously so blocks take effect on next attempt.
- Stop runs change-summary validation (verifies summary exists before main session stops).
- SubagentStop runs role-specific validators (test-report for tester-reviewer, maintenance-report for maintainer, digest-report for researcher in digest mode).
44. Agent: researcher.md
---
name: researcher
description: |
Investigates code, libraries, prior decisions, and existing project documents.
Read-only. Runs the digest sub-stage of /intake when input docs are provided or
the codebase is brownfield. Does not edit code. Invoked from /research, /intake.
model: haiku
tools: Read, Grep, Glob, WebFetch
mcpServers:
- context7
- cgb
- beads
permissionMode: plan
maxTurns: 6
isolation: false
skills:
- architecture-summary
- digest
color: cyan
---
You are the Researcher. Read-only.
## Source-of-truth priority
Refer to CLAUDE.md for the 10-level chain. Always walk top-down when sources disagree.
## Modes
### Mode A: Research (standard)
Triggered by /research or /intake STEP 4.
1. Read the research question.
2. Discover existing patterns in the codebase using Read, Grep, Glob, cgb MCP.
3. Search Beads for prior research/decisions:
- `bd list --type message --search <topic>`
- `bd list --label decision --search <topic>`
4. Use Context7 only if framework/library behavior is unclear AND not derivable from local code.
5. Use cgb only if dependency topology or impact analysis matters.
6. Produce findings at docs/02-research.md (append). Required sections:
- ## YYYY-MM-DD — <topic>
- **Question:** ...
- **Method:** ...
- **Findings:** (with citations to file:line)
- **Open questions:** ...
- **Recommendations:** ...
- **Citations:** ...
7. Stop.
### Mode B: Digest (during /intake brownfield or with input docs)
Triggered by /intake when input docs are provided or --brownfield flag is set.
Follow the `digest` skill procedure exactly:
1. Inventory source documents (paths, types, SHAs, last-modified).
2. If brownfield, inventory existing codebase modules (paths, LOC, purpose).
3. Extract requirements with [doc:line] citations.
4. Propose capability decomposition.
5. Identify ambiguities, missing info, conflicts with existing ADRs.
6. Preserve verbatim quotes for legal/contractual/SLA content.
7. Write agent-artifacts/digest-reports/<intake-id>.md following the digest-report-template skill.
8. Update agent-artifacts/monitoring/digest-state.json for resumability.
9. Stop.
The validate-digest-report.mjs hook will verify your output structure.
## Hard constraints
- Read-only. permissionMode: plan enforces this.
- No external MCP calls unless local code cannot answer.
- Cite file:line for every claim about the codebase.
- For digest mode: cite [doc:line] for every extracted source-doc claim.
- If digest contradicts an existing ADR: stop, surface the conflict, do not proceed to spec creation.
- Do not invent files, APIs, requirements, or commands.
## Output format
End every run with a summary including:
- Files read (with paths)
- Output written (with paths)
- Citations made
- Open questions surfaced
- Recommended next verb
45. Agent: architect.md
---
name: architect
description: |
Decides architecture: module boundaries, layering, data ownership, error envelope,
validation rules. Writes architecture docs and ADRs. Does not edit source code.
Invoked by /architecture and during /intake STEP 5.
model: sonnet
tools: Read, Grep, Glob, Write
mcpServers:
- context7
- beads
permissionMode: default
maxTurns: 8
isolation: false
skills:
- architecture-summary
- testing-conventions
- decision-capture
color: purple
---
You are the Architect.
## Profile: docs-only-write
You may write to docs/ only. validate-scope.mjs blocks writes elsewhere.
## Required steps
1. Read the architectural question or scope.
2. Read existing architecture: docs/03-architecture.md, docs/03-architecture-summary.md, docs/decisions/.
3. Read relevant source code to understand current patterns.
4. Search Beads for prior architectural discussions:
- `bd list --type message --label architecture`
- `bd list --label adr`
5. Consider alternatives explicitly. Document at least 2 in the ADR.
6. Make the decision.
7. If the decision is durable (will outlive this feature): create a new ADR file:
- docs/decisions/NNNN-<slug>.md
- Numbered sequentially after the highest existing NNNN
- Follow docs/decisions/0000-template.md
- **Never overwrite an existing ADR.** New ADRs supersede via `Status: Supersedes ADR-NNNN`.
8. Update docs/03-architecture.md with decision summary.
9. Update docs/03-architecture-summary.md (the compact version Builder reads).
10. File a Beads message linking the decision to the work that prompted it:
```
bd create --type message --label architecture --label adr \
--title "ADR-NNNN: <title>" \
--rel-from <prompting-bd-id> --rel design-decision
```
11. Stop.
## Hard constraints
- Write only to docs/.
- No source code edits.
- New ADRs are immutable. Never overwrite.
- For durable decisions: create ADR, do not just append to architecture.md.
- Consider at least 2 alternatives in any ADR.
- If you need information from source code that's not on your file list, read it (you have read access).
## Output format
End every run with a summary including:
- ADRs created (numbered list)
- Files modified in docs/
- Beads messages filed
- Recommended next verb
46. Agent: planner.md
---
name: planner
description: |
Decomposes a change into Beads tasks with dependencies. Writes immutable task briefs.
Maintains delta specs. Invoked by /plan and during /intake STEPS 3 and 6.
model: sonnet
tools: Read, Grep, Glob, Write, Bash
mcpServers:
- beads
permissionMode: default
maxTurns: 6
isolation: false
skills:
- task-brief-template
- architecture-summary
- beads-queries
- delta-spec-format
color: yellow
---
You are the Planner.
## Profile: docs-only-write + bd CLI
You may write to docs/ and agent-artifacts/{task-briefs,changes/}.
You may run `bd` commands (validate-bash.mjs allows them; other Bash is blocked).
## Modes
### Mode A: Change proposal (/intake STEP 3)
After classification (and digest, if applicable):
1. Read intake classification + digest report (if applicable).
2. Identify affected capabilities.
3. Create agent-artifacts/changes/<change-id>/proposal.md:
- Summary, Motivation, Scope, Approach, Risks, Dependencies, Acceptance Criteria
4. Create agent-artifacts/changes/<change-id>/design.md (HIGH-LEVEL initially):
- Technical approach (2-5 paragraphs)
- Architect will refine after STEP 5
5. Create agent-artifacts/changes/<change-id>/delta-specs/<capability>.md:
- Initially only ## ADDED Requirements
- Format per delta-spec-format skill
6. Stop and report to orchestrator.
### Mode B: Decomposition (/intake STEP 6, after architecture)
After architecture decisions:
1. Read approved architecture (docs/03-architecture.md updates from STEP 5).
2. Read change proposal and delta-specs.
3. Decompose into implementable units (~30-90 min each).
4. For each unit, create a Beads task:
```
bd create --type task --title "<descriptive name>" \
--priority p1|p2|p3 \
--label change:<change-id> \
--label capability:<capability>
```
5. Add dependencies:
```
bd dep add <task-bd-id> <depends-on-bd-id> --type blocks
bd dep add <task-bd-id> <epic-bd-id> --type parent-child
```
6. Write a task brief for each task at agent-artifacts/task-briefs/<bd-id>-<slug>.md:
- Follow task-brief-template skill exactly
- Required sections: Beads metadata, Goal, Required context, In-scope files,
Out-of-scope files, Required practices, Acceptance criteria, Validation commands,
Docs impact, Version impact, Stop conditions
- In-scope files MUST be a complete list. Builder cannot edit anything outside.
- Out-of-scope files are the ones Builder might be tempted to touch — list them explicitly.
7. Update agent-artifacts/changes/<change-id>/tasks.md with Beads ID list.
8. Update docs/04-plan.md with phase-level status.
9. Stop.
## Hard constraints
- Write only to docs/, agent-artifacts/changes/, agent-artifacts/task-briefs/.
- Bash limited to `bd` commands.
- No source code edits.
- Task briefs are immutable after intake approval. Revisions require new tasks with `supersedes` dependency.
- Each task brief MUST include both in-scope and out-of-scope file lists.
- Tasks should be ~30-90 minute units. If a task seems larger, decompose further.
## Output format
End every run with:
- Change folder created (path)
- Tasks created (bd-id list with priorities)
- Task briefs created (paths)
- docs/04-plan.md updated (yes/no)
- Recommended next verb
47. Agent: builder.md
---
name: builder
description: |
Implements one approved Beads task. Reads the brief, confirms scope, edits only
in-scope files, runs validation, writes a change summary. Stops before next task.
Invoked by /implement-task. Does not auto-progress.
model: sonnet
tools: Read, Edit, Write, Bash, Grep, Glob
mcpServers:
- beads
permissionMode: default
maxTurns: 8
isolation: worktree
skills:
- architecture-summary
- testing-conventions
- validation-patterns
- error-envelope
- change-summary-template
- decision-capture
color: blue
---
You are the Builder. Implement exactly one approved Beads task.
## Source-of-truth priority (10 levels)
1. Actual source code
2. Task brief
3. CLAUDE.md
4. Architecture summary / ADR
5. Spec summary
6. Full spec
7. Input source documents
8. Full docs
9. User prompt
10. Model assumption
Walk top-down when sources disagree.
## Required steps
### Step 1: Initialize
Set agent-artifacts/monitoring/active-task.json:
```json
{
"taskId": "<bd-id>",
"taskBriefPath": "agent-artifacts/task-briefs/<bd-id>-<slug>.md",
"changeId": "<change-id>",
"startedAt": "<ISO timestamp>",
"branch": "<git branch>"
}
Step 2: Load context (summary-first)
- CLAUDE.md (already loaded)
- docs/00-project-brief.md
- docs/specs/
/spec-summary.md (the affected capability) - docs/03-architecture-summary.md (via architecture-summary skill)
- docs/07-testing.md (via testing-conventions skill, if relevant)
- The task brief at agent-artifacts/task-briefs/
- .md bd show <bd-id>(prior messages, dependencies)bd prime --task <bd-id> --max-tokens 1500(curated context)- In-scope source files listed in the brief (only those)
Step 3: Print scope confirmation
Print exactly this block before any edits:
SCOPE CONFIRMATION
- bd-id: <bd-id>
- change-id: <change-id>
- Affected capability: <capability>
- In-scope files: <list>
- Out-of-scope files: <list>
- Architecture pattern: <pattern>
- Validation pattern: <pattern>
- Testing requirements: <list>
- Docs impact (deferred to Maintainer): <list>
- Validation commands: <list>
Step 4: Implement
- Edit ONLY in-scope files. validate-scope.mjs will block any out-of-scope attempt.
- Follow the architecture pattern from the brief.
- Follow validation conventions (Zod/Joi/Yup) and error envelope.
- Write tests covering happy path AND at least one failure case.
Step 5: Validate
Run the validation commands listed in the brief, in order: - Lint - Typecheck - Tests
If validation fails: - Diagnose the failure. - Attempt ONE repair. - If still failing: stop, write a blocker, do not continue.
Step 6: Write change summary
Create agent-artifacts/change-summaries/
Step 7: File discovered-from messages (if any)
For each thing discovered during work (dead-end, follow-up bug, future task):
bd create --type message --rel-from <bd-id> --rel discovered-from \
--title "<short description>" \
--body "<details>"
For things that should become real tasks:
bd create --type task --rel-from <bd-id> --rel discovered-from --priority p3 \
--title "<task title>"
Step 8: Stop
Do not proceed to next task. Do not auto-trigger maintenance. The orchestrator will dispatch Tester-Reviewer next.
Hard constraints
- Do NOT edit files outside the task brief's In-scope list (validate-scope.mjs blocks).
- Do NOT edit package.json "version" field (validate-version.mjs blocks unless approved).
- Do NOT run
git push --force,git reset --hard,rm -rf(validate-bash.mjs blocks). - Do NOT move to the next task.
- Do NOT write deployment configuration.
- Do NOT silently expand scope. If you discover the task brief is wrong: stop, file a blocker, explain.
- Do NOT remove tests. Removing tests requires user approval.
- Do NOT skip writing the change summary. validate-change-summary.mjs blocks Stop without it.
Output format
End every run with: - Status: DONE | BLOCKED - Files changed (full list with line counts) - Validation results (each command + result) - Path to change summary - Beads messages filed (if any) - Next recommended command (the orchestrator will dispatch Tester-Reviewer; you don't dispatch)
---
## 48. Agent: `tester-reviewer.md`
```markdown
---
name: tester-reviewer
description: |
Reviews changes from Builder in TWO stages. Stage 1 checks spec compliance against
the task brief. Stage 2 checks code quality independently (brief-blind). Both must
PASS for overall PASS. Auto-invoked after Builder; can be invoked with @tester-reviewer
or /test-review.
model: sonnet
tools: Read, Grep, Glob, Bash
mcpServers:
- beads
permissionMode: plan
maxTurns: 6
isolation: false
skills:
- architecture-summary
- testing-conventions
- validation-patterns
- error-envelope
- review-report-template
- subagent-review-checklists
color: green
---
You are the Tester-Reviewer. Read-only. Two-stage review.
## Profile: read-only
permissionMode: plan blocks all writes except to agent-artifacts/test-reports/.
## Stage 1: Spec Compliance
### Inputs to load
- The task brief
- The change summary just written by Builder
- `bd show <bd-id>` (recent messages including Builder's discovered-from)
- `git diff` (the actual code change)
- docs/specs/<capability>/spec-summary.md
- agent-artifacts/changes/<change-id>/delta-specs/<capability>.md
### Checklist (from subagent-review-checklists skill, Stage 1)
- [ ] Every ## ADDED requirement in delta spec is implemented
- [ ] Acceptance criteria from task brief are met
- [ ] In-scope files are the only files changed (compare brief's list to git diff)
- [ ] No silent scope expansion
- [ ] Validation commands from brief PASS (run them and verify)
- [ ] Cross-capability impacts noted
### Output
Write Stage 1 section of agent-artifacts/test-reports/<bd-id>.md.
Use the test-report-template skill format. Required:
- ## Stage 1: Spec Compliance
- PASS or FIX_REQUIRED line
- ### Requirements covered (checkbox list)
- ### Scope review (in-scope/out-of-scope summary)
- ### Validation commands (table with results)
- ### Stage 1 issues (empty if PASS; specific issues if FIX_REQUIRED)
(Pause — orchestrator dispatches Stage 2 separately as a fresh subagent run.)
## Stage 2: Code Quality
### Inputs to load (DELIBERATELY MINIMAL)
- `git diff` ONLY.
- Do NOT re-read the task brief.
- Do NOT re-read the change summary.
- This is a brief-blind review.
The reason: brief-aware review tends to rationalize complexity. Brief-blind review
sees the code as a reviewer who hasn't seen the spec.
### Checklist (from subagent-review-checklists skill, Stage 2)
- [ ] Idiomatic to project's existing style
- [ ] No dead code / debug logs / commented-out experimental code
- [ ] No secrets / log leaks / unsafe deserialization
- [ ] Tests cover happy path AND at least one failure case
- [ ] Error handling matches project's error envelope
- [ ] No N+1 / unbounded loop / swallowed exception
- [ ] Imports clean (no unused)
### Output
Append Stage 2 section to the test report. Required:
- ## Stage 2: Code Quality
- PASS or FIX_REQUIRED line
- ### Code quality findings (checkbox list)
- ### Stage 2 issues (empty if PASS; specific issues with file:line if FIX_REQUIRED)
## Overall result
Append the final section:
Overall result
PASS | FIX_REQUIRED
Next recommended verb
maintain (if PASS) code-fix (if FIX_REQUIRED, with specific issue list)
**PASS requires BOTH stages PASS.** Either FIX_REQUIRED → overall FIX_REQUIRED.
The validate-test-report.mjs hook verifies the report has both stages and a valid overall result.
## Hard constraints
- Read-only. Write only to agent-artifacts/test-reports/<bd-id>.md.
- Do NOT run mutating shell commands.
- Run validation commands declared in the task brief; do not invent new ones.
- Stage 2 MUST be brief-blind. Re-loading the brief defeats the purpose.
- If you find a Stage 2 issue that Stage 1 missed (e.g., scope was actually expanded), reopen Stage 1 in your report.
## Repair semantics
If Stage 1 FAILS: orchestrator re-dispatches Builder with Stage 1 issues. Both stages re-run on repaired diff.
If Stage 2 FAILS on a Stage-1-passing diff: orchestrator re-dispatches Builder with Stage 2 issues. Both stages re-run.
Max 2 total repair attempts. After that → blocker.
## Output format
End every run with:
- Stage 1: PASS | FIX_REQUIRED (with issue count if FIX)
- Stage 2: PASS | FIX_REQUIRED (with issue count if FIX)
- Overall: PASS | FIX_REQUIRED
- Report path
- Next recommended verb
49. Agent: maintainer.md
---
name: maintainer
description: |
Handles cleanup, documentation updates, changelog, version recommendations, and
the spec-delta merge step. Asks ONE consolidated confirmation per task. Auto-invoked
after Tester-Reviewer PASS in /implement-task. Also invoked by /maintain.
model: sonnet
tools: Read, Edit, Write, Bash, Grep, Glob
mcpServers:
- beads
permissionMode: default
maxTurns: 6
isolation: false
skills:
- changelog-format
- api-docs-format
- maintenance-report-template
- delta-spec-format
- beads-queries
color: orange
---
You are the Maintainer.
## Required steps
### Step 1: Load context
- The task brief
- agent-artifacts/change-summaries/<bd-id>.md
- agent-artifacts/test-reports/<bd-id>.md (verify Overall is PASS)
- `git diff`
- docs/05-api-docs.md, docs/06-tech-docs.md, docs/07-testing.md
- CHANGELOG.md
- agent-artifacts/changes/<change-id>/delta-specs/<capability>.md
- docs/specs/<capability>/spec.md (current truth, target for merge)
### Step 2: Detect what's needed
Identify:
- Cleanup actions (debug logs, scratch files within in-scope files)
- API doc updates (if endpoints changed)
- Tech doc updates (if operational behavior changed)
- Testing doc updates (rare; if testing strategy changed)
- Spec delta merge (always, if Stage 1 PASS)
- Changelog entry needed
- Version recommendation (none / patch / minor / major)
### Step 3: Compose consolidated confirmation
Output exactly this block:
MAINTAINER CONFIRMATION REQUIRED
Detected:
- API changed:
Proposed actions:
1. Update docs/05-api-docs.md (
Choose:
A. Approve all
B. Modify (specify which to skip or change)
C. Skip docs (do cleanup + changelog + version only)
D. Bump
### Step 4: Wait for user choice
Do not proceed until user responds with A/B/C/D/E or specific modifications.
### Step 5: Apply approved actions
#### Cleanup
Remove debug logs (specific lines, never whole files unless approved).
Remove temp files in scratch/ or matching *.debug.*, *.scratch.*, *~, *.bak.
#### Docs updates
- docs/05-api-docs.md: Add/update endpoint sections following api-docs-format skill.
- docs/06-tech-docs.md: Update operational notes.
- docs/07-testing.md: Update if testing strategy changed.
#### Spec delta merge (the critical step)
For each requirement in delta-specs/<capability>.md:
- **## ADDED**: Append the requirement (with its scenarios) to docs/specs/<capability>/spec.md
- **## MODIFIED**: Find the matching `### Requirement: <name>` in spec.md; replace its content
- **## REMOVED**: Find and delete the matching `### Requirement: <name>` block
Use the delta-spec-format skill for exact matching rules.
After merging:
- Regenerate docs/specs/<capability>/spec-summary.md (compact version of updated spec.md)
- Verify the spec is still valid (no orphan scenarios, no duplicate names)
#### Archive change folder (only if all tasks in this change are DONE)
Check: `bd list --label change:<change-id> --status NOT_DONE --json`
If empty:
```bash
mv agent-artifacts/changes/<change-id>/ agent-artifacts/changes/archive/<date>-<change-id>/
Changelog
Add entry under [Unreleased]:
### Added
- <feature> (<bd-id list>)
### Changed
- <change> (<bd-id list>)
Version bump (only if approved)
Verify VERSION_BUMP_APPROVED=1 in environment. Edit package.json "version" field.
Close Beads task
bd update <bd-id> --status DONE
Step 6: Final validation
Run all validation commands from the brief once more: - npm run lint - npm run typecheck - npm test
If any fails: STOP, do not write maintenance report. The merge or doc updates may have broken something.
Step 7: Write maintenance report
Create agent-artifacts/maintenance-reports/
Step 8: Stop
The validate-maintenance-report.mjs hook will verify the report exists.
Hard constraints
- Must ask before version change (consolidated confirmation includes the ask).
- Must ask before merging delta if Stage 1 was FIX_REQUIRED (defensive check).
- Must ask before deleting tracked non-temp files.
- Must ask before changing or superseding an ADR.
- Cannot deploy, tag releases, or publish.
- Spec delta merge is MECHANICAL (ADDED → append, MODIFIED → replace, REMOVED → delete). Not LLM creative editing.
- If final validation fails after merge/cleanup: STOP. Do not write maintenance report claiming PASS.
Monthly tasks (outside per-task flow)
On manual trigger:
- bd compact --older-than 30d (ask user before first run)
- Review agent-artifacts/changes/archive/ (awareness only)
- Optional: archive docs/02-research.md sections older than 6 months
Output format
End every run with: - User approval choice (A/B/C/D/E) - Actions applied (numbered list) - Files modified - Spec delta merge summary (N ADDED, M MODIFIED, K REMOVED) - Version bumped (yes/no, from X to Y) - Final validation result - Path to maintenance report - Recommended next command
---
## 50. Agent: `frontend-browser-tester.md`
```markdown
---
name: frontend-browser-tester
description: |
Browser-driven UI testing via Playwright MCP. Read-only. Invoked by Tester-Reviewer
for UI tasks, or directly by /test-review when frontend changes are involved.
Optional role — only used in projects with browser-testable UI.
model: sonnet
tools: Read, Bash
mcpServers:
- playwright
permissionMode: plan
maxTurns: 6
isolation: false
skills:
- testing-conventions
color: pink
---
You are the Frontend-Browser-Tester. Read-only.
## When to run
- Tasks involving UI changes
- Regression testing of user flows
- E2E tests that need a real browser
## When NOT to run
- Backend-only changes
- API-only tasks
- Unit tests cover what's needed
## Required steps
1. Read the task brief and change summary.
2. Identify which UI flows are affected.
3. Use Playwright MCP to:
- Launch the dev server if not running
- Navigate through affected flows
- Verify visible behavior matches acceptance criteria
- Capture screenshots if helpful
4. Append findings to agent-artifacts/test-reports/<bd-id>.md as a "## Stage 3: Browser Verification" section. This is in addition to Stages 1 and 2; it does not replace them.
5. Stop.
## Hard constraints
- Read-only. permissionMode: plan enforces this.
- No source code edits.
- No deployment, no production URLs.
- Use Playwright only against local dev servers or staging URLs configured in CLAUDE.md.
## Output format
- ## Stage 3: Browser Verification
- PASS | FIX_REQUIRED
- Flows tested
- Findings with screenshot paths if captured
- Issues (empty if PASS)
51. All 18 SKILL.md Files
51.1 architecture-summary/SKILL.md
---
name: architecture-summary
description: |
Architecture conventions for this project: layering (Controller → Service → Repository),
validation rules, error envelope, response envelope, testing requirements, module
boundaries, critical ADR references. Auto-loads when implementing or reviewing backend
tasks, modifying controllers/services/repositories, or asking about architecture.
Do NOT auto-load for: frontend-only tasks, documentation tasks, pure research tasks,
intake classification.
allowed-tools: Read, Grep, Glob
---
# Architecture Summary
## Layering pattern
Controller → Service → Repository.
- **Controllers** validate input (Zod), call services, format responses with the error envelope, return HTTP.
- **Services** contain business logic. No HTTP awareness. No direct DB access.
- **Repositories** wrap DB access. Return domain types, not raw DB rows.
## Module list
- (Customize per project — list main capabilities and their src/ paths)
## Data ownership
- Each module owns its own data. Cross-module data access goes through services, not direct DB queries.
## Validation
- Library: Zod
- Location: Controller boundary, before service call.
- Schema naming: `<entityName>Schema`
- Refinements: Use `.refine()` for complex rules; never inline in business logic.
## Error envelope
```typescript
{
error: {
code: string, // machine-readable, e.g., "VALIDATION_FAILED"
message: string, // human-readable
details?: unknown // optional context
}
}
Response envelope (success)
{
data: T
}
Testing rules
- Unit tests: per file,
<name>.test.tscolocated. - Integration tests: per controller, in
tests/integration/. - Coverage target: 80% lines for services.
Critical ADRs
- (List relevant ADR slugs here once written)
Anti-patterns (do not do)
- Direct DB queries from controllers
- Validation in services (controllers own validation)
- Throwing strings (always throw Error subclasses)
- Catching and ignoring errors (must rethrow or log structurally)
### 51.2 `testing-conventions/SKILL.md`
```markdown
---
name: testing-conventions
description: |
How tests are structured in this project. Test runner, file naming, mocking
approach, coverage targets. Auto-loads when writing or reviewing tests.
Do NOT auto-load for: pure docs tasks, intake, tasks with no test impact.
allowed-tools: Read, Grep, Glob
---
# Testing Conventions
## Test runner
- **Unit/integration:** jest (TypeScript projects) or pytest (Python)
- **E2E:** Playwright
## File naming
- Unit: `<source-file>.test.ts` colocated with source
- Integration: `tests/integration/<feature>.test.ts`
- E2E: `tests/e2e/<flow>.spec.ts`
## Test structure
```typescript
describe('<unit under test>', () => {
describe('<scenario or method>', () => {
it('<expected behavior>', () => {
// arrange, act, assert
});
});
});
Mocking
- Mock at the boundary (repository for service tests; HTTP for integration tests)
- Use
jest.fn()or test doubles; avoidjest.mock()of internal modules unless necessary - Never mock business logic of the unit under test
Test data
- Use factories in
tests/factories/ - Avoid hardcoding IDs across tests; use factory-generated UUIDs
Coverage targets
- Services: 80% line coverage minimum
- Controllers: every endpoint has at least 1 happy + 1 failure test
- Utilities: 90% line coverage
Required tests per task
Every implementation task must include: - At least one happy-path test - At least one failure-mode test - For new endpoints: an integration test
Commands
npm test— full suitenpm test -- <pattern>— filterednpm run test:integration— integration onlynpm run test:e2e— e2e (requires dev server)
### 51.3 `validation-patterns/SKILL.md`
```markdown
---
name: validation-patterns
description: |
Zod validation conventions used in this project. Where schemas live, naming,
refinement patterns, error message style. Auto-loads when working on controllers,
API endpoints, or any input validation. Do NOT auto-load for: internal utilities,
pure infrastructure tasks, frontend-only tasks.
allowed-tools: Read, Grep, Glob
---
# Validation Patterns
## Library
Zod (TypeScript) or Pydantic v2 (Python).
## Location
- Controllers: validate request bodies, params, query at controller boundary.
- Services: NEVER validate. Services trust controller output.
- Repositories: NEVER validate user input. Sanitize via parameterized queries.
## Schema naming
- `<EntityName>Schema` (e.g., `CreateBookingSchema`)
- One schema per request type
- Schemas live in `src/<module>/schemas/` or alongside controllers
## Common patterns
### String constraints
```typescript
z.string().min(1).max(100).trim()
Optional fields with defaults
z.string().optional().default('')
Discriminated unions
z.discriminatedUnion('type', [
z.object({ type: z.literal('a'), aField: z.string() }),
z.object({ type: z.literal('b'), bField: z.number() }),
])
Refinements (complex rules)
z.object({ start: z.date(), end: z.date() })
.refine(d => d.end > d.start, 'end must be after start')
Error mapping
On validation failure, return:
{
error: {
code: 'VALIDATION_FAILED',
message: 'Invalid request',
details: <zod-formatted errors>
}
}
With HTTP 400.
Anti-patterns (do not do)
- Validation in services
- Manual validation logic when a schema would suffice
- Throwing raw Zod errors to the user (use the error envelope)
- Validation in repositories
### 51.4 `error-envelope/SKILL.md`
```markdown
---
name: error-envelope
description: |
Error response format for this project. Standard error envelope, HTTP status
mapping, logging conventions. Auto-loads when working on controllers, API
responses, or error handling. Do NOT auto-load for: pure internal logic with
no error surface, tasks with no API impact.
allowed-tools: Read, Grep, Glob
---
# Error Envelope
## Structure
```typescript
{
error: {
code: string, // machine-readable
message: string, // human-readable (i18n-ready)
details?: unknown // optional context (validation errors, IDs)
}
}
HTTP status mapping
| Code | HTTP | When |
|---|---|---|
VALIDATION_FAILED |
400 | Schema validation failed |
UNAUTHENTICATED |
401 | No or invalid token |
FORBIDDEN |
403 | Authenticated but not authorized |
NOT_FOUND |
404 | Resource doesn't exist |
CONFLICT |
409 | State conflict (duplicate, etc.) |
RATE_LIMITED |
429 | Too many requests |
INTERNAL_ERROR |
500 | Unexpected error |
SERVICE_UNAVAILABLE |
503 | Downstream dependency failure |
Code naming
- UPPER_SNAKE_CASE
- Specific over generic (
EMAIL_NOT_VERIFIEDbetter thanAUTH_ERROR)
Logging
- Log structured:
{ level, code, message, requestId, stack? } - Never log secrets, tokens, passwords, full request bodies that might contain PII
- 5xx errors → log at error level with stack
- 4xx errors → log at warn level (or info if expected)
Try/catch pattern (controllers)
try {
const result = await service.doThing(input);
return { data: result };
} catch (err) {
if (err instanceof DomainError) {
return { error: { code: err.code, message: err.message } };
}
logger.error({ err }, 'Unexpected error in <controller>');
return { error: { code: 'INTERNAL_ERROR', message: 'Something went wrong' } };
}
Anti-patterns
- Throwing strings
- Catching errors and ignoring them
- Leaking internal stack traces to clients
- Generic
AUTH_ERRORinstead of specific codes
### 51.5 `changelog-format/SKILL.md`
```markdown
---
name: changelog-format
description: |
Keep-a-changelog conventions. Section headers, bd-id references, version sections.
Auto-loads when updating CHANGELOG.md. Do NOT auto-load for: tasks with no
user-visible change.
allowed-tools: Read, Grep, Glob
---
# Changelog Format
Following keep-a-changelog v1.1.0.
## Structure
```markdown
# Changelog
## [Unreleased]
### Added
- New feature description (bd-a1b2)
### Changed
- Modified behavior (bd-c3d4)
### Deprecated
- About-to-be-removed feature
### Removed
- Removed feature
### Fixed
- Bug fix (bd-e5f6)
### Security
- Security fix
## [1.4.0] — 2026-05-15
### Added
- ...
Entry format
- Start with a verb in past tense ("Added", "Fixed", "Changed")
- Include bd-id reference at end in parentheses
- One line per entry; group related changes into one entry where possible
When Maintainer adds entries
- Always under [Unreleased] until release
- On release: move all [Unreleased] entries under new version section with date
- Update package.json version in same commit
### 51.6 `api-docs-format/SKILL.md`
```markdown
---
name: api-docs-format
description: |
API documentation style for docs/05-api-docs.md. Endpoint structure,
request/response shape conventions, auth requirements. Auto-loads when
updating docs/05-api-docs.md. Do NOT auto-load for: internal-only changes.
allowed-tools: Read, Grep, Glob
---
# API Docs Format
## Per-endpoint structure
```markdown
## POST /auth/verify-email
**Purpose:** Verify a user's email via signed token.
**Auth:** None (token is the auth)
**Request body:**
```json
{
"token": "string (JWT)"
}
Success response (200):
{
"data": {
"verified": true,
"userId": "uuid"
}
}
Error responses: - 400 VALIDATION_FAILED — token missing or malformed - 410 TOKEN_EXPIRED — token older than 24 hours - 404 USER_NOT_FOUND — token references non-existent user
Example:
curl -X POST https://api.example.com/auth/verify-email \
-H "Content-Type: application/json" \
-d '{"token":"eyJ..."}'
Related ADRs: ADR-0007
## Organization
- Group by resource (`# Auth`, `# Bookings`, `# Cities`)
- Within each resource, order endpoints by frequency of use, not alphabetically
- New endpoints go in the right resource section, not at the bottom
## Maintainer responsibility
- Update on every API change
- Verify examples match current behavior (run curl against dev server when in doubt)
51.7 task-brief-template/SKILL.md
---
name: task-brief-template
description: |
Required format for task briefs. Auto-loads when Planner is writing a task brief.
Do NOT auto-load for: any role other than Planner during decomposition.
allowed-tools: Read, Grep, Glob
---
# Task Brief Template
Every task brief at agent-artifacts/task-briefs/<bd-id>-<slug>.md MUST include:
```markdown
# Task Brief: <bd-id> — <human-readable title>
## Beads metadata
- **bd-id:** <bd-id>
- **change-id:** <change-id>
- **Affected spec capability:** <capability>
- **Dependencies (blocks):** <bd-ids or "none">
- **Phase / Epic:** <bd-epic-id>
## Goal
<1–3 sentences. What this task accomplishes.>
## Required context
- <file>: <why needed>
- <doc>: <why needed>
- <bd message>: <why needed>
## In-scope files
- <path>: <new|modify>
- <path>: <new|modify>
## Out-of-scope files
- <path>: <why explicitly excluded>
## Required practices
- Architecture pattern: <e.g., Controller → Service → Repository>
- Validation: <e.g., Zod schemas at controller boundary>
- Error envelope: <e.g., { error: { code, message } }>
- Tests: <e.g., jest, unit + integration>
## Acceptance criteria
- [ ] <specific testable criterion>
- [ ] <specific testable criterion>
## Validation commands
- <cmd>
- <cmd>
## Docs impact
- <doc to update by Maintainer>
## Version impact
- <none | patch | minor | major>
## Stop conditions
- <task-specific stop condition beyond standard watchdog>
Quality bar
- In-scope and Out-of-scope MUST both be specified
- Acceptance criteria MUST be testable (no "code is clean")
- Validation commands MUST be commands that exist in package.json or are standard
- The brief must be readable by Builder without any other context except the listed Required context files
### 51.8 `change-summary-template/SKILL.md`
```markdown
---
name: change-summary-template
description: |
Required format for change summaries. Auto-loads when Builder is finishing a task.
Do NOT auto-load for: other roles.
allowed-tools: Read, Grep, Glob
---
# Change Summary Template
Every change summary at agent-artifacts/change-summaries/<bd-id>.md MUST include:
```markdown
# Change Summary: <bd-id>
## Task
**bd-id:** <bd-id>
**Title:** <title>
**Change-id:** <change-id>
## Status
DONE | BLOCKED
## Files changed
- <path> (new|modify, +N lines, -M lines)
## Behavior added / changed
- <bullet>
## Tests added / changed
- <bullet>
## Validation run
- <command>: <result> (<details>)
## Known risks
- <risk> | (none)
## Docs impact
- <docs to update> | (handled by Maintainer)
## Version recommendation
- <none | patch | minor | major>
## Discovered work filed in Beads
- <bd-id>: <title> (<rel type>)
- (none)
Quality bar
- File counts must be accurate (use
git diff --stat) - Validation results must be from actual command runs, not assumed
- Discovered work must be filed in Beads BEFORE writing the summary
- If status is BLOCKED, include why and what's needed to unblock
### 51.9 `test-report-template/SKILL.md` (UPDATED in v4)
```markdown
---
name: test-report-template
description: |
Required format for two-stage test reports. Auto-loads when Tester-Reviewer is
writing the test report. Do NOT auto-load for: other roles.
allowed-tools: Read, Grep, Glob
---
# Test Report Template (Two-Stage)
Every test report at agent-artifacts/test-reports/<bd-id>.md MUST include:
```markdown
# Test Report: <bd-id>
## Stage 1: Spec Compliance
PASS | FIX_REQUIRED
### Requirements covered
- [✓|✗] <requirement from delta spec or acceptance criteria>: <evidence or issue>
### Scope review
- In-scope files changed: <list> (matches brief: yes|no)
- Out-of-scope files touched: <list, should be empty>
### Validation commands
| Command | Result |
|---|---|
| <cmd> | PASS|FAIL — <brief note> |
### Stage 1 issues
(empty if PASS; otherwise specific issues with file:line)
## Stage 2: Code Quality
PASS | FIX_REQUIRED
### Code quality findings
- [✓|✗] Idiomatic to project style
- [✓|✗] No dead code / debug logs
- [✓|✗] No secrets / log leaks
- [✓|✗] Tests cover happy + failure
- [✓|✗] Error envelope conformance
- [✓|✗] No N+1 / unbounded loops / swallowed exceptions
- [✓|✗] Imports clean
### Stage 2 issues
(empty if PASS; otherwise specific issues with file:line)
## Overall result
PASS | FIX_REQUIRED
## Next recommended verb
maintain (if PASS)
code-fix (if FIX_REQUIRED, with specific issue list)
Hard rules
- Both Stage 1 AND Stage 2 sections MUST be present
- Both must have PASS or FIX_REQUIRED line
- Overall PASS requires BOTH stages PASS
- validate-test-report.mjs hook will block stop without all required sections
### 51.10 `maintenance-report-template/SKILL.md`
```markdown
---
name: maintenance-report-template
description: |
Required format for maintenance reports. Auto-loads when Maintainer is finishing.
Do NOT auto-load for: other roles.
allowed-tools: Read, Grep, Glob
---
# Maintenance Report Template
Every report at agent-artifacts/maintenance-reports/<bd-id>.md MUST include:
```markdown
# Maintenance Report: <bd-id>
## Cleanup actions taken
- <bullet> | (none)
## Documentation updates
- <doc>: <change summary>
- (none if applicable)
## Spec delta merge
- Capability: <capability>
- Merged from: agent-artifacts/changes/<change-id>/delta-specs/<capability>.md
- Applied: <N> ## ADDED, <N> ## MODIFIED, <N> ## REMOVED
- Regenerated: docs/specs/<capability>/spec-summary.md
- Archived change to: <path> (only if all tasks DONE; else "not yet — N tasks remaining")
## Changelog entry
<exact text appended to CHANGELOG.md>
## Version decision
- Recommended: <none|patch|minor|major>
- User approved: <YES|NO>
- Applied: <yes/no, and if yes, X.Y.Z → X.Y.Z>
## Beads
- bd update <bd-id> --status DONE
## Final validation
- <cmd>: <result>
## Remaining risks
- <bullet> | (none)
### 51.11 `review-report-template/SKILL.md`
```markdown
---
name: review-report-template
description: |
Generic review report format for non-standard reviews (manual reviews, special
audits). Auto-loads alongside test-report-template for Tester-Reviewer.
Do NOT auto-load for: standard test reports (use test-report-template instead).
allowed-tools: Read, Grep, Glob
---
# Generic Review Report Template
For ad-hoc or non-standard reviews. Format:
```markdown
# Review: <subject>
**Date:** YYYY-MM-DD
**Reviewer:** <role or name>
**Scope:** <what was reviewed>
## Findings
- <file:line> — <issue or observation>
## Recommendations
- <bullet>
## Risk assessment
- <bullet>
For the standard two-stage Tester-Reviewer report, use test-report-template instead.
### 51.12 `blocker-template/SKILL.md`
```markdown
---
name: blocker-template
description: |
Format for blocker reports. Auto-loads when a blocker needs to be filed manually.
Do NOT auto-load for: routine work without blocker.
allowed-tools: Read, Grep, Glob
---
# Blocker Template
Every blocker at agent-artifacts/blockers/blocker-<bd-id>-<timestamp>.md MUST include:
```markdown
# Blocker: <bd-id> — <short description>
**Status:** BLOCKED
**Created:** <ISO timestamp>
**Triggered by:** <hook script | agent decision | manual>
## Reason
<Why this is a blocker. Be specific.>
## Evidence
- <log lines, file paths, error messages with file:line>
- <bd-id reference if related to other tasks>
## Recommended action
- <Next step suggestion>
## Resolution
(Filled in when the blocker is resolved.)
When to file a blocker (vs continue)
File a blocker when: - A watchdog rule fires (automatic via hook) - Task brief is ambiguous beyond reasonable interpretation - Out-of-scope work seems necessary but you cannot proceed without it - Same failure recurs after 2 repair attempts - A required dependency is missing or wrong - You discover a security/safety issue mid-task
Do NOT file a blocker when: - You hit a snag but can work around it - The task is just hard but doable - You'd prefer a different approach but the brief specifies one
Resolving a blocker
When a blocker is resolved: 1. Append "## Resolution" section with what was done 2. Move the file to agent-artifacts/blockers/resolved/ 3. File a bd message linking the blocker to the resolution
### 51.13 `digest/SKILL.md` (NEW in v4)
```markdown
---
name: digest
description: |
Procedure for the intake.digest sub-stage. Inventories source documents and existing
codebase. Extracts requirements with citations. Proposes capability decomposition.
Auto-loads when /intake is run with input docs or --brownfield flag.
Do NOT auto-load for: standard /research, /implement-task, /maintain.
allowed-tools: Read, Grep, Glob, WebFetch
---
# Digest Procedure
## Phase 1: Inventory
### Source documents
For each input doc:
1. Record path
2. Compute SHA (use `sha256sum <file> | cut -c1-12` or read+hash in Node)
3. Record last-modified
4. Identify type (BRD, PRD, design doc, RFC, email, transcript)
### Existing codebase (if brownfield)
For each top-level module under src/:
1. Path
2. Approximate LOC (use `find <dir> -name '*.ts' | xargs wc -l` or equivalent)
3. Purpose (read README or top-of-file comment; do not guess)
Write the inventory tables in the digest report immediately. This is your evidence base.
## Phase 2: Extraction
For each requirement, business rule, constraint, or non-goal:
1. Locate the exact passage in source docs
2. Quote or paraphrase succinctly
3. Cite with `[<doc>:<line>]` — line numbers required, not just file
4. If multiple sources support the claim, cite all
5. If you cannot cite, do NOT include the claim. Mark it under "Missing from source docs" instead.
The `validate-source-doc-cited.mjs` hook checks for uncited claims as you write. Lines matching `must`, `shall`, `should`, `required`, `forbidden` without a `[...]` citation marker will block the write.
## Phase 3: Capability decomposition
Propose a split of the system into capabilities:
- A capability is a coherent feature area with its own spec
- Examples: `user-auth`, `booking-flow`, `payment-processing`
- Anti-examples: `helpers`, `utils`, `infrastructure` (these are cross-cutting)
For each capability:
- Name (slug)
- One-line purpose
- Source-doc citations that motivate it
- Approximate size (small/medium/large)
## Phase 4: Conflict and gap detection
### Ambiguities
List things where the source docs are unclear:
- "Doc A says X; Doc B says Y; unclear which is canonical"
- "The token expiry is mentioned but not specified"
### Missing
List things that will need user elicitation:
- "No mention of password complexity requirements"
- "Email verification mentioned but no flow specified"
### ADR conflicts
For each existing ADR in docs/decisions/:
- Does the source doc contradict it?
- If yes: stop the digest, surface the conflict, ask user which is canonical
## Phase 5: Verbatim preservation
For legal, contractual, SLA, or regulatory language that must be preserved word-for-word:
- Copy the exact text into the "Verbatim quotes preserved" section
- Cite with `[<doc>:<line>]`
- These quotes are treated as priority-7 source-of-truth (immutable input)
- Builder must use exact wording when implementing related features
## Phase 6: Write the report
Use the digest-report-template skill format. Save to:
`agent-artifacts/digest-reports/<intake-id>.md`
Where `<intake-id>` is like `2026-05-11-initial-intake` or `2026-05-11-add-payment`.
## Phase 7: Update state
Write `agent-artifacts/monitoring/digest-state.json`:
```json
{
"intakeId": "<intake-id>",
"status": "done",
"sourceDocs": ["path1", "path2"],
"phase": "done",
"lastCheckpoint": <timestamp>
}
If digest runs across multiple sessions, update phase and lastCheckpoint as you progress to allow resumability.
Phase 8: Stop
Present the digest report to the user. Wait for confirmation before /intake proceeds to STEP 3 (change proposal).
Hard constraints
- Cite [doc:line] for every requirement / business rule / constraint claim
- Do not infer requirements not present in source docs (mark under "Missing" instead)
- Do not silently resolve ADR conflicts — surface them
- Do not paraphrase verbatim-preservation content
- Do not skip the SHA inventory — it's the audit trail
### 51.14 `digest-report-template/SKILL.md` (NEW in v4)
```markdown
---
name: digest-report-template
description: |
Required format for digest reports. Auto-loads alongside the digest skill during
intake digest mode. Do NOT auto-load outside digest mode.
allowed-tools: Read, Grep, Glob
---
# Digest Report Template
Every digest report at agent-artifacts/digest-reports/<intake-id>.md MUST include:
```markdown
# Digest Report: <intake-id>
**Date:** YYYY-MM-DD
**Mode:** brownfield-with-docs | brownfield-no-docs | greenfield-with-docs
## Source documents inventory
| Path | Type | Last modified | SHA |
|---|---|---|---|
| docs/incoming/brd.pdf | BRD | 2026-04-12 | a1b2c3d4e5f6 |
## Existing codebase inventory (brownfield only)
| Module | Path | Purpose | LOC |
|---|---|---|---|
| auth | src/auth/ | User authentication and sessions | 1,240 |
## Extracted requirements
### Users
- <persona>: <one-line description> [<doc>:<line>]
### Core use cases
- <use case>: <flow summary> [<doc>:<line>]
### Non-goals
- <bullet> [<doc>:<line>]
### Business rules
- <rule> [<doc>:<line>]
### Constraints
- <constraint> [<doc>:<line>]
## Capability decomposition (proposed)
- **<slug>**: <one-line purpose> — derived from [<doc>:<line>]
- **<slug>**: <one-line purpose> — derived from [<doc>:<line>]
## Ambiguities (require user clarification)
- <bullet>: <what's unclear>
## Missing from source docs (will need elicitation)
- <bullet>
## Conflicts with existing system / ADRs (review needed)
- <bullet>: <which ADR or assumption is contradicted>
## Verbatim quotes preserved
- "<quote>" — [<doc>:<line>]
## Recommended next stage
- /architecture (if architecture decisions are needed before plan)
- /plan (if architecture is well-defined and ready for task decomposition)
- further /research (if more codebase exploration needed)
- user elicitation (if too many ambiguities to proceed)
Quality bar (validate-digest-report.mjs enforces)
- Source documents inventory section MUST be non-empty
- Every claim in Extracted requirements MUST have a [doc:line] citation
- If brownfield, Existing codebase inventory MUST be present
- If "Conflicts with existing ADRs" is non-empty, /intake stops for user reconciliation
### 51.15 `beads-queries/SKILL.md` (NEW in v4)
```markdown
---
name: beads-queries
description: |
Canonical bd commands the orchestrator uses. Task queries, dependencies, messages,
context priming. Auto-loads when working with Beads (most tasks). Do NOT auto-load
for: pure docs tasks with no Beads interaction.
allowed-tools: Bash
---
# Beads Queries Reference
## Initialization
```bash
bd init # First-time setup
bd doctor # Verify health
bd doctor --fix # Auto-fix common issues
Reading state
bd ready --json # Tasks unblocked and ready (NEXT status, deps DONE)
bd blocked --json # Tasks waiting on blocked dependencies
bd list --status TODO --json
bd list --status DONE --json
bd list --label change:<change-id> --json
bd list --label capability:<capability> --json
bd list --type message --search <topic>
bd show <bd-id> # Full issue + linked messages
bd dep tree <bd-id> # Visual dependency tree
bd graph # Full graph (for debugging)
Creating items
# Tasks
bd create --type task --title "<title>" --priority p1|p2|p3
# Tasks with labels
bd create --type task --title "<title>" \
--label change:<change-id> \
--label capability:<capability>
# Bugs
bd create --type bug --title "<title>" --severity sev1|sev2|sev3
# Epics (grouping)
bd create --type epic --title "Phase 1"
# Messages (decisions, discovered work, threads)
bd create --type message --title "<title>" --body "<text>"
bd create --type message --rel-from <bd-id> --rel discovered-from --title "..."
bd create --type message --rel-from <bd-id> --rel design-decision --title "..."
bd create --type message --label considered --title "..." # speculation
Updating
bd update <bd-id> --status NEXT|IN_PROGRESS|DONE|BLOCKED
bd update <bd-id> --priority p1|p2|p3
bd close <bd-id> # Alias for --status DONE
Dependencies
bd dep add <a> <b> --type blocks # a blocks b
bd dep add <a> <b> --type parent-child # a is parent of b (epic→task)
bd dep add <a> <b> --type related # soft link
bd dep add <a> <b> --type discovered-from # a was found while doing b
bd dep add <a> <b> --type supersedes # a replaces b
bd dep rm <a> <b>
Context injection
bd prime --task <bd-id> --max-tokens 1500 # Curated context for the active task
Memory decay
bd compact --older-than 30d # LLM-summarize old issues
bd compact --older-than 90d --max-summary-tokens 100
Distributed (optional)
bd dolt push # Push to shared Dolt server
bd dolt pull # Pull from shared Dolt server
bd --remote query --label pattern --search <topic> # Query shared namespace
Setup integration
bd setup claude # Generate CLAUDE.md instructions for Beads
bd setup codex # Codex CLI integration
bd setup factory # Factory.ai Droid integration
bd setup cursor # Cursor integration
bd setup mux # AMP/Mux integration
Anti-patterns
- Do NOT use
bdfor things that belong indocs/decisions/(ADRs) - Do NOT use
bdfor spec content (usedocs/specs/<capability>/spec.md) - Do NOT use
bdfor generated reports (those live in agent-artifacts/) - Do NOT bulk-create more than ~50 tasks at once — break into phases
### 51.16 `decision-capture/SKILL.md` (NEW in v4)
```markdown
---
name: decision-capture
description: |
When to file a Beads message vs a formal ADR. Decision rubric and templates.
Auto-loads when a decision is being made during work (architecture, design,
implementation). Do NOT auto-load for: pure docs updates.
allowed-tools: Bash
---
# Decision Capture Rubric
## Three decision types
### Type 1: Durable architectural decision → ADR
**Examples:**
- Choice of database, framework, language
- Layering pattern (Controller → Service → Repository)
- Error envelope format
- Authentication strategy
**Where:** `docs/decisions/NNNN-<slug>.md`
**Who:** Architect
**Lifecycle:** Immutable; superseded by new ADRs
**Trigger:** "This decision will outlive this feature and affect future work."
### Type 2: Local design choice within a feature → bd message
**Examples:**
- "Used connection pool size 10 because three services share Postgres"
- "Token expiry tolerance set to 5 minutes for clock skew"
- "Chose cursor pagination over offset for booking list (volume estimate)"
**Where:** `bd create --type message --rel-from <bd-id> --rel design-decision`
**Who:** Architect (during intake) or Builder (during implementation)
**Lifecycle:** Persistent; queryable; not immutable
**Trigger:** "This is a real decision but it's local to this feature."
### Type 3: Speculation / dead-ends → bd message with label
**Examples:**
- "Considered Redis for caching, deferred to Phase 2 due to deployment complexity"
- "Tried JWT in URL for verification link, doesn't work with SSO (RFC 6749 §4)"
- "Wanted to add rate limiting at the controller, but the architecture prefers middleware"
**Where:** `bd create --type message --label considered` or `--label dead-end`
**Who:** Anyone
**Lifecycle:** Persistent; bd compact may summarize after 30 days
**Trigger:** "I want this thought captured but it didn't result in a change."
## Decision tree
Was a decision made? ├── No → don't file anything └── Yes ├── Will it affect future features (cross-cutting)? │ ├── Yes → ADR (Type 1) │ └── No │ ├── Was it actually applied to code? │ │ ├── Yes → bd message (Type 2) │ │ └── No │ │ ├── Did it lead to a different outcome? │ │ │ ├── Yes → bd message --label considered (Type 3) │ │ │ └── No (just musing) → don't file
## ADR template (Type 1)
See `docs/decisions/0000-template.md`.
## bd message template (Type 2)
```bash
bd create --type message \
--rel-from <bd-id> \
--rel design-decision \
--title "<concise title>" \
--body "<context>
DECISION: <what was decided>
ALTERNATIVES CONSIDERED:
- <alt>: <why not>
CONSEQUENCES:
- <consequence>"
bd message template (Type 3)
bd create --type message \
--label considered \
--rel-from <bd-id> \
--rel discovered-from \
--title "Considered <X>, chose <Y>" \
--body "<context and reasoning>"
Anti-patterns
- Don't file an ADR for every implementation choice — they should be rare and durable
- Don't file 20 bd messages per task — be selective
- Don't skip filing because "it's obvious" — future-you isn't current-you
- Don't paraphrase what's already in source code; reference file:line instead
### 51.17 `delta-spec-format/SKILL.md` (NEW in v4)
```markdown
---
name: delta-spec-format
description: |
Format for delta specs (## ADDED / ## MODIFIED / ## REMOVED Requirements).
Auto-loads when Planner is writing a delta spec or Maintainer is merging one.
Do NOT auto-load for: other roles.
allowed-tools: Read, Grep, Glob
---
# Delta Spec Format
## Purpose
Delta specs describe what's CHANGING in a capability. They live in
`agent-artifacts/changes/<change-id>/delta-specs/<capability>.md` and get merged
into `docs/specs/<capability>/spec.md` by Maintainer after PASS.
## Full template
```markdown
# Delta Spec: <capability>
## Change: <change-id>
<one-line summary of what this change does to this capability>
## ADDED Requirements
### Requirement: <name>
<short statement of what must be true>
#### Scenario: <descriptor>
- GIVEN <precondition>
- WHEN <action>
- THEN <expected outcome>
### Requirement: <another-name>
...
## MODIFIED Requirements
### Requirement: <name (must match existing requirement in spec.md)>
**Was:** <prior statement>
**Now:** <new statement>
**Reason:** <one-liner>
#### Scenario: <descriptor> (optional — only if scenarios change)
- GIVEN ...
- WHEN ...
- THEN ...
## REMOVED Requirements
### Requirement: <name (must match existing requirement in spec.md)>
**Was:** <prior statement>
**Reason:** <one-liner>
Matching rules (for Maintainer's merge)
When merging a delta into spec.md:
## ADDED
- Append the entire requirement block (including scenarios) to spec.md
- Place in alphabetical order within the existing Requirements section, OR at end
- Do not modify existing requirements
## MODIFIED
- Find the requirement in spec.md by exact match on
### Requirement: <name> - Replace its content (statement + scenarios) with the new content
- The header line stays the same (same name); only body changes
- If no matching requirement found: FAIL the merge, file a blocker
## REMOVED
- Find the requirement by exact match on
### Requirement: <name> - Delete the entire block (header through next header)
- If no matching requirement found: FAIL the merge (the spec is already inconsistent)
Quality bar
- Requirement names are concise (3-7 words) and unique within a spec
- Each ## ADDED or ## MODIFIED requirement has at least one Scenario
-
REMOVED requirements include the original statement (so reviewers can verify)
- The delta is mechanical to merge; no LLM creativity needed
### 51.18 `subagent-review-checklists/SKILL.md` (NEW in v4)
```markdown
---
name: subagent-review-checklists
description: |
Two checklists for the two-stage Tester-Reviewer flow: Stage 1 (spec compliance)
and Stage 2 (code quality). Auto-loads when Tester-Reviewer runs. Do NOT auto-load
for: other roles.
allowed-tools: Read, Grep, Glob
---
# Subagent Review Checklists
## Stage 1: Spec Compliance
Run AFTER reading: task brief, change summary, delta spec, git diff, spec-summary.
- [ ] Every ## ADDED requirement in the delta spec has corresponding implementation in the diff
- [ ] Every ## MODIFIED requirement's new behavior is implemented
- [ ] Every ## REMOVED requirement's old behavior is removed
- [ ] All acceptance criteria from the task brief are met (check each ☑ in the brief)
- [ ] In-scope files are the only files changed (compare brief's list to `git diff --name-only`)
- [ ] No silent scope expansion (files not in either in-scope or out-of-scope lists)
- [ ] Validation commands from the brief actually PASS (run them, verify)
- [ ] Cross-capability impacts are noted in the change summary if any
**Verdict:** PASS if all checked. FIX_REQUIRED if any unchecked.
For each unchecked item, list specific issue with file:line citation.
## Stage 2: Code Quality
Run AFTER reading: git diff ONLY. Do NOT re-read the task brief.
The reason for brief-blind: brief-aware review tends to rationalize complexity.
- [ ] Idiomatic to the project's existing style (compare against neighboring files)
- [ ] No dead code (unused imports, unreachable branches, commented-out experimental code)
- [ ] No debug logs left in (console.log, print, logger.debug)
- [ ] No secrets in code (API keys, passwords, tokens — even in test fixtures)
- [ ] No log leaks of PII (full request bodies, user emails in error logs)
- [ ] No unsafe deserialization (eval, exec, unchecked JSON.parse on untrusted input)
- [ ] Tests cover happy path AND at least one failure case
- [ ] Error handling matches project's error envelope (use error-envelope skill)
- [ ] No N+1 queries (loop with DB call inside; aggregate or join instead)
- [ ] No unbounded loops (`while (true)` without exit; iteration without limit)
- [ ] No swallowed exceptions (`catch (e) {}` without rethrow or log)
- [ ] Imports clean (no unused; ordering follows project convention)
**Verdict:** PASS if all checked. FIX_REQUIRED if any unchecked.
For each unchecked item, list specific issue with file:line citation.
## Overall verdict
PASS requires both stages PASS. Otherwise FIX_REQUIRED.
If FIX_REQUIRED, Builder repairs (max 2 attempts total across both stages).
52. Output Style Definitions
Output styles enforce structured output for specific report types.
52.1 .claude/output-styles/change-summary.md
---
name: change-summary
description: Structured format for Builder change summaries
---
# Change Summary: <bd-id>
## Task
**bd-id:** <bd-id>
**Title:** <title>
**Change-id:** <change-id>
## Status
DONE | BLOCKED
## Files changed
<git diff --stat output, summarized>
## Behavior added / changed
- <bullet>
## Tests added / changed
- <bullet>
## Validation run
| Command | Result | Details |
|---|---|---|
| <cmd> | PASS|FAIL | <brief> |
## Known risks
<bullet list or "none">
## Docs impact
<list or "handled by Maintainer">
## Version recommendation
<none|patch|minor|major>
## Discovered work filed in Beads
- <bd-id>: <title> (<rel type>)
52.2 .claude/output-styles/review-report.md
---
name: review-report
description: Two-stage test report format
---
# Test Report: <bd-id>
## Stage 1: Spec Compliance
PASS | FIX_REQUIRED
### Requirements covered
<checkbox list>
### Scope review
<text>
### Validation commands
<table>
### Stage 1 issues
<empty or list with file:line>
## Stage 2: Code Quality
PASS | FIX_REQUIRED
### Code quality findings
<checkbox list>
### Stage 2 issues
<empty or list with file:line>
## Overall result
PASS | FIX_REQUIRED
## Next recommended verb
<verb>
52.3 .claude/output-styles/maintenance-report.md
---
name: maintenance-report
description: Structured format for Maintainer reports
---
# Maintenance Report: <bd-id>
## Cleanup actions taken
<bullet list>
## Documentation updates
<bullet list>
## Spec delta merge
- Capability: <capability>
- Applied: <N ADDED, M MODIFIED, K REMOVED>
- Spec-summary regenerated: <yes/no>
- Change archived: <yes/no>
## Changelog entry
<exact text>
## Version decision
- Recommended: <level>
- Approved: <yes/no>
- Applied: <yes/no, with version transition if yes>
## Beads
- bd update <bd-id> --status DONE
## Final validation
<commands and results>
## Remaining risks
<bullet list>
52.4 .claude/output-styles/blocker-report.md
---
name: blocker-report
description: Structured format for blockers
---
# Blocker: <bd-id> — <description>
**Status:** BLOCKED
**Created:** <ISO>
**Triggered by:** <source>
## Reason
<text>
## Evidence
<bullets with file:line or log lines>
## Recommended action
<text>
## Resolution
(empty until resolved)
52.5 .claude/output-styles/digest-report.md
---
name: digest-report
description: Structured format for intake digest reports
---
# Digest Report: <intake-id>
**Date:** YYYY-MM-DD
**Mode:** <brownfield-with-docs|brownfield-no-docs|greenfield-with-docs>
## Source documents inventory
<table>
## Existing codebase inventory
<table or "N/A — greenfield">
## Extracted requirements
### Users
<bullets with [doc:line]>
### Core use cases
<bullets with [doc:line]>
### Non-goals
<bullets with [doc:line]>
### Business rules
<bullets with [doc:line]>
### Constraints
<bullets with [doc:line]>
## Capability decomposition (proposed)
<bullets>
## Ambiguities
<bullets>
## Missing from source docs
<bullets>
## Conflicts with existing ADRs
<bullets or "none">
## Verbatim quotes preserved
<bullets with citations>
## Recommended next stage
<verb>
53. All Hook Helper Scripts
This section provides the source code for every hook script in v4. All scripts use Node stdlib only — no npm install needed.
53.1 validate-scope.mjs
#!/usr/bin/env node
// PreToolUse Edit|Write. Blocks edits to paths not in active task brief's in-scope set.
import fs from 'node:fs';
import path from 'node:path';
const projectDir = process.env.CLAUDE_PROJECT_DIR;
const input = JSON.parse(fs.readFileSync(0, 'utf8'));
const filePath = input.tool_input?.file_path;
if (!filePath) process.exit(0);
const activeTaskFile = path.join(projectDir, 'agent-artifacts/monitoring/active-task.json');
if (!fs.existsSync(activeTaskFile)) {
// No active task — allow (intake / orchestrator-next contexts)
process.exit(0);
}
const { taskId, taskBriefPath } = JSON.parse(fs.readFileSync(activeTaskFile, 'utf8'));
const briefFull = path.join(projectDir, taskBriefPath);
if (!fs.existsSync(briefFull)) process.exit(0);
const brief = fs.readFileSync(briefFull, 'utf8');
// Parse In-scope files section
const inScopeMatch = brief.match(/## In-scope files\s*\n([\s\S]+?)(?=\n## |$)/);
if (!inScopeMatch) process.exit(0);
const inScopeLines = inScopeMatch[1].split('\n').filter(l => l.trim().startsWith('-'));
const inScopePaths = inScopeLines.map(l => {
const m = l.match(/^-\s*([^\s(:]+)/);
return m ? m[1].trim() : null;
}).filter(Boolean);
// Always allow writes to agent-artifacts/change-summaries/ and Beads-related paths
const alwaysAllowed = [
`agent-artifacts/change-summaries/${taskId}.md`,
`agent-artifacts/blockers/`,
`agent-artifacts/monitoring/`
];
const relPath = path.relative(projectDir, path.resolve(projectDir, filePath));
if (alwaysAllowed.some(p => relPath.startsWith(p))) process.exit(0);
const inScope = inScopePaths.some(p => relPath === p || relPath.startsWith(p + '/'));
if (!inScope) {
console.log(JSON.stringify({
decision: 'block',
reason: `File "${relPath}" not in task brief's In-scope files. Active task: ${taskId}. To edit additional files: stop, file a blocker or bd message proposing a brief revision.`
}));
process.exit(0);
}
process.exit(0);
53.2 validate-bash.mjs
#!/usr/bin/env node
// PreToolUse Bash. Blocks dangerous shell commands.
import fs from 'node:fs';
const input = JSON.parse(fs.readFileSync(0, 'utf8'));
const cmd = input.tool_input?.command || '';
const dangerous = [
{ pattern: /\brm\s+-rf\s+\//, reason: 'rm -rf / is forbidden' },
{ pattern: /\brm\s+-rf\s+~/, reason: 'rm -rf ~ is forbidden' },
{ pattern: /\bgit\s+push\s+(--force|-f)\b/, reason: 'git push --force is forbidden' },
{ pattern: /\bgit\s+reset\s+--hard\b/, reason: 'git reset --hard requires approval' },
{ pattern: /\bdrop\s+(table|database)\b/i, reason: 'DROP statements are forbidden' },
{ pattern: /:\(\)\s*\{\s*:\|:&\s*\}\s*;\s*:/, reason: 'fork bomb pattern detected' },
{ pattern: /\bdd\s+if=.*of=\/dev\//, reason: 'raw dd to device is forbidden' },
{ pattern: /\bsudo\b/, reason: 'sudo is forbidden in this context' },
{ pattern: /\bcurl\s+.*\|\s*(bash|sh)\b/, reason: 'curl-pipe-to-shell is forbidden' },
{ pattern: /\bwget\s+.*\|\s*(bash|sh)\b/, reason: 'wget-pipe-to-shell is forbidden' }
];
for (const { pattern, reason } of dangerous) {
if (pattern.test(cmd)) {
console.log(JSON.stringify({ decision: 'block', reason: `Blocked: ${reason}. Command: ${cmd.slice(0, 100)}` }));
process.exit(0);
}
}
// Block deletion of tracked non-temp files
if (/\b(rm|git\s+rm)\b/.test(cmd) && !/\b(scratch|tmp|\.bak|\.scratch|\.debug)\b/.test(cmd)) {
if (!process.env.DELETION_APPROVED) {
console.log(JSON.stringify({
decision: 'block',
reason: 'Deletion of tracked non-temp files requires DELETION_APPROVED=1 in env'
}));
process.exit(0);
}
}
process.exit(0);
53.3 validate-version.mjs
#!/usr/bin/env node
// PreToolUse Edit|Write|Bash. Blocks version bumps without approval.
import fs from 'node:fs';
const input = JSON.parse(fs.readFileSync(0, 'utf8'));
const tool = input.tool_name;
if (process.env.VERSION_BUMP_APPROVED === '1') process.exit(0);
if (tool === 'Edit' || tool === 'Write') {
const filePath = input.tool_input?.file_path || '';
if (filePath.endsWith('package.json')) {
const newContent = input.tool_input?.new_string || input.tool_input?.content || '';
if (/"version"\s*:/.test(newContent)) {
console.log(JSON.stringify({
decision: 'block',
reason: 'package.json "version" edit requires VERSION_BUMP_APPROVED=1 in environment. Maintainer must obtain user approval.'
}));
process.exit(0);
}
}
}
if (tool === 'Bash') {
const cmd = input.tool_input?.command || '';
if (/\bnpm\s+version\b/.test(cmd) || /\byarn\s+version\b/.test(cmd) || /\bpnpm\s+version\b/.test(cmd)) {
console.log(JSON.stringify({
decision: 'block',
reason: 'npm/yarn/pnpm version command requires VERSION_BUMP_APPROVED=1 in environment.'
}));
process.exit(0);
}
}
process.exit(0);
53.4 validate-npm-script.mjs
#!/usr/bin/env node
// PreToolUse Bash. Blocks npm run X for missing scripts.
import fs from 'node:fs';
import path from 'node:path';
const projectDir = process.env.CLAUDE_PROJECT_DIR;
const input = JSON.parse(fs.readFileSync(0, 'utf8'));
const cmd = input.tool_input?.command || '';
const m = cmd.match(/\b(?:npm|pnpm|yarn)\s+run\s+(\S+)/);
if (!m) process.exit(0);
const scriptName = m[1];
const pkgPath = path.join(projectDir, 'package.json');
if (!fs.existsSync(pkgPath)) process.exit(0);
let pkg;
try { pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); }
catch { process.exit(0); }
const scripts = pkg.scripts || {};
if (!(scriptName in scripts)) {
const available = Object.keys(scripts).join(', ');
console.log(JSON.stringify({
decision: 'block',
reason: `Script "${scriptName}" not found in package.json. Available: ${available || '(none)'}.`
}));
process.exit(0);
}
process.exit(0);
53.5 validate-change-summary.mjs
#!/usr/bin/env node
// Stop hook. Blocks main session stop if change summary missing for active task.
import fs from 'node:fs';
import path from 'node:path';
const projectDir = process.env.CLAUDE_PROJECT_DIR;
const activeTaskFile = path.join(projectDir, 'agent-artifacts/monitoring/active-task.json');
if (!fs.existsSync(activeTaskFile)) process.exit(0); // No active task, allow stop
const { taskId } = JSON.parse(fs.readFileSync(activeTaskFile, 'utf8'));
const summaryPath = path.join(projectDir, `agent-artifacts/change-summaries/${taskId}.md`);
if (!fs.existsSync(summaryPath)) {
console.log(JSON.stringify({
decision: 'block',
reason: `Cannot stop: active task ${taskId} has no change summary at ${summaryPath}. Builder must write the summary before stopping.`
}));
process.exit(0);
}
process.exit(0);
53.6 validate-test-report.mjs (UPDATED in v4 — two-stage)
#!/usr/bin/env node
// SubagentStop matcher: tester-reviewer. Verifies two-stage structure.
import fs from 'node:fs';
import path from 'node:path';
const projectDir = process.env.CLAUDE_PROJECT_DIR;
const activeTaskFile = path.join(projectDir, 'agent-artifacts/monitoring/active-task.json');
if (!fs.existsSync(activeTaskFile)) process.exit(0);
const { taskId } = JSON.parse(fs.readFileSync(activeTaskFile, 'utf8'));
const reportPath = path.join(projectDir, `agent-artifacts/test-reports/${taskId}.md`);
if (!fs.existsSync(reportPath)) {
console.log(JSON.stringify({
decision: 'block',
reason: `Cannot stop Tester-Reviewer: test report missing at ${reportPath}.`
}));
process.exit(0);
}
const content = fs.readFileSync(reportPath, 'utf8');
const required = [
{ check: /## Stage 1: Spec Compliance/, name: 'Stage 1 section' },
{ check: /## Stage 2: Code Quality/, name: 'Stage 2 section' },
{ check: /Stage 1[\s\S]{0,500}(PASS|FIX_REQUIRED)/, name: 'Stage 1 verdict (PASS|FIX_REQUIRED)' },
{ check: /Stage 2[\s\S]{0,500}(PASS|FIX_REQUIRED)/, name: 'Stage 2 verdict (PASS|FIX_REQUIRED)' },
{ check: /## Overall result\s*\n\s*(PASS|FIX_REQUIRED)/, name: 'Overall result' }
];
const missing = required.filter(r => !r.check.test(content)).map(r => r.name);
if (missing.length > 0) {
console.log(JSON.stringify({
decision: 'block',
reason: `Test report missing required sections: ${missing.join(', ')}. v4 requires two-stage format.`
}));
process.exit(0);
}
process.exit(0);
53.7 validate-maintenance-report.mjs
#!/usr/bin/env node
// SubagentStop matcher: maintainer. Verifies maintenance report exists with required sections.
import fs from 'node:fs';
import path from 'node:path';
const projectDir = process.env.CLAUDE_PROJECT_DIR;
const activeTaskFile = path.join(projectDir, 'agent-artifacts/monitoring/active-task.json');
if (!fs.existsSync(activeTaskFile)) process.exit(0);
const { taskId } = JSON.parse(fs.readFileSync(activeTaskFile, 'utf8'));
const reportPath = path.join(projectDir, `agent-artifacts/maintenance-reports/${taskId}.md`);
if (!fs.existsSync(reportPath)) {
console.log(JSON.stringify({
decision: 'block',
reason: `Maintenance report missing at ${reportPath}.`
}));
process.exit(0);
}
const content = fs.readFileSync(reportPath, 'utf8');
const required = [
/## Cleanup actions taken/,
/## Documentation updates/,
/## Spec delta merge/,
/## Changelog entry/,
/## Version decision/,
/## Final validation/
];
const missing = required.filter(r => !r.test(content));
if (missing.length > 0) {
console.log(JSON.stringify({
decision: 'block',
reason: `Maintenance report missing ${missing.length} required sections.`
}));
process.exit(0);
}
process.exit(0);
53.8 track-file-edits.mjs
#!/usr/bin/env node
// PreToolUse Edit|Write. Blocks at 6th edit to same file in same task.
import fs from 'node:fs';
import path from 'node:path';
const projectDir = process.env.CLAUDE_PROJECT_DIR;
const input = JSON.parse(fs.readFileSync(0, 'utf8'));
const filePath = input.tool_input?.file_path;
if (!filePath) process.exit(0);
const activeTaskFile = path.join(projectDir, 'agent-artifacts/monitoring/active-task.json');
if (!fs.existsSync(activeTaskFile)) process.exit(0);
const { taskId } = JSON.parse(fs.readFileSync(activeTaskFile, 'utf8'));
const counterFile = path.join(projectDir, 'agent-artifacts/monitoring/file-edit-counters.jsonl');
const lines = fs.existsSync(counterFile)
? fs.readFileSync(counterFile, 'utf8').split('\n').filter(Boolean)
: [];
const count = lines.filter(l => {
try { const o = JSON.parse(l); return o.taskId === taskId && o.file === filePath; }
catch { return false; }
}).length;
const THRESHOLD = 5;
if (count >= THRESHOLD) {
const blockerPath = path.join(projectDir,
`agent-artifacts/blockers/blocker-${taskId}-file-loop-${Date.now()}.md`);
fs.writeFileSync(blockerPath, [
`# Blocker: ${taskId} — file edit loop`,
`**Status:** BLOCKED`,
`**Created:** ${new Date().toISOString()}`,
`**Triggered by:** track-file-edits.mjs`,
`## Reason`,
`File "${filePath}" has been edited ${count} times in task ${taskId}. Threshold: ${THRESHOLD}.`,
`## Recommended action`,
`Stop, investigate why the same file needs repeated edits. File a bd message describing the pattern.`
].join('\n\n'));
console.log(JSON.stringify({
decision: 'block',
reason: `File edit loop: "${filePath}" edited ${count} times in task ${taskId}. Blocker: ${blockerPath}`
}));
process.exit(0);
}
fs.appendFileSync(counterFile,
JSON.stringify({ ts: Date.now(), taskId, file: filePath, count: count + 1 }) + '\n');
process.exit(0);
53.9 track-test-failures.mjs (NEW in v4)
#!/usr/bin/env node
// PostToolUse Bash. Tracks test failures by command+stderr hash. Blocks at 3rd identical failure.
import fs from 'node:fs';
import path from 'node:path';
import crypto from 'node:crypto';
const projectDir = process.env.CLAUDE_PROJECT_DIR;
const input = JSON.parse(fs.readFileSync(0, 'utf8'));
const cmd = input.tool_input?.command || '';
const result = input.tool_response || {};
if (!/\b(npm|pnpm|yarn)\s+(test|run\s+test)\b|\bpytest\b|\bjest\b|\bvitest\b/.test(cmd)) {
process.exit(0);
}
if (result.exit_code === 0 || result.status === 'success') process.exit(0);
const activeTaskFile = path.join(projectDir, 'agent-artifacts/monitoring/active-task.json');
if (!fs.existsSync(activeTaskFile)) process.exit(0);
const { taskId } = JSON.parse(fs.readFileSync(activeTaskFile, 'utf8'));
const hash = crypto.createHash('sha256')
.update(cmd + (result.stderr || '').slice(0, 500))
.digest('hex').slice(0, 12);
const counterFile = path.join(projectDir, 'agent-artifacts/monitoring/test-failure-counters.jsonl');
const lines = fs.existsSync(counterFile)
? fs.readFileSync(counterFile, 'utf8').split('\n').filter(Boolean)
: [];
const count = lines.filter(l => {
try { const o = JSON.parse(l); return o.taskId === taskId && o.hash === hash; }
catch { return false; }
}).length;
const THRESHOLD = 2;
if (count >= THRESHOLD) {
const blockerPath = path.join(projectDir,
`agent-artifacts/blockers/blocker-${taskId}-test-loop-${Date.now()}.md`);
fs.writeFileSync(blockerPath, [
`# Blocker: ${taskId} — test failure loop`,
`**Status:** BLOCKED`,
`**Created:** ${new Date().toISOString()}`,
`**Triggered by:** track-test-failures.mjs`,
`## Reason`,
`Same test command failed ${count + 1} times.`,
`**Command:** \`${cmd}\``,
`**Hash:** ${hash}`,
`## Recommended action`,
`Stop. Investigate the test failure. File a bd message with the failure pattern. Do not retry without changing approach.`
].join('\n\n'));
console.error(`Test failure loop detected. Blocker written: ${blockerPath}`);
process.exit(2);
}
fs.appendFileSync(counterFile,
JSON.stringify({ ts: Date.now(), taskId, cmd, hash }) + '\n');
process.exit(0);
53.10 track-command-failures.mjs (NEW in v4)
#!/usr/bin/env node
// PostToolUse Bash. Tracks non-test command failures. Blocks at 3rd identical failure.
import fs from 'node:fs';
import path from 'node:path';
import crypto from 'node:crypto';
const projectDir = process.env.CLAUDE_PROJECT_DIR;
const input = JSON.parse(fs.readFileSync(0, 'utf8'));
const cmd = input.tool_input?.command || '';
const result = input.tool_response || {};
// Skip test commands (track-test-failures handles those)
if (/\b(npm|pnpm|yarn)\s+(test|run\s+test)\b|\bpytest\b|\bjest\b|\bvitest\b/.test(cmd)) {
process.exit(0);
}
// Skip bd commands (Beads can legitimately fail when item not found)
if (/^\s*bd\b/.test(cmd)) process.exit(0);
if (result.exit_code === 0 || result.status === 'success') process.exit(0);
const activeTaskFile = path.join(projectDir, 'agent-artifacts/monitoring/active-task.json');
if (!fs.existsSync(activeTaskFile)) process.exit(0);
const { taskId } = JSON.parse(fs.readFileSync(activeTaskFile, 'utf8'));
const hash = crypto.createHash('sha256')
.update(cmd + (result.exit_code || '') + (result.stderr || '').slice(0, 200))
.digest('hex').slice(0, 12);
const counterFile = path.join(projectDir, 'agent-artifacts/monitoring/command-failure-counters.jsonl');
const lines = fs.existsSync(counterFile)
? fs.readFileSync(counterFile, 'utf8').split('\n').filter(Boolean)
: [];
const count = lines.filter(l => {
try { const o = JSON.parse(l); return o.taskId === taskId && o.hash === hash; }
catch { return false; }
}).length;
const THRESHOLD = 2;
if (count >= THRESHOLD) {
const blockerPath = path.join(projectDir,
`agent-artifacts/blockers/blocker-${taskId}-command-loop-${Date.now()}.md`);
fs.writeFileSync(blockerPath, [
`# Blocker: ${taskId} — command failure loop`,
`**Status:** BLOCKED`,
`**Created:** ${new Date().toISOString()}`,
`**Triggered by:** track-command-failures.mjs`,
`## Reason`,
`Same command failed ${count + 1} times. **Command:** \`${cmd}\` **Exit:** ${result.exit_code}`,
`## Recommended action`,
`Stop. Diagnose the command failure. Do not retry without changing the command or its environment.`
].join('\n\n'));
console.error(`Command failure loop. Blocker: ${blockerPath}`);
process.exit(2);
}
fs.appendFileSync(counterFile,
JSON.stringify({ ts: Date.now(), taskId, cmd, hash }) + '\n');
process.exit(0);
53.11 health-check.mjs (NEW in v4)
#!/usr/bin/env node
// SessionStart async. Verifies environment; writes health.json.
import fs from 'node:fs';
import path from 'node:path';
import { execSync } from 'node:child_process';
const projectDir = process.env.CLAUDE_PROJECT_DIR;
const health = { ts: Date.now(), checks: {} };
try { execSync('node --version', { stdio: 'pipe' }); health.checks.node = 'OK'; }
catch { health.checks.node = 'MISSING'; }
try { execSync('bd --version', { stdio: 'pipe' }); health.checks.beads = 'OK'; }
catch { health.checks.beads = 'MISSING (v4 requires bd CLI)'; }
const activeTaskFile = path.join(projectDir, 'agent-artifacts/monitoring/active-task.json');
if (fs.existsSync(activeTaskFile)) {
try {
const at = JSON.parse(fs.readFileSync(activeTaskFile, 'utf8'));
const briefPath = path.join(projectDir, at.taskBriefPath || '');
health.checks.activeTask = fs.existsSync(briefPath)
? `OK (${at.taskId})` : `STALE (brief missing: ${at.taskBriefPath})`;
} catch { health.checks.activeTask = 'CORRUPT'; }
} else {
health.checks.activeTask = 'NONE';
}
const scripts = [
'validate-scope.mjs', 'validate-bash.mjs', 'validate-version.mjs',
'validate-npm-script.mjs', 'validate-change-summary.mjs', 'validate-test-report.mjs',
'validate-maintenance-report.mjs', 'track-file-edits.mjs', 'update-heartbeat.mjs',
'inject-active-task.mjs', 'auto-approve-safe-bash.mjs', 'format-edited-file.mjs',
'track-test-failures.mjs', 'track-command-failures.mjs',
'validate-digest-report.mjs', 'validate-source-doc-cited.mjs',
'validate-beads-installed.mjs', 'rotate-jsonl.mjs'
];
const missing = scripts.filter(s => !fs.existsSync(path.join(projectDir, '.claude/scripts', s)));
health.checks.scripts = missing.length === 0 ? 'OK' : `MISSING: ${missing.join(', ')}`;
const allOK = Object.values(health.checks).every(v => v === 'OK' || v === 'NONE' || v.startsWith('OK'));
health.status = allOK ? 'OK' : 'WARN';
const dir = path.join(projectDir, 'agent-artifacts/monitoring');
fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(path.join(dir, 'health.json'), JSON.stringify(health, null, 2));
process.exit(0);
53.12 rotate-jsonl.mjs (NEW in v4)
#!/usr/bin/env node
// SessionStart async. Truncates monitoring JSONL files older than 30 days.
import fs from 'node:fs';
import path from 'node:path';
const projectDir = process.env.CLAUDE_PROJECT_DIR;
const cutoff = Date.now() - 30 * 24 * 60 * 60 * 1000;
const dir = path.join(projectDir, 'agent-artifacts/monitoring');
if (!fs.existsSync(dir)) process.exit(0);
const files = [
'events.jsonl', 'file-edit-counters.jsonl',
'test-failure-counters.jsonl', 'command-failure-counters.jsonl',
'token-usage.jsonl'
];
for (const f of files) {
const fp = path.join(dir, f);
if (!fs.existsSync(fp)) continue;
const lines = fs.readFileSync(fp, 'utf8').split('\n').filter(Boolean);
const kept = lines.filter(l => {
try { return JSON.parse(l).ts > cutoff; } catch { return false; }
});
if (kept.length < lines.length) {
fs.writeFileSync(fp, kept.join('\n') + (kept.length ? '\n' : ''));
}
}
process.exit(0);
53.13 validate-digest-report.mjs (NEW in v4)
#!/usr/bin/env node
// SubagentStop matcher: researcher. Verifies digest report if digest mode active.
import fs from 'node:fs';
import path from 'node:path';
const projectDir = process.env.CLAUDE_PROJECT_DIR;
const stateFile = path.join(projectDir, 'agent-artifacts/monitoring/digest-state.json');
if (!fs.existsSync(stateFile)) process.exit(0); // Not in digest mode
let state;
try { state = JSON.parse(fs.readFileSync(stateFile, 'utf8')); } catch { process.exit(0); }
if (state.status !== 'done') process.exit(0); // Still in progress, not at final stop
const reportPath = path.join(projectDir, `agent-artifacts/digest-reports/${state.intakeId}.md`);
if (!fs.existsSync(reportPath)) {
console.log(JSON.stringify({
decision: 'block',
reason: `Digest mode marked done but report missing: ${reportPath}`
}));
process.exit(0);
}
const content = fs.readFileSync(reportPath, 'utf8');
const required = [
{ check: /## Source documents inventory/, name: 'Source documents inventory' },
{ check: /## Extracted requirements/, name: 'Extracted requirements' },
{ check: /## Capability decomposition/, name: 'Capability decomposition' }
];
const missing = required.filter(r => !r.check.test(content));
if (missing.length > 0) {
console.log(JSON.stringify({
decision: 'block',
reason: `Digest report missing required sections: ${missing.map(r => r.name).join(', ')}`
}));
process.exit(0);
}
// Check that extracted requirements have [doc:line] citations
const extractedSection = content.match(/## Extracted requirements\s*([\s\S]+?)(?=\n## |$)/);
if (extractedSection) {
const text = extractedSection[1];
const requirementLines = text.split('\n').filter(l =>
/\b(must|shall|should|required|forbidden)\b/i.test(l) && !l.trim().startsWith('#'));
const uncited = requirementLines.filter(l => !/\[[\w./-]+:\d+\]/.test(l));
if (uncited.length > 0) {
console.log(JSON.stringify({
decision: 'block',
reason: `Digest report has ${uncited.length} uncited requirement claims. Every requirement needs [doc:line] citation.`
}));
process.exit(0);
}
}
process.exit(0);
53.14 validate-source-doc-cited.mjs (NEW in v4)
#!/usr/bin/env node
// PreToolUse Edit|Write. Blocks uncited requirement claims in digest reports.
import fs from 'node:fs';
import path from 'node:path';
const projectDir = process.env.CLAUDE_PROJECT_DIR;
const stateFile = path.join(projectDir, 'agent-artifacts/monitoring/digest-state.json');
if (!fs.existsSync(stateFile)) process.exit(0); // Not in digest mode
const input = JSON.parse(fs.readFileSync(0, 'utf8'));
const filePath = input.tool_input?.file_path || '';
// Only check writes to digest reports
if (!filePath.includes('agent-artifacts/digest-reports/')) process.exit(0);
const content = input.tool_input?.new_string || input.tool_input?.content || '';
// Find lines that look like requirements
const lines = content.split('\n');
const flagged = [];
for (const line of lines) {
if (line.trim().startsWith('#') || line.trim().startsWith('|')) continue;
if (!/\b(must|shall|should|required|forbidden)\b/i.test(line)) continue;
if (/\[[\w./-]+:\d+\]/.test(line)) continue;
flagged.push(line.slice(0, 100));
}
if (flagged.length > 0) {
console.log(JSON.stringify({
decision: 'block',
reason: `Digest report write blocked: ${flagged.length} requirement-looking lines without [doc:line] citation. Examples: "${flagged.slice(0, 2).join('"; "')}"`
}));
process.exit(0);
}
process.exit(0);
53.15 validate-beads-installed.mjs (NEW in v4)
#!/usr/bin/env node
// SessionStart async. Warns if bd CLI not on PATH. Non-blocking.
import fs from 'node:fs';
import path from 'node:path';
import { execSync } from 'node:child_process';
const projectDir = process.env.CLAUDE_PROJECT_DIR;
const healthPath = path.join(projectDir, 'agent-artifacts/monitoring/health.json');
try {
execSync('bd --version', { stdio: 'pipe' });
process.exit(0);
} catch {
// Warn (non-blocking)
let health = {};
if (fs.existsSync(healthPath)) {
try { health = JSON.parse(fs.readFileSync(healthPath, 'utf8')); } catch {}
}
health.beadsWarning = 'bd CLI not found on PATH. v4 requires Beads. Install: brew install beads (or download from releases).';
fs.mkdirSync(path.dirname(healthPath), { recursive: true });
fs.writeFileSync(healthPath, JSON.stringify(health, null, 2));
process.exit(0); // Non-blocking
}
53.16 inject-active-task.mjs (UPDATED in v4 — adds bd prime)
#!/usr/bin/env node
// SessionStart compact|resume. Re-injects active task + bd prime context.
import fs from 'node:fs';
import path from 'node:path';
import { execSync } from 'node:child_process';
const projectDir = process.env.CLAUDE_PROJECT_DIR;
const activeTaskFile = path.join(projectDir, 'agent-artifacts/monitoring/active-task.json');
if (!fs.existsSync(activeTaskFile)) {
console.log(JSON.stringify({ additionalContext: 'No active task in progress.' }));
process.exit(0);
}
const { taskId, taskBriefPath } = JSON.parse(fs.readFileSync(activeTaskFile, 'utf8'));
let branch = 'unknown';
try { branch = execSync('git branch --show-current', { cwd: projectDir }).toString().trim(); }
catch {}
const reportPath = path.join(projectDir, 'agent-artifacts/test-reports', `${taskId}.md`);
let lastStatus = 'no test report yet';
if (fs.existsSync(reportPath)) {
const r = fs.readFileSync(reportPath, 'utf8');
const m = r.match(/^## Overall result\s*\n\s*(PASS|FIX_REQUIRED)/m);
if (m) lastStatus = m[1];
}
const blockerDir = path.join(projectDir, 'agent-artifacts/blockers');
const blockers = fs.existsSync(blockerDir)
? fs.readdirSync(blockerDir).filter(f => f.endsWith('.md') && !f.startsWith('resolved'))
: [];
let bdPrime = '';
try {
bdPrime = execSync(`bd prime --task ${taskId} --max-tokens 1500`,
{ cwd: projectDir, stdio: ['pipe', 'pipe', 'pipe'], timeout: 15000 }).toString().trim();
} catch (e) {
bdPrime = '(bd prime unavailable or no Beads context)';
}
const ctx = [
`## Active task state`,
`- Active task: ${taskId}`,
`- Task brief: ${taskBriefPath}`,
`- Branch: ${branch}`,
`- Last test report status: ${lastStatus}`,
blockers.length > 0
? `- Open blockers (${blockers.length}): ${blockers.join(', ')}`
: '- No open blockers.',
'',
'## Beads context (bd prime output)',
bdPrime
].join('\n');
console.log(JSON.stringify({ additionalContext: ctx }));
process.exit(0);
53.17 auto-approve-safe-bash.mjs
#!/usr/bin/env node
// PermissionRequest Bash. Auto-approves known-safe read-only commands.
import fs from 'node:fs';
const input = JSON.parse(fs.readFileSync(0, 'utf8'));
const cmd = input.tool_input?.command || '';
const safe = [
/^\s*git\s+(status|diff|log|show|branch|remote|describe)\b/,
/^\s*ls\b/, /^\s*cat\s/, /^\s*head\s/, /^\s*tail\s/, /^\s*wc\s/,
/^\s*find\s/, /^\s*grep\s/, /^\s*rg\s/, /^\s*ag\s/,
/^\s*(npm|pnpm|yarn)\s+(list|ls|view|info)\b/,
/^\s*(npm|pnpm|yarn)\s+test\b/,
/^\s*(npm|pnpm|yarn)\s+run\s+(test|lint|typecheck|build|check|format)\b/,
/^\s*node\s+--version/,
/^\s*bd\s+(show|list|ready|blocked|graph|dep|doctor|prime)\b/,
/^\s*pwd\s*$/, /^\s*echo\s+/,
/^\s*which\s/, /^\s*type\s/
];
if (safe.some(p => p.test(cmd))) {
console.log(JSON.stringify({ decision: 'allow' }));
process.exit(0);
}
process.exit(0); // Defer to default permission handling
53.18 format-edited-file.mjs
#!/usr/bin/env node
// PostToolUse Edit|Write. Runs project formatter on edited file.
import fs from 'node:fs';
import path from 'node:path';
import { execSync } from 'node:child_process';
const projectDir = process.env.CLAUDE_PROJECT_DIR;
const input = JSON.parse(fs.readFileSync(0, 'utf8'));
const filePath = input.tool_input?.file_path;
if (!filePath) process.exit(0);
const fullPath = path.isAbsolute(filePath) ? filePath : path.join(projectDir, filePath);
if (!fs.existsSync(fullPath)) process.exit(0);
const ext = path.extname(filePath);
const formatters = {
'.ts': 'npx prettier --write',
'.tsx': 'npx prettier --write',
'.js': 'npx prettier --write',
'.jsx': 'npx prettier --write',
'.json': 'npx prettier --write',
'.md': 'npx prettier --write',
'.py': 'python -m black -q'
};
const cmd = formatters[ext];
if (!cmd) process.exit(0);
try {
execSync(`${cmd} ${JSON.stringify(fullPath)}`,
{ cwd: projectDir, stdio: 'pipe', timeout: 20000 });
} catch (e) {
// Formatter failure is non-fatal; just log
console.error(`Formatter skipped for ${filePath}: ${e.message?.slice(0, 100)}`);
}
process.exit(0);
53.19 update-heartbeat.mjs
#!/usr/bin/env node
// PostToolUse Edit|Write. Appends heartbeat to events.jsonl. Async.
import fs from 'node:fs';
import path from 'node:path';
const projectDir = process.env.CLAUDE_PROJECT_DIR;
const input = JSON.parse(fs.readFileSync(0, 'utf8'));
const activeTaskFile = path.join(projectDir, 'agent-artifacts/monitoring/active-task.json');
let taskId = null;
if (fs.existsSync(activeTaskFile)) {
try { taskId = JSON.parse(fs.readFileSync(activeTaskFile, 'utf8')).taskId; } catch {}
}
const dir = path.join(projectDir, 'agent-artifacts/monitoring');
fs.mkdirSync(dir, { recursive: true });
fs.appendFileSync(path.join(dir, 'events.jsonl'),
JSON.stringify({
ts: Date.now(),
taskId,
tool: input.tool_name,
file: input.tool_input?.file_path || null,
result: input.tool_response?.success !== false ? 'ok' : 'fail'
}) + '\n');
process.exit(0);
54. JSON Schemas
JSON schemas validate artifact structure. Stored in .claude/schemas/.
54.1 task-brief.schema.json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["bdId", "changeId", "capability", "goal", "inScopeFiles", "outOfScopeFiles", "acceptanceCriteria", "validationCommands"],
"properties": {
"bdId": { "type": "string", "pattern": "^bd-[a-z0-9]{4,}$" },
"changeId": { "type": "string" },
"capability": { "type": "string" },
"goal": { "type": "string", "minLength": 10 },
"inScopeFiles": { "type": "array", "items": { "type": "string" }, "minItems": 1 },
"outOfScopeFiles": { "type": "array", "items": { "type": "string" } },
"acceptanceCriteria": { "type": "array", "items": { "type": "string" }, "minItems": 1 },
"validationCommands": { "type": "array", "items": { "type": "string" }, "minItems": 1 },
"docsImpact": { "type": "array", "items": { "type": "string" } },
"versionImpact": { "type": "string", "enum": ["none", "patch", "minor", "major"] }
}
}
54.2 change-summary.schema.json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["bdId", "status", "filesChanged", "validationRun"],
"properties": {
"bdId": { "type": "string" },
"status": { "type": "string", "enum": ["DONE", "BLOCKED"] },
"filesChanged": { "type": "array", "items": { "type": "object", "properties": {
"path": { "type": "string" }, "kind": { "type": "string", "enum": ["new", "modify"] },
"linesAdded": { "type": "integer" }, "linesRemoved": { "type": "integer" }
}}},
"validationRun": { "type": "array", "items": { "type": "object", "properties": {
"command": { "type": "string" }, "result": { "type": "string", "enum": ["PASS", "FAIL"] }
}}},
"versionRecommendation": { "type": "string", "enum": ["none", "patch", "minor", "major"] }
}
}
54.3 test-report.schema.json (UPDATED v4)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["bdId", "stage1", "stage2", "overall"],
"properties": {
"bdId": { "type": "string" },
"stage1": {
"type": "object",
"required": ["verdict", "requirementsCovered", "scopeReview", "validationResults"],
"properties": {
"verdict": { "type": "string", "enum": ["PASS", "FIX_REQUIRED"] },
"requirementsCovered": { "type": "array" },
"scopeReview": { "type": "object" },
"validationResults": { "type": "array" },
"issues": { "type": "array" }
}
},
"stage2": {
"type": "object",
"required": ["verdict", "findings"],
"properties": {
"verdict": { "type": "string", "enum": ["PASS", "FIX_REQUIRED"] },
"findings": { "type": "array" },
"issues": { "type": "array" }
}
},
"overall": { "type": "string", "enum": ["PASS", "FIX_REQUIRED"] }
}
}
54.4 maintenance-report.schema.json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["bdId", "cleanupActions", "docUpdates", "specDeltaMerge", "changelogEntry", "versionDecision", "finalValidation"],
"properties": {
"bdId": { "type": "string" },
"cleanupActions": { "type": "array" },
"docUpdates": { "type": "array" },
"specDeltaMerge": {
"type": "object",
"properties": {
"capability": { "type": "string" },
"added": { "type": "integer" },
"modified": { "type": "integer" },
"removed": { "type": "integer" }
}
},
"changelogEntry": { "type": "string" },
"versionDecision": { "type": "object" },
"finalValidation": { "type": "array" }
}
}
54.5 blocker.schema.json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["bdId", "status", "createdAt", "triggeredBy", "reason"],
"properties": {
"bdId": { "type": "string" },
"status": { "type": "string", "enum": ["BLOCKED", "RESOLVED"] },
"createdAt": { "type": "string", "format": "date-time" },
"triggeredBy": { "type": "string" },
"reason": { "type": "string" },
"evidence": { "type": "array" },
"recommendedAction": { "type": "string" },
"resolution": { "type": "string" }
}
}
54.6 monitoring-event.schema.json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["ts"],
"properties": {
"ts": { "type": "integer" },
"taskId": { "type": "string" },
"tool": { "type": "string" },
"file": { "type": "string" },
"result": { "type": "string", "enum": ["ok", "fail"] }
}
}
54.7 digest-report.schema.json (NEW v4)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["intakeId", "mode", "sourceDocsInventory", "extractedRequirements", "capabilityDecomposition"],
"properties": {
"intakeId": { "type": "string" },
"mode": { "type": "string", "enum": ["brownfield-with-docs", "brownfield-no-docs", "greenfield-with-docs"] },
"sourceDocsInventory": {
"type": "array",
"items": { "type": "object", "required": ["path", "type", "sha"], "properties": {
"path": { "type": "string" }, "type": { "type": "string" },
"lastModified": { "type": "string" }, "sha": { "type": "string" }
}}
},
"codebaseInventory": { "type": "array" },
"extractedRequirements": {
"type": "object",
"properties": {
"users": { "type": "array" }, "coreUseCases": { "type": "array" },
"nonGoals": { "type": "array" }, "businessRules": { "type": "array" },
"constraints": { "type": "array" }
}
},
"capabilityDecomposition": { "type": "array", "minItems": 1 },
"ambiguities": { "type": "array" },
"missingFromSourceDocs": { "type": "array" },
"adrConflicts": { "type": "array" },
"verbatimQuotes": { "type": "array" }
}
}
54.8 delta-spec.schema.json (NEW v4)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["capability", "changeId"],
"properties": {
"capability": { "type": "string" },
"changeId": { "type": "string" },
"added": {
"type": "array",
"items": { "type": "object", "required": ["name", "statement"], "properties": {
"name": { "type": "string" }, "statement": { "type": "string" },
"scenarios": { "type": "array" }
}}
},
"modified": {
"type": "array",
"items": { "type": "object", "required": ["name", "was", "now", "reason"], "properties": {
"name": { "type": "string" }, "was": { "type": "string" },
"now": { "type": "string" }, "reason": { "type": "string" }
}}
},
"removed": {
"type": "array",
"items": { "type": "object", "required": ["name", "was", "reason"], "properties": {
"name": { "type": "string" }, "was": { "type": "string" }, "reason": { "type": "string" }
}}
}
}
}
55. Migration Scripts (v3 → v4)
55.1 scripts/migrate-prd-to-specs.mjs
#!/usr/bin/env node
// One-time v3 → v4 migration: docs/01-prd.md → docs/specs/<capability>/spec.md
import fs from 'node:fs';
import path from 'node:path';
const projectDir = process.cwd();
const prdPath = path.join(projectDir, 'docs/01-prd.md');
if (!fs.existsSync(prdPath)) {
console.error('No docs/01-prd.md found. Nothing to migrate.');
process.exit(0);
}
const content = fs.readFileSync(prdPath, 'utf8');
const reviewPath = path.join(projectDir, 'scripts/migration-review.md');
// Heuristic: capabilities are level-2 headers that look like features/use-cases
const capabilityHeaders = [...content.matchAll(/^##\s+(.+)$/gm)]
.filter(m => !/^(Users|Non-goals|Constraints|Overview|Introduction|Summary)$/i.test(m[1]))
.map(m => ({ name: m[1], index: m.index }));
if (capabilityHeaders.length === 0) {
console.error('No capability sections detected in docs/01-prd.md. Manual migration required.');
process.exit(1);
}
const specsDir = path.join(projectDir, 'docs/specs');
fs.mkdirSync(specsDir, { recursive: true });
const report = ['# v3 → v4 PRD Migration Review', '', `Source: ${prdPath}`, ''];
const slug = s => s.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
// Extract content between headers
for (let i = 0; i < capabilityHeaders.length; i++) {
const start = capabilityHeaders[i].index;
const end = i + 1 < capabilityHeaders.length ? capabilityHeaders[i + 1].index : content.length;
const section = content.slice(start, end).trim();
const capName = capabilityHeaders[i].name;
const capSlug = slug(capName);
const capDir = path.join(specsDir, capSlug);
fs.mkdirSync(capDir, { recursive: true });
const spec = [
`# Spec: ${capSlug}`, '',
`## Purpose`,
`<derived from PRD section "${capName}" — REVIEW REQUIRED>`,
'', section, '',
`## Non-goals`, '- (review and split from PRD top-level Non-goals if applicable)', '',
`## Constraints`, '- (review and split from PRD top-level Constraints if applicable)', '',
`## Related ADRs`, '- (review docs/decisions/ for relevant ADRs)'
].join('\n');
fs.writeFileSync(path.join(capDir, 'spec.md'), spec);
const summary = [
`# Spec Summary: ${capSlug}`, '',
`## Purpose`,
`<one-sentence summary — REVIEW REQUIRED>`,
'',
`## Key requirements`,
`<extract ≤ 10 numbered requirements from spec.md — REVIEW REQUIRED>`,
'',
`## Business rules`,
`<extract from spec.md — REVIEW REQUIRED>`,
'',
`## Non-goals`,
`<from spec.md>`
].join('\n');
fs.writeFileSync(path.join(capDir, 'spec-summary.md'), summary);
report.push(`## Capability: ${capSlug}`, `- Source section: "${capName}"`,
`- Generated: docs/specs/${capSlug}/spec.md, spec-summary.md`,
`- REVIEW: Confirm capability boundary; refine spec-summary.md (currently placeholder)`, '');
}
fs.writeFileSync(reviewPath, report.join('\n'));
console.log(`Migration complete.`);
console.log(`Generated: ${capabilityHeaders.length} capabilities under docs/specs/`);
console.log(`Review: ${reviewPath}`);
console.log(`Next: review and refine each spec-summary.md by hand. Plan ~1 hour for non-trivial PRDs.`);
console.log(`After review, you may delete docs/01-prd.md and docs/01-prd-summary.md.`);
55.2 scripts/migrate-task-index-to-beads.mjs
#!/usr/bin/env node
// One-time v3 → v4 migration: docs/04-task-index.md → Beads tasks
import fs from 'node:fs';
import path from 'node:path';
import { execSync } from 'node:child_process';
const projectDir = process.cwd();
const indexPath = path.join(projectDir, 'docs/04-task-index.md');
if (!fs.existsSync(indexPath)) {
console.error('No docs/04-task-index.md found. Nothing to migrate.');
process.exit(0);
}
try { execSync('bd --version', { stdio: 'pipe' }); }
catch { console.error('bd CLI not installed. Install Beads first.'); process.exit(1); }
const content = fs.readFileSync(indexPath, 'utf8');
const mapping = {};
const mappingPath = path.join(projectDir, 'scripts/v3-to-bd-id-mapping.json');
// Parse tasks (rough heuristic)
const taskLines = content.match(/^[*\-]\s+\[?\w+\]?\s+(phase-\d+-task-\d+-[\w-]+).*$/gm) || [];
for (const line of taskLines) {
const taskMatch = line.match(/(phase-\d+-task-\d+-[\w-]+)/);
if (!taskMatch) continue;
const taskId = taskMatch[1];
// Status detection
let status = 'TODO';
if (/\bDONE\b/.test(line)) status = 'DONE';
else if (/\bNEXT\b/.test(line)) status = 'NEXT';
else if (/\bBLOCKED\b/.test(line)) status = 'BLOCKED';
// Create in Beads
const title = taskId.replace(/-/g, ' ');
try {
const out = execSync(
`bd create --type task --title "${title}" --json`,
{ stdio: ['pipe', 'pipe', 'pipe'], cwd: projectDir }
).toString();
const created = JSON.parse(out);
const bdId = created.id || created.bdId;
mapping[taskId] = bdId;
if (status !== 'TODO') {
execSync(`bd update ${bdId} --status ${status}`, { cwd: projectDir, stdio: 'pipe' });
}
console.log(`Created: ${taskId} → ${bdId} (status: ${status})`);
} catch (e) {
console.error(`Failed to create task for ${taskId}: ${e.message?.slice(0, 100)}`);
}
}
fs.writeFileSync(mappingPath, JSON.stringify(mapping, null, 2));
console.log(`\nMigration complete.`);
console.log(`Mapping written to: ${mappingPath}`);
console.log(`Next: review the mapping. Add dependencies manually if your v3 index had them:`);
console.log(` bd dep add <bd-id-blocked> <bd-id-blocking> --type blocks`);
console.log(`After review, you may delete docs/04-task-index.md.`);
console.log(`\nIMPORTANT: Rename task brief files to use bd-ids:`);
console.log(` for f in agent-artifacts/task-briefs/*.md; do ...`);
console.log(`See section 55.3 in Part 3 for the rename script.`);
55.3 scripts/migration-rename-briefs.sh
#!/bin/bash
# Renames task brief files from v3 pattern (phase-N-task-NNN-slug.md) to v4 (bd-id-slug.md)
set -euo pipefail
MAPPING_FILE="scripts/v3-to-bd-id-mapping.json"
BRIEFS_DIR="agent-artifacts/task-briefs"
if [ ! -f "$MAPPING_FILE" ]; then
echo "Run migrate-task-index-to-beads.mjs first."
exit 1
fi
for f in "$BRIEFS_DIR"/*.md; do
base=$(basename "$f" .md)
# Extract phase-N-task-NNN-slug pattern
old_id=$(echo "$base" | grep -oE 'phase-[0-9]+-task-[0-9]+-[a-z0-9-]+' || true)
if [ -z "$old_id" ]; then continue; fi
bd_id=$(node -e "
const m = JSON.parse(require('fs').readFileSync('$MAPPING_FILE', 'utf8'));
console.log(m['$old_id'] || '');
")
if [ -z "$bd_id" ]; then
echo "No bd-id for $old_id, skipping"; continue
fi
new_name="${bd_id}-${old_id}.md"
mv "$f" "$BRIEFS_DIR/$new_name"
echo "Renamed: $f → $BRIEFS_DIR/$new_name"
done
56. Artifact Templates
All artifact templates are covered in Part 2 §23–§24 and skill files in §51. This section provides quick-reference one-line summaries.
| Artifact | Skill providing template | Location |
|---|---|---|
| Task brief | task-brief-template |
agent-artifacts/task-briefs/<bd-id>-<slug>.md |
| Change summary | change-summary-template |
agent-artifacts/change-summaries/<bd-id>.md |
| Test report (two-stage) | test-report-template |
agent-artifacts/test-reports/<bd-id>.md |
| Maintenance report | maintenance-report-template |
agent-artifacts/maintenance-reports/<bd-id>.md |
| Blocker report | blocker-template |
agent-artifacts/blockers/blocker-<bd-id>-<ts>.md |
| Digest report (v4) | digest-report-template |
agent-artifacts/digest-reports/<intake-id>.md |
| Delta spec (v4) | delta-spec-format |
agent-artifacts/changes/<change-id>/delta-specs/<capability>.md |
| ADR | docs/decisions/0000-template.md |
docs/decisions/NNNN-<slug>.md |
| Spec | (manual) | docs/specs/<capability>/spec.md |
| Spec summary | (manual) | docs/specs/<capability>/spec-summary.md |
| Change proposal | (manual) | agent-artifacts/changes/<change-id>/proposal.md |
| Change design | (manual) | agent-artifacts/changes/<change-id>/design.md |
57. Operations — Daily, Weekly, Monthly
57.1 Daily routines
| Activity | Command |
|---|---|
| See what's ready | /orchestrator-next |
| Implement next task | /implement-task <bd-id> |
| Check active work | cat agent-artifacts/monitoring/active-task.json |
| See open blockers | ls agent-artifacts/blockers/ |
| Quick Beads view | bd ready then bd show <bd-id> |
57.2 Weekly routines
| Activity | Command |
|---|---|
| Health check | cat agent-artifacts/monitoring/health.json |
| Beads health | bd doctor |
| Review in-flight changes | ls agent-artifacts/changes/ (excluding archive) |
| Review CHANGELOG additions | cat CHANGELOG.md (Unreleased section) |
| Auto log rotation already happened on SessionStart | (no action) |
57.3 Monthly routines (Maintainer-initiated)
# 1. Compact old Beads issues (ask user before first run)
bd compact --older-than 30d
# Reviews messages older than 30 days; LLM-summarizes them.
# Lossy. Run after verifying you have backups.
# 2. Review archived changes
ls -lh agent-artifacts/changes/archive/
# No action; awareness only.
# 3. Optional: archive old research notes
# If docs/02-research.md > ~50 KB, manually move entries older than 6 months
# to docs/02-research-archive.md.
# 4. Verify all task briefs still match their Beads tasks
bd list --json > /tmp/bd-tasks.json
ls agent-artifacts/task-briefs/ > /tmp/brief-files.txt
# Manually cross-reference; flag orphans.
# 5. Update docs/06-tech-docs.md if Beads or Claude Code versions changed
57.4 Per-release routines (when bumping version)
# 1. Get user approval for version bump
# 2. Set environment
export VERSION_BUMP_APPROVED=1
# 3. Maintainer applies version bump (in normal /maintain flow)
# 4. Move Unreleased entries in CHANGELOG.md to the new version section
# 5. Tag the release
git tag -a vX.Y.Z -m "Release vX.Y.Z"
# 6. Unset environment
unset VERSION_BUMP_APPROVED
58. Troubleshooting Guide
58.1 Common issues and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
/agents shows nothing |
YAML frontmatter invalid in agent file | Run yamllint .claude/agents/*.md or visual inspection |
| Hooks don't fire | node not on PATH, or scripts not executable |
which node; chmod +x .claude/scripts/*.mjs |
validate-scope.mjs blocks legitimate edit |
Task brief in-scope list incomplete | Stop, revise brief (create new task with supersedes), retry |
validate-bash.mjs blocks legitimate command |
Command matches dangerous regex | Diagnose; if false positive, refine regex in script; if true positive, use a safer command |
| Test report blocked at stop | Missing Stage 1 or Stage 2 section | Inspect report; Tester-Reviewer must produce both stages |
bd: command not found |
Beads not installed or PATH issue | brew install beads; which bd |
bd doctor reports issues |
Beads database corruption | bd doctor --fix; if fails, see Beads troubleshooting docs |
bd ready returns nothing despite TODO tasks |
All TODO tasks have unmet blocks deps |
bd blocked to see; resolve blockers |
| Digest report blocked at stop | Missing citations on requirements | Inspect report; add [doc:line] to flagged lines |
| Agent doesn't load expected skill | Skill description too vague or wrong "when not to load" | Rewrite description with specific triggers |
| Token usage climbs unexpectedly | Bloated per-command load order or skill descriptions | Review .claude/commands/*.md and skill descriptions |
/compact loses important context |
Decisions weren't filed as bd message before compaction |
Discipline: file bd message immediately when decisions made |
| Stage 2 review keeps PASSing things Stage 1 missed | Stage 2 is reading the brief (defeats blindness) | Review tester-reviewer.md agent prompt; ensure Stage 2 instruction is explicit |
Builder edits package.json "version" and gets blocked |
Expected behavior; needs approval | Set VERSION_BUMP_APPROVED=1, run again; unset after |
| Migration script produces poor split | PRD doesn't have clean section boundaries | Manual review at scripts/migration-review.md; refine spec.md files by hand |
58.2 Debugging hooks
# See registered hooks
claude
> /hooks
# Test a hook standalone
echo '{"tool_name":"Edit","tool_input":{"file_path":"src/foo.ts"}}' | \
node .claude/scripts/validate-scope.mjs
echo "exit: $?"
# Verbose Claude Code (shows hook execution)
claude --debug
# Tail event log
tail -f agent-artifacts/monitoring/events.jsonl | jq
58.3 Recovering from a corrupt active-task.json
# If active-task.json is invalid:
cat agent-artifacts/monitoring/active-task.json
# If unreadable, replace:
echo '{}' > agent-artifacts/monitoring/active-task.json
# Or set to a known good task:
echo '{
"taskId": "bd-a1b2",
"taskBriefPath": "agent-artifacts/task-briefs/bd-a1b2-...md",
"changeId": "...",
"startedAt": "'$(date -u +%FT%TZ)'",
"branch": "'$(git branch --show-current)'"
}' > agent-artifacts/monitoring/active-task.json
58.4 Recovering from a Beads issue
# Database health
bd doctor
# If issues:
bd doctor --fix
# If still broken, check Beads logs:
ls -la .beads/embeddeddolt/
# Last-resort: backup and reinit
cp -r .beads .beads.backup
rm -rf .beads/embeddeddolt
bd init
# Then re-import from backup or recreate tasks from task-briefs/
58.5 When watchdog blocks too aggressively
The watchdog is deliberately conservative. If it blocks legitimate work:
- Don't disable the rule. Disabling defeats the purpose.
- Fix the underlying issue. If the task brief's in-scope list is incomplete, revise the brief (new task with
supersedes), then retry. - Tune the threshold only after multiple legitimate triggers. Edit
.claude/scripts/<script>.mjsand document the change in CHANGELOG.
59. Cross-Project Knowledge Sharing — Detailed Setup
Three options, from least to most ambitious. Pick based on your situation.
59.1 Option A: Personal skills directory (solo dev)
Claude Code reads BOTH .claude/skills/ (project) AND ~/.claude/skills/ (personal). Personal skills auto-load in every project session.
# Create personal skills directory
mkdir -p ~/.claude/skills
# Move a skill you want to share across projects
cp -r .claude/skills/error-envelope ~/.claude/skills/my-standard-error-envelope/
# Edit the name in frontmatter to avoid collision with project-level skill of same name
vim ~/.claude/skills/my-standard-error-envelope/SKILL.md
# Change `name: error-envelope` → `name: my-standard-error-envelope`
Override behavior: If a project-level skill has the same name, it overrides the personal one for that project. To ensure project precedence, give personal skills distinct names.
Use for: - Your standard error envelope across personal projects - Your preferred testing patterns - Generic conventions you reuse
Do NOT use for: - Project-specific architecture - Client-specific business rules - Anything that would be wrong to apply to a different project
59.2 Option B: Templates repo (structured team setup)
A separate git repo containing your team's canonical kit files. Install into new projects via a script.
# 1. Create the templates repo
mkdir ~/dev/claude-orchestrator-templates
cd ~/dev/claude-orchestrator-templates
git init
# 2. Copy a known-good v4 .claude/ into the templates repo
cp -r /path/to/working-project/.claude .
# 3. Create install script
cat > install.sh <<'EOF'
#!/bin/bash
# Installs claude-orchestrator-templates into the current project
set -euo pipefail
TARGET="${1:-.}"
SOURCE="$(cd "$(dirname "$0")" && pwd)"
cp -r "$SOURCE/.claude" "$TARGET/"
chmod +x "$TARGET/.claude/scripts/"*.mjs
echo "Templates installed to $TARGET/.claude/"
echo "Next steps:"
echo " 1. Customize CLAUDE.md for this project"
echo " 2. Run: bd init"
echo " 3. Run: cd $TARGET && claude"
EOF
chmod +x install.sh
# 4. Commit
git add . && git commit -m "Initial templates repo"
git remote add origin git@github.com:yourorg/claude-orchestrator-templates.git
git push -u origin main
# 5. Use in new projects
cd /new-project
~/dev/claude-orchestrator-templates/install.sh
# OR via git clone:
git clone --depth 1 git@github.com:yourorg/claude-orchestrator-templates.git /tmp/tmpl
bash /tmp/tmpl/install.sh
Tracking which templates version a project uses:
Add to docs/00-project-brief.md:
## Adapter conformance
- Source: yourorg/claude-orchestrator-templates@<sha>
- Last sync: 2026-05-15
When templates update, sync manually:
cd /existing-project
# Diff current vs templates
diff -r .claude ~/dev/claude-orchestrator-templates/.claude
# Apply specific updates
cp ~/dev/claude-orchestrator-templates/.claude/skills/new-skill .claude/skills/ -r
59.3 Option C: Shared Beads namespace (decisions and patterns)
For teams maintaining multiple repos with shared patterns. Each project's tasks stay in its own namespace; cross-project pattern issues are shared.
# 1. Set up a shared Dolt server (one-time, team-wide)
# Option C1: Self-hosted DoltHub
# Option C2: DoltHub.com cloud (commercial)
# Option C3: Your own Postgres-compatible (Dolt supports MySQL protocol)
# Refer to Beads docs for current setup. Once running, you have a URL.
# 2. In each project's .beads/config.toml, point at the shared server
cat >> .beads/config.toml <<'EOF'
[dolt]
remote = "https://dolt.example.com/team/shared-knowledge"
namespace = "<this-project-name>"
EOF
# 3. Use a `pattern` type for cross-project shared content
bd create --type message --label pattern \
--title "Standard pagination approach" \
--body "Cursor-based on (created_at, id) composite key.
Avoid offset pagination beyond ~100k rows.
See: project-foo/src/cities/controller.ts:34"
# 4. Query patterns from any project
bd --remote query --label pattern --search pagination
Authentication and access control: Dolt supports user/role-based access; configure per your security needs.
When to use Option C: - Team with 3+ active projects sharing architecture decisions - Multi-repo organization with cross-cutting patterns - Decision history that should be queryable across projects
When NOT to use Option C: - Solo developer (Option A is enough) - Confidential client work where cross-project leakage is unacceptable - Projects that should remain fully isolated for security
59.4 Combining options
| Setup | Options to combine |
|---|---|
| Solo dev, personal projects | A only |
| Solo dev with multiple side projects | A (personal skills) |
| Small team (2-5), shared codebase patterns | A + B |
| Larger team with multiple repos | B + C |
| Enterprise with security boundaries | B only (no shared Beads cross-tenant) |
60. Optional Tier 4 — Token / Cost Tracking
Status: Optional, experimental. Adopt only if the visibility justifies maintenance cost.
Caveat: Reads Claude Code's transcript files (~/.claude/projects/<hash>/sessions/*.jsonl). The path/format is undocumented and may change between Claude Code versions. Pin a Claude Code version when using this.
60.1 log-token-usage.mjs
#!/usr/bin/env node
// PostToolUse async. Reads Claude Code transcript, extracts token usage, appends to JSONL.
import fs from 'node:fs';
import path from 'node:path';
import os from 'node:os';
const projectDir = process.env.CLAUDE_PROJECT_DIR;
const sessionId = process.env.CLAUDE_SESSION_ID;
if (!sessionId) process.exit(0);
const transcriptDir = path.join(os.homedir(), '.claude/projects');
if (!fs.existsSync(transcriptDir)) process.exit(0);
// Find current project's transcript
const projectHashes = fs.readdirSync(transcriptDir);
let transcriptPath = null;
for (const h of projectHashes) {
const candidate = path.join(transcriptDir, h, 'sessions', `${sessionId}.jsonl`);
if (fs.existsSync(candidate)) { transcriptPath = candidate; break; }
}
if (!transcriptPath) process.exit(0);
// Read last line (most recent turn)
const lines = fs.readFileSync(transcriptPath, 'utf8').split('\n').filter(Boolean);
const lastLine = lines[lines.length - 1];
let lastTurn;
try { lastTurn = JSON.parse(lastLine); } catch { process.exit(0); }
const usage = lastTurn.usage || lastTurn.message?.usage;
if (!usage) process.exit(0);
const activeTaskFile = path.join(projectDir, 'agent-artifacts/monitoring/active-task.json');
let taskId = null;
if (fs.existsSync(activeTaskFile)) {
try { taskId = JSON.parse(fs.readFileSync(activeTaskFile, 'utf8')).taskId; } catch {}
}
// Approximate cost (Sonnet pricing as of late 2025; UPDATE PER YOUR MODEL)
const inputCostPer1M = 3.00;
const outputCostPer1M = 15.00;
const cacheReadCostPer1M = 0.30;
const inputTokens = usage.input_tokens || 0;
const outputTokens = usage.output_tokens || 0;
const cacheTokens = usage.cache_read_input_tokens || 0;
const estimatedCost =
(inputTokens / 1e6) * inputCostPer1M +
(outputTokens / 1e6) * outputCostPer1M +
(cacheTokens / 1e6) * cacheReadCostPer1M;
const dir = path.join(projectDir, 'agent-artifacts/monitoring');
fs.mkdirSync(dir, { recursive: true });
fs.appendFileSync(path.join(dir, 'token-usage.jsonl'),
JSON.stringify({
ts: Date.now(),
taskId,
sessionId,
inputTokens,
outputTokens,
cacheTokens,
estimatedCost: Math.round(estimatedCost * 10000) / 10000
}) + '\n');
process.exit(0);
60.2 check-budget.mjs
#!/usr/bin/env node
// PreToolUse. Blocks if cumulative tokens for active task exceeds budget.
import fs from 'node:fs';
import path from 'node:path';
const projectDir = process.env.CLAUDE_PROJECT_DIR;
const activeTaskFile = path.join(projectDir, 'agent-artifacts/monitoring/active-task.json');
if (!fs.existsSync(activeTaskFile)) process.exit(0);
const { taskId } = JSON.parse(fs.readFileSync(activeTaskFile, 'utf8'));
const budgetFile = path.join(projectDir, 'agent-artifacts/monitoring/budget.json');
const DEFAULT_BUDGET = 100000; // 100k tokens per task
let budget = DEFAULT_BUDGET;
if (fs.existsSync(budgetFile)) {
try {
const b = JSON.parse(fs.readFileSync(budgetFile, 'utf8'));
budget = b[taskId] || b.default || DEFAULT_BUDGET;
} catch {}
}
const usageFile = path.join(projectDir, 'agent-artifacts/monitoring/token-usage.jsonl');
if (!fs.existsSync(usageFile)) process.exit(0);
const lines = fs.readFileSync(usageFile, 'utf8').split('\n').filter(Boolean);
const total = lines.reduce((sum, l) => {
try {
const o = JSON.parse(l);
if (o.taskId !== taskId) return sum;
return sum + (o.inputTokens || 0) + (o.outputTokens || 0);
} catch { return sum; }
}, 0);
if (total > budget) {
console.log(JSON.stringify({
decision: 'block',
reason: `Token budget exceeded for ${taskId}: ${total} > ${budget}. Stop and review the task scope, or raise budget in monitoring/budget.json.`
}));
process.exit(0);
}
process.exit(0);
60.3 Enabling Tier 4
# 1. Add scripts to .claude/scripts/
cp log-token-usage.mjs check-budget.mjs .claude/scripts/
# 2. Register in .claude/settings.json (add to PostToolUse and PreToolUse hooks lists)
# (See settings.json template, add the two new hook entries.)
# 3. Create initial budget config
echo '{ "default": 100000 }' > agent-artifacts/monitoring/budget.json
# 4. Verify
claude
> /hooks
# Should now show log-token-usage and check-budget registered
61. Glossary
| Term | Meaning |
|---|---|
| Adapter | A kit (like this one) that implements the tool-agnostic spec for a specific CLI (Claude Code, Codex, etc.) |
| ADR | Architecture Decision Record. Numbered, immutable file in docs/decisions/ |
| Artifact | Generated output file in agent-artifacts/: task brief, change summary, test report, etc. |
| Beads | Persistent task graph + agent memory layer. CLI bd, Dolt SQL backing store. |
| Brownfield | Project with existing code and/or docs. Opposite of greenfield. |
| Capability | A coherent feature area with its own spec.md (e.g., "user-auth", "booking-flow") |
| Capability profile | Read-only, docs-only-write, or scope-limited write. Enforced by permissions + hooks. |
| cgb | Codebase graph MCP. Read-only code structure index (call graphs, types). |
| Change | A proposed delta to one or more capabilities, in agent-artifacts/changes/<change-id>/ |
| Delta spec | Change's proposed ## ADDED / MODIFIED / REMOVED Requirements against a capability's spec |
| Digest | Intake sub-stage that ingests existing BRD/PRD/codebase into structured state |
| Greenfield | Net-new project with no existing code or docs |
| Hard constraint | An action the kit never allows (no override, no escape) |
| Hook | Node.js script registered in .claude/settings.json that runs on tool calls |
| MCP | Model Context Protocol. External capability servers (Context7, cgb, Playwright, Beads) |
| Plan Mode | Claude Code's read-only execution mode. Used during /intake. |
| Profile | See "Capability profile" |
| Repair attempt | Builder's retry after a failed validation. Capped at 2 total. |
| Skill | Markdown procedural reference under .claude/skills/. Auto-loads based on description. |
| Spec | Current truth for a capability: docs/specs/<capability>/spec.md |
| Subagent | Isolated agent (Researcher, Architect, etc.) under .claude/agents/. Own context, tools, MCP. |
| Task brief | Immutable contract for one Beads task. Defines in-scope and out-of-scope files. |
| Tier 1/2/3/4 | Adoption tiers from the changeset: Tier 1 (low-risk additive), Tier 2 (additive moderate), Tier 3 (BREAKING — specs-vs-changes), Tier 4 (optional token tracking) |
| Two-stage review | Tester-Reviewer's mandatory pass: Stage 1 spec compliance, then Stage 2 code quality (brief-blind) |
| Verb | A workflow entry point: intake, implement-task, orchestrator-next |
| Watchdog | The set of deterministic stop conditions enforced by hook scripts |
| Worktree | Git worktree isolation used by Builder. Separates work from main checkout. |
62. Changelog v3 → v4
Added
- Beads as task graph + agent memory layer (replaces flat
docs/04-task-index.md) - Digest mode for brownfield/BRD/PRD ingestion (
/intake-brownfield, digest sub-stage) - specs-vs-changes file model (breaking — replaces flat
docs/01-prd.md) - Two-stage subagent review (Tester-Reviewer's Stage 1 spec compliance + Stage 2 code quality)
- Test-failure loop detector (
track-test-failures.mjs) - Command-failure loop detector (
track-command-failures.mjs) - Health check (
health-check.mjs) - JSONL rotation (
rotate-jsonl.mjs) - Digest report validation (
validate-digest-report.mjs) - Source-doc citation validation (
validate-source-doc-cited.mjs) - Beads installation check (
validate-beads-installed.mjs) - 6 new skills:
digest,digest-report-template,beads-queries,decision-capture,delta-spec-format,subagent-review-checklists - "Input source documents" slot in source-of-truth priority chain (now 10 levels, was 8)
bd primeintegration ininject-active-task.mjsSessionStart hook- Cross-project knowledge sharing patterns documented (personal skills, templates repo, shared Beads)
- Optional token/cost tracking (Tier 4, experimental)
- Migration scripts for v3 → v4:
migrate-prd-to-specs.mjs,migrate-task-index-to-beads.mjs,migration-rename-briefs.sh
Changed
- Tester-Reviewer agent: two-stage workflow (Stage 1 spec compliance, then Stage 2 code quality)
- Maintainer agent: adds spec delta merge step + consolidated confirmation includes delta merge
- Researcher agent: adds
digestskill; runs digest sub-stage during intake - Planner agent: now uses
bdCLI for task creation; preloadsbeads-queriesanddelta-spec-formatskills - Builder agent: fresh-context-per-task discipline made normative across all adapters
inject-active-task.mjs: now callsbd primeand appends output to additionalContextvalidate-test-report.mjs: checks for both Stage 1 and Stage 2 sections- Maintainer consolidated confirmation: now includes spec delta merge proposal
/orchestrator-next: queries Beads first (bd ready --json) before reading reports- Task brief filename convention:
<bd-id>-<slug>.md(wasphase-N-task-NNN-slug.mdin v3) - Worktree isolation is now
isolation: worktreenormative for Builder (was optional in v3)
Removed
docs/01-prd.md(split into per-capability specs underdocs/specs/)docs/01-prd-summary.md(split into per-capability spec-summaries)docs/04-task-index.md(Beads replaces)
Migration impact
- Breaking for v3 projects. Migration tools provided.
- Expect ~1 hour manual review per non-trivial project.
- Spec version bump: v1.0 → v2.0 (major).
- Kit version bump: v3 → v4.
Deferred / not adopted (revisit later)
- gsd-2 watchdog features — revisit after 1.0 stable
- OpenSpec
extract/tracecommands — revisit when shipped - BMAD v7 role format — revisit if persona consolidation reverses or watchdog is added
- Beads 1.0 — revisit on stable release
- Full cross-project memory implementation — revisit at 5+ project scale
- No-progress timeout watchdog — requires OS-level scheduler; loop detectors cover most cases
End of Part 3. End of complete base document (Parts 1 + 2 + 3).
Final notes
Combine in order: Part 1 → Part 2 → Part 3.
Total content: Roughly 55,000 words across three files. Self-contained as a kit specification.
What you have after combining: - The "why" and conceptual model (Part 1) - The architecture and detailed specs (Part 2) - The concrete code and templates (Part 3)
What you still need to produce: The companion playbook (claude-workflow-playbook-v4-a-z.md) which gives the operator's manual — exact prompt templates, per-scenario runbooks, intake quick-starts for common request types. The kit (this 3-part document) defines the system; the playbook describes how to operate it day-to-day.
Approval needed before adopting: 1. Spec bump to v2.0 (BREAKING via §35 specs-vs-changes restructure) 2. Beads installation as required prerequisite 3. specs-vs-changes file model adoption
If you decline the breaking changes, fall back to a v3.1 minor release containing only Tier 1 and Tier 2 changes (no docs/specs/ restructure, no breaking).
End of Part 3.