simt-dev 0.2.2 → 0.3.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 +37 -0
- package/bin/simt-dev.mjs +21 -2
- package/docs/decisions/ADR-074-a-rule-and-a-decision-are-readable-by-uri.md +99 -0
- package/docs/decisions/ADR-075-nothing-leaves-the-machine.md +56 -0
- package/docs/decisions/ADR-076-a-change-to-what-agents-read-is-measured.md +70 -0
- package/docs/decisions/ADR-077-the-command-card-leads-the-region-and-reads-committed-evidence.md +91 -0
- package/docs/decisions/ADR-078-a-line-the-repository-already-proves-does-not-ship.md +117 -0
- package/docs/decisions/ADR-079-the-rules-are-enforced-at-the-tool-call-from-one-policy-table.md +162 -0
- package/docs/decisions/ADR-080-the-tools-own-names-are-not-repositories-and-neither-reader-decides-by-shape.md +99 -0
- package/docs/decisions/ADR-081-a-tracker-is-read-through-the-repositorys-template.md +36 -0
- package/docs/decisions/ADR-082-the-tracker-override-is-a-manifest-key.md +35 -0
- package/package.json +2 -1
- package/src/agenthooks/decide.mjs +123 -0
- package/src/agenthooks/policy.mjs +78 -0
- package/src/agenthooks/render.mjs +240 -0
- package/src/argv.mjs +36 -1
- package/src/bench/ablate.mjs +630 -0
- package/src/bench/adapters.json +149 -0
- package/src/bench/grade.mjs +229 -0
- package/src/bench/mine.mjs +811 -0
- package/src/bench/probes.mjs +1350 -0
- package/src/bench/report.mjs +899 -0
- package/src/bench/run.mjs +1002 -0
- package/src/commands/agenthook.mjs +282 -0
- package/src/commands/bench.mjs +359 -0
- package/src/commands/harness.mjs +156 -5
- package/src/commands/help.mjs +58 -1
- package/src/commands/index.mjs +25 -1
- package/src/commands/plan.mjs +99 -0
- package/src/commands/tokens.mjs +31 -0
- package/src/decisions/index.mjs +23 -5
- package/src/kernel/card.mjs +254 -0
- package/src/kernel/inferability.mjs +188 -0
- package/src/mcp/resources.mjs +157 -0
- package/src/mcp/server.mjs +37 -6
- package/src/mcp/tools.mjs +84 -0
- package/src/measure/context.mjs +177 -6
- package/src/measure/loaderView.mjs +435 -0
- package/src/measure/session.mjs +267 -0
- package/src/plan/index.mjs +650 -0
- package/src/plan/template.mjs +331 -0
- package/src/projection/loaders.mjs +689 -0
- package/src/projection/manifest.mjs +26 -3
- package/src/projection/rules.mjs +80 -5
- package/src/projection/surfaces.mjs +22 -8
- package/src/toolwords.mjs +69 -0
- package/templates/plan/tracker.json +36 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"adapters": {
|
|
4
|
+
"claude": {
|
|
5
|
+
"id": "claude",
|
|
6
|
+
"label": "Claude Code",
|
|
7
|
+
"kind": "cli",
|
|
8
|
+
"bin": "claude",
|
|
9
|
+
"template": "claude -p \"{prompt}\" --output-format json --max-turns {turns}",
|
|
10
|
+
"args": ["-p", "{prompt}", "--output-format", "json", "--max-turns", "{turns}"],
|
|
11
|
+
"modelArgs": ["--model", "{model}"],
|
|
12
|
+
"format": "json",
|
|
13
|
+
"usageSource": "usage.*, total_cost_usd, num_turns",
|
|
14
|
+
"fields": {
|
|
15
|
+
"input": ["usage.input_tokens", "usage.input"],
|
|
16
|
+
"cacheWrite": ["usage.cache_creation_input_tokens", "usage.cacheWrite"],
|
|
17
|
+
"cacheRead": ["usage.cache_read_input_tokens", "usage.cacheRead"],
|
|
18
|
+
"output": ["usage.output_tokens", "usage.output"],
|
|
19
|
+
"turns": ["num_turns", "usage.turns"],
|
|
20
|
+
"toolCalls": ["num_tool_calls", "tool_calls", "usage.toolCalls"],
|
|
21
|
+
"costUsd": ["total_cost_usd", "cost_usd", "costUsd"]
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"codex": {
|
|
25
|
+
"id": "codex",
|
|
26
|
+
"label": "Codex",
|
|
27
|
+
"kind": "cli",
|
|
28
|
+
"bin": "codex",
|
|
29
|
+
"template": "codex exec --json \"{prompt}\"",
|
|
30
|
+
"args": ["exec", "--json", "{prompt}"],
|
|
31
|
+
"modelArgs": ["--model", "{model}"],
|
|
32
|
+
"format": "jsonl",
|
|
33
|
+
"usageSource": "token events in JSONL",
|
|
34
|
+
"fields": {
|
|
35
|
+
"input": ["usage.input_tokens", "input_tokens", "usage.input"],
|
|
36
|
+
"cacheWrite": ["usage.cache_creation_input_tokens", "usage.cache_write_input_tokens", "cache_write_input_tokens"],
|
|
37
|
+
"cacheRead": ["usage.cached_input_tokens", "usage.cache_read_input_tokens", "cached_input_tokens"],
|
|
38
|
+
"output": ["usage.output_tokens", "output_tokens", "usage.output"],
|
|
39
|
+
"turns": ["turns", "num_turns"],
|
|
40
|
+
"toolCalls": ["tool_calls", "num_tool_calls"],
|
|
41
|
+
"costUsd": ["total_cost_usd", "cost_usd", "cost"]
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"gemini": {
|
|
45
|
+
"id": "gemini",
|
|
46
|
+
"label": "Gemini CLI",
|
|
47
|
+
"kind": "cli",
|
|
48
|
+
"bin": "gemini",
|
|
49
|
+
"template": "gemini -p \"{prompt}\" --output-format json",
|
|
50
|
+
"args": ["-p", "{prompt}", "--output-format", "json"],
|
|
51
|
+
"modelArgs": ["--model", "{model}"],
|
|
52
|
+
"format": "json",
|
|
53
|
+
"usageSource": "stats block",
|
|
54
|
+
"fields": {
|
|
55
|
+
"input": ["stats.inputTokens", "stats.tokens.prompt", "stats.prompt_tokens", "usage.input_tokens"],
|
|
56
|
+
"cacheWrite": ["stats.cacheWriteTokens", "stats.tokens.cacheWrite", "usage.cache_creation_input_tokens"],
|
|
57
|
+
"cacheRead": ["stats.cachedTokens", "stats.tokens.cached", "usage.cache_read_input_tokens"],
|
|
58
|
+
"output": ["stats.outputTokens", "stats.tokens.candidates", "stats.completion_tokens", "usage.output_tokens"],
|
|
59
|
+
"turns": ["stats.turns", "stats.totalTurns", "num_turns"],
|
|
60
|
+
"toolCalls": ["stats.toolCalls", "stats.tools.totalCalls", "num_tool_calls"],
|
|
61
|
+
"costUsd": ["stats.totalCostUsd", "stats.costUsd", "total_cost_usd"]
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"opencode": {
|
|
65
|
+
"id": "opencode",
|
|
66
|
+
"label": "opencode",
|
|
67
|
+
"kind": "cli",
|
|
68
|
+
"bin": "opencode",
|
|
69
|
+
"template": "opencode run \"{prompt}\"",
|
|
70
|
+
"args": ["run", "--format", "json", "{prompt}"],
|
|
71
|
+
"modelArgs": ["--model", "{model}"],
|
|
72
|
+
"format": "jsonl",
|
|
73
|
+
"usageSource": "session export",
|
|
74
|
+
"fields": {
|
|
75
|
+
"input": ["tokens.input", "usage.input_tokens", "part.tokens.input"],
|
|
76
|
+
"cacheWrite": ["tokens.cache.write", "usage.cache_creation_input_tokens", "part.tokens.cache.write"],
|
|
77
|
+
"cacheRead": ["tokens.cache.read", "usage.cache_read_input_tokens", "part.tokens.cache.read"],
|
|
78
|
+
"output": ["tokens.output", "usage.output_tokens", "part.tokens.output"],
|
|
79
|
+
"turns": ["turns", "num_turns"],
|
|
80
|
+
"toolCalls": ["toolCalls", "num_tool_calls"],
|
|
81
|
+
"costUsd": ["cost", "part.cost", "total_cost_usd"]
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"cursor": {
|
|
85
|
+
"id": "cursor",
|
|
86
|
+
"label": "Cursor CLI",
|
|
87
|
+
"kind": "cli",
|
|
88
|
+
"bin": "cursor-agent",
|
|
89
|
+
"template": "cursor-agent -p \"{prompt}\" --output-format json",
|
|
90
|
+
"args": ["-p", "{prompt}", "--output-format", "json"],
|
|
91
|
+
"modelArgs": ["--model", "{model}"],
|
|
92
|
+
"format": "json",
|
|
93
|
+
"usageSource": "result JSON",
|
|
94
|
+
"fields": {
|
|
95
|
+
"input": ["usage.input_tokens", "usage.input", "stats.inputTokens"],
|
|
96
|
+
"cacheWrite": ["usage.cache_creation_input_tokens", "usage.cacheWrite", "stats.cacheWriteTokens"],
|
|
97
|
+
"cacheRead": ["usage.cache_read_input_tokens", "usage.cacheRead", "stats.cachedTokens"],
|
|
98
|
+
"output": ["usage.output_tokens", "usage.output", "stats.outputTokens"],
|
|
99
|
+
"turns": ["num_turns", "turns", "usage.turns"],
|
|
100
|
+
"toolCalls": ["num_tool_calls", "tool_calls", "usage.toolCalls", "toolCalls"],
|
|
101
|
+
"costUsd": ["total_cost_usd", "costUsd", "cost_usd"]
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"aider": {
|
|
105
|
+
"id": "aider",
|
|
106
|
+
"label": "Aider",
|
|
107
|
+
"kind": "cli",
|
|
108
|
+
"bin": "aider",
|
|
109
|
+
"template": "aider --message \"{prompt}\" --yes-always",
|
|
110
|
+
"args": ["--message", "{prompt}", "--yes-always", "--analytics-log", "{analyticsLog}"],
|
|
111
|
+
"modelArgs": ["--model", "{model}"],
|
|
112
|
+
"format": "analytics-log",
|
|
113
|
+
"usageSource": "--analytics-log file",
|
|
114
|
+
"fields": {
|
|
115
|
+
"input": ["prompt_tokens", "properties.prompt_tokens", "usage.input_tokens"],
|
|
116
|
+
"cacheWrite": ["cache_creation_input_tokens", "properties.cache_write_tokens"],
|
|
117
|
+
"cacheRead": ["cache_read_input_tokens", "properties.cache_read_tokens"],
|
|
118
|
+
"output": ["completion_tokens", "properties.completion_tokens", "usage.output_tokens"],
|
|
119
|
+
"turns": ["turns", "properties.turns"],
|
|
120
|
+
"toolCalls": ["tool_calls", "properties.tool_calls"],
|
|
121
|
+
"costUsd": ["cost", "total_cost", "properties.cost", "properties.total_cost"]
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
"gold": {
|
|
125
|
+
"id": "gold",
|
|
126
|
+
"label": "Gold Oracle",
|
|
127
|
+
"kind": "builtin",
|
|
128
|
+
"bin": null,
|
|
129
|
+
"template": null,
|
|
130
|
+
"args": [],
|
|
131
|
+
"modelArgs": [],
|
|
132
|
+
"format": "builtin",
|
|
133
|
+
"usageSource": "vault gold.patch",
|
|
134
|
+
"fields": {}
|
|
135
|
+
},
|
|
136
|
+
"noop": {
|
|
137
|
+
"id": "noop",
|
|
138
|
+
"label": "No-op Baseline",
|
|
139
|
+
"kind": "builtin",
|
|
140
|
+
"bin": null,
|
|
141
|
+
"template": null,
|
|
142
|
+
"args": [],
|
|
143
|
+
"modelArgs": [],
|
|
144
|
+
"format": "builtin",
|
|
145
|
+
"usageSource": "none",
|
|
146
|
+
"fields": {}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { readFile, rm } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
import { runGates } from '../hooks/dispatcher.mjs';
|
|
6
|
+
import { packageVersion } from '../package-version.mjs';
|
|
7
|
+
import { readManifest } from '../projection/manifest.mjs';
|
|
8
|
+
import { detectTestRunner } from './mine.mjs';
|
|
9
|
+
|
|
10
|
+
const DEFAULT_GRADE_TIMEOUT_MS = 45_000;
|
|
11
|
+
|
|
12
|
+
function sanitizedEnv(extra = {}) {
|
|
13
|
+
const env = { ...process.env, SIMT_DEV_WORKSPACE: 'off', ...extra };
|
|
14
|
+
for (const key of Object.keys(env)) {
|
|
15
|
+
if (key.startsWith('NODE_TEST')) delete env[key];
|
|
16
|
+
}
|
|
17
|
+
return env;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function runCmd(cmd, args, { cwd, input = null, timeoutMs = DEFAULT_GRADE_TIMEOUT_MS } = {}) {
|
|
21
|
+
return new Promise((resolveOutcome) => {
|
|
22
|
+
const child = spawn(cmd, args, {
|
|
23
|
+
cwd,
|
|
24
|
+
env: sanitizedEnv(),
|
|
25
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
26
|
+
});
|
|
27
|
+
const out = [];
|
|
28
|
+
const err = [];
|
|
29
|
+
let timedOut = false;
|
|
30
|
+
const timer =
|
|
31
|
+
timeoutMs > 0
|
|
32
|
+
? setTimeout(() => {
|
|
33
|
+
timedOut = true;
|
|
34
|
+
child.kill('SIGKILL');
|
|
35
|
+
}, timeoutMs)
|
|
36
|
+
: null;
|
|
37
|
+
|
|
38
|
+
child.stdout.on('data', (chunk) => out.push(chunk));
|
|
39
|
+
child.stderr.on('data', (chunk) => err.push(chunk));
|
|
40
|
+
child.on('error', (error) => {
|
|
41
|
+
if (timer) clearTimeout(timer);
|
|
42
|
+
resolveOutcome({
|
|
43
|
+
code: 1,
|
|
44
|
+
timedOut,
|
|
45
|
+
stdout: Buffer.concat(out),
|
|
46
|
+
stderr: Buffer.from(String(error.message ?? error)),
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
child.on('close', (code) => {
|
|
50
|
+
if (timer) clearTimeout(timer);
|
|
51
|
+
resolveOutcome({
|
|
52
|
+
code: timedOut ? 124 : (code ?? 1),
|
|
53
|
+
timedOut,
|
|
54
|
+
stdout: Buffer.concat(out),
|
|
55
|
+
stderr: Buffer.concat(err),
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (input !== null) child.stdin.end(input);
|
|
60
|
+
else child.stdin.end();
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Extract repository-relative file paths touched by a unified git diff.
|
|
66
|
+
*
|
|
67
|
+
* @param {Buffer|string} patch
|
|
68
|
+
* @returns {string[]}
|
|
69
|
+
*/
|
|
70
|
+
export function extractPatchFiles(patch) {
|
|
71
|
+
const text = Buffer.isBuffer(patch) ? patch.toString('utf8') : String(patch ?? '');
|
|
72
|
+
const files = new Set();
|
|
73
|
+
for (const line of text.split('\n')) {
|
|
74
|
+
const match = /^diff --git a\/(.+?) b\/(.+)$/.exec(line);
|
|
75
|
+
if (match) files.add(match[2]);
|
|
76
|
+
}
|
|
77
|
+
return [...files].sort();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function resetFilesToHead(workDir, initialHead, files) {
|
|
81
|
+
for (const file of files) {
|
|
82
|
+
const existsAtHead = await runCmd('git', ['cat-file', '-e', `${initialHead}:${file}`], { cwd: workDir });
|
|
83
|
+
if (existsAtHead.code === 0) {
|
|
84
|
+
await runCmd('git', ['checkout', '-f', initialHead, '--', file], { cwd: workDir });
|
|
85
|
+
} else {
|
|
86
|
+
await runCmd('git', ['rm', '-f', '--ignore-unmatch', '--', file], { cwd: workDir });
|
|
87
|
+
await rm(join(workDir, file), { force: true }).catch(() => {});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function evaluateDeclaredGates(workDir, testPassed) {
|
|
93
|
+
const { exists, data: manifest, error } = await readManifest(workDir);
|
|
94
|
+
if (!exists) return testPassed;
|
|
95
|
+
if (error || !manifest) return false;
|
|
96
|
+
|
|
97
|
+
const hasCustomTestCmd = Boolean(manifest?.gates?.commands?.test);
|
|
98
|
+
const filterGates = (list) =>
|
|
99
|
+
Array.isArray(list) ? list.filter((g) => g !== 'test' || hasCustomTestCmd) : [];
|
|
100
|
+
|
|
101
|
+
const alignedManifest = {
|
|
102
|
+
...manifest,
|
|
103
|
+
projection: {
|
|
104
|
+
...(manifest.projection ?? {}),
|
|
105
|
+
version: packageVersion(),
|
|
106
|
+
},
|
|
107
|
+
gates: {
|
|
108
|
+
...(manifest.gates ?? {}),
|
|
109
|
+
preCommit: filterGates(manifest?.gates?.preCommit),
|
|
110
|
+
prePush: filterGates(manifest?.gates?.prePush),
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
await runCmd('git', ['add', '-A'], { cwd: workDir });
|
|
115
|
+
const preCommit = await runGates('pre-commit', { root: workDir, manifest: alignedManifest });
|
|
116
|
+
const prePush = await runGates('pre-push', { root: workDir, manifest: alignedManifest });
|
|
117
|
+
return testPassed && !preCommit.failed && !prePush.failed;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Grade an isolated agent workspace after the agent finishes.
|
|
122
|
+
*
|
|
123
|
+
* Measures the agent's diff before applying the hidden verifier test patch, then resets
|
|
124
|
+
* any test files touched by `test.patch` to `initialHead` so an agent cannot pass by
|
|
125
|
+
* weakening or deleting the test suite.
|
|
126
|
+
*
|
|
127
|
+
* @param {string} workDir
|
|
128
|
+
* @param {object} task
|
|
129
|
+
* @param {{vaultRoot: string, initialHead: string, timeoutMs?: number}} options
|
|
130
|
+
* @returns {Promise<{resolved: boolean, gatesPassed: boolean, diff: {files: number, outsideBlastRadius: number}, agentPatch: Buffer}>}
|
|
131
|
+
*/
|
|
132
|
+
export async function gradeWorkspace(workDir, task, { vaultRoot, initialHead, timeoutMs = DEFAULT_GRADE_TIMEOUT_MS }) {
|
|
133
|
+
const taskVaultDir = join(vaultRoot, task.id);
|
|
134
|
+
const [testPatch, goldPatch] = await Promise.all([
|
|
135
|
+
readFile(join(taskVaultDir, 'test.patch')),
|
|
136
|
+
readFile(join(taskVaultDir, 'gold.patch')).catch(() => Buffer.alloc(0)),
|
|
137
|
+
]);
|
|
138
|
+
|
|
139
|
+
// Remove transient brief file if the `brief` arm or agent left one untracked, so it
|
|
140
|
+
// is not counted as an implementation edit outside the task's blast radius.
|
|
141
|
+
await runCmd('git', ['add', '-N', '.'], { cwd: workDir });
|
|
142
|
+
const [diffNamesRes, diffPatchRes] = await Promise.all([
|
|
143
|
+
runCmd('git', ['diff', '--name-only', initialHead], { cwd: workDir }),
|
|
144
|
+
runCmd('git', ['diff', '--binary', initialHead], { cwd: workDir }),
|
|
145
|
+
]);
|
|
146
|
+
|
|
147
|
+
const changedFiles = diffNamesRes.stdout
|
|
148
|
+
.toString('utf8')
|
|
149
|
+
.split('\n')
|
|
150
|
+
.map((s) => s.trim())
|
|
151
|
+
.filter((f) => Boolean(f) && !f.startsWith('.simt-dev/'));
|
|
152
|
+
|
|
153
|
+
const goldFiles = new Set(extractPatchFiles(goldPatch));
|
|
154
|
+
const outsideBlastRadius =
|
|
155
|
+
goldFiles.size > 0
|
|
156
|
+
? changedFiles.filter((file) => !goldFiles.has(file)).length
|
|
157
|
+
: 0;
|
|
158
|
+
const diff = {
|
|
159
|
+
files: changedFiles.length,
|
|
160
|
+
outsideBlastRadius,
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const testPatchFiles = extractPatchFiles(testPatch);
|
|
164
|
+
await resetFilesToHead(workDir, initialHead, testPatchFiles);
|
|
165
|
+
|
|
166
|
+
const applyTest = await runCmd('git', ['apply', '-'], {
|
|
167
|
+
cwd: workDir,
|
|
168
|
+
input: testPatch,
|
|
169
|
+
timeoutMs,
|
|
170
|
+
});
|
|
171
|
+
if (applyTest.code !== 0) {
|
|
172
|
+
return {
|
|
173
|
+
resolved: false,
|
|
174
|
+
gatesPassed: false,
|
|
175
|
+
diff,
|
|
176
|
+
agentPatch: diffPatchRes.stdout,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const runner = await detectTestRunner(workDir, testPatchFiles);
|
|
181
|
+
if (!runner) {
|
|
182
|
+
return {
|
|
183
|
+
resolved: false,
|
|
184
|
+
gatesPassed: false,
|
|
185
|
+
diff,
|
|
186
|
+
agentPatch: diffPatchRes.stdout,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const execSpec = runner.buildExec(workDir);
|
|
191
|
+
const runRes = await runCmd(execSpec.cmd, execSpec.args, { cwd: workDir, timeoutMs });
|
|
192
|
+
const reportText = execSpec.reportPath
|
|
193
|
+
? await readFile(execSpec.reportPath, 'utf8').catch(() => '')
|
|
194
|
+
: '';
|
|
195
|
+
const parsed = runner.parse(runRes.stdout.toString('utf8'), reportText);
|
|
196
|
+
const testRun = {
|
|
197
|
+
code: runRes.code,
|
|
198
|
+
timedOut: runRes.timedOut,
|
|
199
|
+
passed: parsed.passed,
|
|
200
|
+
failed: parsed.failed,
|
|
201
|
+
};
|
|
202
|
+
const passedSet = new Set(testRun.passed);
|
|
203
|
+
const f2pResolved =
|
|
204
|
+
Array.isArray(task.f2p) &&
|
|
205
|
+
task.f2p.length > 0 &&
|
|
206
|
+
task.f2p.every((id) => passedSet.has(id));
|
|
207
|
+
const p2pIntact =
|
|
208
|
+
Array.isArray(task.p2p) &&
|
|
209
|
+
task.p2p.every((id) => passedSet.has(id));
|
|
210
|
+
|
|
211
|
+
const resolved =
|
|
212
|
+
!testRun.timedOut &&
|
|
213
|
+
testRun.code === 0 &&
|
|
214
|
+
testRun.failed.length === 0 &&
|
|
215
|
+
f2pResolved &&
|
|
216
|
+
p2pIntact;
|
|
217
|
+
|
|
218
|
+
const gatesPassed = await evaluateDeclaredGates(
|
|
219
|
+
workDir,
|
|
220
|
+
!testRun.timedOut && testRun.code === 0 && testRun.failed.length === 0,
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
resolved,
|
|
225
|
+
gatesPassed,
|
|
226
|
+
diff,
|
|
227
|
+
agentPatch: diffPatchRes.stdout,
|
|
228
|
+
};
|
|
229
|
+
}
|