@cohortapp/agent-sdk 2.11.15 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (280) hide show
  1. package/.env.example +37 -22
  2. package/README.md +2 -0
  3. package/bin/maestro.mjs +117 -39
  4. package/bin/maestro.test.mjs +175 -5
  5. package/docs/guides/front-door-session.md +313 -0
  6. package/docs/guides/mac-mini.md +100 -28
  7. package/docs/guides/org-onboarding.md +1 -1
  8. package/docs/guides/setup-wizard.md +9 -5
  9. package/docs/runbooks/cohort-cutover.md +11 -1
  10. package/docs/runbooks/mac-mini-bootstrap.md +38 -63
  11. package/lib/cadence-bus-requeue.test.mjs +83 -0
  12. package/lib/cadence-bus.mjs +43 -7
  13. package/lib/channels/inbox-item.mjs +59 -2
  14. package/lib/cli/board.mjs +285 -0
  15. package/lib/cli/board.test.mjs +227 -0
  16. package/lib/cli/design.mjs +185 -0
  17. package/lib/cli/design.test.mjs +270 -0
  18. package/lib/cli/doctor-checks.mjs +441 -0
  19. package/lib/cli/doctor-checks.test.mjs +336 -0
  20. package/lib/cli/global-setup-extras.mjs +454 -0
  21. package/lib/cli/global-setup-extras.test.mjs +462 -0
  22. package/lib/cli/inbox.mjs +304 -0
  23. package/lib/cli/inbox.test.mjs +230 -0
  24. package/lib/cli/session-ack.mjs +63 -0
  25. package/lib/cli/session-ack.test.mjs +63 -0
  26. package/lib/cli/session.mjs +760 -0
  27. package/lib/cli/session.test.mjs +613 -0
  28. package/lib/collective/global-config.mjs +209 -6
  29. package/lib/collective/global-config.test.mjs +145 -0
  30. package/lib/collective/global-skills.mjs +145 -0
  31. package/lib/collective/global-skills.test.mjs +126 -0
  32. package/lib/collective/presence.mjs +4 -3
  33. package/lib/collective/vendor-skills.mjs +305 -0
  34. package/lib/collective/vendor-skills.test.mjs +306 -0
  35. package/lib/comms/send-gate.mjs +115 -0
  36. package/lib/comms/send-gate.test.mjs +113 -0
  37. package/lib/design/design-md.mjs +793 -0
  38. package/lib/design/design-md.test.mjs +318 -0
  39. package/lib/design/fixtures/DESIGN.golden.md +238 -0
  40. package/lib/design/fixtures/PRODUCT.golden.md +67 -0
  41. package/lib/design/fixtures/foundation.json +133 -0
  42. package/lib/design/refresh-gate.mjs +154 -0
  43. package/lib/design/refresh-gate.test.mjs +144 -0
  44. package/lib/design/write.mjs +275 -0
  45. package/lib/design/write.test.mjs +241 -0
  46. package/lib/feature-init.mjs +2 -2
  47. package/lib/mcp/server.test.mjs +9 -4
  48. package/lib/model-router/spawn.test.mjs +21 -0
  49. package/lib/org/board-mine-cache.mjs +99 -0
  50. package/lib/org/board-mine-cache.test.mjs +53 -0
  51. package/lib/org/board.mjs +11 -0
  52. package/lib/org/board.test.mjs +11 -1
  53. package/lib/org/client.mjs +36 -0
  54. package/lib/org/client.test.mjs +46 -0
  55. package/lib/org/inbound/directedness.mjs +18 -2
  56. package/lib/org/inbound/directedness.test.mjs +58 -0
  57. package/lib/org/inbound/index.mjs +8 -1
  58. package/lib/org/inbound/index.test.mjs +22 -0
  59. package/lib/org/mesh-directives.test.mjs +110 -0
  60. package/lib/org/mesh.mjs +61 -1
  61. package/lib/org/protocol.checksum +1 -1
  62. package/lib/org/protocol.mjs +52 -0
  63. package/lib/org/protocol.test.mjs +12 -1
  64. package/lib/org/registry.mjs +3 -2
  65. package/lib/org/tool-surface.mjs +120 -0
  66. package/lib/org/tool-surface.test.mjs +118 -5
  67. package/lib/prompts/parallelism.mjs +79 -0
  68. package/lib/prompts/parallelism.test.mjs +177 -0
  69. package/lib/security/external-content.mjs +1 -1
  70. package/lib/security/external-content.test.mjs +17 -0
  71. package/lib/session/config.mjs +137 -0
  72. package/lib/session/config.test.mjs +92 -0
  73. package/lib/session/feed-core.mjs +229 -0
  74. package/lib/session/feed-core.test.mjs +198 -0
  75. package/lib/session/first-run.mjs +126 -0
  76. package/lib/session/first-run.test.mjs +121 -0
  77. package/lib/session/frontdoor.mjs +266 -0
  78. package/lib/session/frontdoor.test.mjs +205 -0
  79. package/lib/session/handoffs.mjs +295 -0
  80. package/lib/session/handoffs.test.mjs +183 -0
  81. package/lib/session/identity.mjs +220 -0
  82. package/lib/session/identity.test.mjs +180 -0
  83. package/lib/session/inbox-claims.mjs +434 -0
  84. package/lib/session/inbox-claims.test.mjs +286 -0
  85. package/lib/session/launch-args.mjs +161 -0
  86. package/lib/session/launch-args.test.mjs +157 -0
  87. package/lib/session/liveness.mjs +174 -0
  88. package/lib/session/liveness.test.mjs +100 -0
  89. package/lib/session/status-summary.mjs +172 -0
  90. package/lib/session/status-summary.test.mjs +118 -0
  91. package/lib/session-permissions.mjs +39 -3
  92. package/lib/session-permissions.test.mjs +20 -0
  93. package/lib/setup/claude-probe.mjs +161 -24
  94. package/lib/setup/claude-probe.test.mjs +187 -0
  95. package/lib/setup/sections/learning.mjs +2 -1
  96. package/lib/setup/sections/model.mjs +104 -24
  97. package/lib/setup/sections/model.test.mjs +240 -0
  98. package/lib/setup/sections/org.mjs +27 -2
  99. package/lib/setup/sections/org.test.mjs +35 -2
  100. package/lib/setup/sections/verify.mjs +5 -0
  101. package/lib/setup/state.mjs +30 -10
  102. package/lib/setup/state.test.mjs +24 -1
  103. package/lib/singleton.js +11 -3
  104. package/lib/singleton.test.mjs +16 -0
  105. package/lib/subagents/lock.mjs +1 -1
  106. package/lib/telemetry/collect.mjs +270 -6
  107. package/lib/telemetry/collect.test.mjs +196 -1
  108. package/lib/upgrade/global-refresh.mjs +108 -0
  109. package/lib/upgrade/global-refresh.test.mjs +65 -0
  110. package/lib/upgrade/launchd-reconcile.mjs +327 -0
  111. package/lib/upgrade/launchd-reconcile.test.mjs +272 -0
  112. package/lib/upgrade/post-steps.mjs +151 -0
  113. package/lib/upgrade/post-steps.test.mjs +200 -0
  114. package/lib/upgrade/verify.mjs +215 -0
  115. package/lib/upgrade/verify.test.mjs +164 -0
  116. package/lib/voice/outbound.mjs +3 -2
  117. package/lib/voice/post-call-brief.mjs +2 -1
  118. package/lib/voice/session-rotation.mjs +6 -1
  119. package/lib/voice/session-rotation.test.mjs +114 -0
  120. package/package.json +3 -3
  121. package/plugins/maestro-skills/plugin.json +25 -1
  122. package/plugins/maestro-skills/skills/board-work.md +63 -0
  123. package/plugins/maestro-skills/skills/cohort-design.md +153 -0
  124. package/plugins/maestro-skills/skills/inbound-triage.md +80 -0
  125. package/plugins/maestro-skills/skills/main-session.md +102 -0
  126. package/plugins/maestro-skills/skills/peer-sessions.md +65 -0
  127. package/plugins/maestro-skills/skills/persona-discipline.md +75 -0
  128. package/plugins/maestro-skills/vendor/emilkowalski/LICENSE +21 -0
  129. package/plugins/maestro-skills/vendor/emilkowalski/UPSTREAM.json +70 -0
  130. package/plugins/maestro-skills/vendor/emilkowalski/skills/animate/RECIPES.md +324 -0
  131. package/plugins/maestro-skills/vendor/emilkowalski/skills/animate/SKILL.md +199 -0
  132. package/plugins/maestro-skills/vendor/emilkowalski/skills/animation-vocabulary/SKILL.md +173 -0
  133. package/plugins/maestro-skills/vendor/emilkowalski/skills/apple-design/SKILL.md +282 -0
  134. package/plugins/maestro-skills/vendor/emilkowalski/skills/emil-design-eng/SKILL.md +674 -0
  135. package/plugins/maestro-skills/vendor/emilkowalski/skills/find-animation-opportunities/SKILL.md +132 -0
  136. package/plugins/maestro-skills/vendor/emilkowalski/skills/improve-animations/AUDIT.md +115 -0
  137. package/plugins/maestro-skills/vendor/emilkowalski/skills/improve-animations/PLAN-TEMPLATE.md +73 -0
  138. package/plugins/maestro-skills/vendor/emilkowalski/skills/improve-animations/SKILL.md +101 -0
  139. package/plugins/maestro-skills/vendor/emilkowalski/skills/prototype/PICKER.md +197 -0
  140. package/plugins/maestro-skills/vendor/emilkowalski/skills/prototype/SKILL.md +90 -0
  141. package/plugins/maestro-skills/vendor/emilkowalski/skills/review-animations/SKILL.md +112 -0
  142. package/plugins/maestro-skills/vendor/emilkowalski/skills/review-animations/STANDARDS.md +187 -0
  143. package/plugins/maestro-skills/vendor/impeccable/LICENSE +191 -0
  144. package/plugins/maestro-skills/vendor/impeccable/NOTICE.md +11 -0
  145. package/plugins/maestro-skills/vendor/impeccable/SKILL.md +86 -0
  146. package/plugins/maestro-skills/vendor/impeccable/UPSTREAM.json +201 -0
  147. package/plugins/maestro-skills/vendor/impeccable/agents/impeccable-asset-producer.md +42 -0
  148. package/plugins/maestro-skills/vendor/impeccable/agents/impeccable-documenter.md +29 -0
  149. package/plugins/maestro-skills/vendor/impeccable/agents/impeccable-finish-reviewer.md +43 -0
  150. package/plugins/maestro-skills/vendor/impeccable/agents/impeccable-manual-edit-applier.md +97 -0
  151. package/plugins/maestro-skills/vendor/impeccable/reference/adapt.md +312 -0
  152. package/plugins/maestro-skills/vendor/impeccable/reference/adapt.native.md +58 -0
  153. package/plugins/maestro-skills/vendor/impeccable/reference/android.md +46 -0
  154. package/plugins/maestro-skills/vendor/impeccable/reference/animate.md +89 -0
  155. package/plugins/maestro-skills/vendor/impeccable/reference/audit.md +136 -0
  156. package/plugins/maestro-skills/vendor/impeccable/reference/audit.native.md +139 -0
  157. package/plugins/maestro-skills/vendor/impeccable/reference/bolder.md +33 -0
  158. package/plugins/maestro-skills/vendor/impeccable/reference/clarify.md +94 -0
  159. package/plugins/maestro-skills/vendor/impeccable/reference/colorize.md +86 -0
  160. package/plugins/maestro-skills/vendor/impeccable/reference/craft-floor.md +44 -0
  161. package/plugins/maestro-skills/vendor/impeccable/reference/craft.md +5 -0
  162. package/plugins/maestro-skills/vendor/impeccable/reference/critique.md +806 -0
  163. package/plugins/maestro-skills/vendor/impeccable/reference/degraded/asset-producer.md +37 -0
  164. package/plugins/maestro-skills/vendor/impeccable/reference/degraded/documenter.md +24 -0
  165. package/plugins/maestro-skills/vendor/impeccable/reference/degraded/finish-reviewer.md +38 -0
  166. package/plugins/maestro-skills/vendor/impeccable/reference/degraded/manual-edit-applier.md +92 -0
  167. package/plugins/maestro-skills/vendor/impeccable/reference/delight.md +70 -0
  168. package/plugins/maestro-skills/vendor/impeccable/reference/distill.md +111 -0
  169. package/plugins/maestro-skills/vendor/impeccable/reference/doctor.md +54 -0
  170. package/plugins/maestro-skills/vendor/impeccable/reference/document.md +416 -0
  171. package/plugins/maestro-skills/vendor/impeccable/reference/extract.md +69 -0
  172. package/plugins/maestro-skills/vendor/impeccable/reference/harden.md +336 -0
  173. package/plugins/maestro-skills/vendor/impeccable/reference/hooks.md +111 -0
  174. package/plugins/maestro-skills/vendor/impeccable/reference/init.md +131 -0
  175. package/plugins/maestro-skills/vendor/impeccable/reference/ios.md +51 -0
  176. package/plugins/maestro-skills/vendor/impeccable/reference/layout.md +84 -0
  177. package/plugins/maestro-skills/vendor/impeccable/reference/live-setup.md +104 -0
  178. package/plugins/maestro-skills/vendor/impeccable/reference/live.md +325 -0
  179. package/plugins/maestro-skills/vendor/impeccable/reference/new-work.md +147 -0
  180. package/plugins/maestro-skills/vendor/impeccable/reference/onboard.md +234 -0
  181. package/plugins/maestro-skills/vendor/impeccable/reference/operate.md +61 -0
  182. package/plugins/maestro-skills/vendor/impeccable/reference/optimize.md +258 -0
  183. package/plugins/maestro-skills/vendor/impeccable/reference/overdrive.md +127 -0
  184. package/plugins/maestro-skills/vendor/impeccable/reference/polish.md +105 -0
  185. package/plugins/maestro-skills/vendor/impeccable/reference/quieter.md +99 -0
  186. package/plugins/maestro-skills/vendor/impeccable/reference/routing.md +24 -0
  187. package/plugins/maestro-skills/vendor/impeccable/reference/shape.md +59 -0
  188. package/plugins/maestro-skills/vendor/impeccable/reference/typeset.md +80 -0
  189. package/plugins/maestro-skills/vendor/impeccable/reference/visualize.md +46 -0
  190. package/plugins/maestro-skills/vendor/taste-skill/LICENSE +21 -0
  191. package/plugins/maestro-skills/vendor/taste-skill/UPSTREAM.json +37 -0
  192. package/plugins/maestro-skills/vendor/taste-skill/skills/minimalist-skill/SKILL.md +85 -0
  193. package/plugins/maestro-skills/vendor/taste-skill/skills/redesign-skill/SKILL.md +178 -0
  194. package/plugins/maestro-skills/vendor/taste-skill/skills/soft-skill/SKILL.md +98 -0
  195. package/plugins/maestro-skills/vendor/taste-skill/skills/taste-skill/SKILL.md +1206 -0
  196. package/plugins/maestro-skills/vendor/unlazy/LICENSE +21 -0
  197. package/plugins/maestro-skills/vendor/unlazy/SECURITY.md +72 -0
  198. package/plugins/maestro-skills/vendor/unlazy/SKILL.md +104 -0
  199. package/plugins/maestro-skills/vendor/unlazy/UPSTREAM.json +94 -0
  200. package/plugins/maestro-skills/vendor/unlazy/references/dispatch.md +82 -0
  201. package/plugins/maestro-skills/vendor/unlazy/references/gates.md +149 -0
  202. package/plugins/maestro-skills/vendor/unlazy/references/method.md +49 -0
  203. package/plugins/maestro-skills/vendor/unlazy/references/orchestration.md +107 -0
  204. package/plugins/maestro-skills/vendor/unlazy/references/parallel.md +133 -0
  205. package/plugins/maestro-skills/vendor/unlazy/references/token-economy.md +48 -0
  206. package/plugins/maestro-skills/vendor/unlazy/scripts/dispatch-check.mjs +139 -0
  207. package/plugins/maestro-skills/vendor/unlazy/scripts/gate-check.mjs +960 -0
  208. package/plugins/maestro-skills/vendor/unlazy/scripts/gate-lint.mjs +245 -0
  209. package/plugins/maestro-skills/vendor/unlazy/scripts/lib/check-supervisor.mjs +46 -0
  210. package/plugins/maestro-skills/vendor/unlazy/scripts/lib/dispatch.mjs +293 -0
  211. package/plugins/maestro-skills/vendor/unlazy/scripts/lib/gates.mjs +953 -0
  212. package/plugins/maestro-skills/vendor/unlazy/scripts/lib/process-tree.mjs +161 -0
  213. package/plugins/maestro-skills/vendor/unlazy/scripts/lib/regex-worker.mjs +9 -0
  214. package/plugins/maestro-skills/vendor/unlazy/templates/PLAN.md +116 -0
  215. package/plugins/maestro-skills/vendor/unlazy/templates/gates-leaf.md +51 -0
  216. package/plugins/maestro-skills/vendor/unlazy/templates/gates-node.md +51 -0
  217. package/scaffold/CLAUDE.md +24 -0
  218. package/scripts/ci/check-durable-write-seam.mjs +147 -0
  219. package/scripts/ci/check-durable-write-seam.test.mjs +90 -0
  220. package/scripts/ci/check-skill-packs.mjs +388 -0
  221. package/scripts/ci/check-skill-packs.test.mjs +495 -0
  222. package/scripts/ci/check.mjs +6 -0
  223. package/scripts/collective/hook-runner.mjs +39 -4
  224. package/scripts/collective/hook-runner.test.mjs +85 -2
  225. package/scripts/daemon/agent-daemon-board-mine.test.mjs +96 -0
  226. package/scripts/daemon/agent-daemon-design.test.mjs +238 -0
  227. package/scripts/daemon/agent-daemon-frontdoor.test.mjs +60 -0
  228. package/scripts/daemon/agent-daemon.mjs +249 -10
  229. package/scripts/daemon/agent-daemon.test.mjs +73 -0
  230. package/scripts/daemon/assurance-e2e.test.mjs +141 -6
  231. package/scripts/daemon/assurance.mjs +461 -37
  232. package/scripts/daemon/assurance.test.mjs +408 -43
  233. package/scripts/daemon/cadence-consumer-frontdoor.test.mjs +393 -0
  234. package/scripts/daemon/cadence-consumer.mjs +289 -89
  235. package/scripts/daemon/cadence-handlers.mjs +53 -0
  236. package/scripts/daemon/classifier.mjs +1 -1
  237. package/scripts/daemon/dispatcher-resume.test.mjs +166 -0
  238. package/scripts/daemon/dispatcher.mjs +127 -19
  239. package/scripts/daemon/health.mjs +12 -1
  240. package/scripts/daemon/inbox-deferral-session.test.mjs +49 -0
  241. package/scripts/daemon/inbox-deferral.mjs +6 -0
  242. package/scripts/daemon/lib/self-echo.mjs +201 -0
  243. package/scripts/daemon/lib/self-echo.test.mjs +153 -0
  244. package/scripts/daemon/maestro-daemon.mjs +3 -0
  245. package/scripts/daemon/prompt-builder.mjs +19 -3
  246. package/scripts/daemon/responder.mjs +51 -40
  247. package/scripts/daemon/sdk-version.mjs +51 -0
  248. package/scripts/daemon/sdk-version.test.mjs +31 -0
  249. package/scripts/hooks/pre-send-audit.sh +97 -4
  250. package/scripts/hooks/pre-send-audit.test.mjs +140 -1
  251. package/scripts/local-triggers/autoupdate.sh +243 -19
  252. package/scripts/local-triggers/autoupdate.test.mjs +518 -0
  253. package/scripts/local-triggers/generate-plists.sh +24 -1
  254. package/scripts/local-triggers/generate-plists.test.mjs +49 -11
  255. package/scripts/org/send-orgmail.first-contact.test.mjs +102 -0
  256. package/scripts/org/send-orgmail.mjs +27 -3
  257. package/scripts/poller/inbox-privilege-injection.test.mjs +167 -0
  258. package/scripts/poller/slack-poller.mjs +13 -1
  259. package/scripts/poller/utils.mjs +46 -1
  260. package/scripts/poller-launchd/install.sh +19 -11
  261. package/scripts/poller-launchd/install.test.mjs +243 -0
  262. package/scripts/poller-launchd/launchd-poller-wrapper.sh +92 -0
  263. package/scripts/poller-launchd/migrate.sh +66 -0
  264. package/scripts/poller-launchd/poller.plist.template +4 -2
  265. package/scripts/session/feed.mjs +237 -0
  266. package/scripts/session/feed.test.mjs +196 -0
  267. package/scripts/session/supervisor-sh.test.mjs +218 -0
  268. package/scripts/session/supervisor.mjs +328 -0
  269. package/scripts/session/supervisor.sh +141 -0
  270. package/scripts/session/supervisor.test.mjs +482 -0
  271. package/scripts/setup/configure-macos.sh +250 -55
  272. package/scripts/setup/configure-macos.test.mjs +306 -0
  273. package/scripts/setup/init-agent.sh +112 -7
  274. package/scripts/setup/init-agent.test.mjs +220 -1
  275. package/scripts/vendor/skill-packs.mjs +354 -0
  276. package/scripts/vendor/sync-skill-packs.mjs +242 -0
  277. package/scripts/vendor/sync-skill-packs.test.mjs +103 -0
  278. package/scripts/watchdog/memory-watchdog.sh +37 -1
  279. package/scripts/watchdog/memory-watchdog.test.mjs +64 -0
  280. package/scripts/setup/boot-claude-session.sh +0 -94
