@spences10/pi-harness 0.0.2
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/LICENSE +21 -0
- package/README.md +118 -0
- package/dist/enforcement/policy.d.ts +13 -0
- package/dist/enforcement/policy.js +85 -0
- package/dist/enforcement/policy.js.map +1 -0
- package/dist/extension.d.ts +3 -0
- package/dist/extension.js +180 -0
- package/dist/extension.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/prompt.d.ts +1 -0
- package/dist/prompt.js +4 -0
- package/dist/prompt.js.map +1 -0
- package/dist/runtime/files.d.ts +22 -0
- package/dist/runtime/files.js +46 -0
- package/dist/runtime/files.js.map +1 -0
- package/dist/runtime/index.d.ts +8 -0
- package/dist/runtime/index.js +154 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/outcome.d.ts +5 -0
- package/dist/runtime/outcome.js +138 -0
- package/dist/runtime/outcome.js.map +1 -0
- package/dist/runtime/renderers.d.ts +7 -0
- package/dist/runtime/renderers.js +236 -0
- package/dist/runtime/renderers.js.map +1 -0
- package/dist/schema.d.ts +105 -0
- package/dist/schema.js +62 -0
- package/dist/schema.js.map +1 -0
- package/package.json +71 -0
- package/skills/create-harness/SKILL.md +54 -0
- package/skills/execute-harness/SKILL.md +55 -0
- package/skills/review-harness/SKILL.md +55 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { chmodSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join, resolve } from 'node:path';
|
|
4
|
+
import { append_event, harness_paths, read_contract, read_status, write_contract, write_status, } from './files.js';
|
|
5
|
+
import { collect_git_changed_files, write_outcome_artifacts, } from './outcome.js';
|
|
6
|
+
import { build_guard_script, build_outcome_script, build_review_script, build_system_markdown, build_task_markdown, build_validate_script, } from './renderers.js';
|
|
7
|
+
import { DEFAULT_FORBIDDEN_COMMANDS, HARNESS_VERSION, } from '../schema.js';
|
|
8
|
+
function now_iso() {
|
|
9
|
+
return new Date().toISOString();
|
|
10
|
+
}
|
|
11
|
+
function slugify(input) {
|
|
12
|
+
const slug = input
|
|
13
|
+
.toLowerCase()
|
|
14
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
15
|
+
.replace(/^-+|-+$/g, '')
|
|
16
|
+
.slice(0, 48);
|
|
17
|
+
return slug || 'task';
|
|
18
|
+
}
|
|
19
|
+
export function create_harness_runtime(params, default_cwd) {
|
|
20
|
+
const cwd = resolve(params.cwd ?? default_cwd);
|
|
21
|
+
const slug = slugify(params.slug ?? params.task);
|
|
22
|
+
const id = `${slug}-${Date.now().toString(36)}`;
|
|
23
|
+
const harness_dir = join(tmpdir(), `my-pi-harness-${id}`);
|
|
24
|
+
const paths = harness_paths(harness_dir);
|
|
25
|
+
mkdirSync(paths.logs, { recursive: true });
|
|
26
|
+
const contract = {
|
|
27
|
+
version: HARNESS_VERSION,
|
|
28
|
+
id,
|
|
29
|
+
task: params.task,
|
|
30
|
+
cwd,
|
|
31
|
+
created_at: now_iso(),
|
|
32
|
+
status: 'created',
|
|
33
|
+
planner: {
|
|
34
|
+
model: params.planner_model,
|
|
35
|
+
thinking: params.planner_thinking,
|
|
36
|
+
},
|
|
37
|
+
executor: {
|
|
38
|
+
model: params.executor_model,
|
|
39
|
+
thinking: params.executor_thinking ?? 'low',
|
|
40
|
+
},
|
|
41
|
+
reviewer: {
|
|
42
|
+
model: params.reviewer_model,
|
|
43
|
+
thinking: params.reviewer_thinking ?? 'high',
|
|
44
|
+
},
|
|
45
|
+
allowed_paths: params.allowed_paths ?? ['.'],
|
|
46
|
+
forbidden_paths: params.forbidden_paths ?? [
|
|
47
|
+
'.git/**',
|
|
48
|
+
'node_modules/**',
|
|
49
|
+
'dist/**',
|
|
50
|
+
'.env',
|
|
51
|
+
'.env.*',
|
|
52
|
+
],
|
|
53
|
+
allowed_tools: [
|
|
54
|
+
'read',
|
|
55
|
+
'bash',
|
|
56
|
+
'edit',
|
|
57
|
+
'write',
|
|
58
|
+
'lsp_diagnostics',
|
|
59
|
+
],
|
|
60
|
+
validation_commands: params.validation_commands ?? [],
|
|
61
|
+
forbidden_commands: [
|
|
62
|
+
...DEFAULT_FORBIDDEN_COMMANDS,
|
|
63
|
+
...(params.forbidden_commands ?? []),
|
|
64
|
+
],
|
|
65
|
+
allow_test_changes: params.allow_test_changes ?? false,
|
|
66
|
+
baseline_changed_files: collect_git_changed_files(cwd),
|
|
67
|
+
escalation_rules: [
|
|
68
|
+
'Required context contradicts harness.json or TASK.md.',
|
|
69
|
+
'Implementation needs edits outside allowed_paths.',
|
|
70
|
+
'Validation requires weakening tests or changing public behavior outside the contract.',
|
|
71
|
+
'Validation still fails after one focused fix attempt.',
|
|
72
|
+
],
|
|
73
|
+
};
|
|
74
|
+
write_contract(harness_dir, contract);
|
|
75
|
+
writeFileSync(paths.system, build_system_markdown(contract));
|
|
76
|
+
writeFileSync(paths.task, build_task_markdown(contract));
|
|
77
|
+
write_status(harness_dir, {
|
|
78
|
+
id,
|
|
79
|
+
status: 'created',
|
|
80
|
+
log: [
|
|
81
|
+
{
|
|
82
|
+
timestamp: contract.created_at,
|
|
83
|
+
status: 'created',
|
|
84
|
+
note: 'Harness created',
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
});
|
|
88
|
+
writeFileSync(paths.validate, build_validate_script(contract));
|
|
89
|
+
writeFileSync(paths.guard, build_guard_script());
|
|
90
|
+
writeFileSync(paths.outcome_script, build_outcome_script());
|
|
91
|
+
write_outcome_artifacts(harness_dir);
|
|
92
|
+
writeFileSync(paths.review, build_review_script(contract, paths.guard, paths.outcome_script));
|
|
93
|
+
chmodSync(paths.validate, 0o755);
|
|
94
|
+
chmodSync(paths.review, 0o755);
|
|
95
|
+
append_event(harness_dir, {
|
|
96
|
+
timestamp: contract.created_at,
|
|
97
|
+
status: 'created',
|
|
98
|
+
note: 'Harness runtime initialized',
|
|
99
|
+
});
|
|
100
|
+
return { harness_dir, contract };
|
|
101
|
+
}
|
|
102
|
+
export function update_harness_runtime(params) {
|
|
103
|
+
const contract = read_contract(params.harness_dir);
|
|
104
|
+
const status_file = read_status(params.harness_dir);
|
|
105
|
+
const entry = {
|
|
106
|
+
timestamp: now_iso(),
|
|
107
|
+
status: params.status,
|
|
108
|
+
phase: params.phase,
|
|
109
|
+
note: params.note,
|
|
110
|
+
evidence: params.evidence,
|
|
111
|
+
team_status: params.team_status,
|
|
112
|
+
remaining_risks: params.remaining_risks,
|
|
113
|
+
changed_files: params.changed_files,
|
|
114
|
+
};
|
|
115
|
+
if (params.status) {
|
|
116
|
+
status_file.status = params.status;
|
|
117
|
+
contract.status = params.status;
|
|
118
|
+
}
|
|
119
|
+
if (params.phase)
|
|
120
|
+
status_file.phase = params.phase;
|
|
121
|
+
status_file.log.push(entry);
|
|
122
|
+
write_status(params.harness_dir, status_file);
|
|
123
|
+
write_contract(params.harness_dir, contract);
|
|
124
|
+
append_event(params.harness_dir, entry);
|
|
125
|
+
write_outcome_artifacts(params.harness_dir);
|
|
126
|
+
return status_file;
|
|
127
|
+
}
|
|
128
|
+
export function format_harness_summary(harness_dir) {
|
|
129
|
+
const contract = read_contract(harness_dir);
|
|
130
|
+
const status_file = read_status(harness_dir);
|
|
131
|
+
return [
|
|
132
|
+
`Harness: ${contract.id}`,
|
|
133
|
+
`Directory: ${harness_dir}`,
|
|
134
|
+
`Status: ${status_file.status}${status_file.phase ? ` (${status_file.phase})` : ''}`,
|
|
135
|
+
`Task: ${contract.task}`,
|
|
136
|
+
`Allowed paths: ${contract.allowed_paths.join(', ') || '(none)'}`,
|
|
137
|
+
`Validation: ${contract.validation_commands.join(' && ') || '(none)'}`,
|
|
138
|
+
`Outcome: ${harness_paths(harness_dir).outcome_markdown}`,
|
|
139
|
+
].join('\n');
|
|
140
|
+
}
|
|
141
|
+
export function active_harness_context(harness_dir) {
|
|
142
|
+
const paths = harness_paths(harness_dir);
|
|
143
|
+
const contract = read_contract(harness_dir);
|
|
144
|
+
const status_file = read_status(harness_dir);
|
|
145
|
+
return `## Active my-pi harness
|
|
146
|
+
|
|
147
|
+
Harness directory: \`${harness_dir}\`
|
|
148
|
+
Harness id: \`${contract.id}\`
|
|
149
|
+
Status: \`${status_file.status}\`
|
|
150
|
+
Project cwd: \`${contract.cwd}\`
|
|
151
|
+
|
|
152
|
+
Read \`${paths.system}\`, \`${paths.task}\`, and \`${paths.contract}\` before work. Use \`harness_update\` to record phase changes, decisions, validation evidence, team status, remaining risks, and completion. Run \`${paths.validate}\` before declaring completion and \`${paths.review}\` before final review. Review \`${paths.outcome_markdown}\` or \`${paths.outcome}\` for the final outcome artifact.`;
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { HarnessOutcome } from '../schema.js';
|
|
2
|
+
export declare function collect_git_changed_files(cwd: string): string[];
|
|
3
|
+
export declare function collect_harness_outcome(harness_dir: string, cwd?: string | undefined): HarnessOutcome;
|
|
4
|
+
export declare function render_outcome_markdown(outcome: HarnessOutcome): string;
|
|
5
|
+
export declare function write_outcome_artifacts(harness_dir: string): HarnessOutcome;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { writeFileSync } from 'node:fs';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { harness_paths, json_write, read_contract, read_status, } from './files.js';
|
|
5
|
+
function now_iso() {
|
|
6
|
+
return new Date().toISOString();
|
|
7
|
+
}
|
|
8
|
+
function unique(values) {
|
|
9
|
+
return [...new Set(values.filter(Boolean))];
|
|
10
|
+
}
|
|
11
|
+
function git_lines(cwd, args) {
|
|
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);
|
|
36
|
+
const logged_files = log.flatMap((entry) => entry.changed_files ?? []);
|
|
37
|
+
return unique([...collect_git_changed_files(cwd), ...logged_files])
|
|
38
|
+
.filter((file) => !baseline.has(file))
|
|
39
|
+
.sort();
|
|
40
|
+
}
|
|
41
|
+
function latest_log_value(log, key) {
|
|
42
|
+
return [...log]
|
|
43
|
+
.reverse()
|
|
44
|
+
.find((entry) => entry[key] !== undefined)?.[key];
|
|
45
|
+
}
|
|
46
|
+
export function collect_harness_outcome(harness_dir, cwd = process.env.HARNESS_CWD) {
|
|
47
|
+
const contract = read_contract(harness_dir);
|
|
48
|
+
const status_file = read_status(harness_dir);
|
|
49
|
+
const execution_cwd = resolve(cwd ?? contract.cwd);
|
|
50
|
+
const validation_evidence = status_file.log
|
|
51
|
+
.filter((entry) => entry.evidence)
|
|
52
|
+
.map((entry) => ({
|
|
53
|
+
timestamp: entry.timestamp,
|
|
54
|
+
phase: entry.phase,
|
|
55
|
+
evidence: entry.evidence ?? '',
|
|
56
|
+
}));
|
|
57
|
+
const remaining_risks = latest_log_value(status_file.log, 'remaining_risks') ??
|
|
58
|
+
(status_file.status === 'failed'
|
|
59
|
+
? ['Harness marked failed; inspect status.json.']
|
|
60
|
+
: []);
|
|
61
|
+
return {
|
|
62
|
+
id: contract.id,
|
|
63
|
+
status: status_file.status,
|
|
64
|
+
phase: status_file.phase,
|
|
65
|
+
task: contract.task,
|
|
66
|
+
cwd: contract.cwd,
|
|
67
|
+
execution_cwd,
|
|
68
|
+
generated_at: now_iso(),
|
|
69
|
+
changed_files: changed_files(execution_cwd, status_file.log, contract.baseline_changed_files),
|
|
70
|
+
baseline_changed_files: contract.baseline_changed_files,
|
|
71
|
+
validation: {
|
|
72
|
+
commands: contract.validation_commands,
|
|
73
|
+
evidence: validation_evidence,
|
|
74
|
+
},
|
|
75
|
+
team_status: latest_log_value(status_file.log, 'team_status') ??
|
|
76
|
+
'not recorded',
|
|
77
|
+
remaining_risks,
|
|
78
|
+
log: status_file.log,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function list(values, empty) {
|
|
82
|
+
return values.length
|
|
83
|
+
? values.map((value) => `- ${value}`).join('\n')
|
|
84
|
+
: `- ${empty}`;
|
|
85
|
+
}
|
|
86
|
+
export function render_outcome_markdown(outcome) {
|
|
87
|
+
const validation_evidence = outcome.validation.evidence.length
|
|
88
|
+
? outcome.validation.evidence
|
|
89
|
+
.map((entry) => {
|
|
90
|
+
const phase = entry.phase ? ` (${entry.phase})` : '';
|
|
91
|
+
return `- ${entry.timestamp}${phase}: ${entry.evidence}`;
|
|
92
|
+
})
|
|
93
|
+
.join('\n')
|
|
94
|
+
: '- No validation evidence recorded';
|
|
95
|
+
return `# Harness outcome: ${outcome.id}
|
|
96
|
+
|
|
97
|
+
- Status: ${outcome.status}${outcome.phase ? ` (${outcome.phase})` : ''}
|
|
98
|
+
- Generated: ${outcome.generated_at}
|
|
99
|
+
- Project cwd: ${outcome.cwd}
|
|
100
|
+
- Execution cwd: ${outcome.execution_cwd}
|
|
101
|
+
|
|
102
|
+
## Task
|
|
103
|
+
|
|
104
|
+
${outcome.task}
|
|
105
|
+
|
|
106
|
+
## Changed files
|
|
107
|
+
|
|
108
|
+
${list(outcome.changed_files, 'No changed files detected')}
|
|
109
|
+
|
|
110
|
+
## Baseline ignored files
|
|
111
|
+
|
|
112
|
+
${list(outcome.baseline_changed_files, 'No dirty baseline files recorded')}
|
|
113
|
+
|
|
114
|
+
## Validation evidence
|
|
115
|
+
|
|
116
|
+
Commands:
|
|
117
|
+
${list(outcome.validation.commands, 'No validation commands configured')}
|
|
118
|
+
|
|
119
|
+
Evidence:
|
|
120
|
+
${validation_evidence}
|
|
121
|
+
|
|
122
|
+
## Team status
|
|
123
|
+
|
|
124
|
+
${outcome.team_status}
|
|
125
|
+
|
|
126
|
+
## Remaining risks
|
|
127
|
+
|
|
128
|
+
${list(outcome.remaining_risks, 'No remaining risks recorded')}
|
|
129
|
+
`;
|
|
130
|
+
}
|
|
131
|
+
export function write_outcome_artifacts(harness_dir) {
|
|
132
|
+
const outcome = collect_harness_outcome(harness_dir);
|
|
133
|
+
const paths = harness_paths(harness_dir);
|
|
134
|
+
json_write(paths.outcome, outcome);
|
|
135
|
+
writeFileSync(paths.outcome_markdown, render_outcome_markdown(outcome));
|
|
136
|
+
return outcome;
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=outcome.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outcome.js","sourceRoot":"","sources":["../../src/runtime/outcome.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { HarnessContract } from '../schema.js';
|
|
2
|
+
export declare function build_system_markdown(contract: HarnessContract): string;
|
|
3
|
+
export declare function build_task_markdown(contract: HarnessContract): string;
|
|
4
|
+
export declare function build_validate_script(contract: HarnessContract): string;
|
|
5
|
+
export declare function build_guard_script(): string;
|
|
6
|
+
export declare function build_outcome_script(): string;
|
|
7
|
+
export declare function build_review_script(contract: HarnessContract, guard_path: string, outcome_script_path: string): string;
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { shell_quote } from './files.js';
|
|
2
|
+
export function build_system_markdown(contract) {
|
|
3
|
+
return `# Executor system prompt: ${contract.id}
|
|
4
|
+
|
|
5
|
+
You are working inside a my-pi task harness. The harness directory is external control state, not the project source.
|
|
6
|
+
|
|
7
|
+
## Execution contract
|
|
8
|
+
|
|
9
|
+
- Project cwd: \`${contract.cwd}\`
|
|
10
|
+
- Allowed edit paths: ${contract.allowed_paths.map((path) => `\`${path}\``).join(', ') || 'none'}
|
|
11
|
+
- Forbidden edit paths: ${contract.forbidden_paths.map((path) => `\`${path}\``).join(', ') || 'none'}
|
|
12
|
+
- Test changes allowed: ${contract.allow_test_changes ? 'yes' : 'no'}
|
|
13
|
+
- Validation: ${contract.validation_commands.map((command) => `\`${command}\``).join(', ') || 'none'}
|
|
14
|
+
|
|
15
|
+
## Rules
|
|
16
|
+
|
|
17
|
+
1. Recover context from source-of-truth files before editing.
|
|
18
|
+
2. Challenge assumptions and record decisions in \`status.json\` through \`harness_update\`.
|
|
19
|
+
3. Edit only allowed paths.
|
|
20
|
+
4. Do not weaken tests, fake outputs, or bypass validation.
|
|
21
|
+
5. Run \`validate.sh\` before completion.
|
|
22
|
+
6. Escalate instead of silently expanding scope.
|
|
23
|
+
`;
|
|
24
|
+
}
|
|
25
|
+
export function build_task_markdown(contract) {
|
|
26
|
+
return `# Harness task: ${contract.id}
|
|
27
|
+
|
|
28
|
+
## Task
|
|
29
|
+
|
|
30
|
+
${contract.task}
|
|
31
|
+
|
|
32
|
+
## Required loop
|
|
33
|
+
|
|
34
|
+
1. Context recovery
|
|
35
|
+
2. Source-of-truth capture
|
|
36
|
+
3. Assumption challenge
|
|
37
|
+
4. Alignment checkpoint
|
|
38
|
+
5. Surgical execution
|
|
39
|
+
6. Validation evidence
|
|
40
|
+
7. Drift review
|
|
41
|
+
8. Delta/risk report
|
|
42
|
+
|
|
43
|
+
## Escalation rules
|
|
44
|
+
|
|
45
|
+
${contract.escalation_rules.map((rule) => `- ${rule}`).join('\n')}
|
|
46
|
+
`;
|
|
47
|
+
}
|
|
48
|
+
function build_resolve_cwd_script(contract) {
|
|
49
|
+
return `contract_cwd=${shell_quote(contract.cwd)}
|
|
50
|
+
run_cwd="\${HARNESS_CWD:-$(pwd)}"
|
|
51
|
+
if git -C "$run_cwd" rev-parse --git-common-dir >/dev/null 2>&1 && git -C "$contract_cwd" rev-parse --git-common-dir >/dev/null 2>&1; then
|
|
52
|
+
run_common=$(git -C "$run_cwd" rev-parse --path-format=absolute --git-common-dir)
|
|
53
|
+
contract_common=$(git -C "$contract_cwd" rev-parse --path-format=absolute --git-common-dir)
|
|
54
|
+
if [ "$run_common" != "$contract_common" ]; then
|
|
55
|
+
run_cwd="$contract_cwd"
|
|
56
|
+
fi
|
|
57
|
+
else
|
|
58
|
+
run_cwd="$contract_cwd"
|
|
59
|
+
fi
|
|
60
|
+
export HARNESS_CWD="$run_cwd"`;
|
|
61
|
+
}
|
|
62
|
+
export function build_validate_script(contract) {
|
|
63
|
+
const commands = contract.validation_commands.length
|
|
64
|
+
? contract.validation_commands
|
|
65
|
+
: ['echo "No validation commands configured"'];
|
|
66
|
+
return `#!/usr/bin/env bash
|
|
67
|
+
set -euo pipefail
|
|
68
|
+
${build_resolve_cwd_script(contract)}
|
|
69
|
+
cd "$HARNESS_CWD"
|
|
70
|
+
${commands
|
|
71
|
+
.map((command, index) => `echo "[${index + 1}/${commands.length}] ${command.replaceAll('"', '\\"')}"
|
|
72
|
+
bash -lc ${shell_quote(command)}`)
|
|
73
|
+
.join('\n')}
|
|
74
|
+
`;
|
|
75
|
+
}
|
|
76
|
+
export function build_guard_script() {
|
|
77
|
+
return `import { execFileSync } from 'node:child_process';
|
|
78
|
+
import { readFileSync } from 'node:fs';
|
|
79
|
+
import { isAbsolute, relative, resolve } from 'node:path';
|
|
80
|
+
|
|
81
|
+
const contract = JSON.parse(readFileSync(new URL('./harness.json', import.meta.url), 'utf8'));
|
|
82
|
+
const cwd = resolve(process.env.HARNESS_CWD || contract.cwd);
|
|
83
|
+
const baseline = new Set(contract.baseline_changed_files || []);
|
|
84
|
+
|
|
85
|
+
function changed_paths() {
|
|
86
|
+
const status = execFileSync('git', ['-C', cwd, 'status', '--short', '--untracked-files=all'], { encoding: 'utf8' })
|
|
87
|
+
.split('\\n')
|
|
88
|
+
.map((line) => line.trimEnd())
|
|
89
|
+
.filter(Boolean)
|
|
90
|
+
.map((line) => line.slice(3).split(' -> ').pop());
|
|
91
|
+
const diff = execFileSync('git', ['-C', cwd, 'diff', '--name-only'], { encoding: 'utf8' })
|
|
92
|
+
.split('\\n')
|
|
93
|
+
.filter(Boolean);
|
|
94
|
+
return [...new Set([...status, ...diff])];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function escape_regex(value) {
|
|
98
|
+
const special = new Set(['.', '*', '+', '?', '^', '$', '{', '}', '(', ')', '|', '[', ']', '\\\\', '-']);
|
|
99
|
+
return [...value].map((char) => special.has(char) ? '\\\\' + char : char).join('');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function pattern_to_relative(pattern) {
|
|
103
|
+
const normalized_pattern = pattern.replaceAll('\\\\', '/');
|
|
104
|
+
if (!isAbsolute(pattern)) return normalized_pattern;
|
|
105
|
+
return relative(cwd, resolve(pattern)).replaceAll('\\\\', '/');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function pattern_matches(pattern, relative_path) {
|
|
109
|
+
const normalized_pattern = pattern_to_relative(pattern);
|
|
110
|
+
if (normalized_pattern === '.') return true;
|
|
111
|
+
if (normalized_pattern.endsWith('/**')) {
|
|
112
|
+
const prefix = normalized_pattern.slice(0, -3);
|
|
113
|
+
return relative_path === prefix || relative_path.startsWith(prefix + '/');
|
|
114
|
+
}
|
|
115
|
+
if (normalized_pattern.includes('*')) {
|
|
116
|
+
const regex = new RegExp('^' + escape_regex(normalized_pattern).replaceAll('\\\\*\\\\*', '.*').replaceAll('\\\\*', '[^/]*') + '$');
|
|
117
|
+
return regex.test(relative_path);
|
|
118
|
+
}
|
|
119
|
+
return relative_path === normalized_pattern || relative_path.startsWith(normalized_pattern + '/');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function is_test_path(relative_path) {
|
|
123
|
+
return /(^|\\/)(__tests__|tests?)\\//.test(relative_path) || /\\.(test|spec)\\.[cm]?[jt]sx?$/.test(relative_path);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const violations = [];
|
|
127
|
+
for (const path of changed_paths()) {
|
|
128
|
+
if (baseline.has(path)) continue;
|
|
129
|
+
const absolute_path = resolve(cwd, path);
|
|
130
|
+
if (absolute_path !== cwd && !absolute_path.startsWith(cwd + '/')) violations.push(path + ' is outside cwd');
|
|
131
|
+
if (contract.forbidden_paths.some((pattern) => pattern_matches(pattern, path))) violations.push(path + ' matches forbidden_paths');
|
|
132
|
+
if (!contract.allow_test_changes && is_test_path(path)) violations.push(path + ' is a test change but allow_test_changes is false');
|
|
133
|
+
if (!contract.allowed_paths.some((pattern) => pattern_matches(pattern, path))) violations.push(path + ' is outside allowed_paths');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (violations.length) {
|
|
137
|
+
console.error('Harness guard failed:');
|
|
138
|
+
for (const violation of violations) console.error('- ' + violation);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
console.log('Harness guard passed');
|
|
142
|
+
`;
|
|
143
|
+
}
|
|
144
|
+
export function build_outcome_script() {
|
|
145
|
+
return `import { execFileSync } from 'node:child_process';
|
|
146
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
147
|
+
import { dirname, join } from 'node:path';
|
|
148
|
+
import { fileURLToPath } from 'node:url';
|
|
149
|
+
|
|
150
|
+
const harness_dir = dirname(fileURLToPath(import.meta.url));
|
|
151
|
+
const contract = JSON.parse(readFileSync(join(harness_dir, 'harness.json'), 'utf8'));
|
|
152
|
+
const status = JSON.parse(readFileSync(join(harness_dir, 'status.json'), 'utf8'));
|
|
153
|
+
const cwd = process.env.HARNESS_CWD || contract.cwd;
|
|
154
|
+
const baseline = new Set(contract.baseline_changed_files || []);
|
|
155
|
+
|
|
156
|
+
function git_lines(args) {
|
|
157
|
+
try {
|
|
158
|
+
return execFileSync('git', ['-C', cwd, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] })
|
|
159
|
+
.split('\\n')
|
|
160
|
+
.map((line) => line.trimEnd())
|
|
161
|
+
.filter(Boolean);
|
|
162
|
+
} catch {
|
|
163
|
+
return [];
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function unique(values) {
|
|
168
|
+
return [...new Set(values.filter(Boolean))];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const status_files = git_lines(['status', '--short', '--untracked-files=all']).map((line) => line.slice(3).split(' -> ').pop()?.replace(/\\/$/, ''));
|
|
172
|
+
const diff_files = git_lines(['diff', '--name-only']);
|
|
173
|
+
const logged_files = status.log.flatMap((entry) => entry.changed_files ?? []);
|
|
174
|
+
const latest = (key) => [...status.log].reverse().find((entry) => entry[key] !== undefined)?.[key];
|
|
175
|
+
const outcome = {
|
|
176
|
+
id: contract.id,
|
|
177
|
+
status: status.status,
|
|
178
|
+
phase: status.phase,
|
|
179
|
+
task: contract.task,
|
|
180
|
+
cwd: contract.cwd,
|
|
181
|
+
execution_cwd: cwd,
|
|
182
|
+
generated_at: new Date().toISOString(),
|
|
183
|
+
changed_files: unique([...status_files, ...diff_files, ...logged_files]).filter((file) => !baseline.has(file)).sort(),
|
|
184
|
+
baseline_changed_files: contract.baseline_changed_files || [],
|
|
185
|
+
validation: {
|
|
186
|
+
commands: contract.validation_commands,
|
|
187
|
+
evidence: status.log
|
|
188
|
+
.filter((entry) => entry.evidence)
|
|
189
|
+
.map((entry) => ({ timestamp: entry.timestamp, phase: entry.phase, evidence: entry.evidence })),
|
|
190
|
+
},
|
|
191
|
+
team_status: latest('team_status') ?? 'not recorded',
|
|
192
|
+
remaining_risks: latest('remaining_risks') ?? (status.status === 'failed' ? ['Harness marked failed; inspect status.json.'] : []),
|
|
193
|
+
log: status.log,
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const list = (values, empty) => values.length ? values.map((value) => '- ' + value).join('\\n') : '- ' + empty;
|
|
197
|
+
const evidence = outcome.validation.evidence.length
|
|
198
|
+
? outcome.validation.evidence.map((entry) => '- ' + entry.timestamp + (entry.phase ? ' (' + entry.phase + ')' : '') + ': ' + entry.evidence).join('\\n')
|
|
199
|
+
: '- No validation evidence recorded';
|
|
200
|
+
const markdown = '# Harness outcome: ' + outcome.id + '\\n\\n'
|
|
201
|
+
+ '- Status: ' + outcome.status + (outcome.phase ? ' (' + outcome.phase + ')' : '') + '\\n'
|
|
202
|
+
+ '- Generated: ' + outcome.generated_at + '\\n'
|
|
203
|
+
+ '- Project cwd: ' + outcome.cwd + '\\n\\n'
|
|
204
|
+
+ '## Task\\n\\n' + outcome.task + '\\n\\n'
|
|
205
|
+
+ '## Changed files\\n\\n' + list(outcome.changed_files, 'No changed files detected') + '\\n\\n'
|
|
206
|
+
+ '## Validation evidence\\n\\nCommands:\\n' + list(outcome.validation.commands, 'No validation commands configured') + '\\n\\nEvidence:\\n' + evidence + '\\n\\n'
|
|
207
|
+
+ '## Team status\\n\\n' + outcome.team_status + '\\n\\n'
|
|
208
|
+
+ '## Remaining risks\\n\\n' + list(outcome.remaining_risks, 'No remaining risks recorded') + '\\n';
|
|
209
|
+
|
|
210
|
+
writeFileSync(join(harness_dir, 'outcome.json'), JSON.stringify(outcome, null, 2) + '\\n');
|
|
211
|
+
writeFileSync(join(harness_dir, 'OUTCOME.md'), markdown);
|
|
212
|
+
console.log('Wrote ' + join(harness_dir, 'OUTCOME.md'));
|
|
213
|
+
`;
|
|
214
|
+
}
|
|
215
|
+
export function build_review_script(contract, guard_path, outcome_script_path) {
|
|
216
|
+
return `#!/usr/bin/env bash
|
|
217
|
+
set -euo pipefail
|
|
218
|
+
${build_resolve_cwd_script(contract)}
|
|
219
|
+
cd "$HARNESS_CWD"
|
|
220
|
+
echo "## git status"
|
|
221
|
+
git status --short || true
|
|
222
|
+
echo
|
|
223
|
+
echo "## diff stat"
|
|
224
|
+
git diff --stat || true
|
|
225
|
+
echo
|
|
226
|
+
echo "## outcome"
|
|
227
|
+
node ${shell_quote(outcome_script_path)}
|
|
228
|
+
echo
|
|
229
|
+
echo "## harness guard"
|
|
230
|
+
node ${shell_quote(guard_path)}
|
|
231
|
+
echo
|
|
232
|
+
echo "## harness"
|
|
233
|
+
echo ${shell_quote(contract.id)}
|
|
234
|
+
`;
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=renderers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderers.js","sourceRoot":"","sources":["../../src/runtime/renderers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAGzC,MAAM,UAAU,qBAAqB,CACpC,QAAyB;IAEzB,OAAO,6BAA6B,QAAQ,CAAC,EAAE;;;;;;mBAM7B,QAAQ,CAAC,GAAG;wBACP,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM;0BACtE,QAAQ,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM;0BAC1E,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;gBACpD,QAAQ,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,OAAO,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM;;;;;;;;;;CAUnG,CAAC;AACF,CAAC;AAED,MAAM,UAAU,mBAAmB,CAClC,QAAyB;IAEzB,OAAO,mBAAmB,QAAQ,CAAC,EAAE;;;;EAIpC,QAAQ,CAAC,IAAI;;;;;;;;;;;;;;;EAeb,QAAQ,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;CAChE,CAAC;AACF,CAAC;AAED,SAAS,wBAAwB,CAAC,QAAyB;IAC1D,OAAO,gBAAgB,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC;;;;;;;;;;;8BAWnB,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,qBAAqB,CACpC,QAAyB;IAEzB,MAAM,QAAQ,GAAG,QAAQ,CAAC,mBAAmB,CAAC,MAAM;QACnD,CAAC,CAAC,QAAQ,CAAC,mBAAmB;QAC9B,CAAC,CAAC,CAAC,0CAA0C,CAAC,CAAC;IAChD,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiEP,CAAC;AACF,CAAC;AAED,MAAM,UAAU,oBAAoB;IACnC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoEP,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"}
|