@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.
- package/CHANGELOG.md +31 -0
- package/README.md +1 -0
- package/dist/cli/index.js +59 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/templates.d.ts +1 -1
- package/dist/cli/init/templates.js +15 -0
- package/dist/cli/init/templates.js.map +1 -1
- package/dist/cli/pack/hook-branch-protection.js +2 -1
- package/dist/cli/pack/hook-branch-protection.js.map +1 -1
- package/dist/cli/pack/hook-codex-pre-tool-use.js +4 -3
- package/dist/cli/pack/hook-codex-pre-tool-use.js.map +1 -1
- package/dist/cli/pack/hook-codex-stop.js +1 -0
- package/dist/cli/pack/hook-codex-stop.js.map +1 -1
- package/dist/cli/pack/hook-post-tool-use.js +1 -0
- package/dist/cli/pack/hook-post-tool-use.js.map +1 -1
- package/dist/cli/pack/hook-pre-tool-use.js +3 -2
- package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
- package/dist/cli/pack/hook-runtime-reality.d.ts +50 -0
- package/dist/cli/pack/hook-runtime-reality.js +160 -0
- package/dist/cli/pack/hook-runtime-reality.js.map +1 -0
- package/dist/cli/pack/hook-solution-acceptance-writeguard.d.ts +26 -0
- package/dist/cli/pack/hook-solution-acceptance-writeguard.js +187 -0
- package/dist/cli/pack/hook-solution-acceptance-writeguard.js.map +1 -0
- package/dist/cli/pack/hook-solution-acceptance.d.ts +26 -0
- package/dist/cli/pack/hook-solution-acceptance.js +237 -0
- package/dist/cli/pack/hook-solution-acceptance.js.map +1 -0
- package/dist/cli/pause/index.d.ts +17 -3
- package/dist/cli/pause/index.js +30 -6
- package/dist/cli/pause/index.js.map +1 -1
- package/dist/cli/policy/intercept.js +7 -1
- package/dist/cli/policy/intercept.js.map +1 -1
- package/dist/cli/session-start/branch-check.js +4 -2
- package/dist/cli/session-start/branch-check.js.map +1 -1
- package/dist/cli/session-start/index.js +4 -2
- package/dist/cli/session-start/index.js.map +1 -1
- package/dist/cli/validate/checks.js +38 -0
- package/dist/cli/validate/checks.js.map +1 -1
- package/dist/policy-packs/builtin/solution-acceptance-runtime.d.ts +119 -0
- package/dist/policy-packs/builtin/solution-acceptance-runtime.js +289 -0
- package/dist/policy-packs/builtin/solution-acceptance-runtime.js.map +1 -0
- package/dist/policy-packs/builtin/solution-acceptance.d.ts +44 -0
- package/dist/policy-packs/builtin/solution-acceptance.js +185 -0
- package/dist/policy-packs/builtin/solution-acceptance.js.map +1 -0
- package/dist/policy-packs/registry.d.ts +1 -1
- package/dist/policy-packs/registry.js +10 -0
- package/dist/policy-packs/registry.js.map +1 -1
- package/dist/runtime/risk-classifier.js +64 -0
- package/dist/runtime/risk-classifier.js.map +1 -1
- package/dist/runtime/session-id.d.ts +4 -3
- package/dist/runtime/session-id.js +17 -5
- package/dist/runtime/session-id.js.map +1 -1
- package/package.json +3 -1
- 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;
|
|
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"}
|