--- 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/