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,281 @@
1
+ /**
2
+ * Class matching and autonomy resolution (SPEC.md §5.2, §7).
3
+ *
4
+ * Given a loaded policy and an action class, decide the autonomy level that
5
+ * governs the action, and say *why* — which rule matched, which rules were
6
+ * considered, and whether the irreversibility floor overrode the match.
7
+ *
8
+ * This module is pure and deterministic: no I/O, no clock, no randomness, no
9
+ * caching. The same `(load, actionClass, options)` always yields a deeply equal
10
+ * `Resolution`. Every ordering decision below is total, so candidate order and
11
+ * winner selection never depend on object key insertion order beyond what is
12
+ * explicitly documented.
13
+ *
14
+ * ## Matching grammar (SPEC.md §5.2, `schema/policy.schema.json` `classPattern`)
15
+ *
16
+ * Patterns and classes are dot-separated segments. Within a pattern:
17
+ *
18
+ * - a literal segment matches exactly that segment;
19
+ * - `*` in a non-final position matches exactly one segment;
20
+ * - a **trailing** `.*` matches **one or more** remaining segments of any depth.
21
+ *
22
+ * The "one or more" is a deliberate choice: `read.*` matches `read.web` and
23
+ * `read.web.page` but **not** the bare class `read`. The schema's
24
+ * `classPattern` admits `read` and `read.*` as two distinct keys a policy may
25
+ * list separately with different autonomy, so they must not be aliases of one
26
+ * another; and §7 introduces `read.*` as a *namespace*, i.e. the things under
27
+ * `read`, not `read` itself. A policy that wants the bare class covered writes
28
+ * it as its own rule (or relies on `defaults.autonomy`).
29
+ *
30
+ * A bare `*` pattern is a single-segment pattern whose only segment is a
31
+ * wildcard, and it is not "trailing `.*`" — it matches any single-segment class
32
+ * (`read`, `deploy`) and nothing deeper. `*.*` is a wildcard followed by a
33
+ * trailing wildcard, so it matches any class of TWO OR MORE segments. (APRV-137
34
+ * corrects this line, which read "exactly two". The trailing `.*` consumes one
35
+ * or more, so `*.*` matches `a.b` and `a.b.c` alike. `matchesPattern` always
36
+ * behaved this way; only the comment was wrong.)
37
+ *
38
+ * ## Specificity (SPEC.md §5.2, Specificity bullet)
39
+ *
40
+ * Candidates are ordered by the two-part key
41
+ * `(literalSegments DESC, wildcardSegments ASC)`. A trailing `.*` counts as one
42
+ * wildcard segment and contributes no literals. Patterns still tied are equally
43
+ * specific, and then "deny beats allow" decides: the strictest autonomy among
44
+ * the tied rules wins.
45
+ *
46
+ * ## Fail-closed
47
+ *
48
+ * ## The autonomy split (amended SPEC.md §5.2, APRV-127)
49
+ *
50
+ * A rule may declare `supervised-live` (with a `live_rate`) or `supervised-retro`
51
+ * as well as the pre-split `supervised`. Both collapse onto the one enforced
52
+ * `supervised` autonomy, and the difference travels beside it as
53
+ * `Resolution.supervision`: `"live"` means a `live_rate` fraction of the class's
54
+ * actions stop at the human gate BEFORE executing, `"retro"` means every action
55
+ * proceeds and a fraction is reviewed AFTERWARDS. Bare `supervised` is `"retro"`,
56
+ * with a load-time note from `policy-load.ts` naming the alias.
57
+ *
58
+ * Nothing about the SELECTION lives here: this module is pure, and selection
59
+ * needs an operator-held secret. `core/sampler.ts` derives it, and `core/gate.ts`
60
+ * applies it at intake.
61
+ *
62
+ * A not-ok {@link PolicyLoadResult} resolves **every** class to `manual` with
63
+ * provenance `"fail-closed"` — see `policy-load.ts`. An absent
64
+ * `defaults.autonomy` is likewise `manual` (provenance `"default"`): the schema
65
+ * permits omitting `defaults`, and the absence of a grant is not a grant.
66
+ *
67
+ * ## `human-only` (amended SPEC.md §5.2, APRV-185)
68
+ *
69
+ * A fourth level, and the strictest: an action reserved to human hands, taken
70
+ * outside agent execution entirely. It resolves like any other level and
71
+ * nothing here refuses anything — this module is pure — but every enforcement
72
+ * path downstream refuses it with the code `class-human-only`, so a resolution
73
+ * carrying it authorizes no request, no decision, no token and no run.
74
+ *
75
+ * The fail-closed target stays `manual` and deliberately does not follow the
76
+ * new head of the strictness table. A policy that cannot be parsed must remain
77
+ * recoverable through its own gate, and a broken file whose every class became
78
+ * `human-only` would put the repair behind a level that admits no gated repair.
79
+ * Failing closed raises the scrutiny an action gets; it does not remove the
80
+ * path by which a human fixes the file.
81
+ */
82
+ import type { Autonomy, DeclaredAutonomy, PolicyClassRule, PolicyLoadResult, SupervisionMode } from "./policy-load.js";
83
+ /** Where a {@link Resolution}'s autonomy came from. */
84
+ export type Provenance =
85
+ /** A `classes` rule matched. */
86
+ "rule"
87
+ /** No rule matched; `defaults.autonomy` (or its absent-means-manual form). */
88
+ | "default"
89
+ /**
90
+ * Amended SPEC.md §5.2 (APRV-266): no rule matched a `policy.edit` sub-class,
91
+ * so the `policy.edit` line itself decided it. Distinct from `"rule"` because
92
+ * the pattern that decided does not MATCH this class — a reader of the trace
93
+ * has to be able to see that the class inherited rather than matched — and
94
+ * distinct from `"default"` because `defaults.autonomy` did not decide it.
95
+ */
96
+ | "inherited"
97
+ /** The policy failed to load; everything is `manual`. */
98
+ | "fail-closed"
99
+ /** The §7 irreversibility floor overrode the resolved autonomy. */
100
+ | "floor";
101
+ /**
102
+ * Specificity key: `[literalSegments, wildcardSegments, totalSegments]`.
103
+ * Ordered on the first two elements only, as literals DESC then wildcards ASC.
104
+ * `totalSegments` is the sum of the other two, so it can never break a tie they
105
+ * did not already break; it is carried for the explain trace, which reports it.
106
+ */
107
+ export type Specificity = [number, number, number];
108
+ /** A rule whose pattern matched the action class, with its specificity key. */
109
+ export interface Candidate {
110
+ pattern: string;
111
+ rule: PolicyClassRule;
112
+ specificity: Specificity;
113
+ }
114
+ /**
115
+ * Outcome of {@link resolve}.
116
+ *
117
+ * `candidates` is every matching rule in descending specificity order, so a
118
+ * decision trace (APRV-12 `explain`) can be rendered without re-deriving the
119
+ * match.
120
+ */
121
+ export interface Resolution {
122
+ autonomy: Autonomy;
123
+ /**
124
+ * Amended SPEC.md §5.2 (APRV-127): what the winning rule (or the default)
125
+ * actually WROTE, before the split was collapsed onto {@link Autonomy}. A
126
+ * reader that wants to echo the policy's own word — `explain`, the amendment
127
+ * differ, a channel's provenance line — uses this; a reader that wants to
128
+ * know what is enforced uses `autonomy`.
129
+ */
130
+ declaredAutonomy: DeclaredAutonomy;
131
+ /**
132
+ * `"live"` or `"retro"` when `autonomy` is `supervised`, `null` otherwise.
133
+ * Bare `supervised` is `"retro"` — see `policy-load.ts`'s alias note.
134
+ */
135
+ supervision: SupervisionMode | null;
136
+ /**
137
+ * The declared `live_rate` for a `supervised-live` class, else `null`.
138
+ *
139
+ * Never `null` for a live class that reached here through the schema, which
140
+ * requires the key. A live class that somehow carries no usable rate resolves
141
+ * to `1` rather than to `null`: see {@link supervisionOf} for why the missing
142
+ * rate is read as "gate all of them".
143
+ */
144
+ liveRate: number | null;
145
+ /**
146
+ * The declared `retro_rate` for a supervised class, else `null` (amended
147
+ * SPEC.md §5.2, APRV-183).
148
+ *
149
+ * `null` is not "do not sample": it is "this class declared no rate of its
150
+ * own", and the retrospective sampler reads it as the instruction to fall back
151
+ * to `audit.supervised_sample_rate`. A rate the schema would have rejected
152
+ * cannot arrive here, and one that somehow does is read as absent, which puts
153
+ * the class back on the global rate rather than on a number nobody wrote.
154
+ */
155
+ retroRate: number | null;
156
+ provenance: Provenance;
157
+ matched: {
158
+ pattern: string;
159
+ rule: PolicyClassRule;
160
+ } | null;
161
+ approvers: string[] | null;
162
+ limits: Record<string, number> | null;
163
+ floorApplied: boolean;
164
+ /** Whether every equally most-specific rule explicitly permits irreversibility. */
165
+ allowIrreversible: boolean;
166
+ /** The maximum-specificity rule group governing that permission. */
167
+ irreversiblePatterns: string[];
168
+ candidates: Candidate[];
169
+ }
170
+ /** Options for {@link resolve}. */
171
+ export interface ResolveOptions {
172
+ /**
173
+ * Whether the action can be undone. `false` engages the SPEC.md §7
174
+ * irreversibility floor; `true` and `undefined` do not.
175
+ */
176
+ reversible?: boolean;
177
+ }
178
+ /**
179
+ * Strictness order, strictest first (SPEC.md §5.2 "deny beats allow"), over the
180
+ * DECLARED vocabulary.
181
+ *
182
+ * `supervised-live` sits between `manual` and the retrospective modes because it
183
+ * is the only supervised mode that can stop an action before it happens: at rate
184
+ * 1 it is `manual`, at any lower rate it is strictly more scrutiny than review
185
+ * after the fact. `supervised` and `supervised-retro` share a rank because they
186
+ * are the same level under two spellings; the lexicographic tie-break in
187
+ * {@link compareCandidates} then decides between two equally specific rules that
188
+ * spell it differently, deterministically and without preferring either word.
189
+ *
190
+ * Exported because `core/policy-explain.ts` needs the same order to name the
191
+ * tie-break in its trace, and a private mirror of this table there is exactly
192
+ * the drift a decision trace must never have from the decision.
193
+ *
194
+ * The RATE is not part of the order. A tie between `supervised-live 0.5` and
195
+ * `supervised-live 0.01` is a tie between two equally specific rules that
196
+ * disagree about a fraction, and ordering by rate would let a policy author move
197
+ * a rule's precedence by editing a number they were only tuning. The
198
+ * lexicographic tie-break settles it, exactly as it settles every other tie.
199
+ *
200
+ * APRV-185 puts `human-only` above `manual` at the head of the table, and this
201
+ * table is the one place the ordering exists: the tie-break below,
202
+ * `core/policy-explain.ts`'s trace, and `core/agents-md.ts`'s draft merge all
203
+ * read it and hold no copy. It sits above `manual` because it is strictly more
204
+ * scrutiny — `manual` says a human decides and an agent then acts, `human-only`
205
+ * says the human acts — so a tie between the two must resolve to the level that
206
+ * lets no agent execute.
207
+ */
208
+ export declare const STRICTNESS: Readonly<Record<DeclaredAutonomy, number>>;
209
+ /**
210
+ * Collapse a declared level onto the enforced {@link Autonomy} plus its
211
+ * supervision mode and live rate.
212
+ *
213
+ * Pure and total. A `supervised-live` rule whose `live_rate` is absent, or is
214
+ * not a usable proportion, resolves to **1** — every action in the class is
215
+ * gated. `policy.schema.json` requires the key, so this branch is unreachable
216
+ * for a policy that loaded; it is here as the fail-closed backstop, and the
217
+ * direction is the one the rest of this runtime takes everywhere else. The
218
+ * alternative reading, "a rate we could not understand means gate none of them",
219
+ * would turn a typo into a silently disabled control, which is the failure this
220
+ * project exists to prevent.
221
+ *
222
+ * `human-only` (APRV-185) collapses onto itself carrying nothing, exactly as
223
+ * `manual` and `autonomous` do. It names no supervision because it describes no
224
+ * agent execution to supervise, and the schema forbids both rates on it, so
225
+ * there is no fraction here for a reader to misread as live.
226
+ */
227
+ export declare function supervisionOf(declared: DeclaredAutonomy, rule: PolicyClassRule | null): {
228
+ autonomy: Autonomy;
229
+ supervision: SupervisionMode | null;
230
+ liveRate: number | null;
231
+ retroRate: number | null;
232
+ };
233
+ /**
234
+ * The refusal every enforcement path prints for a `human-only` class (APRV-185).
235
+ *
236
+ * One text, in one place, for the same reason `STRICTNESS` is one table: the
237
+ * code `class-human-only` is frozen in four separate unions (`core/gate.ts`,
238
+ * `core/token.ts`, `core/execute.ts`, and the hook's own), and four hand-written
239
+ * explanations of one condition would disagree about what a caller should do the
240
+ * first time one of them was edited.
241
+ *
242
+ * `whatWasRefused` is the verb's own half of the sentence, so the message names
243
+ * the thing that did not happen as well as the reason it cannot.
244
+ */
245
+ export declare function humanOnlyRefusal(actionClass: string, whatWasRefused: string): string;
246
+ /**
247
+ * Does `pattern` match `actionClass`?
248
+ *
249
+ * See the module header for the grammar. Both are split on `.`; the pattern's
250
+ * final segment is the only one that may span more than one class segment, and
251
+ * only when the pattern has more than one segment (a bare `*` is single-span).
252
+ */
253
+ export declare function matchesPattern(pattern: string, actionClass: string): boolean;
254
+ /**
255
+ * Specificity key of a pattern (SPEC.md §5.2). A trailing `.*` is one wildcard
256
+ * segment contributing no literals — which is exactly how it is already counted
257
+ * by segment splitting, so no special case is needed here.
258
+ */
259
+ export declare function specificityOf(pattern: string): Specificity;
260
+ /**
261
+ * Resolve the autonomy governing `actionClass` under `load`.
262
+ *
263
+ * 1. A not-ok load resolves `manual` / `"fail-closed"` for every class.
264
+ * 2. Otherwise collect matching `classes` rules, order them by specificity, and
265
+ * take the most specific; among a full specificity tie the strictest
266
+ * autonomy wins, and among equally strict tied rules the lexicographically
267
+ * smallest pattern is chosen so the outcome is deterministic.
268
+ * 3. With no matching rule, a class in the `policy.edit.*` namespace (APRV-266)
269
+ * inherits the `policy.edit` line when that line is a rule, with provenance
270
+ * `"inherited"`; otherwise the result is `defaults.autonomy` — or `manual`
271
+ * when `defaults` or `defaults.autonomy` is absent — with provenance
272
+ * `"default"`.
273
+ * 4. Finally, `options.reversible === false` engages the §7 floor: a resolved
274
+ * `autonomous` or `supervised` becomes `manual` with `floorApplied: true`
275
+ * and provenance `"floor"`. An already-`manual` outcome is untouched, and
276
+ * keeps its original provenance, because the floor did not decide it.
277
+ *
278
+ * `approvers` and `limits` are carried from the matched rule only; they are
279
+ * `null` when the rule omits them or when no rule matched.
280
+ */
281
+ export declare function resolve(load: PolicyLoadResult, actionClass: string, options?: ResolveOptions): Resolution;
@@ -264,6 +264,8 @@ const FAIL_CLOSED = {
264
264
  approvers: null,
265
265
  limits: null,
266
266
  floorApplied: false,
267
+ allowIrreversible: false,
268
+ irreversiblePatterns: [],
267
269
  candidates: [],
268
270
  };
