@spences10/pi-harness 0.1.2 → 0.2.1
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 +123 -0
- package/README.md +120 -175
- package/dist/assessment.d.ts +4 -12
- package/dist/assessment.js +19 -192
- package/dist/assessment.js.map +1 -1
- package/dist/enforcement/policy.d.ts +2 -0
- package/dist/enforcement/policy.js +47 -73
- package/dist/enforcement/policy.js.map +1 -1
- package/dist/extension.d.ts +1 -1
- package/dist/extension.js +184 -403
- package/dist/extension.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/prompt.d.ts +1 -1
- package/dist/prompt.js +1 -7
- package/dist/prompt.js.map +1 -1
- package/dist/runtime/baseline.d.ts +6 -0
- package/dist/runtime/baseline.js +145 -0
- package/dist/runtime/baseline.js.map +1 -0
- package/dist/runtime/index.d.ts +2 -1
- package/dist/runtime/index.js +72 -29
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/outcome.d.ts +1 -2
- package/dist/runtime/outcome.js +21 -35
- package/dist/runtime/outcome.js.map +1 -1
- package/dist/runtime/renderers.js +34 -70
- package/dist/runtime/renderers.js.map +1 -1
- package/dist/runtime/verification.d.ts +15 -0
- package/dist/runtime/verification.js +97 -0
- package/dist/runtime/verification.js.map +1 -0
- package/dist/schema.d.ts +12 -2
- package/dist/schema.js +6 -8
- package/dist/schema.js.map +1 -1
- package/package.json +6 -5
- package/skills/create-harness/SKILL.md +19 -62
- package/skills/execute-harness/SKILL.md +27 -66
- package/skills/review-harness/SKILL.md +21 -54
package/dist/runtime/outcome.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { execFileSync } from 'node:child_process';
|
|
2
1
|
import { writeFileSync } from 'node:fs';
|
|
3
2
|
import { resolve } from 'node:path';
|
|
3
|
+
import { collect_task_changed_files } from './baseline.js';
|
|
4
4
|
import { harness_paths, json_write, read_contract, read_status, } from './files.js';
|
|
5
5
|
function now_iso() {
|
|
6
6
|
return new Date().toISOString();
|
|
@@ -8,35 +8,12 @@ function now_iso() {
|
|
|
8
8
|
function unique(values) {
|
|
9
9
|
return [...new Set(values.filter(Boolean))];
|
|
10
10
|
}
|
|
11
|
-
function
|
|
12
|
-
try {
|
|
13
|
-
return execFileSync('git', ['-C', cwd, ...args], {
|
|
14
|
-
encoding: 'utf8',
|
|
15
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
|
16
|
-
})
|
|
17
|
-
.split('\n')
|
|
18
|
-
.map((line) => line.trimEnd())
|
|
19
|
-
.filter(Boolean);
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
22
|
-
return [];
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
export function collect_git_changed_files(cwd) {
|
|
26
|
-
const status_files = git_lines(cwd, [
|
|
27
|
-
'status',
|
|
28
|
-
'--short',
|
|
29
|
-
'--untracked-files=all',
|
|
30
|
-
]).map((line) => line.slice(3).split(' -> ').pop()?.replace(/\/$/, ''));
|
|
31
|
-
const diff_files = git_lines(cwd, ['diff', '--name-only']);
|
|
32
|
-
return unique([...status_files, ...diff_files]).sort();
|
|
33
|
-
}
|
|
34
|
-
function changed_files(cwd, log, baseline_changed_files) {
|
|
35
|
-
const baseline = new Set(baseline_changed_files);
|
|
11
|
+
function changed_files(cwd, log, policy) {
|
|
36
12
|
const logged_files = log.flatMap((entry) => entry.changed_files ?? []);
|
|
37
|
-
return unique([
|
|
38
|
-
|
|
39
|
-
|
|
13
|
+
return unique([
|
|
14
|
+
...collect_task_changed_files(cwd, policy),
|
|
15
|
+
...logged_files,
|
|
16
|
+
]).sort();
|
|
40
17
|
}
|
|
41
18
|
function latest_log_value(log, key) {
|
|
42
19
|
return [...log]
|
|
@@ -66,10 +43,16 @@ export function collect_harness_outcome(harness_dir, cwd = process.env.HARNESS_C
|
|
|
66
43
|
cwd: contract.policy.cwd,
|
|
67
44
|
execution_cwd,
|
|
68
45
|
generated_at: now_iso(),
|
|
69
|
-
changed_files: changed_files(execution_cwd, status_file.log, contract.policy
|
|
46
|
+
changed_files: changed_files(execution_cwd, status_file.log, contract.policy),
|
|
70
47
|
baseline_changed_files: contract.policy.baseline_changed_files,
|
|
71
48
|
validation: {
|
|
72
|
-
commands:
|
|
49
|
+
commands: [
|
|
50
|
+
...new Set([
|
|
51
|
+
...(contract.policy.validation_commands ?? []),
|
|
52
|
+
...contract.scaffold.validation_commands,
|
|
53
|
+
]),
|
|
54
|
+
],
|
|
55
|
+
checks: status_file.log.flatMap((entry) => entry.checks ?? []),
|
|
73
56
|
evidence: validation_evidence,
|
|
74
57
|
},
|
|
75
58
|
team_status: latest_log_value(status_file.log, 'team_status') ??
|
|
@@ -107,16 +90,19 @@ ${outcome.task}
|
|
|
107
90
|
|
|
108
91
|
${list(outcome.changed_files, 'No changed files detected')}
|
|
109
92
|
|
|
110
|
-
## Baseline
|
|
93
|
+
## Baseline files
|
|
111
94
|
|
|
112
95
|
${list(outcome.baseline_changed_files, 'No dirty baseline files recorded')}
|
|
113
96
|
|
|
114
97
|
## Validation evidence
|
|
115
98
|
|
|
99
|
+
Executed checks:
|
|
100
|
+
${list(outcome.validation.checks.map((check) => `${check.passed ? 'PASS' : 'FAIL'} ${check.command} — ${check.output_file}`), 'No executed checks recorded')}
|
|
101
|
+
|
|
116
102
|
Commands:
|
|
117
103
|
${list(outcome.validation.commands, 'No validation commands configured')}
|
|
118
104
|
|
|
119
|
-
|
|
105
|
+
Self-reported notes (not execution proof):
|
|
120
106
|
${validation_evidence}
|
|
121
107
|
|
|
122
108
|
## Team status
|
|
@@ -128,8 +114,8 @@ ${outcome.team_status}
|
|
|
128
114
|
${list(outcome.remaining_risks, 'No remaining risks recorded')}
|
|
129
115
|
`;
|
|
130
116
|
}
|
|
131
|
-
export function write_outcome_artifacts(harness_dir) {
|
|
132
|
-
const outcome = collect_harness_outcome(harness_dir);
|
|
117
|
+
export function write_outcome_artifacts(harness_dir, cwd) {
|
|
118
|
+
const outcome = collect_harness_outcome(harness_dir, cwd);
|
|
133
119
|
const paths = harness_paths(harness_dir);
|
|
134
120
|
json_write(paths.outcome, outcome);
|
|
135
121
|
writeFileSync(paths.outcome_markdown, render_outcome_markdown(outcome));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outcome.js","sourceRoot":"","sources":["../../src/runtime/outcome.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"outcome.js","sourceRoot":"","sources":["../../src/runtime/outcome.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
+
import { build_path_helpers } from '../enforcement/policy.js';
|
|
2
|
+
import { build_baseline_helpers } from './baseline.js';
|
|
1
3
|
import { shell_quote } from './files.js';
|
|
2
4
|
export function build_system_markdown(contract) {
|
|
3
5
|
return `# Executor system prompt: ${contract.id}
|
|
4
6
|
|
|
5
7
|
You are working inside a my-pi task harness. The harness directory is external control state, not project source. System, developer, and current user instructions remain authoritative.
|
|
6
8
|
|
|
7
|
-
## Outer policy (
|
|
9
|
+
## Outer policy (accepted contract)
|
|
8
10
|
|
|
9
11
|
- Project cwd: \`${contract.policy.cwd}\`
|
|
10
12
|
- Forbidden edit paths: ${contract.policy.forbidden_paths.map((path) => `\`${path}\``).join(', ') || 'none'}
|
|
13
|
+
- Accepted maximum scope: ${(contract.policy.allowed_paths ?? []).map((path) => `\`${path}\``).join(', ') || 'legacy contract; recreate before execution'}
|
|
14
|
+
- Accepted checks: ${(contract.policy.validation_commands ?? []).map((command) => `\`${command}\``).join(', ') || 'none'}
|
|
11
15
|
|
|
12
|
-
The
|
|
16
|
+
The API and final drift guard preserve this scope and accepted checks. Shell commands retain the host user's permissions; these workflow checks are not OS isolation.
|
|
13
17
|
|
|
14
18
|
## Inner scaffold v${contract.scaffold.version} (amendable execution plan)
|
|
15
19
|
|
|
@@ -25,7 +29,7 @@ The outer policy protects the workspace and verifier. It cannot be weakened by t
|
|
|
25
29
|
4. Edit only allowed paths.
|
|
26
30
|
5. Do not weaken tests, fake outputs, or bypass validation.
|
|
27
31
|
6. Run \`validate.sh\` before completion.
|
|
28
|
-
7.
|
|
32
|
+
7. Ask for new authority rather than expanding accepted scope. Retry ordinary failures without a fixed attempt limit.
|
|
29
33
|
`;
|
|
30
34
|
}
|
|
31
35
|
export function build_task_markdown(contract) {
|
|
@@ -67,9 +71,12 @@ fi
|
|
|
67
71
|
export HARNESS_CWD="$run_cwd"`;
|
|
68
72
|
}
|
|
69
73
|
export function build_validate_script(contract) {
|
|
70
|
-
const commands =
|
|
71
|
-
|
|
72
|
-
|
|
74
|
+
const commands = [
|
|
75
|
+
...new Set([
|
|
76
|
+
...(contract.policy.validation_commands ?? []),
|
|
77
|
+
...contract.scaffold.validation_commands,
|
|
78
|
+
]),
|
|
79
|
+
];
|
|
73
80
|
return `#!/usr/bin/env bash
|
|
74
81
|
set -euo pipefail
|
|
75
82
|
${build_resolve_cwd_script(contract)}
|
|
@@ -82,8 +89,9 @@ bash -lc ${shell_quote(command)}`)
|
|
|
82
89
|
}
|
|
83
90
|
export function build_guard_script() {
|
|
84
91
|
return `import { execFileSync } from 'node:child_process';
|
|
85
|
-
import {
|
|
86
|
-
import {
|
|
92
|
+
import { createHash } from 'node:crypto';
|
|
93
|
+
import { lstatSync, readFileSync, readlinkSync } from 'node:fs';
|
|
94
|
+
import { isAbsolute, matchesGlob, relative, resolve } from 'node:path';
|
|
87
95
|
|
|
88
96
|
const contract = JSON.parse(readFileSync(new URL('./harness.json', import.meta.url), 'utf8'));
|
|
89
97
|
|
|
@@ -99,57 +107,21 @@ function resolve_cwd() {
|
|
|
99
107
|
}
|
|
100
108
|
|
|
101
109
|
const cwd = resolve_cwd();
|
|
102
|
-
|
|
110
|
+
${build_baseline_helpers()}
|
|
103
111
|
|
|
104
|
-
|
|
105
|
-
const status = execFileSync('git', ['-C', cwd, 'status', '--short', '--untracked-files=all'], { encoding: 'utf8' })
|
|
106
|
-
.split('\\n')
|
|
107
|
-
.map((line) => line.trimEnd())
|
|
108
|
-
.filter(Boolean)
|
|
109
|
-
.map((line) => line.slice(3).split(' -> ').pop());
|
|
110
|
-
const diff = execFileSync('git', ['-C', cwd, 'diff', '--name-only'], { encoding: 'utf8' })
|
|
111
|
-
.split('\\n')
|
|
112
|
-
.filter(Boolean);
|
|
113
|
-
return [...new Set([...status, ...diff])];
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function escape_regex(value) {
|
|
117
|
-
const special = new Set(['.', '*', '+', '?', '^', '$', '{', '}', '(', ')', '|', '[', ']', '\\\\', '-']);
|
|
118
|
-
return [...value].map((char) => special.has(char) ? '\\\\' + char : char).join('');
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function pattern_to_relative(pattern) {
|
|
122
|
-
const normalized_pattern = pattern.replaceAll('\\\\', '/');
|
|
123
|
-
if (!isAbsolute(pattern)) return normalized_pattern;
|
|
124
|
-
return relative(cwd, resolve(pattern)).replaceAll('\\\\', '/');
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function pattern_matches(pattern, relative_path) {
|
|
128
|
-
const normalized_pattern = pattern_to_relative(pattern);
|
|
129
|
-
if (normalized_pattern === '.') return true;
|
|
130
|
-
if (normalized_pattern.endsWith('/**')) {
|
|
131
|
-
const prefix = normalized_pattern.slice(0, -3);
|
|
132
|
-
return relative_path === prefix || relative_path.startsWith(prefix + '/');
|
|
133
|
-
}
|
|
134
|
-
if (normalized_pattern.includes('*')) {
|
|
135
|
-
const regex = new RegExp('^' + escape_regex(normalized_pattern).replaceAll('\\\\*\\\\*', '.*').replaceAll('\\\\*', '[^/]*') + '$');
|
|
136
|
-
return regex.test(relative_path);
|
|
137
|
-
}
|
|
138
|
-
return relative_path === normalized_pattern || relative_path.startsWith(normalized_pattern + '/');
|
|
139
|
-
}
|
|
112
|
+
${build_path_helpers()}
|
|
140
113
|
|
|
141
114
|
function is_test_path(relative_path) {
|
|
142
115
|
return /(^|\\/)(__tests__|tests?)\\//.test(relative_path) || /\\.(test|spec)\\.[cm]?[jt]sx?$/.test(relative_path);
|
|
143
116
|
}
|
|
144
117
|
|
|
145
118
|
const violations = [];
|
|
146
|
-
for (const path of
|
|
147
|
-
if (baseline.has(path)) continue;
|
|
119
|
+
for (const path of collect_task_changed_files(cwd, contract.policy)) {
|
|
148
120
|
const absolute_path = resolve(cwd, path);
|
|
149
121
|
if (absolute_path !== cwd && !absolute_path.startsWith(cwd + '/')) violations.push(path + ' is outside cwd');
|
|
150
122
|
if (contract.policy.forbidden_paths.some((pattern) => pattern_matches(pattern, path))) violations.push(path + ' matches forbidden_paths');
|
|
151
|
-
if (!contract.scaffold.allow_test_changes && is_test_path(path)) violations.push(path + ' is a test change but allow_test_changes is false');
|
|
152
|
-
if (!contract.scaffold.allowed_paths.some((pattern) => pattern_matches(pattern, path))) violations.push(path + ' is outside allowed_paths');
|
|
123
|
+
if ((!contract.scaffold.allow_test_changes || contract.policy.allow_test_changes === false) && is_test_path(path)) violations.push(path + ' is a test change but allow_test_changes is false');
|
|
124
|
+
if (!contract.scaffold.allowed_paths.some((pattern) => pattern_matches(pattern, path)) || !(contract.policy.allowed_paths ?? contract.scaffold.allowed_paths).some((pattern) => pattern_matches(pattern, path))) violations.push(path + ' is outside allowed_paths');
|
|
153
125
|
}
|
|
154
126
|
|
|
155
127
|
if (violations.length) {
|
|
@@ -162,8 +134,9 @@ console.log('Harness guard passed');
|
|
|
162
134
|
}
|
|
163
135
|
export function build_outcome_script() {
|
|
164
136
|
return `import { execFileSync } from 'node:child_process';
|
|
165
|
-
import {
|
|
166
|
-
import {
|
|
137
|
+
import { createHash } from 'node:crypto';
|
|
138
|
+
import { lstatSync, readFileSync, readlinkSync, writeFileSync } from 'node:fs';
|
|
139
|
+
import { dirname, join, relative, resolve } from 'node:path';
|
|
167
140
|
import { fileURLToPath } from 'node:url';
|
|
168
141
|
|
|
169
142
|
const harness_dir = dirname(fileURLToPath(import.meta.url));
|
|
@@ -181,25 +154,13 @@ function resolve_cwd() {
|
|
|
181
154
|
}
|
|
182
155
|
|
|
183
156
|
const cwd = resolve_cwd();
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
function git_lines(args) {
|
|
187
|
-
try {
|
|
188
|
-
return execFileSync('git', ['-C', cwd, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] })
|
|
189
|
-
.split('\\n')
|
|
190
|
-
.map((line) => line.trimEnd())
|
|
191
|
-
.filter(Boolean);
|
|
192
|
-
} catch {
|
|
193
|
-
return [];
|
|
194
|
-
}
|
|
195
|
-
}
|
|
157
|
+
${build_baseline_helpers()}
|
|
196
158
|
|
|
197
159
|
function unique(values) {
|
|
198
160
|
return [...new Set(values.filter(Boolean))];
|
|
199
161
|
}
|
|
200
162
|
|
|
201
|
-
const
|
|
202
|
-
const diff_files = git_lines(['diff', '--name-only']);
|
|
163
|
+
const task_files = collect_task_changed_files(cwd, contract.policy);
|
|
203
164
|
const logged_files = status.log.flatMap((entry) => entry.changed_files ?? []);
|
|
204
165
|
const latest = (key) => [...status.log].reverse().find((entry) => entry[key] !== undefined)?.[key];
|
|
205
166
|
const outcome = {
|
|
@@ -210,10 +171,11 @@ const outcome = {
|
|
|
210
171
|
cwd: contract.policy.cwd,
|
|
211
172
|
execution_cwd: cwd,
|
|
212
173
|
generated_at: new Date().toISOString(),
|
|
213
|
-
changed_files: unique([...
|
|
174
|
+
changed_files: unique([...task_files, ...logged_files]).sort(),
|
|
214
175
|
baseline_changed_files: contract.policy.baseline_changed_files || [],
|
|
215
176
|
validation: {
|
|
216
|
-
commands: contract.scaffold.validation_commands,
|
|
177
|
+
commands: [...new Set([...(contract.policy.validation_commands ?? []), ...contract.scaffold.validation_commands])],
|
|
178
|
+
checks: status.log.flatMap((entry) => entry.checks ?? []),
|
|
217
179
|
evidence: status.log
|
|
218
180
|
.filter((entry) => entry.evidence)
|
|
219
181
|
.map((entry) => ({ timestamp: entry.timestamp, phase: entry.phase, evidence: entry.evidence })),
|
|
@@ -233,7 +195,9 @@ const markdown = '# Harness outcome: ' + outcome.id + '\\n\\n'
|
|
|
233
195
|
+ '- Project cwd: ' + outcome.cwd + '\\n\\n'
|
|
234
196
|
+ '## Task\\n\\n' + outcome.task + '\\n\\n'
|
|
235
197
|
+ '## Changed files\\n\\n' + list(outcome.changed_files, 'No changed files detected') + '\\n\\n'
|
|
236
|
-
+ '##
|
|
198
|
+
+ '## Baseline files\\n\\n' + list(outcome.baseline_changed_files, 'No dirty baseline files recorded') + '\\n\\n'
|
|
199
|
+
+ '## Executed checks\\n\\n' + list(outcome.validation.checks.map((check) => (check.passed ? 'PASS ' : 'FAIL ') + check.command + ' — ' + check.output_file), 'No executed checks recorded') + '\\n\\n'
|
|
200
|
+
+ '## Validation evidence\\n\\nCommands:\\n' + list(outcome.validation.commands, 'No validation commands configured') + '\\n\\nSelf-reported notes (not execution proof):\\n' + evidence + '\\n\\n'
|
|
237
201
|
+ '## Team status\\n\\n' + outcome.team_status + '\\n\\n'
|
|
238
202
|
+ '## Remaining risks\\n\\n' + list(outcome.remaining_risks, 'No remaining risks recorded') + '\\n';
|
|
239
203
|
|
|
@@ -248,10 +212,10 @@ set -euo pipefail
|
|
|
248
212
|
${build_resolve_cwd_script(contract)}
|
|
249
213
|
cd "$HARNESS_CWD"
|
|
250
214
|
echo "## git status"
|
|
251
|
-
git status --short || true
|
|
215
|
+
git --no-optional-locks status --short || true
|
|
252
216
|
echo
|
|
253
217
|
echo "## diff stat"
|
|
254
|
-
git diff --stat || true
|
|
218
|
+
git --no-optional-locks diff --stat || true
|
|
255
219
|
echo
|
|
256
220
|
echo "## outcome"
|
|
257
221
|
node ${shell_quote(outcome_script_path)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderers.js","sourceRoot":"","sources":["../../src/runtime/renderers.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"renderers.js","sourceRoot":"","sources":["../../src/runtime/renderers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,UAAU,qBAAqB,CACpC,QAAyB;IAEzB,OAAO,6BAA6B,QAAQ,CAAC,EAAE;;;;;;mBAM7B,QAAQ,CAAC,MAAM,CAAC,GAAG;0BACZ,QAAQ,CAAC,MAAM,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM;4BAC/E,CAAC,QAAQ,CAAC,MAAM,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,4CAA4C;qBACpI,CAAC,QAAQ,CAAC,MAAM,CAAC,mBAAmB,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,OAAO,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM;;;;qBAInG,QAAQ,CAAC,QAAQ,CAAC,OAAO;;wBAEtB,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM;0BAC/E,QAAQ,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;gBAC7D,QAAQ,CAAC,QAAQ,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,OAAO,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM;;;;;;;;;;;CAW5G,CAAC;AACF,CAAC;AAED,MAAM,UAAU,mBAAmB,CAClC,QAAyB;IAEzB,OAAO,mBAAmB,QAAQ,CAAC,EAAE;;;;EAIpC,QAAQ,CAAC,QAAQ,CAAC,IAAI;;;;;;;;;;;;;;EActB,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;CACzE,CAAC;AACF,CAAC;AAED,SAAS,wBAAwB,CAAC,QAAyB;IAC1D,OAAO,gBAAgB,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC;;;;;;;;;;;;;8BAa1B,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,qBAAqB,CACpC,QAAyB;IAEzB,MAAM,QAAQ,GAAG;QAChB,GAAG,IAAI,GAAG,CAAC;YACV,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,mBAAmB,IAAI,EAAE,CAAC;YAC9C,GAAG,QAAQ,CAAC,QAAQ,CAAC,mBAAmB;SACxC,CAAC;KACF,CAAC;IACF,OAAO;;EAEN,wBAAwB,CAAC,QAAQ,CAAC;;EAElC,QAAQ;SACR,GAAG,CACH,CACC,OAAO,EACP,KAAK,EACJ,EAAE,CAAC,UAAU,KAAK,GAAG,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC;WACrE,WAAW,CAAC,OAAO,CAAC,EAAE,CAC/B;SACA,IAAI,CAAC,IAAI,CAAC;CACX,CAAC;AACF,CAAC;AAED,MAAM,UAAU,kBAAkB;IACjC,OAAO;;;;;;;;;;;;;;;;;;;EAmBN,sBAAsB,EAAE;;EAExB,kBAAkB,EAAE;;;;;;;;;;;;;;;;;;;;;CAqBrB,CAAC;AACF,CAAC;AAED,MAAM,UAAU,oBAAoB;IACnC,OAAO;;;;;;;;;;;;;;;;;;;;;EAqBN,sBAAsB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkDzB,CAAC;AACF,CAAC;AAED,MAAM,UAAU,mBAAmB,CAClC,QAAyB,EACzB,UAAkB,EAClB,mBAA2B;IAE3B,OAAO;;EAEN,wBAAwB,CAAC,QAAQ,CAAC;;;;;;;;;OAS7B,WAAW,CAAC,mBAAmB,CAAC;;;OAGhC,WAAW,CAAC,UAAU,CAAC;;;OAGvB,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;CAC9B,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { HarnessContract, HarnessValidationCheck } from '../schema.js';
|
|
2
|
+
export interface HarnessVerificationOptions {
|
|
3
|
+
cwd?: string;
|
|
4
|
+
signal?: AbortSignal;
|
|
5
|
+
run?: (command: string, cwd: string, signal?: AbortSignal) => Promise<{
|
|
6
|
+
passed: boolean;
|
|
7
|
+
output: string;
|
|
8
|
+
}>;
|
|
9
|
+
}
|
|
10
|
+
export declare function resolve_harness_cwd(contract: HarnessContract, candidate?: string): string;
|
|
11
|
+
export declare function verify_harness(harness_dir: string, contract: HarnessContract, options: HarnessVerificationOptions): Promise<{
|
|
12
|
+
checks: HarnessValidationCheck[];
|
|
13
|
+
error?: string;
|
|
14
|
+
cwd?: string;
|
|
15
|
+
}>;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
3
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { join, resolve } from 'node:path';
|
|
5
|
+
import { collect_git_changed_files, file_state } from './baseline.js';
|
|
6
|
+
import { shell_quote } from './files.js';
|
|
7
|
+
import { build_guard_script } from './renderers.js';
|
|
8
|
+
function git(cwd, ...args) {
|
|
9
|
+
return execFileSync('git', ['--no-optional-locks', '-C', cwd, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trimEnd();
|
|
10
|
+
}
|
|
11
|
+
function workspace_state(cwd, contract) {
|
|
12
|
+
const paths = [
|
|
13
|
+
...new Set([
|
|
14
|
+
...collect_git_changed_files(cwd),
|
|
15
|
+
...(cwd === resolve(contract.policy.cwd)
|
|
16
|
+
? Object.keys(contract.policy.baseline_file_states ?? {})
|
|
17
|
+
: []),
|
|
18
|
+
]),
|
|
19
|
+
].sort();
|
|
20
|
+
const files = paths.map((path) => {
|
|
21
|
+
const state = file_state(cwd, path);
|
|
22
|
+
if (state === undefined)
|
|
23
|
+
throw new Error(`Cannot verify an unsupported working-state path: ${path}`);
|
|
24
|
+
return [path, state];
|
|
25
|
+
});
|
|
26
|
+
let head;
|
|
27
|
+
try {
|
|
28
|
+
head = git(cwd, 'rev-parse', '--verify', 'HEAD');
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
head = git(cwd, 'symbolic-ref', 'HEAD');
|
|
32
|
+
}
|
|
33
|
+
return createHash('sha256')
|
|
34
|
+
.update(JSON.stringify({
|
|
35
|
+
head,
|
|
36
|
+
files,
|
|
37
|
+
}))
|
|
38
|
+
.digest('hex');
|
|
39
|
+
}
|
|
40
|
+
export function resolve_harness_cwd(contract, candidate = contract.policy.cwd) {
|
|
41
|
+
const cwd = resolve(candidate);
|
|
42
|
+
if (cwd !== resolve(contract.policy.cwd) &&
|
|
43
|
+
git(cwd, 'rev-parse', '--path-format=absolute', '--git-common-dir') !==
|
|
44
|
+
git(contract.policy.cwd, 'rev-parse', '--path-format=absolute', '--git-common-dir')) {
|
|
45
|
+
throw new Error('Validation cwd must be the accepted repository or one of its linked worktrees');
|
|
46
|
+
}
|
|
47
|
+
return cwd;
|
|
48
|
+
}
|
|
49
|
+
export async function verify_harness(harness_dir, contract, options) {
|
|
50
|
+
const checks = [];
|
|
51
|
+
const attempt = randomUUID();
|
|
52
|
+
let cwd;
|
|
53
|
+
try {
|
|
54
|
+
if (!options.run)
|
|
55
|
+
throw new Error('Completion requires a validation execution backend; notes are not proof');
|
|
56
|
+
if (!contract.policy.validation_commands?.length ||
|
|
57
|
+
!contract.policy.allowed_paths) {
|
|
58
|
+
throw new Error('Recreate this harness with accepted validation commands and scope before execution');
|
|
59
|
+
}
|
|
60
|
+
cwd = resolve_harness_cwd(contract, options.cwd);
|
|
61
|
+
const before = workspace_state(cwd, contract);
|
|
62
|
+
const contract_before = readFileSync(join(harness_dir, 'harness.json'), 'utf8');
|
|
63
|
+
writeFileSync(join(harness_dir, 'guard.mjs'), build_guard_script());
|
|
64
|
+
const commands = [
|
|
65
|
+
...new Set([
|
|
66
|
+
...contract.policy.validation_commands,
|
|
67
|
+
...contract.scaffold.validation_commands,
|
|
68
|
+
]),
|
|
69
|
+
`HARNESS_CWD=${shell_quote(cwd)} node ${shell_quote(join(harness_dir, 'guard.mjs'))}`,
|
|
70
|
+
];
|
|
71
|
+
for (const command of commands) {
|
|
72
|
+
options.signal?.throwIfAborted();
|
|
73
|
+
const result = await options.run(command, cwd, options.signal);
|
|
74
|
+
const output_file = join(harness_dir, 'logs', `validation-${attempt}-${checks.length}.log`);
|
|
75
|
+
writeFileSync(output_file, result.output.slice(0, 1024 * 1024));
|
|
76
|
+
const passed = result.passed === true && !options.signal?.aborted;
|
|
77
|
+
checks.push({ command, passed, output_file });
|
|
78
|
+
if (!passed)
|
|
79
|
+
throw new Error(`Validation failed or was interrupted: ${command}`);
|
|
80
|
+
}
|
|
81
|
+
options.signal?.throwIfAborted();
|
|
82
|
+
if (readFileSync(join(harness_dir, 'harness.json'), 'utf8') !==
|
|
83
|
+
contract_before ||
|
|
84
|
+
workspace_state(cwd, contract) !== before) {
|
|
85
|
+
throw new Error('Contract or workspace changed during validation; rerun against the final artifact');
|
|
86
|
+
}
|
|
87
|
+
return { checks, cwd };
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
return {
|
|
91
|
+
checks,
|
|
92
|
+
cwd,
|
|
93
|
+
error: error instanceof Error ? error.message : String(error),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=verification.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verification.js","sourceRoot":"","sources":["../../src/runtime/verification.ts"],"names":[],"mappings":"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"}
|
package/dist/schema.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export declare const harness_create_params_schema: Type.TObject<{
|
|
|
9
9
|
cwd: Type.TOptional<Type.TString>;
|
|
10
10
|
allowed_paths: Type.TOptional<Type.TArray<Type.TString>>;
|
|
11
11
|
forbidden_paths: Type.TOptional<Type.TArray<Type.TString>>;
|
|
12
|
-
validation_commands: Type.
|
|
12
|
+
validation_commands: Type.TArray<Type.TString>;
|
|
13
13
|
forbidden_commands: Type.TOptional<Type.TArray<Type.TString>>;
|
|
14
14
|
allow_test_changes: Type.TOptional<Type.TBoolean>;
|
|
15
15
|
escalation_rules: Type.TOptional<Type.TArray<Type.TString>>;
|
|
@@ -58,9 +58,12 @@ export type HarnessThinking = Static<typeof thinking_levels_schema>;
|
|
|
58
58
|
export interface HarnessPolicy {
|
|
59
59
|
cwd: string;
|
|
60
60
|
forbidden_paths: string[];
|
|
61
|
-
|
|
61
|
+
allowed_paths?: string[];
|
|
62
|
+
allow_test_changes?: boolean;
|
|
63
|
+
validation_commands?: string[];
|
|
62
64
|
forbidden_commands: string[];
|
|
63
65
|
baseline_changed_files: string[];
|
|
66
|
+
baseline_file_states?: Record<string, string>;
|
|
64
67
|
}
|
|
65
68
|
export interface HarnessScaffold {
|
|
66
69
|
version: number;
|
|
@@ -108,6 +111,12 @@ export interface HarnessLogEntry {
|
|
|
108
111
|
team_status?: string;
|
|
109
112
|
remaining_risks?: string[];
|
|
110
113
|
changed_files?: string[];
|
|
114
|
+
checks?: HarnessValidationCheck[];
|
|
115
|
+
}
|
|
116
|
+
export interface HarnessValidationCheck {
|
|
117
|
+
command: string;
|
|
118
|
+
passed: boolean;
|
|
119
|
+
output_file: string;
|
|
111
120
|
}
|
|
112
121
|
export interface HarnessOutcome {
|
|
113
122
|
id: string;
|
|
@@ -121,6 +130,7 @@ export interface HarnessOutcome {
|
|
|
121
130
|
baseline_changed_files: string[];
|
|
122
131
|
validation: {
|
|
123
132
|
commands: string[];
|
|
133
|
+
checks: HarnessValidationCheck[];
|
|
124
134
|
evidence: Array<{
|
|
125
135
|
timestamp: string;
|
|
126
136
|
phase?: string;
|
package/dist/schema.js
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
import { Type } from 'typebox';
|
|
2
2
|
export const HARNESS_VERSION = 2;
|
|
3
3
|
export const HARNESS_CUSTOM_TYPE = 'pi-harness-state';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
'git reset --hard',
|
|
7
|
-
'git clean -fd',
|
|
8
|
-
'--updateSnapshot',
|
|
9
|
-
' -u ',
|
|
10
|
-
];
|
|
4
|
+
// Explicit command exclusions are optional workflow checks, not a shell sandbox.
|
|
5
|
+
export const DEFAULT_FORBIDDEN_COMMANDS = [];
|
|
11
6
|
export const thinking_levels_schema = Type.Union([
|
|
12
7
|
Type.Literal('off'),
|
|
13
8
|
Type.Literal('minimal'),
|
|
@@ -24,7 +19,10 @@ export const harness_create_params_schema = Type.Object({
|
|
|
24
19
|
description: 'Path or glob-like path the executor may edit, relative to cwd unless absolute',
|
|
25
20
|
}))),
|
|
26
21
|
forbidden_paths: Type.Optional(Type.Array(Type.String({ description: 'Path the executor may not edit' }))),
|
|
27
|
-
validation_commands: Type.
|
|
22
|
+
validation_commands: Type.Array(Type.String({
|
|
23
|
+
minLength: 1,
|
|
24
|
+
description: 'Accepted check run before completion',
|
|
25
|
+
}), { minItems: 1 }),
|
|
28
26
|
forbidden_commands: Type.Optional(Type.Array(Type.String({ description: 'Command substring to block' }))),
|
|
29
27
|
allow_test_changes: Type.Optional(Type.Boolean({
|
|
30
28
|
description: 'Whether executor may edit test files',
|
package/dist/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAe,MAAM,SAAS,CAAC;AAE5C,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC;AACjC,MAAM,CAAC,MAAM,mBAAmB,GAAG,kBAAkB,CAAC;AACtD,MAAM,CAAC,MAAM,0BAA0B,
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spences10/pi-harness",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Ephemeral task harness runtime for my-pi agent workflows",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"files": [
|
|
22
22
|
"dist",
|
|
23
23
|
"skills",
|
|
24
|
-
"README.md"
|
|
24
|
+
"README.md",
|
|
25
|
+
"CHANGELOG.md"
|
|
25
26
|
],
|
|
26
27
|
"type": "module",
|
|
27
28
|
"main": "./dist/index.js",
|
|
@@ -36,11 +37,11 @@
|
|
|
36
37
|
"access": "public"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|
|
39
|
-
"@earendil-works/pi-coding-agent": "1.
|
|
40
|
-
"@types/node": "^
|
|
40
|
+
"@earendil-works/pi-coding-agent": "1.5.0",
|
|
41
|
+
"@types/node": "^26.0.0",
|
|
41
42
|
"typebox": "^1.1.38",
|
|
42
43
|
"typescript": "^7.0.0",
|
|
43
|
-
"vite-plus": "1.
|
|
44
|
+
"vite-plus": "1.4.0"
|
|
44
45
|
},
|
|
45
46
|
"peerDependencies": {
|
|
46
47
|
"@earendil-works/pi-coding-agent": "*",
|