@cohortapp/agent-sdk 2.17.0 → 2.18.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.
Files changed (526) hide show
  1. package/.claude/settings.json +18 -0
  2. package/.env.example +18 -5
  3. package/README.md +1 -0
  4. package/bin/maestro.mjs +62 -0
  5. package/docs/guides/billing-console-keys.md +60 -0
  6. package/docs/guides/front-door-session.md +54 -9
  7. package/docs/guides/mac-mini.md +20 -25
  8. package/docs/guides/setup-wizard.md +1 -1
  9. package/docs/runbooks/fleet-rollout.md +156 -0
  10. package/docs/runbooks/mac-mini-bootstrap.md +12 -14
  11. package/lib/action-executor.js +19 -3
  12. package/lib/budget-guard.mjs +279 -3
  13. package/lib/channels/base-adapter.mjs +3 -1
  14. package/lib/channels/contract.mjs +2 -1
  15. package/lib/channels/inbox-item.mjs +8 -0
  16. package/lib/claude-bin.mjs +5 -6
  17. package/lib/cli/doctor-checks.mjs +141 -10
  18. package/lib/cli/global-setup-extras.mjs +5 -1
  19. package/lib/cli/inbox.mjs +100 -15
  20. package/lib/cli/seat-auth.mjs +463 -0
  21. package/lib/cli/session.mjs +80 -12
  22. package/lib/collective/capture.mjs +8 -6
  23. package/lib/collective/global-config.mjs +63 -1
  24. package/lib/collective/presence.mjs +142 -5
  25. package/lib/comms/send-gate.mjs +559 -1
  26. package/lib/diagnostics/alerts.mjs +49 -0
  27. package/lib/diagnostics/cadence-output-freshness.mjs +288 -0
  28. package/lib/engine/agents/definitions.mjs +343 -0
  29. package/lib/engine/agents/persist.mjs +275 -0
  30. package/lib/engine/agents/runtime.mjs +748 -0
  31. package/lib/engine/agents/usage.mjs +95 -0
  32. package/lib/engine/auth-status.mjs +139 -0
  33. package/lib/engine/budget.mjs +194 -0
  34. package/lib/engine/cli.mjs +1204 -0
  35. package/lib/engine/commands/index.mjs +269 -0
  36. package/lib/engine/context/budget.mjs +219 -0
  37. package/lib/engine/context/cache.mjs +125 -0
  38. package/lib/engine/context/child-env.mjs +215 -0
  39. package/lib/engine/context/compaction.mjs +342 -0
  40. package/lib/engine/context/images.mjs +90 -0
  41. package/lib/engine/context/instructions.mjs +327 -0
  42. package/lib/engine/context/lazy-instructions.mjs +169 -0
  43. package/lib/engine/context/manager.mjs +182 -0
  44. package/lib/engine/context/real-path.mjs +91 -0
  45. package/lib/engine/context/secret-values.mjs +163 -0
  46. package/lib/engine/context/settings.mjs +274 -0
  47. package/lib/engine/context/stream-input.mjs +159 -0
  48. package/lib/engine/guard.mjs +152 -0
  49. package/lib/engine/hooks.mjs +713 -0
  50. package/lib/engine/loop.mjs +560 -0
  51. package/lib/engine/mcp/client.mjs +254 -0
  52. package/lib/engine/mcp/config.mjs +301 -0
  53. package/lib/engine/mcp/http.mjs +201 -0
  54. package/lib/engine/mcp/index.mjs +146 -0
  55. package/lib/engine/mcp/jsonrpc.mjs +147 -0
  56. package/lib/engine/mcp/naming.mjs +66 -0
  57. package/lib/engine/mcp/resources.mjs +89 -0
  58. package/lib/engine/mcp/results.mjs +133 -0
  59. package/lib/engine/mcp/stdio.mjs +137 -0
  60. package/lib/engine/mcp/supervisor.mjs +116 -0
  61. package/lib/engine/messages.mjs +104 -0
  62. package/lib/engine/output/json.mjs +164 -0
  63. package/lib/engine/output/stream-json.mjs +266 -0
  64. package/lib/engine/permissions.mjs +845 -0
  65. package/lib/engine/process-identity.mjs +164 -0
  66. package/lib/engine/process-tree.mjs +551 -0
  67. package/lib/engine/prompt.mjs +60 -0
  68. package/lib/engine/session/store.mjs +299 -0
  69. package/lib/engine/session-runtime/args.mjs +97 -0
  70. package/lib/engine/session-runtime/host.mjs +143 -0
  71. package/lib/engine/session-runtime/inbox.mjs +122 -0
  72. package/lib/engine/session-runtime/notifications.mjs +129 -0
  73. package/lib/engine/session-runtime/registry.mjs +328 -0
  74. package/lib/engine/session-runtime/runner.mjs +344 -0
  75. package/lib/engine/session-runtime/socket.mjs +212 -0
  76. package/lib/engine/session-runtime/wakeup.mjs +115 -0
  77. package/lib/engine/skills/index.mjs +321 -0
  78. package/lib/engine/tools/bash-background.mjs +533 -0
  79. package/lib/engine/tools/bash.mjs +216 -0
  80. package/lib/engine/tools/edit.mjs +97 -0
  81. package/lib/engine/tools/glob.mjs +81 -0
  82. package/lib/engine/tools/grep.mjs +224 -0
  83. package/lib/engine/tools/index.mjs +84 -0
  84. package/lib/engine/tools/list-agents.mjs +32 -0
  85. package/lib/engine/tools/ls.mjs +127 -0
  86. package/lib/engine/tools/monitor.mjs +82 -0
  87. package/lib/engine/tools/notebook-edit.mjs +218 -0
  88. package/lib/engine/tools/read.mjs +103 -0
  89. package/lib/engine/tools/schedule-wakeup.mjs +45 -0
  90. package/lib/engine/tools/schema.mjs +144 -0
  91. package/lib/engine/tools/send-message.mjs +77 -0
  92. package/lib/engine/tools/session.mjs +70 -0
  93. package/lib/engine/tools/todo.mjs +144 -0
  94. package/lib/engine/tools/toolsearch.mjs +217 -0
  95. package/lib/engine/tools/walk.mjs +193 -0
  96. package/lib/engine/tools/web-switch.mjs +31 -0
  97. package/lib/engine/tools/webfetch-html.mjs +387 -0
  98. package/lib/engine/tools/webfetch-net.mjs +340 -0
  99. package/lib/engine/tools/webfetch.mjs +198 -0
  100. package/lib/engine/tools/websearch.mjs +91 -0
  101. package/lib/engine/tools/workflow.mjs +95 -0
  102. package/lib/engine/tools/write.mjs +76 -0
  103. package/lib/engine/tui/line-editor.mjs +137 -0
  104. package/lib/engine/tui/render.mjs +86 -0
  105. package/lib/engine/tui/tui.mjs +274 -0
  106. package/lib/engine/wire/anthropic-messages.mjs +263 -0
  107. package/lib/engine/wire/effort.mjs +36 -0
  108. package/lib/engine/wire/errors.mjs +496 -0
  109. package/lib/engine/wire/http.mjs +441 -0
  110. package/lib/engine/wire/index.mjs +76 -0
  111. package/lib/engine/wire/openai-chat.mjs +332 -0
  112. package/lib/engine/wire/prompt-cache.mjs +79 -0
  113. package/lib/engine/wire/search.mjs +140 -0
  114. package/lib/engine/wire/sse.mjs +114 -0
  115. package/lib/engine/wire/stall.mjs +349 -0
  116. package/lib/engine/wire/token-provider.mjs +175 -0
  117. package/lib/engine/wire/usage.mjs +192 -0
  118. package/lib/engine/workflow/host.mjs +524 -0
  119. package/lib/engine/workflow/journal.mjs +188 -0
  120. package/lib/engine/workflow/json-schema.mjs +171 -0
  121. package/lib/engine/workflow/meta.mjs +329 -0
  122. package/lib/engine/workflow/notifications.mjs +52 -0
  123. package/lib/engine/workflow/runtime.mjs +447 -0
  124. package/lib/engine/workflow/sandbox.mjs +534 -0
  125. package/lib/engine/workflow/worker.mjs +141 -0
  126. package/lib/engine/workflow/worktree.mjs +74 -0
  127. package/lib/execution/disposition.mjs +1 -1
  128. package/lib/execution/intake.mjs +10 -0
  129. package/lib/execution/surface-policy.mjs +15 -0
  130. package/lib/learning/curator.mjs +8 -6
  131. package/lib/learning/reflect.mjs +8 -6
  132. package/lib/model-router/catalog/cohort.yaml +137 -0
  133. package/lib/model-router/catalog.mjs +118 -1
  134. package/lib/model-router/failover.mjs +67 -16
  135. package/lib/model-router/llm-task.mjs +39 -3
  136. package/lib/model-router/resolve.mjs +89 -3
  137. package/lib/model-router/spawn.mjs +46 -47
  138. package/lib/model-router/taxonomy.mjs +126 -4
  139. package/lib/org/cost-sync.mjs +141 -11
  140. package/lib/org/inbound/broadcast.mjs +289 -0
  141. package/lib/org/inbound/collective.mjs +375 -0
  142. package/lib/org/inbound/directedness.mjs +96 -8
  143. package/lib/org/inbound/facts.mjs +78 -2
  144. package/lib/org/inbound/project.mjs +22 -0
  145. package/lib/org/inbound/surfaces.mjs +14 -0
  146. package/lib/org/llm-token.mjs +879 -0
  147. package/lib/org/mesh.mjs +61 -0
  148. package/lib/org/messaging.mjs +3 -1
  149. package/lib/org/protocol.checksum +1 -1
  150. package/lib/org/protocol.mjs +15 -0
  151. package/lib/org/quota.mjs +520 -0
  152. package/lib/org/tool-surface.mjs +104 -16
  153. package/lib/org/ui-parity.mjs +16 -1
  154. package/lib/org/work-ledger.mjs +37 -6
  155. package/lib/rate-guard.mjs +114 -1
  156. package/lib/resource-governor.mjs +41 -6
  157. package/lib/runtime/adapter.mjs +823 -0
  158. package/lib/runtime/child-env.mjs +191 -0
  159. package/lib/runtime/legacy-shell-guard.mjs +97 -0
  160. package/lib/runtime/seat-engine.mjs +162 -0
  161. package/lib/session/ask-ledger.mjs +271 -0
  162. package/lib/session/current-work.mjs +676 -0
  163. package/lib/session/feed-core.mjs +40 -3
  164. package/lib/session/launch-args.mjs +56 -4
  165. package/lib/session/status-summary.mjs +26 -9
  166. package/lib/session/upgrade-notice.mjs +42 -0
  167. package/lib/setup/claude-probe.mjs +117 -13
  168. package/lib/setup/enrich.mjs +13 -10
  169. package/lib/setup/sections/model.mjs +39 -13
  170. package/lib/telemetry/collect.mjs +208 -9
  171. package/lib/upgrade/ignored-drift.mjs +105 -0
  172. package/lib/voice/post-call-brief.mjs +30 -17
  173. package/package.json +13 -3
  174. package/plugins/maestro-skills/skills/board-work.md +5 -0
  175. package/plugins/maestro-skills/skills/inbound-triage.md +56 -15
  176. package/plugins/maestro-skills/skills/main-session.md +18 -7
  177. package/scripts/ci/check-tarball-fidelity.mjs +126 -2
  178. package/scripts/ci/run-tests.mjs +47 -19
  179. package/scripts/cohort-llm/api-key-helper.mjs +92 -0
  180. package/scripts/collective/hook-runner.mjs +29 -2
  181. package/scripts/continuous-monitor.sh +13 -0
  182. package/scripts/cost/track-claude-usage.mjs +15 -0
  183. package/scripts/daemon/agent-daemon.mjs +408 -20
  184. package/scripts/daemon/assurance.mjs +48 -12
  185. package/scripts/daemon/cadence-consumer.mjs +218 -68
  186. package/scripts/daemon/cadence-handlers.mjs +73 -4
  187. package/scripts/daemon/classifier.mjs +75 -26
  188. package/scripts/daemon/context-compiler.mjs +51 -37
  189. package/scripts/daemon/deliver.mjs +30 -1
  190. package/scripts/daemon/dispatcher.mjs +595 -149
  191. package/scripts/daemon/health.mjs +14 -1
  192. package/scripts/daemon/maestro-daemon.mjs +11 -0
  193. package/scripts/daemon/prompt-builder.mjs +24 -0
  194. package/scripts/daemon/responder.mjs +246 -79
  195. package/scripts/daemon/sdk-version.mjs +98 -16
  196. package/scripts/eval/probe-gateway.mjs +635 -0
  197. package/scripts/eval/replay/extract.mjs +270 -0
  198. package/scripts/eval/replay/grade.mjs +260 -0
  199. package/scripts/eval/replay/lib/config.mjs +50 -0
  200. package/scripts/eval/replay/lib/effects.mjs +65 -0
  201. package/scripts/eval/replay/lib/fixture.mjs +188 -0
  202. package/scripts/eval/replay/lib/judge.mjs +72 -0
  203. package/scripts/eval/replay/lib/redact.mjs +136 -0
  204. package/scripts/eval/replay/lib/sandbox.mjs +170 -0
  205. package/scripts/eval/replay/lib/schema-check.mjs +63 -0
  206. package/scripts/eval/replay/lib/transcript.mjs +76 -0
  207. package/scripts/eval/replay/mcp-replay-stub.mjs +101 -0
  208. package/scripts/eval/replay/report.mjs +185 -0
  209. package/scripts/eval/replay/run.mjs +404 -0
  210. package/scripts/fleet/rollout.mjs +1094 -0
  211. package/scripts/hooks/pre-send-audit.sh +36 -245
  212. package/scripts/hooks/pre-write-yaml-validate.mjs +275 -0
  213. package/scripts/hooks/validate-state-yaml.sh +190 -0
  214. package/scripts/huddle/huddle-llm.mjs +361 -0
  215. package/scripts/huddle/huddle-server.mjs +46 -121
  216. package/scripts/local-triggers/autoupdate.sh +448 -78
  217. package/scripts/local-triggers/run-trigger.sh +13 -0
  218. package/scripts/maintenance/pin-integrity.mjs +364 -0
  219. package/scripts/poll-slack-events.sh +41 -9
  220. package/scripts/poller/slack-socket-mode.mjs +28 -3
  221. package/scripts/session/supervisor.mjs +80 -13
  222. package/scripts/spawn-session.sh +13 -0
  223. package/bin/maestro.test.mjs +0 -1574
  224. package/lib/action-executor.test.mjs +0 -871
  225. package/lib/archetype.test.mjs +0 -132
  226. package/lib/assurance/plan-note.test.mjs +0 -234
  227. package/lib/assurance/room-budget.test.mjs +0 -486
  228. package/lib/assurance/tier.test.mjs +0 -174
  229. package/lib/autonomy.test.mjs +0 -66
  230. package/lib/backlog.test.mjs +0 -302
  231. package/lib/backup/policy.test.mjs +0 -305
  232. package/lib/budget-escalate.test.mjs +0 -232
  233. package/lib/budget-guard.envelope.test.mjs +0 -476
  234. package/lib/budget-guard.test.mjs +0 -427
  235. package/lib/cadence-bus-requeue.test.mjs +0 -83
  236. package/lib/cadence-bus-schedule.test.mjs +0 -194
  237. package/lib/cadence-bus.test.mjs +0 -720
  238. package/lib/cadences.test.mjs +0 -230
  239. package/lib/capability/inventory.test.mjs +0 -232
  240. package/lib/capability.test.mjs +0 -78
  241. package/lib/channels/base-adapter.test.mjs +0 -590
  242. package/lib/channels/channels.test.mjs +0 -371
  243. package/lib/channels/contract.test.mjs +0 -162
  244. package/lib/channels/inbox-item.test.mjs +0 -368
  245. package/lib/channels/orgmail/adapter.test.mjs +0 -448
  246. package/lib/channels/pairing.test.mjs +0 -270
  247. package/lib/channels/repeat-suppressor.test.mjs +0 -134
  248. package/lib/channels/slack-adapter.test.mjs +0 -212
  249. package/lib/channels/telegram-adapter.test.mjs +0 -306
  250. package/lib/channels/voice/adapter.test.mjs +0 -278
  251. package/lib/channels/whatsapp/adapter-baileys.test.mjs +0 -359
  252. package/lib/channels/whatsapp/baileys-typing.test.mjs +0 -154
  253. package/lib/charter.test.mjs +0 -89
  254. package/lib/claude-bin.test.mjs +0 -131
  255. package/lib/cli/board.test.mjs +0 -227
  256. package/lib/cli/design.test.mjs +0 -270
  257. package/lib/cli/doctor-checks.test.mjs +0 -336
  258. package/lib/cli/global-setup-extras.test.mjs +0 -462
  259. package/lib/cli/inbox.test.mjs +0 -230
  260. package/lib/cli/session-ack.test.mjs +0 -63
  261. package/lib/cli/session.test.mjs +0 -613
  262. package/lib/collective/capture.test.mjs +0 -121
  263. package/lib/collective/cards.test.mjs +0 -114
  264. package/lib/collective/config.test.mjs +0 -123
  265. package/lib/collective/global-config.test.mjs +0 -220
  266. package/lib/collective/global-skills.test.mjs +0 -126
  267. package/lib/collective/presence.test.mjs +0 -95
  268. package/lib/collective/recall.test.mjs +0 -116
  269. package/lib/collective/vendor-skills.test.mjs +0 -306
  270. package/lib/comms/send-gate.test.mjs +0 -770
  271. package/lib/comms.test.mjs +0 -41
  272. package/lib/context/budget.test.mjs +0 -252
  273. package/lib/context/history-scope.test.mjs +0 -79
  274. package/lib/cost/ledger-row.test.mjs +0 -183
  275. package/lib/design/design-md.test.mjs +0 -318
  276. package/lib/design/fixtures/DESIGN.golden.md +0 -238
  277. package/lib/design/fixtures/PRODUCT.golden.md +0 -67
  278. package/lib/design/fixtures/foundation.json +0 -133
  279. package/lib/design/refresh-gate.test.mjs +0 -144
  280. package/lib/design/write.test.mjs +0 -241
  281. package/lib/diagnostics/alerts.test.mjs +0 -318
  282. package/lib/diagnostics/backup-freshness.test.mjs +0 -185
  283. package/lib/diagnostics/counters.test.mjs +0 -206
  284. package/lib/diagnostics/events.test.mjs +0 -290
  285. package/lib/diagnostics/otel.test.mjs +0 -196
  286. package/lib/diagnostics/trace.test.mjs +0 -251
  287. package/lib/env-compat.test.mjs +0 -104
  288. package/lib/execution/disposition.test.mjs +0 -553
  289. package/lib/execution/drive.test.mjs +0 -270
  290. package/lib/execution/effects.test.mjs +0 -344
  291. package/lib/execution/intake.test.mjs +0 -389
  292. package/lib/execution/journal.test.mjs +0 -261
  293. package/lib/execution/match.test.mjs +0 -235
  294. package/lib/execution/pipeline.test.mjs +0 -392
  295. package/lib/execution/route.test.mjs +0 -186
  296. package/lib/execution/surface-policy.test.mjs +0 -162
  297. package/lib/fs-atomic.test.mjs +0 -72
  298. package/lib/fs-ownership.test.mjs +0 -158
  299. package/lib/goals/admission.test.mjs +0 -164
  300. package/lib/goals/classify.test.mjs +0 -167
  301. package/lib/goals/collaborate.test.mjs +0 -336
  302. package/lib/goals/gaps.test.mjs +0 -284
  303. package/lib/goals/loop.test.mjs +0 -845
  304. package/lib/hooks/bus.test.mjs +0 -387
  305. package/lib/identity/persona.test.mjs +0 -142
  306. package/lib/kpi-sensors.test.mjs +0 -278
  307. package/lib/kpi.test.mjs +0 -244
  308. package/lib/learning/config.test.mjs +0 -75
  309. package/lib/learning/counters.test.mjs +0 -69
  310. package/lib/learning/curator-consolidate.test.mjs +0 -238
  311. package/lib/learning/curator.test.mjs +0 -106
  312. package/lib/learning/reflect.test.mjs +0 -0
  313. package/lib/learning/session-index.test.mjs +0 -125
  314. package/lib/learning/skill-writer.test.mjs +0 -210
  315. package/lib/mandate/audit.test.mjs +0 -195
  316. package/lib/mandate/contract.test.mjs +0 -185
  317. package/lib/mandate/derive.test.mjs +0 -274
  318. package/lib/mandate/model.test.mjs +0 -164
  319. package/lib/mandate/refresh.test.mjs +0 -389
  320. package/lib/mcp/server.test.mjs +0 -426
  321. package/lib/model-router/auth-profiles.test.mjs +0 -580
  322. package/lib/model-router/catalog.test.mjs +0 -385
  323. package/lib/model-router/economics.test.mjs +0 -438
  324. package/lib/model-router/failover.test.mjs +0 -439
  325. package/lib/model-router/health.test.mjs +0 -338
  326. package/lib/model-router/integration-coverage.test.mjs +0 -831
  327. package/lib/model-router/integration.test.mjs +0 -564
  328. package/lib/model-router/ledger.test.mjs +0 -415
  329. package/lib/model-router/llm-task.test.mjs +0 -392
  330. package/lib/model-router/org-credentials.test.mjs +0 -265
  331. package/lib/model-router/pricing-refresh.test.mjs +0 -286
  332. package/lib/model-router/reconcile.test.mjs +0 -316
  333. package/lib/model-router/repair.test.mjs +0 -180
  334. package/lib/model-router/spawn.test.mjs +0 -446
  335. package/lib/model-router/taxonomy.test.mjs +0 -410
  336. package/lib/model-router.test.mjs +0 -1207
  337. package/lib/org/activity.test.mjs +0 -134
  338. package/lib/org/approvals.test.mjs +0 -216
  339. package/lib/org/awareness.test.mjs +0 -159
  340. package/lib/org/board-mine-cache.test.mjs +0 -53
  341. package/lib/org/board.test.mjs +0 -187
  342. package/lib/org/bootstrap-context.test.mjs +0 -153
  343. package/lib/org/client.test.mjs +0 -1206
  344. package/lib/org/cohort-client.test.mjs +0 -126
  345. package/lib/org/cost-sync.test.mjs +0 -153
  346. package/lib/org/doctor.test.mjs +0 -346
  347. package/lib/org/engagement-ledger.test.mjs +0 -112
  348. package/lib/org/engagement.test.mjs +0 -739
  349. package/lib/org/handoff.test.mjs +0 -269
  350. package/lib/org/inbound/directedness.test.mjs +0 -668
  351. package/lib/org/inbound/facts.test.mjs +0 -471
  352. package/lib/org/inbound/hydrate.test.mjs +0 -908
  353. package/lib/org/inbound/index.test.mjs +0 -429
  354. package/lib/org/inbound/project.test.mjs +0 -287
  355. package/lib/org/integration-tools.test.mjs +0 -160
  356. package/lib/org/keys.test.mjs +0 -92
  357. package/lib/org/knowledge.test.mjs +0 -326
  358. package/lib/org/leases.test.mjs +0 -235
  359. package/lib/org/mesh-directives.test.mjs +0 -110
  360. package/lib/org/mesh-integration.test.mjs +0 -127
  361. package/lib/org/mesh.test.mjs +0 -400
  362. package/lib/org/messaging.test.mjs +0 -471
  363. package/lib/org/param-contract.test.mjs +0 -477
  364. package/lib/org/policy.test.mjs +0 -237
  365. package/lib/org/protocol.checksum.test.mjs +0 -90
  366. package/lib/org/protocol.test.mjs +0 -323
  367. package/lib/org/push.test.mjs +0 -792
  368. package/lib/org/registry.test.mjs +0 -100
  369. package/lib/org/resource-tools.test.mjs +0 -361
  370. package/lib/org/tool-access.test.mjs +0 -144
  371. package/lib/org/tool-surface-integration.test.mjs +0 -120
  372. package/lib/org/tool-surface.test.mjs +0 -1268
  373. package/lib/org/typing.test.mjs +0 -291
  374. package/lib/org/ui-parity.test.mjs +0 -560
  375. package/lib/org/verify.test.mjs +0 -194
  376. package/lib/org/work-ledger.test.mjs +0 -273
  377. package/lib/plan/adoption-e2e.test.mjs +0 -366
  378. package/lib/plan/budget-enforcement.test.mjs +0 -400
  379. package/lib/plan/compile.test.mjs +0 -382
  380. package/lib/plan/emit.test.mjs +0 -269
  381. package/lib/plan/explain.test.mjs +0 -188
  382. package/lib/prompts/parallelism.test.mjs +0 -177
  383. package/lib/rag/rag.test.mjs +0 -505
  384. package/lib/rate-guard.test.mjs +0 -272
  385. package/lib/reactive-gate.test.mjs +0 -57
  386. package/lib/render.test.mjs +0 -68
  387. package/lib/resource-governor.test.mjs +0 -488
  388. package/lib/scheduling/dynamic-jobs.test.mjs +0 -344
  389. package/lib/scheduling/jitter.test.mjs +0 -140
  390. package/lib/secrets/broker.test.mjs +0 -280
  391. package/lib/secrets/providers.test.mjs +0 -274
  392. package/lib/security/audit-engine.test.mjs +0 -424
  393. package/lib/security/coerce-args.test.mjs +0 -281
  394. package/lib/security/dangerous-tools.test.mjs +0 -68
  395. package/lib/security/external-content.test.mjs +0 -84
  396. package/lib/security/redact.test.mjs +0 -441
  397. package/lib/security/secret-equal.test.mjs +0 -55
  398. package/lib/session/config.test.mjs +0 -92
  399. package/lib/session/feed-core.test.mjs +0 -198
  400. package/lib/session/first-run.test.mjs +0 -121
  401. package/lib/session/frontdoor.test.mjs +0 -205
  402. package/lib/session/handoffs.test.mjs +0 -183
  403. package/lib/session/identity.test.mjs +0 -180
  404. package/lib/session/inbox-claims.test.mjs +0 -286
  405. package/lib/session/launch-args.test.mjs +0 -157
  406. package/lib/session/liveness.test.mjs +0 -100
  407. package/lib/session/status-summary.test.mjs +0 -118
  408. package/lib/session-permissions.test.mjs +0 -120
  409. package/lib/setup/claude-probe.test.mjs +0 -187
  410. package/lib/setup/completeness.test.mjs +0 -110
  411. package/lib/setup/context-pack.test.mjs +0 -89
  412. package/lib/setup/enrich.test.mjs +0 -115
  413. package/lib/setup/enroll-from-cohort.test.mjs +0 -300
  414. package/lib/setup/integration.test.mjs +0 -162
  415. package/lib/setup/io.test.mjs +0 -77
  416. package/lib/setup/runner.test.mjs +0 -132
  417. package/lib/setup/sections/identity.test.mjs +0 -234
  418. package/lib/setup/sections/inventory.test.mjs +0 -198
  419. package/lib/setup/sections/learning.test.mjs +0 -81
  420. package/lib/setup/sections/mandate.test.mjs +0 -388
  421. package/lib/setup/sections/messaging.test.mjs +0 -127
  422. package/lib/setup/sections/model.test.mjs +0 -240
  423. package/lib/setup/sections/org.test.mjs +0 -346
  424. package/lib/setup/sections/orgmail.test.mjs +0 -118
  425. package/lib/setup/sections/recovery.test.mjs +0 -98
  426. package/lib/setup/sections/subagents.test.mjs +0 -429
  427. package/lib/setup/sections/verify.test.mjs +0 -175
  428. package/lib/setup/sot.test.mjs +0 -81
  429. package/lib/setup/state.test.mjs +0 -115
  430. package/lib/singleton.test.mjs +0 -151
  431. package/lib/subagents/cli.test.mjs +0 -389
  432. package/lib/subagents/client.test.mjs +0 -309
  433. package/lib/subagents/gap.test.mjs +0 -234
  434. package/lib/subagents/lock.test.mjs +0 -248
  435. package/lib/subagents/manifest.test.mjs +0 -175
  436. package/lib/subagents/refs.test.mjs +0 -204
  437. package/lib/subagents/resolve.test.mjs +0 -422
  438. package/lib/subagents/schema.test.mjs +0 -328
  439. package/lib/telemetry/alerts.test.mjs +0 -109
  440. package/lib/telemetry/collect.test.mjs +0 -1274
  441. package/lib/tool-definitions-integration.test.mjs +0 -83
  442. package/lib/tool-definitions.test.mjs +0 -437
  443. package/lib/upgrade/global-refresh.test.mjs +0 -65
  444. package/lib/upgrade/launchd-reconcile.test.mjs +0 -272
  445. package/lib/upgrade/post-steps.test.mjs +0 -200
  446. package/lib/upgrade/verify.test.mjs +0 -164
  447. package/lib/util/fetch-timeout.test.mjs +0 -202
  448. package/lib/util/reconnect.test.mjs +0 -369
  449. package/lib/util/unhandled.test.mjs +0 -216
  450. package/lib/voice/outbound.test.mjs +0 -69
  451. package/lib/voice/session-rotation.test.mjs +0 -114
  452. package/lib/voice/stt.test.mjs +0 -226
  453. package/lib/voice/voice.test.mjs +0 -990
  454. package/scripts/cadence/enqueue-cadence-tick.test.mjs +0 -187
  455. package/scripts/ci/check-docs-accuracy.test.mjs +0 -409
  456. package/scripts/ci/check-durable-write-seam.test.mjs +0 -90
  457. package/scripts/ci/check-no-build-artifacts.test.mjs +0 -71
  458. package/scripts/ci/check-no-residual-identity.test.mjs +0 -202
  459. package/scripts/ci/check-skill-packs.test.mjs +0 -495
  460. package/scripts/ci/check-subagent-frontmatter.test.mjs +0 -124
  461. package/scripts/ci/check.test.mjs +0 -194
  462. package/scripts/ci/conformance-org-api.test.mjs +0 -425
  463. package/scripts/cloud-relay/voice/relay-identity.test.mjs +0 -96
  464. package/scripts/collective/hook-runner.test.mjs +0 -173
  465. package/scripts/cost/fleet-digest.test.mjs +0 -207
  466. package/scripts/cost/track-claude-usage-pricing.test.mjs +0 -183
  467. package/scripts/cost/track-claude-usage.test.mjs +0 -148
  468. package/scripts/daemon/agent-daemon-board-mine.test.mjs +0 -96
  469. package/scripts/daemon/agent-daemon-design.test.mjs +0 -238
  470. package/scripts/daemon/agent-daemon-frontdoor.test.mjs +0 -60
  471. package/scripts/daemon/agent-daemon.test.mjs +0 -995
  472. package/scripts/daemon/assurance-e2e.test.mjs +0 -613
  473. package/scripts/daemon/assurance.test.mjs +0 -1791
  474. package/scripts/daemon/board-mirror.test.mjs +0 -165
  475. package/scripts/daemon/cadence-consumer-frontdoor.test.mjs +0 -393
  476. package/scripts/daemon/cadence-consumer-governance.test.mjs +0 -276
  477. package/scripts/daemon/cadence-consumer.test.mjs +0 -776
  478. package/scripts/daemon/cadence-handlers.test.mjs +0 -837
  479. package/scripts/daemon/classifier-identity.test.mjs +0 -137
  480. package/scripts/daemon/classifier.test.mjs +0 -266
  481. package/scripts/daemon/classify-kind.test.mjs +0 -40
  482. package/scripts/daemon/context-compiler.test.mjs +0 -406
  483. package/scripts/daemon/deliver.test.mjs +0 -564
  484. package/scripts/daemon/dispatcher-cooldown.test.mjs +0 -122
  485. package/scripts/daemon/dispatcher-governance.test.mjs +0 -1013
  486. package/scripts/daemon/dispatcher-resume.test.mjs +0 -166
  487. package/scripts/daemon/dispatcher-session-continuity.test.mjs +0 -365
  488. package/scripts/daemon/execution-ladder.test.mjs +0 -470
  489. package/scripts/daemon/goal-steward-cadence.test.mjs +0 -312
  490. package/scripts/daemon/inbox-deferral-session.test.mjs +0 -49
  491. package/scripts/daemon/inbox-deferral.test.mjs +0 -336
  492. package/scripts/daemon/inbox-wake.test.mjs +0 -199
  493. package/scripts/daemon/integration.test.mjs +0 -149
  494. package/scripts/daemon/lib/self-echo.test.mjs +0 -153
  495. package/scripts/daemon/lib/session-router.test.mjs +0 -554
  496. package/scripts/daemon/prompt-builder-preamble.test.mjs +0 -210
  497. package/scripts/daemon/prompt-builder.test.mjs +0 -556
  498. package/scripts/daemon/responder-cost.test.mjs +0 -68
  499. package/scripts/daemon/responder-history.test.mjs +0 -221
  500. package/scripts/daemon/sdk-version.test.mjs +0 -31
  501. package/scripts/daemon/session-lock.test.mjs +0 -252
  502. package/scripts/daemon/session-outcomes.test.mjs +0 -533
  503. package/scripts/daemon/typing-registry.test.mjs +0 -102
  504. package/scripts/hooks/pre-send-audit.test.mjs +0 -354
  505. package/scripts/huddle/huddle-prompt.test.mjs +0 -176
  506. package/scripts/local-triggers/autoupdate.test.mjs +0 -518
  507. package/scripts/local-triggers/generate-plists.test.mjs +0 -456
  508. package/scripts/media-generation/brand-clause.test.mjs +0 -135
  509. package/scripts/org/send-orgmail.first-contact.test.mjs +0 -102
  510. package/scripts/poller/inbox-privilege-injection.test.mjs +0 -167
  511. package/scripts/poller/inbox-scan-poller.test.mjs +0 -295
  512. package/scripts/poller/lib/cloud-relay-dedup.test.mjs +0 -133
  513. package/scripts/poller/slack-socket-mode.test.mjs +0 -805
  514. package/scripts/poller-launchd/install.test.mjs +0 -243
  515. package/scripts/restore-from-backup.test.mjs +0 -181
  516. package/scripts/session/feed.test.mjs +0 -196
  517. package/scripts/session/supervisor-sh.test.mjs +0 -218
  518. package/scripts/session/supervisor.test.mjs +0 -482
  519. package/scripts/setup/configure-macos.test.mjs +0 -306
  520. package/scripts/setup/gen-subagent-manifest.test.mjs +0 -124
  521. package/scripts/setup/generate-agent-package-json.test.mjs +0 -143
  522. package/scripts/setup/generate-capability.test.mjs +0 -134
  523. package/scripts/setup/init-agent.test.mjs +0 -370
  524. package/scripts/setup/init-skill-marketplace.test.mjs +0 -193
  525. package/scripts/vendor/sync-skill-packs.test.mjs +0 -103
  526. package/scripts/watchdog/memory-watchdog.test.mjs +0 -64
