@monoes/monomindcli 1.9.1 → 1.9.2

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.
@@ -52,7 +52,7 @@ Extract from `$ARGUMENTS`:
52
52
  - `--auto` → mode = auto
53
53
  - `--confirm` → mode = confirm
54
54
  - `--project <name>` → project_name = <name>
55
- - `--iterate <N>` → iterate = N (integer ≥ 1; default 0 = no iteration)
55
+ - `--iterate <N>` → iterate = N (integer ≥ 1; when flag is absent, no iteration runs)
56
56
  - Remaining text = prompt
57
57
 
58
58
  ### Step 2 — Brain Load
@@ -74,21 +74,26 @@ Invoke the intake logic from `_intake.md`:
74
74
  - If user says "decide yourself": make explicit LLM decision, state it, log it with confidence 0.7
75
75
  - Resolve: mode (auto/confirm), project_name, domains_needed
76
76
 
77
- **After intake resolves:** Generate a session ID (`mm-<YYYYMMDDTHHmmss>`) and emit `session:start` to the live dashboard (see Real-Time Dashboard Event Logging in `_protocol.md`). If the server is unreachable, continue without blocking.
78
-
79
- ```javascript
80
- WebFetch({
81
- url: "http://localhost:4242/api/mastermind/event",
82
- method: "POST",
83
- headers: { "Content-Type": "application/json" },
84
- body: JSON.stringify({
85
- type: "session:start",
86
- session: sessionId, // store this ID for all subsequent events
87
- prompt: resolvedPrompt,
88
- mode: mode,
89
- ts: Date.now()
90
- })
91
- })
77
+ **After intake resolves:** Assign shell variables from the intake outputs (these are LLM-resolved values that must be echoed into the bash environment before the curl block runs):
78
+ - `resolved_prompt` = the full cleaned prompt string
79
+ - `mode` = `"auto"` or `"confirm"`
80
+
81
+ Then generate `SESSION_ID` and persist it so iteration cycles can retrieve it across separate Bash calls:
82
+
83
+ ```bash
84
+ REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
85
+ SESSION_ID="mm-$(date -u +%Y%m%dT%H%M%S)"
86
+ mkdir -p "$REPO_ROOT/.monomind/sessions"
87
+ # Persist SESSION_ID and project context so Step 12 can restore it in a new shell
88
+ jq -n --arg sid "$SESSION_ID" --arg proj "$project_name" --arg prompt "$resolved_prompt" \
89
+ '{sessionId:$sid,project_name:$proj,prompt:$prompt}' \
90
+ > "$REPO_ROOT/.monomind/sessions/current.json.tmp" \
91
+ && mv "$REPO_ROOT/.monomind/sessions/current.json.tmp" \
92
+ "$REPO_ROOT/.monomind/sessions/current.json"
93
+ curl -s -o /dev/null -X POST "http://localhost:4242/api/mastermind/event" \
94
+ -H "Content-Type: application/json" \
95
+ -d "$(jq -cn --arg sid "$SESSION_ID" --arg prompt "$resolved_prompt" --arg mode "$mode" --arg proj "$(pwd)" \
96
+ '{type:"session:start",session:$sid,prompt:$prompt,mode:$mode,project:$proj,ts:(now*1000|floor)}')" || true
92
97
  ```
93
98
 
94
99
  ### Step 4 — Decompose
@@ -103,6 +108,32 @@ Complexity threshold for manager agent: any of these is true:
103
108
  - Has external dependencies (APIs, services)
104
109
  - Is estimated to take more than one conversation turn
105
110
 
111
+ **Per-domain goal extraction:** For each activated domain, extract a one-sentence goal from the prompt describing what that domain must accomplish. Then **run the following Bash block**, substituting `<domain_goals_json>` with a JSON object mapping each domain name to its one-sentence goal (use the full `resolved_prompt` as the value for any domain where no specific goal is extractable):
112
+
113
+ ```bash
114
+ REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
115
+ SESSION_STATE="$REPO_ROOT/.monomind/sessions/current.json"
116
+ [ -f "$SESSION_STATE" ] || { echo "ERROR: current.json not found"; exit 1; }
117
+
118
+ # LLM: write the extracted goals JSON object to the temp file below.
119
+ # Use a file (not a shell variable) to avoid quoting issues with apostrophes in goal text.
120
+ # Example content: {"build":"Ship the auth module","marketing":"Draft launch email series"}
121
+ # One JSON object, keys = domain names, values = one-sentence goals.
122
+ SESSION_ID=$(jq -r '.sessionId // empty' "$SESSION_STATE" 2>/dev/null)
123
+ [ -z "$SESSION_ID" ] && { echo "ERROR: SESSION_ID missing in current.json — run Step 3 first"; exit 1; }
124
+ GOALS_FILE="$REPO_ROOT/.monomind/sessions/${SESSION_ID}_goals.json"
125
+ cat > "$GOALS_FILE" << 'GOALS_EOF'
126
+ <domain_goals_json>
127
+ GOALS_EOF
128
+
129
+ # Validate it's real JSON before merging
130
+ jq . "$GOALS_FILE" > /dev/null 2>&1 || { echo "ERROR: domain_goals_json is not valid JSON — check LLM substitution"; exit 1; }
131
+
132
+ jq --slurpfile goals "$GOALS_FILE" '. + {domain_goals:$goals[0]}' \
133
+ "$SESSION_STATE" > "$SESSION_STATE.tmp" && mv "$SESSION_STATE.tmp" "$SESSION_STATE"
134
+ echo "Domain goals written to current.json"
135
+ ```
136
+
106
137
  ### Step 5 — Plan Output
107
138
 
108
139
  Build a plan summary:
@@ -114,48 +145,249 @@ Prompt: <prompt>
114
145
  Mode: <auto|confirm>
115
146
 
116
147
  Domains activated:
117
- ✦ build → Development Manager agent → board: <project>/development
118
- ✦ marketing → Marketing Manager agent → board: <project>/marketing
148
+ ✦ build → Development Manager agent → board: <project>/development (will be created in Step 6)
149
+ ✦ marketing → Marketing Manager agent → board: <project>/marketing (will be created in Step 6)
119
150
 
120
151
  Monotask space: <project_name>
121
152
  Brain loaded: <N> principles, <M> domain summaries
122
153
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
123
154
  ```
