monomind 2.1.3 → 2.1.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 +18 -17
- package/package.json +1 -1
- package/packages/@monomind/cli/.claude/commands/mastermind/createorg.md +8 -9
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/createorg.md +91 -701
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/org-settings.md +46 -47
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/runorg.md +12 -1
- package/packages/@monomind/cli/README.md +18 -17
- package/packages/@monomind/cli/dist/src/orgrt/daemon.d.ts +18 -0
- package/packages/@monomind/cli/dist/src/orgrt/daemon.js +76 -1
- package/packages/@monomind/cli/dist/src/orgrt/forwarder.js +7 -3
- package/packages/@monomind/cli/dist/src/orgrt/server.js +24 -0
- package/packages/@monomind/cli/dist/src/orgrt/session.d.ts +1 -0
- package/packages/@monomind/cli/dist/src/orgrt/session.js +8 -0
- package/packages/@monomind/cli/dist/src/orgrt/types.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/pricing/model-pricing.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/ui/collector.mjs +20 -5
- package/packages/@monomind/cli/dist/src/ui/dashboard.html +270 -499
- package/packages/@monomind/cli/dist/src/ui/orgs-files.js +1 -1
- package/packages/@monomind/cli/dist/src/ui/server.mjs +132 -158
- package/packages/@monomind/cli/package.json +3 -3
- package/packages/@monomind/cli/scripts/publish.sh +6 -0
- package/scripts/generate-agent-avatars.mjs +3 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: mastermind-createorg
|
|
3
|
-
description: Mastermind createorg — design
|
|
3
|
+
description: Mastermind createorg — design and persist an autonomous agent organization (Org Runtime v2) as a `.monomind/orgs/<name>.json` config that `monomind org run/serve` loads directly. Supports optional --schedule flag for daemon-scheduled orgs.
|
|
4
4
|
type: domain-skill
|
|
5
5
|
default_mode: confirm
|
|
6
6
|
---
|
|
@@ -9,6 +9,8 @@ default_mode: confirm
|
|
|
9
9
|
|
|
10
10
|
This skill is invoked by `mastermind:createorg` or directly via `/mastermind:createorg`.
|
|
11
11
|
|
|
12
|
+
Org Runtime v2 (`packages/@monomind/cli/src/orgrt/`) is a Node daemon, not a Task-tool-spawned boss agent. Every role in the config becomes a live SDK agent session (`@anthropic-ai/claude-agent-sdk` `query()`) the moment the org starts — there is no task board, no per-role generated `.claude/agents/*.md` file, and no communication-topology array. Roles address each other directly with the `org_send` tool using their `id` (or `<org>:<id>` cross-org). This skill's only job is to produce a config that validates against `OrgDefSchema` (`packages/@monomind/cli/src/orgrt/types.ts`).
|
|
13
|
+
|
|
12
14
|
---
|
|
13
15
|
|
|
14
16
|
## Inputs
|
|
@@ -16,33 +18,13 @@ This skill is invoked by `mastermind:createorg` or directly via `/mastermind:cre
|
|
|
16
18
|
- `brain_context`: BRAIN CONTEXT block (injected by command, or loaded below if standalone)
|
|
17
19
|
- `prompt`: goal and/or role description for this org
|
|
18
20
|
- `org_name`: desired name for the org (slug, e.g. `content-team`); constrained to `[a-z0-9-]`
|
|
19
|
-
- `roles_desc`: optional explicit role list from user (e.g. "boss, content writer, reviewer, marketer, designer
|
|
20
|
-
- `schedule`: optional schedule string
|
|
21
|
+
- `roles_desc`: optional explicit role list from user (e.g. "boss, content writer, reviewer, marketer, designer")
|
|
22
|
+
- `schedule`: optional schedule string in daemon format — `"<N>s"`, `"<N>m"`, or `"<N>h"` (e.g. `"30m"`, `"2h"`). When provided, the org is picked up by `monomind org serve` on its own interval; omit for a one-shot `monomind org run`.
|
|
23
|
+
- `budget_tokens`: optional total token budget for the org run (default 1,000,000 — split evenly across roles by the daemon)
|
|
21
24
|
- `mode`: auto | confirm
|
|
22
25
|
- `session_id`: session ID passed by command wrapper (snake_case input)
|
|
23
26
|
- `caller`: command | master
|
|
24
27
|
|
|
25
|
-
### Schedule parsing (when `--schedule` is present)
|
|
26
|
-
|
|
27
|
-
Convert the schedule string to `poll_interval_minutes`:
|
|
28
|
-
|
|
29
|
-
| Schedule string | Minutes |
|
|
30
|
-
|---|---|
|
|
31
|
-
| `every N minutes` | N |
|
|
32
|
-
| `every minute` | 1 |
|
|
33
|
-
| `every hour` | 60 |
|
|
34
|
-
| `every N hours` | N × 60 |
|
|
35
|
-
| `daily` / `every day` | 1440 |
|
|
36
|
-
| `every N days` | N × 1440 |
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
# Example: "every 30 minutes" → poll_interval_minutes=30
|
|
40
|
-
# "every 2 hours" → poll_interval_minutes=120
|
|
41
|
-
# "daily" → poll_interval_minutes=1440
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Store the parsed value as `poll_interval_minutes` for use in Steps 4 and 6.7.
|
|
45
|
-
|
|
46
28
|
---
|
|
47
29
|
|
|
48
30
|
## Step 0 — Brain Load (standalone only)
|
|
@@ -59,7 +41,7 @@ Run intake from `_intake.md` if `prompt` is vague (stop at Q3, domain is `ops`).
|
|
|
59
41
|
|
|
60
42
|
If `org_name` is not provided, extract the most prominent product/team noun from `prompt`, slugify it (lowercase, spaces → hyphens, strip non-`[a-z0-9-]` chars), and confirm with the user. Fallback: `org-<YYYYMMDD>`.
|
|
61
43
|
|
|
62
|
-
Reject any `org_name` that does not match `^[a-z0-9][a-z0-9-]{0,63}
|
|
44
|
+
Reject any `org_name` that does not match `^[a-z0-9][a-z0-9-]{0,63}$` (the CLI's own `ORG_NAME_RE` in `org.ts` is slightly looser — `^[a-z0-9][a-z0-9_-]*$/i` — but this skill's stricter slug is always a valid subset).
|
|
63
45
|
|
|
64
46
|
---
|
|
65
47
|
|
|
@@ -67,330 +49,77 @@ Reject any `org_name` that does not match `^[a-z0-9][a-z0-9-]{0,63}$`.
|
|
|
67
49
|
|
|
68
50
|
Parse `roles_desc` (if provided) into a list of role titles. If not provided, derive a set of roles from `prompt` by identifying the human functions needed to achieve the goal.
|
|
69
51
|
|
|
70
|
-
**Required roles to include when deriving roles from the prompt** (**skip this rule for persona-based orgs
|
|
71
|
-
- A coordinator/boss role that owns the goal
|
|
52
|
+
**Required roles to include when deriving roles from the prompt** (**skip this rule for persona-based orgs**, see below, where the characters themselves define the structure):
|
|
53
|
+
- A coordinator/boss role that owns the goal — exactly one role with `reports_to: null`
|
|
72
54
|
- At least one executor role that does the primary work
|
|
73
|
-
- A reviewer
|
|
74
|
-
- A communication layer (middle manager) if team size ≥ 4
|
|
75
|
-
|
|
76
|
-
**If the user provided an explicit `roles_desc`, their list is authoritative — never silently inject roles into it.** If a structural role above is missing (e.g. no coordinator, or ≥4 roles with no middle manager), in confirm mode note the gap in the Step 5 plan as a one-line suggestion ("Suggestion: add a coordinator — none in your list; reply 'add it' or 'go' to keep as-is"); in auto mode, create exactly the roles listed.
|
|
77
|
-
|
|
78
|
-
**Step 2.2 — Detect the org's domain.** From the goal/prompt, classify the org's domain in one or two words (e.g. `software-engineering`, `content-marketing`, `sports-analytics`, `legal`, `medical`, `finance`). This drives agent-type resolution below.
|
|
55
|
+
- A reviewer role if quality output is implied
|
|
79
56
|
|
|
80
|
-
|
|
57
|
+
**If the user provided an explicit `roles_desc`, their list is authoritative — never silently inject roles into it.** If a structural role above is missing (e.g. no coordinator), in confirm mode note the gap in the Step 4 plan as a one-line suggestion; in auto mode, create exactly the roles listed and let the first one default to boss (Step 2.2).
|
|
81
58
|
|
|
82
|
-
**Step 2.
|
|
59
|
+
**Step 2.1 — Assign `id`, `title`, `type`, `reports_to`.**
|
|
83
60
|
|
|
84
|
-
|
|
61
|
+
- `id`: slug derived from title (`Content Writer` → `content-writer`)
|
|
62
|
+
- `title`: display title, as given
|
|
63
|
+
- `type`: `"boss"` for the single root role (the daemon looks for `type === 'boss' || reports_to === null`, falling back to `roles[0]` if neither matches), `"specialist"` for everyone else — or a domain-fit synonym like `"reviewer"` / `"researcher"` purely for readability; the runtime treats `type` as free text except for the `"boss"` match
|
|
64
|
+
- `reports_to`: the boss's `id` for direct reports, `null` only for the boss; for larger teams a middle layer can report to another non-boss role — the runtime does not enforce a shape beyond "each role's `reports_to` must be another role's `id` or null"
|
|
85
65
|
|
|
86
|
-
|
|
87
|
-
|---|---|---|
|
|
88
|
-
| `coordinator` | any (domain-neutral orchestration) | boss / ceo / director / lead / chief |
|
|
89
|
-
| `Project Shepherd` | any (domain-neutral coordination) | middle manager / manager |
|
|
90
|
-
| `researcher` | general research | researcher (broad research roles only) |
|
|
91
|
-
| `coder` | software | engineer / developer / coder / dev |
|
|
92
|
-
| `reviewer` | software — code review | code reviewer |
|
|
93
|
-
| `tester` | software — QA/testing | qa / tester (of software) |
|
|
94
|
-
| `Content Creator` | content/marketing | content writer / copywriter |
|
|
95
|
-
| `Growth Hacker` | marketing | marketer / growth |
|
|
96
|
-
| `SEO Specialist` | search marketing | seo / search |
|
|
97
|
-
| `Social Media Strategist` | social marketing | social media / social |
|
|
98
|
-
| `Product Manager` | product/software | product / product manager |
|
|
99
|
-
| `Monodesign` | design/UI/brand | designer / ui / ux / visual |
|
|
66
|
+
Exactly one role must have `reports_to: null`. If the user's role list has none, promote the first/most senior-sounding role. If it has more than one, ask which is the root (confirm mode) or promote the first one listed (auto mode).
|
|
100
67
|
|
|
101
|
-
**
|
|
68
|
+
**Step 2.2 — Persona / Character Detection.**
|
|
102
69
|
|
|
103
|
-
|
|
104
|
-
2. Find a roster candidate by role keyword.
|
|
105
|
-
3. **Domain-fit gate:** accept the candidate ONLY if its domain column matches what this role actually does in *this org's* domain (Step 2.2). A "reviewer" in a football-analysis org reviews match analyses, not code — `reviewer` fails the gate. An "analyst" there analyzes matches, not general research — `researcher` fails too. When in doubt, the gate fails.
|
|
106
|
-
4. **Gate passed, generic role** → reuse the premade slug as-is.
|
|
107
|
-
5. **Gate passed, use-case-specific role** (e.g. a content writer for a niche audience) → still reuse the premade slug; put the specialization into the role's `responsibilities` and the org goal (runorg passes both to the agent). Do not fork the definition for mere topical focus.
|
|
108
|
-
6. **No candidate, or gate failed** → coin a role-specific slug from the role title (`Football Analyst` → `football-analyst`, `Court Reporter` → `court-reporter`, `Churn Analyst` → `churn-analyst`, devbot `Orchestrator` → `devbot-orchestrator`) and fully generate its definition in Step 2.5 — skills, instructions, and I/O contracts all authored for this role in this org's domain. If a rejected candidate was structurally close, its definition may seed the *shape* of the generated one, but every line must be rewritten for the actual domain.
|
|
109
|
-
7. `general-purpose` is a last resort, only when no sensible slug applies. Never map an editor/content-reviewer role to `reviewer` — that agent is code review; coin `content-editor` (or similar) and generate.
|
|
70
|
+
A role is **persona-based** if its title is a named real person, a well-known fictional character, or a celebrity/historical figure referred to by name. An org is persona-based if ≥50% of its roles are character names, or the goal/prompt contains `panel`, `debate`, `simulation`, `roleplay`, `celebrity`, `character`, `virtual [name]`, `impersonate`, `as [name]`.
|
|
110
71
|
|
|
111
|
-
|
|
72
|
+
Persona roles work the same as any other role in v2 — there is no separate `agent_type`/subagent registry to resolve against. Put the character depth directly into `responsibilities` (the only field `buildRolePrompt` — `orgrt/session.ts:27-39` — actually feeds into the agent's role briefing): write 3-6 specific, voice-defining responsibilities drawn from the character's known career, positions, and communication style, not generic duties. For a living public figure, base it on documented public behavior — do not invent positions they haven't taken.
|
|
112
73
|
|
|
113
74
|
---
|
|
114
75
|
|
|
115
|
-
## Step 2.3 —
|
|
76
|
+
## Step 2.3 — Optional Per-Role Settings
|
|
116
77
|
|
|
117
|
-
|
|
78
|
+
For any role that needs non-default behavior, set (all optional — omit to inherit defaults):
|
|
118
79
|
|
|
119
|
-
|
|
120
|
-
-
|
|
121
|
-
-
|
|
122
|
-
- A celebrity, historical figure, or public persona referred to by name
|
|
80
|
+
- `adapter_config.model`: a model id (e.g. `"claude-opus-4-7"`) — default is `"claude-sonnet-4-5"` (`orgrt/types.ts:33`)
|
|
81
|
+
- `provider`: `{ kind: "subscription" | "api-key" | "base-url" | "bedrock" | "vertex", apiKeyEnv?, baseUrl?, authTokenEnv? }` — default `subscription` (local Claude Code login); only set this if the role needs a different provider/credential
|
|
82
|
+
- `policy`: `{ allowTools?, denyTools?, fileWrite?, fileRead?, webAllow?, maxTokens? }` — glob-based tool/file/web restrictions for that role; leave unset unless the user asked for sandboxing
|
|
123
83
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
**If persona-based:**
|
|
127
|
-
|
|
128
|
-
1. **Ignore the premade roster entirely for these roles.** Do NOT map "Donald Trump" → `coder`, "Elon Musk" → `researcher`, etc.
|
|
129
|
-
|
|
130
|
-
2. **Coin a character-specific `agent_type` slug** from the character's name:
|
|
131
|
-
- `Donald Trump` → `donald-trump`
|
|
132
|
-
- `Sherlock Holmes` → `sherlock-holmes`
|
|
133
|
-
- `Steve Jobs` → `steve-jobs`
|
|
134
|
-
|
|
135
|
-
3. **Derive skills and expertise from what is publicly known about that person**, not from generic software roles. Use your knowledge of the person's career, public persona, communication style, known positions, and characteristic behaviors. For example:
|
|
136
|
-
- Donald Trump: negotiation, real-estate dealmaking, brand promotion, confrontational rhetoric, media manipulation, self-promotion, political populism
|
|
137
|
-
- Elon Musk: first-principles engineering, disruptive product vision, risk-taking, rapid iteration, social media provocation, space/EV technology
|
|
138
|
-
- A fictional detective: deductive reasoning, observation, criminal psychology, pattern recognition
|
|
139
|
-
|
|
140
|
-
4. **The generated agent definition** (written in Step 2.5) must read as that character — their voice, stance, known views, communication style. It is a character simulation, not a generic role.
|
|
141
|
-
|
|
142
|
-
5. **Non-character roles in the same org** (e.g. "Moderator", "Audience") go through the normal Step 2.4 resolution (roster + domain-fit gate, or a role-coined slug).
|
|
143
|
-
|
|
144
|
-
**If NOT persona-based:** proceed normally with Step 2.4.
|
|
84
|
+
Do not invent values for these — only populate a field the user actually specified or clearly implied (e.g. "the researcher should use Opus" → that role's `adapter_config.model`).
|
|
145
85
|
|
|
146
86
|
---
|
|
147
87
|
|
|
148
|
-
## Step
|
|
149
|
-
|
|
150
|
-
**This is the step that makes each created agent actually work.** A role is only usable if it has: skills, an instruction document (system prompt), an input contract, and an output contract. Most of these are missing from a bare role description — **generate them, tailored to the specific agent, rather than leaving them blank.**
|
|
151
|
-
|
|
152
|
-
**For persona/character roles** (identified in Step 2.3): the generated definition must embody the character. The system prompt should open with "You are [Character Name]" and describe their personality, known views, communication style, and behavioral quirks drawn from public knowledge. Skills must reflect their real-world expertise and traits, not generic software capabilities. If the character is a living public figure, base the portrayal on documented public behavior and statements — do not invent positions they haven't taken.
|
|
153
|
-
|
|
154
|
-
For **each** role, do the following:
|
|
88
|
+
## Step 3 — Build Org Config
|
|
155
89
|
|
|
156
|
-
|
|
157
|
-
```bash
|
|
158
|
-
# Match by frontmatter `name:` first, then by filename slug, anywhere under .claude/agents
|
|
159
|
-
at="<agent_type>"
|
|
160
|
-
existing=$(grep -rils "^name:[[:space:]]*${at}\$" .claude/agents 2>/dev/null | head -1)
|
|
161
|
-
[ -z "$existing" ] && existing=$(find .claude/agents -iname "${at}.md" 2>/dev/null | head -1)
|
|
162
|
-
```
|
|
163
|
-
A definition is **usable** only if it exists AND it passed the Step 2.4 domain-fit gate (its instructions describe this role's actual job in this org's domain). A curated def about a different domain (e.g. `reviewer.md` = code review, applied to a "Court Reporter" or "Match Reviewer") does **not** count as usable — treat it as missing, coin a role-specific `agent_type` per Step 2.4 rule 6, and generate.
|
|
164
|
-
|
|
165
|
-
**2. If no usable definition exists, generate one** at `.claude/agents/generated/<agent_type>.md`. Author it specifically for this role and this org's goal — never a generic stub. Use this shape:
|
|
166
|
-
|
|
167
|
-
```markdown
|
|
168
|
-
---
|
|
169
|
-
name: <agent_type>
|
|
170
|
-
description: <one line — who this agent is and what it does>
|
|
171
|
-
capability:
|
|
172
|
-
role: <agent_type>
|
|
173
|
-
goal: <one sentence: the agent's standing objective in this org>
|
|
174
|
-
version: "1.0.0"
|
|
175
|
-
expertise: # 4–6 concrete SKILLS this role needs to do its job well
|
|
176
|
-
- <skill>
|
|
177
|
-
- <skill>
|
|
178
|
-
task_types: # 3–5 kinds of work it performs
|
|
179
|
-
- <task-type>
|
|
180
|
-
input_type: <what this agent consumes — who/what it receives, derived from its inbound communication edges + responsibilities>
|
|
181
|
-
output_type: <what this agent produces — the artifact it hands off or reports, derived from its outbound edges + responsibilities>
|
|
182
|
-
model_preference: sonnet
|
|
183
|
-
termination: <the condition under which this agent's job is done>
|
|
184
|
-
---
|
|
185
|
-
|
|
186
|
-
# <Role Title>
|
|
187
|
-
|
|
188
|
-
<1–2 sentences: the agent's identity and stance.>
|
|
189
|
-
|
|
190
|
-
## Core Responsibilities
|
|
191
|
-
<the role's responsibilities, expanded into numbered, actionable duties>
|
|
192
|
-
|
|
193
|
-
## Operating Guidelines
|
|
194
|
-
<3–6 concrete rules that keep this agent doing the right thing for its domain — what to always do, what never to do, how to handle missing inputs>
|
|
195
|
-
|
|
196
|
-
## Communication
|
|
197
|
-
- **Receives (input)**: <sources + what, from the inbound edges in Step 3>
|
|
198
|
-
- **Sends (output)**: <targets + what, from the outbound edges in Step 3>
|
|
199
|
-
- **Protocol**: <direct / via manager; who it reports to>
|
|
200
|
-
|
|
201
|
-
## Quality Bar
|
|
202
|
-
<one sentence defining "good output" for this role, so the agent can self-check>
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
Generate this content with real domain reasoning — the `expertise`, `input_type`, `output_type`, and instruction body must be specific to *this* agent (a prosecutor's skills are not a judge's). Reuse a generated def across roles of the same `agent_type` (don't regenerate if you just created it this run).
|
|
206
|
-
|
|
207
|
-
**3. Populate the org role.** Set the role's `skills` array to the def's `expertise` list (so the org config is self-describing), and keep `agent_type` pointing at the (possibly newly coined) type. Never leave `skills: []` when expertise was generated.
|
|
208
|
-
|
|
209
|
-
**4. Note generated files** for the Step 8 artifacts list.
|
|
210
|
-
|
|
211
|
-
The dashboard agent drawer and `runorg` both read these definitions (matched by `agent_type`), so generating them here is what makes the Roles/Skills/instructions show up *and* what gives each spawned agent its real instructions at run time.
|
|
212
|
-
|
|
213
|
-
---
|
|
214
|
-
|
|
215
|
-
## Step 2.6 — COMPLETENESS GATE (BLOCKING — do not proceed to Step 3 until all checks pass)
|
|
216
|
-
|
|
217
|
-
This gate prevents saving an org where the dashboard will show "No instruction document" or blank skills. **Run these checks now — one Bash call per role:**
|
|
218
|
-
|
|
219
|
-
**Check 0 — domain fit (judgment, per role using a premade slug):** for every role whose `agent_type` comes from the Step 2.4 roster, re-confirm the gate: the premade agent's definition describes this role's actual job in *this org's* domain. If not, go back to Step 2.4 rule 6 — coin a slug and generate. An org outside the roster's domains (sports, legal, medical, etc.) should have **zero domain-scoped** roster slugs (`coder`, `reviewer`, `tester`, `researcher`, etc.); domain-neutral slugs (`coordinator`, `Project Shepherd`) and roles whose *function itself* matches a premade (e.g. a content writer publishing that org's output → `Content Creator`, per rule 5) remain valid reuses.
|
|
220
|
-
|
|
221
|
-
For each role in the org:
|
|
222
|
-
|
|
223
|
-
```bash
|
|
224
|
-
# Check 1 — agent_type is set
|
|
225
|
-
role_id="<role_id>"
|
|
226
|
-
agent_type="<agent_type>"
|
|
227
|
-
[ -z "$agent_type" ] && echo "FAIL: role '$role_id' has no agent_type" && exit 1
|
|
228
|
-
|
|
229
|
-
# Check 2 — definition file exists on disk
|
|
230
|
-
def_file=".claude/agents/generated/${agent_type}.md"
|
|
231
|
-
# Also check non-generated paths
|
|
232
|
-
fallback=$(find .claude/agents -iname "${agent_type}.md" 2>/dev/null | head -1)
|
|
233
|
-
if [ ! -f "$def_file" ] && [ -z "$fallback" ]; then
|
|
234
|
-
echo "FAIL: no agent definition file for '$agent_type' — generate it now before continuing"
|
|
235
|
-
exit 1
|
|
236
|
-
fi
|
|
237
|
-
echo "OK: $role_id → $agent_type ✓"
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
If any check fails: go back to Step 2.5 and generate the missing file before proceeding. Do not skip this check.
|
|
241
|
-
|
|
242
|
-
**Also verify the `skills` array for each role is non-empty (≥3 items).** If `skills: []` for any role, pull the expertise from the generated definition file and populate it now.
|
|
243
|
-
|
|
244
|
-
---
|
|
245
|
-
|
|
246
|
-
## Step 3 — Suggest Communication Topology
|
|
247
|
-
|
|
248
|
-
Determine topology from team size:
|
|
249
|
-
- 1–3 roles → `mesh` (all communicate directly)
|
|
250
|
-
- 4–6 roles → `star` (boss at center, all report to boss)
|
|
251
|
-
- 7+ roles → `hierarchical` (boss → middle manager(s) → executors)
|
|
252
|
-
|
|
253
|
-
Build directed communication edges:
|
|
254
|
-
|
|
255
|
-
**Communication edge types:**
|
|
256
|
-
- `command`: top-down direction of work
|
|
257
|
-
- `report`: bottom-up status / output delivery
|
|
258
|
-
- `feedback`: peer review or critique
|
|
259
|
-
- `handoff`: one role passes output directly to next role in sequence
|
|
260
|
-
|
|
261
|
-
**Default edges for a 6-role org (boss, content writer, content reviewer, marketer, designer, middle manager):**
|
|
262
|
-
```
|
|
263
|
-
boss → middle_manager (command)
|
|
264
|
-
middle_manager → content_writer (command)
|
|
265
|
-
middle_manager → designer (command)
|
|
266
|
-
middle_manager → marketer (command)
|
|
267
|
-
content_writer → content_reviewer (handoff)
|
|
268
|
-
content_reviewer → middle_manager (report)
|
|
269
|
-
designer → middle_manager (report)
|
|
270
|
-
marketer → middle_manager (report)
|
|
271
|
-
middle_manager → boss (report)
|
|
272
|
-
boss → middle_manager (feedback)
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
Adjust for the actual roles in this run. Assign `reports_to` on each role using the derived topology.
|
|
276
|
-
|
|
277
|
-
**Completeness rules (every role must be properly wired — no orphans):**
|
|
278
|
-
- Every executor role has **≥1 inbound edge** (how work reaches it — usually a `command`) and **≥1 outbound edge** (how its output leaves — usually a `report` or `handoff`).
|
|
279
|
-
- Where one role's output is another's input, connect them with a `handoff` in that direction (e.g. clerk → counsel; writer → editor). Make sequential producer→consumer chains explicit.
|
|
280
|
-
- The coordinator/boss has an inbound `report` from each role it commands, so results flow back up.
|
|
281
|
-
- Peer roles that critique each other get `feedback` edges; adversarial pairs (e.g. prosecutor ↔ defender) get reciprocal `handoff` edges.
|
|
282
|
-
- After building edges, **derive each role's input/output contract from them**: a role's `input_type` summarizes who/what its inbound edges deliver; its `output_type` summarizes what its outbound edges carry. Feed these into the generated definition from Step 2.5 so the spec and the topology agree.
|
|
283
|
-
|
|
284
|
-
A role that ends up with no inbound or no outbound edge is a bug — re-examine the topology before saving.
|
|
285
|
-
|
|
286
|
-
---
|
|
287
|
-
|
|
288
|
-
## Step 3.5 — TOPOLOGY VALIDATION GATE (BLOCKING — do not proceed to Step 4 until all checks pass)
|
|
289
|
-
|
|
290
|
-
Build the communication array now, then validate it with these checks:
|
|
291
|
-
|
|
292
|
-
```bash
|
|
293
|
-
# Check 1 — at least one edge exists
|
|
294
|
-
edges='<communication_json_array>'
|
|
295
|
-
edge_count=$(echo "$edges" | jq 'length')
|
|
296
|
-
[ "$edge_count" -eq 0 ] && echo "FAIL: communication array is empty — define edges now" && exit 1
|
|
297
|
-
|
|
298
|
-
# Check 2 — every role has at least one inbound OR outbound edge
|
|
299
|
-
role_ids=("<id1>" "<id2>" ... ) # all role IDs
|
|
300
|
-
for rid in "${role_ids[@]}"; do
|
|
301
|
-
has_edge=$(echo "$edges" | jq --arg r "$rid" '[.[] | select(.from==$r or .to==$r)] | length')
|
|
302
|
-
[ "$has_edge" -eq 0 ] && echo "FAIL: role '$rid' has no communication edges — it is an orphan" && exit 1
|
|
303
|
-
done
|
|
304
|
-
|
|
305
|
-
# Check 3 — every non-boss executor role has at least one inbound edge (receives work)
|
|
306
|
-
# and at least one outbound edge (delivers output)
|
|
307
|
-
echo "OK: topology validated — $edge_count edges, all roles wired"
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
If any check fails: go back to Step 3 and add the missing edges. **The `communication` array must be non-empty in the saved JSON** — the dashboard Chart tab draws its arrows from this array; an empty array means a blank chart with isolated nodes and no connection lines.
|
|
311
|
-
|
|
312
|
-
---
|
|
313
|
-
|
|
314
|
-
## Step 4 — Build Org Config
|
|
315
|
-
|
|
316
|
-
Produce an org config object using the resolved topology (not hardcoded to `hierarchical`).
|
|
317
|
-
|
|
318
|
-
Ask the user (or infer from prompt) for the optional Paperclip-style fields:
|
|
319
|
-
- **Budget**: max token budget for this org run (e.g. 500000 tokens). Use `unlimited` if not specified.
|
|
320
|
-
- **Governance**: approval policy — `auto` (agents act freely) | `board` (sensitive actions require `/mastermind:approve`) | `strict` (all external actions need approval)
|
|
321
|
-
- **Adapter**: which AI model/adapter the CEO agent should use (e.g. `claude-sonnet-4-6`, `claude-opus-4-7`). Default: `claude-sonnet-4-6`.
|
|
90
|
+
Produce an org config object matching `OrgDefSchema` exactly:
|
|
322
91
|
|
|
323
92
|
```json
|
|
324
93
|
{
|
|
325
94
|
"name": "<org_name>",
|
|
326
95
|
"goal": "<the goal the org exists to achieve>",
|
|
327
|
-
"
|
|
328
|
-
"
|
|
329
|
-
"
|
|
330
|
-
|
|
96
|
+
"status": "stopped",
|
|
97
|
+
"schedule": "<'<N>m' | '<N>h' | '<N>s' from Step 0 input, or null for a one-shot org>",
|
|
98
|
+
"run_config": {
|
|
99
|
+
"max_concurrent_agents": 4,
|
|
100
|
+
"budget_tokens": "<budget_tokens input, or 1000000 default>",
|
|
101
|
+
"memory_namespace": "org:<org_name>",
|
|
102
|
+
"max_turns_per_message": 30
|
|
103
|
+
},
|
|
331
104
|
"roles": [
|
|
332
105
|
{
|
|
333
106
|
"id": "<slug>",
|
|
334
107
|
"title": "<display title>",
|
|
335
|
-
"
|
|
336
|
-
"
|
|
337
|
-
"
|
|
338
|
-
"skills": ["<populated from the generated def's expertise in Step 2.5 — never left empty>"],
|
|
339
|
-
"adapter_config": {
|
|
340
|
-
"model": "<claude model id>",
|
|
341
|
-
"max_tokens": 8192
|
|
342
|
-
}
|
|
108
|
+
"type": "boss | specialist | <domain synonym>",
|
|
109
|
+
"reports_to": "<role id, or null for the single boss>",
|
|
110
|
+
"responsibilities": ["<3-6 specific duties — this text becomes part of the agent's role briefing>"]
|
|
343
111
|
}
|
|
344
|
-
]
|
|
345
|
-
"communication": [
|
|
346
|
-
{
|
|
347
|
-
"from": "<role_id>",
|
|
348
|
-
"to": "<role_id>",
|
|
349
|
-
"type": "command | report | feedback | handoff",
|
|
350
|
-
"protocol": "direct"
|
|
351
|
-
}
|
|
352
|
-
],
|
|
353
|
-
"governance": {
|
|
354
|
-
"policy": "auto | board | strict",
|
|
355
|
-
"approvals_file": ".monomind/orgs/<org_name>-approvals.json"
|
|
356
|
-
},
|
|
357
|
-
"board_id": "<uuid — filled in Step 6 after board creation>",
|
|
358
|
-
"todo_col_id": "<uuid — filled in Step 6>",
|
|
359
|
-
"doing_col_id": "<uuid — filled in Step 6>",
|
|
360
|
-
"done_col_id": "<uuid — filled in Step 6>",
|
|
361
|
-
"board_space": "<org_name>",
|
|
362
|
-
"board_name": "org-tasks",
|
|
363
|
-
"run_config": {
|
|
364
|
-
"checkpoint_interval_min": 30,
|
|
365
|
-
"max_concurrent_agents": 6,
|
|
366
|
-
"memory_namespace": "org:<org_name>",
|
|
367
|
-
"budget_tokens": "<number or 0 for unlimited>",
|
|
368
|
-
"alert_threshold": 0.8,
|
|
369
|
-
"ceo_adapter": "<model id>"
|
|
370
|
-
},
|
|
371
|
-
"loop": "<only included when --schedule was provided; see below>"
|
|
112
|
+
]
|
|
372
113
|
}
|
|
373
114
|
```
|
|
374
115
|
|
|
375
|
-
|
|
116
|
+
`status` starts `"stopped"` regardless of whether `schedule` is set — the org does not run until `monomind org run <name>` (one-shot) or `monomind org serve` (picks up any org whose `schedule` is set) is invoked.
|
|
376
117
|
|
|
377
|
-
|
|
378
|
-
{
|
|
379
|
-
"status": "stopped",
|
|
380
|
-
"loop": {
|
|
381
|
-
"poll_interval_minutes": "<parsed from schedule string>",
|
|
382
|
-
"last_run": null,
|
|
383
|
-
"next_run": null,
|
|
384
|
-
"run_prompt_file": ".monomind/loops/<org_name>.md"
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
`status` starts as `"stopped"`. The org does not run until `/mastermind:runorg --org <org_name>` is called (which transitions it to `"active"`).
|
|
118
|
+
Only include `adapter_config`, `provider`, or `policy` on a role when Step 2.3 populated them for it — leave them out entirely rather than writing empty objects.
|
|
390
119
|
|
|
391
120
|
---
|
|
392
121
|
|
|
393
|
-
## Step
|
|
122
|
+
## Step 4 — Show Plan and Confirm (confirm mode)
|
|
394
123
|
|
|
395
124
|
Render the org plan in a clear human-readable format:
|
|
396
125
|
|
|
@@ -400,40 +129,21 @@ Render the org plan in a clear human-readable format:
|
|
|
400
129
|
║ GOAL: <goal> ║
|
|
401
130
|
╚══════════════════════════════════════════════════╝
|
|
402
131
|
|
|
403
|
-
ROLES (N roles —
|
|
132
|
+
ROLES (N roles — exactly one boss, every reports_to resolves to a real role id)
|
|
404
133
|
─────
|
|
405
|
-
• [boss] CEO / Boss
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
Responsibilities:
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
• [middle_manager] Middle Manager
|
|
413
|
-
Agent type: project-shepherd
|
|
414
|
-
Skills: sprint planning, cross-team coordination, status tracking
|
|
415
|
-
Reports to: boss
|
|
416
|
-
Responsibilities: Sprint planning, cross-team coordination
|
|
417
|
-
Definition file: .claude/agents/generated/project-shepherd.md ✓
|
|
418
|
-
|
|
419
|
-
... (all roles — each showing agent_type, skills, and definition file status)
|
|
420
|
-
|
|
421
|
-
COMMUNICATION TOPOLOGY (N edges — must be non-zero)
|
|
422
|
-
──────────────────────
|
|
423
|
-
boss → middle_manager (command)
|
|
424
|
-
middle_manager → content_writer (command)
|
|
425
|
-
content_writer → content_reviewer (handoff)
|
|
426
|
-
content_reviewer → middle_manager (report)
|
|
427
|
-
middle_manager → boss (report)
|
|
428
|
-
... (all edges — every role must appear here at least once)
|
|
134
|
+
• [boss] CEO / Boss (type: boss, reports_to: none)
|
|
135
|
+
Responsibilities: Strategic oversight, final decisions, coordinates the team via org_send
|
|
136
|
+
|
|
137
|
+
• [content_writer] Content Writer (type: specialist, reports_to: boss)
|
|
138
|
+
Responsibilities: Draft posts per the content calendar, hand off to content_reviewer
|
|
139
|
+
|
|
140
|
+
... (all roles)
|
|
429
141
|
|
|
430
142
|
SETTINGS
|
|
431
143
|
────────
|
|
432
|
-
|
|
433
|
-
Memory: org:<org_name>
|
|
434
|
-
|
|
435
|
-
Schedule: <"every <poll_interval_minutes> minutes" if --schedule; otherwise "manual (no auto-schedule)">
|
|
436
|
-
Status: <"stopped (run /mastermind:runorg --org <org_name> to activate)" if --schedule; otherwise "—">
|
|
144
|
+
Budget: <run_config.budget_tokens> tokens (split evenly across N roles)
|
|
145
|
+
Memory namespace: org:<org_name>
|
|
146
|
+
Schedule: <"every <N> <unit>" if schedule set; otherwise "manual — run with `monomind org run <org_name>`">
|
|
437
147
|
|
|
438
148
|
Type "go" to save this org, or describe changes.
|
|
439
149
|
```
|
|
@@ -444,13 +154,10 @@ If the user requests changes, apply them and re-render. Repeat until confirmed.
|
|
|
444
154
|
|
|
445
155
|
---
|
|
446
156
|
|
|
447
|
-
## Step
|
|
448
|
-
|
|
449
|
-
Set shell variables from the resolved inputs (use the actual `org_name` value from Step 1 and `session_id` input):
|
|
157
|
+
## Step 5 — Save Org Config
|
|
450
158
|
|
|
451
159
|
```bash
|
|
452
|
-
org_name="<resolved org name from Step 1>"
|
|
453
|
-
session_id="<session_id input>" # passed by command wrapper
|
|
160
|
+
org_name="<resolved org name from Step 1>"
|
|
454
161
|
orgJson=".monomind/orgs/${org_name}.json"
|
|
455
162
|
mkdir -p .monomind/orgs
|
|
456
163
|
```
|
|
@@ -458,355 +165,53 @@ mkdir -p .monomind/orgs
|
|
|
458
165
|
Write the confirmed org config as JSON using `jq` to guarantee valid encoding:
|
|
459
166
|
|
|
460
167
|
```bash
|
|
461
|
-
# Build the config JSON from the confirmed org plan and write atomically.
|
|
462
168
|
# Set shell variables from the confirmed plan before running this block:
|
|
463
|
-
#
|
|
464
|
-
# budget_tokens_val — integer or 0 for unlimited (from Step 4)
|
|
465
|
-
# ceo_adapter — model id string (from Step 4)
|
|
466
|
-
# poll_interval_minutes — integer (from --schedule), or "" if no schedule
|
|
169
|
+
# goal, schedule_val ("" if none), budget_tokens_val, roles_json (JSON array matching the role shape above)
|
|
467
170
|
jq -n \
|
|
468
171
|
--arg name "$org_name" \
|
|
469
172
|
--arg goal "$goal" \
|
|
470
|
-
--arg
|
|
173
|
+
--arg schedule "${schedule_val:-}" \
|
|
174
|
+
--argjson budget_tokens "${budget_tokens_val:-1000000}" \
|
|
471
175
|
--argjson roles "$roles_json" \
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
created_at:(now|todate),roles:$roles,communication:$communication,
|
|
478
|
-
governance:{policy:$gov_policy,approvals_file:(".monomind/orgs/"+$name+"-approvals.json")},
|
|
479
|
-
board_space:$name,board_name:"org-tasks",
|
|
480
|
-
run_config:{
|
|
481
|
-
checkpoint_interval_min:30,
|
|
482
|
-
max_concurrent_agents:6,
|
|
483
|
-
memory_namespace:("org:"+$name),
|
|
484
|
-
budget_tokens:$budget_tokens,
|
|
485
|
-
alert_threshold:0.8,
|
|
486
|
-
ceo_adapter:$ceo_adapter
|
|
487
|
-
}}' \
|
|
176
|
+
'{name:$name,goal:$goal,status:"stopped",
|
|
177
|
+
schedule:(if $schedule=="" then null else $schedule end),
|
|
178
|
+
run_config:{max_concurrent_agents:4,budget_tokens:$budget_tokens,
|
|
179
|
+
memory_namespace:("org:"+$name),max_turns_per_message:30},
|
|
180
|
+
roles:$roles}' \
|
|
488
181
|
> "${orgJson}.tmp" && mv "${orgJson}.tmp" "$orgJson"
|
|
489
182
|
```
|
|
490
183
|
|
|
491
|
-
**
|
|
492
|
-
|
|
493
|
-
```bash
|
|
494
|
-
# Only run this block when poll_interval_minutes is set (i.e. --schedule was used)
|
|
495
|
-
tmp="${orgJson}.tmp"
|
|
496
|
-
jq \
|
|
497
|
-
--argjson interval "$poll_interval_minutes" \
|
|
498
|
-
--arg run_prompt_file ".monomind/loops/${org_name}.md" \
|
|
499
|
-
'. + {
|
|
500
|
-
status: "stopped",
|
|
501
|
-
loop: {
|
|
502
|
-
poll_interval_minutes: $interval,
|
|
503
|
-
last_run: null,
|
|
504
|
-
next_run: null,
|
|
505
|
-
run_prompt_file: $run_prompt_file
|
|
506
|
-
}
|
|
507
|
-
}' \
|
|
508
|
-
"$orgJson" > "$tmp" && mv "$tmp" "$orgJson"
|
|
509
|
-
```
|
|
510
|
-
|
|
511
|
-
Create the monotask space, board, and default columns (space is required — abort before creating board if space fails):
|
|
512
|
-
```bash
|
|
513
|
-
# Step 1 — Space (required first)
|
|
514
|
-
space_id=$(monotask space list 2>/dev/null | awk -F' \| ' -v n="$org_name" '$2==n{print $1}' | head -1)
|
|
515
|
-
[ -z "$space_id" ] && space_id=$(monotask space create "$org_name" 2>&1 | grep -oE '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')
|
|
516
|
-
[ -z "$space_id" ] && { echo "ERROR: Could not find or create space '$org_name' — verify monotask is installed (run: monotask --version)"; exit 1; }
|
|
517
|
-
|
|
518
|
-
# Step 2 — Board (created only after space is confirmed)
|
|
519
|
-
board_id=$(monotask board create "org-tasks" --json | jq -r '.id // empty')
|
|
520
|
-
[ -z "$board_id" ] && { echo "ERROR: Failed to create monotask board"; exit 1; }
|
|
521
|
-
|
|
522
|
-
# Step 3 — Link board to space immediately
|
|
523
|
-
monotask space boards add "$space_id" "$board_id" >/dev/null 2>&1 || true
|
|
524
|
-
|
|
525
|
-
# Step 4 — Columns
|
|
526
|
-
todo_col_id=$(monotask column create "$board_id" "Todo" --json | jq -r '.id // empty')
|
|
527
|
-
doing_col_id=$(monotask column create "$board_id" "Doing" --json | jq -r '.id // empty')
|
|
528
|
-
done_col_id=$(monotask column create "$board_id" "Done" --json | jq -r '.id // empty')
|
|
529
|
-
[ -z "$todo_col_id" ] && { echo "ERROR: Failed to create 'Todo' column on board $board_id"; exit 1; }
|
|
530
|
-
[ -z "$doing_col_id" ] && { echo "ERROR: Failed to create 'Doing' column on board $board_id"; exit 1; }
|
|
531
|
-
[ -z "$done_col_id" ] && { echo "ERROR: Failed to create 'Done' column on board $board_id"; exit 1; }
|
|
532
|
-
```
|
|
533
|
-
|
|
534
|
-
Patch the saved org config with the board and column IDs:
|
|
535
|
-
```bash
|
|
536
|
-
tmp="${orgJson}.tmp"
|
|
537
|
-
jq --arg board_id "$board_id" \
|
|
538
|
-
--arg todo_col_id "$todo_col_id" \
|
|
539
|
-
--arg doing_col_id "$doing_col_id" \
|
|
540
|
-
--arg done_col_id "$done_col_id" \
|
|
541
|
-
'. + {board_id:$board_id,todo_col_id:$todo_col_id,doing_col_id:$doing_col_id,done_col_id:$done_col_id}' \
|
|
542
|
-
"$orgJson" > "$tmp" && mv "$tmp" "$orgJson"
|
|
543
|
-
```
|
|
544
|
-
|
|
545
|
-
**POST-SAVE VALIDATION (run immediately after saving — abort if either check fails):**
|
|
184
|
+
**POST-SAVE VALIDATION (run immediately after saving — abort if any check fails):**
|
|
546
185
|
|
|
547
186
|
```bash
|
|
548
|
-
# Check 1 —
|
|
549
|
-
|
|
550
|
-
if [ "$
|
|
551
|
-
echo "ERROR:
|
|
552
|
-
echo "Fix: go back to Step 3 and Step 3.5, define edges, then re-save."
|
|
187
|
+
# Check 1 — exactly one root role (reports_to null)
|
|
188
|
+
root_count=$(jq '[.roles[] | select(.reports_to == null)] | length' "$orgJson")
|
|
189
|
+
if [ "$root_count" -ne 1 ]; then
|
|
190
|
+
echo "ERROR: expected exactly one role with reports_to: null, found $root_count — fix the roles array and re-save."
|
|
553
191
|
exit 1
|
|
554
192
|
fi
|
|
555
|
-
echo "✓
|
|
556
|
-
|
|
557
|
-
# Check 2 — every role
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
193
|
+
echo "✓ Exactly one root role"
|
|
194
|
+
|
|
195
|
+
# Check 2 — every non-root role's reports_to resolves to a real role id
|
|
196
|
+
bad_reports=$(jq -r '
|
|
197
|
+
([.roles[].id]) as $ids |
|
|
198
|
+
[.roles[] | select(.reports_to != null and (.reports_to as $r | $ids | index($r) | not)) | .id]
|
|
199
|
+
| join(", ")' "$orgJson")
|
|
200
|
+
if [ -n "$bad_reports" ]; then
|
|
201
|
+
echo "ERROR: these roles have a reports_to that doesn't match any role id: $bad_reports"
|
|
563
202
|
exit 1
|
|
564
203
|
fi
|
|
565
|
-
echo "✓ All
|
|
566
|
-
|
|
567
|
-
# Check 3 — every role has skills (non-empty array)
|
|
568
|
-
empty_skills=$(jq -r '.roles[] | select((.skills // []) | length == 0) | .id' "$orgJson" 2>/dev/null)
|
|
569
|
-
if [ -n "$empty_skills" ]; then
|
|
570
|
-
echo "WARNING: These roles have empty skills arrays — dashboard Skills tab will show nothing:"
|
|
571
|
-
echo "$empty_skills"
|
|
572
|
-
echo "Fix: populate skills from each role's generated agent definition."
|
|
573
|
-
fi
|
|
574
|
-
|
|
575
|
-
echo "✓ Org config validated — org is ready to run"
|
|
576
|
-
```
|
|
577
|
-
|
|
578
|
-
---
|
|
579
|
-
|
|
580
|
-
## Step 6.7 — Generate Loop Prompt File (scheduled orgs only)
|
|
581
|
-
|
|
582
|
-
**Skip this step if `--schedule` was NOT provided.**
|
|
583
|
-
|
|
584
|
-
If `poll_interval_minutes` is set, generate the self-scheduling loop prompt at `.monomind/loops/<org_name>.md`.
|
|
585
|
-
|
|
586
|
-
This file is the single source of truth for one scheduled iteration. It is passed verbatim as the `prompt` argument to `ScheduleWakeup` at the end of every iteration — the loop is self-perpetuating as long as `status == "active"`.
|
|
587
|
-
|
|
588
|
-
**Loop prompt structure:**
|
|
204
|
+
echo "✓ All reports_to values resolve"
|
|
589
205
|
|
|
590
|
-
|
|
206
|
+
# Check 3 — schema sanity check via the CLI itself (parses with OrgDefSchema, same code path org run/serve use)
|
|
207
|
+
npx monomind@latest org list >/dev/null 2>&1 || echo "WARNING: could not verify via CLI — check .monomind/orgs manually"
|
|
591
208
|
|
|
592
|
-
|
|
593
|
-
# <org_name> — Loop Prompt
|
|
594
|
-
|
|
595
|
-
**Controlled by:** `.monomind/orgs/<org_name>.json` → `status` field
|
|
596
|
-
**Start:** `/mastermind:runorg --org <org_name>` (sets `status: "active"` and runs first iteration)
|
|
597
|
-
**Stop:** `/mastermind:stoporg --org <org_name>` (sets `status: "stopped"` — next wakeup exits without rescheduling)
|
|
598
|
-
**Pause (HIL):** set `status: "paused"` in `.monomind/orgs/<org_name>.json` — loop keeps waking up but skips work until status returns to `"active"`
|
|
599
|
-
|
|
600
|
-
---
|
|
601
|
-
|
|
602
|
-
## Step 0 — Status Gate (REQUIRED FIRST — do not skip)
|
|
603
|
-
|
|
604
|
-
```bash
|
|
605
|
-
ORG_FILE=".monomind/orgs/<org_name>.json"
|
|
606
|
-
LOOP_STATUS=$(jq -r '.status // "stopped"' "$ORG_FILE" 2>/dev/null || echo "stopped")
|
|
607
|
-
```
|
|
608
|
-
|
|
609
|
-
- If `LOOP_STATUS == "active"` → continue to Step 1.
|
|
610
|
-
- If `LOOP_STATUS == "paused"` → print "Org '<org_name>' is paused — skipping iteration. Jump directly to Schedule Next." Do NOT run Steps 1–N.
|
|
611
|
-
- If anything else (including `"stopped"`) → print "Org '<org_name>' status is '$LOOP_STATUS' — exiting loop. **Do NOT call ScheduleWakeup.**" and stop.
|
|
612
|
-
|
|
613
|
-
---
|
|
614
|
-
|
|
615
|
-
## Step 1 — Record Iteration Start
|
|
616
|
-
|
|
617
|
-
```bash
|
|
618
|
-
ORG_FILE=".monomind/orgs/<org_name>.json"
|
|
619
|
-
tmp="${ORG_FILE}.tmp"
|
|
620
|
-
jq '.loop.last_run = (now|todate)' "$ORG_FILE" > "$tmp" && mv "$tmp" "$ORG_FILE"
|
|
621
|
-
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
622
|
-
CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
|
|
623
|
-
# Unique run ID — used to thread all events in the Chat tab under one session
|
|
624
|
-
RUN_ID="run-$(date -u +%Y%m%dT%H%M%S)"
|
|
625
|
-
# Capture Claude project dir for per-agent token tracking
|
|
626
|
-
CLAUDE_PROJECT_DIR="$HOME/.claude/projects/$(echo "$REPO_ROOT" | tr '/' '-' | sed 's/^-//')"
|
|
627
|
-
|
|
628
|
-
# Comm helper — emits org:comms so Chat tab shows agent-to-agent messages
|
|
629
|
-
_comm() {
|
|
630
|
-
local _from="$1" _to="$2" _msg="$3"
|
|
631
|
-
curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
|
|
632
|
-
-H "Content-Type: application/json" \
|
|
633
|
-
-d "$(jq -cn \
|
|
634
|
-
--arg org "<org_name>" \
|
|
635
|
-
--arg runId "$RUN_ID" \
|
|
636
|
-
--arg from "$_from" \
|
|
637
|
-
--arg to "$_to" \
|
|
638
|
-
--arg msg "$_msg" \
|
|
639
|
-
'{type:"org:comms",org:$org,runId:$runId,from:$from,to:$to,msg:$msg,ts:(now*1000|floor)}')" || true
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
# Register this run with the server — creates run file and enables Chat tab dropdown
|
|
643
|
-
curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
|
|
644
|
-
-H "Content-Type: application/json" \
|
|
645
|
-
-d "$(jq -cn \
|
|
646
|
-
--arg org "<org_name>" \
|
|
647
|
-
--arg runId "$RUN_ID" \
|
|
648
|
-
--arg goal "<goal>" \
|
|
649
|
-
'{type:"run:start",org:$org,runId:$runId,goal:$goal,ts:(now*1000|floor)}')" || true
|
|
650
|
-
```
|
|
651
|
-
|
|
652
|
-
---
|
|
653
|
-
|
|
654
|
-
## [Org-specific iteration steps]
|
|
655
|
-
|
|
656
|
-
<IMPORTANT: Generate the actual work steps here from the org's goal and roles. These are NOT generic placeholders — write real, actionable steps derived from the org's goal, roles, and communication topology.>
|
|
657
|
-
|
|
658
|
-
<For a GitHub issue-resolver org, these would be: find next issue, claim it, implement, test, deploy, report.>
|
|
659
|
-
<For a content org, these would be: check content calendar, assign writers, review drafts, publish.>
|
|
660
|
-
<Derive from orgConfig.goal and orgConfig.roles[].responsibilities — be specific.>
|
|
661
|
-
|
|
662
|
-
**REQUIRED — include these patterns at every agent handoff:**
|
|
663
|
-
|
|
664
|
-
1. **Before spawning each agent**, snapshot the Claude project JSONL files and emit a `_comm` from the dispatching role to the receiving role describing the task:
|
|
665
|
-
```bash
|
|
666
|
-
JSONL_SNAP_<N>=$(ls -t "$CLAUDE_PROJECT_DIR"/*.jsonl 2>/dev/null | head -20 | sort)
|
|
667
|
-
_comm "<dispatcher-role-id>" "<agent-role-id>" "Task: <what the agent is being asked to do>"
|
|
668
|
-
```
|
|
669
|
-
|
|
670
|
-
2. **After the agent returns**, emit a `_comm` from that agent back to its caller with the result summary, then emit its token usage:
|
|
671
|
-
```bash
|
|
672
|
-
JSONL_SNAP_<N+1>=$(ls -t "$CLAUDE_PROJECT_DIR"/*.jsonl 2>/dev/null | head -20 | sort)
|
|
673
|
-
NEW_JSONL=$(comm -13 <(echo "$JSONL_SNAP_<N>") <(echo "$JSONL_SNAP_<N+1>") | head -1)
|
|
674
|
-
_comm "<agent-role-id>" "<dispatcher-role-id>" "Result: <one-sentence summary of what the agent returned>"
|
|
675
|
-
if [ -n "$NEW_JSONL" ] && [ -f "$NEW_JSONL" ]; then
|
|
676
|
-
USAGE=$(python3 -c "
|
|
677
|
-
import json, sys
|
|
678
|
-
tin=tout=0
|
|
679
|
-
for l in open(sys.argv[1]):
|
|
680
|
-
try:
|
|
681
|
-
d=json.loads(l)
|
|
682
|
-
u=d.get('message',{}).get('usage',{})
|
|
683
|
-
tin+=u.get('input_tokens',0); tout+=u.get('output_tokens',0)
|
|
684
|
-
except: pass
|
|
685
|
-
cost=tin*3e-6+tout*15e-6
|
|
686
|
-
print(json.dumps({'tokens_in':tin,'tokens_out':tout,'cost_usd':round(cost,6)}))
|
|
687
|
-
" "$NEW_JSONL" 2>/dev/null || echo '{"tokens_in":0,"tokens_out":0,"cost_usd":0}')
|
|
688
|
-
curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
|
|
689
|
-
-H "Content-Type: application/json" \
|
|
690
|
-
-d "$(echo "$USAGE" | jq \
|
|
691
|
-
--arg org "<org_name>" \
|
|
692
|
-
--arg role "<agent-role-id>" \
|
|
693
|
-
--arg runId "$RUN_ID" \
|
|
694
|
-
'. + {type:"agent:usage",org:$org,role:$role,runId:$runId,ts:(now*1000|floor|tostring|tonumber)}')" || true
|
|
695
|
-
fi
|
|
696
|
-
```
|
|
697
|
-
|
|
698
|
-
3. Use the actual content from the agent's return value in the `_comm` `msg` field — not a generic placeholder. The Chat tab shows this text verbatim.
|
|
699
|
-
|
|
700
|
-
4. At cycle end (before Schedule Next), emit the completion comms and event:
|
|
701
|
-
```bash
|
|
702
|
-
_comm "<boss-role-id>" "sys" "Cycle complete: <one-line summary of what was accomplished>"
|
|
703
|
-
curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
|
|
704
|
-
-H "Content-Type: application/json" \
|
|
705
|
-
-d "$(jq -cn \
|
|
706
|
-
--arg org "<org_name>" \
|
|
707
|
-
--arg runId "$RUN_ID" \
|
|
708
|
-
'{type:"org:cycle:complete",org:$org,runId:$runId,ts:(now*1000|floor)}')" || true
|
|
709
|
-
```
|
|
710
|
-
|
|
711
|
-
---
|
|
712
|
-
|
|
713
|
-
## Schedule Next (ONLY if status is active or paused)
|
|
714
|
-
|
|
715
|
-
Re-check org status before rescheduling:
|
|
716
|
-
|
|
717
|
-
```bash
|
|
718
|
-
ORG_FILE=".monomind/orgs/<org_name>.json"
|
|
719
|
-
LOOP_STATUS=$(jq -r '.status // "stopped"' "$ORG_FILE" 2>/dev/null || echo "stopped")
|
|
720
|
-
```
|
|
721
|
-
|
|
722
|
-
If `LOOP_STATUS == "active"` or `LOOP_STATUS == "paused"`:
|
|
723
|
-
|
|
724
|
-
1. Read this loop prompt file verbatim:
|
|
725
|
-
```bash
|
|
726
|
-
LOOP_PROMPT=$(cat .monomind/loops/<org_name>.md)
|
|
727
|
-
```
|
|
728
|
-
|
|
729
|
-
2. Call `ScheduleWakeup` with:
|
|
730
|
-
- `delaySeconds`: `<poll_interval_minutes * 60>`
|
|
731
|
-
- `reason`: `"<org_name>: next scheduled poll (every <poll_interval_minutes> min)"`
|
|
732
|
-
- `prompt`: the full contents of `$LOOP_PROMPT`
|
|
733
|
-
|
|
734
|
-
3. Update `next_run` in the org JSON:
|
|
735
|
-
```bash
|
|
736
|
-
ORG_FILE=".monomind/orgs/<org_name>.json"
|
|
737
|
-
tmp="${ORG_FILE}.tmp"
|
|
738
|
-
next_ts=$(( $(date +%s) + <poll_interval_minutes * 60> ))
|
|
739
|
-
next_iso=$(date -u -r "$next_ts" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
|
|
740
|
-
|| date -u -d "@$next_ts" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
|
|
741
|
-
|| python3 -c "import datetime; print(datetime.datetime.utcfromtimestamp($next_ts).strftime('%Y-%m-%dT%H:%M:%SZ'))")
|
|
742
|
-
jq --arg next "$next_iso" '.loop.next_run = $next' "$ORG_FILE" > "$tmp" && mv "$tmp" "$ORG_FILE"
|
|
743
|
-
```
|
|
744
|
-
|
|
745
|
-
4. Emit `org:loop:scheduled` event:
|
|
746
|
-
```bash
|
|
747
|
-
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
748
|
-
CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
|
|
749
|
-
curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
|
|
750
|
-
-H "Content-Type: application/json" \
|
|
751
|
-
-d "$(jq -cn --arg org "<org_name>" --arg next "$next_iso" --arg proj "$REPO_ROOT" \
|
|
752
|
-
'{type:"org:loop:scheduled",org:$org,next_run:$next,project:$proj,ts:(now*1000|floor)}')" || true
|
|
753
|
-
```
|
|
754
|
-
|
|
755
|
-
If `LOOP_STATUS` is anything else (e.g. `"stopped"`) → print "Org '<org_name>' loop ended — not rescheduling." and exit.
|
|
756
|
-
````
|
|
757
|
-
|
|
758
|
-
**Before writing: substitute ALL `<org_name>` tokens with the actual resolved org name string (the value from Step 1, e.g. `research-pod`), and all `<poll_interval_minutes>` tokens with the numeric value. These are template placeholders — the written file must contain no angle-bracket tokens. Every `ORG_FILE=".monomind/orgs/<org_name>.json"` becomes `ORG_FILE=".monomind/orgs/research-pod.json"`, every `cat .monomind/loops/<org_name>.md` becomes `cat .monomind/loops/research-pod.md`, etc. Leaving any `<...>` token unexpanded will cause shell failures at loop execution time.**
|
|
759
|
-
|
|
760
|
-
**Write this file to disk:**
|
|
761
|
-
|
|
762
|
-
```bash
|
|
763
|
-
mkdir -p .monomind/loops
|
|
764
|
-
# Write the generated loop prompt (with all placeholders substituted)
|
|
765
|
-
# to .monomind/loops/<org_name>.md (← substitute this path too)
|
|
766
|
-
```
|
|
767
|
-
|
|
768
|
-
Use the Write tool (not Bash echo/cat) to write the file so the content is verbatim.
|
|
769
|
-
|
|
770
|
-
The org-specific iteration steps (the block between Step 1 and "Schedule Next") must be **generated from the actual org** — goal, roles, responsibilities — not left as generic placeholders.
|
|
771
|
-
|
|
772
|
-
---
|
|
773
|
-
|
|
774
|
-
## Step 7 — Emit Dashboard Events
|
|
775
|
-
|
|
776
|
-
Read values from the saved JSON file and emit two events: `domain:complete` (for the session stream) and `org:create` (so the dashboard Orgs panel registers the new org immediately):
|
|
777
|
-
|
|
778
|
-
```bash
|
|
779
|
-
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
780
|
-
CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
|
|
781
|
-
orgName=$(jq -r '.name' "$orgJson")
|
|
782
|
-
goal_val=$(jq -r '.goal' "$orgJson")
|
|
783
|
-
rolesCount=$(jq '.roles | length // 0' "$orgJson")
|
|
784
|
-
|
|
785
|
-
# domain:complete — for session correlation
|
|
786
|
-
curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
|
|
787
|
-
-H "Content-Type: application/json" \
|
|
788
|
-
-d "$(jq -cn \
|
|
789
|
-
--arg session "$session_id" \
|
|
790
|
-
--arg orgName "$orgName" \
|
|
791
|
-
--arg goal "$goal_val" \
|
|
792
|
-
--argjson rolesCount "$rolesCount" \
|
|
793
|
-
'{type:"domain:complete",session:$session,domain:"ops",status:"complete",
|
|
794
|
-
org:$orgName,goal:$goal,roles_count:$rolesCount,ts:(now*1000|floor)}')" || true
|
|
795
|
-
|
|
796
|
-
# org:create — so handleOrgEvent routes it to the Orgs panel event log and SSE triggers list refresh
|
|
797
|
-
curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
|
|
798
|
-
-H "Content-Type: application/json" \
|
|
799
|
-
-d "$(jq -cn \
|
|
800
|
-
--arg session "$session_id" \
|
|
801
|
-
--arg org "$orgName" \
|
|
802
|
-
--arg goal "$goal_val" \
|
|
803
|
-
--arg proj "$(pwd)" \
|
|
804
|
-
'{type:"org:create",session:$session,org:$org,goal:$goal,project:$proj,ts:(now*1000|floor)}')" || true
|
|
209
|
+
echo "✓ Org config validated — ready for: monomind org run ${org_name}"
|
|
805
210
|
```
|
|
806
211
|
|
|
807
212
|
---
|
|
808
213
|
|
|
809
|
-
## Step
|
|
214
|
+
## Step 6 — Return Output
|
|
810
215
|
|
|
811
216
|
```yaml
|
|
812
217
|
domain: ops
|
|
@@ -814,12 +219,6 @@ status: complete
|
|
|
814
219
|
artifacts:
|
|
815
220
|
- path: .monomind/orgs/<org_name>.json
|
|
816
221
|
type: config
|
|
817
|
-
- path: .claude/agents/generated/<agent_type>.md
|
|
818
|
-
type: agent-definition
|
|
819
|
-
note: "one per role whose agent_type lacked a usable definition (skills, instructions, input/output)"
|
|
820
|
-
- path: .monomind/loops/<org_name>.md
|
|
821
|
-
type: loop-prompt
|
|
822
|
-
note: "only present when --schedule was used; omit otherwise"
|
|
823
222
|
decisions:
|
|
824
223
|
- what: "Org <org_name> created with N roles"
|
|
825
224
|
why: "Role mapping derived from goal and user description"
|
|
@@ -829,34 +228,25 @@ lessons:
|
|
|
829
228
|
- what_worked: "Auto-suggested roles matched user intent"
|
|
830
229
|
- what_didnt: ""
|
|
831
230
|
next_actions:
|
|
832
|
-
- "Run
|
|
833
|
-
- "
|
|
834
|
-
- "
|
|
835
|
-
- "
|
|
836
|
-
board_url: "monotask://<org_name>/org-tasks"
|
|
837
|
-
run_id: "<current UTC datetime as ISO8601, e.g. via $(date -u +%Y-%m-%dT%H:%M:%SZ)>"
|
|
231
|
+
- "Run `monomind org run <org_name>` to start the organization in the foreground"
|
|
232
|
+
- "Or `monomind org serve` to host it (and any other scheduled orgs) as a background daemon"
|
|
233
|
+
- "Edit .monomind/orgs/<org_name>.json directly, or use /mastermind:org-settings, to change goal/budget/roles"
|
|
234
|
+
- "`monomind org status <org_name>` to check runtime state; `monomind org stop <org_name>` to stop a running org"
|
|
838
235
|
```
|
|
839
236
|
|
|
840
237
|
Print confirmation:
|
|
841
238
|
```
|
|
842
239
|
✓ Org "<org_name>" saved to .monomind/orgs/<org_name>.json
|
|
843
|
-
→ Run:
|
|
240
|
+
→ Run: monomind org run <org_name>
|
|
844
241
|
```
|
|
845
242
|
|
|
846
|
-
If
|
|
243
|
+
If `schedule` was set, also print:
|
|
847
244
|
```
|
|
848
|
-
|
|
849
|
-
Schedule: every <poll_interval_minutes> minutes
|
|
850
|
-
Status: stopped (org will not run until /mastermind:runorg --org <org_name>)
|
|
851
|
-
|
|
852
|
-
Lifecycle:
|
|
853
|
-
Start: /mastermind:runorg --org <org_name>
|
|
854
|
-
Stop: /mastermind:stoporg --org <org_name>
|
|
855
|
-
List: /mastermind:orgs
|
|
245
|
+
Schedule: every <N> <unit> — pick it up with: monomind org serve
|
|
856
246
|
```
|
|
857
247
|
|
|
858
248
|
---
|
|
859
249
|
|
|
860
|
-
## Step
|
|
250
|
+
## Step 7 — Brain Write (standalone only)
|
|
861
251
|
|
|
862
252
|
If `caller` is not "command", follow _protocol.md Brain Write Procedure for domain `ops`.
|