@@ -85,6 +85,19 @@
85
85
  * // to sdkVersion without a schema change. `ok` is the attempt's verdict:
86
86
  * // true = healthy on `to`; false = install failed or rolled back to `from`.
87
87
  * upgrade?: { at: ISO8601, from: string, to: string, ok: boolean },
88
+ * // THE BEATING DAEMON — which process is emitting this beat, what code
89
+ * // it is actually running, and the last health-gate verdict on it.
90
+ * // Also on `machine` (open record). `sdkVersion` ABOVE is what is
91
+ * // INSTALLED on the seat; `daemon.sdkVersion` is what the running
92
+ * // process booted on, and the two differ for exactly as long as an
93
+ * // installed upgrade has not been restarted onto. `pid`+`bootAt` make a
94
+ * // crash loop visible org-side (a pid that moves every beat), which is
95
+ * // the failure that hid for six days behind an hourly "up to date".
96
+ * // `dashboardAt` is the stamp of the file these came from, so a reader
97
+ * // can age the claim; it is never a liveness signal on its own.
98
+ * daemon?: { pid?: number, bootAt?: ISO8601, uptimeS?: number,
99
+ * sdkVersion?: string, dashboardAt?: ISO8601,
100
+ * healthy?: boolean, healthReason?: string },
88
101
  * },
