@orangepro/orangepro-mcp 0.2.43 → 0.2.44
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.
|
@@ -1935,10 +1935,36 @@ export function analyzeRepo(root, opts = {}) {
|
|
|
1935
1935
|
const targetRel = resolvePythonImport(testRel, binding.module) ?? undefined;
|
|
1936
1936
|
return { targetRel, imported: binding.imported, kind: binding.kind };
|
|
1937
1937
|
};
|
|
1938
|
-
const
|
|
1938
|
+
const resolvePythonClassTarget = (testRel, structure, classLocal) => {
|
|
1939
|
+
const binding = pythonProofImportBinding(testRel, structure, classLocal);
|
|
1940
|
+
if (!binding)
|
|
1941
|
+
return null;
|
|
1942
|
+
if (binding.kind !== "named" || !binding.targetRel || !binding.imported || isNonProductFile(binding.targetRel))
|
|
1943
|
+
return null;
|
|
1944
|
+
if (symbolKind(binding.targetRel, binding.imported) === "class") {
|
|
1945
|
+
return { targetRel: binding.targetRel, className: binding.imported };
|
|
1946
|
+
}
|
|
1947
|
+
// One explicit, unique package re-export hop (`pkg.__init__: from .impl import Cls`).
|
|
1948
|
+
// Ambiguity, wildcard exports, cycles, or a second hop fail closed.
|
|
1949
|
+
const exportStructure = nonTsStructureByFile.get(binding.targetRel)?.structure;
|
|
1950
|
+
const exports = (exportStructure?.imports ?? []).filter((candidate) => candidate.kind === "named" && candidate.local === binding.imported && candidate.imported === binding.imported);
|
|
1951
|
+
if (exports.length !== 1)
|
|
1952
|
+
return null;
|
|
1953
|
+
const targetRel = resolvePythonImport(binding.targetRel, exports[0].module);
|
|
1954
|
+
return targetRel && targetRel !== binding.targetRel && !isNonProductFile(targetRel) && symbolKind(targetRel, binding.imported) === "class"
|
|
1955
|
+
? { targetRel, className: binding.imported }
|
|
1956
|
+
: null;
|
|
1957
|
+
};
|
|
1958
|
+
const resolvePythonProofTarget = (testRel, structure, qualifier, callee, shadowed, instanceClass) => {
|
|
1939
1959
|
const conv = conventionSibling(testRel, "python", codeFileSet);
|
|
1940
1960
|
const conventionTargetRel = conv?.relPath && !isNonProductFile(conv.relPath) ? conv.relPath : null;
|
|
1941
1961
|
const hasWildcardImport = structure.imports.some((i) => i.imported === "*");
|
|
1962
|
+
if (instanceClass) {
|
|
1963
|
+
if (!qualifier || shadowed.has(instanceClass) || hasWildcardImport)
|
|
1964
|
+
return null;
|
|
1965
|
+
const resolved = resolvePythonClassTarget(testRel, structure, instanceClass);
|
|
1966
|
+
return resolved ? pythonQualifiedMemberId(resolved.targetRel, resolved.className, callee) : null;
|
|
1967
|
+
}
|
|
1942
1968
|
if (!qualifier) {
|
|
1943
1969
|
if (shadowed.has(callee))
|
|
1944
1970
|
return null;
|
|
@@ -2377,7 +2403,7 @@ export function analyzeRepo(root, opts = {}) {
|
|
|
2377
2403
|
const testExternalId = `test:${testRel}`;
|
|
2378
2404
|
for (const proof of structure.pythonProofCalls ?? []) {
|
|
2379
2405
|
pythonProofAttempted++;
|
|
2380
|
-
const symId = resolvePythonProofTarget(testRel, structure, proof.qualifier, proof.callee, new Set(proof.shadowed));
|
|
2406
|
+
const symId = resolvePythonProofTarget(testRel, structure, proof.qualifier, proof.callee, new Set(proof.shadowed), proof.instanceClass);
|
|
2381
2407
|
if (!symId)
|
|
2382
2408
|
continue;
|
|
2383
2409
|
const edgeKey = `${testExternalId}|${symId}`;
|
|
@@ -1856,19 +1856,195 @@ function singlePythonComparisonCall(comparison) {
|
|
|
1856
1856
|
const calls = namedChildren(comparison).filter((child) => child.type === "call");
|
|
1857
1857
|
return calls.length === 1 ? calls[0] ?? null : null;
|
|
1858
1858
|
}
|
|
1859
|
+
function pythonDirectAssignment(stmt) {
|
|
1860
|
+
const assignment = stmt.type === "assignment" || stmt.type === "annotated_assignment"
|
|
1861
|
+
? stmt
|
|
1862
|
+
: namedChildren(stmt).find((child) => child.type === "assignment" || child.type === "annotated_assignment");
|
|
1863
|
+
if (!assignment)
|
|
1864
|
+
return null;
|
|
1865
|
+
const target = assignment.childForFieldName("left");
|
|
1866
|
+
const value = assignment.childForFieldName("right");
|
|
1867
|
+
return target && value ? { target, value } : null;
|
|
1868
|
+
}
|
|
1869
|
+
function pythonSingleCall(node) {
|
|
1870
|
+
const calls = [...(node.type === "call" ? [node] : []), ...node.descendantsOfType("call").filter((call) => Boolean(call))];
|
|
1871
|
+
const unique = [...new Map(calls.map((call) => [`${call.startIndex}:${call.endIndex}`, call])).values()];
|
|
1872
|
+
return unique.length === 1 ? unique[0] ?? null : null;
|
|
1873
|
+
}
|
|
1874
|
+
function pythonExactCall(node) {
|
|
1875
|
+
if (!node)
|
|
1876
|
+
return null;
|
|
1877
|
+
if (node.type === "call")
|
|
1878
|
+
return node;
|
|
1879
|
+
if (node.type === "await" || node.type === "parenthesized_expression") {
|
|
1880
|
+
const children = namedChildren(node);
|
|
1881
|
+
return children.length === 1 ? pythonExactCall(children[0] ?? null) : null;
|
|
1882
|
+
}
|
|
1883
|
+
return null;
|
|
1884
|
+
}
|
|
1885
|
+
function pythonDirectAssertedResult(assertion, results) {
|
|
1886
|
+
const subject = namedChildren(assertion)[0];
|
|
1887
|
+
if (subject?.type !== "comparison_operator")
|
|
1888
|
+
return null;
|
|
1889
|
+
const operands = namedChildren(subject);
|
|
1890
|
+
if (operands.length !== 2)
|
|
1891
|
+
return null;
|
|
1892
|
+
const candidates = operands.filter((operand) => operand.type === "identifier" && results.has(operand.text));
|
|
1893
|
+
if (candidates.length !== 1)
|
|
1894
|
+
return null;
|
|
1895
|
+
const name = candidates[0].text;
|
|
1896
|
+
const occurrences = subject.descendantsOfType("identifier")
|
|
1897
|
+
.filter((identifier) => Boolean(identifier))
|
|
1898
|
+
.filter((identifier) => identifier.text === name);
|
|
1899
|
+
return occurrences.length === 1 ? name : null;
|
|
1900
|
+
}
|
|
1901
|
+
function pythonLexicalNodes(root) {
|
|
1902
|
+
const out = [];
|
|
1903
|
+
const visit = (node) => {
|
|
1904
|
+
for (const child of namedChildren(node)) {
|
|
1905
|
+
if (child.type === "function_definition" || child.type === "class_definition" || child.type === "lambda")
|
|
1906
|
+
continue;
|
|
1907
|
+
out.push(child);
|
|
1908
|
+
visit(child);
|
|
1909
|
+
}
|
|
1910
|
+
};
|
|
1911
|
+
visit(root);
|
|
1912
|
+
return out;
|
|
1913
|
+
}
|
|
1914
|
+
function pythonBindingNames(node) {
|
|
1915
|
+
if (node.type === "assignment" || node.type === "annotated_assignment" || node.type === "augmented_assignment" || node.type === "named_expression") {
|
|
1916
|
+
const target = node.childForFieldName("left") ?? node.childForFieldName("name");
|
|
1917
|
+
if (target?.type === "attribute") {
|
|
1918
|
+
const dotted = pythonDottedName(target);
|
|
1919
|
+
return dotted ? [dotted.split(".")[0] ?? dotted] : [];
|
|
1920
|
+
}
|
|
1921
|
+
return target ? pythonBindingTargetNames(target) : [];
|
|
1922
|
+
}
|
|
1923
|
+
if (node.type === "for_statement") {
|
|
1924
|
+
const target = node.childForFieldName("left");
|
|
1925
|
+
return target ? pythonBindingTargetNames(target) : [];
|
|
1926
|
+
}
|
|
1927
|
+
if (node.type === "delete_statement") {
|
|
1928
|
+
return namedChildren(node).flatMap((target) => {
|
|
1929
|
+
if (target.type === "attribute") {
|
|
1930
|
+
const dotted = pythonDottedName(target);
|
|
1931
|
+
return dotted ? [dotted.split(".")[0] ?? dotted] : [];
|
|
1932
|
+
}
|
|
1933
|
+
return pythonBindingTargetNames(target);
|
|
1934
|
+
});
|
|
1935
|
+
}
|
|
1936
|
+
return [];
|
|
1937
|
+
}
|
|
1938
|
+
function pythonAssignedInstanceProofCalls(block) {
|
|
1939
|
+
const testFunction = block.parent;
|
|
1940
|
+
const decorated = testFunction?.parent?.type === "decorated_definition" ? testFunction.parent : testFunction;
|
|
1941
|
+
const parameterNames = testFunction?.childForFieldName("parameters")?.descendantsOfType("identifier").map((node) => node?.text).filter(Boolean) ?? [];
|
|
1942
|
+
if (parameterNames.some((name) => name !== "self" && name !== "cls"))
|
|
1943
|
+
return [];
|
|
1944
|
+
if (/\b(?:monkeypatch|mocker|patch|Mock|MagicMock|AsyncMock|create_autospec)\b/.test(decorated?.text ?? ""))
|
|
1945
|
+
return [];
|
|
1946
|
+
const lexicalNodes = pythonLexicalNodes(block);
|
|
1947
|
+
const assignments = lexicalNodes.filter((node) => node.type === "assignment" || node.type === "annotated_assignment");
|
|
1948
|
+
const assertions = lexicalNodes.filter((node) => node.type === "assert_statement");
|
|
1949
|
+
if (assertions.length !== 1)
|
|
1950
|
+
return [];
|
|
1951
|
+
const mutationCounts = new Map();
|
|
1952
|
+
for (const node of lexicalNodes) {
|
|
1953
|
+
for (const name of pythonBindingNames(node))
|
|
1954
|
+
mutationCounts.set(name, (mutationCounts.get(name) ?? 0) + 1);
|
|
1955
|
+
}
|
|
1956
|
+
const instances = new Map();
|
|
1957
|
+
const orderedAssignments = assignments.map(pythonDirectAssignment).filter((assignment) => Boolean(assignment));
|
|
1958
|
+
for (const { target, value } of orderedAssignments) {
|
|
1959
|
+
if (target.type !== "identifier" || mutationCounts.get(target.text) !== 1)
|
|
1960
|
+
continue;
|
|
1961
|
+
const constructor = pythonSingleCall(value);
|
|
1962
|
+
const parts = constructor ? callParts(constructor, "python") : null;
|
|
1963
|
+
if (!parts || parts.qualifier || !/^[A-Za-z_]\w*$/.test(parts.callee))
|
|
1964
|
+
continue;
|
|
1965
|
+
if ((mutationCounts.get(parts.callee) ?? 0) > 0)
|
|
1966
|
+
continue;
|
|
1967
|
+
instances.set(target.text, { className: parts.callee, assignedAt: target.startIndex });
|
|
1968
|
+
}
|
|
1969
|
+
for (const { target, value } of orderedAssignments) {
|
|
1970
|
+
if (target.type === "identifier" && value.type === "identifier" && instances.has(value.text))
|
|
1971
|
+
return [];
|
|
1972
|
+
}
|
|
1973
|
+
const trackedNames = new Set([
|
|
1974
|
+
...instances.keys(),
|
|
1975
|
+
...[...instances.values()].map((instance) => instance.className)
|
|
1976
|
+
]);
|
|
1977
|
+
for (const callNode of lexicalNodes.filter((node) => node.type === "call")) {
|
|
1978
|
+
const call = callParts(callNode, "python");
|
|
1979
|
+
const receiver = call?.qualifier?.split(".")[0] ?? "";
|
|
1980
|
+
if (instances.has(receiver))
|
|
1981
|
+
continue;
|
|
1982
|
+
const args = callNode.childForFieldName("arguments");
|
|
1983
|
+
const argumentNames = args
|
|
1984
|
+
? [
|
|
1985
|
+
...(args.type === "identifier" ? [args] : []),
|
|
1986
|
+
...args.descendantsOfType("identifier").filter((identifier) => Boolean(identifier))
|
|
1987
|
+
].map((identifier) => identifier.text)
|
|
1988
|
+
: [];
|
|
1989
|
+
if (argumentNames.some((name) => trackedNames.has(name)))
|
|
1990
|
+
return [];
|
|
1991
|
+
}
|
|
1992
|
+
const results = new Map();
|
|
1993
|
+
const instanceCalls = lexicalNodes.filter((node) => node.type === "call").filter((callNode) => {
|
|
1994
|
+
const call = callParts(callNode, "python");
|
|
1995
|
+
const receiver = call?.qualifier?.split(".")[0] ?? "";
|
|
1996
|
+
return instances.has(receiver);
|
|
1997
|
+
});
|
|
1998
|
+
if (instanceCalls.length !== 1)
|
|
1999
|
+
return [];
|
|
2000
|
+
for (const { target, value } of orderedAssignments) {
|
|
2001
|
+
if (target.type !== "identifier" || mutationCounts.get(target.text) !== 1)
|
|
2002
|
+
continue;
|
|
2003
|
+
const callNode = pythonExactCall(value);
|
|
2004
|
+
const call = callNode ? callParts(callNode, "python") : null;
|
|
2005
|
+
const receiver = call?.qualifier?.split(".")[0] ?? "";
|
|
2006
|
+
const instance = instances.get(receiver);
|
|
2007
|
+
if (call && instance && instance.assignedAt < value.startIndex) {
|
|
2008
|
+
results.set(target.text, { call, instanceClass: instance.className });
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
const out = [];
|
|
2012
|
+
for (const stmt of assertions) {
|
|
2013
|
+
const direct = directPythonAssertCall(stmt);
|
|
2014
|
+
const directReceiver = direct?.qualifier?.split(".")[0] ?? "";
|
|
2015
|
+
const directInstance = instances.get(directReceiver);
|
|
2016
|
+
if (direct && directInstance && directInstance.assignedAt < stmt.startIndex) {
|
|
2017
|
+
out.push({ assertion: stmt, call: direct, instanceClass: directInstance.className });
|
|
2018
|
+
}
|
|
2019
|
+
const subject = namedChildren(stmt)[0];
|
|
2020
|
+
const assertionCalls = subject
|
|
2021
|
+
? [...(subject.type === "call" ? [subject] : []), ...subject.descendantsOfType("call").filter((call) => Boolean(call))]
|
|
2022
|
+
: [];
|
|
2023
|
+
if (assertionCalls.length > 0)
|
|
2024
|
+
continue;
|
|
2025
|
+
const observed = pythonDirectAssertedResult(stmt, results);
|
|
2026
|
+
const result = observed ? results.get(observed) : undefined;
|
|
2027
|
+
if (result)
|
|
2028
|
+
out.push({ assertion: stmt, ...result });
|
|
2029
|
+
}
|
|
2030
|
+
return out;
|
|
2031
|
+
}
|
|
1859
2032
|
function extractPythonProofCalls(root) {
|
|
1860
2033
|
const out = [];
|
|
1861
2034
|
const seen = new Set();
|
|
1862
|
-
const add = (testName, shadowed, call) => {
|
|
2035
|
+
const add = (testName, shadowed, call, instanceClass) => {
|
|
1863
2036
|
if (!call)
|
|
1864
2037
|
return;
|
|
1865
|
-
const key = `${testName}|${call.qualifier ?? ""}|${call.callee}`;
|
|
2038
|
+
const key = `${testName}|${instanceClass ?? ""}|${call.qualifier ?? ""}|${call.callee}`;
|
|
1866
2039
|
if (seen.has(key))
|
|
1867
2040
|
return;
|
|
1868
2041
|
seen.add(key);
|
|
1869
|
-
out.push({ caller: testName, testName, ...call, shadowed: [...shadowed], assertion: "pytest_assert" });
|
|
2042
|
+
out.push({ caller: testName, testName, ...call, shadowed: [...shadowed], assertion: "pytest_assert", ...(instanceClass ? { instanceClass } : {}) });
|
|
1870
2043
|
};
|
|
1871
2044
|
const processBlock = (block, testName, shadowed) => {
|
|
2045
|
+
for (const associated of pythonAssignedInstanceProofCalls(block)) {
|
|
2046
|
+
add(testName, shadowed, associated.call, associated.instanceClass);
|
|
2047
|
+
}
|
|
1872
2048
|
for (const stmt of blockStatements(block)) {
|
|
1873
2049
|
if (stmt.type === "assert_statement")
|
|
1874
2050
|
add(testName, shadowed, directPythonAssertCall(stmt));
|
package/dist/local/autoProve.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* No key ⇒ writes NO files, mints NO proof, returns explicit guidance.
|
|
14
14
|
*/
|
|
15
15
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
16
|
-
import { basename, dirname, posix, resolve, sep } from "node:path";
|
|
16
|
+
import { basename, dirname, join, posix, relative, resolve, sep } from "node:path";
|
|
17
17
|
import { generateTests, readDeclaredDeps, unresolvedLocalImports } from "./generate/generator.js";
|
|
18
18
|
import { GENERATED_DIR, runHintsFor } from "./generate/runHints.js";
|
|
19
19
|
import { rankRiskGaps } from "./score/risk.js";
|
|
@@ -21,12 +21,12 @@ import { resolveProviderConfig } from "./localConfig.js";
|
|
|
21
21
|
import { buildProvider, DeterministicProvider } from "./generate/providers.js";
|
|
22
22
|
import { resolveContained } from "./reprove/paths.js";
|
|
23
23
|
import { buildRtm } from "./rtm.js";
|
|
24
|
-
import { loadLedger } from "./ledger.js";
|
|
24
|
+
import { loadLedger, targetLanguage } from "./ledger.js";
|
|
25
25
|
import { reportProgress } from "./util/progress.js";
|
|
26
26
|
import { loadGraph, workspacePaths } from "./workspace.js";
|
|
27
27
|
import { systemClock } from "./util/time.js";
|
|
28
28
|
import { redactSecrets } from "./util/redact.js";
|
|
29
|
-
import { classifyBaselineFailure, EXPERIMENTAL_SQLITE_TEST_ENV,
|
|
29
|
+
import { classifyBaselineFailure, EXPERIMENTAL_SQLITE_TEST_ENV, isNeedsSetupCategory, readEnginesNode, targetNeedsExperimentalSqlite } from "./proofRunnability.js";
|
|
30
30
|
/** Real dynamic proof is profile-gated; only wired runner targets are attemptable. */
|
|
31
31
|
function isTsJsFile(file) {
|
|
32
32
|
return /\.[cm]?[jt]sx?$/i.test(file);
|
|
@@ -372,7 +372,74 @@ const GEN_WINDOW = 5;
|
|
|
372
372
|
// file (every eligible symbol × every importing test) cannot starve the shared budget before
|
|
373
373
|
// a provable hard-edge symbol is tried. Small K; promote to a flag if a repo needs a wider sweep.
|
|
374
374
|
const EXISTING_LANE_MAX_WEAK_PER_SYMBOL = 3;
|
|
375
|
+
const RUNNER_ROOT_MARKERS = {
|
|
376
|
+
typescript: ["package.json"],
|
|
377
|
+
javascript: ["package.json"],
|
|
378
|
+
python: ["pyproject.toml", "setup.cfg", "setup.py", "pytest.ini", ".pytest.ini", "tox.ini"],
|
|
379
|
+
go: ["go.mod"],
|
|
380
|
+
java: ["pom.xml", "build.gradle", "build.gradle.kts"],
|
|
381
|
+
rs: ["Cargo.toml"]
|
|
382
|
+
};
|
|
383
|
+
/**
|
|
384
|
+
* The nearest language runner root for a target, bounded by the analyzed source root.
|
|
385
|
+
* A nested root is accepted only when the selected existing test is inside it too;
|
|
386
|
+
* otherwise proof execution falls back to the analyzed root, matching the oracle's
|
|
387
|
+
* containment rule rather than inventing a cross-project plan.
|
|
388
|
+
*/
|
|
389
|
+
export function proofRunnerRoot(sourceRoot, targetRel, testRel) {
|
|
390
|
+
const stop = resolve(sourceRoot);
|
|
391
|
+
const targetAbs = resolve(stop, targetRel);
|
|
392
|
+
if (targetAbs !== stop && !targetAbs.startsWith(stop + sep))
|
|
393
|
+
return ".";
|
|
394
|
+
const language = targetLanguage(`sym:${targetRel}#target`);
|
|
395
|
+
const markers = RUNNER_ROOT_MARKERS[language] ?? [];
|
|
396
|
+
let dir = dirname(targetAbs);
|
|
397
|
+
let found = stop;
|
|
398
|
+
for (;;) {
|
|
399
|
+
if (markers.some((marker) => existsSync(join(dir, marker)))) {
|
|
400
|
+
found = dir;
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
if (dir === stop)
|
|
404
|
+
break;
|
|
405
|
+
const parent = dirname(dir);
|
|
406
|
+
if (parent === dir || (parent !== stop && !parent.startsWith(stop + sep)))
|
|
407
|
+
break;
|
|
408
|
+
dir = parent;
|
|
409
|
+
}
|
|
410
|
+
if (testRel && found !== stop) {
|
|
411
|
+
const testFile = testRel.split("::", 1)[0] ?? testRel;
|
|
412
|
+
const testAbs = resolve(stop, testFile);
|
|
413
|
+
if ((testAbs !== stop && !testAbs.startsWith(stop + sep))
|
|
414
|
+
|| (testAbs !== found && !testAbs.startsWith(found + sep)))
|
|
415
|
+
found = stop;
|
|
416
|
+
}
|
|
417
|
+
const rel = relative(stop, found).split(sep).join("/");
|
|
418
|
+
return rel || ".";
|
|
419
|
+
}
|
|
420
|
+
/** Dominant-language order from all denominator-eligible code behaviors. */
|
|
421
|
+
export function proofLanguageOrder(graph) {
|
|
422
|
+
const counts = new Map();
|
|
423
|
+
const first = new Map();
|
|
424
|
+
for (const node of graph.nodes) {
|
|
425
|
+
if (node.kind !== "CodeSymbol" || node.denominator_eligible !== true)
|
|
426
|
+
continue;
|
|
427
|
+
const language = targetLanguage(node.external_id);
|
|
428
|
+
if (!first.has(language))
|
|
429
|
+
first.set(language, first.size);
|
|
430
|
+
counts.set(language, (counts.get(language) ?? 0) + 1);
|
|
431
|
+
}
|
|
432
|
+
return [...counts.keys()].sort((a, b) => (counts.get(b) - counts.get(a)) || (first.get(a) - first.get(b)));
|
|
433
|
+
}
|
|
375
434
|
export const NO_KEY_MESSAGE = "No provider key; auto-prove skipped — add OPENAI_API_KEY / ANTHROPIC_API_KEY, or use the OrangePro MCP in your coding agent.";
|
|
435
|
+
/** Only failures proven to apply across a runner project may quarantine its siblings. */
|
|
436
|
+
const PROJECT_WIDE_BLOCKERS = new Set([
|
|
437
|
+
"engine_mismatch",
|
|
438
|
+
"tsconfig_missing",
|
|
439
|
+
"runner_missing",
|
|
440
|
+
"module_root_missing",
|
|
441
|
+
"go_package_build_failure"
|
|
442
|
+
]);
|
|
376
443
|
export function isRoastSurvivor(attempt) {
|
|
377
444
|
return attempt.classification === "non_killing" && attempt.mutant_status === "associated_survived";
|
|
378
445
|
}
|
|
@@ -434,7 +501,13 @@ function fileReaderFor(root) {
|
|
|
434
501
|
function classifyProof(result, ctx) {
|
|
435
502
|
if ("status" in result && result.status === "unrunnable") {
|
|
436
503
|
// Setup did not run (env non-event) — nothing was minted, target needs setup.
|
|
437
|
-
|
|
504
|
+
const reason = result.reason;
|
|
505
|
+
const category = /runner binary not found|unsupported or unknown test runner/i.test(reason)
|
|
506
|
+
? "runner_missing"
|
|
507
|
+
: /no go\.mod found|no pom\.xml|no maven or gradle|build\.gradle|no python project root/i.test(reason)
|
|
508
|
+
? "module_root_missing"
|
|
509
|
+
: undefined;
|
|
510
|
+
return { classification: "needs_setup", reason, category };
|
|
438
511
|
}
|
|
439
512
|
const dyn = result;
|
|
440
513
|
const record = dyn.record;
|
|
@@ -442,8 +515,16 @@ function classifyProof(result, ctx) {
|
|
|
442
515
|
return { classification: "proven" };
|
|
443
516
|
const cert = record.dynamic_proof;
|
|
444
517
|
if (cert && cert.baseline_green === false) {
|
|
518
|
+
const failureSummary = dyn.oracle.baseline?.failureSummary;
|
|
519
|
+
if (ctx.targetFileRel.endsWith(".go") && /^#\s+\S+/m.test(failureSummary ?? "")) {
|
|
520
|
+
return {
|
|
521
|
+
classification: "needs_setup",
|
|
522
|
+
reason: "The Go package did not compile before the selected test could run.",
|
|
523
|
+
category: "go_package_build_failure"
|
|
524
|
+
};
|
|
525
|
+
}
|
|
445
526
|
const { category, reason } = classifyBaselineFailure({
|
|
446
|
-
failureSummary
|
|
527
|
+
failureSummary,
|
|
447
528
|
enginesNode: readEnginesNode(ctx.sourceRoot, ctx.targetFileRel),
|
|
448
529
|
runnerNode: ctx.runnerNode ?? process.version
|
|
449
530
|
});
|
|
@@ -457,27 +538,30 @@ function mutantStatusOf(result) {
|
|
|
457
538
|
return "unrunnable";
|
|
458
539
|
return result.record.dynamic_proof?.mutant_status;
|
|
459
540
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
* loading the TARGET FILE with a given runner, independent of which test runs it — so
|
|
463
|
-
* same-file siblings share it. Keyed on the TARGET FILE only (every caller pins runner to
|
|
464
|
-
* undefined): the sole deduped cause is engine_mismatch, a package-level fact both lanes
|
|
465
|
-
* classify against the SAME process.version, so the runner must not be in the key. The old
|
|
466
|
-
* {runner, target file} key split the cache across lanes (lane 1 wrote "auto <file>", the
|
|
467
|
-
* generation lane read "<runner> <file>" -> never matched), silently disabling cross-lane
|
|
468
|
-
* dedup. NEVER merges across different files or a different failure class.
|
|
469
|
-
*/
|
|
470
|
-
function dedupKey(runner, targetFileRel) {
|
|
471
|
-
return `${runner ?? "auto"}\u0000${targetFileRel}`;
|
|
541
|
+
function projectBlockKey(language, projectRoot) {
|
|
542
|
+
return `${language}\u0000${projectRoot}`;
|
|
472
543
|
}
|
|
473
|
-
|
|
474
|
-
|
|
544
|
+
function projectBlockKeys(language, projectRoot, targetFileRel) {
|
|
545
|
+
return [
|
|
546
|
+
projectBlockKey(language, projectRoot),
|
|
547
|
+
`${projectBlockKey(language, projectRoot)}\u0000package:${dirname(targetFileRel).split(sep).join("/")}`
|
|
548
|
+
];
|
|
549
|
+
}
|
|
550
|
+
function classifiedProjectBlockKey(category, language, projectRoot, targetFileRel) {
|
|
551
|
+
return category === "go_package_build_failure"
|
|
552
|
+
? projectBlockKeys(language, projectRoot, targetFileRel)[1]
|
|
553
|
+
: projectBlockKey(language, projectRoot);
|
|
554
|
+
}
|
|
555
|
+
/** A same-project candidate skipped WITHOUT re-running after a classified project-wide failure. */
|
|
556
|
+
function dedupedAttempt(targetSymbol, testPath, projectRoot, blocked) {
|
|
475
557
|
return {
|
|
476
558
|
target_symbol: targetSymbol,
|
|
477
559
|
test_path: testPath,
|
|
478
560
|
classification: "needs_setup",
|
|
479
|
-
reason: `${blocked.reason} (shared
|
|
561
|
+
reason: `${blocked.reason} (shared project/toolchain cause in ${blocked.scopeLabel}; not re-run).`,
|
|
480
562
|
category: blocked.category,
|
|
563
|
+
project_root: projectRoot,
|
|
564
|
+
blocked_by: blocked.blockedBy,
|
|
481
565
|
deduped: true
|
|
482
566
|
};
|
|
483
567
|
}
|
|
@@ -616,7 +700,7 @@ export function existingAssociatedTests(graph, nodeById) {
|
|
|
616
700
|
* the budget. Order within each tier follows the Map's insertion order (graph node/edge order),
|
|
617
701
|
* so the result is deterministic.
|
|
618
702
|
*/
|
|
619
|
-
export function orderExistingAttempts(testsBySymbol, maxWeakPerSymbol = EXISTING_LANE_MAX_WEAK_PER_SYMBOL) {
|
|
703
|
+
export function orderExistingAttempts(testsBySymbol, maxWeakPerSymbol = EXISTING_LANE_MAX_WEAK_PER_SYMBOL, schedule) {
|
|
620
704
|
const hard = [];
|
|
621
705
|
const weak = [];
|
|
622
706
|
for (const [symId, tests] of testsBySymbol) {
|
|
@@ -628,7 +712,60 @@ export function orderExistingAttempts(testsBySymbol, maxWeakPerSymbol = EXISTING
|
|
|
628
712
|
weak.push({ symId, testRel: t.test, hard: false });
|
|
629
713
|
}
|
|
630
714
|
}
|
|
631
|
-
|
|
715
|
+
if (!schedule)
|
|
716
|
+
return [...hard, ...weak];
|
|
717
|
+
const languageOrder = proofLanguageOrder(schedule.graph);
|
|
718
|
+
const languageRank = new Map(languageOrder.map((language, index) => [language, index]));
|
|
719
|
+
const decorate = (attempt, index) => {
|
|
720
|
+
const node = schedule.nodeById.get(attempt.symId);
|
|
721
|
+
const targetRel = node ? symbolFileOf(node) : attempt.symId.replace(/^sym:/, "").split("#")[0];
|
|
722
|
+
return {
|
|
723
|
+
...attempt,
|
|
724
|
+
language: targetLanguage(attempt.symId),
|
|
725
|
+
projectRoot: proofRunnerRoot(schedule.sourceRoot, targetRel, attempt.testRel),
|
|
726
|
+
index
|
|
727
|
+
};
|
|
728
|
+
};
|
|
729
|
+
const stableProjectOrder = (attempts) => {
|
|
730
|
+
const decorated = attempts.map(decorate);
|
|
731
|
+
const firstProject = new Map();
|
|
732
|
+
for (const item of decorated) {
|
|
733
|
+
const key = `${item.language}\u0000${item.projectRoot}`;
|
|
734
|
+
if (!firstProject.has(key))
|
|
735
|
+
firstProject.set(key, firstProject.size);
|
|
736
|
+
}
|
|
737
|
+
return decorated
|
|
738
|
+
.sort((a, b) => ((languageRank.get(a.language) ?? Number.MAX_SAFE_INTEGER) - (languageRank.get(b.language) ?? Number.MAX_SAFE_INTEGER))
|
|
739
|
+
|| ((firstProject.get(`${a.language}\u0000${a.projectRoot}`) ?? 0) - (firstProject.get(`${b.language}\u0000${b.projectRoot}`) ?? 0))
|
|
740
|
+
|| (a.index - b.index))
|
|
741
|
+
.map(({ index: _index, ...attempt }) => attempt);
|
|
742
|
+
};
|
|
743
|
+
return [...stableProjectOrder(hard), ...stableProjectOrder(weak)];
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Stable scheduling view over the existing ORS-ranked candidate list. This changes
|
|
747
|
+
* only which runner receives a scarce proof attempt first; it never changes scores,
|
|
748
|
+
* evidence tiers, or the persisted priority-gap order.
|
|
749
|
+
*/
|
|
750
|
+
export function orderRankedProofCandidates(candidates, graph, sourceRoot) {
|
|
751
|
+
const languageOrder = proofLanguageOrder(graph);
|
|
752
|
+
if (languageOrder.length <= 1)
|
|
753
|
+
return candidates;
|
|
754
|
+
const languageRank = new Map(languageOrder.map((language, index) => [language, index]));
|
|
755
|
+
const firstProject = new Map();
|
|
756
|
+
const decorated = candidates.map((candidate, index) => {
|
|
757
|
+
const language = targetLanguage(candidate.id);
|
|
758
|
+
const projectRoot = proofRunnerRoot(sourceRoot, candidate.file);
|
|
759
|
+
const projectKey = `${language}\u0000${projectRoot}`;
|
|
760
|
+
if (!firstProject.has(projectKey))
|
|
761
|
+
firstProject.set(projectKey, firstProject.size);
|
|
762
|
+
return { candidate, index, language, projectKey };
|
|
763
|
+
});
|
|
764
|
+
return decorated
|
|
765
|
+
.sort((a, b) => ((languageRank.get(a.language) ?? Number.MAX_SAFE_INTEGER) - (languageRank.get(b.language) ?? Number.MAX_SAFE_INTEGER))
|
|
766
|
+
|| ((firstProject.get(a.projectKey) ?? 0) - (firstProject.get(b.projectKey) ?? 0))
|
|
767
|
+
|| (a.index - b.index))
|
|
768
|
+
.map(({ candidate }) => candidate);
|
|
632
769
|
}
|
|
633
770
|
/**
|
|
634
771
|
* PR 1.5 lane — prove the repo's OWN existing tests, NO provider key. For each eligible
|
|
@@ -642,7 +779,7 @@ export function orderExistingAttempts(testsBySymbol, maxWeakPerSymbol = EXISTING
|
|
|
642
779
|
* — the generation lane gets whatever this lane leaves unspent, so TOTAL attempts
|
|
643
780
|
* (existing + generation) never exceed the budget.
|
|
644
781
|
*/
|
|
645
|
-
function proveExistingAssociatedTests(root, graph, sourceRoot, nodeById, opts, proveLoop, proveDeps, alreadyProven,
|
|
782
|
+
function proveExistingAssociatedTests(root, graph, sourceRoot, nodeById, opts, proveLoop, proveDeps, alreadyProven, projectBlocked, budget) {
|
|
646
783
|
const attempts = [];
|
|
647
784
|
const needsSetup = [];
|
|
648
785
|
const provenSymbols = new Set();
|
|
@@ -651,8 +788,12 @@ function proveExistingAssociatedTests(root, graph, sourceRoot, nodeById, opts, p
|
|
|
651
788
|
const changed = opts.changedFiles && opts.changedFiles.length > 0 ? new Set(opts.changedFiles) : null;
|
|
652
789
|
const reader = fileReaderFor(sourceRoot); // R-2: source scan for the node:sqlite env profile
|
|
653
790
|
// Fix 3: hard TESTED_BY/COVERS pairs first, weak MAY_* pairs after and capped per symbol.
|
|
654
|
-
const queue = orderExistingAttempts(existingAssociatedTests(graph, nodeById)
|
|
655
|
-
|
|
791
|
+
const queue = orderExistingAttempts(existingAssociatedTests(graph, nodeById), EXISTING_LANE_MAX_WEAK_PER_SYMBOL, {
|
|
792
|
+
graph,
|
|
793
|
+
nodeById,
|
|
794
|
+
sourceRoot
|
|
795
|
+
});
|
|
796
|
+
for (const { symId, testRel, hard, testName, language, projectRoot } of queue) {
|
|
656
797
|
if (attempted >= budget)
|
|
657
798
|
break;
|
|
658
799
|
const node = nodeById.get(symId);
|
|
@@ -671,10 +812,10 @@ function proveExistingAssociatedTests(root, graph, sourceRoot, nodeById, opts, p
|
|
|
671
812
|
if (provenSymbols.has(symId))
|
|
672
813
|
continue;
|
|
673
814
|
const targetFileRel = symbolFileOf(node);
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
//
|
|
677
|
-
//
|
|
815
|
+
const targetLanguageName = language ?? targetLanguage(symId);
|
|
816
|
+
const runnerRoot = projectRoot ?? proofRunnerRoot(sourceRoot, targetFileRel, testRel);
|
|
817
|
+
// A prior candidate in this exact runner project hit a classified project-wide
|
|
818
|
+
// toolchain/environment failure. Preserve the budget and explain the skip.
|
|
678
819
|
const isPython = isPythonFile(targetFileRel);
|
|
679
820
|
const candidateTestRels = isPython
|
|
680
821
|
? pytestNodeidsForTarget(sourceRoot, testRel, testName).filter((candidate) => isRunnableTestForTarget(node, candidate))
|
|
@@ -682,9 +823,10 @@ function proveExistingAssociatedTests(root, graph, sourceRoot, nodeById, opts, p
|
|
|
682
823
|
if (candidateTestRels.length === 0)
|
|
683
824
|
continue;
|
|
684
825
|
const proofTestRel = candidateTestRels[0];
|
|
685
|
-
const
|
|
826
|
+
const blockedKey = projectBlockKeys(targetLanguageName, runnerRoot, targetFileRel).find((key) => projectBlocked.has(key));
|
|
827
|
+
const blocked = blockedKey ? projectBlocked.get(blockedKey) : undefined;
|
|
686
828
|
if (blocked) {
|
|
687
|
-
const attempt = dedupedAttempt(symId, proofTestRel,
|
|
829
|
+
const attempt = dedupedAttempt(symId, proofTestRel, runnerRoot, blocked);
|
|
688
830
|
attempts.push(attempt);
|
|
689
831
|
needsSetup.push(attempt);
|
|
690
832
|
continue;
|
|
@@ -745,7 +887,8 @@ function proveExistingAssociatedTests(root, graph, sourceRoot, nodeById, opts, p
|
|
|
745
887
|
target_symbol: symId,
|
|
746
888
|
test_path: displayTest,
|
|
747
889
|
classification: "needs_setup",
|
|
748
|
-
reason: `Proof could not run: ${redactSecrets(errMsg(e))}
|
|
890
|
+
reason: `Proof could not run: ${redactSecrets(errMsg(e))}`,
|
|
891
|
+
project_root: runnerRoot
|
|
749
892
|
};
|
|
750
893
|
attempts.push(attempt);
|
|
751
894
|
needsSetup.push(attempt);
|
|
@@ -758,6 +901,7 @@ function proveExistingAssociatedTests(root, graph, sourceRoot, nodeById, opts, p
|
|
|
758
901
|
classification,
|
|
759
902
|
reason,
|
|
760
903
|
category,
|
|
904
|
+
project_root: runnerRoot,
|
|
761
905
|
mutant_status: mutantStatusOf(result)
|
|
762
906
|
};
|
|
763
907
|
attempts.push(attempt);
|
|
@@ -768,9 +912,18 @@ function proveExistingAssociatedTests(root, graph, sourceRoot, nodeById, opts, p
|
|
|
768
912
|
}
|
|
769
913
|
if (classification === "needs_setup") {
|
|
770
914
|
needsSetup.push(attempt);
|
|
771
|
-
//
|
|
772
|
-
|
|
773
|
-
|
|
915
|
+
// Quarantine only a classified project-wide root cause. Target/test-specific
|
|
916
|
+
// red baselines and surviving mutants never suppress siblings.
|
|
917
|
+
if (category && PROJECT_WIDE_BLOCKERS.has(category)) {
|
|
918
|
+
const scopeLabel = category === "go_package_build_failure"
|
|
919
|
+
? dirname(targetFileRel).split(sep).join("/") || "."
|
|
920
|
+
: runnerRoot;
|
|
921
|
+
projectBlocked.set(classifiedProjectBlockKey(category, targetLanguageName, runnerRoot, targetFileRel), {
|
|
922
|
+
category,
|
|
923
|
+
reason: reason ?? "",
|
|
924
|
+
blockedBy: symId,
|
|
925
|
+
scopeLabel
|
|
926
|
+
});
|
|
774
927
|
}
|
|
775
928
|
}
|
|
776
929
|
// non_killing → keep trying this symbol's other associated tests, if any.
|
|
@@ -810,16 +963,17 @@ export async function autoProve(root, opts, deps) {
|
|
|
810
963
|
.filter((r) => r.evidence_tier === "proven")
|
|
811
964
|
.map((r) => r.code_symbol)
|
|
812
965
|
.filter(Boolean));
|
|
813
|
-
//
|
|
814
|
-
//
|
|
815
|
-
|
|
966
|
+
// Shared, run-local quarantine for classified project-wide toolchain failures.
|
|
967
|
+
// Keyed by language + bounded runner root; never populated by a test-specific red
|
|
968
|
+
// baseline, an unknown failure, or a surviving mutant.
|
|
969
|
+
const projectBlocked = new Map();
|
|
816
970
|
// ONE unified dynamic-proof budget for the whole pass (existing-first → then generation).
|
|
817
971
|
// Default 5 ("dynamically prove top 5"); `--auto-limit N` overrides it, clamped to
|
|
818
972
|
// MAX_AUTO_LIMIT. The existing lane consumes from this budget and the generation lane gets
|
|
819
973
|
// only the remainder, so TOTAL attempts (existing + generation) are ≤ budget.
|
|
820
974
|
const autoLimit = Math.max(1, Math.min(MAX_AUTO_LIMIT, Math.floor(opts.autoLimit ?? DEFAULT_AUTO_LIMIT)));
|
|
821
975
|
// ── Lane 1: existing associated tests — NO key required, runs FIRST (PR 1.5). ──
|
|
822
|
-
const ex = proveExistingAssociatedTests(root, graph, sourceRoot, nodeById, opts, proveLoop, proveDeps, alreadyProven,
|
|
976
|
+
const ex = proveExistingAssociatedTests(root, graph, sourceRoot, nodeById, opts, proveLoop, proveDeps, alreadyProven, projectBlocked, autoLimit);
|
|
823
977
|
if (opts.existingOnly) {
|
|
824
978
|
const status = ex.proven > 0 ? "proven-run" : ex.attempted > 0 ? "ran-no-proof" : "no-targets";
|
|
825
979
|
return {
|
|
@@ -872,6 +1026,7 @@ export async function autoProve(root, opts, deps) {
|
|
|
872
1026
|
const changed = new Set(opts.changedFiles);
|
|
873
1027
|
candidates = candidates.filter((g) => changed.has(g.file));
|
|
874
1028
|
}
|
|
1029
|
+
candidates = orderRankedProofCandidates(candidates, graph, sourceRoot);
|
|
875
1030
|
const attempts = [];
|
|
876
1031
|
const needsSetup = [];
|
|
877
1032
|
const skipped = [];
|
|
@@ -917,14 +1072,24 @@ export async function autoProve(root, opts, deps) {
|
|
|
917
1072
|
}
|
|
918
1073
|
const { target_symbol, replacement, runner } = hint.prove_run.args;
|
|
919
1074
|
const targetFileRel = symbolFile(target_symbol);
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
//
|
|
923
|
-
|
|
1075
|
+
const language = targetLanguage(target_symbol);
|
|
1076
|
+
const projectRoot = proofRunnerRoot(sourceRoot, targetFileRel);
|
|
1077
|
+
// Cross-lane quarantine: a classified project-wide runner failure already
|
|
1078
|
+
// explains why this generated candidate cannot run. Skip without spending budget.
|
|
1079
|
+
const blockedKey = projectBlockKeys(language, projectRoot, targetFileRel).find((key) => projectBlocked.has(key));
|
|
1080
|
+
const blocked = blockedKey ? projectBlocked.get(blockedKey) : undefined;
|
|
924
1081
|
if (blocked) {
|
|
925
|
-
const attempt = dedupedAttempt(target_symbol, "",
|
|
1082
|
+
const attempt = dedupedAttempt(target_symbol, "", projectRoot, blocked);
|
|
926
1083
|
attempts.push(attempt);
|
|
927
1084
|
needsSetup.push(attempt);
|
|
1085
|
+
skipped.push({
|
|
1086
|
+
target_symbol,
|
|
1087
|
+
title: test.title,
|
|
1088
|
+
reason: attempt.reason ?? "Skipped after a project-wide proof failure.",
|
|
1089
|
+
language,
|
|
1090
|
+
project_root: projectRoot,
|
|
1091
|
+
blocked_by: blocked.blockedBy
|
|
1092
|
+
});
|
|
928
1093
|
continue;
|
|
929
1094
|
}
|
|
930
1095
|
const filename = basename(hint.prove_run.args.test_path);
|
|
@@ -984,7 +1149,8 @@ export async function autoProve(root, opts, deps) {
|
|
|
984
1149
|
target_symbol,
|
|
985
1150
|
test_path: writeRel,
|
|
986
1151
|
classification: "needs_setup",
|
|
987
|
-
reason: `Proof could not run: ${redactSecrets(errMsg(e))}
|
|
1152
|
+
reason: `Proof could not run: ${redactSecrets(errMsg(e))}`,
|
|
1153
|
+
project_root: projectRoot
|
|
988
1154
|
};
|
|
989
1155
|
attempts.push(attempt);
|
|
990
1156
|
needsSetup.push(attempt);
|
|
@@ -997,6 +1163,7 @@ export async function autoProve(root, opts, deps) {
|
|
|
997
1163
|
classification,
|
|
998
1164
|
reason,
|
|
999
1165
|
category,
|
|
1166
|
+
project_root: projectRoot,
|
|
1000
1167
|
mutant_status: mutantStatusOf(result)
|
|
1001
1168
|
};
|
|
1002
1169
|
attempts.push(attempt);
|
|
@@ -1004,9 +1171,16 @@ export async function autoProve(root, opts, deps) {
|
|
|
1004
1171
|
proven++;
|
|
1005
1172
|
else if (classification === "needs_setup") {
|
|
1006
1173
|
needsSetup.push(attempt);
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1174
|
+
if (category && PROJECT_WIDE_BLOCKERS.has(category)) {
|
|
1175
|
+
const scopeLabel = category === "go_package_build_failure"
|
|
1176
|
+
? dirname(targetFileRel).split(sep).join("/") || "."
|
|
1177
|
+
: projectRoot;
|
|
1178
|
+
projectBlocked.set(classifiedProjectBlockKey(category, language, projectRoot, targetFileRel), {
|
|
1179
|
+
category,
|
|
1180
|
+
reason: reason ?? "",
|
|
1181
|
+
blockedBy: target_symbol,
|
|
1182
|
+
scopeLabel
|
|
1183
|
+
});
|
|
1010
1184
|
}
|
|
1011
1185
|
}
|
|
1012
1186
|
// non_killing stays in `attempts` only — an honest skip, never Proven.
|
|
@@ -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". */
|
|
@@ -115,12 +115,17 @@ 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
|
|
119
121
|
})),
|
|
120
122
|
skipped: auto.skipped.map((s) => ({
|
|
121
123
|
target_symbol: s.target_symbol,
|
|
122
124
|
title: s.title,
|
|
123
|
-
reason: redactSecrets(s.reason)
|
|
125
|
+
reason: redactSecrets(s.reason),
|
|
126
|
+
language: s.language,
|
|
127
|
+
project_root: s.project_root,
|
|
128
|
+
blocked_by: s.blocked_by
|
|
124
129
|
}))
|
|
125
130
|
};
|
|
126
131
|
}
|
package/dist/local/provenance.js
CHANGED
|
@@ -3,7 +3,7 @@ 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.
|
|
6
|
+
export const ANALYZER_VERSION = "orangepro.analyzer.v3";
|
|
7
7
|
export const PROOF_ORACLE_VERSION = "orangepro.targeted_mutation_oracle.v1";
|
|
8
8
|
function stable(value) {
|
|
9
9
|
if (value === null || typeof value !== "object")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orangepro/orangepro-mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.44",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "OrangePro (`opro`) — a local-first, BYOK CLI + MCP server that builds an evidence graph from a local checkout, ingests runtime coverage, and generates grounded tests. Metadata-only exports; no source upload; generated tests stay local.",
|
|
6
6
|
"license": "MIT",
|