skill-probe 0.9.0 → 0.10.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 +118 -35
- package/assets/skill-probe-thief-demo.gif +0 -0
- package/assets/skill-probe-thief-demo.png +0 -0
- package/dist/adapters/claudeCode.d.ts +17 -1
- package/dist/adapters/claudeCode.js +49 -45
- package/dist/adapters/openCode.d.ts +14 -1
- package/dist/adapters/openCode.js +64 -47
- package/dist/baseline.js +0 -0
- package/dist/cli.js +77 -34
- package/dist/config.d.ts +4 -0
- package/dist/config.js +37 -0
- package/dist/context.d.ts +29 -9
- package/dist/context.js +124 -97
- package/dist/diagnose.d.ts +11 -5
- package/dist/diagnose.js +45 -26
- package/dist/doctor.d.ts +2 -1
- package/dist/doctor.js +72 -38
- package/dist/eval.d.ts +17 -5
- package/dist/eval.js +56 -23
- package/dist/fix.d.ts +9 -6
- package/dist/fix.js +82 -70
- package/dist/gen.d.ts +6 -5
- package/dist/gen.js +1 -21
- package/dist/llm.d.ts +2 -0
- package/dist/llm.js +23 -23
- package/dist/manifest.d.ts +22 -3
- package/dist/manifest.js +27 -2
- package/dist/orchestrator.d.ts +47 -5
- package/dist/orchestrator.js +82 -13
- package/dist/proc.d.ts +24 -0
- package/dist/proc.js +101 -0
- package/dist/report.d.ts +4 -2
- package/dist/report.js +88 -24
- package/dist/sandbox.d.ts +18 -0
- package/dist/sandbox.js +98 -0
- package/dist/skills.d.ts +48 -0
- package/dist/skills.js +282 -0
- package/dist/stats.d.ts +27 -5
- package/dist/stats.js +132 -8
- package/dist/types.d.ts +31 -1
- package/dist/types.js +9 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
/** skill-probe CLI.
|
|
3
3
|
* skill-probe --config <f> audit a co-loaded skill library
|
|
4
4
|
* skill-probe fix --config <f> --skill <n> rewrite a skill's description & prove the lift
|
|
5
|
-
* Exit: 0 = pass/applied, 1 = behavioral fail/
|
|
5
|
+
* Exit: 0 = pass/applied, 1 = behavioral fail/not applied, 2 = inconclusive/infra error/usage,
|
|
6
|
+
* 130/143 = interrupted (children killed, sandboxes removed). */
|
|
6
7
|
import { parseArgs } from "node:util";
|
|
7
8
|
import { resolve } from "node:path";
|
|
8
9
|
import { runAudit } from "./eval.js";
|
|
@@ -14,10 +15,14 @@ import { listSkills, generateCases, genConfig } from "./gen.js";
|
|
|
14
15
|
import { loadConfig, parseProbability, parseIntFlag, ConfigError } from "./config.js";
|
|
15
16
|
import { loadBaseline, saveBaseline, compareToBaseline } from "./baseline.js";
|
|
16
17
|
import { renderTable, renderMarkdown, renderJson, renderFix, renderContext, renderDoctor, renderDiagnose, renderBaseline } from "./report.js";
|
|
18
|
+
import { Budget } from "./orchestrator.js";
|
|
19
|
+
import { toolVersion } from "./manifest.js";
|
|
20
|
+
import { installSignalHandlers } from "./proc.js";
|
|
21
|
+
const COMMANDS = new Set(["audit", "context", "diagnose", "doctor", "gen", "fix"]);
|
|
17
22
|
const HELP = `skill-probe — audit a co-loaded agent skill library by real activation behavior.
|
|
18
23
|
|
|
19
24
|
USAGE
|
|
20
|
-
skill-probe --config <file.json> [options]
|
|
25
|
+
skill-probe [audit] --config <file.json> [options] # audit
|
|
21
26
|
skill-probe context --config <file.json> [--ablate] # isolation vs co-loaded activation rates
|
|
22
27
|
skill-probe diagnose --config <file.json> [options] # why a skill fails: routing-miss vs description
|
|
23
28
|
skill-probe doctor [--cwd <dir>] [--config <file.json>] # preflight: setup/auth/config sanity
|
|
@@ -32,12 +37,24 @@ OPTIONS
|
|
|
32
37
|
Recorded in the report — REQUIRED for a fair cross-runtime comparison.
|
|
33
38
|
--k <n> max VALID repeats per prompt (positive integer; default 10)
|
|
34
39
|
--threshold <p> min acceptable reliability, strictly 0..1 (default 0.7 = smoke test)
|
|
35
|
-
--conf <p> confidence
|
|
40
|
+
--conf <p> confidence: 0.9 | 0.95 | 0.99 (default 0.95). A verdict is wrong with
|
|
41
|
+
probability <= 1-conf, however early the run stops.
|
|
42
|
+
--max-turns <n> agent turns per probe before reading the outcome (default 1)
|
|
43
|
+
--timeout <sec> per-probe timeout in seconds (default 120)
|
|
44
|
+
--user-config also load your personal runtime config (user settings, plugins, MCP
|
|
45
|
+
servers) into probes. Off by default (claude-code): results then
|
|
46
|
+
reproduce in CI. opencode always loads its global config.
|
|
47
|
+
--concurrency <n> cases probed in parallel (default 1; mind your rate limits)
|
|
48
|
+
--max-cost <usd> stop probing once runtime probes have spent this much; cases cut short
|
|
49
|
+
are inconclusive (error if never started), never a pass. LLM calls
|
|
50
|
+
(gen, fix's rewrite, diagnose's forced choice) aren't counted
|
|
36
51
|
--json emit JSON instead of a table
|
|
37
52
|
--markdown emit a Markdown table (paste into a PR/README)
|
|
38
53
|
--no-cost hide the cost line (e.g. when on a Claude subscription)
|
|
39
54
|
--quiet suppress the live progress line on stderr
|
|
40
|
-
|
|
55
|
+
-v, --version print the version
|
|
56
|
+
audit only:
|
|
57
|
+
--fail-on-theft fail a case when ANY other skill fires, even if reliability clears the bar
|
|
41
58
|
--save-baseline <f> after the run, save per-case counts + a run manifest to <f>
|
|
42
59
|
--baseline <f> compare this run against a saved baseline: per-case Fisher's exact,
|
|
43
60
|
Benjamini-Hochberg corrected — exit 1 ONLY on a significant regression
|
|
@@ -50,7 +67,7 @@ OPTIONS
|
|
|
50
67
|
doctor only:
|
|
51
68
|
--cwd <dir> project dir containing .claude/skills/ (default: .)
|
|
52
69
|
--config <file> also sanity-check a config (expected skills exist, k/threshold feasible)
|
|
53
|
-
--skip-probe don't run the harmless live
|
|
70
|
+
--skip-probe don't run the harmless live probes (skip the auth/load checks; no cost)
|
|
54
71
|
gen only (uses ANTHROPIC_API_KEY if set, else the logged-in 'claude' CLI):
|
|
55
72
|
--cwd <dir> project dir containing .claude/skills/ (default: .)
|
|
56
73
|
--per-skill <n> should-fire prompts to draft per skill (default 3)
|
|
@@ -60,8 +77,8 @@ OPTIONS
|
|
|
60
77
|
--apply-threshold <p> min P(improvement) to keep the rewrite (default 0.9)
|
|
61
78
|
-h, --help show this help
|
|
62
79
|
|
|
63
|
-
Reliability carries a Wilson CI + sample size k. fix
|
|
64
|
-
|
|
80
|
+
Reliability carries a Wilson CI + sample size k. fix runs a PAIRED before/after in two sandboxes and
|
|
81
|
+
writes the new description only if the Bayesian P(improvement) clears the apply-threshold.`;
|
|
65
82
|
let progressLen = 0;
|
|
66
83
|
function makeProgress(values) {
|
|
67
84
|
if (values["json"] || values["quiet"] || !process.stderr.isTTY)
|
|
@@ -103,13 +120,32 @@ function makeDiagProgress(values) {
|
|
|
103
120
|
progressLen = line.length;
|
|
104
121
|
};
|
|
105
122
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
conf:
|
|
123
|
+
/** The config plus every CLI override that changes what is measured. */
|
|
124
|
+
function configFrom(values, opts = {}) {
|
|
125
|
+
const str = (k) => values[k];
|
|
126
|
+
return loadConfig(values["config"], {
|
|
127
|
+
runtime: str("runtime"), model: str("model"), k: str("k"), conf: str("conf"),
|
|
128
|
+
...(opts.threshold === false ? {} : { threshold: str("threshold") }),
|
|
129
|
+
maxTurns: str("max-turns"), timeout: str("timeout"),
|
|
130
|
+
userConfig: Boolean(values["user-config"]), failOnTheft: Boolean(values["fail-on-theft"]),
|
|
111
131
|
});
|
|
112
|
-
|
|
132
|
+
}
|
|
133
|
+
/** Run-time knobs that DON'T change what is measured (parallelism, spend cap). */
|
|
134
|
+
function runOpts(values) {
|
|
135
|
+
const concurrency = parseIntFlag(values["concurrency"], "--concurrency", 1, 1);
|
|
136
|
+
const raw = values["max-cost"];
|
|
137
|
+
if (raw === undefined)
|
|
138
|
+
return { concurrency };
|
|
139
|
+
const max = Number(raw);
|
|
140
|
+
if (!Number.isFinite(max) || max <= 0) {
|
|
141
|
+
throw new ConfigError([`--max-cost must be a positive number of USD (got ${JSON.stringify(raw)})`]);
|
|
142
|
+
}
|
|
143
|
+
return { concurrency, budget: new Budget(max) };
|
|
144
|
+
}
|
|
145
|
+
async function audit(values) {
|
|
146
|
+
const cfg = configFrom(values);
|
|
147
|
+
const progress = makeProgress(values);
|
|
148
|
+
const result = await runAudit(cfg, { ...runOpts(values), ...(progress ? { onProgress: progress } : {}) });
|
|
113
149
|
clearProgress();
|
|
114
150
|
const showCost = !values["no-cost"];
|
|
115
151
|
let cmp;
|
|
@@ -130,14 +166,9 @@ async function audit(values) {
|
|
|
130
166
|
return result.exitCode;
|
|
131
167
|
}
|
|
132
168
|
async function diagnose(values) {
|
|
133
|
-
const cfg =
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
conf: values["conf"],
|
|
137
|
-
});
|
|
138
|
-
const result = await runDiagnose(cfg, {
|
|
139
|
-
...(makeDiagProgress(values) ? { onProgress: makeDiagProgress(values) } : {}),
|
|
140
|
-
});
|
|
169
|
+
const cfg = configFrom(values);
|
|
170
|
+
const onProgress = makeDiagProgress(values);
|
|
171
|
+
const result = await runDiagnose(cfg, { ...runOpts(values), ...(onProgress ? { onProgress } : {}) });
|
|
141
172
|
clearProgress();
|
|
142
173
|
const showCost = !values["no-cost"];
|
|
143
174
|
console.log(values["json"] ? renderJson(result) : renderDiagnose(result, { showCost }));
|
|
@@ -150,20 +181,17 @@ async function doctor(values) {
|
|
|
150
181
|
...(values["runtime"] ? { runtime: values["runtime"] } : {}),
|
|
151
182
|
...(values["model"] ? { model: values["model"] } : {}),
|
|
152
183
|
...(values["skip-probe"] ? { skipProbe: true } : {}),
|
|
184
|
+
...(values["user-config"] ? { userConfig: true } : {}),
|
|
153
185
|
});
|
|
154
186
|
console.log(values["json"] ? renderJson(result) : renderDoctor(result));
|
|
155
187
|
return result.exitCode;
|
|
156
188
|
}
|
|
157
189
|
async function context(values) {
|
|
158
|
-
const cfg =
|
|
159
|
-
runtime: values["runtime"], model: values["model"],
|
|
160
|
-
k: values["k"], threshold: values["threshold"],
|
|
161
|
-
conf: values["conf"],
|
|
162
|
-
});
|
|
190
|
+
const cfg = configFrom(values);
|
|
163
191
|
const suspects = parseIntFlag(values["suspects"], "--suspects", 3, 1);
|
|
164
192
|
const onProgress = makeContextProgress(values);
|
|
165
193
|
const result = await runContextAudit(cfg, {
|
|
166
|
-
ablate: Boolean(values["ablate"]), suspects,
|
|
194
|
+
ablate: Boolean(values["ablate"]), suspects, ...runOpts(values),
|
|
167
195
|
...(onProgress ? { onProgress } : {}),
|
|
168
196
|
});
|
|
169
197
|
clearProgress();
|
|
@@ -195,12 +223,11 @@ async function fix(values) {
|
|
|
195
223
|
console.error("fix requires --skill <name>");
|
|
196
224
|
return 2;
|
|
197
225
|
}
|
|
198
|
-
const cfg =
|
|
199
|
-
runtime: values["runtime"], model: values["model"],
|
|
200
|
-
k: values["k"], conf: values["conf"],
|
|
201
|
-
});
|
|
226
|
+
const cfg = configFrom(values, { threshold: false });
|
|
202
227
|
const applyBar = parseProbability(values["apply-threshold"], "--apply-threshold", 0.9);
|
|
203
|
-
const result = await runFix(cfg, {
|
|
228
|
+
const result = await runFix(cfg, {
|
|
229
|
+
skill, k: cfg.k, applyBar, ...runOpts(values), ...(cfg.model ? { model: cfg.model } : {}),
|
|
230
|
+
});
|
|
204
231
|
console.log(values["json"] ? renderJson(result) : renderFix(result));
|
|
205
232
|
return result.applied ? 0 : 1;
|
|
206
233
|
}
|
|
@@ -224,6 +251,13 @@ async function main() {
|
|
|
224
251
|
baseline: { type: "string" },
|
|
225
252
|
"save-baseline": { type: "string" },
|
|
226
253
|
"skip-probe": { type: "boolean", default: false },
|
|
254
|
+
"max-turns": { type: "string" },
|
|
255
|
+
timeout: { type: "string" },
|
|
256
|
+
"user-config": { type: "boolean", default: false },
|
|
257
|
+
"fail-on-theft": { type: "boolean", default: false },
|
|
258
|
+
concurrency: { type: "string" },
|
|
259
|
+
"max-cost": { type: "string" },
|
|
260
|
+
version: { type: "boolean", short: "v", default: false },
|
|
227
261
|
json: { type: "boolean", default: false },
|
|
228
262
|
markdown: { type: "boolean", default: false },
|
|
229
263
|
"no-cost": { type: "boolean", default: false },
|
|
@@ -235,7 +269,16 @@ async function main() {
|
|
|
235
269
|
console.log(HELP);
|
|
236
270
|
process.exit(0);
|
|
237
271
|
}
|
|
238
|
-
|
|
272
|
+
if (values.version) {
|
|
273
|
+
console.log(toolVersion());
|
|
274
|
+
process.exit(0);
|
|
275
|
+
}
|
|
276
|
+
const cmd = positionals[0] ?? "audit";
|
|
277
|
+
if (!COMMANDS.has(cmd) || positionals.length > 1) {
|
|
278
|
+
console.error(`unknown command: ${positionals.join(" ")} (expected one of: ${[...COMMANDS].join(", ")})`);
|
|
279
|
+
process.exit(2);
|
|
280
|
+
}
|
|
281
|
+
installSignalHandlers();
|
|
239
282
|
let code;
|
|
240
283
|
if (cmd === "gen") {
|
|
241
284
|
code = await gen(values);
|
|
@@ -245,7 +288,7 @@ async function main() {
|
|
|
245
288
|
}
|
|
246
289
|
else {
|
|
247
290
|
if (!values.config) {
|
|
248
|
-
console.
|
|
291
|
+
console.error(HELP);
|
|
249
292
|
process.exit(2);
|
|
250
293
|
}
|
|
251
294
|
code = cmd === "fix" ? await fix(values)
|
package/dist/config.d.ts
CHANGED
|
@@ -18,5 +18,9 @@ export interface Overrides {
|
|
|
18
18
|
k?: string | undefined;
|
|
19
19
|
threshold?: string | undefined;
|
|
20
20
|
conf?: string | undefined;
|
|
21
|
+
maxTurns?: string | undefined;
|
|
22
|
+
timeout?: string | undefined;
|
|
23
|
+
userConfig?: boolean | undefined;
|
|
24
|
+
failOnTheft?: boolean | undefined;
|
|
21
25
|
}
|
|
22
26
|
export declare function loadConfig(configPath: string, overrides?: Overrides): Config;
|
package/dist/config.js
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
import { resolve, dirname } from "node:path";
|
|
5
5
|
const VALID_CONF = new Set([0.9, 0.95, 0.99]);
|
|
6
|
+
const KNOWN_KEYS = new Set([
|
|
7
|
+
"runtime", "model", "cwd", "k", "threshold", "conf", "cases",
|
|
8
|
+
"maxTurns", "timeout", "userConfig", "failOnTheft",
|
|
9
|
+
]);
|
|
6
10
|
export class ConfigError extends Error {
|
|
7
11
|
errors;
|
|
8
12
|
constructor(errors) {
|
|
@@ -17,6 +21,13 @@ export class ConfigError extends Error {
|
|
|
17
21
|
export function validateConfig(raw, configPath) {
|
|
18
22
|
const r = (raw ?? {});
|
|
19
23
|
const errs = [];
|
|
24
|
+
// a typo'd key ("treshold") would otherwise silently fall back to the default — fail loud.
|
|
25
|
+
// "$schema" and "_"-prefixed keys are allowed for tooling and comments.
|
|
26
|
+
for (const key of Object.keys(r)) {
|
|
27
|
+
if (!KNOWN_KEYS.has(key) && !key.startsWith("$") && !key.startsWith("_")) {
|
|
28
|
+
errs.push(`unknown config key "${key}" (known: ${[...KNOWN_KEYS].join(", ")})`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
20
31
|
const runtime = typeof r["runtime"] === "string" && r["runtime"] ? r["runtime"] : "claude-code";
|
|
21
32
|
let model;
|
|
22
33
|
if (r["model"] !== undefined) {
|
|
@@ -41,6 +52,20 @@ export function validateConfig(raw, configPath) {
|
|
|
41
52
|
if (typeof conf !== "number" || !VALID_CONF.has(conf)) {
|
|
42
53
|
errs.push(`conf must be one of 0.9, 0.95, 0.99 (got ${JSON.stringify(r["conf"])})`);
|
|
43
54
|
}
|
|
55
|
+
const maxTurns = r["maxTurns"];
|
|
56
|
+
if (maxTurns !== undefined && (typeof maxTurns !== "number" || !Number.isInteger(maxTurns) || maxTurns < 1)) {
|
|
57
|
+
errs.push(`maxTurns must be a positive integer (got ${JSON.stringify(maxTurns)})`);
|
|
58
|
+
}
|
|
59
|
+
// seconds in the file / on the CLI; ms internally
|
|
60
|
+
const timeout = r["timeout"];
|
|
61
|
+
if (timeout !== undefined && (typeof timeout !== "number" || !Number.isFinite(timeout) || timeout <= 0)) {
|
|
62
|
+
errs.push(`timeout must be a positive number of seconds (got ${JSON.stringify(timeout)})`);
|
|
63
|
+
}
|
|
64
|
+
for (const key of ["userConfig", "failOnTheft"]) {
|
|
65
|
+
if (r[key] !== undefined && typeof r[key] !== "boolean") {
|
|
66
|
+
errs.push(`${key} must be true or false (got ${JSON.stringify(r[key])})`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
44
69
|
const cases = Array.isArray(r["cases"]) ? r["cases"] : [];
|
|
45
70
|
if (cases.length === 0)
|
|
46
71
|
errs.push("config needs a non-empty `cases` array");
|
|
@@ -63,6 +88,10 @@ export function validateConfig(raw, configPath) {
|
|
|
63
88
|
threshold: threshold,
|
|
64
89
|
conf: conf,
|
|
65
90
|
cases: cases,
|
|
91
|
+
...(maxTurns !== undefined ? { maxTurns: maxTurns } : {}),
|
|
92
|
+
...(timeout !== undefined ? { timeoutMs: Math.round(timeout * 1000) } : {}),
|
|
93
|
+
...(r["userConfig"] === true ? { userConfig: true } : {}),
|
|
94
|
+
...(r["failOnTheft"] === true ? { failOnTheft: true } : {}),
|
|
66
95
|
};
|
|
67
96
|
}
|
|
68
97
|
/** Validate a probability-ish flag (e.g. --apply-threshold): must be a number strictly in (0,1). */
|
|
@@ -104,5 +133,13 @@ export function loadConfig(configPath, overrides = {}) {
|
|
|
104
133
|
merged["threshold"] = Number(overrides.threshold);
|
|
105
134
|
if (overrides.conf !== undefined)
|
|
106
135
|
merged["conf"] = Number(overrides.conf);
|
|
136
|
+
if (overrides.maxTurns !== undefined)
|
|
137
|
+
merged["maxTurns"] = Number(overrides.maxTurns);
|
|
138
|
+
if (overrides.timeout !== undefined)
|
|
139
|
+
merged["timeout"] = Number(overrides.timeout);
|
|
140
|
+
if (overrides.userConfig)
|
|
141
|
+
merged["userConfig"] = true;
|
|
142
|
+
if (overrides.failOnTheft)
|
|
143
|
+
merged["failOnTheft"] = true;
|
|
107
144
|
return validateConfig(merged, configPath);
|
|
108
145
|
}
|
package/dist/context.d.ts
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
/** `skill-probe context` — activation-rate-BY-CONTEXT. The audit measures every skill co-loaded
|
|
2
|
+
* (the real, hard condition). This command additionally measures each skill in ISOLATION (only that
|
|
3
|
+
* skill present) and compares the two: it catches the "fires fine alone, fails under load" case that
|
|
4
|
+
* a single aggregate rate hides. The drop is tested with Fisher's exact test, so interference is
|
|
5
|
+
* reported as a real effect (p-value), not eyeballed.
|
|
6
|
+
*
|
|
7
|
+
* Every condition (isolation, co-loaded, each leave-one-out ablation) runs in a sandbox built the
|
|
8
|
+
* same way — see sandbox.ts — so the ONLY difference between conditions is which skills are
|
|
9
|
+
* present. When the runtime reports what it loaded, each condition is checked against what it was
|
|
10
|
+
* meant to contain; a mismatch makes the comparison untrustworthy instead of silently wrong. */
|
|
11
|
+
import { type Config } from "./types.ts";
|
|
2
12
|
import { reliability } from "./stats.ts";
|
|
3
|
-
import { type RunStats } from "./orchestrator.ts";
|
|
13
|
+
import { type Budget, type RunStats } from "./orchestrator.ts";
|
|
14
|
+
import { buildSandbox } from "./sandbox.ts";
|
|
4
15
|
import { type RunManifest } from "./manifest.ts";
|
|
5
|
-
/**
|
|
6
|
-
|
|
7
|
-
export declare function buildContextDir(srcCwd: string, skillNames: string[]): {
|
|
8
|
-
cwd: string;
|
|
9
|
-
cleanup: () => void;
|
|
10
|
-
};
|
|
16
|
+
/** Kept for compatibility: a sandbox holding only the named skills (plus instruction files). */
|
|
17
|
+
export declare const buildContextDir: typeof buildSandbox;
|
|
11
18
|
type Rel = ReturnType<typeof reliability>;
|
|
12
19
|
interface Condition {
|
|
13
20
|
stats: RunStats;
|
|
@@ -25,6 +32,8 @@ export interface AblationResult {
|
|
|
25
32
|
fisherP: number;
|
|
26
33
|
fisherPAdj: number;
|
|
27
34
|
untrustworthy: boolean;
|
|
35
|
+
/** why the run isn't a valid comparison, when the runtime loaded the wrong skill set */
|
|
36
|
+
loadIssue?: string;
|
|
28
37
|
culprit: boolean;
|
|
29
38
|
}
|
|
30
39
|
export interface ContextCaseResult {
|
|
@@ -38,8 +47,14 @@ export interface ContextCaseResult {
|
|
|
38
47
|
* test per case is a FAMILY, and raw p<0.05 over a 40-case library expects ~2 false flags by
|
|
39
48
|
* chance alone. Interference is decided on THIS value. (1 for untrustworthy cases.) */
|
|
40
49
|
fisherPAdj: number;
|
|
41
|
-
/** infra failures dominated one side
|
|
50
|
+
/** infra failures dominated one side, or a condition loaded the wrong skills — the comparison is
|
|
51
|
+
* not trustworthy (NOT a behavioral result) */
|
|
42
52
|
untrustworthy: boolean;
|
|
53
|
+
/** why a condition isn't valid, when the runtime loaded the wrong skill set */
|
|
54
|
+
loadIssue?: string;
|
|
55
|
+
/** skills OUTSIDE the library (e.g. runtime built-ins) that fired in the co-loaded run — they
|
|
56
|
+
* can't be ablated, only avoided (narrow or rename the expected skill) */
|
|
57
|
+
externalThieves?: string[];
|
|
43
58
|
/** fires reliably alone but drops significantly (BH-corrected) when the library is co-loaded */
|
|
44
59
|
interference: boolean;
|
|
45
60
|
/** leave-one-out runs (only with { ablate: true }, only for interference cases). Empty array =
|
|
@@ -59,6 +74,8 @@ export interface ContextAuditResult {
|
|
|
59
74
|
exitCode: 0 | 1 | 2;
|
|
60
75
|
/** who/what/when of this run — makes reports citable and comparable */
|
|
61
76
|
manifest: RunManifest;
|
|
77
|
+
/** run-level caveats (built-in name clashes, budget cut-offs) */
|
|
78
|
+
warnings: string[];
|
|
62
79
|
}
|
|
63
80
|
export interface ContextProgress {
|
|
64
81
|
caseIndex: number;
|
|
@@ -77,6 +94,9 @@ export interface ContextOpts {
|
|
|
77
94
|
/** max suspect siblings to ablate per flagged case, ranked by probes stolen (default 3) */
|
|
78
95
|
suspects?: number;
|
|
79
96
|
onProgress?: (e: ContextProgress) => void;
|
|
97
|
+
/** cases (and ablation runs) probed in parallel (default 1) */
|
|
98
|
+
concurrency?: number;
|
|
99
|
+
budget?: Budget;
|
|
80
100
|
}
|
|
81
101
|
export declare function runContextAudit(cfg: Config, optsOrProgress?: ContextOpts | ((e: ContextProgress) => void)): Promise<ContextAuditResult>;
|
|
82
102
|
export {};
|