89
102
  * claudeAuth: "ok"|"relogin_required"|"unknown",
90
103
  * alerts: [{ id, severity, kind, detail }],
@@ -120,6 +133,7 @@ import { fileURLToPath } from "node:url";
120
133
  import { createRequire } from "node:module";
121
134
  import { writeJsonAtomic } from "../fs-atomic.mjs";
122
135
  import { probeClaude as defaultProbeClaude } from "../setup/claude-probe.mjs";
136
+ import { readSeatEngine } from "../runtime/adapter.mjs";
123
137
 
124
138
  import { list as listPresence } from "../collective/presence.mjs";
125
139
  import { billableUsd } from "../cost/ledger-row.mjs";
@@ -433,18 +447,64 @@ export async function collectMachine(o = {}) {
433
447
  if (Number.isFinite(disk)) machine.diskUsedPct = pct(disk);
434
448
  }
435
449
 
450
+ // IDENTITY (§3.8) — hostname / tailnetIp / sdkVersion: where the seat is and
451
+ // what it runs. Collected FIRST and on its own terms, no longer as a rider on
452
+ // the inventory probe.
453
+ //
454
+ // WHY IT MOVED. It used to be read out of `collectInventory`'s return, inside
455
+ // `if (o.inventory !== false)` and with no try of its own. Three ways that
456
+ // lost the one field a fleet rollout is verified by, none of them about
457
+ // identity: a caller that turned inventory off lost it as a side effect; a
458
+ // `system_profiler` fork that threw took the whole `collectMachine` down with
459
+ // it (`collectStatus` then substitutes a bare `{loadavg, memUsedPct}` floor —
460
+ // the beat still goes, carrying neither the host stats nor the version); and
461
+ // a seat in that state stayed in it, beat after beat, with nothing on the box
462
+ // or in the org saying why. Identity is three cheap, independent reads and it
463
+ // is what an operator is looking for; it should not be able to fail because a
464
+ // hardware probe did. Each field is independently fail-open inside
465
+ // `collectIdentity`, and the try here is the outer guard for the probe seam.
466
+ //
467
+ // The results are handed on to `collectInventory` below so the tailnet probe
468
+ // forks once per beat, not twice.
469
+ let identity = {};
470
+ if (o.identity !== false) {
471
+ try { identity = await collectIdentity(o, osImpl); } catch { identity = {}; }
472
+ if (identity.hostname) machine.hostname = identity.hostname;
473
+ if (identity.tailnetIp) machine.tailnetIp = identity.tailnetIp;
474
+ if (identity.sdkVersion) machine.sdkVersion = identity.sdkVersion;
475
+ }
476
+
436
477
  // INVENTORY — device / ram / cores. Cached on disk (see collectInventory):
