monomind 2.3.3 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +37 -4
  2. package/package.json +1 -1
  3. package/packages/@monomind/cli/.claude/commands/mastermind/{approve.md → approvev1.md} +12 -9
  4. package/packages/@monomind/cli/.claude/commands/mastermind/help.md +18 -0
  5. package/packages/@monomind/cli/.claude/commands/mastermind/master.md +3 -3
  6. package/packages/@monomind/cli/.claude/commands/mastermind/runorg.md +19 -107
  7. package/packages/@monomind/cli/.claude/commands/mastermind/runorgv1.md +159 -0
  8. package/packages/@monomind/cli/.claude/helpers/handlers/route-handler.cjs +50 -4
  9. package/packages/@monomind/cli/.claude/helpers/handlers/session-restore-handler.cjs +25 -18
  10. package/packages/@monomind/cli/.claude/skills/mastermind-skills/agents.md +5 -1
  11. package/packages/@monomind/cli/.claude/skills/mastermind-skills/{approve.md → approvev1.md} +8 -5
  12. package/packages/@monomind/cli/.claude/skills/mastermind-skills/env.md +7 -2
  13. package/packages/@monomind/cli/.claude/skills/mastermind-skills/{heartbeat.md → heartbeatv1.md} +7 -4
  14. package/packages/@monomind/cli/.claude/skills/mastermind-skills/inbox.md +14 -2
  15. package/packages/@monomind/cli/.claude/skills/mastermind-skills/instance.md +3 -1
  16. package/packages/@monomind/cli/.claude/skills/mastermind-skills/org-settings.md +4 -1
  17. package/packages/@monomind/cli/.claude/skills/mastermind-skills/orgs.md +14 -13
  18. package/packages/@monomind/cli/.claude/skills/mastermind-skills/orgstatus.md +19 -8
  19. package/packages/@monomind/cli/.claude/skills/mastermind-skills/projects.md +3 -0
  20. package/packages/@monomind/cli/.claude/skills/mastermind-skills/runorg.md +37 -725
  21. package/packages/@monomind/cli/.claude/skills/mastermind-skills/runorgv1.md +731 -0
  22. package/packages/@monomind/cli/.claude/skills/mastermind-skills/stoporg.md +10 -3
  23. package/packages/@monomind/cli/.claude/skills/mastermind-skills/tasks.md +4 -1
  24. package/packages/@monomind/cli/README.md +37 -4
  25. package/packages/@monomind/cli/dist/src/commands/cleanup.js +30 -8
  26. package/packages/@monomind/cli/dist/src/commands/doc.js +36 -8
  27. package/packages/@monomind/cli/dist/src/commands/doctor-project-checks.js +23 -10
  28. package/packages/@monomind/cli/dist/src/commands/org-observe.js +30 -26
  29. package/packages/@monomind/cli/dist/src/commands/org.js +52 -2
  30. package/packages/@monomind/cli/dist/src/index.js +7 -0
  31. package/packages/@monomind/cli/dist/src/knowledge/document-pipeline.d.ts +2 -0
  32. package/packages/@monomind/cli/dist/src/knowledge/document-pipeline.js +148 -45
  33. package/packages/@monomind/cli/dist/src/memory/memory-bridge.d.ts +9 -0
  34. package/packages/@monomind/cli/dist/src/memory/memory-bridge.js +115 -68
  35. package/packages/@monomind/cli/dist/src/orgrt/daemon.js +45 -9
  36. package/packages/@monomind/cli/dist/src/orgrt/mailbox.d.ts +23 -0
  37. package/packages/@monomind/cli/dist/src/orgrt/mailbox.js +33 -1
  38. package/packages/@monomind/cli/dist/src/orgrt/migrate.d.ts +26 -0
  39. package/packages/@monomind/cli/dist/src/orgrt/migrate.js +111 -0
  40. package/packages/@monomind/cli/dist/src/orgrt/reporting.js +8 -1
  41. package/packages/@monomind/cli/dist/src/orgrt/session.js +8 -2
  42. package/packages/@monomind/cli/dist/src/ui/dashboard.html +44 -0
  43. package/packages/@monomind/cli/dist/src/ui/orgs.html +2 -2
  44. package/packages/@monomind/cli/dist/src/ui/server.mjs +25 -4
  45. package/packages/@monomind/cli/package.json +2 -2
