@lannguyensi/harness 0.48.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.
Files changed (93) hide show
  1. package/CHANGELOG.md +825 -0
  2. package/dist/cli/add/index.js +15 -3
  3. package/dist/cli/add/index.js.map +1 -1
  4. package/dist/cli/apply/apply.js +19 -3
  5. package/dist/cli/apply/apply.js.map +1 -1
  6. package/dist/cli/approve/risk.d.ts +28 -2
  7. package/dist/cli/approve/risk.js +14 -4
  8. package/dist/cli/approve/risk.js.map +1 -1
  9. package/dist/cli/diff/index.js +22 -1
  10. package/dist/cli/diff/index.js.map +1 -1
  11. package/dist/cli/doctor/format.js +29 -2
  12. package/dist/cli/doctor/format.js.map +1 -1
  13. package/dist/cli/doctor/index.js +62 -3
  14. package/dist/cli/doctor/index.js.map +1 -1
  15. package/dist/cli/doctor/types.d.ts +57 -0
  16. package/dist/cli/explain-policy.d.ts +4 -1
  17. package/dist/cli/explain-policy.js +12 -2
  18. package/dist/cli/explain-policy.js.map +1 -1
  19. package/dist/cli/export.js +12 -1
  20. package/dist/cli/export.js.map +1 -1
  21. package/dist/cli/index.js +29 -7
  22. package/dist/cli/index.js.map +1 -1
  23. package/dist/cli/init/templates.d.ts +34 -1
  24. package/dist/cli/init/templates.js +137 -3
  25. package/dist/cli/init/templates.js.map +1 -1
  26. package/dist/cli/list.js +6 -0
  27. package/dist/cli/list.js.map +1 -1
  28. package/dist/cli/loader.js +15 -0
  29. package/dist/cli/loader.js.map +1 -1
  30. package/dist/cli/pack/hook-codex-pre-tool-use.js +11 -6
  31. package/dist/cli/pack/hook-codex-pre-tool-use.js.map +1 -1
  32. package/dist/cli/pack/hook-post-tool-use.d.ts +6 -4
  33. package/dist/cli/pack/hook-post-tool-use.js.map +1 -1
  34. package/dist/cli/pack/hook-pre-tool-use.js +23 -22
  35. package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
  36. package/dist/cli/policy/intercept.js +59 -9
  37. package/dist/cli/policy/intercept.js.map +1 -1
  38. package/dist/cli/remove/index.d.ts +12 -0
  39. package/dist/cli/remove/index.js +89 -0
  40. package/dist/cli/remove/index.js.map +1 -1
  41. package/dist/cli/validate/checks.d.ts +49 -0
  42. package/dist/cli/validate/checks.js +416 -9
  43. package/dist/cli/validate/checks.js.map +1 -1
  44. package/dist/cli/validate/index.js +11 -0
  45. package/dist/cli/validate/index.js.map +1 -1
  46. package/dist/policy-packs/builtin/understanding-before-execution/index.d.ts +2 -2
  47. package/dist/policy-packs/builtin/understanding-before-execution/index.js +33 -27
  48. package/dist/policy-packs/builtin/understanding-before-execution/index.js.map +1 -1
  49. package/dist/policy-packs/builtin/understanding-before-execution/markers.js +2 -2
  50. package/dist/policy-packs/builtin/understanding-before-execution/markers.js.map +1 -1
  51. package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.d.ts +46 -8
  52. package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.js +71 -12
  53. package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.js.map +1 -1
  54. package/dist/policy-packs/builtin/understanding-before-execution/post-tool-use-boundary.js +6 -3
  55. package/dist/policy-packs/builtin/understanding-before-execution/post-tool-use-boundary.js.map +1 -1
  56. package/dist/policy-packs/builtin/understanding-before-execution.js +9 -8
  57. package/dist/policy-packs/builtin/understanding-before-execution.js.map +1 -1
  58. package/dist/runtime/bash-prefix-parse.d.ts +12 -1
  59. package/dist/runtime/bash-prefix-parse.js +2 -2
  60. package/dist/runtime/bash-prefix-parse.js.map +1 -1
  61. package/dist/runtime/command-normalize.d.ts +112 -0
  62. package/dist/runtime/command-normalize.js +169 -55
  63. package/dist/runtime/command-normalize.js.map +1 -1
  64. package/dist/runtime/deletion-target-resolve.d.ts +28 -0
  65. package/dist/runtime/deletion-target-resolve.js +1055 -0
  66. package/dist/runtime/deletion-target-resolve.js.map +1 -0
  67. package/dist/runtime/index.d.ts +1 -0
  68. package/dist/runtime/index.js +1 -0
  69. package/dist/runtime/index.js.map +1 -1
  70. package/dist/runtime/intercept.js +17 -1
  71. package/dist/runtime/intercept.js.map +1 -1
  72. package/dist/runtime/kubectl-target-parse.d.ts +31 -0
  73. package/dist/runtime/kubectl-target-parse.js +249 -0
  74. package/dist/runtime/kubectl-target-parse.js.map +1 -0
  75. package/dist/runtime/read-only-bash.d.ts +11 -0
  76. package/dist/runtime/read-only-bash.js +288 -7
  77. package/dist/runtime/read-only-bash.js.map +1 -1
  78. package/dist/runtime/risk-classifier.js +14 -1
  79. package/dist/runtime/risk-classifier.js.map +1 -1
  80. package/dist/runtime/when-eval.d.ts +11 -2
  81. package/dist/runtime/when-eval.js +25 -0
  82. package/dist/runtime/when-eval.js.map +1 -1
  83. package/dist/runtime/workflow-policies.d.ts +164 -0
  84. package/dist/runtime/workflow-policies.js +439 -0
  85. package/dist/runtime/workflow-policies.js.map +1 -0
  86. package/dist/schema/index.d.ts +24 -0
  87. package/dist/schema/policies.d.ts +25 -0
  88. package/dist/schema/policies.js +29 -0
  89. package/dist/schema/policies.js.map +1 -1
  90. package/dist/schema/risk.d.ts +6 -0
  91. package/dist/schema/risk.js +80 -0
  92. package/dist/schema/risk.js.map +1 -1
  93. package/package.json +1 -1
