@lannguyensi/harness 0.49.0 → 0.50.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/CHANGELOG.md +683 -0
- package/dist/cli/add/index.js +15 -3
- package/dist/cli/add/index.js.map +1 -1
- package/dist/cli/apply/apply.js +19 -3
- package/dist/cli/apply/apply.js.map +1 -1
- package/dist/cli/approve/risk.d.ts +28 -2
- package/dist/cli/approve/risk.js +14 -4
- package/dist/cli/approve/risk.js.map +1 -1
- package/dist/cli/diff/index.js +22 -1
- package/dist/cli/diff/index.js.map +1 -1
- package/dist/cli/doctor/format.js +12 -2
- package/dist/cli/doctor/format.js.map +1 -1
- package/dist/cli/doctor/index.js +26 -1
- package/dist/cli/doctor/index.js.map +1 -1
- package/dist/cli/doctor/types.d.ts +26 -0
- package/dist/cli/explain-policy.d.ts +4 -1
- package/dist/cli/explain-policy.js +12 -2
- package/dist/cli/explain-policy.js.map +1 -1
- package/dist/cli/export.js +12 -1
- package/dist/cli/export.js.map +1 -1
- package/dist/cli/index.js +29 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/templates.d.ts +1 -1
- package/dist/cli/init/templates.js +65 -1
- package/dist/cli/init/templates.js.map +1 -1
- package/dist/cli/list.js +6 -0
- package/dist/cli/list.js.map +1 -1
- package/dist/cli/loader.js +15 -0
- package/dist/cli/loader.js.map +1 -1
- package/dist/cli/pack/hook-codex-pre-tool-use.js +11 -6
- package/dist/cli/pack/hook-codex-pre-tool-use.js.map +1 -1
- package/dist/cli/pack/hook-post-tool-use.d.ts +6 -4
- package/dist/cli/pack/hook-post-tool-use.js.map +1 -1
- package/dist/cli/pack/hook-pre-tool-use.js +23 -22
- package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
- package/dist/cli/remove/index.d.ts +12 -0
- package/dist/cli/remove/index.js +89 -0
- package/dist/cli/remove/index.js.map +1 -1
- package/dist/cli/validate/checks.d.ts +48 -0
- package/dist/cli/validate/checks.js +231 -0
- package/dist/cli/validate/checks.js.map +1 -1
- package/dist/cli/validate/index.js +11 -0
- package/dist/cli/validate/index.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/index.d.ts +2 -2
- package/dist/policy-packs/builtin/understanding-before-execution/index.js +33 -27
- package/dist/policy-packs/builtin/understanding-before-execution/index.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/markers.js +2 -2
- package/dist/policy-packs/builtin/understanding-before-execution/markers.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.d.ts +46 -8
- package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.js +71 -12
- package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/post-tool-use-boundary.js +6 -3
- package/dist/policy-packs/builtin/understanding-before-execution/post-tool-use-boundary.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution.js +9 -8
- package/dist/policy-packs/builtin/understanding-before-execution.js.map +1 -1
- package/dist/runtime/command-normalize.d.ts +112 -0
- package/dist/runtime/command-normalize.js +169 -55
- package/dist/runtime/command-normalize.js.map +1 -1
- package/dist/runtime/deletion-target-resolve.d.ts +28 -0
- package/dist/runtime/deletion-target-resolve.js +1055 -0
- package/dist/runtime/deletion-target-resolve.js.map +1 -0
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/intercept.js +17 -1
- package/dist/runtime/intercept.js.map +1 -1
- package/dist/runtime/kubectl-target-parse.d.ts +16 -0
- package/dist/runtime/kubectl-target-parse.js +9 -1
- package/dist/runtime/kubectl-target-parse.js.map +1 -1
- package/dist/runtime/read-only-bash.d.ts +11 -0
- package/dist/runtime/read-only-bash.js +288 -7
- package/dist/runtime/read-only-bash.js.map +1 -1
- package/dist/runtime/risk-classifier.js +14 -1
- package/dist/runtime/risk-classifier.js.map +1 -1
- package/dist/runtime/when-eval.d.ts +11 -2
- package/dist/runtime/when-eval.js +25 -0
- package/dist/runtime/when-eval.js.map +1 -1
- package/dist/runtime/workflow-policies.d.ts +164 -0
- package/dist/runtime/workflow-policies.js +439 -0
- package/dist/runtime/workflow-policies.js.map +1 -0
- package/dist/schema/index.d.ts +24 -0
- package/dist/schema/policies.d.ts +25 -0
- package/dist/schema/policies.js +29 -0
- package/dist/schema/policies.js.map +1 -1
- package/dist/schema/risk.d.ts +6 -0
- package/dist/schema/risk.js +80 -0
- package/dist/schema/risk.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime enforcement for `workflows:` (99f47307, Slice 1).
|
|
3
|
+
*
|
|
4
|
+
* `manifest.workflows[]` describes a review-then-merge process (schema:
|
|
5
|
+
* `src/schema/workflows.ts`), but until now nothing read it at
|
|
6
|
+
* enforcement time: `src/runtime/intercept.ts` (the Risk Gate / policy
|
|
7
|
+
* engine `harness policy intercept` runs on every PreToolUse) only ever
|
|
8
|
+
* evaluates `manifest.policies[]`. A `workflows:` declaration alone was
|
|
9
|
+
* documentation, not a gate (`docs/for-agents.md`, pre-Slice-1 wording:
|
|
10
|
+
* "The schema cannot enforce that today").
|
|
11
|
+
*
|
|
12
|
+
* This module closes that gap for exactly ONE step pairing: a
|
|
13
|
+
* `review_subagent` step with `spawn: "required"` followed (at any
|
|
14
|
+
* later index in the same workflow) by a `merge` step. When BOTH
|
|
15
|
+
* `require-review-evidence` and `require-review-evidence-bash` are
|
|
16
|
+
* declared in `manifest.hooks[]`, `deriveWorkflowGatePolicies` returns
|
|
17
|
+
* the SAME policy pair `harness init --template full` would hand-author
|
|
18
|
+
* (`src/cli/init/templates.ts` `review-before-merge` /
|
|
19
|
+
* `review-before-merge-bash`), renamed per workflow so the provenance
|
|
20
|
+
* is visible in `harness list policies` / `harness explain`.
|
|
21
|
+
*
|
|
22
|
+
* Deliberately does NOT touch `src/runtime/intercept.ts`: the engine
|
|
23
|
+
* already knows how to evaluate a `Policy`, so the only thing missing
|
|
24
|
+
* was a `Policy` to hand it. `src/cli/loader.ts#loadManifest` calls
|
|
25
|
+
* `withDerivedPolicies` after parsing and appends the result to
|
|
26
|
+
* `manifest.policies` before any consumer (the CLI `policy intercept`
|
|
27
|
+
* entrypoint, `list`, `explain`, `explain-policy`) reads it.
|
|
28
|
+
*
|
|
29
|
+
* TWO VIEWS OF ONE MANIFEST (review round 3, 99f47307 Slice 1). Every
|
|
30
|
+
* reader of a parsed manifest sees exactly one of:
|
|
31
|
+
*
|
|
32
|
+
* - the HAND-AUTHORED view: `parseManifest(...)` as-is, only what the
|
|
33
|
+
* operator wrote under `policies:`. Right for anything that writes a
|
|
34
|
+
* manifest back to disk or serialises it for later comparison
|
|
35
|
+
* (`harness export`, the `.last-apply` manifest snapshot, `add` /
|
|
36
|
+
* `remove` / `adopt` / `pack reseed` file rewrites, the schema gate in
|
|
37
|
+
* `io/validate-before-write.ts`), because a derived policy written out
|
|
38
|
+
* as if hand-authored would be re-derived on top of itself next time.
|
|
39
|
+
* `withoutDerivedPolicies` turns a derived view back into this one.
|
|
40
|
+
* - the DERIVED view: `withDerivedPolicies(parseManifest(...))`, hand-
|
|
41
|
+
* authored policies plus the workflow gate pair. Right for enforcement
|
|
42
|
+
* (`policy intercept`, hook entrypoints), validation (`validate`,
|
|
43
|
+
* `doctor`, `add`'s asset gate), display (`list`, `explain[-policy]`,
|
|
44
|
+
* `describe`, `dry-run`), and every COMPARISON where the other side is
|
|
45
|
+
* also derived (`diff --since` ref side vs working side; the
|
|
46
|
+
* `.last-apply` snapshot vs the current manifest in restart hints).
|
|
47
|
+
* `loadManifest` hands out this view; the few readers that parse
|
|
48
|
+
* without it (`validate`, `diff`'s ref side, `apply`'s snapshot
|
|
49
|
+
* reader, `add`'s gate) call `withDerivedPolicies` themselves.
|
|
50
|
+
*
|
|
51
|
+
* Both helpers are idempotent and accept either view as input (they
|
|
52
|
+
* partition on `isDerivedPolicy`), so a reader can never "double derive"
|
|
53
|
+
* or strip a hand-authored policy by calling them on the wrong view.
|
|
54
|
+
* Review rounds 1 and 2 each found one reader on the wrong side of this
|
|
55
|
+
* line (`apply` vs `validate`; `diff --since`'s two sides); the table
|
|
56
|
+
* above plus tests/cli/manifest-view-parity.test.ts are the guard.
|
|
57
|
+
*
|
|
58
|
+
* Fail direction: when the two evidence hooks are NOT wired, this
|
|
59
|
+
* returns `[]` for every workflow rather than deriving an
|
|
60
|
+
* unenforceable policy (there would be no hook name to bind it to, and
|
|
61
|
+
* `manifest.policies[].hook` must reference a declared hook, see
|
|
62
|
+
* `ManifestSchema`'s superRefine in `src/schema/index.ts`). That silent
|
|
63
|
+
* non-enforcement case is exactly what `checkWorkflowGateWiring`
|
|
64
|
+
* (`src/cli/validate/checks.ts`) turns into a loud `validate` error, so
|
|
65
|
+
* "declared `spawn: required`, gate never wired" cannot pass silently.
|
|
66
|
+
*/
|
|
67
|
+
/** The two hooks templates.ts wires for the hand-authored merge gate. */
|
|
68
|
+
export const REVIEW_EVIDENCE_HOOK_MCP = "require-review-evidence";
|
|
69
|
+
export const REVIEW_EVIDENCE_HOOK_BASH = "require-review-evidence-bash";
|
|
70
|
+
// Exported (review round 2, F5) so `src/cli/validate/checks.ts` can check a
|
|
71
|
+
// declared hook's `match`/`bash_match` against the EXACT surface the
|
|
72
|
+
// derivation binds to, instead of trusting the hook name alone. See
|
|
73
|
+
// `checkWorkflowGateWiring`'s header for the "right name, wrong trigger"
|
|
74
|
+
// gap this closes.
|
|
75
|
+
export const MERGE_MCP_MATCH = "mcp__agent-tasks__pull_requests_merge";
|
|
76
|
+
// Byte-identical to templates.ts's `review-before-merge-bash` trigger
|
|
77
|
+
// (verified against the parsed FULL_TEMPLATE at authoring time; pinned
|
|
78
|
+
// by tests/runtime/workflow-policies.test.ts's parity assertion so the
|
|
79
|
+
// two cannot silently drift apart).
|
|
80
|
+
export const MERGE_BASH_MATCH = "(^|\\n|;|\\||&|\\()\\s*(\\w+=\\S+\\s+)*gh pr merge\\b";
|
|
81
|
+
/**
|
|
82
|
+
* True when `workflow.steps` contains a `review_subagent` step with
|
|
83
|
+
* `spawn: "required"` at an index earlier than some `merge` step
|
|
84
|
+
* (not necessarily adjacent; a `ci_gate` or another step may sit
|
|
85
|
+
* between them). A workflow with no `merge` step at all, or whose only
|
|
86
|
+
* `review_subagent` steps are `spawn: "optional"` / `"skip"`, returns
|
|
87
|
+
* `false`.
|
|
88
|
+
*/
|
|
89
|
+
export function workflowRequiresMergeGate(workflow) {
|
|
90
|
+
let sawRequiredReview = false;
|
|
91
|
+
for (const step of workflow.steps) {
|
|
92
|
+
if (step.kind === "review_subagent" && step.spawn === "required") {
|
|
93
|
+
sawRequiredReview = true;
|
|
94
|
+
}
|
|
95
|
+
else if (step.kind === "merge" && sawRequiredReview) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
/** True when both merge-gate evidence hooks are declared in `manifest.hooks[]`. */
|
|
102
|
+
export function hasWiredMergeGateHooks(manifest) {
|
|
103
|
+
const hookNames = new Set(manifest.hooks.map((h) => h.name));
|
|
104
|
+
return hookNames.has(REVIEW_EVIDENCE_HOOK_MCP) && hookNames.has(REVIEW_EVIDENCE_HOOK_BASH);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Canonical key for "does this policy intercept the same surface for the
|
|
108
|
+
* same evidence?": event + match + bash_match + requires.ledger_tag. Two
|
|
109
|
+
* policies sharing this key fire on the identical event for the identical
|
|
110
|
+
* ledger tag, so the derivation treats a hand-authored policy with this
|
|
111
|
+
* key as a CANDIDATE for standing in for the derived gate. Whether it
|
|
112
|
+
* actually does is decided by `isEquivalentToDerivedGate` below: it must
|
|
113
|
+
* also extract its template variables the same way (F4, review round 3)
|
|
114
|
+
* and be at least as strong (F1, review round 2).
|
|
115
|
+
*/
|
|
116
|
+
function triggerSurfaceKey(policy) {
|
|
117
|
+
return JSON.stringify({
|
|
118
|
+
event: policy.trigger.event,
|
|
119
|
+
match: policy.trigger.match ?? null,
|
|
120
|
+
bash_match: policy.trigger.bash_match ?? null,
|
|
121
|
+
ledger_tag: policy.requires?.ledger_tag ?? null,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Canonical form of `trigger.extract` for equality: key-sorted so two
|
|
126
|
+
* maps that declare the same variables from the same paths compare equal
|
|
127
|
+
* regardless of authoring order. `null` when the policy extracts nothing.
|
|
128
|
+
*
|
|
129
|
+
* F4 (review round 3): `extract` was NOT part of the dedupe key before,
|
|
130
|
+
* so a hand-authored block policy on the derived surface that extracted
|
|
131
|
+
* `PR_NUMBER` from a WRONG path (say `toolArgs.pr` instead of
|
|
132
|
+
* `toolArgs.prNumber`) counted as equivalent, suppressed the derived
|
|
133
|
+
* gate, and then evaluated its own `review:${PR_NUMBER}` against an
|
|
134
|
+
* unresolved variable. Under `risk.degraded_fail_posture: fail_open` that
|
|
135
|
+
* is an allow with "template variables unresolved": the merge went
|
|
136
|
+
* through with no review evidence at all. A differently-extracting policy
|
|
137
|
+
* no longer dedupes: the derived gate is produced as well, both apply,
|
|
138
|
+
* and `findWeakGatePolicyOverlaps` names the mismatch.
|
|
139
|
+
*/
|
|
140
|
+
function extractKey(policy) {
|
|
141
|
+
const extract = policy.trigger.extract;
|
|
142
|
+
if (extract === undefined)
|
|
143
|
+
return null;
|
|
144
|
+
const sorted = {};
|
|
145
|
+
for (const key of Object.keys(extract).sort()) {
|
|
146
|
+
const value = extract[key];
|
|
147
|
+
if (value !== undefined)
|
|
148
|
+
sorted[key] = value;
|
|
149
|
+
}
|
|
150
|
+
return JSON.stringify(sorted);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* True when a hand-authored policy sharing the derived gate's trigger
|
|
154
|
+
* surface + ledger_tag is strong enough to stand in for it: `enforcement:
|
|
155
|
+
* "block"`, no `when:` risk/environment scoping, and not `operator_only:
|
|
156
|
+
* true` (which carries no `requires:`/ledger_tag at all, so it would not
|
|
157
|
+
* normally share a key, but the check is defensive since operator_only
|
|
158
|
+
* would otherwise read as "block" and pass the enforcement check alone).
|
|
159
|
+
*
|
|
160
|
+
* F1 (review round 2, 99f47307 Slice 1): before this, `triggerSurfaceKey`
|
|
161
|
+
* alone decided dedupe, so a hand-authored `enforcement: "warn"` policy
|
|
162
|
+
* (or a `block` policy scoped down via `when:` to only some environments)
|
|
163
|
+
* on the identical surface silently suppressed the derived BLOCK gate: a
|
|
164
|
+
* `spawn: "required"` workflow step that LOOKED enforced actually
|
|
165
|
+
* degraded to warn-only, or to unenforced outside the `when:` scope,
|
|
166
|
+
* with no diagnostic anywhere. A weaker match no longer dedupes: the
|
|
167
|
+
* derived block gate is ALSO produced (both apply), and
|
|
168
|
+
* `findWeakGatePolicyOverlaps` surfaces the overlap so validate/doctor
|
|
169
|
+
* can flag it instead of leaving it silent.
|
|
170
|
+
*/
|
|
171
|
+
function isAtLeastAsStrongAsDerivedGate(policy) {
|
|
172
|
+
return (policy.enforcement === "block" &&
|
|
173
|
+
policy.when === undefined &&
|
|
174
|
+
policy.operator_only !== true);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Why `policy` does NOT qualify as at-least-as-strong (see
|
|
178
|
+
* `isAtLeastAsStrongAsDerivedGate`), in a form suitable for a diagnostic
|
|
179
|
+
* message. `null` when the policy DOES qualify (nothing weak to report).
|
|
180
|
+
*/
|
|
181
|
+
function weaknessReason(policy) {
|
|
182
|
+
if (isAtLeastAsStrongAsDerivedGate(policy))
|
|
183
|
+
return null;
|
|
184
|
+
if (policy.enforcement !== "block")
|
|
185
|
+
return `enforcement: ${policy.enforcement}`;
|
|
186
|
+
if (policy.when !== undefined)
|
|
187
|
+
return "when: (risk/environment-scoped)";
|
|
188
|
+
if (policy.operator_only === true)
|
|
189
|
+
return "operator_only: true";
|
|
190
|
+
return "weaker than a plain block gate";
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Why a hand-authored policy on the derived gate's surface does NOT stand
|
|
194
|
+
* in for it, or `null` when it does (same surface, same extract, at least
|
|
195
|
+
* as strong). Strength is reported before an extract mismatch: a warn
|
|
196
|
+
* policy with a wrong extract path is first and foremost a warn policy.
|
|
197
|
+
*/
|
|
198
|
+
function nonEquivalenceReason(handPolicy, derivedGate) {
|
|
199
|
+
const weakness = weaknessReason(handPolicy);
|
|
200
|
+
if (weakness !== null)
|
|
201
|
+
return weakness;
|
|
202
|
+
const handExtract = extractKey(handPolicy);
|
|
203
|
+
const gateExtract = extractKey(derivedGate);
|
|
204
|
+
if (handExtract !== gateExtract) {
|
|
205
|
+
return `trigger.extract differs (${handExtract ?? "none"} vs derived ${gateExtract ?? "none"})`;
|
|
206
|
+
}
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
function buildMcpMergeGatePolicy(workflowName) {
|
|
210
|
+
return {
|
|
211
|
+
name: `workflow:${workflowName}:review-before-merge`,
|
|
212
|
+
description: `Derived from workflows[] (workflow "${workflowName}"): a review_subagent step ` +
|
|
213
|
+
`with spawn: "required" precedes a merge step, so block PR merges unless a ` +
|
|
214
|
+
"ledger entry tagged review:<pr-number> exists for this session.",
|
|
215
|
+
trigger: {
|
|
216
|
+
event: "PreToolUse",
|
|
217
|
+
match: MERGE_MCP_MATCH,
|
|
218
|
+
extract: { PR_NUMBER: "toolArgs.prNumber" },
|
|
219
|
+
},
|
|
220
|
+
requires: { ledger_tag: "review:${PR_NUMBER}" },
|
|
221
|
+
hook: REVIEW_EVIDENCE_HOOK_MCP,
|
|
222
|
+
enforcement: "block",
|
|
223
|
+
producers: [
|
|
224
|
+
{
|
|
225
|
+
kind: "mcp",
|
|
226
|
+
verb: "mcp__grounding-mcp__ledger_add",
|
|
227
|
+
example: '{sessionId:"${SESSION_ID}", type:"fact", content:"review:${PR_NUMBER}: <verdict + key findings + nits>", source:"Agent(general-purpose) review"}',
|
|
228
|
+
description: "Spawn a review subagent against the PR diff, capture its verdict, then persist a ledger entry tagged with the PR number. The content should be self-contained enough for an auditor to read without re-opening the chat.",
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
ux: {
|
|
232
|
+
cannot: "You cannot merge PR #${PR_NUMBER} yet.",
|
|
233
|
+
required: ["a recorded review of PR #${PR_NUMBER}"],
|
|
234
|
+
run: ['harness record review --pr ${PR_NUMBER} "<summary>"'],
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
function buildBashMergeGatePolicy(workflowName) {
|
|
239
|
+
return {
|
|
240
|
+
name: `workflow:${workflowName}:review-before-merge-bash`,
|
|
241
|
+
description: `Derived from workflows[] (workflow "${workflowName}"): a review_subagent step ` +
|
|
242
|
+
'with spawn: "required" precedes a merge step, so block `gh pr merge` unless a ' +
|
|
243
|
+
"ledger entry tagged review:<branch> exists for this session.",
|
|
244
|
+
trigger: {
|
|
245
|
+
event: "PreToolUse",
|
|
246
|
+
match: "Bash",
|
|
247
|
+
bash_match: MERGE_BASH_MATCH,
|
|
248
|
+
},
|
|
249
|
+
requires: { ledger_tag: "review:${BRANCH}" },
|
|
250
|
+
hook: REVIEW_EVIDENCE_HOOK_BASH,
|
|
251
|
+
enforcement: "block",
|
|
252
|
+
producers: [
|
|
253
|
+
{
|
|
254
|
+
kind: "mcp",
|
|
255
|
+
verb: "mcp__grounding-mcp__ledger_add",
|
|
256
|
+
example: '{sessionId:"${SESSION_ID}", type:"fact", content:"review:${BRANCH}: <verdict + key findings + nits>", source:"Agent(general-purpose) review"}',
|
|
257
|
+
description: "Spawn a review subagent against the branch diff, capture its verdict, then persist a ledger entry tagged with the branch name. Mirror of the review-before-merge producer for the gh-cli surface.",
|
|
258
|
+
},
|
|
259
|
+
],
|
|
260
|
+
ux: {
|
|
261
|
+
cannot: "You cannot merge the PR for branch ${BRANCH} via `gh pr merge` yet.",
|
|
262
|
+
required: ["a recorded review of the PR for branch ${BRANCH}"],
|
|
263
|
+
run: ['harness record review --pr <pr> "<summary>"'],
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
// F7 (review round 2): a registry of every `Policy` object this module has
|
|
268
|
+
// ever handed back from `deriveWorkflowGatePolicies`. `WeakSet` keys on
|
|
269
|
+
// object identity, not value equality, deliberately: two DIFFERENT
|
|
270
|
+
// workflows can derive value-identical-looking policies with different
|
|
271
|
+
// names, and a hand-authored policy could theoretically share a derived
|
|
272
|
+
// policy's exact shape too (nothing stops an operator hand-copying one
|
|
273
|
+
// into `policies:`). Identity is the only unambiguous "did THIS module
|
|
274
|
+
// produce THIS object" test. `harness export`/`list`/`doctor` use
|
|
275
|
+
// `isDerivedPolicy` to distinguish "declared by the operator" from
|
|
276
|
+
// "derived from workflows[]" without re-deriving and re-comparing.
|
|
277
|
+
const derivedPolicyRegistry = new WeakSet();
|
|
278
|
+
/** True when `policy` is an object this module derived (see `withDerivedPolicies`). */
|
|
279
|
+
export function isDerivedPolicy(policy) {
|
|
280
|
+
return derivedPolicyRegistry.has(policy);
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* The hand-authored slice of `manifest.policies`: everything the operator
|
|
284
|
+
* wrote under `policies:`, none of the workflow-derived entries. Works on
|
|
285
|
+
* either view (see module doc); on a hand-authored view it is the
|
|
286
|
+
* identity.
|
|
287
|
+
*/
|
|
288
|
+
export function handAuthoredPolicies(manifest) {
|
|
289
|
+
return manifest.policies.filter((p) => !isDerivedPolicy(p));
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* The ONE walk that decides, per qualifying workflow and per surface,
|
|
293
|
+
* whether a gate is derived and which hand-authored policies overlap it.
|
|
294
|
+
* `deriveWorkflowGatePolicies` and `findWeakGatePolicyOverlaps` are both
|
|
295
|
+
* projections of this result, so they cannot disagree about what was
|
|
296
|
+
* derived (F1, review round 3: the overlap finder used to re-implement
|
|
297
|
+
* the walk without the `seen` set, and warned about a "derived" gate that
|
|
298
|
+
* a strong hand-authored policy had in fact suppressed).
|
|
299
|
+
*
|
|
300
|
+
* Only HAND-authored policies feed the walk (`handAuthoredPolicies`), so
|
|
301
|
+
* the result is the same whether the input is the hand-authored or the
|
|
302
|
+
* derived view.
|
|
303
|
+
*/
|
|
304
|
+
function deriveWorkflowGates(manifest) {
|
|
305
|
+
const empty = { policies: [], overlaps: [] };
|
|
306
|
+
if (!hasWiredMergeGateHooks(manifest))
|
|
307
|
+
return empty;
|
|
308
|
+
const hand = handAuthoredPolicies(manifest);
|
|
309
|
+
const bySurface = new Map();
|
|
310
|
+
for (const policy of hand) {
|
|
311
|
+
const key = triggerSurfaceKey(policy);
|
|
312
|
+
const bucket = bySurface.get(key);
|
|
313
|
+
if (bucket)
|
|
314
|
+
bucket.push(policy);
|
|
315
|
+
else
|
|
316
|
+
bySurface.set(key, [policy]);
|
|
317
|
+
}
|
|
318
|
+
// Surfaces already covered: by an equivalent hand-authored policy (F1 +
|
|
319
|
+
// F4: at least as strong AND extracting the same way), or by a gate an
|
|
320
|
+
// earlier workflow in this same manifest already derived.
|
|
321
|
+
const seen = new Set();
|
|
322
|
+
const derived = [];
|
|
323
|
+
const overlaps = [];
|
|
324
|
+
for (const workflow of manifest.workflows) {
|
|
325
|
+
if (!workflowRequiresMergeGate(workflow))
|
|
326
|
+
continue;
|
|
327
|
+
const candidates = [
|
|
328
|
+
{ policy: buildMcpMergeGatePolicy(workflow.name), surface: MERGE_MCP_MATCH },
|
|
329
|
+
{ policy: buildBashMergeGatePolicy(workflow.name), surface: "`gh pr merge` (Bash)" },
|
|
330
|
+
];
|
|
331
|
+
for (const { policy: candidate, surface } of candidates) {
|
|
332
|
+
const key = triggerSurfaceKey(candidate);
|
|
333
|
+
if (seen.has(key))
|
|
334
|
+
continue;
|
|
335
|
+
const sharing = bySurface.get(key) ?? [];
|
|
336
|
+
const reasons = sharing.map((hp) => ({
|
|
337
|
+
handPolicy: hp,
|
|
338
|
+
reason: nonEquivalenceReason(hp, candidate),
|
|
339
|
+
}));
|
|
340
|
+
if (reasons.some((r) => r.reason === null)) {
|
|
341
|
+
// An equivalent hand-authored policy already gates this surface;
|
|
342
|
+
// nothing is derived, so there is nothing to overlap with either.
|
|
343
|
+
seen.add(key);
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
derived.push(candidate);
|
|
347
|
+
seen.add(key);
|
|
348
|
+
for (const { handPolicy, reason } of reasons) {
|
|
349
|
+
if (reason === null)
|
|
350
|
+
continue;
|
|
351
|
+
overlaps.push({
|
|
352
|
+
workflowName: workflow.name,
|
|
353
|
+
derivedPolicyName: candidate.name,
|
|
354
|
+
surface,
|
|
355
|
+
handPolicyName: handPolicy.name,
|
|
356
|
+
reason,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return { policies: derived, overlaps };
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Derive the merge-gate `Policy` pair for every workflow that needs one
|
|
365
|
+
* and does not already have an equivalent policy (hand-authored, or
|
|
366
|
+
* derived from an earlier workflow in the same manifest) intercepting
|
|
367
|
+
* the same surface for the same evidence tag.
|
|
368
|
+
*
|
|
369
|
+
* Returns `[]` when `manifest.hooks[]` is missing either evidence hook
|
|
370
|
+
* (see module doc: that gap is a `validate` error, not silent
|
|
371
|
+
* derivation of an unenforceable policy) or when no workflow declares
|
|
372
|
+
* a `spawn: "required"` review step followed by a merge step.
|
|
373
|
+
*/
|
|
374
|
+
export function deriveWorkflowGatePolicies(manifest) {
|
|
375
|
+
return deriveWorkflowGates(manifest).policies;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Every hand-authored policy that shares an ACTUALLY DERIVED gate's
|
|
379
|
+
* trigger surface + ledger_tag but does not stand in for it: weaker
|
|
380
|
+
* (F1, review round 2) or extracting its variables differently (F4,
|
|
381
|
+
* review round 3). Both policies apply in that case, and the overlap is
|
|
382
|
+
* worth flagging: an operator reading `enforcement: warn` on a
|
|
383
|
+
* `two-reviewers-required`-shaped policy might reasonably believe THAT
|
|
384
|
+
* is the only gate on the surface. `src/cli/validate/checks.ts` turns
|
|
385
|
+
* each entry into a warning Diagnostic; `src/cli/doctor/index.ts`
|
|
386
|
+
* renders the same list in the Workflows section.
|
|
387
|
+
*
|
|
388
|
+
* Mirrors `deriveWorkflowGatePolicies` exactly (same walk, same `seen`
|
|
389
|
+
* set): a surface an equivalent hand-authored policy already covers
|
|
390
|
+
* derives nothing and therefore reports nothing, even when a second,
|
|
391
|
+
* weaker policy sits on it too. Returns `[]` when the evidence hooks are
|
|
392
|
+
* not wired or no workflow needs a gate.
|
|
393
|
+
*/
|
|
394
|
+
export function findWeakGatePolicyOverlaps(manifest) {
|
|
395
|
+
return deriveWorkflowGates(manifest).overlaps;
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* The ONE place `workflows[]` gets folded into `manifest.policies` (F2,
|
|
399
|
+
* review round 2). Before this, `src/cli/loader.ts#loadManifest` inlined
|
|
400
|
+
* the append, so every OTHER manifest consumer that parses the manifest
|
|
401
|
+
* a different way (`src/cli/validate/index.ts`'s `loadMergedRaw` +
|
|
402
|
+
* `parseManifest`, notably) saw a manifest with no derived policies at
|
|
403
|
+
* all, and `apply` and `validate` diverged. See the module doc for the
|
|
404
|
+
* full list of readers and which view each one takes.
|
|
405
|
+
*
|
|
406
|
+
* Idempotent and view-agnostic (review round 3): the derivation runs on
|
|
407
|
+
* `handAuthoredPolicies(manifest)` only and any derived entries already
|
|
408
|
+
* present are replaced, so calling this on an already-derived view yields
|
|
409
|
+
* the same policy names with no duplicates. Returns the input object
|
|
410
|
+
* unchanged when there is nothing to derive and nothing to replace.
|
|
411
|
+
*
|
|
412
|
+
* Registers each derived `Policy` object in the module-level
|
|
413
|
+
* `derivedPolicyRegistry` (F7) so `isDerivedPolicy` can later distinguish
|
|
414
|
+
* it from a hand-authored one (used by `harness export`'s filter and
|
|
415
|
+
* `list`/`doctor`'s "(derived from workflows[])" marker).
|
|
416
|
+
*/
|
|
417
|
+
export function withDerivedPolicies(manifest) {
|
|
418
|
+
const hand = handAuthoredPolicies(manifest);
|
|
419
|
+
const derivedPolicies = deriveWorkflowGatePolicies(manifest);
|
|
420
|
+
if (derivedPolicies.length === 0 && hand.length === manifest.policies.length)
|
|
421
|
+
return manifest;
|
|
422
|
+
for (const policy of derivedPolicies)
|
|
423
|
+
derivedPolicyRegistry.add(policy);
|
|
424
|
+
return { ...manifest, policies: [...hand, ...derivedPolicies] };
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* The hand-authored view of a manifest that may carry derived policies:
|
|
428
|
+
* what `harness export` emits and what the `.last-apply` manifest
|
|
429
|
+
* snapshot stores, so nothing that gets written out or re-read later
|
|
430
|
+
* carries a derived policy as if the operator had declared it. Returns
|
|
431
|
+
* the input object unchanged when it carries no derived policy.
|
|
432
|
+
*/
|
|
433
|
+
export function withoutDerivedPolicies(manifest) {
|
|
434
|
+
const hand = handAuthoredPolicies(manifest);
|
|
435
|
+
if (hand.length === manifest.policies.length)
|
|
436
|
+
return manifest;
|
|
437
|
+
return { ...manifest, policies: hand };
|
|
438
|
+
}
|
|
439
|
+
//# sourceMappingURL=workflow-policies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-policies.js","sourceRoot":"","sources":["../../src/runtime/workflow-policies.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AAEH,yEAAyE;AACzE,MAAM,CAAC,MAAM,wBAAwB,GAAG,yBAAyB,CAAC;AAClE,MAAM,CAAC,MAAM,yBAAyB,GAAG,8BAA8B,CAAC;AAExE,4EAA4E;AAC5E,qEAAqE;AACrE,oEAAoE;AACpE,yEAAyE;AACzE,mBAAmB;AACnB,MAAM,CAAC,MAAM,eAAe,GAAG,uCAAuC,CAAC;AACvE,sEAAsE;AACtE,uEAAuE;AACvE,uEAAuE;AACvE,oCAAoC;AACpC,MAAM,CAAC,MAAM,gBAAgB,GAAG,uDAAuD,CAAC;AAExF;;;;;;;GAOG;AACH,MAAM,UAAU,yBAAyB,CAAC,QAAkB;IAC1D,IAAI,iBAAiB,GAAG,KAAK,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;QAClC,IAAI,IAAI,CAAC,IAAI,KAAK,iBAAiB,IAAI,IAAI,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;YACjE,iBAAiB,GAAG,IAAI,CAAC;QAC3B,CAAC;aAAM,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,iBAAiB,EAAE,CAAC;YACtD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,sBAAsB,CAAC,QAAkB;IACvD,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7D,OAAO,SAAS,CAAC,GAAG,CAAC,wBAAwB,CAAC,IAAI,SAAS,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;AAC7F,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,iBAAiB,CAAC,MAA4C;IACrE,OAAO,IAAI,CAAC,SAAS,CAAC;QACpB,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK;QAC3B,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,IAAI;QACnC,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC,UAAU,IAAI,IAAI;QAC7C,UAAU,EAAE,MAAM,CAAC,QAAQ,EAAE,UAAU,IAAI,IAAI;KAChD,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,SAAS,UAAU,CAAC,MAA+B;IACjD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;IACvC,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACvC,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAC9C,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC3B,IAAI,KAAK,KAAK,SAAS;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IAC/C,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;AAChC,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAS,8BAA8B,CAAC,MAAc;IACpD,OAAO,CACL,MAAM,CAAC,WAAW,KAAK,OAAO;QAC9B,MAAM,CAAC,IAAI,KAAK,SAAS;QACzB,MAAM,CAAC,aAAa,KAAK,IAAI,CAC9B,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,cAAc,CAAC,MAAc;IACpC,IAAI,8BAA8B,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IACxD,IAAI,MAAM,CAAC,WAAW,KAAK,OAAO;QAAE,OAAO,gBAAgB,MAAM,CAAC,WAAW,EAAE,CAAC;IAChF,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,iCAAiC,CAAC;IACxE,IAAI,MAAM,CAAC,aAAa,KAAK,IAAI;QAAE,OAAO,qBAAqB,CAAC;IAChE,OAAO,gCAAgC,CAAC;AAC1C,CAAC;AAED;;;;;GAKG;AACH,SAAS,oBAAoB,CAAC,UAAkB,EAAE,WAAmB;IACnE,MAAM,QAAQ,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;IAC5C,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,QAAQ,CAAC;IACvC,MAAM,WAAW,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IAC3C,MAAM,WAAW,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;IAC5C,IAAI,WAAW,KAAK,WAAW,EAAE,CAAC;QAChC,OAAO,4BAA4B,WAAW,IAAI,MAAM,eAAe,WAAW,IAAI,MAAM,GAAG,CAAC;IAClG,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,uBAAuB,CAAC,YAAoB;IACnD,OAAO;QACL,IAAI,EAAE,YAAY,YAAY,sBAAsB;QACpD,WAAW,EACT,uCAAuC,YAAY,6BAA6B;YAChF,4EAA4E;YAC5E,iEAAiE;QACnE,OAAO,EAAE;YACP,KAAK,EAAE,YAAY;YACnB,KAAK,EAAE,eAAe;YACtB,OAAO,EAAE,EAAE,SAAS,EAAE,mBAAmB,EAAE;SAC5C;QACD,QAAQ,EAAE,EAAE,UAAU,EAAE,qBAAqB,EAAE;QAC/C,IAAI,EAAE,wBAAwB;QAC9B,WAAW,EAAE,OAAO;QACpB,SAAS,EAAE;YACT;gBACE,IAAI,EAAE,KAAK;gBACX,IAAI,EAAE,gCAAgC;gBACtC,OAAO,EACL,kJAAkJ;gBACpJ,WAAW,EACT,0NAA0N;aAC7N;SACF;QACD,EAAE,EAAE;YACF,MAAM,EAAE,wCAAwC;YAChD,QAAQ,EAAE,CAAC,uCAAuC,CAAC;YACnD,GAAG,EAAE,CAAC,qDAAqD,CAAC;SAC7D;KACF,CAAC;AACJ,CAAC;AAED,SAAS,wBAAwB,CAAC,YAAoB;IACpD,OAAO;QACL,IAAI,EAAE,YAAY,YAAY,2BAA2B;QACzD,WAAW,EACT,uCAAuC,YAAY,6BAA6B;YAChF,gFAAgF;YAChF,8DAA8D;QAChE,OAAO,EAAE;YACP,KAAK,EAAE,YAAY;YACnB,KAAK,EAAE,MAAM;YACb,UAAU,EAAE,gBAAgB;SAC7B;QACD,QAAQ,EAAE,EAAE,UAAU,EAAE,kBAAkB,EAAE;QAC5C,IAAI,EAAE,yBAAyB;QAC/B,WAAW,EAAE,OAAO;QACpB,SAAS,EAAE;YACT;gBACE,IAAI,EAAE,KAAK;gBACX,IAAI,EAAE,gCAAgC;gBACtC,OAAO,EACL,+IAA+I;gBACjJ,WAAW,EACT,mMAAmM;aACtM;SACF;QACD,EAAE,EAAE;YACF,MAAM,EAAE,qEAAqE;YAC7E,QAAQ,EAAE,CAAC,kDAAkD,CAAC;YAC9D,GAAG,EAAE,CAAC,6CAA6C,CAAC;SACrD;KACF,CAAC;AACJ,CAAC;AAED,2EAA2E;AAC3E,wEAAwE;AACxE,mEAAmE;AACnE,uEAAuE;AACvE,wEAAwE;AACxE,uEAAuE;AACvE,uEAAuE;AACvE,kEAAkE;AAClE,mEAAmE;AACnE,mEAAmE;AACnE,MAAM,qBAAqB,GAAG,IAAI,OAAO,EAAU,CAAC;AAEpD,uFAAuF;AACvF,MAAM,UAAU,eAAe,CAAC,MAAc;IAC5C,OAAO,qBAAqB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAkB;IACrD,OAAO,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9D,CAAC;AAqBD;;;;;;;;;;;;GAYG;AACH,SAAS,mBAAmB,CAAC,QAAkB;IAC7C,MAAM,KAAK,GAA2B,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IACrE,IAAI,CAAC,sBAAsB,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC;IAEpD,MAAM,IAAI,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC9C,KAAK,MAAM,MAAM,IAAI,IAAI,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;QACtC,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,MAAM;YAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;;YAC3B,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IACpC,CAAC;IAED,wEAAwE;IACxE,uEAAuE;IACvE,0DAA0D;IAC1D,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,QAAQ,GAA4B,EAAE,CAAC;IAE7C,KAAK,MAAM,QAAQ,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;QAC1C,IAAI,CAAC,yBAAyB,CAAC,QAAQ,CAAC;YAAE,SAAS;QAEnD,MAAM,UAAU,GAA+C;YAC7D,EAAE,MAAM,EAAE,uBAAuB,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,eAAe,EAAE;YAC5E,EAAE,MAAM,EAAE,wBAAwB,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,sBAAsB,EAAE;SACrF,CAAC;QAEF,KAAK,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,UAAU,EAAE,CAAC;YACxD,MAAM,GAAG,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;YACzC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAS;YAC5B,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YACzC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;gBACnC,UAAU,EAAE,EAAE;gBACd,MAAM,EAAE,oBAAoB,CAAC,EAAE,EAAE,SAAS,CAAC;aAC5C,CAAC,CAAC,CAAC;YACJ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC,EAAE,CAAC;gBAC3C,iEAAiE;gBACjE,kEAAkE;gBAClE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACd,SAAS;YACX,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACxB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACd,KAAK,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;gBAC7C,IAAI,MAAM,KAAK,IAAI;oBAAE,SAAS;gBAC9B,QAAQ,CAAC,IAAI,CAAC;oBACZ,YAAY,EAAE,QAAQ,CAAC,IAAI;oBAC3B,iBAAiB,EAAE,SAAS,CAAC,IAAI;oBACjC,OAAO;oBACP,cAAc,EAAE,UAAU,CAAC,IAAI;oBAC/B,MAAM;iBACP,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;AACzC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,0BAA0B,CAAC,QAAkB;IAC3D,OAAO,mBAAmB,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC;AAChD,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,0BAA0B,CAAC,QAAkB;IAC3D,OAAO,mBAAmB,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC;AAChD,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAkB;IACpD,MAAM,IAAI,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IAC5C,MAAM,eAAe,GAAG,0BAA0B,CAAC,QAAQ,CAAC,CAAC;IAC7D,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,QAAQ,CAAC,MAAM;QAAE,OAAO,QAAQ,CAAC;IAC9F,KAAK,MAAM,MAAM,IAAI,eAAe;QAAE,qBAAqB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACxE,OAAO,EAAE,GAAG,QAAQ,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,eAAe,CAAC,EAAE,CAAC;AAClE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CAAC,QAAkB;IACvD,MAAM,IAAI,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IAC5C,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,QAAQ,CAAC,MAAM;QAAE,OAAO,QAAQ,CAAC;IAC9D,OAAO,EAAE,GAAG,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AACzC,CAAC"}
|
package/dist/schema/index.d.ts
CHANGED
|
@@ -574,26 +574,31 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
|
|
|
574
574
|
"risk.category_in": z.ZodOptional<z.ZodArray<z.ZodEnum<["destructive", "data_loss", "production_mutation", "credential_access", "secret_exfiltration", "network_exfiltration", "deployment_change", "infrastructure_change", "privilege_escalation", "irreversible_action", "mass_update"]>, "many">>;
|
|
575
575
|
"environment.name": z.ZodOptional<z.ZodEnum<["production", "staging", "dev", "local", "unknown"]>>;
|
|
576
576
|
"action.reversible": z.ZodOptional<z.ZodBoolean>;
|
|
577
|
+
"action.deletion_target_unresolvable": z.ZodOptional<z.ZodLiteral<true>>;
|
|
577
578
|
}, "strict", z.ZodTypeAny, {
|
|
578
579
|
"risk.severity_at_least"?: "low" | "medium" | "high" | "critical" | undefined;
|
|
579
580
|
"risk.category_in"?: ("destructive" | "data_loss" | "production_mutation" | "credential_access" | "secret_exfiltration" | "network_exfiltration" | "deployment_change" | "infrastructure_change" | "privilege_escalation" | "irreversible_action" | "mass_update")[] | undefined;
|
|
580
581
|
"environment.name"?: "production" | "staging" | "dev" | "local" | "unknown" | undefined;
|
|
581
582
|
"action.reversible"?: boolean | undefined;
|
|
583
|
+
"action.deletion_target_unresolvable"?: true | undefined;
|
|
582
584
|
}, {
|
|
583
585
|
"risk.severity_at_least"?: "low" | "medium" | "high" | "critical" | undefined;
|
|
584
586
|
"risk.category_in"?: ("destructive" | "data_loss" | "production_mutation" | "credential_access" | "secret_exfiltration" | "network_exfiltration" | "deployment_change" | "infrastructure_change" | "privilege_escalation" | "irreversible_action" | "mass_update")[] | undefined;
|
|
585
587
|
"environment.name"?: "production" | "staging" | "dev" | "local" | "unknown" | undefined;
|
|
586
588
|
"action.reversible"?: boolean | undefined;
|
|
589
|
+
"action.deletion_target_unresolvable"?: true | undefined;
|
|
587
590
|
}>, {
|
|
588
591
|
"risk.severity_at_least"?: "low" | "medium" | "high" | "critical" | undefined;
|
|
589
592
|
"risk.category_in"?: ("destructive" | "data_loss" | "production_mutation" | "credential_access" | "secret_exfiltration" | "network_exfiltration" | "deployment_change" | "infrastructure_change" | "privilege_escalation" | "irreversible_action" | "mass_update")[] | undefined;
|
|
590
593
|
"environment.name"?: "production" | "staging" | "dev" | "local" | "unknown" | undefined;
|
|
591
594
|
"action.reversible"?: boolean | undefined;
|
|
595
|
+
"action.deletion_target_unresolvable"?: true | undefined;
|
|
592
596
|
}, {
|
|
593
597
|
"risk.severity_at_least"?: "low" | "medium" | "high" | "critical" | undefined;
|
|
594
598
|
"risk.category_in"?: ("destructive" | "data_loss" | "production_mutation" | "credential_access" | "secret_exfiltration" | "network_exfiltration" | "deployment_change" | "infrastructure_change" | "privilege_escalation" | "irreversible_action" | "mass_update")[] | undefined;
|
|
595
599
|
"environment.name"?: "production" | "staging" | "dev" | "local" | "unknown" | undefined;
|
|
596
600
|
"action.reversible"?: boolean | undefined;
|
|
601
|
+
"action.deletion_target_unresolvable"?: true | undefined;
|
|
597
602
|
}>>;
|
|
598
603
|
}, "strict", z.ZodTypeAny, {
|
|
599
604
|
name: string;
|
|
@@ -642,6 +647,7 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
|
|
|
642
647
|
"risk.category_in"?: ("destructive" | "data_loss" | "production_mutation" | "credential_access" | "secret_exfiltration" | "network_exfiltration" | "deployment_change" | "infrastructure_change" | "privilege_escalation" | "irreversible_action" | "mass_update")[] | undefined;
|
|
643
648
|
"environment.name"?: "production" | "staging" | "dev" | "local" | "unknown" | undefined;
|
|
644
649
|
"action.reversible"?: boolean | undefined;
|
|
650
|
+
"action.deletion_target_unresolvable"?: true | undefined;
|
|
645
651
|
} | undefined;
|
|
646
652
|
}, {
|
|
647
653
|
name: string;
|
|
@@ -690,6 +696,7 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
|
|
|
690
696
|
"risk.category_in"?: ("destructive" | "data_loss" | "production_mutation" | "credential_access" | "secret_exfiltration" | "network_exfiltration" | "deployment_change" | "infrastructure_change" | "privilege_escalation" | "irreversible_action" | "mass_update")[] | undefined;
|
|
691
697
|
"environment.name"?: "production" | "staging" | "dev" | "local" | "unknown" | undefined;
|
|
692
698
|
"action.reversible"?: boolean | undefined;
|
|
699
|
+
"action.deletion_target_unresolvable"?: true | undefined;
|
|
693
700
|
} | undefined;
|
|
694
701
|
}>, {
|
|
695
702
|
name: string;
|
|
@@ -738,6 +745,7 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
|
|
|
738
745
|
"risk.category_in"?: ("destructive" | "data_loss" | "production_mutation" | "credential_access" | "secret_exfiltration" | "network_exfiltration" | "deployment_change" | "infrastructure_change" | "privilege_escalation" | "irreversible_action" | "mass_update")[] | undefined;
|
|
739
746
|
"environment.name"?: "production" | "staging" | "dev" | "local" | "unknown" | undefined;
|
|
740
747
|
"action.reversible"?: boolean | undefined;
|
|
748
|
+
"action.deletion_target_unresolvable"?: true | undefined;
|
|
741
749
|
} | undefined;
|
|
742
750
|
}, {
|
|
743
751
|
name: string;
|
|
@@ -786,6 +794,7 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
|
|
|
786
794
|
"risk.category_in"?: ("destructive" | "data_loss" | "production_mutation" | "credential_access" | "secret_exfiltration" | "network_exfiltration" | "deployment_change" | "infrastructure_change" | "privilege_escalation" | "irreversible_action" | "mass_update")[] | undefined;
|
|
787
795
|
"environment.name"?: "production" | "staging" | "dev" | "local" | "unknown" | undefined;
|
|
788
796
|
"action.reversible"?: boolean | undefined;
|
|
797
|
+
"action.deletion_target_unresolvable"?: true | undefined;
|
|
789
798
|
} | undefined;
|
|
790
799
|
}>, "many">, {
|
|
791
800
|
name: string;
|
|
@@ -834,6 +843,7 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
|
|
|
834
843
|
"risk.category_in"?: ("destructive" | "data_loss" | "production_mutation" | "credential_access" | "secret_exfiltration" | "network_exfiltration" | "deployment_change" | "infrastructure_change" | "privilege_escalation" | "irreversible_action" | "mass_update")[] | undefined;
|
|
835
844
|
"environment.name"?: "production" | "staging" | "dev" | "local" | "unknown" | undefined;
|
|
836
845
|
"action.reversible"?: boolean | undefined;
|
|
846
|
+
"action.deletion_target_unresolvable"?: true | undefined;
|
|
837
847
|
} | undefined;
|
|
838
848
|
}[], {
|
|
839
849
|
name: string;
|
|
@@ -882,6 +892,7 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
|
|
|
882
892
|
"risk.category_in"?: ("destructive" | "data_loss" | "production_mutation" | "credential_access" | "secret_exfiltration" | "network_exfiltration" | "deployment_change" | "infrastructure_change" | "privilege_escalation" | "irreversible_action" | "mass_update")[] | undefined;
|
|
883
893
|
"environment.name"?: "production" | "staging" | "dev" | "local" | "unknown" | undefined;
|
|
884
894
|
"action.reversible"?: boolean | undefined;
|
|
895
|
+
"action.deletion_target_unresolvable"?: true | undefined;
|
|
885
896
|
} | undefined;
|
|
886
897
|
}[]>>;
|
|
887
898
|
policy_packs: z.ZodDefault<z.ZodEffects<z.ZodArray<z.ZodObject<{
|
|
@@ -963,6 +974,7 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
|
|
|
963
974
|
tool: string;
|
|
964
975
|
}>, "many">>;
|
|
965
976
|
degraded_fail_posture: z.ZodDefault<z.ZodEnum<["preserve_enforcement", "fail_open"]>>;
|
|
977
|
+
safe_deletion_roots: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
966
978
|
}, "strict", z.ZodTypeAny, {
|
|
967
979
|
classifiers: {
|
|
968
980
|
patterns: {
|
|
@@ -974,6 +986,7 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
|
|
|
974
986
|
tool: string;
|
|
975
987
|
}[];
|
|
976
988
|
degraded_fail_posture: "preserve_enforcement" | "fail_open";
|
|
989
|
+
safe_deletion_roots: string[];
|
|
977
990
|
}, {
|
|
978
991
|
classifiers?: {
|
|
979
992
|
patterns: {
|
|
@@ -985,6 +998,7 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
|
|
|
985
998
|
tool: string;
|
|
986
999
|
}[] | undefined;
|
|
987
1000
|
degraded_fail_posture?: "preserve_enforcement" | "fail_open" | undefined;
|
|
1001
|
+
safe_deletion_roots?: string[] | undefined;
|
|
988
1002
|
}>, {
|
|
989
1003
|
classifiers: {
|
|
990
1004
|
patterns: {
|
|
@@ -996,6 +1010,7 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
|
|
|
996
1010
|
tool: string;
|
|
997
1011
|
}[];
|
|
998
1012
|
degraded_fail_posture: "preserve_enforcement" | "fail_open";
|
|
1013
|
+
safe_deletion_roots: string[];
|
|
999
1014
|
}, {
|
|
1000
1015
|
classifiers?: {
|
|
1001
1016
|
patterns: {
|
|
@@ -1007,6 +1022,7 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
|
|
|
1007
1022
|
tool: string;
|
|
1008
1023
|
}[] | undefined;
|
|
1009
1024
|
degraded_fail_posture?: "preserve_enforcement" | "fail_open" | undefined;
|
|
1025
|
+
safe_deletion_roots?: string[] | undefined;
|
|
1010
1026
|
}>>;
|
|
1011
1027
|
environments: z.ZodDefault<z.ZodEffects<z.ZodObject<{
|
|
1012
1028
|
resolvers: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
@@ -2660,6 +2676,7 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
|
|
|
2660
2676
|
"risk.category_in"?: ("destructive" | "data_loss" | "production_mutation" | "credential_access" | "secret_exfiltration" | "network_exfiltration" | "deployment_change" | "infrastructure_change" | "privilege_escalation" | "irreversible_action" | "mass_update")[] | undefined;
|
|
2661
2677
|
"environment.name"?: "production" | "staging" | "dev" | "local" | "unknown" | undefined;
|
|
2662
2678
|
"action.reversible"?: boolean | undefined;
|
|
2679
|
+
"action.deletion_target_unresolvable"?: true | undefined;
|
|
2663
2680
|
} | undefined;
|
|
2664
2681
|
}[];
|
|
2665
2682
|
policy_packs: {
|
|
@@ -2681,6 +2698,7 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
|
|
|
2681
2698
|
tool: string;
|
|
2682
2699
|
}[];
|
|
2683
2700
|
degraded_fail_posture: "preserve_enforcement" | "fail_open";
|
|
2701
|
+
safe_deletion_roots: string[];
|
|
2684
2702
|
};
|
|
2685
2703
|
environments: {
|
|
2686
2704
|
resolvers: {
|
|
@@ -2974,6 +2992,7 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
|
|
|
2974
2992
|
"risk.category_in"?: ("destructive" | "data_loss" | "production_mutation" | "credential_access" | "secret_exfiltration" | "network_exfiltration" | "deployment_change" | "infrastructure_change" | "privilege_escalation" | "irreversible_action" | "mass_update")[] | undefined;
|
|
2975
2993
|
"environment.name"?: "production" | "staging" | "dev" | "local" | "unknown" | undefined;
|
|
2976
2994
|
"action.reversible"?: boolean | undefined;
|
|
2995
|
+
"action.deletion_target_unresolvable"?: true | undefined;
|
|
2977
2996
|
} | undefined;
|
|
2978
2997
|
}[] | undefined;
|
|
2979
2998
|
policy_packs?: {
|
|
@@ -2995,6 +3014,7 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
|
|
|
2995
3014
|
tool: string;
|
|
2996
3015
|
}[] | undefined;
|
|
2997
3016
|
degraded_fail_posture?: "preserve_enforcement" | "fail_open" | undefined;
|
|
3017
|
+
safe_deletion_roots?: string[] | undefined;
|
|
2998
3018
|
} | undefined;
|
|
2999
3019
|
environments?: {
|
|
3000
3020
|
resolvers?: {
|
|
@@ -3288,6 +3308,7 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
|
|
|
3288
3308
|
"risk.category_in"?: ("destructive" | "data_loss" | "production_mutation" | "credential_access" | "secret_exfiltration" | "network_exfiltration" | "deployment_change" | "infrastructure_change" | "privilege_escalation" | "irreversible_action" | "mass_update")[] | undefined;
|
|
3289
3309
|
"environment.name"?: "production" | "staging" | "dev" | "local" | "unknown" | undefined;
|
|
3290
3310
|
"action.reversible"?: boolean | undefined;
|
|
3311
|
+
"action.deletion_target_unresolvable"?: true | undefined;
|
|
3291
3312
|
} | undefined;
|
|
3292
3313
|
}[];
|
|
3293
3314
|
policy_packs: {
|
|
@@ -3309,6 +3330,7 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
|
|
|
3309
3330
|
tool: string;
|
|
3310
3331
|
}[];
|
|
3311
3332
|
degraded_fail_posture: "preserve_enforcement" | "fail_open";
|
|
3333
|
+
safe_deletion_roots: string[];
|
|
3312
3334
|
};
|
|
3313
3335
|
environments: {
|
|
3314
3336
|
resolvers: {
|
|
@@ -3602,6 +3624,7 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
|
|
|
3602
3624
|
"risk.category_in"?: ("destructive" | "data_loss" | "production_mutation" | "credential_access" | "secret_exfiltration" | "network_exfiltration" | "deployment_change" | "infrastructure_change" | "privilege_escalation" | "irreversible_action" | "mass_update")[] | undefined;
|
|
3603
3625
|
"environment.name"?: "production" | "staging" | "dev" | "local" | "unknown" | undefined;
|
|
3604
3626
|
"action.reversible"?: boolean | undefined;
|
|
3627
|
+
"action.deletion_target_unresolvable"?: true | undefined;
|
|
3605
3628
|
} | undefined;
|
|
3606
3629
|
}[] | undefined;
|
|
3607
3630
|
policy_packs?: {
|
|
@@ -3623,6 +3646,7 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
|
|
|
3623
3646
|
tool: string;
|
|
3624
3647
|
}[] | undefined;
|
|
3625
3648
|
degraded_fail_posture?: "preserve_enforcement" | "fail_open" | undefined;
|
|
3649
|
+
safe_deletion_roots?: string[] | undefined;
|
|
3626
3650
|
} | undefined;
|
|
3627
3651
|
environments?: {
|
|
3628
3652
|
resolvers?: {
|