Skip to content

Phase 5 — Task Breakdown

Plan: i-want-to-ea3316 — pre-feature /brainstorm workflow Date: 2026-05-21 Phase status: completed Inputs: 04-technical-design.md (the chosen hybrid + DEC-9..DEC-18), plus locked OQ-1..OQ-5 defaults from Phase 4 Total tasks: 43 across 9 groups Critical path: T-3 → T-10 → T-12 → T-13 → T-14 → T-15 → T-15a → T-16 → T-17 → T-18 → T-19/T-20 → T-22 → T-36 → T-37 → ship Estimated total effort: ~70–92 hours across 6 parallelizable groups (foundation + bundled assets + context utils + tests + docs can run in parallel; core command + integration are sequential)

Ratified OQ defaults applied (user said "all default" on Phase-4 OQs):

  • OQ-1: --deep-brainstorm writes 01-brainstorming.md inline in the feature slug; standalone /brainstorm writes 00-brainstorming.md in its own slug.
  • OQ-2: --agents <n> range {0,1,2,3}. Degradation order: drop edge-cases first, then technical, always keep ux.
  • OQ-3: Tolerant JSON parser lives at new shared src/utils/tolerantJson.ts (~40 LOC). Refactor of AgentOutcomeExtractor + SteeringExtractor to consume it is FOLLOW-UP (not part of v1 scope).
  • OQ-4: FR-22 backport applies to all plan-slug consumers in the same PR (/feature --resume, /brainstorm --resume, /brainstorm --from-brainstorm, plus any other plan-slug consumer found via grep — audit-while-fresh).
  • OQ-5: FR-21 returns RICH directive mirroring CoordinatorEngine.ts:1338-1351. Callers updated (currently only featureCommand.ts:487-503 known).

0. Overview

The work decomposes into nine parallel-friendly groups. Group A (Foundation) and Group B (Bundled assets) ship as PR-1 — pure infrastructure with security value (closes SEC-3 in /feature via the FR-22 backport, fixes the FR-21 latent hang) plus the seven content files (3 roles + 1 workflow + 2 prompts + brainstormContext util). Group C (Core command) is the only deeply sequential thread because each round-loop helper builds on the previous (parse → tap → harvest → grill → synthesize → sentinel). Group D (Context utils) runs parallel to C. Group F (/feature integration) unblocks the user-visible surface. The riskiest tasks cluster around T-13 (tap-before-spawn ordering — H-2), T-15 (re-feed safety, FR-23 wrapUntrusted boundary), T-15a (auto-degrade decision per DEC-16), and T-20 (the --deep-brainstorm inline branch that must not double-write 01-brainstorming.md — H-3).

1. Task groups (visual DAG)

Foundation (parallel) ─ PR-1 candidate
  T-1   tolerantJson util ─────────┐
  T-1a  tolerantJson tests          │
  T-2   projectContext extract ────┤
  T-2a  projectContext tests        │
  T-3   planDir slug+safety ───────┼─→ T-10 (brainstormCommand scaffold)
  T-3a  planDir tests               │
  T-4   MessageBusLike widening ───┤
  T-5   FR-21 askUser directive ───┤
  T-5a  FR-21 tests                 │
  T-6   featureCommand caller fix ──┘ (uses T-5)

Bundled assets (parallel) ─ PR-1 candidate
  T-7   inquisitor-ux role.md ─────┐
  T-8   inquisitor-technical role.md ┼─→ T-10
  T-9   inquisitor-edge-cases role.md┤
  T-11  workflow + 2 prompts ───────┤
  T-11a parser.brainstorm.test       │
  T-11b SkillRegistry.brainstorm.test┘

Context utils (parallel with C) ─ PR-1 candidate
  T-30  brainstormContext util ─────→ T-19 (--from-brainstorm)
  T-30a brainstormContext tests

Core command (sequential within) ─ PR-2 candidate
  T-10  scaffold ──→ T-12 ──→ T-13 ──→ T-14 ──→ T-15 ──→ T-15a ──→ T-16 ──→ T-17

Topology (after T-15)
  T-31  topology save/restore (IR-7 + DEC-17)

Integration with /feature ─ PR-2 candidate
  T-18  registry registration (after T-17)
  T-19  --from-brainstorm (after T-30 + T-3)
  T-20  --deep-brainstorm (after T-10)

Tests (parallel within phase) ─ PR-3 candidate
  T-21..T-29  per-module tests
  T-32        smoke test

Docs (parallel with G) ─ PR-3 candidate
  T-33, T-34

Release (after all)
  T-35  CHANGELOG
  T-36  full test/lint/build
  T-37  code-review

2. Tasks

Group A — Foundation (no blockers; can all run in parallel as PR-1 or as a first commit batch)

T-1: Create src/utils/tolerantJson.ts shared parser [S] [coder] [blocked_by: —]

FR/IR/NFR: DEC-7, DEC-14, NFR-21 Files: create:src/utils/tolerantJson.ts, modify:src/utils/index.tsDescription: Implement tolerantJsonParse<T>(body, opts?): {ok: true, value: T} | {ok: false, error: string} modeled on AgentOutcomeExtractor.ts:73-117. Steps in order: strip leading/trailing ```json … ``` fences; trim; reject if input exceeds NFR-21 cap (8KB per string); JSON.parse with try/catch; if opts.arrayKey provided, accept either {[arrayKey]: T[]} shape OR bare T[] shape (dual-shape per DEC-14); per-item shallow validation deferred to caller (parser is shape-only). Add re-export to src/utils/index.ts. Acceptance:

  • Public signature exactly matches Phase-4 §6.
  • Fenced input \``json\n{"questions":[…]}\n```` parses identically to unfenced.
  • Bodies > 8KB return {ok: false, error: 'payload exceeds 8KB cap (NFR-21)'} (or similar deterministic string).
  • Dual-shape works with {arrayKey: 'questions'} against both {"questions":[…]} and […].
  • Zero .parse() calls (pure JSON; no Zod). Verification: npm test -- tolerantJson. Notes: Do NOT refactor AgentOutcomeExtractor / SteeringExtractor in this PR — that's tracked as tech-debt follow-up per OQ-3.

T-1a: Test src/utils/tolerantJson.test.ts [S] [tester] [blocked_by: T-1]

FR/IR/NFR: DEC-14, NFR-15, NFR-21 Files: create:src/utils/tolerantJson.test.tsDescription: Vitest table-driven: fenced JSON, malformed (unclosed brace), missing arrayKey shape, oversized (>8KB) input, dual-shape both directions, empty string, whitespace-only. Acceptance: ≥85% line coverage of tolerantJson.ts; every error path exercised. Verification: npm test -- tolerantJson. Notes: Use import { describe, it, expect } from 'vitest'; no Jest globals.

T-2: Extract src/utils/projectContext.ts from featureCommand [S] [coder] [blocked_by: —]

