@cohortapp/agent-sdk 2.17.0 → 2.18.5

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 (531) 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-slots.mjs +234 -0
  23. package/lib/collective/capture.mjs +8 -6
  24. package/lib/collective/config.mjs +2 -0
  25. package/lib/collective/global-config.mjs +63 -1
  26. package/lib/collective/loop-guard.mjs +155 -0
  27. package/lib/collective/presence.mjs +142 -5
  28. package/lib/comms/send-gate.mjs +559 -1
  29. package/lib/diagnostics/alerts.mjs +49 -0
  30. package/lib/diagnostics/cadence-output-freshness.mjs +288 -0
  31. package/lib/engine/agents/definitions.mjs +343 -0
  32. package/lib/engine/agents/persist.mjs +275 -0
  33. package/lib/engine/agents/runtime.mjs +748 -0
  34. package/lib/engine/agents/usage.mjs +95 -0
  35. package/lib/engine/auth-status.mjs +139 -0
  36. package/lib/engine/budget.mjs +194 -0
  37. package/lib/engine/cli.mjs +1204 -0
  38. package/lib/engine/commands/index.mjs +269 -0
  39. package/lib/engine/context/budget.mjs +219 -0
  40. package/lib/engine/context/cache.mjs +125 -0
  41. package/lib/engine/context/child-env.mjs +215 -0
  42. package/lib/engine/context/compaction.mjs +342 -0
  43. package/lib/engine/context/images.mjs +90 -0
  44. package/lib/engine/context/instructions.mjs +327 -0
  45. package/lib/engine/context/lazy-instructions.mjs +169 -0
  46. package/lib/engine/context/manager.mjs +182 -0
  47. package/lib/engine/context/real-path.mjs +91 -0
  48. package/lib/engine/context/secret-values.mjs +163 -0
  49. package/lib/engine/context/settings.mjs +274 -0
  50. package/lib/engine/context/stream-input.mjs +159 -0
  51. package/lib/engine/guard.mjs +152 -0
  52. package/lib/engine/hooks.mjs +713 -0
  53. package/lib/engine/loop.mjs +560 -0
  54. package/lib/engine/mcp/client.mjs +254 -0
  55. package/lib/engine/mcp/config.mjs +301 -0
  56. package/lib/engine/mcp/http.mjs +201 -0
  57. package/lib/engine/mcp/index.mjs +146 -0
  58. package/lib/engine/mcp/jsonrpc.mjs +147 -0
  59. package/lib/engine/mcp/naming.mjs +66 -0
  60. package/lib/engine/mcp/resources.mjs +89 -0
  61. package/lib/engine/mcp/results.mjs +133 -0
  62. package/lib/engine/mcp/stdio.mjs +137 -0
  63. package/lib/engine/mcp/supervisor.mjs +116 -0
  64. package/lib/engine/messages.mjs +104 -0
  65. package/lib/engine/output/json.mjs +164 -0
  66. package/lib/engine/output/stream-json.mjs +266 -0
  67. package/lib/engine/permissions.mjs +845 -0
  68. package/lib/engine/process-identity.mjs +164 -0
  69. package/lib/engine/process-tree.mjs +551 -0
  70. package/lib/engine/prompt.mjs +60 -0
  71. package/lib/engine/session/store.mjs +299 -0
  72. package/lib/engine/session-runtime/args.mjs +97 -0
  73. package/lib/engine/session-runtime/host.mjs +143 -0
  74. package/lib/engine/session-runtime/inbox.mjs +122 -0
  75. package/lib/engine/session-runtime/notifications.mjs +129 -0
  76. package/lib/engine/session-runtime/registry.mjs +328 -0
  77. package/lib/engine/session-runtime/runner.mjs +344 -0
  78. package/lib/engine/session-runtime/socket.mjs +212 -0
  79. package/lib/engine/session-runtime/wakeup.mjs +115 -0
  80. package/lib/engine/skills/index.mjs +321 -0
  81. package/lib/engine/tools/bash-background.mjs +533 -0
  82. package/lib/engine/tools/bash.mjs +216 -0
  83. package/lib/engine/tools/edit.mjs +97 -0
  84. package/lib/engine/tools/glob.mjs +81 -0
  85. package/lib/engine/tools/grep.mjs +224 -0
  86. package/lib/engine/tools/index.mjs +84 -0
  87. package/lib/engine/tools/list-agents.mjs +32 -0
  88. package/lib/engine/tools/ls.mjs +127 -0
  89. package/lib/engine/tools/monitor.mjs +82 -0
  90. package/lib/engine/tools/notebook-edit.mjs +218 -0
  91. package/lib/engine/tools/read.mjs +103 -0
  92. package/lib/engine/tools/schedule-wakeup.mjs +45 -0
  93. package/lib/engine/tools/schema.mjs +144 -0
  94. package/lib/engine/tools/send-message.mjs +77 -0
  95. package/lib/engine/tools/session.mjs +70 -0
  96. package/lib/engine/tools/todo.mjs +144 -0
  97. package/lib/engine/tools/toolsearch.mjs +217 -0
  98. package/lib/engine/tools/walk.mjs +193 -0
  99. package/lib/engine/tools/web-switch.mjs +31 -0
  100. package/lib/engine/tools/webfetch-html.mjs +387 -0
  101. package/lib/engine/tools/webfetch-net.mjs +340 -0
  102. package/lib/engine/tools/webfetch.mjs +198 -0
  103. package/lib/engine/tools/websearch.mjs +91 -0
  104. package/lib/engine/tools/workflow.mjs +95 -0
  105. package/lib/engine/tools/write.mjs +76 -0
  106. package/lib/engine/tui/line-editor.mjs +137 -0
  107. package/lib/engine/tui/render.mjs +86 -0
  108. package/lib/engine/tui/tui.mjs +274 -0
  109. package/lib/engine/wire/anthropic-messages.mjs +263 -0
  110. package/lib/engine/wire/effort.mjs +36 -0
  111. package/lib/engine/wire/errors.mjs +496 -0
  112. package/lib/engine/wire/http.mjs +441 -0
  113. package/lib/engine/wire/index.mjs +76 -0
  114. package/lib/engine/wire/openai-chat.mjs +332 -0
  115. package/lib/engine/wire/prompt-cache.mjs +79 -0
  116. package/lib/engine/wire/search.mjs +140 -0
  117. package/lib/engine/wire/sse.mjs +114 -0
  118. package/lib/engine/wire/stall.mjs +349 -0
  119. package/lib/engine/wire/token-provider.mjs +175 -0
  120. package/lib/engine/wire/usage.mjs +192 -0
  121. package/lib/engine/workflow/host.mjs +524 -0
  122. package/lib/engine/workflow/journal.mjs +188 -0
  123. package/lib/engine/workflow/json-schema.mjs +171 -0
  124. package/lib/engine/workflow/meta.mjs +329 -0
  125. package/lib/engine/workflow/notifications.mjs +52 -0
  126. package/lib/engine/workflow/runtime.mjs +447 -0
  127. package/lib/engine/workflow/sandbox.mjs +534 -0
  128. package/lib/engine/workflow/worker.mjs +141 -0
  129. package/lib/engine/workflow/worktree.mjs +74 -0
  130. package/lib/execution/disposition.mjs +1 -1
  131. package/lib/execution/intake.mjs +10 -0
  132. package/lib/execution/surface-policy.mjs +15 -0
  133. package/lib/learning/curator.mjs +8 -6
  134. package/lib/learning/reflect.mjs +8 -6
  135. package/lib/model-router/catalog/cohort.yaml +137 -0
  136. package/lib/model-router/catalog.mjs +118 -1
  137. package/lib/model-router/failover.mjs +67 -16
  138. package/lib/model-router/llm-task.mjs +39 -3
  139. package/lib/model-router/resolve.mjs +89 -3
  140. package/lib/model-router/spawn.mjs +46 -47
  141. package/lib/model-router/taxonomy.mjs +126 -4
  142. package/lib/org/cost-sync.mjs +141 -11
  143. package/lib/org/inbound/broadcast.mjs +289 -0
  144. package/lib/org/inbound/collective.mjs +375 -0
  145. package/lib/org/inbound/directedness.mjs +96 -8
  146. package/lib/org/inbound/facts.mjs +78 -2
  147. package/lib/org/inbound/project.mjs +22 -0
  148. package/lib/org/inbound/surfaces.mjs +14 -0
  149. package/lib/org/llm-token.mjs +879 -0
  150. package/lib/org/mesh.mjs +61 -0
  151. package/lib/org/messaging.mjs +3 -1
  152. package/lib/org/protocol.checksum +1 -1
  153. package/lib/org/protocol.mjs +15 -0
  154. package/lib/org/quota.mjs +520 -0
  155. package/lib/org/tool-surface.mjs +104 -16
  156. package/lib/org/ui-parity.mjs +16 -1
  157. package/lib/org/work-ledger.mjs +37 -6
  158. package/lib/rate-guard.mjs +114 -1
  159. package/lib/resource-governor.mjs +41 -6
  160. package/lib/runtime/adapter.mjs +833 -0
  161. package/lib/runtime/child-env.mjs +191 -0
  162. package/lib/runtime/legacy-shell-guard.mjs +97 -0
  163. package/lib/runtime/seat-engine.mjs +162 -0
  164. package/lib/session/ask-ledger.mjs +271 -0
  165. package/lib/session/current-work.mjs +676 -0
  166. package/lib/session/feed-core.mjs +40 -3
  167. package/lib/session/launch-args.mjs +56 -4
  168. package/lib/session/status-summary.mjs +26 -9
  169. package/lib/session/upgrade-notice.mjs +42 -0
  170. package/lib/setup/claude-probe.mjs +117 -13
  171. package/lib/setup/enrich.mjs +13 -10
  172. package/lib/setup/sections/model.mjs +39 -13
  173. package/lib/telemetry/collect.mjs +229 -11
  174. package/lib/upgrade/ignored-drift.mjs +105 -0
  175. package/lib/voice/post-call-brief.mjs +30 -17
  176. package/package.json +13 -3
  177. package/plugins/maestro-skills/skills/board-work.md +5 -0
  178. package/plugins/maestro-skills/skills/inbound-triage.md +56 -15
  179. package/plugins/maestro-skills/skills/main-session.md +18 -7
  180. package/scaffold/config/collective.yaml +7 -0
  181. package/scripts/ci/check-durable-write-seam.mjs +3 -1
  182. package/scripts/ci/check-tarball-fidelity.mjs +126 -2
  183. package/scripts/ci/run-tests.mjs +47 -19
  184. package/scripts/cohort-llm/api-key-helper.mjs +92 -0
  185. package/scripts/collective/hook-runner.mjs +142 -19
  186. package/scripts/continuous-monitor.sh +13 -0
  187. package/scripts/cost/track-claude-usage.mjs +15 -0
  188. package/scripts/daemon/agent-daemon.mjs +408 -20
  189. package/scripts/daemon/assurance.mjs +48 -12
  190. package/scripts/daemon/cadence-consumer.mjs +218 -68
  191. package/scripts/daemon/cadence-handlers.mjs +73 -4
  192. package/scripts/daemon/classifier.mjs +75 -26
  193. package/scripts/daemon/context-compiler.mjs +51 -37
  194. package/scripts/daemon/deliver.mjs +30 -1
  195. package/scripts/daemon/dispatcher.mjs +595 -149
  196. package/scripts/daemon/health.mjs +14 -1
  197. package/scripts/daemon/maestro-daemon.mjs +11 -0
  198. package/scripts/daemon/prompt-builder.mjs +24 -0
  199. package/scripts/daemon/responder.mjs +246 -79
  200. package/scripts/daemon/sdk-version.mjs +98 -16
  201. package/scripts/eval/probe-gateway.mjs +635 -0
  202. package/scripts/eval/replay/extract.mjs +270 -0
  203. package/scripts/eval/replay/grade.mjs +260 -0
  204. package/scripts/eval/replay/lib/config.mjs +50 -0
  205. package/scripts/eval/replay/lib/effects.mjs +65 -0
  206. package/scripts/eval/replay/lib/fixture.mjs +188 -0
  207. package/scripts/eval/replay/lib/judge.mjs +72 -0
  208. package/scripts/eval/replay/lib/redact.mjs +136 -0
  209. package/scripts/eval/replay/lib/sandbox.mjs +170 -0
  210. package/scripts/eval/replay/lib/schema-check.mjs +63 -0
  211. package/scripts/eval/replay/lib/transcript.mjs +76 -0
  212. package/scripts/eval/replay/mcp-replay-stub.mjs +101 -0
  213. package/scripts/eval/replay/report.mjs +185 -0
  214. package/scripts/eval/replay/run.mjs +404 -0
  215. package/scripts/fleet/rollout.mjs +1151 -0
  216. package/scripts/hooks/pre-send-audit.sh +36 -245
  217. package/scripts/hooks/pre-write-yaml-validate.mjs +275 -0
  218. package/scripts/hooks/validate-state-yaml.sh +190 -0
  219. package/scripts/huddle/huddle-llm.mjs +361 -0
  220. package/scripts/huddle/huddle-server.mjs +46 -121
  221. package/scripts/local-triggers/autoupdate.sh +465 -81
  222. package/scripts/local-triggers/run-trigger.sh +13 -0
  223. package/scripts/maintenance/pin-integrity.mjs +364 -0
  224. package/scripts/poll-slack-events.sh +41 -9
  225. package/scripts/poller/slack-socket-mode.mjs +28 -3
  226. package/scripts/session/supervisor.mjs +80 -13
  227. package/scripts/spawn-session.sh +13 -0
  228. package/bin/maestro.test.mjs +0 -1574
  229. package/lib/action-executor.test.mjs +0 -871
  230. package/lib/archetype.test.mjs +0 -132
  231. package/lib/assurance/plan-note.test.mjs +0 -234
  232. package/lib/assurance/room-budget.test.mjs +0 -486
  233. package/lib/assurance/tier.test.mjs +0 -174
  234. package/lib/autonomy.test.mjs +0 -66
  235. package/lib/backlog.test.mjs +0 -302
  236. package/lib/backup/policy.test.mjs +0 -305
  237. package/lib/budget-escalate.test.mjs +0 -232
  238. package/lib/budget-guard.envelope.test.mjs +0 -476
  239. package/lib/budget-guard.test.mjs +0 -427
  240. package/lib/cadence-bus-requeue.test.mjs +0 -83
  241. package/lib/cadence-bus-schedule.test.mjs +0 -194
  242. package/lib/cadence-bus.test.mjs +0 -720
  243. package/lib/cadences.test.mjs +0 -230
  244. package/lib/capability/inventory.test.mjs +0 -232
  245. package/lib/capability.test.mjs +0 -78
  246. package/lib/channels/base-adapter.test.mjs +0 -590
  247. package/lib/channels/channels.test.mjs +0 -371
  248. package/lib/channels/contract.test.mjs +0 -162
  249. package/lib/channels/inbox-item.test.mjs +0 -368
  250. package/lib/channels/orgmail/adapter.test.mjs +0 -448
  251. package/lib/channels/pairing.test.mjs +0 -270
  252. package/lib/channels/repeat-suppressor.test.mjs +0 -134
  253. package/lib/channels/slack-adapter.test.mjs +0 -212
  254. package/lib/channels/telegram-adapter.test.mjs +0 -306
  255. package/lib/channels/voice/adapter.test.mjs +0 -278
  256. package/lib/channels/whatsapp/adapter-baileys.test.mjs +0 -359
  257. package/lib/channels/whatsapp/baileys-typing.test.mjs +0 -154
  258. package/lib/charter.test.mjs +0 -89
  259. package/lib/claude-bin.test.mjs +0 -131
  260. package/lib/cli/board.test.mjs +0 -227
  261. package/lib/cli/design.test.mjs +0 -270
  262. package/lib/cli/doctor-checks.test.mjs +0 -336
  263. package/lib/cli/global-setup-extras.test.mjs +0 -462
  264. package/lib/cli/inbox.test.mjs +0 -230
  265. package/lib/cli/session-ack.test.mjs +0 -63
  266. package/lib/cli/session.test.mjs +0 -613
  267. package/lib/collective/capture.test.mjs +0 -121
  268. package/lib/collective/cards.test.mjs +0 -114
  269. package/lib/collective/config.test.mjs +0 -123
  270. package/lib/collective/global-config.test.mjs +0 -220
  271. package/lib/collective/global-skills.test.mjs +0 -126
  272. package/lib/collective/presence.test.mjs +0 -95
  273. package/lib/collective/recall.test.mjs +0 -116
  274. package/lib/collective/vendor-skills.test.mjs +0 -306
  275. package/lib/comms/send-gate.test.mjs +0 -770
  276. package/lib/comms.test.mjs +0 -41
  277. package/lib/context/budget.test.mjs +0 -252
  278. package/lib/context/history-scope.test.mjs +0 -79
  279. package/lib/cost/ledger-row.test.mjs +0 -183
  280. package/lib/design/design-md.test.mjs +0 -318
  281. package/lib/design/fixtures/DESIGN.golden.md +0 -238
  282. package/lib/design/fixtures/PRODUCT.golden.md +0 -67
  283. package/lib/design/fixtures/foundation.json +0 -133
  284. package/lib/design/refresh-gate.test.mjs +0 -144
  285. package/lib/design/write.test.mjs +0 -241
  286. package/lib/diagnostics/alerts.test.mjs +0 -318
  287. package/lib/diagnostics/backup-freshness.test.mjs +0 -185
  288. package/lib/diagnostics/counters.test.mjs +0 -206
  289. package/lib/diagnostics/events.test.mjs +0 -290
  290. package/lib/diagnostics/otel.test.mjs +0 -196
  291. package/lib/diagnostics/trace.test.mjs +0 -251
  292. package/lib/env-compat.test.mjs +0 -104
  293. package/lib/execution/disposition.test.mjs +0 -553
  294. package/lib/execution/drive.test.mjs +0 -270
  295. package/lib/execution/effects.test.mjs +0 -344
  296. package/lib/execution/intake.test.mjs +0 -389
  297. package/lib/execution/journal.test.mjs +0 -261
  298. package/lib/execution/match.test.mjs +0 -235
  299. package/lib/execution/pipeline.test.mjs +0 -392
  300. package/lib/execution/route.test.mjs +0 -186
  301. package/lib/execution/surface-policy.test.mjs +0 -162
  302. package/lib/fs-atomic.test.mjs +0 -72
  303. package/lib/fs-ownership.test.mjs +0 -158
  304. package/lib/goals/admission.test.mjs +0 -164
  305. package/lib/goals/classify.test.mjs +0 -167
  306. package/lib/goals/collaborate.test.mjs +0 -336
  307. package/lib/goals/gaps.test.mjs +0 -284
  308. package/lib/goals/loop.test.mjs +0 -845
  309. package/lib/hooks/bus.test.mjs +0 -387
  310. package/lib/identity/persona.test.mjs +0 -142
  311. package/lib/kpi-sensors.test.mjs +0 -278
  312. package/lib/kpi.test.mjs +0 -244
  313. package/lib/learning/config.test.mjs +0 -75
  314. package/lib/learning/counters.test.mjs +0 -69
  315. package/lib/learning/curator-consolidate.test.mjs +0 -238
  316. package/lib/learning/curator.test.mjs +0 -106
  317. package/lib/learning/reflect.test.mjs +0 -0
  318. package/lib/learning/session-index.test.mjs +0 -125
  319. package/lib/learning/skill-writer.test.mjs +0 -210
  320. package/lib/mandate/audit.test.mjs +0 -195
  321. package/lib/mandate/contract.test.mjs +0 -185
  322. package/lib/mandate/derive.test.mjs +0 -274
  323. package/lib/mandate/model.test.mjs +0 -164
  324. package/lib/mandate/refresh.test.mjs +0 -389
  325. package/lib/mcp/server.test.mjs +0 -426
  326. package/lib/model-router/auth-profiles.test.mjs +0 -580
  327. package/lib/model-router/catalog.test.mjs +0 -385
  328. package/lib/model-router/economics.test.mjs +0 -438
  329. package/lib/model-router/failover.test.mjs +0 -439
  330. package/lib/model-router/health.test.mjs +0 -338
  331. package/lib/model-router/integration-coverage.test.mjs +0 -831
  332. package/lib/model-router/integration.test.mjs +0 -564
  333. package/lib/model-router/ledger.test.mjs +0 -415
  334. package/lib/model-router/llm-task.test.mjs +0 -392
  335. package/lib/model-router/org-credentials.test.mjs +0 -265
  336. package/lib/model-router/pricing-refresh.test.mjs +0 -286
  337. package/lib/model-router/reconcile.test.mjs +0 -316
  338. package/lib/model-router/repair.test.mjs +0 -180
  339. package/lib/model-router/spawn.test.mjs +0 -446
  340. package/lib/model-router/taxonomy.test.mjs +0 -410
  341. package/lib/model-router.test.mjs +0 -1207
  342. package/lib/org/activity.test.mjs +0 -134
  343. package/lib/org/approvals.test.mjs +0 -216
  344. package/lib/org/awareness.test.mjs +0 -159
  345. package/lib/org/board-mine-cache.test.mjs +0 -53
  346. package/lib/org/board.test.mjs +0 -187
  347. package/lib/org/bootstrap-context.test.mjs +0 -153
  348. package/lib/org/client.test.mjs +0 -1206
  349. package/lib/org/cohort-client.test.mjs +0 -126
  350. package/lib/org/cost-sync.test.mjs +0 -153
  351. package/lib/org/doctor.test.mjs +0 -346
  352. package/lib/org/engagement-ledger.test.mjs +0 -112
  353. package/lib/org/engagement.test.mjs +0 -739
  354. package/lib/org/handoff.test.mjs +0 -269
  355. package/lib/org/inbound/directedness.test.mjs +0 -668
  356. package/lib/org/inbound/facts.test.mjs +0 -471
  357. package/lib/org/inbound/hydrate.test.mjs +0 -908
  358. package/lib/org/inbound/index.test.mjs +0 -429
  359. package/lib/org/inbound/project.test.mjs +0 -287
  360. package/lib/org/integration-tools.test.mjs +0 -160
  361. package/lib/org/keys.test.mjs +0 -92
  362. package/lib/org/knowledge.test.mjs +0 -326
  363. package/lib/org/leases.test.mjs +0 -235
  364. package/lib/org/mesh-directives.test.mjs +0 -110
  365. package/lib/org/mesh-integration.test.mjs +0 -127
  366. package/lib/org/mesh.test.mjs +0 -400
  367. package/lib/org/messaging.test.mjs +0 -471
  368. package/lib/org/param-contract.test.mjs +0 -477
  369. package/lib/org/policy.test.mjs +0 -237
  370. package/lib/org/protocol.checksum.test.mjs +0 -90
  371. package/lib/org/protocol.test.mjs +0 -323
  372. package/lib/org/push.test.mjs +0 -792
  373. package/lib/org/registry.test.mjs +0 -100
  374. package/lib/org/resource-tools.test.mjs +0 -361
  375. package/lib/org/tool-access.test.mjs +0 -144
  376. package/lib/org/tool-surface-integration.test.mjs +0 -120
  377. package/lib/org/tool-surface.test.mjs +0 -1268
  378. package/lib/org/typing.test.mjs +0 -291
  379. package/lib/org/ui-parity.test.mjs +0 -560
  380. package/lib/org/verify.test.mjs +0 -194
  381. package/lib/org/work-ledger.test.mjs +0 -273
  382. package/lib/plan/adoption-e2e.test.mjs +0 -366
  383. package/lib/plan/budget-enforcement.test.mjs +0 -400
  384. package/lib/plan/compile.test.mjs +0 -382
  385. package/lib/plan/emit.test.mjs +0 -269
  386. package/lib/plan/explain.test.mjs +0 -188
  387. package/lib/prompts/parallelism.test.mjs +0 -177
  388. package/lib/rag/rag.test.mjs +0 -505
  389. package/lib/rate-guard.test.mjs +0 -272
  390. package/lib/reactive-gate.test.mjs +0 -57
  391. package/lib/render.test.mjs +0 -68
  392. package/lib/resource-governor.test.mjs +0 -488
  393. package/lib/scheduling/dynamic-jobs.test.mjs +0 -344
  394. package/lib/scheduling/jitter.test.mjs +0 -140
  395. package/lib/secrets/broker.test.mjs +0 -280
  396. package/lib/secrets/providers.test.mjs +0 -274
  397. package/lib/security/audit-engine.test.mjs +0 -424
  398. package/lib/security/coerce-args.test.mjs +0 -281
  399. package/lib/security/dangerous-tools.test.mjs +0 -68
  400. package/lib/security/external-content.test.mjs +0 -84
  401. package/lib/security/redact.test.mjs +0 -441
  402. package/lib/security/secret-equal.test.mjs +0 -55
  403. package/lib/session/config.test.mjs +0 -92
  404. package/lib/session/feed-core.test.mjs +0 -198
  405. package/lib/session/first-run.test.mjs +0 -121
  406. package/lib/session/frontdoor.test.mjs +0 -205
  407. package/lib/session/handoffs.test.mjs +0 -183
  408. package/lib/session/identity.test.mjs +0 -180
  409. package/lib/session/inbox-claims.test.mjs +0 -286
  410. package/lib/session/launch-args.test.mjs +0 -157
  411. package/lib/session/liveness.test.mjs +0 -100
  412. package/lib/session/status-summary.test.mjs +0 -118
  413. package/lib/session-permissions.test.mjs +0 -120
  414. package/lib/setup/claude-probe.test.mjs +0 -187
  415. package/lib/setup/completeness.test.mjs +0 -110
  416. package/lib/setup/context-pack.test.mjs +0 -89
  417. package/lib/setup/enrich.test.mjs +0 -115
  418. package/lib/setup/enroll-from-cohort.test.mjs +0 -300
  419. package/lib/setup/integration.test.mjs +0 -162
  420. package/lib/setup/io.test.mjs +0 -77
  421. package/lib/setup/runner.test.mjs +0 -132
  422. package/lib/setup/sections/identity.test.mjs +0 -234
  423. package/lib/setup/sections/inventory.test.mjs +0 -198
  424. package/lib/setup/sections/learning.test.mjs +0 -81
  425. package/lib/setup/sections/mandate.test.mjs +0 -388
  426. package/lib/setup/sections/messaging.test.mjs +0 -127
  427. package/lib/setup/sections/model.test.mjs +0 -240
  428. package/lib/setup/sections/org.test.mjs +0 -346
  429. package/lib/setup/sections/orgmail.test.mjs +0 -118
  430. package/lib/setup/sections/recovery.test.mjs +0 -98
  431. package/lib/setup/sections/subagents.test.mjs +0 -429
  432. package/lib/setup/sections/verify.test.mjs +0 -175
  433. package/lib/setup/sot.test.mjs +0 -81
  434. package/lib/setup/state.test.mjs +0 -115
  435. package/lib/singleton.test.mjs +0 -151
  436. package/lib/subagents/cli.test.mjs +0 -389
  437. package/lib/subagents/client.test.mjs +0 -309
  438. package/lib/subagents/gap.test.mjs +0 -234
  439. package/lib/subagents/lock.test.mjs +0 -248
  440. package/lib/subagents/manifest.test.mjs +0 -175
  441. package/lib/subagents/refs.test.mjs +0 -204
  442. package/lib/subagents/resolve.test.mjs +0 -422
  443. package/lib/subagents/schema.test.mjs +0 -328
  444. package/lib/telemetry/alerts.test.mjs +0 -109
  445. package/lib/telemetry/collect.test.mjs +0 -1274
  446. package/lib/tool-definitions-integration.test.mjs +0 -83
  447. package/lib/tool-definitions.test.mjs +0 -437
  448. package/lib/upgrade/global-refresh.test.mjs +0 -65
  449. package/lib/upgrade/launchd-reconcile.test.mjs +0 -272
  450. package/lib/upgrade/post-steps.test.mjs +0 -200
  451. package/lib/upgrade/verify.test.mjs +0 -164
  452. package/lib/util/fetch-timeout.test.mjs +0 -202
  453. package/lib/util/reconnect.test.mjs +0 -369
  454. package/lib/util/unhandled.test.mjs +0 -216
  455. package/lib/voice/outbound.test.mjs +0 -69
  456. package/lib/voice/session-rotation.test.mjs +0 -114
  457. package/lib/voice/stt.test.mjs +0 -226
  458. package/lib/voice/voice.test.mjs +0 -990
  459. package/scripts/cadence/enqueue-cadence-tick.test.mjs +0 -187
  460. package/scripts/ci/check-docs-accuracy.test.mjs +0 -409
  461. package/scripts/ci/check-durable-write-seam.test.mjs +0 -90
  462. package/scripts/ci/check-no-build-artifacts.test.mjs +0 -71
  463. package/scripts/ci/check-no-residual-identity.test.mjs +0 -202
  464. package/scripts/ci/check-skill-packs.test.mjs +0 -495
  465. package/scripts/ci/check-subagent-frontmatter.test.mjs +0 -124
  466. package/scripts/ci/check.test.mjs +0 -194
  467. package/scripts/ci/conformance-org-api.test.mjs +0 -425
  468. package/scripts/cloud-relay/voice/relay-identity.test.mjs +0 -96
  469. package/scripts/collective/hook-runner.test.mjs +0 -173
  470. package/scripts/cost/fleet-digest.test.mjs +0 -207
  471. package/scripts/cost/track-claude-usage-pricing.test.mjs +0 -183
  472. package/scripts/cost/track-claude-usage.test.mjs +0 -148
  473. package/scripts/daemon/agent-daemon-board-mine.test.mjs +0 -96
  474. package/scripts/daemon/agent-daemon-design.test.mjs +0 -238
  475. package/scripts/daemon/agent-daemon-frontdoor.test.mjs +0 -60
  476. package/scripts/daemon/agent-daemon.test.mjs +0 -995
  477. package/scripts/daemon/assurance-e2e.test.mjs +0 -613
  478. package/scripts/daemon/assurance.test.mjs +0 -1791
  479. package/scripts/daemon/board-mirror.test.mjs +0 -165
  480. package/scripts/daemon/cadence-consumer-frontdoor.test.mjs +0 -393
  481. package/scripts/daemon/cadence-consumer-governance.test.mjs +0 -276
  482. package/scripts/daemon/cadence-consumer.test.mjs +0 -776
  483. package/scripts/daemon/cadence-handlers.test.mjs +0 -837
  484. package/scripts/daemon/classifier-identity.test.mjs +0 -137
  485. package/scripts/daemon/classifier.test.mjs +0 -266
  486. package/scripts/daemon/classify-kind.test.mjs +0 -40
  487. package/scripts/daemon/context-compiler.test.mjs +0 -406
  488. package/scripts/daemon/deliver.test.mjs +0 -564
  489. package/scripts/daemon/dispatcher-cooldown.test.mjs +0 -122
  490. package/scripts/daemon/dispatcher-governance.test.mjs +0 -1013
  491. package/scripts/daemon/dispatcher-resume.test.mjs +0 -166
  492. package/scripts/daemon/dispatcher-session-continuity.test.mjs +0 -365
  493. package/scripts/daemon/execution-ladder.test.mjs +0 -470
  494. package/scripts/daemon/goal-steward-cadence.test.mjs +0 -312
  495. package/scripts/daemon/inbox-deferral-session.test.mjs +0 -49
  496. package/scripts/daemon/inbox-deferral.test.mjs +0 -336
  497. package/scripts/daemon/inbox-wake.test.mjs +0 -199
  498. package/scripts/daemon/integration.test.mjs +0 -149
  499. package/scripts/daemon/lib/self-echo.test.mjs +0 -153
  500. package/scripts/daemon/lib/session-router.test.mjs +0 -554
  501. package/scripts/daemon/prompt-builder-preamble.test.mjs +0 -210
  502. package/scripts/daemon/prompt-builder.test.mjs +0 -556
  503. package/scripts/daemon/responder-cost.test.mjs +0 -68
  504. package/scripts/daemon/responder-history.test.mjs +0 -221
  505. package/scripts/daemon/sdk-version.test.mjs +0 -31
  506. package/scripts/daemon/session-lock.test.mjs +0 -252
  507. package/scripts/daemon/session-outcomes.test.mjs +0 -533
  508. package/scripts/daemon/typing-registry.test.mjs +0 -102
  509. package/scripts/hooks/pre-send-audit.test.mjs +0 -354
  510. package/scripts/huddle/huddle-prompt.test.mjs +0 -176
  511. package/scripts/local-triggers/autoupdate.test.mjs +0 -518
  512. package/scripts/local-triggers/generate-plists.test.mjs +0 -456
  513. package/scripts/media-generation/brand-clause.test.mjs +0 -135
  514. package/scripts/org/send-orgmail.first-contact.test.mjs +0 -102
  515. package/scripts/poller/inbox-privilege-injection.test.mjs +0 -167
  516. package/scripts/poller/inbox-scan-poller.test.mjs +0 -295
  517. package/scripts/poller/lib/cloud-relay-dedup.test.mjs +0 -133
  518. package/scripts/poller/slack-socket-mode.test.mjs +0 -805
  519. package/scripts/poller-launchd/install.test.mjs +0 -243
  520. package/scripts/restore-from-backup.test.mjs +0 -181
  521. package/scripts/session/feed.test.mjs +0 -196
  522. package/scripts/session/supervisor-sh.test.mjs +0 -218
  523. package/scripts/session/supervisor.test.mjs +0 -482
  524. package/scripts/setup/configure-macos.test.mjs +0 -306
  525. package/scripts/setup/gen-subagent-manifest.test.mjs +0 -124
  526. package/scripts/setup/generate-agent-package-json.test.mjs +0 -143
  527. package/scripts/setup/generate-capability.test.mjs +0 -134
  528. package/scripts/setup/init-agent.test.mjs +0 -370
  529. package/scripts/setup/init-skill-marketplace.test.mjs +0 -193
  530. package/scripts/vendor/sync-skill-packs.test.mjs +0 -103
  531. package/scripts/watchdog/memory-watchdog.test.mjs +0 -64
