approval-md 0.1.0 → 0.3.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 (416) hide show
  1. package/README.md +629 -559
  2. package/SPEC.md +99 -24
  3. package/dist/src/adapters/agentmail.d.ts +426 -0
  4. package/dist/src/adapters/agentmail.js +2 -2
  5. package/dist/src/adapters/conformance.d.ts +149 -0
  6. package/dist/src/adapters/contract.d.ts +628 -0
  7. package/dist/src/adapters/contract.js +110 -16
  8. package/dist/src/adapters/contract.js.map +1 -1
  9. package/dist/src/adapters/email.d.ts +324 -0
  10. package/dist/src/adapters/env-passphrase.d.ts +93 -0
  11. package/dist/src/adapters/public.d.ts +11 -0
  12. package/dist/src/adapters/public.js +11 -0
  13. package/dist/src/adapters/public.js.map +1 -0
  14. package/dist/src/adapters/registry.d.ts +59 -0
  15. package/dist/src/adapters/registry.js +2 -1
  16. package/dist/src/adapters/registry.js.map +1 -1
  17. package/dist/src/adapters/smtp.d.ts +213 -0
  18. package/dist/src/adapters/vault-provider.d.ts +114 -0
  19. package/dist/src/adapters/vault-provider.js +3 -3
  20. package/dist/src/adapters/zzz.d.ts +66 -0
  21. package/dist/src/adapters/zzz.js +299 -0
  22. package/dist/src/adapters/zzz.js.map +1 -0
  23. package/dist/src/channels/batch.d.ts +109 -0
  24. package/dist/src/channels/cli.d.ts +193 -0
  25. package/dist/src/channels/conformance.d.ts +92 -0
  26. package/dist/src/channels/contract.d.ts +656 -0
  27. package/dist/src/channels/contract.js +200 -7
  28. package/dist/src/channels/contract.js.map +1 -1
  29. package/dist/src/channels/payload-view.d.ts +35 -0
  30. package/dist/src/channels/render-queue.d.ts +149 -0
  31. package/dist/src/channels/tagging.d.ts +196 -0
  32. package/dist/src/channels/telegram.d.ts +1944 -0
  33. package/dist/src/channels/telegram.js +218 -23
  34. package/dist/src/channels/telegram.js.map +1 -1
  35. package/dist/src/channels/web.d.ts +350 -0
  36. package/dist/src/channels/web.js +17 -0
  37. package/dist/src/channels/web.js.map +1 -1
  38. package/dist/src/cli/adapter.d.ts +90 -0
  39. package/dist/src/cli/adapter.js +25 -15
  40. package/dist/src/cli/adapter.js.map +1 -1
  41. package/dist/src/cli/amend.d.ts +59 -0
  42. package/dist/src/cli/amend.js +214 -30
  43. package/dist/src/cli/amend.js.map +1 -1
  44. package/dist/src/cli/args.d.ts +43 -0
  45. package/dist/src/cli/attest.d.ts +50 -0
  46. package/dist/src/cli/attest.js +134 -7
  47. package/dist/src/cli/attest.js.map +1 -1
  48. package/dist/src/cli/audit-card.d.ts +62 -0
  49. package/dist/src/cli/audit.d.ts +59 -0
  50. package/dist/src/cli/channel-telegram.d.ts +879 -0
  51. package/dist/src/cli/channel-telegram.js +311 -13
  52. package/dist/src/cli/channel-telegram.js.map +1 -1
  53. package/dist/src/cli/channel-web.d.ts +131 -0
  54. package/dist/src/cli/channel.d.ts +80 -0
  55. package/dist/src/cli/channel.js +9 -0
  56. package/dist/src/cli/channel.js.map +1 -1
  57. package/dist/src/cli/checkpoint-tap.d.ts +169 -0
  58. package/dist/src/cli/codex-bridge.d.ts +819 -0
  59. package/dist/src/cli/codex-bridge.js +1607 -0
  60. package/dist/src/cli/codex-bridge.js.map +1 -0
  61. package/dist/src/cli/codex.d.ts +2 -0
  62. package/dist/src/cli/codex.js +469 -0
  63. package/dist/src/cli/codex.js.map +1 -0
  64. package/dist/src/cli/coverage.d.ts +61 -0
  65. package/dist/src/cli/daemon.d.ts +120 -0
  66. package/dist/src/cli/daemon.js +4 -1
  67. package/dist/src/cli/daemon.js.map +1 -1
  68. package/dist/src/cli/doctor.d.ts +129 -0
  69. package/dist/src/cli/doctor.js +586 -17
  70. package/dist/src/cli/doctor.js.map +1 -1
  71. package/dist/src/cli/env.d.ts +65 -0
  72. package/dist/src/cli/execute.d.ts +202 -0
  73. package/dist/src/cli/execute.js +25 -2
  74. package/dist/src/cli/execute.js.map +1 -1
  75. package/dist/src/cli/exit-codes.d.ts +73 -0
  76. package/dist/src/cli/feedback.d.ts +60 -0
  77. package/dist/src/cli/gate-window.d.ts +40 -0
  78. package/dist/src/cli/gate.d.ts +68 -0
  79. package/dist/src/cli/git-scope.d.ts +190 -0
  80. package/dist/src/cli/gloss-attach.d.ts +85 -0
  81. package/dist/src/cli/gloss-codex-child.d.ts +9 -0
  82. package/dist/src/cli/gloss-codex.d.ts +24 -0
  83. package/dist/src/cli/gloss-options.d.ts +42 -0
  84. package/dist/src/cli/gloss.d.ts +265 -0
  85. package/dist/src/cli/help.d.ts +107 -0
  86. package/dist/src/cli/help.js +320 -93
  87. package/dist/src/cli/help.js.map +1 -1
  88. package/dist/src/cli/hook-codex.d.ts +126 -0
  89. package/dist/src/cli/hook-codex.js +226 -0
  90. package/dist/src/cli/hook-codex.js.map +1 -0
  91. package/dist/src/cli/hook.d.ts +787 -0
  92. package/dist/src/cli/hook.js +1235 -181
  93. package/dist/src/cli/hook.js.map +1 -1
  94. package/dist/src/cli/import.d.ts +35 -0
  95. package/dist/src/cli/import.js +1 -1
  96. package/dist/src/cli/import.js.map +1 -1
  97. package/dist/src/cli/init.d.ts +84 -0
  98. package/dist/src/cli/init.js +2 -2
  99. package/dist/src/cli/init.js.map +1 -1
  100. package/dist/src/cli/instructions.d.ts +23 -0
  101. package/dist/src/cli/journal.d.ts +41 -0
  102. package/dist/src/cli/log-advance.d.ts +287 -0
  103. package/dist/src/cli/log-advance.js +102 -11
  104. package/dist/src/cli/log-advance.js.map +1 -1
  105. package/dist/src/cli/log-anchor.d.ts +176 -0
  106. package/dist/src/cli/log-checkpoint.d.ts +22 -0
  107. package/dist/src/cli/log-sync.d.ts +243 -0
  108. package/dist/src/cli/log-verbs.d.ts +16 -0
  109. package/dist/src/cli/log-verbs.js +7 -1
  110. package/dist/src/cli/log-verbs.js.map +1 -1
  111. package/dist/src/cli/long-help.d.ts +70 -0
  112. package/dist/src/cli/main.d.ts +77 -0
  113. package/dist/src/cli/main.js +159 -7
  114. package/dist/src/cli/main.js.map +1 -1
  115. package/dist/src/cli/mcp.d.ts +52 -0
  116. package/dist/src/cli/paths.d.ts +56 -0
  117. package/dist/src/cli/payload.d.ts +58 -0
  118. package/dist/src/cli/policy-apply.d.ts +195 -0
  119. package/dist/src/cli/policy-apply.js +573 -0
  120. package/dist/src/cli/policy-apply.js.map +1 -0
  121. package/dist/src/cli/policy.d.ts +43 -0
  122. package/dist/src/cli/policy.js +14 -1
  123. package/dist/src/cli/policy.js.map +1 -1
  124. package/dist/src/cli/preflight.d.ts +501 -0
  125. package/dist/src/cli/preflight.js +689 -45
  126. package/dist/src/cli/preflight.js.map +1 -1
  127. package/dist/src/cli/progress.d.ts +78 -0
  128. package/dist/src/cli/prompt.d.ts +209 -0
  129. package/dist/src/cli/quickstart.d.ts +46 -0
  130. package/dist/src/cli/quickstart.js +297 -0
  131. package/dist/src/cli/quickstart.js.map +1 -0
  132. package/dist/src/cli/records.d.ts +34 -0
  133. package/dist/src/cli/render.d.ts +22 -0
  134. package/dist/src/cli/sandbox.d.ts +51 -0
  135. package/dist/src/cli/sandbox.js +17 -1
  136. package/dist/src/cli/sandbox.js.map +1 -1
  137. package/dist/src/cli/scaffold.d.ts +79 -0
  138. package/dist/src/cli/scaffold.js +1 -1
  139. package/dist/src/cli/setup-adapter.d.ts +137 -0
  140. package/dist/src/cli/setup-adapter.js +38 -4
  141. package/dist/src/cli/setup-adapter.js.map +1 -1
  142. package/dist/src/cli/setup-channel.d.ts +126 -0
  143. package/dist/src/cli/setup-channel.js +28 -1
  144. package/dist/src/cli/setup-channel.js.map +1 -1
  145. package/dist/src/cli/setup-checkpoint.d.ts +57 -0
  146. package/dist/src/cli/setup-common.d.ts +277 -0
  147. package/dist/src/cli/setup-common.js +3 -2
  148. package/dist/src/cli/setup-common.js.map +1 -1
  149. package/dist/src/cli/setup-flow.d.ts +287 -0
  150. package/dist/src/cli/setup-service.d.ts +96 -0
  151. package/dist/src/cli/setup.d.ts +204 -0
  152. package/dist/src/cli/setup.js +94 -2
  153. package/dist/src/cli/setup.js.map +1 -1
  154. package/dist/src/cli/style.d.ts +320 -0
  155. package/dist/src/cli/token.d.ts +39 -0
  156. package/dist/src/cli/up.d.ts +155 -0
  157. package/dist/src/cli/up.js +119 -53
  158. package/dist/src/cli/up.js.map +1 -1
  159. package/dist/src/cli/usage.d.ts +37 -0
  160. package/dist/src/cli/values.d.ts +40 -0
  161. package/dist/src/cli/values.js +3 -4
  162. package/dist/src/cli/values.js.map +1 -1
  163. package/dist/src/cli/vault.d.ts +59 -0
  164. package/dist/src/cli/vault.js +2 -2
  165. package/dist/src/cli/vault.js.map +1 -1
  166. package/dist/src/cli/verb-registry.d.ts +76 -0
  167. package/dist/src/cli/verb-registry.js +344 -11
  168. package/dist/src/cli/verb-registry.js.map +1 -1
  169. package/dist/src/cli/wordmark.d.ts +31 -0
  170. package/dist/src/cli/wordmark.js +2 -2
  171. package/dist/src/codex/broker.d.ts +229 -0
  172. package/dist/src/codex/broker.js +548 -0
  173. package/dist/src/codex/broker.js.map +1 -0
  174. package/dist/src/codex/doctor.d.ts +13 -0
  175. package/dist/src/codex/doctor.js +41 -0
  176. package/dist/src/codex/doctor.js.map +1 -0
  177. package/dist/src/codex/manifest.d.ts +49 -0
  178. package/dist/src/codex/manifest.js +103 -0
  179. package/dist/src/codex/manifest.js.map +1 -0
  180. package/dist/src/codex/runner.d.ts +178 -0
  181. package/dist/src/codex/runner.js +231 -0
  182. package/dist/src/codex/runner.js.map +1 -0
  183. package/dist/src/codex/serve.d.ts +56 -0
  184. package/dist/src/codex/serve.js +98 -0
  185. package/dist/src/codex/serve.js.map +1 -0
  186. package/dist/src/codex/templates.d.ts +41 -0
  187. package/dist/src/codex/templates.js +319 -0
  188. package/dist/src/codex/templates.js.map +1 -0
  189. package/dist/src/codex/trust.d.ts +19 -0
  190. package/dist/src/codex/trust.js +183 -0
  191. package/dist/src/codex/trust.js.map +1 -0
  192. package/dist/src/codex/workspace-commit.d.ts +219 -0
  193. package/dist/src/codex/workspace-commit.js +549 -0
  194. package/dist/src/codex/workspace-commit.js.map +1 -0
  195. package/dist/src/codex/workspace-plan.d.ts +131 -0
  196. package/dist/src/codex/workspace-plan.js +561 -0
  197. package/dist/src/codex/workspace-plan.js.map +1 -0
  198. package/dist/src/core/actor.d.ts +2 -0
  199. package/dist/src/core/actor.js +5 -0
  200. package/dist/src/core/actor.js.map +1 -0
  201. package/dist/src/core/advance-cycle.d.ts +221 -0
  202. package/dist/src/core/advance-cycle.js +66 -2
  203. package/dist/src/core/advance-cycle.js.map +1 -1
  204. package/dist/src/core/agents-md.d.ts +278 -0
  205. package/dist/src/core/agents-md.js +33 -31
  206. package/dist/src/core/agents-md.js.map +1 -1
  207. package/dist/src/core/apply-patch.d.ts +49 -0
  208. package/dist/src/core/apply-patch.js +266 -0
  209. package/dist/src/core/apply-patch.js.map +1 -0
  210. package/dist/src/core/attest.d.ts +635 -0
  211. package/dist/src/core/attest.js +326 -4
  212. package/dist/src/core/attest.js.map +1 -1
  213. package/dist/src/core/audit.d.ts +510 -0
  214. package/dist/src/core/audit.js +13 -0
  215. package/dist/src/core/audit.js.map +1 -1
  216. package/dist/src/core/budgets.d.ts +238 -0
  217. package/dist/src/core/channel-owner.d.ts +213 -0
  218. package/dist/src/core/channel-owner.js +358 -0
  219. package/dist/src/core/channel-owner.js.map +1 -0
  220. package/dist/src/core/checkpoint.d.ts +500 -0
  221. package/dist/src/core/child-env.d.ts +88 -0
  222. package/dist/src/core/clock.d.ts +52 -0
  223. package/dist/src/core/command-class.d.ts +697 -0
  224. package/dist/src/core/command-class.js +713 -25
  225. package/dist/src/core/command-class.js.map +1 -1
  226. package/dist/src/core/commit-guard.d.ts +272 -0
  227. package/dist/src/core/commit-guard.js +424 -0
  228. package/dist/src/core/commit-guard.js.map +1 -0
  229. package/dist/src/core/coverage-sources/adapter.d.ts +40 -0
  230. package/dist/src/core/coverage-sources/gh.d.ts +48 -0
  231. package/dist/src/core/coverage-sources/git.d.ts +101 -0
  232. package/dist/src/core/coverage.d.ts +217 -0
  233. package/dist/src/core/credential-spec.d.ts +72 -0
  234. package/dist/src/core/daemon-actor.d.ts +45 -0
  235. package/dist/src/core/daemon-actor.js +54 -0
  236. package/dist/src/core/daemon-actor.js.map +1 -0
  237. package/dist/src/core/dark-session.d.ts +432 -0
  238. package/dist/src/core/dark-session.js +266 -82
  239. package/dist/src/core/dark-session.js.map +1 -1
  240. package/dist/src/core/decision-refusal.d.ts +206 -0
  241. package/dist/src/core/decision-refusal.js +24 -2
  242. package/dist/src/core/decision-refusal.js.map +1 -1
  243. package/dist/src/core/env-file.d.ts +455 -0
  244. package/dist/src/core/env-file.js +60 -1
  245. package/dist/src/core/env-file.js.map +1 -1
  246. package/dist/src/core/execute.d.ts +871 -0
  247. package/dist/src/core/execute.js +59 -8
  248. package/dist/src/core/execute.js.map +1 -1
  249. package/dist/src/core/frontmatter.d.ts +78 -0
  250. package/dist/src/core/gate-window.d.ts +312 -0
  251. package/dist/src/core/gate.d.ts +1449 -0
  252. package/dist/src/core/gate.js +149 -14
  253. package/dist/src/core/gate.js.map +1 -1
  254. package/dist/src/core/gesture-refusal.d.ts +166 -0
  255. package/dist/src/core/gesture-refusal.js +188 -0
  256. package/dist/src/core/gesture-refusal.js.map +1 -0
  257. package/dist/src/core/git-run.d.ts +73 -0
  258. package/dist/src/core/harness-version.d.ts +157 -0
  259. package/dist/src/core/harness-version.js +4 -1
  260. package/dist/src/core/harness-version.js.map +1 -1
  261. package/dist/src/core/harness-wait.d.ts +55 -0
  262. package/dist/src/core/head-retry.d.ts +107 -0
  263. package/dist/src/core/instance.d.ts +310 -0
  264. package/dist/src/core/instance.js +113 -0
  265. package/dist/src/core/instance.js.map +1 -1
  266. package/dist/src/core/intake-limits.d.ts +247 -0
  267. package/dist/src/core/jcs.d.ts +52 -0
  268. package/dist/src/core/journal.d.ts +144 -0
  269. package/dist/src/core/live-draw.d.ts +436 -0
  270. package/dist/src/core/log-reconcile.d.ts +89 -0
  271. package/dist/src/core/log-subscribe.d.ts +36 -0
  272. package/dist/src/core/log-subscribe.js +162 -0
  273. package/dist/src/core/log-subscribe.js.map +1 -0
  274. package/dist/src/core/log.d.ts +316 -0
  275. package/dist/src/core/log.js.map +1 -1
  276. package/dist/src/core/loop.d.ts +274 -0
  277. package/dist/src/core/loop.js +11 -0
  278. package/dist/src/core/loop.js.map +1 -1
  279. package/dist/src/core/md-fence.d.ts +41 -0
  280. package/dist/src/core/money.d.ts +147 -0
  281. package/dist/src/core/payload-census.d.ts +74 -0
  282. package/dist/src/core/payload-store.d.ts +175 -0
  283. package/dist/src/core/payload.d.ts +71 -0
  284. package/dist/src/core/policy-diff.d.ts +292 -0
  285. package/dist/src/core/policy-diff.js +27 -4
  286. package/dist/src/core/policy-diff.js.map +1 -1
  287. package/dist/src/core/policy-expectations.d.ts +199 -0
  288. package/dist/src/core/policy-explain.d.ts +160 -0
  289. package/dist/src/core/policy-explain.js +63 -3
  290. package/dist/src/core/policy-explain.js.map +1 -1
  291. package/dist/src/core/policy-load.d.ts +567 -0
  292. package/dist/src/core/policy-load.js +36 -6
  293. package/dist/src/core/policy-load.js.map +1 -1
  294. package/dist/src/core/policy-match.d.ts +324 -0
  295. package/dist/src/core/policy-match.js +72 -9
  296. package/dist/src/core/policy-match.js.map +1 -1
  297. package/dist/src/core/policy-proposal.d.ts +317 -0
  298. package/dist/src/core/policy-proposal.js +102 -2
  299. package/dist/src/core/policy-proposal.js.map +1 -1
  300. package/dist/src/core/prompt-layout.d.ts +221 -0
  301. package/dist/src/core/protected-path-guard.d.ts +566 -0
  302. package/dist/src/core/protected-path-guard.js +848 -55
  303. package/dist/src/core/protected-path-guard.js.map +1 -1
  304. package/dist/src/core/question-preempted.d.ts +141 -0
  305. package/dist/src/core/question-preempted.js +152 -0
  306. package/dist/src/core/question-preempted.js.map +1 -0
  307. package/dist/src/core/read-scope.d.ts +172 -0
  308. package/dist/src/core/read-scope.js +252 -0
  309. package/dist/src/core/read-scope.js.map +1 -0
  310. package/dist/src/core/registration.d.ts +25 -0
  311. package/dist/src/core/reindex.d.ts +99 -0
  312. package/dist/src/core/sampler.d.ts +313 -0
  313. package/dist/src/core/sandbox.d.ts +371 -0
  314. package/dist/src/core/sandbox.js +190 -1
  315. package/dist/src/core/sandbox.js.map +1 -1
  316. package/dist/src/core/seal.d.ts +165 -0
  317. package/dist/src/core/sender-identity.d.ts +476 -0
  318. package/dist/src/core/sender-identity.js +572 -0
  319. package/dist/src/core/sender-identity.js.map +1 -0
  320. package/dist/src/core/shlex.d.ts +102 -0
  321. package/dist/src/core/shlex.js +159 -0
  322. package/dist/src/core/shlex.js.map +1 -0
  323. package/dist/src/core/state.d.ts +505 -0
  324. package/dist/src/core/task-file.d.ts +185 -0
  325. package/dist/src/core/telegram-config.d.ts +93 -0
  326. package/dist/src/core/token.d.ts +409 -0
  327. package/dist/src/core/token.js +21 -38
  328. package/dist/src/core/token.js.map +1 -1
  329. package/dist/src/core/validate.d.ts +138 -0
  330. package/dist/src/core/values.d.ts +147 -0
  331. package/dist/src/core/values.js +36 -1
  332. package/dist/src/core/values.js.map +1 -1
  333. package/dist/src/core/vault.d.ts +291 -0
  334. package/dist/src/core/verified-snapshot.d.ts +204 -0
  335. package/dist/src/core/verify.d.ts +336 -0
  336. package/dist/src/core/version.d.ts +8 -0
  337. package/dist/src/core/wysiwys.d.ts +370 -0
  338. package/dist/src/daemon/advance-child.d.ts +39 -0
  339. package/dist/src/daemon/advance.d.ts +476 -0
  340. package/dist/src/daemon/advance.js +25 -4
  341. package/dist/src/daemon/advance.js.map +1 -1
  342. package/dist/src/daemon/audit.d.ts +87 -0
  343. package/dist/src/daemon/daemon.d.ts +1180 -0
  344. package/dist/src/daemon/daemon.js +9 -0
  345. package/dist/src/daemon/daemon.js.map +1 -1
  346. package/dist/src/daemon/dark-session.d.ts +64 -0
  347. package/dist/src/daemon/draw-child.d.ts +36 -0
  348. package/dist/src/daemon/draw.d.ts +154 -0
  349. package/dist/src/daemon/git-evidence.d.ts +173 -0
  350. package/dist/src/daemon/git-evidence.js +1 -1
  351. package/dist/src/daemon/projection.d.ts +180 -0
  352. package/dist/src/daemon/prune.d.ts +207 -0
  353. package/dist/src/mcp/http.d.ts +113 -0
  354. package/dist/src/mcp/server.d.ts +265 -0
  355. package/dist/src/mcp/server.js +17 -1
  356. package/dist/src/mcp/server.js.map +1 -1
  357. package/docs/adapter-api.md +106 -0
  358. package/docs/cli-reference.md +1316 -63
  359. package/docs/codex-enforced-session.md +103 -0
  360. package/docs/codex-workspace-broker.md +118 -0
  361. package/package.json +14 -2
  362. package/schema/codex-instance.schema.json +82 -0
  363. package/schema/event.schema.json +539 -9
  364. package/schema/fixtures/codex-instance/invalid/unpinned-codex-version.json +40 -0
  365. package/schema/fixtures/codex-instance/valid/canonical.json +40 -0
  366. package/schema/fixtures/event/invalid/approval-granted-sender-hashed-false.json +20 -0
  367. package/schema/fixtures/event/invalid/approval-granted-sender-hashed-raw-id.json +20 -0
  368. package/schema/fixtures/event/invalid/audit-gesture-refused-human-actor.json +16 -0
  369. package/schema/fixtures/event/invalid/audit-gesture-refused-no-actor-no-sender.json +15 -0
  370. package/schema/fixtures/event/invalid/audit-gesture-refused-unknown-gesture.json +16 -0
  371. package/schema/fixtures/event/invalid/audit-question-preempted-agent-actor.json +16 -0
  372. package/schema/fixtures/event/invalid/audit-question-preempted-no-question-id.json +16 -0
  373. package/schema/fixtures/event/invalid/audit-question-preempted-unknown-source.json +15 -0
  374. package/schema/fixtures/event/invalid/gate-path-signed-off-absolute-path.json +14 -0
  375. package/schema/fixtures/event/invalid/gate-path-signed-off-agent-actor.json +14 -0
  376. package/schema/fixtures/event/invalid/gate-path-signed-off-missing-path.json +13 -0
  377. package/schema/fixtures/event/valid/approval-granted-sender-hashed.json +20 -0
  378. package/schema/fixtures/event/valid/audit-gesture-refused-review-note.json +21 -0
  379. package/schema/fixtures/event/valid/audit-gesture-refused-sender-key-unavailable.json +19 -0
  380. package/schema/fixtures/event/valid/audit-gesture-refused.json +19 -0
  381. package/schema/fixtures/event/valid/audit-question-preempted-no-verdict.json +16 -0
  382. package/schema/fixtures/event/valid/audit-question-preempted.json +20 -0
  383. package/schema/fixtures/event/valid/gate-path-signed-off.json +14 -0
  384. package/schema/fixtures/event/valid/harness-kind-claude-code.json +23 -0
  385. package/schema/fixtures/event/valid/harness-kind-codex.json +23 -0
  386. package/schema/fixtures/event/valid/harness-kind-cursor.json +23 -0
  387. package/schema/fixtures/event/valid/harness-kind-grok.json +23 -0
  388. package/schema/fixtures/event/valid/harness-kind-muse.json +23 -0
  389. package/schema/fixtures/policy/invalid/senders-half-keyed.json +20 -0
  390. package/schema/fixtures/policy/valid/canonical.json +1 -1
  391. package/schema/fixtures/policy/valid/senders-keyed.json +24 -0
  392. package/schema/fixtures/policy-md/valid/canonical.md +1 -1
  393. package/schema/fixtures/policy-md/valid/with-values.md +5 -7
  394. package/schema/fixtures/values/invalid/class-shaped.json +1 -1
  395. package/schema/fixtures/values/invalid/duplicate-entry.json +1 -1
  396. package/schema/fixtures/values/invalid/non-string-item.json +1 -1
  397. package/schema/fixtures/values/invalid/over-cap.json +1 -1
  398. package/schema/fixtures/values/invalid/unknown-key.json +1 -1
  399. package/schema/fixtures/values/invalid/version-float.json +1 -0
  400. package/schema/fixtures/values/invalid/version-integer.json +1 -0
  401. package/schema/fixtures/values/invalid/version-wrong-string.json +1 -0
  402. package/schema/fixtures/values/valid/empty-lists.json +2 -3
  403. package/schema/fixtures/values/valid/full.json +5 -7
  404. package/schema/fixtures/values/valid/minimal.json +1 -1
  405. package/schema/fixtures/values-md/invalid/schema-invalid.md +5 -3
  406. package/schema/fixtures/values-md/invalid/two-blocks.md +3 -3
  407. package/schema/fixtures/values-md/invalid/unterminated.md +2 -2
  408. package/schema/fixtures/values-md/invalid/version-1.md +69 -0
  409. package/schema/fixtures/values-md/invalid/version-unquoted.md +64 -0
  410. package/schema/fixtures/values-md/invalid/yaml-error.md +2 -2
  411. package/schema/fixtures/values-md/valid/absent.md +1 -1
  412. package/schema/fixtures/values-md/valid/with-values.md +5 -7
  413. package/schema/policy.schema.json +75 -3
  414. package/schema/values.schema.json +7 -11
  415. package/templates/codex/README.md +9 -0
  416. package/schema/fixtures/values/invalid/version-string.json +0 -1
