/trekbrief Phase 3 carried the interview form inline. It now asks for the new skill voyage:interviewing by name (allowed-tools gains Skill) and keeps only its own contract: the size class and budget, the three fixed questions (framing first, class, storage), where each answer lands in the brief, and the answers that decide nothing. Step 3a and the form points of Step 3c move into the skill with their credits (Pocock, Osmani, Spec Kit; licences and line ranges checked at the pinned commits). Red baseline first (3 helper interviews against a fixed answer sheet, Opus 5.5): 10-11 questions in round 1 (3 of 3), 15 of 31 without a recommendation, a hollow answer written in as a decision (2 of 3), unasked scope as a requirement (2 of 3). With the skill: 4, 0 of 12, 0 of 3, 0 of 3. Fact questions and stopping did not fail without it. Also: pointers to voyage:writing-criteria (S3) where criteria are asked and written, both review skill names (S3 + S4) in the brief-reviewer prompt; the intent pins that read moved text now read the skill, same assertions; the Phase 7 line_ref moves with the shorter Phase 3; 7 eval cases (3/2/2). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1222 lines
56 KiB
Markdown
1222 lines
56 KiB
Markdown
---
|
||
name: trekbrief
|
||
description: Interactive interview that produces a task brief with explicit research plan. Feeds /trekresearch and /trekplan. Optionally orchestrates the full pipeline end-to-end.
|
||
argument-hint: "[--quick] <task description> | --approve <project-dir | brief-file>"
|
||
allowed-tools: Agent, Read, Glob, Grep, Write, Edit, Bash, AskUserQuestion, Skill
|
||
---
|
||
|
||
# Ultrabrief Local v2.1
|
||
|
||
Interactive requirements-gathering command. Produces a **task brief** — a
|
||
structured markdown file that declares intent, goal, constraints, and an
|
||
**explicit research plan** with copy-paste-ready `/trekresearch` commands.
|
||
|
||
Pipeline position:
|
||
|
||
```
|
||
/trekbrief → brief.md (this command)
|
||
/trekresearch --project <dir> → research/*.md
|
||
/trekplan --project <dir> → plan.md
|
||
/trekexecute --project <dir> → execution
|
||
```
|
||
|
||
The brief is the contract between the user's intent and `/trekplan`.
|
||
Every decision the plan makes must trace back to content in the brief.
|
||
|
||
**This command is always interactive.** There is no background mode — the
|
||
interview requires user input. After the brief is written, the command
|
||
optionally orchestrates the rest of the pipeline (research + plan) in
|
||
foreground if the user opts in.
|
||
|
||
## Phase 1 — Parse mode and validate input
|
||
|
||
Parse `$ARGUMENTS`:
|
||
|
||
1. If arguments start with `--quick`: set **mode = quick**. The interview
|
||
proposes `small` as the starting size class (Phase 3 still says the class
|
||
aloud, the operator can override it, and it moves up if the task proves
|
||
bigger). Strip the flag; remainder is the task description.
|
||
|
||
2. Otherwise: **mode = default**. Interview probes each section until the
|
||
completeness gate (Phase 3) and brief-review gate (Phase 4) both pass.
|
||
|
||
3. `--gates` flag (autonomy control, may combine with any mode): when
|
||
present, set `gates_mode = true`. This re-enables approval pauses at
|
||
every phase boundary in the downstream pipeline (research, plan,
|
||
execute) and at every wave in the executor. Default `gates_mode = false`
|
||
means auto mode runs continuously until the main-merge gate (which is
|
||
the one boundary that ALWAYS pauses, regardless of `gates_mode`). Strip
|
||
the flag from `$ARGUMENTS` before further parsing. 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}`.
|
||
|
||
4. `--approve <project-dir | brief-file>`: set **mode = approve**. No
|
||
interview, no drafting. Set `APPROVE_ARG` to the argument and resolve it:
|
||
|
||
```bash
|
||
node ${CLAUDE_PLUGIN_ROOT}/lib/validators/intent-approval.mjs --resolve "{APPROVE_ARG}"
|
||
```
|
||
|
||
A directory resolves to `<dir>/brief.md` (a `/trekplan --project` brief). A
|
||
file resolves to itself, whatever its name (a `/trekplan --brief` brief).
|
||
On exit 1, print its `error` and stop. Otherwise set `BRIEF_PATH`,
|
||
`PROJECT_DIR` and `PLAN_COMMAND` from `brief_path`, `project_dir` and
|
||
`plan_command`, jump straight to **Phase 4h**, run it and print its
|
||
report; if the stamp printed `stamped: true`, run **Phase 7** with the
|
||
approve-mode values it names, then stop. This is the remedy `/trekplan` names when it halts on an
|
||
unapproved or stale intent, on both of its brief modes.
|
||
|
||
In every other mode, `BRIEF_PATH = {PROJECT_DIR}/brief.md` and
|
||
`PLAN_COMMAND = /trekplan --project {PROJECT_DIR}`.
|
||
|
||
If no task description is provided (and mode is not approve), output usage and stop:
|
||
|
||
```
|
||
Usage: /trekbrief <task description>
|
||
/trekbrief --quick <task description>
|
||
/trekbrief --approve <project-dir | brief-file>
|
||
|
||
Modes:
|
||
default Dynamic interview until quality gates pass — brief with research plan
|
||
--quick Compact start; still escalates on weak sections — brief with research plan
|
||
--approve Approve an existing brief's ## Intent + ## Goal so /trekplan will plan it
|
||
|
||
Examples:
|
||
/trekbrief Add user authentication with JWT tokens
|
||
/trekbrief --quick Add rate limiting to the API
|
||
/trekbrief Migrate from Express to Fastify
|
||
```
|
||
|
||
Report:
|
||
```
|
||
Mode: {default | quick | approve}
|
||
Task: {task description}
|
||
```
|
||
|
||
## Phase 2 — Generate slug and create project directory
|
||
|
||
Generate a slug from the task description: first 3-4 meaningful words,
|
||
lowercase, hyphens. Example: "Migrate from Express to Fastify" → `fastify-migration`.
|
||
|
||
Set today's date as `YYYY-MM-DD` (UTC).
|
||
|
||
Create the project directory:
|
||
|
||
```bash
|
||
PROJECT_DIR=".claude/projects/{YYYY-MM-DD}-{slug}"
|
||
mkdir -p "$PROJECT_DIR/research"
|
||
```
|
||
|
||
Report:
|
||
```
|
||
Project directory: .claude/projects/{YYYY-MM-DD}-{slug}/
|
||
```
|
||
|
||
If the directory already exists and is non-empty, warn and ask:
|
||
> "Directory {path} exists. Overwrite, reuse (keep existing files), or pick new slug?"
|
||
|
||
Use `AskUserQuestion` with three options. If "pick new slug", ask for a
|
||
new slug and restart Phase 2.
|
||
|
||
## Phase 2.5 — Framing declaration (v5.5)
|
||
|
||
Before gathering ANY brief content, the operator MUST declare how this brief
|
||
relates to prior operator intent. This is the **first layer of the
|
||
framing-alignment defense** (CLAUDE.md cross-cutting invariant): the premise is
|
||
declared explicitly *before* the interview can drift, and long before `/trekplan`
|
||
can polish a wrong premise with reasoning power.
|
||
|
||
**This runs BEFORE any brief prose is drafted.** The committed value is written to
|
||
brief frontmatter as `framing: <value>` in Step 4a and is REQUIRED for
|
||
`brief_version: "2.2"` (the validator emits `BRIEF_MISSING_FRAMING` otherwise).
|
||
|
||
It is the **first question of the Phase 3 round** — asked in the same
|
||
`AskUserQuestion` call as the round's other questions, not as a call of its
|
||
own — with four canonical options:
|
||
|
||
| Option | Maps to `framing:` | Meaning |
|
||
|--------|--------------------|---------|
|
||
| **Preserve** | `preserve` | Same intent as before; this brief continues a prior direction unchanged. |
|
||
| **Refine** | `refine` | Same core intent, sharpened or narrowed scope. |
|
||
| **Replace** | `replace` | Supersedes a prior brief's approach; intent re-stated from scratch. |
|
||
| **New direction** | `new-direction` | Net-new intent; no prior brief to anchor against. |
|
||
|
||
Commit the answer to `state.framing` immediately.
|
||
|
||
**No safe default.** `framing` cannot be guessed — the danger the invariant guards
|
||
against is exactly "delta from last is a dangerous default anchor after a rejected
|
||
iteration." Therefore **this question is asked even in `--quick` mode** and is the
|
||
one dialog that has no skip path. If the operator force-stops here, re-surface the
|
||
four options once more; the brief cannot be written at `brief_version: "2.2"`
|
||
without a committed framing value. A hollow answer («høres bra ut», «gjør som
|
||
du vil», an empty reply) is not a framing value either: ask it again.
|
||
|
||
Report:
|
||
```
|
||
Framing: {preserve | refine | replace | new-direction}
|
||
```
|
||
|
||
## Phase 3 — Completeness loop
|
||
|
||
Phase 3 gathers the brief's content in **one question round**, sized by the
|
||
task's **size class** (ceremony by size). Use `AskUserQuestion` for every question.
|
||
|
||
**The interview form is the skill `voyage:interviewing`.** Call the Skill tool
|
||
with `voyage:interviewing` before the first question and run the round in its
|
||
form: facts looked up first, never asked; one round with the recommended answer
|
||
first; what you can predict and what is over the budget written down; the stop
|
||
rule. This phase holds only what is `/trekbrief`'s own: the size class and its
|
||
budget (Step 3b), the fixed questions and where each answer lands in the brief
|
||
(Step 3c).
|
||
|
||
### Step 3b — Ceremony by size (K1)
|
||
|
||
Put the task in one of three size classes. In doubt, take the heaviest that fits.
|
||
|
||
| Class | Typical task | What the pipeline does |
|
||
|---|---|---|
|
||
| `small` | one change in one place, no new dependency, no interface change, easy to undo | one round, no follow-up; brief-reviewer runs once and its gaps become written assumptions, not questions, written after the review stamp (Step 4e, passed gate, step 4); Phase 3.5 is skipped |
|
||
| `bounded` | several files or one module behind a clear boundary, no architecture decision | the round + at most one follow-up; at most one brief-review follow-up; Phase 3.5 asks one question for all four phases |
|
||
| `architectural` | a new module, interface, dependency, data model or migration; anything hard to undo | follow-up rounds while answers open new branches; the full brief-review loop; Phase 3.5 asks per phase |
|
||
|
||
- **Said aloud.** State the class and why in one line *before* the round
|
||
("This reads as `small`: one setting on one screen, no new dependency."),
|
||
and put it to the operator as one of the round's questions: your class as
|
||
the recommendation, the other two as the override.
|
||
- **Only ever moves up.** When an answer, the brief-reviewer or the repo shows
|
||
more than the class assumed (a migration, a new dependency, an interface
|
||
change), raise the class, say so in one line, and use the new budget from
|
||
then on. You never lower it yourself; only the operator's explicit override
|
||
lowers it.
|
||
- **Written to the brief** as `ceremony: <class>` in Step 4a. An absent field
|
||
reads as `architectural`, so a brief from another producer gets the
|
||
heaviest treatment downstream.
|
||
|
||
### Step 3c — The question round
|
||
|
||
The round runs in the form of `voyage:interviewing`. What `/trekbrief` adds:
|
||
|
||
1. **Candidates.** The coverage list is the question bank below (Intent, Goal,
|
||
Success Criteria, Research Plan, Non-Goals, Constraints, Preferences, NFRs,
|
||
Prior Attempts), plus three fixed questions every round carries: framing
|
||
(Phase 2.5, first), the size class (Step 3b) and where the brief is stored
|
||
(`artifact_store`, below).
|
||
2. **The budget is the class budget** (table below). A `bounded` or
|
||
`architectural` round is consecutive `AskUserQuestion` calls with no
|
||
drafting in between.
|
||
3. **Where the written-down answers land.** Predictions ("assumed, not
|
||
asked") and the Deferred list, one line of why each, go into
|
||
`## Open Questions / Assumptions`, where the operator sees them before
|
||
approving; the Deferred list is also printed with the round.
|
||
4. **Follow-ups by class.** A follow-up round is only for an answer that opens
|
||
a branch you cannot predict (`small`: never; `bounded`: at most one).
|
||
5. **Answers that decide nothing.** «høres bra ut», «gjør som du vil», "sounds
|
||
good", "whatever you think" and an empty reply are not a decision. Record
|
||
the recommended answer as an assumption ("not confirmed — the answer was
|
||
«…»"), never as the operator's choice.
|
||
|
||
**Budget per class** (interview questions, the three fixed ones included; the
|
||
Phase 4h approval is not counted):
|
||
|
||
| Class | Questions | Rounds |
|
||
|---|---|---|
|
||
| `small` | 4 | one `AskUserQuestion` call, no follow-up |
|
||
| `bounded` | 8 | the round + at most one follow-up |
|
||
| `architectural` | 12 per round | the round + follow-ups while answers open new branches |
|
||
|
||
**Where the brief is stored** (`artifact_store`; operator decision
|
||
2026-10-06: the approval log is always committed, the brief only by choice).
|
||
One question every round. Recommend `git` for a private repo and `local` when
|
||
the repo is public or you cannot tell:
|
||
|
||
| Option | Maps to | Consequence |
|
||
|---|---|---|
|
||
| **In git** | `artifact_store: git` | `brief.md` (and later `plan.md`) is committed together with its approval. |
|
||
| **Only on this machine** | `artifact_store: local` | The project folder is kept out of git through the repo's `.git/info/exclude`, a file that is never committed. |
|
||
|
||
The approval log is committed either way; it holds hashes, times and file
|
||
names, never the text.
|
||
|
||
**Borrowed, with credit.** The size classes, "heaviest in doubt" and the
|
||
one-way ratchet: obra/superpowers@8ca22db (MIT, © 2025 Jesse Vincent),
|
||
skills/brainstorming. The class ladder: Kiro's docs (kiro.dev — Plan mode,
|
||
Quick Spec, full spec; read 2026-10-06; an idea only, Kiro is a closed
|
||
product). The answers that decide nothing (Step 3c, point 5):
|
||
addyosmani/agent-skills@9d0c60d (MIT, © 2025 Addy Osmani), skills/interview-me.
|
||
The interview form's own credits are in `voyage:interviewing`.
|
||
|
||
### Internal state
|
||
|
||
Track this structure in memory as the loop runs:
|
||
|
||
```
|
||
state = {
|
||
intent: { content: "", probes: 0 }, # required
|
||
goal: { content: "", probes: 0 }, # required
|
||
success_criteria: { content: [], probes: 0 }, # required
|
||
research_plan: { topics: [], probes: 0 }, # required
|
||
non_goals: { content: [], probes: 0 }, # optional
|
||
constraints: { content: [], probes: 0 }, # optional
|
||
preferences: { content: [], probes: 0 }, # optional
|
||
nfrs: { content: [], probes: 0 }, # optional
|
||
prior_attempts: { content: "", probes: 0 }, # optional
|
||
ceremony: "small | bounded | architectural", # Step 3b; only moves up
|
||
artifact_store: "git | local", # Step 3c
|
||
assumptions: [], # predicted, hollow or force-stopped answers
|
||
deferred: [], # over budget, with why
|
||
question_history: [] # list of questions asked
|
||
}
|
||
```
|
||
|
||
`content` is raw user answers merged; `probes` is how many times this
|
||
section has been asked; `question_history` prevents re-asking the same
|
||
variant twice.
|
||
|
||
### Required sections (initial-signal gate)
|
||
|
||
Four sections MUST have substantive content before exiting Phase 3:
|
||
|
||
1. **Intent** — full sentence or paragraph (not a single word or phrase)
|
||
2. **Goal** — full sentence or paragraph
|
||
3. **Success Criteria** — at least one concrete, testable item
|
||
4. **Research Plan** — either ≥ 1 topic probed, OR the user has explicitly
|
||
confirmed "no external research needed", OR (when you can predict it) it
|
||
is written down as the assumption "no external research needed", which the
|
||
operator sees in the brief before approving
|
||
|
||
"Substantive" means: non-empty, not a trivial one-word reply, not
|
||
"I don't know" without a recorded assumption. The strict falsifiability
|
||
check happens in Phase 4 (brief-review gate); Phase 3 is just the
|
||
initial-signal bar.
|
||
|
||
Optional sections (Non-Goals, Constraints, Preferences, NFRs, Prior
|
||
Attempts) do not gate exit. If they remain empty after the required
|
||
sections pass, they will be recorded as "Not discussed — no constraints
|
||
assumed" in Phase 4's draft.
|
||
|
||
### Question bank (per section)
|
||
|
||
The coverage list Step 3c draws its candidates from. Each variant is a
|
||
starting point for a round question, not a script; write it as a full
|
||
sentence with your recommended answer first.
|
||
|
||
**Intent** (required):
|
||
- _Anchor_ (probes=0, content empty): "Why are we doing this? What is the
|
||
motivation, the user need, or the strategic context behind the task?"
|
||
- _Follow-up_ (probes≥1, content present but shallow): "What happens if
|
||
we do nothing? Who is affected?"
|
||
- _Sharpen_ (user mentioned a symptom): "You mentioned {X}. Is {X} the
|
||
symptom or the underlying cause?"
|
||
|
||
**Goal** (required):
|
||
- _Anchor_: "Describe the end state in 1–3 sentences — specific enough to
|
||
disagree with."
|
||
- _Follow-up_: "How would you recognize this is done when looking at
|
||
the UI / API / codebase?"
|
||
|
||
**Success Criteria** (required): how a criterion is written so the review can
|
||
run it is the skill `voyage:writing-criteria`; call it before proposing one.
|
||
- _Anchor_: "How do we verify it is actually done? List 2–4 concrete,
|
||
testable conditions — commands to run, observations, or metrics."
|
||
A command the review can run is one test-runner call (`npm test`,
|
||
`node --test <file>`, `bash tests/<script>.sh`); propose a script under
|
||
`tests/` for anything else (the product's own binary, a pipe, `jq`). When
|
||
the brief requires bash 3.2, propose `/bin/bash tests/<script>.sh`: plain
|
||
`bash` is whichever bash comes first on PATH, which may be newer.
|
||
- _Sharpen_ (criterion is vague): "'{quoted criterion}' is subjective.
|
||
Which command, observation, or metric would prove this is met?"
|
||
- _Quantify_ (performance/quality claim): "You mentioned it should be
|
||
{fast/reliable/secure}. What number or threshold counts as success?"
|
||
|
||
**Research Plan** (required, strictest):
|
||
- _Anchor_ (no topics yet): "Are there technologies, libraries, or
|
||
decisions in this task you do not have solid current knowledge of?
|
||
Examples might be library choice, a protocol, or a security pattern."
|
||
- _Per-topic sharpen_ (topic exists but incomplete): "For topic
|
||
'{title}': which parts of the plan depend on the answer? What
|
||
confidence level do you need — high, medium, or low?"
|
||
- _Scope question_: "Is '{topic}' answerable from the existing codebase,
|
||
from external docs, or both?"
|
||
- _Confirm none_ (user refuses all topics): "Confirming: no external
|
||
research needed — you already know everything the plan will depend on?"
|
||
|
||
**Non-Goals** (optional):
|
||
- _Anchor_: "What is explicitly NOT in scope? This prevents scope-guardian
|
||
from flagging gaps for things we deliberately don't do."
|
||
|
||
**Constraints** (optional):
|
||
- _Anchor_: "Technical, time, or resource constraints the plan must
|
||
respect? Dependencies, compatibility, deadlines, or budget."
|
||
- _Sharpen_: "You mentioned {deadline / budget / compatibility}. Is it
|
||
hard or guidance?"
|
||
|
||
**Preferences** (optional):
|
||
- _Anchor_: "Preferences for libraries, patterns, or architectural style?"
|
||
|
||
**NFRs** (optional):
|
||
- _Anchor_: "Performance, security, accessibility, or scalability targets?
|
||
Quantified wherever possible."
|
||
|
||
**Prior Attempts** (optional):
|
||
- _Anchor_: "Has this been attempted before? What worked or failed?"
|
||
|
||
### Selection rule
|
||
|
||
Within the round (Step 3c), and in a follow-up round:
|
||
|
||
1. A required section below the initial-signal gate outranks every optional
|
||
one, in this order: Intent → Goal → Success Criteria → Research Plan — unless
|
||
you can predict it from the task and the repo, in which case it becomes a
|
||
written assumption the operator sees before approving.
|
||
2. An optional section is a candidate only when the task hints that it
|
||
matters to scope (constraints, NFRs); it is asked at most once.
|
||
3. Per section, pick the variant: _Anchor_ when nothing is known, _Follow-up_
|
||
or _Sharpen_ when an answer is shallow, per-topic sharpen for Research Plan
|
||
topics.
|
||
4. Never repeat a question in `question_history`. Append each asked question,
|
||
increment its section's `probes`, and merge the answer into `content` —
|
||
never overwrite.
|
||
|
||
### Research topic identification
|
||
|
||
As the user answers Intent, Goal, or Success Criteria, listen for:
|
||
|
||
- **Unfamiliar technologies** — libraries, frameworks, protocols not
|
||
clearly present in the codebase
|
||
- **Version upgrades** — migrating to a new major version
|
||
- **Security-sensitive decisions** — auth, crypto, data handling
|
||
- **Architectural choices** — pattern X vs Y, library A vs B
|
||
- **Unknown integrations** — third-party APIs, external services
|
||
- **Compliance / legal** — GDPR, accessibility, industry regulations
|
||
|
||
When you hear one, add a *candidate* topic to `research_plan.topics` with
|
||
only a title and why-it-matters. Probe it on the next Research Plan
|
||
iteration using the per-topic sharpen question to fill in:
|
||
- Research question (must end in `?`)
|
||
- Required for plan steps
|
||
- Scope (local / external / both)
|
||
- Confidence needed (high / medium / low)
|
||
- Estimated cost (quick / standard / deep)
|
||
|
||
If the user says "I know this" to a candidate topic, remove it from the
|
||
list. Trust the user. If no topics emerge after probing, the user confirms
|
||
"no external research needed" → `research_plan` gate passes with 0 topics.
|
||
|
||
### Quick mode adjustments
|
||
|
||
If **mode = quick**:
|
||
- Propose `small` as the class in Step 3b. It is still said aloud, the
|
||
operator can still override it, and it still moves up when the task proves
|
||
bigger; `--quick` then follows the new class's budget.
|
||
- Prefer _Anchor_ variants over _Sharpen_.
|
||
|
||
### Force-stop path
|
||
|
||
If the user says "skip", "stop asking", "just proceed", "enough", or
|
||
similar, break the loop immediately:
|
||
- Mark any required sections still below the initial-signal gate as
|
||
`{ incomplete_forced_stop: true }` in state.
|
||
- Proceed to Phase 4 with a note that the brief will carry a reduced
|
||
confidence flag.
|
||
|
||
### Exit condition
|
||
|
||
Exit Phase 3 when:
|
||
- All four required sections meet the initial-signal gate (asked, or written
|
||
down as an assumption the operator will see), OR
|
||
- The class budget is used up — what is left goes on the Deferred list, OR
|
||
- The user has force-stopped.
|
||
|
||
Report:
|
||
```
|
||
Class: {small | bounded | architectural}{ (raised from X: <why>) }
|
||
Phase 3 complete: {N} of {budget} questions asked in {R} round(s).
|
||
Assumed, not asked: {K} Deferred: {D}
|
||
Stored: {in git | only on this machine}
|
||
Proceeding to draft and review.
|
||
```
|
||
|
||
## Phase 3.5 — Per-phase effort dialog
|
||
|
||
Phase 3.5 is the v5.1 entry-point for **adaptive-depth execution**. After
|
||
Phase 3 has gathered intent / goal / success criteria / research plan, the
|
||
operator commits an effort level and (optional) model per downstream phase
|
||
(`research`, `plan`, `execute`, `review`). The committed signals are written
|
||
to brief frontmatter as a `phase_signals:` list that the four downstream
|
||
commands read via their `## Composition rule (v5.1)` section.
|
||
|
||
### State requirements
|
||
|
||
Before entering Phase 3.5 the following must be populated:
|
||
|
||
- `state.intent` — the Phase 3 Intent answer (1+ paragraph)
|
||
- `state.goal` — the Phase 3 Goal answer
|
||
- `state.success_criteria` — at least one falsifiable SC
|
||
- `state.research_plan.topics` — list (may be empty)
|
||
|
||
If any are absent: skip Phase 3.5 entirely and write `phase_signals_partial:
|
||
true` to the draft frontmatter. Do not block.
|
||
|
||
### By size class (and --quick)
|
||
|
||
- `small` (and `--quick` while the class stays `small`): skip Phase 3.5
|
||
entirely and auto-write `phase_signals_partial: true` to draft frontmatter.
|
||
The brief satisfies the v5.1 sequencing gate without the dialog, and the
|
||
downstream commands fall back to the profile.
|
||
- `bounded`: ask ONE question in the Step 3c round — the tier for all four
|
||
phases, with 4 options from the mapping below — and write the same tier
|
||
for `research`, `plan`, `execute` and `review`.
|
||
- `architectural`: the per-phase dialog below, as one `AskUserQuestion` call
|
||
carrying four questions (one per phase).
|
||
|
||
### Default-derivation heuristic (LLM judgment, not algorithmic)
|
||
|
||
Before each phase question, propose a default tier marked `(default)`. Use
|
||
these signals — they are weak heuristics, not rules:
|
||
|
||
- `research_topics_count` → high (`high`), low (`low`), absent (`low`)
|
||
- `sc_count` (count of falsifiable SCs) → high (≥6 ⇒ `high`), low (≤2 ⇒ `low`)
|
||
- Goal complexity: keywords like "rewrite", "migration", "refactor across",
|
||
"new platform" ⇒ `high`; "typo", "small bugfix", "docs touch-up" ⇒ `low`
|
||
- Otherwise: `standard`
|
||
|
||
Mix these into one proposed default per phase. Document the proposed tier
|
||
in the question body so the operator sees why it was picked.
|
||
|
||
### The dialog — one call, one tier-coupled question per phase
|
||
|
||
For `[research, plan, execute, review]`, ask one question each, all four in
|
||
one `AskUserQuestion` call, each question with 4 options:
|
||
|
||
| Option | Maps to phase_signals entry |
|
||
|--------|----------------------------|
|
||
| **Low effort** | `{phase: <name>, effort: low, model: sonnet}` |
|
||
| **Standard (default)** | `{phase: <name>, effort: standard}` *(model omitted — composition falls through to profile)* |
|
||
| **High effort** | `{phase: <name>, effort: high, model: opus}` |
|
||
| **Fable (max quality)** | `{phase: <name>, effort: high, model: fable}` |
|
||
|
||
The proposed tier per phase (from the default-derivation heuristic) MUST be
|
||
labelled `(default)` and listed first so the operator can one-click
|
||
accept. Commit the chosen tiers to an in-memory `effort_state` dict as the
|
||
answers come back — no bulk summary-before-commit. A hollow or empty answer
|
||
for a phase leaves that phase uncommitted (the force-stop path below).
|
||
|
||
The mapping table is canonical:
|
||
- `low → {effort: low, model: sonnet}` (force sonnet for the low-cost path)
|
||
- `standard → {effort: standard}` (model omitted; composition rule resolves via profile)
|
||
- `high → {effort: high, model: opus}` (force opus for the high-confidence path)
|
||
- `fable → {effort: high, model: fable}` (force Fable 5.1 for the max-quality path)
|
||
|
||
The fable tier reuses `effort: high` semantics — full swarm, contrarian
|
||
always-on; `EFFORT_LEVELS` is unchanged (Voyage effort is orchestration
|
||
shape, not model reasoning effort). Model reasoning effort is inherited from
|
||
the session: Fable 5.1's default effort is `high`, NOT xhigh. To run xhigh, the
|
||
operator chooses it explicitly via `/effort xhigh`, `--effort xhigh`, or
|
||
`CLAUDE_CODE_EFFORT_LEVEL` — Claude Code saves effort per model, so a level
|
||
saved for one model does not carry to another.
|
||
|
||
### Force-stop handling
|
||
|
||
If in answer to the dialog the operator says "stop",
|
||
"skip", "enough", "just write it", or similar, do NOT exit silently — apply
|
||
the Phase 4f force-stop pattern verbatim:
|
||
|
||
```
|
||
You stopped before committing per-phase signals. Remaining phases:
|
||
- {list of phases not yet answered}
|
||
|
||
The brief will still be valid (v5.1 supports `phase_signals_partial: true`
|
||
as a force-stop record). Downstream commands will fall back to the profile
|
||
resolver for the un-committed phases.
|
||
|
||
Continue anyway?
|
||
```
|
||
|
||
Then `AskUserQuestion`:
|
||
|
||
| Option | Action |
|
||
|--------|--------|
|
||
| **Answer one more phase** | Return to the next un-answered phase question. |
|
||
| **Stop now (record partial)** | Drop any in-progress `effort_state` and set `phase_signals_partial: true` in draft frontmatter. Mutually exclusive with `phase_signals`. Break Phase 3.5. |
|
||
|
||
This pattern matches Step 4f (line 436-458) so the force-stop UX is
|
||
identical across both surfaces.
|
||
|
||
### Hand-off to Phase 4a
|
||
|
||
If `effort_state` is fully populated (4 commits, no force-stop): write a
|
||
`phase_signals:` block to draft frontmatter — one entry per phase,
|
||
preserving the canonical-mapping form above. Omit `model:` for standard
|
||
tier (composition falls through to profile).
|
||
|
||
If `phase_signals_partial: true` was set: write that single line to draft
|
||
frontmatter and skip the `phase_signals:` block (mutually exclusive per
|
||
validator).
|
||
|
||
Phase 4a (Step 4a — Draft in memory) reads from `effort_state` /
|
||
`phase_signals_partial` and incorporates the appropriate frontmatter block
|
||
into the draft brief.
|
||
|
||
### Sequencing gate (downstream)
|
||
|
||
`brief_version: 2.1` activates the validator's sequencing gate. If the
|
||
final brief reaches `/trekplan`, `/trekresearch`, `/trekexecute`, or
|
||
`/trekreview` WITHOUT `phase_signals` and WITHOUT `phase_signals_partial:
|
||
true`, the validator emits `BRIEF_V51_MISSING_SIGNALS` and the command
|
||
halts with a friendly hint pointing back to `/trekbrief`.
|
||
|
||
## Phase 4 — Draft, review, and revise
|
||
|
||
Phase 4 runs a **draft → brief-reviewer → revise** loop. The draft is
|
||
not written to disk until the brief-review quality gate passes (or the
|
||
iteration cap is hit). This ensures the brief that reaches `/trekplan`
|
||
has already survived a critical review.
|
||
|
||
Read the brief template first:
|
||
`@${CLAUDE_PLUGIN_ROOT}/templates/trekbrief-template.md`
|
||
|
||
### Loop bound
|
||
|
||
**The cap follows the size class:** `small` 1 review iteration (no
|
||
follow-up question: its gaps become assumptions after the stamp on a passed gate
|
||
(Step 4e, step 4), into `## Brief Quality` on an exhausted loop), `bounded` 2 (one follow-up),
|
||
`architectural` 3 (two rounds of targeted follow-ups). A follow-up question
|
||
counts against the class budget; when the reviewer shows the task is bigger
|
||
than its class, raise the class first (Step 3b) and use the new cap.
|
||
|
||
### Iteration step-by-step
|
||
|
||
**Step 4a — Draft in memory**
|
||
|
||
Build the brief text from Phase 3 state by filling the template:
|
||
|
||
- **Frontmatter:** populate `task`, `slug`, `project_dir`, `research_topics`
|
||
(count of topics), `research_status: pending`, `auto_research: false`
|
||
(will update in Phase 5 if user opts in), `interview_turns` (total
|
||
questions asked across Phase 3 + Phase 4), `source: interview`. Set
|
||
`brief_version: "2.2"` and `framing: <state.framing>` (committed in Phase
|
||
2.5 — never omit; the validator blocks a 2.2 brief without it). Set
|
||
`ceremony: <state.ceremony>` and `artifact_store: <state.artifact_store>`
|
||
(both optional in the schema, so no `brief_version` bump; always written
|
||
by this command).
|
||
- **TL;DR (v5.5, required at 2.2):** write a `## TL;DR` section (≤ 5 content
|
||
lines) at the very top of the body, before `## Intent`. It is the
|
||
framing-anchored one-glance summary — what the brief asks for and how it
|
||
relates to prior intent given `framing: <state.framing>`. Drafting it FIRST
|
||
forces the wrong-premise check before the rest of the prose is written. Keep
|
||
it to ≤ 5 lines (the validator warns with `BRIEF_TLDR_TOO_LONG` above that).
|
||
- **Intent:** expand the user's motivation into 3–5 sentences. Load-bearing.
|
||
- **Goal:** concrete end state.
|
||
- **Non-Goals:** from state, or "- None explicitly stated" bullet if empty.
|
||
- **Constraints / Preferences / NFRs:** from state, or "Not discussed — no
|
||
constraints assumed" note if empty.
|
||
- **Success Criteria:** falsifiable commands/observations from state, each
|
||
written in the form of `voyage:writing-criteria`.
|
||
- **Research Plan:** one `### Topic N: {title}` section per topic with the
|
||
full structure from the template. If 0 topics: write the "No external
|
||
research needed — user confirmed solid knowledge of all plan
|
||
dependencies" note.
|
||
- **Open Questions / Assumptions:** from any `"I don't know"` answers
|
||
recorded during Phase 3, plus implicit gaps; every `state.assumptions`
|
||
entry marked "assumed, not asked" or "not confirmed — the answer was «…»"
|
||
(what you said apart from what the operator said); and the Deferred list,
|
||
one line of why each.
|
||
- **Prior Attempts:** from state, or "None — fresh task."
|
||
|
||
**Step 4b — Write draft to disk**
|
||
|
||
Write the draft to `{PROJECT_DIR}/brief.md.draft` (not `brief.md` — the
|
||
final file is only written after the gate passes).
|
||
|
||
**Step 4c — Launch brief-reviewer**
|
||
|
||
**Gather memory context first (v5.5, layer 2 of the framing defense).** If the
|
||
operator's environment exposes memory facts (e.g. an auto-memory `MEMORY.md` plus
|
||
`feedback_*` / `project_*` topic files), collect the paths or excerpts of those
|
||
relevant to this task. This is best-effort and environment-dependent: if no memory
|
||
is available, pass nothing — the reviewer scores the memory-alignment dimension
|
||
`5` (N/A) when no context is supplied.
|
||
|
||
Launch the `brief-reviewer` agent with the prompt below, then wait for its result before Step 4d: the Agent tool runs it in the background and tells this session when it is done (voyage-lekerepo trial 2, 2026-10-08), and no gate, revision or guess at the verdict comes before the reviewer's own output.
|
||
|
||
> **Never pass the Agent tool's `name` parameter — at this or any other spawn
|
||
> site in this command.** `name` does not label a subagent, it changes its kind:
|
||
> the spawn is recorded as `taskKind: "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
|
||
> calls `SendMessage(to: "main")`, and every agent in `agents/` declares a
|
||
> `tools:` 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`.
|
||
|
||
> "Review this task brief for quality: `{PROJECT_DIR}/brief.md.draft`.
|
||
> Check completeness, consistency, testability, scope clarity,
|
||
> research-plan validity, and memory alignment. Judge the success criteria by
|
||
> `voyage:writing-criteria` and review by `voyage:reviewing-adversarially`.
|
||
> Report findings, verdict, and the required machine-readable JSON block.
|
||
> Operator memory context (compare the brief's Intent/Goal and its declared
|
||
> `framing:` value against these for EXPLICIT contradictions only):
|
||
> {memory paths or excerpts, or "none supplied"}."
|
||
|
||
**Step 4d — Parse JSON scores**
|
||
|
||
Parse the agent's output. Locate the **last** fenced ```json``` block.
|
||
Extract per-dimension scores:
|
||
|
||
```
|
||
review = {
|
||
completeness: { score, gaps },
|
||
consistency: { score, issues },
|
||
testability: { score, weak_criteria },
|
||
scope_clarity: { score, unclear_sections },
|
||
research_plan: { score, invalid_topics },
|
||
memory_alignment:{ score, contradictions }, # v5.5 — layer 2
|
||
verdict: "PROCEED | PROCEED_WITH_RISKS | REVISE"
|
||
}
|
||
```
|
||
|
||
**JSON fallback:** if the JSON block is missing, invalid, or a dimension
|
||
is missing, treat all dimensions as `score: 3` and set the `verdict` from
|
||
the prose verdict if present, otherwise `PROCEED_WITH_RISKS`. Emit an
|
||
internal note that the reviewer output was degraded. This ensures the
|
||
loop never deadlocks on a parser error.
|
||
|
||
**Step 4e — Gate evaluation**
|
||
|
||
The gate **passes** when all of the following are true:
|
||
|
||
- `completeness.score ≥ 4`
|
||
- `consistency.score ≥ 4`
|
||
- `testability.score ≥ 4`
|
||
- `scope_clarity.score ≥ 4`
|
||
- `research_plan.score == 5`
|
||
- `memory_alignment.score ≥ 4` (v5.5 — a score ≤ 3 means the reviewer found an
|
||
EXPLICIT contradiction between the brief and operator memory; this is a
|
||
framing-alignment blocker, not a wording nit. A `5` is also returned when no
|
||
memory context was supplied, so this never blocks environments without memory.)
|
||
|
||
(Research Plan requires a perfect score because its format is checked
|
||
mechanically: ends in `?`, `Required for plan steps` filled, scope is
|
||
one of `local | external | both`, confidence is `high | medium | low`.
|
||
Anything less means at least one topic is malformed and planning will
|
||
stumble.)
|
||
|
||
**If gate passes:**
|
||
1. Move `brief.md.draft` → `brief.md` (atomic rename).
|
||
2. Delete the draft file if rename is not possible on the OS; write
|
||
`brief.md` fresh.
|
||
3. Stamp the reviewed body, so `/trekplan` does not run the same review on
|
||
the same text again (operator decision 2026-10-08):
|
||
`node ${CLAUDE_PLUGIN_ROOT}/lib/validators/brief-review-stamp.mjs --stamp "{PROJECT_DIR}/brief.md"`.
|
||
Any later edit to the body makes the stamp stale and `/trekplan` reviews
|
||
again. Exit 1 (not stamped) is said in one line; it only means the review
|
||
runs twice. Only a passed gate is stamped.
|
||
4. Class `small` only: now write the reviewer's gaps into `## Open Questions / Assumptions`. They come after the stamp on purpose: the reviewer never saw them, so the stamp reads stale and `/trekplan` reviews the brief once more.
|
||
5. Break the loop and proceed to Step 4g.
|
||
|
||
**If gate fails AND iteration count < the class cap:**
|
||
1. Identify the weakest dimension (lowest score; tie broken by priority:
|
||
research_plan > memory_alignment > testability > completeness >
|
||
consistency > scope_clarity).
|
||
2. Generate a targeted follow-up question from the dimension's detail
|
||
field (gaps / issues / weak_criteria / unclear_sections / invalid_topics /
|
||
contradictions).
|
||
Example generators:
|
||
- `completeness.gaps: ["Non-Goals empty, unclear if deliberate"]`
|
||
→ "You did not specify anything out-of-scope. Is that deliberate, or
|
||
are there things we should explicitly exclude?"
|
||
- `testability.weak_criteria: ["'system should be fast'"]`
|
||
→ "'System should be fast' is not falsifiable. Which metric or
|
||
threshold proves this is met — e.g., p95 < 200ms, or throughput
|
||
≥ X requests/sec?"
|
||
- `research_plan.invalid_topics: [{"topic":"JWT","issue":"Required for plan steps empty"}]`
|
||
→ "For research topic 'JWT': which plan steps depend on the answer?
|
||
Give one or two concrete kinds of step (e.g., 'library selection',
|
||
'threat model', 'migration strategy')."
|
||
- `memory_alignment.contradictions: [{"brief_claim":"continue the REST approach","memory_fact":"team decided to move to GraphQL","file":"project_api.md"}]`
|
||
→ "Your brief's framing says 'preserve', but memory records the team
|
||
moved off REST to GraphQL. Is this brief intentionally reviving REST,
|
||
or should the framing be 'replace' / 'new-direction'?"
|
||
3. Ask via `AskUserQuestion`. Record the answer into Phase 3 state.
|
||
4. Return to Step 4a with incremented iteration count. The reviewer sees
|
||
an updated draft, so you MUST re-read the brief and regenerate the
|
||
review each iteration — do not reuse stale scores.
|
||
5. When launching the reviewer on iteration 2 or 3, include prior
|
||
questions in the prompt so it does not produce circular follow-ups:
|
||
> "Questions already asked during this interview: {list from
|
||
> question_history}. Focus on issues that remain after those answers —
|
||
> do not re-raise gaps that have already been addressed."
|
||
|
||
**If gate fails AND iteration count == the class cap (loop exhausted):**
|
||
1. Move `brief.md.draft` → `brief.md`.
|
||
2. Add `brief_quality: partial` to the frontmatter (edit the file
|
||
post-rename — insert the key above the closing `---`).
|
||
3. Add a `## Brief Quality` section near the end with the failing
|
||
dimensions and their `detail` arrays from the final review, formatted:
|
||
```
|
||
## Brief Quality
|
||
|
||
Review loop exhausted after 3 iterations. The following dimensions
|
||
did not reach the pass threshold:
|
||
|
||
- **Research Plan (score 2/5):** Topic 'JWT library' missing
|
||
Required-for-plan-steps field.
|
||
- **Testability (score 3/5):** Success criterion "works correctly"
|
||
is not falsifiable.
|
||
|
||
Downstream planning will treat these as reduced-confidence areas.
|
||
```
|
||
4. Break the loop and proceed to Step 4g.
|
||
|
||
### Step 4f — Force-stop handling
|
||
|
||
If during any `AskUserQuestion` in Step 4e the user says "stop", "skip",
|
||
"enough", "just write it", or similar, do NOT exit the loop immediately.
|
||
Instead, surface the current review findings in plain text:
|
||
|
||
```
|
||
Brief-reviewer would flag these issues:
|
||
- Research Plan (score 2/5): Topic 'JWT library choice' missing Required-for-plan-steps field.
|
||
- Testability (score 3/5): Success criterion "works correctly" is not falsifiable.
|
||
|
||
Continue anyway? The plan will have lower confidence in these areas.
|
||
```
|
||
|
||
Then ask via `AskUserQuestion`:
|
||
|
||
| Option | Action |
|
||
|--------|--------|
|
||
| **Answer one more follow-up** | Return to Step 4e with the current weakest-dimension question. |
|
||
| **Stop now (accept partial brief)** | Finalize brief with `brief_quality: partial` and the `## Brief Quality` section (same path as iteration-cap exhaustion). Break loop. |
|
||
|
||
The force-stop path is distinct from a silent iteration cap: the user
|
||
sees exactly which dimensions are weak and chooses informed.
|
||
|
||
### Step 4g — Finalize
|
||
|
||
After the loop exits (pass, cap, or force-stop), ensure:
|
||
- `brief.md` exists at `{PROJECT_DIR}/brief.md`.
|
||
- `brief.md.draft` no longer exists.
|
||
- If the loop ended without a clean pass, frontmatter contains
|
||
`brief_quality: partial` and a `## Brief Quality` section exists.
|
||
- If the loop ended with a clean pass, `brief_quality` is either
|
||
absent or set to `complete`.
|
||
|
||
Populate the "How to continue" footer with the actual project path and
|
||
topic questions.
|
||
|
||
**Schema sanity check (since v3.1.0):** before reporting, run the brief
|
||
validator. This catches frontmatter typos and state-machine inconsistencies
|
||
the brief-reviewer rubric does not check (e.g. `research_status: skipped`
|
||
with `research_topics: 3` and no `brief_quality: partial`).
|
||
|
||
```bash
|
||
node ${CLAUDE_PLUGIN_ROOT}/lib/validators/brief-validator.mjs --json "{PROJECT_DIR}/brief.md"
|
||
```
|
||
|
||
If the validator returns errors, report them to the user and offer to
|
||
re-enter Phase 4 with the validator's hints in scope. If only warnings,
|
||
note them in the final report — except `BRIEF_SC_NOT_RUNNABLE`: a success
|
||
criterion whose command `/trekreview` will never run (the product's own
|
||
binary, `jq`, `grep`, a pipe). Name each one to the user in plain words and
|
||
offer to rewrite it as a script under `tests/` that the criterion runs
|
||
(`bash tests/<script>.sh` exits 0) before the brief is approved; if the user
|
||
keeps it, it stays a criterion a human judges, and the report says so. The
|
||
same goes for `BRIEF_SC_SUBPOINTS` (a criterion with sub-points and no command
|
||
of its own: offer one script that checks them all, or one criterion per
|
||
sub-point) and `BRIEF_SC_BASH_NOT_PINNED` (the brief requires bash 3.2 but a
|
||
criterion runs plain `bash`: offer `/bin/bash`).
|
||
|
||
**Build the operator-annotation HTML, then print the report.** After the
|
||
brief is validated, run `scripts/annotate.mjs` to produce a self-contained
|
||
HTML file the operator opens in their browser. The HTML renders the brief
|
||
with line numbers, lets the operator click any line to attach their own
|
||
note (not Claude-generated suggestions — the operator drives every
|
||
annotation), keeps a sidebar of all notes, persists state in localStorage,
|
||
and exposes a "Copy Prompt" button that generates a single structured
|
||
prompt with every note. The operator copies that prompt and pastes it
|
||
back into Claude; Claude revises `brief.md` freehand from the notes.
|
||
|
||
```bash
|
||
ANNOT_HTML=$(node ${CLAUDE_PLUGIN_ROOT}/scripts/annotate.mjs "{PROJECT_DIR}/brief.md" 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. The report below
|
||
still mentions the (failed) path so the operator can debug.
|
||
|
||
Then print this block **verbatim** (substitute `{PROJECT_DIR}` and
|
||
`$ANNOT_HTML`):
|
||
|
||
```
|
||
Brief written: {PROJECT_DIR}/brief.md
|
||
Annotation HTML: file://{$ANNOT_HTML}
|
||
Review iterations: {1..3}
|
||
Final quality: {complete | partial}
|
||
Validator: {PASS | warnings(N)}
|
||
Research topics identified: {N}
|
||
|
||
────────────────────────────────────────────────────────────────────
|
||
To review and annotate this brief, open the HTML above 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 and Claude revises brief.md
|
||
from your notes. Annotations persist in your browser if you close
|
||
the tab and reopen the same file.
|
||
────────────────────────────────────────────────────────────────────
|
||
```
|
||
|
||
## Phase 4h — Intent approval (veikart steg 1)
|
||
|
||
`/trekplan` does not plan against an intent the operator has not approved
|
||
(it halts on `BRIEF_INTENT_NOT_APPROVED`). This phase is where that approval
|
||
is given. It runs on EVERY brief, before the Phase 5 manual/auto fork, so
|
||
both paths pass through it — and it is **asked even in `--quick` mode**; there
|
||
is no skip path and no default answer.
|
||
|
||
1. Show the operator the `## Intent` and `## Goal` sections of
|
||
`{BRIEF_PATH}` **verbatim** — the exact text the approval will
|
||
bind to. Do not summarize them.
|
||
2. Ask via `AskUserQuestion` — one question:
|
||
**"Is this the intent /trekplan should plan against?"**
|
||
|
||
| Option | Effect |
|
||
|--------|--------|
|
||
| **Approve** | Check the answer (step 3), then stamp, log and commit (steps 4–6). |
|
||
| **Revise first** | The operator states the change; edit `## Intent` / `## Goal` in `{BRIEF_PATH}`, re-run the Phase 4g validator, then return to step 1. |
|
||
| **Leave unapproved** | Write nothing. `/trekplan` will halt on this brief until `/trekbrief --approve {BRIEF_PATH}` is run. |
|
||
|
||
3. **Is the answer an approval?** Only an explicit "Approve" is. Check the
|
||
operator's answer as given, verbatim, on stdin (never in the command line):
|
||
|
||
```bash
|
||
node ${CLAUDE_PLUGIN_ROOT}/lib/util/approval-log.mjs answer <<'ANSWER'
|
||
{OPERATOR_ANSWER}
|
||
ANSWER
|
||
```
|
||
|
||
Exit 0 → step 4. Exit 1 → it is not an approval: «høres bra ut»,
|
||
«gjør som du vil», "sounds good", "looks good", "whatever you think" and an
|
||
empty answer all land here, as does anything you would have to infer approval
|
||
from (silence, an earlier answer). Say in one line that the answer was not
|
||
taken as approval and why, then ask step 2 once more; a second
|
||
non-approval is "Leave unapproved". Never stamp after exit 1.
|
||
|
||
4. **Stamp the brief:**
|
||
|
||
```bash
|
||
node ${CLAUDE_PLUGIN_ROOT}/lib/validators/intent-approval.mjs --stamp "{BRIEF_PATH}"
|
||
```
|
||
|
||
This writes `intent_approved_hash` (sha256 over the normalized `## Intent`
|
||
+ `## Goal`) and `intent_approved_at` into the frontmatter, and emits the
|
||
`brief-approved` lifecycle event. It prints JSON; if `stamped` is `false`
|
||
(e.g. `## Goal` is empty), report the `reason` and return to step 1. Editing
|
||
`## Intent` or `## Goal` afterwards makes the approval stale — `/trekplan`
|
||
halts until the operator approves the new text.
|
||
|
||
5. **Log the approval** (K2), whatever `artifact_store` says. `{INTENT_HASH}`
|
||
is the `intent_hash` the stamp just printed:
|
||
|
||
```bash
|
||
node ${CLAUDE_PLUGIN_ROOT}/lib/util/approval-log.mjs append --kind intent --subject "{BRIEF_PATH}" --sha256 "{INTENT_HASH}"
|
||
```
|
||
|
||
It appends one hash-chained record to the target repo's
|
||
`.claude/voyage/approvals.jsonl`. On exit 1 or 2, print its `reason` in one
|
||
line: the brief is stamped but not logged, so `/trekplan` halts
|
||
(`not-logged`) until `/trekbrief --approve {BRIEF_PATH}` succeeds.
|
||
|
||
6. **Commit in the target repo.** `{ARTIFACT_STORE}` is the brief's
|
||
`artifact_store` (`local` when absent, e.g. in `--approve` mode on another
|
||
producer's brief); `{slug}` is the brief's `slug`; `{model}` is this session's model name (e.g. `Opus 5.5`):
|
||
|
||
```bash
|
||
node ${CLAUDE_PLUGIN_ROOT}/lib/util/artifact-commit.mjs --store {ARTIFACT_STORE} --artifact "{BRIEF_PATH}" --message "docs(voyage): approve intent {slug}" --trailer "Co-Authored-By: Claude {model} <noreply@anthropic.com>"
|
||
```
|
||
|
||
`git`: the brief and the log in one commit. `local`: only the log; the
|
||
project folder is kept out of git through `.git/info/exclude`. The commit
|
||
names its paths, so anything else the operator has staged stays staged and
|
||
uncommitted. On exit 1 (e.g. the repo ignores `.claude/`) or 2 (not a git
|
||
repository), print the `reason` in one line and continue: the approval is
|
||
logged on disk, not committed, and `/trekplan` reports it as uncommitted.
|
||
|
||
What the stamp and the log do NOT prove: any session on this machine could
|
||
run the same commands. They are a trace that this step ran against this
|
||
text, not the operator's signature; the commit is what makes an approval
|
||
visible in `git log` (see the headers of `lib/validators/intent-approval.mjs`
|
||
and `lib/util/approval-log.mjs`).
|
||
|
||
Report:
|
||
```
|
||
Intent: {approved (sha256:…12 hex) | left unapproved — /trekplan will halt}
|
||
Logged: {record {seq} in .claude/voyage/approvals.jsonl | no — {reason}}
|
||
Committed: {{short sha}: brief + log | {short sha}: log only, brief kept on this machine | no — {reason}}
|
||
```
|
||
|
||
In `--approve` mode, run **Phase 7** (`"mode": "approve"`) when the stamp
|
||
printed `stamped: true`, then stop and print the next step:
|
||
`{PLAN_COMMAND}` (only if approved).
|
||
|
||
## Phase 5 — Auto-orchestration opt-in (if research_topics > 0)
|
||
|
||
**Skip this phase if research_topics = 0.** Proceed directly to Phase 6.
|
||
|
||
Ask the user via `AskUserQuestion`:
|
||
|
||
**Question:** "You have {N} research topic(s). How do you want to proceed?"
|
||
|
||
| Option | Description |
|
||
|--------|-------------|
|
||
| **Manual (default)** | Print the commands. You run `/trekresearch` and `/trekplan` yourself, choosing depth per topic. |
|
||
| **Auto (managed by Claude Code)** | I run all {N} research topics sequentially in foreground, then automatically trigger `/trekplan` when research completes. This session blocks until the plan is ready. |
|
||
|
||
### Manual path (default)
|
||
|
||
Output:
|
||
|
||
```
|
||
## Brief complete
|
||
|
||
Project: {PROJECT_DIR}/
|
||
Brief: {PROJECT_DIR}/brief.md
|
||
Research topics: {N}
|
||
|
||
Next steps (run in order or parallel):
|
||
|
||
{For each topic:}
|
||
/trekresearch --project {PROJECT_DIR} --external "{topic question}"
|
||
|
||
Then:
|
||
/trekplan --project {PROJECT_DIR}
|
||
|
||
Then:
|
||
/trekexecute --project {PROJECT_DIR}
|
||
```
|
||
|
||
Stop. Do not continue to Phase 6.
|
||
|
||
### Auto path
|
||
|
||
**Auto requires an approved intent.** If Phase 4h ended "Leave unapproved",
|
||
the auto chain would halt at `/trekplan` anyway — print the manual-path
|
||
output instead and stop.
|
||
|
||
Set `auto_research: true` in the brief's frontmatter (edit the file).
|
||
|
||
The `brief-approved` lifecycle event was already emitted by the Phase 4h
|
||
stamp — on both paths — so it is not emitted again here.
|
||
|
||
If `gates_mode == true`: pause here via `AskUserQuestion` —
|
||
"Auto-mode confirmed. Proceed to research now? (yes/no)". If the user
|
||
answers no, fall back to the manual path output and stop. Otherwise
|
||
proceed to Phase 6.
|
||
|
||
If `gates_mode == false` (default in auto): proceed directly to Phase 6.
|
||
The chain stops only at the main-merge gate (see `commands/trekexecute.md`
|
||
Phase 8).
|
||
|
||
Proceed to Phase 6.
|
||
|
||
## Phase 6 — Auto research dispatch (auto path only)
|
||
|
||
**Runs only when user opted into auto mode.**
|
||
|
||
### Step 6a — Confirm proceed
|
||
|
||
Tell the user auto mode will run in foreground and block the session, then
|
||
confirm via `AskUserQuestion`:
|
||
|
||
**Question:** "Auto mode runs {N} research topic(s) sequentially and then
|
||
the plan — all in foreground. This session blocks until the plan is ready.
|
||
Continue?"
|
||
|
||
| Option | Action |
|
||
|--------|--------|
|
||
| **Continue — auto** | Proceed. |
|
||
| **Cancel — do manual** | Revert to manual path (print commands, stop). |
|
||
|
||
If cancelled → fall back to manual path output and stop.
|
||
|
||
### Step 6b — Run research topics sequentially (inline)
|
||
|
||
Set `research_status: in_progress` in the brief's frontmatter.
|
||
|
||
For each research topic (index i = 1 .. N), invoke `/trekresearch`
|
||
inline in this main-context session:
|
||
|
||
```
|
||
/trekresearch --project {PROJECT_DIR} {--external | --local | (none)} "{topic i question}"
|
||
```
|
||
|
||
Pass the scope flag that matches the topic's scope hint. Wait for each
|
||
invocation to finish writing the research brief at
|
||
`{PROJECT_DIR}/research/{NN}-{topic-slug}.md` before moving to the next
|
||
topic.
|
||
|
||
> **Why sequential inline instead of parallel background?** Historically,
|
||
> background orchestrator-agents could not spawn the research swarm —
|
||
> before Claude Code 2.1.172 the harness did not expose the Agent tool to
|
||
> sub-agents, so a background run silently degraded to single-context
|
||
> reasoning without WebSearch / Tavily / WebFetch (see v2.4.0
|
||
> release notes). As of CC 2.1.172 sub-agents can spawn sub-agents (up to 5
|
||
> levels deep), so a delegated redesign is under evaluation (see
|
||
> `docs/cc-upgrade-2.1.181-decision-matrix.md`, W1/CC-26). Until then,
|
||
> running each research pass inline in main context keeps the swarm intact.
|
||
|
||
### Step 6c — Verify all briefs landed
|
||
|
||
After the last topic completes, verify each research brief file exists:
|
||
|
||
```bash
|
||
ls -1 {PROJECT_DIR}/research/*.md | wc -l
|
||
```
|
||
|
||
Expected count: N. If any are missing, report and ask the user how to
|
||
proceed (retry, skip missing topic, cancel).
|
||
|
||
Update brief frontmatter: `research_status: complete`.
|
||
|
||
### Step 6d — Auto-trigger planning (inline foreground)
|
||
|
||
Invoke the planning command inline in this session:
|
||
|
||
```
|
||
/trekplan --project {PROJECT_DIR}
|
||
```
|
||
|
||
The planning pipeline runs all phases (exploration, synthesis, review) in
|
||
main context. Wait for the plan to be written to `{PROJECT_DIR}/plan.md`
|
||
before continuing.
|
||
|
||
### Step 6e — Report completion
|
||
|
||
When the planning-orchestrator finishes, present:
|
||
|
||
```
|
||
## Ultrabrief + Ultraresearch + Voyage Complete (auto mode)
|
||
|
||
**Project:** {PROJECT_DIR}/
|
||
**Brief:** {PROJECT_DIR}/brief.md
|
||
**Research briefs:** {N} in {PROJECT_DIR}/research/
|
||
**Plan:** {PROJECT_DIR}/plan.md
|
||
|
||
### Pipeline summary
|
||
|
||
| Step | Status |
|
||
|------|--------|
|
||
| Brief | Complete ({interview_turns} interview turns) |
|
||
| Research | Complete ({N} topics, sequential foreground) |
|
||
| Plan | Complete ({steps} steps, critic: {verdict}) |
|
||
|
||
Next:
|
||
/trekexecute --project {PROJECT_DIR}
|
||
|
||
Or:
|
||
/trekexecute --dry-run --project {PROJECT_DIR} # preview
|
||
/trekexecute --validate --project {PROJECT_DIR} # schema check
|
||
```
|
||
|
||
## Phase 7 — Stats tracking
|
||
|
||
Append one record to `trekbrief-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.
|
||
|
||
```bash
|
||
node ${CLAUDE_PLUGIN_ROOT}/lib/stats/stats-append.mjs trekbrief <<'JSON'
|
||
{
|
||
"ts": "{ISO-8601}",
|
||
"task": "{task description (first 100 chars)}",
|
||
"slug": "{slug}",
|
||
"mode": "{default | quick | approve}",
|
||
"interview_turns": {N},
|
||
"review_iterations": {1..3},
|
||
"brief_quality": "{complete | partial}",
|
||
"research_topics": {N},
|
||
"auto_research": {true | false},
|
||
"auto_result": "{completed | cancelled | failed | manual}",
|
||
"intent_approved": {true | false},
|
||
"project_dir": "{path}"
|
||
}
|
||
JSON
|
||
```
|
||
|
||
`intent_approved` is `true` only when Phase 4h ran the stamp (the operator
|
||
answered "Approve"). `slug` is the brief's `slug`, the same one the stamp's
|
||
`brief-approved` record and `/trekplan`'s record carry.
|
||
|
||
In `--approve` mode the record carries fixed values: `"mode": "approve"`,
|
||
`"interview_turns": 0`, `"review_iterations": 0`, `brief_quality`,
|
||
`research_topics` and `auto_research` from the brief's frontmatter (`complete`,
|
||
`0`, `false` when absent), `"auto_result": "manual"`, `"intent_approved": true`.
|
||
|
||
The writer adds three keys itself: `order_id`, `order_claimed_at` and
|
||
`order_reason` - the coord-queue order this run is working on, read from the
|
||
queue's claimed orders (exactly one: its id and claim time; zero or several:
|
||
`order_id: null` and the reason). Never type them: any `order_*` key in the
|
||
record is replaced. `scripts/yardstick.mjs` joins its `use` number on
|
||
`order_id`.
|
||
|
||
The writer prints `{"written": true, ...}`. On `"written": false` (exit 1),
|
||
print its `reason` in one line and continue. A stats failure never blocks the
|
||
workflow, but it is reported, not skipped silently.
|
||
|
||
## Profile (v4.1)
|
||
|
||
Accepts `--profile <name>` where `<name>` is one of `economy`, `balanced`,
|
||
`premium`, `fable`, or a custom profile under `voyage-profiles/`. Default: `premium`.
|
||
|
||
Resolution order (per `lib/profiles/resolver.mjs`):
|
||
1. `--profile` flag (source: `flag`)
|
||
2. `VOYAGE_PROFILE` environment variable (source: `env`)
|
||
3. `premium` default (source: `default`)
|
||
|
||
Examples:
|
||
```
|
||
/trekbrief --profile economy
|
||
VOYAGE_PROFILE=balanced /trekbrief
|
||
```
|
||
|
||
Stats records emit `profile`, `phase_models`, and `profile_source` so operators
|
||
can audit which profile drove which session.
|
||
|
||
## Hard rules
|
||
|
||
1. **Interactive only.** This command requires user input. There is no
|
||
`--fg` or background mode — the interview cannot run headless.
|
||
2. **Brief is the contract.** Every section must have substantive content
|
||
or an explicit "Not discussed" note. No empty sections.
|
||
3. **Intent is load-bearing.** Do not accept a one-line intent. Expand with
|
||
the user until motivation is clear — the plan and every review agent
|
||
will trace decisions back to this.
|
||
4. **Research topics must be answerable.** Each topic's research question
|
||
must be phrased so `/trekresearch` can answer it. If a topic is
|
||
too vague, split or reformulate before writing.
|
||
5. **Never invent research topics the user did not agree to.** Topics
|
||
come from the interview. If the user says "I know this", respect it.
|
||
6. **Project dir is the single source of truth.** Every artifact (brief,
|
||
research briefs, plan, progress) lives in one project directory.
|
||
Never scatter files across `.claude/research/`, `.claude/plans/`, etc.
|
||
7. **Auto mode blocks foreground.** If the user opts into auto, this
|
||
session waits for research + planning to complete. Document this in
|
||
the opt-in question.
|
||
8. **Ceremony by size.** The size class sets the question budget and the
|
||
review cap (Phase 3, Phase 4). It is said aloud, the operator can override
|
||
it, and on your own you only ever move it up. The budget never hides a gap:
|
||
what it leaves out is on the Deferred list or written down as an assumption.
|
||
11. **Only an explicit approval approves.** «høres bra ut», «gjør som du vil»
|
||
and an empty answer decide nothing (Phase 4h step 3). Every approval is
|
||
logged in the target repo's approval log and committed there.
|
||
9. **Never write `brief.md` while the review gate is still pending.**
|
||
Draft lives in `brief.md.draft` until the loop terminates. A caller
|
||
that sees `brief.md` must be able to trust that Phase 4 finished.
|
||
10. **Privacy:** never log prompt text, secrets, or credentials.
|