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)

  1. Setup Procedure
  2. Complete .claude/settings.json
  3. Agent: researcher.md
  4. Agent: architect.md
  5. Agent: planner.md
  6. Agent: builder.md
  7. Agent: tester-reviewer.md
  8. Agent: maintainer.md
  9. Agent: frontend-browser-tester.md
  10. All 18 SKILL.md Files
  11. Output Style Definitions
  12. All Hook Helper Scripts
  13. JSON Schemas
  14. Migration Scripts (v3 → v4)
  15. Artifact Templates
  16. Operations — Daily, Weekly, Monthly
  17. Troubleshooting Guide
  18. Cross-Project Knowledge Sharing — Detailed Setup
  19. Optional Tier 4 — Token / Cost Tracking
  20. Glossary
  21. 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


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)

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

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/.md following change-summary-template skill. Required sections: Task, Status, Files changed, Behavior added/changed, Tests added/changed, Validation run, Known risks, Docs impact, Version recommendation, Discovered work.

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

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

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: - Tech behavior changed: - User-visible change: - Spec delta to merge: (capability: ) - Version bump recommended: - Cleanup needed: (count: N)

Proposed actions: 1. Update docs/05-api-docs.md () 2. Update docs/06-tech-docs.md () 3. Update docs/07-testing.md () 4. Merge delta into docs/specs//spec.md 5. Regenerate docs/specs//spec-summary.md 6. Archive change folder if all tasks done 7. Add CHANGELOG entry under Unreleased 8. Bump version 9. Close bd- in Beads with status DONE 10. Remove debug logs / scratch files

Choose: A. Approve all B. Modify (specify which to skip or change) C. Skip docs (do cleanup + changelog + version only) D. Bump only (no doc changes) E. Don't bump version (everything else)


### 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/.md following maintenance-report-template skill. Required sections: Cleanup, Documentation updates, Spec delta merge, Changelog, Version, Beads, Final validation, Remaining risks.

Step 8: Stop

The validate-maintenance-report.mjs hook will verify the report exists.

Hard constraints

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

Critical ADRs

Anti-patterns (do not do)


### 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

Test data

Coverage targets

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


### 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)


### 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

Logging

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


### 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

When Maintainer adds entries


### 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


### 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


### 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


### 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


### 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)


### 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


### 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


### 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

## MODIFIED

## REMOVED

Quality bar


### 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:

  1. Don't disable the rule. Disabling defeats the purpose.
  2. Fix the underlying issue. If the task brief's in-scope list is incomplete, revise the brief (new task with supersedes), then retry.
  3. Tune the threshold only after multiple legitimate triggers. Edit .claude/scripts/<script>.mjs and 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

Changed

Removed

Migration impact

Deferred / not adopted (revisit later)


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.