@lannguyensi/harness 0.30.1 → 0.32.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 (53) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +1 -0
  3. package/dist/cli/index.js +59 -0
  4. package/dist/cli/index.js.map +1 -1
  5. package/dist/cli/init/templates.d.ts +1 -1
  6. package/dist/cli/init/templates.js +15 -0
  7. package/dist/cli/init/templates.js.map +1 -1
  8. package/dist/cli/pack/hook-branch-protection.js +2 -1
  9. package/dist/cli/pack/hook-branch-protection.js.map +1 -1
  10. package/dist/cli/pack/hook-codex-pre-tool-use.js +4 -3
  11. package/dist/cli/pack/hook-codex-pre-tool-use.js.map +1 -1
  12. package/dist/cli/pack/hook-codex-stop.js +1 -0
  13. package/dist/cli/pack/hook-codex-stop.js.map +1 -1
  14. package/dist/cli/pack/hook-post-tool-use.js +1 -0
  15. package/dist/cli/pack/hook-post-tool-use.js.map +1 -1
  16. package/dist/cli/pack/hook-pre-tool-use.js +3 -2
  17. package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
  18. package/dist/cli/pack/hook-runtime-reality.d.ts +50 -0
  19. package/dist/cli/pack/hook-runtime-reality.js +160 -0
  20. package/dist/cli/pack/hook-runtime-reality.js.map +1 -0
  21. package/dist/cli/pack/hook-solution-acceptance-writeguard.d.ts +26 -0
  22. package/dist/cli/pack/hook-solution-acceptance-writeguard.js +187 -0
  23. package/dist/cli/pack/hook-solution-acceptance-writeguard.js.map +1 -0
  24. package/dist/cli/pack/hook-solution-acceptance.d.ts +26 -0
  25. package/dist/cli/pack/hook-solution-acceptance.js +237 -0
  26. package/dist/cli/pack/hook-solution-acceptance.js.map +1 -0
  27. package/dist/cli/pause/index.d.ts +17 -3
  28. package/dist/cli/pause/index.js +30 -6
  29. package/dist/cli/pause/index.js.map +1 -1
  30. package/dist/cli/policy/intercept.js +7 -1
  31. package/dist/cli/policy/intercept.js.map +1 -1
  32. package/dist/cli/session-start/branch-check.js +4 -2
  33. package/dist/cli/session-start/branch-check.js.map +1 -1
  34. package/dist/cli/session-start/index.js +4 -2
  35. package/dist/cli/session-start/index.js.map +1 -1
  36. package/dist/cli/validate/checks.js +38 -0
  37. package/dist/cli/validate/checks.js.map +1 -1
  38. package/dist/policy-packs/builtin/solution-acceptance-runtime.d.ts +119 -0
  39. package/dist/policy-packs/builtin/solution-acceptance-runtime.js +289 -0
  40. package/dist/policy-packs/builtin/solution-acceptance-runtime.js.map +1 -0
  41. package/dist/policy-packs/builtin/solution-acceptance.d.ts +44 -0
  42. package/dist/policy-packs/builtin/solution-acceptance.js +185 -0
  43. package/dist/policy-packs/builtin/solution-acceptance.js.map +1 -0
  44. package/dist/policy-packs/registry.d.ts +1 -1
  45. package/dist/policy-packs/registry.js +10 -0
  46. package/dist/policy-packs/registry.js.map +1 -1
  47. package/dist/runtime/risk-classifier.js +64 -0
  48. package/dist/runtime/risk-classifier.js.map +1 -1
  49. package/dist/runtime/session-id.d.ts +4 -3
  50. package/dist/runtime/session-id.js +17 -5
  51. package/dist/runtime/session-id.js.map +1 -1
  52. package/package.json +3 -1
  53. package/scripts/runtime-reality-docker-probe.mjs +71 -0
