@monoes/monomindcli 1.9.14 → 1.9.16

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.
@@ -108,6 +108,8 @@ Complexity threshold for manager agent: any of these is true:
108
108
  - Has external dependencies (APIs, services)
109
109
  - Is estimated to take more than one conversation turn
110
110
 
111
+ **DOMAIN EXCEPTION — `idea`:** The `idea` domain MUST always be handled by the master invoking `Skill("mastermind:idea")` directly — NEVER by spawning a Task agent. Spawned agents do not have Skill tool access, so delegating `idea` to a Task agent silently degrades to raw analysis with no pipeline execution. After `mastermind:idea` returns, treat its output as the `idea` domain's unified output schema and proceed to the next domain.
112
+
111
113
  **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
114
 
113
115
  ```bash
@@ -164,95 +166,87 @@ If mode = auto: proceed immediately.
164
166
 
165
167
  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
168
 
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; }
169
+ **Board naming convention:** Every board is named `<project_name>-<domain>` (e.g. `factory-idea`, `factory-build`). This canonical name is stable across runs — mastermind finds the existing board instead of creating a new one every time.
172
170
 
171
+ ```bash
172
+ # Compatible with macOS bash 3.2 — no associative arrays, uses jq accumulation instead
173
173
  REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
174
174
  SESSION_STATE="$REPO_ROOT/.monomind/sessions/current.json"
175
175
 
176
- # Reload persisted context (this is a fresh shell; Step 3 wrote these)
177
176
  SESSION_ID=$(jq -r '.sessionId // empty' "$SESSION_STATE" 2>/dev/null)
178
177
  [ -z "$SESSION_ID" ] && { echo "ERROR: SESSION_ID missing in current.json — run Step 3 first"; exit 1; }
179
178
  project_name=$(jq -r '.project_name // ""' "$SESSION_STATE")
180
179
  [ -z "$project_name" ] && { echo "ERROR: project_name is empty in current.json — run Step 3 first"; exit 1; }
181
180
  resolved_prompt=$(jq -r '.prompt // ""' "$SESSION_STATE")
182
181
 
183
- # domains_needed: NOT yet in current.json at this point — must be LLM-substituted inline.
184
182
  # 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"
183
+ domains_needed="DOMAINS_LIST_HERE"
184
+ [ "$domains_needed" = "DOMAINS_LIST_HERE" ] && { echo "ERROR: LLM did not substitute DOMAINS_LIST_HERE"; exit 1; }
185
+ [ -z "$domains_needed" ] && { echo "ERROR: domains_needed is empty — nothing to do"; exit 1; }
186
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)
187
+ # Resolve space once find existing by exact name or create
188
+ space_id=$(monotask space list 2>/dev/null | awk -F'|' '{gsub(/^ +| +$/,"",$1);gsub(/^ +| +$/,"",$2);if($2==n)print $1}' n="$project_name" | head -1)
190
189
  [ -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
190
  [ -z "$space_id" ] && { echo "ERROR: Could not find or create space '$project_name'"; exit 1; }
191
+ echo "Space: $space_id ($project_name)"
192
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)
193
+ # jq accumulation state (replaces bash 4.3+ associative arrays)
194
+ state_patch='{}'
201
195
 
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
196
  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
197
+ canonical="${project_name}-${domain}"
198
+
199
+ # Find existing board by canonical name reuse across runs
200
+ board_id=$(monotask board list 2>/dev/null | awk -F'|' '{gsub(/^ +| +$/,"",$1);gsub(/^ +| +$/,"",$2);if($2==n)print $1}' n="$canonical" | head -1)
201
+
202
+ if [ -n "$board_id" ]; then
203
+ echo "Reusing board: $board_id ($canonical)"
204
+ # Fetch existing column IDs
205
+ cols_json=$(monotask column list "$board_id" --json 2>/dev/null || echo '[]')
206
+ todo_col=$(echo "$cols_json" | jq -r '[.[] | select(.title=="Todo" or .title=="Backlog")] | .[0].id // empty')
207
+ doing_col=$(echo "$cols_json" | jq -r '[.[] | select(.title=="Doing" or .title=="In Progress")] | .[0].id // empty')
208
+ done_col=$(echo "$cols_json" | jq -r '[.[] | select(.title=="Done")] | .[0].id // empty')
209
+ else
210
+ echo "Creating board: $canonical"
211
+ board_id=$(monotask board create --space "$space_id" "$canonical" --json 2>/dev/null | jq -r '.id // empty')
212
+ [ -z "$board_id" ] && { echo "ERROR: Failed to create board '$canonical'"; exit 1; }
213
+ monotask space boards add "$space_id" "$board_id" >/dev/null 2>&1 || true
214
+ todo_col=$(monotask column create "$board_id" "Todo" --json | jq -r '.id // empty')
215
+ [ -z "$todo_col" ] && { echo "ERROR: Failed to create Todo column for $domain"; exit 1; }
216
+ doing_col=$(monotask column create "$board_id" "Doing" --json | jq -r '.id // empty')
217
+ [ -z "$doing_col" ] && { echo "ERROR: Failed to create Doing column for $domain"; exit 1; }
218
+ done_col=$(monotask column create "$board_id" "Done" --json | jq -r '.id // empty')
219
+ [ -z "$done_col" ] && { echo "ERROR: Failed to create Done column for $domain"; exit 1; }
220
+ fi
224
221
 
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)
222
+ domain_goal=$(jq -r --arg d "$domain" '.domain_goals[$d] // empty' "$SESSION_STATE")
223
+ [ -z "$domain_goal" ] && domain_goal="$resolved_prompt"
238
224
 
225
+ state_patch=$(echo "$state_patch" | jq \
226
+ --arg d "$domain" --arg b "$board_id" \
227
+ --arg t "$todo_col" --arg g "$doing_col" --arg e "$done_col" \
228
+ --arg goal "$domain_goal" \
229
+ '.board_ids[$d]=$b | .todo_cols[$d]=$t | .doing_cols[$d]=$g | .done_cols[$d]=$e | .domain_goals[$d]=$goal')
230
+
231
+ echo "DOMAIN=$domain BOARD=$board_id TODO=$todo_col DOING=$doing_col DONE=$done_col"
232
+ done
233
+
234
+ # Persist to current.json — one atomic merge
239
235
  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"
236
+ --argjson patch "$state_patch" \
237
+ '. + $patch + {domains_needed:($domains | split(" ") | map(select(length>0)))}' \
238
+ "$SESSION_STATE" > "$SESSION_STATE.tmp" && mv "$SESSION_STATE.tmp" "$SESSION_STATE"
239
+ echo "Session state saved to current.json"
250
240
  ```
251
241
 
252
242
  ### Step 7 — Spawn Domain Managers
253
243
 
244
+ **BEFORE THIS STEP:** If `idea` is in `domains_needed`, invoke `Skill("mastermind:idea")` directly now (master context has Skill tool access). Pass the resolved prompt, project path, and mode. Write the result to `.monomind/sessions/<SESSION_ID>/idea.json` and mark the `idea` domain as handled. Do NOT include `idea` in the Task spawning below.
245
+
254
246
  **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
247
 
248
+ **BASH TOOL REQUIREMENT:** Domain managers must run `monotask` CLI commands. Only use subagent_types that include Bash in their tool list. If the registry returns an agent without Bash (e.g. `Product Manager`, `Backend Architect`), override it with `general-purpose` (which has all tools). Agents without Bash cannot create cards, emit curl events, or write session files — they will silently produce degraded output.
249
+
256
250
  **Phase A — Registry selection** (run as one Bash call; must complete before Phase C):
257
251
 
258
252
  ```bash