124
155
 
125
- If mode = confirm: show plan and wait for user to say "go" or modify. If mode = auto: proceed immediately.
156
+ If mode = confirm: show plan and wait for user response. Valid responses:
157
+ - "go" or "proceed" — continue immediately
158
+ - Any modification (e.g. "add sales domain", "remove marketing") — apply the change, re-show the plan, wait again
159
+ - "cancel" or "stop" — emit `session:complete` with `status: blocked`, reason "cancelled by user", then STOP
160
+
161
+ If mode = auto: proceed immediately.
126
162
 
127
163
  ### Step 6 — Monotask Setup
128
164
 
129
- For each active domain:
130
- 1. Find or create monotask space named `<project_name>`
131
- 2. Create a board named `<domain>` within that space
132
- 3. Note the board ID for the domain manager's task briefing
165
+ Follow the Monotask Space+Board Setup Procedure from `_protocol.md`. Resolve the space **once**, then create one board per active domain. Use `project_name` as the space name so all boards across repos and domains share the same space.
166
+
167
+ ```bash
168
+ # Require bash 4.3+ for associative arrays and namerefs (local -n introduced in 4.3)
169
+ # macOS ships bash 3.2; install via: brew install bash
170
+ (( BASH_VERSINFO[0] * 100 + BASH_VERSINFO[1] < 403 )) && \
171
+ { echo "ERROR: bash 4.3+ required for namerefs (current: $BASH_VERSION). Install: brew install bash"; exit 1; }
172
+
173
+ REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
174
+ SESSION_STATE="$REPO_ROOT/.monomind/sessions/current.json"
175
+
176
+ # Reload persisted context (this is a fresh shell; Step 3 wrote these)
177
+ SESSION_ID=$(jq -r '.sessionId // empty' "$SESSION_STATE" 2>/dev/null)
178
+ [ -z "$SESSION_ID" ] && { echo "ERROR: SESSION_ID missing in current.json — run Step 3 first"; exit 1; }
179
+ project_name=$(jq -r '.project_name // ""' "$SESSION_STATE")
180
+ [ -z "$project_name" ] && { echo "ERROR: project_name is empty in current.json — run Step 3 first"; exit 1; }
181
+ resolved_prompt=$(jq -r '.prompt // ""' "$SESSION_STATE")
182
+
183
+ # domains_needed: NOT yet in current.json at this point — must be LLM-substituted inline.
184
+ # LLM: replace DOMAINS_LIST_HERE with space-separated domain names, e.g.: build marketing sales
185
+ # Domain names must be single words (no spaces). Example: "build marketing sales"
186
+
187
+ # Resolve space once for all domains
188
+ # Use awk with literal pipe to avoid BSD awk \| regex fragility
189
+ space_id=$(monotask space list 2>/dev/null | awk -F'|' '{gsub(/^ +| +$/,"",$2); if($2==n) gsub(/^ +| +$/,"",$1); if($2==n) print $1}' n="$project_name" | head -1)
190
+ [ -z "$space_id" ] && space_id=$(monotask space create "$project_name" 2>/dev/null | grep -oE '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')
191
+ [ -z "$space_id" ] && { echo "ERROR: Could not find or create space '$project_name'"; exit 1; }
192
+
193
+ # Associative arrays — no eval, no injection risk
194
+ declare -A board_ids todo_cols doing_cols done_cols domain_goals
195
+
196
+ # Hydrate domain_goals from Step 4's current.json write — prevents Step 4 extraction being clobbered
197
+ # Use NUL-delimited pairs (not @tsv) to safely handle backslashes, tabs, and other special chars in goals
198
+ while IFS= read -r -d '' k && IFS= read -r -d '' v; do
199
+ [[ -n "$k" ]] && domain_goals[$k]="$v"
200
+ done < <(jq -j '.domain_goals // {} | to_entries[] | (.key + "\u0000" + (.value // "") + "\u0000")' "$SESSION_STATE" 2>/dev/null)
201
+
202
+ # Loop over every active domain — LLM: replace DOMAINS_LIST_HERE with the resolved domain list
203
+ domains_needed="DOMAINS_LIST_HERE"
204
+ [ "$domains_needed" = "DOMAINS_LIST_HERE" ] && { echo "ERROR: LLM did not substitute DOMAINS_LIST_HERE with domain names"; exit 1; }
205
+ [ -z "$domains_needed" ] && { echo "ERROR: domains_needed is empty — nothing to do"; exit 1; }
206
+ for domain in $domains_needed; do
207
+ board_id=$(monotask board create "$domain" --json | jq -r '.id // empty')
208
+ [ -z "$board_id" ] && { echo "ERROR: Failed to create $domain board"; exit 1; }
209
+ monotask space boards add "$space_id" "$board_id" >/dev/null 2>&1 \
210
+ || echo "WARN: could not attach $domain board to space $space_id (non-fatal)"
211
+ todo_col=$(monotask column create "$board_id" "Todo" --json | jq -r '.id // empty')
212
+ [ -z "$todo_col" ] && { echo "ERROR: Failed to create Todo column for $domain"; exit 1; }
213
+ doing_col=$(monotask column create "$board_id" "Doing" --json | jq -r '.id // empty')
214
+ [ -z "$doing_col" ] && { echo "ERROR: Failed to create Doing column for $domain"; exit 1; }
215
+ done_col=$(monotask column create "$board_id" "Done" --json | jq -r '.id // empty')
216
+ [ -z "$done_col" ] && { echo "ERROR: Failed to create Done column for $domain"; exit 1; }
217
+ board_ids[$domain]=$board_id
218
+ todo_cols[$domain]=$todo_col
219
+ doing_cols[$domain]=$doing_col
220
+ done_cols[$domain]=$done_col
221
+ # Fall back to full prompt only for domains not extracted by Step 4
222
+ [ -z "${domain_goals[$domain]}" ] && domain_goals[$domain]="$resolved_prompt"
223
+ done
224
+
225
+ # Persist all session state needed by later shells — board/col IDs, goals, domain list
226
+ # (each Bash tool call is a fresh shell — associative arrays don't survive)
227
+ _to_json_map() {
228
+ local -n _arr=$1
229
+ for k in "${!_arr[@]}"; do
230
+ jq -n --arg k "$k" --arg v "${_arr[$k]}" '{key:$k,value:$v}'
231
+ done | jq -s 'from_entries // {}'
232
+ }
233
+ domains_goals_json=$(_to_json_map domain_goals)
234
+ board_ids_json=$(_to_json_map board_ids)
235
+ todo_cols_json=$(_to_json_map todo_cols)
236
+ doing_cols_json=$(_to_json_map doing_cols)
237
+ done_cols_json=$(_to_json_map done_cols)
238
+
239
+ jq --arg domains "$domains_needed" \
240
+ --argjson goals "$domains_goals_json" \
241
+ --argjson boards "$board_ids_json" \
242
+ --argjson todos "$todo_cols_json" \
243
+ --argjson doings "$doing_cols_json" \
244
+ --argjson dones "$done_cols_json" \
245
+ '. + {domains_needed:($domains | split(" ") | map(select(length>0))),
246
+ domain_goals:$goals, board_ids:$boards,
247
+ todo_cols:$todos, doing_cols:$doings, done_cols:$dones}' \
248
+ "$REPO_ROOT/.monomind/sessions/current.json" > "$REPO_ROOT/.monomind/sessions/current.json.tmp" \
249
+ && mv "$REPO_ROOT/.monomind/sessions/current.json.tmp" "$REPO_ROOT/.monomind/sessions/current.json"
250
+ ```
133
251
 