@@ -84,7 +84,21 @@
84
84
  * // Also on `machine` (open record), so hq's fleet view can show it next
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
- * upgrade?: { at: ISO8601, from: string, to: string, ok: boolean },
87
+ * upgrade?: { at: ISO8601, from: string, to: string, ok: boolean,
88
+ * reason?: string, healthy?: boolean }, // reason: WHY a failure failed
89
+ * // THE BEATING DAEMON — which process is emitting this beat, what code
90
+ * // it is actually running, and the last health-gate verdict on it.
91
+ * // Also on `machine` (open record). `sdkVersion` ABOVE is what is
92
+ * // INSTALLED on the seat; `daemon.sdkVersion` is what the running
93
+ * // process booted on, and the two differ for exactly as long as an
94
+ * // installed upgrade has not been restarted onto. `pid`+`bootAt` make a
95
+ * // crash loop visible org-side (a pid that moves every beat), which is
96
+ * // the failure that hid for six days behind an hourly "up to date".
97
+ * // `dashboardAt` is the stamp of the file these came from, so a reader
98
+ * // can age the claim; it is never a liveness signal on its own.
99
+ * daemon?: { pid?: number, bootAt?: ISO8601, uptimeS?: number,
100
+ * sdkVersion?: string, dashboardAt?: ISO8601,
101
+ * healthy?: boolean, healthReason?: string },
88
102
  * },