@@ -15,18 +15,18 @@ This file is a reference loaded by mastermind domain skills and master. It is NE
15
15
  Execute at the START of every mastermind run (master or standalone domain command). Load in this order:
16
16
 
17
17
  **Step A — Tier 3 core principles (all domains):**
18
- Call `mcp__monomind__agentdb_hierarchical-recall` with:
19
- - namespace: `mastermind:principles`
20
- - limit: 20
18
+ Try `mcp__monobrain__agentdb_hierarchical-recall` with query `"mastermind principles"`, topK 20.
19
+ If it returns `"AgentDB bridge not available"` or any error, fall back to:
20
+ `mcp__monobrain__memory_search` with query `"mastermind principles"`, namespace `"mastermind:principles"`, limit 20.
21
21
 
22
22
  **Step B — Tier 2 weekly summary for this domain:**
23
- Call `mcp__monomind__agentdb_context-synthesize` with:
24
- - namespace: `mastermind:<domain>:weekly`
25
- - query: [current prompt keywords]
23
+ Try `mcp__monobrain__agentdb_context-synthesize` with query `[current prompt keywords]`, maxEntries 10.
24
+ If it fails, fall back to:
25
+ `mcp__monobrain__memory_search` with query `[current prompt keywords]`, namespace `"mastermind:<domain>:weekly"`, limit 10.
26
26
 
27
27
  **Step C — Relevant graph nodes:**
28
- Call `mcp__monomind__monograph_query` with:
29
- - query: [3-5 keywords extracted from current prompt]
28
+ Call `mcp__monobrain__graphify_query` with question `[3-5 keywords extracted from current prompt]`, depth 2.
29
+ If the graph is not built yet (error: "No graph found"), skip this tier — continue without graph context.
30
30
 
31
31
  Combine all results into a **BRAIN CONTEXT** block. Insert this block before any planning, decomposition, or agent spawning step. Format:
32
32
 
@@ -55,13 +55,20 @@ score = confidence × (1 / (days_since_run + 1)) × log(uses + 1)
55
55
  - `uses`: 1 (first write)
56
56
 
57
57
  **Step 2 — Append to Tier 1 raw log:**
58
- Call `mcp__monomind__agentdb_hierarchical-store` with:
58
+ Try `mcp__monobrain__agentdb_hierarchical-store` with:
59
59
  - namespace: `mastermind:<domain>:raw`
60
60
  - content: [full unified output schema YAML from this run, as a string]
61
61
  - metadata: `{ score, project, run_id, date: ISO8601, domain }`
62
62
 
63
+ If AgentDB is unavailable, fall back to `mcp__monobrain__memory_store`:
64
+ - key: `mastermind:<domain>:run:<run_id>`
65
+ - value: [JSON-encoded unified output schema]
66
+ - namespace: `mastermind:<domain>:raw`
67
+ - tags: `["mastermind", "<domain>", "run"]`
68
+
63
69
  **Step 3 — Check weekly compaction trigger:**
64
- Call `mcp__monomind__agentdb_health` on namespace `mastermind:<domain>:raw`.
70
+ Try `mcp__monobrain__agentdb_health` on namespace `mastermind:<domain>:raw`.
71
+ If unavailable, call `mcp__monobrain__memory_stats` and check entry count manually.
65
72
  If `entry_count >= 20` OR `days_since_last_compaction >= 7`:
66
73
  1. Retrieve all Tier 1 entries since last compaction
67
74
  2. Produce a per-domain weekly summary (use LLM synthesis: "Summarize the key decisions, patterns, and lessons from these run logs in under 300 words")
@@ -201,25 +208,25 @@ Every mastermind run MUST emit structured events to the live dashboard via WebFe
201
208
  }
202
209
  ```
203
210
 
204
- ### How to Emit (WebFetch pattern)
205
-
206
- ```javascript
207
- WebFetch({
208
- url: "http://localhost:4242/api/mastermind/event",
209
- method: "POST",
210
- headers: { "Content-Type": "application/json" },
211
- body: JSON.stringify({
212
- type: "session:start",
213
- session: crypto.randomUUID(), // or generate a timestamp-based ID
214
- prompt: resolvedPrompt,
215
- mode: mode,
216
- project: process.cwd(), // REQUIRED: absolute path for multi-project support
217
- ts: Date.now()
218
- })
219
- })
211
+ ### How to Emit (curl-first — WebFetch is blocked for localhost in Claude Code runtimes)
212
+
213
+ **Always use curl via Bash.** WebFetch is restricted for `localhost` URLs in Claude Code agent runtimes and will return ECONNREFUSED even when the server is running. Use this pattern:
214
+
215
+ ```bash
216
+ curl -s -o /dev/null -X POST "http://localhost:4242/api/mastermind/event" \
217
+ -H "Content-Type: application/json" \
218
+ -d "$(jq -cn \
219
+ --arg sid "$SESSION_ID" \
220
+ --arg type "session:start" \
221
+ --arg prompt "$resolved_prompt" \
222
+ --arg mode "$mode" \
223
+ --arg proj "$(pwd)" \
224
+ '{type:$type,session:$sid,prompt:$prompt,mode:$mode,project:$proj,ts:(now*1000|floor)}')" || true
220
225
  ```
221
226
 
222
- **If the server is not running** (WebFetch returns an error), log a warning and continue — event logging is non-blocking and MUST NOT abort the run.
227
+ **Always append `|| true`** — event emission is non-blocking and MUST NOT abort the run.
228
+
229
+ **If Bash is unavailable** (e.g. the agent type has no Bash tool): skip dashboard events entirely. They are observability-only and do not affect pipeline correctness. The master context always has Bash and emits session:start, domain:dispatch, and session:complete on behalf of the run.
223
230
 
224
231
  **Session ID:** Generate once at session:start and reuse across all subsequent events for this run. A simple ID format: `mm-<ISO8601-compact>` (e.g. `mm-20260505T142300`).
225
232
 
