monomind 2.3.2 → 2.3.4
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.
- package/README.md +37 -4
- package/package.json +1 -1
- package/packages/@monomind/cli/.claude/commands/mastermind/_repeat.md +9 -9
- package/packages/@monomind/cli/.claude/commands/mastermind/{approve.md → approvev1.md} +14 -11
- package/packages/@monomind/cli/.claude/commands/mastermind/architect.md +3 -3
- package/packages/@monomind/cli/.claude/commands/mastermind/createorg.md +4 -4
- package/packages/@monomind/cli/.claude/commands/mastermind/help.md +18 -0
- package/packages/@monomind/cli/.claude/commands/mastermind/master.md +6 -6
- package/packages/@monomind/cli/.claude/commands/mastermind/repeat.md +5 -5
- package/packages/@monomind/cli/.claude/commands/mastermind/runorg.md +20 -108
- package/packages/@monomind/cli/.claude/commands/mastermind/runorgv1.md +159 -0
- package/packages/@monomind/cli/.claude/commands/mastermind/stoporg.md +1 -1
- package/packages/@monomind/cli/.claude/helpers/control-start.cjs +52 -4
- package/packages/@monomind/cli/.claude/helpers/event-logger.cjs +9 -1
- package/packages/@monomind/cli/.claude/helpers/handlers/capture-handler.cjs +9 -1
- package/packages/@monomind/cli/.claude/helpers/handlers/route-handler.cjs +88 -15
- package/packages/@monomind/cli/.claude/helpers/handlers/session-restore-handler.cjs +25 -18
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/_protocol.md +1 -1
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/_repeat.md +3 -3
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/adapters.md +1 -1
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/agents.md +6 -2
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/{approve.md → approvev1.md} +8 -5
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/architect.md +8 -8
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/backup.md +1 -1
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/bootstrap.md +1 -1
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/costs.md +1 -1
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/env.md +7 -2
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/{heartbeat.md → heartbeatv1.md} +9 -6
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/idea.md +1 -1
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/inbox.md +14 -2
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/instance.md +3 -1
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/monitor.md +2 -2
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/org-settings.md +5 -2
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/orgs.md +14 -13
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/orgstatus.md +19 -8
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/plugins.md +1 -1
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/projects.md +3 -0
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/runorg.md +37 -719
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/runorgv1.md +731 -0
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/stoporg.md +12 -5
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/tasks.md +4 -1
- package/packages/@monomind/cli/README.md +37 -4
- package/packages/@monomind/cli/dist/src/commands/cleanup.js +30 -8
- package/packages/@monomind/cli/dist/src/commands/doctor-project-checks.js +23 -10
- package/packages/@monomind/cli/dist/src/commands/org-observe.js +30 -26
- package/packages/@monomind/cli/dist/src/commands/org.js +52 -2
- package/packages/@monomind/cli/dist/src/knowledge/document-pipeline.js +0 -0
- package/packages/@monomind/cli/dist/src/memory/memory-bridge.js +31 -12
- package/packages/@monomind/cli/dist/src/orgrt/daemon.js +45 -9
- package/packages/@monomind/cli/dist/src/orgrt/mailbox.d.ts +23 -0
- package/packages/@monomind/cli/dist/src/orgrt/mailbox.js +33 -1
- package/packages/@monomind/cli/dist/src/orgrt/migrate.d.ts +26 -0
- package/packages/@monomind/cli/dist/src/orgrt/migrate.js +111 -0
- package/packages/@monomind/cli/dist/src/orgrt/reporting.js +8 -1
- package/packages/@monomind/cli/dist/src/orgrt/session.js +8 -2
- package/packages/@monomind/cli/dist/src/ui/dashboard.html +44 -0
- package/packages/@monomind/cli/dist/src/ui/orgs.html +2 -2
- package/packages/@monomind/cli/dist/src/ui/server.mjs +86 -0
- 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.
|