/code-review high on main..HEAD: the four commands that parse their own arguments in prose (/trekbrief, /trekplan, /trekresearch, /trekexecute) lost their --profile handling, and /trekresearch its --engine, so a typed value became the path or the question. Each now strips a retired flag with its value; the arg parser retires --profile only for the commands that had it (not /trekendsession) and --engine for /trekresearch, seen red first. Stale lines R-b left: architecture's hook row (9 scripts) and bridge agent, the gap row's removed event ledger, README's contrarian marker, and a dangling "also" in /trekexecute Phase 8. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
167 lines
20 KiB
Markdown
167 lines
20 KiB
Markdown
# Voyage — Architecture, project layout, state, terminology
|
||
|
||
Imported from `CLAUDE.md` via pointer.
|
||
|
||
## Quality infrastructure (v3.4.0)
|
||
|
||
`lib/` contains zero-dep validators, parsers, and autonomy primitives wired into the four commands:
|
||
|
||
- `lib/util/{frontmatter,result,atomic-write,autonomy-gate}.mjs` — shared YAML-frontmatter parser + Result helpers + `atomicWriteJson(path, obj)` for tmp+rename writes + `--gates` flag parser (v3.4.0)
|
||
- `lib/parsers/{plan-schema,manifest-yaml,arg-parser,jaccard,finding-id}.mjs` — pure parsers (no I/O), unit-tested. `manifest-yaml` extended in v3.4.0 with additive `skip_commit_check` + `memory_write` flags (forward-compat: unknown keys ignored)
|
||
- `lib/review/{rule-catalogue,plan-review-dedup}.mjs` — version-pinned rule catalogue (17 keys) + Phase 9 inline dedup helpers (v3.4.0)
|
||
- `lib/review/{verdict-gate,engine-findings,recurrence}.mjs` — the review gate (K7: 0 ALLOW, or WARN with every counting MAJOR answered · 1 BLOCK · 2 never green), Claude Code `/code-review` + `/security-review` findings → reviewer payload, recurring finding classes → operator-approved project-memory lines (review_version 1.2); `lib/review/fill-finding-ids.mjs` computes the coordinator's `@@ID_<n>@@` placeholders over `(file, line, rule_key)`; `lib/validators/brief-review-stamp.mjs` lets `/trekplan` skip a brief review `/trekbrief` already ran on the same text
|
||
- `lib/validators/{brief,research,plan,progress,session-state}-validator.mjs` — schema validators with CLI shims (`node lib/validators/X.mjs --json <path>`)
|
||
- `lib/validators/architecture-discovery.mjs` — drift-WARN external-contract discovery for `architecture/overview.md`
|
||
- `lib/validators/query-privacy-gate.mjs` — gates an outbound research query before it leaves the machine (no command calls it now: its callers were the removed STORM steps); the hard-block tier (secret-shaped strings) is not operator-overridable, so a query that trips it must be reformulated rather than forced through. CLI shim: `node lib/validators/query-privacy-gate.mjs "<query>"`
|
||
|
||
Wiring points (replaces previous prose-grep instructions):
|
||
- `/trekbrief` Phase 4g → `brief-validator` (post-write sanity check)
|
||
- `/trekplan` Phase 1 → `brief-validator --soft`, `research-validator --dir`, `architecture-discovery`
|
||
- `/trekplan` Phase 8 → `plan-validator --strict` (replaces 3 `grep -cE` calls)
|
||
- `/trekexecute --validate` → `plan-validator --strict` + `progress-validator`
|
||
|
||
Tests under `tests/**/*.test.mjs` (~290 tests, 0 deps). `npm test` is the fork-readiness gate. v3.4.0 adds: hook baseline regression pins (`tests/hooks/{path-guard,bash-guard}.test.mjs` exercising `pre-write-executor.mjs` + `pre-bash-executor.mjs` denylist BLOCK paths).
|
||
|
||
Doc-consistency test at `tests/lib/doc-consistency.test.mjs` pins agent-table count, command-table coverage, plan_version invariant, Handover 7 presence, and `session-state-validator` CLI shim.
|
||
|
||
`docs/HANDOVER-CONTRACTS.md` is the single source of truth for the 7 pipeline handovers (brief→research, research→plan, architecture→plan EXTERNAL, plan→execute, progress.json resume, review→plan, `.session-state.local.json`). Read it before changing any artifact format.
|
||
|
||
`hooks/scripts/pre-compact-flush.mjs` (PreCompact event, CC v2.1.105+) fixes a documented P0: keeps `progress.json` in sync with git history before context compaction so `--resume` works after long conversations. Atomic write, monotonic only, never blocks compaction.
|
||
|
||
`hooks/scripts/session-title.mjs` (UserPromptSubmit, CC v2.1.94+) sets `sessionTitle` to `voyage:<command>:<slug>` for voyage-command invocations. Helps parallel sessions identify themselves in process lists.
|
||
|
||
`hooks/scripts/post-compact-flush.mjs` (PostCompact event, v3.4.0) re-injects `.session-state.local.json` after context compaction so multi-session work survives a compaction boundary. Companion to `pre-compact-flush.mjs` (which writes the state file before compaction); together they form the rehydrate cycle that keeps `/trekcontinue` reliable across long-running multi-session work.
|
||
|
||
## Architecture
|
||
|
||
**Brief:** 7-phase workflow: Parse mode → Create project dir → Phase 3 completeness loop (section-driven, no question cap) → Phase 4 draft/review/revise with `brief-reviewer` as stop-gate (max 3 iterations; gate = all dimensions ≥ 4 and research plan = 5) → Finalize (`brief.md` on pass, or `brief_quality: partial` on cap/force-stop) → Manual/auto opt-in. Always interactive. Auto mode runs research + plan inline in the main context (v2.4.0).
|
||
|
||
**Research:** Foreground workflow (v2.4.0): Parse mode → Interview → Parallel research swarm (5 local + 4 external, spawned from main context) → Follow-ups → Triangulation → Synthesis + brief. With `--project`, writes to `{dir}/research/NN-slug.md`.
|
||
|
||
**Plan:** Foreground workflow (v2.4.0): Parse mode (validate brief input) → Codebase sizing → Brief review (`brief-reviewer`) → Parallel exploration (6-8 agents, spawned from main context) → Deep-dives → Synthesis (with architecture-note cross-reference if present) → Planning → Adversarial review (`plan-critic` + `scope-guardian`) → Present/refine → Handoff. With `--project`, writes to `{dir}/plan.md` and auto-detects `{dir}/architecture/overview.md` (produced by an opt-in upstream architect plugin if installed; not bundled).
|
||
|
||
**Decompose:** Parse plan → Analyze step dependencies → Group into sessions → Identify parallel waves → Generate session specs + dependency graph + `orders.md` (one starter line per session).
|
||
|
||
**Execute:** Parse plan → Security scan (Phase 2.4) → Detect Execution Strategy → Single-session (step loop) or multi-session (one wave at a time: a worktree plus a session order and starter per session; the user opens every session, and a re-run merges the wave — Voyage never starts a session) → Phase 7.5 manifest audit → Phase 7.6 bounded recovery (if partial) → Phase 8 atomically writes `progress.json` + `.session-state.local.json` (Handover 7) → Report. With `--project`, reads `{dir}/plan.md`. Phase 2.55 (pre-flight stop) and Phase 4 (entry-condition stop) also write `.session-state.local.json` so `/trekcontinue` can surface the stop and prompt for next steps.
|
||
|
||
**Continue:** `/trekcontinue` reads `{dir}/.session-state.local.json` (Handover 7), validates schema-v1 via `session-state-validator`, narrates a 3-line summary (project / next-session-label / brief-path), and immediately begins executing the next session. Auto-discovers active project state files under `.claude/projects/*/.session-state.local.json` if no explicit `<project-dir>` argument. Operator-invoked only — never auto-loaded via SessionStart. The `/trekendsession` helper is the informal-flow producer: writes the same state file for ad-hoc multi-session handovers that don't run through `/trekexecute`.
|
||
|
||
**Delegate the engine, keep the policy (cross-cutting; audited S32, 2026-06-20).** Every swarm and interview in the pipeline rides a *native* Claude Code primitive and re-implements no engine of its own: parallel sub-agent fan-out is a single-message multi-`Agent`-call dispatch (research swarm, exploration swarm, reviewer swarm), and interview turn-taking is `AskUserQuestion` (brief Phase 3, the research interview, the trekplan research-status gate, the trekreview scope gate). Voyage's value is the **policy** layered on top — never the scheduler, concurrency loop, or menu loop underneath: typed agent roles + effort defaults + codebase-size scaling (exploration); 4-angle decomposition (docs/community/security/contrarian) + per-source schemas + triangulation (research); the 17-key rule catalogue + no-cross-feed isolation + deterministic dedup/verdict (review); the section-driven completeness loop + framing gate (brief). Concretely: the brief's Phase-3 "selection rule" picks *which* question to ask (policy); `AskUserQuestion` does the asking (engine). The V01/V07/V08/V11/V24 balance-analysis audit confirmed all five already delegate natively — no engine re-implementation found; the doc-consistency test (`tests/lib/doc-consistency.test.mjs` § S32) pins the native-delegation prose so a hand-rolled engine cannot creep back in.
|
||
|
||
**Operator-UX guarantee (since v5.0.2):** `/trekbrief`, `/trekplan`, and `/trekreview` MUST always emit (a) a plain `file://<abs path>` URL AND (b) a copy-pasteable `open file://<abs path>` command in the final report block. The file:// URL must use an ABSOLUTE path (not relative or `~/`-prefixed) so terminals with cmd+click support (Ghostty, iTerm2, modern Terminal.app) can resolve it without shell interpretation. This is a non-negotiable operator-UX contract — the doc-consistency test pins both forms in all three commands' final report blocks.
|
||
|
||
**Operator-annotation HTML (v5.0.3):** the last step of `/trekbrief`, `/trekplan`, and `/trekreview` runs `scripts/annotate.mjs` against the just-written `.md` and prints the resulting `file://<abs path>` link. The HTML is self-contained (zero npm deps, zero external network, design-system-styled, light + dark + print) and modelled on an existing annotation surface the operator pointed at. The operator opens the file, the document renders as a proper article (headings / paragraphs / lists / tables / code / quotes — every element gets a stable `data-anchor-id`). In annotation mode (default ON, pencil-toggle in topbar), the operator can **select any text or click any element** → a form popover opens at the cursor with: section context auto-detected from nearest h1/h2, the anchored snippet (selection if any, else element text), **three intent buttons (Fix / Change / Question)**, comment textarea, Save/Cancel. The sidebar (Show annotations button) lists every annotation grouped by section with intent badge + snippet + comment + delete; clicking a card scrolls to and flashes the source element. **Copy Prompt** assembles a structured markdown (`### N. [Intent] Section: <…>` + `Quote: «…»` + `Comment: …`) and copies to clipboard. Persistence: `localStorage` keyed on absolute artifact path (`voyage-annotate:v2:<abs path>`). v5.0.0 removed the v4.2/v4.3 bespoke playground SPA + `/trekrevise` + Handover 8; v5.0.1 pointed at `/playground document-critique` (Claude-leads, wrong direction); v5.0.2 was operator-led but too thin (line-click + freeform note, no intents); v5.0.3 matches the annotation reference the operator first pointed at, with pencil-toggle / selection capture / intent categories / popover form / structured export. See [CHANGELOG.md](../CHANGELOG.md) § v5.0.3.
|
||
|
||
**Security:** 3-layer defense-in-depth: plugin hooks (pre-bash-executor, pre-write-executor), prompt-level denylist (holds even if a hook does not fire), pre-execution plan scan (Phase 2.4). Hard Rules 14-16 enforce verify command security, repo-boundary writes, and sensitive path protection.
|
||
|
||
**Pipeline:** `/trekbrief` produces the task brief. `/trekresearch --project <dir>` fills in `{dir}/research/`. `/trekplan --project <dir>` reads brief + research to produce `{dir}/plan.md` (and auto-discovers `{dir}/architecture/overview.md` if an opt-in upstream architect plugin produced one). `/trekexecute --project <dir>` executes and writes `{dir}/progress.json`. `/trekreview --project <dir>` produces `{dir}/review.md`. `/trekbrief`, `/trekplan`, and `/trekreview` each end by running `scripts/annotate.mjs` on the just-written artifact, producing `{dir}/{artifact}.html` — a self-contained operator-annotation surface — and printing the `file://` link. The operator opens it, clicks lines, writes their own notes, copies a structured prompt, pastes back, Claude revises the `.md`. All artifacts live in one project directory.
|
||
|
||
**Project-directory contract (v3.0.0):** trekplan owns the directory layout below. The `architecture/` subdirectory is opt-in and produced by an opt-in upstream architect plugin (not bundled) — the architect plugin is no longer publicly distributed, but the `architecture/overview.md` slot remains available for any compatible producer.
|
||
|
||
```
|
||
.claude/projects/{YYYY-MM-DD}-{slug}/
|
||
brief.md ← trekbrief writes; everyone reads
|
||
brief.html ← trekbrief annotates (operator-annotation HTML, gitignored, re-buildable from brief.md)
|
||
research/*.md ← trekresearch writes; plan + architect read
|
||
architecture/ ← OPT-IN, owned by an opt-in upstream architect plugin (not bundled)
|
||
overview.md
|
||
gaps.md
|
||
plan.md ← trekplan writes; trekexecute reads
|
||
plan.html ← trekplan annotates
|
||
progress.json ← trekexecute writes
|
||
review.md ← trekreview writes; trekplan reads (Handover 6)
|
||
review.html ← trekreview annotates
|
||
```
|
||
|
||
The `.html` files (`brief.html`, `plan.html`, `review.html`) are produced by `scripts/annotate.mjs` and live alongside their `.md` siblings in the project directory. They are re-buildable from the `.md` source at any time (deterministic, byte-identical output on re-run), so they are conventionally gitignored along with the rest of `.claude/projects/`. Operator annotations live in browser `localStorage` keyed on the absolute artifact path — they survive refresh and browser-close, but are local to the operator's machine.
|
||
|
||
No code-level dependency between plugins — the contract is filesystem-level only.
|
||
|
||
### Primitives per step (decision matrix)
|
||
|
||
Which native Claude Code primitive each pipeline step runs on today, and the alternatives that were considered and where they landed. Companion to *Delegate the engine, keep the policy* above. The orchestration runs inline in the session, not in a delegated orchestrator agent, because the swarm's results end in the main context anyway (the orchestrator reference docs are removed).
|
||
|
||
| Step | Primitive today (production) | Alternatives considered (status) |
|
||
|---|---|---|
|
||
| **brief** | Inline interview loop → `brief.md` (framing gate) | `AskUserQuestion` as the Q&A engine (recommended, not yet wired); Skill/Workflow auto-orchestration (rejected — one-shot, can't span slash-command/handover boundaries) |
|
||
| **research** | Inline spawns a foreground swarm; MCP per agent¹ | Delegated `synthesis-agent` (PoC measured Δ main-context ≈ 0 → removed) |
|
||
| **plan** | Inline: parallel explorer swarm → synthesis → adversarial `plan-critic` | Delegated orchestrator via nested sub-agents (CC-26 → lean NO, not wired) |
|
||
| **execute** | Inline step loop; multi-session via `git worktree` + session orders the user runs, wave by wave; deterministic manifest audit | CC `TaskCreate`/`TodoWrite` for progress/resume (insufficient — carries no step status / attempts / SHA / drift → own typed `progress.json` contract) |
|
||
| **review** | Inline parallel reviewers (no cross-feed) → `review-coordinator` Judge | **Workflow** substrate for Phase 5–6 (bake-off POSITIVE: +4.4 % tokens / +54 % wall-time → shipped opt-in, since removed; wholesale substrate swap declined) |
|
||
| **continue** | Inline reads `.session-state.local.json` → zero-confirm resume | CC `--resume` (transcript replay, not typed work-state → insufficient) |
|
||
| **cross-cutting** | 9 hook scripts: `pre-bash` + `pre-write` guards, `pre-plan-gate`, `session-title`, `post-approval-log`, `post-edit-check`, `pre-`/`post-compact` flush, `stop-test-lock` | — |
|
||
|
||
¹ MCP per research agent: `docs-researcher` → Microsoft Learn + Tavily · `community-`/`security-`/`contrarian-researcher` → Tavily (+ WebSearch/WebFetch). Graceful degradation when an MCP server is absent.
|
||
|
||
**Legend:** *production* = wired and active · *not wired* = considered, deferred (delegated orchestrator, the `AskUserQuestion` brief engine).
|
||
|
||
## Model & effort
|
||
|
||
`opus`, `sonnet`, and `fable` are model **aliases**, not pinned ids. On the
|
||
Anthropic API (verified 2026-10-02 against the Claude Code model-config docs)
|
||
the `opus` alias resolves to **Opus 5.5** and `sonnet` to **Sonnet 5.5**, both
|
||
with default reasoning effort **`medium`**; `fable` resolves to **Fable 5.1**
|
||
(the most capable model in Claude Code), whose default reasoning effort is
|
||
**`high`**. Other providers resolve `opus` and `sonnet` to other versions.
|
||
|
||
**Reasoning effort inherits from the session.** Fable 5.1's default reasoning
|
||
effort is `high`, NOT xhigh, and Claude Code saves effort per model, so a level
|
||
saved for one model does not carry to another. To run Fable 5.1 at xhigh,
|
||
choose it explicitly: `/effort xhigh`, `--effort xhigh`, or
|
||
`CLAUDE_CODE_EFFORT_LEVEL`.
|
||
|
||
**Native `effort:` on agents.** Voyage sets the reasoning axis statically on
|
||
selected agents, overriding the session level:
|
||
|
||
- **Nine agents pin `high`:** the retrieval agents `task-finder`,
|
||
`git-historian`, `dependency-tracer`, `architecture-mapper`, the
|
||
adversarial-reasoning agents `plan-critic`, `risk-assessor`,
|
||
`contrarian-researcher`, `review-coordinator`, and `/trekexecute`'s step
|
||
helper `step-implementer`. No agent pins below `high`
|
||
(operator rule, 2026-10-05: standard is Opus 5.5 at effort High).
|
||
- **All other agents:** unset → inherits the session's effort level (a session
|
||
that nothing sets runs at its model's default — `medium` on Opus 5.5).
|
||
|
||
**Every agent is pinned `model: opus`.** The pin is set in each agent's
|
||
frontmatter (operator decision, commit `40d8742`). The operator wants Opus on
|
||
all Voyage work and accepted the roughly fivefold sub-agent cost against
|
||
Sonnet. The glue, mechanical, retrieval and dormant roles were reconsidered
|
||
for a Sonnet downgrade (operator, 2026-06-20) and kept on Opus: the pin stays
|
||
firm.
|
||
|
||
Native-effort precedence (harness): env `CLAUDE_CODE_EFFORT_LEVEL` > frontmatter
|
||
`effort:` > session setting > model default, and a `maxEffortLevel` setting or
|
||
an organization effort cap still limits the result. `MAX_THINKING_TOKENS=0` has
|
||
no effect on Opus 5.5, Sonnet 5.5 or the Fable models: thinking cannot be turned
|
||
off there. `availableModels` restricts which models can be selected — it does
|
||
**not** bound effort.
|
||
|
||
## State
|
||
|
||
All artifacts in one project directory (default):
|
||
- Project root: `.claude/projects/{YYYY-MM-DD}-{slug}/`
|
||
- `brief.md` + `brief.html` (task brief from `/trekbrief`; `.html` is the operator-annotation surface from `scripts/annotate.mjs`)
|
||
- `research/{NN}-{slug}.md` (research briefs from `/trekresearch --project`)
|
||
- `architecture/overview.md` + `architecture/gaps.md` (opt-in, produced by an opt-in upstream architect plugin, not bundled)
|
||
- `plan.md` + `plan.html` (from `/trekplan --project`)
|
||
- `sessions/session-*.md` (from `--decompose`)
|
||
- `progress.json` (from `/trekexecute --project`)
|
||
- `review.md` + `review.html` (from `/trekreview --project`)
|
||
- `.session-state.local.json` (Handover 7 — gitignored via `*.local.json`; written by `/trekexecute` Phase 8/2.55/4 or `/trekendsession`; read by `/trekcontinue`)
|
||
|
||
Legacy paths (still work without `--project`):
|
||
- Research briefs: `.claude/research/trekresearch-{date}-{slug}.md`
|
||
- Plans: `.claude/plans/trekplan-{date}-{slug}.md`
|
||
- Sessions: `.claude/trekplan-sessions/{slug}/session-*.md`
|
||
- Session orders: `.claude/trekplan-sessions/{slug}/orders.md` (`--decompose`) and `orders/` (`/trekexecute` Phase 2.6: one order per session, `wave.json` while a wave awaits its sessions, `merged-waves.json`, and each session's archived `wave-W-session-N.progress.json`)
|
||
- Progress: `{plan-dir}/.trekexecute-progress-{slug}.json`
|
||
|
||
## Terminology
|
||
|
||
- **Task brief** — produced by `/trekbrief`. Declares intent, goal, and research plan. Drives planning.
|
||
- **Research brief** — produced by `/trekresearch`. Answers a specific research question. Feeds planning.
|
||
- **Architecture note** — opt-in, produced by an opt-in upstream architect plugin (not bundled; the architect plugin is no longer publicly distributed, but the `architecture/overview.md` filesystem slot remains available for any compatible producer). Proposes which Claude Code features fit the task with brief-anchored rationale + explicit gaps. When present, enriches planning.
|
||
- **Review** — produced by `/trekreview`. Independent post-hoc review of delivered code against the task brief. **Handover 6 (review → plan)** routes BLOCKER + MAJOR findings into `/trekplan --brief review.md` for a remediation plan. The plan's optional `source_findings:` frontmatter list is the audit trail back to the consumed findings. MINOR + SUGGESTION are skipped for v1.0 plan-input.
|
||
- **Session state** — `.session-state.local.json` per project. **Handover 7** — produced by any session-end mechanism (`/trekexecute` Phase 8/2.55/4, `/trekendsession` helper, future graceful-handoff v2.2). Consumed by `/trekcontinue` to resume the next session in a fresh chat. Schema v1 is forward-compat (unknown top-level keys ignored). Never committed (gitignored via `*.local.json`).
|
||
|
||
A project typically has 1 task brief, 0–N research briefs, 0 or 1 architecture note, 0–N reviews (one per review iteration), and 0 or 1 session-state file (overwritten on every session-end).
|