approval-md 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/README.md +63 -24
  2. package/SPEC.md +57 -11
  3. package/dist/src/channels/contract.d.ts +34 -1
  4. package/dist/src/channels/contract.js +200 -7
  5. package/dist/src/channels/contract.js.map +1 -1
  6. package/dist/src/channels/telegram.d.ts +123 -11
  7. package/dist/src/channels/telegram.js +218 -23
  8. package/dist/src/channels/telegram.js.map +1 -1
  9. package/dist/src/channels/web.d.ts +9 -0
  10. package/dist/src/channels/web.js +17 -0
  11. package/dist/src/channels/web.js.map +1 -1
  12. package/dist/src/cli/amend.js +214 -30
  13. package/dist/src/cli/amend.js.map +1 -1
  14. package/dist/src/cli/attest.d.ts +9 -0
  15. package/dist/src/cli/attest.js +134 -7
  16. package/dist/src/cli/attest.js.map +1 -1
  17. package/dist/src/cli/channel-telegram.d.ts +99 -26
  18. package/dist/src/cli/channel-telegram.js +311 -13
  19. package/dist/src/cli/channel-telegram.js.map +1 -1
  20. package/dist/src/cli/channel.d.ts +9 -0
  21. package/dist/src/cli/channel.js +9 -0
  22. package/dist/src/cli/channel.js.map +1 -1
  23. package/dist/src/cli/codex-bridge.d.ts +819 -0
  24. package/dist/src/cli/codex-bridge.js +1607 -0
  25. package/dist/src/cli/codex-bridge.js.map +1 -0
  26. package/dist/src/cli/codex.d.ts +1 -1
  27. package/dist/src/cli/codex.js +304 -7
  28. package/dist/src/cli/codex.js.map +1 -1
  29. package/dist/src/cli/daemon.js +4 -1
  30. package/dist/src/cli/daemon.js.map +1 -1
  31. package/dist/src/cli/doctor.js +467 -12
  32. package/dist/src/cli/doctor.js.map +1 -1
  33. package/dist/src/cli/execute.js +25 -2
  34. package/dist/src/cli/execute.js.map +1 -1
  35. package/dist/src/cli/help.d.ts +6 -2
  36. package/dist/src/cli/help.js +165 -60
  37. package/dist/src/cli/help.js.map +1 -1
  38. package/dist/src/cli/hook-codex.d.ts +49 -1
  39. package/dist/src/cli/hook-codex.js +60 -1
  40. package/dist/src/cli/hook-codex.js.map +1 -1
  41. package/dist/src/cli/hook.d.ts +459 -3
  42. package/dist/src/cli/hook.js +1062 -114
  43. package/dist/src/cli/hook.js.map +1 -1
  44. package/dist/src/cli/import.js +1 -1
  45. package/dist/src/cli/import.js.map +1 -1
  46. package/dist/src/cli/main.js +5 -3
  47. package/dist/src/cli/main.js.map +1 -1
  48. package/dist/src/cli/policy-apply.d.ts +195 -0
  49. package/dist/src/cli/policy-apply.js +573 -0
  50. package/dist/src/cli/policy-apply.js.map +1 -0
  51. package/dist/src/cli/policy.js +14 -1
  52. package/dist/src/cli/policy.js.map +1 -1
  53. package/dist/src/cli/preflight.d.ts +151 -13
  54. package/dist/src/cli/preflight.js +398 -41
  55. package/dist/src/cli/preflight.js.map +1 -1
  56. package/dist/src/cli/sandbox.js +17 -1
  57. package/dist/src/cli/sandbox.js.map +1 -1
  58. package/dist/src/cli/scaffold.d.ts +1 -1
  59. package/dist/src/cli/scaffold.js +1 -1
  60. package/dist/src/cli/setup-channel.d.ts +9 -0
  61. package/dist/src/cli/setup-channel.js +28 -1
  62. package/dist/src/cli/setup-channel.js.map +1 -1
  63. package/dist/src/cli/setup-common.d.ts +3 -1
  64. package/dist/src/cli/setup-common.js +3 -2
  65. package/dist/src/cli/setup-common.js.map +1 -1
  66. package/dist/src/cli/setup.d.ts +2 -0
  67. package/dist/src/cli/setup.js +94 -2
  68. package/dist/src/cli/setup.js.map +1 -1
  69. package/dist/src/cli/up.js +115 -51
  70. package/dist/src/cli/up.js.map +1 -1
  71. package/dist/src/cli/values.js +3 -4
  72. package/dist/src/cli/values.js.map +1 -1
  73. package/dist/src/cli/verb-registry.js +174 -9
  74. package/dist/src/cli/verb-registry.js.map +1 -1
  75. package/dist/src/cli/wordmark.d.ts +2 -2
  76. package/dist/src/cli/wordmark.js +2 -2
  77. package/dist/src/codex/broker.d.ts +229 -0
  78. package/dist/src/codex/broker.js +548 -0
  79. package/dist/src/codex/broker.js.map +1 -0
  80. package/dist/src/codex/runner.d.ts +178 -0
  81. package/dist/src/codex/runner.js +231 -0
  82. package/dist/src/codex/runner.js.map +1 -0
  83. package/dist/src/codex/serve.d.ts +56 -0
  84. package/dist/src/codex/serve.js +98 -0
  85. package/dist/src/codex/serve.js.map +1 -0
  86. package/dist/src/codex/workspace-commit.d.ts +219 -0
  87. package/dist/src/codex/workspace-commit.js +549 -0
  88. package/dist/src/codex/workspace-commit.js.map +1 -0
  89. package/dist/src/core/advance-cycle.d.ts +51 -0
  90. package/dist/src/core/advance-cycle.js +66 -2
  91. package/dist/src/core/advance-cycle.js.map +1 -1
  92. package/dist/src/core/agents-md.d.ts +20 -18
  93. package/dist/src/core/agents-md.js +33 -31
  94. package/dist/src/core/agents-md.js.map +1 -1
  95. package/dist/src/core/attest.d.ts +215 -0
  96. package/dist/src/core/attest.js +317 -7
  97. package/dist/src/core/attest.js.map +1 -1
  98. package/dist/src/core/audit.d.ts +18 -0
  99. package/dist/src/core/audit.js +13 -0
  100. package/dist/src/core/audit.js.map +1 -1
  101. package/dist/src/core/channel-owner.d.ts +213 -0
  102. package/dist/src/core/channel-owner.js +358 -0
  103. package/dist/src/core/channel-owner.js.map +1 -0
  104. package/dist/src/core/command-class.d.ts +154 -0
  105. package/dist/src/core/command-class.js +673 -20
  106. package/dist/src/core/command-class.js.map +1 -1
  107. package/dist/src/core/commit-guard.d.ts +272 -0
  108. package/dist/src/core/commit-guard.js +424 -0
  109. package/dist/src/core/commit-guard.js.map +1 -0
  110. package/dist/src/core/daemon-actor.d.ts +45 -0
  111. package/dist/src/core/daemon-actor.js +54 -0
  112. package/dist/src/core/daemon-actor.js.map +1 -0
  113. package/dist/src/core/dark-session.d.ts +109 -8
  114. package/dist/src/core/dark-session.js +266 -82
  115. package/dist/src/core/dark-session.js.map +1 -1
  116. package/dist/src/core/decision-refusal.d.ts +23 -2
  117. package/dist/src/core/decision-refusal.js +24 -2
  118. package/dist/src/core/decision-refusal.js.map +1 -1
  119. package/dist/src/core/env-file.d.ts +5 -0
  120. package/dist/src/core/env-file.js +60 -1
  121. package/dist/src/core/env-file.js.map +1 -1
  122. package/dist/src/core/execute.d.ts +15 -2
  123. package/dist/src/core/execute.js +15 -2
  124. package/dist/src/core/execute.js.map +1 -1
  125. package/dist/src/core/gate.d.ts +86 -1
  126. package/dist/src/core/gate.js +81 -1
  127. package/dist/src/core/gate.js.map +1 -1
  128. package/dist/src/core/gesture-refusal.d.ts +166 -0
  129. package/dist/src/core/gesture-refusal.js +188 -0
  130. package/dist/src/core/gesture-refusal.js.map +1 -0
  131. package/dist/src/core/harness-version.d.ts +1 -1
  132. package/dist/src/core/harness-version.js +3 -1
  133. package/dist/src/core/harness-version.js.map +1 -1
  134. package/dist/src/core/instance.d.ts +59 -2
  135. package/dist/src/core/instance.js +113 -0
  136. package/dist/src/core/instance.js.map +1 -1
  137. package/dist/src/core/log.d.ts +39 -1
  138. package/dist/src/core/log.js.map +1 -1
  139. package/dist/src/core/policy-explain.d.ts +10 -0
  140. package/dist/src/core/policy-explain.js +32 -0
  141. package/dist/src/core/policy-explain.js.map +1 -1
  142. package/dist/src/core/policy-load.d.ts +41 -1
  143. package/dist/src/core/policy-load.js +21 -3
  144. package/dist/src/core/policy-load.js.map +1 -1
  145. package/dist/src/core/policy-match.d.ts +43 -0
  146. package/dist/src/core/policy-match.js +52 -0
  147. package/dist/src/core/policy-match.js.map +1 -1
  148. package/dist/src/core/policy-proposal.d.ts +52 -0
  149. package/dist/src/core/policy-proposal.js +102 -2
  150. package/dist/src/core/policy-proposal.js.map +1 -1
  151. package/dist/src/core/protected-path-guard.d.ts +117 -4
  152. package/dist/src/core/protected-path-guard.js +362 -48
  153. package/dist/src/core/protected-path-guard.js.map +1 -1
  154. package/dist/src/core/question-preempted.d.ts +141 -0
  155. package/dist/src/core/question-preempted.js +152 -0
  156. package/dist/src/core/question-preempted.js.map +1 -0
  157. package/dist/src/core/read-scope.d.ts +172 -0
  158. package/dist/src/core/read-scope.js +252 -0
  159. package/dist/src/core/read-scope.js.map +1 -0
  160. package/dist/src/core/sandbox.d.ts +81 -0
  161. package/dist/src/core/sandbox.js +190 -1
  162. package/dist/src/core/sandbox.js.map +1 -1
  163. package/dist/src/core/sender-identity.d.ts +476 -0
  164. package/dist/src/core/sender-identity.js +572 -0
  165. package/dist/src/core/sender-identity.js.map +1 -0
  166. package/dist/src/core/shlex.d.ts +102 -0
  167. package/dist/src/core/shlex.js +159 -0
  168. package/dist/src/core/shlex.js.map +1 -0
  169. package/dist/src/core/values.d.ts +18 -8
  170. package/dist/src/core/values.js +36 -1
  171. package/dist/src/core/values.js.map +1 -1
  172. package/dist/src/daemon/advance.d.ts +10 -0
  173. package/dist/src/daemon/advance.js +25 -4
  174. package/dist/src/daemon/advance.js.map +1 -1
  175. package/dist/src/daemon/daemon.js +9 -0
  176. package/dist/src/daemon/daemon.js.map +1 -1
  177. package/dist/src/daemon/git-evidence.d.ts +2 -2
  178. package/dist/src/daemon/git-evidence.js +1 -1
  179. package/dist/src/mcp/server.js +8 -0
  180. package/dist/src/mcp/server.js.map +1 -1
  181. package/docs/cli-reference.md +932 -32
  182. package/docs/codex-enforced-session.md +75 -2
  183. package/docs/codex-workspace-broker.md +118 -0
  184. package/package.json +3 -1
  185. package/schema/event.schema.json +538 -9
  186. package/schema/fixtures/event/invalid/approval-granted-sender-hashed-false.json +20 -0
  187. package/schema/fixtures/event/invalid/approval-granted-sender-hashed-raw-id.json +20 -0
  188. package/schema/fixtures/event/invalid/audit-gesture-refused-human-actor.json +16 -0
  189. package/schema/fixtures/event/invalid/audit-gesture-refused-no-actor-no-sender.json +15 -0
  190. package/schema/fixtures/event/invalid/audit-gesture-refused-unknown-gesture.json +16 -0
  191. package/schema/fixtures/event/invalid/audit-question-preempted-agent-actor.json +16 -0
  192. package/schema/fixtures/event/invalid/audit-question-preempted-no-question-id.json +16 -0
  193. package/schema/fixtures/event/invalid/audit-question-preempted-unknown-source.json +15 -0
  194. package/schema/fixtures/event/invalid/gate-path-signed-off-absolute-path.json +14 -0
  195. package/schema/fixtures/event/invalid/gate-path-signed-off-agent-actor.json +14 -0
  196. package/schema/fixtures/event/invalid/gate-path-signed-off-missing-path.json +13 -0
  197. package/schema/fixtures/event/valid/approval-granted-sender-hashed.json +20 -0
  198. package/schema/fixtures/event/valid/audit-gesture-refused-review-note.json +21 -0
  199. package/schema/fixtures/event/valid/audit-gesture-refused-sender-key-unavailable.json +19 -0
  200. package/schema/fixtures/event/valid/audit-gesture-refused.json +19 -0
  201. package/schema/fixtures/event/valid/audit-question-preempted-no-verdict.json +16 -0
  202. package/schema/fixtures/event/valid/audit-question-preempted.json +20 -0
  203. package/schema/fixtures/event/valid/gate-path-signed-off.json +14 -0
  204. package/schema/fixtures/event/valid/harness-kind-claude-code.json +23 -0
  205. package/schema/fixtures/event/valid/harness-kind-codex.json +23 -0
  206. package/schema/fixtures/event/valid/harness-kind-cursor.json +23 -0
  207. package/schema/fixtures/event/valid/harness-kind-grok.json +23 -0
  208. package/schema/fixtures/event/valid/harness-kind-muse.json +23 -0
  209. package/schema/fixtures/policy/invalid/senders-half-keyed.json +20 -0
  210. package/schema/fixtures/policy/valid/canonical.json +1 -1
  211. package/schema/fixtures/policy/valid/senders-keyed.json +24 -0
  212. package/schema/fixtures/policy-md/valid/canonical.md +1 -1
  213. package/schema/fixtures/policy-md/valid/with-values.md +5 -7
  214. package/schema/fixtures/values/invalid/class-shaped.json +1 -1
  215. package/schema/fixtures/values/invalid/duplicate-entry.json +1 -1
  216. package/schema/fixtures/values/invalid/non-string-item.json +1 -1
  217. package/schema/fixtures/values/invalid/over-cap.json +1 -1
  218. package/schema/fixtures/values/invalid/unknown-key.json +1 -1
  219. package/schema/fixtures/values/invalid/version-float.json +1 -0
  220. package/schema/fixtures/values/invalid/version-integer.json +1 -0
  221. package/schema/fixtures/values/invalid/version-wrong-string.json +1 -0
  222. package/schema/fixtures/values/valid/empty-lists.json +2 -3
  223. package/schema/fixtures/values/valid/full.json +5 -7
  224. package/schema/fixtures/values/valid/minimal.json +1 -1
  225. package/schema/fixtures/values-md/invalid/schema-invalid.md +5 -3
  226. package/schema/fixtures/values-md/invalid/two-blocks.md +3 -3
  227. package/schema/fixtures/values-md/invalid/unterminated.md +2 -2
  228. package/schema/fixtures/values-md/invalid/version-1.md +69 -0
  229. package/schema/fixtures/values-md/invalid/version-unquoted.md +64 -0
  230. package/schema/fixtures/values-md/invalid/yaml-error.md +2 -2
  231. package/schema/fixtures/values-md/valid/absent.md +1 -1
  232. package/schema/fixtures/values-md/valid/with-values.md +5 -7
  233. package/schema/policy.schema.json +54 -2
  234. package/schema/values.schema.json +7 -11
  235. package/schema/fixtures/values/invalid/version-string.json +0 -1