134
252
  ### Step 7 — Spawn Domain Managers
135
253
 
136
- **Before spawning:** For EACH domain in `domains_needed`, emit a `domain:dispatch` event to the live dashboard:
254
+ **Before spawning**, select the best domain manager agent type from the registry for each active domain. Do not hardcode `coordinator` pick the agent whose expertise best fits the domain goal.
255
+
256
+ **Phase A — Registry selection** (run as one Bash call; must complete before Phase C):
257
+
258
+ ```bash
259
+ # Require bash 4.3+ for associative arrays and namerefs
260
+ (( BASH_VERSINFO[0] * 100 + BASH_VERSINFO[1] < 403 )) && \
261
+ { echo "ERROR: bash 4.3+ required (current: $BASH_VERSION). Install: brew install bash"; exit 1; }
262
+
263
+ REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
264
+ REGISTRY="$REPO_ROOT/.monomind/registry.json"
265
+
266
+ # Reload state from current.json — this is a new shell; no inherited variables
267
+ SESSION_STATE="$REPO_ROOT/.monomind/sessions/current.json"
268
+ [ -f "$SESSION_STATE" ] || { echo "ERROR: current.json not found — run from Step 3"; exit 1; }
269
+ domains_needed=$(jq -r '.domains_needed[]? // empty' "$SESSION_STATE" | tr '\n' ' ')
270
+ [ -z "$domains_needed" ] && { echo "ERROR: domains_needed is empty in current.json"; exit 1; }
271
+
272
+ # Returns: best agent name from registry for the given domain+goal
273
+ pick_domain_manager() {
274
+ local domain="$1"
275
+ local goal="$2"
276
+ local kw cats result
277
+ kw=$(echo "$goal" | tr '[:upper:]' '[:lower:]' | grep -oE '[a-z]{5,}' | sort -u | tr '\n' ' ')
278
+ case "$domain" in
279
+ build) cats="engineering development architecture" ;;
280
+ marketing) cats="marketing paid-media strategy" ;;
281
+ sales) cats="sales strategy" ;;
282
+ research) cats="academic specialized strategy" ;;
283
+ content) cats="marketing specialized" ;;
284
+ ops) cats="project-management strategy support" ;;
285
+ release) cats="devops github engineering" ;;
286
+ review) cats="engineering testing analysis" ;;
287
+ finance) cats="strategy specialized" ;;
288
+ architect) cats="architecture engineering" ;;
289
+ idea) cats="product strategy marketing" ;;
290
+ *) cats="core strategy" ;;
291
+ esac
292
+ result=$(jq -r \
293
+ --arg cats "$cats" \
294
+ --arg kw "$kw" \
295
+ '[ .agents[] | select(.deprecated != true)
296
+ | select(.category as $c | ($cats | split(" ") | any(. == $c)))
297
+ | {name: .name,
298
+ score: (
299
+ (.name | ascii_downcase) as $n |
300
+ # Score on ANY keyword match, not just the first
301
+ (if ($kw | length) > 0
302
+ then ([$kw | split(" ")[] | select(length > 0) | if ($n | contains(.)) then 1 else 0 end] | add // 0)
303
+ else 0 end) +
304
+ (if $n | test("manager|director|coordinator") then 1 else 0 end)
305
+ )}
306
+ ] | sort_by(-.score) | .[0].name // empty' \
307
+ "$REGISTRY" 2>/dev/null)
308
+ if [ -z "$result" ]; then
309
+ echo "WARN: registry lookup failed for domain=$domain, using coordinator fallback" >&2
310
+ echo "coordinator"
311
+ else
312
+ echo "$result"
313
+ fi
314
+ }
315
+
316
+ declare -A domain_managers
317
+ for domain in $domains_needed; do
318
+ goal=$(jq -r --arg d "$domain" '.domain_goals[$d] // empty' "$SESSION_STATE")
319
+ [ -z "$goal" ] && goal=$(jq -r '.prompt // ""' "$SESSION_STATE")
320
+ manager=$(pick_domain_manager "$domain" "$goal")
321
+ domain_managers[$domain]="$manager"
322
+ echo "Domain manager for $domain: $manager"
323
+ done
324
+
325
+ # Persist domain_managers so Phase C can reload them without stdout parsing
326
+ domain_managers_json=$(for k in "${!domain_managers[@]}"; do
327
+ jq -n --arg k "$k" --arg v "${domain_managers[$k]}" '{key:$k,value:$v}'
328
+ done | jq -s 'from_entries // {}')
329
+ [ -z "$domain_managers_json" ] && domain_managers_json="{}"
330
+ jq --argjson mgrs "$domain_managers_json" '. + {domain_managers:$mgrs}' \
331
+ "$SESSION_STATE" > "$SESSION_STATE.tmp" && mv "$SESSION_STATE.tmp" "$SESSION_STATE"
332
+
333
+ # Emit board/column lookup for LLM use in Phase C Task construction:
334
+ echo "--- Phase C board/col IDs (loaded from current.json) ---"
335
+ for domain in $domains_needed; do
336
+ board=$(jq -r --arg d "$domain" '.board_ids[$d] // ""' "$SESSION_STATE")
337
+ todo=$(jq -r --arg d "$domain" '.todo_cols[$d] // ""' "$SESSION_STATE")
338
+ doing=$(jq -r --arg d "$domain" '.doing_cols[$d] // ""' "$SESSION_STATE")
339
+ done_c=$(jq -r --arg d "$domain" '.done_cols[$d] // ""' "$SESSION_STATE")
340
+ mgr=$(jq -r --arg d "$domain" '.domain_managers[$d] // "coordinator"' "$SESSION_STATE")
341
+ echo "DOMAIN=$domain MANAGER=$mgr BOARD=$board TODO=$todo DOING=$doing DONE=$done_c"
342
+ done
343
+ ```
137
344
 
