approval-md 0.0.1 → 0.2.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 (722) hide show
  1. package/LICENSE +176 -0
  2. package/NOTICE +5 -0
  3. package/README.md +940 -4
  4. package/SPEC.md +476 -34
  5. package/cli.js +29 -3
  6. package/dist/src/adapters/agentmail.d.ts +426 -0
  7. package/dist/src/adapters/agentmail.js +1200 -0
  8. package/dist/src/adapters/agentmail.js.map +1 -0
  9. package/dist/src/adapters/conformance.d.ts +149 -0
  10. package/dist/src/adapters/conformance.js +461 -0
  11. package/dist/src/adapters/conformance.js.map +1 -0
  12. package/dist/src/adapters/contract.d.ts +628 -0
  13. package/dist/src/adapters/contract.js +1035 -0
  14. package/dist/src/adapters/contract.js.map +1 -0
  15. package/dist/src/adapters/email.d.ts +324 -0
  16. package/dist/src/adapters/email.js +749 -0
  17. package/dist/src/adapters/email.js.map +1 -0
  18. package/dist/src/adapters/env-passphrase.d.ts +93 -0
  19. package/dist/src/adapters/env-passphrase.js +132 -0
  20. package/dist/src/adapters/env-passphrase.js.map +1 -0
  21. package/dist/src/adapters/public.d.ts +11 -0
  22. package/dist/src/adapters/public.js +11 -0
  23. package/dist/src/adapters/public.js.map +1 -0
  24. package/dist/src/adapters/registry.d.ts +59 -0
  25. package/dist/src/adapters/registry.js +77 -0
  26. package/dist/src/adapters/registry.js.map +1 -0
  27. package/dist/src/adapters/smtp.d.ts +213 -0
  28. package/dist/src/adapters/smtp.js +499 -0
  29. package/dist/src/adapters/smtp.js.map +1 -0
  30. package/dist/src/adapters/vault-provider.d.ts +114 -0
  31. package/dist/src/adapters/vault-provider.js +161 -0
  32. package/dist/src/adapters/vault-provider.js.map +1 -0
  33. package/dist/src/adapters/zzz.d.ts +66 -0
  34. package/dist/src/adapters/zzz.js +299 -0
  35. package/dist/src/adapters/zzz.js.map +1 -0
  36. package/dist/src/channels/batch.d.ts +109 -0
  37. package/dist/src/channels/batch.js +121 -0
  38. package/dist/src/channels/batch.js.map +1 -0
  39. package/dist/src/channels/cli.d.ts +193 -0
  40. package/dist/src/channels/cli.js +468 -0
  41. package/dist/src/channels/cli.js.map +1 -0
  42. package/dist/src/channels/conformance.d.ts +92 -0
  43. package/dist/src/channels/conformance.js +445 -0
  44. package/dist/src/channels/conformance.js.map +1 -0
  45. package/dist/src/channels/contract.d.ts +623 -0
  46. package/dist/src/channels/contract.js +494 -0
  47. package/dist/src/channels/contract.js.map +1 -0
  48. package/dist/src/channels/payload-view.d.ts +35 -0
  49. package/dist/src/channels/payload-view.js +43 -0
  50. package/dist/src/channels/payload-view.js.map +1 -0
  51. package/dist/src/channels/render-queue.d.ts +149 -0
  52. package/dist/src/channels/render-queue.js +564 -0
  53. package/dist/src/channels/render-queue.js.map +1 -0
  54. package/dist/src/channels/tagging.d.ts +196 -0
  55. package/dist/src/channels/tagging.js +723 -0
  56. package/dist/src/channels/tagging.js.map +1 -0
  57. package/dist/src/channels/telegram.d.ts +1832 -0
  58. package/dist/src/channels/telegram.js +3190 -0
  59. package/dist/src/channels/telegram.js.map +1 -0
  60. package/dist/src/channels/web.d.ts +341 -0
  61. package/dist/src/channels/web.js +903 -0
  62. package/dist/src/channels/web.js.map +1 -0
  63. package/dist/src/cli/adapter.d.ts +90 -0
  64. package/dist/src/cli/adapter.js +288 -0
  65. package/dist/src/cli/adapter.js.map +1 -0
  66. package/dist/src/cli/amend.d.ts +59 -0
  67. package/dist/src/cli/amend.js +2171 -0
  68. package/dist/src/cli/amend.js.map +1 -0
  69. package/dist/src/cli/args.d.ts +43 -0
  70. package/dist/src/cli/args.js +86 -0
  71. package/dist/src/cli/args.js.map +1 -0
  72. package/dist/src/cli/attest.d.ts +41 -0
  73. package/dist/src/cli/attest.js +307 -0
  74. package/dist/src/cli/attest.js.map +1 -0
  75. package/dist/src/cli/audit-card.d.ts +62 -0
  76. package/dist/src/cli/audit-card.js +201 -0
  77. package/dist/src/cli/audit-card.js.map +1 -0
  78. package/dist/src/cli/audit.d.ts +59 -0
  79. package/dist/src/cli/audit.js +460 -0
  80. package/dist/src/cli/audit.js.map +1 -0
  81. package/dist/src/cli/channel-telegram.d.ts +806 -0
  82. package/dist/src/cli/channel-telegram.js +2063 -0
  83. package/dist/src/cli/channel-telegram.js.map +1 -0
  84. package/dist/src/cli/channel-web.d.ts +131 -0
  85. package/dist/src/cli/channel-web.js +357 -0
  86. package/dist/src/cli/channel-web.js.map +1 -0
  87. package/dist/src/cli/channel.d.ts +71 -0
  88. package/dist/src/cli/channel.js +438 -0
  89. package/dist/src/cli/channel.js.map +1 -0
  90. package/dist/src/cli/checkpoint-tap.d.ts +169 -0
  91. package/dist/src/cli/checkpoint-tap.js +238 -0
  92. package/dist/src/cli/checkpoint-tap.js.map +1 -0
  93. package/dist/src/cli/codex.d.ts +2 -0
  94. package/dist/src/cli/codex.js +172 -0
  95. package/dist/src/cli/codex.js.map +1 -0
  96. package/dist/src/cli/coverage.d.ts +61 -0
  97. package/dist/src/cli/coverage.js +343 -0
  98. package/dist/src/cli/coverage.js.map +1 -0
  99. package/dist/src/cli/daemon.d.ts +120 -0
  100. package/dist/src/cli/daemon.js +631 -0
  101. package/dist/src/cli/daemon.js.map +1 -0
  102. package/dist/src/cli/doctor.d.ts +129 -0
  103. package/dist/src/cli/doctor.js +2762 -0
  104. package/dist/src/cli/doctor.js.map +1 -0
  105. package/dist/src/cli/env.d.ts +65 -0
  106. package/dist/src/cli/env.js +302 -0
  107. package/dist/src/cli/env.js.map +1 -0
  108. package/dist/src/cli/execute.d.ts +202 -0
  109. package/dist/src/cli/execute.js +1682 -0
  110. package/dist/src/cli/execute.js.map +1 -0
  111. package/dist/src/cli/exit-codes.d.ts +73 -0
  112. package/dist/src/cli/exit-codes.js +82 -0
  113. package/dist/src/cli/exit-codes.js.map +1 -0
  114. package/dist/src/cli/feedback.d.ts +60 -0
  115. package/dist/src/cli/feedback.js +205 -0
  116. package/dist/src/cli/feedback.js.map +1 -0
  117. package/dist/src/cli/gate-window.d.ts +40 -0
  118. package/dist/src/cli/gate-window.js +294 -0
  119. package/dist/src/cli/gate-window.js.map +1 -0
  120. package/dist/src/cli/gate.d.ts +68 -0
  121. package/dist/src/cli/gate.js +557 -0
  122. package/dist/src/cli/gate.js.map +1 -0
  123. package/dist/src/cli/git-scope.d.ts +190 -0
  124. package/dist/src/cli/git-scope.js +295 -0
  125. package/dist/src/cli/git-scope.js.map +1 -0
  126. package/dist/src/cli/gloss-attach.d.ts +85 -0
  127. package/dist/src/cli/gloss-attach.js +107 -0
  128. package/dist/src/cli/gloss-attach.js.map +1 -0
  129. package/dist/src/cli/gloss-codex-child.d.ts +9 -0
  130. package/dist/src/cli/gloss-codex-child.js +149 -0
  131. package/dist/src/cli/gloss-codex-child.js.map +1 -0
  132. package/dist/src/cli/gloss-codex.d.ts +24 -0
  133. package/dist/src/cli/gloss-codex.js +255 -0
  134. package/dist/src/cli/gloss-codex.js.map +1 -0
  135. package/dist/src/cli/gloss-options.d.ts +42 -0
  136. package/dist/src/cli/gloss-options.js +79 -0
  137. package/dist/src/cli/gloss-options.js.map +1 -0
  138. package/dist/src/cli/gloss.d.ts +265 -0
  139. package/dist/src/cli/gloss.js +362 -0
  140. package/dist/src/cli/gloss.js.map +1 -0
  141. package/dist/src/cli/help.d.ts +103 -0
  142. package/dist/src/cli/help.js +2339 -0
  143. package/dist/src/cli/help.js.map +1 -0
  144. package/dist/src/cli/hook-codex.d.ts +78 -0
  145. package/dist/src/cli/hook-codex.js +167 -0
  146. package/dist/src/cli/hook-codex.js.map +1 -0
  147. package/dist/src/cli/hook.d.ts +331 -0
  148. package/dist/src/cli/hook.js +2849 -0
  149. package/dist/src/cli/hook.js.map +1 -0
  150. package/dist/src/cli/import.d.ts +35 -0
  151. package/dist/src/cli/import.js +175 -0
  152. package/dist/src/cli/import.js.map +1 -0
  153. package/dist/src/cli/init.d.ts +84 -0
  154. package/dist/src/cli/init.js +336 -0
  155. package/dist/src/cli/init.js.map +1 -0
  156. package/dist/src/cli/instructions.d.ts +23 -0
  157. package/dist/src/cli/instructions.js +262 -0
  158. package/dist/src/cli/instructions.js.map +1 -0
  159. package/dist/src/cli/journal.d.ts +41 -0
  160. package/dist/src/cli/journal.js +238 -0
  161. package/dist/src/cli/journal.js.map +1 -0
  162. package/dist/src/cli/log-advance.d.ts +287 -0
  163. package/dist/src/cli/log-advance.js +840 -0
  164. package/dist/src/cli/log-advance.js.map +1 -0
  165. package/dist/src/cli/log-anchor.d.ts +176 -0
  166. package/dist/src/cli/log-anchor.js +387 -0
  167. package/dist/src/cli/log-anchor.js.map +1 -0
  168. package/dist/src/cli/log-checkpoint.d.ts +22 -0
  169. package/dist/src/cli/log-checkpoint.js +128 -0
  170. package/dist/src/cli/log-checkpoint.js.map +1 -0
  171. package/dist/src/cli/log-sync.d.ts +243 -0
  172. package/dist/src/cli/log-sync.js +849 -0
  173. package/dist/src/cli/log-sync.js.map +1 -0
  174. package/dist/src/cli/log-verbs.d.ts +16 -0
  175. package/dist/src/cli/log-verbs.js +360 -0
  176. package/dist/src/cli/log-verbs.js.map +1 -0
  177. package/dist/src/cli/long-help.d.ts +70 -0
  178. package/dist/src/cli/long-help.js +148 -0
  179. package/dist/src/cli/long-help.js.map +1 -0
  180. package/dist/src/cli/main.d.ts +77 -0
  181. package/dist/src/cli/main.js +1206 -0
  182. package/dist/src/cli/main.js.map +1 -0
  183. package/dist/src/cli/mcp.d.ts +52 -0
  184. package/dist/src/cli/mcp.js +306 -0
  185. package/dist/src/cli/mcp.js.map +1 -0
  186. package/dist/src/cli/paths.d.ts +56 -0
  187. package/dist/src/cli/paths.js +79 -0
  188. package/dist/src/cli/paths.js.map +1 -0
  189. package/dist/src/cli/payload.d.ts +58 -0
  190. package/dist/src/cli/payload.js +253 -0
  191. package/dist/src/cli/payload.js.map +1 -0
  192. package/dist/src/cli/policy.d.ts +43 -0
  193. package/dist/src/cli/policy.js +229 -0
  194. package/dist/src/cli/policy.js.map +1 -0
  195. package/dist/src/cli/preflight.d.ts +363 -0
  196. package/dist/src/cli/preflight.js +1175 -0
  197. package/dist/src/cli/preflight.js.map +1 -0
  198. package/dist/src/cli/progress.d.ts +78 -0
  199. package/dist/src/cli/progress.js +112 -0
  200. package/dist/src/cli/progress.js.map +1 -0
  201. package/dist/src/cli/prompt.d.ts +209 -0
  202. package/dist/src/cli/prompt.js +312 -0
  203. package/dist/src/cli/prompt.js.map +1 -0
  204. package/dist/src/cli/quickstart.d.ts +46 -0
  205. package/dist/src/cli/quickstart.js +297 -0
  206. package/dist/src/cli/quickstart.js.map +1 -0
  207. package/dist/src/cli/records.d.ts +34 -0
  208. package/dist/src/cli/records.js +66 -0
  209. package/dist/src/cli/records.js.map +1 -0
  210. package/dist/src/cli/render.d.ts +22 -0
  211. package/dist/src/cli/render.js +132 -0
  212. package/dist/src/cli/render.js.map +1 -0
  213. package/dist/src/cli/sandbox.d.ts +51 -0
  214. package/dist/src/cli/sandbox.js +150 -0
  215. package/dist/src/cli/sandbox.js.map +1 -0
  216. package/dist/src/cli/scaffold.d.ts +79 -0
  217. package/dist/src/cli/scaffold.js +137 -0
  218. package/dist/src/cli/scaffold.js.map +1 -0
  219. package/dist/src/cli/setup-adapter.d.ts +137 -0
  220. package/dist/src/cli/setup-adapter.js +509 -0
  221. package/dist/src/cli/setup-adapter.js.map +1 -0
  222. package/dist/src/cli/setup-channel.d.ts +117 -0
  223. package/dist/src/cli/setup-channel.js +635 -0
  224. package/dist/src/cli/setup-channel.js.map +1 -0
  225. package/dist/src/cli/setup-checkpoint.d.ts +57 -0
  226. package/dist/src/cli/setup-checkpoint.js +196 -0
  227. package/dist/src/cli/setup-checkpoint.js.map +1 -0
  228. package/dist/src/cli/setup-common.d.ts +275 -0
  229. package/dist/src/cli/setup-common.js +376 -0
  230. package/dist/src/cli/setup-common.js.map +1 -0
  231. package/dist/src/cli/setup-flow.d.ts +287 -0
  232. package/dist/src/cli/setup-flow.js +476 -0
  233. package/dist/src/cli/setup-flow.js.map +1 -0
  234. package/dist/src/cli/setup-service.d.ts +96 -0
  235. package/dist/src/cli/setup-service.js +308 -0
  236. package/dist/src/cli/setup-service.js.map +1 -0
  237. package/dist/src/cli/setup.d.ts +202 -0
  238. package/dist/src/cli/setup.js +473 -0
  239. package/dist/src/cli/setup.js.map +1 -0
  240. package/dist/src/cli/style.d.ts +320 -0
  241. package/dist/src/cli/style.js +469 -0
  242. package/dist/src/cli/style.js.map +1 -0
  243. package/dist/src/cli/token.d.ts +39 -0
  244. package/dist/src/cli/token.js +274 -0
  245. package/dist/src/cli/token.js.map +1 -0
  246. package/dist/src/cli/up.d.ts +155 -0
  247. package/dist/src/cli/up.js +849 -0
  248. package/dist/src/cli/up.js.map +1 -0
  249. package/dist/src/cli/usage.d.ts +37 -0
  250. package/dist/src/cli/usage.js +91 -0
  251. package/dist/src/cli/usage.js.map +1 -0
  252. package/dist/src/cli/values.d.ts +40 -0
  253. package/dist/src/cli/values.js +189 -0
  254. package/dist/src/cli/values.js.map +1 -0
  255. package/dist/src/cli/vault.d.ts +59 -0
  256. package/dist/src/cli/vault.js +362 -0
  257. package/dist/src/cli/vault.js.map +1 -0
  258. package/dist/src/cli/verb-registry.d.ts +76 -0
  259. package/dist/src/cli/verb-registry.js +2341 -0
  260. package/dist/src/cli/verb-registry.js.map +1 -0
  261. package/dist/src/cli/wordmark.d.ts +31 -0
  262. package/dist/src/cli/wordmark.js +52 -0
  263. package/dist/src/cli/wordmark.js.map +1 -0
  264. package/dist/src/codex/doctor.d.ts +13 -0
  265. package/dist/src/codex/doctor.js +41 -0
  266. package/dist/src/codex/doctor.js.map +1 -0
  267. package/dist/src/codex/manifest.d.ts +49 -0
  268. package/dist/src/codex/manifest.js +103 -0
  269. package/dist/src/codex/manifest.js.map +1 -0
  270. package/dist/src/codex/templates.d.ts +41 -0
  271. package/dist/src/codex/templates.js +319 -0
  272. package/dist/src/codex/templates.js.map +1 -0
  273. package/dist/src/codex/trust.d.ts +19 -0
  274. package/dist/src/codex/trust.js +183 -0
  275. package/dist/src/codex/trust.js.map +1 -0
  276. package/dist/src/codex/workspace-plan.d.ts +131 -0
  277. package/dist/src/codex/workspace-plan.js +561 -0
  278. package/dist/src/codex/workspace-plan.js.map +1 -0
  279. package/dist/src/core/actor.d.ts +2 -0
  280. package/dist/src/core/actor.js +5 -0
  281. package/dist/src/core/actor.js.map +1 -0
  282. package/dist/src/core/advance-cycle.d.ts +170 -0
  283. package/dist/src/core/advance-cycle.js +200 -0
  284. package/dist/src/core/advance-cycle.js.map +1 -0
  285. package/dist/src/core/agents-md.d.ts +276 -0
  286. package/dist/src/core/agents-md.js +747 -0
  287. package/dist/src/core/agents-md.js.map +1 -0
  288. package/dist/src/core/apply-patch.d.ts +49 -0
  289. package/dist/src/core/apply-patch.js +266 -0
  290. package/dist/src/core/apply-patch.js.map +1 -0
  291. package/dist/src/core/attest.d.ts +420 -0
  292. package/dist/src/core/attest.js +589 -0
  293. package/dist/src/core/attest.js.map +1 -0
  294. package/dist/src/core/audit.d.ts +492 -0
  295. package/dist/src/core/audit.js +882 -0
  296. package/dist/src/core/audit.js.map +1 -0
  297. package/dist/src/core/budgets.d.ts +238 -0
  298. package/dist/src/core/budgets.js +449 -0
  299. package/dist/src/core/budgets.js.map +1 -0
  300. package/dist/src/core/checkpoint.d.ts +500 -0
  301. package/dist/src/core/checkpoint.js +738 -0
  302. package/dist/src/core/checkpoint.js.map +1 -0
  303. package/dist/src/core/child-env.d.ts +88 -0
  304. package/dist/src/core/child-env.js +86 -0
  305. package/dist/src/core/child-env.js.map +1 -0
  306. package/dist/src/core/clock.d.ts +52 -0
  307. package/dist/src/core/clock.js +43 -0
  308. package/dist/src/core/clock.js.map +1 -0
  309. package/dist/src/core/command-class.d.ts +543 -0
  310. package/dist/src/core/command-class.js +2356 -0
  311. package/dist/src/core/command-class.js.map +1 -0
  312. package/dist/src/core/coverage-sources/adapter.d.ts +40 -0
  313. package/dist/src/core/coverage-sources/adapter.js +71 -0
  314. package/dist/src/core/coverage-sources/adapter.js.map +1 -0
  315. package/dist/src/core/coverage-sources/gh.d.ts +48 -0
  316. package/dist/src/core/coverage-sources/gh.js +136 -0
  317. package/dist/src/core/coverage-sources/gh.js.map +1 -0
  318. package/dist/src/core/coverage-sources/git.d.ts +101 -0
  319. package/dist/src/core/coverage-sources/git.js +269 -0
  320. package/dist/src/core/coverage-sources/git.js.map +1 -0
  321. package/dist/src/core/coverage.d.ts +217 -0
  322. package/dist/src/core/coverage.js +337 -0
  323. package/dist/src/core/coverage.js.map +1 -0
  324. package/dist/src/core/credential-spec.d.ts +72 -0
  325. package/dist/src/core/credential-spec.js +23 -0
  326. package/dist/src/core/credential-spec.js.map +1 -0
  327. package/dist/src/core/dark-session.d.ts +331 -0
  328. package/dist/src/core/dark-session.js +714 -0
  329. package/dist/src/core/dark-session.js.map +1 -0
  330. package/dist/src/core/decision-refusal.d.ts +185 -0
  331. package/dist/src/core/decision-refusal.js +265 -0
  332. package/dist/src/core/decision-refusal.js.map +1 -0
  333. package/dist/src/core/env-file.d.ts +450 -0
  334. package/dist/src/core/env-file.js +837 -0
  335. package/dist/src/core/env-file.js.map +1 -0
  336. package/dist/src/core/execute.d.ts +858 -0
  337. package/dist/src/core/execute.js +1271 -0
  338. package/dist/src/core/execute.js.map +1 -0
  339. package/dist/src/core/frontmatter.d.ts +78 -0
  340. package/dist/src/core/frontmatter.js +100 -0
  341. package/dist/src/core/frontmatter.js.map +1 -0
  342. package/dist/src/core/gate-window.d.ts +312 -0
  343. package/dist/src/core/gate-window.js +506 -0
  344. package/dist/src/core/gate-window.js.map +1 -0
  345. package/dist/src/core/gate.d.ts +1364 -0
  346. package/dist/src/core/gate.js +3002 -0
  347. package/dist/src/core/gate.js.map +1 -0
  348. package/dist/src/core/git-run.d.ts +73 -0
  349. package/dist/src/core/git-run.js +93 -0
  350. package/dist/src/core/git-run.js.map +1 -0
  351. package/dist/src/core/harness-version.d.ts +157 -0
  352. package/dist/src/core/harness-version.js +211 -0
  353. package/dist/src/core/harness-version.js.map +1 -0
  354. package/dist/src/core/harness-wait.d.ts +55 -0
  355. package/dist/src/core/harness-wait.js +58 -0
  356. package/dist/src/core/harness-wait.js.map +1 -0
  357. package/dist/src/core/head-retry.d.ts +107 -0
  358. package/dist/src/core/head-retry.js +121 -0
  359. package/dist/src/core/head-retry.js.map +1 -0
  360. package/dist/src/core/instance.d.ts +253 -0
  361. package/dist/src/core/instance.js +319 -0
  362. package/dist/src/core/instance.js.map +1 -0
  363. package/dist/src/core/intake-limits.d.ts +247 -0
  364. package/dist/src/core/intake-limits.js +350 -0
  365. package/dist/src/core/intake-limits.js.map +1 -0
  366. package/dist/src/core/jcs.d.ts +52 -0
  367. package/dist/src/core/jcs.js +132 -0
  368. package/dist/src/core/jcs.js.map +1 -0
  369. package/dist/src/core/journal.d.ts +144 -0
  370. package/dist/src/core/journal.js +200 -0
  371. package/dist/src/core/journal.js.map +1 -0
  372. package/dist/src/core/live-draw.d.ts +436 -0
  373. package/dist/src/core/live-draw.js +703 -0
  374. package/dist/src/core/live-draw.js.map +1 -0
  375. package/dist/src/core/log-reconcile.d.ts +89 -0
  376. package/dist/src/core/log-reconcile.js +136 -0
  377. package/dist/src/core/log-reconcile.js.map +1 -0
  378. package/dist/src/core/log-subscribe.d.ts +36 -0
  379. package/dist/src/core/log-subscribe.js +162 -0
  380. package/dist/src/core/log-subscribe.js.map +1 -0
  381. package/dist/src/core/log.d.ts +278 -0
  382. package/dist/src/core/log.js +546 -0
  383. package/dist/src/core/log.js.map +1 -0
  384. package/dist/src/core/loop.d.ts +274 -0
  385. package/dist/src/core/loop.js +487 -0
  386. package/dist/src/core/loop.js.map +1 -0
  387. package/dist/src/core/md-fence.d.ts +41 -0
  388. package/dist/src/core/md-fence.js +74 -0
  389. package/dist/src/core/md-fence.js.map +1 -0
  390. package/dist/src/core/money.d.ts +147 -0
  391. package/dist/src/core/money.js +195 -0
  392. package/dist/src/core/money.js.map +1 -0
  393. package/dist/src/core/payload-census.d.ts +74 -0
  394. package/dist/src/core/payload-census.js +146 -0
  395. package/dist/src/core/payload-census.js.map +1 -0
  396. package/dist/src/core/payload-store.d.ts +175 -0
  397. package/dist/src/core/payload-store.js +340 -0
  398. package/dist/src/core/payload-store.js.map +1 -0
  399. package/dist/src/core/payload.d.ts +71 -0
  400. package/dist/src/core/payload.js +80 -0
  401. package/dist/src/core/payload.js.map +1 -0
  402. package/dist/src/core/policy-diff.d.ts +292 -0
  403. package/dist/src/core/policy-diff.js +588 -0
  404. package/dist/src/core/policy-diff.js.map +1 -0
  405. package/dist/src/core/policy-expectations.d.ts +199 -0
  406. package/dist/src/core/policy-expectations.js +394 -0
  407. package/dist/src/core/policy-expectations.js.map +1 -0
  408. package/dist/src/core/policy-explain.d.ts +150 -0
  409. package/dist/src/core/policy-explain.js +258 -0
  410. package/dist/src/core/policy-explain.js.map +1 -0
  411. package/dist/src/core/policy-load.d.ts +527 -0
  412. package/dist/src/core/policy-load.js +536 -0
  413. package/dist/src/core/policy-load.js.map +1 -0
  414. package/dist/src/core/policy-match.d.ts +281 -0
  415. package/dist/src/core/policy-match.js +478 -0
  416. package/dist/src/core/policy-match.js.map +1 -0
  417. package/dist/src/core/policy-proposal.d.ts +265 -0
  418. package/dist/src/core/policy-proposal.js +458 -0
  419. package/dist/src/core/policy-proposal.js.map +1 -0
  420. package/dist/src/core/prompt-layout.d.ts +221 -0
  421. package/dist/src/core/prompt-layout.js +422 -0
  422. package/dist/src/core/prompt-layout.js.map +1 -0
  423. package/dist/src/core/protected-path-guard.d.ts +453 -0
  424. package/dist/src/core/protected-path-guard.js +1566 -0
  425. package/dist/src/core/protected-path-guard.js.map +1 -0
  426. package/dist/src/core/registration.d.ts +25 -0
  427. package/dist/src/core/registration.js +39 -0
  428. package/dist/src/core/registration.js.map +1 -0
  429. package/dist/src/core/reindex.d.ts +99 -0
  430. package/dist/src/core/reindex.js +336 -0
  431. package/dist/src/core/reindex.js.map +1 -0
  432. package/dist/src/core/sampler.d.ts +313 -0
  433. package/dist/src/core/sampler.js +388 -0
  434. package/dist/src/core/sampler.js.map +1 -0
  435. package/dist/src/core/sandbox.d.ts +290 -0
  436. package/dist/src/core/sandbox.js +424 -0
  437. package/dist/src/core/sandbox.js.map +1 -0
  438. package/dist/src/core/seal.d.ts +165 -0
  439. package/dist/src/core/seal.js +290 -0
  440. package/dist/src/core/seal.js.map +1 -0
  441. package/dist/src/core/state.d.ts +505 -0
  442. package/dist/src/core/state.js +1009 -0
  443. package/dist/src/core/state.js.map +1 -0
  444. package/dist/src/core/task-file.d.ts +185 -0
  445. package/dist/src/core/task-file.js +464 -0
  446. package/dist/src/core/task-file.js.map +1 -0
  447. package/dist/src/core/telegram-config.d.ts +93 -0
  448. package/dist/src/core/telegram-config.js +114 -0
  449. package/dist/src/core/telegram-config.js.map +1 -0
  450. package/dist/src/core/token.d.ts +409 -0
  451. package/dist/src/core/token.js +561 -0
  452. package/dist/src/core/token.js.map +1 -0
  453. package/dist/src/core/validate.d.ts +138 -0
  454. package/dist/src/core/validate.js +0 -0
  455. package/dist/src/core/validate.js.map +1 -0
  456. package/dist/src/core/values.d.ts +137 -0
  457. package/dist/src/core/values.js +153 -0
  458. package/dist/src/core/values.js.map +1 -0
  459. package/dist/src/core/vault.d.ts +291 -0
  460. package/dist/src/core/vault.js +612 -0
  461. package/dist/src/core/vault.js.map +1 -0
  462. package/dist/src/core/verified-snapshot.d.ts +204 -0
  463. package/dist/src/core/verified-snapshot.js +506 -0
  464. package/dist/src/core/verified-snapshot.js.map +1 -0
  465. package/dist/src/core/verify.d.ts +336 -0
  466. package/dist/src/core/verify.js +549 -0
  467. package/dist/src/core/verify.js.map +1 -0
  468. package/dist/src/core/version.d.ts +8 -0
  469. package/dist/src/core/version.js +9 -0
  470. package/dist/src/core/version.js.map +1 -0
  471. package/dist/src/core/wysiwys.d.ts +370 -0
  472. package/dist/src/core/wysiwys.js +728 -0
  473. package/dist/src/core/wysiwys.js.map +1 -0
  474. package/dist/src/daemon/advance-child.d.ts +39 -0
  475. package/dist/src/daemon/advance-child.js +78 -0
  476. package/dist/src/daemon/advance-child.js.map +1 -0
  477. package/dist/src/daemon/advance.d.ts +466 -0
  478. package/dist/src/daemon/advance.js +849 -0
  479. package/dist/src/daemon/advance.js.map +1 -0
  480. package/dist/src/daemon/audit.d.ts +87 -0
  481. package/dist/src/daemon/audit.js +90 -0
  482. package/dist/src/daemon/audit.js.map +1 -0
  483. package/dist/src/daemon/daemon.d.ts +1180 -0
  484. package/dist/src/daemon/daemon.js +1988 -0
  485. package/dist/src/daemon/daemon.js.map +1 -0
  486. package/dist/src/daemon/dark-session.d.ts +64 -0
  487. package/dist/src/daemon/dark-session.js +119 -0
  488. package/dist/src/daemon/dark-session.js.map +1 -0
  489. package/dist/src/daemon/draw-child.d.ts +36 -0
  490. package/dist/src/daemon/draw-child.js +132 -0
  491. package/dist/src/daemon/draw-child.js.map +1 -0
  492. package/dist/src/daemon/draw.d.ts +154 -0
  493. package/dist/src/daemon/draw.js +458 -0
  494. package/dist/src/daemon/draw.js.map +1 -0
  495. package/dist/src/daemon/git-evidence.d.ts +173 -0
  496. package/dist/src/daemon/git-evidence.js +345 -0
  497. package/dist/src/daemon/git-evidence.js.map +1 -0
  498. package/dist/src/daemon/projection.d.ts +180 -0
  499. package/dist/src/daemon/projection.js +233 -0
  500. package/dist/src/daemon/projection.js.map +1 -0
  501. package/dist/src/daemon/prune.d.ts +207 -0
  502. package/dist/src/daemon/prune.js +376 -0
  503. package/dist/src/daemon/prune.js.map +1 -0
  504. package/dist/src/mcp/http.d.ts +113 -0
  505. package/dist/src/mcp/http.js +343 -0
  506. package/dist/src/mcp/http.js.map +1 -0
  507. package/dist/src/mcp/server.d.ts +265 -0
  508. package/dist/src/mcp/server.js +602 -0
  509. package/dist/src/mcp/server.js.map +1 -0
  510. package/docs/adapter-api.md +106 -0
  511. package/docs/cli-reference.md +5716 -0
  512. package/docs/codex-enforced-session.md +30 -0
  513. package/package.json +53 -4
  514. package/schema/.gitkeep +0 -0
  515. package/schema/LICENSE +117 -0
  516. package/schema/codex-instance.schema.json +82 -0
  517. package/schema/envelope.schema.json +137 -0
  518. package/schema/event.schema.json +1811 -0
  519. package/schema/fixtures/codex-instance/invalid/unpinned-codex-version.json +40 -0
  520. package/schema/fixtures/codex-instance/valid/canonical.json +40 -0
  521. package/schema/fixtures/envelope/invalid/action-missing-idempotency-key.json +15 -0
  522. package/schema/fixtures/envelope/invalid/action-unknown-class-format.json +14 -0
  523. package/schema/fixtures/envelope/invalid/confidence-out-of-range.json +11 -0
  524. package/schema/fixtures/envelope/invalid/est-cost-bare-number.json +14 -0
  525. package/schema/fixtures/envelope/invalid/est-cost-noncanonical-string.json +14 -0
  526. package/schema/fixtures/envelope/invalid/malformed-assignee.json +10 -0
  527. package/schema/fixtures/envelope/invalid/malformed-created-by.json +7 -0
  528. package/schema/fixtures/envelope/invalid/malformed-max-latency.json +11 -0
  529. package/schema/fixtures/envelope/invalid/malformed-payload-hash.json +14 -0
  530. package/schema/fixtures/envelope/invalid/max-cost-bare-number.json +11 -0
  531. package/schema/fixtures/envelope/invalid/missing-origin.json +3 -0
  532. package/schema/fixtures/envelope/invalid/negative-est-cost.json +14 -0
  533. package/schema/fixtures/envelope/invalid/unknown-state.json +7 -0
  534. package/schema/fixtures/envelope/invalid/unknown-top-level-field.json +8 -0
  535. package/schema/fixtures/envelope/valid/action-payload-hash.json +17 -0
  536. package/schema/fixtures/envelope/valid/actions-without-budget.json +18 -0
  537. package/schema/fixtures/envelope/valid/canonical.json +25 -0
  538. package/schema/fixtures/envelope/valid/minimal.json +7 -0
  539. package/schema/fixtures/envelope/valid/multi-action-executed.json +30 -0
  540. package/schema/fixtures/envelope/valid/record-write-stage.json +22 -0
  541. package/schema/fixtures/event/invalid/approval-granted-agent-actor.json +15 -0
  542. package/schema/fixtures/event/invalid/approval-granted-empty-batch-delivery-id.json +17 -0
  543. package/schema/fixtures/event/invalid/approval-granted-fifth-reaction.json +16 -0
  544. package/schema/fixtures/event/invalid/approval-granted-missing-actor.json +14 -0
  545. package/schema/fixtures/event/invalid/approval-requested-missing-action-key.json +14 -0
  546. package/schema/fixtures/event/invalid/approval-withdrawn-agent-policy-drift.json +15 -0
  547. package/schema/fixtures/event/invalid/approval-withdrawn-missing-reason.json +15 -0
  548. package/schema/fixtures/event/invalid/approval-withdrawn-system-actor.json +15 -0
  549. package/schema/fixtures/event/invalid/audit-decision-refused-human-actor.json +17 -0
  550. package/schema/fixtures/event/invalid/audit-decision-refused-missing-code.json +16 -0
  551. package/schema/fixtures/event/invalid/audit-reviewed-agent-actor.json +15 -0
  552. package/schema/fixtures/event/invalid/audit-reviewed-loved-no-note.json +16 -0
  553. package/schema/fixtures/event/invalid/audit-reviewed-system-actor.json +15 -0
  554. package/schema/fixtures/event/invalid/bad-actor-prefix.json +15 -0
  555. package/schema/fixtures/event/invalid/est-cost-bare-number.json +17 -0
  556. package/schema/fixtures/event/invalid/execution-completed-fabricated-exit-code.json +16 -0
  557. package/schema/fixtures/event/invalid/execution-completed-provider-ref-empty-id.json +18 -0
  558. package/schema/fixtures/event/invalid/execution-completed-provider-ref-extra-field.json +19 -0
  559. package/schema/fixtures/event/invalid/execution-completed-provider-ref-id-not-string.json +18 -0
  560. package/schema/fixtures/event/invalid/execution-completed-provider-ref-missing-adapter.json +17 -0
  561. package/schema/fixtures/event/invalid/execution-failed-open-reported-by.json +16 -0
  562. package/schema/fixtures/event/invalid/execution-indeterminate-open-reason.json +14 -0
  563. package/schema/fixtures/event/invalid/execution-reconciled-agent-actor.json +17 -0
  564. package/schema/fixtures/event/invalid/execution-started-negative-env-stripped.json +16 -0
  565. package/schema/fixtures/event/invalid/gate-bypassed-missing-opened-seq.json +15 -0
  566. package/schema/fixtures/event/invalid/gate-closed-non-integer-opened-seq.json +13 -0
  567. package/schema/fixtures/event/invalid/gate-opened-agent-actor.json +16 -0
  568. package/schema/fixtures/event/invalid/gate-organ-attested-absolute-path.json +14 -0
  569. package/schema/fixtures/event/invalid/gate-organ-attested-agent-actor.json +14 -0
  570. package/schema/fixtures/event/invalid/gate-organ-attested-missing-organ-path.json +13 -0
  571. package/schema/fixtures/event/invalid/harness-unknown-kind.json +18 -0
  572. package/schema/fixtures/event/invalid/harness-version-multiline.json +16 -0
  573. package/schema/fixtures/event/invalid/log-checkpoint-agent-actor.json +17 -0
  574. package/schema/fixtures/event/invalid/log-checkpoint-missing-signature.json +16 -0
  575. package/schema/fixtures/event/invalid/log-checkpoint-short-signed-hash.json +17 -0
  576. package/schema/fixtures/event/invalid/log-checkpoint-unknown-signature-alg.json +17 -0
  577. package/schema/fixtures/event/invalid/malformed-ts.json +15 -0
  578. package/schema/fixtures/event/invalid/missing-alg.json +14 -0
  579. package/schema/fixtures/event/invalid/missing-hash.json +14 -0
  580. package/schema/fixtures/event/invalid/non-integer-seq.json +15 -0
  581. package/schema/fixtures/event/invalid/payload-pruned-human-actor.json +14 -0
  582. package/schema/fixtures/event/invalid/payload-pruned-missing-hash.json +14 -0
  583. package/schema/fixtures/event/invalid/policy-declined-agent-actor.json +15 -0
  584. package/schema/fixtures/event/invalid/policy-proposed-missing-diff.json +19 -0
  585. package/schema/fixtures/event/invalid/policy-proposed-system-actor.json +26 -0
  586. package/schema/fixtures/event/invalid/short-hash.json +15 -0
  587. package/schema/fixtures/event/invalid/unknown-alg.json +15 -0
  588. package/schema/fixtures/event/invalid/unknown-event-type.json +15 -0
  589. package/schema/fixtures/event/invalid/unknown-top-level-field.json +16 -0
  590. package/schema/fixtures/event/valid/approval-expired.json +15 -0
  591. package/schema/fixtures/event/valid/approval-granted-batch.json +19 -0
  592. package/schema/fixtures/event/valid/approval-granted-reaction.json +16 -0
  593. package/schema/fixtures/event/valid/approval-granted.json +15 -0
  594. package/schema/fixtures/event/valid/approval-rejected.json +15 -0
  595. package/schema/fixtures/event/valid/approval-requested.json +19 -0
  596. package/schema/fixtures/event/valid/approval-revoked.json +15 -0
  597. package/schema/fixtures/event/valid/approval-withdrawn-policy-drift.json +17 -0
  598. package/schema/fixtures/event/valid/approval-withdrawn.json +16 -0
  599. package/schema/fixtures/event/valid/audit-decision-refused.json +20 -0
  600. package/schema/fixtures/event/valid/audit-reviewed-reaction.json +17 -0
  601. package/schema/fixtures/event/valid/audit-reviewed.json +15 -0
  602. package/schema/fixtures/event/valid/audit-sampled.json +14 -0
  603. package/schema/fixtures/event/valid/budget-exceeded.json +21 -0
  604. package/schema/fixtures/event/valid/envelope-drift.json +16 -0
  605. package/schema/fixtures/event/valid/execution-completed-harness-report.json +16 -0
  606. package/schema/fixtures/event/valid/execution-completed-provider-ref.json +18 -0
  607. package/schema/fixtures/event/valid/execution-completed.json +15 -0
  608. package/schema/fixtures/event/valid/execution-failed-harness-report.json +16 -0
  609. package/schema/fixtures/event/valid/execution-failed.json +15 -0
  610. package/schema/fixtures/event/valid/execution-indeterminate.json +15 -0
  611. package/schema/fixtures/event/valid/execution-reconciled.json +17 -0
  612. package/schema/fixtures/event/valid/execution-started-env-stripped.json +17 -0
  613. package/schema/fixtures/event/valid/execution-started.json +14 -0
  614. package/schema/fixtures/event/valid/gate-bypassed-harness-version.json +18 -0
  615. package/schema/fixtures/event/valid/gate-bypassed.json +19 -0
  616. package/schema/fixtures/event/valid/gate-closed.json +14 -0
  617. package/schema/fixtures/event/valid/gate-opened.json +16 -0
  618. package/schema/fixtures/event/valid/gate-organ-attested.json +14 -0
  619. package/schema/fixtures/event/valid/genesis-null-prev.json +14 -0
  620. package/schema/fixtures/event/valid/log-checkpoint.json +17 -0
  621. package/schema/fixtures/event/valid/payload-pruned-orphan.json +13 -0
  622. package/schema/fixtures/event/valid/payload-pruned.json +17 -0
  623. package/schema/fixtures/event/valid/policy-declined.json +16 -0
  624. package/schema/fixtures/event/valid/policy-proposed.json +35 -0
  625. package/schema/fixtures/event/valid/policy-updated.json +14 -0
  626. package/schema/fixtures/event/valid/reconciliation-required.json +18 -0
  627. package/schema/fixtures/event/valid/reconciliation-satisfied.json +17 -0
  628. package/schema/fixtures/event/valid/route-accepted.json +15 -0
  629. package/schema/fixtures/event/valid/route-proposed.json +16 -0
  630. package/schema/fixtures/event/valid/spec-example.json +15 -0
  631. package/schema/fixtures/event/valid/task-registered-harness-version.json +23 -0
  632. package/schema/fixtures/event/valid/task-registered.json +14 -0
  633. package/schema/fixtures/hash/known-answer-pre-121.json +74 -0
  634. package/schema/fixtures/hash/known-answer.json +74 -0
  635. package/schema/fixtures/policy/invalid/bad-approval-ttl.json +7 -0
  636. package/schema/fixtures/policy/invalid/bad-web-port.json +4 -0
  637. package/schema/fixtures/policy/invalid/checkpoint-key-not-base64.json +6 -0
  638. package/schema/fixtures/policy/invalid/class-rule-missing-autonomy.json +9 -0
  639. package/schema/fixtures/policy/invalid/empty-class-key.json +6 -0
  640. package/schema/fixtures/policy/invalid/live-rate-on-human-only.json +7 -0
  641. package/schema/fixtures/policy/invalid/malformed-class-key.json +6 -0
  642. package/schema/fixtures/policy/invalid/missing-version.json +8 -0
  643. package/schema/fixtures/policy/invalid/negative-limit.json +9 -0
  644. package/schema/fixtures/policy/invalid/non-numeric-limit.json +9 -0
  645. package/schema/fixtures/policy/invalid/non-positive-max-pending.json +9 -0
  646. package/schema/fixtures/policy/invalid/on-expiry-grant.json +8 -0
  647. package/schema/fixtures/policy/invalid/payload-retention-bare-number.json +4 -0
  648. package/schema/fixtures/policy/invalid/payload-retention-compound.json +4 -0
  649. package/schema/fixtures/policy/invalid/payload-retention-fractional.json +4 -0
  650. package/schema/fixtures/policy/invalid/payload-retention-zero.json +4 -0
  651. package/schema/fixtures/policy/invalid/protected-paths-escape.json +4 -0
  652. package/schema/fixtures/policy/invalid/protected-paths-glob.json +4 -0
  653. package/schema/fixtures/policy/invalid/retro-rate-on-human-only.json +7 -0
  654. package/schema/fixtures/policy/invalid/retro-rate-on-manual.json +7 -0
  655. package/schema/fixtures/policy/invalid/retro-rate-zero.json +7 -0
  656. package/schema/fixtures/policy/invalid/sample-rate-too-high.json +5 -0
  657. package/schema/fixtures/policy/invalid/sampling-secret-env-empty.json +7 -0
  658. package/schema/fixtures/policy/invalid/sampling-secret-env-not-string.json +6 -0
  659. package/schema/fixtures/policy/invalid/skew-tolerance-compound.json +6 -0
  660. package/schema/fixtures/policy/invalid/unknown-autonomy.json +7 -0
  661. package/schema/fixtures/policy/invalid/unknown-class-rule-key.json +6 -0
  662. package/schema/fixtures/policy/invalid/unknown-top-level-key.json +7 -0
  663. package/schema/fixtures/policy/invalid/vault-passphrase-env-empty.json +6 -0
  664. package/schema/fixtures/policy/invalid/vault-passphrase-literal.json +6 -0
  665. package/schema/fixtures/policy/invalid/version-not-string.json +4 -0
  666. package/schema/fixtures/policy/valid/canonical.json +47 -0
  667. package/schema/fixtures/policy/valid/checkpoint-keys.json +18 -0
  668. package/schema/fixtures/policy/valid/class-approvers-limits.json +25 -0
  669. package/schema/fixtures/policy/valid/class-retro-rate.json +17 -0
  670. package/schema/fixtures/policy/valid/global-budgets.json +19 -0
  671. package/schema/fixtures/policy/valid/human-only.json +9 -0
  672. package/schema/fixtures/policy/valid/minimal.json +6 -0
  673. package/schema/fixtures/policy/valid/protected-paths.json +10 -0
  674. package/schema/fixtures/policy/valid/record-namespace.json +13 -0
  675. package/schema/fixtures/policy/valid/request-volume-limits.json +26 -0
  676. package/schema/fixtures/policy/valid/retention-and-sampling-secret.json +16 -0
  677. package/schema/fixtures/policy/valid/skew-tolerance.json +15 -0
  678. package/schema/fixtures/policy/valid/vault-passphrase-env.json +14 -0
  679. package/schema/fixtures/policy/valid/wildcards.json +15 -0
  680. package/schema/fixtures/policy-md/invalid/alias-bomb.md +15 -0
  681. package/schema/fixtures/policy-md/invalid/no-fence.md +7 -0
  682. package/schema/fixtures/policy-md/invalid/protected-route-not-a-subclass.md +16 -0
  683. package/schema/fixtures/policy-md/invalid/schema-invalid-autonomy.md +16 -0
  684. package/schema/fixtures/policy-md/invalid/schema-invalid-read-proof.md +17 -0
  685. package/schema/fixtures/policy-md/invalid/two-fences.md +19 -0
  686. package/schema/fixtures/policy-md/invalid/unclosed-fence.md +11 -0
  687. package/schema/fixtures/policy-md/invalid/wrong-info-string.md +11 -0
  688. package/schema/fixtures/policy-md/invalid/yaml-syntax-error.md +13 -0
  689. package/schema/fixtures/policy-md/precedence/both/APPROVAL.md +7 -0
  690. package/schema/fixtures/policy-md/precedence/both/APPROVALS.md +7 -0
  691. package/schema/fixtures/policy-md/precedence/fallback-only/APPROVALS.md +7 -0
  692. package/schema/fixtures/policy-md/valid/canonical.md +50 -0
  693. package/schema/fixtures/policy-md/valid/daemon-read-proof.md +18 -0
  694. package/schema/fixtures/policy-md/valid/minimal.md +3 -0
  695. package/schema/fixtures/policy-md/valid/prose-lookalikes.md +54 -0
  696. package/schema/fixtures/policy-md/valid/routed-protected-paths.md +49 -0
  697. package/schema/fixtures/policy-md/valid/with-values.md +79 -0
  698. package/schema/fixtures/sample-record/invalid/bad-date-time.json +4 -0
  699. package/schema/fixtures/sample-record/invalid/missing-required-field.json +3 -0
  700. package/schema/fixtures/sample-record/invalid/unknown-top-level-field.json +5 -0
  701. package/schema/fixtures/sample-record/invalid/wrong-type.json +4 -0
  702. package/schema/fixtures/sample-record/valid/minimal.json +4 -0
  703. package/schema/fixtures/sample-record/valid/with-note.json +5 -0
  704. package/schema/fixtures/values/invalid/class-shaped.json +9 -0
  705. package/schema/fixtures/values/invalid/duplicate-entry.json +4 -0
  706. package/schema/fixtures/values/invalid/non-string-item.json +4 -0
  707. package/schema/fixtures/values/invalid/over-cap.json +26 -0
  708. package/schema/fixtures/values/invalid/unknown-key.json +5 -0
  709. package/schema/fixtures/values/invalid/version-string.json +1 -0
  710. package/schema/fixtures/values/valid/empty-lists.json +7 -0
  711. package/schema/fixtures/values/valid/full.json +20 -0
  712. package/schema/fixtures/values/valid/minimal.json +1 -0
  713. package/schema/fixtures/values-md/invalid/schema-invalid.md +62 -0
  714. package/schema/fixtures/values-md/invalid/two-blocks.md +69 -0
  715. package/schema/fixtures/values-md/invalid/unterminated.md +61 -0
  716. package/schema/fixtures/values-md/invalid/yaml-error.md +63 -0
  717. package/schema/fixtures/values-md/valid/absent.md +50 -0
  718. package/schema/fixtures/values-md/valid/with-values.md +79 -0
  719. package/schema/policy.schema.json +501 -0
  720. package/schema/sample-record.schema.json +26 -0
  721. package/schema/values.schema.json +55 -0
  722. package/templates/codex/README.md +9 -0