@@ -0,0 +1,573 @@
1
+ /**
2
+ * `approval policy apply <proposal.md>` (APRV-343) — apply a proposal
3
+ * document's quoted replacements to `APPROVAL.md`, then run the amendment.
4
+ *
5
+ * ## The hand-ritual this replaces
6
+ *
7
+ * Agents may not write `APPROVAL.md`: it is `policy.core`, and this repository's
8
+ * policy holds that class human-only. So a policy change an agent proposes
9
+ * travels as a document under `docs/proposals/` that quotes each current line
10
+ * byte for byte beside its replacement, and the human pastes. On 2026-09-16 the
11
+ * paste was done by copying a prepared file over `APPROVAL.md` and running
12
+ * `approval policy amend`. Two things go wrong with that, and both have:
13
+ *
14
+ * - **a whole-file copy reverts what it did not know about.** The prepared file
15
+ * was written against the policy as it stood when the proposal was drafted,
16
+ * so anything landed in between is silently undone. This is the failure
17
+ * `base-policy-diverged` catches for the COMMIT, one step too late to help;
18
+ * - **a paste carries its wrapper.** One paste of a proposal page took the
19
+ * page's own fence with it, which hid a block from the loader (APRV-273).
20
+ *
21
+ * This verb answers both by construction. It never writes bytes that are not
22
+ * anchored to bytes already in the live file, and it reads fences by their
23
+ * backtick run, so a four-backtick wrapper around a three-backtick block is the
24
+ * wrapper it is rather than part of the content.
25
+ *
26
+ * ## The contract, and the three questions APRV-343 left open
27
+ *
28
+ * A proposal is markdown. Anywhere in it, a fenced block whose immediately
29
+ * preceding non-blank line is a LABEL is a member of a pair:
30
+ *
31
+ * - `Current:` — the bytes as they stand in the live policy;
32
+ * - `Replace with:` — what they become;
33
+ * - `Supersedes:` — optional, and the answer to the first open question.
34
+ *
35
+ * **How supersession is declared.** Explicitly, by a third block, and never by
36
+ * position. A later section that rewrites a line an earlier section already
37
+ * rewrote quotes the earlier section's RESULT under a `Supersedes:` label, and
38
+ * the applier looks for that text when the section's own `Current` block is no
39
+ * longer in the file — which is exactly the state the earlier section left
40
+ * behind. Position could not carry this: two sections that touch one line are
41
+ * not in general in the order the file needs, and a rule inferred from order is
42
+ * a rule nobody can read off the page. A pair whose `Current` AND `Supersedes`
43
+ * blocks both occur is ambiguous and refuses, because it means the file
44
+ * contains both spellings and no verb here will pick.
45
+ *
46
+ * **Whether a whole-file replacement is accepted.** No, and that is the
47
+ * decision rather than an omission. The verb's whole value is that every byte it
48
+ * writes is anchored to a byte it proved present, which is what makes a stale
49
+ * proposal a refusal instead of a silent revert. A whole-file blob has no
50
+ * anchor: applying one is a paste with extra steps, and `approval policy amend`
51
+ * over a hand-edited file is already the supported way to do that deliberately.
52
+ *
53
+ * **How the values block is treated.** Identically, and by knowing nothing
54
+ * about it. The applier is a byte-level replacement over the whole file; it
55
+ * does not parse the policy, does not locate blocks, and does not care which
56
+ * fence a pair lands in. The values block is inert (SPEC §11.1 invariant 10),
57
+ * so applying one changes no verdict, and the attestation the amend appends
58
+ * covers the whole file's bytes either way (SPEC §5.2, §5.3). A verb that
59
+ * treated the two blocks differently would be asserting a distinction the file
60
+ * does not make.
61
+ *
62
+ * ## Refuse before writing, always
63
+ *
64
+ * Every pair is resolved against an IN-MEMORY copy, in document order, before a
65
+ * byte reaches the disk. A proposal whose third pair is stale writes nothing at
66
+ * all, so "a stale proposal cannot half-apply" is a property of the code rather
67
+ * than of the order somebody wrote the sections in.
68
+ *
69
+ * ## It publishes, because one command means one command (APRV-360)
70
+ *
71
+ * The amendment runs with `--pr` unless `--no-publish` says otherwise. Before
72
+ * this, `apply` ran the amend with no flag: the policy was written and
73
+ * attested, and the operator was handed a printed procedure for getting it onto
74
+ * main. On 2026-09-18 that procedure opened with `git checkout -b
75
+ * policy-amend-<seq> origin/main`, the primary's main was fourteen commits
76
+ * behind, and the switch refused rather than overwrite `QUEUE.md`, the working
77
+ * log and six payloads. The ceremony stalled with an edited, attested,
78
+ * unpublished policy, which is the one state in which every gated operation on
79
+ * the box refuses.
80
+ *
81
+ * A verb whose whole value is that the edit and its attestation are ONE act has
82
+ * no business stopping one step short of the act being visible. `--no-publish`
83
+ * is the operator who wants it to stop at the commit; `--pr` is accepted and
84
+ * does nothing, because runbooks already say it.
85
+ *
86
+ * ## Human-only
87
+ *
88
+ * `approval policy apply` classifies `policy.core` (`core/command-class.ts`),
89
+ * which this repository's policy holds human-only, so the hook denies an agent
90
+ * that runs it. The verb ALSO refuses an agent identity itself, with
91
+ * `apply-agent-actor`: a lock that exists only in the classifier is a lock that
92
+ * a harness without a hook does not have. It mints no class — `policy.core`
93
+ * already exists and already covers the policy's own machinery (SPEC §11.1
94
+ * invariant 9).
95
+ */
96
+ import { readFileSync, writeFileSync } from "node:fs";
97
+ import { isAbsolute, resolve as resolvePathSegments } from "node:path";
98
+ import { commandPolicyAmend } from "./amend.js";
99
+ import { EXIT_IO, EXIT_OK, EXIT_USAGE } from "./exit-codes.js";
100
+ import { POLICY_APPLY_HELP } from "./help.js";
101
+ import { boolFlag, parseFlags, stringFlag } from "./args.js";
102
+ import { preflightPolicyPath } from "./preflight.js";
103
+ import { readLineFromStdin } from "./prompt.js";
104
+ import { style } from "./style.js";
105
+ import { usageErrorText } from "./usage.js";
106
+ const FLAGS = {
107
+ "--policy": "string",
108
+ "--dir": "string",
109
+ "--log": "string",
110
+ "--as": "string",
111
+ "--yes": "boolean",
112
+ "--dry-run": "boolean",
113
+ "--no-amend": "boolean",
114
+ "--pr": "boolean",
115
+ // APRV-360: the opt-out from the publishing this verb now does by default.
116
+ // It is the same word `policy amend` uses, and it reaches the amend
117
+ // unchanged, so an operator who knows one knows the other.
118
+ "--no-publish": "boolean",
119
+ "--json": "boolean",
120
+ "--help": "boolean",
121
+ "-h": "boolean",
122
+ };
123
+ /** An agent identity, the one `--as` form this verb refuses outright. */
124
+ const AGENT_ACTOR = /^agent:.+/u;
125
+ /**
126
+ * Machine-readable refusal codes. Frozen public API, printed in the help.
127
+ *
128
+ * Distinct by repair, which is the whole test: `proposal-stale` means the live
129
+ * file moved and the proposal has to be rewritten against it; `proposal-
130
+ * ambiguous` means the quoted text is not specific enough and the proposal has
131
+ * to quote more; `proposal-malformed` means the page is not a proposal yet.
132
+ *
133
+ * Two outcomes are deliberately NOT in this union, because neither is this
134
+ * verb's refusal to make:
135
+ *
136
+ * - an operator who answers no at the confirmation gets exit 0 and `aborted:`
137
+ * on stdout, exactly as `policy amend` does. Nothing failed, and an error
138
+ * object at exit 0 would be a contradiction a caller has to resolve;
139
+ * - an amendment that refuses has already printed its OWN code, from its own
140
+ * frozen union. Wrapping it in a second one would put two error objects on
141
+ * one stream and make a caller guess which is the answer. What this verb adds
142
+ * is the sentence naming the state that refusal leaves behind.
143
+ */
144
+ export const POLICY_APPLY_REFUSAL_CODES = [
145
+ "usage",
146
+ "io",
147
+ "apply-agent-actor",
148
+ "proposal-empty",
149
+ "proposal-malformed",
150
+ "proposal-stale",
151
+ "proposal-ambiguous",
152
+ ];
153
+ /**
154
+ * Every fenced block in `text` that declares a language.
155
+ *
156
+ * Two rules, and both are APRV-273:
157
+ *
158
+ * 1. **A fence is closed by a run of AT LEAST as many backticks as opened it.**
159
+ * That is CommonMark's rule and it is the one that makes a four-backtick
160
+ * wrapper work: the inner ```` ```yaml approval-values ```` lines are content,
161
+ * not fences, so the block quoted is the block including its own fences.
162
+ * 2. **An opener with no info string is not a block this verb reads.** The
163
+ * incident was a bare fence swallowing a block whose language nobody
164
+ * declared; a proposal that wants a block applied says what it is.
165
+ */
166
+ export function fencesOf(text) {
167
+ const lines = text.split("\n");
168
+ const found = [];
169
+ let label = "";
170
+ for (let index = 0; index < lines.length; index += 1) {
171
+ const line = lines[index];
172
+ const opener = /^(`{3,})(.*)$/u.exec(line);
173
+ if (opener === null) {
174
+ // The label is the last non-blank line before a fence, so a blank line
175
+ // between the two does not lose it and a paragraph two lines up does not
176
+ // become one.
177
+ if (line.trim().length > 0)
178
+ label = line.trim();
179
+ continue;
180
+ }
181
+ const ticks = opener[1].length;
182
+ const info = opener[2].trim();
183
+ const body = [];
184
+ let closed = false;
185
+ let cursor = index + 1;
186
+ for (; cursor < lines.length; cursor += 1) {
187
+ const candidate = lines[cursor];
188
+ const closer = /^(`{3,})\s*$/u.exec(candidate);
189
+ if (closer !== null && closer[1].length >= ticks) {
190
+ closed = true;
191
+ break;
192
+ }
193
+ body.push(candidate);
194
+ }
195
+ // An unterminated fence is not a block. Reading to the end of the document
196
+ // would turn a typo into a replacement covering half the page.
197
+ if (closed && info.length > 0) {
198
+ found.push({ label, body: body.join("\n"), line: index + 1 });
199
+ }
200
+ label = "";
201
+ index = cursor;
202
+ }
203
+ return found;
204
+ }
205
+ /**
206
+ * The label vocabulary.
207
+ *
208
+ * Canonical spellings are `Current:`, `Replace with:` and `Supersedes:`. The
209
+ * match is on the leading keyword rather than the whole line so a page can say
210
+ * "Section 1's line, which this supersedes:" — which `docs/proposals/
211
+ * approval-md-2026-09.md` does, and which reads better than a bare label on a
212
+ * page a human is meant to understand before any verb does.
213
+ */
214
+ function roleOf(label) {
215
+ const text = label.toLowerCase();
216
+ if (/^current\b/u.test(text))
217
+ return "current";
218
+ if (/^replace[ -]with\b/u.test(text))
219
+ return "replacement";
220
+ if (/supersede/u.test(text))
221
+ return "supersedes";
222
+ return null;
223
+ }
224
+ /**
225
+ * Read a proposal document into ordered pairs.
226
+ *
227
+ * A block whose label names no role is skipped in silence, which is what lets a
228
+ * proposal page carry a `bash` block of commands to run afterwards without the
229
+ * applier mistaking it for policy text.
230
+ */
231
+ export function parseProposal(text) {
232
+ const lines = text.split("\n");
233
+ const headingAt = (line) => {
234
+ for (let index = line - 1; index >= 0; index -= 1) {
235
+ const candidate = (lines[index] ?? "").trim();
236
+ if (/^#{2,6}\s+/u.test(candidate))
237
+ return candidate.replace(/^#+\s+/u, "");
238
+ }
239
+ return "the proposal";
240
+ };
241
+ const pairs = [];
242
+ let current = null;
243
+ let supersedes = null;
244
+ for (const fence of fencesOf(text)) {
245
+ const role = roleOf(fence.label);
246
+ if (role === null)
247
+ continue;
248
+ if (role === "supersedes") {
249
+ if (supersedes !== null) {
250
+ return {
251
+ ok: false,
252
+ code: "proposal-malformed",
253
+ message: `two Supersedes blocks before one Replace with (lines ${String(supersedes.line)} and ${String(fence.line)}); a pair names at most one superseded text, because two would be two claims about which bytes are in the file`,
254
+ };
255
+ }
256
+ supersedes = fence;
257
+ continue;
258
+ }
259
+ if (role === "current") {
260
+ if (current !== null) {
261
+ return {
262
+ ok: false,
263
+ code: "proposal-malformed",
264
+ message: `two Current blocks before one Replace with (lines ${String(current.line)} and ${String(fence.line)}); every Current block needs the replacement that goes with it, or the page is stating a fact rather than proposing a change`,
265
+ };
266
+ }
267
+ current = fence;
268
+ continue;
269
+ }
270
+ if (current === null) {
271
+ return {
272
+ ok: false,
273
+ code: "proposal-malformed",
274
+ message: `the Replace with block at line ${String(fence.line)} has no Current block above it; a replacement with nothing to anchor it is a paste, and this verb writes no byte it has not proved present`,
275
+ };
276
+ }
277
+ pairs.push({
278
+ heading: headingAt(fence.line),
279
+ current: current.body,
280
+ replacement: fence.body,
281
+ supersedes: supersedes === null ? null : supersedes.body,
282
+ line: fence.line,
283
+ });
284
+ current = null;
285
+ supersedes = null;
286
+ }
287
+ if (current !== null) {
288
+ return {
289
+ ok: false,
290
+ code: "proposal-malformed",
291
+ message: `the Current block at line ${String(current.line)} has no Replace with block after it; nothing was applied`,
292
+ };
293
+ }
294
+ if (pairs.length === 0) {
295
+ return {
296
+ ok: false,
297
+ code: "proposal-empty",
298
+ message: "this document declares no Current/Replace with pairs. A pair is two fenced blocks WITH a declared language, each introduced by its label on the line above (`Current:` and `Replace with:`); a fence with no language is skipped, which is the APRV-273 hazard this parser is built around",
299
+ };
300
+ }
301
+ return { ok: true, pairs };
302
+ }
303
+ /** How many times `needle` occurs in `haystack`, counting non-overlapping hits. */
304
+ function occurrences(haystack, needle) {
305
+ if (needle.length === 0)
306
+ return 0;
307
+ let count = 0;
308
+ let at = haystack.indexOf(needle);
309
+ while (at >= 0) {
310
+ count += 1;
311
+ at = haystack.indexOf(needle, at + needle.length);
312
+ }
313
+ return count;
314
+ }
315
+ /**
316
+ * Resolve every pair against an in-memory copy of the policy, in order.
317
+ *
318
+ * Nothing here touches the disk, which is what makes the refusals honest: a
319
+ * proposal is applied whole or not at all, and the caller writes only the text
320
+ * this function returned.
321
+ */
322
+ export function planApply(live, pairs) {
323
+ let text = live;
324
+ const applied = [];
325
+ for (const [index, pair] of pairs.entries()) {
326
+ const ordinal = `pair ${String(index + 1)} (${pair.heading}, line ${String(pair.line)})`;
327
+ const own = occurrences(text, pair.current);
328
+ const carried = pair.supersedes === null ? 0 : occurrences(text, pair.supersedes);
329
+ if (own > 0 && carried > 0) {
330
+ return {
331
+ ok: false,
332
+ code: "proposal-ambiguous",
333
+ message: `${ordinal}: the policy contains BOTH the text this pair quotes as current and the text it says it supersedes. Two spellings of one line is a question about which of them the file means, and no verb here will pick. Nothing was written`,
334
+ };
335
+ }
336
+ const target = own > 0 ? pair.current : carried > 0 ? pair.supersedes : null;
337
+ const hits = own > 0 ? own : carried;
338
+ if (target === null) {
339
+ return {
340
+ ok: false,
341
+ code: "proposal-stale",
342
+ message: `${ordinal}: the text this pair quotes as current is not in the policy${pair.supersedes === null ? "" : ", and neither is the text it says it supersedes"}. The file has moved since the proposal was written, so applying it would write bytes nobody checked against what is there. Nothing was written; rewrite the proposal against the live file`,
343
+ };
344
+ }
345
+ if (hits > 1) {
346
+ return {
347
+ ok: false,
348
+ code: "proposal-ambiguous",
349
+ message: `${ordinal}: the text this pair quotes occurs ${String(hits)} times in the policy, so which one it means is a guess. Nothing was written; quote enough surrounding lines to make it unique`,
350
+ };
351
+ }
352
+ text = text.replace(target, () => pair.replacement);
353
+ applied.push({
354
+ heading: pair.heading,
355
+ matched: target,
356
+ replacement: pair.replacement,
357
+ superseded: own === 0,
358
+ });
359
+ }
360
+ return { ok: true, text, applied };
361
+ }
362
+ // ---------------------------------------------------------------------------
363
+ // The verb
364
+ // ---------------------------------------------------------------------------
365
+ function detail(cause) {
366
+ return cause instanceof Error ? cause.message : String(cause);
367
+ }
368
+ function refuse(streams, json, code, message, exitCode) {
369
+ if (json)
370
+ streams.err(`${JSON.stringify({ ok: false, error: { code, message } })}\n`);
371
+ else
372
+ streams.err(`approval: ${message}\n`);
373
+ return exitCode;
374
+ }
375
+ function usageRefusal(streams, json, message) {
376
+ if (json)
377
+ streams.err(`${JSON.stringify({ ok: false, error: { code: "usage", message } })}\n`);
378
+ else
379
+ streams.err(usageErrorText(message, POLICY_APPLY_HELP));
380
+ return EXIT_USAGE;
381
+ }
382
+ /** Every line of a block, prefixed, so a diff reads as one. */
383
+ function block(text, marker) {
384
+ return text.split("\n").map((line) => `${marker} ${line}`);
385
+ }
386
+ export function commandPolicyApply(argv, streams, cwd) {
387
+ const parsed = parseFlags(argv, FLAGS);
388
+ if (!parsed.ok)
389
+ return usageRefusal(streams, wantsJson(argv), parsed.message);
390
+ const json = boolFlag(parsed.flags, "--json");
391
+ if (boolFlag(parsed.flags, "--help") || boolFlag(parsed.flags, "-h")) {
392
+ streams.out(`${POLICY_APPLY_HELP}\n`);
393
+ return EXIT_OK;
394
+ }
395
+ const proposalArg = parsed.positionals[0];
396
+ if (proposalArg === undefined) {
397
+ return usageRefusal(streams, json, "missing <proposal.md>: the document whose pairs to apply");
398
+ }
399
+ const extra = parsed.positionals[1];
400
+ if (extra !== undefined) {
401
+ return usageRefusal(streams, json, `unexpected argument ${JSON.stringify(extra)}`);
402
+ }
403
+ // APRV-360. `--pr` is the default now, so passing it is a no-op kept for the
404
+ // runbooks that already say it; passing both asks for opposite ceremonies,
405
+ // and the amend refuses that pair in the same words.
406
+ const noPublish = boolFlag(parsed.flags, "--no-publish");
407
+ if (boolFlag(parsed.flags, "--pr") && noPublish) {
408
+ return usageRefusal(streams, json, "--pr and --no-publish ask for opposite ceremonies: --pr opens the pull request (which is what apply does anyway), --no-publish stops at the commit. Pass one of them");
409
+ }
410
+ // Identity first, as the amendment ceremony does it, and for the stricter
411
+ // reason: an agent may not write this file at all, so telling it after it has
412
+ // read a diff would be telling it too late.
413
+ const asFlag = stringFlag(parsed.flags, "--as");
414
+ if (asFlag !== null && AGENT_ACTOR.test(asFlag)) {
415
+ return refuse(streams, json, "apply-agent-actor", `apply is human-only: ${asFlag} may not write the policy file. APPROVAL.md is policy.core and this verb classifies as policy.core too, so the hook denies an agent that runs it; the refusal here is the same lock in a harness that has no hook. Propose the change as a document under docs/proposals/ and let a human run this verb`, EXIT_USAGE);
416
+ }
417
+ const proposalPath = isAbsolute(proposalArg) ? proposalArg : resolvePathSegments(cwd, proposalArg);
418
+ let proposalText;
419
+ try {
420
+ proposalText = readFileSync(proposalPath, "utf8");
421
+ }
422
+ catch (cause) {
423
+ return refuse(streams, json, "io", `the proposal ${proposalPath} could not be read: ${detail(cause)}`, EXIT_IO);
424
+ }
425
+ const policyPath = preflightPolicyPath(stringFlag(parsed.flags, "--policy"), stringFlag(parsed.flags, "--dir"), cwd);
426
+ if (policyPath === null) {
427
+ return refuse(streams, json, "io", `no policy file to apply to: pass --policy <path>, or run this where APPROVAL.md is`, EXIT_IO);
428
+ }
429
+ let live;
430
+ try {
431
+ live = readFileSync(policyPath, "utf8");
432
+ }
433
+ catch (cause) {
434
+ return refuse(streams, json, "io", `the policy ${policyPath} could not be read: ${detail(cause)}`, EXIT_IO);
435
+ }
436
+ const parsedProposal = parseProposal(proposalText);
437
+ if (!parsedProposal.ok) {
438
+ return refuse(streams, json, parsedProposal.code, parsedProposal.message, EXIT_USAGE);
439
+ }
440
+ const plan = planApply(live, parsedProposal.pairs);
441
+ if (!plan.ok)
442
+ return refuse(streams, json, plan.code, plan.message, EXIT_USAGE);
443
+ const dryRun = boolFlag(parsed.flags, "--dry-run");
444
+ if (plan.text === live) {
445
+ // Every pair resolved and the bytes did not move: the proposal has already
446
+ // been applied. A success, and a no-op, and it says which.
447
+ if (json) {
448
+ streams.out(`${JSON.stringify({
449
+ ok: true,
450
+ policy: policyPath,
451
+ proposal: proposalPath,
452
+ pairs: plan.applied.length,
453
+ noop: true,
454
+ dryRun,
455
+ applied: [],
456
+ })}\n`);
457
+ }
458
+ else {
459
+ streams.out(`nothing to apply: all ${String(plan.applied.length)} replacement(s) already read as the proposal asks. ${policyPath} is unchanged\n`);
460
+ }
461
+ return EXIT_OK;
462
+ }
463
+ const st = style({ json });
464
+ if (!json) {
465
+ streams.out(`${st.heading("Applying")}\n`);
466
+ streams.out(` ${st.key("proposal")} ${proposalPath}\n`);
467
+ streams.out(` ${st.key("policy")} ${policyPath}\n\n`);
468
+ for (const [index, entry] of plan.applied.entries()) {
469
+ streams.out(`${st.value(`pair ${String(index + 1)} — ${entry.heading}`)}${entry.superseded ? st.muted(" (matched the text it supersedes)") : ""}\n`);
470
+ for (const line of block(entry.matched, "-"))
471
+ streams.out(` ${st.fail(line)}\n`);
472
+ for (const line of block(entry.replacement, "+"))
473
+ streams.out(` ${st.ok(line)}\n`);
474
+ streams.out("\n");
475
+ }
476
+ }
477
+ if (dryRun) {
478
+ if (json) {
479
+ streams.out(`${JSON.stringify({
480
+ ok: true,
481
+ policy: policyPath,
482
+ proposal: proposalPath,
483
+ pairs: plan.applied.length,
484
+ noop: false,
485
+ dryRun: true,
486
+ applied: plan.applied,
487
+ })}\n`);
488
+ }
489
+ else {
490
+ streams.out("--dry-run: nothing was written and nothing was attested\n");
491
+ }
492
+ return EXIT_OK;
493
+ }
494
+ // The confirmation is for the WRITE. The amendment asks its own, about the
495
+ // semantic diff, and the two are different questions: this one is "are these
496
+ // the bytes", that one is "is this the policy". `--yes` answers both, because
497
+ // an operator who passed it has said so once for the whole ceremony.
498
+ const assumeYes = boolFlag(parsed.flags, "--yes");
499
+ if (!assumeYes) {
500
+ if (json || process.stdin.isTTY !== true) {
501
+ return usageRefusal(streams, json, "apply needs a confirmation it cannot ask for: stdin is not a terminal (or --json was given). Re-run with --yes, or with --dry-run to see the replacements without writing anything");
502
+ }
503
+ streams.out(`write these ${String(plan.applied.length)} replacement(s) to ${policyPath}? [y/N] `);
504
+ const answer = (readLineFromStdin() ?? "").trim().toLowerCase();
505
+ if (answer !== "y" && answer !== "yes") {
506
+ streams.out("aborted: nothing was written and nothing was attested\n");
507
+ return EXIT_OK;
508
+ }
509
+ }
510
+ try {
511
+ writeFileSync(policyPath, plan.text, "utf8");
512
+ }
513
+ catch (cause) {
514
+ return refuse(streams, json, "io", `${policyPath} could not be written: ${detail(cause)}. Nothing was attested`, EXIT_IO);
515
+ }
516
+ if (boolFlag(parsed.flags, "--no-amend")) {
517
+ const owed = noPublish ? "approval policy amend --no-publish" : "approval policy amend --pr";
518
+ if (json) {
519
+ streams.out(`${JSON.stringify({
520
+ ok: true,
521
+ policy: policyPath,
522
+ proposal: proposalPath,
523
+ pairs: plan.applied.length,
524
+ noop: false,
525
+ dryRun: false,
526
+ amended: false,
527
+ owed,
528
+ applied: plan.applied,
529
+ })}\n`);
530
+ }
531
+ else {
532
+ streams.out(`${st.glyph("ok")} ${String(plan.applied.length)} replacement(s) written to ${policyPath}\n`);
533
+ streams.err(`approval: --no-amend, so the policy is EDITED AND UNATTESTED: every gated operation refuses policy-not-attested until you run \`${owed}\`\n`);
534
+ }
535
+ return EXIT_OK;
536
+ }
537
+ // And then the amendment, in this process. The edit and its attestation are
538
+ // one act, so a verb that wrote the file and left the operator to remember
539
+ // the second half would have built the interregnum it exists to close.
540
+ const logFlag = stringFlag(parsed.flags, "--log");
541
+ const amendArgv = [
542
+ ...(stringFlag(parsed.flags, "--policy") === null ? [] : ["--policy", policyPath]),
543
+ ...(stringFlag(parsed.flags, "--dir") === null
544
+ ? []
545
+ : ["--dir", stringFlag(parsed.flags, "--dir")]),
546
+ ...(logFlag === null ? [] : ["--log", logFlag]),
547
+ ...(asFlag === null ? [] : ["--as", asFlag]),
548
+ ...(assumeYes ? ["--yes"] : []),
549
+ ...(json ? ["--json"] : []),
550
+ // APRV-360: the ceremony finishes its own job. This verb exists to be ONE
551
+ // command for the human, and a run that attested the policy and then
552
+ // printed a procedure for publishing it is not one command; on 2026-09-18
553
+ // the procedure it printed was a branch switch the primary refused, and the
554
+ // ceremony stalled with an edited, attested, unpublished policy. So `--pr`
555
+ // is passed unless the operator asked for the quiet form, and `--pr` on
556
+ // this verb's own argv is now a no-op that changes nothing.
557
+ ...(noPublish ? ["--no-publish"] : ["--pr"]),
558
+ ];
559
+ const code = commandPolicyAmend(amendArgv, streams, cwd);
560
+ if (code !== EXIT_OK) {
561
+ // The write happened and the amendment did not. Said plainly, on stderr,
562
+ // because the operator is now holding an edited policy no attestation
563
+ // covers and the next gate operation will tell them so in a worse place.
564
+ const owed = noPublish ? "approval policy amend --no-publish" : "approval policy amend --pr";
565
+ streams.err(`approval: the replacements ARE written to ${policyPath}, and \`approval policy amend\` exited ${String(code)}: the policy is edited and unattested until it succeeds. Fix what it reported and run \`${owed}\`\n`);
566
+ }
567
+ return code;
568
+ }
569
+ /** `--json` before the flags are parsed, so a parse error can honour it. */
570
+ function wantsJson(argv) {
571
+ return argv.includes("--json");
572
+ }
573
+ //# sourceMappingURL=policy-apply.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy-apply.js","sourceRoot":"","sources":["../../../src/cli/policy-apply.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8FG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAEvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE9C,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAiB,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,KAAK,GAA6B;IACtC,UAAU,EAAE,QAAQ;IACpB,OAAO,EAAE,QAAQ;IACjB,OAAO,EAAE,QAAQ;IACjB,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,SAAS;IAClB,WAAW,EAAE,SAAS;IACtB,YAAY,EAAE,SAAS;IACvB,MAAM,EAAE,SAAS;IACjB,2EAA2E;IAC3E,oEAAoE;IACpE,2DAA2D;IAC3D,cAAc,EAAE,SAAS;IACzB,QAAQ,EAAE,SAAS;IACnB,QAAQ,EAAE,SAAS;IACnB,IAAI,EAAE,SAAS;CAChB,CAAC;AAEF,yEAAyE;AACzE,MAAM,WAAW,GAAG,YAAY,CAAC;AAEjC;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,OAAO;IACP,IAAI;IACJ,mBAAmB;IACnB,gBAAgB;IAChB,oBAAoB;IACpB,gBAAgB;IAChB,oBAAoB;CACZ,CAAC;AAkBX;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,KAAK,GAAY,EAAE,CAAC;IAC1B,IAAI,KAAK,GAAG,EAAE,CAAC;IACf,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAW,CAAC;QACpC,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,uEAAuE;YACvE,yEAAyE;YACzE,cAAc;YACd,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;gBAAE,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAChD,SAAS;QACX,CAAC;QACD,MAAM,KAAK,GAAI,MAAM,CAAC,CAAC,CAAY,CAAC,MAAM,CAAC;QAC3C,MAAM,IAAI,GAAI,MAAM,CAAC,CAAC,CAAY,CAAC,IAAI,EAAE,CAAC;QAC1C,MAAM,IAAI,GAAa,EAAE,CAAC;QAC1B,IAAI,MAAM,GAAG,KAAK,CAAC;QACnB,IAAI,MAAM,GAAG,KAAK,GAAG,CAAC,CAAC;QACvB,OAAO,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,EAAE,CAAC;YAC1C,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAW,CAAC;YAC1C,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC/C,IAAI,MAAM,KAAK,IAAI,IAAK,MAAM,CAAC,CAAC,CAAY,CAAC,MAAM,IAAI,KAAK,EAAE,CAAC;gBAC7D,MAAM,GAAG,IAAI,CAAC;gBACd,MAAM;YACR,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACvB,CAAC;QACD,2EAA2E;QAC3E,+DAA+D;QAC/D,IAAI,MAAM,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC;QAChE,CAAC;QACD,KAAK,GAAG,EAAE,CAAC;QACX,KAAK,GAAG,MAAM,CAAC;IACjB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AASD;;;;;;;;GAQG;AACH,SAAS,MAAM,CAAC,KAAa;IAC3B,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IACjC,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/C,IAAI,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,aAAa,CAAC;IAC3D,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,YAAY,CAAC;IACjD,OAAO,IAAI,CAAC;AACd,CAAC;AAkBD;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,SAAS,GAAG,CAAC,IAAY,EAAU,EAAE;QACzC,KAAK,IAAI,KAAK,GAAG,IAAI,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;YAClD,MAAM,SAAS,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YAC9C,IAAI,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC;gBAAE,OAAO,SAAS,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAC7E,CAAC;QACD,OAAO,cAAc,CAAC;IACxB,CAAC,CAAC;IAEF,MAAM,KAAK,GAAmB,EAAE,CAAC;IACjC,IAAI,OAAO,GAAiB,IAAI,CAAC;IACjC,IAAI,UAAU,GAAiB,IAAI,CAAC;IACpC,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACjC,IAAI,IAAI,KAAK,IAAI;YAAE,SAAS;QAC5B,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;YAC1B,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;gBACxB,OAAO;oBACL,EAAE,EAAE,KAAK;oBACT,IAAI,EAAE,oBAAoB;oBAC1B,OAAO,EAAE,wDAAwD,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,gHAAgH;iBACnO,CAAC;YACJ,CAAC;YACD,UAAU,GAAG,KAAK,CAAC;YACnB,SAAS;QACX,CAAC;QACD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;gBACrB,OAAO;oBACL,EAAE,EAAE,KAAK;oBACT,IAAI,EAAE,oBAAoB;oBAC1B,OAAO,EAAE,qDAAqD,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,8HAA8H;iBAC3O,CAAC;YACJ,CAAC;YACD,OAAO,GAAG,KAAK,CAAC;YAChB,SAAS;QACX,CAAC;QACD,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,oBAAoB;gBAC1B,OAAO,EAAE,kCAAkC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,4IAA4I;aAC1M,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,IAAI,CAAC;YACT,OAAO,EAAE,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC;YAC9B,OAAO,EAAE,OAAO,CAAC,IAAI;YACrB,WAAW,EAAE,KAAK,CAAC,IAAI;YACvB,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI;YACxD,IAAI,EAAE,KAAK,CAAC,IAAI;SACjB,CAAC,CAAC;QACH,OAAO,GAAG,IAAI,CAAC;QACf,UAAU,GAAG,IAAI,CAAC;IACpB,CAAC;IAED,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,OAAO;YACL,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,oBAAoB;YAC1B,OAAO,EAAE,6BAA6B,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,0DAA0D;SACrH,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO;YACL,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,gBAAgB;YACtB,OAAO,EACL,4RAA4R;SAC/R,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AAC7B,CAAC;AAoBD,mFAAmF;AACnF,SAAS,WAAW,CAAC,QAAgB,EAAE,MAAc;IACnD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAClC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,EAAE,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAClC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;QACf,KAAK,IAAI,CAAC,CAAC;QACX,EAAE,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,KAA8B;IACpE,IAAI,IAAI,GAAG,IAAI,CAAC;IAChB,MAAM,OAAO,GAAkB,EAAE,CAAC;IAClC,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QAC5C,MAAM,OAAO,GAAG,QAAQ,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,OAAO,UAAU,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;QACzF,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QAElF,IAAI,GAAG,GAAG,CAAC,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YAC3B,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,oBAAoB;gBAC1B,OAAO,EAAE,GAAG,OAAO,iOAAiO;aACrP,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;QAC7E,MAAM,IAAI,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;QACrC,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,gBAAgB;gBACtB,OAAO,EAAE,GAAG,OAAO,8DACjB,IAAI,CAAC,UAAU,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,iDAClC,6LAA6L;aAC9L,CAAC;QACJ,CAAC;QACD,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;YACb,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,oBAAoB;gBAC1B,OAAO,EAAE,GAAG,OAAO,sCAAsC,MAAM,CAAC,IAAI,CAAC,+HAA+H;aACrM,CAAC;QACJ,CAAC;QACD,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC;YACX,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,OAAO,EAAE,MAAM;YACf,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,UAAU,EAAE,GAAG,KAAK,CAAC;SACtB,CAAC,CAAC;IACL,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AACrC,CAAC;AAED,8EAA8E;AAC9E,WAAW;AACX,8EAA8E;AAE9E,SAAS,MAAM,CAAC,KAAc;IAC5B,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC;AAED,SAAS,MAAM,CACb,OAAgB,EAChB,IAAa,EACb,IAA4B,EAC5B,OAAe,EACf,QAAgB;IAEhB,IAAI,IAAI;QAAE,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;;QACjF,OAAO,CAAC,GAAG,CAAC,aAAa,OAAO,IAAI,CAAC,CAAC;IAC3C,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,YAAY,CAAC,OAAgB,EAAE,IAAa,EAAE,OAAe;IACpE,IAAI,IAAI;QAAE,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;;QAC1F,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAC7D,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,+DAA+D;AAC/D,SAAS,KAAK,CAAC,IAAY,EAAE,MAAc;IACzC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAc,EAAE,OAAgB,EAAE,GAAW;IAC9E,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACvC,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,YAAY,CAAC,OAAO,EAAE,SAAS,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAC9E,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC9C,IAAI,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC;QACrE,OAAO,CAAC,GAAG,CAAC,GAAG,iBAAiB,IAAI,CAAC,CAAC;QACtC,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAC1C,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO,YAAY,CAAC,OAAO,EAAE,IAAI,EAAE,0DAA0D,CAAC,CAAC;IACjG,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IACpC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,YAAY,CAAC,OAAO,EAAE,IAAI,EAAE,uBAAuB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACrF,CAAC;IAED,6EAA6E;IAC7E,2EAA2E;IAC3E,qDAAqD;IACrD,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC;IACzD,IAAI,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAChD,OAAO,YAAY,CACjB,OAAO,EACP,IAAI,EACJ,sKAAsK,CACvK,CAAC;IACJ,CAAC;IAED,0EAA0E;IAC1E,8EAA8E;IAC9E,4CAA4C;IAC5C,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAChD,IAAI,MAAM,KAAK,IAAI,IAAI,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAChD,OAAO,MAAM,CACX,OAAO,EACP,IAAI,EACJ,mBAAmB,EACnB,wBAAwB,MAAM,ySAAyS,EACvU,UAAU,CACX,CAAC;IACJ,CAAC;IAED,MAAM,YAAY,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,mBAAmB,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IACnG,IAAI,YAAoB,CAAC;IACzB,IAAI,CAAC;QACH,YAAY,GAAG,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IACpD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,MAAM,CACX,OAAO,EACP,IAAI,EACJ,IAAI,EACJ,gBAAgB,YAAY,uBAAuB,MAAM,CAAC,KAAK,CAAC,EAAE,EAClE,OAAO,CACR,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,mBAAmB,CACpC,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,EACpC,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,EACjC,GAAG,CACJ,CAAC;IACF,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;QACxB,OAAO,MAAM,CACX,OAAO,EACP,IAAI,EACJ,IAAI,EACJ,oFAAoF,EACpF,OAAO,CACR,CAAC;IACJ,CAAC;IACD,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAC1C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,MAAM,CACX,OAAO,EACP,IAAI,EACJ,IAAI,EACJ,cAAc,UAAU,uBAAuB,MAAM,CAAC,KAAK,CAAC,EAAE,EAC9D,OAAO,CACR,CAAC;IACJ,CAAC;IAED,MAAM,cAAc,GAAG,aAAa,CAAC,YAAY,CAAC,CAAC;IACnD,IAAI,CAAC,cAAc,CAAC,EAAE,EAAE,CAAC;QACvB,OAAO,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,cAAc,CAAC,IAAI,EAAE,cAAc,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IACxF,CAAC;IACD,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC;IACnD,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,OAAO,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAEhF,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;IACnD,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QACvB,2EAA2E;QAC3E,2DAA2D;QAC3D,IAAI,IAAI,EAAE,CAAC;YACT,OAAO,CAAC,GAAG,CACT,GAAG,IAAI,CAAC,SAAS,CAAC;gBAChB,EAAE,EAAE,IAAI;gBACR,MAAM,EAAE,UAAU;gBAClB,QAAQ,EAAE,YAAY;gBACtB,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;gBAC1B,IAAI,EAAE,IAAI;gBACV,MAAM;gBACN,OAAO,EAAE,EAAE;aACZ,CAAC,IAAI,CACP,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CACT,yBAAyB,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,sDAAsD,UAAU,iBAAiB,CACtI,CAAC;QACJ,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,EAAE,GAAG,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3B,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,YAAY,IAAI,CAAC,CAAC;QACzD,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,UAAU,MAAM,CAAC,CAAC;QACzD,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;YACpD,OAAO,CAAC,GAAG,CACT,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC,GACzD,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC,CAAC,CAAC,EACtE,IAAI,CACL,CAAC;YACF,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC;gBAAE,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClF,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,CAAC;gBAAE,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACpF,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IAED,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,IAAI,EAAE,CAAC;YACT,OAAO,CAAC,GAAG,CACT,GAAG,IAAI,CAAC,SAAS,CAAC;gBAChB,EAAE,EAAE,IAAI;gBACR,MAAM,EAAE,UAAU;gBAClB,QAAQ,EAAE,YAAY;gBACtB,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;gBAC1B,IAAI,EAAE,KAAK;gBACX,MAAM,EAAE,IAAI;gBACZ,OAAO,EAAE,IAAI,CAAC,OAAO;aACtB,CAAC,IAAI,CACP,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAC;QAC3E,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,2EAA2E;IAC3E,6EAA6E;IAC7E,8EAA8E;IAC9E,qEAAqE;IACrE,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAClD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,IAAI,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;YACzC,OAAO,YAAY,CACjB,OAAO,EACP,IAAI,EACJ,oLAAoL,CACrL,CAAC;QACJ,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,eAAe,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,sBAAsB,UAAU,UAAU,CAAC,CAAC;QAClG,MAAM,MAAM,GAAG,CAAC,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAChE,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YACvC,OAAO,CAAC,GAAG,CAAC,yDAAyD,CAAC,CAAC;YACvE,OAAO,OAAO,CAAC;QACjB,CAAC;IACH,CAAC;IAED,IAAI,CAAC;QACH,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,MAAM,CACX,OAAO,EACP,IAAI,EACJ,IAAI,EACJ,GAAG,UAAU,0BAA0B,MAAM,CAAC,KAAK,CAAC,wBAAwB,EAC5E,OAAO,CACR,CAAC;IACJ,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,YAAY,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,oCAAoC,CAAC,CAAC,CAAC,4BAA4B,CAAC;QAC7F,IAAI,IAAI,EAAE,CAAC;YACT,OAAO,CAAC,GAAG,CACT,GAAG,IAAI,CAAC,SAAS,CAAC;gBAChB,EAAE,EAAE,IAAI;gBACR,MAAM,EAAE,UAAU;gBAClB,QAAQ,EAAE,YAAY;gBACtB,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;gBAC1B,IAAI,EAAE,KAAK;gBACX,MAAM,EAAE,KAAK;gBACb,OAAO,EAAE,KAAK;gBACd,IAAI;gBACJ,OAAO,EAAE,IAAI,CAAC,OAAO;aACtB,CAAC,IAAI,CACP,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CACT,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,8BAA8B,UAAU,IAAI,CAC7F,CAAC;YACF,OAAO,CAAC,GAAG,CACT,mIAAmI,IAAI,MAAM,CAC9I,CAAC;QACJ,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,4EAA4E;IAC5E,2EAA2E;IAC3E,uEAAuE;IACvE,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG;QAChB,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QAClF,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI;YAC5C,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,CAAC,OAAO,EAAE,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAW,CAAC,CAAC;QAC3D,GAAG,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC/C,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC5C,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/B,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3B,0EAA0E;QAC1E,qEAAqE;QACrE,0EAA0E;QAC1E,4EAA4E;QAC5E,2EAA2E;QAC3E,wEAAwE;QACxE,4DAA4D;QAC5D,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;KAC7C,CAAC;IACF,MAAM,IAAI,GAAG,kBAAkB,CAAC,SAAS,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;IACzD,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,yEAAyE;QACzE,sEAAsE;QACtE,yEAAyE;QACzE,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,oCAAoC,CAAC,CAAC,CAAC,4BAA4B,CAAC;QAC7F,OAAO,CAAC,GAAG,CACT,6CAA6C,UAAU,0CAA0C,MAAM,CAAC,IAAI,CAAC,2FAA2F,IAAI,MAAM,CACnN,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,4EAA4E;AAC5E,SAAS,SAAS,CAAC,IAAuB;IACxC,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACjC,CAAC"}
@@ -30,9 +30,11 @@ import { isAbsolute, join, resolve as resolvePathSegments } from "node:path";
30
30
  import { explain, isActionClass } from "../core/policy-explain.js";
31
31
  import { loadPolicy, POLICY_FILENAMES } from "../core/policy-load.js";
32
32
  import { commandPolicyAmend } from "./amend.js";
33
+ import { commandPolicyApply } from "./policy-apply.js";
33
34
  import { boolFlag, parseFlags, stringFlag } from "./args.js";
34
35
  import { commandPolicyAttest } from "./attest.js";
35
36
  import { EXIT_IO, EXIT_OK, EXIT_USAGE } from "./exit-codes.js";
37
+ import { resolveScratchRoots } from "./hook.js";
36
38
  import { POLICY_CHECK_HELP, POLICY_HELP, POLICY_TEST_HELP } from "./help.js";
37
39
  import { relPath, style } from "./style.js";
38
40
  import { usageErrorText } from "./usage.js";
@@ -189,7 +191,13 @@ function runVerb(argv, streams, cwd, helpText) {
189
191
  if (!check.ok)
190
192
  return ioError(streams, json, check.message);
191
193
  }
192
- const explanation = explain(loadPolicy(options), actionClass, reversibleFlag === null ? {} : { reversible: reversibleFlag === "true" });
194
+ const explanation = explain(loadPolicy(options), actionClass, {
195
+ ...(reversibleFlag === null ? {} : { reversible: reversibleFlag === "true" }),
196
+ // APRV-347. Resolved HERE, where the process may touch a disk, so the read
197
+ // scope a reader is shown is the one this machine would actually enforce
198
+ // rather than a description of the key.
199
+ systemReadRoots: resolveScratchRoots(cwd),
200
+ });
193
201
  if (json) {
194
202
  streams.out(`${JSON.stringify(explanation)}\n`);
195
203
  }
@@ -222,6 +230,11 @@ export function commandPolicy(argv, streams, cwd) {
222
230
  // It writes too, and for the same reason it lives in its own file.
223
231
  case "amend":
224
232
  return commandPolicyAmend(rest, streams, cwd);
233
+ // APRV-343. It writes the policy FILE, which nothing else in this CLI does,
234
+ // and then hands over to the amendment above so the edit and its
235
+ // attestation stay one act.
236
+ case "apply":
237
+ return commandPolicyApply(rest, streams, cwd);
225
238
  default:
226
239
  return usageError(streams, wantsJson(argv), `unknown subcommand ${JSON.stringify(sub)} for \`approval policy\``, POLICY_HELP);
227
240
  }