89
103
  * claudeAuth: "ok"|"relogin_required"|"unknown",
90
104
  * alerts: [{ id, severity, kind, detail }],
@@ -120,6 +134,7 @@ import { fileURLToPath } from "node:url";
120
134
  import { createRequire } from "node:module";
121
135
  import { writeJsonAtomic } from "../fs-atomic.mjs";
122
136
  import { probeClaude as defaultProbeClaude } from "../setup/claude-probe.mjs";
137
+ import { readSeatEngine } from "../runtime/adapter.mjs";
123
138
 
124
139
  import { list as listPresence } from "../collective/presence.mjs";
125
140
  import { billableUsd } from "../cost/ledger-row.mjs";
@@ -433,18 +448,64 @@ export async function collectMachine(o = {}) {
433
448
  if (Number.isFinite(disk)) machine.diskUsedPct = pct(disk);
434
449
  }
435
450
 
451
+ // IDENTITY (§3.8) — hostname / tailnetIp / sdkVersion: where the seat is and
452
+ // what it runs. Collected FIRST and on its own terms, no longer as a rider on
453
+ // the inventory probe.
454
+ //
455
+ // WHY IT MOVED. It used to be read out of `collectInventory`'s return, inside
456
+ // `if (o.inventory !== false)` and with no try of its own. Three ways that
457
+ // lost the one field a fleet rollout is verified by, none of them about
458
+ // identity: a caller that turned inventory off lost it as a side effect; a
459
+ // `system_profiler` fork that threw took the whole `collectMachine` down with
460
+ // it (`collectStatus` then substitutes a bare `{loadavg, memUsedPct}` floor —
461
+ // the beat still goes, carrying neither the host stats nor the version); and
462
+ // a seat in that state stayed in it, beat after beat, with nothing on the box
463
+ // or in the org saying why. Identity is three cheap, independent reads and it
464
+ // is what an operator is looking for; it should not be able to fail because a
465
+ // hardware probe did. Each field is independently fail-open inside
466
+ // `collectIdentity`, and the try here is the outer guard for the probe seam.
467
+ //
468
+ // The results are handed on to `collectInventory` below so the tailnet probe
469
+ // forks once per beat, not twice.
470
+ let identity = {};
471
+ if (o.identity !== false) {
472
+ try { identity = await collectIdentity(o, osImpl); } catch { identity = {}; }
473
+ if (identity.hostname) machine.hostname = identity.hostname;
474
+ if (identity.tailnetIp) machine.tailnetIp = identity.tailnetIp;
475
+ if (identity.sdkVersion) machine.sdkVersion = identity.sdkVersion;
476
+ }
477
+
436
478
  // INVENTORY — device / ram / cores. Cached on disk (see collectInventory):
