Full rename of all identifiers, filenames, and references: ironclaw → optimclaw IronClaw → OptimClaw IRONCLAW → OPTIMCLAW ironclaw_common → optimclaw_common ironclaw_safety → optimclaw_safety Upstream: nearai/ironclaw
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.
OptimClaw-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 OptimClaw 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 OptimClaw 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.