@@ -0,0 +1,164 @@
1
+ import type { Manifest, Policy, Workflow } from "../schema/index.js";
2
+ /**
3
+ * Runtime enforcement for `workflows:` (99f47307, Slice 1).
4
+ *
5
+ * `manifest.workflows[]` describes a review-then-merge process (schema:
6
+ * `src/schema/workflows.ts`), but until now nothing read it at
7
+ * enforcement time: `src/runtime/intercept.ts` (the Risk Gate / policy
8
+ * engine `harness policy intercept` runs on every PreToolUse) only ever
9
+ * evaluates `manifest.policies[]`. A `workflows:` declaration alone was
10
+ * documentation, not a gate (`docs/for-agents.md`, pre-Slice-1 wording:
11
+ * "The schema cannot enforce that today").
12
+ *
13
+ * This module closes that gap for exactly ONE step pairing: a
14
+ * `review_subagent` step with `spawn: "required"` followed (at any
15
+ * later index in the same workflow) by a `merge` step. When BOTH
16
+ * `require-review-evidence` and `require-review-evidence-bash` are
17
+ * declared in `manifest.hooks[]`, `deriveWorkflowGatePolicies` returns
18
+ * the SAME policy pair `harness init --template full` would hand-author
19
+ * (`src/cli/init/templates.ts` `review-before-merge` /
20
+ * `review-before-merge-bash`), renamed per workflow so the provenance
21
+ * is visible in `harness list policies` / `harness explain`.
22
+ *
23
+ * Deliberately does NOT touch `src/runtime/intercept.ts`: the engine
24
+ * already knows how to evaluate a `Policy`, so the only thing missing
25
+ * was a `Policy` to hand it. `src/cli/loader.ts#loadManifest` calls
26
+ * `withDerivedPolicies` after parsing and appends the result to
27
+ * `manifest.policies` before any consumer (the CLI `policy intercept`
28
+ * entrypoint, `list`, `explain`, `explain-policy`) reads it.
29
+ *
30
+ * TWO VIEWS OF ONE MANIFEST (review round 3, 99f47307 Slice 1). Every
31
+ * reader of a parsed manifest sees exactly one of:
32
+ *
33
+ * - the HAND-AUTHORED view: `parseManifest(...)` as-is, only what the
34
+ * operator wrote under `policies:`. Right for anything that writes a
35
+ * manifest back to disk or serialises it for later comparison
36
+ * (`harness export`, the `.last-apply` manifest snapshot, `add` /
37
+ * `remove` / `adopt` / `pack reseed` file rewrites, the schema gate in
38
+ * `io/validate-before-write.ts`), because a derived policy written out
39
+ * as if hand-authored would be re-derived on top of itself next time.
40
+ * `withoutDerivedPolicies` turns a derived view back into this one.
41
+ * - the DERIVED view: `withDerivedPolicies(parseManifest(...))`, hand-
42
+ * authored policies plus the workflow gate pair. Right for enforcement
43
+ * (`policy intercept`, hook entrypoints), validation (`validate`,
44
+ * `doctor`, `add`'s asset gate), display (`list`, `explain[-policy]`,
45
+ * `describe`, `dry-run`), and every COMPARISON where the other side is
46
+ * also derived (`diff --since` ref side vs working side; the
47
+ * `.last-apply` snapshot vs the current manifest in restart hints).
48
+ * `loadManifest` hands out this view; the few readers that parse
49
+ * without it (`validate`, `diff`'s ref side, `apply`'s snapshot
50
+ * reader, `add`'s gate) call `withDerivedPolicies` themselves.
51
+ *
52
+ * Both helpers are idempotent and accept either view as input (they
53
+ * partition on `isDerivedPolicy`), so a reader can never "double derive"
54
+ * or strip a hand-authored policy by calling them on the wrong view.
55
+ * Review rounds 1 and 2 each found one reader on the wrong side of this
56
+ * line (`apply` vs `validate`; `diff --since`'s two sides); the table
57
+ * above plus tests/cli/manifest-view-parity.test.ts are the guard.
58
+ *
59
+ * Fail direction: when the two evidence hooks are NOT wired, this
60
+ * returns `[]` for every workflow rather than deriving an
61
+ * unenforceable policy (there would be no hook name to bind it to, and
62
+ * `manifest.policies[].hook` must reference a declared hook, see
63
+ * `ManifestSchema`'s superRefine in `src/schema/index.ts`). That silent
64
+ * non-enforcement case is exactly what `checkWorkflowGateWiring`
65
+ * (`src/cli/validate/checks.ts`) turns into a loud `validate` error, so
66
+ * "declared `spawn: required`, gate never wired" cannot pass silently.
67
+ */
68
+ /** The two hooks templates.ts wires for the hand-authored merge gate. */
69
+ export declare const REVIEW_EVIDENCE_HOOK_MCP = "require-review-evidence";
70
+ export declare const REVIEW_EVIDENCE_HOOK_BASH = "require-review-evidence-bash";
71
+ export declare const MERGE_MCP_MATCH = "mcp__agent-tasks__pull_requests_merge";
72
+ export declare const MERGE_BASH_MATCH = "(^|\\n|;|\\||&|\\()\\s*(\\w+=\\S+\\s+)*gh pr merge\\b";
73
+ /**
74
+ * True when `workflow.steps` contains a `review_subagent` step with
75
+ * `spawn: "required"` at an index earlier than some `merge` step
76
+ * (not necessarily adjacent; a `ci_gate` or another step may sit
77
+ * between them). A workflow with no `merge` step at all, or whose only
78
+ * `review_subagent` steps are `spawn: "optional"` / `"skip"`, returns
79
+ * `false`.
80
+ */
81
+ export declare function workflowRequiresMergeGate(workflow: Workflow): boolean;
82
+ /** True when both merge-gate evidence hooks are declared in `manifest.hooks[]`. */
83
+ export declare function hasWiredMergeGateHooks(manifest: Manifest): boolean;
84
+ /** True when `policy` is an object this module derived (see `withDerivedPolicies`). */
85
+ export declare function isDerivedPolicy(policy: Policy): boolean;
86
+ /**
87
+ * The hand-authored slice of `manifest.policies`: everything the operator
88
+ * wrote under `policies:`, none of the workflow-derived entries. Works on
89
+ * either view (see module doc); on a hand-authored view it is the
90
+ * identity.
91
+ */
92
+ export declare function handAuthoredPolicies(manifest: Manifest): Policy[];
93
+ /** One weaker hand-authored policy sharing a derived gate's trigger surface. */
94
+ export interface WeakGatePolicyOverlap {
95
+ /** The workflow whose `spawn: "required"` step derives the gate. */
96
+ workflowName: string;
97
+ /** Name of the derived policy that is ALSO in force (`workflow:<name>:review-before-merge[-bash]`). */
98
+ derivedPolicyName: string;
99
+ /** Human-readable surface label for the message (`mcp__agent-tasks__pull_requests_merge` or `` `gh pr merge` (Bash) ``). */
100
+ surface: string;
101
+ /** The weaker hand-authored policy's name. */
102
+ handPolicyName: string;
103
+ /** Why it does not stand in for the derived gate (see `nonEquivalenceReason`). */
104
+ reason: string;
105
+ }
106
+ /**
107
+ * Derive the merge-gate `Policy` pair for every workflow that needs one
108
+ * and does not already have an equivalent policy (hand-authored, or
109
+ * derived from an earlier workflow in the same manifest) intercepting
110
+ * the same surface for the same evidence tag.
111
+ *
112
+ * Returns `[]` when `manifest.hooks[]` is missing either evidence hook
113
+ * (see module doc: that gap is a `validate` error, not silent
114
+ * derivation of an unenforceable policy) or when no workflow declares
115
+ * a `spawn: "required"` review step followed by a merge step.
116
+ */
117
+ export declare function deriveWorkflowGatePolicies(manifest: Manifest): Policy[];
118
+ /**
119
+ * Every hand-authored policy that shares an ACTUALLY DERIVED gate's
120
+ * trigger surface + ledger_tag but does not stand in for it: weaker
121
+ * (F1, review round 2) or extracting its variables differently (F4,
122
+ * review round 3). Both policies apply in that case, and the overlap is
123
+ * worth flagging: an operator reading `enforcement: warn` on a
124
+ * `two-reviewers-required`-shaped policy might reasonably believe THAT
125
+ * is the only gate on the surface. `src/cli/validate/checks.ts` turns
126
+ * each entry into a warning Diagnostic; `src/cli/doctor/index.ts`
127
+ * renders the same list in the Workflows section.
128
+ *
129
+ * Mirrors `deriveWorkflowGatePolicies` exactly (same walk, same `seen`
130
+ * set): a surface an equivalent hand-authored policy already covers
131
+ * derives nothing and therefore reports nothing, even when a second,
132
+ * weaker policy sits on it too. Returns `[]` when the evidence hooks are
133
+ * not wired or no workflow needs a gate.
134
+ */
135
+ export declare function findWeakGatePolicyOverlaps(manifest: Manifest): WeakGatePolicyOverlap[];
136
+ /**
137
+ * The ONE place `workflows[]` gets folded into `manifest.policies` (F2,
138
+ * review round 2). Before this, `src/cli/loader.ts#loadManifest` inlined
139
+ * the append, so every OTHER manifest consumer that parses the manifest
140
+ * a different way (`src/cli/validate/index.ts`'s `loadMergedRaw` +
141
+ * `parseManifest`, notably) saw a manifest with no derived policies at
142
+ * all, and `apply` and `validate` diverged. See the module doc for the
143
+ * full list of readers and which view each one takes.
144
+ *
145
+ * Idempotent and view-agnostic (review round 3): the derivation runs on
146
+ * `handAuthoredPolicies(manifest)` only and any derived entries already
147
+ * present are replaced, so calling this on an already-derived view yields
148
+ * the same policy names with no duplicates. Returns the input object
149
+ * unchanged when there is nothing to derive and nothing to replace.
150
+ *
151
+ * Registers each derived `Policy` object in the module-level
152
+ * `derivedPolicyRegistry` (F7) so `isDerivedPolicy` can later distinguish
153
+ * it from a hand-authored one (used by `harness export`'s filter and
154
+ * `list`/`doctor`'s "(derived from workflows[])" marker).
155
+ */
156
+ export declare function withDerivedPolicies(manifest: Manifest): Manifest;
157
+ /**
158
+ * The hand-authored view of a manifest that may carry derived policies:
159
+ * what `harness export` emits and what the `.last-apply` manifest
160
+ * snapshot stores, so nothing that gets written out or re-read later
161
+ * carries a derived policy as if the operator had declared it. Returns
162
+ * the input object unchanged when it carries no derived policy.
163
+ */
164
+ export declare function withoutDerivedPolicies(manifest: Manifest): Manifest;
@@ -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":"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"}