---
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
---
# 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).
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 {effort, model, source}; 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 one-line review.md noting "No diff
between {before_sha} and {after_sha}; nothing to review." Verdict: ALLOW.
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.
VOYAGE_ROOT="${CLAUDE_PLUGIN_ROOT:-}"
case "$VOYAGE_ROOT" in
/*) ;;
*) VOYAGE_ROOT="$(ls -d "$HOME"/.claude/plugins/cache/*/voyage 2>/dev/null | head -1)" ;;
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 reaches a shell: the runner's own
# ALLOWLIST of test runners (npm test, node --test, pytest, bash tests/]