@@ -0,0 +1,313 @@
1
+ # The front-door session — operator guide
2
+
3
+ Every seat runs two launchd jobs and one brain. This page is for the person
4
+ who has to look after a seat: what is running, how to look at it, how to stop
5
+ and restart it, and how to read `maestro session status` and `maestro doctor`.
6
+ The design is `docs/superpowers/specs/2026-09-08-front-door-session-design.md`;
7
+ this is the operator's view of §3 of it.
8
+
9
+ ## 1. What runs on a seat
10
+
11
+ ```
12
+ launchd (gui/<uid>)
13
+ ├── ai.maestro.<first>-daemon KeepAlive scripts/daemon/maestro-daemon.mjs ← plumbing
14
+ │ the one SSE stream to os.cohortapp.com · presence beat every 30 s ·
15
+ │ Slack/Telegram/WhatsApp/orgmail adapters · classify + hydrate inbound
16
+ │ → state/inbox/cohort/*.yaml · cadence bus
17
+ └── ai.maestro.<first>-session KeepAlive scripts/session/supervisor.sh ← brain
18
+ screen -D -m -S maestro-<first> claude --name <first>-main --resume <id> …
19
+ └── a persistent monitor inside the session: scripts/session/feed.mjs
20
+ heartbeat every 15 s · tails the inbox + handoffs
21
+ → one JSON line per event → the session acts
22
+ ```
23
+
24
+ **The daemon** is the plumbing and is unchanged in what it holds: the single
25
+ allowed SSE connection per seat with its durable cursor, the presence beat,
26
+ the channel adapters, the directedness/self-echo/dedupe pipeline. It never
27
+ reasons.
28
+
29
+ **The session** is the agent's front door. It is a normal interactive Claude
30
+ Code session (`claude --name <first>-main`) kept alive inside a terminal
31
+ multiplexer (`screen` ships with macOS; `tmux` is preferred when installed).
32
+ Every inbound Cohort event — DMs, space messages, threads, inbound email,
33
+ comments on files and boards, huddle calls — reaches it as a JSON line from
34
+ the feed, and it either answers in the same turn, runs a dynamic workflow, or
35
+ hands the heavy work to a peer session it spawns. Meaty asks go onto the
36
+ relevant Board; the session works its board items proactively when idle.
37
+
38
+ **Who receives inbound.** While the session's heartbeat
39
+ (`state/session/heartbeat.json`, written every 15 s) is fresher than 90 s, the
40
+ daemon leaves Cohort inbox items in place for the session to claim and writes
41
+ escalate/guarded cadence ticks as handoffs (`state/session/handoffs/`). When the
42
+ heartbeat is stale, the daemon falls back to today's `claude --print` dispatch
43
+ for both. Liveness is measured, never assumed, so nothing is dropped in either
44
+ direction. The beat reports which lane is active: `machine.frontDoor`
45
+ (`"session"` when the job exists, `"daemon"` otherwise) and
46
+ `machine.sessionLive`. They ride `machine` — the beat field hq stores as an
47
+ open record — rather than `session`, which hq validates strictly and which
48
+ stays `null` while no work session runs.
49
+
50
+ **Exactly one.** The supervisor takes an O_EXCL lock (`lib/singleton.js`,
51
+ name `session`); a second supervisor exits 0 so launchd does not thrash. An
52
+ orphaned mux session with no lock holder is adopted, not duplicated.
53
+
54
+ **Continuity.** The session id is stable (`state/session/main-session.json`);
55
+ every relaunch is `claude --resume <id>`, so the session keeps its memory
56
+ across restarts and reboots. If a resume fails within 20 s the supervisor
57
+ rotates to a fresh id (at most three times an hour, then it sleeps).
58
+
59
+ **Relaunch only on death.** The supervisor runs the multiplexer in the
60
+ foreground and exits 75 when the session ends; launchd's
61
+ `KeepAlive {SuccessfulExit:false}` + `ThrottleInterval 30` bring it back.
62
+ There is no cron, no periodic kill, no memory watchdog SIGTERM for the main
63
+ session (`memory-watchdog.sh` spares `<first>-main` and the feed).
64
+
65
+ **Auth.** The supervisor sources `.env` the same way `launchd-wrapper.sh`
66
+ does, so the seat's `CLAUDE_CODE_OAUTH_TOKEN` (from `claude setup-token`)
67
+ reaches `claude`. The keychain login is not relied on.
68
+
69
+ ## 2. Install and first start
70
+
71
+ ```bash
72
+ cd ~/<agent-name>
73
+ maestro session install # renders + bootstraps ai.maestro.<first>-session
74
+ maestro session status # expect: lock held · mux maestro-<first> · heartbeat N s ago (live)
75
+ ```
76
+
77
+ `init-agent.sh` and `generate-plists.sh` render the same plist; `install`
78
+ is the idempotent front door for it. It needs the SDK installed **globally**
79
+ (`npm i -g @cohortapp/agent-sdk`) because the session, like any Claude Code
80
+ session on the seat, resolves `maestro` and `cohort-mcp` from PATH — `maestro
81
+ doctor` checks both.
82
+
83
+ ## 3. How to attach
84
+
85
+ ```bash
86
+ maestro session attach # opens the live session in this terminal
87
+ ```
88
+
89
+ Detach with `Ctrl-A D` (screen) or `Ctrl-B D` (tmux). Detaching does not stop
90
+ anything. You are looking at the agent's own working session: type into it
91
+ the way you would into any Claude Code session, but remember that whatever it
92
+ sends outbound goes through the send-gate and the persona audit like any
93
+ other turn. Over SSH this is the same command; Tailscale SSH
94
+ (`scripts/setup/configure-macos.sh --tailscale-ssh`) is how an operator
95
+ reaches a seat.
96
+
97
+ ## 4. How to stop, start and restart
98
+
99
+ | You want | Run | What happens |
100
+ | --- | --- | --- |
101
+ | Restart on the current code (or after an upgrade) | `maestro session restart` | writes `state/session/restart-requested`; the session finishes its turn, exits the mux, the supervisor exits 75, launchd relaunches it with `--resume` |
102
+ | Stop it and keep it stopped | `maestro session stop` | `launchctl bootout` of the session job; the daemon's `--print` lane takes over inbound within 90 s |
103
+ | Start it again | `maestro session start` | `launchctl bootstrap` of the job |
104
+ | Stop the whole seat | `touch .emergency-stop` | daemon exits clean and stays down; stop the session separately with `session stop` |
105
+
106
+ Never `launchctl kickstart -k` the session job from a script or a cadence:
107
+ it is long-lived interactive state. The hourly autoupdate job knows this — it
108
+ restarts the daemon and health-gates it, but for the session it only writes
109
+ `state/session/upgrade-notice.json`; the session restarts itself at an idle
110
+ moment via `maestro session restart` and comes back on the new code.
111
+
112
+ One consequence of `maestro upgrade` reconciling launchd (§4a): every
113
+ generated job that is installed but **not loaded** is bootstrapped again on
114
+ the next upgrade, the session job included. `maestro session stop` therefore
115
+ holds until the next hour's autoupdate lands a new version; to keep a seat's
116
+ session down for longer, use the autoupdate kill-switch
117
+ (`touch ~/.maestro-no-autoupdate`) or remove the session plist. The
118
+ kill-switch and the emergency stop (`.emergency-stop`, `state/EMERGENCY_STOP`)
119
+ also **hold** the launchd step of a by-hand `maestro upgrade`: plists are
120
+ still written, nothing is bootstrapped, and each held row says which marker
121
+ held it. `scripts/session/supervisor.sh` refuses to start under an emergency
122
+ stop as well (exit 0, so launchd does not relaunch it), which covers the one
123
+ path the hold cannot — the next login re-bootstrapping `~/Library/LaunchAgents`.
124
+
125
+ ## 4a. How a seat upgrades itself
126
+
127
+ The hourly `ai.maestro.<first>-autoupdate` job
128
+ (`scripts/local-triggers/autoupdate.sh`) installs a strictly-newer SDK into
129
+ the agent dir and runs **that package's** `maestro upgrade`. So on the first
130
+ hop from any older version it is the *new* `upgrade` that runs, and it puts
131
+ the seat on the new architecture by itself — the "seat reconcile" steps at
132
+ the end of every non-dry upgrade, each fail-open and each reported in the
133
+ summary and in `.maestro/upgrade-result.json` `{from, to, at, steps}`:
134
+
135
+ | Step | What it does |
136
+ | --- | --- |
137
+ | `plists` | `scripts/local-triggers/generate-plists.sh`, unconditionally |
138
+ | `launchd` | installs every generated plist missing from `~/Library/LaunchAgents` (mode 600, `launchctl bootstrap gui/<uid>`; `launchctl load` only when launchd does not already hold the label) — this is how the session job reaches an existing seat; **rewrites** changed ones (file only — launchd keeps the loaded definition until the next login or an explicit `bootout` + `bootstrap`; nothing is ever `kickstart`ed, least of all the `-autoupdate` job this upgrade runs inside); bootstraps installed-but-unloaded ones; never `bootout`s anything, orphans are only reported. "Loaded" is measured in the GUI domain (`launchctl print gui/<uid>`), so the answer is right over ssh too. Held entirely (files written, nothing started) under an emergency stop or the autoupdate kill-switch |
139
+ | `globalSetup` | `maestro global-setup` — identity block, cohort MCP server, session settings, global skills |
140
+ | `globalInstall` | `npm i -g @cohortapp/agent-sdk@<this version>` so `maestro` / `cohort` / `cohort-mcp` on PATH match the agent dir (`MAESTRO_SKIP_GLOBAL_INSTALL=1` skips) |
141
+ | `verify` | the `maestro upgrade --verify` report, below |
142
+
143
+ autoupdate then kickstarts the daemon and health-gates it: the daemon
144
+ process must be alive with a fresh `org-mesh connected` / `[daemon] Running`
145
+ line, and — when a `-session` plist was generated — the session label must be
146
+ in `launchctl list` with a live supervisor process. A missing session is
147
+ logged as `reconcile-failed` and does **not** roll back (the daemon's
148
+ `--print` lane is the front door meanwhile; doctor says what to do). Every
149
+ launchd and `pgrep` question is scoped to *this* agent (`ai.maestro.<first>-*`
150
+ and its own agent dir), so two seats on one Mac never gate on — or restart —
151
+ each other.
152
+
153
+ An unhealthy daemon **rolls back**: the previous SDK is reinstalled into the
154
+ agent dir and *that* package's `upgrade` runs, the global install is put back
155
+ to the previous version, and a `-session` job that **this run** installed is
156
+ booted out and its plist removed (a pre-existing session job is the
157
+ operator's and is never touched). What a rollback does *not* undo: files the
158
+ new version added that the old manifest never listed stay on disk, and the
159
+ old `upgrade` rewrites the manifest without `sdkVersion` — `maestro doctor` /
160
+ `upgrade --verify` report both on the next healthy hop. A version that failed
161
+ the health gate here is then **held**: `state/autoupdate/last.json`
162
+ (`{from, to, at, ok, healthy, reason}`) is read at the top of every run and a
163
+ `to` it records as `unhealthy-rolled-back` / `rollback-unhealthy` is not
164
+ retried for 24 h (`MAESTRO_AUTOUPDATE_FAILED_HOLD_S`; delete the file to retry
165
+ now) — without that memory a bad release would install, reconcile, roll back
166
+ and refresh the global install twice an hour until someone touched the
167
+ kill-switch. The beat reports `last.json` as `machine.upgrade` next to
168
+ `sdkVersion` in the Fleet view, so a held seat shows its failure and its age.
169
+ Two runs cannot overlap (`state/locks/autoupdate/`, stale after two hours),
170
+ and `npm view` / `npm install` are retried three times with backoff.
171
+
172
+ `maestro upgrade --verify` (also printed by `maestro doctor`) answers "is
173
+ this seat wholly on one version?": the global bin, the agent dir's package,
174
+ `.maestro/shipped-manifest.json`, the running daemon
175
+ (`state/dashboards/daemon-health.yaml` `sdk_version` — the version of the
176
+ *copied* `scripts/daemon` the process started on, read from the manifest
177
+ before `node_modules`, so a daemon restarted between autoupdate's `npm
178
+ install` and the file merge does not report the version it is not yet
179
+ running), every generated plist installed **and** loaded (in the GUI domain),
180
+ and the global-setup markers. It exits 1 on any mismatch and names the fix
181
+ per row.
182
+
183
+ ## 5. How to read status
184
+
185
+ `maestro session status` prints:
186
+
187
+ - **lock** — who holds `state/session.lock` (pid, identity) or "free";
188
+ - **mux** — `maestro-<first>` present or not, which multiplexer;
189
+ - **session id** and how many times it has been resumed;
190
+ - **heartbeat** — age in seconds and `live` / `stale` against the 90 s bar;
191
+ - **open handoffs** — cadence ticks waiting in `state/session/handoffs/`;
192
+ - **peers** — sessions it has spawned (`maestro session peers` lists them
193
+ and prunes dead ones).
194
+
195
+ `--brief` gives the one-liner the SessionStart hook shows every new session.
196
+
197
+ `maestro doctor` adds the fleet view of the same facts and a remedy per line:
198
+ the Claude auth mode and what `claude auth status` reports (the CLI reports
199
+ that a credential is present — it does not validate a token; a bad token
200
+ shows up on the first real spawn, in `logs/sessions` and on the beat as
201
+ `relogin_required`); where each Cohort value
202
+ came from (`config/org.yaml` beats the environment beats `.env`) and which
203
+ values lost; whether `COHORT_ORG_ID` is the org's ID or its slug; the global
204
+ SDK install and whether it is current; Tailscale SSH; and the session job —
205
+ installed / loaded / heartbeat live. The rungs read, in order:
206
+
207
+ ```
208
+ ✓ Claude auth: subscription OAuth token (CLAUDE_CODE_OAUTH_TOKEN + MAESTRO_PREFER_SUBSCRIPTION_AUTH=1)
209
+ ✓ Claude auth (subscription — OAuth token): CLI reports a login for the token; the token itself is only proven by a real spawn (a rejection shows in logs/sessions and on the beat as relogin_required)
210
+ ✓ Cohort base ← org.yaml (https://os.cohortapp.com)
211
+ ✓ Cohort orgId ← org.yaml (org_default_adaptic)
212
+ ⚠ Cohort orgId: org.yaml wins over .env COHORT_ORG_ID=adaptic — the losing value is ignored
213
+ ✓ COHORT_ORG_ID is the org ID (org_default_adaptic) — matches the server
214
+ ✓ @cohortapp/agent-sdk 2.12.0 installed globally (npm latest 2.12.0)
215
+ ✓ Tailscale SSH is on (RunSSH=true)
216
+ ✓ Main session job ai.maestro.ethan-session loaded, heartbeat live (12 s ago)
217
+ ```
218
+
219
+ From the Fleet view in Cohort the same seat shows `hostname`, `tailnetIp`,
220
+ `sdkVersion`, `frontDoor` and `sessionLive` off its beat — `sdkVersion` per
221
+ seat is how a fleet-wide rollout is confirmed after a publish.
222
+
223
+ ## 6. What the session does with an event
224
+
225
+ Each line from the feed is one unit of work. The policy the session follows
226
+ (skill `inbound-triage`):
227
+
228
+ 1. **Answer in the turn** when the ask is answerable in one reply from what
229
+ the agent already knows. `maestro inbox reply <id>` routes through the same
230
+ send-gate and audit as every other outbound path.
231
+ 2. Otherwise **acknowledge in-channel in the same turn**, file the ask on the
232
+ conversation's board (`maestro board track <inbox-id> --stage accepted
233
+ --title … --why …`), and either run the work as a dynamic workflow inside
234
+ the session or spawn a peer (`maestro session spawn --name <slug> "<prompt>"`).
235
+ Report back in-channel and `board track --stage done` on completion.
236
+ 3. **Handoffs** (`{"type":"handoff", …}`) are cadence ticks the daemon has
237
+ handed over; the session runs the rendered prompt and `maestro session ack
238
+ <tickId>`. A handoff not acked within 30 minutes is re-enqueued by the
239
+ daemon on the legacy lane, so a wedged session cannot starve a cadence.
240
+ 4. A claimed inbox item neither replied nor done within 20 minutes is
241
+ re-opened by the daemon's assurance sweep and re-emitted (or dispatched
242
+ legacy if the session is no longer live).
243
+
244
+ To the humans it talks to, the agent is one persona. Peer sessions and
245
+ sub-agents are "my team" / "a colleague"; the persona audit hook blocks any
246
+ outbound text that says otherwise.
247
+
248
+ ## 6a. DESIGN.md and the parallelism directive
249
+
250
+ Two things reach every prompt a seat runs, and neither is left to a session to
251
+ remember.
252
+
253
+ **DESIGN.md and PRODUCT.md.** `maestro design sync` reads the workspace's SAVED
254
+ brand foundation (`branding.getFoundation` — never a draft) and renders it into
255
+ `DESIGN.md` (colour, type, radius, spacing and component tokens, in the
256
+ google-labs-code/design.md frontmatter shape, with `{colors.primary}`-style
257
+ refs) and `PRODUCT.md` (positioning, voice, tone, lexicon) in the agent dir,
258
+ plus the raw snapshot at `state/design/foundation.json`. The daemon re-runs the
259
+ same code path every 15 minutes and rewrites the files when the foundation's
260
+ version has moved, so a seat's design files follow the humans editing the
261
+ foundation without anyone re-running a command.
262
+
263
+ Both files are GENERATED. An edit to them is overwritten on the next sync;
264
+ foundation changes are human-gated and go through the `design_propose_change`
265
+ tool. The write is byte-idempotent — an unchanged foundation touches no mtime,
266
+ so the poll never churns the tree, and the render stamp is kept per output
267
+ directory so a `--out` run and the daemon's poll of the agent dir cannot
268
+ invalidate each other's files. Every posture here is fail-open: an un-enrolled
269
+ seat writes nothing, and a failed read keeps the last-good files rather than
270
+ blanking them. Design skills are told to read `$AGENT_ROOT/DESIGN.md` when the
271
+ working directory is not the agent dir.
272
+
273
+ A workspace that has never SAVED a foundation writes nothing at all, and the
274
+ CLI says why. hq answers such a read with its own product defaults and
275
+ `unversioned: true` — a complete, plausible palette that is nobody's brand — and
276
+ a DESIGN.md rendered from it would ground every design skill on the seat in the
277
+ wrong palette with a generated file's authority behind it.
278
+
279
+ maestro design sync # into the agent dir
280
+ maestro design sync --out docs/brand --json
281
+
282
+ **The parallelism directive.** `lib/prompts/parallelism.mjs` holds one constant,
283
+ prepended exactly once (it is idempotent, and the seams compose) to every
284
+ peer-session prompt by `maestro session spawn`, to every sub-session prompt
285
+ built by the daemon's prompt-builder, and to every cadence trigger prompt on the
286
+ escalate/guarded lane — both when a sub-session is spawned for it and when a
287
+ live main session is handed it (`renderCadencePromptBody`, one seam for both, so
288
+ the two lanes send the same bytes). It grants the standing permission to
289
+ dispatch independent tasks as one parallel batch, and carries the safety rule
290
+ that makes that safe on a shared checkout: check the planned file scope first,
291
+ and never assign two agents to edit the same file — sequence or re-scope
292
+ instead. Sessions ran plans strictly serially before it existed, because
293
+ nothing in the prompt had ever said they need not.
294
+
295
+ ## 7. Files
296
+
297
+ | Path | What |
298
+ | --- | --- |
299
+ | `state/session/heartbeat.json` | `{pid, ppid, sessionId, name, ts, feedVersion}`, every 15 s |
300
+ | `state/session/main-session.json` | `{sessionId, createdAt, resumes}` — the stable id |
301
+ | `state/session/handoffs/<tickId>.json` | cadence ticks waiting for the session; `done/` when acked |
302
+ | `state/session/peers.json` | spawned peer sessions |
303
+ | `state/session/feed-seen.json` | the feed's seen-set (no replay after a monitor restart) |
304
+ | `state/session/restart-requested` | `maestro session restart` marker |
305
+ | `state/session/upgrade-notice.json` | written by autoupdate; the session restarts itself when idle |
306
+ | `state/session.lock` | the singleton lock |
307
+ | `state/inbox/cohort/*.yaml` | inbound items (`.dispatched` = claimed, `.processed` = done, `.deferred`) |
308
+ | `DESIGN.md`, `PRODUCT.md` | generated from the workspace brand foundation by `maestro design sync` and the daemon's 15-minute poll; edits are overwritten |
309
+ | `state/design/foundation.json` | `{fetchedAt, renderedAt, version, versionLabel, foundation}` — the last synced foundation; the refresh gate reads it back |
310
+ | `state/org/board-mine.json` | `{ts, items}` — the daemon's 5-minute cache of hq `board.mine` (this agent's items across every board); read by the SessionStart primer, the `session_status` MCP tool and `maestro board mine` |
311
+ | `.maestro/upgrade-result.json` | `{from, to, at, steps:{plists, launchd, globalSetup, globalInstall, verify}}` — what the last `maestro upgrade` did to the seat |
312
+ | `state/autoupdate/last.json` | `{from, to, at, ok, healthy, reason}` — the last autoupdate attempt; the beat's `machine.upgrade` |
313
+ | `state/locks/autoupdate/` | autoupdate's overlap lock (pid inside; stale after 2 h) |
@@ -1,10 +1,15 @@
1
1
  # Mac mini bring-up — an always-on Cohort agent, end to end
2
2
 
3
3
  The one-page operator runbook: from a boxed Apple-silicon Mac mini to a fully
4
- enrolled AI colleague on `os.cohortapp.com` — messaging, board work, and a real
5
- workspace email address — verified by `cohort doctor`. OS-level hardening and
6
- fleet mechanics live in the deeper runbook: [Mac Mini Bootstrap](../runbooks/mac-mini-bootstrap.md)
7
- (this page links it rather than duplicating it).
4
+ enrolled AI colleague on `os.cohortapp.com` — messaging, board work, a real
5
+ workspace email address and an always-on **main session** as its front door —
6
+ verified by `cohort doctor`. OS-level hardening and fleet mechanics live in the
7
+ deeper runbook: [Mac Mini Bootstrap](../runbooks/mac-mini-bootstrap.md); the
8
+ session itself is described in [Front-door Session](front-door-session.md)
9
+ (this page links both rather than duplicating them).
10
+
11
+ The sequence, in one line: **token → create → setup → init-agent →
12
+ session install → tailscale-ssh → doctor.**
8
13
 
9
14
  ## 1. Hardware / prereqs
10
15
 
@@ -22,13 +27,27 @@ sudo pmset -a sleep 0 displaysleep 0 autorestart 1
22
27
  xcode-select --install
23
28
  /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
24
29
  brew install node@20 git python@3.12
25
- npm i -g @anthropic-ai/claude-code && claude login # Max subscription — the daemon
26
- # strips API keys so sub-sessions
27
- # ride keychain OAuth
28
- npm i -g @cohortapp/agent-sdk
30
+ npm i -g @anthropic-ai/claude-code
31
+ npm i -g @cohortapp/agent-sdk # GLOBAL on purpose: every Claude Code session on
32
+ # this seat needs `maestro` + `cohort-mcp` on PATH
29
33
  ```
30
34
 
31
- ## 3. Create the agent repo
35
+ ## 3. The Claude token — one auth story for the whole seat
36
+
37
+ Every `claude` the seat runs (the daemon's `--print` lane, the main session,
38
+ cadence sub-sessions) authenticates with a **long-lived subscription OAuth
39
+ token**, never the interactive keychain login (which expires headlessly and
40
+ has stranded seats before). Mint it on **any** machine where you are logged
41
+ in to Claude Code with the Max subscription — your laptop is fine:
42
+
43
+ ```bash
44
+ claude setup-token # prints one token; copy it
45
+ ```
46
+
47
+ Keep it in your clipboard / password manager for step 5. It is per seat: mint
48
+ a fresh one whenever `cohort doctor` reports the token as rejected.
49
+
50
+ ## 4. Create the agent repo
32
51
 
33
52
  ```bash
34
53
  cohort create <agent-name>
@@ -40,7 +59,7 @@ including `.mcp.json`, which exposes the `cohort-mcp` org tool surface to
40
59
  interactive Claude sessions, and the PreToolUse hooks that keep its outbound
41
60
  writes on the CLI lane).
42
61
 
43
- ## 4. Enroll against `https://os.cohortapp.com`
62
+ ## 5. Enroll against `https://os.cohortapp.com` — and paste the token
44
63
 
45
64
  Two lanes — pick one:
46
65
 
@@ -49,9 +68,14 @@ Two lanes — pick one:
49
68
 
50
69
  ```bash
51
70
  export COHORT_API_KEY=nlk_… COHORT_ORG_ID=<org-ID> COHORT_AGENT_ID=<member-slug>
52
- cohort setup
71
+ cohort setup # the model section (order 30) asks for the token from step 3
72
+ # headless / one-paste bootstrap: the token comes from the environment instead of a prompt
73
+ CLAUDE_CODE_OAUTH_TOKEN=<token> cohort setup --headless
53
74
  ```
54
75
 
76
+ Either way `.env` ends up with `CLAUDE_CODE_OAUTH_TOKEN` +
77
+ `MAESTRO_PREFER_SUBSCRIPTION_AUTH=1`, mode 600. No `ANTHROPIC_API_KEY`.
78
+
55
79
  > **`COHORT_ORG_ID` is the org's ID, not its slug** — this line said
56
80
  > `<org-slug>` and that would 401 every call. The value rides as `x-org-id`
57
81
  > (`lib/org/client.mjs#baseHeaders`) and hq compares it with strict equality
@@ -68,12 +92,13 @@ Two lanes — pick one:
68
92
  - **Pre-auth pairing**: `cohort pair <code>` → an org admin approves the
69
93
  handshake in Cohort (`pairing.request`/`pairing.approve`).
70
94
 
71
- ## 5. Wizard
95
+ ## 6. Wizard
72
96
 
73
- `cohort setup` walks the sections in order — the org trio is:
97
+ `cohort setup` walks the sections in order — the auth section and the org trio are:
74
98
 
75
99
  | Order | Section | What it does |
76
100
  | --- | --- | --- |
101
+ | 30 | `model` | Claude auth — **`oauth-token` by default**: paste the `claude setup-token` output; writes `CLAUDE_CODE_OAUTH_TOKEN` + `MAESTRO_PREFER_SUBSCRIPTION_AUTH=1`, chmods `.env` 600 |
77
102
  | 75 | `org` | endpoint + token (the enrollment SoT) |
78
103
  | 76 | `messaging` | messaging.read/write + calling.write scopes, home channels |
79
104
  | 77 | `orgmail` | **workspace mailbox** — writes `config/orgmail.yaml` |
@@ -82,28 +107,65 @@ For 77 to go live, an admin must have verified a domain and assigned this
82
107
  agent a mailbox in Cohort → Settings → Email first (the wizard's verify step
83
108
  probes `email.inbox` and WARNS until then — it never blocks setup).
84
109
 
85
- ## 6. Always-on (launchd)
110
+ ## 7. Always-on (launchd): the daemon, then the main session
86
111
 
87
112
  ```bash
88
- scripts/setup/init-agent.sh # deps → state dirs → generate-plists.sh → launchctl load
113
+ scripts/setup/init-agent.sh # deps → state dirs → generate-plists.sh → launchctl bootstrap
114
+ maestro session install # the front-door main session job (KeepAlive)
115
+ maestro session status # lock holder, mux name, heartbeat age — expect "live"
89
116
  launchctl list | grep ai.maestro
90
117
  ```
91
118
 
92
- Expect the daemon plus the cadence triggers; the daemon plist is KeepAlive
93
- `{SuccessfulExit:false, Crashed:true}` so it restarts on crash but respects a
94
- clean stop.
119
+ Expect the daemon, the **session**, the cadence triggers and the hourly
120
+ autoupdate job. The daemon plist is KeepAlive `{SuccessfulExit:false,
121
+ Crashed:true}`; the session plist is KeepAlive `{SuccessfulExit:false}` with a
122
+ 30 s throttle — it relaunches only on death, never on a schedule. Once the
123
+ session's heartbeat is live the daemon stops spawning `--print` sessions for
124
+ Cohort inbox items and hands them to the session instead; if the heartbeat
125
+ goes stale (> 90 s) the daemon falls back to its own lane, so nothing is ever
126
+ dropped. `maestro session attach` opens the live session in your terminal
127
+ (`Ctrl-A D` / `Ctrl-B D` detaches); see [Front-door Session](front-door-session.md).
128
+
129
+ ## 8. Remote operator access — Tailscale SSH
130
+
131
+ ```bash
132
+ scripts/setup/configure-macos.sh --tailscale-ssh # tailscale set --ssh + Remote Login on
133
+ ```
134
+
135
+ This is what makes every later step remote (`ssh <agent-user>@<tailnet-host>`
136
+ from an operator machine, no `authorized_keys` to manage). Allow it in the
137
+ tailnet ACL once for the fleet; `cohort doctor` warns while it is off.
95
138
 
96
- ## 7. Verify — `cohort doctor` is the gate
139
+ ## 9. Verify — `cohort doctor` is the gate
97
140
 
98
141
  ```bash
99
142
  cohort doctor
100
143
  ```
101
144
 
102
- All green includes the **Cohort connectivity** block: enrollment resolved →
103
- directory reachable (latency) → protocol version aligned (the server's
104
- `x-org-protocol` header; a drift warning means run `cohort upgrade`) → **key
105
- paired to a workforce member** (the `messaging.channels` gate) → **workspace
106
- mailbox reachable** (when `config/orgmail.yaml` exists).
145
+ All green includes, in this order:
146
+
147
+ - **Claude auth** — the mode in effect (`subscription OAuth token` is the
148
+ expected line) and what `claude auth status` reports. The CLI reports that
149
+ a credential is PRESENT — it answers "logged in" for any token value — so a
150
+ bad token is only caught by the first real spawn (`logs/sessions`, and the
151
+ beat flips to `relogin_required`); a `claude -p "ping"` after doctor is the
152
+ cheap way to prove it. "keychain login"
153
+ or "nothing configured" means step 3/5 was skipped.
154
+ - **Cohort sources** — for each of `base` / `orgId` / `token` / `agentId`,
155
+ which file won (`config/org.yaml` beats the environment beats `.env`) and
156
+ every value that lost. If you edited `.env` and nothing changed, this line
157
+ is why.
158
+ - **`COHORT_ORG_ID`** checked against the server: "you set the slug" names the
159
+ real ID to use.
160
+ - **Global SDK install** — `@cohortapp/agent-sdk` present globally and current
161
+ against npm; `maestro` / `cohort-mcp` resolve on PATH.
162
+ - **Tailscale SSH** on (step 8).
163
+ - **Main session job** installed, loaded, heartbeat live (step 7).
164
+ - The **Cohort connectivity** block: enrollment resolved → directory reachable
165
+ (latency) → protocol version aligned (the server's `x-org-protocol` header;
166
+ a drift warning means run `cohort upgrade`) → **key paired to a workforce
167
+ member** (the `messaging.channels` gate) → **workspace mailbox reachable**
168
+ (when `config/orgmail.yaml` exists).
107
169
 
108
170
  Then two live probes:
109
171
 
@@ -115,15 +177,25 @@ Then two live probes:
115
177
  tick ≤60s). First-contact recipients park as approvals in /decisions — by
116
178
  design.
117
179
 
118
- ## 8. Operations
180
+ ## 10. Operations
119
181
 
120
182
  - **Kill switch**: touch `.emergency-stop` in the repo root (human-only). The
121
183
  daemon exits clean, so launchd stops the restart treadmill. Resume with
122
- `scripts/resume-operations.sh`.
184
+ `scripts/resume-operations.sh`. The main session is stopped separately with
185
+ `maestro session stop` (and comes back with `maestro session start`).
123
186
  - **Email-only kill switch**: delete/rename `config/orgmail.yaml` and restart —
124
187
  the gate-file loop skips the platform; server-side an admin can flip the
125
188
  mailbox to Disabled in Settings → Email.
126
- - **Upgrade**: `cohort upgrade` (framework files), `npm i -g @cohortapp/agent-sdk@latest`
127
- (the SDK itself). A doctor "protocol drift" warning is the upgrade cue.
189
+ - **Upgrade**: automatic — the hourly `ai.maestro.<first>-autoupdate` job installs
190
+ a newer SDK, runs `cohort upgrade` (which also regenerates and installs the
191
+ launchd jobs, re-runs `global-setup` and refreshes the global npm install —
192
+ `docs/guides/front-door-session.md` §4a), restarts the daemon and
193
+ health-gates it. It does **not** restart the main session; it writes
194
+ `state/session/upgrade-notice.json` and the session restarts itself at an
195
+ idle moment. By hand: `npm i -g @cohortapp/agent-sdk@latest && cohort upgrade`,
196
+ then `maestro session restart`; `maestro upgrade --verify` confirms the seat
197
+ is wholly on one version. Verified fleet-wide by `sdkVersion` and
198
+ `machine.upgrade` on each seat's beat (the Fleet view). A doctor "protocol
199
+ drift" warning is the cue.
128
200
  - **Health**: `scripts/healthcheck.sh`; logs under `logs/` (`logs/audit/` carries
129
201
  the per-action JSONL rows from BOTH the native executor and the MCP plane).
@@ -71,7 +71,7 @@ context from Cohort and shape the agent's config from it.
71
71
  | Var | Required | Purpose |
72
72
  | --- | --- | --- |
73
73
  | `COHORT_API_KEY` | yes | Bearer OrgApiKey for the org-data DTO reads AND the `/v1` read/RPC surface. hq's documented agent var. (`COHORT_API_TOKEN` / `COHORT_TOKEN` are accepted for back-compat.) |
74
- | `COHORT_ORG_ID` | yes | Org id / slug — the tenant. Sent as `?slug=` on org-data reads and as the `x-org-id` pin on `/v1`. |
74
+ | `COHORT_ORG_ID` | yes | The org **ID** (`org_default_<slug>`), never the bare slug — hq compares the `x-org-id` pin with strict equality against the ID, so a slug 401s every `/v1` call (`maestro setup` warns, `maestro doctor` checks it against the server). Also sent as `?slug=` on legacy org-data reads. |
75
75
  | `COHORT_AGENT_ID` | optional | This agent's member slug. When set it selects `members/<slug>` + keys relationships; when absent the pull resolves via `whoami`. |
76
76
  | `COHORT_BASE` / `COHORT_API_URL` | optional | Server origin. Falls back to the persisted `config/org.yaml` endpoint. |
77
77
  | `COHORT_AGENT_EMAIL` | optional | Email hint for `whoami` resolution when no `COHORT_AGENT_ID` is given. |
@@ -52,7 +52,7 @@ core runner.
52
52
  |---------|-------|-------------|
53
53
  | `identity` | 10 | Agent name and title, the **function × altitude** archetype, and the principal. Writes `config/agent.json`. |
54
54
  | `company` | 20 | Company-context interview — name, website, industry, one-line + detailed overview, stage/size, footprint, regulation, top priorities, and key people. Writes `config/company.json`. |
55
- | `model` | 30 | Model/provider choice: a Claude Code subscription (keychain OAuth, no API key) or a pasted Anthropic API key. Writes the model lines of `.env`. |
55
+ | `model` | 30 | Claude auth. Three modes, **`oauth-token` the default**: run `claude setup-token` on any logged-in machine and paste the result (headless: `CLAUDE_CODE_OAUTH_TOKEN` in the environment) → `.env` gets `CLAUDE_CODE_OAUTH_TOKEN` + `MAESTRO_PREFER_SUBSCRIPTION_AUTH=1`; or this machine's keychain login; or a pasted Anthropic API key. Chmods `.env` 600. |
56
56
  | `comms` | 45 | Wires each messaging channel — Slack, Gmail, SMS, WhatsApp, Telegram, voice — into `.env`/gate files and verifies inbound. |
57
57
  | `tools` | 50 | Selects which channels and MCP servers the agent should run. |
58
58
  | `operating-model` | 60 | Deterministically generates the operating charter, a seeded WBS backlog (≥5 open items), 40–60 sub-agents + skills + workflows + MCP servers + event-routing, archetype cadences, the autonomy policy, the communication profile, and the launchd plists. |
@@ -102,14 +102,17 @@ re-running `maestro setup` (or `maestro setup enrich`) without the flag.
102
102
  ## 5. The verify probe and capability table
103
103
 
104
104
  The final `verify` section runs a live check and prints an `N/M` capability table.
105
- It aggregates every other section's `verify()` plus a Claude API probe and the
106
- completeness gate. Per enabled channel/MCP it runs the right liveness check
105
+ It aggregates every other section's `verify()` plus the Claude auth probe
106
+ (`claude auth status` in subscription mode — which reports that a credential
107
+ is present, not that it is valid; an API ping in api-key mode, which does
108
+ prove the key — the same probe `maestro doctor` and the fleet beat use) and
109
+ the completeness gate. Per enabled channel/MCP it runs the right liveness check
107
110
  (`auth.test` for Slack, an IMAP login for Gmail, a Twilio lookup, a relay
108
111
  `/health`, etc.), and prints a remedy line under any failure:
109
112
 
110
113
  ```
111
114
  Capability summary — 7/9 checks passing
112
- ✓ Claude API reachable
115
+ ✓ Claude auth (subscription — OAuth token)
113
116
  ✓ identity complete
114
117
  ✓ Slack auth.test
115
118
  ✗ Gmail IMAP login [comms]
@@ -118,7 +121,8 @@ Capability summary — 7/9 checks passing
118
121
  ```
119
122
 
120
123
  Setup exits **non-zero only on hard failures** — no identity, an unresolvable
121
- archetype, or the Claude API being unreachable. Optional channels and best-effort
124
+ archetype, or Claude auth being rejected (a probe that cannot run — no CLI, no
125
+ network — is reported as "could not verify" and does not fail the run). Optional channels and best-effort
122
126
  enrichment are reported but never fail the run. The same completeness gate backs
123
127
  `maestro doctor`, so `doctor` and `setup --status` agree on what "done" means.
124
128
 
@@ -55,7 +55,17 @@ machine's `config/org.yaml` pins `server.url` (e.g.
55
55
  service changes the `*.up.railway.app` host and strands the fleet. Move the
56
56
  fleet to a stable custom domain first (§2), then rename freely.
57
57
 
58
- > ⚠︎ **2026-08-16 — this warning appears to have come true, and it is unresolved.**
58
+ > ✅ **RESOLVED 2026-09-08.** The working host is **`https://os.cohortapp.com`** —
59
+ > the org plane (`/api/v1/*`, the `/v1` RPC binding, `presence.beat`, the
60
+ > agent SSE stream) is served by the product app `cohort-app` on the
61
+ > `cohort-os` Railway project, not by a separate org-server service. There is
62
+ > nothing to repoint at `neolith-production`. `DEPLOYMENT.md` §5.1 now templates
63
+ > `os.cohortapp.com`; `.env.example` defaults `COHORT_BASE` to it; `maestro
64
+ > doctor` prints the effective base per seat (org.yaml vs .env vs env) so a
65
+ > straggler pinning the dead host is one `doctor` run away from being found.
66
+ > The paragraph below is kept as the record of what was observed.
67
+ >
68
+ > ⚠︎ **2026-08-16 — this warning appears to have come true (resolved above).**
59
69
  > `https://neolith.up.railway.app` is now UNROUTED: Railway's edge returns
60
70
  > `x-railway-fallback: true` with a 404 on `/`, `/v1` and `/v1/ops`, meaning no
61
71
  > service is attached to that hostname. Contrast the app, whose rename DID