iriai-build 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/bin/iriai-build.js +78 -0
  2. package/bridge-v3.js +98 -0
  3. package/cli/bootstrap.js +83 -0
  4. package/cli/commands/implementation.js +64 -0
  5. package/cli/commands/index.js +46 -0
  6. package/cli/commands/launch.js +153 -0
  7. package/cli/commands/plan.js +117 -0
  8. package/cli/commands/setup.js +80 -0
  9. package/cli/commands/slack.js +97 -0
  10. package/cli/commands/transfer.js +111 -0
  11. package/cli/config.js +92 -0
  12. package/cli/display.js +121 -0
  13. package/cli/terminal-input.js +666 -0
  14. package/cli/wait.js +82 -0
  15. package/index.js +1488 -0
  16. package/lib/agent-process.js +170 -0
  17. package/lib/bridge-state.js +126 -0
  18. package/lib/constants.js +137 -0
  19. package/lib/health-monitor.js +113 -0
  20. package/lib/prompt-builder.js +565 -0
  21. package/lib/signal-watcher.js +215 -0
  22. package/lib/slack-helpers.js +224 -0
  23. package/lib/state-machines/feature-lead.js +408 -0
  24. package/lib/state-machines/operator-agent.js +173 -0
  25. package/lib/state-machines/planning-role.js +161 -0
  26. package/lib/state-machines/role-agent.js +186 -0
  27. package/lib/state-machines/team-orchestrator.js +160 -0
  28. package/package.json +31 -0
  29. package/v3/.handover-html-evidence.md +35 -0
  30. package/v3/KICKOFF-HTML-EVIDENCE.md +98 -0
  31. package/v3/PLAN-HTML-EVIDENCE-HARDENING.md +603 -0
  32. package/v3/adapters/desktop-adapter.js +78 -0
  33. package/v3/adapters/interface.js +146 -0
  34. package/v3/adapters/slack-adapter.js +608 -0
  35. package/v3/adapters/slack-helpers.js +179 -0
  36. package/v3/adapters/terminal-adapter.js +249 -0
  37. package/v3/agent-supervisor.js +320 -0
  38. package/v3/artifact-portal.js +1184 -0
  39. package/v3/bridge.db +0 -0
  40. package/v3/constants.js +170 -0
  41. package/v3/db.js +76 -0
  42. package/v3/file-io.js +216 -0
  43. package/v3/helpers.js +174 -0
  44. package/v3/operator.js +364 -0
  45. package/v3/orchestrator.js +2886 -0
  46. package/v3/plan-compiler.js +440 -0
  47. package/v3/prompt-builder.js +849 -0
  48. package/v3/queries.js +461 -0
  49. package/v3/recovery.js +508 -0
  50. package/v3/review-sessions.js +360 -0
  51. package/v3/roles/accessibility-auditor/CLAUDE.md +50 -0
  52. package/v3/roles/analytics-engineer/CLAUDE.md +40 -0
  53. package/v3/roles/architect/CLAUDE.md +809 -0
  54. package/v3/roles/backend-implementer/CLAUDE.md +97 -0
  55. package/v3/roles/code-reviewer/CLAUDE.md +89 -0
  56. package/v3/roles/database-implementer/CLAUDE.md +97 -0
  57. package/v3/roles/deployer/CLAUDE.md +42 -0
  58. package/v3/roles/designer/CLAUDE.md +386 -0
  59. package/v3/roles/documentation/CLAUDE.md +40 -0
  60. package/v3/roles/feature-lead/CLAUDE.md +233 -0
  61. package/v3/roles/frontend-implementer/CLAUDE.md +97 -0
  62. package/v3/roles/implementer/CLAUDE.md +97 -0
  63. package/v3/roles/integration-tester/CLAUDE.md +174 -0
  64. package/v3/roles/observability-engineer/CLAUDE.md +40 -0
  65. package/v3/roles/operator/CLAUDE.md +322 -0
  66. package/v3/roles/orchestrator/CLAUDE.md +288 -0
  67. package/v3/roles/package-implementer/CLAUDE.md +47 -0
  68. package/v3/roles/performance-analyst/CLAUDE.md +49 -0
  69. package/v3/roles/plan-compiler/CLAUDE.md +163 -0
  70. package/v3/roles/planning-lead/CLAUDE.md +41 -0
  71. package/v3/roles/pm/CLAUDE.md +806 -0
  72. package/v3/roles/regression-tester/CLAUDE.md +135 -0
  73. package/v3/roles/release-manager/CLAUDE.md +43 -0
  74. package/v3/roles/security-auditor/CLAUDE.md +90 -0
  75. package/v3/roles/smoke-tester/CLAUDE.md +97 -0
  76. package/v3/roles/test-author/CLAUDE.md +42 -0
  77. package/v3/roles/verifier/CLAUDE.md +90 -0
  78. package/v3/schema.sql +134 -0
  79. package/v3/slack-adapter.js +510 -0
  80. package/v3/slack-helpers.js +346 -0