437
478
  // the chip and the installed RAM do not change between beats, so the
438
479
  // system_profiler fork happens once per machine, not every 30 seconds.
480
+ //
481
+ // WRAPPED, and the try is not decorative. `collectInventory` is not total:
482
+ // `execFileSafe` swallows a failing FORK, but the reads around it do not all
483
+ // have a try of their own (the platform read at the top of the probe block is
484
+ // the live example), so the function can and does throw. Before identity was
485
+ // hoisted above this, that throw propagated out of `collectMachine` and
486
+ // `collectStatus` substituted its bare `{loadavg, memUsedPct}` floor — the
487
+ // beat went out carrying neither host stats NOR the version, which is how two
488
+ // seats beat for days with no hostname and no sdkVersion. Now it costs its
489
+ // own three fields and nothing else.
439
490
  if (o.inventory !== false) {
440
- const inv = await collectInventory(o);
441
- if (inv.device) machine.device = inv.device;
442
- if (inv.ram) machine.ram = inv.ram;
443
- if (Number.isFinite(inv.cores)) machine.cores = inv.cores;
444
- // IDENTITY (§3.8) — where the seat is and what it runs.
445
- if (inv.hostname) machine.hostname = inv.hostname;
446
- if (inv.tailnetIp) machine.tailnetIp = inv.tailnetIp;
447
- if (inv.sdkVersion) machine.sdkVersion = inv.sdkVersion;
491
+ try {
492
+ const inv = await collectInventory({
493
+ ...o,
494
+ // Hand the identity answers on so the tailnet probe forks ONCE per
495
+ // beat, not twice. An empty string is an answer ("probed, none
496
+ // found") — and when `identity` is switched off entirely it is the
497
+ // right answer to hand on: a caller that asked for no identity should
498
+ // not pay for a tailnet fork inside the inventory probe either.
499
+ // `collectInventory` still returns those two fields; we ignore them —
500
+ // `machine.hostname/tailnetIp/sdkVersion` are set above or not at all.
501
+ tailnetIp: typeof o.tailnetIp === "string" ? o.tailnetIp : String(identity.tailnetIp || ""),
502
+ sdkVersion: typeof o.sdkVersion === "string" ? o.sdkVersion : String(identity.sdkVersion || ""),
503
+ });
504
+ if (inv.device) machine.device = inv.device;
505
+ if (inv.ram) machine.ram = inv.ram;
506
+ if (Number.isFinite(inv.cores)) machine.cores = inv.cores;
507
+ } catch { /* device/ram/cores drop; identity above already landed */ }
448
508
  }