@@ -240,53 +247,78 @@ WebFetch({
240
247
 
241
248
  **Always follow this exact order — never create a board without first ensuring a space exists.**
242
249
 
250
+ **Board naming convention:** Boards are named `<project_name>-<domain>` (e.g. `factory-idea`, `factory-build`). This canonical name is stable across runs — find the existing board first, create only if it does not exist.
251
+
243
252
  Every mastermind run that needs a task board MUST:
244
253
  1. Resolve the space (find existing or create new) — space name = `project_name`
245
- 2. Create the board within that space
246
- 3. Link the board to the space immediately after creation
247
- 4. Create columns (Todo Doing → Done)
248
- 5. Create cards within those columns
254
+ 2. Find existing board by canonical name `<project_name>-<domain>` or create it with `--space`
255
+ 3. Fetch column IDs from existing board OR create columns for new board
256
+ 4. Create cards within those columns
249
257
 
250
258
  If the user is running across multiple repos for the same project, they MUST use the same `project_name` so all boards land in one space.
251
259
 
252
260
  ### Canonical bash block (substitute `<domain>` with: build, marketing, ops, content, etc.)
253
261
 
254
262
  ```bash
255
- # Step 1 Resolve space (required; abort if it cannot be created)
263
+ # Compatible with macOS bash 3.2
256
264
  project_name="${project_name:-$(basename "$PWD")}"
257
- space_id=$(monotask space list 2>/dev/null | awk -F' \| ' -v n="$project_name" '$2==n{print $1}' | head -1)
258
- [ -z "$space_id" ] && space_id=$(monotask space create "$project_name" 2>&1 | grep -oE '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')
259
- [ -z "$space_id" ] && { echo "ERROR: Could not find or create space '$project_name' — verify monotask is installed (monotask --version)"; exit 1; }
265
+ canonical="${project_name}-<domain>"
260
266
 
261
- # Step 2Create board
262
- board_id=$(monotask board create "<domain>" --json | jq -r '.id // empty')
263
- [ -z "$board_id" ] && { echo "ERROR: Failed to create board '<domain>'"; exit 1; }
264
-
265
- # Step 3 — Link board to space immediately
266
- monotask space boards add "$space_id" "$board_id" >/dev/null 2>&1 || true
267
+ # Step 1Resolve space
268
+ space_id=$(monotask space list 2>/dev/null | awk -F'|' '{gsub(/^ +| +$/,"",$1);gsub(/^ +| +$/,"",$2);if($2==n)print $1}' n="$project_name" | head -1)
269
+ [ -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}')
270
+ [ -z "$space_id" ] && { echo "ERROR: Could not find or create space '$project_name' — verify monotask is installed (monotask --version)"; exit 1; }
267
271
 
268
- # Step 4Create columns
269
- todo_col=$(monotask column create "$board_id" "Todo" --json | jq -r '.id')
270
- doing_col=$(monotask column create "$board_id" "Doing" --json | jq -r '.id')
271
- done_col=$(monotask column create "$board_id" "Done" --json | jq -r '.id')
272
+ # Step 2Find existing board by canonical name or create
273
+ board_id=$(monotask board list 2>/dev/null | awk -F'|' '{gsub(/^ +| +$/,"",$1);gsub(/^ +| +$/,"",$2);if($2==n)print $1}' n="$canonical" | head -1)
274
+ if [ -n "$board_id" ]; then
275
+ # Step 3a Fetch column IDs from existing board
276
+ cols_json=$(monotask column list "$board_id" --json 2>/dev/null || echo '[]')
277
+ todo_col=$(echo "$cols_json" | jq -r '[.[] | select(.title=="Todo" or .title=="Backlog")] | .[0].id // empty')
278
+ doing_col=$(echo "$cols_json" | jq -r '[.[] | select(.title=="Doing" or .title=="In Progress")] | .[0].id // empty')
279
+ done_col=$(echo "$cols_json" | jq -r '[.[] | select(.title=="Done")] | .[0].id // empty')
280
+ else
281
+ # Step 3b — Create board and columns
282
+ board_id=$(monotask board create --space "$space_id" "$canonical" --json | jq -r '.id // empty')
283
+ [ -z "$board_id" ] && { echo "ERROR: Failed to create board '$canonical'"; exit 1; }
284
+ monotask space boards add "$space_id" "$board_id" >/dev/null 2>&1 || true
285
+ todo_col=$(monotask column create "$board_id" "Todo" --json | jq -r '.id')
286
+ doing_col=$(monotask column create "$board_id" "Doing" --json | jq -r '.id')
287
+ done_col=$(monotask column create "$board_id" "Done" --json | jq -r '.id')
288
+ fi
272
289
  ```
273
290
 
274
- When master.md runs multiple domains, resolve the space **once** before the loop, then repeat steps 2–4 per domain:
291
+ When master.md runs multiple domains, resolve the space **once** before the loop, then repeat steps 2–3 per domain using jq accumulation (no bash 4.3+ needed):
275
292
 
276
293
  ```bash
277
- # Resolve space once
294
+ # Compatible with macOS bash 3.2 — jq accumulation instead of declare -A
278
295
  project_name="<resolved project_name>"
279
- space_id=$(monotask space list 2>/dev/null | awk -F' \| ' -v n="$project_name" '$2==n{print $1}' | head -1)
280
- [ -z "$space_id" ] && space_id=$(monotask space create "$project_name" 2>&1 | grep -oE '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')
296
+ space_id=$(monotask space list 2>/dev/null | awk -F'|' '{gsub(/^ +| +$/,"",$1);gsub(/^ +| +$/,"",$2);if($2==n)print $1}' n="$project_name" | head -1)
297
+ [ -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}')
281
298
  [ -z "$space_id" ] && { echo "ERROR: Could not find or create space '$project_name'"; exit 1; }
282
299
 
283
- # Repeat per active domain (e.g. build, marketing, ops):
284
- board_<domain>=$(monotask board create "<domain>" --json | jq -r '.id // empty')
285
- [ -z "$board_<domain>" ] && { echo "ERROR: Failed to create <domain> board"; exit 1; }
286
- monotask space boards add "$space_id" "$board_<domain>" >/dev/null 2>&1 || true
287
- todo_<domain>=$(monotask column create "$board_<domain>" "Todo" --json | jq -r '.id')
288
- doing_<domain>=$(monotask column create "$board_<domain>" "Doing" --json | jq -r '.id')
289
- done_<domain>=$(monotask column create "$board_<domain>" "Done" --json | jq -r '.id')
300
+ state_patch='{}'
301
+ for domain in build marketing ops; do # substitute actual domain list
302
+ canonical="${project_name}-${domain}"
303
+ board_id=$(monotask board list 2>/dev/null | awk -F'|' '{gsub(/^ +| +$/,"",$1);gsub(/^ +| +$/,"",$2);if($2==n)print $1}' n="$canonical" | head -1)
304
+ if [ -n "$board_id" ]; then
305
+ cols_json=$(monotask column list "$board_id" --json 2>/dev/null || echo '[]')
306
+ todo_col=$(echo "$cols_json" | jq -r '[.[] | select(.title=="Todo" or .title=="Backlog")] | .[0].id // empty')
307
+ doing_col=$(echo "$cols_json" | jq -r '[.[] | select(.title=="Doing" or .title=="In Progress")] | .[0].id // empty')
308
+ done_col=$(echo "$cols_json" | jq -r '[.[] | select(.title=="Done")] | .[0].id // empty')
309
+ else
310
+ board_id=$(monotask board create --space "$space_id" "$canonical" --json | jq -r '.id // empty')
311
+ [ -z "$board_id" ] && { echo "ERROR: Failed to create $canonical board"; exit 1; }
312
+ monotask space boards add "$space_id" "$board_id" >/dev/null 2>&1 || true
313
+ todo_col=$(monotask column create "$board_id" "Todo" --json | jq -r '.id')
314
+ doing_col=$(monotask column create "$board_id" "Doing" --json | jq -r '.id')
315
+ done_col=$(monotask column create "$board_id" "Done" --json | jq -r '.id')
316
+ fi
317
+ state_patch=$(echo "$state_patch" | jq \
318
+ --arg d "$domain" --arg b "$board_id" \
319
+ --arg t "$todo_col" --arg g "$doing_col" --arg e "$done_col" \
320
+ '.board_ids[$d]=$b | .todo_cols[$d]=$t | .doing_cols[$d]=$g | .done_cols[$d]=$e')
321
+ done
290
322
  ```
291
323
 
292
324
  ---
@@ -105,6 +105,12 @@ COL_ELABORATED=$(echo "$columns"| jq -r '.[] | select(.title == "Elaborated") |
105
105
  COL_TASKED=$(echo "$columns" | jq -r '.[] | select(.title == "Tasked") | .id' | head -1)
106
106
  COL_ICED=$(echo "$columns" | jq -r '.[] | select(.title == "Iced") | .id' | head -1)
107
107
  COL_REJECTED=$(echo "$columns" | jq -r '.[] | select(.title == "Rejected") | .id' | head -1)
108
+ [ -z "$COL_NEW" ] && { echo "ERROR: Could not find 'New' column on BOARD_ID=$BOARD_ID"; exit 1; }
109
+ [ -z "$COL_EVALUATED" ] && { echo "ERROR: Could not find 'Evaluated' column on BOARD_ID=$BOARD_ID"; exit 1; }
110
+ [ -z "$COL_ELABORATED" ] && { echo "ERROR: Could not find 'Elaborated' column on BOARD_ID=$BOARD_ID"; exit 1; }
111
+ [ -z "$COL_TASKED" ] && { echo "ERROR: Could not find 'Tasked' column on BOARD_ID=$BOARD_ID"; exit 1; }
112
+ [ -z "$COL_ICED" ] && { echo "ERROR: Could not find 'Iced' column on BOARD_ID=$BOARD_ID"; exit 1; }
113
+ [ -z "$COL_REJECTED" ] && { echo "ERROR: Could not find 'Rejected' column on BOARD_ID=$BOARD_ID"; exit 1; }
108
114
  ```
109
115
 
110
116
  **After either branch above, validate and echo all values.** This is the canonical source for Step 4 Task construction.
@@ -173,7 +179,7 @@ echo "Selected specialists: $specialist_list"
173
179
  ```
174
180
 
175
181
  **CRITICAL — Variable substitution required before constructing the Task call:**
176
- The Task agent runs in an isolated context and cannot inherit shell variables. Before writing the Task prompt below, read the literal UUID values from the `=== IDEA BOARD LITERAL VALUES ===` echo block above and embed them as **hard-coded strings** in the prompt. Do NOT write `${BOARD_ID}`, `${COL_NEW}`, etc. in the prompt — the agent will receive those as literal dollar-sign strings and its `monotask card create` calls will fail, causing it to improvise with `monotask board create` and corrupt board names. Replace every `${BOARD_ID}`, `${COL_NEW}`, `${project_name}`, `${brain_context}`, `${prompt}`, `${date}`, and `${specialist_list}` with the actual value before calling Task.
182
+ The Task agent runs in an isolated context and cannot inherit shell variables. Before writing the Task prompt below, read the literal UUID values from the `=== IDEA BOARD LITERAL VALUES ===` echo block above and embed them as **hard-coded strings** in the prompt. Do NOT write `${BOARD_ID}`, `${COL_NEW}`, etc. in the prompt — the agent will receive those as literal dollar-sign strings and its `monotask card create` calls will fail, causing it to improvise with `monotask board create` and corrupt board names. Replace every `${BOARD_ID}`, `${COL_NEW}`, `${project_name}`, `${brain_context}`, `${prompt}`, `${date}`, and `${specialist_list}` with the actual value before calling Task. Leave `$result`, `$CARD_ID`, and other loop-internal variables as-is — they are bash variables the agent itself will set at runtime, not substitution targets.
177
183
 
178
184
  Spawn the Idea Manager with `run_in_background: false` so its output is available for Step 5.
179
185
 
@@ -242,8 +248,8 @@ For each unique idea, create one card in the New column (COL_NEW = ${COL_NEW}):
242
248
 
243
249
  result=$(monotask card create "${BOARD_ID}" "${COL_NEW}" "<idea title ≤80 chars>" --json)
244
250
  CARD_ID=$(echo "$result" | jq -r '.id // empty')
245
- monotask card comment add "${BOARD_ID}" "$CARD_ID" "DESCRIPTION: <2-3 sentence description>
246
- CATEGORY: <feature | technical-baseline | business-operation>
251
+ monotask card set-description "${BOARD_ID}" "$CARD_ID" "<2-3 sentence description>"
252
+ monotask card comment add "${BOARD_ID}" "$CARD_ID" "CATEGORY: <feature | technical-baseline | business-operation>
247
253
  SOURCE: <which specialist angle produced this>"
248
254
  monotask card label add "${BOARD_ID}" "$CARD_ID" "mastermind-idea"
249
255
  monotask card label add "${BOARD_ID}" "$CARD_ID" "category:<category>"
@@ -264,20 +270,29 @@ END_IDEAS_OUTPUT`
264
270
  })
265
271
  ```
266
272
 
267
- Parse the `IDEAS_OUTPUT` JSON block from the agent's response. If zero ideas were returned, report "Idea Manager produced no ideas." and STOP.
273
+ Parse the `IDEAS_OUTPUT` JSON block from the agent's response and assign it:
274
+ ```bash
275
+ ideas_output_json='<paste the JSON array from IDEAS_OUTPUT here>'
276
+ ```
277
+ If zero ideas were returned, report "Idea Manager produced no ideas." and STOP.
268
278
 
269
279
  ---
270
280
 
271
281
  ### Step 5 — Validation (Product Manager Evaluation)
272
282
 
283
+ **Build `ideas_list` before constructing the Step 5 Task prompt:**
284
+ ```bash
285
+ # Format the full IDEAS_OUTPUT array (from Step 4) as the literal string to embed in the Task prompt.
286
+ # Each line: card_id | title | description | category | source_angle
287
+ ideas_list=$(echo "$ideas_output_json" | jq -r \
288
+ '.[] | "- card_id: \(.card_id)\n title: \(.title)\n description: \(.description)\n category: \(.category)\n source: \(.source_angle)\n"')
289
+ ```
290
+
273
291
  **CRITICAL — Variable substitution required for Step 5 Task call:**
274
- The PM agent runs in an isolated Task context and cannot inherit shell variables. Read the literal UUID values from the `=== IDEA BOARD LITERAL VALUES ===` echo block (Step 3) and embed them as hard-coded strings in the Task prompt. Do NOT pass `${BOARD_ID}`, `${COL_EVALUATED}`, `${COL_ICED}`, `${COL_REJECTED}` etc. the agent will receive them as literal dollar-sign strings and its `monotask card move` / `monotask card set-impact` calls will silently fail.
292
+ Before constructing the Task prompt below, read the literal UUID values from the `=== IDEA BOARD LITERAL VALUES ===` echo block (Step 3) and embed them as hard-coded strings. Also embed the full `brain_context`, `prompt`, and `ideas_list` (built above) as literal text. Replace every `${BOARD_ID}`, `${COL_EVALUATED}`, `${COL_ICED}`, `${COL_REJECTED}`, `${brain_context}`, `${prompt}`, `${project_name}`, `${date}`, and `${ideas_list}` with its actual value before calling Task the agent receives unsubstituted `${...}` strings verbatim and silently skips every board update.
275
293
 
276
294
  Spawn a single `Product Manager` agent via the Task tool. The PM agent has Bash tool access and is responsible for both producing verdicts and executing all board updates directly.
277
295
 
278
- **CRITICAL — Variable substitution required for Step 5 Task call (same rule as Step 4):**
279
- Before constructing the Task prompt below, read the literal UUID values from the `=== IDEA BOARD LITERAL VALUES ===` echo block (Step 3) and embed them as hard-coded strings. Also embed the full `brain_context`, `prompt`, and the entire ideas list (with card IDs) as literal text. Do NOT write `${BOARD_ID}`, `${COL_EVALUATED}`, `${brain_context}`, etc. — the agent receives them as literal dollar-sign strings and silently skips every board update.
280
-
281
296
  ```javascript
282
297
  Task({
283
298
  subagent_type: "Product Manager",
@@ -347,7 +362,11 @@ END_VERDICTS_OUTPUT`
347
362
  })
348
363
  ```
349
364
 
350
- After the PM agent completes, parse the `VERDICTS_OUTPUT` block from the agent's response (schema is embedded in the Task prompt above). If **all** ideas are iced or rejected, output a summary table and STOP — skip Steps 6–7.
365
+ After the PM agent completes, parse the `VERDICTS_OUTPUT` block from the agent's response and assign it:
366
+ ```bash
367
+ verdicts_output_json='<paste the JSON array from VERDICTS_OUTPUT here>'
368
+ ```
369
+ This variable is used throughout Steps 6a and 6c to build idea lists, registry keywords, and inherit impact/effort scores — it must be set before proceeding. If **all** ideas are iced or rejected, output a summary table and STOP — skip Steps 6–7.
351
370
 
352
371
  ---
353
372
 
@@ -355,16 +374,29 @@ After the PM agent completes, parse the `VERDICTS_OUTPUT` block from the agent's
355
374
 
356
375
  #### 6a. Elaboration (conditional)
357
376
 
358
- For any evaluated idea with `skipElaboration: true`, move it directly to `Elaborated` and write a rationale comment so future readers understand why no deep elaboration was needed:
377
+ For any evaluated idea with `skipElaboration: true`, move it directly to `Elaborated`, set a description, and write a rationale comment so future readers understand why no deep elaboration was needed:
359
378
  ```bash
379
+ monotask card set-description "$BOARD_ID" "$CARD_ID" "Elaboration skipped — PM assessed this as straightforward.\n\nRationale: <rationale from VERDICTS_OUTPUT for this card_id>\n\nImpact: <impact>/10 | Effort: <effort>/10"
360
380
  monotask card move "$BOARD_ID" "$CARD_ID" "$COL_ELABORATED" --json
361
381
  monotask card comment add "$BOARD_ID" "$CARD_ID" "Elaboration skipped: PM assessed this idea as straightforward with no significant unknowns. Rationale: <rationale from VERDICTS_OUTPUT for this card_id>"
362
382
  ```
363
383
 
364
384
  For ideas with `skipElaboration: false`, **split by category** before spawning agents:
365
385
 
386
+ **Build `dev_ideas_list` and `ops_ideas_list` before constructing the Step 6a Task prompts:**
387
+ ```bash
388
+ # Filter VERDICTS_OUTPUT (parsed in Step 5) by category and format as literal text.
389
+ dev_ideas_list=$(echo "$verdicts_output_json" | jq -r \
390
+ '.[] | select(.verdict == "evaluated") | select(.category == "feature" or .category == "technical-baseline") |
391
+ "- card_id: \(.card_id)\n title: \(.title)\n category: \(.category)\n rationale: \(.rationale)\n"')
392
+
393
+ ops_ideas_list=$(echo "$verdicts_output_json" | jq -r \
394
+ '.[] | select(.verdict == "evaluated") | select(.category == "business-operation") |
395
+ "- card_id: \(.card_id)\n title: \(.title)\n category: \(.category)\n rationale: \(.rationale)\n"')
396
+ ```
397
+
366
398
  **CRITICAL — Variable substitution required for Step 6a Task calls:**
367
- Elaboration agents run in isolated Task contexts. Before constructing each Task prompt, replace every `${brain_context}`, `${dev_ideas_list}`, and `${ops_ideas_list}` with the actual literal text — `brain_context` from the brain load, `dev_ideas_list` and `ops_ideas_list` from the IDEAS_OUTPUT/VERDICTS_OUTPUT parsed in Steps 4–5 (filter by category). Do NOT leave any `${...}` placeholders in the prompt — the agent receives them as literal dollar-sign strings and produces empty ELABORATION_OUTPUT blocks.
399
+ Elaboration agents run in isolated Task contexts. Before constructing each Task prompt, replace every `${brain_context}`, `${dev_ideas_list}`, and `${ops_ideas_list}` with the actual literal text — `brain_context` from the brain load, `dev_ideas_list` and `ops_ideas_list` built above. Do NOT leave any `${...}` placeholders in the prompt — the agent receives them as literal dollar-sign strings and produces empty ELABORATION_OUTPUT blocks.
368
400
 
369
401
  **Dev ideas** (`feature` or `technical-baseline`):
370
402
  Spawn two agents in parallel via Task tool:
@@ -484,42 +516,81 @@ END_ELABORATION_OUTPUT`
484
516
  })
485
517
  ```
486
518
 
487
- After all agents complete, merge their outputs per idea (same card_id concatenate findings). For each idea, write findings to the card description and as comments, then move the card:
519
+ Wait for all background agents to complete (do not proceed until all or two, if only one category — return ELABORATION_OUTPUT blocks). Then build `merged_elaboration_json` by merging agent outputs per `card_id` and injecting `category` from `verdicts_output_json`:
488
520
 
489
521
  ```bash
490
- # Set the card description to the merged elaboration findings (this is the primary content field)
491
- monotask card set-description "$BOARD_ID" "$CARD_ID" "## Elaboration Findings
522
+ # Assign each agent's ELABORATION_OUTPUT JSON array from its output block:
523
+ dev_researcher_json='<ELABORATION_OUTPUT array from the dev researcher agent>'
524
+ dev_codebase_json='<ELABORATION_OUTPUT array from the dev code-explorer agent>'
525
+ ops_researcher_json='<ELABORATION_OUTPUT array from the ops researcher agent>'
526
+ ops_pm_json='<ELABORATION_OUTPUT array from the ops PM agent>'
527
+ # Use '[]' for any track that had no ideas (e.g. if all ideas were dev, set ops_* to '[]')
528
+
529
+ # Merge dev track: join researcher + codebase findings by card_id, inject category from verdicts
530
+ dev_merged=$(jq -n \
531
+ --argjson r "$dev_researcher_json" \
532
+ --argjson c "$dev_codebase_json" \
533
+ --argjson v "$verdicts_output_json" \
534
+ '[$r[] | {card_id: .card_id, blocking_issue: .blocking_issue,
535
+ researcher_findings: .findings,
536
+ codebase_findings: ([$c[] | select(.card_id == .card_id)] | first | .findings // ""),
537
+ category: ([$v[] | select(.card_id == .card_id)] | first | .category // "feature")}]')
538
+
539
+ # Merge ops track: join researcher + PM findings by card_id, inject category
540
+ ops_merged=$(jq -n \
541
+ --argjson r "$ops_researcher_json" \
542
+ --argjson p "$ops_pm_json" \
543
+ --argjson v "$verdicts_output_json" \
544
+ '[$r[] | {card_id: .card_id, blocking_issue: .blocking_issue,
545
+ researcher_findings: .findings,
546
+ pm_findings: ([$p[] | select(.card_id == .card_id)] | first | .findings // ""),
547
+ category: ([$v[] | select(.card_id == .card_id)] | first | .category // "business-operation")}]')
548
+
549
+ # Combine both tracks
550
+ merged_elaboration_json=$(jq -s 'add' <(echo "$dev_merged") <(echo "$ops_merged"))
551
+ ```
492
552
 
493
- ### Dev ideas Edge cases & prior art:
494
- <researcher findings>
553
+ Use process substitution (not a pipeline) so the while loop runs in the main shell and variables remain in scope after `done`:
495
554
 
496
- ### Dev ideas — Codebase constraints:
497
- <code-explorer findings>"
555
+ ```bash
556
+ while IFS= read -r idea; do
557
+ CARD_ID=$(echo "$idea" | jq -r '.card_id')
558
+ category=$(echo "$idea" | jq -r '.category')
559
+ blocking_issue=$(echo "$idea" | jq -r '.blocking_issue // empty')
498
560
 
499
- # OR for business-operation ideas:
500
- monotask card set-description "$BOARD_ID" "$CARD_ID" "## Elaboration Findings
561
+ if [ "$category" = "business-operation" ]; then
562
+ researcher_findings=$(echo "$idea" | jq -r '.researcher_findings')
563
+ pm_findings=$(echo "$idea" | jq -r '.pm_findings')
564
+ monotask card set-description "$BOARD_ID" "$CARD_ID" "## Elaboration Findings
501
565
 
502
566
  ### Industry context & benchmarks:
503
- <researcher findings>
567
+ $researcher_findings
504
568
 
505
569
  ### Feasibility & stakeholder impact:
506
- <PM findings>"
507
-
508
- # Also add each finding as a comment for traceability
509
- # Dev ideas:
510
- monotask card comment add "$BOARD_ID" "$CARD_ID" "Edge cases & prior art: <researcher findings>"
511
- monotask card comment add "$BOARD_ID" "$CARD_ID" "Codebase constraints: <code-explorer findings>"
512
-
513
- # Business-operation ideas:
514
- monotask card comment add "$BOARD_ID" "$CARD_ID" "Industry context & benchmarks: <researcher findings>"
515
- monotask card comment add "$BOARD_ID" "$CARD_ID" "Feasibility & stakeholder impact: <PM findings>"
516
-
517
- # If neither agent found a blocking issue:
518
- monotask card move "$BOARD_ID" "$CARD_ID" "$COL_ELABORATED" --json
519
-
520
- # If either agent found a blocking issue (mutually exclusive with the above):
521
- monotask card move "$BOARD_ID" "$CARD_ID" "$COL_ICED" --json
522
- monotask card comment add "$BOARD_ID" "$CARD_ID" "Blocked during elaboration: <issue>"
570
+ $pm_findings"
571
+ monotask card comment add "$BOARD_ID" "$CARD_ID" "Industry context & benchmarks: $researcher_findings"
572
+ monotask card comment add "$BOARD_ID" "$CARD_ID" "Feasibility & stakeholder impact: $pm_findings"
573
+ else
574
+ researcher_findings=$(echo "$idea" | jq -r '.researcher_findings')
575
+ codebase_findings=$(echo "$idea" | jq -r '.codebase_findings')
576
+ monotask card set-description "$BOARD_ID" "$CARD_ID" "## Elaboration Findings
577
+
578
+ ### Edge cases & prior art:
579
+ $researcher_findings
580
+
581
+ ### Codebase constraints:
582
+ $codebase_findings"
583
+ monotask card comment add "$BOARD_ID" "$CARD_ID" "Edge cases & prior art: $researcher_findings"
584
+ monotask card comment add "$BOARD_ID" "$CARD_ID" "Codebase constraints: $codebase_findings"
585
+ fi
586
+
587
+ if [ -n "$blocking_issue" ]; then
588
+ monotask card move "$BOARD_ID" "$CARD_ID" "$COL_ICED" --json
589
+ monotask card comment add "$BOARD_ID" "$CARD_ID" "Blocked during elaboration: $blocking_issue"
590
+ else
591
+ monotask card move "$BOARD_ID" "$CARD_ID" "$COL_ELABORATED" --json
592
+ fi
593
+ done < <(echo "$merged_elaboration_json" | jq -c '.[]')
523
594
  ```
524
595
 
525
596
  #### 6b. User Confirmation Gate
@@ -528,6 +599,10 @@ monotask card comment add "$BOARD_ID" "$CARD_ID" "Blocked during elaboration: <i
528
599
 
529
600
  **Confirm mode only:** If `mode` is `confirm` (default), present a review table of all elaborated ideas to the user.
530
601
 
602
+ Build the table from `verdicts_output_json` (filtered to `verdict == "evaluated"`). For each row:
603
+ - **Impact** and **Effort** come from `.impact` and `.effort` fields in `verdicts_output_json`
604
+ - **Track**: `feature` or `technical-baseline` → `dev`; `business-operation` → `ops`
605
+
531
606
  Print this exact format:
532
607
 
533
608
  ```
@@ -580,8 +655,9 @@ After applying all user instructions, proceed to Step 6c with the remaining idea
580
655
  REGISTRY=".monomind/registry.json"
581
656
 
582
657
  # Dev decomposition agent — pick the most relevant engineering/architecture specialist
583
- # Use the idea titles and descriptions as the keyword signal
584
- DEV_IDEA_CONTEXT="<concatenated titles+descriptions of all dev ideas>"
658
+ # Build keyword signal from elaborated dev idea titles (from VERDICTS_OUTPUT filtered above)
659
+ DEV_IDEA_CONTEXT=$(echo "$verdicts_output_json" | jq -r \
660
+ '[.[] | select(.verdict=="evaluated") | select(.category=="feature" or .category=="technical-baseline") | .title] | join(" ")')
585
661
  dev_decomp_agent=$(jq -r \
586
662
  --arg kw "$(echo "$DEV_IDEA_CONTEXT" | tr '[:upper:]' '[:lower:]' | grep -oE '[a-z]{5,}' | sort -u | tr '\n' ' ')" \
587
663
  '[ .agents[] | select(.deprecated != true)
@@ -601,7 +677,8 @@ dev_decomp_agent=$(jq -r \
601
677
  dev_decomp_agent="${dev_decomp_agent:-Software Architect}"
602
678
 
603
679
  # Ops decomposition agent — pick the most relevant strategy/sales/product specialist
604
- OPS_IDEA_CONTEXT="<concatenated titles+descriptions of all ops ideas>"
680
+ OPS_IDEA_CONTEXT=$(echo "$verdicts_output_json" | jq -r \
681
+ '[.[] | select(.verdict=="evaluated") | select(.category=="business-operation") | .title] | join(" ")')
605
682
  ops_decomp_agent=$(jq -r \
606
683
  --arg kw "$(echo "$OPS_IDEA_CONTEXT" | tr '[:upper:]' '[:lower:]' | grep -oE '[a-z]{5,}' | sort -u | tr '\n' ' ')" \
607
684
  '[ .agents[] | select(.deprecated != true)
@@ -624,30 +701,88 @@ echo "Dev decomp: $dev_decomp_agent | Ops decomp: $ops_decomp_agent"
624
701
  ```
625
702
 
626
703
  **CRITICAL — Variable substitution required for Step 6c Task calls:**
627
- Decomposition agents also run in isolated Task contexts. Before constructing each Task prompt, embed the literal card IDs (from the elaborated ideas list), `brain_context`, and `prompt` as hard-coded strings. Do NOT write `${card_id}`, `${brain_context}`, etc. as template placeholders.
704
+ Before constructing each Task prompt, replace `${brain_context}`, `${prompt}`, `${project_name}`, `${dev_ideas_elaborated}`, and `${ops_ideas_elaborated}` with actual literal text. Also substitute the `subagent_type` values: replace `dev_decomp_agent` and `ops_decomp_agent` with the string values echoed by the registry selection above (e.g. `"Software Architect"`). Build those lists from the VERDICTS_OUTPUT and ELABORATION_OUTPUT results:
628
705
 
629
- Each decomposition agent's Task prompt should begin with:
630
- > `SAFETY CHECK: You are a decomposition agent. You must NOT call monotask board create, space create, or column create. Your only job is producing a TASKS_OUTPUT block — the outer skill creates the cards. If you are unsure of any card ID, list it as "UNKNOWN" rather than inventing a value.`
706
+ ```bash
707
+ # Build elaborated idea lists including card comments (full context for decomposition agents)
708
+ dev_ideas_elaborated=$(echo "$verdicts_output_json" | jq -r \
709
+ '[.[] | select(.verdict=="evaluated") | select(.category=="feature" or .category=="technical-baseline") |
710
+ "card_id: \(.card_id)\ntitle: \(.title)\ncategory: \(.category)\nrationale: \(.rationale)\nimpact: \(.impact) effort: \(.effort)"] | join("\n\n")')
711
+
712
+ ops_ideas_elaborated=$(echo "$verdicts_output_json" | jq -r \
713
+ '[.[] | select(.verdict=="evaluated") | select(.category=="business-operation") |
714
+ "card_id: \(.card_id)\ntitle: \(.title)\ncategory: \(.category)\nrationale: \(.rationale)\nimpact: \(.impact) effort: \(.effort)"] | join("\n\n")')
715
+ ```
631
716
 
632
717
  **Spawn decomposition agents by track** — run both in parallel if both tracks have elaborated ideas:
633
718
 
634
- - For **dev ideas** (`feature` or `technical-baseline`): spawn the agent selected as `$dev_decomp_agent`.
635
- - For **business-operation ideas**: spawn the agent selected as `$ops_decomp_agent`.
719
+ ```javascript
720
+ // Dev decomposition agent (only if dev_ideas_elaborated is non-empty)
721
+ Task({
722
+ subagent_type: dev_decomp_agent, // value from registry selection above
723
+ description: "Task decomposition: dev ideas for " + project_name,
724
+ run_in_background: true,
725
+ prompt: `SAFETY CHECK: You are a decomposition agent. You must NOT call monotask board create, space create, or column create. Your only job is producing a TASKS_OUTPUT block — the outer skill creates the cards. If you are unsure of any card ID, list it as "UNKNOWN" rather than inventing a value.
636
726
 
637
- Provide each agent with:
638
- - Their subset of elaborated ideas (titles, descriptions, all card comments, literal card IDs, and category)
639
- - The literal `brain_context` and `prompt` values
727
+ Decompose the following dev ideas into concrete subtasks (2–6 per idea). Each subtask should be independently implementable.
640
728
 
641
- Each agent must output a `TASKS_OUTPUT` block. For each elaborated idea, produce 2–6 subtasks. If an idea's scope is unclear, flag it instead of decomposing.
729
+ PROJECT: ${project_name}
730
+ GOAL: ${prompt}
731
+
732
+ BRAIN CONTEXT:
733
+ ${brain_context}
734
+
735
+ DEV IDEAS TO DECOMPOSE:
736
+ ${dev_ideas_elaborated}
737
+
738
+ For each idea, produce 2–6 subtasks. If an idea's scope is unclear, flag it in FLAGGED instead of decomposing.
642
739
 
643
- ```
644
740
  TASKS_OUTPUT
645
741
  [
646
742
  {
647
- "parent_card_id": "<ideation board card ID>",
743
+ "parent_card_id": "<ideation board card ID from the list above>",
648
744
  "title": "<subtask title ≤80 chars>",
649
- "description": "<what to build/do>",
650
- "category": "feature | technical-baseline | business-operation",
745
+ "description": "<what to build/do — specific and actionable>",
746
+ "category": "feature | technical-baseline",
747
+ "agent": "<recommended subagent_type>",
748
+ "effort": <1-10>,
749
+ "has_prerequisites": <true | false>
750
+ }
751
+ ]
752
+ FLAGGED
753
+ [
754
+ { "card_id": "<ideation card ID>", "question": "<what needs clarifying>" }
755
+ ]
756
+ END_TASKS_OUTPUT`
757
+ })
758
+
759
+ // Ops decomposition agent (only if ops_ideas_elaborated is non-empty)
760
+ Task({
761
+ subagent_type: ops_decomp_agent, // value from registry selection above
762
+ description: "Task decomposition: ops ideas for " + project_name,
763
+ run_in_background: true,
764
+ prompt: `SAFETY CHECK: You are a decomposition agent. You must NOT call monotask board create, space create, or column create. Your only job is producing a TASKS_OUTPUT block — the outer skill creates the cards. If you are unsure of any card ID, list it as "UNKNOWN" rather than inventing a value.
765
+
766
+ Decompose the following business-operation ideas into concrete subtasks (2–6 per idea). Each subtask should be independently actionable.
767
+
768
+ PROJECT: ${project_name}
769
+ GOAL: ${prompt}
770
+
771
+ BRAIN CONTEXT:
772
+ ${brain_context}
773
+
774
+ OPS IDEAS TO DECOMPOSE:
775
+ ${ops_ideas_elaborated}
776
+
777
+ For each idea, produce 2–6 subtasks. If an idea's scope is unclear, flag it in FLAGGED instead of decomposing.
778
+
779
+ TASKS_OUTPUT
780
+ [
781
+ {
782
+ "parent_card_id": "<ideation board card ID from the list above>",
783
+ "title": "<subtask title ≤80 chars>",
784
+ "description": "<what to build/do — specific and actionable>",
785
+ "category": "business-operation",
651
786
  "agent": "<recommended subagent_type>",
652
787
  "effort": <1-10>,
653
788
  "has_prerequisites": <true | false>
@@ -657,7 +792,8 @@ FLAGGED
657
792
  [
658
793
  { "card_id": "<ideation card ID>", "question": "<what needs clarifying>" }
659
794
  ]
660
- END_TASKS_OUTPUT
795
+ END_TASKS_OUTPUT`
796
+ })
661
797
  ```
662
798
 
663
799
  **After both decomposition agents return**, the outer skill creates task cards on the appropriate board for each task's category. Each task card inherits the parent idea's `impact` and `effort` scores from the VERDICTS_OUTPUT parsed in Step 5 — look up by `parent_card_id`.
@@ -680,11 +816,14 @@ monotask column create "$TASK_BOARD_ID" "Human in Loop" --json >/dev/null
680
816
  monotask column create "$TASK_BOARD_ID" "Done" --json >/dev/null
681
817
  ```
682
818
 
683
- Look up column IDs:
819
+ Look up and validate column IDs:
684
820
  ```bash
821
+ [ -z "$TASK_BOARD_ID" ] && { echo "ERROR: TASK_BOARD_ID is empty — aborting to prevent card creation on null board"; exit 1; }
685
822
  task_columns=$(monotask column list "$TASK_BOARD_ID" --json)
686
823
  TASK_COL_TODO=$(echo "$task_columns" | jq -r '.[] | select(.title == "Todo") | .id' | head -1)
687
824
  TASK_COL_BACKLOG=$(echo "$task_columns" | jq -r '.[] | select(.title == "Backlog") | .id' | head -1)
825
+ [ -z "$TASK_COL_TODO" ] && { echo "ERROR: Could not find 'Todo' column on TASK_BOARD_ID=$TASK_BOARD_ID"; exit 1; }
826
+ [ -z "$TASK_COL_BACKLOG" ] && { echo "ERROR: Could not find 'Backlog' column on TASK_BOARD_ID=$TASK_BOARD_ID"; exit 1; }
688
827
  ```
689
828
 
690
829
  ---
@@ -705,19 +844,43 @@ monotask column create "$OPS_BOARD_ID" "Human in Loop" --json >/dev/null
705
844
  monotask column create "$OPS_BOARD_ID" "Done" --json >/dev/null
706
845
  ```
707
846
 
708
- Look up column IDs:
847
+ Look up and validate column IDs:
709
848
  ```bash
849
+ [ -z "$OPS_BOARD_ID" ] && { echo "ERROR: OPS_BOARD_ID is empty — aborting to prevent card creation on null board"; exit 1; }
710
850
  ops_columns=$(monotask column list "$OPS_BOARD_ID" --json)
711
851
  OPS_COL_TODO=$(echo "$ops_columns" | jq -r '.[] | select(.title == "Todo") | .id' | head -1)
712
852
  OPS_COL_BACKLOG=$(echo "$ops_columns" | jq -r '.[] | select(.title == "Backlog") | .id' | head -1)
853
+ [ -z "$OPS_COL_TODO" ] && { echo "ERROR: Could not find 'Todo' column on OPS_BOARD_ID=$OPS_BOARD_ID"; exit 1; }
854
+ [ -z "$OPS_COL_BACKLOG" ] && { echo "ERROR: Could not find 'Backlog' column on OPS_BOARD_ID=$OPS_BOARD_ID"; exit 1; }
713
855
  ```
714
856
 
715
857
  ---
716
858
 
717
- **Create task cards** for each task in the merged TASKS_OUTPUT:
859
+ **Assign TASKS_OUTPUT from each decomposition agent, then merge and iterate:**
718
860
 
719
861
  ```bash
720
- if [ "$category" = "business-operation" ]; then
862
+ # Parse the TASKS_OUTPUT block from the dev decomp agent's response and assign:
863
+ dev_tasks_json='<TASKS_OUTPUT JSON array from dev decomp agent, or [] if no dev ideas>'
864
+ # Parse the TASKS_OUTPUT block from the ops decomp agent's response and assign:
865
+ ops_tasks_json='<TASKS_OUTPUT JSON array from ops decomp agent, or [] if no ops ideas>'
866
+
867
+ merged_tasks_json=$(jq -s 'add' <(echo "$dev_tasks_json") <(echo "$ops_tasks_json"))
868
+
869
+ # Use process substitution (not a pipeline) so variables set inside the loop
870
+ # remain in scope after done — needed for the parent-annotation step below.
871
+ # Also initialise the subtask-accumulator map before the loop.
872
+ declare -A parent_subtask_summaries # parent_card_id -> newline-separated task summary lines
873
+
874
+ while IFS= read -r task; do
875
+ parent_card_id=$(echo "$task" | jq -r '.parent_card_id')
876
+ title=$(echo "$task" | jq -r '.title')
877
+ description=$(echo "$task" | jq -r '.description')
878
+ category=$(echo "$task" | jq -r '.category')
879
+ agent=$(echo "$task" | jq -r '.agent')
880
+ task_effort=$(echo "$task" | jq -r '.effort')
881
+ has_prerequisites=$(echo "$task" | jq -r '.has_prerequisites')
882
+
883
+ if [ "$category" = "business-operation" ]; then
721
884
  TARGET_BOARD="$OPS_BOARD_ID"
722
885
  COL_TARGET=$([ "$has_prerequisites" = "true" ] && echo "$OPS_COL_BACKLOG" || echo "$OPS_COL_TODO")
723
886
  BOARD_LABEL="Operations Tasks"
@@ -727,14 +890,20 @@ else
727
890
  BOARD_LABEL="Implementation Tasks"
728
891
  fi
729
892
 
893
+ # Inherit impact and effort from parent idea — look up from verdicts_output_json by parent_card_id
894
+ parent_impact=$(echo "$verdicts_output_json" | jq -r --arg id "$parent_card_id" '.[] | select(.card_id == $id) | .impact // 5')
895
+ parent_effort=$(echo "$verdicts_output_json" | jq -r --arg id "$parent_card_id" '.[] | select(.card_id == $id) | .effort // 5')
896
+ # Default to 5 if lookup returned empty (e.g. parent_card_id was "UNKNOWN")
897
+ [ -z "$parent_impact" ] && parent_impact=5
898
+ [ -z "$parent_effort" ] && parent_effort=5
899
+
730
900
  # Create task card as a proper subtask of the parent idea card (cross-board link)
731
901
  # Signature: subtask add <PARENT_BOARD_ID> <PARENT_CARD_ID> <CHILD_BOARD_ID> <COL_ID> <TITLE>
732
902
  TASK_CARD_ID=$(monotask card subtask add "$BOARD_ID" "$parent_card_id" "$TARGET_BOARD" "$COL_TARGET" "<task title>" --json | jq -r '.id')
733
903
  # Set the task description as the primary content field
734
904
  monotask card set-description "$TARGET_BOARD" "$TASK_CARD_ID" "<what to build/do — from TASKS_OUTPUT description>"
735
- # Inherit impact and effort from parent idea (looked up from VERDICTS_OUTPUT by parent_card_id)
736
- monotask card set-impact "$TARGET_BOARD" "$TASK_CARD_ID" <parent_impact>
737
- monotask card set-effort "$TARGET_BOARD" "$TASK_CARD_ID" <parent_effort>
905
+ monotask card set-impact "$TARGET_BOARD" "$TASK_CARD_ID" "$parent_impact"
906
+ monotask card set-effort "$TARGET_BOARD" "$TASK_CARD_ID" "$parent_effort"
738
907
  monotask card comment add "$TARGET_BOARD" "$TASK_CARD_ID" \
739
908
  "SOURCE: mastermind:idea | <first 100 chars of prompt>
740
909
  AGENT: <agent>
@@ -743,14 +912,22 @@ PARENT IDEA IMPACT: <parent_impact>/10 PARENT IDEA EFFORT: <parent_effort>/10
743
912
  CATEGORY: <category>
744
913
  PARENT IDEA: <idea title> (card: <parent_card_id> on ideation board)"
745
914
  monotask card label add "$TARGET_BOARD" "$TASK_CARD_ID" "mastermind:idea"
746
- monotask card label add "$TARGET_BOARD" "$TASK_CARD_ID" "category:<category>"
915
+ monotask card label add "$TARGET_BOARD" "$TASK_CARD_ID" "category:$category"
916
+
917
+ # Accumulate subtask summary for this parent (used in the post-loop annotation step)
918
+ parent_subtask_summaries[$parent_card_id]+=" - $title (agent: $agent, effort: $task_effort/10, board: $BOARD_LABEL)\n"
919
+
920
+ done < <(echo "$merged_tasks_json" | jq -c '.[]')
747
921
  ```
748
922
 
749
- Group tasks by `parent_card_id`. For each parent idea, annotate and move to `Tasked`:
923
+ After the loop, annotate each parent idea card and move it to `Tasked`:
750
924
  ```bash
751
- monotask card comment add "$BOARD_ID" "$parent_card_id" \
752
- "Subtasks created on <board_label>: <list of titles with agent and effort>"
753
- monotask card move "$BOARD_ID" "$parent_card_id" "$COL_TASKED" --json
925
+ for parent_card_id in "${!parent_subtask_summaries[@]}"; do
926
+ subtask_list="${parent_subtask_summaries[$parent_card_id]}"
927
+ monotask card comment add "$BOARD_ID" "$parent_card_id" \
928
+ "Subtasks created:\n${subtask_list}"
929
+ monotask card move "$BOARD_ID" "$parent_card_id" "$COL_TASKED" --json
930
+ done
754
931
  ```
755
932
 
756
933
  For each entry in FLAGGED, move the idea to `Iced`:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monoes/monomindcli",
3
- "version": "1.9.14",
3
+ "version": "1.9.16",
4
4
  "type": "module",
5
5
  "description": "Monomind CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
6
6
  "main": "dist/src/index.js",