@@ -0,0 +1,849 @@
1
+ // prompt-builder.js — All prompt templates ported from lib/prompt-builder.js.
2
+ // Unchanged prompts, just imports from v3/constants.js.
3
+
4
+ import { IRIAI_TEAM_DIR, IMPL_BASE } from "./constants.js";
5
+
6
+ // ─── Operator (Ephemeral) ────────────────────────────────────────────────────
7
+
8
+ /**
9
+ * Build prompt for ephemeral operator invocation.
10
+ * v3 difference: context comes from SQLite events, not conversation-history file.
11
+ */
12
+ export function buildOperatorPrompt({ featureName, operatorDir, flDir, featureDir, history, userMessage, activeAgents, pendingDecision, planDir, activePlanningRole, directoryMap }) {
13
+ return `You are the Operator for feature '${featureName}'.
14
+
15
+ ## Your Signal Directory
16
+ OPERATOR_DIR=${operatorDir}
17
+
18
+ ## Feature Lead Directory
19
+ FL_DIR=${flDir || "(not created yet — planning phase)"}
20
+
21
+ ## Feature Signal Tree Root
22
+ FEATURE_DIR=${featureDir}
23
+
24
+ ## Plan Directory
25
+ PLAN_DIR=${planDir || "(not set)"}
26
+
27
+ ## Active Planning Role
28
+ ACTIVE_PLANNING_ROLE=${activePlanningRole || "(none — implementation phase)"}
29
+
30
+ ## Directory Map (Codebase Topology)
31
+ DIRECTORY_MAP=${directoryMap}
32
+
33
+ ## Implementation Signal Base
34
+ IMPL_SIGNAL_BASE=${IMPL_BASE}
35
+
36
+ ## iriai-team Directory
37
+ IRIAI_TEAM_DIR=${IRIAI_TEAM_DIR}
38
+
39
+ ## Active Agents
40
+ ${activeAgents || "(none running)"}
41
+
42
+ ## Pending Decision
43
+ ${pendingDecision || "(no pending decisions)"}
44
+
45
+ ## Conversation History
46
+ ${history || "(no prior conversation)"}
47
+
48
+ ## Current Event
49
+ ${userMessage}
50
+
51
+ ---
52
+
53
+ Read your CLAUDE.md at ${operatorDir}/CLAUDE.md for your full role definition and capabilities.
54
+
55
+ Handle the current event above. You have the conversation history for context — refer to it to maintain continuity. Write your response to ${operatorDir}/.agent-response and exit.
56
+
57
+ To include file attachments (screenshots, GIFs, logs) in your response, embed markers in your .agent-response text:
58
+ [gif:/absolute/path/to/file.gif]
59
+ The bridge will upload each file as an attachment in the same thread.
60
+
61
+ To create a decision for the user, include a [DECISION] block in your response:
62
+ [DECISION]
63
+ id: <semantic-id>
64
+ type: approval|choice|confirmation
65
+ title: <title>
66
+ context: <brief context>
67
+ options:
68
+ - id: approve, label: Approve, style: primary
69
+ - id: reject, label: Reject, style: danger
70
+ [/DECISION]
71
+
72
+ To route a message to a specific agent, prefix with [ROUTE:<agent_key>]:
73
+ [ROUTE:fl-my-feature] <formatted message for the agent>`;
74
+ }
75
+
76
+ // ─── Operator Relay (Format Agent Output for User) ──────────────────────────
77
+
78
+ /**
79
+ * Build prompt for Operator relay invocation — formatting another agent's output for the user.
80
+ */
81
+ export function buildOperatorRelayPrompt({
82
+ featureName, operatorDir, featureDir, history, activeAgents, pendingDecision,
83
+ sourceAgent, eventHint, rawContent,
84
+ }) {
85
+ return `You are the Operator for feature '${featureName}'. You are the SOLE voice to the user. No other agent posts directly.
86
+
87
+ ## Your Task
88
+ Format the following agent output for human consumption. Then write your formatted message to ${operatorDir}/.agent-response and exit.
89
+
90
+ ## Source Agent
91
+ ${sourceAgent}
92
+
93
+ ## Event Type
94
+ ${eventHint}
95
+
96
+ ## Raw Agent Output
97
+ --- BEGIN RAW OUTPUT ---
98
+ ${rawContent}
99
+ --- END RAW OUTPUT ---
100
+
101
+ ## Active Agents
102
+ ${activeAgents || "(none running)"}
103
+
104
+ ## Pending Decision
105
+ ${pendingDecision || "(no pending decisions)"}
106
+
107
+ ## Conversation History
108
+ ${history || "(no prior conversation)"}
109
+
110
+ ---
111
+
112
+ ## Formatting Rules
113
+
114
+ 1. **NEVER invent information.** Only relay what the agent reported. Do not add analysis, opinions, or suggestions beyond what the agent wrote.
115
+
116
+ 2. **Preserve ALL evidence verbatim** — test output, code snippets, error logs, diff output, quotes, file paths, command output. Wrap in code blocks (\`\`\`). NEVER paraphrase, summarize, or omit evidence.
117
+
118
+ 3. **Keep your narrative ≤ 200 words.** The user reads on mobile. Be concise. Bold key points.
119
+
120
+ 4. **Attribute the source** — start your message with \`*[${sourceAgent}]*\` so the user knows which agent produced this.
121
+
122
+ 5. **For questions:** Bold the question. Present numbered options. Note any recommendation the agent made.
123
+
124
+ 6. **For gate evidence:** Present clearly with all evidence preserved. Include a \`[DECISION]\` block for approval:
125
+ \`\`\`
126
+ [DECISION]
127
+ id: gate-<N>-review
128
+ type: approval
129
+ title: Gate <N> Review
130
+ context: <brief summary>
131
+ options:
132
+ - id: approve, label: Approve, style: primary
133
+ - id: reject, label: Reject, style: danger
134
+ [/DECISION]
135
+ \`\`\`
136
+
137
+ 7. **Pass through \`[gif:path]\` and \`[evidence:path]\` markers unchanged** — the bridge handles file upload.
138
+
139
+ 8. **Do NOT do any work beyond formatting.** Read the agent's output, format it, write to \`.agent-response\`, exit.
140
+
141
+ ## Output
142
+ Write your formatted message to:
143
+ \`\`\`bash
144
+ cat > ${operatorDir}/.agent-response << 'MSG_EOF'
145
+ <your formatted message here>
146
+ MSG_EOF
147
+ \`\`\``;
148
+ }
149
+
150
+ // ─── Role Agent ──────────────────────────────────────────────────────────────
151
+
152
+ export function buildRolePrompt({ role, signalDir, task, recoveryContext }) {
153
+ let recovery = "";
154
+ if (recoveryContext?.type === "handover") {
155
+ recovery = `
156
+ ---
157
+ CONTEXT HANDOVER: Your previous session ran low on context and wrote a structured handover.
158
+ Read the handover below carefully — it contains your completed work, current state, and remaining tasks.
159
+ Pick up EXACTLY where the previous session left off. Do NOT redo completed work.
160
+ ---
161
+
162
+ HANDOVER FROM PREVIOUS SESSION:
163
+ ${recoveryContext.content}
164
+
165
+ ---
166
+ END OF HANDOVER
167
+ `;
168
+ } else if (recoveryContext?.type === "crash") {
169
+ recovery = `
170
+ ---
171
+ RECOVERY (retry ${recoveryContext.retryCount}): Your previous session crashed, possibly from context exhaustion.
172
+ Before starting work, check the current state of any files you would modify — some changes
173
+ may already be done. Use git status and git diff to see what has changed. Do NOT redo completed
174
+ work. Pick up from where the previous session left off.
175
+ ---
176
+
177
+ `;
178
+ }
179
+
180
+ return `You are the ${role}. Read your instructions at ${signalDir}/CLAUDE.md FIRST before starting any work.
181
+ ${recovery}
182
+ Your task is below. If it has YAML frontmatter (between --- delimiters), read ALL fields before starting work. Pay special attention to:
183
+ - scope.modify: ONLY modify these files
184
+ - acceptance.user_criteria: this defines 'done'
185
+ - counterexamples: do NOT do these things
186
+ - context_files: read these FIRST
187
+
188
+ ${task}
189
+
190
+ ---
191
+ IMPORTANT — OUTPUT AND COMPLETION:
192
+ 1. When your work is complete, write a summary to your output file. Use structured YAML frontmatter if your CLAUDE.md specifies it:
193
+ \`\`\`bash
194
+ cat > ${signalDir}/.output << 'OUTPUT_EOF'
195
+ <your structured output here — see CLAUDE.md for format>
196
+ OUTPUT_EOF
197
+ \`\`\`
198
+ 2. Do NOT write to HANDOVER.md — the orchestrator will consolidate all role outputs.
199
+ 3. Then signal completion as your very last action:
200
+ \`\`\`bash
201
+ echo DONE > ${signalDir}/.done
202
+ \`\`\`
203
+ Do NOT forget the .done signal. The orchestrator is waiting for it.`;
204
+ }
205
+
206
+ // ─── Team Orchestrator ───────────────────────────────────────────────────────
207
+
208
+ export function buildOrchestratorPrompt({ teamDir, orchDir, task, recoveryContext, teamReposDir }) {
209
+ let recovery = "";
210
+ if (recoveryContext) {
211
+ recovery = `
212
+ ---
213
+ RECOVERY (retry ${recoveryContext.retryCount}): Your previous session crashed, possibly from context exhaustion.
214
+ Before re-dispatching roles, check the current state:
215
+ - Read ${teamDir}/HANDOVER.md to understand what roles have already completed
216
+ - Read ${teamDir}/STEP-SUMMARY.md if it exists for step progress
217
+ - Check each role's signal dir for .done files — roles that already finished do NOT need re-dispatch
218
+ - Use git status and git diff in your worktree to see what code changes exist
219
+ Do NOT redo completed work. Only dispatch roles for remaining uncompleted steps.
220
+ ---
221
+ `;
222
+ }
223
+
224
+ return `You are a Team Orchestrator. Read your instructions at ${orchDir}/CLAUDE.md FIRST.
225
+
226
+ CRITICAL: You are a DISPATCHER. You must NEVER write code, edit source files, run tests, or fix bugs yourself. ALL implementation work is done by your role agents via .task files. If something needs fixing, re-dispatch the appropriate role — do NOT do it yourself.
227
+
228
+ Read your team config at ${teamDir}/.team-config to see which roles are available.
229
+ Read ${teamDir}/GATE-CONTEXT.md for your team's assignment and cross-team context.
230
+ ${recovery}${teamReposDir ? `
231
+ CRITICAL — TEAM REPO ISOLATION: Your team's repos are at ${teamReposDir}/.
232
+ Each repo is a git worktree on your team's branch. When writing .task files for your role agents,
233
+ ALL file paths MUST use your team's repo path (${teamReposDir}/<repo-name>/...), NOT the original
234
+ repo paths or the shared feature-level repos. This prevents your team's work from colliding with
235
+ other teams' branches.
236
+ ` : ""}
237
+ CRITICAL — SIGNAL FILE PATHS: Your signal directory is ${teamDir} (NOT your working directory).
238
+ Use these ABSOLUTE paths for ALL signal files:
239
+ - Dispatch to role: ${teamDir}/roles/<role>/.task
240
+ - Monitor role completion: ${teamDir}/roles/<role>/.done
241
+ - Read role output: ${teamDir}/roles/<role>/.output
242
+ - Gate evidence YAML: ${orchDir}/.gate-evidence.yaml
243
+ - Gate ready signal: ${orchDir}/.gate-ready
244
+ - Question escalation: ${orchDir}/.question
245
+ - Receive answer: ${orchDir}/.answer
246
+ - HANDOVER.md: ${teamDir}/HANDOVER.md
247
+ - STEP-SUMMARY.md: ${teamDir}/STEP-SUMMARY.md
248
+ Do NOT use relative paths for signal files — your working directory is a worktree, not the signal directory.
249
+
250
+ YOUR ASSIGNMENT:
251
+ ${task}
252
+
253
+ ---
254
+ IMPORTANT — GATE COMPLETION: When ALL steps are complete for this gate and your team's code reviewer has approved:
255
+ 1. First, write your gate evidence YAML (all journeys MUST have visual evidence):
256
+ \`\`\`bash
257
+ cat > ${orchDir}/.gate-evidence.yaml << 'EVIDENCE_EOF'
258
+ <your structured evidence YAML — see CLAUDE.md for schema>
259
+ EVIDENCE_EOF
260
+ \`\`\`
261
+ 2. Then signal gate ready:
262
+ \`\`\`bash
263
+ echo READY > ${orchDir}/.gate-ready
264
+ \`\`\`
265
+ Do NOT signal .gate-ready without .gate-evidence.yaml — the Feature Lead will auto-reject.
266
+ Do NOT proceed beyond this gate's assigned work. The Feature Lead will dispatch your next gate.`;
267
+ }
268
+
269
+ // ─── Artifact Summarizer ─────────────────────────────────────────────────────
270
+
271
+ export function buildArtifactSummarizerPrompt({ planDir, outputPath, artifacts }) {
272
+ const fileList = artifacts.map(a => `- ${a.name} (${a.sizeKB} KB, ${a.lines} lines)`).join("\n");
273
+
274
+ return `You are an artifact summarizer. Your ONLY job is to read planning artifacts and produce a
275
+ compressed summary that preserves all structural decisions but omits verbose details.
276
+
277
+ ## Plan Directory
278
+ ${planDir}
279
+
280
+ ## Artifacts to Summarize
281
+ ${fileList}
282
+
283
+ ## Instructions
284
+
285
+ 1. Read each artifact file listed above from ${planDir}/
286
+ 2. Write a single summary file to: ${outputPath}
287
+
288
+ ## Summary Format
289
+
290
+ The summary MUST follow this exact structure:
291
+
292
+ \`\`\`markdown
293
+ # Artifact Summary (auto-generated)
294
+
295
+ ## PRD Synopsis
296
+ [3-5 sentences: what the feature does, key requirements, user types]
297
+
298
+ ## Design Decisions Synopsis
299
+ [Key UX decisions: interaction patterns, navigation, visual approach — 10-15 bullet points max]
300
+
301
+ ### Component Hierarchy (preserved)
302
+ [Copy the component hierarchy trees verbatim — these are compact and critical]
303
+
304
+ ### User Flows (compressed)
305
+ [For each flow: 1-line summary + entry point + key states. NO step-by-step expansion]
306
+
307
+ ### States Table (preserved)
308
+ [Copy any state tables verbatim — they're already compact]
309
+
310
+ ### Test IDs (preserved)
311
+ [Copy data-testid tables verbatim — the Architect needs exact IDs]
312
+
313
+ ### Visual Specs Reference
314
+ [If terrain/building/animation specs exist: list their CATEGORIES and COUNT only.
315
+ e.g., "10 terrain types defined (bay, mesa, plateau...)" — do NOT reproduce the full specs.
316
+ Note the file path where full specs live.]
317
+
318
+ ## Data Artifacts
319
+ [For JSON files: describe the schema + record count. Do NOT reproduce contents.
320
+ e.g., "city-catalog.json: 100 cities, schema: { name, terrain, culture, districts[], ... }"]
321
+
322
+ ## Mockup
323
+ [Note mockup.html exists at path. List the screens/tabs it contains. 1 line each.]
324
+
325
+ ## Handover State
326
+ [If HANDOVER.md exists: what's done, what's remaining, any blockers]
327
+ \`\`\`
328
+
329
+ ## Rules
330
+ - Target size: 3-6 KB. If your summary exceeds 8 KB, you are including too much detail.
331
+ - PRESERVE: component hierarchies, state tables, test ID tables, flow names — these are contracts.
332
+ - COMPRESS: prose descriptions, visual specs, step-by-step flows, data contents.
333
+ - REFERENCE: large data files by path + schema, never inline their contents.
334
+ - Do NOT add analysis or suggestions. Just summarize what exists.
335
+
336
+ Write the summary file and exit immediately. Do not signal .done or .output.`;
337
+ }
338
+
339
+ // ─── Planning Role ───────────────────────────────────────────────────────────
340
+
341
+ export function buildPlanningRolePrompt({ task, signalDir, featureSlug }) {
342
+ return `${task}
343
+
344
+ ---
345
+
346
+ SLACK MODE ACTIVE: All user interaction happens via signal files in your working directory.
347
+ You are NOT in an interactive terminal. The user interacts via the bridge.
348
+
349
+ COMMUNICATION PROTOCOL:
350
+ 1. To send a message to the user: write your message to .agent-response
351
+ echo "your message here" > .agent-response
352
+ The bridge will post it to the user's thread and delete the file.
353
+
354
+ 2. To receive a message from the user: poll for .user-message
355
+ while [ ! -f .user-message ]; do sleep 5; done
356
+ Read the content, then delete it:
357
+ MSG=$(cat .user-message) && rm -f .user-message
358
+
359
+ 3. For questions needing user input: write to .agent-response with your question.
360
+ Include numbered options and bold the question. Keep messages under 300 words.
361
+ Then poll for .user-message for the user's reply.
362
+
363
+ 4. When you encounter a clarifying question from another agent or need to raise
364
+ a question yourself, write the FULL question verbatim to .agent-response.
365
+ Include: which role is asking, what phase/task it concerns, the options considered,
366
+ and any recommendation. The bridge will post it to the user with your role attribution.
367
+
368
+ 5. Format for mobile: the user reads on their phone. Under 300 words per message.
369
+ Use numbered options for choices. Bold key questions.
370
+
371
+ IMPORTANT: After each .agent-response write, wait 2 seconds before polling for
372
+ .user-message to give the bridge time to pick up and delete your response file.
373
+
374
+ ---
375
+
376
+ CONTEXT MANAGEMENT (MANDATORY):
377
+
378
+ You run inside a finite context window with NO automatic compaction. If you exhaust it,
379
+ you crash and lose all in-memory state. Proactively manage your context.
380
+
381
+ THRESHOLDS:
382
+ - After ~40 major tool uses → strongly consider a context refresh
383
+ - After ~60 major tool uses → you MUST refresh at the next natural boundary
384
+ - If you feel context is getting heavy → refresh immediately
385
+
386
+ Major tool uses include: reading files, writing files, running bash commands, each polling
387
+ iteration that produces output.
388
+
389
+ BEFORE REFRESHING:
390
+ 1. Write your progress, current state, and remaining tasks to ${signalDir}/.handover
391
+ 2. Save any partial artifacts to $PLAN_DIR/ (even if incomplete, mark them as WIP)
392
+ 3. Then signal refresh:
393
+ echo REFRESH > ${signalDir}/.needs-restart
394
+
395
+ The bridge will restart you with a fresh context. Your new session will be told to read
396
+ .handover to resume where you left off.
397
+
398
+ IMPORTANT: Reference artifacts by file path. Do NOT read large files unless necessary.
399
+ When the PRD exists at $PLAN_DIR/prd.md, reference it by path — it is never embedded in
400
+ your prompt. When context.md or design-decisions.md exist, read them from disk as needed.
401
+
402
+ EMERGENCY REFRESH:
403
+ If context is critically low:
404
+ 1. Write MINIMAL but complete .handover with current state and remaining work
405
+ 2. Signal immediately: echo REFRESH > ${signalDir}/.needs-restart
406
+
407
+ ---
408
+
409
+ Your signal directory: ${signalDir}
410
+ Feature: ${featureSlug || "unknown"}
411
+
412
+ When done, signal completion as usual:
413
+ echo "DONE" > .done
414
+ echo "<summary>" > .output`;
415
+ }
416
+
417
+ // ─── Feature Lead: Shared Prompt Fragments ───────────────────────────────────
418
+
419
+ function buildCommInstructions(featureLeadDir) {
420
+ return `
421
+ ## BRIDGE MODE ACTIVE
422
+
423
+ All user interaction happens via signal files. You are NOT in an interactive terminal.
424
+
425
+ COMMUNICATION PROTOCOL:
426
+ 1. To send a message to the user: write to ${featureLeadDir}/.agent-response
427
+ \`\`\`bash
428
+ cat > ${featureLeadDir}/.agent-response << 'MSG_EOF'
429
+ Your message here
430
+ MSG_EOF
431
+ \`\`\`
432
+ The bridge posts it to the user and deletes the file.
433
+
434
+ 2. To receive a message from the user: poll for .user-message
435
+ \`\`\`bash
436
+ while [ ! -f ${featureLeadDir}/.user-message ]; do sleep 5; done
437
+ MSG=$(cat ${featureLeadDir}/.user-message) && rm -f ${featureLeadDir}/.user-message
438
+ \`\`\`
439
+
440
+ 3. For questions needing user input: write to .agent-response with your question.
441
+ Include numbered options and bold the question. Then poll for .user-message.
442
+
443
+ 4. Gate approval: write gate evidence to .agent-response. The bridge posts it with
444
+ approval buttons. The user's response arrives as .user-message.
445
+
446
+ 5. Format concisely: under 300 words per message. Numbered options. Bold key questions.
447
+
448
+ 6. After writing .agent-response, wait 2 seconds before polling for .user-message.
449
+
450
+ 7. On startup, introduce yourself by writing to .agent-response:
451
+ 'Feature Lead online for <feature>. Reading plan and preparing dispatch...'
452
+ `;
453
+ }
454
+
455
+ function buildMonitorInstructions(numTeams, teamSignalBase, featureLeadDir, featureReviewDir) {
456
+ return `
457
+ ## Monitor Teams Until All Are Gate-Ready
458
+
459
+ After dispatching, you MUST stay active and monitor teams continuously. Poll every 75 seconds:
460
+ \`\`\`bash
461
+ # Poll loop — run this repeatedly
462
+ for i in $(seq 1 ${numTeams}); do
463
+ orch="${teamSignalBase}/team-$i/orchestrator"
464
+ [ -f "$orch/.gate-ready" ] && echo "READY: Team $i"
465
+ [ -f "$orch/.question" ] && echo "QUESTION: Team $i"
466
+ [ -f "$orch/.crashed" ] && echo "CRASHED: Team $i"
467
+ done
468
+ # Also check for user messages
469
+ if [ -f "${featureLeadDir}/.user-message" ]; then
470
+ echo "USER MESSAGE: $(cat ${featureLeadDir}/.user-message)"
471
+ rm -f "${featureLeadDir}/.user-message"
472
+ fi
473
+ \`\`\`
474
+
475
+ While monitoring:
476
+ - When a team signals .gate-ready: narrate it to the user using the GATE-READY format from your CLAUDE.md. Read the team's HANDOVER.md for a summary.
477
+ - When a team has a .question: handle it per the Question Handling section (answer or escalate to user)
478
+ - When a team .crashed: handle per Failure Recovery (read HANDOVER.md, re-dispatch with recovery .task)
479
+ - When a USER MESSAGE appears: read it, respond via .agent-response (status update, answer question, etc.), then continue monitoring
480
+ - Keep polling until ALL ${numTeams} teams have signaled .gate-ready
481
+ - Do NOT exit or signal .phase-done until all teams are ready
482
+
483
+ ### Health Monitor
484
+ If a team has been running for > 20 minutes with no new .done signals from roles, or all roles
485
+ show .done but no .gate-ready appears within 10 minutes, dispatch the health-monitor:
486
+ \`\`\`bash
487
+ cat > ${featureReviewDir}/health-monitor/.task << 'TASK_EOF'
488
+ Run a full health check across all teams.
489
+ Specific concern: [describe what you observe]
490
+ Teams to check: [all / specific team numbers]
491
+ TASK_EOF
492
+ \`\`\`
493
+ Then poll for feature-review/health-monitor/.done, read its .output, and act on recommendations.`;
494
+ }
495
+
496
+ function buildGateReviewInstructions(featureLeadDir, featureReviewDir, featureStatusFile, dashboardFile) {
497
+ return `
498
+ ## Gate Review (once all teams are gate-ready)
499
+
500
+ Follow the Gate Evidence Document Protocol from your CLAUDE.md. The ordered steps below ensure
501
+ adversarial cross-check happens LAST (after all review agents have completed):
502
+
503
+ 1. **Read team evidence** — Read \`.gate-evidence.yaml\` from each team's orchestrator signal dir.
504
+ If any team lacks \`.gate-evidence.yaml\`, REJECT the gate immediately.
505
+ 2. **Push team branches** and create PRs (team branch → integration branch) per repo using gh CLI
506
+ 3. **Dispatch feature-level review agents** by writing .task files:
507
+ \`\`\`bash
508
+ PREVIEW_ENV=""
509
+ if [ -f "${featureLeadDir}/preview-env.json" ]; then
510
+ PREVIEW_ENV="Preview environment: ${featureLeadDir}/preview-env.json"
511
+ fi
512
+ cat > ${featureReviewDir}/integration-tester/.task << TASK_EOF
513
+ <your review task here>
514
+ \$PREVIEW_ENV
515
+ TASK_EOF
516
+ cat > ${featureReviewDir}/code-reviewer/.task << 'TASK_EOF'
517
+ <your review task here>
518
+ TASK_EOF
519
+ cat > ${featureReviewDir}/security-auditor/.task << 'TASK_EOF'
520
+ <your review task here>
521
+ TASK_EOF
522
+ \`\`\`
523
+ 4. **Wait for all review agents** to complete (.done files) — poll every 75s:
524
+ \`\`\`bash
525
+ while [ ! -f ${featureReviewDir}/integration-tester/.done ] || \\
526
+ [ ! -f ${featureReviewDir}/code-reviewer/.done ] || \\
527
+ [ ! -f ${featureReviewDir}/security-auditor/.done ]; do
528
+ sleep 75
529
+ done
530
+ \`\`\`
531
+ 5. **Adversarial cross-check** (FINAL step before user sees it):
532
+ - Cross-check evidence across ALL teams for inconsistencies
533
+ - Call \`get_screenshots\` for critical journeys and independently verify orchestrator claims
534
+ - Review feature-level review agent outputs
535
+ - If discrepancies found → REJECT gate, do NOT escalate to user
536
+ 6. **Merge evidence** — Combine all team YAMLs + feature-level review outputs into:
537
+ \`\`\`bash
538
+ cat > ${featureLeadDir}/.gate-evidence.yaml << 'EVIDENCE_EOF'
539
+ <merged evidence YAML>
540
+ EVIDENCE_EOF
541
+ \`\`\`
542
+ Include: \`coverage_matrix\`, \`deviations\`, \`self_reported_risks\` (aggregated from all teams)
543
+ Include: \`reviewer_comments\` with your FL assessment
544
+ Include: \`cross_team_surface\` (APIs, contracts, shared state)
545
+ 7. **Compile feature gate HTML** — Call \`compile_gate_evidence\` MCP tool:
546
+ - \`evidence_yaml_path\`: ${featureLeadDir}/.gate-evidence.yaml
547
+ - \`output_html_path\`: ${featureLeadDir}/.gate-evidence.html
548
+ - \`doc_type\`: \`"feature"\`
549
+ - \`team_html_paths\`: list of \`{ team_num, html_path }\` for each team's gate HTML
550
+ - If tool returns ERROR → re-dispatch affected verification role → retry from step 4
551
+ - Do NOT proceed until \`compile_gate_evidence\` succeeds
552
+ 8. **Post feature gate HTML** to .agent-response — the HTML IS the message:
553
+ - \`[evidence:${featureLeadDir}/.gate-evidence.html]\` marker
554
+ - \`[DECISION]\` block with approve/reject buttons
555
+ - No text summary needed — the HTML contains everything
556
+ 9. **WAIT** for the user to approve or reject the gate
557
+ 10. If approved: merge PRs, tag gate checkpoint, update worktrees, clear .gate-ready files
558
+ 11. Update ${featureStatusFile} and ${dashboardFile} with gate results
559
+ 12. If this is the FINAL gate: create PRs to main, generate RETROSPECTIVE.md, cleanup, then signal:
560
+ \`\`\`bash
561
+ echo COMPLETE > ${featureLeadDir}/.feature-complete
562
+ \`\`\`
563
+
564
+ IMPORTANT: Do NOT dispatch the next gate yourself. After approval, just merge, clear signals,
565
+ update status files, and signal phase-done. A new session will handle the next gate dispatch.
566
+
567
+ When the gate is approved and merged (or final gate complete):
568
+ \`\`\`bash
569
+ echo GATE_HANDLED > ${featureLeadDir}/.phase-done
570
+ \`\`\``;
571
+ }
572
+
573
+ function buildContextManagementInstructions(featureLeadDir, dashboardLog, featureStatusFile, dashboardFile) {
574
+ return `
575
+ ## Context Management (MANDATORY)
576
+
577
+ You run inside a finite context window with NO automatic compaction. If you exhaust it, you crash —
578
+ and ANY state held only in your conversation memory is PERMANENTLY LOST. You must proactively manage
579
+ your context to ensure you NEVER crash before checkpointing.
580
+
581
+ ### Self-Monitoring
582
+
583
+ Track your context usage by counting major operations. Each of these costs significant context:
584
+ - Reading a file (especially large files like implementation plans or HANDOVER.md)
585
+ - Writing a task file, GATE-CONTEXT.md, or status update
586
+ - Running a bash command and processing its output
587
+ - Each polling iteration that produces output
588
+
589
+ **Thresholds:**
590
+ - After ~40 major tool uses in a single session → strongly consider a context refresh
591
+ - After ~60 major tool uses → you MUST refresh at the next natural boundary
592
+ - If you feel the conversation is getting long or you are losing track of details → refresh immediately
593
+
594
+ ### When to Refresh
595
+
596
+ Trigger a context refresh at any natural boundary:
597
+ - After dispatching a gate (all .task files written, status updated)
598
+ - After completing gate review and getting user approval (after merge + cleanup)
599
+ - After handling 3+ questions or crashes in one session
600
+ - After a long monitoring stretch with many poll cycles
601
+ - Whenever you sense your context is getting heavy — trust your judgment
602
+
603
+ ### Pre-Refresh Checklist (MANDATORY — NEVER SKIP)
604
+
605
+ Before writing the context refresh signal, you MUST update ALL of the following.
606
+ Skipping any of these means permanent data loss:
607
+
608
+ 1. **FEATURE-STATUS.md** (at ${featureStatusFile}) — Update with:
609
+ - Current phase and gate number
610
+ - All team statuses (which are ready, working, crashed)
611
+ - Decisions you made this session (question answers, contract changes)
612
+ - What you were doing when you decided to refresh
613
+ - What needs to happen next (explicit next steps for the fresh session)
614
+
615
+ 2. **DASHBOARD.md** (at ${dashboardFile}) — Rebuild with latest team status, gate progress, open questions
616
+
617
+ 3. **.dashboard-log** — Append a refresh event:
618
+ \`\`\`bash
619
+ echo "$(date +%H:%M:%S) | feature-lead | context-refresh" >> ${dashboardLog}
620
+ \`\`\`
621
+
622
+ 4. **GATE-CONTEXT.md (all teams)** — Complete or note which teams still need context
623
+
624
+ 5. **Cross-team contracts** — Record any established/modified contracts in FEATURE-STATUS.md (at ${featureStatusFile})
625
+
626
+ 6. **Open questions** — Verify .answer files written, log decision rationale
627
+
628
+ ### How to Refresh
629
+
630
+ After completing the full checklist above:
631
+ \`\`\`bash
632
+ echo REFRESH > ${featureLeadDir}/.context-refresh
633
+ \`\`\`
634
+
635
+ ### Emergency Refresh
636
+
637
+ If context is critically low:
638
+ 1. Write MINIMAL but complete FEATURE-STATUS.md update (at ${featureStatusFile})
639
+ 2. Signal immediately:
640
+ \`\`\`bash
641
+ echo REFRESH > ${featureLeadDir}/.context-refresh
642
+ \`\`\``;
643
+ }
644
+
645
+ // ─── Feature Lead: Init Prompt ───────────────────────────────────────────────
646
+
647
+ export function buildFeatureLeadInitPrompt({
648
+ featureName, numTeams, teamType, teamSignalBase,
649
+ planReadInstruction, featureLeadDir, featureReviewDir, dashboardLog,
650
+ featureDir, teamWorktreeBase,
651
+ }) {
652
+ const featureStatusFile = `${featureDir}/FEATURE-STATUS.md`;
653
+ const dashboardFile = `${featureDir}/DASHBOARD.md`;
654
+ const monitor = buildMonitorInstructions(numTeams, teamSignalBase, featureLeadDir, featureReviewDir);
655
+ const gateReview = buildGateReviewInstructions(featureLeadDir, featureReviewDir, featureStatusFile, dashboardFile);
656
+ const contextMgmt = buildContextManagementInstructions(featureLeadDir, dashboardLog, featureStatusFile, dashboardFile);
657
+ const slackComm = buildCommInstructions(featureLeadDir);
658
+
659
+ return `You are the Feature Lead for '${featureName}' with ${numTeams} teams (type: ${teamType}).
660
+
661
+ Read your instructions at ~/src/iriai/iriai-team/roles-v2/feature-lead/CLAUDE.md.
662
+ Read the feature status at ${featureStatusFile}.
663
+ ${planReadInstruction}
664
+
665
+ ## Dispatch First Gate
666
+
667
+ 1. Read and understand the full implementation plan
668
+ 2. Read ${featureStatusFile} — if a gate is already complete/approved, dispatch the NEXT gate (not Gate 1)
669
+ 3. Identify the gate boundaries in the plan
670
+ 4. Partition the current gate's work across ${numTeams} teams
671
+ 4. Write GATE-CONTEXT.md for each team (at ${teamSignalBase}/team-N/GATE-CONTEXT.md)
672
+ 5. Write team manifests if not present (at ${teamSignalBase}/team-N/manifest.yaml)
673
+ 6. Dispatch to each team orchestrator by writing .task files (at ${teamSignalBase}/team-N/orchestrator/.task)
674
+ 7. Update ${featureStatusFile} with gate 1 status
675
+ 8. Update ${dashboardFile}
676
+ ${teamWorktreeBase ? `
677
+ ## CRITICAL — Per-Team Repo Isolation
678
+
679
+ Each team has its own isolated git worktree for every repo, on its own branch:
680
+ - Team N's repos: ${teamWorktreeBase}/team-N/repos/<repo-name>/
681
+ - Team N's branch: feature/${featureName}/team-N
682
+
683
+ When writing GATE-CONTEXT.md and .task files for each team, ALL file paths MUST reference
684
+ the team's own repo worktree path. For example, if repo "my-backend" exists:
685
+ - Team 1: ${teamWorktreeBase}/team-1/repos/my-backend/
686
+ - Team 2: ${teamWorktreeBase}/team-2/repos/my-backend/
687
+
688
+ NEVER use the original repo path or the shared feature-level repos path in task files.
689
+ Each team must only modify files in its own worktree to prevent branch collisions.
690
+ ` : ""}
691
+ ${monitor}
692
+ ${gateReview}
693
+ ${contextMgmt}
694
+ ${slackComm}`;
695
+ }
696
+
697
+ // ─── Feature Lead: Refresh/Continue Prompt ───────────────────────────────────
698
+
699
+ export function buildFeatureLeadRefreshPrompt({
700
+ featureName, numTeams, teamSignalBase,
701
+ planReadInstruction, featureLeadDir, featureReviewDir, dashboardLog,
702
+ gateEvidenceTs, featureDir, teamWorktreeBase,
703
+ }) {
704
+ const featureStatusFile = `${featureDir}/FEATURE-STATUS.md`;
705
+ const dashboardFile = `${featureDir}/DASHBOARD.md`;
706
+ const monitor = buildMonitorInstructions(numTeams, teamSignalBase, featureLeadDir, featureReviewDir);
707
+ const gateReview = buildGateReviewInstructions(featureLeadDir, featureReviewDir, featureStatusFile, dashboardFile);
708
+ const contextMgmt = buildContextManagementInstructions(featureLeadDir, dashboardLog, featureStatusFile, dashboardFile);
709
+ const slackComm = buildCommInstructions(featureLeadDir);
710
+
711
+ const gateDedup = gateEvidenceTs
712
+ ? `\nIMPORTANT: Gate evidence was ALREADY posted to the user (ref: ${gateEvidenceTs}).\nDo NOT re-post gate evidence. You are waiting for the user's GATE APPROVED / GATE REJECTED response.\nPoll for .user-message, do not repost.\n`
713
+ : "";
714
+
715
+ return `You are the Feature Lead for '${featureName}' with ${numTeams} teams.
716
+
717
+ Read your instructions at ~/src/iriai/iriai-team/roles-v2/feature-lead/CLAUDE.md.
718
+
719
+ CONTEXT REFRESH: Your previous session requested a context refresh. All state has been
720
+ persisted to disk. Read these files to restore your state:
721
+
722
+ 1. ${featureStatusFile} — your persistent memory (source of truth)
723
+ 2. ${dashboardFile} — current team status
724
+ 3. Each team's GATE-CONTEXT.md at ${teamSignalBase}/team-N/GATE-CONTEXT.md
725
+
726
+ Check FEATURE-STATUS.md (at ${featureStatusFile}) for 'what was in progress' and 'next steps' to understand
727
+ exactly where to resume. Do not re-do completed work.
728
+ ${gateDedup}
729
+ ${planReadInstruction}
730
+ ${teamWorktreeBase ? `
731
+ ## Per-Team Repo Isolation
732
+
733
+ Each team has its own isolated git worktree on its own branch:
734
+ - Team N's repos: ${teamWorktreeBase}/team-N/repos/<repo-name>/
735
+ - Team N's branch: feature/${featureName}/team-N
736
+
737
+ ALL file paths in GATE-CONTEXT.md and .task files MUST use team-specific repo paths.
738
+ ` : ""}
739
+ ${monitor}
740
+ ${gateReview}
741
+ ${contextMgmt}
742
+ ${slackComm}`;
743
+ }
744
+
745
+ // ─── Feature Lead: Trigger-Based Phase Prompt ────────────────────────────────
746
+
747
+ export function buildFeatureLeadTriggerPrompt({
748
+ featureName, numTeams, trigger, teamSignalBase,
749
+ featureLeadDir, featureReviewDir, dashboardLog,
750
+ questionTeams, crashedTeams, recoveryContext,
751
+ featureDir, teamWorktreeBase,
752
+ }) {
753
+ const featureStatusFile = `${featureDir}/FEATURE-STATUS.md`;
754
+ const dashboardFile = `${featureDir}/DASHBOARD.md`;
755
+ const monitor = buildMonitorInstructions(numTeams, teamSignalBase, featureLeadDir, featureReviewDir);
756
+ const gateReview = buildGateReviewInstructions(featureLeadDir, featureReviewDir, featureStatusFile, dashboardFile);
757
+ const contextMgmt = buildContextManagementInstructions(featureLeadDir, dashboardLog, featureStatusFile, dashboardFile);
758
+ const slackComm = buildCommInstructions(featureLeadDir);
759
+
760
+ const teamRepoNote = teamWorktreeBase ? `
761
+ Per-team repo worktrees: ${teamWorktreeBase}/team-N/repos/<repo-name>/ (branch: feature/${featureName}/team-N).
762
+ ALL task file paths MUST use team-specific repo paths to prevent branch collisions.
763
+ ` : "";
764
+
765
+ let base = `You are the Feature Lead for '${featureName}' with ${numTeams} teams.
766
+
767
+ Read your instructions at ~/src/iriai/iriai-team/roles-v2/feature-lead/CLAUDE.md.
768
+ Read the feature status at ${featureStatusFile}.
769
+ Read ${dashboardFile} for current state.
770
+ ${teamRepoNote}`;
771
+
772
+ if (recoveryContext) {
773
+ base = `RECOVERY: Your prior session crashed during this phase. Resume from where you left off.
774
+ Read ${featureStatusFile} for current state.
775
+ Read ${dashboardFile} for team status.
776
+
777
+ ${base}`;
778
+ }
779
+
780
+ switch (trigger) {
781
+ case "gate-ready":
782
+ base += `
783
+ TRIGGER: All ${numTeams} teams have signaled .gate-ready.
784
+ ${gateReview}`;
785
+ break;
786
+
787
+ case "question":
788
+ base += `
789
+ TRIGGER: Teams ${questionTeams.join(", ")} have questions that need resolution.
790
+
791
+ You must:
792
+ 1. Read each questioning team's .question file at ${teamSignalBase}/team-N/orchestrator/.question
793
+ 2. Read ${featureStatusFile} for cross-team context
794
+ 3. If you can resolve the question with your cross-team knowledge:
795
+ - Write the answer to ${teamSignalBase}/team-N/orchestrator/.answer
796
+ - Remove the .question file
797
+ 4. If you cannot resolve it: surface it to the user and wait for their response
798
+ - Then write the user's answer to ${teamSignalBase}/team-N/orchestrator/.answer
799
+ - Remove the .question file
800
+ 5. Update ${featureStatusFile} and ${dashboardFile}
801
+
802
+ After handling questions, continue monitoring teams:
803
+ ${monitor}
804
+ ${gateReview}`;
805
+ break;
806
+
807
+ case "crash":
808
+ base += `
809
+ TRIGGER: Teams ${crashedTeams.join(", ")} have crashed orchestrators.
810
+
811
+ You must:
812
+ 1. Read the crashed team's HANDOVER.md to understand progress
813
+ 2. Determine what work was completed and what remains
814
+ 3. Write a recovery .task to ${teamSignalBase}/team-N/orchestrator/.task that:
815
+ - References HANDOVER.md for completed work
816
+ - Assigns only the remaining work
817
+ 4. Remove the .crashed file (run-team.sh will detect the new .task)
818
+ 5. If recovery isn't possible, surface the issue to the user
819
+ 6. Update ${featureStatusFile} and ${dashboardFile}
820
+
821
+ After handling crashes, continue monitoring teams:
822
+ ${monitor}
823
+ ${gateReview}`;
824
+ break;
825
+
826
+ case "idle-redispatch":
827
+ base += `
828
+ TRIGGER: All ${numTeams} teams are idle — they have no active .task files.
829
+ This typically happens after a session restart (launch.sh continue).
830
+
831
+ ## Re-dispatch Teams
832
+
833
+ 1. Read ${featureStatusFile} to understand the current gate and what was dispatched
834
+ 2. Read each team's GATE-CONTEXT.md and HANDOVER.md to understand their assignment and progress
835
+ 3. For each team, write a new .task file to ${teamSignalBase}/team-N/orchestrator/.task
836
+ - If the team has made progress (check HANDOVER.md), assign only remaining work
837
+ - If no progress, re-dispatch the full gate assignment from GATE-CONTEXT.md
838
+ 4. Update ${featureStatusFile} and ${dashboardFile}
839
+ ${monitor}
840
+ ${gateReview}`;
841
+ break;
842
+ }
843
+
844
+ base += `
845
+ ${contextMgmt}
846
+ ${slackComm}`;
847
+
848
+ return base;
849
+ }