1217 lines
55 KiB
Markdown
1217 lines
55 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, or a command that stands after a flag or path
|
||
in backticks). 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 build the page where the
|
||
operator writes their own notes. The loop that follows is the skill
|
||
`voyage:annotating`: call it when the operator pastes the notes back (a
|
||
Question is answered, not applied; after Phase 4h, a note on `## Intent` or
|
||
`## Goal` makes the approval stale, and that is said before the edit).
|
||
|
||
```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: Claude answers each Question
|
||
and revises brief.md from your Fix and Change 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 /trekplan 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
|
||
```
|
||
|
||
## 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.
|