probatio 0.3.0 → 0.5.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/README.md +10 -198
- package/bin/probatio.js +52 -0
- package/package.json +21 -29
- package/LICENSE +0 -21
- package/dist/cli.js +0 -721
- package/dist/contract.js +0 -32
- package/dist/flags.js +0 -87
- package/dist/golden/check.js +0 -188
- package/dist/golden/record.js +0 -395
- package/dist/golden/recorder.mjs +0 -78
- package/dist/ledger/build.js +0 -348
- package/dist/ledger/check.js +0 -133
- package/dist/matrix/report.js +0 -256
- package/dist/mcp.js +0 -188
- package/dist/memory/state.js +0 -222
- package/dist/mutate/affected.js +0 -173
- package/dist/mutate/child-lines.mjs +0 -39
- package/dist/mutate/coverage-map.js +0 -229
- package/dist/mutate/csharp-coverage.js +0 -161
- package/dist/mutate/fail-reporter.mjs +0 -65
- package/dist/mutate/find.js +0 -13
- package/dist/mutate/generate.js +0 -156
- package/dist/mutate/go-coverage.js +0 -100
- package/dist/mutate/ids.js +0 -22
- package/dist/mutate/java-coverage.js +0 -217
- package/dist/mutate/kill-label.js +0 -34
- package/dist/mutate/load-failure.mjs +0 -40
- package/dist/mutate/mocha-coverage.cjs +0 -77
- package/dist/mutate/node-batch-loader.mjs +0 -99
- package/dist/mutate/node-batch.mjs +0 -167
- package/dist/mutate/node-coverage.mjs +0 -77
- package/dist/mutate/operators.js +0 -181
- package/dist/mutate/patch.js +0 -136
- package/dist/mutate/precise-lines.mjs +0 -304
- package/dist/mutate/probatio-jacoco-run.java +0 -516
- package/dist/mutate/project-config.js +0 -58
- package/dist/mutate/py-pytest-reporter.py +0 -145
- package/dist/mutate/py-reporter.py +0 -79
- package/dist/mutate/run.js +0 -1335
- package/dist/mutate/rust-coverage.js +0 -144
- package/dist/mutate/sealed.js +0 -118
- package/dist/mutate/suite-decision.js +0 -130
- package/dist/mutate/suites.js +0 -1524
- package/dist/mutate/tally.js +0 -240
- package/dist/mutate/text-operators.js +0 -637
- package/dist/mutate/titles.js +0 -23
- package/dist/node-version.js +0 -13
- package/dist/swarm/check-kill.js +0 -114
- package/dist/swarm/queue.js +0 -68
- package/dist/verify/change.js +0 -340
- package/schemas/check-kill.schema.json +0 -93
- package/schemas/findings.add.schema.json +0 -53
- package/schemas/gap.fix.schema.json +0 -53
- package/schemas/gap.revert.schema.json +0 -53
- package/schemas/golden.check.schema.json +0 -89
- package/schemas/golden.compare.schema.json +0 -151
- package/schemas/golden.record.schema.json +0 -126
- package/schemas/guard.check.schema.json +0 -53
- package/schemas/ledger.build.schema.json +0 -132
- package/schemas/ledger.check.schema.json +0 -143
- package/schemas/matrix.report.schema.json +0 -129
- package/schemas/mcp.schema.json +0 -53
- package/schemas/mutate.generate.schema.json +0 -127
- package/schemas/mutate.help.schema.json +0 -53
- package/schemas/mutate.run.schema.json +0 -272
- package/schemas/mutate.sealed.schema.json +0 -53
- package/schemas/mutate.tally.schema.json +0 -137
- package/schemas/queue.claim.schema.json +0 -53
- package/schemas/queue.reap.schema.json +0 -71
- package/schemas/queue.seed.schema.json +0 -71
- package/schemas/schema.schema.json +0 -74
- package/schemas/seal.schema.json +0 -69
- package/schemas/status.schema.json +0 -103
- package/schemas/verify-change.schema.json +0 -222
package/dist/contract.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { homedir } from "node:os";
|
|
2
|
-
/** Bumped when a field is renamed, removed, or changes meaning. 2: `drop` became `noKillsYet`, and a compile or link failure became `unviable`, not `killed`. */
|
|
3
|
-
export const SCHEMA_VERSION = 2;
|
|
4
|
-
/** Replace the home directory so a result never carries a user's name or home path. */
|
|
5
|
-
export function scrub(value, home = homedir(), keepPaths = false) {
|
|
6
|
-
if (typeof value === "string") {
|
|
7
|
-
if (keepPaths || !home || !value.includes(home))
|
|
8
|
-
return value;
|
|
9
|
-
return value.split(home).join("~");
|
|
10
|
-
}
|
|
11
|
-
if (Array.isArray(value))
|
|
12
|
-
return value.map((item) => scrub(item, home, keepPaths));
|
|
13
|
-
if (value && typeof value === "object") {
|
|
14
|
-
const out = {};
|
|
15
|
-
for (const [key, item] of Object.entries(value)) {
|
|
16
|
-
// nextCall.argv is executed. A tilde is not expanded by spawn or by MCP.
|
|
17
|
-
out[key] = scrub(item, home, keepPaths || key === "nextCall");
|
|
18
|
-
}
|
|
19
|
-
return out;
|
|
20
|
-
}
|
|
21
|
-
return value;
|
|
22
|
-
}
|
|
23
|
-
export function render(envelope, human, home = homedir()) {
|
|
24
|
-
const clean = scrub(envelope, home);
|
|
25
|
-
if (!human)
|
|
26
|
-
return `${JSON.stringify(clean, null, 2)}\n`;
|
|
27
|
-
const lines = [clean.summary, clean.next];
|
|
28
|
-
const call = clean.nextCall;
|
|
29
|
-
if (call && Array.isArray(call.argv))
|
|
30
|
-
lines.push(`next call: probatio ${call.argv.join(" ")}`);
|
|
31
|
-
return `${lines.filter((line) => line.length > 0).join("\n")}\n`;
|
|
32
|
-
}
|
package/dist/flags.js
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
/** `--key value`, `--key=value`, `--flag`, `--no-flag`. Words before the first flag are the command. */
|
|
2
|
-
export function parseArgs(argv) {
|
|
3
|
-
const command = [];
|
|
4
|
-
const flags = new Map();
|
|
5
|
-
let seenFlag = false;
|
|
6
|
-
for (let i = 0; i < argv.length; i++) {
|
|
7
|
-
const token = argv[i];
|
|
8
|
-
if (token === "--") {
|
|
9
|
-
seenFlag = true;
|
|
10
|
-
continue;
|
|
11
|
-
}
|
|
12
|
-
if (!token.startsWith("--")) {
|
|
13
|
-
if (!seenFlag)
|
|
14
|
-
command.push(token);
|
|
15
|
-
else
|
|
16
|
-
push(flags, "_", token);
|
|
17
|
-
continue;
|
|
18
|
-
}
|
|
19
|
-
seenFlag = true;
|
|
20
|
-
const body = token.slice(2);
|
|
21
|
-
const eq = body.indexOf("=");
|
|
22
|
-
if (eq !== -1) {
|
|
23
|
-
push(flags, body.slice(0, eq), body.slice(eq + 1));
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
if (body.startsWith("no-") && body.length > 3) {
|
|
27
|
-
push(flags, body.slice(3), false);
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
const next = argv[i + 1];
|
|
31
|
-
if (next === undefined || next.startsWith("--"))
|
|
32
|
-
push(flags, body, true);
|
|
33
|
-
else {
|
|
34
|
-
push(flags, body, next);
|
|
35
|
-
i++;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
const human = flags.get("human")?.some((value) => value === true) ?? false;
|
|
39
|
-
return { command, flags, human };
|
|
40
|
-
}
|
|
41
|
-
function push(flags, key, value) {
|
|
42
|
-
const list = flags.get(key);
|
|
43
|
-
if (list)
|
|
44
|
-
list.push(value);
|
|
45
|
-
else
|
|
46
|
-
flags.set(key, [value]);
|
|
47
|
-
}
|
|
48
|
-
export function text(flags, key) {
|
|
49
|
-
const values = flags.get(key);
|
|
50
|
-
if (!values || values.length === 0)
|
|
51
|
-
return undefined;
|
|
52
|
-
const last = values[values.length - 1];
|
|
53
|
-
if (typeof last === "boolean")
|
|
54
|
-
return undefined;
|
|
55
|
-
return last;
|
|
56
|
-
}
|
|
57
|
-
export function texts(flags, key) {
|
|
58
|
-
return (flags.get(key) ?? []).filter((value) => typeof value === "string");
|
|
59
|
-
}
|
|
60
|
-
export function bool(flags, key, fallback) {
|
|
61
|
-
const values = flags.get(key);
|
|
62
|
-
if (!values || values.length === 0)
|
|
63
|
-
return fallback;
|
|
64
|
-
const last = values[values.length - 1];
|
|
65
|
-
if (typeof last === "boolean")
|
|
66
|
-
return last;
|
|
67
|
-
if (last === "true")
|
|
68
|
-
return true;
|
|
69
|
-
if (last === "false")
|
|
70
|
-
return false;
|
|
71
|
-
return fallback;
|
|
72
|
-
}
|
|
73
|
-
export function int(flags, key) {
|
|
74
|
-
const raw = text(flags, key);
|
|
75
|
-
if (raw === undefined)
|
|
76
|
-
return undefined;
|
|
77
|
-
const value = Number(raw);
|
|
78
|
-
if (!Number.isInteger(value) || value < 0)
|
|
79
|
-
throw new Error(`--${key} must be a non-negative integer`);
|
|
80
|
-
return value;
|
|
81
|
-
}
|
|
82
|
-
export function requireText(flags, key) {
|
|
83
|
-
const value = text(flags, key);
|
|
84
|
-
if (!value)
|
|
85
|
-
throw new Error(`--${key} is required`);
|
|
86
|
-
return value;
|
|
87
|
-
}
|
package/dist/golden/check.js
DELETED
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { homedir } from "node:os";
|
|
3
|
-
import { SCHEMA_VERSION } from "../contract.js";
|
|
4
|
-
const WORDING_KEYS = ["next", "nextLead"];
|
|
5
|
-
/** `Golden-Change: <row>: <why>`. The why starts after the first colon-and-space, so a row may contain colons. */
|
|
6
|
-
export function goldenChanges(message) {
|
|
7
|
-
const named = new Map();
|
|
8
|
-
for (const match of message.matchAll(/^Golden-Change:\s*(.+?):\s+(.+)$/gm))
|
|
9
|
-
named.set(match[1], match[2]);
|
|
10
|
-
return named;
|
|
11
|
-
}
|
|
12
|
-
export function readGolden(file) {
|
|
13
|
-
return asTable(readFileSync(file, "utf8"));
|
|
14
|
-
}
|
|
15
|
-
export function checkGolden(input) {
|
|
16
|
-
const home = input.home ?? homedir();
|
|
17
|
-
const recorded = normalizeTable(input.recorded, input.roots);
|
|
18
|
-
const actual = normalizeTable(input.actual, input.roots);
|
|
19
|
-
const ids = [...new Set([...Object.keys(recorded.rows), ...Object.keys(actual.rows)])].sort();
|
|
20
|
-
const wording = [];
|
|
21
|
-
const contract = [];
|
|
22
|
-
const invariant = [];
|
|
23
|
-
for (const id of ids) {
|
|
24
|
-
const before = recorded.rows[id];
|
|
25
|
-
const after = actual.rows[id];
|
|
26
|
-
if (after) {
|
|
27
|
-
const violation = invariantOf(id, after, home);
|
|
28
|
-
if (violation)
|
|
29
|
-
invariant.push(violation);
|
|
30
|
-
}
|
|
31
|
-
if (JSON.stringify(wordingOf(before)) !== JSON.stringify(wordingOf(after)))
|
|
32
|
-
wording.push(id);
|
|
33
|
-
if (JSON.stringify(contractOf(before)) !== JSON.stringify(contractOf(after)))
|
|
34
|
-
contract.push(id);
|
|
35
|
-
}
|
|
36
|
-
const allowed = goldenChanges(input.message);
|
|
37
|
-
const unauthorized = contract.filter((id) => !allowed.has(id));
|
|
38
|
-
const canWrite = input.update !== null && invariant.length === 0 && unauthorized.length === 0;
|
|
39
|
-
const wordingWrite = canWrite && (input.update === "wording" || input.update === "all");
|
|
40
|
-
const contractWrite = canWrite && contract.every((id) => allowed.has(id));
|
|
41
|
-
const table = structuredClone(input.recorded);
|
|
42
|
-
if (wordingWrite || (canWrite && contractWrite && contract.length > 0)) {
|
|
43
|
-
for (const id of ids) {
|
|
44
|
-
const after = input.actual.rows[id];
|
|
45
|
-
const contractRow = contract.includes(id);
|
|
46
|
-
const wordingRow = wording.includes(id);
|
|
47
|
-
if (contractRow && contractWrite) {
|
|
48
|
-
if (!after)
|
|
49
|
-
delete table.rows[id];
|
|
50
|
-
else
|
|
51
|
-
table.rows[id] = after;
|
|
52
|
-
}
|
|
53
|
-
else if (wordingRow && wordingWrite && after && table.rows[id]) {
|
|
54
|
-
table.rows[id] = { ...table.rows[id], ...wordingOf(after) };
|
|
55
|
-
}
|
|
56
|
-
else if (wordingRow && wordingWrite && after && !table.rows[id] && !contractRow) {
|
|
57
|
-
table.rows[id] = wordingOf(after);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
const write = invariant.length === 0 && unauthorized.length === 0 && input.update !== null && (wording.length > 0 || contract.length > 0);
|
|
62
|
-
const ok = invariant.length === 0 && unauthorized.length === 0 && (wording.length === 0 || input.update !== null);
|
|
63
|
-
const summary = ok
|
|
64
|
-
? wording.length === 0 && contract.length === 0
|
|
65
|
-
? "Golden rows match."
|
|
66
|
-
: `Re-recorded ${wording.length} wording row(s) and ${contract.length} contract row(s).`
|
|
67
|
-
: invariant.length > 0
|
|
68
|
-
? `Invariant failed: ${invariant.join("; ")}.`
|
|
69
|
-
: unauthorized.length > 0
|
|
70
|
-
? `Contract changed for ${unauthorized.join(", ")} without a Golden-Change trailer.`
|
|
71
|
-
: `Wording changed for ${wording.join(", ")}. Re-record with --update wording.`;
|
|
72
|
-
return {
|
|
73
|
-
write: write && ok,
|
|
74
|
-
table,
|
|
75
|
-
envelope: {
|
|
76
|
-
schemaVersion: SCHEMA_VERSION,
|
|
77
|
-
ok,
|
|
78
|
-
command: "golden.check",
|
|
79
|
-
summary,
|
|
80
|
-
next: ok ? "Review the re-recorded rows." : "The table was not written.",
|
|
81
|
-
nextCall: null,
|
|
82
|
-
wording,
|
|
83
|
-
contract,
|
|
84
|
-
invariant,
|
|
85
|
-
updated: write && ok,
|
|
86
|
-
},
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
export function goldenShape(text) {
|
|
90
|
-
if (!text.trim())
|
|
91
|
-
return "rows";
|
|
92
|
-
let parsed;
|
|
93
|
-
try {
|
|
94
|
-
parsed = JSON.parse(text);
|
|
95
|
-
}
|
|
96
|
-
catch {
|
|
97
|
-
return "rows";
|
|
98
|
-
}
|
|
99
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
100
|
-
return "flat";
|
|
101
|
-
const rows = parsed.rows;
|
|
102
|
-
if (rows && typeof rows === "object" && !Array.isArray(rows))
|
|
103
|
-
return "rows";
|
|
104
|
-
return "flat";
|
|
105
|
-
}
|
|
106
|
-
export function writeGolden(file, table, shape = "rows") {
|
|
107
|
-
const rows = shape === "flat" ? table.rows : sortRows(table.rows);
|
|
108
|
-
const payload = shape === "flat" ? rows : { rows };
|
|
109
|
-
writeFileSync(file, `${JSON.stringify(payload, null, 2)}\n`);
|
|
110
|
-
}
|
|
111
|
-
function invariantOf(id, row, home) {
|
|
112
|
-
if (row.ok === true && (typeof row.reason !== "string" || row.reason.trim().length === 0)) {
|
|
113
|
-
return `${id} has ok without a reason`;
|
|
114
|
-
}
|
|
115
|
-
if (home && containsHome(row, home))
|
|
116
|
-
return `${id} contains a home path`;
|
|
117
|
-
return null;
|
|
118
|
-
}
|
|
119
|
-
function containsHome(value, home) {
|
|
120
|
-
if (typeof value === "string")
|
|
121
|
-
return value.includes(home);
|
|
122
|
-
if (Array.isArray(value))
|
|
123
|
-
return value.some((item) => containsHome(item, home));
|
|
124
|
-
if (value && typeof value === "object")
|
|
125
|
-
return Object.values(value).some((item) => containsHome(item, home));
|
|
126
|
-
return false;
|
|
127
|
-
}
|
|
128
|
-
function wordingOf(row) {
|
|
129
|
-
if (!row || typeof row !== "object" || Array.isArray(row))
|
|
130
|
-
return {};
|
|
131
|
-
const out = {};
|
|
132
|
-
for (const key of WORDING_KEYS) {
|
|
133
|
-
if (Object.prototype.hasOwnProperty.call(row, key))
|
|
134
|
-
out[key] = row[key];
|
|
135
|
-
}
|
|
136
|
-
return out;
|
|
137
|
-
}
|
|
138
|
-
function contractOf(row) {
|
|
139
|
-
if (!row)
|
|
140
|
-
return {};
|
|
141
|
-
const out = {};
|
|
142
|
-
for (const key of Object.keys(row).sort()) {
|
|
143
|
-
if (WORDING_KEYS.includes(key))
|
|
144
|
-
continue;
|
|
145
|
-
out[key] = row[key];
|
|
146
|
-
}
|
|
147
|
-
return out;
|
|
148
|
-
}
|
|
149
|
-
function normalizeTable(table, roots) {
|
|
150
|
-
return { rows: normalize(table.rows, roots) };
|
|
151
|
-
}
|
|
152
|
-
function normalize(value, roots) {
|
|
153
|
-
if (typeof value === "string") {
|
|
154
|
-
let out = value;
|
|
155
|
-
for (const root of roots) {
|
|
156
|
-
if (root.length > 1 && out.includes(root))
|
|
157
|
-
out = out.split(root).join("{root}");
|
|
158
|
-
}
|
|
159
|
-
return out;
|
|
160
|
-
}
|
|
161
|
-
if (Array.isArray(value))
|
|
162
|
-
return value.map((item) => normalize(item, roots));
|
|
163
|
-
if (value && typeof value === "object") {
|
|
164
|
-
const out = {};
|
|
165
|
-
for (const [key, item] of Object.entries(value))
|
|
166
|
-
out[key] = normalize(item, roots);
|
|
167
|
-
return out;
|
|
168
|
-
}
|
|
169
|
-
return value;
|
|
170
|
-
}
|
|
171
|
-
export function asTable(text) {
|
|
172
|
-
if (!text.trim())
|
|
173
|
-
return { rows: {} };
|
|
174
|
-
const parsed = JSON.parse(text);
|
|
175
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
176
|
-
return { rows: {} };
|
|
177
|
-
const record = parsed;
|
|
178
|
-
const rows = record.rows && typeof record.rows === "object" && !Array.isArray(record.rows)
|
|
179
|
-
? record.rows
|
|
180
|
-
: record;
|
|
181
|
-
return { rows };
|
|
182
|
-
}
|
|
183
|
-
function sortRows(rows) {
|
|
184
|
-
const out = {};
|
|
185
|
-
for (const key of Object.keys(rows).sort())
|
|
186
|
-
out[key] = rows[key];
|
|
187
|
-
return out;
|
|
188
|
-
}
|