138
- ```javascript
139
- // Emit for each domain — use WebFetch for each (or batch them)
140
- WebFetch({
141
- url: "http://localhost:4242/api/mastermind/event",
142
- method: "POST",
143
- headers: { "Content-Type": "application/json" },
144
- body: JSON.stringify({
145
- type: "domain:dispatch",
146
- session: sessionId,
147
- domain: "<domain-id>", // e.g. "build", "marketing"
148
- cmd: "<one-line goal for this domain>",
149
- ts: Date.now()
150
- })
151
- })
345
+ **Phase B — Dashboard dispatch** + **Phase C — Task spawning** (run in one message — B is a Bash call, C is the Task tool calls; they are independent of each other):
346
+
347
+ Phase B:
348
+ ```bash
349
+ REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
350
+ SESSION_STATE="$REPO_ROOT/.monomind/sessions/current.json"
351
+ SESSION_ID=$(jq -r '.sessionId // empty' "$SESSION_STATE" 2>/dev/null)
352
+ [ -z "$SESSION_ID" ] && { echo "ERROR: SESSION_ID not found in current.json"; exit 1; }
353
+ domains_needed=$(jq -r '.domains_needed[]? // empty' "$SESSION_STATE" | tr '\n' ' ')
354
+ for domain in $domains_needed; do
355
+ goal=$(jq -r --arg d "$domain" '.domain_goals[$d] // empty' "$SESSION_STATE")
356
+ [ -z "$goal" ] && goal=$(jq -r '.prompt // ""' "$SESSION_STATE")
357
+ curl -s -o /dev/null -X POST "http://localhost:4242/api/mastermind/event" \
358
+ -H "Content-Type: application/json" \
359
+ -d "$(jq -cn --arg sid "$SESSION_ID" --arg d "$domain" --arg cmd "$goal" \
360
+ '{type:"domain:dispatch",session:$sid,domain:$d,cmd:$cmd,ts:(now*1000|floor)}')" || true
361
+ done
152
362
  ```
153
363
 
154
364
  Spawn ALL domain manager agents in ONE message using the Task tool (parallel execution).
155
365
 
366
+ **Before constructing the Task calls:** load board/column UUIDs and domain manager names from `current.json` — that is the authoritative source. The Phase A echo lines are a human-readable diagnostic only; do not parse them as the primary data source.
367
+
368
+ ```bash
369
+ REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
370
+ SESSION_STATE="$REPO_ROOT/.monomind/sessions/current.json"
371
+ SESSION_ID=$(jq -r '.sessionId // empty' "$SESSION_STATE" 2>/dev/null)
372
+ [ -z "$SESSION_ID" ] && { echo "ERROR: SESSION_ID missing"; exit 1; }
373
+
374
+ # Emit one line per domain for LLM to read before constructing Task calls
375
+ for domain in $(jq -r '.domains_needed[]? // empty' "$SESSION_STATE"); do
376
+ echo "DOMAIN=$domain \
377
+ MANAGER=$(jq -r --arg d "$domain" '.domain_managers[$d] // "coordinator"' "$SESSION_STATE") \
378
+ BOARD=$(jq -r --arg d "$domain" '.board_ids[$d] // ""' "$SESSION_STATE") \
379
+ TODO=$(jq -r --arg d "$domain" '.todo_cols[$d] // ""' "$SESSION_STATE") \
380
+ DOING=$(jq -r --arg d "$domain" '.doing_cols[$d] // ""' "$SESSION_STATE") \
381
+ DONE=$(jq -r --arg d "$domain" '.done_cols[$d] // ""' "$SESSION_STATE") \
382
+ GOAL=$(jq -r --arg d "$domain" '.domain_goals[$d] // .prompt' "$SESSION_STATE" | tr -d '\n')"
383
+ done
384
+ ```
385
+
386
+ Use each `MANAGER` value as `subagent_type`, `BOARD`/`TODO`/`DOING`/`DONE` as board and column IDs. Do NOT use placeholder strings.
387
+
156
388
  Each Task call must include a complete briefing following the Monotask Task Briefing Standard from `_protocol.md`. Include:
157
389
  - The full BRAIN CONTEXT block
158
- - The board ID
390
+ - The board ID (from `current.json` above)
159
391
  - The specific goal for this domain
160
392
  - The project name and run context
161
393
  - Instruction to create monotask cards directly using `monotask card create $BOARD_ID $COL_TODO_ID "<title>" --json` for all sub-tasks
@@ -163,95 +395,125 @@ Each Task call must include a complete briefing following the Monotask Task Brie
163
395
  - Instruction to spawn specialized agents using the domain-appropriate swarm topology
164
396
  - Instruction to return the unified output schema when done
165
397
 
166
- Example Task call for Development Manager:
398
+ Example Task call for Development Manager. Substitute all **pre-known** `<…>` placeholders (project_name, SESSION_ID, board/col IDs, goals, manager name) before calling Task. Placeholders like `<status>`, `<path1>`, `<action1>` are filled at runtime by the spawned agent — do not attempt to substitute them. `subagent_type` is the **string value** of `$domain_manager_build` (e.g. `"Backend Architect"`), not a variable reference.
399
+
400
+ **IMPORTANT — `<SESSION_ID>` appears 6 times in the template below. ALL must be replaced with the resolved value:**
401
+ 1. `SESSION ID: <SESSION_ID>` — the header line in the prompt
402
+ 2. `--arg sid '<SESSION_ID>'` in the agent:spawn curl call
403
+ 3. `--arg sid '<SESSION_ID>'` in the intercom curl call
404
+ 4. `mkdir -p "…/sessions/<SESSION_ID>"` — the output directory
405
+ 5. `> "…/sessions/<SESSION_ID>/build.json"` — the output file path
406
+ 6. `--arg sid '<SESSION_ID>'` in the domain:complete curl call
407
+
408
+ Missing any one causes silent failures (output files written to a literal `<SESSION_ID>` directory that doesn't exist; Step 9 finds nothing and reports `complete` with zero domains).
409
+
167
410
  ```javascript
