harnery 0.29.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (512) hide show
  1. package/README.md +4 -4
  2. package/dist/commander.d.ts +8 -0
  3. package/dist/commander.d.ts.map +1 -1
  4. package/dist/commander.js +14 -13
  5. package/dist/commands/adapter.d.ts +12 -0
  6. package/dist/commands/adapter.d.ts.map +1 -0
  7. package/dist/commands/{harness.js → adapter.js} +35 -35
  8. package/dist/commands/agents.d.ts +3 -2
  9. package/dist/commands/agents.d.ts.map +1 -1
  10. package/dist/commands/agents.js +61 -60
  11. package/dist/commands/artifacts.d.ts +4 -0
  12. package/dist/commands/artifacts.d.ts.map +1 -0
  13. package/dist/commands/artifacts.js +143 -0
  14. package/dist/commands/backup.d.ts +1 -1
  15. package/dist/commands/backup.d.ts.map +1 -1
  16. package/dist/commands/backup.js +5 -2
  17. package/dist/commands/browse.d.ts.map +1 -1
  18. package/dist/commands/browse.js +310 -7
  19. package/dist/commands/checkpoint.d.ts +12 -0
  20. package/dist/commands/checkpoint.d.ts.map +1 -0
  21. package/dist/commands/checkpoint.js +162 -0
  22. package/dist/commands/context.d.ts +2 -2
  23. package/dist/commands/context.d.ts.map +1 -1
  24. package/dist/commands/context.js +3 -151
  25. package/dist/commands/decision.d.ts.map +1 -1
  26. package/dist/commands/decision.js +31 -7
  27. package/dist/commands/deinit.d.ts +2 -2
  28. package/dist/commands/deinit.js +15 -15
  29. package/dist/commands/doctor.js +23 -23
  30. package/dist/commands/governor.d.ts +4 -0
  31. package/dist/commands/governor.d.ts.map +1 -0
  32. package/dist/commands/{supervisor.js → governor.js} +133 -108
  33. package/dist/commands/init.d.ts +12 -12
  34. package/dist/commands/init.d.ts.map +1 -1
  35. package/dist/commands/init.js +44 -30
  36. package/dist/commands/{session.d.ts → journal.d.ts} +2 -2
  37. package/dist/commands/journal.d.ts.map +1 -0
  38. package/dist/commands/{scratch.js → journal.js} +47 -47
  39. package/dist/commands/policy.js +1 -1
  40. package/dist/commands/presence.js +2 -2
  41. package/dist/commands/sync.d.ts +1 -1
  42. package/dist/commands/sync.js +3 -3
  43. package/dist/commands/tunnel.d.ts +16 -0
  44. package/dist/commands/tunnel.d.ts.map +1 -1
  45. package/dist/commands/tunnel.js +193 -23
  46. package/dist/commands/web-fetch.js +1 -1
  47. package/dist/commands/web.d.ts +19 -0
  48. package/dist/commands/web.d.ts.map +1 -1
  49. package/dist/commands/web.js +51 -0
  50. package/dist/commands/work.d.ts +2 -1
  51. package/dist/commands/work.d.ts.map +1 -1
  52. package/dist/commands/work.js +55 -25
  53. package/dist/commands/workflow.d.ts.map +1 -1
  54. package/dist/commands/workflow.js +35 -28
  55. package/dist/core/{harnesses → adapters}/attest-projection.d.ts +2 -2
  56. package/dist/core/adapters/attest-projection.d.ts.map +1 -0
  57. package/dist/core/{harnesses → adapters}/attest.d.ts +16 -16
  58. package/dist/core/adapters/attest.d.ts.map +1 -0
  59. package/dist/core/{harnesses → adapters}/attest.js +9 -9
  60. package/dist/core/{harnesses → adapters}/attestation.d.ts +16 -16
  61. package/dist/core/adapters/attestation.d.ts.map +1 -0
  62. package/dist/core/{harnesses → adapters}/attestation.js +20 -20
  63. package/dist/core/{harnesses → adapters}/bench.d.ts +12 -12
  64. package/dist/core/adapters/bench.d.ts.map +1 -0
  65. package/dist/core/{harnesses → adapters}/bench.js +12 -12
  66. package/dist/core/adapters/index.d.ts +13 -0
  67. package/dist/core/adapters/index.d.ts.map +1 -0
  68. package/dist/core/adapters/index.js +6 -0
  69. package/dist/core/{harnesses → adapters}/profiles.d.ts +11 -11
  70. package/dist/core/adapters/profiles.d.ts.map +1 -0
  71. package/dist/core/{harnesses → adapters}/profiles.js +11 -11
  72. package/dist/core/adapters/registry.d.ts +15 -0
  73. package/dist/core/adapters/registry.d.ts.map +1 -0
  74. package/dist/core/{harnesses → adapters}/registry.js +11 -11
  75. package/dist/core/{harnesses → adapters}/types.d.ts +28 -23
  76. package/dist/core/adapters/types.d.ts.map +1 -0
  77. package/dist/core/{harnesses → adapters}/types.js +1 -1
  78. package/dist/core/agents/canonical-emit.d.ts +4 -4
  79. package/dist/core/agents/canonical-emit.js +5 -5
  80. package/dist/core/agents/cli-emit.js +1 -1
  81. package/dist/core/agents/cli.js +30 -26
  82. package/dist/core/agents/codex-replay.d.ts +1 -1
  83. package/dist/core/agents/codex-replay.js +6 -6
  84. package/dist/core/agents/coord-client.d.ts +21 -2
  85. package/dist/core/agents/coord-client.d.ts.map +1 -1
  86. package/dist/core/agents/coord-client.js +122 -27
  87. package/dist/core/agents/events/consume.d.ts +1 -1
  88. package/dist/core/agents/events/emit.d.ts +5 -6
  89. package/dist/core/agents/events/emit.d.ts.map +1 -1
  90. package/dist/core/agents/events/emit.js +2 -2
  91. package/dist/core/agents/render/prompt-context.d.ts +21 -9
  92. package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
  93. package/dist/core/agents/render/prompt-context.js +82 -30
  94. package/dist/core/agents/render/session-context.d.ts +1 -1
  95. package/dist/core/agents/render/session-context.js +5 -5
  96. package/dist/core/agents/rules/claim-conflict.d.ts +2 -6
  97. package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
  98. package/dist/core/agents/rules/claim-conflict.js +1 -1
  99. package/dist/core/agents/rules/commit-conflict.js +98 -22
  100. package/dist/core/agents/rules/stop-hook.d.ts +9 -9
  101. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
  102. package/dist/core/agents/rules/stop-hook.js +23 -5
  103. package/dist/core/agents/rules/verdict.d.ts +14 -0
  104. package/dist/core/agents/rules/verdict.d.ts.map +1 -0
  105. package/dist/core/agents/rules/verdict.js +1 -0
  106. package/dist/core/agents/session-events.js +3 -3
  107. package/dist/core/agents/state/council.d.ts +1 -1
  108. package/dist/core/agents/state/council.js +1 -1
  109. package/dist/core/agents/state/heartbeat-projector.d.ts +3 -2
  110. package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -1
  111. package/dist/core/agents/state/heartbeat-projector.js +14 -10
  112. package/dist/core/agents/state/heartbeat-writer.d.ts +1 -1
  113. package/dist/core/agents/state/heartbeat-writer.js +12 -12
  114. package/dist/core/agents/state/{scratch.d.ts → journal.d.ts} +7 -7
  115. package/dist/core/agents/state/{scratch.d.ts.map → journal.d.ts.map} +1 -1
  116. package/dist/core/agents/state/{scratch.js → journal.js} +12 -12
  117. package/dist/core/agents/state/pidmap.d.ts +28 -4
  118. package/dist/core/agents/state/pidmap.d.ts.map +1 -1
  119. package/dist/core/agents/state/pidmap.js +130 -15
  120. package/dist/core/agents/state/proc-start.d.ts +95 -0
  121. package/dist/core/agents/state/proc-start.d.ts.map +1 -0
  122. package/dist/core/agents/state/proc-start.js +199 -0
  123. package/dist/core/agents/state/shell-mutation.d.ts +1 -1
  124. package/dist/core/agents/state/shell-mutation.js +1 -1
  125. package/dist/core/agents/state/stale-sweep.js +7 -7
  126. package/dist/core/artifacts/index.d.ts +86 -0
  127. package/dist/core/artifacts/index.d.ts.map +1 -0
  128. package/dist/core/artifacts/index.js +421 -0
  129. package/dist/core/config.d.ts +9 -2
  130. package/dist/core/config.d.ts.map +1 -1
  131. package/dist/core/config.js +26 -2
  132. package/dist/core/context/index.d.ts +12 -12
  133. package/dist/core/context/index.js +10 -10
  134. package/dist/core/governor/adapter-spread.d.ts +52 -0
  135. package/dist/core/governor/adapter-spread.d.ts.map +1 -0
  136. package/dist/core/governor/adapter-spread.js +52 -0
  137. package/dist/core/governor/index.d.ts +7 -0
  138. package/dist/core/governor/index.d.ts.map +1 -0
  139. package/dist/core/governor/index.js +6 -0
  140. package/dist/core/governor/plan-api.d.ts +38 -0
  141. package/dist/core/governor/plan-api.d.ts.map +1 -0
  142. package/dist/core/governor/plan-api.js +83 -0
  143. package/dist/core/governor/plan-read.d.ts +5 -0
  144. package/dist/core/governor/plan-read.d.ts.map +1 -0
  145. package/dist/core/{supervisor → governor}/plan-read.js +71 -51
  146. package/dist/core/governor/plan-types.d.ts +181 -0
  147. package/dist/core/governor/plan-types.d.ts.map +1 -0
  148. package/dist/core/{supervisor → governor}/plan-types.js +4 -4
  149. package/dist/core/governor/planning.d.ts +48 -0
  150. package/dist/core/governor/planning.d.ts.map +1 -0
  151. package/dist/core/{supervisor → governor}/planning.js +143 -94
  152. package/dist/core/governor/read.d.ts +6 -0
  153. package/dist/core/governor/read.d.ts.map +1 -0
  154. package/dist/core/governor/read.js +4 -0
  155. package/dist/core/governor/runner.d.ts +37 -0
  156. package/dist/core/governor/runner.d.ts.map +1 -0
  157. package/dist/core/{supervisor → governor}/runner.js +15 -15
  158. package/dist/core/governor/service-read.d.ts +6 -0
  159. package/dist/core/governor/service-read.d.ts.map +1 -0
  160. package/dist/core/{supervisor → governor}/service-read.js +12 -12
  161. package/dist/core/governor/service.d.ts +114 -0
  162. package/dist/core/governor/service.d.ts.map +1 -0
  163. package/dist/core/{supervisor → governor}/service.js +57 -57
  164. package/dist/core/governor/state.d.ts +116 -0
  165. package/dist/core/governor/state.d.ts.map +1 -0
  166. package/dist/core/{supervisor → governor}/state.js +290 -116
  167. package/dist/core/hooks/adapter/detect.d.ts +9 -0
  168. package/dist/core/hooks/{harness → adapter}/detect.d.ts.map +1 -1
  169. package/dist/core/hooks/{harness → adapter}/detect.js +8 -8
  170. package/dist/core/hooks/{harness → adapter}/events.d.ts +15 -14
  171. package/dist/core/hooks/adapter/events.d.ts.map +1 -0
  172. package/dist/core/hooks/{harness → adapter}/events.js +5 -5
  173. package/dist/core/hooks/{harness → adapter}/output.d.ts +20 -14
  174. package/dist/core/hooks/adapter/output.d.ts.map +1 -0
  175. package/dist/core/hooks/{harness → adapter}/output.js +30 -22
  176. package/dist/core/hooks/{harness → adapter}/parse.d.ts +5 -5
  177. package/dist/core/hooks/{harness → adapter}/parse.d.ts.map +1 -1
  178. package/dist/core/hooks/{harness → adapter}/parse.js +4 -4
  179. package/dist/core/hooks/{harness → adapter}/wiring.d.ts +15 -15
  180. package/dist/core/hooks/{harness → adapter}/wiring.d.ts.map +1 -1
  181. package/dist/core/hooks/{harness → adapter}/wiring.js +13 -13
  182. package/dist/core/hooks/cli.d.ts +3 -3
  183. package/dist/core/hooks/cli.js +175 -151
  184. package/dist/core/hooks/effects/image-capture.d.ts +5 -5
  185. package/dist/core/hooks/effects/image-capture.d.ts.map +1 -1
  186. package/dist/core/hooks/effects/image-capture.js +3 -3
  187. package/dist/core/hooks/effects/index.d.ts +12 -12
  188. package/dist/core/hooks/effects/index.d.ts.map +1 -1
  189. package/dist/core/hooks/effects/index.js +17 -17
  190. package/dist/core/hooks/events/emit.d.ts +2 -2
  191. package/dist/core/hooks/events/emit.d.ts.map +1 -1
  192. package/dist/core/hooks/events/emit.js +1 -1
  193. package/dist/core/hooks/events/rotate.js +1 -1
  194. package/dist/core/hooks/events/schema.d.ts +13 -5
  195. package/dist/core/hooks/events/schema.d.ts.map +1 -1
  196. package/dist/core/hooks/guard-path.d.ts +2 -2
  197. package/dist/core/hooks/guard-path.js +2 -2
  198. package/dist/core/hooks/index.d.ts +1 -1
  199. package/dist/core/hooks/index.d.ts.map +1 -1
  200. package/dist/core/hooks/resolve/anchor.d.ts +14 -5
  201. package/dist/core/hooks/resolve/anchor.d.ts.map +1 -1
  202. package/dist/core/hooks/resolve/anchor.js +64 -7
  203. package/dist/core/hooks/resolve/coord-root.d.ts +3 -3
  204. package/dist/core/hooks/resolve/coord-root.js +4 -4
  205. package/dist/core/hooks/resolve/intent.js +1 -1
  206. package/dist/core/hooks/resolve/owner.d.ts +1 -1
  207. package/dist/core/hooks/resolve/owner.d.ts.map +1 -1
  208. package/dist/core/hooks/resolve/owner.js +11 -5
  209. package/dist/core/{scratch → journal}/index.d.ts +25 -25
  210. package/dist/core/{scratch → journal}/index.d.ts.map +1 -1
  211. package/dist/core/{scratch → journal}/index.js +38 -38
  212. package/dist/core/policy/evaluate.d.ts +1 -1
  213. package/dist/core/policy/evaluate.d.ts.map +1 -1
  214. package/dist/core/policy/evaluate.js +7 -7
  215. package/dist/core/policy/types.d.ts +3 -3
  216. package/dist/core/policy/types.d.ts.map +1 -1
  217. package/dist/core/work/runner.d.ts.map +1 -1
  218. package/dist/core/work/runner.js +11 -2
  219. package/dist/core/work/state.d.ts +28 -3
  220. package/dist/core/work/state.d.ts.map +1 -1
  221. package/dist/core/work/state.js +148 -16
  222. package/dist/core/workflow/adapters.d.ts +17 -0
  223. package/dist/core/workflow/adapters.d.ts.map +1 -0
  224. package/dist/core/workflow/adapters.js +21 -0
  225. package/dist/core/workflow/approvals.js +6 -6
  226. package/dist/core/workflow/billing.d.ts +9 -9
  227. package/dist/core/workflow/billing.js +9 -9
  228. package/dist/core/workflow/child-env.d.ts +10 -2
  229. package/dist/core/workflow/child-env.d.ts.map +1 -1
  230. package/dist/core/workflow/child-env.js +12 -4
  231. package/dist/core/workflow/engine.d.ts +15 -5
  232. package/dist/core/workflow/engine.d.ts.map +1 -1
  233. package/dist/core/workflow/engine.js +151 -83
  234. package/dist/core/workflow/evidence-preflight.d.ts +24 -0
  235. package/dist/core/workflow/evidence-preflight.d.ts.map +1 -0
  236. package/dist/core/workflow/evidence-preflight.js +134 -0
  237. package/dist/core/workflow/index.d.ts +3 -2
  238. package/dist/core/workflow/index.d.ts.map +1 -1
  239. package/dist/core/workflow/index.js +2 -1
  240. package/dist/core/workflow/proof.d.ts +28 -5
  241. package/dist/core/workflow/proof.d.ts.map +1 -1
  242. package/dist/core/workflow/proof.js +77 -34
  243. package/dist/core/workflow/run-state.d.ts +1 -1
  244. package/dist/core/workflow/run-state.js +7 -7
  245. package/dist/core/workflow/sandbox-projection.d.ts +6 -6
  246. package/dist/core/workflow/sandbox-projection.d.ts.map +1 -1
  247. package/dist/core/workflow/sandbox-projection.js +11 -11
  248. package/dist/core/workflow/spawn-claude.d.ts +3 -3
  249. package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
  250. package/dist/core/workflow/spawn-claude.js +28 -8
  251. package/dist/core/workflow/spawn-codex.d.ts +4 -4
  252. package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
  253. package/dist/core/workflow/spawn-codex.js +26 -8
  254. package/dist/core/workflow/spawn-cursor.d.ts +4 -4
  255. package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
  256. package/dist/core/workflow/spawn-cursor.js +27 -8
  257. package/dist/core/workflow/spawn-failure.d.ts +15 -1
  258. package/dist/core/workflow/spawn-failure.d.ts.map +1 -1
  259. package/dist/core/workflow/spawn-failure.js +35 -1
  260. package/dist/core/workflow/specialists.js +2 -2
  261. package/dist/core/workflow/{journal.d.ts → transcript.d.ts} +8 -8
  262. package/dist/core/workflow/transcript.d.ts.map +1 -0
  263. package/dist/core/workflow/{journal.js → transcript.js} +14 -14
  264. package/dist/core/workflow/types.d.ts +119 -46
  265. package/dist/core/workflow/types.d.ts.map +1 -1
  266. package/dist/core/workflow/types.js +11 -1
  267. package/dist/core/workflow/validate.d.ts +1 -1
  268. package/dist/core/workflow/validate.js +1 -1
  269. package/dist/core/workflow/workspaces/cancellation.js +2 -2
  270. package/dist/core/workflow/workspaces/cleanup.js +2 -2
  271. package/dist/core/workflow/workspaces/execution.js +6 -6
  272. package/dist/core/workflow/workspaces/inspect.js +9 -9
  273. package/dist/core/workflow/workspaces/integration.js +14 -14
  274. package/dist/core/workflow/workspaces/lifecycle.d.ts +1 -1
  275. package/dist/core/workflow/workspaces/lifecycle.d.ts.map +1 -1
  276. package/dist/core/workflow/workspaces/lifecycle.js +9 -9
  277. package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
  278. package/dist/core/workflow/workspaces/local-git.js +39 -5
  279. package/dist/core/workflow/workspaces/recovery.js +3 -3
  280. package/dist/core/workflow/workspaces/state.d.ts +1 -1
  281. package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
  282. package/dist/core/workflow/workspaces/state.js +3 -3
  283. package/dist/core/workflow/workspaces/types.d.ts +1 -1
  284. package/dist/core/workflow/workspaces/types.d.ts.map +1 -1
  285. package/dist/lib/browser/asserts.d.ts +28 -0
  286. package/dist/lib/browser/asserts.d.ts.map +1 -0
  287. package/dist/lib/browser/asserts.js +152 -0
  288. package/dist/lib/browser/client.d.ts +49 -0
  289. package/dist/lib/browser/client.d.ts.map +1 -1
  290. package/dist/lib/browser/client.js +155 -14
  291. package/dist/lib/browser/content-checks.d.ts +108 -0
  292. package/dist/lib/browser/content-checks.d.ts.map +1 -0
  293. package/dist/lib/browser/content-checks.js +474 -0
  294. package/dist/lib/browser/critique.d.ts +91 -0
  295. package/dist/lib/browser/critique.d.ts.map +1 -0
  296. package/dist/lib/browser/critique.js +167 -0
  297. package/dist/lib/browser/geometry.d.ts +32 -0
  298. package/dist/lib/browser/geometry.d.ts.map +1 -1
  299. package/dist/lib/browser/geometry.js +244 -1
  300. package/dist/lib/browser/index.d.ts +4 -1
  301. package/dist/lib/browser/index.d.ts.map +1 -1
  302. package/dist/lib/browser/index.js +2 -0
  303. package/dist/lib/claude-desktop.d.ts.map +1 -1
  304. package/dist/lib/claude-desktop.js +4 -1
  305. package/dist/lib/council/index.d.ts +6 -6
  306. package/dist/lib/council/index.d.ts.map +1 -1
  307. package/dist/lib/council/index.js +8 -8
  308. package/dist/lib/exec.d.ts +7 -0
  309. package/dist/lib/exec.d.ts.map +1 -1
  310. package/dist/lib/exec.js +6 -3
  311. package/dist/lib/identities/assume.d.ts +2 -2
  312. package/dist/lib/identities/assume.js +7 -7
  313. package/dist/lib/instructions/apply.d.ts +3 -3
  314. package/dist/lib/instructions/apply.d.ts.map +1 -1
  315. package/dist/lib/instructions/apply.js +75 -27
  316. package/dist/lib/instructions/host-addendum.d.ts +54 -0
  317. package/dist/lib/instructions/host-addendum.d.ts.map +1 -0
  318. package/dist/lib/instructions/host-addendum.js +107 -0
  319. package/dist/lib/instructions/templates.d.ts +10 -3
  320. package/dist/lib/instructions/templates.d.ts.map +1 -1
  321. package/dist/lib/instructions/templates.js +41 -12
  322. package/dist/lib/machine.d.ts +2 -2
  323. package/dist/lib/machine.js +2 -2
  324. package/package.json +26 -21
  325. package/schemas/config.schema.json +26 -1
  326. package/src/commander.ts +22 -13
  327. package/src/commands/{harness.ts → adapter.ts} +46 -46
  328. package/src/commands/agents.ts +71 -64
  329. package/src/commands/artifacts.ts +168 -0
  330. package/src/commands/backup.ts +47 -33
  331. package/src/commands/browse.ts +438 -2
  332. package/src/commands/checkpoint.ts +196 -0
  333. package/src/commands/context.ts +3 -176
  334. package/src/commands/decision.ts +41 -7
  335. package/src/commands/deinit.ts +16 -16
  336. package/src/commands/doctor.ts +28 -28
  337. package/src/commands/{supervisor.ts → governor.ts} +184 -154
  338. package/src/commands/init.ts +50 -34
  339. package/src/commands/{scratch.ts → journal.ts} +60 -60
  340. package/src/commands/policy.ts +1 -1
  341. package/src/commands/presence.ts +2 -2
  342. package/src/commands/sync.ts +3 -3
  343. package/src/commands/tunnel.ts +237 -34
  344. package/src/commands/web-fetch.ts +1 -1
  345. package/src/commands/web.ts +56 -1
  346. package/src/commands/work.ts +72 -38
  347. package/src/commands/workflow.ts +43 -34
  348. package/src/core/{harnesses → adapters}/attest-projection.ts +2 -2
  349. package/src/core/{harnesses → adapters}/attest.ts +27 -27
  350. package/src/core/{harnesses → adapters}/attestation.ts +41 -41
  351. package/src/core/{harnesses → adapters}/bench.ts +34 -43
  352. package/src/core/adapters/index.ts +56 -0
  353. package/src/core/{harnesses → adapters}/profiles.ts +16 -16
  354. package/src/core/{harnesses → adapters}/registry.ts +23 -23
  355. package/src/core/{harnesses → adapters}/types.ts +28 -23
  356. package/src/core/agents/canonical-emit.ts +6 -6
  357. package/src/core/agents/cli-emit.ts +1 -1
  358. package/src/core/agents/cli.ts +30 -28
  359. package/src/core/agents/codex-replay.ts +6 -6
  360. package/src/core/agents/coord-client.ts +119 -28
  361. package/src/core/agents/events/consume.ts +1 -1
  362. package/src/core/agents/events/emit.ts +7 -6
  363. package/src/core/agents/render/prompt-context.ts +107 -31
  364. package/src/core/agents/render/session-context.ts +6 -6
  365. package/src/core/agents/rules/claim-conflict.ts +4 -7
  366. package/src/core/agents/rules/commit-conflict.ts +103 -22
  367. package/src/core/agents/rules/stop-hook.ts +33 -17
  368. package/src/core/agents/rules/verdict.ts +13 -0
  369. package/src/core/agents/session-events.ts +4 -4
  370. package/src/core/agents/state/council.ts +1 -1
  371. package/src/core/agents/state/heartbeat-projector.ts +16 -12
  372. package/src/core/agents/state/heartbeat-writer.ts +12 -12
  373. package/src/core/agents/state/{scratch.ts → journal.ts} +12 -12
  374. package/src/core/agents/state/pidmap.ts +130 -15
  375. package/src/core/agents/state/proc-start.ts +208 -0
  376. package/src/core/agents/state/shell-mutation.ts +1 -1
  377. package/src/core/agents/state/stale-sweep.ts +7 -7
  378. package/src/core/artifacts/index.ts +557 -0
  379. package/src/core/config.ts +30 -2
  380. package/src/core/context/index.ts +15 -15
  381. package/src/core/governor/adapter-spread.ts +82 -0
  382. package/src/core/governor/index.ts +91 -0
  383. package/src/core/governor/plan-api.ts +115 -0
  384. package/src/core/{supervisor → governor}/plan-read.ts +120 -96
  385. package/src/core/{supervisor → governor}/plan-types.ts +64 -50
  386. package/src/core/{supervisor → governor}/planning.ts +240 -188
  387. package/src/core/governor/read.ts +59 -0
  388. package/src/core/{supervisor → governor}/runner.ts +39 -39
  389. package/src/core/{supervisor → governor}/service-read.ts +22 -22
  390. package/src/core/{supervisor → governor}/service.ts +117 -119
  391. package/src/core/{supervisor → governor}/state.ts +389 -174
  392. package/src/core/hooks/adapter/detect.ts +30 -0
  393. package/src/core/hooks/{harness → adapter}/events.ts +16 -14
  394. package/src/core/hooks/{harness → adapter}/output.ts +31 -23
  395. package/src/core/hooks/{harness → adapter}/parse.ts +5 -5
  396. package/src/core/hooks/{harness → adapter}/wiring.ts +21 -21
  397. package/src/core/hooks/cli.ts +185 -162
  398. package/src/core/hooks/effects/image-capture.ts +6 -6
  399. package/src/core/hooks/effects/index.ts +17 -17
  400. package/src/core/hooks/events/emit.ts +3 -3
  401. package/src/core/hooks/events/rotate.ts +1 -1
  402. package/src/core/hooks/events/schema.ts +15 -7
  403. package/src/core/hooks/guard-path.ts +2 -2
  404. package/src/core/hooks/index.ts +1 -1
  405. package/src/core/hooks/resolve/anchor.ts +63 -8
  406. package/src/core/hooks/resolve/coord-root.ts +4 -4
  407. package/src/core/hooks/resolve/intent.ts +1 -1
  408. package/src/core/hooks/resolve/owner.ts +11 -5
  409. package/src/core/{scratch → journal}/index.ts +53 -53
  410. package/src/core/policy/evaluate.ts +8 -8
  411. package/src/core/policy/types.ts +3 -3
  412. package/src/core/work/runner.ts +11 -2
  413. package/src/core/work/state.ts +185 -21
  414. package/src/core/workflow/adapters.ts +33 -0
  415. package/src/core/workflow/approvals.ts +7 -7
  416. package/src/core/workflow/billing.ts +12 -12
  417. package/src/core/workflow/child-env.ts +13 -4
  418. package/src/core/workflow/engine.ts +166 -90
  419. package/src/core/workflow/evidence-preflight.ts +165 -0
  420. package/src/core/workflow/index.ts +6 -3
  421. package/src/core/workflow/proof.ts +94 -48
  422. package/src/core/workflow/run-state.ts +7 -7
  423. package/src/core/workflow/sandbox-projection.ts +16 -16
  424. package/src/core/workflow/spawn-claude.ts +34 -12
  425. package/src/core/workflow/spawn-codex.ts +30 -12
  426. package/src/core/workflow/spawn-cursor.ts +31 -12
  427. package/src/core/workflow/spawn-failure.ts +41 -1
  428. package/src/core/workflow/specialists.ts +2 -2
  429. package/src/core/workflow/{journal.ts → transcript.ts} +16 -16
  430. package/src/core/workflow/types.ts +130 -46
  431. package/src/core/workflow/validate.ts +1 -1
  432. package/src/core/workflow/workspaces/cancellation.ts +2 -2
  433. package/src/core/workflow/workspaces/cleanup.ts +2 -2
  434. package/src/core/workflow/workspaces/execution.ts +6 -6
  435. package/src/core/workflow/workspaces/inspect.ts +14 -11
  436. package/src/core/workflow/workspaces/integration.ts +18 -14
  437. package/src/core/workflow/workspaces/lifecycle.ts +10 -10
  438. package/src/core/workflow/workspaces/local-git.ts +47 -5
  439. package/src/core/workflow/workspaces/recovery.ts +3 -3
  440. package/src/core/workflow/workspaces/state.ts +4 -3
  441. package/src/core/workflow/workspaces/types.ts +1 -1
  442. package/src/lib/browser/asserts.ts +177 -0
  443. package/src/lib/browser/client.ts +189 -14
  444. package/src/lib/browser/content-checks.ts +571 -0
  445. package/src/lib/browser/critique.ts +231 -0
  446. package/src/lib/browser/geometry.ts +297 -1
  447. package/src/lib/browser/index.ts +34 -0
  448. package/src/lib/claude-desktop.ts +4 -1
  449. package/src/lib/council/index.ts +9 -9
  450. package/src/lib/exec.ts +13 -3
  451. package/src/lib/identities/assume.ts +7 -7
  452. package/src/lib/instructions/apply.ts +87 -25
  453. package/src/lib/instructions/host-addendum.ts +126 -0
  454. package/src/lib/instructions/templates.ts +44 -15
  455. package/src/lib/machine.ts +2 -2
  456. package/dist/commands/harness.d.ts +0 -12
  457. package/dist/commands/harness.d.ts.map +0 -1
  458. package/dist/commands/scratch.d.ts +0 -4
  459. package/dist/commands/scratch.d.ts.map +0 -1
  460. package/dist/commands/session.d.ts.map +0 -1
  461. package/dist/commands/session.js +0 -162
  462. package/dist/commands/supervisor.d.ts +0 -4
  463. package/dist/commands/supervisor.d.ts.map +0 -1
  464. package/dist/core/harnesses/attest-projection.d.ts.map +0 -1
  465. package/dist/core/harnesses/attest.d.ts.map +0 -1
  466. package/dist/core/harnesses/attestation.d.ts.map +0 -1
  467. package/dist/core/harnesses/bench.d.ts.map +0 -1
  468. package/dist/core/harnesses/index.d.ts +0 -13
  469. package/dist/core/harnesses/index.d.ts.map +0 -1
  470. package/dist/core/harnesses/index.js +0 -6
  471. package/dist/core/harnesses/profiles.d.ts.map +0 -1
  472. package/dist/core/harnesses/registry.d.ts +0 -15
  473. package/dist/core/harnesses/registry.d.ts.map +0 -1
  474. package/dist/core/harnesses/types.d.ts.map +0 -1
  475. package/dist/core/hooks/harness/detect.d.ts +0 -9
  476. package/dist/core/hooks/harness/events.d.ts.map +0 -1
  477. package/dist/core/hooks/harness/output.d.ts.map +0 -1
  478. package/dist/core/supervisor/index.d.ts +0 -7
  479. package/dist/core/supervisor/index.d.ts.map +0 -1
  480. package/dist/core/supervisor/index.js +0 -6
  481. package/dist/core/supervisor/plan-api.d.ts +0 -23
  482. package/dist/core/supervisor/plan-api.d.ts.map +0 -1
  483. package/dist/core/supervisor/plan-api.js +0 -50
  484. package/dist/core/supervisor/plan-read.d.ts +0 -5
  485. package/dist/core/supervisor/plan-read.d.ts.map +0 -1
  486. package/dist/core/supervisor/plan-types.d.ts +0 -170
  487. package/dist/core/supervisor/plan-types.d.ts.map +0 -1
  488. package/dist/core/supervisor/planning.d.ts +0 -33
  489. package/dist/core/supervisor/planning.d.ts.map +0 -1
  490. package/dist/core/supervisor/read.d.ts +0 -6
  491. package/dist/core/supervisor/read.d.ts.map +0 -1
  492. package/dist/core/supervisor/read.js +0 -4
  493. package/dist/core/supervisor/runner.d.ts +0 -37
  494. package/dist/core/supervisor/runner.d.ts.map +0 -1
  495. package/dist/core/supervisor/service-read.d.ts +0 -6
  496. package/dist/core/supervisor/service-read.d.ts.map +0 -1
  497. package/dist/core/supervisor/service.d.ts +0 -114
  498. package/dist/core/supervisor/service.d.ts.map +0 -1
  499. package/dist/core/supervisor/state.d.ts +0 -95
  500. package/dist/core/supervisor/state.d.ts.map +0 -1
  501. package/dist/core/workflow/harnesses.d.ts +0 -17
  502. package/dist/core/workflow/harnesses.d.ts.map +0 -1
  503. package/dist/core/workflow/harnesses.js +0 -21
  504. package/dist/core/workflow/journal.d.ts.map +0 -1
  505. package/src/commands/session.ts +0 -187
  506. package/src/core/harnesses/index.ts +0 -56
  507. package/src/core/hooks/harness/detect.ts +0 -30
  508. package/src/core/supervisor/index.ts +0 -89
  509. package/src/core/supervisor/plan-api.ts +0 -73
  510. package/src/core/supervisor/read.ts +0 -59
  511. package/src/core/workflow/harnesses.ts +0 -33
  512. /package/dist/core/{harnesses → adapters}/attest-projection.js +0 -0