437
479
  // the chip and the installed RAM do not change between beats, so the
438
480
  // system_profiler fork happens once per machine, not every 30 seconds.
481
+ //
482
+ // WRAPPED, and the try is not decorative. `collectInventory` is not total:
483
+ // `execFileSafe` swallows a failing FORK, but the reads around it do not all
484
+ // have a try of their own (the platform read at the top of the probe block is
485
+ // the live example), so the function can and does throw. Before identity was
486
+ // hoisted above this, that throw propagated out of `collectMachine` and
487
+ // `collectStatus` substituted its bare `{loadavg, memUsedPct}` floor — the
488
+ // beat went out carrying neither host stats NOR the version, which is how two
489
+ // seats beat for days with no hostname and no sdkVersion. Now it costs its
490
+ // own three fields and nothing else.
439
491
  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;
492
+ try {
493
+ const inv = await collectInventory({
494
+ ...o,
495
+ // Hand the identity answers on so the tailnet probe forks ONCE per
496
+ // beat, not twice. An empty string is an answer ("probed, none
497
+ // found") — and when `identity` is switched off entirely it is the
498
+ // right answer to hand on: a caller that asked for no identity should
499
+ // not pay for a tailnet fork inside the inventory probe either.
500
+ // `collectInventory` still returns those two fields; we ignore them —
501
+ // `machine.hostname/tailnetIp/sdkVersion` are set above or not at all.
502
+ tailnetIp: typeof o.tailnetIp === "string" ? o.tailnetIp : String(identity.tailnetIp || ""),
503
+ sdkVersion: typeof o.sdkVersion === "string" ? o.sdkVersion : String(identity.sdkVersion || ""),
504
+ });
505
+ if (inv.device) machine.device = inv.device;
506
+ if (inv.ram) machine.ram = inv.ram;
507
+ if (Number.isFinite(inv.cores)) machine.cores = inv.cores;
508
+ } catch { /* device/ram/cores drop; identity above already landed */ }
448
509
  }
