* refactor: restructure CLAUDE.md into modular rules and add pr-shepherd command Trim CLAUDE.md from 710 lines to 92 by moving detailed guidance into path-scoped `.claude/rules/` files that load on demand. Add a new `/pr-shepherd` command that consolidates the full PR lifecycle (review, fix, quality gate, CI fix loop, merge) into one workflow. Changes: - CLAUDE.md: keep only essentials (build commands, code style, architecture, module specs, config reference, debugging) - .claude/rules/review-discipline.md: 15+ review rules, scoped to src/**/*.rs - .claude/rules/database.md: dual-backend rules with SQL dialect translation table, scoped to src/db/** and migrations/** - .claude/rules/safety-and-sandbox.md: safety layer and sandbox rules, scoped to src/safety/**, src/sandbox/**, src/secrets/** - .claude/rules/testing.md: test tiers and patterns, scoped to src/** and tests/** - .claude/rules/tools.md: tool architecture and implementation pattern, scoped to src/tools/** and tools-src/** - .claude/commands/pr-shepherd.md: 7-phase PR lifecycle command that subsumes review-pr, respond-pr, ship, and manual CI fix loops [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: address review feedback on CLAUDE.md restructure - Restore project structure tree in CLAUDE.md (zmanian blocking) - Create .claude/rules/skills.md with trust model, SKILL.md format, selection pipeline, and skill tools (zmanian blocking) - Restore configuration section with key env vars (zmanian medium) - Restore "Adding a New Channel" guide (zmanian medium) - Add heartbeat mention to Workspace & Memory section (zmanian low) - Fix pr-shepherd: replace `git add -A` with specific file staging (zmanian) - Fix pr-shepherd: ask user for merge strategy instead of hardcoding --squash (zmanian) - Fix pr-shepherd: replace `--watch` with polling + 10min timeout (zmanian) - Fix testing.md: "skipped if DB is unreachable" not "expected to fail" (Copilot) [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: address review comments on PR #750 - Narrow `crate::` import rule: `super::` is fine in tests and intra-module refs - Fix capabilities file naming: `<name>.capabilities.json` sidecar, not bare `capabilities.json` - Update mechanical verification checklist to match narrowed import rule Co-Authored-By: Claude Opus 4.6 <[email protected]> * refactor: move Bedrock docs from CLAUDE.md to src/llm/CLAUDE.md Bedrock provider details (auth, config, feature flag) belong in the LLM module spec, not the top-level guide. Added file map entry, provider table row, and dedicated section in src/llm/CLAUDE.md. Co-Authored-By: Claude Opus 4.6 <[email protected]> * refactor: move env var config block out of CLAUDE.md Replace 20-line config block with one-liner pointing to .env.example and src/llm/CLAUDE.md. Config details are only needed during deployment, not everyday coding. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: use gh pr checkout for fork-safe PR checkout in pr-shepherd Replaces git fetch/checkout with gh pr checkout {number} which handles both same-repo and fork-based PRs automatically. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: address Copilot review round 5 on PR #750 - Add gh pr list and gh pr checkout to pr-shepherd allowed-tools - Align crate:: import rule in pr-shepherd with updated CLAUDE.md guidance - Fix vector type in database.md: BLOB (flexible dims), not F32_BLOB(1536) - Update MCP limitation: stdio/HTTP/Unix transports exist, no streaming Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]>
10 KiB
description, disable-model-invocation, allowed-tools, argument-hint
| description | disable-model-invocation | allowed-tools | argument-hint |
|---|---|---|---|
| Full PR lifecycle — review, fix findings, address comments, quality gate, push, CI fix loop, merge | true | Bash(gh pr view:*), Bash(gh pr diff:*), Bash(gh pr comment:*), Bash(gh pr merge:*), Bash(gh pr checks:*), Bash(gh pr edit:*), Bash(gh pr list:*), Bash(gh pr checkout:*), Bash(gh api:*), Bash(gh repo view:*), Bash(gh run view:*), Bash(gh run watch:*), Bash(git diff:*), Bash(git log:*), Bash(git fetch:*), Bash(git checkout:*), Bash(git status:*), Bash(git branch:*), Bash(git add:*), Bash(git commit:*), Bash(git push:*), Bash(git merge:*), Bash(git rebase:*), Bash(cargo fmt:*), Bash(cargo clippy:*), Bash(cargo test:*), Bash(cargo check:*), Read, Edit, Write, Grep, Glob, Agent | <pr-number or url> [--fix] [--merge] [--review-only] |
PR Shepherd
Full PR lifecycle: review → fix → quality gate → push → CI → merge.
Parse $ARGUMENTS:
- Extract PR number from bare number or
https://github.com/owner/repo/pull/123URL. - Flags:
--fix(auto-fix without asking),--merge(merge when CI green),--review-only(stop after review, don't fix). - If no PR number, detect from current branch:
gh pr list --head $(git branch --show-current) --json number --jq '.[0].number' - If still nothing, stop and ask the user.
Phase 1: Situational Awareness
Gather everything in parallel:
PR metadata:
gh pr view {number} --json number,title,body,author,baseRefName,headRefName,headRefOid,state,isDraft,mergeable,mergeStateStatus,files,additions,deletions,labels,reviewRequests
Diff:
gh pr diff {number}
gh pr diff {number} --name-only
CI status:
gh pr checks {number} --json name,status,conclusion,detailsUrl
Review comments (human + bot):
gh api --paginate repos/{owner}/{repo}/pulls/{number}/comments
gh api --paginate repos/{owner}/{repo}/pulls/{number}/reviews
Resolve {owner}/{repo}:
gh repo view --json owner,name --jq '"\(.owner.login)/\(.name)"'
Save headRefOid — needed for posting line comments later.
Assess the situation and print a status card:
PR #{number}: {title}
Author: {author} Base: {base} ← {head}
Size: +{additions} -{deletions} across {file_count} files
CI: {PASS|FAIL|PENDING|NONE} Mergeable: {yes|no|conflict}
Reviews: {N approved, N changes_requested, N comments-only, N bot-only}
Unresolved comments: {N}
Draft: {yes|no}
Decide the mode based on situation:
- Has unresolved review comments → Phase 2a (address comments first, then review remaining)
- No reviews yet / bot-only reviews → Phase 2b (full deep review)
- CI failing, no review issues → Phase 4 (jump to CI fix)
- Everything green + approved → Phase 6 (ready to merge)
Phase 2a: Address Existing Review Comments
For each unresolved review comment or review with CHANGES_REQUESTED:
-
Read the referenced code at the file and line mentioned. Never assess without reading.
-
Classify each comment:
- ✅ Valid & unresolved — needs a code fix
- ✅ Already fixed — a later commit addressed it
- ❌ False positive — explain why the code is correct
- 🔧 Nit — optional improvement, not blocking
-
Deduplicate — bots (Copilot, Gemini) often post the same finding. Group by actual issue.
Present a table:
| # | Source | File:Line | Issue | Status | Planned Fix |
|---|
Wait for user confirmation (unless --fix flag set), then proceed to Phase 3.
Phase 2b: Deep Review (6 Lenses)
Read EVERY changed file in full (not just diff hunks). For PRs touching >20 files, prioritize: service logic > handlers > types > tests > docs. Batch reads in parallel via Agent tool.
IronClaw-specific checks (always)
- No
.unwrap()or.expect()in production code - Prefer
crate::for cross-module imports (super::OK in tests/intra-module) - Error types use
thiserror - If persistence touched, both backends updated (postgres.rs AND libsql/)
- New tools implement
Tooltrait correctly and registered - External tool output passes through safety layer
- Tool parameters redacted before logging/SSE
- No byte-index slicing on external strings
- Case-insensitive comparisons where needed
Correctness
Off-by-one, wrong operators, inverted conditions, unreachable code, type confusion, error propagation, broken invariants, TOCTOU races.
Edge cases & failure handling
Empty/None/zero-length input, external service failures, integer boundaries, malformed/adversarial input, partial failure handling.
Security (assume adversarial actors)
Auth/authz bypass, IDOR, injection (SQL/command/log/header), data leakage in logs/errors/API responses, resource exhaustion, replay/race conditions.
Test coverage
New public functions tested? Error paths tested? Edge cases covered? Existing tests still valid?
Architecture
Follows existing patterns? Unnecessary abstractions? Duplicated logic? Clean module dependencies?
Present findings as a table:
| # | Severity | Category | File:Line | Finding | Suggested Fix |
|---|
Severity: Critical > High > Medium > Low > Nit
If --review-only flag is set, post findings as GitHub comments (see Phase 2c) and STOP.
Otherwise, ask which findings to fix (default: all Critical + High + Medium). Then proceed to Phase 3.
Phase 2c: Post Review Comments on GitHub
For each finding the user approved (or all Critical/High/Medium if --fix):
Line-specific findings — post as PR review comments:
gh api repos/{owner}/{repo}/pulls/{number}/comments \
-f body="**{Severity}**: {finding}\n\n{explanation}\n\n**Suggested fix:** {suggestion}" \
-f path="{file}" \
-f commit_id="{headRefOid}" \
-F line={line} \
-f side="RIGHT"
Cross-cutting/architectural findings — post as regular PR comment:
gh pr comment {number} --body "..."
Phase 3: Fix
Checkout the PR branch if not already on it (handles fork PRs automatically):
gh pr checkout {number}
Implement fixes for:
- All approved review comment fixes (from Phase 2a)
- All approved review findings (from Phase 2b)
Follow IronClaw conventions:
thiserrorfor errorscrate::imports- No
.unwrap()in production - Both DB backends if persistence touched
- Regression test for every bug fix (enforced by commit-msg hook; bypass only with
[skip-regression-check]if genuinely not feasible)
After all fixes implemented, proceed to Phase 4.
Phase 4: Quality Gate
Run the full IronClaw shipping checklist:
cargo fmt
cargo clippy --all --benches --tests --examples --all-features
cargo test --lib
If persistence changes are present, also verify feature isolation:
cargo check --no-default-features --features libsql
cargo check --all-features
If any step fails: fix the issue and re-run. Do NOT proceed past a failing step. Loop up to 3 times per step. If still failing after 3 attempts, report the failure and stop.
Phase 5: Commit & Push
Stage changed files by name (never git add -A — it can include unintended files):
git add path/to/changed/file1 path/to/changed/file2
git commit -m "{message}"
Commit message format:
- For review fixes:
fix: address review findings on PR #{number} - For comment responses:
fix: address review comments on PR #{number} - For CI fixes:
fix: resolve CI failures on PR #{number} - Include specifics in the body (which findings/comments were addressed)
Push:
git push origin {headRefName}
Reply to addressed review comments on GitHub. For each comment that was fixed, reply with the commit SHA and a brief description of what was done. For false positives, reply explaining why no change was needed.
Phase 6: CI Monitor & Fix Loop
Wait briefly for CI to start, then poll (do NOT use --watch as it can hang indefinitely):
gh pr checks {number} --json name,status,conclusion
Re-check every 30 seconds, up to 10 minutes. If still pending after 10 minutes, report status and ask the user whether to keep waiting.
If CI passes → proceed to Phase 7.
If CI fails (up to 3 fix attempts):
-
Identify the failing check:
gh run view {run_id} --log-failedIf
--log-failedshows nothing useful:gh run view {run_id} --log | tail -100 -
Diagnose and fix the failure.
-
Re-run Phase 4 (quality gate).
-
Commit and push (Phase 5).
-
Go back to top of Phase 6.
After 3 failed CI fix attempts: Report what's failing and why, then stop. Don't keep looping.
Phase 7: Merge Decision
Print final status:
PR #{number}: {title}
CI: ✅ PASS
Reviews: {summary}
Findings fixed: {N}
Comments addressed: {N}
Commits added: {N}
Auto-merge conditions (if --merge flag or user confirms):
- CI is passing
- No unresolved CHANGES_REQUESTED reviews
- PR is not draft
- PR is mergeable (no conflicts)
If all conditions met, ask the user for merge strategy:
"CI is green. Merge this PR? [squash/rebase/merge/no]"
Then execute:
gh pr merge {number} --{strategy} --delete-branch
If any condition NOT met, report what's blocking and let the user decide.
Rules
- Read before judging. Never comment on code you haven't read in full. Verify line numbers.
- Be specific. "Line 42 returns 404 but should return 400 because X" not "this might have issues."
- Fix the pattern, not just the instance. When fixing a bug, grep for the same pattern across
src/. - Respect the commit-msg hook. Bug fixes need regression tests. Use
[skip-regression-check]only if genuinely not feasible. - Don't over-fix. Only change what was flagged. Don't refactor surrounding code or add improvements beyond the review scope.
- Credit original authors. If taking over someone else's PR, credit them in commits and comments.
- No secrets in comments. Never include customer data, credentials, or PII in GitHub comments.
- Distinguish certainty. "This IS a bug" vs "This COULD be a bug if X." Be honest.
- Round up severity when uncertain. Cheaper to dismiss a false alarm than miss a real bug.
- Parallel where possible. Use Agent tool for parallel file reads on large PRs. Batch
gh apicalls.