@@ -0,0 +1,731 @@
1
+ ---
2
+ name: mastermind-runorgv1
3
+ description: "LEGACY-ORG-V1 (2026-07): superseded by `monomind org run <name>` (SDK Org Runtime v2), delegated to by default via `/mastermind:runorg`. Mastermind runorgv1 — load a saved org definition and start it as a persistent, prompt-orchestrated agent organization. The boss agent coordinates all roles, agents pick up tasks from a shared board, and the org runs until stopped. Reach it only via `/mastermind:runorgv1`."
4
+ type: domain-skill
5
+ default_mode: auto
6
+ ---
7
+
8
+ # Mastermind Run Org (v1, legacy)
9
+
10
+ > **LEGACY-ORG-V1 — prompt-orchestrated path. Reach it only via `/mastermind:runorgv1`. For everything else use `/mastermind:runorg` (v2 delegator).**
11
+ > This prompt-orchestrated path (Task-tool-spawned boss, monotask board, `ORG_VARS`/`runcontext.json`-style variable threading, manual dashboard-event curl calls) has no delivery guarantees and no ground-truth event stream — the v2 daemon spawns every role as its own live SDK session, routes messages directly via the `org_send` tool, and forwards dashboard events itself. It remains only for orgs not yet migrated off the v1 `topology`/`communication`/`board_id` config shape. New orgs created with `/mastermind:createorg` are already v2-shaped and MUST be started with `monomind org run <name>` — do not route them through this skill.
12
+
13
+ This skill is invoked by `mastermind:runorgv1` or directly via `/mastermind:runorgv1`.
14
+
15
+ ---
16
+
17
+ ## Inputs
18
+
19
+ - `brain_context`: BRAIN CONTEXT block (injected by command, or loaded below if standalone)
20
+ - `org_name`: name of the org to run (matches a file at `.monomind/orgs/<org_name>.json`)
21
+ - `session_id`: session ID generated by the command
22
+ - `task`: optional override task/goal for this run (if omitted, uses org's stored goal)
23
+ - `caller`: command | master (controls whether brain load/write runs here)
24
+
25
+ ---
26
+
27
+ ## Step 0 — Brain Load (standalone only)
28
+
29
+ If `caller` is not "command", load brain context following _protocol.md Brain Load Procedure with namespace: `ops`.
30
+
31
+ ---
32
+
33
+ ## Step 1 — Load Org Config
34
+
35
+ Read `.monomind/orgs/<org_name>.json`. If the file does not exist:
36
+ ```bash
37
+ orgs=$(ls .monomind/orgs/*.json 2>/dev/null | grep -vE -- '-approvals|-state|-activity|-goals|-routines|-projects|-members|-issues|-workspaces|-worktrees|-environments|-plugins|-adapters|-bootstrap|-threads|-budgets|-project-workspaces|-approval-comments' | xargs -I{} basename {} .json)
38
+ if [ -z "$orgs" ]; then
39
+ echo "No saved orgs found. Run /mastermind:createorg to define one."
40
+ else
41
+ echo "Available orgs: $orgs"
42
+ fi
43
+ ```
44
+ Return `status: blocked` with message: "Org '<org_name>' not found. Run /mastermind:createorg to define it."
45
+
46
+ **v2 config guard — check this before anything else.** `/mastermind:createorg` now writes v2-shaped configs with no `topology`/`board_id`/`communication` field at all:
47
+ ```bash
48
+ is_v2=$(jq -r 'if has("topology") or has("board_id") then "no" else "yes" end' ".monomind/orgs/${org_name}.json")
49
+ ```
50
+ If `is_v2 == "yes"`, **stop and return `status: blocked`** with message: "Org '<org_name>' is a v2 config (Org Runtime v2) — this skill only understands the deprecated v1 board/topology shape. Start it with: `monomind org run <org_name>` (or `monomind org serve` if it has a `schedule`)." Do not attempt to derive a topology, spawn a boss via Task tool, or touch a monotask board for a v2 config — every field this skill reads from Step 2 onward (`topology`, `board_id`, `todo_col_id`, `communication`) is simply absent there.
51
+
52
+ Only proceed past this guard for a genuine legacy v1 config (has `topology` and/or `board_id`).
53
+
54
+ Validate the config has at minimum: `name`, `goal`, `roles` (non-empty array). The `communication` field is optional; fall back to `edges` if absent (both have the same schema: `from`, `to`, `type`).
55
+
56
+ ---
57
+
58
+ ## Step 1.5 — Detect Scheduled Org
59
+
60
+ After loading the org config, check whether this is a scheduled (loop) org:
61
+
62
+ ```bash
63
+ orgFile=".monomind/orgs/${org_name}.json"
64
+ has_schedule=$(jq -r 'if .loop.poll_interval_minutes then "yes" else "no" end' "$orgFile")
65
+ ```
66
+
67
+ If `has_schedule == "yes"`, this is a **scheduled org**. Follow the **Scheduled Org Path** below (Steps 1.6 onward) instead of the standard Steps 2–8.
68
+
69
+ If `has_schedule == "no"`, skip to **Step 2** (standard persistent daemon path).
70
+
71
+ ---
72
+
73
+ ## Step 1.6 — Scheduled Org Path
74
+
75
+ This section handles orgs created with `--schedule`. The loop is owned by the org — it starts with `runorgv1`, runs each iteration via the loop prompt file, and stops with `stoporg`.
76
+
77
+ ### 1.6.1 — Guard against double-activation, then set status to "active"
78
+
79
+ ```bash
80
+ orgFile=".monomind/orgs/${org_name}.json"
81
+ current_loop_status=$(jq -r '.status // "stopped"' "$orgFile")
82
+ ```
83
+
84
+ If `current_loop_status == "active"`:
85
+ - Print:
86
+ ```
87
+ WARNING: Org '<org_name>' is already active. Starting a second instance would create parallel loop chains.
88
+ To restart cleanly: run /mastermind:stoporg --org <org_name> first, then /mastermind:runorgv1 --org <org_name>.
89
+ To force restart anyway, set status to "stopped" in .monomind/orgs/<org_name>.json and re-run.
90
+ ```
91
+ - Return `status: blocked`. **STOP — do not proceed.**
92
+
93
+ If `current_loop_status == "paused"`:
94
+ - Print: "Org '<org_name>' was paused. Reactivating..."
95
+ - Proceed (set to "active" below).
96
+
97
+ For all other statuses (stopped, or absent):
98
+
99
+ ```bash
100
+ tmp="${orgFile}.tmp"
101
+ jq '.status = "active"' "$orgFile" > "$tmp" && mv "$tmp" "$orgFile"
102
+ ```
103
+
104
+ ### 1.6.2 — Derive scheduled-org variables
105
+
106
+ ```bash
107
+ orgName=$(jq -r '.name' "$orgFile")
108
+ goal="${task:-$(jq -r '.goal' "$orgFile")}"
109
+ sessionId="$session_id"
110
+ poll_interval_minutes=$(jq -r '.loop.poll_interval_minutes' "$orgFile")
111
+ poll_interval_seconds=$(( poll_interval_minutes * 60 ))
112
+ run_prompt_file=$(jq -r '.loop.run_prompt_file' "$orgFile")
113
+ REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
114
+ CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
115
+ ```
116
+
117
+ ### 1.6.3 — Emit org:start event
118
+
119
+ ```bash
120
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
121
+ -H "Content-Type: application/json" \
122
+ -d "$(jq -cn \
123
+ --arg session "$sessionId" \
124
+ --arg org "$orgName" \
125
+ --arg goal "$goal" \
126
+ --arg proj "$REPO_ROOT" \
127
+ --argjson ci "${CHECKPOINT_INTERVAL_MS:-600000}" \
128
+ '{type:"org:start",session:$session,org:$org,goal:$goal,project:$proj,checkpointInterval:$ci,ts:(now*1000|floor)}')" || true
129
+ ```
130
+
131
+ ### 1.6.4 — Execute first iteration inline
132
+
133
+ Read the loop prompt file and execute its instructions directly (this IS the first iteration):
134
+
135
+ ```bash
136
+ [ ! -f "$run_prompt_file" ] && {
137
+ echo "ERROR: Loop prompt file '${run_prompt_file}' not found."
138
+ echo "Re-run: /mastermind:createorg --name ${orgName} --schedule \"every ${poll_interval_minutes} minutes\" to regenerate it."
139
+ exit 1
140
+ }
141
+ ```
142
+
143
+ Execute the steps described in `$run_prompt_file` — the file contains the full iteration instructions for this org. Follow them exactly, starting from Step 0 (status gate) through the org-specific work steps.
144
+
145
+ Since this is the first run and status was just set to "active", Step 0 will pass. The org-specific iteration steps run normally.
146
+
147
+ ### 1.6.5 — Schedule next iteration via ScheduleWakeup
148
+
149
+ After the iteration completes, re-check status and schedule the next wakeup:
150
+
151
+ ```bash
152
+ LOOP_STATUS=$(jq -r '.status // "stopped"' "$orgFile" 2>/dev/null || echo "stopped")
153
+ ```
154
+
155
+ If `LOOP_STATUS == "active"` or `LOOP_STATUS == "paused"`:
156
+
157
+ 1. Read the loop prompt:
158
+ ```bash
159
+ LOOP_PROMPT=$(cat "$run_prompt_file")
160
+ ```
161
+
162
+ 2. Emit `org:stop` to signal this iteration is pausing:
163
+ ```bash
164
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
165
+ -H "Content-Type: application/json" \
166
+ -d "$(jq -cn \
167
+ --arg org "$orgName" \
168
+ --arg runId "run-$(date -u +%Y%m%dT%H%M%S)" \
169
+ '{type:"org:stop",org:$org,runId:$runId,ts:(now*1000|floor),reason:"scheduled-iteration-complete"}')" || true
170
+ ```
171
+
172
+ 3. Call `ScheduleWakeup` with:
173
+ - `delaySeconds`: `$poll_interval_seconds`
174
+ - `reason`: `"<orgName>: next scheduled poll (every <poll_interval_minutes> min)"`
175
+ - `prompt`: full contents of `$LOOP_PROMPT`
176
+
177
+ 4. Update `next_run` in org JSON:
178
+ ```bash
179
+ next_ts=$(( $(date +%s) + poll_interval_seconds ))
180
+ next_iso=$(date -u -r "$next_ts" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
181
+ || date -u -d "@$next_ts" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
182
+ || python3 -c "import datetime; print(datetime.datetime.utcfromtimestamp($next_ts).strftime('%Y-%m-%dT%H:%M:%SZ'))")
183
+ tmp="${orgFile}.tmp"
184
+ jq --arg next "$next_iso" '.loop.next_run = $next' "$orgFile" > "$tmp" && mv "$tmp" "$orgFile"
185
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
186
+ -H "Content-Type: application/json" \
187
+ -d "$(jq -cn --arg org "$orgName" --arg next "$next_iso" --arg proj "$REPO_ROOT" \
188
+ '{type:"org:loop:scheduled",org:$org,next_run:$next,project:$proj,ts:(now*1000|floor)}')" || true
189
+ ```
190
+
191
+ If `LOOP_STATUS` is anything else → print "Org was stopped during this run — loop will not continue." No ScheduleWakeup.
192
+
193
+ ### 1.6.6 — Report to user
194
+
195
+ ```
196
+ ╔══════════════════════════════════════════════════════════╗
197
+ ║ ORG STARTED: <orgName> ║
198
+ ║ GOAL: <goal> ║
199
+ ║ MODE: scheduled loop ║
200
+ ╚══════════════════════════════════════════════════════════╝
201
+
202
+ SCHEDULE
203
+ ────────
204
+ Interval: every <poll_interval_minutes> minutes
205
+ Status: active
206
+ Next run: <next_iso (or "see .monomind/orgs/<orgName>.json")>
207
+
208
+ To stop: /mastermind:stoporg --org <orgName>
209
+ Status: /mastermind:orgstatus --org <orgName>
210
+ All orgs: /mastermind:orgs
211
+ ```
212
+
213
+ ### 1.6.7 — Return (skip Steps 2–8)
214
+
215
+ ```yaml
216
+ domain: ops
217
+ status: complete
218
+ decisions:
219
+ - what: "Scheduled org <orgName> activated, first iteration complete, next wakeup in <poll_interval_minutes> min"
220
+ why: "Loop started by runorgv1; ScheduleWakeup queued"
221
+ confidence: 0.9
222
+ outcome: pending
223
+ next_actions:
224
+ - "Stop: /mastermind:stoporg --org <orgName>"
225
+ - "Status: /mastermind:orgstatus --org <orgName>"
226
+ run_id: "<runId>"
227
+ ```
228
+
229
+ **STOP HERE** — do not execute Steps 2–8 for scheduled orgs.
230
+
231
+ ---
232
+
233
+ ## Step 2 — Derive Variables
234
+
235
+ Before spawning any agents, derive all variables from the loaded config. These are used in Steps 3 and 4.
236
+
237
+ ```
238
+ orgConfig = parsed JSON from .monomind/orgs/<org_name>.json
239
+ orgName = orgConfig.name
240
+ goal = task (if provided) or orgConfig.goal
241
+ sessionId = provided session_id input
242
+ stopFile = `.monomind/orgs/.stops/${orgName}.stop`
243
+ memNs = `org:${orgName}`
244
+ topology = orgConfig.topology
245
+ checkpointMin = orgConfig.run_config?.checkpoint_interval_min ?? 30
246
+ ```
247
+
248
+ Find the boss role — use filter to detect ambiguity before dereferencing:
249
+
250
+ ```
251
+ rootRoles = orgConfig.roles.filter(r => r.reports_to === null || r.reports_to === undefined)
252
+ ```
253
+
254
+ Abort with `status: blocked` if `rootRoles.length === 0` — message: "Org '<orgName>' has no root role; exactly one role must have reports_to: null."
255
+
256
+ Abort with `status: blocked` if `rootRoles.length > 1` — message: "Org '<orgName>' has multiple root roles; exactly one must have reports_to: null."
257
+
258
+ ```
259
+ bossRole = rootRoles[0]
260
+ bossRole_id = bossRole.id
261
+ bossRole_title = bossRole.title
262
+ bossRole_agent_type = bossRole.agent_type
263
+ directReports = orgConfig.roles.filter(r => r.reports_to === bossRole.id)
264
+ runId = new Date().toISOString()
265
+ ```
266
+
267
+ Also validate that every role has non-empty `id`, `title`, `agent_type`, and `responsibilities` (array). Abort with `status: blocked` listing any malformed role — do this before Step 3 initializes infrastructure.
268
+
269
+ After the conceptual derivations above, **run the ENTIRE Step 2 + Step 3 setup as a SINGLE Bash tool call** so all variables persist through infrastructure creation. Copy the full script below into one Bash call — do NOT split it across multiple calls:
270
+
271
+ ```bash
272
+ # ── STEP 2+3 COMBINED (must run as one Bash call — variables must persist) ──
273
+ orgFile=".monomind/orgs/${org_name}.json"
274
+ orgName=$(jq -r '.name' "$orgFile")
275
+ goal="${task:-$(jq -r '.goal' "$orgFile")}"
276
+ sessionId="$session_id"
277
+ stopFile=".monomind/orgs/.stops/${orgName}.stop"
278
+ memNs="org:${orgName}"
279
+ topology=$(jq -r '.topology' "$orgFile")
280
+ checkpointMin=$(jq -r '.run_config.checkpoint_interval_min // 30' "$orgFile")
281
+ bossRole_id=$(jq -r '[(.roles // [])[] | select(.reports_to == null or .reports_to == "")][0].id' "$orgFile")
282
+ bossRole_title=$(jq -r '[(.roles // [])[] | select(.reports_to == null or .reports_to == "")][0].title' "$orgFile")
283
+ bossRole_agent_type=$(jq -r '[(.roles // [])[] | select(.reports_to == null or .reports_to == "")][0].agent_type' "$orgFile")
284
+ if [ -z "$bossRole_id" ] || [ "$bossRole_id" = "null" ]; then
285
+ echo "ERROR: Org '${org_name}' has no root role (reports_to: null). Check the org config."
286
+ exit 1
287
+ fi
288
+ board_id=$(jq -r '.board_id // empty' "$orgFile")
289
+ todo_col=$(jq -r '.todo_col_id // empty' "$orgFile")
290
+ doing_col=$(jq -r '.doing_col_id // empty' "$orgFile")
291
+ done_col=$(jq -r '.done_col_id // empty' "$orgFile")
292
+ runId="run-$(date -u +%Y%m%dT%H%M%S)"
293
+
294
+ # Validate board IDs (warning only — boss can still scan/fix code without a board)
295
+ [ -z "$board_id" ] && echo "WARN: board_id missing from org config — task board commands will be skipped by boss"
296
+
297
+ # Remove any stale stop file (MUST happen before boss spawns)
298
+ mkdir -p .monomind/orgs/.stops
299
+ rm -f "$stopFile"
300
+
301
+ # Initialize org state file
302
+ stateFile=".monomind/orgs/${orgName}-state.json"
303
+ if [ ! -f "$stateFile" ]; then
304
+ jq -n --arg org "$orgName" --arg runId "$runId" \
305
+ '{org:$org,run_id:$runId,started_at:(now|todate),agents:{}}' > "$stateFile"
306
+ else
307
+ tmp="${stateFile}.tmp"
308
+ jq --arg runId "$runId" '.run_id = $runId | .started_at = (now|todate)' \
309
+ "$stateFile" > "$tmp" && mv "$tmp" "$stateFile"
310
+ fi
311
+
312
+ # Resolve git-safe monomind directory
313
+ REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
314
+ CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
315
+ GIT_COMMON=$(git rev-parse --git-common-dir 2>/dev/null || true)
316
+ # Non-git project: never fabricate a .git/ dir — fall back to .monomind
317
+ [ -z "$GIT_COMMON" ] && GIT_COMMON="__NOGIT__"
318
+ if [[ "$GIT_COMMON" != /* ]]; then GIT_COMMON="$REPO_ROOT/$GIT_COMMON"; fi
319
+ GIT_COMMON="${GIT_COMMON%/}"
320
+ if [ "$GIT_COMMON" = "__NOGIT__" ] || [ "${GIT_COMMON#*__NOGIT__}" != "$GIT_COMMON" ]; then MONO_DIR="$REPO_ROOT/.monomind"; else MONO_DIR="${GIT_COMMON}/monomind"; fi
321
+
322
+ # Create run file and write run:start
323
+ runDir="${MONO_DIR}/orgs/${orgName}/runs"
324
+ mkdir -p "$runDir"
325
+ runFile="${runDir}/${runId}.jsonl"
326
+ jq -cn \
327
+ --arg runId "$runId" --arg org "$orgName" --arg goal "$goal" \
328
+ --arg bossRole "$bossRole_id" --arg proj "$REPO_ROOT" \
329
+ '{type:"run:start",runId:$runId,org:$org,orgName:$org,goal:$goal,bossRole:$bossRole,project:$proj,ts:(now*1000|floor)}' \
330
+ >> "$runFile"
331
+
332
+ # Emit session:start first — creates the chat tab session record and writes active-session.json
333
+ # so capture-handler can tag all subsequent agent:spawn / agent:complete events with this session
334
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
335
+ -H "Content-Type: application/json" \
336
+ -d "$(jq -cn \
337
+ --arg session "$sessionId" \
338
+ --arg org "$orgName" \
339
+ --arg prompt "$goal" \
340
+ '{type:"session:start",session:$session,org:$org,prompt:$prompt,ts:(now*1000|floor)}')" || true
341
+
342
+ # Emit org:start — must run here while CTRL_URL/runId/orgName are in scope
343
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
344
+ -H "Content-Type: application/json" \
345
+ -d "$(jq -cn \
346
+ --arg session "$sessionId" \
347
+ --arg org "$orgName" \
348
+ --arg runId "$runId" \
349
+ --arg goal "$goal" \
350
+ --arg proj "$REPO_ROOT" \
351
+ --argjson ci "${CHECKPOINT_INTERVAL_MS:-600000}" \
352
+ '{type:"org:start",session:$session,org:$org,runId:$runId,goal:$goal,project:$proj,checkpointInterval:$ci,ts:(now*1000|floor)}')" || true
353
+
354
+ # Write context file so Step 3 can read values even if Bash output is truncated
355
+ contextFile="${MONO_DIR}/orgs/${orgName}/runcontext.json"
356
+ jq -cn \
357
+ --arg orgName "$orgName" --arg runId "$runId" --arg sessionId "$sessionId" \
358
+ --arg goal "$goal" --arg bossRole_id "$bossRole_id" --arg bossRole_title "$bossRole_title" \
359
+ --arg bossRole_agent_type "$bossRole_agent_type" --arg board_id "$board_id" \
360
+ --arg todo_col "$todo_col" --arg doing_col "$doing_col" --arg done_col "$done_col" \
361
+ --arg checkpointMin "$checkpointMin" --arg memNs "$memNs" --arg CTRL_URL "$CTRL_URL" \
362
+ --arg MONO_DIR "$MONO_DIR" --arg runFile "$runFile" --arg REPO_ROOT "$REPO_ROOT" \
363
+ '{orgName:$orgName,runId:$runId,sessionId:$sessionId,goal:$goal,bossRole_id:$bossRole_id,bossRole_title:$bossRole_title,bossRole_agent_type:$bossRole_agent_type,board_id:$board_id,todo_col:$todo_col,doing_col:$doing_col,done_col:$done_col,checkpointMin:$checkpointMin,memNs:$memNs,CTRL_URL:$CTRL_URL,MONO_DIR:$MONO_DIR,runFile:$runFile,REPO_ROOT:$REPO_ROOT}' \
364
+ > "$contextFile"
365
+ echo "RUNCONTEXT: $contextFile"
366
+ ```
367
+
368
+ After the script completes, note the `RUNCONTEXT:` path it printed. Then run a second Bash call to read it — this survives output truncation:
369
+
370
+ ```bash
371
+ cat "<RUNCONTEXT path from above>"
372
+ ```
373
+
374
+ Use the JSON values from that file for Step 3. Do NOT rely on any `ORG_VARS:`-style stdout from the script above — long bash output gets truncated and the values would be lost (see FA-1 in the org-dashboard-v2 ADR).
375
+
376
+ ---
377
+
378
+ ## Step 3 — Spawn Boss Agent
379
+
380
+ Using values from the context file (not shell variables — they don't persist), spawn the boss agent via Task tool:
381
+
382
+ ```javascript
383
+ Task({
384
+ subagent_type: bossRole.agent_type,
385
+ description: `You are the ${bossRole.title} of org "${orgName}". You run persistently until stopped.`,
386
+ run_in_background: true,
387
+ prompt: `You are the ${bossRole.title} of the autonomous organization "${orgName}".
388
+
389
+ ORG GOAL: ${goal}
390
+
391
+ YOUR ROLE: ${bossRole.title}
392
+ YOUR RESPONSIBILITIES: ${bossRole.responsibilities.join('; ')}
393
+
394
+ YOUR TEAM (direct reports):
395
+ ${directReports.map(r => `• ${r.title} (subagent_type="${r.agent_type}") — ${r.responsibilities.join(', ')}`).join('\n')}
396
+
397
+ FULL COMMUNICATION TOPOLOGY:
398
+ ${(orgConfig.communication || orgConfig.edges || []).map(e => `${e.from} → ${e.to} (${e.type})`).join('\n')}
399
+
400
+ SHARED INFRASTRUCTURE:
401
+ - Task board (monotask): board_id=<board_id>
402
+ Columns: Todo=<todo_col> Doing=<doing_col> Done=<done_col>
403
+ - Memory namespace: <memNs> (use: npx monomind@latest memory store/search --namespace <memNs>)
404
+ - Dashboard events: POST to mastermind control server via curl — see CTRL_URL resolution in DASHBOARD EVENTS section below
405
+ - Session ID for all events: ${sessionId}
406
+
407
+ TASK BOARD COMMANDS (use these for all task management):
408
+ # List all unclaimed Todo cards for a specific role (server-side filters — fast):
409
+ monotask card list ${board_id} --col ${todo_col} --label "role:<role_id>" --json
410
+
411
+ # List all unclaimed Todo cards (any role):
412
+ monotask card list ${board_id} --col ${todo_col} --json | jq '[.[] | select(.labels | index("claimed") | not)]'
413
+
414
+ # List all in-progress cards:
415
+ monotask card list ${board_id} --col ${doing_col} --json
416
+
417
+ # List all cards for a role across all columns:
418
+ monotask card list ${board_id} --label "role:<role_id>" --json
419
+
420
+ # Create a new task in Todo (tagged for a specific role):
421
+ CARD_ID=$(monotask card create ${board_id} ${todo_col} "Task title" --json | jq -r .id)
422
+ monotask card label add ${board_id} $CARD_ID "role:<role_id>"
423
+
424
+ # Break task into subtasks:
425
+ monotask card subtask add ${board_id} $CARD_ID ${board_id} ${todo_col} "Subtask title" --json
426
+
427
+ # Mark a task as blocked by a prerequisite (won't claim until prereq is Done):
428
+ monotask card prerequisite add ${board_id} $BLOCKED_ID ${board_id} $PREREQ_ID
429
+
430
+ # Claim a task (move to Doing + mark claimed):
431
+ monotask card move ${board_id} $CARD_ID ${doing_col}
432
+ monotask card label add ${board_id} $CARD_ID "claimed"
433
+
434
+ # Complete a task:
435
+ monotask card move ${board_id} $CARD_ID ${done_col}
436
+
437
+ ORG STATE FILE (update per agent per cycle for dashboard visibility):
438
+ stateFile=".monomind/orgs/${orgName}-state.json"
439
+ # Mark agent as running:
440
+ jq --arg id "<role_id>" --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
441
+ '.agents[$id].status = "running" | .agents[$id].last_heartbeat = $ts' \
442
+ "$stateFile" > "${stateFile}.tmp" && mv "${stateFile}.tmp" "$stateFile"
443
+ # Mark agent as idle after completing:
444
+ jq --arg id "<role_id>" \
445
+ '.agents[$id].status = "idle" | .agents[$id].last_heartbeat_complete = (now|todate)' \
446
+ "$stateFile" > "${stateFile}.tmp" && mv "${stateFile}.tmp" "$stateFile"
447
+ # Add token counts when known:
448
+ jq --arg id "<role_id>" --argjson in <tokens_in> --argjson out <tokens_out> \
449
+ '.agents[$id].tokens_in = ((.agents[$id].tokens_in // 0) + $in) | .agents[$id].tokens_out = ((.agents[$id].tokens_out // 0) + $out)' \
450
+ "$stateFile" > "${stateFile}.tmp" && mv "${stateFile}.tmp" "$stateFile"
451
+
452
+ APPROVAL GATE (when governance.policy is "board" or "strict"):
453
+ Before any external action (sending emails, posting content, making purchases, modifying infrastructure):
454
+ approvalsFile=".monomind/orgs/${orgName}-approvals.json"
455
+ [ ! -f "$approvalsFile" ] && echo '{"approvals":[]}' > "$approvalsFile"
456
+ approval_id="req-$(date +%s)-$$-$RANDOM"
457
+ jq --arg id "$approval_id" --arg agent "<role_id>" --arg title "<action summary>" \
458
+ --arg action "<full action description>" --arg risk "medium" \
459
+ '.approvals += [{"id":$id,"agent_id":$agent,"title":$title,"action":$action,"risk_level":$risk,"status":"pending","requested_at":(now|todate)}]' \
460
+ "$approvalsFile" > "${approvalsFile}.tmp" && mv "${approvalsFile}.tmp" "$approvalsFile"
461
+ # Emit approval request event so human sees it in dashboard
462
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" -H "Content-Type: application/json" \
463
+ -d "$(jq -cn --arg org "${orgName}" --arg id "$approval_id" --arg title "<title>" \
464
+ '{type:"org:approval:requested",org:$org,approval_id:$id,title:$title,ts:(now*1000|floor)}')" || true
465
+ # Poll until approved or rejected (max 30 min).
466
+ # Check the approvals file first (updated by both dashboard UI and /mastermind:approvev1 command),
467
+ # then fall back to memory in case an external notifier wrote there.
468
+ for i in $(seq 1 60); do
469
+ status=$(jq -r --arg id "$approval_id" '(.approvals // [])[] | select(.id == $id) | .status // ""' "$approvalsFile" 2>/dev/null || echo "")
470
+ if [ -z "$status" ] || [ "$status" = "pending" ]; then
471
+ mem_status=$(npx monomind@latest memory get --key "approval:${approval_id}" --namespace "${memNs}" 2>/dev/null | jq -r '.status // ""' 2>/dev/null)
472
+ [ -n "$mem_status" ] && status="$mem_status"
473
+ fi
474
+ [ "$status" = "approved" ] && break
475
+ [ "$status" = "rejected" ] && { echo "Action rejected by governance policy — skip this action"; break; }
476
+ sleep 30
477
+ done
478
+
479
+ DASHBOARD EVENT HELPER — resolve once at startup, reuse in every step:
480
+ REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
481
+ CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
482
+ # Git-safe monomind dir — shared across all worktrees, survives branch switches:
483
+ GIT_COMMON=$(git rev-parse --git-common-dir 2>/dev/null || true)
484
+ # Non-git project: never fabricate a .git/ dir — fall back to .monomind
485
+ [ -z "$GIT_COMMON" ] && GIT_COMMON="__NOGIT__"
486
+ # Resolve to absolute path (git returns relative ".git" for main checkout)
487
+ if [[ "$GIT_COMMON" != /* ]]; then GIT_COMMON="$REPO_ROOT/$GIT_COMMON"; fi
488
+ GIT_COMMON="${GIT_COMMON%/}" # strip trailing slash if any
489
+ if [ "$GIT_COMMON" = "__NOGIT__" ] || [ "${GIT_COMMON#*__NOGIT__}" != "$GIT_COMMON" ]; then MONO_DIR="$REPO_ROOT/.monomind"; else MONO_DIR="${GIT_COMMON}/monomind"; fi
490
+ runFile="${MONO_DIR}/orgs/${orgName}/runs/${runId}.jsonl"
491
+
492
+ ⚠️ DASHBOARD EVENTS ARE MANDATORY — run every curl below exactly as written.
493
+ Do NOT skip, defer, or summarize. These keep the human-visible dashboard current.
494
+ Use Bash tool for all curl calls. Never use WebFetch (it doesn't support POST).
495
+ EVERY event MUST include runId:"${runId}" and org:"${orgName}" so the server routes it to the run file.
496
+
497
+ STOP DETECTION (check this FIRST in every loop iteration):
498
+ [ -f "${stopFile}" ] && echo STOP_REQUESTED
499
+ If the file exists: write run:complete to run file, emit org:complete via curl, then "rm -f ${stopFile}", then exit.
500
+ # REQUIRED — write run:complete directly to run file:
501
+ jq -cn --arg runId "${runId}" --arg org "${orgName}" \
502
+ '{type:"run:complete",runId:$runId,org:$org,status:"stopped",ts:(now*1000|floor)}' >> "${runFile}" || true
503
+ # REQUIRED — emit org:complete via curl (includes runId so it also gets appended):
504
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" -H "Content-Type: application/json" \
505
+ -d "$(jq -cn --arg s "${sessionId}" --arg o "${orgName}" --arg rid "${runId}" --arg p "$REPO_ROOT" \
506
+ '{type:"org:complete",session:$s,org:$o,runId:$rid,project:$p,ts:(now*1000|floor)}')" || true
507
+
508
+ OPERATING LOOP:
509
+ 1. Check for stop signal (above). Exit with run:complete + org:complete events if found.
510
+
511
+ 1b. Check user mailbox for pending messages:
512
+ _threads="${REPO_ROOT}/.monomind/orgs/${orgName}-threads.json"
513
+ if [ -f "$_threads" ]; then
514
+ _pending=$(jq '[.[] | select(.status == "pending")]' "$_threads" 2>/dev/null || echo "[]")
515
+ if [ "$(echo "$_pending" | jq 'length')" -gt 0 ]; then
516
+ echo "$_pending" | jq -r '.[] | .text // .msg // ""'
517
+ jq '[.[] | if .status == "pending" then .status = "processed" else . end]' "$_threads" > "${_threads}.tmp" && mv "${_threads}.tmp" "$_threads" || true
518
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" -H "Content-Type: application/json" \
519
+ -d "$(jq -cn --arg s "${sessionId}" --arg o "${orgName}" --arg rid "${runId}" \
520
+ --arg msg "Received user message — adjusting plan if needed" \
521
+ '{type:"org:comms",session:$s,org:$o,runId:$rid,from:"boss",to:"user",msg:$msg,ts:(now*1000|floor)}')" || true
522
+ fi
523
+ fi
524
+
525
+ 2. List unclaimed Todo cards:
526
+ monotask card list ${board_id} --col ${todo_col} --json | jq '[.[] | select(.labels | index("claimed") | not)]'
527
+
528
+ 3. For each unclaimed Todo card, assign to the appropriate role based on its "role:<id>" label.
529
+ Skip cards whose prerequisites are not yet in the Done column.
530
+
531
+ 4. For EACH role agent you are about to spawn — do these steps in order:
532
+ a) Move card to Doing and mark claimed:
533
+ monotask card move ${board_id} $CARD_ID ${doing_col}
534
+ monotask card label add ${board_id} $CARD_ID "claimed"
535
+ b) REQUIRED — emit org:comms to dashboard (include runId):
536
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" -H "Content-Type: application/json" \
537
+ -d "$(jq -cn --arg s "${sessionId}" --arg o "${orgName}" --arg rid "${runId}" --arg p "$REPO_ROOT" \
538
+ --arg role "<role_id>" --arg task "<card title>" \
539
+ '{type:"org:comms",session:$s,org:$o,runId:$rid,from:"boss",to:$role,msg:("Assigning: "+$task),project:$p,ts:(now*1000|floor)}')" || true
540
+ c) REQUIRED — emit org:agent:online before spawning (include runId):
541
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" -H "Content-Type: application/json" \
542
+ -d "$(jq -cn --arg s "${sessionId}" --arg o "${orgName}" --arg rid "${runId}" --arg p "$REPO_ROOT" \
543
+ --arg role "<role_id>" --arg title "<role title>" --arg at "<agent_type>" \
544
+ '{type:"org:agent:online",session:$s,org:$o,runId:$rid,role:$role,title:$title,agent_type:$at,project:$p,ts:(now*1000|floor)}')" || true
545
+ d) Spawn the agent via Task tool (run_in_background: true) with:
546
+ - Full role briefing (title, responsibilities, who they report to, memory namespace)
547
+ - The specific card title and card_id to work on
548
+ - Report output via: npx monomind@latest memory store --key "${memNs}:report:<card_id>" --namespace "${memNs}"
549
+ - When complete, move card to Done: monotask card move ${board_id} $CARD_ID ${done_col}
550
+ - REQUIRED: Include the following DASHBOARD EVENTS block verbatim in every specialist prompt so
551
+ they can emit their own events to the dashboard:
552
+
553
+ DASHBOARD EVENTS — emit these at the start and end of your work (REQUIRED):
554
+ REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
555
+ CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
556
+ GIT_COMMON=$(git rev-parse --git-common-dir 2>/dev/null || true)
557
+ # Non-git project: never fabricate a .git/ dir — fall back to .monomind
558
+ [ -z "$GIT_COMMON" ] && GIT_COMMON="__NOGIT__"
559
+ if [[ "$GIT_COMMON" != /* ]]; then GIT_COMMON="$REPO_ROOT/$GIT_COMMON"; fi
560
+ GIT_COMMON="${GIT_COMMON%/}"
561
+ if [ "$GIT_COMMON" = "__NOGIT__" ] || [ "${GIT_COMMON#*__NOGIT__}" != "$GIT_COMMON" ]; then MONO_DIR="$REPO_ROOT/.monomind"; else MONO_DIR="${GIT_COMMON}/monomind"; fi
562
+ runFile="${MONO_DIR}/orgs/${orgName}/runs/${runId}.jsonl"
563
+ # On start — announce you are working:
564
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" -H "Content-Type: application/json" \
565
+ -d "$(jq -cn --arg s "${sessionId}" --arg o "${orgName}" --arg rid "${runId}" \
566
+ --arg from "<role_id>" --arg msg "Starting: <card title>" \
567
+ '{type:"org:comms",session:$s,org:$o,runId:$rid,from:$from,to:"boss",msg:$msg,ts:(now*1000|floor)}')" || true
568
+ # On completion — report back:
569
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" -H "Content-Type: application/json" \
570
+ -d "$(jq -cn --arg s "${sessionId}" --arg o "${orgName}" --arg rid "${runId}" \
571
+ --arg from "<role_id>" --arg msg "Completed: <one-sentence summary of output>" \
572
+ '{type:"org:comms",session:$s,org:$o,runId:$rid,from:$from,to:"boss",msg:$msg,ts:(now*1000|floor)}')" || true
573
+ # When your task is fully complete — signal offline:
574
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" -H "Content-Type: application/json" \
575
+ -d "$(jq -cn --arg s "${sessionId}" --arg o "${orgName}" --arg rid "${runId}" \
576
+ --arg from "<role_id>" \
577
+ '{type:"org:agent:offline",session:$s,org:$o,runId:$rid,from:$from,ts:(now*1000|floor)}')" || true
578
+ # TOKEN TRACKING — estimate and persist token usage to state.json for the Budgets dashboard:
579
+ # Count approximate output tokens from your work (1 word ≈ 1.3 tokens):
580
+ _tok_out=$(echo -n "<paste a representative sample of your final output here>" | wc -w)
581
+ _tok_out=$(( _tok_out * 13 / 10 ))
582
+ _tok_in=8000 # fixed estimate for task context
583
+ stateFile="$REPO_ROOT/.monomind/orgs/${orgName}-state.json"
584
+ [ -f "$stateFile" ] && jq --arg id "<role_id>" --argjson in $_tok_in --argjson out $_tok_out \
585
+ '.agents[$id].tokens_in = ((.agents[$id].tokens_in // 0) + $in) | .agents[$id].tokens_out = ((.agents[$id].tokens_out // 0) + $out)' \
586
+ "$stateFile" > "${stateFile}.tmp" && mv "${stateFile}.tmp" "$stateFile" || true
587
+
588
+ Fill in the literal values for orgName="${orgName}", runId="${runId}", sessionId="${sessionId}" — embed them
589
+ directly in the prompt string so the specialist doesn't need to resolve them.
590
+ For token tracking: set _tok_out to the actual word count of your output text (use wc -w on your
591
+ output), and replace <role_id> with the literal role id string.
592
+
593
+ 5. Collect completed results from memory (search "${memNs}:report:")
594
+
595
+ 6. Synthesize reports, make decisions, create new cards in Todo column based on progress toward goal.
596
+ REQUIRED — after each decision, emit org:comms (include runId):
597
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" -H "Content-Type: application/json" \
598
+ -d "$(jq -cn --arg s "${sessionId}" --arg o "${orgName}" --arg rid "${runId}" --arg p "$REPO_ROOT" \
599
+ --arg msg "<one-sentence summary of decision or finding>" \
600
+ '{type:"org:comms",session:$s,org:$o,runId:$rid,from:"boss",to:"all",msg:$msg,project:$p,ts:(now*1000|floor)}')" || true
601
+
602
+ 7. Write cycle-complete entry (directly to run file AND to activity log):
603
+ pending_count=$(monotask card list ${board_id} --col ${todo_col} --json 2>/dev/null | jq '[.[] | select(.labels | index("claimed") | not)] | length' 2>/dev/null || echo 0)
604
+ jq -cn --arg runId "${runId}" --arg org "${orgName}" --argjson pend "${pending_count:-0}" \
605
+ '{type:"run:cycle:complete",runId:$runId,org:$org,pending:$pend,ts:(now*1000|floor)}' >> "${runFile}" || true
606
+ activityFile=".monomind/orgs/${orgName}-activity.jsonl"
607
+ echo "{\"type\":\"run:cycle:complete\",\"org\":\"${orgName}\",\"runId\":\"${runId}\",\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"pending\":${pending_count:-0}}" >> "$activityFile" 2>/dev/null || true
608
+
609
+ 8. REQUIRED — emit org:checkpoint with a one-sentence progress summary (include runId):
610
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" -H "Content-Type: application/json" \
611
+ -d "$(jq -cn --arg s "${sessionId}" --arg o "${orgName}" --arg rid "${runId}" --arg p "$REPO_ROOT" \
612
+ --arg prog "<one sentence: what was done this cycle and what is next>" \
613
+ --argjson pend "${pending_count:-0}" \
614
+ '{type:"org:checkpoint",session:$s,org:$o,runId:$rid,progress:$prog,pending_tasks:$pend,project:$p,ts:(now*1000|floor)}')" || true
615
+
616
+ 9. Wait ${checkpointMin} minutes, then loop back to step 1.
617
+
618
+ AGENT TYPES FOR YOUR TEAM:
619
+ ${orgConfig.roles.filter(r => r.id !== bossRole.id).map(r =>
620
+ `• ${r.title}: subagent_type="${r.agent_type}"`
621
+ ).join('\n')}
622
+
623
+ START NOW: resolve CTRL_URL, check for stop signal, assess the board, create initial tasks if none exist, then begin the operating loop.`
624
+ })
625
+ ```
626
+
627
+ ---
628
+
629
+ ## Step 5 — Emit Boss Online Event
630
+
631
+ Emit `org:agent:online` for the boss role (team member events are emitted by the boss itself).
632
+
633
+ **IMPORTANT**: Shell variables do NOT persist across separate Bash tool calls. Use the values read from the `runcontext.json` file in Step 2+3 and substitute each literal value directly into the curl command before running it. Do NOT use `$variableName` syntax — replace each placeholder with its literal string from the context file.
634
+
635
+ ```bash
636
+ # Replace <CTRL_URL>, <sessionId>, <orgName>, <runId>, <bossRole_id>, <bossRole_title>, <bossRole_agent_type>, <REPO_ROOT>
637
+ # with the LITERAL values from runcontext.json read in Step 2+3.
638
+ curl -s -X POST "<CTRL_URL>/api/mastermind/event" \
639
+ -H "Content-Type: application/json" \
640
+ -d "$(jq -cn \
641
+ --arg session "<sessionId>" \
642
+ --arg org "<orgName>" \
643
+ --arg runId "<runId>" \
644
+ --arg role "<bossRole_id>" \
645
+ --arg title "<bossRole_title>" \
646
+ --arg agent_type "<bossRole_agent_type>" \
647
+ --arg proj "<REPO_ROOT>" \
648
+ '{type:"org:agent:online",session:$session,org:$org,runId:$runId,role:$role,title:$title,agent_type:$agent_type,project:$proj,ts:(now*1000|floor)}')" || true
649
+ ```
650
+
651
+ ---
652
+
653
+ ## Step 6 — Report to User
654
+
655
+ Print the org startup summary:
656
+
657
+ ```
658
+ ╔══════════════════════════════════════════════════════════╗
659
+ ║ ORG STARTED: <orgName> ║
660
+ ║ GOAL: <goal> ║
661
+ ║ MODE: persistent daemon ║
662
+ ╚══════════════════════════════════════════════════════════╝
663
+
664
+ ACTIVE ROLES
665
+ ────────────
666
+ • [<bossRole.id>] <bossRole.title> → running as <bossRole.agent_type> agent
667
+ • [<each direct report>] <title> → will be spawned by boss on demand
668
+ ... (all roles from config)
669
+
670
+ BOARD: <orgName>/org-tasks
671
+ MEMORY: org:<orgName>
672
+ DASHBOARD: see .monomind/control.json for URL (default http://localhost:4242)
673
+
674
+ To stop: click STOP in the dashboard Orgs panel
675
+ or run: CTRL_URL=$(jq -r '.url // "http://localhost:4242"' .monomind/control.json 2>/dev/null || echo "http://localhost:4242"); curl -X POST "${CTRL_URL}/api/orgs/<orgName>/stop"
676
+ ```
677
+
678
+ ---
679
+
680
+ ## Step 7 — Return Output Schema
681
+
682
+ ```yaml
683
+ domain: ops
684
+ status: complete
685
+ artifacts: []
686
+ decisions:
687
+ - what: "Org <orgName> started with <N> roles, boss agent running"
688
+ why: "Loaded from .monomind/orgs/<orgName>.json"
689
+ confidence: 0.9
690
+ outcome: pending
691
+ lessons:
692
+ - what_worked: "Boss agent spawned and org loop started"
693
+ - what_didnt: ""
694
+ next_actions:
695
+ - "Monitor in Mastermind Orgs panel (CTRL_URL from .monomind/control.json, default http://localhost:4242)"
696
+ - "To stop: click STOP in dashboard or: CTRL_URL=$(jq -r '.url // \"http://localhost:4242\"' .monomind/control.json); curl -X POST \"${CTRL_URL}/api/orgs/<orgName>/stop\""
697
+ board_url: "monotask://<orgName>/org-tasks"
698
+ run_id: "<runId>"
699
+ ```
700
+
701
+ ---
702
+
703
+ ## Step 8 — Brain Write (standalone only)
704
+
705
+ If `caller` is not "command", follow _protocol.md Brain Write Procedure for domain `ops`.
706
+
707
+ ---
708
+
709
+ ## Reporting Artifacts (optional)
710
+
711
+ When you create or modify a file that represents output of this org run, emit:
712
+
713
+ ```bash
714
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
715
+ -H "Content-Type: application/json" \
716
+ -d "$(jq -cn \
717
+ --arg s "${sessionId}" \
718
+ --arg o "${orgName}" \
719
+ --arg rid "${runId}" \
720
+ --arg from "${AGENT_ROLE}" \
721
+ --arg label "Short description" \
722
+ --arg path "${FILE_PATH}" \
723
+ --arg mime "text/markdown" \
724
+ '{type:"org:artifact",session:$s,org:$o,runId:$rid,from:$from,artifact:{label:$label,type:"file",path:$path,mimeType:$mime},ts:(now*1000|floor)}')" || true
725
+ ```
726
+
727
+ The dashboard chat will show this as an artifact card with a "View" button. Fill in:
728
+ - `${AGENT_ROLE}` — the literal role id of the agent emitting the artifact
729
+ - `${FILE_PATH}` — the absolute or repo-relative path to the created/modified file
730
+ - `label` — a short human-readable description (e.g., "Weekly report", "Analysis output")
731
+ - `mimeType` — `text/markdown`, `application/json`, `text/plain`, etc.