449
510
 
450
511
  // spend24h — the agent's own rolling 24h LLM spend, off the cost ledger.
@@ -1307,7 +1368,142 @@ export function upgradeSummary(last) {
1307
1368
  const at = typeof last.at === "string" ? last.at : "";
1308
1369
  const to = typeof last.to === "string" ? last.to : "";
1309
1370
  if (!at || !to) return null;
1310
- return { at, from: typeof last.from === "string" ? last.from : "", to, ok: last.ok === true };
1371
+ // `reason` and `healthy` are the two fields autoupdate.sh has always written
1372
+ // to state/autoupdate/last.json and this projection used to drop. Without
1373
+ // them a failed attempt reaches the org as a bare `ok:false` — which is the
1374
+ // state three seats were in on 2026-09-24: provably behind, provably having
1375
+ // tried, and no way to learn WHY short of reading a file on a machine nobody
1376
+ // could reach. "install-failed" and "unhealthy-rolled-back" are different
1377
+ // problems with different fixes and they looked identical from here.
1378
+ //
1379
+ // Bounded on the way out: a reason is a short token plus at most a trimmed
1380
+ // error line, and the beat is not a log shipper.
1381
+ const reason = typeof last.reason === "string" ? last.reason.trim().slice(0, 300) : "";
1382
+ return {
1383
+ at,
1384
+ from: typeof last.from === "string" ? last.from : "",
1385
+ to,
1386
+ ok: last.ok === true,
1387
+ ...(reason ? { reason } : {}),
1388
+ ...(typeof last.healthy === "boolean" ? { healthy: last.healthy } : {}),
1389
+ };
1390
+ }
1391
+
1392
+ // ---------------------------------------------------------------------------
1393
+ // Daemon — WHICH process is beating, what code it is running, and the gate's
1394
+ // last verdict on it (`machine.daemon`)
1395
+ // ---------------------------------------------------------------------------
1396
+
1397
+ /**
1398
+ * Top-level scalars we lift out of state/dashboards/daemon-health.yaml. The file
1399
+ * is written by scripts/daemon/health.mjs as flat `key: value` lines plus one
1400
+ * indented `dispatcher:` JSON block, so a line regex over the UNINDENTED lines
1401
+ * reads it exactly and cannot be confused by the nested block. We do NOT pull a
1402
+ * YAML parser in for four scalars on a 30-second loop.
1403
+ *
1404
+ * Fail-open → null (no file, unreadable, or no recognisable line).
1405
+ * @param {string} text
1406
+ * @returns {{pid?:number, sdkVersion?:string, installedSdkVersion?:string,
1407
+ * versionsAgree?:boolean, versionDrift?:string, bootAt?:string,
1408
+ * updatedAt?:string, uptimeS?:number}|null}
1409
+ */
1410
+ export function parseDaemonHealthYaml(text) {
1411
+ if (typeof text !== "string" || text === "") return null;
1412
+ const out = {};
1413
+ for (const line of text.split("\n")) {
1414
+ // Unindented `key: value` only — an indented line belongs to `dispatcher:`.
1415
+ const m = /^([a-z_]+):[ \t]+(.+?)[ \t]*$/.exec(line);
1416
+ if (!m) continue;
1417
+ const [, k, v] = m;
1418
+ if (k === "pid") { const n = Number(v); if (Number.isInteger(n) && n > 0) out.pid = n; }
1419
+ else if (k === "sdk_version") { const s = v.trim(); if (s && s !== "undefined" && s !== "null") out.sdkVersion = s; }
1420
+ // The three the resolver added so a version can be CHECKED and not merely
1421
+ // read. `sdk_version_agrees` is a tri-state written as a word: "unknown"
1422
+ // is not false and must never collapse to one.
1423
+ else if (k === "sdk_version_installed") { const s = v.trim(); if (s && s !== "undefined" && s !== "null") out.installedSdkVersion = s; }
1424
+ else if (k === "sdk_version_agrees") { const s = v.trim(); if (s === "true" || s === "false") out.versionsAgree = s === "true"; }
1425
+ else if (k === "sdk_version_drift") { const s = v.trim(); if (s && s !== "undefined" && s !== "null") out.versionDrift = s; }
1426
+ else if (k === "daemon_start") { const s = v.trim(); if (!Number.isNaN(Date.parse(s))) out.bootAt = new Date(Date.parse(s)).toISOString(); }
1427
+ else if (k === "updated") { const s = v.trim(); if (!Number.isNaN(Date.parse(s))) out.updatedAt = new Date(Date.parse(s)).toISOString(); }
1428
+ else if (k === "uptime_s") { const n = Number(v); if (Number.isFinite(n) && n >= 0) out.uptimeS = Math.floor(n); }
1429
+ }
1430
+ return Object.keys(out).length > 0 ? out : null;
1431
+ }
1432
+
1433
+ /** state/dashboards/daemon-health.yaml as text, or "" — fail-open. */
1434
+ function readDaemonHealthText(agentRoot) {
1435
+ if (!agentRoot) return "";
1436
+ try {
1437
+ return readFileSync(join(resolve(agentRoot), "state", "dashboards", "daemon-health.yaml"), "utf8");
1438
+ } catch { return ""; }
1439
+ }
1440
+
1441
+ /**
1442
+ * Pure: the beat's `machine.daemon` — WHO is beating and whether the last health
1443
+ * gate believed it.
1444
+ *
1445
+ * WHY THIS FIELD EXISTS. `machine.sdkVersion` is the version INSTALLED on the
1446
+ * seat. It is not the version RUNNING: autoupdate installs into node_modules
1447
+ * while the old daemon process keeps executing the code it booted on, and a
1448
+ * hand-run `maestro upgrade` never restarts the daemon at all. One seat logged
1449
+ * "up to date" hourly for six days while its daemon crash-looped on the
1450
+ * previous version ~25 s at a time; the org saw nothing, because nothing the
1451
+ * beat carried could tell the two versions apart. `pid` + `bootAt` make the
1452
+ * distinction visible org-side: a pid that changes every beat is a crash loop,
1453
+ * and `sdkVersion` here differing from `machine.sdkVersion` is an installed-but-
1454
+ * not-restarted seat. Neither needs an ssh tour to see any more.
1455
+ *
1456
+ * ADDITIVE AND SMALL — six scalars, every one optional. It rides `machine`,
1457
+ * which hq validates as an OPEN record (`presence/beat.ts#statusSchema`), so it
1458
+ * lands the moment the daemon emits it and renders when the reader learns it.
1459
+ * It is NOT put on `session`, whose hq schema is `.strict()` and would refuse
1460
+ * the whole beat.
1461
+ *
1462
+ * FRESHNESS. `pid`/`bootAt`/`sdkVersion` describe the process that wrote the
1463
+ * dashboard, which a DEAD daemon leaves behind verbatim. `dashboardAt` is that
1464
+ * file's own stamp, carried so a reader can age the claim rather than trust it;
1465
+ * the ledger beat's server-stamped `at` remains the only liveness signal.
1466
+ *
1467
+ * @param {object|null} dash parsed daemon-health.yaml scalars, or null
1468
+ * @param {object|null} last state/autoupdate/last.json, or null
1469
+ * THE SEAT'S OWN VERDICT RIDES ALONG. `installedSdkVersion` + `versionsAgree`
1470
+ * are what the daemon's resolver concluded on the box, where both files are
1471
+ * readable; hq compares `machine.sdkVersion` against `machine.daemon.sdkVersion`
1472
+ * and reaches the same conclusion from the outside. Carrying both is not
1473
+ * redundancy — they are read at different instants from different files, and a
1474
+ * reader that sees them disagree has learned something real. `versionsAgree` is
1475
+ * carried ONLY as a real boolean: an inferred running version yields no verdict
1476
+ * at all, and absence must never read as agreement.
1477
+ *
1478
+ * @returns {{pid?:number, bootAt?:string, uptimeS?:number, sdkVersion?:string,
1479
+ * installedSdkVersion?:string, versionsAgree?:boolean,
1480
+ * versionDrift?:string, dashboardAt?:string, healthy?:boolean,
1481
+ * healthReason?:string}|null}
1482
+ */
1483
+ export function daemonSummary(dash, last) {
1484
+ const out = {};
1485
+ if (dash && typeof dash === "object") {
1486
+ if (Number.isInteger(dash.pid) && dash.pid > 0) out.pid = dash.pid;
1487
+ if (typeof dash.bootAt === "string" && dash.bootAt) out.bootAt = dash.bootAt;
1488
+ if (Number.isFinite(dash.uptimeS) && dash.uptimeS >= 0) out.uptimeS = Math.floor(dash.uptimeS);
1489
+ if (typeof dash.sdkVersion === "string" && dash.sdkVersion) out.sdkVersion = dash.sdkVersion;
1490
+ if (typeof dash.installedSdkVersion === "string" && dash.installedSdkVersion) out.installedSdkVersion = dash.installedSdkVersion;
1491
+ if (typeof dash.versionsAgree === "boolean") out.versionsAgree = dash.versionsAgree;
1492
+ if (dash.versionsAgree === false && typeof dash.versionDrift === "string" && dash.versionDrift) out.versionDrift = dash.versionDrift.slice(0, 200);
1493
+ if (typeof dash.updatedAt === "string" && dash.updatedAt) out.dashboardAt = dash.updatedAt;
1494
+ }
1495
+ // The health GATE's verdict, as autoupdate.sh last recorded it
1496
+ // (`{from,to,at,ok,healthy,reason}`). `healthy` is the gate's answer about the
1497
+ // DAEMON — pid stability, no fatal line since boot, a server-acknowledged
1498
+ // beat — which is a different question from `upgrade.ok` (did the last install
1499
+ // land). Only a real boolean is carried: a record written before the gate
1500
+ // existed has no opinion, and absence must not read as "healthy".
1501
+ if (last && typeof last === "object" && typeof last.healthy === "boolean") {
1502
+ out.healthy = last.healthy;
1503
+ const reason = typeof last.reason === "string" ? last.reason.trim() : "";
1504
+ if (!last.healthy && reason) out.healthReason = reason.slice(0, 200);
1505
+ }
1506
+ return Object.keys(out).length > 0 ? out : null;
1311
1507
  }