@@ -0,0 +1,289 @@
1
+ // Builtin Policy Pack runtime: `solution-acceptance` (consumer half).
2
+ //
3
+ // The PRODUCER lives in grounding-mcp (`solution_evaluate` / `solution_gate`,
4
+ // @lannguyensi/grounding-mcp >= 0.3.2): it runs a real `preflight run --json`
5
+ // and records a HEAD-pinned verdict marker outside the agent-writable
6
+ // evidence ledger. harness is the CONSUMER: it reimplements the marker read +
7
+ // gate decision here so it carries NO grounding-mcp runtime dependency (the
8
+ // same precedent as understanding-before-execution reimplementing its
9
+ // approval-marker read). Schema parity with the producer is pinned by a
10
+ // golden-fixture test against a real 0.3.2 marker; if grounding-mcp changes
11
+ // the Verdict shape, that test fails loudly.
12
+ //
13
+ // Two enforcement surfaces are built on top of these helpers (see the pack
14
+ // definition + the two hook verbs):
15
+ //
16
+ // 1. completion-gate — denies the task-finishing tools (agent-tasks MCP
17
+ // task_finish / task_submit_pr / task_merge / pull_requests_merge, plus
18
+ // a belt-and-suspenders bash match for `git push` / `gh pr merge`)
19
+ // unless a ready verdict exists at the CURRENT git HEAD.
20
+ //
21
+ // 2. write-guard — the actual anti-forgery closure. Relocating the verdict
22
+ // dir is NOT sufficient: understanding-gate allows all Bash post-approval
23
+ // (it does no write-target inspection in the allow path), so an approved
24
+ // agent could otherwise hand-write a `ready:true` marker. The write-guard
25
+ // blocks the agent's enumerated write paths into the verdict dir.
26
+ //
27
+ // Anti-forgery honesty (operator decision, 2026-05-30): v1 closes the
28
+ // ENUMERATED-WRITE-PATH residual (the Bash/Edit/Write spellings below), NOT
29
+ // arbitrary same-uid forgery. A `0500` chmod on the dir would be incoherent
30
+ // here because the producer (grounding-mcp) and the agent share a uid, so it
31
+ // would block the producer too. Content-authenticity against an unguarded
32
+ // write primitive is closed by marker signing, a cross-repo follow-up that
33
+ // already touches the producer. This module must NOT claim the residual is
34
+ // fully closed.
35
+ import * as fs from "node:fs";
36
+ import * as os from "node:os";
37
+ import * as path from "node:path";
38
+ export const PACK_NAME = "solution-acceptance";
39
+ /**
40
+ * agent-tasks MCP verbs that mark a completion boundary. The gate fires on
41
+ * these (matched by exact tool name `mcp__agent-tasks__<verb>`). These MCP
42
+ * choke points are reliable: unlike the bash matcher they cannot be evaded
43
+ * by shell indirection.
44
+ */
45
+ export const DEFAULT_PROTECTED_COMPLETION_TOOLS = [
46
+ "task_finish",
47
+ "task_submit_pr",
48
+ "task_merge",
49
+ "pull_requests_merge",
50
+ ];
51
+ /**
52
+ * Belt-and-suspenders bash matcher for `git push` / `gh pr merge`. Regex on
53
+ * the typed command, so an env-var indirection
54
+ * (`B=main && git push origin $B`) evades it — the MCP verbs above are the
55
+ * load-bearing choke points; hardening this is follow-up `7207d8f9`.
56
+ * Tolerates a leading `cd … &&`, inline `VAR=val` assignments, and `git -C
57
+ * <path> push`.
58
+ */
59
+ export const DEFAULT_PUSH_BASH_RE = /(?:^|\n|;|\||&&|\()\s*(?:\w+=\S+\s+)*(?:git(?:\s+-C\s+\S+)?\s+push|gh\s+pr\s+merge)\b/;
60
+ /**
61
+ * Resolve the completion verbs the gate fires on: the pack's
62
+ * `config.protected_completion_tools` override, else the default set.
63
+ * Always non-empty. Lives here (not in the pack module) so the
64
+ * completion-gate hook can share it without importing the pack's zod
65
+ * surface (mirrors `resolveProtectedBranches` in branch-protection-runtime).
66
+ */
67
+ export function resolveProtectedCompletionTools(pack) {
68
+ const raw = pack.config["protected_completion_tools"];
69
+ if (Array.isArray(raw) &&
70
+ raw.length > 0 &&
71
+ raw.every((t) => typeof t === "string" && t.length > 0)) {
72
+ return raw;
73
+ }
74
+ return [...DEFAULT_PROTECTED_COMPLETION_TOOLS];
75
+ }
76
+ /** Env knob that overrides the verdict directory (mirrors the producer). */
77
+ export const VERDICT_DIR_ENV = "SOLUTION_VERDICT_DIR";
78
+ /**
79
+ * Stable tail of the default verdict dir. The write-guard's reference
80
+ * detection matches on this so ANY spelling of the home prefix is caught
81
+ * (`~/.local/state/...`, `$HOME/...`, `$XDG_STATE_HOME/...`, the literal
82
+ * absolute path).
83
+ */
84
+ export const VERDICT_DIR_TAIL = path.join("agent-grounding", "solution-verdicts");
85
+ /**
86
+ * Resolve the verdict directory. Resolution order MUST match grounding-mcp's
87
+ * `verdictDir()` so the consumer reads exactly where the producer writes
88
+ * (operator decision B: both sides use the producer default; no apply-time
89
+ * env threading, no divergence risk):
90
+ * 1. SOLUTION_VERDICT_DIR
91
+ * 2. $XDG_STATE_HOME/agent-grounding/solution-verdicts
92
+ * 3. ~/.local/state/agent-grounding/solution-verdicts
93
+ */
94
+ export function verdictDir(env = process.env, homedir = os.homedir) {
95
+ const override = env[VERDICT_DIR_ENV];
96
+ if (override && override.trim().length > 0)
97
+ return override;
98
+ const xdg = env["XDG_STATE_HOME"];
99
+ const base = xdg && xdg.trim().length > 0 ? xdg : path.join(homedir(), ".local", "state");
100
+ return path.join(base, "agent-grounding", "solution-verdicts");
101
+ }
102
+ /**
103
+ * Reduce a verdict id to a single safe path segment. Mirrors the producer's
104
+ * `sanitizeVerdictId`: non-portable chars collapse to `_`, `path.basename`
105
+ * strips any residual separator (path-traversal guard), empty / dot-only ids
106
+ * are rejected.
107
+ */
108
+ export function sanitizeVerdictId(id) {
109
+ const cleaned = id.replace(/[^A-Za-z0-9._-]/g, "_");
110
+ const base = path.basename(cleaned);
111
+ if (base === "" || base === "." || base === "..") {
112
+ throw new Error(`invalid verdict id: ${JSON.stringify(id)}`);
113
+ }
114
+ return base;
115
+ }
116
+ export function verdictPathFor(dir, id) {
117
+ return path.join(dir, `${sanitizeVerdictId(id)}.json`);
118
+ }
119
+ /**
120
+ * Read + validate the verdict marker for `id`, or null when it is absent,
121
+ * unparseable, a symlink, or not a regular file. The lstat + symlink reject
122
+ * mirrors `checkApprovalMarker`: defense-in-depth against a symlink planted
123
+ * at the marker path pointing at agent-controlled content.
124
+ */
125
+ export function readVerdict(dir, id) {
126
+ let p;
127
+ try {
128
+ p = verdictPathFor(dir, id);
129
+ }
130
+ catch {
131
+ return null; // invalid id
132
+ }
133
+ let stat;
134
+ try {
135
+ stat = fs.lstatSync(p);
136
+ }
137
+ catch {
138
+ return null;
139
+ }
140
+ if (stat.isSymbolicLink() || !stat.isFile())
141
+ return null;
142
+ let raw;
143
+ try {
144
+ raw = fs.readFileSync(p, "utf8");
145
+ }
146
+ catch {
147
+ return null;
148
+ }
149
+ try {
150
+ const parsed = JSON.parse(raw);
151
+ if (typeof parsed.id !== "string" ||
152
+ typeof parsed.head !== "string" ||
153
+ typeof parsed.ready !== "boolean") {
154
+ return null;
155
+ }
156
+ return {
157
+ id: parsed.id,
158
+ head: parsed.head,
159
+ ready: parsed.ready,
160
+ confidence: typeof parsed.confidence === "number" ? parsed.confidence : 0,
161
+ blockers: Array.isArray(parsed.blockers) ? parsed.blockers : [],
162
+ timestamp: typeof parsed.timestamp === "string" ? parsed.timestamp : "",
163
+ source: typeof parsed.source === "string" ? parsed.source : "",
164
+ };
165
+ }
166
+ catch {
167
+ return null;
168
+ }
169
+ }
170
+ /**
171
+ * Evaluate the gate for `id` at `currentHead`. Mirrors grounding-mcp
172
+ * `evaluateGate` EXACTLY: allow iff `verdict.ready === true` AND
173
+ * `verdict.head === currentHead`. `confidence` is INFORMATIONAL ONLY and
174
+ * never gates — a `ready:true confidence:0` verdict at HEAD passes — so the
175
+ * harness consumer stays byte-parity with the producer's `solution_gate`
176
+ * (an operator running `solution_gate` and the harness gate must agree).
177
+ */
178
+ export function evaluateGate(verdict, currentHead, id) {
179
+ if (!verdict) {
180
+ return {
181
+ allowed: false,
182
+ reason: `no solution-acceptance verdict recorded for "${id}" (run mcp__agent-grounding__solution_evaluate first)`,
183
+ verdict: null,
184
+ };
185
+ }
186
+ if (!verdict.ready) {
187
+ const why = verdict.blockers.length > 0 ? `: ${verdict.blockers.join("; ")}` : "";
188
+ return {
189
+ allowed: false,
190
+ reason: `solution-acceptance verdict for "${id}" is not ready${why} (fix, then re-run solution_evaluate)`,
191
+ verdict,
192
+ };
193
+ }
194
+ if (!currentHead) {
195
+ return {
196
+ allowed: false,
197
+ reason: `cannot resolve the current git HEAD to confirm the verdict for "${id}" is at HEAD`,
198
+ verdict,
199
+ };
200
+ }
201
+ if (verdict.head !== currentHead) {
202
+ return {
203
+ allowed: false,
204
+ reason: `stale solution-acceptance verdict for "${id}": recorded at ${verdict.head.slice(0, 7)}, current HEAD ${currentHead.slice(0, 7)} (re-run solution_evaluate after the rework)`,
205
+ verdict,
206
+ };
207
+ }
208
+ return {
209
+ allowed: true,
210
+ reason: `solution-acceptance verdict for "${id}" is ready at HEAD ${currentHead.slice(0, 7)} (confidence ${Math.round(verdict.confidence * 100)}%)`,
211
+ verdict,
212
+ };
213
+ }
214
+ // ── Write-guard target detection ──
215
+ /**
216
+ * Is `target` inside `dir` after resolution? Used for the path-tool arm
217
+ * (Write/Edit/MultiEdit/NotebookEdit `file_path`) and for a Bash shell whose
218
+ * cwd is the protected dir. A relative `target` resolves against `cwd`
219
+ * (falling back to process.cwd()).
220
+ */
221
+ export function isInsideDir(target, dir, cwd) {
222
+ if (typeof target !== "string" || target.length === 0)
223
+ return false;
224
+ const absDir = path.resolve(dir);
225
+ const absTarget = path.isAbsolute(target)
226
+ ? path.resolve(target)
227
+ : path.resolve(cwd ?? process.cwd(), target);
228
+ const rel = path.relative(absDir, absTarget);
229
+ return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
230
+ }
231
+ /**
232
+ * Does a Bash command TEXTUALLY reference the verdict dir? Catches the
233
+ * enumerated spellings without shell-evaluating (same contract as
234
+ * read-only-bash):
235
+ * - the literal absolute dir,
236
+ * - the `$SOLUTION_VERDICT_DIR` env token,
237
+ * - the stable tail `agent-grounding/solution-verdicts` (covers `~/...`,
238
+ * `$HOME/...`, `$XDG_STATE_HOME/...`, and absolute spellings), and
239
+ * - the dir's LEAF segment (`solution-verdicts` for the default).
240
+ *
241
+ * The leaf segment closes the `cd <parent> && write <relative-into-dir>`
242
+ * descent (where the parent path and the child redirect never form the
243
+ * contiguous tail): ANY relative write into the dir from a cwd that is not
244
+ * the dir itself must name the leaf somewhere in the command, and a
245
+ * `cd <…/leaf>` to first make cwd==dir would itself contain the leaf. The
246
+ * write-guard's cwd-inside check covers the only remaining case (cwd already
247
+ * inside the dir). The leaf needle is length-guarded so a short custom
248
+ * basename does not over-block; the default leaf is distinctive, and a
249
+ * non-default dir already warns at validate time.
250
+ *
251
+ * `chmod`/`chattr` that target the dir are caught the same way, so the
252
+ * FS-perm-loosening attack is covered.
253
+ *
254
+ * Honest residual: a path constructed at runtime inside an interpreter with
255
+ * no textual reference (e.g. base64-decoded inside `python3 -c`) is NOT
256
+ * caught. That is what marker signing (follow-up) closes.
257
+ */
258
+ export function bashReferencesVerdictDir(command, dir) {
259
+ if (typeof command !== "string" || command.length === 0)
260
+ return false;
261
+ const leaf = path.basename(dir);
262
+ // Direct literal references + the distinctive leaf segment.
263
+ if (command.includes(dir) ||
264
+ command.includes(VERDICT_DIR_ENV) ||
265
+ command.includes(VERDICT_DIR_TAIL) ||
266
+ (leaf.length >= 6 && command.includes(leaf))) {
267
+ return true;
268
+ }
269
+ // Glob-obscured references. bash expands `*?[` against EXISTING paths at
270
+ // runtime, so a glob like `solution-ver*/<id>.json` reaches the dir
271
+ // without the literal leaf ever appearing in the command text, and a
272
+ // matching glob can OVERWRITE an existing marker (flipping ready:false ->
273
+ // true). We cannot safely expand globs (that is the shell-eval surface
274
+ // read-only-bash refuses), so when a glob metachar is present we match the
275
+ // leaf's distinctive sub-words: a single glob can split the hyphenated
276
+ // leaf but not erase every >=6-char word of it (`solution-ver*` keeps
277
+ // "solution"; `solu*verdicts` keeps "verdicts"). The leaf words, not the
278
+ // parent segment, are used on purpose: the parent here is `agent-grounding`,
279
+ // which is also a repo name and would over-block legitimate work. A command
280
+ // that globs EVERY path segment is the residual the marker-signing
281
+ // follow-up closes.
282
+ if (/[*?[]/.test(command)) {
283
+ const leafWords = leaf.split(/[^A-Za-z0-9]+/).filter((w) => w.length >= 6);
284
+ if (leafWords.some((w) => command.includes(w)))
285
+ return true;
286
+ }
287
+ return false;
288
+ }
289
+ //# sourceMappingURL=solution-acceptance-runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"solution-acceptance-runtime.js","sourceRoot":"","sources":["../../../src/policy-packs/builtin/solution-acceptance-runtime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import { z } from "zod";
2
+ import type { PolicyPack } from "../../schema/index.js";
3
+ import { type Runtime } from "../runtime.js";
4
+ import type { PackContribution } from "../types.js";
5
+ import { PACK_NAME } from "./solution-acceptance-runtime.js";
6
+ export { PACK_NAME };
7
+ /**
8
+ * Zod schema for this pack's `config:` block. Strict by design so typo'd
9
+ * keys fail loud at lint time (mirrors sibling packs).
10
+ */
11
+ export declare const configSchema: z.ZodObject<{
12
+ protected_completion_tools: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
13
+ ux: z.ZodOptional<z.ZodObject<{
14
+ cannot: z.ZodString;
15
+ required: z.ZodArray<z.ZodString, "many">;
16
+ run: z.ZodArray<z.ZodString, "many">;
17
+ }, "strict", z.ZodTypeAny, {
18
+ cannot: string;
19
+ required: string[];
20
+ run: string[];
21
+ }, {
22
+ cannot: string;
23
+ required: string[];
24
+ run: string[];
25
+ }>>;
26
+ }, "strict", z.ZodTypeAny, {
27
+ ux?: {
28
+ cannot: string;
29
+ required: string[];
30
+ run: string[];
31
+ } | undefined;
32
+ protected_completion_tools?: string[] | undefined;
33
+ }, {
34
+ ux?: {
35
+ cannot: string;
36
+ required: string[];
37
+ run: string[];
38
+ } | undefined;
39
+ protected_completion_tools?: string[] | undefined;
40
+ }>;
41
+ export declare function resolve(pack: PolicyPack, runtime?: Runtime): {
42
+ contribution: PackContribution;
43
+ warnings: string[];
44
+ };
@@ -0,0 +1,185 @@
1
+ // Builtin Policy Pack: `solution-acceptance`.
2
+ //
3
+ // Consumer half of the solution-acceptance gate (the "Verifier-gated Done"
4
+ // anti-lazyness mechanism). Makes a task's completion EARNED from a real
5
+ // preflight run, not self-attested. The producer is grounding-mcp's
6
+ // `solution_evaluate` (>= 0.3.2); harness reads the HEAD-pinned verdict
7
+ // marker it writes and gates the task-finishing tools on it.
8
+ //
9
+ // Two PreToolUse hooks, both `blocking: hard`:
10
+ //
11
+ // 1. completion-gate (`harness pack hook solution-acceptance`): denies the
12
+ // agent-tasks completion verbs (task_finish / task_submit_pr /
13
+ // task_merge / pull_requests_merge) — and, belt-and-suspenders, a
14
+ // `git push` / `gh pr merge` bash command — unless a READY verdict
15
+ // exists at the CURRENT git HEAD. The verdict id is the active-claim
16
+ // task id; with no active claim the gate fails CLOSED.
17
+ //
18
+ // 2. write-guard (`harness pack hook solution-acceptance-writeguard`): the
19
+ // actual anti-forgery closure. Relocating the verdict dir is NOT enough
20
+ // — understanding-gate allows all Bash post-approval — so this hook
21
+ // blocks the agent's enumerated write paths (Bash redirects/`tee`/`mv`/
22
+ // `cp`/`ln`/interpreter writes that reference the dir, `chmod`/`chattr`
23
+ // on the dir, and Write/Edit/MultiEdit/NotebookEdit whose `file_path`
24
+ // lands inside the dir). The ONLY remaining writer is the producer
25
+ // (the operator-launched grounding-mcp MCP server, which runs real
26
+ // preflight and does not flow through the agent's gated tool surface).
27
+ //
28
+ // Anti-forgery scope (operator decision, 2026-05-30): v1 closes the
29
+ // ENUMERATED-WRITE-PATH residual, NOT arbitrary same-uid forgery. Content
30
+ // signing (a cross-repo follow-up that also touches the producer) is what
31
+ // closes an unguarded write primitive. See solution-acceptance-runtime.ts.
32
+ //
33
+ // Pack is OFF by default; enable per-installation via
34
+ // `harness pack add solution-acceptance`. The `full` init template wires it
35
+ // as a disabled-by-policy exemplar... (see templates.ts). It REQUIRES
36
+ // grounding-mcp under `tools.mcp` (the producer) and the `preflight` binary
37
+ // on PATH; `harness validate` / `harness doctor` warn when the producer is
38
+ // absent (a missing producer means the gate can never see a verdict and
39
+ // would deadlock).
40
+ import { z } from "zod";
41
+ import { PolicyUxSchema } from "../../schema/policies.js";
42
+ import { DEFAULT_RUNTIME } from "../runtime.js";
43
+ import { PACK_NAME, resolveProtectedCompletionTools, } from "./solution-acceptance-runtime.js";
44
+ export { PACK_NAME };
45
+ /**
46
+ * Zod schema for this pack's `config:` block. Strict by design so typo'd
47
+ * keys fail loud at lint time (mirrors sibling packs).
48
+ */
49
+ export const configSchema = z
50
+ .object({
51
+ // Override the agent-tasks completion verbs the gate fires on. The
52
+ // names are bare verbs (e.g. "task_finish"); the matcher prefixes
53
+ // `mcp__agent-tasks__`. Defaults to DEFAULT_PROTECTED_COMPLETION_TOOLS.
54
+ protected_completion_tools: z.array(z.string().min(1)).min(1).optional(),
55
+ // `ux` renders the agent-facing remediation block when a gate trips.
56
+ ux: PolicyUxSchema.optional(),
57
+ })
58
+ .strict();
59
+ const HOOK_NAME_PREFIX = `policy-pack:${PACK_NAME}`;
60
+ const COMPLETION_BLOCKER_COMMAND = "harness pack hook solution-acceptance";
61
+ const WRITEGUARD_BLOCKER_COMMAND = "harness pack hook solution-acceptance-writeguard";
62
+ const WRITEGUARD_MATCH_CLAUDE = "Edit|Write|MultiEdit|NotebookEdit|Bash";
63
+ const WRITEGUARD_MATCH_CODEX = "apply_patch|Bash";
64
+ /**
65
+ * PreToolUse `match` for the completion-gate. `Bash` is always included for
66
+ * the `git push` / `gh pr merge` belt-and-suspenders arm; on the Claude
67
+ * runtime the agent-tasks MCP verbs are appended (the reliable choke
68
+ * points). Codex has no agent-tasks MCP surface here, so it gets the Bash
69
+ * arm only (documented limitation).
70
+ */
71
+ function completionMatch(runtime, tools) {
72
+ if (runtime === "codex")
73
+ return "Bash";
74
+ const mcp = tools.map((t) => `mcp__agent-tasks__${t}`).join("|");
75
+ return `Bash|${mcp}`;
76
+ }
77
+ function buildHooks(runtime, tools) {
78
+ const writeGuardMatch = runtime === "codex" ? WRITEGUARD_MATCH_CODEX : WRITEGUARD_MATCH_CLAUDE;
79
+ return [
80
+ {
81
+ name: `${HOOK_NAME_PREFIX}:completion-gate`,
82
+ event: "PreToolUse",
83
+ match: completionMatch(runtime, tools),
84
+ command: COMPLETION_BLOCKER_COMMAND,
85
+ blocking: "hard",
86
+ budget_ms: 5000,
87
+ description: "Blocker: deny the task-finishing tools (agent-tasks completion verbs + `git push` / `gh pr merge`) unless a ready solution-acceptance verdict exists at the current git HEAD. Fail-closed.",
88
+ },
89
+ {
90
+ name: `${HOOK_NAME_PREFIX}:write-guard`,
91
+ event: "PreToolUse",
92
+ match: writeGuardMatch,
93
+ command: WRITEGUARD_BLOCKER_COMMAND,
94
+ blocking: "hard",
95
+ budget_ms: 5000,
96
+ description: "Anti-forgery write-guard: deny any agent write into the solution-verdict dir (redirects/tee/mv/cp/ln/interpreter writes that reference it, chmod/chattr on it, and path-tool file_path inside it). The producer (grounding-mcp MCP server) is the only legitimate writer.",
97
+ },
98
+ ];
99
+ }
100
+ function buildInstructions(pack, tools, runtime) {
101
+ const description = pack.description?.trim() ?? "";
102
+ return `# Policy Pack: ${PACK_NAME}
103
+
104
+ > Operator audit copy. This pack makes task completion EARNED from a real
105
+ > preflight run (the producer, grounding-mcp \`solution_evaluate\`) rather
106
+ > than self-attested, and closes the enumerated-write-path forgery residual
107
+ > on the verdict marker.
108
+
109
+ ## Runtime
110
+
111
+ ${runtime}
112
+
113
+ ## Producer (required)
114
+
115
+ This pack is a pure CONSUMER. It needs the producer wired:
116
+
117
+ - \`grounding-mcp\` (>= 0.3.2) declared under \`tools.mcp\`, exposing
118
+ \`solution_evaluate\` / \`solution_gate\`.
119
+ - The \`preflight\` binary (\`@lannguyensi/agent-preflight\`) on PATH (or
120
+ \`SOLUTION_PREFLIGHT_BIN\`), which \`solution_evaluate\` shells out to.
121
+
122
+ If the producer is absent the gate can never see a verdict and would
123
+ deadlock; \`harness validate\` / \`harness doctor\` warn about this.
124
+
125
+ ## Effect
126
+
127
+ Two \`PreToolUse\` hooks (both blocking: hard) are wired into the
128
+ harness-managed settings:
129
+
130
+ 1. \`completion-gate\` (\`${COMPLETION_BLOCKER_COMMAND}\`): denies the
131
+ completion verbs unless a READY verdict exists at the CURRENT git HEAD.
132
+ Gated tools: ${tools.map((t) => `\`${t}\``).join(", ")} (matched as
133
+ \`mcp__agent-tasks__<verb>\`), plus a \`git push\` / \`gh pr merge\` bash
134
+ match. The verdict id is the active-claim task id; with no active claim
135
+ the gate fails CLOSED.
136
+
137
+ 2. \`write-guard\` (\`${WRITEGUARD_BLOCKER_COMMAND}\`): denies any agent write
138
+ into the verdict dir. The only legitimate writer is the producer.
139
+
140
+ ## Earning a verdict
141
+
142
+ \`\`\`
143
+ mcp__agent-grounding__solution_evaluate({ id: "<active-task-id>" })
144
+ \`\`\`
145
+
146
+ This runs \`preflight run --json\` and records a HEAD-pinned verdict. A
147
+ not-ready run (failing checks, dirty tree) records a not-ready verdict; any
148
+ commit after a green run shifts HEAD and invalidates it, so re-run after
149
+ every change.
150
+
151
+ ## Anti-forgery scope (v1)
152
+
153
+ v1 closes the ENUMERATED-WRITE-PATH residual: the write-guard blocks the
154
+ agent's Bash/Edit/Write spellings that target the verdict dir. It does NOT
155
+ close arbitrary same-uid forgery (an unguarded write primitive). Marker
156
+ signing — a follow-up that also changes the producer — is what closes
157
+ content-authenticity. A \`0500\` chmod on the dir is deliberately NOT used:
158
+ producer and agent share a uid, so it would block the producer too.
159
+
160
+ ## Out of scope (v1)
161
+
162
+ - Goodhart test-count-delta guard (producer side).
163
+ - LLM-judge layer / relative best-of-N ranking.
164
+ - Explicit verdict-id source for untasked sessions (follow-up; today an
165
+ untasked session fails closed).
166
+
167
+ ## Pack metadata
168
+ ${description ? `\n> ${description.replace(/\n/g, "\n> ")}\n` : ""}
169
+ - Source: \`builtin\`
170
+ - Pack: \`${PACK_NAME}\`
171
+ - Runtime: \`${runtime}\`
172
+ `;
173
+ }
174
+ export function resolve(pack, runtime = DEFAULT_RUNTIME) {
175
+ const tools = resolveProtectedCompletionTools(pack);
176
+ const hooks = buildHooks(runtime, tools);
177
+ const files = [
178
+ {
179
+ relativePath: `policy-packs/${PACK_NAME}/instructions.md`,
180
+ content: buildInstructions(pack, tools, runtime),
181
+ },
182
+ ];
183
+ return { contribution: { hooks, files }, warnings: [] };
184
+ }
185
+ //# sourceMappingURL=solution-acceptance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"solution-acceptance.js","sourceRoot":"","sources":["../../../src/policy-packs/builtin/solution-acceptance.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@ import type { PolicyPack } from "../schema/index.js";
3
3
  import { type ResolvePackOptions } from "./builtin/understanding-before-execution.js";
4
4
  import { type Runtime } from "./runtime.js";
5
5
  import type { PackContribution } from "./types.js";
6
- export declare const KNOWN_BUILTIN_PACKS: readonly ["understanding-before-execution", "branch-protection"];
6
+ export declare const KNOWN_BUILTIN_PACKS: readonly ["understanding-before-execution", "branch-protection", "solution-acceptance"];
7
7
  export type BuiltinPackName = (typeof KNOWN_BUILTIN_PACKS)[number];
8
8
  export declare function isBuiltinPackName(name: string): name is BuiltinPackName;
9
9
  export interface ResolveBuiltinResult {
@@ -6,10 +6,12 @@
6
6
  // out of scope for v1; their resolution lands in a later sub-task.
7
7
  import { configSchema as branchProtectionConfigSchema, PACK_NAME as BRANCH_PROTECTION, resolve as resolveBranchProtection, } from "./builtin/branch-protection.js";
8
8
  import { configSchema as understandingBeforeExecutionConfigSchema, PACK_NAME as UNDERSTANDING_BEFORE_EXECUTION, resolve as resolveUnderstandingBeforeExecution, VERSION_COMMAND as UNDERSTANDING_BEFORE_EXECUTION_VERSION_COMMAND, } from "./builtin/understanding-before-execution.js";
9
+ import { configSchema as solutionAcceptanceConfigSchema, PACK_NAME as SOLUTION_ACCEPTANCE, resolve as resolveSolutionAcceptance, } from "./builtin/solution-acceptance.js";
9
10
  import { DEFAULT_RUNTIME } from "./runtime.js";
10
11
  export const KNOWN_BUILTIN_PACKS = [
11
12
  UNDERSTANDING_BEFORE_EXECUTION,
12
13
  BRANCH_PROTECTION,
14
+ SOLUTION_ACCEPTANCE,
13
15
  ];
14
16
  export function isBuiltinPackName(name) {
15
17
  return KNOWN_BUILTIN_PACKS.includes(name);
@@ -22,6 +24,8 @@ export function resolveBuiltin(pack, runtime = DEFAULT_RUNTIME, opts = {}) {
22
24
  return resolveUnderstandingBeforeExecution(pack, runtime, opts);
23
25
  case BRANCH_PROTECTION:
24
26
  return resolveBranchProtection(pack, runtime);
27
+ case SOLUTION_ACCEPTANCE:
28
+ return resolveSolutionAcceptance(pack, runtime);
25
29
  }
26
30
  }
27
31
  /**
@@ -39,6 +43,8 @@ export function resolveBuiltinConfigSchema(packName) {
39
43
  return understandingBeforeExecutionConfigSchema;
40
44
  case BRANCH_PROTECTION:
41
45
  return branchProtectionConfigSchema;
46
+ case SOLUTION_ACCEPTANCE:
47
+ return solutionAcceptanceConfigSchema;
42
48
  }
43
49
  }
44
50
  /**
@@ -59,6 +65,10 @@ export function resolveBuiltinVersionCommand(packName) {
59
65
  return UNDERSTANDING_BEFORE_EXECUTION_VERSION_COMMAND;
60
66
  case BRANCH_PROTECTION:
61
67
  return null;
68
+ case SOLUTION_ACCEPTANCE:
69
+ // Blocker is harness itself; the producer (grounding-mcp) is probed
70
+ // via its tools.mcp min_version, not a pack-side bin.
71
+ return null;
62
72
  }
63
73
  }
64
74
  //# sourceMappingURL=registry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/policy-packs/registry.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,EAAE;AACF,kEAAkE;AAClE,sEAAsE;AACtE,oEAAoE;AACpE,mEAAmE;AAInE,OAAO,EACL,YAAY,IAAI,4BAA4B,EAC5C,SAAS,IAAI,iBAAiB,EAC9B,OAAO,IAAI,uBAAuB,GACnC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,YAAY,IAAI,wCAAwC,EACxD,SAAS,IAAI,8BAA8B,EAC3C,OAAO,IAAI,mCAAmC,EAC9C,eAAe,IAAI,8CAA8C,GAElE,MAAM,6CAA6C,CAAC;AACrD,OAAO,EAAE,eAAe,EAAgB,MAAM,cAAc,CAAC;AAG7D,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,8BAA8B;IAC9B,iBAAiB;CACT,CAAC;AAGX,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAQ,mBAAyC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACnE,CAAC;AAOD,MAAM,UAAU,cAAc,CAC5B,IAAgB,EAChB,UAAmB,eAAe,EAClC,OAA2B,EAAE;IAE7B,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/C,QAAQ,IAAI,CAAC,IAAuB,EAAE,CAAC;QACrC,KAAK,8BAA8B;YACjC,OAAO,mCAAmC,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QAClE,KAAK,iBAAiB;YACpB,OAAO,uBAAuB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAClD,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,0BAA0B,CACxC,QAAgB;IAEhB,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9C,QAAQ,QAA2B,EAAE,CAAC;QACpC,KAAK,8BAA8B;YACjC,OAAO,wCAAwC,CAAC;QAClD,KAAK,iBAAiB;YACpB,OAAO,4BAA4B,CAAC;IACxC,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,4BAA4B,CAC1C,QAAgB;IAEhB,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9C,QAAQ,QAA2B,EAAE,CAAC;QACpC,KAAK,8BAA8B;YACjC,OAAO,8CAA8C,CAAC;QACxD,KAAK,iBAAiB;YACpB,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/policy-packs/registry.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,EAAE;AACF,kEAAkE;AAClE,sEAAsE;AACtE,oEAAoE;AACpE,mEAAmE;AAInE,OAAO,EACL,YAAY,IAAI,4BAA4B,EAC5C,SAAS,IAAI,iBAAiB,EAC9B,OAAO,IAAI,uBAAuB,GACnC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,YAAY,IAAI,wCAAwC,EACxD,SAAS,IAAI,8BAA8B,EAC3C,OAAO,IAAI,mCAAmC,EAC9C,eAAe,IAAI,8CAA8C,GAElE,MAAM,6CAA6C,CAAC;AACrD,OAAO,EACL,YAAY,IAAI,8BAA8B,EAC9C,SAAS,IAAI,mBAAmB,EAChC,OAAO,IAAI,yBAAyB,GACrC,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAgB,MAAM,cAAc,CAAC;AAG7D,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,8BAA8B;IAC9B,iBAAiB;IACjB,mBAAmB;CACX,CAAC;AAGX,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAQ,mBAAyC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACnE,CAAC;AAOD,MAAM,UAAU,cAAc,CAC5B,IAAgB,EAChB,UAAmB,eAAe,EAClC,OAA2B,EAAE;IAE7B,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/C,QAAQ,IAAI,CAAC,IAAuB,EAAE,CAAC;QACrC,KAAK,8BAA8B;YACjC,OAAO,mCAAmC,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QAClE,KAAK,iBAAiB;YACpB,OAAO,uBAAuB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAChD,KAAK,mBAAmB;YACtB,OAAO,yBAAyB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACpD,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,0BAA0B,CACxC,QAAgB;IAEhB,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9C,QAAQ,QAA2B,EAAE,CAAC;QACpC,KAAK,8BAA8B;YACjC,OAAO,wCAAwC,CAAC;QAClD,KAAK,iBAAiB;YACpB,OAAO,4BAA4B,CAAC;QACtC,KAAK,mBAAmB;YACtB,OAAO,8BAA8B,CAAC;IAC1C,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,4BAA4B,CAC1C,QAAgB;IAEhB,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9C,QAAQ,QAA2B,EAAE,CAAC;QACpC,KAAK,8BAA8B;YACjC,OAAO,8CAA8C,CAAC;QACxD,KAAK,iBAAiB;YACpB,OAAO,IAAI,CAAC;QACd,KAAK,mBAAmB;YACtB,oEAAoE;YACpE,sDAAsD;YACtD,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC"}