168
411
  Task({
169
- subagent_type: "coordinator",
170
- description: `You are the Development Manager for project <project_name>.
171
-
172
- CONTEXT: Mastermind run <date> | Project: <project_name> | Master spawned you.
173
-
174
- BRAIN CONTEXT:
175
- <paste brain context here>
176
-
177
- YOUR BOARD: <board_id> (monotask://<project_name>/development)
178
-
179
- GOAL: <domain-specific goal extracted from prompt>
180
-
181
- SESSION ID: <sessionId> use this in all dashboard events
182
-
183
- YOUR RESPONSIBILITIES:
184
- 1. Resolve column IDs first:
185
- ```bash
186
- columns=$(monotask column list "$BOARD_ID" --json)
187
- COL_TODO_ID=$(echo "$columns" | jq -r '.[] | select(.name == "Todo" or .name == "Backlog") | .id' | head -1)
188
- COL_DONE_ID=$(echo "$columns" | jq -r '.[] | select(.name == "Done") | .id' | head -1)
189
- ```
190
- Then break this goal into discrete tasks by creating monotask cards: `monotask card create "$BOARD_ID" "$COL_TODO_ID" "<title>" --json`
191
- Each task description MUST follow the Monotask Task Briefing Standard (full context, goal, scope, constraints, success criteria, agent, swarm, dependencies)
192
- 2. Spawn specialized agents for each task using the Task tool:
193
- - Backend work: subagent_type "backend-dev"
194
- - Frontend work: subagent_type "frontend-dev"
195
- - Testing: subagent_type "tester"
196
- - Code review: subagent_type "reviewer"
197
- Default swarm: hierarchical 6 agents raft
198
- 3. BEFORE spawning each agent, emit agent:spawn to the live dashboard:
199
- WebFetch({ url: "http://localhost:4242/api/mastermind/event", method: "POST",
200
- headers: {"Content-Type":"application/json"},
201
- body: JSON.stringify({ type:"agent:spawn", session:"<sessionId>",
202
- domain:"build", agent:"<agent-slug>", task:"<task-description>", ts:Date.now() }) })
203
- 4. If you hand off artifacts to another domain manager, emit intercom:
204
- WebFetch({ url: "http://localhost:4242/api/mastermind/event", method: "POST",
205
- headers: {"Content-Type":"application/json"},
206
- body: JSON.stringify({ type:"intercom", session:"<sessionId>",
207
- from:"build", to:"<other-domain>", msg:"<one-line summary>", ts:Date.now() }) })
208
- 5. Execute tasks via /monomind:do --board <board_id>
209
- 6. Collect all agent outputs
210
- 7. BEFORE returning, emit domain:complete to the live dashboard:
211
- WebFetch({ url: "http://localhost:4242/api/mastermind/event", method: "POST",
212
- headers: {"Content-Type":"application/json"},
213
- body: JSON.stringify({ type:"domain:complete", session:"<sessionId>",
214
- domain:"build", status:"complete|partial|blocked",
215
- artifacts:["/path/file1"], decisions:[{what:"...",confidence:0.9}], ts:Date.now() }) })
216
- 8. Return unified output schema to master:
217
- domain: build
218
- status: complete|partial|blocked
219
- artifacts: [...]
220
- decisions: [...]
221
- lessons: [...]
222
- next_actions: [...]
223
- board_url: monotask://<project_name>/development
224
- run_id: <ISO8601-timestamp>`,
225
- run_in_background: true
412
+ subagent_type: "<value of domain_manager_build, e.g. Backend Architect>",
413
+ description: "Development Manager for project <project_name>",
414
+ run_in_background: false, // foreground so Step 8 can collect output synchronously
415
+ prompt: "You are the Development Manager for project <project_name>.\n\n" +
416
+ "CONTEXT: Mastermind run <date> | Project: <project_name> | Master spawned you.\n\n" +
417
+ "SESSION ID: <SESSION_ID> — use in all dashboard events below.\n\n" +
418
+ "BRAIN CONTEXT:\n<paste brain context here>\n\n" +
419
+ "YOUR BOARD: <board_build> (monotask://<project_name>/build)\n" +
420
+ "TODO COL: <todo_col_build> | DOING COL: <doing_col_build> | DONE COL: <done_col_build>\n\n" +
421
+ "GOAL: <build_goal>\n\n" +
422
+ "YOUR RESPONSIBILITIES:\n" +
423
+ "1. Break this goal into discrete tasks using:\n" +
424
+ " monotask card create <board_build> <todo_col_build> '<title>' --json\n" +
425
+ " Each card description MUST include: context, goal, scope, constraints, success criteria, agent, dependencies.\n\n" +
426
+ "2. Spawn specialized agents for each task using the Task tool:\n" +
427
+ " - Backend work: subagent_type 'backend-dev'\n" +
428
+ " - Frontend work: subagent_type 'frontend-dev'\n" +
429
+ " - Testing: subagent_type 'tester'\n" +
430
+ " - Code review: subagent_type 'reviewer'\n" +
431
+ " Default swarm: hierarchical 6 agents raft\n\n" +
432
+ "3. BEFORE spawning each agent, emit agent:spawn via curl (NOT WebFetch — use jq for correct ms timestamps):\n" +
433
+ " curl -s -o /dev/null -X POST http://localhost:4242/api/mastermind/event \\\n" +
434
+ " -H 'Content-Type: application/json' \\\n" +
435
+ " -d \"$(jq -cn --arg sid '<SESSION_ID>' --arg agent '<slug>' --arg task '<title>' \\\n" +
436
+ " '{type:\"agent:spawn\",session:$sid,domain:\"build\",agent:$agent,task:$task,ts:(now*1000|floor)}')\" || true\n\n" +
437
+ "4. If handing off artifacts to another domain, emit intercom via curl:\n" +
438
+ " curl -s -o /dev/null -X POST http://localhost:4242/api/mastermind/event \\\n" +
439
+ " -H 'Content-Type: application/json' \\\n" +
440
+ " -d \"$(jq -cn --arg sid '<SESSION_ID>' --arg to '<domain>' --arg msg '<summary>' \\\n" +
441
+ " '{type:\"intercom\",session:$sid,from:\"build\",to:$to,msg:$msg,ts:(now*1000|floor)}')\" || true\n\n" +
442
+ "5. Execute tasks via /monomind:do --board <board_build>\n" +
443
+ "6. Collect all agent outputs\n\n" +
444
+ "7. BEFORE returning, write your output schema to disk AND emit domain:complete:\n" +
445
+ " REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)\n" +
446
+ " mkdir -p \"$REPO_ROOT/.monomind/sessions/<SESSION_ID>\"\n" +
447
+ " jq -n --arg domain 'build' --arg status '<status>' \\\n" +
448
+ " --argjson artifacts '[\"<path1>\",\"<path2>\"]' \\\n" +
449
+ " --argjson next_actions '[\"<action1>\"]' \\\n" +
450
+ " '{domain:$domain,status:$status,artifacts:$artifacts,next_actions:$next_actions}' \\\n" +
451
+ " > \"$REPO_ROOT/.monomind/sessions/<SESSION_ID>/build.json\"\n" +
452
+ " curl -s -o /dev/null -X POST http://localhost:4242/api/mastermind/event \\\n" +
453
+ " -H 'Content-Type: application/json' \\\n" +
454
+ " -d \"$(jq -cn --arg sid '<SESSION_ID>' --arg status '<status>' \\\n" +
455
+ " '{type:\"domain:complete\",session:$sid,domain:\"build\",status:$status,ts:(now*1000|floor)}')\" || true\n\n" +
456
+ "8. Return unified output schema:\n" +
457
+ " domain: build\n" +
458
+ " status: complete|partial|blocked\n" +
459
+ " artifacts: [...]\n" +
460
+ " decisions: [...]\n" +
461
+ " lessons: [...]\n" +
462
+ " next_actions: [...]\n" +
463
+ " board_url: monotask://<project_name>/build\n" +
464
+ " run_id: <ISO8601-timestamp>"
226
465
  })
