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,635 @@
1
+ /**
2
+ * Policy attestation (SPEC.md §5.2, §11) — the mechanical form of "agents MUST
3
+ * NOT be able to modify `APPROVAL.md`".
4
+ *
5
+ * The problem this solves is not that an agent *can* edit the policy file — on
6
+ * a single machine nothing stops it — but that an edited policy would otherwise
7
+ * take effect silently. Attestation closes that: a human runs
8
+ * `approval policy attest`, which appends a `policy.updated` event carrying the
9
+ * SHA-256 of the policy file's exact bytes. From then on the live file either
10
+ * hashes to the latest attestation or it does not, and {@link checkAttestation}
11
+ * says which. Gate operations (request intake, grant recording, token minting —
12
+ * APRV-16) refuse on anything but a match. An edited policy is inoperative
13
+ * until a human re-attests it.
14
+ *
15
+ * ## What this proves, and what it does not
16
+ *
17
+ * Human identity at v0.1 is **config-declared**: `--as human:<id>` or the
18
+ * `APPROVAL_HUMAN` environment variable (see {@link resolveHumanActor}). The
19
+ * trust boundary is the local machine. Anyone who can set that environment
20
+ * variable and write to the log is inside the boundary, so an attestation
21
+ * proves that *someone with local control* signed off — not *who*. There is no
22
+ * cryptographic identity here and this module claims none; that is future work,
23
+ * stated plainly rather than dressed up. What attestation buys, even so, is
24
+ * real: a policy edit made by an agent mid-run cannot become operative without
25
+ * a separate, deliberate, logged human act.
26
+ *
27
+ * ## Fail closed, in this order
28
+ *
29
+ * A policy file that cannot be read is `unreadable`, never `attested`. No
30
+ * attestation at all is `not-attested`. Bytes that do not match the latest
31
+ * attestation are `hash-mismatch`. Only an exact match is `attested`.
32
+ *
33
+ * Everything here is read-only except {@link appendAttestation}, which writes
34
+ * through `core/log.ts`'s `appendEvent` — the one sanctioned append path — and
35
+ * therefore inherits its locking, chain stamping, and write-boundary schema
36
+ * validation. Nothing in this file opens the log itself.
37
+ */
38
+ import { type ClockOptions } from "./clock.js";
39
+ import { type ProtectedPathEntry } from "./command-class.js";
40
+ import { type AppendOptions, type EventRecord } from "./log.js";
41
+ import type { ValidationError } from "./validate.js";
42
+ /** Environment variable naming the human on whose behalf the CLI attests. */
43
+ export declare const HUMAN_ACTOR_ENV = "APPROVAL_HUMAN";
44
+ /**
45
+ * The machine-readable refusal code emitted when policy is not attested.
46
+ *
47
+ * Deliberately distinct from any generic policy-load failure. A caller must be
48
+ * able to tell "the policy says manual" from "the policy is unverified", because
49
+ * the repairs are different: the first is answered by asking a human to approve
50
+ * an action, the second by asking a human to attest a file.
51
+ */
52
+ export declare const ATTESTATION_REFUSAL = "policy-not-attested";
53
+ /**
54
+ * The payload field carrying the attested policy hash a gate event was decided
55
+ * under (APRV-118, amended SPEC.md §5.2).
56
+ *
57
+ * Written on `approval.requested` and `approval.granted`, and named here rather
58
+ * than in `core/gate.ts` because the value is this module's: it is the SHA-256
59
+ * the live policy file matched when {@link checkAttestation} said `attested`.
60
+ * Pinning it lets a reader of the log answer a question attestation alone cannot
61
+ * — whether the approver decided under the rules the requester was routed by.
62
+ */
63
+ export declare const POLICY_HASH_FIELD = "policy_sha256";
64
+ /** Is `value` a lowercase-hex SHA-256, the shape {@link POLICY_HASH_FIELD} takes? */
65
+ export declare function isPolicySha256(value: unknown): value is string;
66
+ /** Why an attestation check refused. Mirrors the non-`attested` statuses. */
67
+ export type AttestationRefusalDetail = "not-attested" | "hash-mismatch" | "unreadable";
68
+ /** The refusal a gate operation surfaces to its caller (APRV-16 consumes it). */
69
+ export interface AttestationRefusal {
70
+ code: typeof ATTESTATION_REFUSAL;
71
+ detail: AttestationRefusalDetail;
72
+ message: string;
73
+ }
74
+ /**
75
+ * Why an attestation append failed: every reason `appendEvent` can give, plus
76
+ * the one rule this module enforces on its own.
77
+ *
78
+ * ### Why `actor-not-human` lives here and not in `core/log.ts`
79
+ *
80
+ * Until APRV-20 pass two the non-human-actor refusal reused `validation`, which
81
+ * conflated two different facts: "the record failed `event.schema.json` at the
82
+ * write boundary" and "the caller is not allowed to perform this verb". A
83
+ * caller branching on `validation` could not tell a malformed event from a
84
+ * forbidden one, and the two call for opposite responses (fix the record;
85
+ * fetch a human).
86
+ *
87
+ * The fix does **not** add the code to `APPEND_ERROR_CODES`. That union is the
88
+ * log writer's vocabulary — the ways a byte can fail to reach the file — and
89
+ * "only humans may attest" is a fact about attestation, not about writing.
90
+ * Widening the writer's union to carry a caller's policy rule would oblige every
91
+ * future append site to consider a code that can only ever come from this one,
92
+ * and would make `core/log.ts` the place people look for permission rules.
93
+ * Instead this module widens the union *for itself*: `AppendResult` remains
94
+ * assignable to {@link AttestationAppendResult}, so nothing downstream is
95
+ * forced to change, and the CLI adds one case.
96
+ */
97
+ export declare const ATTEST_ERROR_CODES: readonly ["lock-timeout", "corrupt-tail", "validation", "canonicalization", "io", "head-moved", "actor-not-human"];
98
+ export type AttestErrorCode = (typeof ATTEST_ERROR_CODES)[number];
99
+ export interface AttestError {
100
+ code: AttestErrorCode;
101
+ message: string;
102
+ /** Schema errors, present when `code` is "validation". */
103
+ errors?: ValidationError[];
104
+ }
105
+ /** {@link appendAttestation}'s result: `AppendResult` widened by one code. */
106
+ export type AttestationAppendResult = {
107
+ ok: true;
108
+ record: EventRecord;
109
+ line: string;
110
+ } | {
111
+ ok: false;
112
+ error: AttestError;
113
+ };
114
+ /** Options for {@link appendAttestation}: the append's, plus the clock. */
115
+ export interface AttestOptions extends AppendOptions, ClockOptions {
116
+ /**
117
+ * When supplied, append only if the file bytes read by this call have this
118
+ * digest. A ceremony that displayed bytes before asking for confirmation can
119
+ * thereby bind the record to what it showed rather than silently signing a
120
+ * replacement written between display and append.
121
+ */
122
+ expectedSha256?: string;
123
+ }
124
+ /** The result of comparing the live policy file against the log. */
125
+ export type AttestationStatus = {
126
+ status: "attested";
127
+ seq: number;
128
+ sha256: string;
129
+ } | {
130
+ status: "not-attested";
131
+ } | {
132
+ status: "hash-mismatch";
133
+ attestedSha256: string;
134
+ liveSha256: string;
135
+ seq: number;
136
+ } | {
137
+ status: "unreadable";
138
+ message: string;
139
+ };
140
+ /**
141
+ * SHA-256 (lowercase hex) of a file's **exact bytes**.
142
+ *
143
+ * Bytes, not text: no encoding round-trip, no newline normalization, no parse.
144
+ * A policy file that differs only in trailing whitespace is a different policy
145
+ * file as far as attestation is concerned, which is the conservative reading
146
+ * and the only one that survives an adversary who knows what gets normalized.
147
+ *
148
+ * Throws if the file cannot be read; callers that need a status rather than an
149
+ * exception use {@link checkAttestation}, which converts that into
150
+ * `unreadable`.
151
+ */
152
+ export declare function policyFileHash(path: string): string;
153
+ /**
154
+ * SHA-256 (lowercase hex) of policy bytes a caller has already read.
155
+ *
156
+ * The same digest {@link policyFileHash} computes, over bytes rather than a
157
+ * path. It exists so a gate operation can read `APPROVAL.md` once and hash the
158
+ * exact buffer it is also about to parse (APRV-142): hashing by path a second
159
+ * time would reopen the window where the attestation check and the parse can
160
+ * see different bytes.
161
+ */
162
+ export declare function policyBytesHash(bytes: Uint8Array): string;
163
+ /**
164
+ * Append a `policy.updated` attestation for `policyPath` to `logPath`.
165
+ *
166
+ * `actor` MUST match `^human:.+`. An `agent:` or `system:` actor is refused
167
+ * before anything is read or written — the refusal is a structured result,
168
+ * never a throw, consistent with the rest of the write path. Its code is
169
+ * `actor-not-human`, this module's own (see {@link ATTEST_ERROR_CODES}): the
170
+ * actor failed the rule *this verb* enforces, which is a different fact from a
171
+ * record failing the event schema, and the two used to share `validation`.
172
+ *
173
+ * The event is deliberately minimal:
174
+ *
175
+ * ```json
176
+ * { "event": "policy.updated", "actor": "human:alice",
177
+ * "payload": { "policy_path": "APPROVAL.md", "sha256": "<64 hex>" } }
178
+ * ```
179
+ *
180
+ * `policy_path` is the **basename**, not the absolute path: the log is meant to
181
+ * be copied, exported, and read on other machines, and an absolute path would
182
+ * leak the writer's home directory into a permanent record while saying nothing
183
+ * a reader can use. `sha256` is the file's byte digest — the field
184
+ * {@link checkAttestation} compares against.
185
+ *
186
+ * `ts` is **not** a parameter. `policy.updated` is one of the gate-typed events
187
+ * of amended SPEC.md §8 (A2), so its timestamp is assigned by the runtime at the
188
+ * write boundary — read once from {@link AttestOptions.clock}, which defaults to
189
+ * the real clock and which tests inject. Attestation is the verb that decides
190
+ * which policy bytes are operative from a moment onward; a caller that could
191
+ * choose that moment could backdate the answer.
192
+ *
193
+ * ## Why this append carries no `expectedHead` (APRV-20 finding B1)
194
+ *
195
+ * Every *other* append site in the codebase reads the log, decides something
196
+ * from what it read, and must therefore prove the log has not moved before it
197
+ * writes. This one does not read the log at all. Its only precondition is the
198
+ * actor check and the policy file's bytes — neither of which the log can
199
+ * invalidate — so there is no check-then-act window to close. A concurrent
200
+ * appender simply means this attestation lands after that record, which is
201
+ * correct: attestation is an unconditional assertion about a file's bytes at a
202
+ * moment, and the *latest* attestation is the one `checkAttestation` honors.
203
+ * Passing a precondition here would only manufacture spurious failures.
204
+ */
205
+ /**
206
+ * The value a terminal attestation stores for the bytes it attests (APRV-356).
207
+ *
208
+ * One author for the shape, because two parties need it and they must agree:
209
+ * {@link appendAttestation} writes it, and `cli/amend.ts` has to name the file
210
+ * it will land in before the append happens, so the ceremony commit can carry
211
+ * it. A second spelling of `{ text }` anywhere would be a store file the
212
+ * ceremony quietly left behind.
213
+ *
214
+ * Deliberately narrower than `proposalPayloadValue` in `core/policy-proposal.ts`,
215
+ * which carries `policy_path` beside the text: a proposal is a prompt and names
216
+ * the file it is asking about, while an attestation is a binding and the bytes
217
+ * are the whole of it. The two therefore address different files in the store
218
+ * for the same policy, which costs one duplicate and keeps each record's
219
+ * binding meaning exactly one thing.
220
+ */
221
+ export declare function attestedPolicyPayload(text: string): {
222
+ text: string;
223
+ };
224
+ /** The store hash {@link attestedPolicyPayload} addresses for these bytes. */
225
+ export declare function attestedPolicyPayloadHash(text: string): string;
226
+ export declare function appendAttestation(logPath: string, policyPath: string, actor: string, options?: AttestOptions): AttestationAppendResult;
227
+ /**
228
+ * Is this record an attestation — a `policy.updated` carrying a `sha256`?
229
+ *
230
+ * `policy.updated` records **without** a `payload.sha256` are ignored for
231
+ * attestation purposes. Two reasons, one forward and one backward: the event
232
+ * type predates this verb (SPEC.md §8 has always had it, and the schema keeps
233
+ * it base-only with no required payload fields), so pre-attestation logs and
234
+ * hand-rolled tooling may carry `policy.updated` events that assert nothing
235
+ * about bytes; and treating such a record as an attestation would mean a
236
+ * payload-less event could *satisfy* the guard, which is exactly backwards.
237
+ * Ignoring them means an old log reads as `not-attested` — fail closed.
238
+ */
239
+ export declare function attestationSha256(record: EventRecord): string | null;
240
+ /**
241
+ * Compare the live policy file against the latest attestation in `records`.
242
+ *
243
+ * `records` is the log in append order, as a caller has already read and
244
+ * verified it; this function does no I/O on the log and never writes. The
245
+ * **latest** attestation wins — re-attesting after an edit is the supported
246
+ * repair, and an older attestation must not be able to vouch for bytes that a
247
+ * newer one already disagreed with.
248
+ *
249
+ * `payload.policy_path` is recorded but deliberately **not** matched against
250
+ * `policyPath`: v0.1 has exactly one policy file per approval home (SPEC.md
251
+ * §5), and a filename filter would let an attestation of `APPROVALS.md` and a
252
+ * live `APPROVAL.md` drift apart silently. Comparing only bytes means the
253
+ * mismatch surfaces as a mismatch.
254
+ */
255
+ export declare function checkAttestation(records: EventRecord[], policyPath: string): AttestationStatus;
256
+ /** The `unreadable` status for a policy read the caller performed itself. */
257
+ export declare function unreadablePolicyStatus(policyPath: string, cause: string): AttestationStatus;
258
+ /**
259
+ * {@link checkAttestation} against bytes the caller already holds.
260
+ *
261
+ * Same comparison, no read. A gate operation reads the policy file once and
262
+ * passes that one buffer here and to the parser (APRV-142), which is what makes
263
+ * "attested one policy, enforced another" structurally impossible rather than
264
+ * merely unlikely.
265
+ *
266
+ * The `unreadable` status has no counterpart here: bytes that exist were read.
267
+ * A caller whose read failed calls {@link unreadablePolicyStatus} instead, so
268
+ * the fail-closed ordering of {@link checkAttestation} survives the split.
269
+ */
270
+ export declare function checkAttestationOfBytes(records: EventRecord[], bytes: Uint8Array): AttestationStatus;
271
+ /**
272
+ * The refusal for a non-`attested` status, or `null` when policy is attested.
273
+ *
274
+ * One code (`policy-not-attested`) with a `detail` discriminator, rather than
275
+ * three codes: a caller that wants to refuse needs to branch on one value, and
276
+ * a caller that wants to explain has `detail` and `message`.
277
+ */
278
+ export declare function attestationRefusal(status: AttestationStatus): AttestationRefusal | null;
279
+ /**
280
+ * The human identity to record as `actor`, or `null` when none is declared.
281
+ *
282
+ * Precedence: `options.actor` (the CLI's `--as`) first, then the
283
+ * `APPROVAL_HUMAN` environment variable. An explicit `--as` that does not match
284
+ * `^human:.+` yields `null` rather than falling back to the environment —
285
+ * silently substituting a different identity for the one the caller typed would
286
+ * be worse than refusing.
287
+ *
288
+ * This is **config-declared identity**. The trust boundary is the local
289
+ * machine: whoever can set `APPROVAL_HUMAN` and write to the log is inside it.
290
+ * v0.1 makes no cryptographic claim about who attested, only that a locally
291
+ * privileged act occurred and was recorded.
292
+ */
293
+ export declare function resolveHumanActor(options?: {
294
+ actor?: string;
295
+ }): string | null;
296
+ /**
297
+ * The event an ORGAN attestation is: a human's sign-off on the exact bytes of
298
+ * one of the gate's own organ files.
299
+ *
300
+ * ## Why the organs need this at all
301
+ *
302
+ * `.claude/settings.json` and Cursor's hook files classify `policy.core`, and
303
+ * `policy.core` is human-only in this repository's policy and inert to agents
304
+ * by §11.1 invariant 9. So the gate mints NOTHING for them: `core/gate.ts`
305
+ * refuses a human-only class before any record is appended, which means the
306
+ * protected-path guard's `granted-file` and `granted-command` verdicts cannot
307
+ * exist for an organ, however correctly a human edited it. PR #300 is that hole,
308
+ * exactly: a human hand-committed hook entries and CI failed the change because
309
+ * the only evidence the guard could accept was evidence the gate is designed
310
+ * never to produce.
311
+ *
312
+ * Attestation is the primitive that already fits. It records "a human saw these
313
+ * exact bytes", it needs no grant, and it matches CONTENT rather than a path, so
314
+ * the guard can hash the blob at head and ask the log about it.
315
+ *
316
+ * ## Why a separate event type
317
+ *
318
+ * `core/log.ts`'s `EventType` note carries the argument in full. In one line:
319
+ * every reader of the POLICY attestation selects on `event === "policy.updated"`,
320
+ * so a distinct type is invisible to all of them by construction, and
321
+ * {@link checkAttestationOfBytes} returns at the first `sha256`-bearing record
322
+ * it meets scanning backwards — an organ record wearing `policy.updated` would
323
+ * have made a correctly attested policy read as `hash-mismatch`.
324
+ */
325
+ export declare const ORGAN_ATTESTATION_EVENT: "gate.organ.attested";
326
+ /**
327
+ * The payload field naming which organ was attested.
328
+ *
329
+ * Repository-relative and `/`-separated, never absolute: the log is meant to be
330
+ * copied and read on other machines, and an absolute path would leak the
331
+ * writer's home directory into a permanent record. Unlike the policy
332
+ * attestation, which records a BASENAME because v0.1 has exactly one policy file
333
+ * per approval home, an organ record has to carry the whole relative path —
334
+ * there are several organs, they live in different directories, and the guard's
335
+ * rule is that a digest attested for one path is not evidence for another.
336
+ */
337
+ export declare const ORGAN_PATH_FIELD = "organ_path";
338
+ /**
339
+ * Why an organ attestation was refused: the policy attestation's codes, plus
340
+ * the two rules this verb has of its own.
341
+ *
342
+ * Two codes and not one, because the repairs are different and a caller must be
343
+ * able to tell them apart. `path-is-policy` means the caller aimed the organ
344
+ * verb at the policy file, whose attestation is `approval policy attest` with no
345
+ * `--organ` and which the gate reads on every operation. `path-not-organ` covers
346
+ * everything else the runtime will not attest this way: the approval home, whose
347
+ * contents are the human's own ceremony surface, and any ordinary file, which is
348
+ * not part of the gate and needs no attestation to be edited.
349
+ */
350
+ export declare const ORGAN_ATTEST_ERROR_CODES: readonly ["lock-timeout", "corrupt-tail", "validation", "canonicalization", "io", "head-moved", "actor-not-human", "path-not-organ", "path-is-policy"];
351
+ export type OrganAttestErrorCode = (typeof ORGAN_ATTEST_ERROR_CODES)[number];
352
+ export interface OrganAttestError {
353
+ code: OrganAttestErrorCode;
354
+ message: string;
355
+ /** Schema errors, present when `code` is "validation". */
356
+ errors?: ValidationError[];
357
+ }
358
+ /** {@link appendOrganAttestation}'s result: the append's, widened by two codes. */
359
+ export type OrganAttestationAppendResult = {
360
+ ok: true;
361
+ record: EventRecord;
362
+ line: string;
363
+ } | {
364
+ ok: false;
365
+ error: OrganAttestError;
366
+ };
367
+ /**
368
+ * Which file to attest, in the two spellings that are not the same fact.
369
+ *
370
+ * `path` is the identity the record carries and the guard matches on, so it is
371
+ * repository-relative. `root` is where that path is rooted on THIS machine, and
372
+ * it never reaches the log. Splitting them is what keeps an absolute path out
373
+ * of a permanent record without making the record ambiguous about which of
374
+ * several organs it names.
375
+ */
376
+ export interface OrganTarget {
377
+ /** Repository-relative, e.g. `.claude/settings.json`. */
378
+ path: string;
379
+ /** The checkout `path` is relative to; `join(root, path)` is hashed. */
380
+ root: string;
381
+ }
382
+ /** The identity and digest an organ attestation carries, or `null`. */
383
+ export interface OrganAttestationFields {
384
+ organPath: string;
385
+ sha256: string;
386
+ }
387
+ /** An organ attestation found in the log, with the record it came from. */
388
+ export interface OrganAttestation extends OrganAttestationFields {
389
+ record: EventRecord;
390
+ }
391
+ /**
392
+ * Append a `gate.organ.attested` record for `target` to `logPath`.
393
+ *
394
+ * The rules, all of them refused as structured results and never as throws, in
395
+ * the order they are checked:
396
+ *
397
+ * 1. `actor` MUST match `^human:.+`. Attestation is the verb an agent must not
398
+ * perform, and it is refused here in code and again at the CLI, exactly as
399
+ * {@link appendAttestation} is.
400
+ * 2. The path must be repository-relative: absolute paths and `..` are refused,
401
+ * because the recorded identity has to mean the same file on the machine
402
+ * that later reads the log.
403
+ * 3. The policy file is refused with its own code. It has its own attestation
404
+ * and the gate reads it on every operation; letting the organ verb write a
405
+ * record about it would create a second thing that looks like a policy
406
+ * attestation and is not one.
407
+ * 4. The path must be a gate organ ({@link isGateOrganPath}). Nothing else is
408
+ * attestable this way, so the verb cannot be turned into a general
409
+ * "bless these bytes" primitive for any file at all.
410
+ *
411
+ * The digest is computed by the runtime from the file's exact bytes. There is
412
+ * no parameter for it, which is the same reason there is no `ts` parameter: a
413
+ * caller who could supply the hash could attest bytes nobody read, and a caller
414
+ * who could supply the moment could backdate what was operative when.
415
+ *
416
+ * Like {@link appendAttestation} this append carries no `expectedHead`. It reads
417
+ * nothing from the log, so it has no check-then-act window to close; a
418
+ * concurrent appender only means this record lands after theirs.
419
+ */
420
+ export declare function appendOrganAttestation(logPath: string, target: OrganTarget, actor: string, options?: AttestOptions): OrganAttestationAppendResult;
421
+ /**
422
+ * Is this record an organ attestation, and which bytes of which file does it
423
+ * vouch for?
424
+ *
425
+ * A record missing either field is ignored, for the reason a `policy.updated`
426
+ * with no `sha256` is ignored: a record that asserts nothing about bytes must
427
+ * never be able to SATISFY a check about bytes. Fail closed.
428
+ */
429
+ export declare function organAttestationOf(record: EventRecord): OrganAttestationFields | null;
430
+ /**
431
+ * The record in which a human attested THIS digest FOR THIS path, latest first,
432
+ * or `null`.
433
+ *
434
+ * Both halves are required and that is the whole rule: a digest attested for
435
+ * some other organ is not evidence for this one. Two organ files with identical
436
+ * bytes (two checkouts of the same hook entry, say) are still two files, and a
437
+ * human who signed one has said nothing about the other.
438
+ *
439
+ * Unlike the policy attestation there is no "latest wins" supersession here.
440
+ * Every organ attestation stands for the bytes it names: the guard asks about a
441
+ * blob at a commit that may be months old, and the newest attestation of a file
442
+ * says nothing about whether an older set of bytes was once signed off. The
443
+ * gate reads none of these records, so a stale one authorizes nothing on its
444
+ * own — it is evidence about a change, and evidence does not expire the way an
445
+ * operative policy does.
446
+ */
447
+ export declare function findOrganAttestation(records: readonly EventRecord[], organPath: string, sha256: string): EventRecord | null;
448
+ /**
449
+ * The most recent attestation of `organPath` at ANY digest, or `null`.
450
+ *
451
+ * What a status surface needs to tell "this file has never been attested" from
452
+ * "this file was attested and has been edited since". Nothing enforcing reads
453
+ * it: {@link findOrganAttestation} is the question the guard asks.
454
+ */
455
+ export declare function latestOrganAttestation(records: readonly EventRecord[], organPath: string): OrganAttestation | null;
456
+ /**
457
+ * The event a protected-path SIGN-OFF is: a human's statement that they read
458
+ * one `policy.edit` file at these exact bytes and stand behind them.
459
+ *
460
+ * ## The hole this fills
461
+ *
462
+ * SPEC.md's amendment-provenance rule says text that reached a protected file
463
+ * without a grant carries `(Amended APRV-n, pending sign-off.)` until a human
464
+ * ratifies it, and doubt resolves to pending. Nothing recorded the
465
+ * ratification. A human who had read a diff and agreed with it had exactly two
466
+ * ways to get it past the protected-path guard: re-make the edit under a grant,
467
+ * or change the guard. Both are the wrong shape — the first re-does work the
468
+ * human has already done with their eyes, and the second edits an enforcement
469
+ * path to admit one change.
470
+ *
471
+ * ## Why it is not the organ record, and not a grant either
472
+ *
473
+ * It sits between them, and the ordering is the point.
474
+ *
475
+ * A grant binds the exact hunk: `{before, after}` bytes a human saw in a
476
+ * prompt. It is the strongest evidence in the system about a CHANGE, and
477
+ * nothing here weakens it — {@link findPathSignOff} is asked only after the
478
+ * guard's hunk search has failed, so a covered change still passes on its
479
+ * grants and still prints them as its reason.
480
+ *
481
+ * {@link ORGAN_ATTESTATION_EVENT} is the other end. An organ is `policy.core`,
482
+ * a policy may resolve `policy.core` to `human-only`, and the gate mints
483
+ * nothing for a human-only class (§11.1 invariant 9), so for an organ content
484
+ * attestation is not the weaker evidence, it is the only evidence that can
485
+ * exist. A protected path is not in that position: a `policy.edit` grant for it
486
+ * is obtainable, so this record must never be the first thing a reader reaches
487
+ * for. A separate type is what makes that ordering structural rather than a
488
+ * convention every reader has to remember, and it is why the organ verb's
489
+ * refusals and this one's are not merged.
490
+ *
491
+ * The `gate.` prefix is deliberate: SPEC.md §8 keys the write-boundary clock on
492
+ * it, and a signer who could supply the moment could place a sign-off before
493
+ * bytes they had not yet seen.
494
+ */
495
+ export declare const PATH_SIGN_OFF_EVENT: "gate.path.signed_off";
496
+ /**
497
+ * The payload field naming which file was signed off.
498
+ *
499
+ * Repository-relative and `/`-separated, for the reason {@link ORGAN_PATH_FIELD}
500
+ * is: the log is copied and read on other machines, and an absolute path would
501
+ * leak the signer's home directory into a permanent record. Spelled `path`
502
+ * rather than `signed_path` because the record type already says what kind of
503
+ * statement it is, and a reader should not have to learn a second noun.
504
+ */
505
+ export declare const SIGN_OFF_PATH_FIELD = "path";
506
+ /**
507
+ * Why a sign-off was refused: the attestation codes, plus the three path rules
508
+ * this verb has of its own.
509
+ *
510
+ * Three and not one, because the repair differs in each case and a caller has
511
+ * to be able to tell them apart without reading prose:
512
+ *
513
+ * - `path-is-policy` — the policy file, whose sign-off is its attestation and
514
+ * which the gate reads on every operation. A second record that looked like
515
+ * a statement about the policy's bytes is exactly what the organ verb refuses
516
+ * to create, for the same reason.
517
+ * - `path-is-core` — a `policy.core` surface or the log directory. An organ is
518
+ * signed off with `--organ`, which is a different record under different
519
+ * rules; the approval home and the log are the human's own ceremony surface
520
+ * and are not ratified by any verb at all.
521
+ * - `path-not-protected` — an ordinary file, or a path that is not
522
+ * repository-relative. Nothing to ratify: an unprotected file's edits are not
523
+ * gated, so a record about them would assert authority over nothing.
524
+ */
525
+ export declare const PATH_SIGN_OFF_ERROR_CODES: readonly ["lock-timeout", "corrupt-tail", "validation", "canonicalization", "io", "head-moved", "actor-not-human", "path-not-protected", "path-is-policy", "path-is-core"];
526
+ export type PathSignOffErrorCode = (typeof PATH_SIGN_OFF_ERROR_CODES)[number];
527
+ export interface PathSignOffError {
528
+ code: PathSignOffErrorCode;
529
+ message: string;
530
+ /** Schema errors, present when `code` is "validation". */
531
+ errors?: ValidationError[];
532
+ }
533
+ /** {@link appendPathSignOff}'s result: the append's, widened by three codes. */
534
+ export type PathSignOffAppendResult = {
535
+ ok: true;
536
+ record: EventRecord;
537
+ line: string;
538
+ } | {
539
+ ok: false;
540
+ error: PathSignOffError;
541
+ };
542
+ /**
543
+ * Which file to sign off, in the two spellings that are not the same fact,
544
+ * plus the policy's own protected list.
545
+ *
546
+ * `path` is the identity the record carries and the checker matches on, so it
547
+ * is repository-relative; `root` is where that path is rooted on THIS machine
548
+ * and never reaches the log. `protectedPaths` is `policy.protected_paths`: a
549
+ * project that widened its protected surface may sign off the files it added,
550
+ * and a caller that omits the list gets the built-in set alone, which is the
551
+ * strictly NARROWER answer and therefore the fail-closed one.
552
+ */
553
+ export interface SignOffTarget {
554
+ /** Repository-relative, e.g. `SPEC.md`. */
555
+ path: string;
556
+ /** The checkout `path` is relative to; `join(root, path)` is hashed. */
557
+ root: string;
558
+ /** `policy.protected_paths`, widening which paths are signable. */
559
+ protectedPaths?: readonly ProtectedPathEntry[];
560
+ }
561
+ /** The identity and digest a sign-off carries, or `null`. */
562
+ export interface PathSignOffFields {
563
+ path: string;
564
+ sha256: string;
565
+ }
566
+ /** A sign-off found in the log, with the record it came from. */
567
+ export interface PathSignOff extends PathSignOffFields {
568
+ record: EventRecord;
569
+ }
570
+ /**
571
+ * Append a `gate.path.signed_off` record for `target` to `logPath`.
572
+ *
573
+ * The rules, refused as structured results and never as throws, in the order
574
+ * they are checked — and the order is normative, because a path can break more
575
+ * than one of them and the caller's repair depends on which they are told:
576
+ *
577
+ * 1. `actor` MUST match `^human:.+`. This is the verb an agent must not
578
+ * perform: a record the party under oversight could write would let it
579
+ * ratify its own text, which is the whole of what the pending-sign-off
580
+ * suffix exists to prevent (§11.1 invariants 4 and 9).
581
+ * 2. The path must be repository-relative: absolute paths and `..` are refused,
582
+ * because the recorded identity has to mean the same file on the machine
583
+ * that later reads the log.
584
+ * 3. The policy file is refused with its own code; it has its own attestation.
585
+ * 4. `policy.core` and `log.mutate` surfaces are refused with their own code.
586
+ * 5. What remains must classify `policy.edit` or a `policy.edit.*` sub-class.
587
+ * Anything else is not a protected path, and this verb is not a general
588
+ * "bless these bytes" primitive.
589
+ *
590
+ * Every one of those is decided BEFORE the file is read, so a path this verb
591
+ * would never sign off is refused whether or not it exists.
592
+ *
593
+ * The digest is computed by the runtime from the file's exact bytes. There is
594
+ * no parameter for it and none for `ts`, for the reason
595
+ * {@link appendOrganAttestation} has neither: a caller who could supply the
596
+ * hash could ratify bytes nobody read, and one who could supply the moment
597
+ * could backdate what they had seen when.
598
+ *
599
+ * Like the attestations this append carries no `expectedHead`: it reads nothing
600
+ * from the log, so it has no check-then-act window to close.
601
+ */
602
+ export declare function appendPathSignOff(logPath: string, target: SignOffTarget, actor: string, options?: AttestOptions): PathSignOffAppendResult;
603
+ /**
604
+ * Is this record a protected-path sign-off, and which bytes of which file does
605
+ * it stand behind?
606
+ *
607
+ * A record missing either field is ignored, for the reason a `policy.updated`
608
+ * with no `sha256` is ignored: a record that asserts nothing about bytes must
609
+ * never be able to SATISFY a check about bytes. Fail closed.
610
+ */
611
+ export declare function pathSignOffOf(record: EventRecord): PathSignOffFields | null;
612
+ /**
613
+ * The record in which a human signed off THIS digest FOR THIS path, latest
614
+ * first, or `null`.
615
+ *
616
+ * Both halves are required and that is the whole rule, exactly as it is for an
617
+ * organ: a digest signed for some other file is not evidence about this one,
618
+ * and bytes edited after a sign-off are bytes nobody signed. Two files that
619
+ * happen to hash alike are still two files, and a human who read one has said
620
+ * nothing about the other.
621
+ *
622
+ * There is no "latest wins" supersession. A sign-off stands for the bytes it
623
+ * names: the checker asks about a blob at a commit that may be old, and the
624
+ * newest sign-off of a file says nothing about whether an earlier state was
625
+ * once ratified.
626
+ */
627
+ export declare function findPathSignOff(records: readonly EventRecord[], path: string, sha256: string): EventRecord | null;
628
+ /**
629
+ * The most recent sign-off of `path` at ANY digest, or `null`.
630
+ *
631
+ * What a status surface needs to tell "this file was never signed off" from
632
+ * "this file was signed off and has been edited since". Nothing enforcing reads
633
+ * it: {@link findPathSignOff} is the question the guard asks.
634
+ */
635
+ export declare function latestPathSignOff(records: readonly EventRecord[], path: string): PathSignOff | null;