449
509
 
450
510
  // spend24h — the agent's own rolling 24h LLM spend, off the cost ledger.
@@ -1310,6 +1370,123 @@ export function upgradeSummary(last) {
1310
1370
  return { at, from: typeof last.from === "string" ? last.from : "", to, ok: last.ok === true };
1311
1371
  }
1312
1372
 
1373
+ // ---------------------------------------------------------------------------
1374
+ // Daemon — WHICH process is beating, what code it is running, and the gate's
1375
+ // last verdict on it (`machine.daemon`)
1376
+ // ---------------------------------------------------------------------------
1377
+
1378
+ /**
1379
+ * Top-level scalars we lift out of state/dashboards/daemon-health.yaml. The file
1380
+ * is written by scripts/daemon/health.mjs as flat `key: value` lines plus one
1381
+ * indented `dispatcher:` JSON block, so a line regex over the UNINDENTED lines
1382
+ * reads it exactly and cannot be confused by the nested block. We do NOT pull a
1383
+ * YAML parser in for four scalars on a 30-second loop.
1384
+ *
1385
+ * Fail-open → null (no file, unreadable, or no recognisable line).
1386
+ * @param {string} text
1387
+ * @returns {{pid?:number, sdkVersion?:string, installedSdkVersion?:string,
1388
+ * versionsAgree?:boolean, versionDrift?:string, bootAt?:string,
1389
+ * updatedAt?:string, uptimeS?:number}|null}
1390
+ */
1391
+ export function parseDaemonHealthYaml(text) {
1392
+ if (typeof text !== "string" || text === "") return null;
1393
+ const out = {};
1394
+ for (const line of text.split("\n")) {
1395
+ // Unindented `key: value` only — an indented line belongs to `dispatcher:`.
1396
+ const m = /^([a-z_]+):[ \t]+(.+?)[ \t]*$/.exec(line);
1397
+ if (!m) continue;
1398
+ const [, k, v] = m;
1399
+ if (k === "pid") { const n = Number(v); if (Number.isInteger(n) && n > 0) out.pid = n; }
1400
+ else if (k === "sdk_version") { const s = v.trim(); if (s && s !== "undefined" && s !== "null") out.sdkVersion = s; }
1401
+ // The three the resolver added so a version can be CHECKED and not merely
1402
+ // read. `sdk_version_agrees` is a tri-state written as a word: "unknown"
1403
+ // is not false and must never collapse to one.
1404
+ else if (k === "sdk_version_installed") { const s = v.trim(); if (s && s !== "undefined" && s !== "null") out.installedSdkVersion = s; }
1405
+ else if (k === "sdk_version_agrees") { const s = v.trim(); if (s === "true" || s === "false") out.versionsAgree = s === "true"; }
1406
+ else if (k === "sdk_version_drift") { const s = v.trim(); if (s && s !== "undefined" && s !== "null") out.versionDrift = s; }
1407
+ else if (k === "daemon_start") { const s = v.trim(); if (!Number.isNaN(Date.parse(s))) out.bootAt = new Date(Date.parse(s)).toISOString(); }
1408
+ else if (k === "updated") { const s = v.trim(); if (!Number.isNaN(Date.parse(s))) out.updatedAt = new Date(Date.parse(s)).toISOString(); }
1409
+ else if (k === "uptime_s") { const n = Number(v); if (Number.isFinite(n) && n >= 0) out.uptimeS = Math.floor(n); }
1410
+ }
1411
+ return Object.keys(out).length > 0 ? out : null;
1412
+ }
1413
+
1414
+ /** state/dashboards/daemon-health.yaml as text, or "" — fail-open. */
1415
+ function readDaemonHealthText(agentRoot) {
1416
+ if (!agentRoot) return "";
1417
+ try {
1418
+ return readFileSync(join(resolve(agentRoot), "state", "dashboards", "daemon-health.yaml"), "utf8");
1419
+ } catch { return ""; }
1420
+ }
1421
+
1422
+ /**
1423
+ * Pure: the beat's `machine.daemon` — WHO is beating and whether the last health
1424
+ * gate believed it.
1425
+ *
1426
+ * WHY THIS FIELD EXISTS. `machine.sdkVersion` is the version INSTALLED on the
1427
+ * seat. It is not the version RUNNING: autoupdate installs into node_modules
1428
+ * while the old daemon process keeps executing the code it booted on, and a
1429
+ * hand-run `maestro upgrade` never restarts the daemon at all. One seat logged
1430
+ * "up to date" hourly for six days while its daemon crash-looped on the
1431
+ * previous version ~25 s at a time; the org saw nothing, because nothing the
1432
+ * beat carried could tell the two versions apart. `pid` + `bootAt` make the
1433
+ * distinction visible org-side: a pid that changes every beat is a crash loop,
1434
+ * and `sdkVersion` here differing from `machine.sdkVersion` is an installed-but-
1435
+ * not-restarted seat. Neither needs an ssh tour to see any more.
1436
+ *
1437
+ * ADDITIVE AND SMALL — six scalars, every one optional. It rides `machine`,
1438
+ * which hq validates as an OPEN record (`presence/beat.ts#statusSchema`), so it
1439
+ * lands the moment the daemon emits it and renders when the reader learns it.
1440
+ * It is NOT put on `session`, whose hq schema is `.strict()` and would refuse
1441
+ * the whole beat.
1442
+ *
1443
+ * FRESHNESS. `pid`/`bootAt`/`sdkVersion` describe the process that wrote the
1444
+ * dashboard, which a DEAD daemon leaves behind verbatim. `dashboardAt` is that
1445
+ * file's own stamp, carried so a reader can age the claim rather than trust it;
1446
+ * the ledger beat's server-stamped `at` remains the only liveness signal.
1447
+ *
1448
+ * @param {object|null} dash parsed daemon-health.yaml scalars, or null
1449
+ * @param {object|null} last state/autoupdate/last.json, or null
1450
+ * THE SEAT'S OWN VERDICT RIDES ALONG. `installedSdkVersion` + `versionsAgree`
1451
+ * are what the daemon's resolver concluded on the box, where both files are
1452
+ * readable; hq compares `machine.sdkVersion` against `machine.daemon.sdkVersion`
1453
+ * and reaches the same conclusion from the outside. Carrying both is not
1454
+ * redundancy — they are read at different instants from different files, and a
1455
+ * reader that sees them disagree has learned something real. `versionsAgree` is
1456
+ * carried ONLY as a real boolean: an inferred running version yields no verdict
1457
+ * at all, and absence must never read as agreement.
1458
+ *
1459
+ * @returns {{pid?:number, bootAt?:string, uptimeS?:number, sdkVersion?:string,
1460
+ * installedSdkVersion?:string, versionsAgree?:boolean,
1461
+ * versionDrift?:string, dashboardAt?:string, healthy?:boolean,
1462
+ * healthReason?:string}|null}
1463
+ */
1464
+ export function daemonSummary(dash, last) {
1465
+ const out = {};
1466
+ if (dash && typeof dash === "object") {
1467
+ if (Number.isInteger(dash.pid) && dash.pid > 0) out.pid = dash.pid;
1468
+ if (typeof dash.bootAt === "string" && dash.bootAt) out.bootAt = dash.bootAt;
1469
+ if (Number.isFinite(dash.uptimeS) && dash.uptimeS >= 0) out.uptimeS = Math.floor(dash.uptimeS);
1470
+ if (typeof dash.sdkVersion === "string" && dash.sdkVersion) out.sdkVersion = dash.sdkVersion;
1471
+ if (typeof dash.installedSdkVersion === "string" && dash.installedSdkVersion) out.installedSdkVersion = dash.installedSdkVersion;
1472
+ if (typeof dash.versionsAgree === "boolean") out.versionsAgree = dash.versionsAgree;
1473
+ if (dash.versionsAgree === false && typeof dash.versionDrift === "string" && dash.versionDrift) out.versionDrift = dash.versionDrift.slice(0, 200);
1474
+ if (typeof dash.updatedAt === "string" && dash.updatedAt) out.dashboardAt = dash.updatedAt;
1475
+ }
1476
+ // The health GATE's verdict, as autoupdate.sh last recorded it
1477
+ // (`{from,to,at,ok,healthy,reason}`). `healthy` is the gate's answer about the
1478
+ // DAEMON — pid stability, no fatal line since boot, a server-acknowledged
1479
+ // beat — which is a different question from `upgrade.ok` (did the last install
1480
+ // land). Only a real boolean is carried: a record written before the gate
1481
+ // existed has no opinion, and absence must not read as "healthy".
1482
+ if (last && typeof last === "object" && typeof last.healthy === "boolean") {
1483
+ out.healthy = last.healthy;
1484
+ const reason = typeof last.reason === "string" ? last.reason.trim() : "";
1485
+ if (!last.healthy && reason) out.healthReason = reason.slice(0, 200);
1486
+ }
1487
+ return Object.keys(out).length > 0 ? out : null;
1488
+ }
1489
+
1313
1490
  /** state/autoupdate/last.json, or null. */