@@ -17,18 +17,18 @@ import { assertWorkId, readWorkItem, type WorkRecord } from "../work/read.ts";
17
17
  import { workflowScriptDigest } from "../workflow/run-state.ts";
18
18
  import { normalizeWorkflowSpecialists } from "../workflow/specialists.ts";
19
19
  import type { WorkflowSpecialistProfile } from "../workflow/types.ts";
20
- import { readSupervisorPlans } from "./plan-read.ts";
20
+ import { readGovernorPlans } from "./plan-read.ts";
21
21
  import {
22
- type CreateSupervisorReplanningInput,
23
- MAX_SUPERVISOR_PLAN_REVIEWERS,
24
- MAX_SUPERVISOR_PLAN_REVISION_ROUNDS,
25
- type SupervisorPlanHistory,
26
- type SupervisorPlanRecord,
27
- type SupervisorReplanningPolicy,
28
- supervisorGraphFingerprint,
22
+ type CreateGovernorReplanningInput,
23
+ type GovernorPlanHistory,
24
+ type GovernorPlanRecord,
25
+ type GovernorReplanningPolicy,
26
+ governorGraphFingerprint,
27
+ MAX_GOVERNOR_PLAN_REVIEWERS,
28
+ MAX_GOVERNOR_PLAN_REVISION_ROUNDS,
29
29
  } from "./plan-types.ts";
