approval-md 0.1.0 → 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 (266) hide show
  1. package/README.md +584 -553
  2. package/SPEC.md +42 -13
  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 +623 -0
  27. package/dist/src/channels/payload-view.d.ts +35 -0
  28. package/dist/src/channels/render-queue.d.ts +149 -0
  29. package/dist/src/channels/tagging.d.ts +196 -0
  30. package/dist/src/channels/telegram.d.ts +1832 -0
  31. package/dist/src/channels/web.d.ts +341 -0
  32. package/dist/src/cli/adapter.d.ts +90 -0
  33. package/dist/src/cli/adapter.js +25 -15
  34. package/dist/src/cli/adapter.js.map +1 -1
  35. package/dist/src/cli/amend.d.ts +59 -0
  36. package/dist/src/cli/args.d.ts +43 -0
  37. package/dist/src/cli/attest.d.ts +41 -0
  38. package/dist/src/cli/audit-card.d.ts +62 -0
  39. package/dist/src/cli/audit.d.ts +59 -0
  40. package/dist/src/cli/channel-telegram.d.ts +806 -0
  41. package/dist/src/cli/channel-web.d.ts +131 -0
  42. package/dist/src/cli/channel.d.ts +71 -0
  43. package/dist/src/cli/checkpoint-tap.d.ts +169 -0
  44. package/dist/src/cli/codex.d.ts +2 -0
  45. package/dist/src/cli/codex.js +172 -0
  46. package/dist/src/cli/codex.js.map +1 -0
  47. package/dist/src/cli/coverage.d.ts +61 -0
  48. package/dist/src/cli/daemon.d.ts +120 -0
  49. package/dist/src/cli/doctor.d.ts +129 -0
  50. package/dist/src/cli/doctor.js +119 -5
  51. package/dist/src/cli/doctor.js.map +1 -1
  52. package/dist/src/cli/env.d.ts +65 -0
  53. package/dist/src/cli/execute.d.ts +202 -0
  54. package/dist/src/cli/exit-codes.d.ts +73 -0
  55. package/dist/src/cli/feedback.d.ts +60 -0
  56. package/dist/src/cli/gate-window.d.ts +40 -0
  57. package/dist/src/cli/gate.d.ts +68 -0
  58. package/dist/src/cli/git-scope.d.ts +190 -0
  59. package/dist/src/cli/gloss-attach.d.ts +85 -0
  60. package/dist/src/cli/gloss-codex-child.d.ts +9 -0
  61. package/dist/src/cli/gloss-codex.d.ts +24 -0
  62. package/dist/src/cli/gloss-options.d.ts +42 -0
  63. package/dist/src/cli/gloss.d.ts +265 -0
  64. package/dist/src/cli/help.d.ts +103 -0
  65. package/dist/src/cli/help.js +173 -51
  66. package/dist/src/cli/help.js.map +1 -1
  67. package/dist/src/cli/hook-codex.d.ts +78 -0
  68. package/dist/src/cli/hook-codex.js +167 -0
  69. package/dist/src/cli/hook-codex.js.map +1 -0
  70. package/dist/src/cli/hook.d.ts +331 -0
  71. package/dist/src/cli/hook.js +186 -80
  72. package/dist/src/cli/hook.js.map +1 -1
  73. package/dist/src/cli/import.d.ts +35 -0
  74. package/dist/src/cli/init.d.ts +84 -0
  75. package/dist/src/cli/init.js +2 -2
  76. package/dist/src/cli/init.js.map +1 -1
  77. package/dist/src/cli/instructions.d.ts +23 -0
  78. package/dist/src/cli/journal.d.ts +41 -0
  79. package/dist/src/cli/log-advance.d.ts +287 -0
  80. package/dist/src/cli/log-advance.js +102 -11
  81. package/dist/src/cli/log-advance.js.map +1 -1
  82. package/dist/src/cli/log-anchor.d.ts +176 -0
  83. package/dist/src/cli/log-checkpoint.d.ts +22 -0
  84. package/dist/src/cli/log-sync.d.ts +243 -0
  85. package/dist/src/cli/log-verbs.d.ts +16 -0
  86. package/dist/src/cli/log-verbs.js +7 -1
  87. package/dist/src/cli/log-verbs.js.map +1 -1
  88. package/dist/src/cli/long-help.d.ts +70 -0
  89. package/dist/src/cli/main.d.ts +77 -0
  90. package/dist/src/cli/main.js +155 -5
  91. package/dist/src/cli/main.js.map +1 -1
  92. package/dist/src/cli/mcp.d.ts +52 -0
  93. package/dist/src/cli/paths.d.ts +56 -0
  94. package/dist/src/cli/payload.d.ts +58 -0
  95. package/dist/src/cli/policy.d.ts +43 -0
  96. package/dist/src/cli/preflight.d.ts +363 -0
  97. package/dist/src/cli/preflight.js +294 -7
  98. package/dist/src/cli/preflight.js.map +1 -1
  99. package/dist/src/cli/progress.d.ts +78 -0
  100. package/dist/src/cli/prompt.d.ts +209 -0
  101. package/dist/src/cli/quickstart.d.ts +46 -0
  102. package/dist/src/cli/quickstart.js +297 -0
  103. package/dist/src/cli/quickstart.js.map +1 -0
  104. package/dist/src/cli/records.d.ts +34 -0
  105. package/dist/src/cli/render.d.ts +22 -0
  106. package/dist/src/cli/sandbox.d.ts +51 -0
  107. package/dist/src/cli/scaffold.d.ts +79 -0
  108. package/dist/src/cli/setup-adapter.d.ts +137 -0
  109. package/dist/src/cli/setup-adapter.js +38 -4
  110. package/dist/src/cli/setup-adapter.js.map +1 -1
  111. package/dist/src/cli/setup-channel.d.ts +117 -0
  112. package/dist/src/cli/setup-checkpoint.d.ts +57 -0
  113. package/dist/src/cli/setup-common.d.ts +275 -0
  114. package/dist/src/cli/setup-flow.d.ts +287 -0
  115. package/dist/src/cli/setup-service.d.ts +96 -0
  116. package/dist/src/cli/setup.d.ts +202 -0
  117. package/dist/src/cli/style.d.ts +320 -0
  118. package/dist/src/cli/token.d.ts +39 -0
  119. package/dist/src/cli/up.d.ts +155 -0
  120. package/dist/src/cli/up.js +4 -2
  121. package/dist/src/cli/up.js.map +1 -1
  122. package/dist/src/cli/usage.d.ts +37 -0
  123. package/dist/src/cli/values.d.ts +40 -0
  124. package/dist/src/cli/vault.d.ts +59 -0
  125. package/dist/src/cli/vault.js +2 -2
  126. package/dist/src/cli/vault.js.map +1 -1
  127. package/dist/src/cli/verb-registry.d.ts +76 -0
  128. package/dist/src/cli/verb-registry.js +176 -8
  129. package/dist/src/cli/verb-registry.js.map +1 -1
  130. package/dist/src/cli/wordmark.d.ts +31 -0
  131. package/dist/src/cli/wordmark.js +2 -2
  132. package/dist/src/codex/doctor.d.ts +13 -0
  133. package/dist/src/codex/doctor.js +41 -0
  134. package/dist/src/codex/doctor.js.map +1 -0
  135. package/dist/src/codex/manifest.d.ts +49 -0
  136. package/dist/src/codex/manifest.js +103 -0
  137. package/dist/src/codex/manifest.js.map +1 -0
  138. package/dist/src/codex/templates.d.ts +41 -0
  139. package/dist/src/codex/templates.js +319 -0
  140. package/dist/src/codex/templates.js.map +1 -0
  141. package/dist/src/codex/trust.d.ts +19 -0
  142. package/dist/src/codex/trust.js +183 -0
  143. package/dist/src/codex/trust.js.map +1 -0
  144. package/dist/src/codex/workspace-plan.d.ts +131 -0
  145. package/dist/src/codex/workspace-plan.js +561 -0
  146. package/dist/src/codex/workspace-plan.js.map +1 -0
  147. package/dist/src/core/actor.d.ts +2 -0
  148. package/dist/src/core/actor.js +5 -0
  149. package/dist/src/core/actor.js.map +1 -0
  150. package/dist/src/core/advance-cycle.d.ts +170 -0
  151. package/dist/src/core/agents-md.d.ts +276 -0
  152. package/dist/src/core/apply-patch.d.ts +49 -0
  153. package/dist/src/core/apply-patch.js +266 -0
  154. package/dist/src/core/apply-patch.js.map +1 -0
  155. package/dist/src/core/attest.d.ts +420 -0
  156. package/dist/src/core/attest.js +13 -1
  157. package/dist/src/core/attest.js.map +1 -1
  158. package/dist/src/core/audit.d.ts +492 -0
  159. package/dist/src/core/budgets.d.ts +238 -0
  160. package/dist/src/core/checkpoint.d.ts +500 -0
  161. package/dist/src/core/child-env.d.ts +88 -0
  162. package/dist/src/core/clock.d.ts +52 -0
  163. package/dist/src/core/command-class.d.ts +543 -0
  164. package/dist/src/core/command-class.js +43 -8
  165. package/dist/src/core/command-class.js.map +1 -1
  166. package/dist/src/core/coverage-sources/adapter.d.ts +40 -0
  167. package/dist/src/core/coverage-sources/gh.d.ts +48 -0
  168. package/dist/src/core/coverage-sources/git.d.ts +101 -0
  169. package/dist/src/core/coverage.d.ts +217 -0
  170. package/dist/src/core/credential-spec.d.ts +72 -0
  171. package/dist/src/core/dark-session.d.ts +331 -0
  172. package/dist/src/core/decision-refusal.d.ts +185 -0
  173. package/dist/src/core/env-file.d.ts +450 -0
  174. package/dist/src/core/execute.d.ts +858 -0
  175. package/dist/src/core/execute.js +44 -6
  176. package/dist/src/core/execute.js.map +1 -1
  177. package/dist/src/core/frontmatter.d.ts +78 -0
  178. package/dist/src/core/gate-window.d.ts +312 -0
  179. package/dist/src/core/gate.d.ts +1364 -0
  180. package/dist/src/core/gate.js +68 -13
  181. package/dist/src/core/gate.js.map +1 -1
  182. package/dist/src/core/git-run.d.ts +73 -0
  183. package/dist/src/core/harness-version.d.ts +157 -0
  184. package/dist/src/core/harness-version.js +2 -1
  185. package/dist/src/core/harness-version.js.map +1 -1
  186. package/dist/src/core/harness-wait.d.ts +55 -0
  187. package/dist/src/core/head-retry.d.ts +107 -0
  188. package/dist/src/core/instance.d.ts +253 -0
  189. package/dist/src/core/intake-limits.d.ts +247 -0
  190. package/dist/src/core/jcs.d.ts +52 -0
  191. package/dist/src/core/journal.d.ts +144 -0
  192. package/dist/src/core/live-draw.d.ts +436 -0
  193. package/dist/src/core/log-reconcile.d.ts +89 -0
  194. package/dist/src/core/log-subscribe.d.ts +36 -0
  195. package/dist/src/core/log-subscribe.js +162 -0
  196. package/dist/src/core/log-subscribe.js.map +1 -0
  197. package/dist/src/core/log.d.ts +278 -0
  198. package/dist/src/core/loop.d.ts +274 -0
  199. package/dist/src/core/loop.js +11 -0
  200. package/dist/src/core/loop.js.map +1 -1
  201. package/dist/src/core/md-fence.d.ts +41 -0
  202. package/dist/src/core/money.d.ts +147 -0
  203. package/dist/src/core/payload-census.d.ts +74 -0
  204. package/dist/src/core/payload-store.d.ts +175 -0
  205. package/dist/src/core/payload.d.ts +71 -0
  206. package/dist/src/core/policy-diff.d.ts +292 -0
  207. package/dist/src/core/policy-diff.js +27 -4
  208. package/dist/src/core/policy-diff.js.map +1 -1
  209. package/dist/src/core/policy-expectations.d.ts +199 -0
  210. package/dist/src/core/policy-explain.d.ts +150 -0
  211. package/dist/src/core/policy-explain.js +31 -3
  212. package/dist/src/core/policy-explain.js.map +1 -1
  213. package/dist/src/core/policy-load.d.ts +527 -0
  214. package/dist/src/core/policy-load.js +15 -3
  215. package/dist/src/core/policy-load.js.map +1 -1
  216. package/dist/src/core/policy-match.d.ts +281 -0
  217. package/dist/src/core/policy-match.js +20 -9
  218. package/dist/src/core/policy-match.js.map +1 -1
  219. package/dist/src/core/policy-proposal.d.ts +265 -0
  220. package/dist/src/core/prompt-layout.d.ts +221 -0
  221. package/dist/src/core/protected-path-guard.d.ts +453 -0
  222. package/dist/src/core/protected-path-guard.js +514 -35
  223. package/dist/src/core/protected-path-guard.js.map +1 -1
  224. package/dist/src/core/registration.d.ts +25 -0
  225. package/dist/src/core/reindex.d.ts +99 -0
  226. package/dist/src/core/sampler.d.ts +313 -0
  227. package/dist/src/core/sandbox.d.ts +290 -0
  228. package/dist/src/core/seal.d.ts +165 -0
  229. package/dist/src/core/state.d.ts +505 -0
  230. package/dist/src/core/task-file.d.ts +185 -0
  231. package/dist/src/core/telegram-config.d.ts +93 -0
  232. package/dist/src/core/token.d.ts +409 -0
  233. package/dist/src/core/token.js +21 -38
  234. package/dist/src/core/token.js.map +1 -1
  235. package/dist/src/core/validate.d.ts +138 -0
  236. package/dist/src/core/values.d.ts +137 -0
  237. package/dist/src/core/vault.d.ts +291 -0
  238. package/dist/src/core/verified-snapshot.d.ts +204 -0
  239. package/dist/src/core/verify.d.ts +336 -0
  240. package/dist/src/core/version.d.ts +8 -0
  241. package/dist/src/core/wysiwys.d.ts +370 -0
  242. package/dist/src/daemon/advance-child.d.ts +39 -0
  243. package/dist/src/daemon/advance.d.ts +466 -0
  244. package/dist/src/daemon/audit.d.ts +87 -0
  245. package/dist/src/daemon/daemon.d.ts +1180 -0
  246. package/dist/src/daemon/dark-session.d.ts +64 -0
  247. package/dist/src/daemon/draw-child.d.ts +36 -0
  248. package/dist/src/daemon/draw.d.ts +154 -0
  249. package/dist/src/daemon/git-evidence.d.ts +173 -0
  250. package/dist/src/daemon/git-evidence.js +1 -1
  251. package/dist/src/daemon/projection.d.ts +180 -0
  252. package/dist/src/daemon/prune.d.ts +207 -0
  253. package/dist/src/mcp/http.d.ts +113 -0
  254. package/dist/src/mcp/server.d.ts +265 -0
  255. package/dist/src/mcp/server.js +9 -1
  256. package/dist/src/mcp/server.js.map +1 -1
  257. package/docs/adapter-api.md +106 -0
  258. package/docs/cli-reference.md +389 -36
  259. package/docs/codex-enforced-session.md +30 -0
  260. package/package.json +12 -2
  261. package/schema/codex-instance.schema.json +82 -0
  262. package/schema/event.schema.json +2 -1
  263. package/schema/fixtures/codex-instance/invalid/unpinned-codex-version.json +40 -0
  264. package/schema/fixtures/codex-instance/valid/canonical.json +40 -0
  265. package/schema/policy.schema.json +21 -1
  266. package/templates/codex/README.md +9 -0
