voyage/docs/architecture.md
Kjell Tore Guttormsen b70c37f0e7
fix(cleanup): R-b review round — retired flags in the prose parsers
/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>
2026-11-07 21:36:13 +01:00

20 KiB
Raw Blame History

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 § 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).