@@ -0,0 +1,714 @@
1
+ /**
2
+ * The dark-session detector (APRV-192): the log is asked what git already knows.
3
+ *
4
+ * ## The hole this closes
5
+ *
6
+ * APRV-151 built the CI-side grant cross-check, and its root cause is the thing
7
+ * that check cannot reach. A session whose harness never invoked the hook writes
8
+ * NO records at all: not a grant, not a refusal, not a request. Nothing inside
9
+ * such a session is in a position to notice, because the component that would
10
+ * have written the record is the component that did not run, and asking a
11
+ * session whether it is hooked asks the party under oversight to report its own
12
+ * oversight (SPEC.md §11). The CI guard sees only the diffs that reach a pull
13
+ * request; a session that ran dark and pushed nothing, or pushed to a branch
14
+ * nobody opened a pull request for, leaves the guard with nothing to read.
15
+ *
16
+ * So the direction is reversed. APRV-42's git evidence asks "does every log
17
+ * entry match git?"; this asks **"does every piece of git activity match a log
18
+ * entry?"** Git is the artifact a session cannot help producing: a commit has an
19
+ * author, a date and a tree, a linked worktree has a root and a birth, and none
20
+ * of those is authored by the runtime's hook. The absence of a record beside
21
+ * them is the signal.
22
+ *
23
+ * ## Two arms, because there are two questions
24
+ *
25
+ * **Arm A, evidence.** For each commit in the window, every guarded path it
26
+ * changed must carry evidence in the verified log that a human decided it. This
27
+ * is not reimplemented: it is {@link evaluateProtectedPaths}, APRV-151's own
28
+ * evaluator, called per commit, so the verdict vocabulary (`attested`,
29
+ * `granted-file`, `granted-command`), the recency bound, the exempt evidence
30
+ * surface and the `no-evidence` code are one implementation with two callers.
31
+ * Arm A runs over EVERY checkout, the primary included, because a guarded-path
32
+ * commit with no evidence is dark wherever it was made.
33
+ *
34
+ * **Arm B, silence.** A linked worktree that produced commits in the window (or
35
+ * that came into existence in it) and to which NOT ONE record in the window can
36
+ * be attributed is a dark session, whatever it touched. This is the arm that
37
+ * catches the APRV-151 root cause on paths the CI guard never sees, and it is
38
+ * the arm that needs an attribution rule.
39
+ *
40
+ * ## How a record is attributed to a worktree
41
+ *
42
+ * Hook task ids are `hook:<session>:<tool-use>` and carry no checkout, so the
43
+ * id is not the key. The bound material is. Every gated file-tool call binds
44
+ * `{tool, rule, file, …}` with `file` ABSOLUTE (`cli/hook.ts`), and every gated
45
+ * shell call binds `{command, cwd}` with `cwd` absolute. A record whose bound
46
+ * material names a path inside a worktree root was written by a session working
47
+ * in that worktree. That is PR #169's checkout-root anchoring, which APRV-151's
48
+ * notes recorded as the stronger answer to the two-checkouts problem and did not
49
+ * have a caller for; it has one now.
50
+ *
51
+ * The branch name is the second key, and a weak one: a branch `aprv-192-…`
52
+ * names task `APRV-192`, and a `task.registered` for that id in the window is
53
+ * attribution. Weak because a branch name is chosen by the party under
54
+ * oversight, so it may only ever ADD attribution (SPEC.md §11.1 invariant 4:
55
+ * a self-reported field may raise scrutiny and never lower it) — which is
56
+ * exactly what it does here, since attribution is what stops an alarm. Stated
57
+ * plainly rather than hidden: a session that names its branch after a task some
58
+ * other session registered escapes arm B. It does not escape arm A, and the
59
+ * cost of the alternative (no branch key at all) is an alarm on every worktree
60
+ * whose session worked entirely outside the payload store's reach.
61
+ *
62
+ * ## Fail closed in the report
63
+ *
64
+ * Uncertainty is never "fine". A log that does not verify, a payload whose
65
+ * bytes will not resolve, a commit git could not date, a worktree whose root
66
+ * cannot be placed: each is its own `undetermined` verdict with its own code,
67
+ * counted separately from the clean ones, and the renderer never folds an
68
+ * undetermined subject into the pass count. What this module does NOT do is
69
+ * escalate on uncertainty, because a detector is not an enforcement path: it
70
+ * reports, a human reads, and nothing here changes what the gate authorizes.
71
+ *
72
+ * ## What keeps the human's own commits quiet (AC4)
73
+ *
74
+ * Three exemptions, narrow and documented, and each of them fails NOISY when it
75
+ * does not apply:
76
+ *
77
+ * 1. A commit that touches only the daemon's own append surface
78
+ * ({@link EXEMPT_PREFIXES}) is a records advance carrying evidence, not a
79
+ * session doing work. Requiring evidence for the evidence is circular.
80
+ * 2. A commit authored by the git-evidence identity is the daemon's own
81
+ * (`daemon/git-evidence.ts`), and the daemon is not a session.
82
+ * 3. Arm B's subject is LINKED WORKTREES ONLY. The agent-session artifact is
83
+ * the worktree; the human commits in the primary checkout, where the policy
84
+ * ceremony and the genesis events are made. The primary is not thereby
85
+ * passed: it stays wholly subject to arm A, so a ceremony commit passes on
86
+ * its attestation and an unevidenced guarded-path commit there is still
87
+ * reported. And the limit is stated on every report as its own coverage
88
+ * line, so an agent session that ran in the primary checkout shows up as a
89
+ * gap in the report rather than as silence.
90
+ *
91
+ * ## Layout
92
+ *
93
+ * The evaluator ({@link evaluateDarkSessions}) is PURE: no IO, no clock, no
94
+ * git, so a fixture is enough to test it and a repository is never required.
95
+ * The observing half below the fold does run git and read the payload store,
96
+ * and it lives here rather than under `daemon/` because `approval doctor`
97
+ * reports the same findings and `src/cli/` may not import `src/daemon/`
98
+ * (APRV-59). The one thing that stays in `daemon/dark-session.ts` is the
99
+ * append, which is the one thing a reader must never do.
100
+ */
101
+ import { spawnSync } from "node:child_process";
102
+ import { existsSync, readFileSync, statSync } from "node:fs";
103
+ import { basename, join } from "node:path";
104
+ import { policyBytesHash } from "./attest.js";
105
+ import { childEnvironment } from "./child-env.js";
106
+ import { tick as readClock } from "./clock.js";
107
+ import { loadPayload, payloadStoreDirFor } from "./payload-store.js";
108
+ import { loadPolicy } from "./policy-load.js";
109
+ import { EXEMPT_PREFIXES, evaluateProtectedPaths, isExemptPath, isGuardedPath, } from "./protected-path-guard.js";
110
+ /**
111
+ * The events a hooked session cannot avoid writing.
112
+ *
113
+ * A registration opens the question, a request puts it to a human, and a start
114
+ * records that something ran. A session that made any gated tool call at all
115
+ * wrote at least one of these; a session that wrote none of them made no gated
116
+ * call the runtime ever saw, which is the definition of dark.
117
+ */
118
+ export const SESSION_EVENTS = [
119
+ "task.registered",
120
+ "approval.requested",
121
+ "execution.started",
122
+ // APRV-214. A tool call the open window let through is recorded rather than
123
+ // authorized, and it is recorded loudly: a session running behind a window is
124
+ // the opposite of dark, and counting it as dark would report the one state a
125
+ // human deliberately created as the state this sweep exists to find.
126
+ "gate.bypassed",
127
+ ];
128
+ /**
129
+ * How a subject came out. **Frozen union**, additive-only, in the same sense as
130
+ * every other verdict vocabulary here (SPEC.md §11.1 invariant 6).
131
+ */
132
+ export const DARK_SESSION_VERDICTS = ["hooked", "dark", "exempt", "undetermined"];
133
+ /**
134
+ * Why a subject got the verdict it got. **Frozen union**, additive-only.
135
+ *
136
+ * `no-evidence` is spelled exactly as APRV-151 spells it, because it IS
137
+ * APRV-151's finding, arrived at through the same evaluator.
138
+ */
139
+ export const DARK_SESSION_CODES = [
140
+ /** Arm B: git activity in the window, and not one attributable record. */
141
+ "no-records",
142
+ /** Arm A: a guarded path changed and the log carries no evidence for it. */
143
+ "no-evidence",
144
+ /** Exempt: the commits touch only the daemon's own append surface. */
145
+ "evidence-surface",
146
+ /** Exempt: authored by the daemon's own git identity. */
147
+ "daemon-authored",
148
+ /** Exempt from arm B (never from arm A): the primary checkout. */
149
+ "primary-checkout",
150
+ /** Undetermined: the log did not verify, so nothing may be read from it. */
151
+ "log-unverified",
152
+ /** Undetermined: git could not be run, or answered nothing. */
153
+ "git-unavailable",
154
+ /** Undetermined: bound material would not resolve, so records cannot be placed. */
155
+ "payload-unresolvable",
156
+ /** Undetermined: git named no usable date for the activity. */
157
+ "activity-undated",
158
+ ];
159
+ /** The observation key: one subject, one state of the world. */
160
+ export function observationKey(subject, newestSha, born) {
161
+ return `${subject}@${newestSha ?? "no-commit"}#${born ?? "unknown-birth"}`;
162
+ }
163
+ /** A record's payload as a map. The log's payload shape is open at v0.1. */
164
+ function payloadOf(record) {
165
+ const value = record.payload;
166
+ return typeof value === "object" && value !== null && !Array.isArray(value)
167
+ ? value
168
+ : {};
169
+ }
170
+ /** Every `payload_hash` a record names, on the record or on its envelope actions. */
171
+ function hashesOf(record) {
172
+ const payload = payloadOf(record);
173
+ const found = [];
174
+ const direct = payload["payload_hash"];
175
+ if (typeof direct === "string")
176
+ found.push(direct);
177
+ const envelope = payload["envelope"];
178
+ const actions = typeof envelope === "object" && envelope !== null && !Array.isArray(envelope)
179
+ ? envelope["actions"]
180
+ : payload["actions"];
181
+ if (Array.isArray(actions)) {
182
+ for (const action of actions) {
183
+ if (typeof action !== "object" || action === null)
184
+ continue;
185
+ const hash = action["payload_hash"];
186
+ if (typeof hash === "string")
187
+ found.push(hash);
188
+ }
189
+ }
190
+ return found;
191
+ }
192
+ /** The absolute paths one piece of bound material names. */
193
+ function pathsIn(material) {
194
+ if (typeof material !== "object" || material === null || Array.isArray(material))
195
+ return [];
196
+ const map = material;
197
+ const found = [];
198
+ for (const key of ["file", "cwd", "path"]) {
199
+ const value = map[key];
200
+ if (typeof value === "string" && value.length > 0)
201
+ found.push(value);
202
+ }
203
+ const argv = map["argv"];
204
+ if (Array.isArray(argv)) {
205
+ for (const word of argv)
206
+ if (typeof word === "string" && word.startsWith("/"))
207
+ found.push(word);
208
+ }
209
+ return found;
210
+ }
211
+ /** Is `path` inside `root`, or `root` itself? Both are absolute and resolved. */
212
+ function inside(path, root) {
213
+ if (path === root)
214
+ return true;
215
+ return path.startsWith(root.endsWith("/") ? root : `${root}/`);
216
+ }
217
+ /** The Backlog.md task id a branch name begins with, upper-cased (`aprv-192-x`). */
218
+ export function taskIdFromBranch(branch) {
219
+ if (branch === null)
220
+ return null;
221
+ const match = /^([A-Za-z][A-Za-z0-9_]*-\d+)/u.exec(branch);
222
+ return match?.[1]?.toUpperCase() ?? null;
223
+ }
224
+ /** Records inside the window, in log order. */
225
+ function inWindow(records, window) {
226
+ const from = Date.parse(window.from);
227
+ const to = Date.parse(window.to);
228
+ if (Number.isNaN(from) || Number.isNaN(to))
229
+ return [...records];
230
+ return records.filter((record) => {
231
+ const at = Date.parse(record.ts);
232
+ return !Number.isNaN(at) && at >= from && at <= to;
233
+ });
234
+ }
235
+ function attribute(checkout, windowed, payloadFor) {
236
+ const seqs = [];
237
+ let unresolved = false;
238
+ const branchTask = taskIdFromBranch(checkout.branch);
239
+ for (const record of windowed) {
240
+ if (!SESSION_EVENTS.includes(record.event))
241
+ continue;
242
+ // The strong key: bound material naming a path inside this checkout.
243
+ let placed = false;
244
+ for (const hash of hashesOf(record)) {
245
+ const material = payloadFor(hash);
246
+ if (material === null) {
247
+ unresolved = true;
248
+ continue;
249
+ }
250
+ if (pathsIn(material).some((path) => inside(path, checkout.root))) {
251
+ placed = true;
252
+ break;
253
+ }
254
+ }
255
+ // The weak key: this checkout's branch names a task this record is under.
256
+ // It may only ADD attribution, never remove it (invariant 4).
257
+ if (!placed && branchTask !== null && typeof record.task === "string") {
258
+ placed = record.task.toUpperCase().startsWith(branchTask);
259
+ }
260
+ if (placed)
261
+ seqs.push(record.seq);
262
+ }
263
+ return { seqs, unresolved };
264
+ }
265
+ /** Did this commit touch anything but the daemon's own append surface? */
266
+ function touchesOnlyEvidence(commit) {
267
+ return commit.changedPaths.length > 0 && commit.changedPaths.every((path) => isExemptPath(path));
268
+ }
269
+ /** The limit arm B carries, repeated wherever the primary checkout is reported. */
270
+ const coverageNote = "A session that ran in the primary checkout is not covered by arm B; that is a stated limit of this detector, not a clean bill.";
271
+ /**
272
+ * Evaluate what git showed against what the log carries. Pure: no IO, no clock.
273
+ */
274
+ export function evaluateDarkSessions(input) {
275
+ const coverage = "arm B (a checkout with git activity and no records) covers LINKED WORKTREES only: the " +
276
+ "agent-session artifact is the worktree, and the primary checkout is where the human's " +
277
+ "policy ceremony and genesis commits are made. A session that ran IN the primary checkout " +
278
+ "is outside arm B and is covered only by arm A (guarded paths must carry evidence).";
279
+ const findings = [];
280
+ for (const checkout of input.activity.checkouts) {
281
+ findings.push(judge(checkout, input));
282
+ }
283
+ return {
284
+ ok: findings.every((finding) => finding.verdict !== "dark"),
285
+ settled: findings.every((finding) => finding.verdict !== "undetermined"),
286
+ findings,
287
+ window: input.window,
288
+ coverage,
289
+ };
290
+ }
291
+ function base(checkout, verdict, code, guardedPaths, attributed, detail) {
292
+ const newestSha = checkout.commits.length === 0 ? null : checkout.commits[0].sha;
293
+ return {
294
+ subject: checkout.name,
295
+ root: checkout.root,
296
+ primary: checkout.primary,
297
+ branch: checkout.branch,
298
+ verdict,
299
+ code,
300
+ commits: checkout.commits.length,
301
+ guardedPaths,
302
+ attributed,
303
+ newestSha,
304
+ key: observationKey(checkout.name, newestSha, checkout.born),
305
+ detail,
306
+ };
307
+ }
308
+ function judge(checkout, input) {
309
+ const where = `${checkout.name} (${checkout.root}${checkout.branch === null ? ", detached HEAD" : `, branch ${checkout.branch}`})`;
310
+ if (input.activity.unavailable !== null) {
311
+ return base(checkout, "undetermined", "git-unavailable", [], [], `${where}: git could not be asked what happened here (${input.activity.unavailable}), so this sweep establishes nothing about it. Reported as uncertainty rather than as a pass.`);
312
+ }
313
+ if (input.records === null) {
314
+ return base(checkout, "undetermined", "log-unverified", [], [], `${where}: the log does not verify (${input.logDetail ?? "no detail"}), and nothing in this runtime reads evidence out of records that have not passed chain verification. Reported as uncertainty rather than as a pass.`);
315
+ }
316
+ const records = input.records;
317
+ // The daemon's own commits, and a records advance carrying evidence, are not
318
+ // sessions. Both exemptions are computed BEFORE any alarm, and both are
319
+ // narrow: a commit doing anything else on the same subject removes them.
320
+ const substantive = checkout.commits.filter((commit) => !input.daemonEmails.includes(commit.authorEmail.toLowerCase()) && !touchesOnlyEvidence(commit));
321
+ if (checkout.commits.length > 0 && substantive.length === 0) {
322
+ const daemonAuthored = checkout.commits.every((commit) => input.daemonEmails.includes(commit.authorEmail.toLowerCase()));
323
+ return base(checkout, "exempt", daemonAuthored ? "daemon-authored" : "evidence-surface", [], [], daemonAuthored
324
+ ? `${where}: every one of the ${String(checkout.commits.length)} commit(s) here was authored by the daemon's own git identity, which is the runtime witnessing the log rather than a session doing work`
325
+ : `${where}: every one of the ${String(checkout.commits.length)} commit(s) here touches only the daemon's append surface (${EXEMPT_PREFIXES.join(", ")}) — a records advance carrying evidence, and requiring evidence for the evidence is circular`);
326
+ }
327
+ // ---------------------------------------------------------------------
328
+ // Arm A: guarded paths need evidence, in every checkout.
329
+ // ---------------------------------------------------------------------
330
+ const guarded = new Set();
331
+ for (const commit of substantive) {
332
+ for (const path of commit.changedPaths) {
333
+ if (isGuardedPath(path, input.policyProtectedPaths))
334
+ guarded.add(path);
335
+ }
336
+ }
337
+ const guardedPaths = [...guarded].sort();
338
+ const failures = [];
339
+ if (guardedPaths.length > 0) {
340
+ const changeTs = new Map();
341
+ for (const commit of substantive) {
342
+ for (const path of commit.changedPaths) {
343
+ if (!guarded.has(path))
344
+ continue;
345
+ const seen = changeTs.get(path);
346
+ if (seen === undefined || (commit.ts !== null && seen !== null && commit.ts > seen)) {
347
+ changeTs.set(path, commit.ts);
348
+ }
349
+ }
350
+ }
351
+ const logWindow = {
352
+ firstSeq: records.length === 0 ? null : records[0].seq,
353
+ lastSeq: records.length === 0 ? null : records[records.length - 1].seq,
354
+ firstTs: records.length === 0 ? null : records[0].ts,
355
+ lastTs: records.length === 0 ? null : records[records.length - 1].ts,
356
+ base: input.window.from,
357
+ head: `${checkout.name}@${substantive[0].sha.slice(0, 12)}`,
358
+ };
359
+ // APRV-202 made the guard ask whether THIS change was granted, so it needs
360
+ // the path's bytes on both sides of the change. Here the change is the span
361
+ // of substantive commits that touched the path: base is the parent of the
362
+ // oldest of them, head is the newest. A blob git cannot show, or one that
363
+ // is binary, answers null and the guard fails the path as
364
+ // change-unreadable rather than falling back to the path-level rule.
365
+ const blobCache = new Map();
366
+ const blobsFor = (path) => {
367
+ const cached = blobCache.get(path);
368
+ if (cached !== undefined)
369
+ return cached;
370
+ const touching = substantive.filter((commit) => commit.changedPaths.includes(path));
371
+ let value = null;
372
+ if (touching.length > 0) {
373
+ const byTime = [...touching].sort((a, b) => (a.ts ?? "").localeCompare(b.ts ?? ""));
374
+ const baseRev = `${byTime[0].sha}^`;
375
+ const headRev = byTime[byTime.length - 1].sha;
376
+ const inTree = (rev) => {
377
+ const listed = git(["ls-tree", "--name-only", rev, "--", path], checkout.root);
378
+ return listed.ok && listed.stdout.trim().length > 0;
379
+ };
380
+ const show = (rev) => {
381
+ const shown = git(["show", `${rev}:${path}`], checkout.root);
382
+ return shown.ok ? shown.stdout : null;
383
+ };
384
+ const baseHas = inTree(baseRev);
385
+ const headHas = inTree(headRev);
386
+ const baseText = baseHas ? show(baseRev) : null;
387
+ const headText = headHas ? show(headRev) : null;
388
+ const unreadable = (baseHas && baseText === null) ||
389
+ (headHas && headText === null) ||
390
+ (baseText !== null && baseText.includes("\u0000")) ||
391
+ (headText !== null && headText.includes("\u0000"));
392
+ if (!unreadable)
393
+ value = { base: baseText, head: headText };
394
+ }
395
+ blobCache.set(path, value);
396
+ return value;
397
+ };
398
+ const report = evaluateProtectedPaths({
399
+ changedPaths: guardedPaths,
400
+ blobsFor,
401
+ records,
402
+ logStatus: "ok",
403
+ policyProtectedPaths: input.policyProtectedPaths,
404
+ policySha256AtHead: input.policySha256,
405
+ policyPath: input.policyPath,
406
+ payloadFor: input.payloadFor,
407
+ changeTsFor: (path) => changeTs.get(path) ?? null,
408
+ ...(input.lookbackMs === undefined ? {} : { lookbackMs: input.lookbackMs }),
409
+ window: logWindow,
410
+ });
411
+ for (const finding of report.findings)
412
+ if (!finding.ok)
413
+ failures.push(finding);
414
+ }
415
+ const attribution = attribute(checkout, inWindow(records, input.window), input.payloadFor);
416
+ if (failures.length > 0) {
417
+ return base(checkout, "dark", "no-evidence", guardedPaths, attribution.seqs, `${where}: ${String(failures.length)} guarded path(s) changed here in ${input.window.from}..${input.window.to} with no evidence in the log that a human decided them — ${failures
418
+ .map((finding) => `${finding.path}: ${finding.detail}`)
419
+ .join(" | ")}`);
420
+ }
421
+ // ---------------------------------------------------------------------
422
+ // Arm B: silence, in a linked worktree.
423
+ // ---------------------------------------------------------------------
424
+ if (checkout.primary) {
425
+ return base(checkout, "exempt", "primary-checkout", guardedPaths, attribution.seqs, `${where}: the primary checkout is outside arm B by design — it is where the human's policy ceremony and genesis commits are made, and the agent-session artifact this detector keys on is the linked worktree. Arm A did run here and found evidence for every one of the ${String(guardedPaths.length)} guarded path(s) it changed. ${coverageNote}`);
426
+ }
427
+ const produced = substantive.length > 0 || checkout.born !== null;
428
+ if (!produced) {
429
+ return base(checkout, "hooked", null, guardedPaths, attribution.seqs, `${where}: no commits in ${input.window.from}..${input.window.to} and no birth inside it, so there is no git activity for the log to owe a record against`);
430
+ }
431
+ if (attribution.seqs.length > 0) {
432
+ return base(checkout, "hooked", null, guardedPaths, attribution.seqs, `${where}: ${String(substantive.length)} commit(s) in the window, and ${String(attribution.seqs.length)} record(s) in the log are attributable to this checkout (seq ${attribution.seqs
433
+ .slice(0, 5)
434
+ .join(", ")}${attribution.seqs.length > 5 ? ", …" : ""}), so a hook ran here`);
435
+ }
436
+ if (attribution.unresolved) {
437
+ return base(checkout, "undetermined", "payload-unresolvable", guardedPaths, [], `${where}: ${String(substantive.length)} commit(s) in the window and no attributable record, but at least one record's bound material would not resolve from the payload store, so a record written from HERE may exist and be unplaceable. Reported as uncertainty rather than as an alarm or as a pass; the repair is the payload store, and \`approval doctor\` reports its health.`);
438
+ }
439
+ if (substantive.length > 0 && substantive.every((commit) => commit.ts === null)) {
440
+ return base(checkout, "undetermined", "activity-undated", guardedPaths, [], `${where}: git reported ${String(substantive.length)} commit(s) here and dated none of them, so whether this activity falls in ${input.window.from}..${input.window.to} cannot be established. Reported as uncertainty rather than as a pass.`);
441
+ }
442
+ return base(checkout, "dark", "no-records", guardedPaths, [], `${where}: DARK SESSION. ${substantive.length === 0
443
+ ? `this worktree came into existence at ${checkout.born ?? "an unknown instant"} inside the window`
444
+ : `${String(substantive.length)} commit(s) were made here in the window (newest ${substantive[0].sha.slice(0, 12)} by ${substantive[0].author})`}, and NOT ONE ${SESSION_EVENTS.join("/")} record in ${input.window.from}..${input.window.to} can be attributed to it. A session whose harness hook never fired writes exactly this: no grant, no refusal, no request. What this establishes is the absence, not what the session did; the repair is \`approval doctor\`'s harness-hook-wiring row in that checkout and \`approval instructions hook\`.`);
445
+ }
446
+ /** The report as lines an operator reads. Pure. */
447
+ export function renderDarkSessionReport(report) {
448
+ const lines = [`dark-session sweep: ${report.window.from} .. ${report.window.to}`];
449
+ if (report.findings.length === 0) {
450
+ lines.push(" no checkouts observed");
451
+ }
452
+ for (const finding of report.findings) {
453
+ const label = finding.verdict === "dark"
454
+ ? ` DARK ${finding.subject} [${finding.code ?? "?"}]`
455
+ : finding.verdict === "undetermined"
456
+ ? ` UNDETERMINED ${finding.subject} [${finding.code ?? "?"}]`
457
+ : ` ${finding.verdict === "exempt" ? "EXEMPT" : "OK"} ${finding.subject}${finding.code === null ? "" : ` [${finding.code}]`}`;
458
+ lines.push(label);
459
+ lines.push(` ${finding.detail}`);
460
+ }
461
+ lines.push(` ${report.ok ? "no dark session" : "DARK SESSION(S) FOUND"}; ${report.settled ? "every subject established" : "SOME SUBJECTS NOT ESTABLISHED"}`);
462
+ lines.push(` coverage: ${report.coverage}`);
463
+ return `${lines.join("\n")}\n`;
464
+ }
465
+ // ===========================================================================
466
+ // Observing: git, the payload store, and the clock
467
+ // ===========================================================================
468
+ /**
469
+ * Everything below this line does IO, and it lives here rather than in
470
+ * `daemon/` for one reason: `approval doctor` reports the same findings and
471
+ * `src/cli/` may not import `src/daemon/` (APRV-59, `tests/layering.test.ts`).
472
+ * Two observers would be two answers to one question, so there is one, and the
473
+ * daemon's module keeps only the thing a reader must never do — the append.
474
+ */
475
+ /** How far back a sweep looks, absent an operator saying otherwise. */
476
+ export const DEFAULT_DARK_WINDOW_MS = 24 * 60 * 60 * 1000;
477
+ /** How often the sweep runs, absent an operator saying otherwise. */
478
+ export const DEFAULT_DARK_INTERVAL_MS = 15 * 60 * 1000;
479
+ /**
480
+ * The git identities whose commits are the runtime's own.
481
+ *
482
+ * Spelled here rather than imported from `daemon/git-evidence.ts`, which is
483
+ * where the daemon's commit identity is defined: core must not depend on the
484
+ * daemon. `tests/dark-session.test.ts` pins the two against each other so they
485
+ * cannot drift apart, the same device `APPROVALD_VERSION` uses for its own
486
+ * duplicate of the package version.
487
+ */
488
+ export const DAEMON_EVIDENCE_EMAILS = ["approvald@noreply.approval.md"];
489
+ /**
490
+ * Record and field separators inside `git log --pretty`.
491
+ *
492
+ * ASCII 30 and 31, which cannot reach the parser inside a path: git quotes any
493
+ * path containing a control character, so a filename engineered to carry one of
494
+ * these arrives escaped and cannot forge a commit boundary. That matters more
495
+ * here than in an ordinary parser, because the party who chooses the filenames
496
+ * is the party this module reports on.
497
+ */
498
+ const RS = "\u001e";
499
+ const FS = "\u001f";
500
+ function detailOf(cause) {
501
+ return cause instanceof Error ? cause.message : String(cause);
502
+ }
503
+ /**
504
+ * One git invocation, non-interactive, in a scrubbed environment.
505
+ *
506
+ * The same shape `daemon/git-evidence.ts` uses and for the same reasons: an
507
+ * inherited `GIT_DIR` would redirect the command at another repository, a
508
+ * terminal prompt would hang a daemon silently, and the sweep has no use for a
509
+ * credential (APRV-205's scrub).
510
+ */
511
+ function git(args, cwd) {
512
+ const env = { ...childEnvironment().env, GIT_TERMINAL_PROMPT: "0" };
513
+ delete env["GIT_DIR"];
514
+ delete env["GIT_WORK_TREE"];
515
+ const result = spawnSync("git", args, { cwd, encoding: "utf8", env, maxBuffer: 32 * 1024 * 1024 });
516
+ if (result.error !== undefined || result.status === null) {
517
+ return { ok: false, stdout: "", stderr: detailOf(result.error ?? "git did not run") };
518
+ }
519
+ return { ok: result.status === 0, stdout: result.stdout ?? "", stderr: result.stderr ?? "" };
520
+ }
521
+ /**
522
+ * When this checkout came into being, as git and the filesystem can say.
523
+ *
524
+ * A linked worktree's `.git` is a FILE written at `git worktree add`, so its
525
+ * birth is the worktree's. The primary's `.git` is a directory whose birth is
526
+ * the clone's, which is why arm B never reads this for the primary. `null` when
527
+ * the platform reports no usable birth time, which is a reporting gap and never
528
+ * a correctness one: a worktree with no birth and no commits simply produces no
529
+ * activity for the log to owe a record against.
530
+ */
531
+ function birthOf(root) {
532
+ try {
533
+ const stats = statSync(join(root, ".git"));
534
+ const born = stats.birthtimeMs > 0 ? stats.birthtimeMs : stats.mtimeMs;
535
+ if (!Number.isFinite(born) || born <= 0)
536
+ return null;
537
+ return new Date(born).toISOString();
538
+ }
539
+ catch {
540
+ return null;
541
+ }
542
+ }
543
+ /** The checkouts git knows about: the primary first, then every linked one. */
544
+ function worktrees(root) {
545
+ const listed = git(["worktree", "list", "--porcelain"], root);
546
+ if (!listed.ok) {
547
+ return {
548
+ ok: false,
549
+ message: `\`git worktree list\` failed in ${root}: ${listed.stderr.trim() || "no output"}`,
550
+ };
551
+ }
552
+ const found = [];
553
+ let path = null;
554
+ let branch = null;
555
+ const flush = () => {
556
+ if (path === null)
557
+ return;
558
+ found.push({
559
+ root: path,
560
+ name: found.length === 0 ? "primary" : basename(path),
561
+ primary: found.length === 0,
562
+ branch,
563
+ born: birthOf(path),
564
+ commits: [],
565
+ });
566
+ path = null;
567
+ branch = null;
568
+ };
569
+ for (const line of listed.stdout.split("\n")) {
570
+ if (line.startsWith("worktree ")) {
571
+ flush();
572
+ path = line.slice("worktree ".length).trim();
573
+ }
574
+ else if (line.startsWith("branch ")) {
575
+ const ref = line.slice("branch ".length).trim();
576
+ branch = ref.startsWith("refs/heads/") ? ref.slice("refs/heads/".length) : ref;
577
+ }
578
+ }
579
+ flush();
580
+ return { ok: true, found };
581
+ }
582
+ /** The trunk to exclude a worktree's own commits from, or `null` when there is none. */
583
+ function trunkOf(root) {
584
+ for (const candidate of ["origin/main", "origin/master", "main", "master"]) {
585
+ if (git(["rev-parse", "--verify", "--quiet", `${candidate}^{commit}`], root).ok)
586
+ return candidate;
587
+ }
588
+ return null;
589
+ }
590
+ /** Parse `git log`'s record-separated output into commits. */
591
+ function parseCommits(text, ref) {
592
+ const commits = [];
593
+ for (const chunk of text.split(RS)) {
594
+ const lines = chunk.split("\n").filter((line) => line.length > 0);
595
+ const header = lines.shift();
596
+ if (header === undefined)
597
+ continue;
598
+ const [sha, ts, author, email] = header.split(FS);
599
+ if (sha === undefined || sha.length === 0)
600
+ continue;
601
+ commits.push({
602
+ sha,
603
+ ts: ts === undefined || ts.length === 0 ? null : ts,
604
+ author: author ?? "",
605
+ authorEmail: (email ?? "").toLowerCase(),
606
+ changedPaths: lines,
607
+ ref,
608
+ });
609
+ }
610
+ return commits;
611
+ }
612
+ /**
613
+ * Commits on one checkout inside the window, newest first.
614
+ *
615
+ * For a linked worktree the range is `HEAD --not <trunk>`: a branch carries all
616
+ * of the trunk's history, and counting the trunk's commits as this worktree's
617
+ * would attribute every merged commit to whichever worktree happened to branch
618
+ * from it. For the primary the range is plain `HEAD`, because the primary's own
619
+ * commits are exactly what arm A is there to check.
620
+ */
621
+ function commitsOf(checkout, trunk, from, to) {
622
+ const range = checkout.primary || trunk === null ? ["HEAD"] : ["HEAD", "--not", trunk];
623
+ const run = git([
624
+ "log",
625
+ ...range,
626
+ `--since=${from}`,
627
+ `--until=${to}`,
628
+ `--pretty=format:${RS}%H${FS}%aI${FS}%an <%ae>${FS}%ae`,
629
+ "--name-only",
630
+ ], checkout.root);
631
+ if (!run.ok) {
632
+ return {
633
+ ok: false,
634
+ message: `\`git log\` failed in ${checkout.root}: ${run.stderr.trim() || "no output"}`,
635
+ };
636
+ }
637
+ return { ok: true, commits: parseCommits(run.stdout, checkout.branch ?? "HEAD") };
638
+ }
639
+ /** Everything git could tell the sweep about a repository in one window. */
640
+ export function observeGitActivity(root, from, to) {
641
+ const listed = worktrees(root);
642
+ if (!listed.ok)
643
+ return { checkouts: [], unavailable: listed.message };
644
+ const trunk = trunkOf(root);
645
+ const checkouts = [];
646
+ for (const checkout of listed.found) {
647
+ if (!existsSync(checkout.root))
648
+ continue;
649
+ const found = commitsOf(checkout, trunk, from, to);
650
+ if (!found.ok)
651
+ return { checkouts: [], unavailable: found.message };
652
+ checkouts.push({ ...checkout, commits: found.commits });
653
+ }
654
+ return { checkouts, unavailable: null };
655
+ }
656
+ /** The policy file's name and byte digest, for the `attested` verdict. */
657
+ function policyFacts(options) {
658
+ const where = options.policy.file !== undefined
659
+ ? { file: options.policy.file }
660
+ : { dir: options.policy.dir ?? options.root };
661
+ if (options.schemaDir !== undefined)
662
+ where.schemaDir = options.schemaDir;
663
+ const load = loadPolicy(where);
664
+ // Fail closed exactly as the daemon's TTL read does: a policy that will not
665
+ // load widens nothing, so the guarded set is the built-in one and no path is
666
+ // quietly dropped out of it.
667
+ const protectedPaths = load.ok ? (load.policy.protected_paths ?? []) : [];
668
+ const path = options.policy.file ?? join(options.policy.dir ?? options.root, "APPROVAL.md");
669
+ let sha256 = null;
670
+ try {
671
+ sha256 = policyBytesHash(readFileSync(path));
672
+ }
673
+ catch {
674
+ sha256 = null;
675
+ }
676
+ return { path: basename(path), sha256, protectedPaths };
677
+ }
678
+ /**
679
+ * Observe and judge, appending nothing.
680
+ *
681
+ * The read-only half, which is the half `approval doctor` calls: doctor reports
682
+ * and the daemon writes, so two processes never become two writers of one fact.
683
+ */
684
+ export function reportDarkSessions(options) {
685
+ const now = readClock(options.clock === undefined ? {} : { clock: options.clock });
686
+ const at = Date.parse(now);
687
+ const from = new Date((Number.isNaN(at) ? Date.now() : at) - options.windowMs).toISOString();
688
+ const observe = options.observe ?? observeGitActivity;
689
+ const activity = observe(options.root, from, now);
690
+ const facts = policyFacts(options);
691
+ const storeDir = payloadStoreDirFor(options.logPath);
692
+ const cache = new Map();
693
+ const payloadFor = (hash) => {
694
+ if (cache.has(hash))
695
+ return cache.get(hash) ?? null;
696
+ const loaded = loadPayload(storeDir, hash);
697
+ const value = loaded.ok ? loaded.value : null;
698
+ cache.set(hash, value);
699
+ return value;
700
+ };
701
+ const report = evaluateDarkSessions({
702
+ activity,
703
+ records: options.records,
704
+ ...(options.logDetail === undefined ? {} : { logDetail: options.logDetail }),
705
+ policyProtectedPaths: facts.protectedPaths,
706
+ policyPath: facts.path,
707
+ policySha256: facts.sha256,
708
+ payloadFor,
709
+ daemonEmails: DAEMON_EVIDENCE_EMAILS,
710
+ window: { from, to: now },
711
+ });
712
+ return { report, window: { from, to: now } };
713
+ }
714
+ //# sourceMappingURL=dark-session.js.map