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.
- package/README.md +63 -24
- package/SPEC.md +57 -11
- package/dist/src/channels/contract.d.ts +34 -1
- package/dist/src/channels/contract.js +200 -7
- package/dist/src/channels/contract.js.map +1 -1
- package/dist/src/channels/telegram.d.ts +123 -11
- package/dist/src/channels/telegram.js +218 -23
- package/dist/src/channels/telegram.js.map +1 -1
- package/dist/src/channels/web.d.ts +9 -0
- package/dist/src/channels/web.js +17 -0
- package/dist/src/channels/web.js.map +1 -1
- package/dist/src/cli/amend.js +214 -30
- package/dist/src/cli/amend.js.map +1 -1
- package/dist/src/cli/attest.d.ts +9 -0
- package/dist/src/cli/attest.js +134 -7
- package/dist/src/cli/attest.js.map +1 -1
- package/dist/src/cli/channel-telegram.d.ts +99 -26
- package/dist/src/cli/channel-telegram.js +311 -13
- package/dist/src/cli/channel-telegram.js.map +1 -1
- package/dist/src/cli/channel.d.ts +9 -0
- package/dist/src/cli/channel.js +9 -0
- package/dist/src/cli/channel.js.map +1 -1
- package/dist/src/cli/codex-bridge.d.ts +819 -0
- package/dist/src/cli/codex-bridge.js +1607 -0
- package/dist/src/cli/codex-bridge.js.map +1 -0
- package/dist/src/cli/codex.d.ts +1 -1
- package/dist/src/cli/codex.js +304 -7
- package/dist/src/cli/codex.js.map +1 -1
- package/dist/src/cli/daemon.js +4 -1
- package/dist/src/cli/daemon.js.map +1 -1
- package/dist/src/cli/doctor.js +467 -12
- package/dist/src/cli/doctor.js.map +1 -1
- package/dist/src/cli/execute.js +25 -2
- package/dist/src/cli/execute.js.map +1 -1
- package/dist/src/cli/help.d.ts +6 -2
- package/dist/src/cli/help.js +165 -60
- package/dist/src/cli/help.js.map +1 -1
- package/dist/src/cli/hook-codex.d.ts +49 -1
- package/dist/src/cli/hook-codex.js +60 -1
- package/dist/src/cli/hook-codex.js.map +1 -1
- package/dist/src/cli/hook.d.ts +459 -3
- package/dist/src/cli/hook.js +1062 -114
- package/dist/src/cli/hook.js.map +1 -1
- package/dist/src/cli/import.js +1 -1
- package/dist/src/cli/import.js.map +1 -1
- package/dist/src/cli/main.js +5 -3
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/cli/policy-apply.d.ts +195 -0
- package/dist/src/cli/policy-apply.js +573 -0
- package/dist/src/cli/policy-apply.js.map +1 -0
- package/dist/src/cli/policy.js +14 -1
- package/dist/src/cli/policy.js.map +1 -1
- package/dist/src/cli/preflight.d.ts +151 -13
- package/dist/src/cli/preflight.js +398 -41
- package/dist/src/cli/preflight.js.map +1 -1
- package/dist/src/cli/sandbox.js +17 -1
- package/dist/src/cli/sandbox.js.map +1 -1
- package/dist/src/cli/scaffold.d.ts +1 -1
- package/dist/src/cli/scaffold.js +1 -1
- package/dist/src/cli/setup-channel.d.ts +9 -0
- package/dist/src/cli/setup-channel.js +28 -1
- package/dist/src/cli/setup-channel.js.map +1 -1
- package/dist/src/cli/setup-common.d.ts +3 -1
- package/dist/src/cli/setup-common.js +3 -2
- package/dist/src/cli/setup-common.js.map +1 -1
- package/dist/src/cli/setup.d.ts +2 -0
- package/dist/src/cli/setup.js +94 -2
- package/dist/src/cli/setup.js.map +1 -1
- package/dist/src/cli/up.js +115 -51
- package/dist/src/cli/up.js.map +1 -1
- package/dist/src/cli/values.js +3 -4
- package/dist/src/cli/values.js.map +1 -1
- package/dist/src/cli/verb-registry.js +174 -9
- package/dist/src/cli/verb-registry.js.map +1 -1
- package/dist/src/cli/wordmark.d.ts +2 -2
- package/dist/src/cli/wordmark.js +2 -2
- package/dist/src/codex/broker.d.ts +229 -0
- package/dist/src/codex/broker.js +548 -0
- package/dist/src/codex/broker.js.map +1 -0
- package/dist/src/codex/runner.d.ts +178 -0
- package/dist/src/codex/runner.js +231 -0
- package/dist/src/codex/runner.js.map +1 -0
- package/dist/src/codex/serve.d.ts +56 -0
- package/dist/src/codex/serve.js +98 -0
- package/dist/src/codex/serve.js.map +1 -0
- package/dist/src/codex/workspace-commit.d.ts +219 -0
- package/dist/src/codex/workspace-commit.js +549 -0
- package/dist/src/codex/workspace-commit.js.map +1 -0
- package/dist/src/core/advance-cycle.d.ts +51 -0
- package/dist/src/core/advance-cycle.js +66 -2
- package/dist/src/core/advance-cycle.js.map +1 -1
- package/dist/src/core/agents-md.d.ts +20 -18
- package/dist/src/core/agents-md.js +33 -31
- package/dist/src/core/agents-md.js.map +1 -1
- package/dist/src/core/attest.d.ts +215 -0
- package/dist/src/core/attest.js +317 -7
- package/dist/src/core/attest.js.map +1 -1
- package/dist/src/core/audit.d.ts +18 -0
- package/dist/src/core/audit.js +13 -0
- package/dist/src/core/audit.js.map +1 -1
- package/dist/src/core/channel-owner.d.ts +213 -0
- package/dist/src/core/channel-owner.js +358 -0
- package/dist/src/core/channel-owner.js.map +1 -0
- package/dist/src/core/command-class.d.ts +154 -0
- package/dist/src/core/command-class.js +673 -20
- package/dist/src/core/command-class.js.map +1 -1
- package/dist/src/core/commit-guard.d.ts +272 -0
- package/dist/src/core/commit-guard.js +424 -0
- package/dist/src/core/commit-guard.js.map +1 -0
- package/dist/src/core/daemon-actor.d.ts +45 -0
- package/dist/src/core/daemon-actor.js +54 -0
- package/dist/src/core/daemon-actor.js.map +1 -0
- package/dist/src/core/dark-session.d.ts +109 -8
- package/dist/src/core/dark-session.js +266 -82
- package/dist/src/core/dark-session.js.map +1 -1
- package/dist/src/core/decision-refusal.d.ts +23 -2
- package/dist/src/core/decision-refusal.js +24 -2
- package/dist/src/core/decision-refusal.js.map +1 -1
- package/dist/src/core/env-file.d.ts +5 -0
- package/dist/src/core/env-file.js +60 -1
- package/dist/src/core/env-file.js.map +1 -1
- package/dist/src/core/execute.d.ts +15 -2
- package/dist/src/core/execute.js +15 -2
- package/dist/src/core/execute.js.map +1 -1
- package/dist/src/core/gate.d.ts +86 -1
- package/dist/src/core/gate.js +81 -1
- package/dist/src/core/gate.js.map +1 -1
- package/dist/src/core/gesture-refusal.d.ts +166 -0
- package/dist/src/core/gesture-refusal.js +188 -0
- package/dist/src/core/gesture-refusal.js.map +1 -0
- package/dist/src/core/harness-version.d.ts +1 -1
- package/dist/src/core/harness-version.js +3 -1
- package/dist/src/core/harness-version.js.map +1 -1
- package/dist/src/core/instance.d.ts +59 -2
- package/dist/src/core/instance.js +113 -0
- package/dist/src/core/instance.js.map +1 -1
- package/dist/src/core/log.d.ts +39 -1
- package/dist/src/core/log.js.map +1 -1
- package/dist/src/core/policy-explain.d.ts +10 -0
- package/dist/src/core/policy-explain.js +32 -0
- package/dist/src/core/policy-explain.js.map +1 -1
- package/dist/src/core/policy-load.d.ts +41 -1
- package/dist/src/core/policy-load.js +21 -3
- package/dist/src/core/policy-load.js.map +1 -1
- package/dist/src/core/policy-match.d.ts +43 -0
- package/dist/src/core/policy-match.js +52 -0
- package/dist/src/core/policy-match.js.map +1 -1
- package/dist/src/core/policy-proposal.d.ts +52 -0
- package/dist/src/core/policy-proposal.js +102 -2
- package/dist/src/core/policy-proposal.js.map +1 -1
- package/dist/src/core/protected-path-guard.d.ts +117 -4
- package/dist/src/core/protected-path-guard.js +362 -48
- package/dist/src/core/protected-path-guard.js.map +1 -1
- package/dist/src/core/question-preempted.d.ts +141 -0
- package/dist/src/core/question-preempted.js +152 -0
- package/dist/src/core/question-preempted.js.map +1 -0
- package/dist/src/core/read-scope.d.ts +172 -0
- package/dist/src/core/read-scope.js +252 -0
- package/dist/src/core/read-scope.js.map +1 -0
- package/dist/src/core/sandbox.d.ts +81 -0
- package/dist/src/core/sandbox.js +190 -1
- package/dist/src/core/sandbox.js.map +1 -1
- package/dist/src/core/sender-identity.d.ts +476 -0
- package/dist/src/core/sender-identity.js +572 -0
- package/dist/src/core/sender-identity.js.map +1 -0
- package/dist/src/core/shlex.d.ts +102 -0
- package/dist/src/core/shlex.js +159 -0
- package/dist/src/core/shlex.js.map +1 -0
- package/dist/src/core/values.d.ts +18 -8
- package/dist/src/core/values.js +36 -1
- package/dist/src/core/values.js.map +1 -1
- package/dist/src/daemon/advance.d.ts +10 -0
- package/dist/src/daemon/advance.js +25 -4
- package/dist/src/daemon/advance.js.map +1 -1
- package/dist/src/daemon/daemon.js +9 -0
- package/dist/src/daemon/daemon.js.map +1 -1
- package/dist/src/daemon/git-evidence.d.ts +2 -2
- package/dist/src/daemon/git-evidence.js +1 -1
- package/dist/src/mcp/server.js +8 -0
- package/dist/src/mcp/server.js.map +1 -1
- package/docs/cli-reference.md +932 -32
- package/docs/codex-enforced-session.md +75 -2
- package/docs/codex-workspace-broker.md +118 -0
- package/package.json +3 -1
- package/schema/event.schema.json +538 -9
- package/schema/fixtures/event/invalid/approval-granted-sender-hashed-false.json +20 -0
- package/schema/fixtures/event/invalid/approval-granted-sender-hashed-raw-id.json +20 -0
- package/schema/fixtures/event/invalid/audit-gesture-refused-human-actor.json +16 -0
- package/schema/fixtures/event/invalid/audit-gesture-refused-no-actor-no-sender.json +15 -0
- package/schema/fixtures/event/invalid/audit-gesture-refused-unknown-gesture.json +16 -0
- package/schema/fixtures/event/invalid/audit-question-preempted-agent-actor.json +16 -0
- package/schema/fixtures/event/invalid/audit-question-preempted-no-question-id.json +16 -0
- package/schema/fixtures/event/invalid/audit-question-preempted-unknown-source.json +15 -0
- package/schema/fixtures/event/invalid/gate-path-signed-off-absolute-path.json +14 -0
- package/schema/fixtures/event/invalid/gate-path-signed-off-agent-actor.json +14 -0
- package/schema/fixtures/event/invalid/gate-path-signed-off-missing-path.json +13 -0
- package/schema/fixtures/event/valid/approval-granted-sender-hashed.json +20 -0
- package/schema/fixtures/event/valid/audit-gesture-refused-review-note.json +21 -0
- package/schema/fixtures/event/valid/audit-gesture-refused-sender-key-unavailable.json +19 -0
- package/schema/fixtures/event/valid/audit-gesture-refused.json +19 -0
- package/schema/fixtures/event/valid/audit-question-preempted-no-verdict.json +16 -0
- package/schema/fixtures/event/valid/audit-question-preempted.json +20 -0
- package/schema/fixtures/event/valid/gate-path-signed-off.json +14 -0
- package/schema/fixtures/event/valid/harness-kind-claude-code.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-codex.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-cursor.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-grok.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-muse.json +23 -0
- package/schema/fixtures/policy/invalid/senders-half-keyed.json +20 -0
- package/schema/fixtures/policy/valid/canonical.json +1 -1
- package/schema/fixtures/policy/valid/senders-keyed.json +24 -0
- package/schema/fixtures/policy-md/valid/canonical.md +1 -1
- package/schema/fixtures/policy-md/valid/with-values.md +5 -7
- package/schema/fixtures/values/invalid/class-shaped.json +1 -1
- package/schema/fixtures/values/invalid/duplicate-entry.json +1 -1
- package/schema/fixtures/values/invalid/non-string-item.json +1 -1
- package/schema/fixtures/values/invalid/over-cap.json +1 -1
- package/schema/fixtures/values/invalid/unknown-key.json +1 -1
- package/schema/fixtures/values/invalid/version-float.json +1 -0
- package/schema/fixtures/values/invalid/version-integer.json +1 -0
- package/schema/fixtures/values/invalid/version-wrong-string.json +1 -0
- package/schema/fixtures/values/valid/empty-lists.json +2 -3
- package/schema/fixtures/values/valid/full.json +5 -7
- package/schema/fixtures/values/valid/minimal.json +1 -1
- package/schema/fixtures/values-md/invalid/schema-invalid.md +5 -3
- package/schema/fixtures/values-md/invalid/two-blocks.md +3 -3
- package/schema/fixtures/values-md/invalid/unterminated.md +2 -2
- package/schema/fixtures/values-md/invalid/version-1.md +69 -0
- package/schema/fixtures/values-md/invalid/version-unquoted.md +64 -0
- package/schema/fixtures/values-md/invalid/yaml-error.md +2 -2
- package/schema/fixtures/values-md/valid/absent.md +1 -1
- package/schema/fixtures/values-md/valid/with-values.md +5 -7
- package/schema/policy.schema.json +54 -2
- package/schema/values.schema.json +7 -11
- 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":"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"}
|
package/dist/src/cli/policy.js
CHANGED
|
@@ -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,
|
|
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
|
}
|