@@ -0,0 +1,149 @@
1
+ /**
2
+ * `.approval/QUEUE.md` — the queue projection of SPEC.md §9.1.
3
+ *
4
+ * > **The queue** (`.approval/QUEUE.md`): a rendered, read-only markdown view of
5
+ * > pending requests (task, actions, declared effects, cost, TTL countdown) plus
6
+ * > the sampled-audit backlog. Regenerated on every relevant event. This is the
7
+ * > screenshot; it is never the truth.
8
+ *
9
+ * Three properties are the whole point of this module.
10
+ *
11
+ * **It is a pure function of (verified log, policy, `now`).** No ambient clock,
12
+ * no locale, no hostname, no environment, no random temp name in the bytes. The
13
+ * evaluation instant is a parameter — the same discipline `channels/tagging.ts`
14
+ * already keeps — so an identical log rendered at an identical `now` produces
15
+ * identical bytes, and a rendering can be reproduced later from the log alone.
16
+ * Every number here is formatted by hand with integer arithmetic for the same
17
+ * reason: `toLocaleString` would make the output depend on the machine.
18
+ *
19
+ * **It re-derives nothing.** Entries come from
20
+ * {@link buildPendingQueue}, which is the one place a {@link ChannelRequest} is
21
+ * built. So the class, the autonomy, the provenance, the budgets, the
22
+ * attestation and the TTL a reader sees in QUEUE.md are the same answers the
23
+ * gate itself would give — not a second implementation that could drift. This
24
+ * module's entire job is text.
25
+ *
26
+ * **It never writes the log.** {@link writeQueue} writes exactly one file, and
27
+ * it is `QUEUE.md`.
28
+ *
29
+ * ## Why this lives in `src/channels/`, not `src/core/`
30
+ *
31
+ * It consumes `channels/contract.ts`'s tagged requests through
32
+ * `channels/tagging.ts`. Putting it in `core/` would make core import channels,
33
+ * inverting the dependency the codebase is built on: core decides, channels
34
+ * display, and this file is display — a read-only markdown surface that collects
35
+ * no gesture and holds no authority. It sits beside `tagging.ts` and `batch.ts`
36
+ * because it is the third consumer of the same tagged data, and it deliberately
37
+ * does not implement {@link ../channels/contract.js Channel}: QUEUE.md notifies
38
+ * nobody and answers nothing.
39
+ *
40
+ * ## B3: computed vs claimed
41
+ *
42
+ * SPEC.md §9: "Every displayed field is one of two kinds and MUST be visibly
43
+ * distinguished". Here that is structural, not typographic: computed fields are
44
+ * rendered as `computed · <source>` lines under **Computed by the runtime**, and
45
+ * claimed fields under a separate **Claimed by `<actor>` (not verified)** block
46
+ * that names its author. A reader skimming the file cannot mistake an agent's
47
+ * cost estimate for a budget verdict, because they are not in the same list.
48
+ *
49
+ * ## The full payload is NOT in this file — flagged for human review
50
+ *
51
+ * SPEC.md §10.4 requires channels to present the full payload before collecting
52
+ * a decision. QUEUE.md collects no decision: it is a read-only summary, and the
53
+ * decision surfaces are the channels (`approval grant`, the web page, Telegram),
54
+ * each of which presents the bytes at decision time. Inlining payloads here
55
+ * would put every pending payload — recipients, bodies, argv — into a file that
56
+ * is regenerated on every event, checked into nothing, and read by anyone with
57
+ * the working directory, with no gesture ever collected from it. So the queue
58
+ * carries the **binding** (`payload_hash`) and points at the channels for the
59
+ * bytes, and the rendered header says exactly that to the human reading it.
60
+ *
61
+ * Which is a different statement from "the renderer cannot see the payload".
62
+ * Since APRV-28 it can: `channels/tagging.ts` reads the payload store beside the
63
+ * log (SPEC.md §6.2's "the payload itself is stored or referenced by the request
64
+ * so channels can display it"), so a request whose bytes were supplied at intake
65
+ * is *summarizable* here and appears in the pending section — which is why this
66
+ * file's pending count now agrees with the queue every channel shows, where
67
+ * before it silently disagreed. The bytes still do not appear in this file.
68
+ * Requests whose material nobody holds remain in {@link QueueRender.skipped},
69
+ * rendered in their own section with the reason, never silently dropped.
70
+ */
71
+ import type { LogHead } from "../core/log.js";
72
+ import { type TagOptions } from "./tagging.js";
73
+ /**
74
+ * Rendering options. A superset of {@link TagOptions} with nothing added: the
75
+ * renderer's inputs are exactly the tagger's inputs, plus `now`, because it
76
+ * derives nothing the tagger does not already derive.
77
+ */
78
+ export type RenderQueueOptions = TagOptions;
79
+ /**
80
+ * Why the renderer refused.
81
+ *
82
+ * The three log codes are `channels/tagging.ts`'s and `core/state.ts`'s,
83
+ * verbatim, so a corrupt log is reported as corruption by every layer with one
84
+ * vocabulary. `write-failed` is {@link writeQueue}'s alone — the renderer itself
85
+ * cannot fail for any other reason, because it is a pure function.
86
+ */
87
+ export declare const RENDER_QUEUE_REFUSAL_CODES: readonly ["log-unreadable", "log-torn-tail", "log-corrupt", "write-failed"];
88
+ export type RenderQueueRefusalCode = (typeof RENDER_QUEUE_REFUSAL_CODES)[number];
89
+ export interface RenderQueueRefusal {
90
+ ok: false;
91
+ code: RenderQueueRefusalCode;
92
+ message: string;
93
+ }
94
+ /** A successful render: the bytes, plus what went into them. */
95
+ export interface QueueRender {
96
+ ok: true;
97
+ /** The complete file contents. Deterministic for a given (log, policy, now). */
98
+ markdown: string;
99
+ /** The chain head the render derives from; `null` for an empty log. */
100
+ head: LogHead | null;
101
+ /** Pending requests rendered, in log order. */
102
+ pending: number;
103
+ /** Live requests that could not be tagged, listed with their reason. */
104
+ skipped: number;
105
+ /** `audit.sampled` events with no later `audit.reviewed`. */
106
+ auditBacklog: number;
107
+ }
108
+ export type RenderQueueResult = QueueRender | RenderQueueRefusal;
109
+ export interface QueueWrite extends Omit<QueueRender, "markdown"> {
110
+ /** Where the file was written. */
111
+ path: string;
112
+ /** Bytes written (UTF-8). */
113
+ bytes: number;
114
+ }
115
+ export type WriteQueueResult = QueueWrite | RenderQueueRefusal;
116
+ /**
117
+ * Render the queue.
118
+ *
119
+ * Deterministic in the strong sense: for a fixed log, policy and `now`, the
120
+ * returned string is byte-identical across processes and machines. Nothing here
121
+ * reads a clock, a locale, an environment variable, or a hostname.
122
+ *
123
+ * Expiry is not this module's judgment: `channels/tagging.ts` derives state
124
+ * through `core/state.ts` with the policy TTL and the same `now`, so a request
125
+ * whose TTL has elapsed is `expired`, not `requested`, and never reaches the
126
+ * pending section. The countdown a reader sees therefore never reaches zero on
127
+ * a listed entry.
128
+ *
129
+ * The verified read happens twice — once here for the head and the audit
130
+ * records, once inside {@link buildPendingQueue} — which is a deliberate trade:
131
+ * a second walk of the chain costs a few milliseconds on a rendering path, and
132
+ * the alternative is widening the tagger's public result to carry records it
133
+ * has no other reason to expose.
134
+ */
135
+ export declare function renderQueue(logPath: string, options: RenderQueueOptions, now: string): RenderQueueResult;
136
+ /**
137
+ * Render and write `.approval/QUEUE.md` atomically.
138
+ *
139
+ * Temp file in the destination directory, `fsync`, then `rename` — a reader
140
+ * either sees the previous complete rendering or the new one, never a half-
141
+ * written queue. The temp name is the only non-deterministic byte anywhere in
142
+ * this module and it never reaches the file's contents; it is removed on every
143
+ * failure path, so a failed render leaves no debris beside the queue.
144
+ *
145
+ * Writes this one file and nothing else. In particular it does not touch the
146
+ * log: the log is opened read-only by the verified read and is not reopened
147
+ * here.
148
+ */
149
+ export declare function writeQueue(logPath: string, queuePath: string, options: RenderQueueOptions, now: string): WriteQueueResult;
@@ -0,0 +1,196 @@
1
+ /**
2
+ * The runtime-side tagger: log + policy in, render-ready request out
3
+ * (SPEC.md §9, §10.3, §10.4).
4
+ *
5
+ * This is the only place a {@link ChannelRequest} is built, and it is on the
6
+ * runtime side of the boundary on purpose. Every `computed` field here is
7
+ * derived by the same modules the gate itself uses — `core/state.ts` for the
8
+ * verified read and the approval derivation, `core/policy-match.ts` /
9
+ * `policy-explain.ts` for autonomy, `core/budgets.ts` for the verdicts,
10
+ * `core/attest.ts` for the attestation status, `core/payload.ts` for the
11
+ * content binding — so what a channel displays as "computed" is the same answer
12
+ * the gate would give, not a second implementation that could disagree with it.
13
+ * Every `claimed` field is copied out of a log record and stamped with the actor
14
+ * who authored that record.
15
+ *
16
+ * ## Why this takes a log *path*, not records
17
+ *
18
+ * SPEC.md §11.1(1): enforcement paths read only verified records. A channel
19
+ * request is what a human's decision is made from, so it is an enforcement
20
+ * surface in the sense that matters — a request built from a spliced log would
21
+ * put a fabricated action in front of an approver. Accepting an
22
+ * `EventRecord[]` would let a caller hand over anything; taking the path and
23
+ * calling {@link readVerifiedRecords} here means the chain is walked, the hashes
24
+ * recomputed and the schemas checked before a single field is tagged, and a log
25
+ * that does not verify produces a refusal rather than a queue entry.
26
+ *
27
+ * ## Where the full payload comes from — flagged for human review
28
+ *
29
+ * v0.1's log records a `payload_hash`, never the payload bytes: the binding is
30
+ * a commitment, and putting the bytes in an append-only log would make every
31
+ * approved payload permanent and world-readable to anyone with the log. So the
32
+ * material to render comes from the payload store beside the log
33
+ * (`core/payload-store.ts`, APRV-28), or from a caller-supplied override
34
+ * ({@link TagOptions.payload}) where an operator holds the bytes somewhere else,
35
+ * and this module **verifies it against the recorded hash** before tagging it
36
+ * `computed` — material that does not hash to the bound value is refused
37
+ * `payload-mismatch` and never reaches a channel. That verification is what
38
+ * makes `fullPayload` a computed field rather than one more agent claim, and it
39
+ * is the same check `core/token.ts` makes at spend time.
40
+ *
41
+ * Determinism: no clock. `now` is a parameter, so a queue rendering is
42
+ * replayable exactly as it was rendered.
43
+ */
44
+ import { type ChannelRequest } from "./contract.js";
45
+ /**
46
+ * Where the payload material for an action comes from.
47
+ *
48
+ * Returns `undefined` when the caller holds no material for that key — which
49
+ * falls back to the payload store, and then, if that holds nothing either, is a
50
+ * refusal for a manual request (§10.4) and merely a missing field otherwise.
51
+ * Never throws: an adapter that cannot produce material says so by returning
52
+ * `undefined`.
53
+ *
54
+ * The bound `payload_hash` is passed as a second argument for sources that are
55
+ * addressed by content rather than by key; sources that key on the action alone
56
+ * ignore it.
57
+ */
58
+ export type PayloadSource = (actionKey: string, payloadHash: string) => unknown;
59
+ export interface TagOptions {
60
+ /** Where `APPROVAL.md` lives. Same semantics as `core/gate.ts`'s option. */
61
+ policy?: {
62
+ dir?: string;
63
+ file?: string;
64
+ };
65
+ /** Schema directory, passed to the verified read and the policy load. */
66
+ schemaDir?: string;
67
+ /**
68
+ * The payload bytes to render, checked against the recorded binding.
69
+ *
70
+ * An **override**: when it is absent, or returns `undefined` for a key, the
71
+ * payload store beside the log answers instead (APRV-28). An operator with a
72
+ * `--payload-dir` therefore still wins for the keys it covers, and gets the
73
+ * store for the rest.
74
+ */
75
+ payload?: PayloadSource;
76
+ /**
77
+ * Where the payload store lives. Defaults to `.approval/payloads/` beside the
78
+ * log, resolved by `core/payload-store.ts` from the log path the caller
79
+ * already passed. `null` disables the fallback entirely, which is what a
80
+ * caller that wants to prove the store is not answering asks for.
81
+ */
82
+ payloadStoreDir?: string | null;
83
+ /**
84
+ * Truncate the rendered payload text at this many characters. Unset means no
85
+ * truncation. A truncated rendering is legal for a unit request and is what
86
+ * `channels/batch.ts` refuses to fold into a batch (SPEC.md §10.3, B7).
87
+ */
88
+ maxPayloadChars?: number;
89
+ }
90
+ /** Why the tagger refused. Frozen per SPEC.md §11.1(6). */
91
+ export declare const CHANNEL_TAG_REFUSAL_CODES: readonly [
92
+ /** The log could not be opened. */
93
+ "log-unreadable",
94
+ /** The log's final line is unterminated (a crashed write). */
95
+ "log-torn-tail",
96
+ /** The chain does not verify; nothing may be rendered from it. */
97
+ "log-corrupt",
98
+ /** No `approval.requested` record for this action key. */
99
+ "not-requested",
100
+ /** There is a request, but it is decided or expired — nothing to approve. */
101
+ "not-awaiting",
102
+ /** The request carries no usable `payload.class`; policy cannot be resolved. */
103
+ "class-missing",
104
+ /** The request carries no `payload_hash`; there is no binding to display. */
105
+ "payload-hash-missing",
106
+ /** No payload material was supplied for a manual request (§10.4). */
107
+ "payload-unavailable",
108
+ /** The supplied material does not hash to the bound `payload_hash`. */
109
+ "payload-mismatch",
110
+ /** The supplied material cannot be canonicalized (a cycle, a NaN, …). */
111
+ "payload-unrenderable",
112
+ /** {@link createChannelRequest} refused the assembled request. */
113
+ "request-invalid",
114
+ /**
115
+ * An attestation prompt's policy bytes are no longer the bytes on disk
116
+ * (APRV-109).
117
+ *
118
+ * Skipped rather than rendered: the hash, the diff and the advisory the
119
+ * proposal recorded all describe a file that has since changed, and a prompt
120
+ * showing them would ask a human to sign for bytes nobody is holding. The
121
+ * repair is to propose the amendment again, which re-derives all three from
122
+ * the file as it now stands.
123
+ */
124
+ "proposal-stale"];
125
+ export type ChannelTagRefusalCode = (typeof CHANNEL_TAG_REFUSAL_CODES)[number];
126
+ export interface ChannelTagRefusal {
127
+ ok: false;
128
+ code: ChannelTagRefusalCode;
129
+ message: string;
130
+ }
131
+ export type BuildChannelRequestResult = {
132
+ ok: true;
133
+ request: ChannelRequest;
134
+ } | ChannelTagRefusal;
135
+ /**
136
+ * Build the render-ready request for one action key.
137
+ *
138
+ * Computed, and what derives each:
139
+ *
140
+ * | field | derivation | `source` |
141
+ * | --- | --- | --- |
142
+ * | `class`, `task`, `payload_hash`, `requested_ts`, `chain`, `state` | the verified log | `log` |
143
+ * | `autonomy`, `provenance` | `explain()` over the attested policy | `policy-match` |
144
+ * | `budgets` | `evaluateBudgetsWithTask()` at `now` | `budgets` |
145
+ * | `attestation` | `checkAttestation()` against the live policy file | `attestation` |
146
+ * | `fullPayload` | supplied material, hash-checked | `payload-binding` |
147
+ * | `command_breakdown`, `protected_path` | the classifier, re-run over the hash-checked material | `classifier` |
148
+ * | `ttl_remaining_ms`, `waiting` | arithmetic on `now` | `clock` |
149
+ *
150
+ * Claimed, and who authored each: `summary` and `est_cost_usd` carry the actor
151
+ * of the `approval.requested` record — the party that submitted the declaration
152
+ * — and `rationale` / `confidence` carry the registering actor.
153
+ *
154
+ * Refuses an unknown or undecidable key rather than rendering a partial one:
155
+ * `not-requested` (no such request), `not-awaiting` (already granted, rejected,
156
+ * revoked or expired), and the payload codes above. An unverifiable log refuses
157
+ * `log-corrupt` before anything is derived.
158
+ */
159
+ export declare function buildChannelRequest(logPath: string, actionKey: string, options: TagOptions, now: string): BuildChannelRequestResult;
160
+ /**
161
+ * `just now`, `4 min ago`, `2h 05m ago` — how long the question has waited.
162
+ *
163
+ * Exported since APRV-216 so the Telegram queue summary states a request's age
164
+ * in the same words the request's own prompt does. One phrasing and one
165
+ * rounding: a summary calling a question `2h 05m` old beside a prompt that
166
+ * calls it something else leaves a reader deciding which of the two the
167
+ * runtime meant.
168
+ */
169
+ export declare function ageText(ms: number): string;
170
+ /** One key the queue could not render, and why. */
171
+ export interface SkippedRequest {
172
+ action_key: string;
173
+ code: ChannelTagRefusalCode;
174
+ message: string;
175
+ }
176
+ export type PendingQueueResult = {
177
+ ok: true;
178
+ /** Every live request awaiting a human decision, in log order. */
179
+ requests: ChannelRequest[];
180
+ /**
181
+ * Keys that are live but could not be rendered — most often a manual
182
+ * request whose payload material the caller does not hold. Surfaced
183
+ * rather than silently dropped: a request missing from a queue is a
184
+ * request nobody will approve, and an operator must be able to see why.
185
+ */
186
+ skipped: SkippedRequest[];
187
+ } | ChannelTagRefusal;
188
+ /**
189
+ * Build the pending queue: every action key with a live `approval.requested`.
190
+ *
191
+ * Order is log order (oldest request first), which is the order SPEC.md §9's
192
+ * queue projection asks for. One verified read serves the whole queue, and one
193
+ * policy load serves every entry, so an entry cannot disagree with its
194
+ * neighbour about what the policy says.
195
+ */
196
+ export declare function buildPendingQueue(logPath: string, options: TagOptions, now: string): PendingQueueResult;