{
  "version": "act 2.1.1",
  "slug": "code-review",
  "name": "code-review",
  "description": "Review code for bugs, complexity, unused symbols, and structural issues using AST-aware analysis. Use when reviewing PRs, checking code quality, finding dead code, analyzing function complexity, auditing a codebase, or checking for type errors. Each call reports the kinds it modeled in `modeled_kinds` \u2014 that is the honest coverage signal; the supported-language list is whatever act ships.",
  "url": "https://act101.ai/docs/skills/code-review",
  "body_md": "# Code Review with act\n\nAnalyze code for issues using act's query tools.\n\n## Start Here\n\nCheck workspace status first \u2014 some tools require LSP:\n```\nstatus()\n```\n\n## Exploration Pattern\n\nFor systematic file exploration, use a two-pass approach: parser-only tools first (`skeleton`, `symbols`), then LSP tools (`diagnostics`, `references`, `callers`). Start broad (directory skeletons), narrow (file symbols), then deep (reference chains).\n\n## Core Review Tools\n\n### Always available (parser-only)\n\n**skeleton** \u2014 Get file structure. Shows function signatures, class declarations, nesting depth.\n```\nskeleton(file=\"src/auth/login.ts\")\n```\n\n**symbols** \u2014 List all symbols with kinds and locations. Spot missing exports, large symbol counts.\n```\nsymbols(file=\"src/auth/login.ts\")\n```\n\n**unsafe_surface** \u2014 Flag dangerous constructs: unsafe blocks, `eval`, raw SQL, FFI, reflective invocation, unsafe deserialization. Syntactic floor (works without LSP), upgrades confidence when LSP is warm. Run it on any file handling input, queries, or native interop.\n```\nunsafe_surface(file=\"src/db/query.ts\")\n```\n\n### Requires LSP\n\n**diagnostics** \u2014 Compiler/linter errors, warnings, hints. The primary bug-finding tool.\n```\ndiagnostics(file=\"src/auth/login.ts\")\n```\n\n**references** \u2014 Find all references to a symbol. Detect unused exports (0 external references).\n```\nreferences(symbol=\"validateToken\", file=\"src/auth/token.ts\")\n```\n\n**callers** \u2014 Find functions that call a symbol. Understand coupling and blast radius.\n```\ncallers(symbol=\"processPayment\", file=\"src/billing.ts\")\n```\n\n**definition** \u2014 Jump to where a symbol is defined. Trace imports to source.\n```\ndefinition(symbol=\"UserService\", file=\"src/api.ts\", line=15, column=10)\n```\n\n**get_type** \u2014 Get the inferred type at a position. Check type correctness.\n```\nget_type(file=\"src/api.ts\", line=42, column=12)\n```\n\n## Review Workflow\n\n### For a single file\n1. `diagnostics` \u2014 get all errors/warnings\n2. `skeleton` \u2014 check structure (function sizes, nesting)\n3. `symbols` \u2014 check symbol density and naming\n\n### For a directory/module\n1. `skeleton` on each file \u2014 build structural overview\n2. `diagnostics` on each file \u2014 collect all issues\n3. `references` on exports \u2014 find unused public API\n4. `callers` on key functions \u2014 check coupling\n\n### For a PR (changed files)\n1. `diagnostics` on each changed file\n2. `skeleton` on changed files \u2014 check new structure\n3. `references` on renamed/moved symbols \u2014 verify all references updated\n4. `callers` on modified functions \u2014 assess impact (requires LSP)\n5. `analyze_impact` (symbol mode, Architecture) on modified functions \u2014 transitive callers without LSP.\n   Use `skeleton` first to confirm the symbol name, then: `analyze_impact(target=<file>, symbol=<name>)`.\n   Review `confidence`, `modeled_kinds`, and `unresolved` fields for analysis gaps.\n6. `analyze_api_diff` (Architecture) on changed PUBLIC surfaces \u2014 run it against the PR\n   base branch: `analyze_api_diff(base_ref=<base>)` (CLI twin `act analyze api-diff --base-ref <base>`;\n   omit `base_ref` to diff against HEAD). It diffs the merge-base of the base branch and the working\n   tree, returning per-symbol `rows` and `summary` counts. Cite the verdicts \u2014 **Breaking** (e.g. a\n   parameter added), **PossiblyBreaking** (other signature drift), **Additive** (a new export) \u2014 and\n   report the `summary` breaking/possibly_breaking/additive counts. Surface any **Unjudgeable** rows:\n   they name a grammar whose visibility act doesn't model, so the change is unjudged, NOT safe. Skip\n   this step when the PR touches no public API.\n7. `scan` \u2014 AI-code security pass on the change: hardcoded credentials\n   (pattern + entropy heuristic), `.cursorrules` backdoors, MCP-config RCE,\n   typosquat/hallucinated dependencies, GitHub Actions expression injection,\n   LLM-output-to-exec flows, prompt-injection surfaces, plus an AI-Code\n   Health Score.\n   Prefer `scan(root=<repo>, base_ref=<base branch>)` \u2014 it computes the\n   changed set itself (merge-base, rename-aware) and the report's `scope`\n   section discloses the diff scoping; scores are diff-scoped, never\n   comparable to full-repo scores. Fall back to\n   `scan(root=<repo>, files=[<changed files>])` when there is no git base\n   to diff against \u2014 path-scoped scans are cheap (the architecture graph\n   builds over the selected files only) and equally disclosed via the\n   `scope` section (`mode: \"paths\"`); their scores carry the same\n   never-comparable rule. With a committed `.act/baseline.json`, add\n   `baseline=\".act/baseline.json\"` \u2014 `baseline.new_finding_ids` is then\n   exactly \"findings this change introduces\".\n8. **Merge-safety verdict** \u2014 run `gate(base_ref=<base branch>)` (MCP) or\n   `act gate --base-ref <base branch>` (CLI) to get a deterministic per-function\n   verdict for the entire changed set. Cite its output directly; do not re-derive\n   merge-safety prose from `verify_diff_semantics`, `verify_test_impact`, or\n   `verify_side_effects` individually \u2014 those are the gate engine's inputs, and\n   the gate is the rule owner. The safe-to-merge skill follows the same verdict\n   rules and extends the gate workflow with post-merge validation.\n\n   Verdict semantics (exit codes: 0 MERGE, 1 BLOCK, 2 REVIEW, 3 UNKNOWN, 4 error):\n   - `MERGE` \u2014 every changed function cleared all three checks\n   - `REVIEW` \u2014 at least one function needs human judgment (no automatic block)\n   - `BLOCK` \u2014 at least one function failed a check; do not merge\n   - `UNKNOWN` \u2014 verification could not complete for at least one function;\n     treat as **not-verified**, never as a pass\n\n   **Receipts:** `gate --receipts` (or `gate(receipts=true)`) writes one\n   `<id>.json` file to `.act/receipts/` per verified function. Functions whose\n   file, symbol, and span-hashes match a valid receipt are reported as\n   `pre-verified by receipt <id>` \u2014 reviewers may accept that as evidence without\n   re-running the trio. Any hash or schema mismatch discards the receipt and\n   re-verifies from scratch automatically. Emission mechanics and receipt schema\n   live in the refactor-receipt skill.\n\n   **Worked example** (fixture: `auth.ts` with two working-tree changes):\n\n   ```\n   $ act gate --receipts\n   act gate \u2014 working tree vs HEAD (2 changed function(s))\n     MERGE   auth.ts::formatLabel \u2014 format (format-only)\n     MERGE   auth.ts::validateAge \u2014 format (format-only)\n   verdict: MERGE\n   $ echo $?\n   0\n   ```\n\n   Second run (receipts already written, consumed from cache):\n\n   ```\n   $ act gate --receipts\n   act gate \u2014 working tree vs HEAD (2 changed function(s))\n     MERGE   auth.ts::formatLabel \u2014 format (pre-verified by receipt 60e278c850577270)\n     MERGE   auth.ts::validateAge \u2014 format (pre-verified by receipt 8bb683bbfe8e27cf)\n   verdict: MERGE\n   $ echo $?\n   0\n   ```\n\n   A behavior change without test reach blocks (same fixture, `age >= 18`\n   flipped to `age > 18`):\n\n   ```\n   $ act gate\n   act gate \u2014 working tree vs HEAD (1 changed function(s))\n     BLOCK   auth.ts::validateAge \u2014 behavior, NO test reaches it (behavior change with no test reaching it)\n   verdict: BLOCK\n   $ echo $?\n   1\n   ```\n\n## What to Look For\n\n| Signal | Tool | Severity |\n|--------|------|----------|\n| Compiler errors | diagnostics | Error |\n| Type mismatches | diagnostics, get_type | Error |\n| Unused imports/variables | diagnostics | Warning |\n| Function >50 lines | skeleton | Warning |\n| Function >5 parameters | skeleton | Warning |\n| Nesting depth >4 | skeleton | Warning |\n| Symbol with 0 references | references | Info |\n| High caller count (>10) | callers | Info |\n| eval / raw SQL / FFI / unsafe deserialize | unsafe_surface | Error/Warning |\n| Hardcoded credential / `.cursorrules` backdoor / MCP-config RCE | scan | Error |\n\n## Advanced Patterns\n\nSee [review-patterns.md](references/review-patterns.md) for detailed review checklists and multi-file analysis strategies.\n\n## Error Recovery\n\nSee [error-recovery.md](references/error-recovery.md) for handling LSP failures, file-not-found, and ambiguous symbols.\n\n## Output Format\n\nReport findings grouped by severity:\n\n```\n## Review: src/auth/login.ts\n\n### Errors (2)\n- Line 45: Type 'string' is not assignable to type 'number' [diagnostics]\n- Line 72: Cannot find name 'authConfig' [diagnostics]\n\n### Warnings (3)\n- Line 15-80: Function `handleLogin` is 65 lines (>50 threshold) [skeleton]\n- Line 23: Unused import 'lodash' [diagnostics]\n- Line 90: `validateToken` has 0 external references \u2014 possibly dead code [references]\n\n### Info (1)\n- `processAuth` called from 12 locations \u2014 high coupling [callers]\n```"
}