@@ -0,0 +1,1449 @@
1
+ /**
2
+ * The gate: request lifecycle and write-boundary transition enforcement
3
+ * (SPEC.md §6.3, §7, §10.1).
4
+ *
5
+ * This is the module that decides whether a side effect may be authorized, and
6
+ * it is the only module that appends approval lifecycle events. Everything it
7
+ * knows it derives from the append-only log; everything it decides it decides
8
+ * before a byte is written.
9
+ *
10
+ * ## Four rules this module exists to enforce
11
+ *
12
+ * 1. **State is derived, never stored.** {@link requestState} rebuilds one
13
+ * action's approval state from the log alone. There is no status field, no
14
+ * cache, no in-memory session. The envelope's `state:` key is a projection
15
+ * written by the daemon *after* the event lands (SPEC.md §6.3), never a
16
+ * source this module reads.
17
+ * 2. **Illegal transitions are refused before append.** A second grant, a grant
18
+ * on a rejected request, a revoke of an executed action, a decision after the
19
+ * TTL — each is refused with its own machine-readable code and **nothing is
20
+ * appended**. The one deliberate exception is a failed budget check, which
21
+ * appends `budget.exceeded` *and then* refuses: a budget refusal is a fact
22
+ * about the world that an operator must be able to see afterwards, and a
23
+ * refusal nobody can audit is how quiet budget creep starts.
24
+ * 3. **No approval events off the manual path** (amended SPEC.md §6.3). An
25
+ * action whose class resolves to `supervised` or `autonomous` produces *no*
26
+ * `approval.*` record at all — {@link request} returns `proceed: true` and
27
+ * appends nothing. Its authorization is recorded by `execution.started`,
28
+ * which APRV-18 appends, and which is also where its budget is charged (see
29
+ * the consumption contract in `core/budgets.ts`).
30
+ * 4. **Time is assigned by the runtime, not by the caller** (amended SPEC.md
31
+ * §8, A2). No public function here takes a `ts`. TTL lapse, budget windows,
32
+ * and the timestamp stamped on every append all come from one read of
33
+ * {@link GateOptions.clock} — the real clock unless a caller injects one —
34
+ * made once per operation, so a gate decision is still replayable from its
35
+ * inputs while the party being judged no longer authors the clock it is
36
+ * judged by. Tests inject a fixed clock; production passes none.
37
+ *
38
+ * ## Lazy expiry — the named requirement
39
+ *
40
+ * A request expires when `ts > requestTs + defaults.approval_ttl`, **whether or
41
+ * not** an `approval.expired` event exists. Nothing may depend on a daemon
42
+ * having run: if the expiry sweep is asleep, a late grant must still be refused.
43
+ * {@link requestState} therefore computes expiry two ways — from the event, and
44
+ * lazily from the arithmetic — and treats them as equivalent.
45
+ *
46
+ * When {@link decide} refuses a decision because the TTL has lapsed and no
47
+ * `approval.expired` event exists yet, it **first appends that event** (actor
48
+ * {@link EXPIRY_ACTOR}) and then refuses. The alternative — refuse silently and
49
+ * leave the log claiming the request is still live — was rejected: the log is
50
+ * the truth, and a state every reader can derive but no reader can see recorded
51
+ * makes the log disagree with itself. The append is the same one
52
+ * {@link expire} would have made, so a later sweep is a no-op rather than a
53
+ * duplicate.
54
+ *
55
+ * ## `defaults.on_expiry`
56
+ *
57
+ * SPEC.md §5 defines exactly one value, `reject`. An expired request is
58
+ * terminal here under either setting: no grant, no reject, no revoke ever
59
+ * follows it. `on_expiry` is recorded in the `approval.expired` payload so the
60
+ * projection layer (M5) can render the envelope's `state:` as `rejected` rather
61
+ * than `expired` when the policy asks for it. Re-requesting the same action key
62
+ * after expiry is a *new* request and is allowed — the key has not executed, and
63
+ * refusing forever would make a lapsed TTL more punishing than a human's "no".
64
+ *
65
+ * ## The budgets contract (`core/budgets.ts`)
66
+ *
67
+ * That module obligates this one: every `approval.granted` this module appends
68
+ * carries `payload.est_cost_usd` (number, USD) and `payload.class` (the dotted
69
+ * class). `approval.requested` carries them too, so the grant can copy them from
70
+ * the request rather than re-derive them from a file that may have changed. An
71
+ * action that declared no cost is recorded as `0` — an authorization with no
72
+ * declared cost is still an authorization, and still counts as one action.
73
+ *
74
+ * ## Reads are verified, writes are compare-and-append (APRV-20)
75
+ *
76
+ * The gate no longer trusts the bytes it reads. {@link readGateRecords}
77
+ * delegates to `core/state.ts`, which runs the *same* chain verification
78
+ * `approval log verify` runs — one walk, one vocabulary — and refuses
79
+ * `log-corrupt` on anything that does not verify. The gate still does not
80
+ * *diagnose* corruption: it reports that the log is untrustworthy and points at
81
+ * `approval log verify` for the detail, because two modules with two opinions
82
+ * about what "corrupt" means is worse than one.
83
+ *
84
+ * Every append this module makes is authorized by something it read, so every
85
+ * append passes `expectedHead` — the `(seq, hash)` observed at that read. If any
86
+ * record landed in between, `appendEvent` refuses `head-moved` under its lock
87
+ * and nothing is written.
88
+ *
89
+ * Every writer of this module then re-derives and tries again, bounded
90
+ * (APRV-150 for the two harness writers, APRV-236 for {@link register},
91
+ * {@link request}, {@link decide}, {@link withdraw} and
92
+ * {@link finishHarnessExecution}): see {@link withHeadMovedRetry} and
93
+ * `core/head-retry.ts` for why a lost race is not a verdict, and why the retry
94
+ * is a new read plus new checks plus a new compare-and-append rather than a
95
+ * second attempt at the same write. {@link expire} is the one exception, and it
96
+ * needs none: it is materialisation the daemon's next tick performs again.
97
+ *
98
+ * It does not define execution tokens — `core/token.ts` does. {@link decide}'s
99
+ * grant path calls that module's `mintToken` at the seam APRV-17 documented,
100
+ * records only the digest in the `approval.granted` payload, and returns the raw
101
+ * token to its caller. {@link decide} still appends no `execution.*` event:
102
+ * spending a token is `core/token.ts`'s `consumeToken`.
103
+ *
104
+ * The one place this module writes an execution event is
105
+ * {@link consumeHarnessGrant} (APRV-117), and it is the exception that proves
106
+ * the rule: a harness grant mints no token, so nothing else in the system could
107
+ * record that it had been spent, and an authorization with no record of its
108
+ * spending is an authorization that never runs out. See that function for why
109
+ * the marker is `execution.started` and why no completion ever follows it.
110
+ */
111
+ import { type AttestationRefusalDetail } from "./attest.js";
112
+ import type { Reaction } from "./audit.js";
113
+ import { type BudgetVerdict } from "./budgets.js";
114
+ import { type IntakeVerdict } from "./intake-limits.js";
115
+ import { type ClockOptions } from "./clock.js";
116
+ import { type HarnessProvenance } from "./harness-version.js";
117
+ import { type AppendError, type AppendOptions, type EventRecord, type LogHead } from "./log.js";
118
+ import { type UsdInput } from "./money.js";
119
+ import { type Autonomy, type PolicyLoadResult } from "./policy-load.js";
120
+ import { type Resolution } from "./policy-match.js";
121
+ import { type DrawAsker, type DrawRefusalReason, type LiveDrawRecord } from "./live-draw.js";
122
+ import { LIVE_SELECTION, type LiveSelectorUnavailableReason } from "./sampler.js";
123
+ import { type Decision, type RequestState, type WithdrawReason } from "./state.js";
124
+ import { type ValidationError } from "./validate.js";
125
+ /**
126
+ * The approval-state derivation moved to `core/state.ts` in APRV-20 (finding
127
+ * S4: `gate.ts` and `token.ts` imported each other). It is re-exported here, its
128
+ * documented home, so every existing importer — the CLI, the tests — is
129
+ * unaffected by the move.
130
+ */
131
+ export { requestState, type Decision, type DeclaredAction, type ExecutionFacts, type RequestDerivation, type RequestState, WITHDRAW_REASONS, isWithdrawReason, type WithdrawReason, } from "./state.js";
132
+ /** Actor stamped on runtime-originated expiry events (SPEC.md §8 `system:`). */
133
+ export declare const EXPIRY_ACTOR = "system:gate";
134
+ /**
135
+ * The closed set of gate refusal codes. Agents branch on these, so the union is
136
+ * frozen public API in the same sense the exit codes are: adding a code is a
137
+ * spec change, redefining one is a breaking change.
138
+ */
139
+ export declare const GATE_REFUSAL_CODES: readonly [
140
+ /** Policy is unattested or its bytes changed (`core/attest.ts`). */
141
+ "policy-not-attested",
142
+ /**
143
+ * The policy attested now is not the policy the request was routed under
144
+ * (APRV-118, amended SPEC.md §5.2): the hash pinned on `approval.requested`
145
+ * differs from the hash in force at the moment of the grant.
146
+ *
147
+ * Distinct from `policy-not-attested`, and the distinction is the whole point.
148
+ * That code says the live file is unverified; this one says the file is
149
+ * perfectly verified and is a DIFFERENT file from the one that decided this
150
+ * action's autonomy, its limits, and its TTL. A human re-attested in between,
151
+ * so the routing that put the question in front of an approver was computed
152
+ * from rules nobody is enforcing any more, and a grant recorded here would
153
+ * claim a decision under rules the approver never saw. The pending request is
154
+ * void: nothing is appended, and the action is requested again so that it is
155
+ * routed, budgeted, and displayed under the policy actually in force.
156
+ */
157
+ "policy-drift",
158
+ /** The envelope failed `envelope.schema.json`, or the task file has none. */
159
+ "envelope-invalid",
160
+ /** The task file could not be read. */
161
+ "task-file-unreadable",
162
+ /** This task id already has a `task.registered` record. */
163
+ "task-already-registered",
164
+ /**
165
+ * The task has log history and the file no longer carries an envelope
166
+ * (APRV-63).
167
+ *
168
+ * Observed live in APRV-60: a third-party rewrite of a task file dropped the
169
+ * `approval:` key it did not recognize. Without this code the file reads as an
170
+ * ordinary envelope-less task, and a re-registration from a stripped file
171
+ * would narrow the record silently — declaring fewer actions, or none, for a
172
+ * task the log already says declared them. The loss is named instead, and the
173
+ * envelope is restored by a human from the log; nothing here repairs a file.
174
+ */
175
+ "envelope-missing",
176
+ /** No `task.registered` record for this task id. */
177
+ "not-registered",
178
+ /** The task is registered but declares no action with this key (SPEC.md §7). */
179
+ "action-not-registered",
180
+ /** A live `approval.requested` for this action key already exists. */
181
+ "duplicate-request",
182
+ /** The action key already has an `execution.*` record (idempotency). */
183
+ "already-executed",
184
+ /**
185
+ * APRV-14 verdicts failed; a `budget.exceeded` event was appended. Covers
186
+ * class limits, `policy.budgets`, and — since S2 — the registered envelope's
187
+ * own `budget.max_cost_usd`, which appears as a `task`-scoped verdict in
188
+ * `verdicts` and in the appended event's payload.
189
+ */
190
+ "budget-exceeded",
191
+ /**
192
+ * The approver's queue is at the ceiling the policy declared (SPEC.md §5.2's
193
+ * `limits.max_pending`, per class or on a `budgets` scope; APRV-173).
194
+ *
195
+ * A limit on ATTENTION rather than on money, which is why it is its own code
196
+ * and why it fires where it does: after the legality checks that say whether
197
+ * this request may exist at all, and before budgets, which are about the
198
+ * world's exposure rather than the human's. An agent that floods the queue
199
+ * with cheap in-budget requests spends nothing and still defeats the gate,
200
+ * because an approver facing two hundred prompts stops reading them and
201
+ * starts clearing them.
202
+ *
203
+ * Nothing is appended, deliberately, and this is the one refusal shaped
204
+ * differently from `budget-exceeded` on purpose (Carter's approved reading,
205
+ * 2026-08-31). A `budget.exceeded` record exists because a budget refusal is
206
+ * a fact about a commitment audit must be able to reconstruct; a record per
207
+ * refused flood request would hand the flooder the log growth it was refused
208
+ * the queue for. `error.limits` carries the failing verdicts, and the
209
+ * requests that WERE admitted are all in the log to count from.
210
+ *
211
+ * Transient in the sense that matters to a caller: the queue drains when a
212
+ * human decides, a requester withdraws, or a TTL lapses. Retrying at once
213
+ * gets the same answer.
214
+ */
215
+ "queue-full",
216
+ /**
217
+ * This origin created more requests in the last hour than the policy's
218
+ * `limits.requests_per_hour` allows (SPEC.md §5.2, APRV-173).
219
+ *
220
+ * Distinct from `queue-full`, and the distinction is the repair. That code
221
+ * says the queue is full whoever is asking, so the caller waits for an
222
+ * approver; this one says the caller's own recent volume is the problem, so
223
+ * it slows down. Origin is the requesting actor at v0.1, which the runtime
224
+ * assigns rather than the caller (see `core/intake-limits.ts`), so a
225
+ * requester cannot re-label itself into a fresh hour.
226
+ *
227
+ * Counted over request CREATION, not over live requests: a request that was
228
+ * answered a minute after it was made still spent the origin's share of the
229
+ * hour. A ceiling that forgot each request as it was answered could be
230
+ * cleared by withdrawing every request as fast as it was made.
231
+ *
232
+ * Nothing is appended, for the same reason `queue-full` appends nothing.
233
+ */
234
+ "rate-limited",
235
+ /**
236
+ * The action resolves to `manual` and its registered declaration carries no
237
+ * `payload_hash` (amended SPEC.md §6.2: MUST for `manual` actions).
238
+ *
239
+ * Enforced here rather than in `envelope.schema.json` because the schema
240
+ * cannot know an action's resolved autonomy — that answer depends on the
241
+ * policy, the irreversibility floor, and the class, none of which the
242
+ * envelope alone determines. A manual action with nothing to bind to would
243
+ * give a human a decision about bytes nobody committed to, so intake refuses
244
+ * and nothing is appended.
245
+ *
246
+ * Since APRV-146 the same code answers the same fact at the harness write
247
+ * boundary: {@link startHarnessExecution} refuses a start that names no
248
+ * payload hash, and {@link consumeHarnessGrant} refuses a spend that presents
249
+ * none (or a grant whose request recorded none). The fact is identical at both
250
+ * ends — a binding is required here and there is none — and the repair is the
251
+ * same shape: state the bytes, or request the action again so the record does.
252
+ * `payload-mismatch` stays the code for bytes that are stated and wrong.
253
+ */
254
+ "payload-hash-required",
255
+ /**
256
+ * Payload material was supplied at intake and does not hash to the
257
+ * `payload_hash` the registration declared (APRV-28).
258
+ *
259
+ * The same code, and the same reason, as `core/token.ts`'s refusal at spend
260
+ * time: a grant approves specific bytes, so material that hashes to something
261
+ * else is not the payload this request is about. Refused before anything is
262
+ * stored and before anything is appended.
263
+ */
264
+ "payload-mismatch",
265
+ /**
266
+ * The declared payload material could not be stored (APRV-28): it cannot be
267
+ * canonicalized, or the store directory could not be written.
268
+ *
269
+ * Fails closed rather than requesting anyway. A manual request whose bytes no
270
+ * channel can display is a request no human can answer — SPEC.md §10.4 —
271
+ * so intake refuses and the log is left untouched.
272
+ */
273
+ "payload-store-failed",
274
+ /**
275
+ * A grant was attempted on a request whose payload carries no usable `class`.
276
+ *
277
+ * Its own code since APRV-20 pass two: the previous behavior substituted the
278
+ * empty string and granted anyway, which recorded an authorization that no
279
+ * class-scoped budget could ever charge and no policy rule could ever match.
280
+ * Fail closed and say which fact was missing.
281
+ */
282
+ "grant-classless-request",
283
+ /**
284
+ * The action's class resolves to `human-only` (APRV-185, amended SPEC.md
285
+ * §5.2): the policy reserves it to human hands, and a person performs it
286
+ * outside agent execution entirely.
287
+ *
288
+ * Its own code, and distinct from every rejection, because nobody decided
289
+ * anything. A `reject` is a human's answer to a question that was legitimately
290
+ * asked; this is the policy answering that the question does not arise — there
291
+ * is no approval to seek, no approver to ask, and no grant that could be
292
+ * recorded. An agent that read a rejection would sensibly try again with a
293
+ * better summary; an agent that reads this must stop asking and hand the
294
+ * action to a person.
295
+ *
296
+ * Every verb of this module that could mint or withdraw authority returns it:
297
+ * {@link request}, {@link decide} in all three of its decisions, and
298
+ * {@link consumeHarnessGrant}. Grant is the obvious one. Reject and revoke are
299
+ * refused too, and the reason is stated plainly rather than assumed: those
300
+ * verbs WITHDRAW authority, and withdrawing authority that cannot exist would
301
+ * write a decision record about a human-only class into the log, which reads
302
+ * afterwards as a class the gate transacts in. A pending request that a policy
303
+ * amendment has since raised to `human-only` is not stranded by that: it
304
+ * authorizes nothing, no token can be minted for it and no run can spend it,
305
+ * and its requester withdraws it (`withdraw`) or its TTL lapses (`expire`).
306
+ * Neither of those verbs is refused here, deliberately — they are the exits
307
+ * from a question nobody may answer.
308
+ *
309
+ * Evaluated immediately after the check that establishes a request exists at
310
+ * all, and before every other check on the path, on all three verbs. A class
311
+ * that cannot be transacted in is answered before any question about who may
312
+ * decide it, under which policy hash, or against which budget.
313
+ */
314
+ "class-human-only",
315
+ /**
316
+ * A `harness.launch.*` class that no rule of this policy names (APRV-354).
317
+ *
318
+ * The gate's half of the hook's `hook-harness-launch-unruled`, and it exists
319
+ * so there is no second door. The hook refuses a harness launch it classified
320
+ * from a command line; this refuses one a caller DECLARES, through
321
+ * `approval register` and `approval request`, which is the other way an
322
+ * action class reaches the gate.
323
+ *
324
+ * SPEC.md §7: the family resolves only under an explicit rule, never under
325
+ * `defaults.autonomy`. What a grant of it covers is the launch and nothing
326
+ * the launched session then does, so a class arriving by upgrade rather than
327
+ * by an operator's decision would be a capability nobody chose.
328
+ *
329
+ * Evaluated in the same position as `class-human-only` and immediately above
330
+ * it: both are the policy answering before any question about registration,
331
+ * budget or approver, and this one is the narrower statement of the two — not
332
+ * "reserved to human hands" but "not spoken about at all". Nothing is
333
+ * appended, so no `approval.requested` exists for a class no rule governs.
334
+ */
335
+ "harness-launch-unruled",
336
+ /**
337
+ * Loop safety escalated the task to manual (SPEC.md §10.2, APRV-18): three
338
+ * consecutive `execution.failed` events. Only the non-manual paths are
339
+ * refused — see {@link request}.
340
+ */
341
+ "loop-escalated",
342
+ /**
343
+ * A harness outcome was reported for an action key whose `execution.started`
344
+ * carries no `execution: "harness"` marker (APRV-145).
345
+ *
346
+ * The mirror image of `core/execute.ts`'s `execution-delegated`, and the pair
347
+ * is what keeps the two write surfaces from overlapping by one record. That
348
+ * code refuses a HUMAN recovery verb over a harness start; this one refuses a
349
+ * HARNESS report over a start this runtime is watching itself. An untrusted
350
+ * report that could close an `approval run` execution would be reporting an
351
+ * exit code the runtime was about to observe for itself, and the outcome the
352
+ * log kept would be whichever one landed first.
353
+ */
354
+ "not-delegated",
355
+ /**
356
+ * Every harness-marked start the reported tool call opened already carries an
357
+ * outcome (APRV-145). An execution has exactly one, and a second report would
358
+ * be a second answer about one command — including a `completed` written over
359
+ * a `failed`, which is a streak cleared by repetition rather than by recovery.
360
+ *
361
+ * Named for the fact rather than for the reporter, and spelled exactly as
362
+ * `core/execute.ts` spells the same fact, so a reader who has met one has met
363
+ * both.
364
+ */
365
+ "already-finished",
366
+ /** No request to decide. */
367
+ "not-requested",
368
+ /** The request already has a terminal decision. */
369
+ "already-decided",
370
+ /** Revoke was attempted on a request that is not granted. */
371
+ "not-granted",
372
+ /**
373
+ * A decision was attempted on a request the requester had already withdrawn
374
+ * (APRV-106, amended SPEC.md §6.3).
375
+ *
376
+ * Distinct from `already-decided` because the facts and the repairs are
377
+ * distinct. `already-decided` says a human answered and the answer stands;
378
+ * this one says nobody answered and nobody can — the party that asked has
379
+ * stopped listening, so a grant here would authorize an action no process is
380
+ * waiting to perform. The repair is to request the action again, which is a
381
+ * new request with a new decision, not to try the decision a second time.
382
+ */
383
+ "request-withdrawn",
384
+ /**
385
+ * A withdrawal was attempted by an actor other than the one that appended the
386
+ * matching `approval.requested` (APRV-106).
387
+ *
388
+ * Withdrawal is the requester's own retraction, and nothing more. If any
389
+ * actor could withdraw, then any actor could clear an approver's queue — the
390
+ * queue would become deniable by whoever reached the log first, which is the
391
+ * one property the gate exists to deny. A human who wants a pending request
392
+ * gone rejects it, on the record, as themselves.
393
+ */
394
+ "not-requester",
395
+ /** The TTL lapsed — judged from the request's own ts, event or no event. */
396
+ "expired",
397
+ /** `expire` was called on a request whose TTL has not lapsed. */
398
+ "not-expired",
399
+ /** The actor is not a well-formed `human:`/`agent:` identity. */
400
+ "actor-invalid",
401
+ /** A human-only verb was attempted by a non-human actor. */
402
+ "actor-not-human",
403
+ /**
404
+ * A grant was recorded by a person the resolved rule's `approvers` list does
405
+ * not name (APRV-137, amended SPEC.md §5.2).
406
+ *
407
+ * Distinct from `actor-not-human`, and the distinction is the repair. That
408
+ * code says the actor is not a person at all, and the fix is to run the verb
409
+ * as one. This one says the actor IS a person and is not one the policy
410
+ * named for this class, so the fix is to ask a named approver. Before this
411
+ * code the list was parsed, surfaced by `policy explain`, and enforced
412
+ * nowhere: a policy writing `approvers: [alice]` on `financial.spend` bound
413
+ * nothing while its author believed it bound the class.
414
+ *
415
+ * Scope, and its limits. The check is defense in depth inside the trust
416
+ * boundary §11 states plainly: human identity in v0.1 is config-declared, so
417
+ * anyone who can set that configuration can present any name on this list.
418
+ * What it defends is the honest mistake and the wrong-approver routing, not
419
+ * an actor choosing whose name to wear. The check binds `grant` alone;
420
+ * reject and revoke withdraw authority rather than confer it, and
421
+ * restricting them would leave a request standing, or an authorization live,
422
+ * because the wrong person tried to end it.
423
+ */
424
+ "actor-not-approver",
425
+ /** The log could not be read, or holds a line that is not a record. */
426
+ "log-unreadable",
427
+ /** The log's final line is unterminated (a crashed write). */
428
+ "log-torn-tail",
429
+ /**
430
+ * The chain does not verify (APRV-20 finding S1). Distinct from
431
+ * `log-unreadable`, which is a filesystem fact: this one says the log's own
432
+ * contents contradict each other, so nothing may be authorized from it.
433
+ */
434
+ "log-corrupt",
435
+ /**
436
+ * The rendered semantic diff of a proposed policy is larger than a channel
437
+ * prompt can show whole (APRV-109, amended SPEC.md §10.3).
438
+ *
439
+ * A refusal rather than a truncation, and its own code so a caller can tell
440
+ * "this amendment is too big for a phone" from every other reason a proposal
441
+ * fails. A prompt that showed two thirds of a policy change would collect a
442
+ * signature for the third it did not show; the repair is to read the diff at
443
+ * a terminal and attest there, which the message names.
444
+ */
445
+ "diff-too-large",
446
+ /** No `policy.proposed` record at the named seq (APRV-109). */
447
+ "proposal-not-found",
448
+ /**
449
+ * The policy bytes changed after the attestation prompt was rendered
450
+ * (APRV-109).
451
+ *
452
+ * Distinct from `policy-drift`, which is about a pending approval routed
453
+ * under superseded rules. This one says the human is looking at a hash the
454
+ * file no longer has, so attesting would name bytes the approver was never
455
+ * shown. Nothing is appended and the amendment is proposed again.
456
+ */
457
+ "proposal-stale",
458
+ /**
459
+ * An attestation was proposed for a policy file that already matches its
460
+ * attestation (APRV-109). There is no amendment to sign, and a prompt for one
461
+ * would ask a human to re-attest bytes already in force.
462
+ */
463
+ "policy-already-attested",
464
+ /**
465
+ * A grant carrying `reaction: loved` or `reaction: disliked` and no non-blank
466
+ * note (APRV-239, amended SPEC.md §5.2).
467
+ *
468
+ * Grant only. Evaluated with the other checks that read nothing, and nothing
469
+ * is appended. `reject` and `revoke` accept no reaction at all, which is a
470
+ * usage error at the verb rather than a member of this union: their reason IS
471
+ * their note, and there is no second field for a grade to sit in.
472
+ *
473
+ * Its own code rather than the audit path's `note-required` because a caller
474
+ * branching on a gate refusal is branching on this union, and the two verbs
475
+ * are answered by two different modules. The message names `--note`, which is
476
+ * the whole of the fix.
477
+ */
478
+ "reaction-note-required",
479
+ /**
480
+ * The append itself failed; `append` carries the underlying error. Its
481
+ * `code` is `head-moved` when the log grew between this module's read and its
482
+ * append: every check that authorized the write was made against an older log,
483
+ * so nothing was written. Since APRV-236 this code reaches a caller only after
484
+ * the bounded read-check-append retry is spent (`core/head-retry.ts`), and its
485
+ * message says how many attempts were made. A single lost race is no longer
486
+ * reported at all: it is re-derived, and the answer the fresh log supports is
487
+ * what the caller receives.
488
+ */
489
+ "append-failed",
490
+ /**
491
+ * A `delivery: "self"` request could not publish a delivery address (APRV-211):
492
+ * the ephemeral private key could not be written beside the log.
493
+ *
494
+ * Fail closed, and unlike APRV-105's ordinary sealed path, which drops the
495
+ * convenience and leaves the paste path standing. There is no paste path
496
+ * here — the requester is a process, not a terminal — so a request admitted
497
+ * without an address would spend a human's decision on an authorization
498
+ * nothing can ever open. Nothing is appended; the next attempt asks again.
499
+ */
500
+ "token-delivery-unavailable"];
501
+ export type GateRefusalCode = (typeof GATE_REFUSAL_CODES)[number];
502
+ /** Every gate failure is one of these. Nothing here throws. */
503
+ export interface GateRefusal {
504
+ ok: false;
505
+ code: GateRefusalCode;
506
+ message: string;
507
+ /** Attestation discriminator, when `code` is `policy-not-attested`. */
508
+ detail?: AttestationRefusalDetail;
509
+ /** The derived state at refusal time, for transition refusals. */
510
+ state?: RequestState;
511
+ /** The failing verdicts, when `code` is `budget-exceeded`. */
512
+ verdicts?: BudgetVerdict[];
513
+ /**
514
+ * The failing request-volume verdicts, when `code` is `queue-full` or
515
+ * `rate-limited` (APRV-173). Separate from `verdicts` because they are a
516
+ * different measurement with a different shape, and because a caller that
517
+ * branched on `verdicts` to read money out of a budget refusal must not find
518
+ * queue counts there.
519
+ */
520
+ limits?: IntakeVerdict[];
521
+ /** Schema errors, when `code` is `envelope-invalid`. */
522
+ errors?: ValidationError[];
523
+ /** The underlying append error, when `code` is `append-failed`. */
524
+ append?: AppendError;
525
+ /**
526
+ * The two policy hashes actually compared, when `code` is `policy-drift`
527
+ * (APRV-235): the hash the request (or the grant) pinned, and the hash
528
+ * attested at the moment of the refusal.
529
+ *
530
+ * Carried on the refusal so that whatever RECORDS the refusal records the
531
+ * comparison that was made. Re-deriving the pair afterwards would be a second
532
+ * comparison, at a second instant, against a file that may have moved again,
533
+ * and a record describing a comparison nobody performed is worse than no
534
+ * record at all. `core/decision-refusal.ts` copies these verbatim.
535
+ */
536
+ drift?: {
537
+ requested: string;
538
+ attested: string;
539
+ };
540
+ /**
541
+ * An event appended *alongside* the refusal: the `budget.exceeded` record, or
542
+ * the lazily-materialised `approval.expired` record. Never an authorization.
543
+ */
544
+ record?: EventRecord;
545
+ }
546
+ /**
547
+ * Options shared by every gate operation.
548
+ *
549
+ * Note what is **not** here and no longer a parameter anywhere in this module:
550
+ * `ts`. Under amended SPEC.md §8 a gate-typed event's timestamp is assigned by
551
+ * the runtime at the write boundary, so it is read from {@link ClockOptions
552
+ * clock} (defaulting to the real clock) rather than accepted from the caller.
553
+ * The refusal the spec asks for is structural: there is no parameter to pass.
554
+ */
555
+ export interface GateOptions extends ClockOptions {
556
+ /** Schema directory, passed to both envelope validation and the append. */
557
+ schemaDir?: string;
558
+ /**
559
+ * Where to find `APPROVAL.md`. `dir`/`file` have the same semantics as
560
+ * `loadPolicy`.
561
+ *
562
+ * `read` is the one read seam a gate operation uses to fetch the policy bytes
563
+ * (APRV-142), defaulting to `readFileSync`. It exists so a test can simulate
564
+ * a file swapped mid-operation and prove the swap cannot land: the seam is
565
+ * called exactly once per gate operation, so a reader that returns different
566
+ * bytes on its second call has no second call to return them to. It is not a
567
+ * widening of anything — a caller holding `GateOptions` can already name any
568
+ * file through `file`.
569
+ */
570
+ policy?: {
571
+ dir?: string;
572
+ file?: string;
573
+ read?: (path: string) => Uint8Array;
574
+ };
575
+ /** Lock tuning for the append path. */
576
+ append?: AppendOptions;
577
+ /**
578
+ * How many times a writer of this module re-derives its verdict after a
579
+ * `head-moved` refusal, at most `head-retry.ts`'s `HEAD_MOVED_ATTEMPTS`
580
+ * (APRV-150, APRV-236, {@link withHeadMovedRetry}).
581
+ *
582
+ * Only ever lowers the bound. `1` is the pre-APRV-150 behaviour — one read,
583
+ * one set of checks, one append, and a lost race is reported as a refusal —
584
+ * which is what a test pins the old shape with. A larger number, a zero or a
585
+ * fraction is ignored in favour of the runtime's own value: the ceiling is not
586
+ * a caller's to raise.
587
+ */
588
+ retryOnHeadMoved?: number;
589
+ /**
590
+ * Where payload material is stored (APRV-28). Defaults to the convention
591
+ * `core/payload-store.ts` defines: `.approval/payloads/`, beside the log.
592
+ */
593
+ payloadStoreDir?: string;
594
+ /**
595
+ * Where per-request private keys live (APRV-105). Defaults to the convention
596
+ * `core/seal.ts` defines: `.approval/keys/`, beside the log. Under the default
597
+ * `token_delivery: manual` nothing here is ever written or read.
598
+ */
599
+ keyStoreDir?: string;
600
+ /**
601
+ * The environment the operator's sampling secret is read from (APRV-127).
602
+ * Injected by tests; defaults to `process.env`. The secret is never read from
603
+ * the policy file or from anywhere else inside the repository.
604
+ */
605
+ env?: NodeJS.ProcessEnv;
606
+ /**
607
+ * How a process with no sampling secret asks the daemon for a live draw
608
+ * (APRV-208). Defaults to `core/live-draw.ts`'s `askDaemonDraw`, which
609
+ * `spawnSync`s a relay against the owner-only socket under the approval home.
610
+ *
611
+ * A seam for tests, and stated plainly rather than defended: an in-process
612
+ * caller that supplies a lying asker can wave a live action through, and so
613
+ * can one that points `policy.file` at a policy of its own. Both are the same
614
+ * trust boundary, which is the process holding `GateOptions` — SPEC.md §11's
615
+ * "the trust boundary is the local machine". The property this seam does NOT
616
+ * weaken is the one that matters across processes: a HOOK never sets it, the
617
+ * default asker talks only to the socket under the approval home, and the
618
+ * verdict it brings back is recorded with the MAC an operator recomputes.
619
+ */
620
+ drawAsk?: DrawAsker;
621
+ }
622
+ type ReadOutcome = {
623
+ ok: true;
624
+ records: EventRecord[];
625
+ head: LogHead | null;
626
+ } | GateRefusal;
627
+ /**
628
+ * Read the log's records, refusing unless the whole chain verifies.
629
+ *
630
+ * Delegates to `core/state.ts`'s {@link readVerifiedRecords}: since APRV-20
631
+ * (finding S1) the gate does not merely parse the log, it verifies it. A
632
+ * corrupt log refuses `log-corrupt` and authorizes nothing; a torn tail refuses
633
+ * `log-torn-tail`, unchanged, because the repair is a human decision and never a
634
+ * gate's; an unopenable file refuses `log-unreadable`, an I/O fact rather than an
635
+ * accusation.
636
+ *
637
+ * The returned `head` is what every append site here passes as `expectedHead`,
638
+ * so a decision derived from these records cannot land on a log that moved
639
+ * underneath it.
640
+ */
641
+ export declare function readGateRecords(logPath: string, schemaDir?: string): ReadOutcome;
642
+ /**
643
+ * The policy as a gate operation would load it: one read through the seam of
644
+ * {@link GateOptions.policy}, parsed, failing closed (APRV-324).
645
+ *
646
+ * Exported for one caller, `channels/contract.ts`, which resolves an
647
+ * authenticated sender to an approver identity BEFORE it calls {@link decide}
648
+ * and must do so against the same file, discovered the same way, through the
649
+ * same injectable reader. Writing a second `loadPolicy` call there would have
650
+ * been a second discovery path that could find a different file.
651
+ *
652
+ * It is still a SECOND read of the bytes, one operation earlier, and that is
653
+ * deliberate rather than overlooked: `decide` reads once per attempt on purpose
654
+ * (see {@link PolicyRead}), and pinning bytes across its bounded retry would
655
+ * make a re-attestation inside the retry window invisible to the drift check.
656
+ * What closes the gap is the check itself — a policy that changes between the
657
+ * two reads is either unattested or a different hash from the one the request
658
+ * pinned, and `decide` refuses `policy-not-attested` or `policy-drift` rather
659
+ * than authorizing under bytes the resolution never saw.
660
+ */
661
+ export declare function readGatePolicy(options: GateOptions): PolicyLoadResult;
662
+ /** One declared action of an envelope (SPEC.md §6.2 `actions[]`). */
663
+ export interface RegisteredAction {
664
+ class: string;
665
+ idempotency_key: string;
666
+ summary?: string;
667
+ reversible?: boolean;
668
+ /** Canonical decimal USD string (APRV-121); see `core/money.ts`. */
669
+ est_cost_usd?: string;
670
+ /**
671
+ * The content binding of amended SPEC.md §6.2. MUST be present for an action
672
+ * that resolves to `manual`; the enforcement point is {@link request}, not
673
+ * registration, because autonomy is not known until policy is consulted and
674
+ * refusing at registration would make an envelope unregisterable for a
675
+ * property of a policy file it never mentions.
676
+ */
677
+ payload_hash?: string;
678
+ }
679
+ /**
680
+ * What to register: a task file to read, or an already-in-hand envelope.
681
+ *
682
+ * The task **id** is not part of the envelope — `envelope.schema.json` governs
683
+ * the value of the `approval:` key only, and `id:` is a sibling board key owned
684
+ * by Backlog.md (SPEC.md §6). So the file form reads it from the frontmatter's
685
+ * `id`, and the in-memory form takes it explicitly.
686
+ */
687
+ export type RegisterSource = {
688
+ file: string;
689
+ } | {
690
+ task: string;
691
+ envelope: unknown;
692
+ };
693
+ export type RegisterResult = {
694
+ ok: true;
695
+ record: EventRecord;
696
+ task: string;
697
+ actions: RegisteredAction[];
698
+ } | GateRefusal;
699
+ /**
700
+ * Validate an envelope and append `task.registered`.
701
+ *
702
+ * Fail closed: the envelope is validated against `envelope.schema.json` **before
703
+ * anything is read from it and before any byte is written**. A schema-invalid
704
+ * envelope leaves the log untouched.
705
+ *
706
+ * Double registration is refused. Re-registering a task id would give the same
707
+ * id two different declared action sets in one log, and every later lookup
708
+ * ("what class is this key?") would have to pick one — silently. Envelope
709
+ * *changes* are `envelope.drift` (SPEC.md §6.3, M5), not a second registration.
710
+ *
711
+ * `actor` is a `human:` or `agent:` identity; registration is an ordinary
712
+ * proposal, not a privileged act, so an agent may perform it. `system:` is
713
+ * refused: the runtime does not author tasks.
714
+ *
715
+ * The registration payload carries the envelope's `actions` and — since S2 —
716
+ * its `budget` block, so the task's own `max_cost_usd` cap is enforced from the
717
+ * log rather than from a task file that may be edited afterwards.
718
+ */
719
+ export declare function register(logPath: string, source: RegisterSource, actor: string, options?: RegisterOptions): RegisterResult;
720
+ /**
721
+ * {@link register}'s options, plus the one field only a harness hook passes.
722
+ *
723
+ * `harness` is APRV-227's provenance pair, and where it comes from is the whole
724
+ * of its safety. It is a CALL option: the hook process derives it from its own
725
+ * event and its own PATH and hands it in. It is NOT read from the envelope, the
726
+ * task file, or anything else on disk — an envelope field would be a value an
727
+ * agent authors about the binary that is supposed to be watching it, and a task
728
+ * file is a file an agent edits. Copied into the payload verbatim when present
729
+ * and omitted entirely when absent; nothing downstream reads it back (SPEC.md
730
+ * §11.1 invariant 4 — see `core/harness-version.ts`).
731
+ */
732
+ export interface RegisterOptions extends GateOptions {
733
+ harness?: HarnessProvenance;
734
+ }
735
+ /**
736
+ * The declared action for `(task, actionKey)`, as registered in the log.
737
+ *
738
+ * SPEC.md §7: "an action's class MUST be declared before an execution token can
739
+ * be requested for it". The declaration lives in `task.registered`, so the log —
740
+ * not the file, which may have been edited since — is what the gate reads back.
741
+ */
742
+ export declare function registeredAction(records: EventRecord[], task: string, actionKey: string): {
743
+ ok: true;
744
+ action: RegisteredAction;
745
+ } | GateRefusal;
746
+ /** The action being submitted to the gate. */
747
+ export interface RequestInput {
748
+ task: string;
749
+ actionKey: string;
750
+ /** The dotted side-effect class (SPEC.md §7). */
751
+ cls: string;
752
+ /** Canonical decimal USD string (APRV-121); a JSON number is read as the historical form. */
753
+ est_cost_usd?: UsdInput;
754
+ reversible?: boolean;
755
+ summary?: string;
756
+ /**
757
+ * The content binding (amended SPEC.md §6.2). A fallback only: {@link request}
758
+ * prefers the value on the `task.registered` record, because the log is what
759
+ * the human's policy was attested against and a caller-supplied hash could
760
+ * name bytes the registration never declared.
761
+ */
762
+ payload_hash?: string;
763
+ /**
764
+ * The concrete payload material, to be filed in the payload store (APRV-28).
765
+ *
766
+ * Wrapped in an object so that "supplied, and the material happens to be
767
+ * `undefined`" is distinguishable from "not supplied at all" — the first is a
768
+ * payload that cannot be bound to and is refused, the second is the ordinary
769
+ * case of a caller that stored the bytes some other way (or holds none).
770
+ *
771
+ * Its hash MUST equal the declared `payload_hash`; a difference refuses
772
+ * `payload-mismatch` and stores nothing. Material supplied for an action that
773
+ * resolves to `supervised` or `autonomous` is ignored: that path records no
774
+ * request, so there is no binding a stored payload could belong to.
775
+ */
776
+ payload?: {
777
+ value: unknown;
778
+ };
779
+ /**
780
+ * `"harness"` when this request will never be executed through
781
+ * `approval run` (APRV-106).
782
+ *
783
+ * The Claude Code hook is the case it exists for: the hook asks the gate a
784
+ * permission question and the *harness* runs the command, so a grant here has
785
+ * nothing to hand a token to. Recorded on `approval.requested` and copied by
786
+ * {@link decide} onto the grant, where it suppresses the mint. See
787
+ * `DeclaredAction.execution` in `core/state.ts` for why a false claim can
788
+ * only remove the claimant's own capability.
789
+ */
790
+ execution?: "harness";
791
+ /**
792
+ * ISO-8601 instant after which the requester stops waiting (APRV-106).
793
+ *
794
+ * Recorded for CHANNELS TO DISPLAY and for nothing else: an approver seeing
795
+ * "requester waits until 09:23 UTC" knows that an answer at 09:40 reaches
796
+ * nobody. It bounds no TTL, charges no budget, and gates nothing — the
797
+ * policy's `defaults.approval_ttl` remains the only deadline with authority.
798
+ */
799
+ wait_until?: string;
800
+ /**
801
+ * The caller has established that loop safety floors this action to `manual`
802
+ * for this invocation (APRV-145, amended SPEC.md §10.2).
803
+ *
804
+ * The THIRD way into the manual path, beside a class that resolves manual and
805
+ * an action the APRV-127 live draw selected, and it works exactly as that
806
+ * second one does: the non-manual branch is skipped and everything below it
807
+ * runs unchanged, so nothing in the manual path knows or asks how the action
808
+ * got here. The flag is a fact the caller computed from the log
809
+ * (`core/loop.ts`'s `harnessLoopFloor`) and it can only ever ADD scrutiny: a
810
+ * caller that sets it wrongly asks a human about a command that did not need
811
+ * one, and a caller that omits it is refused at the write boundary by
812
+ * {@link startHarnessExecution}, which re-checks the same streaks.
813
+ */
814
+ loopFloor?: boolean;
815
+ /**
816
+ * `"self"` when the requesting process will consume its own grant, in its own
817
+ * process, and no other principal needs the raw token (APRV-211).
818
+ *
819
+ * The daemon's cadence advance is the case it exists for. The daemon is the
820
+ * requester AND the executor: it asks the gate, a human answers on the phone,
821
+ * and the same process spends the answer through {@link startExecution}'s
822
+ * APRV-105 sealed path. Before this field, the grant handed its raw token back
823
+ * to the GRANTING surface — the Telegram listener's terminal — which printed
824
+ * it under "single-use · stored nowhere · copy it now", the APRV-166 relay
825
+ * path meant for a requester in another process. Nobody was ever going to
826
+ * carry that value anywhere; it was a live credential rendered for a principal
827
+ * that was not the requester, which SPEC.md §11.1's raw-secrets invariant
828
+ * exists to prevent.
829
+ *
830
+ * So it does two things and nothing else. {@link request} mints the sealed
831
+ * delivery address for this action REGARDLESS of `defaults.token_delivery`,
832
+ * because there is no terminal on the other end of the paste path and a
833
+ * request with no address would be an authorization nobody could open; and
834
+ * {@link decide} withholds the raw token from its own return value, so no
835
+ * granting surface has one to print. The token still exists, still binds to
836
+ * the payload bytes, is still single-use, and is still minted only by a
837
+ * human's grant. Nothing here reduces scrutiny: it removes a reader, not a
838
+ * check.
839
+ *
840
+ * Refused when the key cannot be written (`token-delivery-unavailable`), which
841
+ * is the fail-closed direction: a self-delivered grant nobody can open would
842
+ * spend a human's decision on an authorization that can never execute.
843
+ */
844
+ delivery?: "self";
845
+ }
846
+ /**
847
+ * Why a `supervised-live` action was, or was not, sent to the human gate
848
+ * (amended SPEC.md §5.2/§6.3, APRV-127).
849
+ *
850
+ * Returned to the caller and **never written to the log**. See
851
+ * {@link liveVerdict} for why the log carries no trace of the selection.
852
+ */
853
+ export interface LiveVerdict {
854
+ /** The class's declared `live_rate`, as `policy-match.ts` resolved it. */
855
+ rate: number;
856
+ /** True when this action must stop for a human before it may execute. */
857
+ gated: boolean;
858
+ /**
859
+ * Machine-readable and closed, because a supervisor branches on it:
860
+ *
861
+ * - `selected` — the HMAC fell under the rate. This is the fraction working.
862
+ * - `not-selected` — it did not. The action proceeds, and still enters the
863
+ * retrospective pool.
864
+ * - `payload-hash-absent` — the registration declared no `payload_hash`, so
865
+ * there is nothing to select over. Gated: an action whose bytes nobody
866
+ * named cannot be shown to have been fairly sampled, and the manual path
867
+ * refuses it by name a moment later.
868
+ * - the three {@link LiveSelectorUnavailableReason}s — no usable secret.
869
+ * Gated. See `core/sampler.ts` on why live selection fails closed where
870
+ * retrospective sampling fails open.
871
+ */
872
+ reason: "selected" | "not-selected" | "payload-hash-absent" | LiveSelectorUnavailableReason | DrawRefusalReason;
873
+ /** The algorithm an operator holding the secret recomputes to check this. */
874
+ selection: typeof LIVE_SELECTION;
875
+ /** The NAME of the secret's environment variable. Never the secret. */
876
+ secretEnv: string | null;
877
+ /**
878
+ * The delegation, when this verdict was not computed in this process
879
+ * (APRV-208). Absent for an in-process draw, which is why a sampled
880
+ * supervised-live request in an operator's own terminal is still byte-for-byte
881
+ * a manual one. See {@link LiveDrawRecord} for why a DELEGATED verdict is
882
+ * recorded and an in-process one is not.
883
+ */
884
+ draw?: LiveDrawRecord;
885
+ }
886
+ export type RequestResult = {
887
+ ok: true;
888
+ autonomy: Autonomy;
889
+ /** True when execution may start now: the supervised/autonomous path. */
890
+ proceed: boolean;
891
+ resolution: Resolution;
892
+ /** The `approval.requested` record, or `null` off the manual path. */
893
+ record: EventRecord | null;
894
+ /** Digest of the attested policy bytes that produced this intake verdict. */
895
+ policySha256: string;
896
+ /**
897
+ * The live-selection verdict, for a `supervised-live` class only
898
+ * (APRV-127). Absent for every other class: there was no fraction to fall
899
+ * inside or outside of.
900
+ */
901
+ live?: LiveVerdict;
902
+ } | GateRefusal;
903
+ /**
904
+ * Gate intake.
905
+ *
906
+ * Check order, and why it is this order:
907
+ *
908
+ * 1. **Actor.** A malformed identity is a bad call, not a policy question.
909
+ * 2. **Attestation.** An unverified policy cannot answer anything, so it is
910
+ * checked before the policy is consulted rather than after.
911
+ * 3. **Policy resolution** (`loadPolicy` + `resolve`, including the §7
912
+ * irreversibility floor). A failed load resolves everything to `manual` —
913
+ * that is `policy-match.ts`'s contract, and this module does not soften it.
914
+ * 3b. **Declaration** (SPEC.md §7, APRV-147), for a `manual` resolution and for
915
+ * a `supervised-live` one. The log must carry a `task.registered` for the
916
+ * task and an action with this idempotency key, or the request is refused
917
+ * `not-registered` / `action-not-registered` and nothing is appended. Before
918
+ * the live draw and before the binding below, so an undeclared action never
919
+ * reaches a human's queue, never has the live fraction drawn over a hash it
920
+ * chose for itself, and hears the real reason rather than
921
+ * `payload-hash-required`.
922
+ * 4. **Off the manual path, retain supplied bound material, then stop — unless
923
+ * the live fraction says otherwise.** `supervised`/`autonomous` append **no
924
+ * event** (amended SPEC.md §6.3) and return `proceed: true`. When the caller
925
+ * supplies payload material, it is checked against the registered declaration
926
+ * and retained for the later execution evidence. Their budget is charged at `execution.started`,
927
+ * which APRV-18 appends — checking budgets here as well would charge them
928
+ * twice or, worse, pass here and fail there. A `supervised-live` class
929
+ * (APRV-127) draws its declared fraction here: an action the draw selects
930
+ * falls through into everything below and is treated as `manual` from this
931
+ * line on, and an action it does not proceeds exactly as before.
932
+ * 5. **Content binding** (amended SPEC.md §6.2, A1). A manual action whose
933
+ * registered declaration carries no `payload_hash` is refused
934
+ * `payload-hash-required` and nothing is appended. This is the first check
935
+ * after the manual path is known, because a request with nothing to bind to
936
+ * should never reach a human's queue at all.
937
+ * 5b. **Payload material**, when the caller supplied any (APRV-28). Its hash is
938
+ * checked against the declaration here — before legality, before budgets,
939
+ * before any file — and the bytes are written to the payload store in the
940
+ * step immediately before the append, so a refused request stores nothing.
941
+ * See the two comments in the body for the ordering and the one orphan it
942
+ * permits.
943
+ * 6. **Request legality**, then **budgets**, then the append. Legality first
944
+ * because a duplicate request is a caller bug that no budget outcome should
945
+ * obscure, and because refusing it must leave the log untouched.
946
+ *
947
+ * The `approval.requested` payload carries `class`, `est_cost_usd`, and (on the
948
+ * manual path, always) `payload_hash` — the budgets contract requires the first
949
+ * two on the grant and the token binding requires the third, and the grant
950
+ * copies all of them from here rather than re-deriving them from a file that
951
+ * may have changed.
952
+ */
953
+ export declare function request(logPath: string, input: RequestInput, actor: string, options?: GateOptions): RequestResult;
954
+ export interface DecideOptions extends GateOptions {
955
+ /** Free-text note recorded in the event payload (SPEC.md §8's example). */
956
+ note?: string;
957
+ /**
958
+ * The channel delivery id of the batch this decision answered (amended
959
+ * SPEC.md §10.3, APRV-38), recorded as `payload.batch_delivery_id` on
960
+ * `approval.granted` / `approval.rejected`.
961
+ *
962
+ * The log never batches: one gesture over five requests is five events, and
963
+ * this is the only thing tying them back together for audit. It is recorded
964
+ * on grant and reject alone, the two decisions a channel can collect;
965
+ * `revoke` is a considered act performed against the log through the CLI and
966
+ * never arrives as part of a batch gesture, so a value supplied with it is
967
+ * ignored rather than written.
968
+ *
969
+ * Empty strings are ignored for the same reason the schema requires
970
+ * `minLength: 1`: a batch id that identifies no batch is worse than none,
971
+ * since audit would read it as a grouping that never existed.
972
+ */
973
+ batchDeliveryId?: string;
974
+ /**
975
+ * The graded reaction the approver gave, on `grant` only (APRV-239, amended
976
+ * SPEC.md §5.2), recorded as `payload.reaction` on `approval.granted`.
977
+ *
978
+ * A human answering the gate is already saying what they think of the action;
979
+ * this is where they can say it in one word rather than in prose nothing can
980
+ * read back. It is GUIDANCE and never enforcement: the grant record itself is
981
+ * the authorization, and no routing, matching, sampling, budget, token or
982
+ * execution decision reads this field (SPEC.md §11.1 invariant 10). It cannot
983
+ * widen or narrow what the grant authorizes, and a `disliked` grant is exactly
984
+ * as much of a grant as a `loved` one.
985
+ *
986
+ * Ignored on `reject` and `revoke` — the CLI refuses the flag outright there,
987
+ * as a usage error naming `--note` — because their reason is their note and a
988
+ * grade beside a refusal is a second answer to a question with one.
989
+ */
990
+ reaction?: Reaction;
991
+ /**
992
+ * The surface that collected the decision (amended SPEC.md §8, APRV-324),
993
+ * recorded as the record's top-level `channel`.
994
+ *
995
+ * The base event schema has defined the field since v0.1 and the decision
996
+ * events never set it, so a reader of a grant could not tell a tap on a phone
997
+ * from a line typed into a terminal. Set by the decision surface, from its own
998
+ * name, exactly as `audit.decision_refused` has always set it.
999
+ */
1000
+ channel?: string;
1001
+ /**
1002
+ * The authenticated sender the `actor` was resolved from (amended SPEC.md
1003
+ * §6.3, APRV-324), recorded as `payload.sender`.
1004
+ *
1005
+ * ABSENT is meaningful and is the common case: it says the attribution came
1006
+ * from the surface's configuration rather than from anything the transport
1007
+ * authenticated, which is how every decision before this key existed was
1008
+ * made. Present, it is the transport's own attribution — never a name, handle
1009
+ * or id a message claimed about itself (§11.1 invariant 4).
1010
+ *
1011
+ * This is a RECORD of how the actor was chosen, and it is not the choosing:
1012
+ * `channels/contract.ts` resolves the sender against the attested policy
1013
+ * before calling this verb, and no check here reads this field. There is no
1014
+ * CLI flag that supplies it, so no agent-reachable surface mints one.
1015
+ *
1016
+ * `hashed` (APRV-370) says the `id` is the operator's keyed digest of the
1017
+ * account rather than the account, which is what a policy mapping senders in
1018
+ * the keyed form records. Present-and-`true` or absent, never `false`: a raw
1019
+ * record is the record this runtime already wrote, and it is written
1020
+ * unchanged.
1021
+ */
1022
+ sender?: {
1023
+ channel: string;
1024
+ id: string;
1025
+ hashed?: true;
1026
+ };
1027
+ /**
1028
+ * How {@link DecideOptions.sender} became the actor (APRV-324), recorded as
1029
+ * `payload.sender_source`. `policy` is the attested `approvers[id].senders`
1030
+ * mapping, and it is the only member today — a closed vocabulary so a future
1031
+ * source (a signed receipt, APRV-249) is distinguishable in a log rather than
1032
+ * silently mixed in with policy-attested ones.
1033
+ */
1034
+ senderSource?: "policy";
1035
+ }
1036
+ export type DecideResult = {
1037
+ ok: true;
1038
+ decision: Decision;
1039
+ state: RequestState;
1040
+ record: EventRecord;
1041
+ /**
1042
+ * The raw single-use execution token, on `grant` only (APRV-17). Returned
1043
+ * here and nowhere else: the log carries only its SHA-256, so this value
1044
+ * is unrecoverable once the caller drops it.
1045
+ *
1046
+ * Absent — on a grant that DID mint one — when the request declared
1047
+ * self-delivery and the token was sealed to its address (APRV-211). The
1048
+ * authorization is complete; the granting surface simply has no copy to
1049
+ * print, because the requester is a process that opens the seal itself.
1050
+ * See {@link RequestInput.delivery}.
1051
+ */
1052
+ token?: string;
1053
+ } | GateRefusal;
1054
+ /**
1055
+ * Record a human decision on a request.
1056
+ *
1057
+ * **Human-only**, enforced here in code and again by the event schema for
1058
+ * grant/reject. `revoke` is human-only too: withdrawing an authorization is a
1059
+ * decision about an authorization, and an agent that could revoke could also
1060
+ * churn the queue.
1061
+ *
1062
+ * Attestation is required **for `grant` only**. Grant is the authorizing
1063
+ * decision, so an unverified policy must not be able to produce one. Reject and
1064
+ * revoke *withdraw* authority, and refusing them on an unattested policy would
1065
+ * leave a live grant standing because a file changed — the strict direction and
1066
+ * the safe direction point the same way, and it is not "refuse everything".
1067
+ *
1068
+ * Attestation also answers a question it could not answer before APRV-118:
1069
+ * *which* policy. The hash the live file matched is compared against the hash
1070
+ * `approval.requested` pinned, and a difference refuses `policy-drift` with
1071
+ * nothing appended. Attestation alone catches an unattested edit; this catches
1072
+ * an attested one, which is the case where every check still passes and the
1073
+ * rules have nonetheless changed underneath a pending question. The hash in
1074
+ * force is then recorded on the grant, so the log states the rules the approver
1075
+ * decided under rather than leaving a reader to assume they were the
1076
+ * requester's.
1077
+ *
1078
+ * Budgets are re-evaluated at grant time. A request may have sat in the queue
1079
+ * while other actions consumed the window, and the moment that matters for a
1080
+ * commitment is the moment the human commits.
1081
+ *
1082
+ * On `grant` a single-use execution token is minted (`core/token.ts`) and its
1083
+ * SHA-256 recorded in the payload as `token_sha256`, **alongside the request's
1084
+ * `payload_hash`** (amended SPEC.md §10, A1). The token is therefore bound to
1085
+ * three things — the request, its `idempotency_key`, and the bytes — and
1086
+ * `core/token.ts` refuses `payload-mismatch` for anything else. The raw token
1087
+ * is returned in `token` and is written nowhere: whoever calls this is the only
1088
+ * party that will ever hold it, and a lost token is unrecoverable by design —
1089
+ * revoke and request again.
1090
+ */
1091
+ export declare function decide(logPath: string, actionKey: string, decision: Decision, actor: string, options?: DecideOptions): DecideResult;
1092
+ export interface WithdrawOptions extends GateOptions {
1093
+ /** Why the requester is retracting. Defaults to `cancelled`. */
1094
+ reason?: WithdrawReason;
1095
+ /** The requester's free-text elaboration, recorded in the payload. */
1096
+ note?: string;
1097
+ }
1098
+ export type WithdrawResult = {
1099
+ ok: true;
1100
+ state: RequestState;
1101
+ record: EventRecord;
1102
+ } | GateRefusal;
1103
+ /**
1104
+ * Retract a pending request, as the party that opened it (amended SPEC.md §6.3,
1105
+ * APRV-106).
1106
+ *
1107
+ * ## Why the verb exists
1108
+ *
1109
+ * Observed live on 2026-08-19. A builder's `git commit --amend` went through the
1110
+ * Claude Code hook, which classified it manual and appended
1111
+ * `approval.requested`. The hook waited nine minutes, got nothing, denied the
1112
+ * tool call and moved on — but the request stayed pending for the policy's 24h
1113
+ * TTL. Half an hour later the human was pinged on their phone and approved it,
1114
+ * and the grant authorized nothing at all: the hook had long since answered,
1115
+ * and a retried tool call is a new request with a new key. A person spent
1116
+ * attention on a question whose asker had left. SPEC.md §11 makes human
1117
+ * attention the audit budget, and a decision nobody can consume must not be
1118
+ * solicited; so the asker takes the question back.
1119
+ *
1120
+ * ## The four rules
1121
+ *
1122
+ * 1. **Requester-only.** The actor MUST equal the actor of the
1123
+ * `approval.requested` that opened the current cycle, else `not-requester`.
1124
+ * Anything looser would make the approver's queue clearable by whoever
1125
+ * reached the log first. A human who wants a pending request gone rejects
1126
+ * it, on the record, as themselves.
1127
+ * 2. **Pending-only.** `not-requested` when there is nothing to withdraw,
1128
+ * `already-decided` when a human has answered, `request-withdrawn` for a
1129
+ * second withdrawal, `expired` when the TTL has lapsed — and expiry is
1130
+ * judged here exactly as {@link decide} judges it, from the request's own
1131
+ * timestamp, with the same lazy materialisation of the `approval.expired`
1132
+ * record. A lapse is a lapse whether or not an event says so, and a
1133
+ * withdrawal that pretended otherwise would rewrite the reason a request
1134
+ * ended.
1135
+ * 3. **No attestation, no budget.** Withdrawal removes a question; it authorizes
1136
+ * nothing and commits nothing. Refusing it on an unattested policy would
1137
+ * leave requests standing in a human's queue because a file changed, which
1138
+ * is the strict direction pointing the wrong way.
1139
+ * 4. **Compare-and-append, like everything else here.** The legality check and
1140
+ * the write are made against the same head (SPEC.md §11.1(5)), so a grant
1141
+ * that lands in between wins and this withdrawal never overwrites it. Since
1142
+ * APRV-236 the loser of that race re-reads and re-checks rather than
1143
+ * reporting the lost race: the human's answer is on the fresh head, so the
1144
+ * refusal the requester receives is `already-decided`, which is the fact they
1145
+ * need. `tests/concurrency.test.ts` races the two.
1146
+ *
1147
+ * `ts` is assigned at the write boundary from the injected clock, like every
1148
+ * other gate-typed event (SPEC.md §8, A2): there is no parameter to pass one.
1149
+ */
1150
+ export declare function withdraw(logPath: string, actionKey: string, actor: string, options?: WithdrawOptions): WithdrawResult;
1151
+ /**
1152
+ * What a harness invocation may do with a request some earlier invocation
1153
+ * opened for the very same bytes.
1154
+ *
1155
+ * `pending` — the question is still in front of a human. A retry ADOPTS it and
1156
+ * waits out the remainder, rather than opening a second one: two prompts for
1157
+ * one command spend a human's attention twice on a single question, and
1158
+ * attention is the audit budget (SPEC.md §11).
1159
+ *
1160
+ * `granted` — a human answered, the TTL has not lapsed, and nothing has spent
1161
+ * the grant yet. A retry proceeds on it, once, through
1162
+ * {@link consumeHarnessGrant}.
1163
+ */
1164
+ export type HarnessCarryKind = "pending" | "granted";
1165
+ /** A request an identical harness invocation may adopt or spend (APRV-117). */
1166
+ export interface HarnessCarry {
1167
+ actionKey: string;
1168
+ task: string | null;
1169
+ kind: HarnessCarryKind;
1170
+ /** `seq` of the `approval.requested` that opened the current cycle. */
1171
+ requestSeq: number | null;
1172
+ /** `seq` of the `approval.granted`, on `granted` only. */
1173
+ decisionSeq: number | null;
1174
+ }
1175
+ export declare function findHarnessCarry(records: EventRecord[], payloadHash: string, cls: string, ts: string, ttlMs: number | null): HarnessCarry | null;
1176
+ /**
1177
+ * `GateOptions` plus the one thing a harness spend states about itself
1178
+ * (APRV-146).
1179
+ */
1180
+ export interface ConsumeHarnessOptions extends GateOptions {
1181
+ /**
1182
+ * The hash of the payload the harness is about to run (amended SPEC.md §10.4).
1183
+ *
1184
+ * REQUIRED for every spend. It is never read from the log: a value read from
1185
+ * the log would prove nothing, and the whole point is that the process about
1186
+ * to run the command states, independently, what it holds so the runtime can
1187
+ * compare it against what the human approved. Optional in the type and
1188
+ * enforced at the write boundary, exactly as `core/execute.ts` enforces
1189
+ * `presentedPayloadHash`, so omitting it is a machine-readable refusal rather
1190
+ * than a compile error a caller could silence with a placeholder.
1191
+ */
1192
+ presentedPayloadHash?: string;
1193
+ /**
1194
+ * The task id of the invocation doing the spending (APRV-200).
1195
+ *
1196
+ * Used for one thing and nothing else: to decide whether the recorded
1197
+ * {@link HARNESS_GRANT_ORIGIN} is `direct` or `carried`. It never gates the
1198
+ * spend, never changes a refusal, and never appears on the record itself.
1199
+ *
1200
+ * It is a CLAIM, in §9's computed-versus-claimed vocabulary, and it is bounded
1201
+ * the way §11.1 invariant 4 bounds every claim: the only value it can produce
1202
+ * unaided is the one that ADDS scrutiny. `direct` is reachable solely by
1203
+ * presenting the task the request record already carries, which is a fact the
1204
+ * gate reads out of the verified log rather than out of the caller; anything
1205
+ * else, absence included, records `carried`.
1206
+ */
1207
+ spendingTask?: string;
1208
+ }
1209
+ /**
1210
+ * How the authorization reached the process that spent it (APRV-200).
1211
+ *
1212
+ * `direct` — the tool call that spent this grant is the tool call that asked for
1213
+ * it. One process opened the request, waited, saw the decision and proceeded, so
1214
+ * the gate observed the whole ordering: nothing this runtime authorized could
1215
+ * have run before the human answered.
1216
+ *
1217
+ * `carried` — a LATER tool call spent it, under APRV-117's carryover or its
1218
+ * adoption sibling. The asking invocation had already returned a verdict (a
1219
+ * `hook-timeout` deny, which leaves the request open), and whether the harness
1220
+ * honoured that verdict is a fact this runtime never observes: it decides, and
1221
+ * the harness executes. So the ordering the record implies — grant, then
1222
+ * execution — is only guaranteed for `direct`, and this marker is what lets an
1223
+ * auditor tell the two apart from the committed log alone. See
1224
+ * `docs/claude-code-hook.md`, "When the grant can follow the write".
1225
+ *
1226
+ * Absent on an `execution.started` that names no `grant_seq`: an unattended
1227
+ * execution has no grant, so it has no origin to report (SPEC.md §6.3).
1228
+ */
1229
+ export type HarnessGrantOrigin = "direct" | "carried";
1230
+ /** The payload field {@link HarnessGrantOrigin} is recorded under. */
1231
+ export declare const HARNESS_GRANT_ORIGIN = "grant_origin";
1232
+ /**
1233
+ * The payload field naming the TOOL CALL that spent a carried grant (APRV-287).
1234
+ *
1235
+ * A carried grant's `execution.started` names the task of the request, because
1236
+ * that is the task the log holds the approval lifecycle under. The tool call
1237
+ * that actually ran the command is a different one, and until this field the
1238
+ * runtime had no way back to it: the completion counterpart rebuilds a task id
1239
+ * from the reporting event's session and tool-use id, found no start under it,
1240
+ * and refused `not-delegated`. The consequence was the one an operator saw on
1241
+ * 2026-09-06 — a granted commit-and-push completed, no `execution.completed`
1242
+ * was ever written, and the loop floor the refusal text promises would clear on
1243
+ * a completion stayed shut over the rest of the session.
1244
+ *
1245
+ * DERIVED, never declared: the value is the task id the runtime minted for the
1246
+ * spending invocation from the harness's session and tool-use ids, the same one
1247
+ * {@link HARNESS_GRANT_ORIGIN} is computed against. A reporter cannot name a
1248
+ * bucket with it, because the only thing it can reach is a start this runtime
1249
+ * wrote for that same tool call.
1250
+ *
1251
+ * Absent where the spend is `direct` (the record's own `task` already names the
1252
+ * tool call) and on every record written before this field existed, which is why
1253
+ * every reader treats absence as "no second name" rather than as a fault.
1254
+ */
1255
+ export declare const HARNESS_SPENDING_TASK = "spent_by_task";
1256
+ export type ConsumeHarnessResult = {
1257
+ ok: true;
1258
+ record: EventRecord;
1259
+ } | GateRefusal;
1260
+ /**
1261
+ * Spend a harness grant, exactly once (APRV-117).
1262
+ *
1263
+ * ## Why this is `execution.started`, and why it is alone
1264
+ *
1265
+ * A harness grant mints no token (APRV-106), so nothing in `core/token.ts`
1266
+ * records that it was used, and without such a record a grant could authorize
1267
+ * an unbounded number of identical retries for the whole TTL. The consumption
1268
+ * marker has to be a real event through compare-and-append (SPEC.md §11.1(5)),
1269
+ * and it has to be one the gate already reads as terminal for an idempotency
1270
+ * key. `execution.started` is exactly that: {@link request} refuses a key that
1271
+ * has one as `already-executed`, and {@link decide} refuses to revoke past it.
1272
+ * Reusing it means the single-use rule is the gate's existing rule rather than
1273
+ * a second one written next to it.
1274
+ *
1275
+ * **No `execution.completed` or `execution.failed` follows, ever.** The harness
1276
+ * runs the command; this runtime hands over permission and never observes an
1277
+ * exit status. Appending a completion would fabricate an outcome, and in this
1278
+ * vocabulary it would also assert something with consequences — an
1279
+ * `execution.completed` clears a task's loop-escalation streak (SPEC.md §10.2).
1280
+ * A harness execution is therefore recorded as begun and never as finished,
1281
+ * which is precisely what the runtime knows. The `execution: "harness"` marker
1282
+ * on the payload says so on the record itself, so a reader of the start event
1283
+ * alone can see why no outcome ever lands.
1284
+ *
1285
+ * ## What it refuses
1286
+ *
1287
+ * Attestation is checked here, and not as a formality: this is the one
1288
+ * enforcement path that reaches a harness `allow` without passing through
1289
+ * {@link request} (that happened in an earlier process, possibly against
1290
+ * earlier policy bytes). A policy that changed since the human attested it
1291
+ * cannot answer anything, so it answers nothing. `policy-drift` is the second
1292
+ * half of the same idea and is APRV-134: attested is not enough when what is
1293
+ * attested is a DIFFERENT policy from the one the approver decided under, and
1294
+ * the gap between a tap and a retry's spend is exactly where a re-attestation
1295
+ * fits.
1296
+ *
1297
+ * Everything else follows the derivation: `not-requested` when the key has no
1298
+ * request, `expired` when the TTL lapsed (judged from the request's own `ts`,
1299
+ * event or no event, exactly as {@link decide} judges it), `already-executed`
1300
+ * when something already spent it, `not-granted` for every other state and for
1301
+ * a grant that is not harness-executed. The content binding is checked last and
1302
+ * refuses twice over (APRV-146): `payload-hash-required` when the grant records
1303
+ * no bytes or the consumer states none, `payload-mismatch` when the bytes stated
1304
+ * are not the bytes approved. Budgets are not re-evaluated: the
1305
+ * authorization was charged at `approval.granted`, and `core/budgets.ts`'s
1306
+ * consumption contract already dedupes a start event against a grant carrying
1307
+ * the same `action_key`.
1308
+ *
1309
+ * ## What the record says about ORDER (APRV-200)
1310
+ *
1311
+ * The start carries `grant_origin`, which answers a question the log could not
1312
+ * previously be asked: was the tool call that spent this grant the tool call
1313
+ * that asked for it? `direct` says yes, and the gate observed the whole ordering
1314
+ * in one process. `carried` says a LATER invocation spent it — APRV-117's
1315
+ * carryover, or its adoption sibling — which means the asking invocation had
1316
+ * already returned a verdict, and this runtime never sees whether the harness
1317
+ * honoured it. A grant that arrives after the effect it names is a ratification
1318
+ * and not an approval, and `carried` is the window in which that is possible.
1319
+ * See {@link HarnessGrantOrigin} and `docs/claude-code-hook.md`.
1320
+ */
1321
+ export declare function consumeHarnessGrant(logPath: string, actionKey: string, actor: string, options?: ConsumeHarnessOptions): ConsumeHarnessResult;
1322
+ /** What the harness is about to run, as one class of one command. */
1323
+ export interface HarnessStartInput {
1324
+ task: string;
1325
+ actionKey: string;
1326
+ cls: string;
1327
+ /**
1328
+ * The hash of the bytes the verdict was computed over, and which are about to
1329
+ * run (amended SPEC.md §6.2/§10.4, APRV-140).
1330
+ *
1331
+ * REQUIRED. A start event that cannot say WHAT ran records only that something
1332
+ * did, which is the whole of what APRV-140 set out to fix and is exactly the
1333
+ * state the harness path was left in. Optional in the type and enforced at the
1334
+ * write boundary, on the reading `core/execute.ts` gives
1335
+ * `presentedPayloadHash`: a caller that omits it is refused
1336
+ * `payload-hash-required` and told what to compute, rather than turned away by
1337
+ * the compiler and left to satisfy it with a placeholder.
1338
+ */
1339
+ payload_hash?: string;
1340
+ /** Canonical decimal USD string (APRV-121); a JSON number is read as the historical form. */
1341
+ est_cost_usd?: UsdInput;
1342
+ }
1343
+ export type HarnessStartResult = {
1344
+ ok: true;
1345
+ record: EventRecord;
1346
+ } | GateRefusal;
1347
+ /**
1348
+ * Charge and record a harness execution that no human was asked about
1349
+ * (APRV-141).
1350
+ *
1351
+ * ## The blind spot this closes
1352
+ *
1353
+ * `core/budgets.ts` computes consumption from `approval.granted` and
1354
+ * `execution.started`, and `core/audit.ts` draws its retrospective sample from
1355
+ * `execution.started` alone. The harness hook wrote neither for a supervised or
1356
+ * autonomous verdict — the comment said, correctly, that writing one per agent
1357
+ * action fills the log — so under Claude Code the majority of real activity
1358
+ * consumed no budget, `daily_actions` included, and was invisible to the
1359
+ * overseer that exists to read a sample of it. A budget that the busiest
1360
+ * execution path does not charge is not a budget, and the decision recorded on
1361
+ * APRV-141 is that the log volume is the lesser cost.
1362
+ *
1363
+ * ## Why this record and not a new event type
1364
+ *
1365
+ * It is the same `execution.started` {@link consumeHarnessGrant} appends, with
1366
+ * the same `execution: "harness"` marker saying why no `execution.completed` or
1367
+ * `execution.failed` will ever follow: the harness runs the command and this
1368
+ * runtime never observes an exit status. Reusing the shape means budgets and
1369
+ * audit count these without learning a second vocabulary, and the gate's
1370
+ * existing single-use rule (a key with an `execution.started` is
1371
+ * `already-executed`) applies unchanged. What differs is only the authorization
1372
+ * being recorded: there, a human's grant; here, the policy itself.
1373
+ *
1374
+ * ## What it refuses
1375
+ *
1376
+ * The same two facts the hook's own guard checks and `core/execute.ts` checks
1377
+ * before an unattended start — attestation and loop-escalation — re-checked at
1378
+ * the write boundary against the records this append is authorized by, plus the
1379
+ * budget verdict this record is the charge for. A class that resolves `manual`
1380
+ * is refused outright: a manual action is authorized by a grant and spent
1381
+ * through {@link consumeHarnessGrant} or a token, and admitting one here would
1382
+ * be a second, unapproved spender.
1383
+ *
1384
+ * Since APRV-146 the content binding is refused here too. `payload-hash-required`
1385
+ * says the caller named no bytes, and it is a refusal rather than an omitted
1386
+ * field because a start event with no `payload_hash` is a record that says
1387
+ * something ran without saying what — the state APRV-140 closed everywhere else.
1388
+ */
1389
+ export declare function startHarnessExecution(logPath: string, input: HarnessStartInput, actor: string, options?: GateOptions): HarnessStartResult;
1390
+ /**
1391
+ * Which untrusted reporter asserted a harness outcome. CLOSED, and extended only
1392
+ * by a task that adds the case.
1393
+ *
1394
+ * It names the reporter and reduces nothing: it is a CLAIMED field in the
1395
+ * computed-versus-claimed vocabulary of SPEC.md §9, recorded so a reader can
1396
+ * tell a report from an observation without reading the record's provenance out
1397
+ * of its shape.
1398
+ */
1399
+ export declare const HARNESS_REPORTERS: readonly ["post-tool-use"];
1400
+ export type HarnessReporter = (typeof HARNESS_REPORTERS)[number];
1401
+ export declare function isHarnessReporter(value: unknown): value is HarnessReporter;
1402
+ /** What a harness says happened to one tool call. */
1403
+ export interface HarnessFinishInput {
1404
+ /** The harness's identifier for the run of tool calls. */
1405
+ sessionId: string;
1406
+ /** The harness's identifier for this tool call. */
1407
+ toolUseId: string;
1408
+ /** Read from the reporting event by a CLOSED set of readings; never guessed. */
1409
+ outcome: "completed" | "failed";
1410
+ reportedBy: HarnessReporter;
1411
+ /**
1412
+ * The exit code, when the harness stated one, and `null` otherwise.
1413
+ *
1414
+ * Claude Code's post-execution event carries none (`docs/claude-code-hook.md`),
1415
+ * so in practice this is `null` on that adapter. It is `null` rather than
1416
+ * omitted-and-inferred for the reason `execution.indeterminate` carries a null
1417
+ * one: a fabricated number reads exactly like a measured one.
1418
+ */
1419
+ exitCode?: number | null;
1420
+ }
1421
+ export type HarnessFinishResult = {
1422
+ ok: true;
1423
+ task: string;
1424
+ records: EventRecord[];
1425
+ } | GateRefusal;
1426
+ export declare function finishHarnessExecution(logPath: string, input: HarnessFinishInput, actor: string, options?: GateOptions): HarnessFinishResult;
1427
+ export type ExpireResult = {
1428
+ ok: true;
1429
+ record: EventRecord;
1430
+ } | GateRefusal;
1431
+ /**
1432
+ * Append `approval.expired` for a live request whose TTL has lapsed.
1433
+ *
1434
+ * The system verb: no human decides an expiry, so the actor is
1435
+ * {@link EXPIRY_ACTOR} and there is no identity to resolve. Used by the daemon's
1436
+ * sweep (M5) and by tests; `decide` performs the same append itself when it
1437
+ * discovers a lapse first.
1438
+ *
1439
+ * Refuses when the request is not live (`not-requested`, `already-decided`) or
1440
+ * when the TTL has not lapsed (`not-expired`, which also covers a policy that
1441
+ * declares no `defaults.approval_ttl` — no TTL means no lapse, and expiring a
1442
+ * request the policy never bounded would be the runtime inventing a deadline).
1443
+ *
1444
+ * `defaults.on_expiry` is recorded in the payload. Its only v0.1 value,
1445
+ * `reject`, does not change the mechanics here — an expired request is terminal
1446
+ * either way — it tells the projection layer to render the envelope's `state:`
1447
+ * as `rejected`.
1448
+ */
1449
+ export declare function expire(logPath: string, actionKey: string, options?: GateOptions): ExpireResult;