1312
1508
 
1313
1509
  /** state/autoupdate/last.json, or null. */
@@ -1570,7 +1766,12 @@ function subscriptionModeFromEnv(agentRoot, env) {
1570
1766
  function probeAuthForBeat(o, nowMs) {
1571
1767
  if (o.probeClaude === false) return "unknown";
1572
1768
  const agentRoot = resolve(o.agentRoot);
1573
- if (!subscriptionModeFromEnv(agentRoot, o.env || process.env)) return "unknown";
1769
+ // W4 integration (row 25): an engine-cohort seat's probe is the engine's `auth status` — a
1770
+ // real call to the gateway that proves the seat credential — so it is asked whatever the
1771
+ // seat's Claude env says. A claude seat still probes only on the subscription path.
1772
+ let cohortSeat = false;
1773
+ try { cohortSeat = (o.seatEngine || readSeatEngine)(agentRoot) === "cohort"; } catch { cohortSeat = false; /* unreadable config: the default engine */ }
1774
+ if (!cohortSeat && !subscriptionModeFromEnv(agentRoot, o.env || process.env)) return "unknown";
1574
1775
  const injected = typeof o.probeClaude === "function";
1575
1776
  if (!injected) {
1576
1777
  const hit = _authProbeMemo.get(agentRoot);
@@ -1737,6 +1938,21 @@ export async function collectStatus(o = {}) {
1737
1938
  if (up) machine.upgrade = up;
1738
1939
  } catch { /* no upgrade field */ }
1739
1940
 
1941
+ // 4d. WHO is beating — `machine.daemon` {pid, bootAt, uptimeS, sdkVersion,
1942
+ // dashboardAt, healthy?, healthReason?}. This is what makes fleet version
1943
+ // drift a QUERY instead of an ssh tour: `machine.sdkVersion` is what is
1944
+ // INSTALLED, `machine.daemon.sdkVersion` is what is RUNNING, and a pid that
1945
+ // moves every beat is a crash loop. Fail-open in the same way every other
1946
+ // probe here is — a throw drops the field, never the beat.
1947
+ try {
1948
+ const dash = opt.daemonDashboard !== undefined
1949
+ ? opt.daemonDashboard
1950
+ : parseDaemonHealthYaml(readDaemonHealthText(opt.agentRoot));
1951
+ const last = opt.autoupdateLast !== undefined ? opt.autoupdateLast : readAutoupdateLast(opt.agentRoot);
1952
+ const d = daemonSummary(dash, last);
1953
+ if (d) machine.daemon = d;
1954
+ } catch { /* no daemon field */ }
1955
+
1740
1956
  const status = {
1741
1957
  state,
1742
1958
  activity: act.activity || "idle",
@@ -1782,6 +1998,8 @@ export const _internals = {
1782
1998
  sessionJobInstalled,
1783
1999
  readSessionAttention,
1784
2000
  upgradeSummary,
2001
+ parseDaemonHealthYaml,
2002
+ daemonSummary,
1785
2003
  detectClaudeAuth,
1786
2004
  resetAuthProbeCache,
1787
2005
  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.5",
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,