diff --git a/docs/architecture.md b/docs/architecture.md index 0493b4f..d845a3c 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -49,6 +49,8 @@ Doc-consistency test at `tests/lib/doc-consistency.test.mjs` pins agent-table co **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 `` 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 12-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://` URL AND (b) a copy-pasteable `open file://` 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://` link. The HTML is self-contained (zero npm deps, zero external network, design-system-styled, light + dark + print) and modelled on `~/repos/claude-code-100x/claude-code-100x/build-site.js` (lines 1431–2255). 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 (Fiks / Endre / Spørsmål)**, 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:`). 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 claude-code-100x 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. diff --git a/tests/lib/doc-consistency.test.mjs b/tests/lib/doc-consistency.test.mjs index 4e1fcff..4474a72 100644 --- a/tests/lib/doc-consistency.test.mjs +++ b/tests/lib/doc-consistency.test.mjs @@ -1038,3 +1038,65 @@ test('S18: HANDOVER-CONTRACTS documents the pre-2.2 zero-framing-enforcement hol 'HANDOVER-CONTRACTS.md must state that pre-2.2 briefs receive zero framing enforcement', ); }); + +// ── S32 — "delegate the engine, keep the policy": native-delegation hygiene ── +// +// Balance-analysis V01 / V07 / V08 / V11 / V24 audit (2026-06-20): the brief +// interview and the research / exploration / reviewer swarms must ride NATIVE +// Claude Code primitives — parallel `Agent` spawn in a single message + +// `AskUserQuestion` for interviews — and re-implement no scheduler / +// concurrency-loop / menu engine of their own. The audit found all five ALREADY +// native (no code change), so these pins are the standing guard against an +// engine creeping back in, plus the documented principle in docs/architecture.md. +// Fix the SOURCE (command prose / the architecture.md note), not the test. + +const SWARM_COMMANDS = ['commands/trekresearch.md', 'commands/trekplan.md', 'commands/trekreview.md']; + +test('S32: docs/architecture.md records the "delegate the engine, keep the policy" principle', () => { + const t = read('docs/architecture.md'); + assert.ok( + /delegate the engine, keep the policy/i.test(t), + 'docs/architecture.md must name the "delegate the engine, keep the policy" principle (V01/V07/V08/V11/V24 audit, S32)', + ); + // The note must name the native primitives it delegates to (so the principle is + // anchored to real CC tools, not abstract prose). + assert.ok( + /AskUserQuestion/.test(t) && /\bAgent\b/.test(t), + 'the principle note must name the native primitives delegated to (Agent + AskUserQuestion)', + ); +}); + +test('S32: each swarm command lists the Agent tool and mandates native parallel single-message spawn', () => { + for (const f of SWARM_COMMANDS) { + const text = read(f); + assert.ok( + /^allowed-tools:.*\bAgent\b/m.test(text), + `${f}: allowed-tools frontmatter must list Agent (the native swarm-spawn engine)`, + ); + assert.ok( + /in parallel/i.test(text), + `${f}: must instruct parallel swarm spawn (native delegation, not a hand-rolled scheduler)`, + ); + assert.ok( + /single (assistant )?message|one message|multiple tool calls/i.test(text), + `${f}: must mandate single-message multi-tool-call dispatch (the native parallel-Agent pattern)`, + ); + } +}); + +test('S32: trekbrief Phase 3 delegates Q&A turn-taking to AskUserQuestion (V01 — selection rule is policy, asking is native)', () => { + const text = read('commands/trekbrief.md'); + assert.ok( + /^allowed-tools:.*AskUserQuestion/m.test(text), + 'commands/trekbrief.md: allowed-tools must list AskUserQuestion (the native Q&A engine)', + ); + const p3Start = text.indexOf('## Phase 3 — Completeness loop'); + assert.ok(p3Start >= 0, 'trekbrief.md must have a "## Phase 3 — Completeness loop" section'); + const p3End = text.indexOf('## Phase 3.5', p3Start); + const p3 = text.slice(p3Start, p3End > p3Start ? p3End : undefined); + assert.ok( + /Use `AskUserQuestion` for every question/.test(p3), + 'Phase 3 must delegate Q&A turn-taking to AskUserQuestion — the selection rule picks WHICH ' + + 'question to ask (policy); AskUserQuestion does the asking (engine). No hand-rolled menu loop.', + ); +});