@orangepro/orangepro-mcp 0.2.43 → 0.2.45
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/dist/local/analyze/analyzer.js +331 -17
- package/dist/local/analyze/treeSitter/engine.js +1157 -13
- package/dist/local/autoProve.js +377 -97
- package/dist/local/operations.js +65 -16
- package/dist/local/proofDoctor.js +27 -5
- package/dist/local/provenance.js +2 -2
- package/dist/local/score/risk.js +284 -104
- package/dist/local/score/riskConfig.js +17 -1
- package/dist/local/viz/behaviorReportData.js +54 -18
- package/dist/local/viz/behaviorReportHtml.js +12 -1
- package/package.json +1 -1
- package/scripts/spikes/python-dynamic-proof-spike.mjs +399 -103
- package/scripts/spikes/python-mutate.py +72 -24
package/dist/local/operations.js
CHANGED
|
@@ -152,6 +152,19 @@ function defaultDynamicProofRunner(args, opts = {}) {
|
|
|
152
152
|
exitCode: child.status ?? 1
|
|
153
153
|
};
|
|
154
154
|
}
|
|
155
|
+
/** Metadata only. A runner process exiting zero is not a mutation proof. */
|
|
156
|
+
function runnerProcessDiagnostics(run, args, cwd, scriptPath, startedAt, oracle) {
|
|
157
|
+
const tail = (text) => redactSecrets(text).split(/\r?\n/).filter(Boolean).slice(-20).map((line) => line.slice(0, 500));
|
|
158
|
+
return {
|
|
159
|
+
command: redactSecrets([process.execPath, scriptPath, ...args].join(" ")).slice(0, 4096),
|
|
160
|
+
cwd: redactSecrets(cwd),
|
|
161
|
+
exit_code: Number.isInteger(run.exitCode) ? run.exitCode : null,
|
|
162
|
+
duration_ms: Math.max(0, Date.now() - startedAt),
|
|
163
|
+
...((oracle.baseline?.timedOut || oracle.mutant?.timedOut) ? { failure_class: "timeout" } : {}),
|
|
164
|
+
stdout_tail: run.exitCode === 0 ? [] : tail(run.stdout),
|
|
165
|
+
stderr_tail: tail(run.stderr)
|
|
166
|
+
};
|
|
167
|
+
}
|
|
155
168
|
function parseDynamicProofJson(stdout, stderr) {
|
|
156
169
|
try {
|
|
157
170
|
return JSON.parse(stdout);
|
|
@@ -1084,11 +1097,14 @@ export function opDynamicProof(root, opts, deps = defaultDeps()) {
|
|
|
1084
1097
|
// match the exact receiver, never a same-named decl on another type.
|
|
1085
1098
|
if (symbolTarget.memberQualifier)
|
|
1086
1099
|
args.push("--recv", symbolTarget.memberQualifier);
|
|
1100
|
+
const scriptPath = dynamicProofSpikePathFor("go");
|
|
1101
|
+
const startedAt = Date.now();
|
|
1087
1102
|
const run = (deps.dynamicProofRunner ?? defaultDynamicProofRunner)(args, {
|
|
1088
1103
|
cwd: goRoot,
|
|
1089
|
-
scriptPath
|
|
1104
|
+
scriptPath
|
|
1090
1105
|
});
|
|
1091
1106
|
oracle = mapGoOracle(parseDynamicProofJson(run.stdout, run.stderr));
|
|
1107
|
+
oracle.diagnostics = runnerProcessDiagnostics(run, args, goRoot, scriptPath, startedAt, oracle);
|
|
1092
1108
|
testRel = opts.test_run;
|
|
1093
1109
|
replacementMode = GO_SENTINEL_LABEL;
|
|
1094
1110
|
runner = "go";
|
|
@@ -1127,11 +1143,14 @@ export function opDynamicProof(root, opts, deps = defaultDeps()) {
|
|
|
1127
1143
|
];
|
|
1128
1144
|
if (opts.timeout_ms !== undefined)
|
|
1129
1145
|
args.push("--timeout-ms", String(opts.timeout_ms));
|
|
1146
|
+
const scriptPath = dynamicProofSpikePathFor("java");
|
|
1147
|
+
const startedAt = Date.now();
|
|
1130
1148
|
const run = (deps.dynamicProofRunner ?? defaultDynamicProofRunner)(args, {
|
|
1131
1149
|
cwd: javaRoot,
|
|
1132
|
-
scriptPath
|
|
1150
|
+
scriptPath
|
|
1133
1151
|
});
|
|
1134
1152
|
oracle = mapJavaOracle(parseDynamicProofJson(run.stdout, run.stderr));
|
|
1153
|
+
oracle.diagnostics = runnerProcessDiagnostics(run, args, javaRoot, scriptPath, startedAt, oracle);
|
|
1135
1154
|
testRel = opts.test_run;
|
|
1136
1155
|
replacementMode = JAVA_SENTINEL_LABEL;
|
|
1137
1156
|
runner = "junit";
|
|
@@ -1144,6 +1163,10 @@ export function opDynamicProof(root, opts, deps = defaultDeps()) {
|
|
|
1144
1163
|
testRel = toWorkspaceRel(sourceRoot, resolveContained(sourceRoot, opts.test_path));
|
|
1145
1164
|
replacementMode = opts.replacement_mode ?? "return-json";
|
|
1146
1165
|
runner = opts.runner ?? "auto";
|
|
1166
|
+
const configuredPythonRunner = loadRiskConfig(sourceRoot).config.proof.python_runner;
|
|
1167
|
+
const pythonRunner = opts.runner !== undefined
|
|
1168
|
+
? (opts.runner === "pytest" ? "python -m pytest" : "auto")
|
|
1169
|
+
: (opts.python_runner?.trim() || configuredPythonRunner);
|
|
1147
1170
|
if (replacementMode !== "return-json") {
|
|
1148
1171
|
throw new Error("prove --replacement-mode promise-json is not supported for Python targets.");
|
|
1149
1172
|
}
|
|
@@ -1153,26 +1176,28 @@ export function opDynamicProof(root, opts, deps = defaultDeps()) {
|
|
|
1153
1176
|
if ((opts.test_env?.length ?? 0) > 0) {
|
|
1154
1177
|
throw new Error("prove --test-env is not supported for Python targets yet.");
|
|
1155
1178
|
}
|
|
1156
|
-
//
|
|
1157
|
-
//
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
reportProgress(`proof scope: Python project root ${pyRoot} (narrowed from the analyzed path)`);
|
|
1162
|
-
}
|
|
1179
|
+
// R7.1: pass the bounded analyzed root to the Python spike. The spike derives
|
|
1180
|
+
// the nearest project from the TEST (not the target), then refuses a target
|
|
1181
|
+
// outside that explicit project boundary. Narrowing here from the target would
|
|
1182
|
+
// silently erase that boundary evidence in monorepos.
|
|
1183
|
+
const pyRoot = resolve(sourceRoot);
|
|
1163
1184
|
const args = [
|
|
1164
1185
|
"--root",
|
|
1165
1186
|
pyRoot,
|
|
1166
1187
|
"--test",
|
|
1167
|
-
|
|
1188
|
+
testRel,
|
|
1168
1189
|
"--target",
|
|
1169
|
-
|
|
1190
|
+
targetRel,
|
|
1170
1191
|
"--func",
|
|
1171
1192
|
method,
|
|
1172
1193
|
"--mode",
|
|
1173
1194
|
"sentinel",
|
|
1195
|
+
"--python-runner",
|
|
1196
|
+
pythonRunner,
|
|
1174
1197
|
"--json"
|
|
1175
1198
|
];
|
|
1199
|
+
if (opts.skip_preflight)
|
|
1200
|
+
args.push("--skip-preflight");
|
|
1176
1201
|
if (opts.timeout_ms !== undefined)
|
|
1177
1202
|
args.push("--timeout-ms", String(opts.timeout_ms));
|
|
1178
1203
|
const run = (deps.dynamicProofRunner ?? defaultDynamicProofRunner)(args, {
|
|
@@ -1180,6 +1205,9 @@ export function opDynamicProof(root, opts, deps = defaultDeps()) {
|
|
|
1180
1205
|
scriptPath: dynamicProofSpikePathFor("python")
|
|
1181
1206
|
});
|
|
1182
1207
|
oracle = parseDynamicProofJson(run.stdout, run.stderr);
|
|
1208
|
+
if (run.exitCode !== 0 && (oracle.status === "proven" || oracle.proven === true)) {
|
|
1209
|
+
throw new Error(`Python proof runner exited ${run.exitCode}; no dynamic proof can be credited. ${redactSecrets(run.stderr).slice(0, 300)}`);
|
|
1210
|
+
}
|
|
1183
1211
|
}
|
|
1184
1212
|
else {
|
|
1185
1213
|
if (!opts.test_path)
|
|
@@ -1225,13 +1253,17 @@ export function opDynamicProof(root, opts, deps = defaultDeps()) {
|
|
|
1225
1253
|
args.push("--jest-config", toWorkspaceRel(sourceRoot, resolveContained(sourceRoot, opts.jest_config)));
|
|
1226
1254
|
for (const entry of opts.test_env ?? [])
|
|
1227
1255
|
args.push("--test-env", entry);
|
|
1256
|
+
const scriptPath = dynamicProofSpikePath();
|
|
1257
|
+
const startedAt = Date.now();
|
|
1228
1258
|
const run = (deps.dynamicProofRunner ?? defaultDynamicProofRunner)(args, { cwd: sourceRoot });
|
|
1229
1259
|
oracle = parseDynamicProofJson(run.stdout, run.stderr);
|
|
1260
|
+
oracle.diagnostics = runnerProcessDiagnostics(run, args, sourceRoot, scriptPath, startedAt, oracle);
|
|
1230
1261
|
}
|
|
1231
1262
|
const closed = dynamicProofSucceeded(oracle);
|
|
1232
1263
|
const baselineGreen = oracle.baseline?.exitCode === 0 && oracle.baseline?.timedOut !== true;
|
|
1233
1264
|
const mutantFailedAssertion = oracle.mutant?.assertionFailure === true && oracle.mutant?.timedOut !== true;
|
|
1234
1265
|
const artifactIdentity = artifactIdentityFor(graph, sourceRoot);
|
|
1266
|
+
const proofDiagnostics = oracle.diagnostics;
|
|
1235
1267
|
const dynamicProof = {
|
|
1236
1268
|
proof_kind: "dynamic_targeted",
|
|
1237
1269
|
baseline_green: baselineGreen,
|
|
@@ -1239,12 +1271,14 @@ export function opDynamicProof(root, opts, deps = defaultDeps()) {
|
|
|
1239
1271
|
// Derived from the targeted mutation kill: if the credited subject is mocked/replaced,
|
|
1240
1272
|
// mutating its real body cannot cause the same test to fail at an assertion.
|
|
1241
1273
|
target_not_mocked: closed,
|
|
1242
|
-
sentinel: oracle.replacementMode ?? replacementMode,
|
|
1274
|
+
sentinel: oracle.mutation?.sentinel ?? oracle.replacementMode ?? replacementMode,
|
|
1275
|
+
...(oracle.mutation?.source ? { sentinel_source: oracle.mutation.source } : {}),
|
|
1243
1276
|
runner: oracle.runner ?? runner,
|
|
1244
1277
|
test_path: oracle.test ?? testRel,
|
|
1245
1278
|
mutant_status: oracle.status,
|
|
1246
1279
|
oracle_version: PROOF_ORACLE_VERSION,
|
|
1247
|
-
run_fingerprint: artifactIdentity.run_fingerprint
|
|
1280
|
+
run_fingerprint: artifactIdentity.run_fingerprint,
|
|
1281
|
+
...(proofDiagnostics ? proofDiagnostics : {})
|
|
1248
1282
|
};
|
|
1249
1283
|
const result = appendLedgerRecord(root, {
|
|
1250
1284
|
run_id: opts.run_id,
|
|
@@ -1274,6 +1308,9 @@ export function opDynamicProof(root, opts, deps = defaultDeps()) {
|
|
|
1274
1308
|
test: oracle.test,
|
|
1275
1309
|
target: oracle.target,
|
|
1276
1310
|
method: oracle.method,
|
|
1311
|
+
category: oracle.category,
|
|
1312
|
+
diagnostics: oracle.diagnostics,
|
|
1313
|
+
mutation: oracle.mutation,
|
|
1277
1314
|
baseline: oracle.baseline
|
|
1278
1315
|
? {
|
|
1279
1316
|
exitCode: oracle.baseline.exitCode,
|
|
@@ -1389,6 +1426,8 @@ export function opProveLoop(root, opts, deps = defaultDeps()) {
|
|
|
1389
1426
|
replacement: opts.replacement,
|
|
1390
1427
|
replacement_mode: opts.replacement_mode,
|
|
1391
1428
|
runner: opts.runner,
|
|
1429
|
+
python_runner: opts.python_runner,
|
|
1430
|
+
skip_preflight: opts.skip_preflight,
|
|
1392
1431
|
timeout_ms: opts.timeout_ms,
|
|
1393
1432
|
link_node_modules: opts.link_node_modules,
|
|
1394
1433
|
vitest_config: opts.vitest_config,
|
|
@@ -1583,7 +1622,8 @@ export async function opStart(root, opts = {}, deps = defaultDeps()) {
|
|
|
1583
1622
|
reportProgress("start: deterministic graph is ready", { current: 4, total: 8 });
|
|
1584
1623
|
// A per-repo/user risk config that fails to parse must announce itself here, not
|
|
1585
1624
|
// silently revert to defaults (round-three review finding #1).
|
|
1586
|
-
|
|
1625
|
+
const loadedRiskConfig = loadRiskConfig(scanRoot);
|
|
1626
|
+
for (const w of loadedRiskConfig.warnings)
|
|
1587
1627
|
warnings.push(w);
|
|
1588
1628
|
const staticSnapshot = writeStartStaticSnapshot(root, opts.baseRef, warnings);
|
|
1589
1629
|
const aiProviderConfigured = deps.aiProvider !== undefined || resolveProviderConfig(providerEnv, providerOpts) !== null;
|
|
@@ -1652,6 +1692,8 @@ export async function opStart(root, opts = {}, deps = defaultDeps()) {
|
|
|
1652
1692
|
try {
|
|
1653
1693
|
autoProveResult = await autoProve(root, {
|
|
1654
1694
|
autoLimit: opts.proofLimit ?? opts.autoLimit,
|
|
1695
|
+
proof_existing_limit: opts.proofLimit ?? opts.autoLimit ?? loadedRiskConfig.config.proof.attempt_limit,
|
|
1696
|
+
proof_gen_limit: loadedRiskConfig.config.proof.baseline_green_target,
|
|
1655
1697
|
noAuto: opts.noAuto,
|
|
1656
1698
|
provider: providerOpts.provider,
|
|
1657
1699
|
model: providerOpts.model,
|
|
@@ -1902,7 +1944,9 @@ export async function opStart(root, opts = {}, deps = defaultDeps()) {
|
|
|
1902
1944
|
const dynForReport = {
|
|
1903
1945
|
attempted: autoProveResult.attempted,
|
|
1904
1946
|
proven: autoProveResult.proven,
|
|
1905
|
-
needsSetup: autoProveResult.needs_setup
|
|
1947
|
+
needsSetup: autoProveResult.needs_setup
|
|
1948
|
+
.filter((a) => !a.deduped)
|
|
1949
|
+
.map((a) => ({ category: a.category, reason: a.reason }))
|
|
1906
1950
|
};
|
|
1907
1951
|
coverageHtml = opBehaviorCoverageHtml(root, `${WORKSPACE_DIR}/behavior-coverage.html`, dynForReport).behavior_coverage_path;
|
|
1908
1952
|
}
|
|
@@ -1956,6 +2000,11 @@ export async function opStart(root, opts = {}, deps = defaultDeps()) {
|
|
|
1956
2000
|
else if (autoProveResult.ran) {
|
|
1957
2001
|
nextActions.push(`Dynamic proof: attempted the top ${autoProveResult.attempted} target(s); ${autoProveResult.proven} dynamically proven. Static breadth (behaviors, flows, Associated signals) is mapped regardless.`);
|
|
1958
2002
|
if (autoProveResult.proven === 0) {
|
|
2003
|
+
const allEnvironmentUnavailable = autoProveResult.attempts.length > 0
|
|
2004
|
+
&& autoProveResult.attempts.every((attempt) => attempt.category === "environment_unavailable" || attempt.category === "collection_error");
|
|
2005
|
+
if (allEnvironmentUnavailable) {
|
|
2006
|
+
nextActions.push("Proof not run: test environment could not be started (see ledger)");
|
|
2007
|
+
}
|
|
1959
2008
|
const dom = dominantBlockReason(autoProveResult.needs_setup);
|
|
1960
2009
|
if (dom)
|
|
1961
2010
|
nextActions.push(`Dynamic proof closed 0: blocked because ${dom.label} (${dom.count}/${dom.total}). This is a sandbox setup gap, not a static-test failure; Static Associated signals are still shown.`);
|
|
@@ -2328,7 +2377,7 @@ function sidecarDynamicProof(root, graph) {
|
|
|
2328
2377
|
attempted: attempts.attempted,
|
|
2329
2378
|
proven: attempts.proven,
|
|
2330
2379
|
needsSetup: attempts.attempts
|
|
2331
|
-
.filter((a) => a.classification === "needs_setup")
|
|
2380
|
+
.filter((a) => a.classification === "needs_setup" && !a.deduped)
|
|
2332
2381
|
.map((a) => ({ category: a.category, reason: a.reason }))
|
|
2333
2382
|
};
|
|
2334
2383
|
}
|
|
@@ -23,7 +23,7 @@ import { workspacePaths } from "./workspace.js";
|
|
|
23
23
|
import { redactSecrets } from "./util/redact.js";
|
|
24
24
|
import { targetLanguage } from "./ledger.js";
|
|
25
25
|
import { readEnginesNode, satisfiesNodeRange } from "./proofRunnability.js";
|
|
26
|
-
export const PROOF_ATTEMPTS_SCHEMA_VERSION = "orangepro.proof_attempts.
|
|
26
|
+
export const PROOF_ATTEMPTS_SCHEMA_VERSION = "orangepro.proof_attempts.v2";
|
|
27
27
|
export const PROOF_ATTEMPTS_FILE = "proof-attempts.json";
|
|
28
28
|
export const PROOF_DOCTOR_SCHEMA_VERSION = "orangepro.proof_doctor.v1";
|
|
29
29
|
/** Languages with a shipped dynamic-proof profile. Everything else is honest "not yet". */
|
|
@@ -107,7 +107,7 @@ export function distillProofAttempts(auto, meta) {
|
|
|
107
107
|
artifact_identity: meta.graph.artifact_identity,
|
|
108
108
|
attempted: auto.attempted,
|
|
109
109
|
proven: auto.proven,
|
|
110
|
-
attempts: auto.attempts.map((a) => ({
|
|
110
|
+
attempts: auto.attempts.filter((a) => !a.deduped).map((a) => ({
|
|
111
111
|
target_symbol: a.target_symbol,
|
|
112
112
|
test_path: a.test_path || undefined,
|
|
113
113
|
classification: a.classification,
|
|
@@ -115,12 +115,34 @@ export function distillProofAttempts(auto, meta) {
|
|
|
115
115
|
category: a.category,
|
|
116
116
|
reason: a.reason ? redactSecrets(a.reason) : undefined,
|
|
117
117
|
deduped: a.deduped,
|
|
118
|
-
language: targetLanguage(a.target_symbol)
|
|
118
|
+
language: targetLanguage(a.target_symbol),
|
|
119
|
+
project_root: a.project_root,
|
|
120
|
+
blocked_by: a.blocked_by,
|
|
121
|
+
command: a.command,
|
|
122
|
+
cwd: a.cwd,
|
|
123
|
+
exit_code: a.exit_code,
|
|
124
|
+
duration_ms: a.duration_ms,
|
|
125
|
+
failure_class: a.failure_class,
|
|
126
|
+
stdout_tail: a.stdout_tail?.slice(-40).map((line) => redactSecrets(line)),
|
|
127
|
+
stderr_tail: a.stderr_tail?.slice(-40).map((line) => redactSecrets(line)),
|
|
128
|
+
runner_fallback: a.runner_fallback?.map((line) => redactSecrets(line))
|
|
119
129
|
})),
|
|
120
|
-
skipped: auto.skipped.map((
|
|
130
|
+
skipped: [...auto.skipped, ...auto.attempts.filter((a) => a.deduped).map((a) => ({
|
|
131
|
+
target_symbol: a.target_symbol,
|
|
132
|
+
title: a.target_symbol,
|
|
133
|
+
reason: a.reason ?? "Skipped after a classified project-wide proof failure.",
|
|
134
|
+
language: targetLanguage(a.target_symbol),
|
|
135
|
+
project_root: a.project_root,
|
|
136
|
+
blocked_by: a.blocked_by,
|
|
137
|
+
category: a.category
|
|
138
|
+
}))].map((s) => ({
|
|
121
139
|
target_symbol: s.target_symbol,
|
|
122
140
|
title: s.title,
|
|
123
|
-
reason: redactSecrets(s.reason)
|
|
141
|
+
reason: redactSecrets(s.reason),
|
|
142
|
+
category: s.category,
|
|
143
|
+
language: s.language,
|
|
144
|
+
project_root: s.project_root,
|
|
145
|
+
blocked_by: s.blocked_by
|
|
124
146
|
}))
|
|
125
147
|
};
|
|
126
148
|
}
|
package/dist/local/provenance.js
CHANGED
|
@@ -3,8 +3,8 @@ import { ORS_VERSION } from "./score/risk.js";
|
|
|
3
3
|
import { ORANGEPRO_VERSION } from "./version.js";
|
|
4
4
|
import { hashString } from "./util/hash.js";
|
|
5
5
|
export const ARTIFACT_IDENTITY_VERSION = "orangepro.artifact_identity.v1";
|
|
6
|
-
export const ANALYZER_VERSION = "orangepro.analyzer.
|
|
7
|
-
export const PROOF_ORACLE_VERSION = "orangepro.targeted_mutation_oracle.
|
|
6
|
+
export const ANALYZER_VERSION = "orangepro.analyzer.v5";
|
|
7
|
+
export const PROOF_ORACLE_VERSION = "orangepro.targeted_mutation_oracle.v3";
|
|
8
8
|
function stable(value) {
|
|
9
9
|
if (value === null || typeof value !== "object")
|
|
10
10
|
return JSON.stringify(value);
|