1314
1491
  function readAutoupdateLast(agentRoot) {
1315
1492
  if (!agentRoot) return null;
@@ -1570,7 +1747,12 @@ function subscriptionModeFromEnv(agentRoot, env) {
1570
1747
  function probeAuthForBeat(o, nowMs) {
1571
1748
  if (o.probeClaude === false) return "unknown";
1572
1749
  const agentRoot = resolve(o.agentRoot);
1573
- if (!subscriptionModeFromEnv(agentRoot, o.env || process.env)) return "unknown";
1750
+ // W4 integration (row 25): an engine-cohort seat's probe is the engine's `auth status` — a
1751
+ // real call to the gateway that proves the seat credential — so it is asked whatever the
1752
+ // seat's Claude env says. A claude seat still probes only on the subscription path.
1753
+ let cohortSeat = false;
1754
+ try { cohortSeat = (o.seatEngine || readSeatEngine)(agentRoot) === "cohort"; } catch { cohortSeat = false; /* unreadable config: the default engine */ }
1755
+ if (!cohortSeat && !subscriptionModeFromEnv(agentRoot, o.env || process.env)) return "unknown";
1574
1756
  const injected = typeof o.probeClaude === "function";
1575
1757
  if (!injected) {
1576
1758
  const hit = _authProbeMemo.get(agentRoot);
@@ -1737,6 +1919,21 @@ export async function collectStatus(o = {}) {
1737
1919
  if (up) machine.upgrade = up;
1738
1920
  } catch { /* no upgrade field */ }
1739
1921
 
1922
+ // 4d. WHO is beating — `machine.daemon` {pid, bootAt, uptimeS, sdkVersion,
1923
+ // dashboardAt, healthy?, healthReason?}. This is what makes fleet version
1924
+ // drift a QUERY instead of an ssh tour: `machine.sdkVersion` is what is
1925
+ // INSTALLED, `machine.daemon.sdkVersion` is what is RUNNING, and a pid that
1926
+ // moves every beat is a crash loop. Fail-open in the same way every other
1927
+ // probe here is — a throw drops the field, never the beat.
1928
+ try {
1929
+ const dash = opt.daemonDashboard !== undefined
1930
+ ? opt.daemonDashboard
1931
+ : parseDaemonHealthYaml(readDaemonHealthText(opt.agentRoot));
1932
+ const last = opt.autoupdateLast !== undefined ? opt.autoupdateLast : readAutoupdateLast(opt.agentRoot);
1933
+ const d = daemonSummary(dash, last);
1934
+ if (d) machine.daemon = d;
1935
+ } catch { /* no daemon field */ }
1936
+
1740
1937
  const status = {
1741
1938
  state,
1742
1939
  activity: act.activity || "idle",
@@ -1782,6 +1979,8 @@ export const _internals = {
1782
1979
  sessionJobInstalled,
1783
1980
  readSessionAttention,
1784
1981
  upgradeSummary,
1982
+ parseDaemonHealthYaml,
1983
+ daemonSummary,
1785
1984
  detectClaudeAuth,
1786
1985
  resetAuthProbeCache,
1787
1986
  scanForRelogin,
@@ -0,0 +1,105 @@
1
+ /**
2
+ * lib/upgrade/ignored-drift.mjs — what a `.maestroignore`-protected file is
3
+ * missing from upstream, said per file.
4
+ *
5
+ * `maestro upgrade` never touches a path `.maestroignore` names — that is the
6
+ * file's whole purpose. Until this module, the summary said only "14 ignored
7
+ * (.maestroignore protected)": a count, with no way to tell from the log
8
+ * whether a protected fork was one line behind upstream or a whole release
9
+ * behind. On one seat the protected daemon lacked 24 upstream references to a
10
+ * feature the release notes said had shipped, and nothing in any upgrade log
11
+ * said so. A protected file is a fork the operator owns, and an owner needs a
12
+ * per-file answer to "how far has upstream moved under this?".
13
+ *
14
+ * `classifyIgnored` is PURE over the two texts (either may be null for absent)
15
+ * and answers one of:
16
+ *
17
+ * identical — the protection is currently idle (safe to drop the entry)
18
+ * local-only — the seat has it, upstream ships nothing at that path
19
+ * upstream-only — upstream ships it, the seat has never taken it
20
+ * drifts — both exist and differ: `onlyHere` / `onlyUpstream` are
21
+ * the line counts each side has that the other does not
22
+ *
23
+ * The line counts are a MULTISET difference, not an edit script: cheap, stable
24
+ * across a whole tree, and honest about what they are — a size of drift, not a
25
+ * patch. A moved block counts as zero drift, which is the right answer for
26
+ * "is upstream's fix in here?" and the wrong one for "is this file identical?"
27
+ * (that is `identical`, decided by bytes first).
28
+ *
29
+ * @module lib/upgrade/ignored-drift
30
+ */
31
+
32
+ "use strict";
33
+
34
+ /** Where `maestro upgrade` writes the report, relative to the agent root. */
35
+ export const IGNORED_DRIFT_REL = ".maestro/ignored-drift.json";
36
+
37
+ /**
38
+ * Multiset line difference of two texts.
39
+ * @param {string} a
40
+ * @param {string} b
41
+ * @returns {{onlyA:number, onlyB:number}}
42
+ */
43
+ export function lineDrift(a, b) {
44
+ const count = (text) => {
45
+ const m = new Map();
46
+ for (const line of String(text).split("\n")) m.set(line, (m.get(line) || 0) + 1);
47
+ return m;
48
+ };
49
+ const ma = count(a);
50
+ const mb = count(b);
51
+ let onlyA = 0;
52
+ let onlyB = 0;
53
+ for (const [line, n] of ma) onlyA += Math.max(0, n - (mb.get(line) || 0));
54
+ for (const [line, n] of mb) onlyB += Math.max(0, n - (ma.get(line) || 0));
55
+ return { onlyA, onlyB };
56
+ }
57
+
58
+ /**
59
+ * Classify one protected path.
60
+ * @param {string} path repo-relative path (reported back verbatim)
61
+ * @param {string|null} localText the seat's bytes, or null when absent
62
+ * @param {string|null} upstreamText the package's bytes, or null when absent
63
+ * @returns {{path:string, state:"identical"|"local-only"|"upstream-only"|"drifts", onlyHere?:number, onlyUpstream?:number}}
64
+ */
65
+ export function classifyIgnored(path, localText, upstreamText) {
66
+ const hasLocal = typeof localText === "string";
67
+ const hasUpstream = typeof upstreamText === "string";
68
+ if (hasLocal && !hasUpstream) return { path, state: "local-only" };
69
+ if (!hasLocal && hasUpstream) return { path, state: "upstream-only" };
70
+ if (!hasLocal && !hasUpstream) return { path, state: "local-only" }; // named in .maestroignore, present nowhere — the entry is idle
71
+ if (localText === upstreamText) return { path, state: "identical" };
72
+ const d = lineDrift(localText, upstreamText);
73
+ return { path, state: "drifts", onlyHere: d.onlyA, onlyUpstream: d.onlyB };
74
+ }
75
+
76
+ /**
77
+ * Build the whole report.
78
+ * @param {Array<{path:string, local:string|null, upstream:string|null}>} entries
79
+ * @param {{sdkVersion?:string, at?:string}} [meta]
80
+ */
81
+ export function buildIgnoredDriftReport(entries, meta = {}) {
82
+ const files = entries.map((e) => classifyIgnored(e.path, e.local, e.upstream));
83
+ const counts = { identical: 0, "local-only": 0, "upstream-only": 0, drifts: 0 };
84
+ for (const f of files) counts[f.state] += 1;
85
+ return {
86
+ sdkVersion: meta.sdkVersion || null,
87
+ at: meta.at || null,
88
+ counts,
89
+ files,
90
+ };
91
+ }
92
+
93
+ /**
94
+ * One log line per protected file, for the upgrade summary.
95
+ * @param {{path:string, state:string, onlyHere?:number, onlyUpstream?:number}} f
96
+ */
97
+ export function formatIgnoredLine(f) {
98
+ switch (f.state) {
99
+ case "identical": return ` = ${f.path} (identical to upstream — the entry is idle)`;
100
+ case "local-only": return ` · ${f.path} (upstream ships nothing here)`;
101
+ case "upstream-only": return ` ! ${f.path} (upstream ships it; this seat has never taken it)`;
102
+ case "drifts": return ` ~ ${f.path} (drifts: ${f.onlyUpstream} upstream line${f.onlyUpstream === 1 ? "" : "s"} not here, ${f.onlyHere} local line${f.onlyHere === 1 ? "" : "s"} not upstream)`;
103
+ default: return ` ? ${f.path}`;
104
+ }
105
+ }
@@ -37,6 +37,8 @@ import { spawn } from "node:child_process";
37
37
  import { writeFileSync, readFileSync, mkdirSync, existsSync, appendFileSync } from "node:fs";
38
38
  import { join, resolve, dirname } from "node:path";
39
39
  import { writeJsonAtomic } from "../fs-atomic.mjs";
40
+ import { buildSpawn } from "../runtime/adapter.mjs";
41
+ import { resolveSeatSpawn } from "../runtime/seat-engine.mjs";
40
42
 
41
43
  const BRIEF_PROMPT = `You are an executive operator processing the transcript of a voice call.
42
44
  Produce a structured post-call brief in JSON only — no preamble, no commentary.
@@ -166,25 +168,36 @@ export async function generateBrief(opts = {}) {
166
168
  // Claude --print runner (single-shot, JSON-only output)
167
169
  // ---------------------------------------------------------------------------
168
170
 
169
- function runClaudeBrief({ agentRoot, model, systemPrompt, input }) {
171
+ /**
172
+ * The voice-brief spawn for this seat (conformance row 36). Binary + argv + env
173
+ * from the runtime adapter (lane "voice-brief"), with the seat's engine:
174
+ * claude --print --dangerously-skip-permissions --strict-mcp-config --model <m>
175
+ * <prompt>, env {...env, AGENT_ROOT, AGENT_DIR}; the binary resolved
176
+ * by claude-bin (a safe $CLAUDE_BIN, then the known install paths)
177
+ * instead of a bare `claude`, which ENOENTs under launchd. The
178
+ * buildSpawn input is exactly what it was before the seat was read.
179
+ * cohort `node lib/engine/cli.mjs run -p …` with the seat token and every
180
+ * Anthropic/Claude credential scrubbed; no token → a refused spec
181
+ * (the caller writes the fallback brief), never claude.
182
+ * @param {{agentRoot:string, model:string, prompt:string, env:object}} o
183
+ * @param {{resolveSeatSpawn?:Function, buildSpawn?:Function, adapterDeps?:object}} [deps]
184
+ */
185
+ export async function briefSpawnSpec({ agentRoot, model, prompt, env }, deps = {}) {
186
+ const seat = await (deps.resolveSeatSpawn || resolveSeatSpawn)(agentRoot);
187
+ return (deps.buildSpawn || buildSpawn)({ lane: "voice-brief", model, prompt, mcp: "strict", agentRoot, env, ...seat.fields }, deps.adapterDeps);
188
+ }
189
+
190
+ async function runClaudeBrief({ agentRoot, model, systemPrompt, input }) {
191
+ const fullPrompt = `${systemPrompt}\n\n---\n${input}`;
192
+ const spec = await briefSpawnSpec({ agentRoot, model, prompt: fullPrompt, env: process.env });
170
193
  return new Promise((resolveP) => {
171
- const claudeBin = process.env.CLAUDE_BIN || "claude";
172
- const fullPrompt = `${systemPrompt}\n\n---\n${input}`;
173
- const proc = spawn(claudeBin, [
174
- "--print",
175
- "--dangerously-skip-permissions",
176
- "--strict-mcp-config",
177
- "--model", model,
178
- fullPrompt,
179
- ], {
194
+ if (!spec.ok) {
195
+ resolveP(fallbackBrief(input, spec.error.message));
196
+ return;
197
+ }
198
+ const proc = spawn(spec.bin, spec.argv, {
180
199
  cwd: agentRoot,
181
- env: {
182
- ...process.env,
183
- AGENT_ROOT: agentRoot,
184
- AGENT_DIR: agentRoot,
185
- // Voice briefs are routine summarization; let model router pick
186
- // the cheap backend by signalling it as a "fast" tier task.
187
- },
200
+ env: spec.env,
188
201
  stdio: ["ignore", "pipe", "pipe"],
189
202
  });
190
203
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cohortapp/agent-sdk",
3
- "version": "2.17.0",
3
+ "version": "2.18.4",
4
4
  "description": "Cohort Agent SDK — autonomous AI colleague runtime. Deploy senior AI colleagues on dedicated Mac minis, wired to the Cohort operating surface.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -114,18 +114,28 @@
114
114
  ".claude/settings.json",
115
115
  ".env.example",
116
116
  "README.md",
117
- "framework-features.json"
117
+ "framework-features.json",
118
+ "!**/*.test.mjs",
119
+ "!**/*.test.js",
120
+ "!**/fixtures/**",
121
+ "!**/test-fixtures/**",
122
+ "!**/__fixtures__/**",
123
+ "!test/**",
124
+ "!scripts/eval/replay/examples/**"
118
125
  ],
119
126
  "publishConfig": {
120
127
  "access": "public",
121
128
  "registry": "https://registry.npmjs.org/"
122
129
  },
123
130
  "scripts": {
131
+ "fleet:rollout": "node scripts/fleet/rollout.mjs",
124
132
  "test": "node scripts/ci/run-tests.mjs",
125
133
  "test:coverage": "node scripts/ci/run-coverage.mjs",
126
- "test:list": "node --test bin/maestro.test.mjs lib/action-executor.test.mjs lib/archetype.test.mjs lib/autonomy.test.mjs lib/backlog.test.mjs lib/budget-guard.test.mjs lib/cadence-bus-schedule.test.mjs lib/cadence-bus.test.mjs lib/cadences.test.mjs lib/capability.test.mjs lib/channels/base-adapter.test.mjs lib/channels/channels.test.mjs lib/channels/contract.test.mjs lib/channels/inbox-item.test.mjs lib/channels/pairing.test.mjs lib/channels/slack-adapter.test.mjs lib/channels/telegram-adapter.test.mjs lib/channels/whatsapp/baileys-typing.test.mjs lib/charter.test.mjs lib/claude-bin.test.mjs lib/collective/capture.test.mjs lib/collective/cards.test.mjs lib/collective/config.test.mjs lib/collective/global-config.test.mjs lib/collective/presence.test.mjs lib/collective/recall.test.mjs lib/comms.test.mjs lib/fs-atomic.test.mjs lib/fs-ownership.test.mjs lib/learning/config.test.mjs lib/learning/counters.test.mjs lib/learning/curator-consolidate.test.mjs lib/learning/curator.test.mjs lib/learning/reflect.test.mjs lib/learning/session-index.test.mjs lib/learning/skill-writer.test.mjs lib/model-router.test.mjs lib/model-router/catalog.test.mjs lib/model-router/economics.test.mjs lib/model-router/failover.test.mjs lib/model-router/health.test.mjs lib/model-router/integration.test.mjs lib/model-router/integration-coverage.test.mjs lib/model-router/ledger.test.mjs lib/model-router/llm-task.test.mjs lib/model-router/org-credentials.test.mjs lib/model-router/reconcile.test.mjs lib/model-router/repair.test.mjs lib/model-router/spawn.test.mjs lib/model-router/taxonomy.test.mjs lib/org/approvals.test.mjs lib/org/board.test.mjs lib/org/client.test.mjs lib/org/cost-sync.test.mjs lib/org/handoff.test.mjs lib/org/keys.test.mjs lib/org/knowledge.test.mjs lib/org/activity.test.mjs lib/org/leases.test.mjs lib/org/mesh.test.mjs lib/org/messaging.test.mjs lib/org/cohort-client.test.mjs lib/org/policy.test.mjs lib/org/registry.test.mjs lib/org/tool-surface.test.mjs lib/org/ui-parity.test.mjs lib/org/doctor.test.mjs lib/org/verify.test.mjs lib/mcp/server.test.mjs lib/channels/orgmail/adapter.test.mjs lib/rag/rag.test.mjs lib/rate-guard.test.mjs lib/render.test.mjs lib/resource-governor.test.mjs lib/security/coerce-args.test.mjs lib/security/dangerous-tools.test.mjs lib/security/external-content.test.mjs lib/security/redact.test.mjs lib/security/secret-equal.test.mjs lib/session-permissions.test.mjs lib/setup/completeness.test.mjs lib/setup/context-pack.test.mjs lib/setup/enrich.test.mjs lib/setup/enroll-from-cohort.test.mjs lib/setup/integration.test.mjs lib/setup/io.test.mjs lib/setup/runner.test.mjs lib/setup/sections/identity.test.mjs lib/setup/sections/learning.test.mjs lib/setup/sections/messaging.test.mjs lib/setup/sections/org.test.mjs lib/setup/sections/orgmail.test.mjs lib/setup/sections/recovery.test.mjs lib/setup/sections/subagents.test.mjs lib/setup/sot.test.mjs lib/setup/state.test.mjs lib/singleton.test.mjs lib/subagents/schema.test.mjs lib/subagents/refs.test.mjs lib/subagents/manifest.test.mjs lib/subagents/lock.test.mjs lib/subagents/resolve.test.mjs lib/subagents/gap.test.mjs lib/subagents/client.test.mjs lib/subagents/cli.test.mjs lib/telemetry/alerts.test.mjs lib/telemetry/collect.test.mjs lib/tool-definitions.test.mjs lib/voice/session-rotation.test.mjs lib/voice/outbound.test.mjs lib/voice/stt.test.mjs lib/voice/voice.test.mjs scripts/cadence/enqueue-cadence-tick.test.mjs scripts/ci/check-durable-write-seam.test.mjs scripts/ci/check-no-build-artifacts.test.mjs scripts/ci/check-no-residual-identity.test.mjs scripts/ci/check.test.mjs scripts/collective/hook-runner.test.mjs scripts/cost/fleet-digest.test.mjs scripts/daemon/cadence-consumer-governance.test.mjs scripts/daemon/cadence-consumer.test.mjs scripts/daemon/cadence-handlers.test.mjs scripts/daemon/classify-kind.test.mjs scripts/daemon/dispatcher-cooldown.test.mjs scripts/daemon/dispatcher-governance.test.mjs scripts/daemon/inbox-deferral.test.mjs scripts/daemon/lib/session-router.test.mjs scripts/daemon/prompt-builder.test.mjs scripts/daemon/typing-registry.test.mjs scripts/hooks/pre-send-audit.test.mjs scripts/local-triggers/generate-plists.test.mjs scripts/poller/inbox-scan-poller.test.mjs scripts/poller/lib/cloud-relay-dedup.test.mjs scripts/poller/slack-socket-mode.test.mjs scripts/setup/gen-subagent-manifest.test.mjs scripts/setup/init-agent.test.mjs services/webhook-relay/server.test.mjs lib/cadence-bus-requeue.test.mjs lib/cli/inbox.test.mjs lib/cli/session-ack.test.mjs lib/org/mesh-directives.test.mjs lib/session/frontdoor.test.mjs lib/session/handoffs.test.mjs lib/session/inbox-claims.test.mjs scripts/daemon/agent-daemon-frontdoor.test.mjs scripts/daemon/cadence-consumer-frontdoor.test.mjs scripts/daemon/dispatcher-resume.test.mjs scripts/daemon/inbox-deferral-session.test.mjs scripts/watchdog/memory-watchdog.test.mjs lib/upgrade/launchd-reconcile.test.mjs lib/upgrade/global-refresh.test.mjs lib/upgrade/verify.test.mjs lib/upgrade/post-steps.test.mjs lib/org/board-mine-cache.test.mjs scripts/daemon/agent-daemon-board-mine.test.mjs scripts/local-triggers/autoupdate.test.mjs scripts/daemon/sdk-version.test.mjs",
134
+ "test:list": "node scripts/ci/run-tests.mjs --list",
127
135
  "test:setup": "node --test lib/setup/io.test.mjs lib/setup/state.test.mjs lib/setup/sot.test.mjs lib/setup/completeness.test.mjs lib/setup/context-pack.test.mjs lib/setup/enrich.test.mjs lib/setup/runner.test.mjs lib/setup/integration.test.mjs lib/setup/sections/subagents.test.mjs",
128
136
  "check": "node scripts/ci/check.mjs",
137
+ "test:engine-conformance": "node test/engine-conformance/run.mjs",
138
+ "eval:replay": "node scripts/eval/replay/run.mjs",
129
139
  "conformance:org": "node scripts/ci/conformance-org-api.mjs",
130
140
  "conformance:org:write": "node scripts/ci/conformance-org-api.mjs --write",
131
141
  "test:router": "node --test lib/model-router.test.mjs lib/model-router/catalog.test.mjs lib/model-router/economics.test.mjs lib/model-router/failover.test.mjs lib/model-router/health.test.mjs lib/model-router/integration.test.mjs lib/model-router/integration-coverage.test.mjs lib/model-router/ledger.test.mjs lib/model-router/llm-task.test.mjs lib/model-router/org-credentials.test.mjs lib/model-router/reconcile.test.mjs lib/model-router/repair.test.mjs lib/model-router/spawn.test.mjs lib/model-router/taxonomy.test.mjs scripts/cost/fleet-digest.test.mjs",
@@ -54,6 +54,11 @@ accepted` (or `maestro board track <inbox-id> --stage accepted --title …
54
54
  the message and dedupes, so a re-delivered ask finds the same row. See
55
55
  `inbound-triage`.
56
56
 
57
+ If you got the ask through the inbox, `maestro inbox claim <id>` has ALREADY
58
+ filed it — and `--title` / `--why` are read only by the step that opens the
59
+ row, so they belong on that claim (`maestro inbox claim <id> --title … --why
60
+ …`). Sending them later, against a row that exists, is accepted and ignored.
61
+
57
62
  ## What not to do
58
63
 
59
64
  - Do not narrate every step on the board; the board is for the state a
@@ -11,26 +11,39 @@ no: see "The one-interim rule" at the foot of this skill.
11
11
 
12
12
  ## The three outcomes
13
13
 
14
+ > **The board row is automatic — you do not open it.** `maestro inbox claim`
15
+ > opens the row and puts it in `running`, `reply` closes it with what you said,
16
+ > `done` closes it if you never replied, and `defer` parks it in `blocked` with
17
+ > your reason. The server's own gate decides whether an ask deserves a row at
18
+ > all, from the text, so a claimed one-line question mints nothing. Use
19
+ > `maestro board track` only to say something the four commands cannot: a
20
+ > `--stage blocked` with `notify` when you need unblocking mid-work, or a
21
+ > `--stage review` when a human should look before it goes out.
22
+
14
23
  1. **Reply now.** The ask is answerable in one message from what you already
15
24
  know or can look up in under a minute (a status, a fact, a yes/no, a link,
16
25
  a short opinion). `maestro inbox claim <id>` → `maestro inbox reply <id>
17
- --text "…"` → `maestro inbox done <id>`. No board row: the ledger on the
18
- server already records that you answered.
26
+ --text "…"` → `maestro inbox done <id>`. No board row: the server's gate
27
+ reads the text as answerable in the turn and mints nothing.
19
28
 
20
29
  2. **File it, work here.** The ask needs real work — reading, drafting,
21
30
  building, several steps — but you can finish it in this session inside an
22
31
  hour or two without blocking the front door. In ONE turn:
23
- - `maestro inbox claim <id>`
24
- - `maestro board track <id> --stage accepted --title "<what you took on>"
25
- --why "<one line: why this is more than a reply>"`
32
+ - `maestro inbox claim <id>` — this opens the row and puts it in `running`.
33
+ If the row deserves a better name than the ask's own first sentence, say
34
+ so ON THE CLAIM: `maestro inbox claim <id> --title "<what you took on>"
35
+ --why "<one line: why this is more than a reply>"`. The claim is the only
36
+ moment either is read — the server writes the title and the reason when
37
+ the row OPENS and protects them afterwards, so a later `maestro board
38
+ track … --title/--why` on the same ask is accepted and changes nothing.
26
39
  - then run the work — a `Workflow` when it has distinct steps, plain tool
27
- use when it does not. Track meaningful changes only: `--stage working`
28
- when the substantive part starts, `--stage blocked` (say what you need,
29
- `notify` who can unblock) when stuck, `--stage review` when a human should
30
- look before it goes out.
31
- - report back in the SAME channel/thread with `maestro inbox reply <id>`
32
- (or `messaging_send` to the thread), then `maestro board track <id>
33
- --stage done` and `maestro inbox done <id>`.
40
+ use when it does not. Track only what the four inbox commands cannot say:
41
+ `--stage blocked` (say what you need, `notify` who can unblock) when
42
+ stuck, `--stage review` when a human should look before it goes out.
43
+ - report back in the SAME channel/thread with `maestro inbox reply <id>`,
44
+ which closes the row and tags the channel, then `maestro inbox done <id>`.
45
+ If you reported with `messaging_send` instead, `maestro inbox done <id>`
46
+ is what closes the row.
34
47
 
35
48
  3. **File it, hand to a peer.** Same as 2, but the work is long (hours), heavy
36
49
  (a repo build, a large research pass), or would block you from answering the
@@ -39,13 +52,14 @@ no: see "The one-interim rule" at the foot of this skill.
39
52
  with a prompt that names the deliverable, the channel and thread to report
40
53
  to, the inbox id, and the instruction to `SendMessage` you a two-line
41
54
  status when done. You stay the one who talks to the human; the peer talks to
42
- you. When it reports back, you send the result and close the row
43
- (`--stage done`) and the item (`maestro inbox done <id>`).
55
+ you. When it reports back, you send the result and close the item
56
+ (`maestro inbox reply` then `maestro inbox done <id>`), which closes the row.
44
57
 
45
58
  ## How to pick
46
59
 
47
60
  - Would a competent colleague answer this from their chair in one message?
48
- → reply now.
61
+ → reply now. A "what are you doing / working on right now?" is this kind:
62
+ answer it from `maestro session status` (`currentWork`), never from memory.
49
63
  - Does it produce an artefact (a doc, a deck, a number that needs checking, a
50
64
  change in a system)? → file it. The server derives the board from the
51
65
  message: a DM's ask lands on that conversation's board, a space's ask on the
@@ -56,6 +70,33 @@ no: see "The one-interim rule" at the foot of this skill.
56
70
  and say in-channel what you need and from whom. That is a message with
57
71
  content in it, so it is always worth sending; do not guess.
58
72
 
73
+ ## Addressed to the whole room (`collective` on the feed line)
74
+
75
+ A message a human sent to everyone carries `collective` on its `inbound` line:
76
+ `roll-call`, `work`, or `ambient`. It is empty for a DM, an @mention or a task.
77
+ Nobody was named, so nobody is more responsible than you: **you answer for your
78
+ own seat, and you never answer for anyone else's.**
79
+
80
+ - `collective: roll-call` — "can everyone give me an update on what they're
81
+ doing right now?" → **outcome 1**. Answer from `maestro session status`
82
+ (`currentWork`) in at most five short lines: what you are on, what is queued,
83
+ what is blocked. No board row — reporting on yourself is not work. Do not
84
+ summarise, guess at or speak for any colleague; they answer for themselves.
85
+ - `collective: work` — "everyone, please review the binder by Friday" → **outcome
86
+ 2 or 3**, exactly as if it had been addressed to you alone. Claim it, `board
87
+ track --stage accepted`, do YOUR slice, and reply with what you will do and by
88
+ when. The board row is yours and covers your slice only; twelve other seats
89
+ are opening twelve other rows for the same message and that is correct.
90
+ - `collective: ambient` — the room was addressed and nothing was asked of you
91
+ ("everyone should take a look sometime", "FYI the binder is up"). **Outcome 1
92
+ at most, and usually silence.** Do not mint work from it. A board that grows a
93
+ row per seat per aside is a board nobody opens by the end of the week.
94
+
95
+ Why it is on the line at all: when your front door is live the daemon does not
96
+ dispatch these items, so there is no other plane that will act on them. If the
97
+ field is missing or empty on a message that plainly addressed the room, treat it
98
+ as `ambient` and use your judgement — it is a hint, not an instruction.
99
+
59
100
  ## Special topics
60
101
 
61
102
  - **Calls** (`topic: call`): answer in the channel — join if you are free now,