The three producing commands name voyage:annotating where they build the page; the description of the page and the freehand revision left them. The skill: the person writes every note, Fix/Change edit only the quoted text, a Question is answered and changes nothing, the approval an edit costs is said before the edit, the page is rebuilt. Research documents on request (decision 62). Red baseline 3 runs per scenario without and with the skill; six eval cases; 11 pins. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
73 KiB
| name | description | argument-hint | allowed-tools |
|---|---|---|---|
| trekplan | Deep implementation planning from a task brief. Requires --brief or --project. Runs parallel specialized agents, optional external research, and adversarial review. | --brief <path> | --project <dir> [--fg | --quick | --research <brief> | --decompose <plan> | --export headless <plan>] | Agent, Read, Glob, Grep, Write, Edit, Bash, AskUserQuestion |
Voyage Local v2.0
Deep, multi-phase implementation planning driven by a task brief.
Planning consumes the brief (produced by /trekbrief) and any
research briefs referenced in it, then runs specialized exploration
agents, synthesis, and adversarial review to produce an executable plan.
v2.0 is a breaking release. The interview phase has been extracted
into /trekbrief. This command no longer accepts free-text task
descriptions — it requires either --brief <path> or --project <dir>.
Pipeline position:
/trekbrief → brief.md
/trekresearch → research/*.md
/trekplan → plan.md (this command)
/trekexecute → execution
Phase 1 — Parse mode and validate input
Parse $ARGUMENTS for mode flags. Order of precedence:
-
--export headless <plan-path>— extract{format}(first token after--export) and{plan-path}(remainder).headlessis the only export format; it is a backwards-compatible alias for--decompose. Set mode = decompose and run the decomposition pipeline (Phase 1.5).The PR / issue / markdown export variants were removed — Claude Code reformats a plan into a PR body, issue comment, or stripped markdown ad-hoc on request, so a dedicated export path added maintenance without value. If
{format}is anything other thanheadless:Error: export format '{format}' is not supported. The only export is 'headless' (an alias for --decompose). For PR / issue / markdown output, ask Claude to reformat the plan directly.If the plan file does not exist:
Error: plan file not found: {path} -
--decompose <plan-path>— extract the plan path. Set mode = decompose. If the plan file does not exist:Error: plan file not found: {path} -
--project <dir>— extract the project directory path.- Resolve
{dir}(trim trailing slash). - Derive implicit flags:
--brief {dir}/brief.md- Plan destination:
{dir}/plan.md - Research briefs auto-discovered from
{dir}/research/*.md(sorted).
- If
{dir}does not exist or{dir}/brief.mdis missing:Error: project directory not initialized. Run /trekbrief to create it. Missing: {dir}/brief.md - Set project_dir = {dir}, brief_path = {dir}/brief.md.
- Validate inputs (soft mode — warnings do not block, errors do):
Each call exits 0 on success or with a structured JSON error report on stderr. Surface any warnings in the user-facing summary at Phase 3, but do not abort.# Brief schema sanity check (frontmatter + state machine, soft on body sections). # When --min-brief-version was passed, append --min-version {min_brief_version} # so the validator emits BRIEF_VERSION_BELOW_MINIMUM (warn, never block) for an # older brief that sidesteps framing enforcement. node ${CLAUDE_PLUGIN_ROOT}/lib/validators/brief-validator.mjs --soft --json [--min-version {min_brief_version}] "{dir}/brief.md" # v5.9 — composed phase-model resolution (brief > profile > default) for # the plan 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 injected at Agent-spawn sites below. # Append --profile {profile} when the operator passed --profile. node ${CLAUDE_PLUGIN_ROOT}/lib/profiles/resolver.mjs --resolve-phase-model --phase plan --brief-path "{dir}/brief.md" [--profile {profile}] --json # Research briefs (if any) — drift-warn only, none of these block the run [ -d "{dir}/research" ] && \ node ${CLAUDE_PLUGIN_ROOT}/lib/validators/research-validator.mjs --soft --dir "{dir}/research" --json # Architecture note discovery (EXTERNAL CONTRACT — drift-WARN, never drift-FAIL) node ${CLAUDE_PLUGIN_ROOT}/lib/validators/architecture-discovery.mjs --json "{dir}" - Set has_research_brief = true if
{dir}/research/*.mdmatches ≥ 1 file. - Read the architecture-discovery JSON output: set has_architecture_note = true
if
found == true. The discovery module emits warnings if the file lives at a non-canonical path (e.g.architecture-overview.md); preserve them for the user-facing summary. If set, architecture_note_path = {result.overview}. Produced by an external opt-in architect plugin (no longer publicly distributed; the filesystem slot remains available for any compatible producer). Missing file is fine — additive discovery, not required.
- Resolve
-
--brief <path>— extract the brief path. If the file does not exist:Error: brief file not found: {path}Set brief_path = {path}. Plan destination will be derived in Phase 3 from the brief's slug and date (see Phase 3).
-
--research <brief.md> [brief2.md] [brief3.md]— collect paths after--researchuntil the next--flag or a token that does not look like a file path. Maximum 3 briefs. Set has_research_brief = true. Validate each path exists — if any is missing:Error: research brief not found: {path}--researchcombines with--brief,--project,--fg, and--quick. When combined with--project, the explicit--researchbriefs are appended to the auto-discovered ones (deduplicated by path). -
--fg— accepted as a no-op alias for backwards compatibility. All phases always run in the main session as of v2.4.0. -
--quick— set mode = quick. Skip agent swarm; use lightweight Glob/Grep scan and go directly to planning + adversarial review. -
--gates— autonomy control. When present, setgates_mode = true. Pause for operator confirmation after Phase 5 (exploration), Phase 7 (synthesis), and Phase 9 (adversarial review). Defaultgates_mode = falselets phases flow continuously. The flag is consumed by the autonomy-gate state machine via the CLI shim:node ${CLAUDE_PLUGIN_ROOT}/lib/util/autonomy-gate.mjs --state X --event Y --gates {true|false}. -
--min-brief-version <ver>— (optional) version floor. Set min_brief_version = {ver} (e.g.2.2). Forwarded to the brief-validator as--min-version {ver}; when the brief declares an older version it emits aBRIEF_VERSION_BELOW_MINIMUMwarning (never blocks) — framing enforcement only fires at≥ 2.2, so an older brief sidesteps the framing-alignment defense silently. Absent → no version check. Seedocs/HANDOVER-CONTRACTS.md§Handover 1 for the pre-2.2 enforcement hole this guards. -
If neither
--briefnor--projectis present after flag parsing, output usage and stop:
Usage: /trekplan --brief <path-to-brief.md>
/trekplan --project <project-dir>
/trekplan --brief <path> --research <research-brief.md>
/trekplan --project <dir> --fg
/trekplan --project <dir> --quick
/trekplan --export headless <plan-path>
/trekplan --decompose <plan-path>
A brief is required. Produce one with /trekbrief first.
Modes:
--brief Plan from a brief file (foreground, v2.4.0+)
--project Plan from a project directory (brief.md + research/ auto-resolved)
--research Add up to 3 extra research briefs as planning context
--fg No-op alias (foreground is the only mode as of v2.4.0)
--quick Skip exploration agent swarm; plan directly
--min-brief-version <ver> Warn (never block) if the brief is older than <ver> (e.g. 2.2)
--export headless Legacy alias for --decompose (the only remaining export format)
--decompose Split an existing plan into self-contained headless sessions
Examples:
/trekplan --project .claude/projects/2026-04-18-jwt-auth
/trekplan --brief .claude/projects/2026-04-18-jwt-auth/brief.md
/trekplan --project .claude/projects/2026-04-18-jwt-auth --research extra.md
/trekplan --project .claude/projects/2026-04-18-jwt-auth --fg
/trekplan --export headless .claude/plans/trekplan-2026-04-06-rate-limiting.md
/trekplan --decompose .claude/plans/trekplan-2026-04-06-rate-limiting.md
Migrating from v1.x? See MIGRATION.md in this plugin. The old --spec flag
and free-text interview mode were removed in v2.0.
Do not continue past this step if no brief was provided.
Read the brief
Intent approval gate (veikart steg 1). Before anything is read for
planning, check that the operator approved THIS brief's intent. Runs for
--brief and --project alike; --quick does not skip it and there is no
override flag:
node ${CLAUDE_PLUGIN_ROOT}/lib/validators/intent-approval.mjs --check --json "{brief_path}"
Exit 0 → continue. Any other exit → halt — do not read further, spawn no
agent, write no plan. Print each error's message and hint, then stop:
| Code | Meaning | What the operator does |
|---|---|---|
BRIEF_INTENT_NOT_APPROVED |
No approval marker (intent_approved_hash) in the brief |
/trekbrief --approve {brief_path} — read ## Intent + ## Goal, answer "Approve" |
BRIEF_INTENT_APPROVAL_STALE |
## Intent or ## Goal changed after approval |
Same command — the approval covers the old text, not this one |
BRIEF_INTENT_APPROVAL_INVALID |
Marker malformed, or ## Intent / ## Goal missing, empty or present more than once |
Fix the brief, then the same command |
If the command does not print the JSON report (module not found, node
missing, an unresolved ${CLAUDE_PLUGIN_ROOT}), halt as well and show the
raw error: an approval check that could not run is never a pass.
trekreview briefs are exempt (the check returns exit 0 for them). The
exemption is read from the brief's own frontmatter, so a brief relabelled
type: trekreview walks past this gate. That is
the same trust class as self-stamping. What the
marker does NOT prove — it is a trace that the approval step ran against this
text, not a signature: see the header of lib/validators/intent-approval.mjs.
Approval log gate (K2). The marker passed; now check that the approval is
in the target repo's approval log, the chain is intact, and the logged
approval covers this intent. {intent_approved_hash} is the brief's
frontmatter value (the check above just confirmed it is current). Same scope
as the check above: both brief modes, --quick included, no override flag;
trekreview briefs skip it (they carry no intent hash).
node ${CLAUDE_PLUGIN_ROOT}/lib/util/approval-log.mjs verify --json --kind intent --subject "{brief_path}" --sha256 "{intent_approved_hash}"
Exit 0 (approved) → continue; if uncommitted is above 0, say in one line
that N approvals are in the log but not committed yet. Exit 1 → halt — no
agent, no plan — print status and detail:
status |
Meaning | What the operator does |
|---|---|---|
broken |
A record in .claude/voyage/approvals.jsonl was edited, removed or reordered |
Restore the log from git (git checkout -- .claude/voyage/approvals.jsonl), then re-run |
diverged |
The committed log is no longer the start of the file: committed approvals were rewritten | Same: restore from git; the rewrite shows in git diff |
not-logged |
No intent approval for this brief in the log (approved before the log existed, or the log step failed) | /trekbrief --approve {brief_path} |
mismatch |
The latest logged approval covers other intent text | /trekbrief --approve {brief_path} |
Exit 2, or no JSON at all → halt and show the raw error: a log check that could not run is never a pass.
Read the brief file and parse its frontmatter. Extract:
task— one-line task descriptionslug— slug for plan filenamesproject_dir— if present, overrides derived project path (optional)research_topics— N (used as a sanity check)research_status—pending | in_progress | complete | skippedceremony— size class (K1)small | bounded | architectural; absent =architectural(heaviest in doubt)artifact_store—git | local, where Phase 11 keeps the plan; absent =local
If research_status == pending and research_topics > 0:
- Warn the user: "Brief declares {N} research topics but research is still pending. Plan confidence will be lower. Continue anyway?"
AskUserQuestion: Continue with low confidence / Cancel — run research first.- If cancel: print the research invocations from the brief's "How to continue" section and stop.
Report the detected mode:
Mode: {foreground | quick | decompose}
Brief: {brief_path}
Project: {project_dir or "-"}
Research: {N local briefs, M extra via --research}
Class: {small | bounded | architectural} {(absent → architectural)}
When the input is type:trekreview (Handover 6)
The brief input may be a review.md produced by /trekreview
instead of a brief.md produced by /trekbrief. Both files
share the same handover slot — type is the discriminator.
If fm.type === 'trekreview':
- Skip the
research_statusgate above (review.md has noresearch_topicsand no Research Plan section). - Extract the
findingsarray from the frontmatter — this is the list of 40-char hex finding-IDs the review surfaced. - Read the body's last fenced
jsonblock to recover the full finding objects (the frontmatter only has IDs; the JSON has theseverity,file,line,rule_key,title,detail,recommended_actionpayload). - Filter findings to severity ∈
{BLOCKER, MAJOR}. MINOR and SUGGESTION are skipped for v1.0 plan-input — they are advisory only and would inflate the plan with low-priority churn. - Treat each remaining finding as a plan goal:
recommended_action→ step intentfile→ primaryFiles:targetid→ goes into the plan'ssource_findings:frontmatter list
- When writing
plan.md, populate the frontmatter fieldsource_findings: [<id1>, <id2>, ...]containing exactly the IDs of the BLOCKER + MAJOR findings consumed. The list provides the audit trail back toreview.md. - Use block-style YAML for the
source_findings:list. The frontmatter parser atlib/util/frontmatter.mjsdoes not support flow-style arrays;source_findings: [a, b]is broken — use:source_findings: - 0123456789abcdef0123456789abcdef01234567 - fedcba9876543210fedcba9876543210fedcba98
source_findings: is additive and optional — plans produced from a
type: brief input simply omit the field. No plan_version bump is
required for this addition (backwards compatible).
Phase 1.5 — Decompose (runs only when mode = decompose)
Skip this phase entirely unless mode = decompose. (--export headless is a
backwards-compatible alias that sets mode = decompose, so it lands here too.)
Read the plan file. Verify it contains an Implementation Plan section with numbered steps. If no steps are found, report and stop:
Error: plan has no implementation steps. Run /trekplan first to generate a plan.
Determine the output directory from the plan slug:
- Extract the slug from the plan filename (e.g.,
trekplan-2026-04-06-auth-refactor→auth-refactor) - Output directory:
.claude/trekplan-sessions/{slug}/
Launch the session-decomposer agent:
Plan file: {plan path}
Plugin root: ${CLAUDE_PLUGIN_ROOT}
Output directory: .claude/trekplan-sessions/{slug}/
The session-decomposer will:
- Parse the plan's steps and their file dependencies
- Build a dependency graph between steps
- Group steps into sessions of 3–5 steps each
- Identify which sessions can run in parallel (waves)
- Generate one session spec file per session
- Generate a dependency diagram (mermaid)
- Write the session orders (
orders.md): one starter line per session, wave by wave
When the session-decomposer completes, present the summary to the user:
## Decomposition Complete
**Master plan:** {plan path}
**Sessions:** {N} across {W} waves
**Output:** .claude/trekplan-sessions/{slug}/
### Sessions
| # | Title | Steps | Wave | Parallel |
|---|-------|-------|------|----------|
{session table from decomposer}
### Files generated
- Session specs: .claude/trekplan-sessions/{slug}/session-*.md
- Dependency graph: .claude/trekplan-sessions/{slug}/dependency-graph.md
- Session orders: .claude/trekplan-sessions/{slug}/orders.md
You can:
- Review individual session specs before running
- Run the sessions yourself, wave by wave: open a Claude Code session per spec
and give it the starter line from orders.md (`/trekexecute <spec>`). Run the
sessions of one wave in parallel only in separate git worktrees.
Voyage does not start these sessions.
Then stop. Do not continue to any subsequent phase.
Phase 2 — (removed in v2.0)
The interview phase has moved to /trekbrief. This command no
longer asks the user any requirements questions — the brief is the
authoritative input.
Phase 3 — Destination and context recap (foreground)
Determine the plan destination path:
- If
project_diris set (from--projector the brief'sproject_dirfrontmatter field): plan destination = {project_dir}/plan.md. - Otherwise: derive slug and date — if the brief has frontmatter
slugandcreated, use them; otherwise extract from the brief filename. Destination:.claude/plans/trekplan-{YYYY-MM-DD}-{slug}.md.
Collect all research briefs (from --research flag and auto-discovered
{project_dir}/research/*.md).
Report to the user:
Planning pipeline running in foreground.
Brief: {brief_path}
Project: {project_dir or "-"}
Plan: {plan destination}
Research briefs: {N}
Architecture note: {present | none}
Then continue to the next phase inline.
Why foreground (for now)? The planning-orchestrator was moved out of background mode in v2.4.0 because, before Claude Code 2.1.172, the harness did not expose the Agent tool to sub-agents, so an orchestrator launched with
run_in_background: truecould not spawn the documented exploration swarm (architecture-mapper,task-finder,plan-critic, etc.) and silently degraded to single-context reasoning. As of CC 2.1.172 sub-agents can spawn sub-agents (up to 5 levels deep), so that block no longer holds — a delegated redesign is under evaluation (seedocs/cc-upgrade-2.1.181-decision-matrix.md, W1/CC-26). Until then, running the phases inline in main context keeps the swarm intact.
All remaining phases run inline in the main command context.
Phase 4 — Codebase sizing
Determine codebase scale to calibrate agent turns (not agent count).
Run via Bash:
find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.mts" -o -name "*.cts" -o -name "*.js" -o -name "*.jsx" -o -name "*.mjs" -o -name "*.cjs" -o -name "*.py" -o -name "*.go" -o -name "*.rs" -o -name "*.java" -o -name "*.rb" -o -name "*.c" -o -name "*.cpp" -o -name "*.h" -o -name "*.cs" -o -name "*.swift" -o -name "*.kt" -o -name "*.sh" -o -name "*.md" \) -not -path "*/node_modules/*" -not -path "*/.git/*" -not -path "*/vendor/*" -not -path "*/dist/*" -not -path "*/build/*" | wc -l
Classify:
- Small (< 50 files)
- Medium (50–500 files)
- Large (> 500 files)
Report:
Codebase: {N} source files ({scale}). Deploying exploration agents.
Phase 4b — Brief review
First check whether /trekbrief already reviewed exactly this text:
node ${CLAUDE_PLUGIN_ROOT}/lib/validators/brief-review-stamp.mjs --check "{brief_path}"
Exit 0 (current): the body is unchanged since /trekbrief's brief-reviewer
gate passed. Skip the review, say so in one line ("Brief review: skipped —
unchanged since /trekbrief's review"), and continue to Phase 5. Exit 1
(stale: edited after that review; absent: no stamp — a gate that did not
pass, or a brief from another producer) or 2 (could not run): review it as
below (operator decision 2026-10-08).
Launch the brief-reviewer agent. It has voyage:writing-criteria and
voyage:reviewing-adversarially preloaded through its skills: line; name
both in the prompt anyway, so the rules hold even where preloading did not
happen.
Prompt: "Review this task brief for quality: {brief_path}. Check completeness,
consistency, testability, scope clarity, and research-plan validity.
Follow the writing-criteria skill (each success criterion is one command the
criteria runner accepts, and it can go red) and the reviewing-adversarially skill.
Read local files only inside the repo under exploration, plus paths this prompt gives you (the brief, the plan, and templates it names)."
Handle the verdict:
- PROCEED — continue to Phase 5.
- PROCEED_WITH_RISKS — continue, carry flagged risks as
[ASSUMPTION]in the plan. - REVISE — present findings and ask the user for clarification
(foreground is the only mode). If the user force-stops, carry outstanding
findings as
[ASSUMPTION]entries.
Phase 5 — Parallel exploration (specialized agents + research)
If mode = quick: Do NOT launch any exploration agents. Instead, run a lightweight file check:
Globfor files matching key terms from the brief's task/intent (up to 3 patterns)Grepfor function/type definitions matching key terms (up to 3 patterns)
Report findings as:
Quick scan: {N} potentially relevant files found via Glob/Grep.
No agent swarm — proceeding directly to planning.
Then skip Phase 6 (deep-dives) and proceed to Phase 7 (Synthesis) with only the quick-scan results.
All other modes: Launch exploration agents in parallel (all in a single
message). Use the specialized agents from the agents/ directory.
Never pass the Agent tool's
nameparameter — at this or any other spawn site in this command.namedoes not label a subagent, it changes its kind: the spawn is recorded astaskKind: "in_process_teammate"(spawnDepth: 0) instead of a subagent (spawnDepth: 1). A teammate's final assistant text is not a return value — it reaches the orchestrator only if the teammate itself callsSendMessage(to: "main"), and every agent inagents/declares atools:allowlist without SendMessage. The agent still runs and still produces correct output; the result is simply never delivered, so the phase presents as a hung agent that no re-prompting can revive. Measured 2026-08-17: named 0/5 returned, unnamed 3/3. Mechanism, denominators, and the on-disk recovery path:docs/agent-return-channel-defect.md.
All agents run for all codebase sizes. Scale maxTurns by size (small: halved,
medium: default, large: default) instead of dropping agents.
| Agent | Small | Medium | Large | Purpose |
|---|---|---|---|---|
architecture-mapper |
Yes | Yes | Yes | Codebase structure, patterns, anti-patterns |
dependency-tracer |
Yes | Yes | Yes | Module connections, data flow, side effects |
risk-assessor |
Yes | Yes | Yes | Risks, edge cases, failure modes |
task-finder |
Yes | Yes | Yes | Task-relevant files, functions, types, reuse candidates |
test-strategist |
Yes | Yes | Yes | Test patterns, coverage gaps, strategy |
git-historian |
Yes | Yes | Yes | Recent changes, ownership, hot files, active branches |
research-scout |
Conditional | Conditional | Conditional | External docs (only when unfamiliar tech detected AND no research brief covers it) |
convention-scanner |
No | Yes | Yes | Coding conventions, naming, style, test patterns |
research-scout's web search is not a local read; the line above bounds its local reads only. The session decomposer (--decompose) is not an exploring or reviewing agent and does not carry the line.
Always launch (all codebase sizes):
architecture-mapper — full codebase structure, tech stack, patterns, anti-patterns. Prompt: "Analyze the architecture of this codebase. The task being planned is: {task}. Brief file: {brief_path} — read it first. Read local files only inside the repo under exploration, plus paths this prompt gives you (the brief, the plan, and templates it names)."
dependency-tracer — module connections, data flow, side effects for task-relevant code. Prompt: "Trace dependencies and data flow relevant to this task: {task}. Focus on modules that will be affected by the implementation. Brief file: {brief_path} — read it first. Read local files only inside the repo under exploration, plus paths this prompt gives you (the brief, the plan, and templates it names)."
risk-assessor — risks, edge cases, failure modes, technical debt near task area. Prompt: "Assess risks and failure modes for implementing this task: {task}. Check for complexity hotspots, security boundaries, and technical debt in the relevant code. Brief file: {brief_path} — read it first. Read local files only inside the repo under exploration, plus paths this prompt gives you (the brief, the plan, and templates it names)."
task-finder — all files, functions, types, and interfaces directly related to the task. Prompt: "Find all code relevant to this task: {task}. Include existing implementations that solve similar problems, API boundaries, database models, configuration files. Report file paths and line numbers for every finding. Brief file: {brief_path} — read it first. Read local files only inside the repo under exploration, plus paths this prompt gives you (the brief, the plan, and templates it names)."
test-strategist — existing test patterns, coverage gaps, test strategy. Prompt: "Analyze the test infrastructure and design a test strategy for this task: {task}. Discover existing patterns and identify coverage gaps. Brief file: {brief_path} — read it first. Read local files only inside the repo under exploration, plus paths this prompt gives you (the brief, the plan, and templates it names)."
git-historian — recent changes, code ownership, hot files, active branches.
Prompt: "Analyze git history relevant to this task: {task}. Brief file:
{brief_path} — read its Constraints and Non-Goals for files it keeps unchanged,
and never recommend changing one. Report recent changes, ownership, hot files,
and active branches that may affect planning. Name the remote as git remote -v
shows it.
Read local files only inside the repo under exploration, plus paths this prompt gives you (the brief, the plan, and templates it names)."
Launch for medium+ codebases (50+ files):
Convention Scanner — use the convention-scanner plugin agent (model: "opus")
for medium+ codebases only.
Prompt: "Discover the coding conventions relevant to this task: {task}. Brief file: {brief_path} — read it first. Read local files only inside the repo under exploration, plus paths this prompt gives you (the brief, the plan, and templates it names). Provide concrete examples from the codebase, not generic advice."
Conditional: External research
After reading the brief, determine if the task involves technologies, APIs, or libraries that are:
- Not clearly present in the codebase
- Being upgraded to a new major version
- Being used in an unfamiliar way
Skip research-scout for any topic already answered by an attached research
brief. If the brief's research_status == complete and all Research Plan
topics have corresponding research files, skip research-scout entirely.
If yes (and not covered by attached briefs): launch research-scout in parallel with the other agents. Prompt: "Research the following technologies for this task: {task}. Specific questions: {list specific questions about the technology}. Technologies to research: {list}. Brief file: {brief_path} — read it first. Read local files only inside the repo under exploration, plus paths this prompt gives you (the brief, the plan, and templates it names)."
If no external technology is involved or all topics are covered by briefs: skip research-scout and note: "No external research needed — covered by research briefs / well-represented in codebase."
Phase 6 — Targeted deep-dives
After all Phase 5 agents complete, review their results and identify knowledge gaps — areas where exploration was too shallow to plan confidently.
Common reasons for deep-dives:
- A critical function was found but its implementation details are unclear
- A dependency chain needs tracing to understand side effects
- A test pattern was identified but the test infrastructure needs more detail
- A risk was flagged but the actual impact needs verification
For each significant gap, spawn a targeted deep-dive agent (model: "opus", subagent_type: "Explore") with a narrow, specific brief. Each deep-dive prompt carries these two lines: Brief file: {brief_path} — read it first. Read local files only inside the repo under exploration, plus paths this prompt gives you (the brief, the plan, and templates it names).
Launch up to 3 deep-dive agents in parallel. If no gaps exist, skip this phase and note: "Initial exploration was sufficient — no deep-dives needed."
Phase 7 — Synthesis
After all agents complete (initial + deep-dives + research), synthesize:
- Read all agent results carefully
- Identify overlaps and contradictions between agents
- Build a mental model of the codebase architecture
- Catalog reusable code: existing functions, utilities, patterns
- Integrate research findings with codebase analysis
- Note remaining gaps — things you cannot determine from code or research (these become assumptions in the plan, marked explicitly)
- For each finding, track whether it came from codebase analysis or external research — the plan must distinguish these sources
Do NOT write this synthesis to disk. It is internal working context only.
Phase 8 — Deep planning
Schema-drift defense (sealed inline so this contract survives even when
agents/planning-orchestrator.mdis not implicitly loaded by the session model).The plan you write MUST satisfy these regexes. The executor parses with strict regex matching; any deviation breaks parsing and forces a re-plan.
STEP_HEADING_REGEX = /^### Step (\d+):\s+(.+?)\s*$/m FORBIDDEN_HEADING_REGEX = /^(?:##|###) (?:Fase|Phase|Stage|Steg) \d+/mFORBIDDEN headings (parser rejects these — do not emit them under Implementation Plan):
## Fase 1,### Fase 1— Norwegian narrative format## Phase 1,### Phase 1— narrative phase format## Stage 1,### Stage 1— narrative stage format## Steg 1,### Steg 1— Norwegian step word### 1.or### 1)— numbered without "Step"### Step 1 —(em-dash instead of colon)- Any heading that doesn't match
STEP_HEADING_REGEXREQUIRED step shape — copy this canonical example verbatim, substituting file paths, descriptions, and patterns. Preserve the exact heading format, bullet field names, and Manifest YAML structure. Do not invent new field names. Do not skip fields. Do not nest steps under sub-headings.
### Step 1: Add JWT verification middleware - **Files:** `src/middleware/jwt.ts` - **Changes:** Create new middleware function `verifyJWT(req, res, next)` that reads `Authorization: Bearer <token>` header, verifies signature with `process.env.JWT_SECRET`, attaches decoded payload to `req.user`, and returns 401 on invalid/missing token. (new file) - **Reuses:** `jsonwebtoken.verify()` (already in package.json), pattern from `src/middleware/cors.ts` - **Interface out:** `verifyJWT(req, res, next)`; sets `req.user = {id, roles}` — read by Step 2 - **Review focus:** an expired token and a token signed with another key — pinned by `src/middleware/jwt.test.ts` - **Test first:** - File: `src/middleware/jwt.test.ts` (new) - Verifies: valid token attaches user; invalid token returns 401; missing header returns 401 - Pattern: `src/middleware/cors.test.ts` (follow this style) - **Verify:** `npm test -- jwt.test.ts` → expected: `3 passing` - **Regression:** `npm test` → expected: exit 0 - **On failure:** revert — `git checkout -- src/middleware/jwt.ts src/middleware/jwt.test.ts` - **Checkpoint:** `git add src/middleware/jwt.ts src/middleware/jwt.test.ts && git commit -m "feat(auth): add JWT verification middleware"` - **Manifest:** ```yaml manifest: expected_paths: - src/middleware/jwt.ts - src/middleware/jwt.test.ts min_file_count: 2 commit_message_pattern: "^feat\\(auth\\): add JWT verification middleware$" bash_syntax_check: [] forbidden_paths: - src/middleware/cors.ts must_contain: - path: src/middleware/jwt.ts pattern: "verifyJWT" ```plan_version 1.8 or later additions (the validator enforces them; 1.7 plans stay valid):
Test first:withFile:andVerifies:on EVERY step; exactly the optional fieldsInterface in:,Interface out:,Review focus:(never empty when present), plusRegression:below; at least one step withEnd-to-end:and a backticked command — the "run the thing" step; the header line carries`plan_version: 1.9` · `ceremony: <class>`; an architectural plan carries## Design(see "Design part" below); and every check the run executes can run — each## Verificationcriterion, the first backticked span of eachVerify:(the step's test command) and eachEnd-to-end:command pass the criteria runner's allowlist, and eachEnd-to-end:command also stands under## Verification(PLAN_VERIFICATION_NOT_RUNNABLE,PLAN_STEP_VERIFY_NOT_RUNNABLE,PLAN_END_TO_END_NOT_RUNNABLE,PLAN_END_TO_END_NOT_VERIFIED).plan_version 1.9 additions (the validator enforces them on a 1.9 plan; 1.8 and 1.7 plans are read as before, with the warning
PLAN_STEP_PATH_UNTICKEDfor a path outside backticks): the file first on every line. EachFiles:entry (the field's first line and each list item) and eachTest first:File:line opens with its backticked path(s), separated only by commas or spaces; everything from the first other text on is description and is never read. So every path stands before any annotation:`src/a.mjs`, `src/b.mjs` (new), never`src/a.mjs` (new), `src/b.mjs`(the second path would be description); two test files share one line,File: `a.test.mjs`, `b.test.mjs` (new). A line that is no list item continues the entry above it, so a list wrapped after a comma keeps its paths (`src/a.mjs`,with`src/b.mjs`on the next line names both) and a wrapped description stays description: once the entry so far does not end with a comma, a wrapped line is description even when it opens with a backticked span (operator decision 29), and aFile:line with nothing after it takes its first wrapped line. TheFile:line may open with-,*,+,1.or1), and any list item (theVerifies:line too) ends it. A 1.9 step always names a file: an entry that does not open with a backticked path, the bare word none (written without backticks), or an emptyFiles:isPLAN_STEP_PATH_NOT_FIRST; a leading span that is no path (a call, an option, a URL) isPLAN_STEP_PATH_NOT_A_PATH. So is a backticked placeholder (`none`,`TBD`,`TODO`,`TBA`,`N/A`,`NA`,`...`) and a mixed-case bare name such as`Parser`(no.or/, neither in capitals likeLICENSEnor a build file likeMakefile), by operator decision 22: write./Namefor such a file. The canonical step above already opens each line with its path.Two step rules at every plan_version (no version bump; a plan without the field is unchanged): the optional
Regression:field (K10) holds ONE backticked command the criteria runner accepts (voyage:writing-criteria) — usuallynpm test, the suite the step must not break.Verify:stays the step's own test, the one that is red first;Regression:is what the executor runs green before the checkpoint, so a step whose red test is a script can still keep the frozen suite green. Write it on a step that can break existing tests; leave it out otherwise. ARegression:that can never run isPLAN_STEP_REGRESSION_NOT_RUNNABLE. AndOn failure:(K11) opens with one ofrevert,retry,skip,escalate— the words/trekexecutereads — then the specifics; any other opening is the warningPLAN_STEP_ON_FAILURE_UNKNOWN. A step whoseFiles:are all tests is the warningPLAN_STEP_TESTS_ONLY: the test for a behaviour belongs in the step that builds it (see "Steps are vertical slices" below).Validator self-check (mandatory after writing
plan.md): runnode ${CLAUDE_PLUGIN_ROOT}/lib/validators/plan-validator.mjs --strict --json {plan_path}and re-revise the plan if it fails. The validator is the source of truth for heading shape, manifest presence, and required-field coverage. If${CLAUDE_PLUGIN_ROOT}is unset (rare in practice), fall back to the equivalent path under your validators cache or the repo'slib/validators/.
Read the brief file (from --brief or --project).
Read the plan template: @${CLAUDE_PLUGIN_ROOT}/templates/plan-template.md
Ceremony. Read ceremony: from the brief's frontmatter: small,
bounded or architectural. Absent means architectural (heaviest on doubt).
Write it into the plan's header line. Only architectural gets the design part.
Write the plan following the template structure. The plan MUST include:
Required sections
-
Context — Why this change is needed. Use the brief's Intent verbatim or tightly paraphrased. The plan's motivation must trace directly to the brief. Global Constraints follows it: the brief's Constraints and NFRs that bind more than one step, quoted verbatim (exact values, never paraphrased). Design follows that, for
ceremony: architecturalonly — see "Design part" below. -
Codebase Analysis — Tech stack, patterns, relevant files, reusable code, external tech researched. Every file path must be real (verified during exploration).
-
Research Sources — If any research briefs or research-scout was used: table of technologies, sources, findings, and confidence levels. Omit if none.
-
Implementation Plan — Ordered steps. Each step specifies:
- Exact files to modify or create (with paths)
- What changes to make and why
- Which existing code to reuse
- Dependencies on other steps
- Whether the step is based on codebase analysis or external research
- Test first: — the test file and what it verifies, on EVERY step, written in the step that builds the behaviour it tests
- Interface in: / Interface out: — what the step consumes (and which
earlier step or existing code produces it) and what it produces for later
steps. Write them whenever a step hands something to another step; a
step's
Interface in:must match an earlierInterface out:. - Review focus: — inputs the brief implies but no test exercises yet, each pinned to a test; and any Global Constraint the step could break, quoted verbatim.
- Regression: (optional, K10) — the one suite command run green before the checkpoint, on a step that can break existing tests
- On failure: — opens with the recovery action (
revert,retry,skiporescalate, K11), then the specifics - Checkpoint: —
git addof the step's Files and test files, then the git commit, after success
Steps are vertical slices: each cuts a narrow but complete path through the layers it touches and is verifiable on its own. Never "all the tests, then all the code". Preparatory refactoring comes first as its own steps; a wide change goes expand → migrate → contract.
The test for a behaviour belongs in the step that builds it. A step cannot consist of only tests for behaviour an earlier step delivered: at its start point that behaviour already exists, so its tests pass there and it can never be proven red —
/trekexecutestops on it (not-red-at-base, stop class 4). The voyage-lekerepo trial (2026-10-09) lost a run that way to a step that only addedtests/e2e.shand a guard script for step 1's behaviour. Put such tests in the step that builds the behaviour; the validator warns on a step whoseFiles:are all tests (PLAN_STEP_TESTS_ONLY).The "run the thing" step. At least one step carries
End-to-end: \` → expected:: the product run the way a user runs it (the CLI, the server and a request, the app; for iOS, the simulator). Without it the result is "not verified end to end", never green. The command passes the criteria runner's allowlist — the product's own binary does not, so the run is wrapped in a script:End-to-end: `bash tests/e2e.sh`. The same command is also listed under## Verification: Phase 7 of/trekexecuteruns that section, and a passed criterion that runs the step'sEnd-to-end:command is what verifies the run end to end.End-to-end:rides on the last step that builds behaviour, withtests/e2e.shin that step'sFiles:`: a step of its own that only adds the script is a step of only tests, and it can never be proven red. -
Execution Strategy — For plans with > 5 steps: group steps into sessions (3–5 steps each), organize sessions into waves (parallel where independent), specify scope fences per session. Omit for plans with ≤ 5 steps.
-
Alternatives Considered — At least one alternative approach with pros/cons and reason for rejection.
-
Risks and Mitigations — From the risk-assessor findings and the brief's open questions. What could go wrong and how to handle it.
-
Test Strategy — From the test-strategist findings (if available). What tests to write and which patterns to follow.
-
Verification — Reuse the brief's Success Criteria as the baseline. Each criterion must be an executable command or observable condition. The heading is exactly
## Verificationand the criteria are written in one of two forms. This is not style:lib/verification/criteria-runner.mjsis what runs this section in trekexecute Phase 7, and it reads those two forms and nothing else —## Verification (acceptance),### Verification, an untagged fence or a table all parse to zero criteria, which fells the run without verifying anything.- a bullet whose FIRST backticked span is the command:
- [ ] `npm test` -> expected: exit 0 - or a shell-tagged fence, one command per line:
npm test node --test tests/lib/x.test.mjs
Each command is written by the
voyage:writing-criteriaskill (use it by name before you write this section and the steps'Verify:,Regression:andEnd-to-end:lines): one command the criteria runner accepts, in plain words, a script undertests/(bash tests/<script>.sh) for anything more, and a command that can go red. Anything the runner refuses is reported NOT RUN, never executed and never failed, and an unrunnable plan criterion stops/trekexecutePhase 7. - a bullet whose FIRST backticked span is the command:
-
Estimated Scope — File counts and complexity rating.
Quality standards
- Every file path in the plan must exist in the codebase (or be explicitly marked as "new file to create")
- Every "reuses" reference must point to a real function/pattern found during exploration
- Steps must be ordered by dependency (not by file path or importance)
- Verification criteria must be concrete and executable
- The plan must be implementable by someone who has not seen the exploration results — it must stand on its own
- Research-based decisions must cite their source
- Every implementation decision must be traceable to a brief section (Intent, Goal, Constraint, Preference, NFR, or Success Criterion)
- Interfaces are consistent across steps: nothing is consumed before a step or existing code produces it, under the same name and type
Borrowed, with credit: the per-step interfaces, Global Constraints quoted verbatim and Review Focus from obra/superpowers@8ca22db writing-plans (MIT, © 2025 Jesse Vincent); quoting constraints into the step from GitHub Spec Kit (MIT); vertical slices and prefactoring first from mattpocock/skills@c55ee46 to-tickets (MIT, © 2026 Matt Pocock); "plan the verification too" — the run step — from Boris Cherny's public Claude Code threads; test-first as a rule from Spec Kit, made a validator check here instead of a rule the model ticks.
Design part (ceremony: architectural)
Read the design template: @${CLAUDE_PLUGIN_ROOT}/templates/design-template.md
Write ## Design between ## Global Constraints and ## Implementation Plan
with the template's subsections: Architecture, Data Flow, Interfaces, Data
Models, Error Handling, Module Map, Architecture Rules, Requirements Check
(Decisions (ADR) and Gotchas only when they apply). The test strategy is the
plan's own ## Test Strategy.
- Derived from: the brief path and the first 12 hex of its sha256
(
shasum -a 256 "{brief_path}"). - Module map: a mermaid graph; dependencies point one way, no cycle.
- Architecture rules: first see what the target repo can check, read-only:
Exit 0 or 1: the repo has dependency-cruiser or import-linter configured with a local binary, so a rule can be "checked by" it. Exit 2: nothing to check with — a rule is then "advisory —node ${CLAUDE_PLUGIN_ROOT}/lib/verification/arch-rules.mjs --repo ..claude/rules/<file>.md" (a plan step writes that file), or "checked by" a test or linter the repo already has. Voyage installs nothing; adding a tool is a plan step the operator approves. Every checked rule names the step (Proof column) that shows it pass, fail on a planted violation and pass again. For the two supported tools that step's Verify isnode ${CLAUDE_PLUGIN_ROOT}/lib/verification/arch-rules.mjs --repo . --prove --rule <rule name> --plant <violation file>=<dest>(exit 0 = proven: the planted run tripped THAT rule; the plant is removed afterwards, and is never written over an existing path or outside the repo). - Requirements Check: compare the brief's requirements with each other.
For each conflict or gap ask the operator ONE two-choice question with
AskUserQuestion, recommendation first: "keep" (as written) or "change like
this" (a concrete proposal). Record the answers in the table. If an answer
changes the brief's Intent, Goal or Success Criteria, stop: the brief is
re-run through
/trekbrief— the planner never edits it.
Write the plan
Use the plan destination computed in Phase 3:
--projectmode:{project_dir}/plan.md--briefmode:.claude/plans/trekplan-{YYYY-MM-DD}-{slug}.md
Create the parent directory if it does not exist.
In plan mode (Claude Code has told this session it is in plan mode and named its plan file): edits outside that plan file are blocked until the operator approves, so write the plan to Claude Code's plan file instead and run Phases 9 and 10 against it. Phase 10 copies it to the destination after the approval.
Phase 9 — Adversarial review (bounded critic loop)
Phase 9 is a loop of at most 3 critic rounds. Each round reviews the plan as it stands, and a command — not this session's judgment — records the verdict and decides whether the plan may go on. A BLOCKER or an open MAJOR stops the plan: revise and run the next round. The loop is done on a round that leaves neither, or when the operator accepts the open MAJORs after the last round; after round 3 with a BLOCKER still open it stops and asks the operator. It never goes on silently.
From round 2. Round 1 is unchanged: the reviewers read the whole plan. From round 2 on, first print what changed since the text the last round reviewed:
node ${CLAUDE_PLUGIN_ROOT}/lib/validators/plan-gate.mjs --diff "{plan_path}"
It prints diff-base: <sha256> (round N) and each changed hunk with the
plan's full-file line numbers. Hand its output to both reviewers, with the
round-2 sentence their prompts gain below. When it says diff-base: none,
the round is a full review: the reviewers read the whole plan as in round 1,
and rule A lowers nothing. The gate, not this
session, counts the round by rule A:
Rule A: from round 2 on, a new major finding that cites a plan line outside the diff since the last reviewed text counts as minor; a blocker, a security finding and a finding with no plan line count at full severity, and a previous finding stays counted until it is marked resolved.
The reviewers state rule B, and the gate records each lowering they make from round 1 on:
Rule B: a major that a named test, Regression: command or gate in the plan will fail on during execution is minor — never a blocker, never a security finding.
One round:
First take the hash of the plan the reviewers are about to read, and keep the printed value for this round:
node ${CLAUDE_PLUGIN_ROOT}/lib/validators/plan-gate.mjs --hash "{plan_path}"
Then launch two review agents in parallel — emit both Agent tool calls in a single assistant message turn (same pattern as Phase 5 exploration). They have zero data dependencies; serializing them wastes 30–60 seconds per run.
Both reviewers are asked for voyage:writing-criteria and
voyage:reviewing-adversarially by name. plan-critic has both preloaded;
scope-guardian has voyage:reviewing-adversarially preloaded and gets the
writing-criteria rule in one sentence in its prompt, for the brief's criteria
it traces into ## Verification.
plan-critic — adversarial review of the plan.
Prompt: "Review this implementation plan for the task: {task}.
Plan file: {plan path}. Read it and find every problem — missing steps,
wrong ordering, fragile assumptions, missing error handling, scope creep,
underspecified steps. Rate each finding as blocker, major, or minor.
Follow the writing-criteria skill (every check is one command the criteria
runner accepts, and one that can never go red is a finding) and the
reviewing-adversarially skill.
End your response with the REQUIRED machine-readable json findings block
(schema in agents/plan-critic.md) so the orchestrator can pipe it to the
dedup helper. You have no Write tool — return the block inline, do not write a file.
Read local files only inside the repo under exploration, plus paths this prompt gives you (the brief, the plan, and templates it names)."
From round 2 the prompt adds one sentence, followed by the --diff output:
"Previous findings are in the plan's ## Critic Verdict; the diff since the last reviewed text is below. Mark each previous finding by id in previous."
scope-guardian — scope alignment check.
Prompt: "Check this implementation plan against the brief.
Task: {task}. Brief file: {brief_path}. Plan file: {plan path}.
Find scope creep (plan does more than the brief requires) and scope gaps
(plan misses brief requirements). Check that referenced files and functions
exist. Verify that every Success Criterion in the brief is covered by the
plan's Verification section. Follow the writing-criteria skill for those
criteria (each is one command the criteria runner accepts, and it can go red)
and the reviewing-adversarially skill.
End your response with the REQUIRED machine-readable
json findings block (schema in agents/scope-guardian.md). You have no Write
tool — return the block inline, do not write a file.
Read local files only inside the repo under exploration, plus paths this prompt gives you (the brief, the plan, and templates it names)."
From round 2 the prompt adds one sentence, followed by the --diff output:
"Previous findings are in the plan's ## Critic Verdict; the diff since the last reviewed text is below. Mark each previous finding by id in previous."
After both complete, extract each reviewer's trailing json findings block
and pipe both into the dedup helper via stdin — the reviewers are read-only
(Read/Glob/Grep, no Write), so they cannot persist temp files; the
orchestrator does the persistence by piping their inline blocks. The dedup
output goes straight on to the plan gate, which writes this round's verdict
into the plan's ## Critic Verdict section:
node ${CLAUDE_PLUGIN_ROOT}/lib/review/plan-review-dedup.mjs --stdin <<'JSON' | node ${CLAUDE_PLUGIN_ROOT}/lib/validators/plan-gate.mjs --record "{plan_path}" --reviewed {the hash from --hash}
{ "plan_critic": <plan-critic's json block>, "scope_guardian": <scope-guardian's json block> }
JSON
plan-gate.mjs --record prints one JSON line: round, verdict
(BLOCK = a blocker is open, REVISE = majors only, PASS) and counts.
It also prints diff_base (the sha256 of the text this round was counted
against by rule A, or null), diff_note (one line: why a round from round 2
on was counted fully), lowered (how many findings count below what a
reviewer rated) and snapshot (kept: … or not kept: <reason>). The
snapshot is the operative text this round reviewed, kept under
<git dir>/voyage/plan-snapshots/ and never committed; the next round's
--diff reads it. A plan outside any git repository (plan mode) keeps no
snapshot (not kept: no git repository), so its next round counts fully.
A failed snapshot write does not fail the round.
Exit 1 means the round cap is spent (see "After round 3"). Exit 2 means the
round cannot be recorded: broken input, a reviewer missing from it (an absent
review is not a clean one), or a plan that changed after --hash — the
verdict would bless text nobody reviewed. Show the message and rerun the round
on the plan as it is; never record around it.
The helper exits non-zero on malformed stdin — a broken hand-off surfaces
loudly instead of collapsing into a silently-empty merge (the historical Phase-9
defect, where the read-only reviewers never wrote the files and the dedup ran on
nothing). The merged array attributes each finding to [plan-critic, scope-guardian]
when both reviewers raised the same issue (exact match on file:line:rule_key,
Jaccard ≥ 0.7 on text tokens, or — same file and same line — most of the
shorter text's content words in the longer one), and keeps the copy rule B counts more severe
(a refused lowering included). Revise the plan once for the merged set, not twice for the
duplicates. Source: research/05 R1 + R2.
After each round, by the recorded verdict:
- BLOCK or REVISE, rounds left: revise the plan for the merged
findings, add rows to
## Revisions(what changed and why), and run the next round. Any edit outside the review log — a step, the design, the verification — makes the recorded verdict stale, so a revised plan is always re-reviewed. - PASS (minors only, or clean): fill in the quality header and
## Plan Quality Scorefrom plan-critic's score. These and## Revisionsare the review log and do not stale the verdict. Do not edit a step for the open minors now — that stales the verdict the round just earned. Go to Phase 10, which lists the minors for the operator to pick from (operator decision 2026-10-08: a real weakness among the minors after the last round used to be carried with no way to a fix). - REVISE after the last round: the gate reports
PLAN_GATE_OPEN_MAJOR_FINAL. Stop and ask the operator with AskUserQuestion — each open major in plain words, recommendation first (operator decision 2026-10-08):- Fix now — revise the plan for the listed majors, then record one more
round with
--extra-round(only after this yes; one yes = one round). - Build as is — after this yes, record it:
node ${CLAUDE_PLUGIN_ROOT}/lib/validators/plan-gate.mjs --accept-open "{plan_path}". The acceptance is bound to this round and this plan text; the majors stay listed in the Phase 10 summary. A blocker is never accepted.
- Fix now — revise the plan for the listed majors, then record one more
round with
After round 3 with a BLOCKER open, plan-gate.mjs reports
PLAN_GATE_ROUNDS_EXHAUSTED and --record refuses a fourth round. Stop and ask
the operator with AskUserQuestion — the open blockers listed in plain words,
recommendation first:
- One more round — revise for the listed blockers, then record with
--extra-round(only after this yes; one yes = one round; the gate refuses the flag before the cap is spent). - Stop here — the plan is not ready; it stays saved with its verdict, and
/trekexecuteis not offered.
Then check the plan with the gate (K3), which every later step relies on:
node ${CLAUDE_PLUGIN_ROOT}/lib/validators/plan-gate.mjs --json "{plan_path}"
Exit 0 = the plan validates, the verdict matches it, no BLOCKER is open, and
every open MAJOR was accepted by the operator. Exit 1 = stop; its reasons
say why. Exit 2 = it could not read the plan.
Borrowed, with credit: the bounded review loop that ends only on a recorded
verdict, never on a claim in prose, from Kiro's design-loop workflow pattern
(kiro.dev, docs only); the independent staff-engineer review and "re-plan
instead of pushing on" from Boris Cherny's public Claude Code threads.
Phase 10 — Present and refine
Run the gate (K3) on the plan first; its exit code decides what may be offered:
node ${CLAUDE_PLUGIN_ROOT}/lib/validators/plan-gate.mjs --json "{plan_path}"
Present a summary to the user:
## Voyage Complete
**Task:** {task description}
**Mode:** {foreground | quick}
**Brief:** {brief_path}
**Project:** {project_dir or "-"}
**Plan:** {plan_path}
**Exploration:** {N} agents deployed ({N} specialized + {N} deep-dives + {research status})
**Scope:** {N} files to modify, {N} to create — {complexity}
### Key decisions
- {Decision 1 and rationale}
- {Decision 2 and rationale}
### Implementation steps ({N} total)
1. {Step 1 summary}
2. {Step 2 summary}
...
### Research findings
{Summary of external research + attached research briefs, or "No external research used."}
### Adversarial review
**Critic rounds:** {N} of {max} — last verdict {BLOCK | REVISE | PASS}
**Plan critic:** {Summary — blockers/majors/minors found, how addressed}
**Scope guardian:** {Summary — creep/gaps found, how addressed}
**Open majors:** {list with "accepted by the operator" for each, or "none"}
**Open minors:** {numbered from the last recorded verdict — "1. {plain words} ({file}:{line})" — or "none"}
**Plan gate (K3):** {PASS | STOP — the reasons}
You can:
- Say which open minors to fix ("fix 1 and 3")
- Ask questions or request changes to refine the plan
- Say **"execute"** to start implementing ← only when the gate exited 0
- Say **"save"** to keep the plan for later
When the gate exited 1, replace the "execute" line with
Execution is blocked: {the gate's reasons} and do not start implementing,
whatever is said next, until the gate exits 0.
What counts as approval. "Execute", "go" or "start" (or the plan-mode approval below) approves the plan. A hedged reply — "sounds good", "looks reasonable", "do what you think", "I guess" — is not approval: ask once, plainly, whether to execute. (Borrowed: addyosmani/agent-skills@9d0c60d build command, MIT, © 2025 Addy Osmani.)
If the user asks questions or requests changes:
- Update the plan file in-place
- Show what changed
- A change outside the review log makes the verdict stale: run one Phase 9
round on the changed plan (it counts toward the 3), then re-run the gate.
When the 3 rounds are spent, record that round with
--extra-round: the operator's request for the change is their yes, for this one round. - Re-present the summary
Picking open minors. The summary lists the open minors numbered. Nothing
is fixed unless the operator picks: minors they do not name are carried, as
before. When they pick some ("fix 1 and 3"), revise the plan for exactly those,
add rows to ## Revisions, and run one Phase 9 round on the revised plan —
with --extra-round when the 3 rounds are spent; the operator's pick is their
yes for that one round. The new round's verdict decides as in Phase 9 (a new
major after the last round goes to the operator again).
In plan mode
When this session is in Claude Code's plan mode, the plan sits in Claude Code's plan file (Phase 8), and approval goes through Claude Code's own plan-approval prompt:
- Run the gate on the plan file. Exit 1: do not present the plan for approval; fix it (Phase 9) or, after round 3, ask the operator as Phase 9 says.
- Exit 0: present the plan with
ExitPlanMode. Claude Code shows the operator the plan file and asks how to proceed. A hook on that approval (when one is installed) runs the same gate on the plan file's text and can send a bad plan back. - Approved: the session leaves plan mode. Copy the plan file to the plan destination from Phase 3, then run the gate on the destination — it must exit 0 again, which also proves the copy is the reviewed text. Then continue with the operator's choice (Phase 11).
- "No, keep planning": revise per the operator's words, run one Phase 9 round, and present again from step 1.
Without plan mode, all of the above happens in this chat, as described first.
The summary and the annotation HTML below are produced either way. Not
verified: that /trekplan runs end to end inside plan mode (exploration
agents, the gate's Bash calls and --record writing to the plan file all under
plan mode's restrictions). Until an operator has run it, both paths stay.
Build the operator-annotation HTML and print the link
After the plan summary, run scripts/annotate.mjs to build the page
where the operator writes their own notes on plan.md. The loop that
follows is the skill voyage:annotating: call it when the operator pastes
the notes back (a Question is answered, not applied; an approved plan that
changes needs a new approval before /trekexecute, said before the edit).
ANNOT_HTML=$(node ${CLAUDE_PLUGIN_ROOT}/scripts/annotate.mjs "{plan_path}" 2>&1)
# stdout is the absolute path to the .html on success.
If annotate.mjs exits non-zero, surface a one-line warning and continue
— the annotation HTML is a convenience, not a gate.
Then print this block verbatim (substituting {plan_path} and
$ANNOT_HTML):
────────────────────────────────────────────────────────────────────
Plan written: {plan_path}
Annotation HTML: file://{$ANNOT_HTML}
To review and annotate the plan, open it in a browser:
open file://{$ANNOT_HTML}
Click any line to add YOUR OWN note. The sidebar collects every note,
the "Copy Prompt" button gathers them into one structured prompt.
Paste that prompt back into this chat: Claude answers each Question
and revises plan.md from your Fix and Change notes. Annotations
persist in your browser if you close the tab and reopen the same file.
────────────────────────────────────────────────────────────────────
Phase 11 — Handoff
Plan approval (before every handoff below)
Whatever the operator answered in Phase 10 ("save", "execute", …), first ask whether this plan, as written now, is approved. The approval binds to the plan file's sha256, so any later edit makes it stale.
- Ask via
AskUserQuestion— one question: "Approve this plan as written?" Options: Approve (recommended only when the critic left no open BLOCKER) / Not yet. - Check the answer as given, verbatim, on stdin:
node ${CLAUDE_PLUGIN_ROOT}/lib/util/approval-log.mjs answer <<'ANSWER'
{OPERATOR_ANSWER}
ANSWER
Exit 1 is not an approval («høres bra ut», «gjør som du vil», an empty
answer): say so in one line and treat it as Not yet.
3. On exit 0, log the approval and commit it in the target repo
({artifact_store} from Phase 1; local keeps plan.md out of git and
commits only the log; {model} is this session's model name, e.g. Opus 5.5):
node ${CLAUDE_PLUGIN_ROOT}/lib/util/approval-log.mjs append --kind plan --subject "{plan_path}" --sha256-of "{plan_path}"
node ${CLAUDE_PLUGIN_ROOT}/lib/util/artifact-commit.mjs --store {artifact_store} --artifact "{plan_path}" --message "docs(voyage): approve plan {slug}" --trailer "Co-Authored-By: Claude {model} <noreply@anthropic.com>"
Print one line each: Plan approval: record {seq} and Committed: {short sha} | no — {reason}. A failed append or commit is reported, never hidden.
Not yet → "save" keeps the plan unapproved; "execute" does not start — return to Phase 10 to refine.
"save" / "later" / "done"
Confirm the plan and brief file locations and exit.
"execute" / "go" / "start"
Begin implementing the plan step by step in this session. Follow the plan exactly. Mark each step complete as you go.
"execute with team" / "team"
There is no team path. Tell the user that parallel team execution is not available
from /trekplan, then proceed exactly as for "execute" (sequential, in this
session). Parallel implementation is --decompose followed by /trekexecute's
multi-session waves.
Phase 12 — Session tracking
After the plan is presented (Phase 10) or after handoff (Phase 11), append a
session record to trekplan-stats.jsonl in the plugin data directory. Run it
exactly like this. The record goes on stdin, and the writer resolves the
directory: CLAUDE_PLUGIN_DATA, which the Bash tool env does not carry, else
the directory scripts/yardstick.mjs reads.
node ${CLAUDE_PLUGIN_ROOT}/lib/stats/stats-append.mjs trekplan <<'JSON'
{
"ts": "{ISO-8601 timestamp}",
"task": "{task description (first 100 chars)}",
"mode": "{default|fg|quick}",
"slug": "{the brief's slug}",
"brief_path": "{brief_path}",
"project_dir": "{project_dir or null}",
"codebase_size": "{small|medium|large}",
"codebase_files": {N},
"agents_deployed": {N},
"deep_dives": {N},
"research_briefs_used": {N},
"research_scout_used": {true|false},
"critic_verdict": "{BLOCK|REVISE|PASS}",
"guardian_verdict": "{ALIGNED|CREEP|GAP|MIXED}",
"outcome": "{execute|execute_team|save|refine}"
}
JSON
slug is the brief's slug, so this record pairs with the brief's
brief-approved and trekbrief records. critic_verdict is the verdict of
the last round plan-gate.mjs --record wrote into the plan. The writer prints
{"written": true, ...}. On "written": false (exit 1), print its reason in
one line and continue. A tracking failure never blocks the main workflow, but
it is reported, not skipped silently.
Profile (v4.1)
Accepts --profile <name> where <name> is economy, balanced, premium, fable,
or a custom profile under voyage-profiles/. Default: premium.
Resolution order (per lib/profiles/resolver.mjs):
--profileflag (source:flag)VOYAGE_PROFILEenv-var (source:env)premiumdefault (source:default)
The selected profile drives phase_models.plan (model used for the planning
LLM), parallel_agents_min/max (the exploration swarm size), and is
recorded in plan.md frontmatter so /trekexecute and /trekcontinue can
inherit it across the pipeline.
Examples:
/trekplan --profile economy --project .claude/projects/2026-05-09-add-auth
/trekplan --profile balanced --brief brief.md
VOYAGE_PROFILE=balanced /trekplan --project ...
Stats records emit profile, phase_models, parallel_agents, and
profile_source so operators can audit which profile drove which session.
Composition rule (v5.1)
Independent of the profile system. When brief.md carries
phase_signals (brief_version ≥ 2.1), each downstream phase resolves
effort + model as:
effort_for_phase = brief.phase_signals[<phase>]?.effort ?? 'standard'
model_for_phase = brief.phase_signals[<phase>]?.model ?? profile.phase_models[<phase>]
The brief signal wins per-phase when present; the profile fills any
gaps. Both fields are mechanically resolved by the single composed CLI
node ${CLAUDE_PLUGIN_ROOT}/lib/profiles/resolver.mjs --resolve-phase-model
invoked in Phase 1; the resolved JSON is {model, source}, plus
effort only when the brief's phase signal sets one (without it the
phase runs at effort standard: no change). It is captured as
phase_signal_result and passed to Agent tool calls explicitly. The
resolver controls the model parameter at Agent-spawn sites only — the
orchestrator's own model is fixed at invocation time (command frontmatter
omits model:, so it follows the session model) and cannot be switched
mid-turn. Sub-agents fall back to model: in their own agents/*.md
frontmatter when no spawn-site injection happens.
For /trekplan specifically: effort == 'low' activates the existing
--quick-equivalent code-path (skip Phase 5 agent swarm — plan directly
without exploration agents). effort == 'standard' (or absent) → no
change. effort == 'high' activates the high-effort behavior documented
under ### High-effort behavior (v5.1.1) below.
Sequencing gate surface
Phase 1 already calls brief-validator.mjs --soft. If the validator
returns BRIEF_V51_MISSING_SIGNALS in errors (brief_version ≥ 2.1
without phase_signals or phase_signals_partial: true), halt with a
one-line message: Brief is brief_version 2.1 but does not carry phase_signals — re-run /trekbrief to commit them (Phase 3.5). Enforcement is
validator-only; this surface just makes the friendly hint readable.
High-effort behavior (v5.1.1)
When phase_signal_result.effort == 'high' for the plan phase, the
additional adversarial Pass 2 on the post-revision plan is
unavailable, skipped. Append the section header to plan.md before
the trailing JSON block with exactly that status, so a reader can tell
a deliberately dropped pass from one that failed:
## Adversarial Pass 2 (v5.1.1 high-effort)
**Status:** unavailable, skipped — the third-party bridge agent that
ran this pass was dropped in v5.10.1 by operator directive. No
substitute is wired. Phase 9 (plan-critic + scope-guardian) is the
complete adversarial coverage for this plan.
Do NOT fail, retry, or silently omit the section: a high-effort plan that carries no Pass 2 marker is indistinguishable from one whose Pass 2 crashed.
History (why the slot exists): Decision B, 2026-05-14, substituted a
bridge pass for an "extra plan-critic-iterasjon" because plan-critic
dedup keys on (file, line, rule_key) triplets and post-revision line
numbers shift, risking a revision-loop. Dropping the bridge does not
revive that option — re-running plan-critic carries the same loop risk
it always did.
Standard and low effort: do NOT emit the section at all.
Hard rules
- Brief-driven: Every plan decision must trace back to a section of the brief (Intent, Goal, Constraint, Preference, NFR, Success Criterion). If a step has no brief basis, it is scope creep — flag it or remove it.
- No interview: Never ask the user requirements questions. If the brief is
inadequate, stop and ask the user to run
/trekbriefagain. The one exception is the design part's Requirements Check: one two-choice question ("keep" / "change like this") per conflict or gap found between the brief's own requirements. - Scope: Only explore the current working directory and its subdirectories. Never read files outside the repo (no ~/.env, no credentials, no other repos).
- Cost: Model resolution at Agent-spawn sites is a three-layer fallback:
brief
phase_signals[<phase>].model>profile.phase_models[<phase>]> agent frontmattermodel:. The composed resolver returns the first two layers asphase_signal_result.model; spawn sites inject it, and agent frontmatter is the fallback when no injection happens. - Privacy: Never log, store, or repeat file contents that look like secrets, tokens, or credentials. Never log prompt text.
- No premature execution: Do not modify any project files until the user
explicitly approves the plan, and never while
plan-gate.mjsexits non-zero on it. - Bounded critic loop: at most 3 critic rounds (Phase 9). After the third
with a BLOCKER open, stop and ask the operator; never continue silently, and
never write or edit the
## Critic Verdictblock by hand — onlyplan-gate.mjs --recordwrites it. Open minors are listed numbered in the Phase 10 summary; only the ones the operator picks are fixed, with one round. - Plan stands alone: The plan file must be understandable without access to the exploration results. Include all necessary context.
- Honesty: If exploration reveals the task is trivial (single file, obvious change), say so. Do not inflate the plan to justify the process. Suggest the user just implements it directly.
- Research transparency: Always distinguish codebase-derived decisions from research-derived decisions in the plan.