30
30
 
31
- export const SUPERVISOR_INTENT_SCHEMA_VERSION = 1 as const;
31
+ export const GOVERNOR_INTENT_SCHEMA_VERSION = 1 as const;
32
32
 
33
33
  const GOAL_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/;
34
34
  const MAX_TITLE = 200;
@@ -38,8 +38,15 @@ const MAX_INTENT_BYTES = 256 * 1024;
38
38
  const MAX_TEMPLATES = 20;
39
39
  const TEMPLATE_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
40
40
  const FOREIGN_LEASE_STALE_MS = 24 * 60 * 60 * 1_000;
41
+ /** Ceiling on CONSECUTIVE uncharged (upstream) replans (ADR 0046). Because an
42
+ * uncharged replan does not spend `max_replans`, an unending vendor outage would
43
+ * otherwise replan forever; this bound stops it and names the outside service.
44
+ * Kept low — each replan is a real planner round-trip — and a module constant
45
+ * rather than a policy field to keep the frozen replanning schema unchanged; an
46
+ * environment failure self-bounds at one because it stops immediately. */
47
+ const MAX_UNCHARGED_REPLANS = 3;
41
48
 
42
- export interface SupervisorLimits {
49
+ export interface GovernorLimits {
43
50
  max_cycles: number;
44
51
  max_runtime_ms: number;
45
52
  max_parallel_work: number;
@@ -48,38 +55,38 @@ export interface SupervisorLimits {
48
55
  agent_concurrency: number;
49
56
  }
50
57
 
51
- export interface SupervisorAutomationPolicy {
58
+ export interface GovernorAutomationPolicy {
52
59
  accept_passing_proof: boolean;
53
60
  resume_approved: boolean;
54
61
  retry_blocked: boolean;
55
62
  }
56
63
 
57
- export interface SupervisorMission {
64
+ export interface GovernorMission {
58
65
  objective: string;
59
66
  acceptance: string[];
60
67
  max_milestones: number;
61
68
  }
62
69
 
63
- export interface CreateSupervisorMissionInput {
70
+ export interface CreateGovernorMissionInput {
64
71
  objective: string;
65
72
  acceptance: readonly string[];
66
73
  maxMilestones?: number;
67
74
  }
68
75
 
69
- export interface SupervisorIntent {
70
- schema_version: typeof SUPERVISOR_INTENT_SCHEMA_VERSION;
76
+ export interface GovernorIntent {
77
+ schema_version: typeof GOVERNOR_INTENT_SCHEMA_VERSION;
71
78
  id: string;
72
79
  title: string;
73
80
  root_work_id: string;
74
81
  specialists: Record<string, WorkflowSpecialistProfile>;
75
- limits: SupervisorLimits;
76
- automation: SupervisorAutomationPolicy;
77
- mission?: SupervisorMission;
78
- replanning?: SupervisorReplanningPolicy;
82
+ limits: GovernorLimits;
83
+ automation: GovernorAutomationPolicy;
84
+ mission?: GovernorMission;
85
+ replanning?: GovernorReplanningPolicy;
79
86
  created_at: string;
80
87
  }
81
88
 
82
- export type SupervisorState =
89
+ export type GovernorState =
83
90
  | "ready"
84
91
  | "running"
85
92
  | "awaiting_attention"
@@ -87,7 +94,7 @@ export type SupervisorState =
87
94
  | "budget_exhausted"
88
95
  | "succeeded";
89
96
 
90
- export type SupervisorNextAction =
97
+ export type GovernorNextAction =
91
98
  | "run"
92
99
  | "wait_for_run"
93
100
  | "resolve_approval"
@@ -100,19 +107,23 @@ export type SupervisorNextAction =
100
107
  | "retry_plan"
101
108
  | "none";
102
109
 
103
- export interface SupervisorProjection {
110
+ export interface GovernorProjection {
104
111
  id: string;
105
112
  title: string;
106
113
  root_work_id: string;
107
114
  root_materialized: boolean;
108
- state: SupervisorState;
115
+ state: GovernorState;
109
116
  reason: string;
110
- next_action: SupervisorNextAction;
117
+ next_action: GovernorNextAction;
111
118
  work_ids: string[];
112
119
  ready_work: string[];
113
120
  resumable_work: string[];
114
121
  retryable_work: string[];
115
122
  attention_work: string[];
123
+ /** Work stopped because a human must rule, with the docket id when the script
124
+ * named one. Empty in the ordinary case. A reader that wants to tell an
125
+ * operator what is actually waiting on them should start here. */
126
+ decision_blocked_work: Array<{ work_id: string; decision_id?: string }>;
116
127
  attempts_used: number;
117
128
  attempts_remaining: number;
118
129
  specialists: string[];
@@ -123,41 +134,53 @@ export interface SupervisorProjection {
123
134
  milestones_remaining: number;
124
135
  pending_plan_id?: string;
125
136
  attention_plan_id?: string;
126
- latest_plan_status?: SupervisorPlanRecord["status"];
137
+ latest_plan_status?: GovernorPlanRecord["status"];
138
+ /**
139
+ * How consumed (charged) replans that did not advance the graph broke down,
140
+ * present only when at least one replan was spent by a planner run that
141
+ * produced no proposal. `reviewer_rejection` counts replans where a proposal
142
+ * was produced and independent review rejected it (review-round exhaustion,
143
+ * reviewer attention, or review failure); `planner_no_proposal` counts replans
144
+ * where the planner run produced no reviewable proposal at all. Absent when a
145
+ * goal has no planner no-proposal history, so goals that only ever reached
146
+ * review project exactly as before this field existed.
147
+ */
148
+ replan_consumption?: {
149
+ reviewer_rejection: number;
150
+ planner_no_proposal: number;
151
+ };
127
152
  governed_work_ids: string[];
128
153
  }
129
154
 
130
- export interface SupervisorRecord {
131
- intent: SupervisorIntent;
132
- projection: SupervisorProjection;
155
+ export interface GovernorRecord {
156
+ intent: GovernorIntent;
157
+ projection: GovernorProjection;
133
158
  work: WorkRecord[];
134
- plans: SupervisorPlanRecord[];
159
+ plans: GovernorPlanRecord[];
135
160
  }
136
161
 
137
- export interface CreateSupervisorInput {
162
+ export interface CreateGovernorInput {
138
163
  coordRoot: string;
139
164
  rootWorkId?: string;
140
165
  specialists: Readonly<Record<string, WorkflowSpecialistProfile>>;
141
166
  title?: string;
142
167
  id?: string;
143
- limits?: Partial<SupervisorLimits>;
144
- automation?: Partial<SupervisorAutomationPolicy>;
145
- mission?: CreateSupervisorMissionInput;
146
- replanning?: CreateSupervisorReplanningInput;
168
+ limits?: Partial<GovernorLimits>;
169
+ automation?: Partial<GovernorAutomationPolicy>;
170
+ mission?: CreateGovernorMissionInput;
171
+ replanning?: CreateGovernorReplanningInput;
147
172
  }
148
173
 
149
- export function createSupervisor(input: CreateSupervisorInput): SupervisorRecord {
174
+ export function createGovernor(input: CreateGovernorInput): GovernorRecord {
150
175
  const coordRoot = resolve(input.coordRoot);
151
- const id = input.id ?? newSupervisorId();
152
- assertSupervisorId(id);
176
+ const id = input.id ?? newGovernorId();
177
+ assertGovernorId(id);
153
178
  const mission = input.mission ? normalizeMission(input.mission) : undefined;
154
179
  if (mission && !input.replanning) {
155
- throw new Error("supervisor mission requires a replanning policy");
180
+ throw new Error("governor mission requires a replanning policy");
156
181
  }
157
182
  if (!input.rootWorkId && !mission) {
158
- throw new Error(
159
- "supervisor creation without root work requires a mission and replanning policy",
160
- );
183
+ throw new Error("governor creation without root work requires a mission and replanning policy");
161
184
  }
162
185
  if (input.rootWorkId) assertWorkId(input.rootWorkId);
163
186
  const root = input.rootWorkId ? readWorkItem(coordRoot, input.rootWorkId) : undefined;
@@ -175,12 +198,12 @@ export function createSupervisor(input: CreateSupervisorInput): SupervisorRecord
175
198
  "objective-first mission max_replans must exceed max_milestones to reserve completion review",
176
199
  );
177
200
  }
178
- const intent: SupervisorIntent = {
179
- schema_version: SUPERVISOR_INTENT_SCHEMA_VERSION,
201
+ const intent: GovernorIntent = {
202
+ schema_version: GOVERNOR_INTENT_SCHEMA_VERSION,
180
203
  id,
181
204
  title: bounded(
182
205
  input.title ?? root?.intent.title ?? missionTitle(mission!),
183
- "supervisor title",
206
+ "governor title",
184
207
  MAX_TITLE,
185
208
  ),
186
209
  root_work_id: input.rootWorkId ?? missionRootId(id),
@@ -191,42 +214,41 @@ export function createSupervisor(input: CreateSupervisorInput): SupervisorRecord
191
214
  ...(replanning ? { replanning } : {}),
192
215
  created_at: new Date().toISOString(),
193
216
  };
194
- const path = supervisorIntentPath(coordRoot, id);
217
+ const path = governorIntentPath(coordRoot, id);
195
218
  mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
196
219
  chmodSync(dirname(path), 0o700);
197
220
  writePrivateJson(path, intent);
198
- return readSupervisor(coordRoot, id);
221
+ return readGovernor(coordRoot, id);
199
222
  }
200
223
 
201
- export function readSupervisor(coordRoot: string, goalId: string): SupervisorRecord {
202
- return readSupervisorInternal(coordRoot, goalId, false);
224
+ export function readGovernor(coordRoot: string, goalId: string): GovernorRecord {
225
+ return readGovernorInternal(coordRoot, goalId, false);
203
226
  }
204
227
 
205
- /** @internal Runner seam; ignores the lease held by this supervisor process. */
206
- export function readSupervisorIgnoringLease(coordRoot: string, goalId: string): SupervisorRecord {
207
- return readSupervisorInternal(coordRoot, goalId, true);
228
+ /** @internal Runner seam; ignores the lease held by this governor process. */
229
+ export function readGovernorIgnoringLease(coordRoot: string, goalId: string): GovernorRecord {
230
+ return readGovernorInternal(coordRoot, goalId, true);
208
231
  }
209
232
 
210
- export function listSupervisors(coordRoot: string): SupervisorRecord[] {
211
- const base = join(resolve(coordRoot), ".harnery", "supervisors");
233
+ export function listGovernors(coordRoot: string): GovernorRecord[] {
234
+ const base = join(resolve(coordRoot), ".harnery", "governors");
212
235
  if (!existsSync(base)) return [];
213
- const records: SupervisorRecord[] = [];
236
+ const records: GovernorRecord[] = [];
214
237
  for (const name of readdirSync(base)) {
215
- if (!GOAL_ID.test(name) || !existsSync(supervisorIntentPath(coordRoot, name))) continue;
216
- records.push(readSupervisor(coordRoot, name));
238
+ if (!GOAL_ID.test(name) || !existsSync(governorIntentPath(coordRoot, name))) continue;
239
+ records.push(readGovernor(coordRoot, name));
217
240
  }
218
241
  return records.sort((left, right) =>
219
242
  right.intent.created_at.localeCompare(left.intent.created_at),
220
243
  );
221
244
  }
222
245
 
223
- export function collectSupervisorWork(coordRoot: string, rootWorkId: string): WorkRecord[] {
246
+ export function collectGovernorWork(coordRoot: string, rootWorkId: string): WorkRecord[] {
224
247
  const records = new Map<string, WorkRecord>();
225
248
  const visiting = new Set<string>();
226
249
  const visit = (workId: string): void => {
227
250
  if (records.has(workId)) return;
228
- if (visiting.has(workId))
229
- throw new Error(`supervisor work graph contains a cycle at ${workId}`);
251
+ if (visiting.has(workId)) throw new Error(`governor work graph contains a cycle at ${workId}`);
230
252
  visiting.add(workId);
231
253
  const record = readWorkItem(coordRoot, workId);
232
254
  for (const dependency of record.intent.dependencies) visit(dependency);
@@ -237,18 +259,18 @@ export function collectSupervisorWork(coordRoot: string, rootWorkId: string): Wo
237
259
  return Array.from(records.values());
238
260
  }
239
261
 
240
- export function assertSupervisorId(goalId: string): void {
241
- if (!GOAL_ID.test(goalId)) throw new Error(`invalid supervisor id ${JSON.stringify(goalId)}`);
262
+ export function assertGovernorId(goalId: string): void {
263
+ if (!GOAL_ID.test(goalId)) throw new Error(`invalid governor id ${JSON.stringify(goalId)}`);
242
264
  }
243
265
 
244
- export function newSupervisorId(): string {
266
+ export function newGovernorId(): string {
245
267
  return `goal-${new Date().toISOString().replace(/[:.]/g, "-")}-${randomBytes(3).toString("hex")}`;
246
268
  }
247
269
 
248
- /** @internal Runner seam; one exclusive crash-recoverable supervisor lease. */
249
- export function acquireSupervisorLease(coordRoot: string, goalId: string): () => void {
250
- readSupervisorIntent(coordRoot, goalId);
251
- const path = join(supervisorDir(coordRoot, goalId), "lease.json");
270
+ /** @internal Runner seam; one exclusive crash-recoverable governor lease. */
271
+ export function acquireGovernorLease(coordRoot: string, goalId: string): () => void {
272
+ readGovernorIntent(coordRoot, goalId);
273
+ const path = join(governorDir(coordRoot, goalId), "lease.json");
252
274
  const owner = {
253
275
  pid: process.pid,
254
276
  host: hostname(),
@@ -274,11 +296,11 @@ export function acquireSupervisorLease(coordRoot: string, goalId: string): () =>
274
296
  const existing = readLease(path);
275
297
  if (existing && leaseIsLive(existing)) {
276
298
  throw new Error(
277
- `supervisor ${goalId} is already running under pid ${existing.pid} on ${existing.host}`,
299
+ `governor ${goalId} is already running under pid ${existing.pid} on ${existing.host}`,
278
300
  );
279
301
  }
280
302
  unlinkSync(path);
281
- if (!acquire()) throw new Error(`supervisor ${goalId} lease raced with another process`);
303
+ if (!acquire()) throw new Error(`governor ${goalId} lease raced with another process`);
282
304
  }
283
305
  return () => {
284
306
  try {
@@ -290,19 +312,19 @@ export function acquireSupervisorLease(coordRoot: string, goalId: string): () =>
290
312
  };
291
313
  }
292
314
 
293
- function readSupervisorInternal(
315
+ function readGovernorInternal(
294
316
  coordRootRaw: string,
295
317
  goalId: string,
296
318
  ignoreLease: boolean,
297
- ): SupervisorRecord {
319
+ ): GovernorRecord {
298
320
  const coordRoot = resolve(coordRootRaw);
299
- const intent = readSupervisorIntent(coordRoot, goalId);
300
- const plans = readSupervisorPlans(coordRoot, intent.id, intent.root_work_id);
321
+ const intent = readGovernorIntent(coordRoot, goalId);
322
+ const plans = readGovernorPlans(coordRoot, intent.id, intent.root_work_id);
301
323
  const activeRootExists = workIntentExists(coordRoot, plans.active_root_work_id);
302
324
  if (!activeRootExists && (!intent.mission || plans.generation > 0)) {
303
- throw new Error(`supervisor ${intent.id} root work is missing`);
325
+ throw new Error(`governor ${intent.id} root work is missing`);
304
326
  }
305
- const work = activeRootExists ? collectSupervisorWork(coordRoot, plans.active_root_work_id) : [];
327
+ const work = activeRootExists ? collectGovernorWork(coordRoot, plans.active_root_work_id) : [];
306
328
  const governed = collectGovernedWork(coordRoot, intent.root_work_id, plans, work);
307
329
  return {
308
330
  intent,
@@ -314,14 +336,30 @@ function readSupervisorInternal(
314
336
 
315
337
  function deriveProjection(
316
338
  coordRoot: string,
317
- intent: SupervisorIntent,
339
+ intent: GovernorIntent,
318
340
  work: WorkRecord[],
319
341
  governed: WorkRecord[],
320
- plans: SupervisorPlanHistory,
342
+ plans: GovernorPlanHistory,
321
343
  ignoreLease: boolean,
322
- ): SupervisorProjection {
344
+ ): GovernorProjection {
323
345
  const root = work.find((record) => record.intent.id === plans.active_root_work_id);
324
346
  const attemptsUsed = governed.reduce((sum, record) => sum + record.projection.attempts_used, 0);
347
+ // The goal budget, like the per-item budget, counts only charged attempts
348
+ // (ADR 0046): an uncharged environment/upstream attempt on any child does not
349
+ // spend the goal's total. attempts_used stays the raw count for display.
350
+ const chargedUsed = governed.reduce((sum, record) => sum + record.projection.charged_attempts, 0);
351
+ // Replans, like per-item attempts (ADR 0046), are budgeted by the CHARGED
352
+ // count: an uncharged planner failure (environment/upstream) does not spend
353
+ // max_replans. replans_used stays the raw count for display.
354
+ const unchargedReplans = plans.plans.filter(
355
+ (plan) => plan.status === "failed" && isUnchargedPlanClass(plan.class),
356
+ ).length;
357
+ const chargedReplans = plans.plans.length - unchargedReplans;
358
+ // Attribute the consumed replans that did not advance the graph so the
359
+ // projection can distinguish planner no-proposal exhaustion from reviewer
360
+ // rejection through existing plan seams (review receipt presence), rather than
361
+ // reporting both as a single undifferentiated replan exhaustion.
362
+ const replanConsumption = classifyReplanConsumption(plans);
325
363
  const readyWork = work
326
364
  .filter((record) => record.projection.state === "ready")
327
365
  .map((record) => record.intent.id);
@@ -356,6 +394,15 @@ function deriveProjection(
356
394
  (record) => record.projection.state === "blocked" && record.projection.next_action === "none",
357
395
  )
358
396
  .map((record) => record.intent.id);
397
+ // Work stopped on a person, kept apart from the rest of terminalBlocked. Every
398
+ // other terminal block is something an agent could in principle clear; this one
399
+ // can only be cleared by a ruling, so it is the thing worth saying out loud.
400
+ const decisionBlocked = work
401
+ .filter((record) => record.projection.blocked_on_decision !== undefined)
402
+ .map((record) => ({
403
+ work_id: record.intent.id,
404
+ decision_id: record.projection.blocked_on_decision || undefined,
405
+ }));
359
406
  const attentionWork = unique([
360
407
  ...pendingApproval,
361
408
  ...(intent.automation.accept_passing_proof ? [] : reviews),
@@ -371,6 +418,12 @@ function deriveProjection(
371
418
  record.intent.id === intent.root_work_id && record.projection.state === "succeeded",
372
419
  );
373
420
  const milestonesCompleted = plans.milestones_completed + (initialRootAccepted ? 1 : 0);
421
+ const resumableDispatchable = intent.automation.resume_approved ? resumableWork : [];
422
+ const attemptDispatchable = [
423
+ ...readyWork,
424
+ ...(intent.automation.retry_blocked ? retryableWork : []),
425
+ ];
426
+ const dispatchable = [...resumableDispatchable, ...attemptDispatchable];
374
427
  const base = {
375
428
  id: intent.id,
376
429
  title: intent.title,
@@ -381,23 +434,25 @@ function deriveProjection(
381
434
  resumable_work: resumableWork,
382
435
  retryable_work: retryableWork,
383
436
  attention_work: attentionWork,
437
+ decision_blocked_work: decisionBlocked,
384
438
  attempts_used: attemptsUsed,
385
- attempts_remaining: Math.max(0, intent.limits.max_total_attempts - attemptsUsed),
439
+ attempts_remaining: Math.max(0, intent.limits.max_total_attempts - chargedUsed),
386
440
  specialists: Object.keys(intent.specialists),
387
441
  plan_generation: plans.generation,
388
442
  replans_used: plans.plans.length,
389
- replans_remaining: Math.max(0, (intent.replanning?.max_replans ?? 0) - plans.plans.length),
443
+ replans_remaining: Math.max(0, (intent.replanning?.max_replans ?? 0) - chargedReplans),
390
444
  milestones_completed: milestonesCompleted,
391
445
  milestones_remaining: Math.max(0, (intent.mission?.max_milestones ?? 0) - milestonesCompleted),
392
446
  pending_plan_id: plans.latest?.status === "proposed" ? plans.latest.request.id : undefined,
393
447
  latest_plan_status: plans.latest?.status,
448
+ ...(replanConsumption.planner_no_proposal > 0 ? { replan_consumption: replanConsumption } : {}),
394
449
  governed_work_ids: governed.map((record) => record.intent.id),
395
450
  };
396
- if (!ignoreLease && supervisorLeaseIsLive(coordRoot, intent.id)) {
451
+ if (!ignoreLease && governorLeaseIsLive(coordRoot, intent.id)) {
397
452
  return {
398
453
  ...base,
399
454
  state: "running",
400
- reason: "the foreground supervisor holds the goal lease",
455
+ reason: "the foreground governor holds the goal lease",
401
456
  next_action: "wait_for_run",
402
457
  };
403
458
  }
@@ -409,6 +464,19 @@ function deriveProjection(
409
464
  next_action: "none",
410
465
  };
411
466
  }
467
+ // ADR 0050: a mission whose completion was reopened dispatches the reopened work
468
+ // before anything else. Without this the goal falls through to the milestone
469
+ // branch below, which still sees a succeeded root when the reopened item is one
470
+ // of its children, and would spend a replan reassessing a mission whose real
471
+ // remaining work is already sitting in ready_work.
472
+ if (intent.mission && plans.latest?.status === "reopened" && dispatchable.length > 0) {
473
+ return {
474
+ ...base,
475
+ state: "ready",
476
+ reason: `mission completion was reopened; ${dispatchable.length} work item${dispatchable.length === 1 ? " is" : "s are"} dispatchable`,
477
+ next_action: "run",
478
+ };
479
+ }
412
480
  if (plans.latest?.status === "proposed") {
413
481
  return {
414
482
  ...base,
@@ -433,7 +501,58 @@ function deriveProjection(
433
501
  next_action: "replan",
434
502
  };
435
503
  }
436
- const triggerFingerprint = supervisorGraphFingerprint({
504
+ // ADR 0046: a planner failure that never touched the plan is handled before
505
+ // the replan path. Otherwise a "failed" plan falls through to canReplan and
506
+ // the runner replans an unchanged environment on every tick — the failure mode
507
+ // that burned the measured 19 "codex not found" replans.
508
+ if (plans.latest?.status === "failed" && plans.latest.class === "environment") {
509
+ // A missing precondition (the planner binary was absent). Retrying an
510
+ // unchanged environment cannot help, so the goal STOPS and names it. A human
511
+ // who installs the binary can re-run; the failure was uncharged, so the
512
+ // replan budget is intact.
513
+ return {
514
+ ...base,
515
+ state: "blocked",
516
+ reason: `planning could not start; a required precondition is missing: ${plans.latest.reason ?? "the planner binary was absent"}`,
517
+ next_action: "none",
518
+ };
519
+ }
520
+ if (plans.latest?.status === "failed" && plans.latest.class === "decision") {
521
+ // The planner concluded that a human must rule before this can be planned.
522
+ // Stop and say so. Replanning would re-ask a question the planner already
523
+ // answered correctly, which is how a goal spins without a person noticing.
524
+ return {
525
+ ...base,
526
+ state: "blocked",
527
+ reason: `planning is waiting on a human decision: ${plans.latest.reason ?? "the planner did not say which"}`,
528
+ next_action: "none",
529
+ };
530
+ }
531
+ if (plans.latest?.status === "failed" && plans.latest.class === "upstream") {
532
+ // The vendor refused. Uncharged replans do not spend max_replans, so the
533
+ // only brake on an unending outage is this consecutive bound; at the limit
534
+ // the goal stops and names the outside service, distinct from work-blocked.
535
+ let trailingUncharged = 0;
536
+ for (let index = plans.plans.length - 1; index >= 0; index--) {
537
+ const plan = plans.plans[index]!;
538
+ if (plan.status !== "failed" || !isUnchargedPlanClass(plan.class)) {
539
+ break;
540
+ }
541
+ trailingUncharged++;
542
+ }
543
+ if (trailingUncharged >= MAX_UNCHARGED_REPLANS) {
544
+ return {
545
+ ...base,
546
+ state: "blocked",
547
+ reason: `planning is blocked waiting on an outside service after ${trailingUncharged} consecutive uncharged attempt(s): ${plans.latest.reason ?? "the vendor refused"}`,
548
+ next_action: "none",
549
+ };
550
+ }
551
+ // Under the bound: fall through so the goal replans when nothing else is
552
+ // dispatchable — the vendor may have recovered. canReplan reads
553
+ // chargedReplans, so these uncharged attempts do not exhaust max_replans.
554
+ }
555
+ const triggerFingerprint = governorGraphFingerprint({
437
556
  rootWorkId: plans.active_root_work_id,
438
557
  generation: plans.generation,
439
558
  work,
@@ -443,23 +562,27 @@ function deriveProjection(
443
562
  plans.latest.request.prior_root_work_id === plans.active_root_work_id &&
444
563
  plans.latest.status === "attention";
445
564
  if (latestHandledSameGraph && plans.latest) {
565
+ const canRetry =
566
+ intent.replanning !== undefined && chargedReplans < intent.replanning.max_replans;
446
567
  return {
447
568
  ...base,
448
569
  state: "awaiting_attention",
449
- reason: plans.latest.reason ?? `plan ${plans.latest.request.id} requires attention`,
570
+ reason: canRetry
571
+ ? (plans.latest.reason ?? `plan ${plans.latest.request.id} requires attention`)
572
+ : exhaustedAttentionReason(plans.latest, replanConsumption),
450
573
  attention_plan_id: plans.latest.request.id,
451
- next_action:
452
- intent.replanning && plans.plans.length < intent.replanning.max_replans
453
- ? "retry_plan"
454
- : "none",
574
+ next_action: canRetry ? "retry_plan" : "none",
455
575
  };
456
576
  }
457
577
  if (!root && intent.mission) {
458
- if (!intent.replanning || plans.plans.length >= intent.replanning.max_replans) {
578
+ if (!intent.replanning || chargedReplans >= intent.replanning.max_replans) {
459
579
  return {
460
580
  ...base,
461
581
  state: "budget_exhausted",
462
- reason: "mission exhausted its planning budget before creating an initial milestone",
582
+ reason: replanBudgetReason(
583
+ "mission exhausted its planning budget before creating an initial milestone",
584
+ replanConsumption,
585
+ ),
463
586
  next_action: "none",
464
587
  };
465
588
  }
@@ -470,13 +593,16 @@ function deriveProjection(
470
593
  next_action: "plan_initial",
471
594
  };
472
595
  }
473
- if (!root) throw new Error(`supervisor ${intent.id} root work is missing`);
596
+ if (!root) throw new Error(`governor ${intent.id} root work is missing`);
474
597
  if (root.projection.state === "succeeded" && intent.mission) {
475
- if (!intent.replanning || plans.plans.length >= intent.replanning.max_replans) {
598
+ if (!intent.replanning || chargedReplans >= intent.replanning.max_replans) {
476
599
  return {
477
600
  ...base,
478
601
  state: "budget_exhausted",
479
- reason: "mission exhausted its planning budget at a milestone boundary",
602
+ reason: replanBudgetReason(
603
+ "mission exhausted its planning budget at a milestone boundary",
604
+ replanConsumption,
605
+ ),
480
606
  next_action: "none",
481
607
  };
482
608
  }
@@ -495,12 +621,6 @@ function deriveProjection(
495
621
  next_action: "none",
496
622
  };
497
623
  }
498
- const resumableDispatchable = intent.automation.resume_approved ? resumableWork : [];
499
- const attemptDispatchable = [
500
- ...readyWork,
501
- ...(intent.automation.retry_blocked ? retryableWork : []),
502
- ];
503
- const dispatchable = [...resumableDispatchable, ...attemptDispatchable];
504
624
  if (intent.automation.accept_passing_proof && reviews.length > 0) {
505
625
  return {
506
626
  ...base,
@@ -517,7 +637,7 @@ function deriveProjection(
517
637
  next_action: "run",
518
638
  };
519
639
  }
520
- if (attemptsUsed >= intent.limits.max_total_attempts && attemptDispatchable.length > 0) {
640
+ if (chargedUsed >= intent.limits.max_total_attempts && attemptDispatchable.length > 0) {
521
641
  return {
522
642
  ...base,
523
643
  state: "budget_exhausted",
@@ -565,7 +685,7 @@ function deriveProjection(
565
685
  next_action: "retry",
566
686
  };
567
687
  }
568
- if (attemptsUsed >= intent.limits.max_total_attempts && cancelled.length === 0) {
688
+ if (chargedUsed >= intent.limits.max_total_attempts && cancelled.length === 0) {
569
689
  return {
570
690
  ...base,
571
691
  state: "budget_exhausted",
@@ -575,7 +695,7 @@ function deriveProjection(
575
695
  }
576
696
  const canReplan =
577
697
  intent.replanning !== undefined &&
578
- plans.plans.length < intent.replanning.max_replans &&
698
+ chargedReplans < intent.replanning.max_replans &&
579
699
  cancelled.length === 0 &&
580
700
  !latestHandledSameGraph;
581
701
  if (canReplan) {
@@ -590,54 +710,149 @@ function deriveProjection(
590
710
  }
591
711
  if (
592
712
  intent.replanning &&
593
- plans.plans.length >= intent.replanning.max_replans &&
713
+ chargedReplans >= intent.replanning.max_replans &&
594
714
  cancelled.length === 0
595
715
  ) {
596
716
  return {
597
717
  ...base,
598
718
  state: "budget_exhausted",
599
- reason: `goal exhausted its ${intent.replanning.max_replans} replans`,
719
+ reason: replanBudgetReason(
720
+ `goal exhausted its ${intent.replanning.max_replans} replans`,
721
+ replanConsumption,
722
+ ),
600
723
  next_action: "none",
601
724
  };
602
725
  }
603
726
  return {
604
727
  ...base,
605
728
  state: "blocked",
729
+ // Lead with the decisions when there are any. "3 work items need
730
+ // intervention" reads as a queue an agent will get to; "waiting on you to
731
+ // rule on X" is the only phrasing that reaches the person who can clear it.
606
732
  reason:
607
- attentionWork.length > 0
608
- ? `${attentionWork.length} work item${attentionWork.length === 1 ? " needs" : "s need"} intervention`
609
- : "goal graph has no legal progress action",
733
+ decisionBlocked.length > 0
734
+ ? `${decisionBlocked.length} work item${decisionBlocked.length === 1 ? " is" : "s are"} waiting on a human decision: ${describeDecisionBlocks(decisionBlocked)}`
735
+ : attentionWork.length > 0
736
+ ? `${attentionWork.length} work item${attentionWork.length === 1 ? " needs" : "s need"} intervention`
737
+ : "goal graph has no legal progress action",
610
738
  next_action: "none",
611
739
  };
612
740
  }
613
741
 
614
- function readSupervisorIntent(coordRoot: string, goalId: string): SupervisorIntent {
615
- assertSupervisorId(goalId);
616
- const path = supervisorIntentPath(coordRoot, goalId);
617
- if (!existsSync(path)) throw new Error(`supervisor ${goalId} does not exist`);
742
+ /** Bounded, operator-readable list of what is waiting on a ruling. */
743
+ function describeDecisionBlocks(
744
+ blocks: readonly { work_id: string; decision_id?: string }[],
745
+ ): string {
746
+ const shown = blocks
747
+ .slice(0, 3)
748
+ .map((b) => (b.decision_id ? `${b.work_id} (${b.decision_id})` : b.work_id))
749
+ .join(", ");
750
+ return blocks.length > 3 ? `${shown}, +${blocks.length - 3} more` : shown;
751
+ }
752
+
753
+ interface ReplanConsumption {
754
+ reviewer_rejection: number;
755
+ planner_no_proposal: number;
756
+ }
757
+
758
+ /**
759
+ * Classify each consumed replan that did not advance the graph, using existing
760
+ * plan seams only. A plan carries a review receipt exactly when a proposal was
761
+ * produced and independently reviewed, so its presence separates a reviewer
762
+ * rejection from a planner run that produced no reviewable proposal. Uncharged
763
+ * environment/upstream planner failures (ADR 0046) do not spend the replan
764
+ * budget and are attributed to their outside precondition, not counted here.
765
+ */
766
+ /** Whether a failed plan told us nothing about the plan, and so must not spend
767
+ * a replan (ADR 0046). All three classes mean "replanning this cannot help":
768
+ * the binary is missing, the vendor is down, or a person has to rule. */
769
+ function isUnchargedPlanClass(value: GovernorPlanRecord["class"]): boolean {
770
+ return value === "environment" || value === "upstream" || value === "decision";
771
+ }
772
+
773
+ function classifyReplanConsumption(plans: GovernorPlanHistory): ReplanConsumption {
774
+ let reviewerRejection = 0;
775
+ let plannerNoProposal = 0;
776
+ for (const plan of plans.plans) {
777
+ if (plan.status === "failed" && isUnchargedPlanClass(plan.class)) {
778
+ continue;
779
+ }
780
+ if (plan.review && plan.review.status !== "passed") {
781
+ reviewerRejection++;
782
+ } else if (
783
+ !plan.review &&
784
+ (plan.status === "attention" ||
785
+ plan.status === "interrupted" ||
786
+ plan.status === "failed" ||
787
+ plan.status === "retry_requested")
788
+ ) {
789
+ // `retry_requested` is a planner no-proposal attention the operator asked to
790
+ // replan: the original run still produced no reviewable proposal and still
791
+ // spent a charged replan, so it belongs in this bucket even though a later
792
+ // plan superseded it. Omitting it would misattribute a retried no-proposal
793
+ // to nothing and let the reason read as pure review exhaustion.
794
+ plannerNoProposal++;
795
+ }
796
+ }
797
+ return { reviewer_rejection: reviewerRejection, planner_no_proposal: plannerNoProposal };
798
+ }
799
+
800
+ /**
801
+ * Reason for a goal held at `awaiting_attention/none` because its latest plan is
802
+ * an unresolved attention and no replan slot remains. The latest plan's own
803
+ * per-plan reason always leads (a reviewed-and-rejected latest reports its
804
+ * review-round exhaustion verbatim), then `replanBudgetReason` names the planner
805
+ * no-proposal share of the consumed budget. This keeps the `projection.reason`
806
+ * field carrying the same truth the operator-visible row does: a goal whose
807
+ * budget was mostly spent by planner no-proposal runs is not attributed wholesale
808
+ * to review just because its final, reviewed plan happened to be rejected.
809
+ */
810
+ function exhaustedAttentionReason(
811
+ latest: GovernorPlanRecord,
812
+ consumption: ReplanConsumption,
813
+ ): string {
814
+ const fallback = latest.reason ?? `plan ${latest.request.id} requires attention`;
815
+ return replanBudgetReason(fallback, consumption);
816
+ }
817
+
818
+ /**
819
+ * Append planner no-proposal attribution to a replan-exhaustion reason. Returns
820
+ * the base reason unchanged when no consumed replan was a planner no-proposal
821
+ * outcome, so goals without that history project exactly as before.
822
+ */
823
+ function replanBudgetReason(base: string, consumption: ReplanConsumption): string {
824
+ if (consumption.planner_no_proposal <= 0) return base;
825
+ const count = consumption.planner_no_proposal;
826
+ return `${base}; ${count} replan${count === 1 ? "" : "s"} ended with the planner producing no proposal`;
827
+ }
828
+
829
+ function readGovernorIntent(coordRoot: string, goalId: string): GovernorIntent {
830
+ assertGovernorId(goalId);
831
+ const path = governorIntentPath(coordRoot, goalId);
832
+ if (!existsSync(path)) throw new Error(`governor ${goalId} does not exist`);
618
833
  const size = statSync(path).size;
619
834
  if (size <= 0 || size > MAX_INTENT_BYTES) {
620
- throw new Error(`supervisor intent has invalid size ${size}`);
835
+ throw new Error(`governor intent has invalid size ${size}`);
621
836
  }
622
- let intent: SupervisorIntent;
837
+ let intent: GovernorIntent;
623
838
  try {
624
- intent = JSON.parse(readFileSync(path, "utf8")) as SupervisorIntent;
839
+ intent = JSON.parse(readFileSync(path, "utf8")) as GovernorIntent;
625
840
  } catch (error) {
626
- throw new Error(`cannot parse supervisor intent ${goalId}: ${(error as Error).message}`);
841
+ throw new Error(`cannot parse governor intent ${goalId}: ${(error as Error).message}`);
627
842
  }
628
843
  validateIntent(intent, goalId);
629
844
  return intent;
630
845
  }
631
846
 
632
- function validateIntent(intent: SupervisorIntent, goalId: string): void {
847
+ function validateIntent(intent: GovernorIntent, goalId: string): void {
633
848
  if (
634
- intent.schema_version !== SUPERVISOR_INTENT_SCHEMA_VERSION ||
849
+ intent.schema_version !== GOVERNOR_INTENT_SCHEMA_VERSION ||
635
850
  intent.id !== goalId ||
636
851
  !validTimestamp(intent.created_at)
637
852
  ) {
638
- throw new Error(`supervisor intent ${goalId} has an unsupported or mismatched schema`);
853
+ throw new Error(`governor intent ${goalId} has an unsupported or mismatched schema`);
639
854
  }
640
- bounded(intent.title, "supervisor title", MAX_TITLE);
855
+ bounded(intent.title, "governor title", MAX_TITLE);
641
856
  assertWorkId(intent.root_work_id);
642
857
  if (intent.mission) {
643
858
  const normalizedMission = normalizeMission({
@@ -646,79 +861,79 @@ function validateIntent(intent: SupervisorIntent, goalId: string): void {
646
861
  maxMilestones: intent.mission.max_milestones,
647
862
  });
648
863
  if (JSON.stringify(normalizedMission) !== JSON.stringify(intent.mission)) {
649
- throw new Error(`supervisor intent ${goalId} mission is not canonical`);
864
+ throw new Error(`governor intent ${goalId} mission is not canonical`);
650
865
  }
651
866
  if (!intent.replanning) {
652
- throw new Error(`supervisor intent ${goalId} mission requires replanning`);
867
+ throw new Error(`governor intent ${goalId} mission requires replanning`);
653
868
  }
654
869
  }
655
870
  const specialists = normalizeWorkflowSpecialists(intent.specialists);
656
871
  if (JSON.stringify(specialists) !== JSON.stringify(intent.specialists)) {
657
- throw new Error(`supervisor intent ${goalId} specialists are not canonical`);
872
+ throw new Error(`governor intent ${goalId} specialists are not canonical`);
658
873
  }
659
874
  if (JSON.stringify(normalizeLimits(intent.limits)) !== JSON.stringify(intent.limits)) {
660
- throw new Error(`supervisor intent ${goalId} limits are not canonical`);
875
+ throw new Error(`governor intent ${goalId} limits are not canonical`);
661
876
  }
662
877
  if (
663
878
  JSON.stringify(normalizeAutomation(intent.automation)) !== JSON.stringify(intent.automation)
664
879
  ) {
665
- throw new Error(`supervisor intent ${goalId} automation policy is not canonical`);
880
+ throw new Error(`governor intent ${goalId} automation policy is not canonical`);
666
881
  }
667
882
  if (intent.replanning) validateReplanning(intent.replanning, specialists, intent.limits, goalId);
668
883
  }
669
884
 
670
- function normalizeMission(input: CreateSupervisorMissionInput): SupervisorMission {
885
+ function normalizeMission(input: CreateGovernorMissionInput): GovernorMission {
671
886
  if (!Array.isArray(input.acceptance)) {
672
- throw new Error("supervisor mission acceptance must be an array");
887
+ throw new Error("governor mission acceptance must be an array");
673
888
  }
674
889
  if (input.acceptance.length < 1 || input.acceptance.length > MAX_ACCEPTANCE) {
675
- throw new Error(`supervisor mission acceptance must contain 1 to ${MAX_ACCEPTANCE} criteria`);
890
+ throw new Error(`governor mission acceptance must contain 1 to ${MAX_ACCEPTANCE} criteria`);
676
891
  }
677
892
  const acceptance = input.acceptance.map((criterion, index) =>
678
- bounded(criterion, `supervisor mission acceptance[${index}]`, 500),
893
+ bounded(criterion, `governor mission acceptance[${index}]`, 500),
679
894
  );
680
895
  if (new Set(acceptance).size !== acceptance.length) {
681
- throw new Error("supervisor mission acceptance criteria must be unique");
896
+ throw new Error("governor mission acceptance criteria must be unique");
682
897
  }
683
898
  return {
684
- objective: bounded(input.objective, "supervisor mission objective", MAX_OBJECTIVE),
899
+ objective: bounded(input.objective, "governor mission objective", MAX_OBJECTIVE),
685
900
  acceptance,
686
- max_milestones: positive(input.maxMilestones ?? 4, "supervisor mission max_milestones", 20),
901
+ max_milestones: positive(input.maxMilestones ?? 4, "governor mission max_milestones", 20),
687
902
  };
688
903
  }
689
904
 
690
- function missionTitle(mission: SupervisorMission): string {
905
+ function missionTitle(mission: GovernorMission): string {
691
906
  return mission.objective.length <= MAX_TITLE
692
907
  ? mission.objective
693
908
  : `${mission.objective.slice(0, MAX_TITLE - 1).trimEnd()}…`;
694
909
  }
695
910
 
696
- function normalizeLimits(input: Partial<SupervisorLimits> | undefined): SupervisorLimits {
911
+ function normalizeLimits(input: Partial<GovernorLimits> | undefined): GovernorLimits {
697
912
  return {
698
- max_cycles: positive(input?.max_cycles ?? 50, "supervisor max_cycles", 1_000),
913
+ max_cycles: positive(input?.max_cycles ?? 50, "governor max_cycles", 1_000),
699
914
  max_runtime_ms: positive(
700
915
  input?.max_runtime_ms ?? 4 * 60 * 60 * 1_000,
701
- "supervisor max_runtime_ms",
916
+ "governor max_runtime_ms",
702
917
  7 * 24 * 60 * 60 * 1_000,
703
918
  ),
704
- max_parallel_work: positive(input?.max_parallel_work ?? 1, "supervisor max_parallel_work", 20),
919
+ max_parallel_work: positive(input?.max_parallel_work ?? 1, "governor max_parallel_work", 20),
705
920
  max_total_attempts: positive(
706
921
  input?.max_total_attempts ?? 100,
707
- "supervisor max_total_attempts",
922
+ "governor max_total_attempts",
708
923
  10_000,
709
924
  ),
710
925
  max_agents_per_work: positive(
711
926
  input?.max_agents_per_work ?? 20,
712
- "supervisor max_agents_per_work",
927
+ "governor max_agents_per_work",
713
928
  1_000,
714
929
  ),
715
- agent_concurrency: positive(input?.agent_concurrency ?? 4, "supervisor agent_concurrency", 100),
930
+ agent_concurrency: positive(input?.agent_concurrency ?? 4, "governor agent_concurrency", 100),
716
931
  };
717
932
  }
718
933
 
719
934
  function normalizeAutomation(
720
- input: Partial<SupervisorAutomationPolicy> | undefined,
721
- ): SupervisorAutomationPolicy {
935
+ input: Partial<GovernorAutomationPolicy> | undefined,
936
+ ): GovernorAutomationPolicy {
722
937
  return {
723
938
  accept_passing_proof: boolean(input?.accept_passing_proof ?? false, "accept_passing_proof"),
724
939
  resume_approved: boolean(input?.resume_approved ?? true, "resume_approved"),
@@ -727,10 +942,10 @@ function normalizeAutomation(
727
942
  }
728
943
 
729
944
  function normalizeReplanning(
730
- input: CreateSupervisorReplanningInput,
945
+ input: CreateGovernorReplanningInput,
731
946
  specialistsInput: Readonly<Record<string, WorkflowSpecialistProfile>>,
732
- limits: SupervisorLimits,
733
- ): SupervisorReplanningPolicy {
947
+ limits: GovernorLimits,
948
+ ): GovernorReplanningPolicy {
734
949
  const specialists = normalizeWorkflowSpecialists(specialistsInput);
735
950
  const plannerSpecialist = bounded(input.plannerSpecialist, "planner specialist", 100);
736
951
  if (!specialists[plannerSpecialist]) {
@@ -794,17 +1009,17 @@ function normalizeReplanning(
794
1009
  }
795
1010
 
796
1011
  function normalizeReviewPolicy(
797
- input: NonNullable<CreateSupervisorReplanningInput["review"]>,
1012
+ input: NonNullable<CreateGovernorReplanningInput["review"]>,
798
1013
  specialists: Record<string, WorkflowSpecialistProfile>,
799
1014
  plannerSpecialist: string,
800
- limits: SupervisorLimits,
801
- ): NonNullable<SupervisorReplanningPolicy["review"]> {
1015
+ limits: GovernorLimits,
1016
+ ): NonNullable<GovernorReplanningPolicy["review"]> {
802
1017
  if (!Array.isArray(input.reviewerSpecialists) || input.reviewerSpecialists.length < 1) {
803
1018
  throw new Error("replanning review requires at least one reviewer specialist");
804
1019
  }
805
- if (input.reviewerSpecialists.length > MAX_SUPERVISOR_PLAN_REVIEWERS) {
1020
+ if (input.reviewerSpecialists.length > MAX_GOVERNOR_PLAN_REVIEWERS) {
806
1021
  throw new Error(
807
- `replanning review cannot exceed ${MAX_SUPERVISOR_PLAN_REVIEWERS} reviewer specialists`,
1022
+ `replanning review cannot exceed ${MAX_GOVERNOR_PLAN_REVIEWERS} reviewer specialists`,
808
1023
  );
809
1024
  }
810
1025
  const reviewerSpecialists = input.reviewerSpecialists.map((specialist, index) =>
@@ -824,7 +1039,7 @@ function normalizeReviewPolicy(
824
1039
  const maxRevisionRounds = nonNegative(
825
1040
  input.maxRevisionRounds,
826
1041
  "replanning review max_revision_rounds",
827
- MAX_SUPERVISOR_PLAN_REVISION_ROUNDS,
1042
+ MAX_GOVERNOR_PLAN_REVISION_ROUNDS,
828
1043
  );
829
1044
  const worstCaseAgents =
830
1045
  1 + reviewerSpecialists.length * (maxRevisionRounds + 1) + maxRevisionRounds;
@@ -840,24 +1055,24 @@ function normalizeReviewPolicy(
840
1055
  }
841
1056
 
842
1057
  function validateReplanning(
843
- policy: SupervisorReplanningPolicy,
1058
+ policy: GovernorReplanningPolicy,
844
1059
  specialists: Record<string, WorkflowSpecialistProfile>,
845
- limits: SupervisorLimits,
1060
+ limits: GovernorLimits,
846
1061
  goalId: string,
847
1062
  ): void {
848
1063
  if (!specialists[policy.planner_specialist]) {
849
- throw new Error(`supervisor ${goalId} planner specialist is not in the frozen team`);
1064
+ throw new Error(`governor ${goalId} planner specialist is not in the frozen team`);
850
1065
  }
851
1066
  boolean(policy.auto_apply, "replanning auto_apply");
852
1067
  positive(policy.max_replans, "replanning max_replans", 100);
853
1068
  positive(policy.max_work_items_per_plan, "replanning max_work_items_per_plan", 25);
854
1069
  positive(policy.max_total_work_items, "replanning max_total_work_items", 100);
855
1070
  if (policy.max_work_items_per_plan > policy.max_total_work_items) {
856
- throw new Error(`supervisor ${goalId} replanning work-item bounds are inconsistent`);
1071
+ throw new Error(`governor ${goalId} replanning work-item bounds are inconsistent`);
857
1072
  }
858
1073
  const entries = Object.entries(policy.templates ?? {});
859
1074
  if (entries.length < 1 || entries.length > MAX_TEMPLATES) {
860
- throw new Error(`supervisor ${goalId} has invalid replanning templates`);
1075
+ throw new Error(`governor ${goalId} has invalid replanning templates`);
861
1076
  }
862
1077
  let rootCapable = false;
863
1078
  for (const [id, template] of entries) {
@@ -870,32 +1085,32 @@ function validateReplanning(
870
1085
  template.max_attempts > 100 ||
871
1086
  typeof template.root !== "boolean"
872
1087
  ) {
873
- throw new Error(`supervisor ${goalId} replanning template ${id} is invalid`);
1088
+ throw new Error(`governor ${goalId} replanning template ${id} is invalid`);
874
1089
  }
875
1090
  rootCapable ||= template.root;
876
1091
  }
877
- if (!rootCapable) throw new Error(`supervisor ${goalId} has no root-capable replanning template`);
1092
+ if (!rootCapable) throw new Error(`governor ${goalId} has no root-capable replanning template`);
878
1093
  if (policy.review !== undefined) {
879
1094
  const review = policy.review;
880
1095
  if (
881
1096
  !Array.isArray(review.reviewer_specialists) ||
882
1097
  review.reviewer_specialists.length < 1 ||
883
- review.reviewer_specialists.length > MAX_SUPERVISOR_PLAN_REVIEWERS ||
1098
+ review.reviewer_specialists.length > MAX_GOVERNOR_PLAN_REVIEWERS ||
884
1099
  !Number.isSafeInteger(review.max_revision_rounds) ||
885
1100
  review.max_revision_rounds < 0 ||
886
- review.max_revision_rounds > MAX_SUPERVISOR_PLAN_REVISION_ROUNDS
1101
+ review.max_revision_rounds > MAX_GOVERNOR_PLAN_REVISION_ROUNDS
887
1102
  ) {
888
- throw new Error(`supervisor ${goalId} replanning review policy is invalid`);
1103
+ throw new Error(`governor ${goalId} replanning review policy is invalid`);
889
1104
  }
890
1105
  if (new Set(review.reviewer_specialists).size !== review.reviewer_specialists.length) {
891
- throw new Error(`supervisor ${goalId} replanning reviewers are not unique`);
1106
+ throw new Error(`governor ${goalId} replanning reviewers are not unique`);
892
1107
  }
893
1108
  for (const specialist of review.reviewer_specialists) {
894
1109
  if (!specialists[specialist]) {
895
- throw new Error(`supervisor ${goalId} reviewer specialist is not in the frozen team`);
1110
+ throw new Error(`governor ${goalId} reviewer specialist is not in the frozen team`);
896
1111
  }
897
1112
  if (specialist === policy.planner_specialist) {
898
- throw new Error(`supervisor ${goalId} reviewer specialist cannot be planner`);
1113
+ throw new Error(`governor ${goalId} reviewer specialist cannot be planner`);
899
1114
  }
900
1115
  }
901
1116
  const worstCaseAgents =
@@ -903,7 +1118,7 @@ function validateReplanning(
903
1118
  review.reviewer_specialists.length * (review.max_revision_rounds + 1) +
904
1119
  review.max_revision_rounds;
905
1120
  if (worstCaseAgents > limits.max_agents_per_work) {
906
- throw new Error(`supervisor ${goalId} review policy exceeds the agent budget`);
1121
+ throw new Error(`governor ${goalId} review policy exceeds the agent budget`);
907
1122
  }
908
1123
  }
909
1124
  }
@@ -911,12 +1126,12 @@ function validateReplanning(
911
1126
  function collectGovernedWork(
912
1127
  coordRoot: string,
913
1128
  originalRootWorkId: string,
914
- plans: SupervisorPlanHistory,
1129
+ plans: GovernorPlanHistory,
915
1130
  active: WorkRecord[],
916
1131
  ): WorkRecord[] {
917
1132
  const governed = new Map<string, WorkRecord>();
918
1133
  if (workIntentExists(coordRoot, originalRootWorkId)) {
919
- for (const record of collectSupervisorWork(coordRoot, originalRootWorkId)) {
1134
+ for (const record of collectGovernorWork(coordRoot, originalRootWorkId)) {
920
1135
  governed.set(record.intent.id, record);
921
1136
  }
922
1137
  }
@@ -934,17 +1149,17 @@ function missionRootId(goalId: string): string {
934
1149
  return `${goalId.slice(0, 87)}-mission-root`;
935
1150
  }
936
1151
 
937
- function supervisorDir(coordRoot: string, goalId: string): string {
938
- assertSupervisorId(goalId);
939
- return join(resolve(coordRoot), ".harnery", "supervisors", goalId);
1152
+ function governorDir(coordRoot: string, goalId: string): string {
1153
+ assertGovernorId(goalId);
1154
+ return join(resolve(coordRoot), ".harnery", "governors", goalId);
940
1155
  }
941
1156
 
942
- function supervisorIntentPath(coordRoot: string, goalId: string): string {
943
- return join(supervisorDir(coordRoot, goalId), "intent.json");
1157
+ function governorIntentPath(coordRoot: string, goalId: string): string {
1158
+ return join(governorDir(coordRoot, goalId), "intent.json");
944
1159
  }
945
1160
 
946
- function supervisorLeaseIsLive(coordRoot: string, goalId: string): boolean {
947
- const lease = readLease(join(supervisorDir(coordRoot, goalId), "lease.json"));
1161
+ function governorLeaseIsLive(coordRoot: string, goalId: string): boolean {
1162
+ const lease = readLease(join(governorDir(coordRoot, goalId), "lease.json"));
948
1163
  return lease ? leaseIsLive(lease) : false;
949
1164
  }
950
1165
 
@@ -986,7 +1201,7 @@ function leaseIsLive(lease: { pid: number; host: string; created_at: string }):
986
1201
  function writePrivateJson(path: string, value: unknown): void {
987
1202
  const body = `${JSON.stringify(value, null, 2)}\n`;
988
1203
  if (Buffer.byteLength(body) > MAX_INTENT_BYTES) {
989
- throw new Error(`supervisor intent exceeds ${MAX_INTENT_BYTES} bytes`);
1204
+ throw new Error(`governor intent exceeds ${MAX_INTENT_BYTES} bytes`);
990
1205
  }
991
1206
  writeFileSync(path, body, { encoding: "utf8", flag: "wx", mode: 0o600 });
992
1207
  chmodSync(path, 0o600);
@@ -1015,7 +1230,7 @@ function nonNegative(value: unknown, field: string, max: number): number {
1015
1230
  }
1016
1231
 
1017
1232
  function boolean(value: unknown, field: string): boolean {
1018
- if (typeof value !== "boolean") throw new Error(`supervisor ${field} must be boolean`);
1233
+ if (typeof value !== "boolean") throw new Error(`governor ${field} must be boolean`);
1019
1234
  return value;
1020
1235
  }
1021
1236