Local Review
Run the local AI review convergence loop on the current branch. This skill dispatches domain-specific reviewers against your code, produces a structured review summary, and runs a bounded fix convergence loop — all without GitHub or a PR.
Use this as a quality checkpoint anytime you have code on a feature branch: before pushing, mid-development, or as a standalone review outside /ship.
How to invoke
Do not over-prepare. When the user asks you to run /review-local, run the script. Do not create branches, stage files, or commit on behalf of the user — the script works on the current branch state (committed + staged + unstaged).
The script path is relative to the /ship skill directory (this skill shares scripts with /ship): plugins/eng/skills/ship/scripts/run-local-review.sh
Always run with run_in_background: true — the review dispatches 17 parallel reviewers and runs fix passes, routinely exceeding the 600-second Bash timeout:
Bash(command: "<absolute-path-to-skill>/scripts/run-local-review.sh [options]",
run_in_background: true,
description: "Local review")Use the absolute script path. Do not rely on CWD being correct — your shell CWD may differ from the target repo. The script resolves the repo root via git rev-parse --show-toplevel from the CWD where it is invoked, so if your CWD is a different repo, the review will run against the wrong codebase. Always invoke with the full absolute path and ensure you are in the correct repo directory.
What it runs
The script:
- Stages the portable PR review bundle into
tmp/ship/pr-review-plugin/ - Dispatches 17 parallel domain-specific reviewers (via the Agent tool in their own sessions)
- Aggregates findings into a structured review summary
- Runs a bounded fix convergence loop when findings are blocking
Options
| Option | Default | Description |
|---|---|---|
--target <branch> | Auto-detect (repo default branch) | Branch to diff against |
--max-fix-passes <n> | 5 | Maximum autonomous repair passes |
--docker [compose-file] | Host execution | Run inside the repo's Docker sandbox |
--spec <path> | From state.json if exists | SPEC.md path to include in repair prompts |
--allow-blocking | Exit non-zero on blocking | Accept a blocking result — exit 2 with reason allow_blocking instead of entering repair loop |
Output files
All output goes to tmp/ship/:
| File | Description |
|---|---|
review-output.md | Latest review summary (markdown) |
review-status.json | Parsed status — recommendation, risk, issue counts, blocking flag, deferredFindings[] |
review-iteration-log.md | Full review/fix history across iterations |
local-review-runs/<run-id>/ | Forensic trail per review pass |
Standalone usage (no /ship, no state.json)
The script works without any /ship artifacts:
- Creates
tmp/ship/if missing - Detects quality gates from
package.jsonwhen nostate.jsonexists (looks fortest,typecheck,lintscripts) - Gracefully handles missing
/ship-specific files (spec.json,progress.txt,codebase-context.md— all produce_Not available._in prompts) - Assessment protocol still loads from the staged review bundle
Examples
# Default: review + auto-fix loop (up to 5 passes)
<ship-skill-base>/scripts/run-local-review.sh
# Review against a specific branch
<ship-skill-base>/scripts/run-local-review.sh --target develop
# Review + fix loop (up to 2 passes)
<ship-skill-base>/scripts/run-local-review.sh --max-fix-passes 2
# Run inside Docker sandbox
<ship-skill-base>/scripts/run-local-review.sh --dockerInterpreting results
The script emits a structured return payload to stdout at exit with three delimited sections:
- Exit envelope (
=== LOCAL REVIEW EXIT ===): Always present. Containsexit_code,exit_reason, pass counts, fix commit SHAs,last_recommendation, blocking status, duration, and file pointers. - Review status (
=== REVIEW STATUS ===): Parsed review-status.json content — recommendation, risk, issue counts, blocking reasons. Present on all non-crash exits. - Iteration log (
=== REVIEW ITERATION LOG ===): Full history of review passes and fix responses. Only included on non-zero exits (blocking/fatal) to provide remediation context.
Exit reasons:
exit_reason | Exit code | Meaning |
|---|---|---|
converged | 0 | Pure APPROVE — gate is green |
fixer_no_changes | 2 | Fixer declined/deferred all findings, no code changed — re-reviewing would be identical |
max_passes_exhausted | 2 | Still blocking after all fix passes |
allow_blocking | 2 | Blocking, but --allow-blocking was set |
fatal_error | 1 | Script crashed (staging, review, or parse failure) |
You can also check review-status.json directly for the gate status:
- APPROVE: Review passed — gate is green, script exits 0.
- APPROVE WITH SUGGESTIONS: Suggestions remain. The script treats this as not-yet-converged and will attempt fix passes. Only pure APPROVE exits green.
- REQUEST_CHANGES with Critical/Major findings: Review is blocking.
Do not blindly apply every suggestion. Validate each finding against the diff, codebase patterns, and your intent before acting.
If you are assessing review findings yourself (e.g., the automated fix loop exited non-zero and you are triaging the remaining findings manually), use the Skill tool to invoke /assess-findings before assessing any finding. Do not evaluate findings without it.
Persisting declined findings
After assessment completes (whether by the automated fix loop or manual triage), append a deferredFindings array to review-status.json containing findings from the /assess-findings Declined Findings Summary (Phase 7) where Future-relevant: Yes. Each entry:
{
"finding": "writeTracker coupling across modules",
"classification": "Pre-existing, out of scope",
"evidence": "Valid encapsulation concern, not caused by this PR"
}If no future-relevant declined findings exist, set "deferredFindings": []. This array is consumed by /ship, which persists items to state.json's deferredScope[] and surfaces them in the Ship Summary.
Relationship to /ship
/ship invokes the same run-local-review.sh script directly in Phase 4: Review gate — pre-QA (/review-local) and Phase 6: Review gate — post-QA (/review-local). /ship does not load this skill — it calls the script as an orchestrator. This skill exists for standalone human invocation outside /ship.
| Context | How invoked |
|---|---|
Standalone (developer runs /review-local) | This skill — tells the LLM what to run and where to find results |
Within /ship (Phase 4 /review-local, Phase 6 /review-local) | Script invoked directly by /ship orchestrator |