---
name: trekreview
description: |
Independent post-hoc review of delivered code against the brief. Produces
review.md with severity-tagged findings (BLOCKER/MAJOR/MINOR/SUGGESTION)
per Handover 6 (review → plan).
argument-hint: "--project
[--since [] [--quick] [--validate] [--dry-run]"
allowed-tools: Agent, Read, Glob, Grep, Write, Edit, Bash, AskUserQuestion, Skill
---
# Ultrareview Local v1.0
Independent post-hoc review of code delivered by `/trekexecute`
against the contract in `brief.md`. Produces `review.md` — a structured
artifact with severity-tagged findings that `/trekplan --brief
review.md` can consume as plan input (Handover 6).
Pipeline position:
```
/trekbrief → brief.md
/trekresearch → research/*.md
/trekplan → plan.md
/trekexecute → progress.json (+ commits)
/trekreview → review.md (this command)
```
The review is **independent**: each reviewer runs without cross-feeding,
and the coordinator applies BOUNDED operations only. Synthesis-level
inference across files is forbidden in v1.0 (Judge Agent pattern).
**The review is a gate (review_version 1.1, ordre 5).** In this order:
Claude Code's own `/code-review` and `/security-review` are the engine
(Phase 4.7); Voyage's control against the brief runs after them and is the
part that stops (Phase 5–6); the verdict is written in `review.md` and one
command decides on it (`lib/review/verdict-gate.mjs`, Phase 8: 0 = ALLOW/WARN,
1 = BLOCK, 2 = could not decide, never green). No finding is struck for
length. A finding class seen in two reviews becomes one proposal for the
project's memory, approved line by line (Phase 8.7). Order credited to Boris
Cherny's public workflow (engine → brief control); the gate to Kiro and Spec
Kit — see `docs/HANDOVER-CONTRACTS.md` § Handover 6.
See `agents/review-orchestrator.md` for the canonical workflow this
command executes inline.
## Phase 1 — Parse mode and validate input
Parse the arguments with the shared arg-parser. Run it first, exactly like
this, before anything else:
```bash
node ${CLAUDE_PLUGIN_ROOT}/lib/parsers/arg-parser.mjs --command trekreview -- $ARGUMENTS
```
It prints the parse as JSON on stdout. It also checks the required flag
itself: on exit 1 it has printed the error and usage lines on stderr.
The parser recognizes these flags (see `lib/parsers/arg-parser.mjs`
FLAG_SCHEMA `trekreview` entry):
| Flag | Type | Purpose |
|------|------|---------|
| `--project ` | valued | Required. Path to trekplan project folder containing `brief.md`. |
| `--since ][` | valued | Optional. Override "before" SHA for the diff. Validated via `git rev-parse --verify`. |
| `--quick` | boolean | Skip the brief-conformance pass; run only the code-correctness reviewer; skip the coordinator's reasonableness filter. |
| `--validate` | boolean | Schema-only check on existing `{project_dir}/review.md`. No LLM calls. |
| `--dry-run` | boolean | Print the discovered scope and triage map. Skip writes. |
| `--fg` | boolean | No-op alias (foreground is default). |
| `--workflow` | boolean | **(opt-in, NW2)** Run Phase 5–6 on the bake-off-validated Workflow substrate (`scripts/trekreview-armB.workflow.mjs`) instead of the default prose Agent-tool path. Requires **Claude Code 2.1.154+**. Combines with `--quick`. See *§ Phase 5–6 via the Workflow substrate*. |
Resolution:
1. If the parser exits 1 (for example `--project` is missing), print its
stderr lines verbatim, exactly as the parser wrote them, and stop. Do not
compose the error message yourself.
2. Trim trailing slash from `{dir}`. Set:
- `project_dir = {dir}`
- `brief_path = {dir}/brief.md`
- `review_path = {dir}/review.md`
3. If `{dir}` does not exist or `{dir}/brief.md` is missing:
```
Error: project directory not initialized. Run /trekbrief first.
Missing: {dir}/brief.md
```
Set `mode`:
- `validate` if `--validate` is set (overrides everything else; skip to Phase 8.5).
- `dry-run` if `--dry-run` is set.
- `quick` if `--quick` is set.
- `default` otherwise.
Set `workflow_substrate` (orthogonal to `mode` — a substrate choice, not a behavior mode):
- `true` if `--workflow` is set — Phase 5–6 run on the Workflow substrate (see the Phase 5
routing gate). The Workflow tool requires **Claude Code 2.1.154+**; if it is unavailable,
fall back to the prose path and note the fallback in the Executive Summary.
- `false` otherwise. **Default stays prose**: the substrate is opt-in, so the lower
portability floor of the prose path is preserved unless the operator opts in.
## Phase 2 — Validate brief
Run the brief validator in soft mode — the brief is upstream context, not
something this command produces, so partial grades are acceptable as long
as the file is parseable:
```bash
node ${CLAUDE_PLUGIN_ROOT}/lib/validators/brief-validator.mjs --soft --json "{brief_path}"
# v5.9 — composed phase-model resolution (brief > profile > default) for the
# review phase. ONE call returns {model, source}, plus
# effort only when the brief's phase signal sets one; without it the phase
# runs at effort `standard` (no change). Captured as
# phase_signal_result and used in Phase 7 at the reviewer-launch site to
# inject the resolved model. Append --profile {profile} when the operator
# passed --profile.
node ${CLAUDE_PLUGIN_ROOT}/lib/profiles/resolver.mjs --resolve-phase-model --phase review --brief-path "{brief_path}" [--profile {profile}] --json
```
Read the JSON output. If `valid: false` AND any error has code
`BRIEF_MISSING_REQUIRED_FIELD` or `FRONTMATTER_PARSE_ERROR`: stop and
ask the user to re-run `/trekbrief`. Other soft errors become
warnings in the review's Executive Summary.
Read the brief frontmatter. Capture for review.md:
- `task` → review frontmatter `task`
- `slug` → review frontmatter `slug`
- `project_dir` → review frontmatter `project_dir` (defaults to the
CLI `--project` value when missing)
## Phase 3 — Discover scope SHA range
Determine the "before" SHA that bounds the review:
1. **`--since ][` override** — if set, validate via:
```bash
git rev-parse --verify "$since_ref"
```
On failure: print `Error: --since ref is not a valid git revision: {ref}` and stop.
Set `before_sha = $(git rev-parse --verify "$since_ref")`.
2. **Preferred path** — read `{project_dir}/progress.json` if it exists.
Extract `session_start_sha`. Validate it via `git rev-parse --verify`.
Set `before_sha = session_start_sha`.
3. **Fallback** — no `progress.json`. Use the brief's mtime to find the
most recent commit at or before the brief was written:
```bash
brief_mtime=$(stat -f %m "{brief_path}") # macOS; on Linux use stat -c %Y
before_sha=$(git log --until="@$brief_mtime" -n 1 --format=%H)
```
Emit a clear warning that gets surfaced in the review's Executive
Summary: "scope_sha_start unavailable — falling back to brief mtime
({timestamp}). Coverage may include unrelated commits."
Compute the "after" SHA: `after_sha=$(git rev-parse HEAD)`.
Capture working-tree changes (uncommitted at review time):
```bash
git diff --name-only "$before_sha".."$after_sha"
git diff --name-only HEAD # uncommitted (annotated [uncommitted])
```
The combined file list is the review scope. Note that the
`[uncommitted]` annotation is a **brief-level contract** — the brief's
Assumptions section declares this is allowed; the review surfaces it
explicitly in the Coverage table.
If the file count is `0`, write a minimal review.md per the template:
`verdict: ALLOW`, `findings: []` (the empty list is the one inline form the
parser reads — a bare `findings:` is null and fails validation), the
required sections (`## Executive Summary`, `## Coverage`, `## Declined to
Judge` with `None.` and `Trimmed: 0`, `## Remediation Summary`), each saying
"No diff between {before_sha} and {after_sha}; nothing to review.", and the
trailing block `{"verdict": "ALLOW", "counts": {"BLOCKER": 0, "MAJOR": 0,
"MINOR": 0, "SUGGESTION": 0}, "allow_blocked_by": [], "findings": []}` —
without it the verdict gate
cannot back the ALLOW and exits 2. There is nothing for the Phase 4.7 engine
to review, so its absence does not withhold ALLOW here. Skip Phases 4–7.
Continue to Phase 8 (validate + stats).
## Phase 4 — Triage gate (deterministic path-pattern classifier)
The triage gate is **deterministic** — no LLM judgment. It classifies
every file from Phase 3 into a treatment bucket:
| Treatment | When |
|-----------|------|
| `skip` | Matches `*.lock`, `*.svg`, `dist/**`, `build/**`, `node_modules/**`, OR the file's first 3 lines contain a generated-file marker (`@generated`, `Code generated by`, `DO NOT EDIT`). |
| `deep-review` | Matches `auth/**`, `crypto/**`, `**/security/**`, `hooks/**`. |
| `summary-only` | Default treatment for everything else. |
Hard refuse-with-suggestion gates — use `AskUserQuestion`:
```
if (reviewed_files_count > 100) → ask user
if (estimated_diff_tokens > 100000) → ask user
```
Token estimation: `wc -c "$diff_file" / 4` (rough proxy). Use
`AskUserQuestion` with the prompt:
> The diff under review is large (`{N}` files / `~{T}` tokens). Continue
> with the full scope, narrow with `--since `, or stop?
Options:
1. **Continue** — proceed at this scope.
2. **Narrow** — print suggested `git log --oneline {before}..HEAD` so the
user can pick a closer ref, then stop.
3. **Stop** — cancel.
Record the treatment for every file. Files marked `skip` MUST appear in
the Coverage section of `review.md` — never silently drop them. Silent
drops are `COVERAGE_SILENT_SKIP` (MAJOR) per the rule catalogue.
If `mode == dry-run`: print the triage map and exit.
## Phase 4.5 — Run the brief's success-criteria checks
**Skipped in `quick` mode** (that mode does not launch
`brief-conformance-reviewer`, so there is nobody to hand the result to) and in
`dry-run`.
`brief-conformance-reviewer` is asked to decide whether each Success Criterion's
verification command passes. Its tools are `Read`, `Glob`, `Grep` — it cannot
run anything, and it stays that way: a reviewer that executes the code it
reviews is not an independent reviewer. So THIS command runs the commands, and
the reviewer judges the RESULT.
```bash
# Resolve the plugin root ONCE. ${CLAUDE_PLUGIN_ROOT} is substituted in this
# command's text but is EMPTY in the Bash tool's process env, and a bare
# `node ${CLAUDE_PLUGIN_ROOT}/lib/…` then runs `node /lib/…`, which exits 1 —
# indistinguishable from a criterion that failed.
# Only the literal form is substituted (a `:-` default is never replaced); the
# fallback takes the highest installed version under cache//voyage/.
VOYAGE_ROOT="${CLAUDE_PLUGIN_ROOT}"
case "$VOYAGE_ROOT" in
/*) ;;
*) VOYAGE_ROOT="$(ls -d "$HOME"/.claude/plugins/cache/*/voyage/[0-9]*/ 2>/dev/null \
| awk -F/ '{ split($(NF-1), v, "."); printf "%09d%09d%09d %s\n", v[1], v[2], v[3], $(0) }' \
| sort | tail -1 | cut -d' ' -f2-)"
VOYAGE_ROOT="${VOYAGE_ROOT%/}" ;;
esac
if [ ! -f "$VOYAGE_ROOT/lib/verification/criteria-runner.mjs" ]; then
echo "[voyage] success-criteria checks could not run - plugin root unresolved."
echo " NOT a pass: hand the reviewer NO results and say so."
exit 2
fi
# Every command is screened twice before it runs (with no shell): the runner's own
# ALLOWLIST of test runners (npm test, node --test, pytest, bash tests/]