227
466
  ```
228
467
 
229
- ### Step 8 — Wait for Reports
468
+ ### Step 8 — Collect Reports
230
469
 
231
- Collect the unified output schema from each domain manager. If a manager reports `status: blocked`, note the blocker but continue collecting from others — do not abort.
470
+ Domain managers run in foreground (no `run_in_background`), so their unified output schemas are returned synchronously as each Task call completes. Each domain manager writes its canonical output schema to `.monomind/sessions/<SESSION_ID>/<domain>.json` before returning — that file is the source of truth for Step 9 aggregation. The Task tool's text return value is informational only; do not attempt to parse it as JSON. If a manager reports `status: blocked`, record it but continue collecting from all others — do not abort the run.
232
471
 
233
472
  ### Step 9 — Synthesize
234
473
 
235
- 1. Collect all domain output schemas
236
- 2. Identify any cross-domain artifacts needed (e.g. a release that requires both build and review)
237
- 3. Write cross-domain artifacts to disk if needed
238
- 4. **Emit `session:complete` to the live dashboard:**
239
-
240
- ```javascript
241
- WebFetch({
242
- url: "http://localhost:4242/api/mastermind/event",
243
- method: "POST",
244
- headers: { "Content-Type": "application/json" },
245
- body: JSON.stringify({
246
- type: "session:complete",
247
- session: sessionId,
248
- status: overallStatus, // "complete" | "partial" | "blocked"
249
- domains: completedDomains,
250
- ts: Date.now()
251
- })
252
- })
474
+ 1. Collect all domain output schemas from Step 8
475
+ 2. Compute aggregate status — read from per-domain output files (precedence: blocked > partial > complete):
476
+
477
+ ```bash
478
+ # Single bash block: aggregate status + emit dashboard event
479
+ # (variables don't persist between Bash tool calls — keep aggregation and curl together)
480
+ (( BASH_VERSINFO[0] * 100 + BASH_VERSINFO[1] < 400 )) && \
481
+ { echo "ERROR: bash 4+ required (current: $BASH_VERSION). Install: brew install bash"; exit 1; }
482
+ REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
483
+ SESSION_ID=$(jq -r '.sessionId // empty' "$REPO_ROOT/.monomind/sessions/current.json" 2>/dev/null)
484
+ [ -z "$SESSION_ID" ] && { echo "ERROR: SESSION_ID missing"; exit 1; }
485
+
486
+ overall_status="complete"
487
+ completed_domains=()
488
+ found_domain_files=0
489
+ for domain_file in "$REPO_ROOT/.monomind/sessions/${SESSION_ID}"/*.json; do
490
+ [ -f "$domain_file" ] || continue
491
+ domain=$(jq -r '.domain // ""' "$domain_file")
492
+ [ -z "$domain" ] && continue # skip auxiliary files that aren't domain output schemas
493
+ found_domain_files=$(( found_domain_files + 1 ))
494
+ status=$(jq -r '.status // "blocked"' "$domain_file")
495
+ case "$status" in
496
+ blocked) overall_status="blocked" ;;
497
+ partial) [ "$overall_status" != "blocked" ] && overall_status="partial" ;;
498
+ esac
499
+ [ "$status" = "complete" ] && completed_domains+=("$domain")
500
+ done
501
+ (( found_domain_files == 0 )) && { overall_status="blocked"; echo "WARN: no domain output files found for session $SESSION_ID — all domain managers may have failed"; }
502
+ echo "overall_status=$overall_status completed_domains=${completed_domains[*]}"
503
+
504
+ completed_domains_json=$(jq -n '$ARGS.positional' --args "${completed_domains[@]}")
505
+
506
+ curl -s -o /dev/null -X POST "http://localhost:4242/api/mastermind/event" \
507
+ -H "Content-Type: application/json" \
508
+ -d "$(jq -cn \
509
+ --arg sid "$SESSION_ID" \
510
+ --arg status "$overall_status" \
511
+ --argjson domains "$completed_domains_json" \
512
+ '{type:"session:complete",session:$sid,status:$status,domains:$domains,ts:(now*1000|floor)}')" || true
253
513
  ```