269
271
  /**
@@ -365,6 +367,8 @@ function fromDefaults(load, candidates) {
365
367
  approvers: null,
366
368
  limits: null,
367
369
  floorApplied: false,
370
+ allowIrreversible: false,
371
+ irreversiblePatterns: [],
368
372
  candidates,
369
373
  };
370
374
  }
@@ -379,9 +383,11 @@ function fromRules(candidates) {
379
383
  // lexicographically smallest strictest — deterministic regardless of the
380
384
  // policy file's key order.
381
385
  let winner = best;
386
+ const governing = [];
382
387
  for (const candidate of candidates) {
383
388
  if (compareSpecificity(candidate.specificity, best.specificity) !== 0)
384
389
  break;
390
+ governing.push(candidate);
385
391
  if (STRICTNESS[candidate.rule.autonomy] < STRICTNESS[winner.rule.autonomy]) {
386
392
  winner = candidate;
387
393
  }
@@ -394,23 +400,26 @@ function fromRules(candidates) {
394
400
  approvers: winner.rule.approvers ?? null,
395
401
  limits: winner.rule.limits ?? null,
396
402
  floorApplied: false,
403
+ // APRV-317: lexicographic order and strictness still select the ordinary
404
+ // winner, but neither may silently discard a tied rule's refusal to waive
405
+ // the floor. The capability is the intersection of the governing group.
406
+ allowIrreversible: governing.every((candidate) => candidate.rule.allow_irreversible === true),
407
+ irreversiblePatterns: governing.map((candidate) => candidate.pattern),
397
408
  candidates,
398
409
  };
399
410
  }
400
411
  /**
401
412
  * SPEC.md §7 irreversibility floor, applied *after* class resolution: an action
402
- * declared `reversible: false` MUST NOT execute under `autonomous` or
403
- * `supervised`. Retrospective sampling cannot undo an irreversible action, so
404
- * the floor resolves to `manual` and records that it, rather than the matched
405
- * rule, determined the outcome.
413
+ * declared `reversible: false` resolves to `manual` unless the attested class
414
+ * rule group explicitly permits it. When no permission exists, the resolution
415
+ * records that the floor, rather than the matched rule, determined the outcome.
406
416
  *
407
417
  * ## The floor is a floor, not a proof (amended SPEC.md §7, APRV-127)
408
418
  *
409
- * This is the enforcement point for APRV-127's rule that `supervised-retro`
410
- * REFUSES an action declaring `reversible: false`: such an action never resolves
411
- * supervised at all, in either mode, so there is no retrospective path for it to
412
- * take. Retrospective review of an irreversible action is regret with a paper
413
- * trail, and the grammar must not offer it.
419
+ * This remains the enforcement point for APRV-127's default: a supervised rule
420
+ * with no APRV-317 opt-in sends a truthful irreversible action to manual. The
421
+ * only exception is the unanimous capability computed from operator policy
422
+ * above; action metadata has no field that can supply it.
414
423
  *
415
424
  * What the floor is NOT is evidence that anything else is reversible.
416
425
  * `reversible` is SELF-REPORTED by the action's own declaration. A truthful
@@ -445,6 +454,8 @@ function applyFloor(resolution, options) {
445
454
  return resolution;
446
455
  if (resolution.autonomy === "manual" || resolution.autonomy === "human-only")
447
456
  return resolution;
457
+ if (resolution.allowIrreversible)
458
+ return resolution;
448
459
  return {
449
460
  ...resolution,
450
461
  autonomy: "manual",
@@ -1 +1 @@
1
- {"version":3,"file":"policy-match.js","sourceRoot":"","sources":["../../../src/core/policy-match.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"policy-match.js","sourceRoot":"","sources":["../../../src/core/policy-match.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,265 @@
1
+ /**
2
+ * The attestation ceremony, collected through a channel (APRV-109, amended
3
+ * SPEC.md §10.1/§10.3/§11).
4
+ *
5
+ * ## The problem
6
+ *
7
+ * Attestation is human-only (`core/attest.ts`), and identity is
8
+ * config-declared, so the two policy ceremonies — `policy attest` and `policy
9
+ * amend` — required the human to be at a terminal. Every other decision in this
10
+ * system had already been reduced to a tap on a phone; the one act that decides
11
+ * which rules are in force had not. This module is the missing half: an agent
12
+ * prepares the policy edit and appends a *proposal*, a channel puts it in front
13
+ * of the approver like any other manual prompt, and the tap appends the
14
+ * `policy.updated` attestation under the human identity the listener holds,
15
+ * exactly as a grant lands today.
16
+ *
17
+ * ## What is computed, and why it has to be
18
+ *
19
+ * A proposal carries three things a channel renders: the SHA-256 of the policy
20
+ * file's exact bytes, the semantic diff of what those bytes change about class
21
+ * resolution, and the load advisory. **All three are derived here from the
22
+ * bytes** — none is accepted from the proposing agent. {@link ProposeInput}
23
+ * has no field for a hash, a diff or a verdict, so the refusal of a
24
+ * caller-authored value is structural in the same way the refusal of a
25
+ * caller-supplied `ts` is (amended SPEC.md §8, A2). An agent that could author
26
+ * the diff summary could show an approver one story and attest another file.
27
+ *
28
+ * The BASELINE the diff is taken against is the one place a caller supplies
29
+ * material, and it is checked rather than trusted: bytes whose own SHA-256 is
30
+ * not the latest attested hash are refused as a baseline and the proposal falls
31
+ * to hash-only mode. That is `cli/amend.ts`'s rule ("a baseline nobody can
32
+ * verify is not a baseline"), enforced here so every caller inherits it.
33
+ *
34
+ * ## Fail closed, in this order
35
+ *
36
+ * - A policy file that cannot be read proposes nothing.
37
+ * - A live file that already matches its attestation proposes nothing: there is
38
+ * no amendment to sign, and a prompt for one would ask a human to re-attest
39
+ * bytes already in force.
40
+ * - A rendered diff larger than {@link ATTESTATION_DIFF_MAX_CHARS} REFUSES
41
+ * (`diff-too-large`) rather than truncating. A phone that shows two thirds of
42
+ * a policy change collects a signature for the third it did not show, and the
43
+ * repair — read it at a terminal and run `approval policy amend` there — is a
44
+ * real repair rather than a smaller lie.
45
+ * - A tap whose proposal's bytes are no longer the bytes on disk refuses
46
+ * `proposal-stale` and attests nothing. The hash the human was shown is the
47
+ * hash that gets attested, or nothing does.
48
+ * - A decline, a supersession and a lapsed deadline all attest nothing. Only
49
+ * {@link decideAttestation}`(…, "attest", …)` ever appends a `policy.updated`.
50
+ *
51
+ * Everything that writes here goes through `core/log.ts`'s `appendEvent` with a
52
+ * compare-and-append precondition and a runtime-assigned timestamp, so the new
53
+ * records inherit the same write-boundary discipline as the rest of the gate.
54
+ */
55
+ import { type ClockOptions } from "./clock.js";
56
+ import { type AppendOptions, type EventRecord } from "./log.js";
57
+ import type { GateRefusal } from "./gate.js";
58
+ /**
59
+ * The event an agent appends to ask for an attestation.
60
+ *
61
+ * Named `proposed` rather than `requested` deliberately: `approval.requested`
62
+ * is the approval lifecycle, with its own TTL, its own budgets and its own
63
+ * grant. This is a different question with a different answer event, and giving
64
+ * it the same word would invite a reader — or a projection — to treat a policy
65
+ * attestation as an ordinary authorization.
66
+ */
67
+ export declare const PROPOSAL_EVENT = "policy.proposed";
68
+ /** The event a decline appends. An attestation appends `policy.updated`. */
69
+ export declare const DECLINE_EVENT = "policy.declined";
70
+ /**
71
+ * The idempotency key an attestation prompt is rendered under.
72
+ *
73
+ * `policy.attest:<sha256>` — derived from the proposed bytes and from nothing
74
+ * else, so two proposals of the same policy text carry the same key and a tap
75
+ * on either answers the same question. It is also what
76
+ * `channels/contract.ts` routes on: a gesture whose key starts with this prefix
77
+ * becomes an attestation, never a grant.
78
+ */
79
+ export declare const ATTESTATION_KEY_PREFIX = "policy.attest:";
80
+ /** The action class an attestation prompt is resolved and rendered under. */
81
+ export declare const ATTESTATION_CLASS = "policy.edit";
82
+ /** `policy.attest:<sha256>` for the given policy bytes. */
83
+ export declare function attestationActionKey(sha256: string): string;
84
+ /** Is this an attestation prompt's key rather than an ordinary action's? */
85
+ export declare function isAttestationActionKey(actionKey: string): boolean;
86
+ /** The proposed policy hash named by an attestation key, or `null`. */
87
+ export declare function attestationKeySha256(actionKey: string): string | null;
88
+ /**
89
+ * The largest rendered diff a channel prompt may carry, in characters.
90
+ *
91
+ * Telegram's hard limit is 4096 characters for one message and the prompt
92
+ * carries a dozen other lines besides the diff, so this is the budget the
93
+ * *smallest* supported channel can show whole. It is a refusal threshold and
94
+ * never a truncation point: see the module header.
95
+ */
96
+ export declare const ATTESTATION_DIFF_MAX_CHARS = 2400;
97
+ /** The same bound, in lines: a diff nobody will scroll is a diff nobody reads. */
98
+ export declare const ATTESTATION_DIFF_MAX_LINES = 60;
99
+ /** The load advisory a channel renders beside the diff. */
100
+ export interface LoadAdvisory {
101
+ ok: boolean;
102
+ /** The load failure's machine-readable code, or `null` on a clean load. */
103
+ code: string | null;
104
+ message: string | null;
105
+ }
106
+ /** The semantic diff summary a channel renders, as the log records it. */
107
+ export interface DiffSummary {
108
+ /** False in hash-only mode: no verifiable baseline, so no semantic diff. */
109
+ available: boolean;
110
+ /** Why the diff is unavailable; `null` when it is available. */
111
+ reason: string | null;
112
+ /** The rendered diff, line by line, exactly as a channel prints it. */
113
+ lines: string[];
114
+ /** The one-line headline (`3 class resolution(s), 1 default(s)`). */
115
+ headline: string;
116
+ /** The SHA-256 of the baseline the diff was taken against, when there was one. */
117
+ baseline_sha256: string | null;
118
+ }
119
+ /** What {@link proposeAttestation} was asked to propose. */
120
+ export interface ProposeInput {
121
+ /** The policy file whose bytes are being proposed. */
122
+ policyPath: string;
123
+ /**
124
+ * The previously-attested policy TEXT, for the semantic diff.
125
+ *
126
+ * Checked, never trusted: bytes whose SHA-256 is not the latest attestation's
127
+ * are refused as a baseline and the proposal falls to hash-only mode with the
128
+ * reason recorded. Callers recover them from `HEAD:<path>` (`cli/amend.ts`);
129
+ * a caller with nothing to offer passes nothing.
130
+ */
131
+ baseline?: Uint8Array | null;
132
+ /**
133
+ * The proposer's own words about the amendment. CLAIMED, and rendered as
134
+ * such: it is the one field on the prompt the runtime does not stand behind.
135
+ */
136
+ note?: string;
137
+ /**
138
+ * When the proposing process stops waiting (RFC 3339). Display only, and it
139
+ * can only raise urgency: see `ChannelRequest.waiting`.
140
+ */
141
+ waitUntil?: string;
142
+ }
143
+ /** Options for both verbs: the append's, plus the clock and the schema dir. */
144
+ export interface ProposalOptions extends ClockOptions {
145
+ schemaDir?: string;
146
+ append?: AppendOptions;
147
+ /** Where the full policy text is stored for the channel to display. */
148
+ payloadStoreDir?: string;
149
+ }
150
+ export type ProposeResult = {
151
+ ok: true;
152
+ record: EventRecord;
153
+ sha256: string;
154
+ diff: DiffSummary;
155
+ load: LoadAdvisory;
156
+ } | GateRefusal;
157
+ export type AttestationDecision = "attest" | "decline";
158
+ export type DecideAttestationResult = {
159
+ ok: true;
160
+ decision: AttestationDecision;
161
+ record: EventRecord;
162
+ sha256: string;
163
+ } | GateRefusal;
164
+ /**
165
+ * The policy file a proposal concerns, discovered the way `loadPolicy` does so
166
+ * the proposed file and the enforced file are never two different files.
167
+ */
168
+ export declare function proposalPolicyPath(dir: string): string;
169
+ /** The load advisory for policy text, as a channel renders it. */
170
+ export declare function adviseLoad(policyPath: string, text: string, schemaDir?: string): LoadAdvisory;
171
+ /** The rendered size of a diff summary, as the channel budget counts it. */
172
+ export declare function diffSize(summary: DiffSummary): {
173
+ chars: number;
174
+ lines: number;
175
+ };
176
+ /**
177
+ * The semantic diff between the attested baseline and the proposed bytes.
178
+ *
179
+ * Hash-only mode (`available: false`) whenever the baseline cannot be *proved*
180
+ * to be the attested text: no baseline was offered, none was ever attested, or
181
+ * the offered bytes hash to something other than the attestation. The reason is
182
+ * recorded so a channel can print it instead of a diff, which is the honest
183
+ * rendering of "we cannot show you what this changes".
184
+ */
185
+ export declare function summarizeDiff(policyPath: string, baseline: Uint8Array | null | undefined, live: Uint8Array, attestedSha256: string | null, schemaDir?: string): DiffSummary;
186
+ /** The stored value a channel displays as the prompt's full payload. */
187
+ export declare function proposalPayloadValue(policyPath: string, text: string): Record<string, unknown>;
188
+ /**
189
+ * Append a `policy.proposed` asking a human to attest `policyPath`'s bytes.
190
+ *
191
+ * The record's payload is the prompt: `sha256`, `diff` and `load` are all
192
+ * derived here, `note` and `wait_until` are the proposer's and are labelled
193
+ * claimed by every channel. `payload_hash` binds the full policy text stored
194
+ * beside the log, so the approver can read the whole file rather than only the
195
+ * summary (SPEC.md §10.4).
196
+ *
197
+ * No attestation is required to append one. That is the point of the verb: the
198
+ * live policy is mid-amendment and therefore unattested, which is exactly the
199
+ * state in which every other gate operation refuses. This one asks a human to
200
+ * end that state.
201
+ */
202
+ export declare function proposeAttestation(logPath: string, input: ProposeInput, actor: string, options?: ProposalOptions): ProposeResult;
203
+ /** What became of a proposal, derived from the log and from nothing else. */
204
+ export type ProposalState =
205
+ /** Nobody has answered, nothing supersedes it, and its deadline has not passed. */
206
+ "open"
207
+ /** A human attested these bytes: a `policy.updated` carries the same hash. */
208
+ | "attested"
209
+ /** A human declined: a `policy.declined` names this proposal. */
210
+ | "declined"
211
+ /** A newer proposal for the same policy path replaced it. */
212
+ | "superseded"
213
+ /** Its `wait_until` passed with no answer. Nothing was attested. */
214
+ | "expired";
215
+ export interface ProposalDerivation {
216
+ seq: number;
217
+ actionKey: string;
218
+ sha256: string;
219
+ state: ProposalState;
220
+ record: EventRecord;
221
+ }
222
+ /** Every `policy.proposed` record in the log, in append order. */
223
+ export declare function proposalRecords(records: readonly EventRecord[]): EventRecord[];
224
+ /**
225
+ * Derive one proposal's state at `now`.
226
+ *
227
+ * Terminal states are read off the log; `expired` is arithmetic on the
228
+ * proposer's own `wait_until` and materialises no event, because a lapsed
229
+ * attestation prompt has nothing to record: nothing was attested, and the
230
+ * proposal record already says everything a reader needs. That is the
231
+ * fail-closed reading — a prompt nobody answered leaves the policy exactly as
232
+ * unattested as it was.
233
+ */
234
+ export declare function proposalState(records: readonly EventRecord[], seq: number, now: string): ProposalDerivation | null;
235
+ /** Every proposal still awaiting a human answer at `now`, in log order. */
236
+ export declare function openProposals(records: readonly EventRecord[], now: string): ProposalDerivation[];
237
+ /** The open proposal whose bytes hash to `sha256`, or `null`. */
238
+ export declare function openProposalFor(records: readonly EventRecord[], sha256: string, now: string): ProposalDerivation | null;
239
+ /** Options for {@link decideAttestation}. */
240
+ export interface DecideAttestationOptions extends ProposalOptions {
241
+ /** The approver's free-text note, recorded on the answer. */
242
+ note?: string;
243
+ /** Where `APPROVAL.md` lives, when it is not the proposal's own directory. */
244
+ policyPath?: string;
245
+ /** The channel delivery id this gesture answered, for audit. */
246
+ batchDeliveryId?: string;
247
+ }
248
+ /**
249
+ * Answer a proposal: attest the bytes the prompt displayed, or decline them.
250
+ *
251
+ * Human-only, in code — the same rule `core/attest.ts` enforces, restated here
252
+ * because this is a second door onto the same act and the rule must hold for
253
+ * every caller and not only for the CLI's.
254
+ *
255
+ * **The attested hash is the hash the prompt displayed.** Before anything is
256
+ * appended the live file is re-read and re-hashed, and any difference refuses
257
+ * `proposal-stale`: the human signed for bytes they were shown, and bytes that
258
+ * changed underneath the prompt are a different policy that has to be proposed
259
+ * again. This is the one check that makes "the phone shows the diff and the
260
+ * hash" mean anything.
261
+ *
262
+ * A decline appends `policy.declined` and attests nothing. So does a lapsed
263
+ * deadline, by appending nothing at all.
264
+ */
265
+ export declare function decideAttestation(logPath: string, seq: number, decision: AttestationDecision, actor: string, options?: DecideAttestationOptions): DecideAttestationResult;