FR/IR/NFR: DEC-10, DEP-5 Files: create:src/utils/projectContext.ts, modify:src/commands/featureCommand.ts, modify:src/utils/index.tsDescription: Lift loadProjectContext (and small companion hasFleetContext) from featureCommand.ts:126-141 into a new util file. Public exports: hasFleetContext(cwd: string): boolean, loadProjectContext(cwd: string): string. Replace the inline body in featureCommand.ts with an import. Add re-exports to src/utils/index.ts. Acceptance:

  • featureCommand still loads project context identically (existing tests still pass).
  • Named exports only (no export default).
  • .js extension on the relative import. Verification: npm test -- featureCommand (regression) + npm run build. Notes: This is pure refactor; do not change error-handling semantics.

T-2a: Test src/utils/projectContext.test.ts [S] [tester] [blocked_by: T-2]

FR/IR/NFR: NFR-9, NFR-15 Files: create:src/utils/projectContext.test.tsDescription: Cases: empty .fleet/context/ dir, populated dir (concatenated output verified), missing .fleet/ dir entirely (returns empty string, not throw), fs read error (graceful), neutralizePromptInput invocation sanity (NFR-9 already satisfied by existing call site — assert it's still called). Acceptance: ≥85% line coverage of projectContext.ts. Verification: npm test -- projectContext.

T-3: Add slug validators + safe planDir resolver to src/commands/planDir.ts [S] [coder] [blocked_by: —]

FR/IR/NFR: FR-22, DEC-6, DEC-10, SEC-3 Files: modify:src/commands/planDir.tsDescription: Add SLUG_REGEX = /^[a-z0-9][a-z0-9-]{0,63}$/ constant, validateSlug(slug): {ok:true} | {ok:false, error}, and resolveSafePlanDir(cwd, slug): {ok:true, dir} | {ok:false, error} which composes path.join(cwd, '.plans', slug) and guards via isPathInsideDir from src/utils/fs.ts:40. Pure additions; no behavior change for existing callers. Acceptance:

  • Regex rejects uppercase, leading hyphen, length > 64, empty.
  • resolveSafePlanDir('/repo', '../../etc'){ok:false, error: <traversal msg>}.
  • resolveSafePlanDir('/repo', 'feature-abc123'){ok:true, dir: '/repo/.plans/feature-abc123'}.
  • Named exports; no default. Verification: npm test -- planDir (after T-3a) + npm run build. Notes: This is the backport surface for OQ-4 — /feature --resume, /brainstorm --resume, and /brainstorm --from-brainstorm will all call through it. Grep for other path.join(cwd, '.plans', ...) sites and route them through here too in subsequent tasks.

T-3a: Tests for planDir helpers [S] [tester] [blocked_by: T-3]

FR/IR/NFR: FR-22, SEC-3, NFR-15 Files: create:src/commands/planDir.test.ts (or add to existing if present) Description: SEC-3 regression suite: ../../etc, foo/../escape, /absolute/path, legit-slug accepted, Bad-SLUG rejected (uppercase), -leading-dash rejected, a accepted, 65-char string rejected. Verify isPathInsideDir invoked for resolved path. Acceptance: Every regex branch + traversal vector covered. Verification: npm test -- planDir.

T-4: Widen MessageBusLike with optional subscribe? [S] [coder] [blocked_by: —]

FR/IR/NFR: DEC-11, IR-10 Files: modify:src/types/commands.tsDescription: Add subscribe?(agentId: string, handler: (envelope: MessageEnvelope) => void): () => void; to the MessageBusLike interface at :84-92. One-line addition; optional so existing test stubs continue compiling without modification. Acceptance:

  • tsc --noEmit green.
  • Existing tests that build a partial MessageBusLike stub do not need to be updated.
  • No barrel changes (interface re-export already exists). Verification: npm run build + npm test. Notes: Brainstorm guards on if (!context.messageBus.subscribe) return { ok:false, error: '...' }.

T-5: FR-21 — make CoordinatorEngine.askUser direct path honor --unattended [M] [coder] [blocked_by: —]

FR/IR/NFR: FR-21, DEC-15, OOS-2 (promoted) Files: modify:src/coordinator/CoordinatorEngine.tsDescription: In the direct path at :1074-1097, mirror the unattended-directive logic from the tool path at :1338-1351. Factor into a private unattendedDirective(question, choices): string to dedupe the two sites. The returned string is the rich directive (e.g., [unattended] Coordinator must answer immediately. Question: <q>. Choices: <c|c|c>), not an empty string (per OQ-5). Acceptance:

  • When state.session.unattended === true, the direct path returns the rich directive string.
  • When not unattended, the direct path behaves exactly as before (awaits real user input).
  • Single shared helper used by both call sites (no duplicated literal).
  • All known callers of the direct path identified via grep (currently only featureCommand.ts:487-503). Verification: npm test -- CoordinatorEngine (after T-5a) + manual grep audit logged in PR description. Notes: Audit grep: rg "askUser\(" --type ts — verify only featureCommand.ts:487-503 consumes the return value of the direct path. T-6 updates that caller.

T-5a: Test FR-21 in CoordinatorEngine.test.ts [S] [tester] [blocked_by: T-5]

FR/IR/NFR: FR-21, NFR-15 Files: modify:src/coordinator/CoordinatorEngine.test.tsDescription: Two new cases: (1) direct path with unattended=true returns the rich directive string and does NOT call eventHandler.onUserInput; (2) direct path with unattended=false calls onUserInput and returns the user answer. Acceptance: Both cases pass; no regression in existing askUser tests. Verification: npm test -- CoordinatorEngine.

T-6: Update featureCommand.ts:487-503 caller to handle directive string [S] [coder] [blocked_by: T-5]

FR/IR/NFR: FR-21, DEC-15 Files: modify:src/commands/featureCommand.ts, modify:src/commands/featureCommand.test.tsDescription: At the pre-stage grilling site (:487-503), inspect the returned string from coordinator.askUser; if it starts with the unattended-directive marker, early-out the grilling loop with a clean status message rather than treating the directive as the user's answer. Acceptance:

  • Pre-stage grilling under --unattended no longer treats the directive as an answer.
  • New unit test asserts early-exit message format.
  • Existing pre-stage grilling tests (non-unattended path) still pass. Verification: npm test -- featureCommand.

Group B — Bundled assets (parallel, no blockers — pure content authoring)

T-7: Create src/skills/bundled/roles/inquisitor-ux.role.md [S] [coder] [blocked_by: —]

FR/IR/NFR: FR-4, IR-4, NFR-8 Files: create:src/skills/bundled/roles/inquisitor-ux.role.mdDescription: YAML frontmatter name: inquisitor-ux, description, agent_type: explorer (read-only, NFR-8 enforced), version: 1. Body system prompt instructs: focus only on UX/user-journey angles; produce 3–8 questions; emit ONCE via report_to_coordinator with report_type: 'partial_result'; output payload schema {"questions":[{"text": string, "angle": "ux", "priority": 1-10}]} (DEC-7); no file writes; then exit. Acceptance:

  • SkillRegistry.getRole('inquisitor-ux') discoverable after rebuild (verified by T-11b).
  • Frontmatter parses cleanly via existing skills parser.
  • agent_type is explorer (read-only — NFR-8 cannot be subverted to coder). Verification: T-11b passes.

T-8: Create src/skills/bundled/roles/inquisitor-technical.role.md [S] [coder] [blocked_by: —]

FR/IR/NFR: FR-4, IR-4, NFR-8 Files: create:src/skills/bundled/roles/inquisitor-technical.role.mdDescription: Same shape as T-7; angle = technical / integration / data-model. Acceptance: Same as T-7; verified by T-11b. Verification: T-11b passes.

T-9: Create src/skills/bundled/roles/inquisitor-edge-cases.role.md [S] [coder] [blocked_by: —]

FR/IR/NFR: FR-4, IR-4, NFR-8 Files: create:src/skills/bundled/roles/inquisitor-edge-cases.role.mdDescription: Same shape as T-7; angle = edge-cases / failure-modes / hostile-input. Acceptance: Same as T-7; verified by T-11b. Verification: T-11b passes.

T-11: Create brainstorm.workflow.md + round.prompt.md + synthesize.prompt.md [M] [coder] [blocked_by: —]

FR/IR/NFR: IR-3, IR-6, FR-8, NFR-5, NFR-6, NFR-14 Files: create:src/skills/bundled/workflows/brainstorm.workflow.md, create:src/skills/bundled/workflows/brainstorm/round.prompt.md, create:src/skills/bundled/workflows/brainstorm/synthesize.prompt.mdDescription: Workflow frontmatter declares brainstorm metadata (name, description, coordinator role, preQuestions: ['What is the topic to brainstorm about?'] for standalone-launched-without-topic). Stage list is documentation only — the JS loop in T-10/T-15 drives execution (DEC-8/IR-6). round.prompt.md is the per-round template with ${topic}, ${round}, ${prior_qa}, ${angles} placeholders. synthesize.prompt.md instructs the coordinator to compose two artifact strings (narrative + machine-readable matching FR-14 schema). Acceptance:

  • Workflow file parses via existing skills parser (NFR-14 — covered by T-11a).
  • Both prompt files exist and are referenced from the workflow file.
  • No new WorkflowStage fields introduced (NFR-5). Verification: T-11a passes.

T-11a: Test src/skills/parser.brainstorm.test.ts [S] [tester] [blocked_by: T-11]

FR/IR/NFR: IR-3, NFR-14, NFR-15 Files: create:src/skills/parser.brainstorm.test.tsDescription: Parses brainstorm.workflow.md end-to-end via the existing skills parser. Asserts stage count, NFR-14 dual-prompt rule (per-stage .prompt.md exists), preQuestions block parses identically to feature-pipeline. Acceptance: Parses without warnings; dual-prompt rule asserted. Verification: npm test -- parser.brainstorm.

T-11b: Test src/skills/SkillRegistry.brainstorm.test.ts [S] [tester] [blocked_by: T-7, T-8, T-9]

FR/IR/NFR: IR-4, NFR-15 Files: create:src/skills/SkillRegistry.brainstorm.test.tsDescription: All 3 inquisitor roles discoverable via registry.getRole('inquisitor-ux'|'inquisitor-technical'|'inquisitor-edge-cases'). composeAgentPrompt('inquisitor-ux', []) succeeds and contains the role body. Acceptance: All 3 roles found; each composes without error. Verification: npm test -- SkillRegistry.brainstorm.


Group C — Core command impl (sequential — each builds on prior)

T-10: Scaffold src/commands/brainstormCommand.ts shell [M] [coder] [blocked_by: T-1, T-2, T-3, T-4, T-5, T-7, T-8, T-9, T-11]

FR/IR/NFR: IR-5, IR-6 Files: create:src/commands/brainstormCommand.tsDescription: Public surface per Phase-4 §6: createBrainstormCommand, parseBrainstormArgs, runBrainstormInline. Internal types: BrainstormArgs, Question, QAPair, InquisitorRecord, InquisitorAngle. Stubs for the major helpers (waitForInquisitors, dedupeAndPrioritize, runRoundWithInquisitors, runRoundWithCoordinator, synthesizeAndWrite, setTopology). Named exports only. .js extensions on relative imports. No logic yet — handler returns {ok:false, error:'not implemented'} to keep build green. Acceptance:

  • tsc --noEmit green.
  • File compiles, exports listed.
  • Branded AgentId type used (no plain string for agent IDs).
  • No TypeScript enums (string literal unions for InquisitorAngle, etc.). Verification: npm run build.

T-12: Implement parseBrainstormArgs [S] [coder] [blocked_by: T-10]

FR/IR/NFR: FR-1, FR-2, FR-3, FR-11, FR-13, NFR-9, DEC-13, OQ-2 Files: modify:src/commands/brainstormCommand.tsDescription: Parse [--rounds <n>] [--resume <slug>] [--agents <n>] [--unattended] "<topic>" from a raw arg string. Validate --rounds ∈ [1,10]; --agents ∈ {0,1,2,3} (default 3 per DEC-13); --unattended → return {error} per FR-11 with the actionable message verbatim; --resume → call validateSlug from T-3; missing topic (without --resume) → error. Sanitize topic via neutralizePromptInput/escapeXml (NFR-9) before storing. Acceptance:

  • Out-of-range --rounds rejected with actionable error.
  • --unattended returns exact FR-11 message: /brainstorm is interactive by design; --unattended is not supported. Omit the flag or use /feature --unattended for non-interactive planning.
  • --agents 4 rejected; --agents 0..3 accepted.
  • --resume bad-SLUG rejected via T-3 validator.
  • Topic neutralized (NFR-9) — verified by spy on neutralizePromptInput. Verification: T-21 covers.

T-13: Implement coordinator-tap + waitForInquisitors [M] [coder] [blocked_by: T-10, T-4]

FR/IR/NFR: DEC-2, IR-10, H-2 Files: modify:src/commands/brainstormCommand.tsDescription: Two helpers (~25 LOC each):

  1. Tap registration: installInquisitorTap(messageBus, inquisitorIds, accumulator): () => void — calls messageBus.subscribe('coordinator', handler). Handler filters env.message.type === 'text' && content.startsWith('[partial_result] ') && inquisitorIds.has(env.from), strips the prefix, calls tolerantJsonParse from T-1, merges into per-inquisitor Map<AgentId, InquisitorRecord> (concat questions on multi-envelope, not replace).
  2. Event wait: waitForInquisitors(stateStore, ids: Set<AgentId>, timeoutMs): Promise<void> — subscribes to agent.completed/agent.failed on stateStore for the diff set; resolves when all known IDs finished OR timeout fires. Includes ~500ms post-quiesce drain. Acceptance:
  • Tap registered synchronously (returns unsubscribe closure).
  • Multi-envelope inquisitor (streaming partial_results) MERGES question lists, does not replace.
  • Timeout returns cleanly (does not throw).
  • Inquisitors with no agent.completed after timeout get status: 'timeout' in their record. Verification: T-22 covers ordering; T-13 verification via T-22's mock-bus tap-before-spawn assertion.

T-14: Implement round-1 spawn with --agents <n> branching [M] [coder] [blocked_by: T-13]

FR/IR/NFR: FR-4, DEC-5, DEC-13, OQ-2, NFR-9 Files: modify:src/commands/brainstormCommand.tsDescription: Implement runRoundWithInquisitors(round, transcript, agentsN):

  1. Compute angles = ['ux','technical','edge-cases'].slice(0, agentsN) (OQ-2 degradation order: ux always kept).
  2. Snapshot existing agent IDs from stateStore.getState().agents.
  3. Install tap BEFORE sending spawn prompt (H-2 — verified in T-22).
  4. Send XML-tagged spawn prompt via coordinator.sendAndWait instructing coordinator to spawn_worker Nx in parallel with role: 'inquisitor-<angle>', agent_type: 'explorer'.
  5. Wait for new agent IDs (diff vs snapshot) whose role starts with inquisitor-.
  6. waitForInquisitors(diffIds, perRoundTimeoutMs).
  7. Drain (~500ms), then dispose tap.
  8. Branch: agentsN === 0 → skip spawn entirely; no tap; jump to runRoundWithCoordinator. Acceptance:
  • agentsN=0 does NOT install tap and does NOT call sendAndWait with a spawn prompt.
  • agentsN=1 spawns only ux; agentsN=2 spawns ux + technical; agentsN=3 all three.
  • Tap installed BEFORE the sendAndWait Promise resolves (regression-tested via spy in T-22).
  • Topic re-sanitized via neutralizePromptInput in spawn prompt (NFR-9). Verification: T-23 (n=0), T-24 (n=3) cover.

T-15: Implement round-loop + grilling + termination gate [M] [coder] [blocked_by: T-14]

FR/IR/NFR: FR-5, FR-6, FR-7, FR-12 (retired — see notes), FR-15, FR-23, IR-2, IR-6, NFR-21, SEC-2 Files: modify:src/commands/brainstormCommand.tsDescription: JS while-loop per Phase-4 §8:

  • Round 1: runRoundWithInquisitors (if agentsN>0) else runRoundWithCoordinator.
  • Rounds 2+: ALWAYS runRoundWithCoordinator (FR-12 retired in Phase 3 — no re-spawn each round in v1; --agents <n> covers the escape hatch). Coordinator follow-ups use sendAndWait with prior transcript wrapped via wrapUntrusted from src/utils/promptInjectionGuard.ts:78 (FR-23 / SEC-2).
  • dedupeAndPrioritize(questions, 8) — Jaccard ≥ 0.6 similarity; cap at 8 (FR-5).
  • For each prioritized question: ans = await coordinator.askUser({question: q.text}) (FR-6, IR-2, T-153 deterministic path). Cap answer length at NFR-21 8KB (truncate with elision marker …[truncated, see NDJSON transcript]).
  • FR-15 confidence heuristic: if ans.length < 10 OR /\b(maybe|probably|i think)\b/i.test(ans), ask coordinator.askUser({question: 'How confident? (certain/likely/guess)', choices: [...]}) and set confidence on the QAPair.
  • Push QAPair to transcript.
  • After round ≥ MIN_ROUNDS (=1): askUser({choices:['summarize','keep grilling']}) — match /summari[sz]e/i to exit (FR-7).
  • Loop also bounded by args.rounds cap (FR-13). Acceptance:
  • Round-2+ prompts contain <untrusted> wrapping around prior answers (asserted by T-26).
  • Dedupe cap = 8 (FR-5).
  • Confidence asked only on short/hedged answers (asserted by table-driven test in T-22).
  • Termination gate respects summarize and keep grilling per FR-7.
  • Answers >8KB truncated with elision marker (NFR-21). Verification: T-22, T-26. Notes: FR-12's --full-inquisitors-each-round flag is RETIRED per Phase-3 DEC-6 ("1 retired (FR-12)"); the --agents <n> escape hatch from DEC-13 supersedes it. Document retirement in CHANGELOG (T-35).

T-15a: Implement FR-25 threshold check with DEC-16 auto-degrade [S] [coder] [blocked_by: T-15]

FR/IR/NFR: FR-25, DEC-16, H-1 Files: modify:src/commands/brainstormCommand.tsDescription: After harvest, count inquisitors with status === 'success' && questions.length > 0. If zero, auto-degrade (do NOT hard-abort): log yellow warning "All inquisitors failed (statuses: ux=<s>, technical=<s>, edge-cases=<s>); degrading to coordinator-only grilling"; set local degraded = true; persist full raw bodies in the per-worker NDJSON file under <sessionDir>.workers/; continue to runRoundWithCoordinator for this round and subsequent rounds. Acceptance:

  • Zero parseable inquisitors → command CONTINUES (no abort).
  • Yellow warning text printed (via chalk).
  • NDJSON file written with raw bodies (verified via file existence + content check).
  • degraded flag persists for the rest of the session. Verification: T-25 covers.

T-16: Implement synthesis prompt + JS-side artifact write [M] [coder] [blocked_by: T-15]

FR/IR/NFR: FR-8, FR-9, FR-10, FR-14, IR-8, NFR-10, NFR-20, SEC-5 Files: modify:src/commands/brainstormCommand.tsDescription: One final sendAndWait sends synthesize.prompt.md (from T-11) interpolated with transcript + topic + slug, asking the coordinator to compose two markdown strings: (1) narrative 00-brainstorming.md (Vision / Where it Fits / Constraints / Per-Inquisitor Findings / Full Q&A Transcript / Assumptions / Open Questions per FR-8); (2) machine-readable 00-brainstorming.context.md matching FR-14 schema (use serializeBrainstormContext from T-30 if available, otherwise the prompt asks the LLM to produce schema-compliant YAML). JS writes BOTH via atomicWriteFile(path, content, {mode: 0o644}) — explicit mode override per SEC-5/NFR-20. Print FR-10 single-line success summary: ✓ Brainstorm complete: <N> questions across <M> rounds → .plans/<slug>/00-brainstorming.md. IR-8: prefix MUST be 00- for standalone (matches OQ-1 default). Acceptance:

  • Both files written with mode 0o644 (assert via fs.statSync(...).mode & 0o777 === 0o644 on POSIX; document Windows behavior).
  • Files use 00- prefix in standalone mode; 01- prefix when invoked via runBrainstormInline (T-20).
  • FR-10 success line printed exactly as specified.
  • IR-9: synthesis errors do NOT leave a half-written pair (atomicWriteFile + sentinel from T-17 enforce). Verification: T-22 covers; T-29 covers inline path.

T-17: Implement .brainstorm-complete sentinel + --resume reject [S] [coder] [blocked_by: T-16]

FR/IR/NFR: FR-3, FR-24, IR-9, EC-9 Files: modify:src/commands/brainstormCommand.tsDescription: After BOTH artifact writes from T-16 succeed, write .plans/<slug>/.brainstorm-complete (empty or ISO timestamp content). --resume <slug> logic: if sentinel exists, print artifact paths and exit idempotently (FR-3); if sentinel missing but plan dir exists, treat as "in progress" — discard and start fresh round 1 against same topic (per IR-9 v1 simplicity). Acceptance:

  • Sentinel written ONLY after both artifacts succeed.
  • --resume <existing-completed-slug> exits idempotently with paths printed.
  • --resume <missing-slug> errors with actionable list of available brainstorms.
  • --resume <in-progress-slug> discards partial state and starts fresh. Verification: T-22 covers; smoke in T-32.

Group D — Context utilities (sequential — runs parallel with Group C)

T-30: Create src/utils/brainstormContext.ts [M] [coder] [blocked_by: —]

FR/IR/NFR: FR-14, FR-16, FR-18, DEC-18, NFR-4 Files: create:src/utils/brainstormContext.ts, modify:src/utils/index.tsDescription: Implement BrainstormContext interface (FR-14 schema), CURRENT_SCHEMA_VERSION = 1, parseBrainstormContext(filePath): {ok, ctx} | {ok:false, error} (focused ~15-LOC YAML reader for the FR-14 schema — does NOT promote parseFrontmatter per DEC-18), serializeBrainstormContext(ctx): string, formatBrainstormDigest(ctx): string for /feature Phase-2 seed. FR-18: reject schema_version > 1 with actionable error. NFR-4: load latency target < 100ms. Acceptance:

  • parseBrainstormContext round-trips with serializeBrainstormContext for FR-14 sample.
  • schema_version: 2 returns {ok:false, error: 'Brainstorm artifact schema_version 2 is newer than this CLI supports (max 1). Upgrade agents-fleet or regenerate the brainstorm.'} (FR-18 verbatim).
  • Malformed YAML returns {ok:false, error}, does not throw.
  • No new js-yaml dep (NFR-4 implicit). Verification: T-30a.

T-30a: Test src/utils/brainstormContext.test.ts [S] [tester] [blocked_by: T-30]

FR/IR/NFR: FR-14, FR-18, NFR-14, NFR-15 Files: create:src/utils/brainstormContext.test.tsDescription: YAML round-trip including qa_pairs nested objects, schema_version=2 rejection (FR-18 verbatim message), malformed YAML graceful error, missing required field graceful error, digest formatting snapshot. Acceptance: ≥85% coverage of brainstormContext.ts. Verification: npm test -- brainstormContext.


Group E — Topology + IR-7

T-31: Implement topology save/restore in brainstormCommand outer try/finally [S] [coder] [blocked_by: T-15]

FR/IR/NFR: IR-7, DEC-17, H-4, H-6 Files: modify:src/commands/brainstormCommand.tsDescription: At entry: snapshot topoBefore = stateStore.getState().activeTopology; call stateStore.setState(p => ({...p, activeTopology: 'hub'}), {invalidates: ['meta']}). Outer finally: conditional restore per DEC-17 — only restore if stateStore.getState().activeTopology === 'hub' (i.e., the value we set is still there); skip restore otherwise to avoid clobbering a concurrent /loop-driven change (H-4/H-6). Acceptance:

  • Entry flips topology to hub.
  • Normal exit restores topoBefore.
  • If something else flips topology mid-run (e.g., concurrent /loop), finally does NOT clobber.
  • No state mutation in place (FleetStateStore conventions). Verification: T-27 covers.

Group F — Integration with /feature

T-18: Register /brainstorm in src/commands/registry.ts [S] [coder] [blocked_by: T-17]

FR/IR/NFR: FR-1, IR-5 Files: modify:src/commands/registry.tsDescription: Import createBrainstormCommand from brainstormCommand.js. Push into BUILTIN_COMMANDS array (:178-215). Add 'brainstorm' (+ aliases 'brain', 'bs') to Workflows category mapping (:53-58). Usage line: /brainstorm [--rounds <n>] [--resume <slug>] [--agents <n>] "<topic>". Acceptance:

  • /help lists /brainstorm under Workflows.
  • findCommand('brain') and findCommand('bs') resolve to the same command.
  • type: 'fleet' (per FR-1). Verification: T-32 smoke test.

T-19: Implement featureCommand --from-brainstorm <slug> [M] [coder] [blocked_by: T-30, T-3]

FR/IR/NFR: FR-16, FR-17, FR-18, NFR-13 Files: modify:src/commands/featureCommand.tsDescription: Add --from-brainstorm <slug> to parseFeatureArgs (:29-59). When present:

  1. validateSlug(slug) via T-3.
  2. resolveSafePlanDir(cwd, slug) (closes SEC-3 in this path too — FR-22 / OQ-4 audit).
  3. parseBrainstormContext(<dir>/00-brainstorming.context.md) via T-30.
  4. If file missing → FR-17 error listing available brainstorms (ls .plans/ filtered by presence of 00-brainstorming.context.md). If none, suggest /brainstorm "<topic>" first.
  5. If schema_version > 1 → FR-18 error verbatim.
  6. Write Phase-1 pointer stub at <featurePlanDir>/01-brainstorming.md with body > Brainstorm imported from .plans/<src>/00-brainstorming.md.
  7. Prepend formatBrainstormDigest(ctx) to the Phase-2 prompt.
  8. Mark Phase 1 complete; proceed normally. Acceptance:
  • Missing slug → FR-17 error with available list.
  • Newer schema → FR-18 error verbatim.
  • Digest prepended to Phase-2 prompt (asserted via spy/snapshot in T-28).
  • Pointer stub written; original 01-brainstorming.md content from /feature's Phase 1 NOT generated. Verification: T-28.

T-20: Implement featureCommand --deep-brainstorm [M] [coder] [blocked_by: T-10, T-16, T-17]

FR/IR/NFR: FR-19, DEC-12, H-3, OQ-1 Files: modify:src/commands/featureCommand.tsDescription: Add --deep-brainstorm to parseFeatureArgs. When present: call runBrainstormInline(context, feature, planDir, {agentsN, rounds}) (exported from brainstormCommand.ts per T-10). Inline run writes 01-brainstorming.md + 01-brainstorming.context.md in the feature slug dir (OQ-1 default — matches /feature numbering). Mark Phase 1 complete via the SAME mechanism the normal Phase-1 stage uses; skip the normal preQuestions Phase-1 entirely to avoid H-3 double-write. Proceed to Phase 2 with formatBrainstormDigest(ctx) prepended to the prompt. Acceptance:

  • --deep-brainstorm "topic" produces exactly ONE 01-brainstorming.md (H-3 mitigation; verified by T-29).
  • Mutual exclusion with --from-brainstorm enforced (error if both passed).
  • Phase 2 proceeds with digest prepended.
  • Inline call writes with 01- prefix (not 00-) per OQ-1. Verification: T-29. Notes: This is the H-3 mitigation crux. Verify ordering: brainstorm inline → mark phase1Completed=true → skip normal phase1 stage → run phase2.

Group G — Tests (parallel where possible)

T-21: brainstormCommand.test.ts arg parsing [M] [tester] [blocked_by: T-12]

FR/IR/NFR: FR-1, FR-11, FR-13, NFR-12, DEC-13 Files: create:src/commands/brainstormCommand.test.ts (arg-parse section) Description: Table-driven: FR-1 usage variants, FR-13 range guard (0, 1, 10, 11), FR-11 --unattended refusal verbatim message, --agents 0..3 accepted, --agents 4 rejected, --resume <valid-slug> accepted, --resume <invalid> rejected via T-3 validator, missing topic without --resume errors. Acceptance: Every flag branch covered. Verification: npm test -- brainstormCommand.

T-22: brainstormCommand.test.ts full loop with mocks [M] [tester] [blocked_by: T-17, T-31]

FR/IR/NFR: FR-5, FR-6, FR-7, FR-8, FR-10, FR-15, NFR-12 Files: modify:src/commands/brainstormCommand.test.tsDescription: Mocked coordinator + messageBus. Single-round happy path: spawn → harvest → dedupe → grill → synthesize → both artifacts written → sentinel written → success line printed. Summarize branch (FR-7). Keep-grilling branch runs another round. Tap-before-spawn ordering assertion via vi.spyOn (regression for H-2). FR-15 confidence asked on short answer. FR-10 success line exact match. Acceptance: All branches covered; H-2 ordering asserted. Verification: npm test -- brainstormCommand. Notes: Defensive crew-hijack pattern from researchCommand.test.ts:214-235.

T-23: brainstormCommand.test.ts --agents 0 path [S] [tester] [blocked_by: T-14]

FR/IR/NFR: DEC-13, OQ-2, NFR-12 Files: modify:src/commands/brainstormCommand.test.tsDescription: --agents 0: no worker spawn, no bus-tap registered, coordinator-only follow-ups from round 1. Assert via spies that messageBus.subscribe is NOT called and coordinator.sendAndWait is NOT called with a spawn-prompt pattern. Acceptance: Pure single-coord path verified. Verification: npm test -- brainstormCommand.

T-24: brainstormCommand.test.ts --agents 3 path [S] [tester] [blocked_by: T-14]

FR/IR/NFR: FR-4, DEC-13, OQ-2, NFR-12 Files: modify:src/commands/brainstormCommand.test.tsDescription: --agents 3 (default): spawns 3 inquisitors with correct role names, tap registered, harvest occurs, accumulator populated for all 3. Acceptance: All 3 angles spawned; accumulator has 3 records. Verification: npm test -- brainstormCommand.

T-25: brainstormCommand.test.ts FR-25 auto-degrade [S] [tester] [blocked_by: T-15a]

FR/IR/NFR: FR-25, DEC-16, NFR-12 Files: modify:src/commands/brainstormCommand.test.tsDescription: When ALL 3 mocked inquisitors return unparseable JSON (or timeout), command CONTINUES with coordinator-only grilling (does NOT abort). Yellow warning printed. NDJSON file written with raw bodies. Acceptance: Command exits with ok:true; warning text matches; NDJSON file exists. Verification: npm test -- brainstormCommand.

T-26: brainstormCommand.test.ts SEC-2 re-feed safety [S] [tester] [blocked_by: T-15]

FR/IR/NFR: FR-23, SEC-2, NFR-12 Files: modify:src/commands/brainstormCommand.test.tsDescription: Round-2+ prompt content captured via coordinator.sendAndWait spy contains <untrusted>...</untrusted> wrapping around all round-1 answers. Also assert synthesis prompt wraps user answers. Acceptance: Every re-feed boundary uses wrapUntrusted. Verification: npm test -- brainstormCommand.

T-27: brainstormCommand.test.ts topology save/restore [S] [tester] [blocked_by: T-31]

FR/IR/NFR: IR-7, DEC-17, H-4, H-6, NFR-12 Files: modify:src/commands/brainstormCommand.test.tsDescription: (a) activeTopology flipped to 'hub' on entry, restored on normal exit; (b) conditional restore — if mid-run an external mutator changes topology to 'mesh', the finally does NOT restore (current !== 'hub'). Acceptance: Both cases pass. Verification: npm test -- brainstormCommand.

T-28: featureCommand.test.ts --from-brainstorm [M] [tester] [blocked_by: T-19]

FR/IR/NFR: FR-16, FR-17, FR-18, NFR-13 Files: modify:src/commands/featureCommand.test.tsDescription: Missing-slug error contains available-brainstorms list (FR-17); when no brainstorms exist, suggests /brainstorm "<topic>". Schema-too-new error matches FR-18 verbatim. Pointer stub written. Digest prepended to Phase-2 prompt (snapshot or contains assertion). Acceptance: All FR-16/17/18 branches covered. Verification: npm test -- featureCommand.

T-29: featureCommand.test.ts --deep-brainstorm chain [M] [tester] [blocked_by: T-20]

FR/IR/NFR: FR-19, FR-20, IR-1, NFR-3, NFR-6, NFR-13, H-3 Files: modify:src/commands/featureCommand.test.tsDescription: (a) --deep-brainstorm "topic" produces exactly ONE 01-brainstorming.md (H-3 — no double-write); (b) runBrainstormInline called with correct args; (c) Phase 2 follows naturally with digest prepended; (d) IR-1 regression: plain /feature "topic" still produces the same 7 artifacts in the same order as before (NFR-6 — feature-pipeline.workflow.md still parses); (e) NFR-3 baseline: no >5% regression on plain /feature brainstorm phase (informal: existing test timing as proxy). Acceptance: All cases pass; H-3 single-write asserted. Verification: npm test -- featureCommand.

T-32: Integration / smoke test for /brainstorm registration [S] [tester] [blocked_by: T-18]

FR/IR/NFR: FR-1, FR-11, IR-5, NFR-15 Files: create:src/commands/brainstormCommand.smoke.test.ts (or extend existing CLI smoke) Description: Headless invocation of the CLI: assert /brainstorm is registered (appears in /help), accepts a topic, refuses --unattended with FR-11 verbatim message. No real coordinator spawn (use mock context). Acceptance: All three smoke checks pass. Verification: npm test -- brainstormCommand.smoke.


Group H — Documentation

T-33: Update docs/commands-manual.md with /brainstorm + flags [M] [coder] [blocked_by: T-18, T-19, T-20]

FR/IR/NFR: NFR-17, IR-8, Q-D4 (retired) Files: modify:docs/commands-manual.mdDescription: Add /brainstorm section with usage, flags (--rounds, --resume, --agents, refusal of --unattended), the 00- vs 01- prefix convention (IR-8) including sibling-slug discoverability note (Q-D4 was retired in Phase 4 — note inline that --deep-brainstorm writes inline 01- per OQ-1, NOT a sibling slug). Update /feature section with --from-brainstorm <slug> and --deep-brainstorm examples. Acceptance: Doc reads cleanly; all flags documented; both chain modes shown with end-to-end examples. Verification: Manual review.

T-34: One-line README mention + paragraph in FLEET.md/architecture.md [S] [coder] [blocked_by: T-18]

FR/IR/NFR: NFR-18, NFR-19 Files: modify:README.md, modify:FLEET.md OR .fleet/context/architecture.mdDescription: README feature-list bullet for /brainstorm. FLEET.md (or architecture.md) paragraph placing /brainstorm alongside /feature description. Acceptance: Both files updated; one-line README mention; ≥1 paragraph in architecture doc. Verification: Manual review.


Group I — Release prep

T-35: CHANGELOG [Unreleased] entry [S] [coder] [blocked_by: T-33, T-34]

FR/IR/NFR: — Files: modify:CHANGELOG.mdDescription: Describes /brainstorm v1 features, --deep-brainstorm inline composition decision (DEC-12), --agents <n> escape hatch (DEC-13), the security backports (FR-22 slug validation across plan-slug consumers; FR-21 askUser unattended directive), the retirement of FR-12 (--full-inquisitors-each-round superseded by --agents <n>), known limitations (OOS-2 → now FR-21; OOS-14 A/B benchmark left for telemetry collection — see OQ-8). Acceptance: Entry is user-facing prose, not internal jargon; security backports called out. Verification: Manual review.

T-36: Run full test suite + lint + build [S] [tester] [blocked_by: all impl + test tasks]

FR/IR/NFR: NFR-15, NFR-16 Files: — Description: npm test && npm run build. ESLint may error (known issue per CLAUDE.md — @typescript-eslint not installed) — record but do not block. Capture coverage % per file vs NFR-16 (statements 80 / branches 67 / functions 75 / lines 81; new code ≥ 85% per OQ-6). Acceptance:

  • Zero test failures.
  • Build green.
  • Coverage report attached; new code at or above 85% line coverage (or OQ-6 final answer).
  • No regression in existing coverage thresholds. Verification: CI green.

T-37: Code-review pass [M] [reviewer] [blocked_by: T-36]

FR/IR/NFR: NFR-7, NFR-8, NFR-11, FR-23 Files: — Description: Targeted review. Spot-check:

  • Tap registration ordering (T-13/T-14 — synchronous before sendAndWait; H-2 mitigation present).
  • FR-23 wrapUntrusted invoked at EVERY re-feed boundary (round 2+ prompt, synthesis prompt) — grep wrapUntrusted in brainstormCommand.ts.
  • No leaked subscribers — every subscribe paired with disposal in finally.
  • atomicWriteFile mode override {mode: 0o644} present at both artifact sites (NFR-10/NFR-20/SEC-5).
  • parseFrontmatter NOT promoted to public API (DEC-18).
  • No new MCP servers, no new external network calls, no new subprocess spawns (NFR-11).
  • Inquisitor roles all agent_type: explorer (NFR-8 — no path to coder).
  • Session schema not changed (NFR-7) — grep for session/persistence type modifications.
  • Branded AgentId/SessionId used (no plain string).
  • No TypeScript enums introduced.
  • No export default introduced.
  • .js extensions on relative imports. Acceptance: Review checklist signed off; no high-severity issues. Verification: PR approval.

3. Estimated effort summary

GroupTasksEst. effort (h)Parallelizable?
A Foundation9 (T-1, T-1a, T-2, T-2a, T-3, T-3a, T-4, T-5, T-5a, T-6)12–16Yes (all parallel)
B Bundled assets6 (T-7..T-9, T-11, T-11a, T-11b)6–8Yes (parallel)
C Core command8 (T-10, T-12..T-17, T-15a)18–24No (sequential within)
D Context utils2 (T-30, T-30a)4–5Parallel with C
E Topology1 (T-31)1–2After T-15
F /feature integration3 (T-18, T-19, T-20)6–8After C + D
G Tests10 (T-21..T-29, T-32)15–20Parallel within phase
H Docs2 (T-33, T-34)3–4Parallel with G
I Release3 (T-35, T-36, T-37)3–5After all
Total44~68–92 hoursCritical path: A → C → F → G → I ≈ 40h

Note: count is 44 (the foundation group expanded to 10 sub-items with T-6 added; aligns with "~30–40 tasks total, expand/contract OK").

4. Critical path (sequential)

T-3 → T-10 → T-12 → T-13 → T-14 → T-15 → T-15a → T-16 → T-17 → T-18 → T-19/T-20 (in parallel) → T-22 → T-36 → T-37 → ship.

Estimated critical-path effort: ~40 hours.

5. Risks visible at task-breakdown granularity (in addition to 04-technical-design.md §11)

#RiskMitigation
TB-1Single big PR vs split: 70–90h of work in 16–26 files. Reviewers will rubber-stamp or stall.Split into 3 PRs per §6 recommendation. PR-1 lands security backports independently; PR-2 lands feature; PR-3 polishes.
TB-2T-5 (FR-21 directive) may break an unaudited featureCommand test — grep audit in T-5 is mandatory but human-fallible; a non-obvious test may consume askUser return value indirectly.Run full npm test -- featureCommand immediately after T-5 lands; flag any new failures in PR-1 description and triage before merging.
TB-3Merge conflict with WIP abortCommand work currently in the repo (src/commands/abortCommand.ts exists per grep). If that branch lands first, registry.ts changes in T-18 may conflict.Coordinate landing order via repo maintainer; T-18 conflict resolution is mechanical (both just push into BUILTIN_COMMANDS).
TB-4FR-12 retirement may surprise users who expected --full-inquisitors-each-round from earlier docs/discussions.CHANGELOG T-35 explicitly notes retirement and points to --agents <n> as the equivalent escape hatch.
TB-5Bundled markdown asset cache must be rebuilt for new role files to appear in SkillRegistry. The npm run build step does the copy (per CLAUDE.md). T-11b will fail in npm run dev mode if cache is stale.Run npm run build before T-11b verification, or document the dev-mode caveat in PR description.

Single big PR vs split:

  • Option A — single PR: ~70–90 hours of work, 16–26 files, hard to review.
  • Option B — 3 PRs (recommended):
    • PR-1: Foundation + bundled assets + context util (Groups A + B + D) — 25–30h. Lands FR-21 fix, FR-22 backport (closes SEC-3 in /feature --resume independently), tolerantJson util, projectContext extract, planDir hardening, 3 role files, workflow + 2 prompts, brainstormContext util. Independently shippable as infrastructure + security improvements. Reviewers can focus on the security surface.
    • PR-2: Core command + integration (Groups C + E + F) — 25–35h. Lands brainstormCommand itself, --from-brainstorm, --deep-brainstorm. Builds on PR-1. User-visible feature.
    • PR-3: Tests + docs + release (Groups G + H + I) — 20–25h. Final polish, integration tests, docs, CHANGELOG, release prep. Most reviewable; mostly additive.

Rationale: PR-1 has standalone security value (closes SEC-3 in /feature); PR-2 lands the user-visible feature with safety nets already in place; PR-3 makes it shippable.

7. Open questions for Phase 6 (DoD)

  • ⚠️ OPEN QUESTION OQ-6: Coverage target — NFR-16 says ≥85% line coverage on new code. Confirm threshold before T-36 fails the bar. Default if unspecified: enforce 85% on new files (brainstormCommand.ts, tolerantJson.ts, projectContext.ts, brainstormContext.ts, planDir additions); existing-file additions follow repo defaults (80/67/75/81).
  • ⚠️ OPEN QUESTION OQ-7: Performance gate — NFR-1/NFR-2 were relaxed to "best-effort" in Phase 3. Should DoD include any latency assertion (e.g., "round-1 spawn-to-first-question ≤ 30s on idle fleet"), or omit? Default if unspecified: omit hard assertion; rely on informal observation during smoke tests.
  • ⚠️ OPEN QUESTION OQ-8: A/B telemetry — should v1 ship with a one-line intelDb.recordEvent('brainstorm.completed', {agentsN, roundsCompleted, questionsAsked, durationMs}) so OOS-14 has data on day 1? Tiny cost (~5 LOC); defers the v1.1 default-flip decision. Default if unspecified: include (add as a follow-up task during PR-3 if approved).

8. Requirement coverage matrix

Every FR/IR/NFR from 02-requirements.md (including Phase-3 amendments FR-21..FR-25 and NFR-20/21) is mapped below. Items flagged OOS/RETIRED are explicitly out-of-scope per Phase 3/4 decisions.

ReqTask(s)Notes
FR-1T-12, T-18, T-21command parsed + registered
FR-2T-12, T-3slug via planDir.ts (FR-22 backport)
FR-3T-12, T-17--resume honors sentinel
FR-4T-7, T-8, T-9, T-143 inquisitor roles spawned
FR-5T-15dedupe + prioritize ≤8
FR-6T-15one-at-a-time askUser
FR-7T-15, T-22termination gate
FR-8T-16, T-11two artifacts + synthesis prompt
FR-9T-16atomicWriteFile per file (sentinel restores pair guarantee — see FR-24)
FR-10T-16, T-22success line verbatim
FR-11T-12, T-21, T-32--unattended refusal verbatim
FR-12RETIRED in Phase 3 (DEC-6)Superseded by DEC-13 --agents <n>. T-35 documents retirement.
FR-13T-12, T-21--rounds range guard
FR-14T-30, T-30acontext.md schema v1
FR-15T-15, T-22confidence heuristic on short/hedged answers
FR-16T-19, T-28--from-brainstorm digest seed
FR-17T-19, T-28missing-slug actionable error
FR-18T-19, T-30, T-28schema-too-new error verbatim
FR-19T-20, T-29--deep-brainstorm inlined (DEC-12) writes 01-
FR-20T-29plain /feature unchanged (IR-1 regression)
FR-21T-5, T-5a, T-6rich directive (DEC-15 / OQ-5)
FR-22T-3, T-3a, + applied in T-12/T-19backport audited across plan-slug consumers (OQ-4)
FR-23T-15, T-26wrapUntrusted at every re-feed boundary
FR-24T-17, T-22sentinel after both artifacts
FR-25T-15a, T-25auto-degrade per DEC-16
IR-1T-29plain /feature regression
IR-2T-15reuses coordinator.askUser direct path
IR-3T-11, T-11aworkflow format reused
IR-4T-7, T-8, T-9, T-11brole format reused
IR-5T-18, T-32registry registration
IR-6T-15loop in command, not workflow runner
IR-7T-31, T-27hub topology save/restore
IR-8T-16, T-20, T-3300- standalone / 01- inlined
IR-9T-16, T-17atomic + sentinel == no partial artifacts on abort
IR-10T-13bus subscribe; no new event variants
NFR-1OQ-7 (deferred)best-effort; no hard gate in v1
NFR-2OQ-7 (deferred)best-effort; no hard gate in v1
NFR-3T-29plain /feature latency regression check (informal)
NFR-4T-30, T-30aparser <100ms (implicit via custom YAML reader)
NFR-5T-37no WorkflowStage breaking change (review checklist)
NFR-6T-29feature-pipeline still parses (existing tests pass)
NFR-7T-37session schema unchanged (review checklist)
NFR-8T-7, T-8, T-9, T-37inquisitors agent_type: explorer
NFR-9T-12, T-14topic sanitized with neutralizePromptInput
NFR-10T-16mode 0o644 (see also NFR-20)
NFR-11T-37no new MCP / network / subprocess (review checklist)
NFR-12T-21..T-27, T-22brainstormCommand unit tests
NFR-13T-28, T-29featureCommand integration tests
NFR-14T-11a, T-30aparser tests
NFR-15T-36full vitest suite green
NFR-16T-36, OQ-6coverage thresholds; new code ≥85%
NFR-17T-33docs/commands-manual.md
NFR-18T-34README mention
NFR-19T-34FLEET.md / architecture.md paragraph
NFR-20T-16explicit {mode: 0o644} (SEC-5)
NFR-21T-1, T-158KB cap on payloads + answers, with elision marker

All FRs (1–25), IRs (1–10), and NFRs (1–21) are accounted for. FR-12 explicitly retired (DEC-6); NFR-1/NFR-2 deferred to OQ-7 per Phase-3 relaxation.

9. Phase exit criteria

  • [x] Every FR / IR / NFR from 02-requirements.md (including FR-21..25, NFR-20..21 amendments) mapped to ≥1 task (see §8 matrix) or explicitly flagged RETIRED/DEFERRED.
  • [x] Critical path identified (T-3 → T-10 → T-12 → … → T-37, ~40h).
  • [x] Tasks atomic (each ≤4h, sized S/M; no L without justification) and tagged with blocked_by dependencies.
  • [x] Test tasks paired 1:1 with impl tasks where possible (T-1↔T-1a, T-2↔T-2a, T-3↔T-3a, T-5↔T-5a, T-30↔T-30a, T-12↔T-21, T-15↔T-22/T-26, T-15a↔T-25, T-31↔T-27, T-19↔T-28, T-20↔T-29).
  • [x] PR breakdown recommended (3 PRs per §6).
  • [x] 3 new open questions for DoD logged (OQ-6, OQ-7, OQ-8).
  • [x] Artifact written.