254
514
 
515
+ 3. Identify any cross-domain artifacts needed (e.g. a release that requires both build and review)
516
+ 4. Write cross-domain artifacts to disk if needed
255
517
  5. Compose the action summary for the user:
256
518
 
257
519
  ```
@@ -292,6 +554,66 @@ Follow the Brain Write Procedure from `_protocol.md` for each domain that ran:
292
554
  Show the action summary (Step 9). If any compaction ran during Step 10, append:
293
555
  > "Brain updated: compacted <N> entries into <M> summaries."
294
556
 
557
+ **Persist session state for iteration cycles:** Aggregate artifacts from per-domain output files written by each domain manager, then persist to disk so Step 12 can load it:
558
+
559
+ ```bash
560
+ (( BASH_VERSINFO[0] * 100 + BASH_VERSINFO[1] < 400 )) && \
561
+ { echo "ERROR: bash 4+ required (current: $BASH_VERSION). Install: brew install bash"; exit 1; }
562
+ REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
563
+ SESSION_STATE="$REPO_ROOT/.monomind/sessions/current.json"
564
+
565
+ # Restore variables from current.json (this is a fresh shell)
566
+ SESSION_ID=$(jq -r '.sessionId // empty' "$SESSION_STATE" 2>/dev/null)
567
+ [ -z "$SESSION_ID" ] && { echo "ERROR: SESSION_ID not found"; exit 1; }
568
+ resolved_prompt=$(jq -r '.prompt // ""' "$SESSION_STATE")
569
+ project_name=$(jq -r '.project_name // ""' "$SESSION_STATE")
570
+
571
+ # Aggregate artifacts, next_actions, completed_domains, and overall_status
572
+ # from per-domain output files (single source of truth — Step 9 shell is gone)
573
+ all_artifacts=()
574
+ all_next_actions=()
575
+ completed_domains=()
576
+ overall_status="complete"
577
+ found_domain_files=0
578
+ for domain_file in "$REPO_ROOT/.monomind/sessions/${SESSION_ID}"/*.json; do
579
+ [ -f "$domain_file" ] || continue
580
+ domain=$(jq -r '.domain // ""' "$domain_file")
581
+ [ -z "$domain" ] && continue # skip auxiliary files that aren't domain output schemas
582
+ found_domain_files=$(( found_domain_files + 1 ))
583
+ status=$(jq -r '.status // "blocked"' "$domain_file")
584
+ case "$status" in
585
+ blocked) overall_status="blocked" ;;
586
+ partial) [ "$overall_status" != "blocked" ] && overall_status="partial" ;;
587
+ esac
588
+ [ "$status" = "complete" ] && completed_domains+=("$domain")
589
+ while IFS= read -r art; do all_artifacts+=("$art"); done \
590
+ < <(jq -r '.artifacts[]? // empty' "$domain_file" 2>/dev/null)
591
+ while IFS= read -r act; do all_next_actions+=("$act"); done \
592
+ < <(jq -r '.next_actions[]? // empty' "$domain_file" 2>/dev/null)
593
+ done
594
+ (( found_domain_files == 0 )) && { overall_status="blocked"; echo "WARN: no domain output files found for session $SESSION_ID"; }
595
+
596
+ artifacts_json=$(jq -n '$ARGS.positional' --args "${all_artifacts[@]}")
597
+ next_actions_json=$(jq -n '$ARGS.positional' --args "${all_next_actions[@]}")
598
+ completed_domains_json=$(jq -n '$ARGS.positional' --args "${completed_domains[@]}")
599
+
600
+ jq -n \
601
+ --arg sessionId "$SESSION_ID" \
602
+ --arg prompt "$resolved_prompt" \
603
+ --arg project_name "$project_name" \
604
+ --arg status "$overall_status" \
605
+ --argjson completed_domains "$completed_domains_json" \
606
+ --argjson artifacts "$artifacts_json" \
607
+ --argjson next_actions "$next_actions_json" \
608
+ --arg run_id "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
609
+ '{sessionId:$sessionId,prompt:$prompt,project_name:$project_name,status:$status,
610
+ completed_domains:$completed_domains,artifacts:$artifacts,next_actions:$next_actions,
611
+ run_id:$run_id}' \
612
+ > "$REPO_ROOT/.monomind/sessions/${SESSION_ID}.json.tmp" \
613
+ && mv "$REPO_ROOT/.monomind/sessions/${SESSION_ID}.json.tmp" \
614
+ "$REPO_ROOT/.monomind/sessions/${SESSION_ID}.json"
615
+ ```
616
+
295
617
  ---
296
618
 
297
619
  ### Step 12 — Iteration Loop (only if `--iterate <N>` was set and N ≥ 1)
@@ -302,9 +624,30 @@ After Step 11, run N autonomous improvement cycles. Each cycle is a full self-di
302
624
 
303
625
  #### 12a — Assess Current State
304
626
 
305
- Load fresh brain context (repeat Brain Load Procedure from `_protocol.md`). Then evaluate the project's current state by examining:
306
- - What was just completed (artifacts from the most recent run's output schema)
307
- - What the brain's `next_actions` entries suggest
627
+ Load fresh brain context (repeat Brain Load Procedure from `_protocol.md`). Load the persisted session state:
628
+
629
+ ```bash
630
+ REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
631
+ # Restore SESSION_ID — may be in a new shell context
632
+ SESSION_ID=$(jq -r '.sessionId // empty' "$REPO_ROOT/.monomind/sessions/current.json" 2>/dev/null)
633
+ [ -z "$SESSION_ID" ] && { echo "ERROR: SESSION_ID not found in current.json — cannot continue iteration."; exit 1; }
634
+
635
+ SESSION_FILE="$REPO_ROOT/.monomind/sessions/${SESSION_ID}.json"
636
+ SESSION_STATE="$REPO_ROOT/.monomind/sessions/current.json"
637
+ # Echo to stdout — bash variables don't survive tool call boundaries; only stdout is visible to the LLM
638
+ # Emit run summary (artifacts, next_actions, project_name) from the session file
639
+ jq '{artifacts:.artifacts,next_actions:.next_actions,project_name:.project_name}' "$SESSION_FILE" 2>/dev/null \
640
+ || echo '{"artifacts":[],"next_actions":[],"project_name":""}'
641
+
642
+ # Emit board_ids from current.json (not carried in SESSION_FILE) so Step 12c can look up board UUIDs
643
+ echo "--- board_ids (from current.json) ---"
644
+ jq '{board_ids:(.board_ids // {})}' "$SESSION_STATE" 2>/dev/null \
645
+ || echo '{"board_ids":{}}'
646
+ ```
647
+
648
+ Then evaluate the project's current state by examining:
649
+ - What was just completed (artifacts from the `artifacts` array printed above)
650
+ - What `next_actions` entries printed above suggest
308
651
  - What the `next_actions` from all domain outputs say
309
652
  - What the git diff shows (if applicable) — any test failures, TODOs, or incomplete work
310
653
  - What gaps exist relative to the original prompt's success criteria
@@ -333,16 +676,18 @@ Log this as a decision in the cycle's output schema with `confidence` set accord
333
676
 
334
677
  Execute the chosen activity by invoking the appropriate domain skill directly (Steps 4–10 of the main flow, condensed):
335
678
 
336
- - Test → `Skill("mastermind:build")` with a testing-focused prompt
337
- - Debug/Fix → `Skill("mastermind:build")` with the specific issue as prompt
338
- - Review → `Skill("mastermind:review")` with scope = artifacts from last run
339
- - Improve/Refactor → `Skill("mastermind:build")` with refactor prompt
340
- - Add feature → `Skill("mastermind:build")` with the next feature from next_actions
341
- - Research → `Skill("mastermind:research")` with the open question as prompt
342
- - Content/Docs → `Skill("mastermind:content")` with scope = new artifacts
343
- - Release → `Skill("mastermind:release")` with project scope
679
+ - Test → invoke `/mastermind:build` with a testing-focused prompt
680
+ - Debug/Fix → invoke `/mastermind:build` with the specific failing test or error as prompt
681
+ - Review → invoke `/mastermind:review` with scope = artifacts from last run
682
+ - Improve/Refactor → invoke `/mastermind:build` with refactor prompt
683
+ - Add feature → invoke `/mastermind:build` with the next feature from the `next_actions` array printed by the Step 12a output above
684
+ - Research → invoke `/mastermind:research` with the open question as prompt
685
+ - Content/Docs → invoke `/mastermind:content` with scope = new artifacts
686
+ - Release → invoke `/mastermind:release` with project scope
687
+
688
+ Always pass: the current brain_context, project_name (from the `project_name` field above), the relevant board_id (look up `.board_ids[<chosen_domain>]` from the `board_ids` map printed above), and mode = auto (iteration cycles never pause for confirmation).
344
689
 
345
- Always pass: the current brain_context, project_name, the relevant board_id, and mode = auto (iteration cycles never pause for confirmation).
690
+ **Constraint:** Only invoke domains whose board_id already exists in the `board_ids` map. If the chosen activity maps to a domain not in `board_ids` (e.g. `release` was not activated in Step 6), choose the next highest-priority activity whose domain IS in `board_ids`, or invoke `build` as the safe fallback — its board is almost always present.
346
691
 
347
692
  #### 12d — Brain Write
348
693