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-brainstormwrites01-brainstorming.mdinline in the feature slug; standalone/brainstormwrites00-brainstorming.mdin 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 ofAgentOutcomeExtractor+SteeringExtractorto 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 onlyfeatureCommand.ts:487-503known).
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-review2. 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 refactorAgentOutcomeExtractor/SteeringExtractorin 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). .jsextension 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-brainstormwill all call through it. Grep for otherpath.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 --noEmitgreen.- Existing tests that build a partial
MessageBusLikestub do not need to be updated. - No barrel changes (interface re-export already exists). Verification:
npm run build+npm test. Notes: Brainstorm guards onif (!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 onlyfeatureCommand.ts:487-503consumes 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
--unattendedno 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_typeisexplorer(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
WorkflowStagefields 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 --noEmitgreen.- File compiles, exports listed.
- Branded
AgentIdtype used (no plainstringfor 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
--roundsrejected with actionable error. --unattendedreturns 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 4rejected;--agents 0..3accepted.--resume bad-SLUGrejected 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):
- Tap registration:
installInquisitorTap(messageBus, inquisitorIds, accumulator): () => void— callsmessageBus.subscribe('coordinator', handler). Handler filtersenv.message.type === 'text' && content.startsWith('[partial_result] ') && inquisitorIds.has(env.from), strips the prefix, callstolerantJsonParsefrom T-1, merges into per-inquisitorMap<AgentId, InquisitorRecord>(concat questions on multi-envelope, not replace). - Event wait:
waitForInquisitors(stateStore, ids: Set<AgentId>, timeoutMs): Promise<void>— subscribes toagent.completed/agent.failedon 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.completedafter timeout getstatus: '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):
- Compute
angles = ['ux','technical','edge-cases'].slice(0, agentsN)(OQ-2 degradation order: ux always kept). - Snapshot existing agent IDs from
stateStore.getState().agents. - Install tap BEFORE sending spawn prompt (H-2 — verified in T-22).
- Send XML-tagged spawn prompt via
coordinator.sendAndWaitinstructing coordinator tospawn_workerNx in parallel withrole: 'inquisitor-<angle>',agent_type: 'explorer'. - Wait for new agent IDs (diff vs snapshot) whose role starts with
inquisitor-. waitForInquisitors(diffIds, perRoundTimeoutMs).- Drain (~500ms), then dispose tap.
- Branch:
agentsN === 0→ skip spawn entirely; no tap; jump torunRoundWithCoordinator. Acceptance:
agentsN=0does NOT install tap and does NOT callsendAndWaitwith a spawn prompt.agentsN=1spawns only ux;agentsN=2spawns ux + technical;agentsN=3all three.- Tap installed BEFORE the
sendAndWaitPromise resolves (regression-tested via spy in T-22). - Topic re-sanitized via
neutralizePromptInputin 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(ifagentsN>0) elserunRoundWithCoordinator. - 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 usesendAndWaitwith prior transcript wrapped viawrapUntrustedfromsrc/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 < 10OR/\b(maybe|probably|i think)\b/i.test(ans), askcoordinator.askUser({question: 'How confident? (certain/likely/guess)', choices: [...]})and setconfidenceon the QAPair. - Push
QAPairto transcript. - After round ≥
MIN_ROUNDS(=1):askUser({choices:['summarize','keep grilling']})— match/summari[sz]e/ito exit (FR-7). - Loop also bounded by
args.roundscap (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
summarizeandkeep grillingper FR-7. - Answers >8KB truncated with elision marker (NFR-21). Verification: T-22, T-26. Notes: FR-12's
--full-inquisitors-each-roundflag 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).
degradedflag 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 viafs.statSync(...).mode & 0o777 === 0o644on POSIX; document Windows behavior). - Files use
00-prefix in standalone mode;01-prefix when invoked viarunBrainstormInline(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:
parseBrainstormContextround-trips withserializeBrainstormContextfor FR-14 sample.schema_version: 2returns{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),finallydoes 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:
/helplists/brainstormunder Workflows.findCommand('brain')andfindCommand('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:
validateSlug(slug)via T-3.resolveSafePlanDir(cwd, slug)(closes SEC-3 in this path too — FR-22 / OQ-4 audit).parseBrainstormContext(<dir>/00-brainstorming.context.md)via T-30.- If file missing → FR-17 error listing available brainstorms (
ls .plans/filtered by presence of00-brainstorming.context.md). If none, suggest/brainstorm "<topic>"first. - If
schema_version > 1→ FR-18 error verbatim. - Write Phase-1 pointer stub at
<featurePlanDir>/01-brainstorming.mdwith body> Brainstorm imported from .plans/<src>/00-brainstorming.md. - Prepend
formatBrainstormDigest(ctx)to the Phase-2 prompt. - 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.mdcontent 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 ONE01-brainstorming.md(H-3 mitigation; verified by T-29).- Mutual exclusion with
--from-brainstormenforced (error if both passed). - Phase 2 proceeds with digest prepended.
- Inline call writes with
01-prefix (not00-) 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
wrapUntrustedinvoked at EVERY re-feed boundary (round 2+ prompt, synthesis prompt) — grepwrapUntrustedinbrainstormCommand.ts. - No leaked subscribers — every
subscribepaired with disposal infinally. atomicWriteFilemode override{mode: 0o644}present at both artifact sites (NFR-10/NFR-20/SEC-5).parseFrontmatterNOT 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/SessionIdused (no plainstring). - No TypeScript enums introduced.
- No
export defaultintroduced. .jsextensions on relative imports. Acceptance: Review checklist signed off; no high-severity issues. Verification: PR approval.
3. Estimated effort summary
| Group | Tasks | Est. effort (h) | Parallelizable? |
|---|---|---|---|
| A Foundation | 9 (T-1, T-1a, T-2, T-2a, T-3, T-3a, T-4, T-5, T-5a, T-6) | 12–16 | Yes (all parallel) |
| B Bundled assets | 6 (T-7..T-9, T-11, T-11a, T-11b) | 6–8 | Yes (parallel) |
| C Core command | 8 (T-10, T-12..T-17, T-15a) | 18–24 | No (sequential within) |
| D Context utils | 2 (T-30, T-30a) | 4–5 | Parallel with C |
| E Topology | 1 (T-31) | 1–2 | After T-15 |
| F /feature integration | 3 (T-18, T-19, T-20) | 6–8 | After C + D |
| G Tests | 10 (T-21..T-29, T-32) | 15–20 | Parallel within phase |
| H Docs | 2 (T-33, T-34) | 3–4 | Parallel with G |
| I Release | 3 (T-35, T-36, T-37) | 3–5 | After all |
| Total | 44 | ~68–92 hours | Critical 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)
| # | Risk | Mitigation |
|---|---|---|
| TB-1 | Single 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-2 | T-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-3 | Merge 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-4 | FR-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-5 | Bundled 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. |
6. Recommended PR breakdown
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 --resumeindependently),tolerantJsonutil,projectContextextract,planDirhardening, 3 role files, workflow + 2 prompts,brainstormContextutil. 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
brainstormCommanditself,--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.
- PR-1: Foundation + bundled assets + context util (Groups A + B + D) — 25–30h. Lands FR-21 fix, FR-22 backport (closes SEC-3 in
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.
| Req | Task(s) | Notes |
|---|---|---|
| FR-1 | T-12, T-18, T-21 | command parsed + registered |
| FR-2 | T-12, T-3 | slug via planDir.ts (FR-22 backport) |
| FR-3 | T-12, T-17 | --resume honors sentinel |
| FR-4 | T-7, T-8, T-9, T-14 | 3 inquisitor roles spawned |
| FR-5 | T-15 | dedupe + prioritize ≤8 |
| FR-6 | T-15 | one-at-a-time askUser |
| FR-7 | T-15, T-22 | termination gate |
| FR-8 | T-16, T-11 | two artifacts + synthesis prompt |
| FR-9 | T-16 | atomicWriteFile per file (sentinel restores pair guarantee — see FR-24) |
| FR-10 | T-16, T-22 | success line verbatim |
| FR-11 | T-12, T-21, T-32 | --unattended refusal verbatim |
| FR-12 | RETIRED in Phase 3 (DEC-6) | Superseded by DEC-13 --agents <n>. T-35 documents retirement. |
| FR-13 | T-12, T-21 | --rounds range guard |
| FR-14 | T-30, T-30a | context.md schema v1 |
| FR-15 | T-15, T-22 | confidence heuristic on short/hedged answers |
| FR-16 | T-19, T-28 | --from-brainstorm digest seed |
| FR-17 | T-19, T-28 | missing-slug actionable error |
| FR-18 | T-19, T-30, T-28 | schema-too-new error verbatim |
| FR-19 | T-20, T-29 | --deep-brainstorm inlined (DEC-12) writes 01- |
| FR-20 | T-29 | plain /feature unchanged (IR-1 regression) |
| FR-21 | T-5, T-5a, T-6 | rich directive (DEC-15 / OQ-5) |
| FR-22 | T-3, T-3a, + applied in T-12/T-19 | backport audited across plan-slug consumers (OQ-4) |
| FR-23 | T-15, T-26 | wrapUntrusted at every re-feed boundary |
| FR-24 | T-17, T-22 | sentinel after both artifacts |
| FR-25 | T-15a, T-25 | auto-degrade per DEC-16 |
| IR-1 | T-29 | plain /feature regression |
| IR-2 | T-15 | reuses coordinator.askUser direct path |
| IR-3 | T-11, T-11a | workflow format reused |
| IR-4 | T-7, T-8, T-9, T-11b | role format reused |
| IR-5 | T-18, T-32 | registry registration |
| IR-6 | T-15 | loop in command, not workflow runner |
| IR-7 | T-31, T-27 | hub topology save/restore |
| IR-8 | T-16, T-20, T-33 | 00- standalone / 01- inlined |
| IR-9 | T-16, T-17 | atomic + sentinel == no partial artifacts on abort |
| IR-10 | T-13 | bus subscribe; no new event variants |
| NFR-1 | OQ-7 (deferred) | best-effort; no hard gate in v1 |
| NFR-2 | OQ-7 (deferred) | best-effort; no hard gate in v1 |
| NFR-3 | T-29 | plain /feature latency regression check (informal) |
| NFR-4 | T-30, T-30a | parser <100ms (implicit via custom YAML reader) |
| NFR-5 | T-37 | no WorkflowStage breaking change (review checklist) |
| NFR-6 | T-29 | feature-pipeline still parses (existing tests pass) |
| NFR-7 | T-37 | session schema unchanged (review checklist) |
| NFR-8 | T-7, T-8, T-9, T-37 | inquisitors agent_type: explorer |
| NFR-9 | T-12, T-14 | topic sanitized with neutralizePromptInput |
| NFR-10 | T-16 | mode 0o644 (see also NFR-20) |
| NFR-11 | T-37 | no new MCP / network / subprocess (review checklist) |
| NFR-12 | T-21..T-27, T-22 | brainstormCommand unit tests |
| NFR-13 | T-28, T-29 | featureCommand integration tests |
| NFR-14 | T-11a, T-30a | parser tests |
| NFR-15 | T-36 | full vitest suite green |
| NFR-16 | T-36, OQ-6 | coverage thresholds; new code ≥85% |
| NFR-17 | T-33 | docs/commands-manual.md |
| NFR-18 | T-34 | README mention |
| NFR-19 | T-34 | FLEET.md / architecture.md paragraph |
| NFR-20 | T-16 | explicit {mode: 0o644} (SEC-5) |
| NFR-21 | T-1, T-15 | 8KB 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_bydependencies. - [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.