@dzhechkov/harness-core 0.8.6 → 0.8.11
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/.dz-manifest.json +361 -97
- package/README.md +48 -1
- package/dist/amendment-trace.d.ts.map +1 -1
- package/dist/amendment-trace.js +12 -1
- package/dist/amendment-trace.js.map +1 -1
- package/dist/codex-invoke.d.ts +73 -0
- package/dist/codex-invoke.d.ts.map +1 -0
- package/dist/codex-invoke.js +80 -0
- package/dist/codex-invoke.js.map +1 -0
- package/dist/compounding.d.ts +54 -0
- package/dist/compounding.d.ts.map +1 -1
- package/dist/compounding.js +221 -1
- package/dist/compounding.js.map +1 -1
- package/dist/discrimination-gate.d.ts +63 -3
- package/dist/discrimination-gate.d.ts.map +1 -1
- package/dist/discrimination-gate.js +113 -16
- package/dist/discrimination-gate.js.map +1 -1
- package/dist/eta.d.ts +92 -0
- package/dist/eta.d.ts.map +1 -0
- package/dist/eta.js +488 -0
- package/dist/eta.js.map +1 -0
- package/dist/event-chain.d.ts +30 -0
- package/dist/event-chain.d.ts.map +1 -1
- package/dist/event-chain.js +24 -0
- package/dist/event-chain.js.map +1 -1
- package/dist/feature-adr-checkpoints.js +1 -1
- package/dist/feature-adr-decision-recall.d.ts +167 -0
- package/dist/feature-adr-decision-recall.d.ts.map +1 -0
- package/dist/feature-adr-decision-recall.js +519 -0
- package/dist/feature-adr-decision-recall.js.map +1 -0
- package/dist/feature-adr-landing.d.ts +37 -0
- package/dist/feature-adr-landing.d.ts.map +1 -0
- package/dist/feature-adr-landing.js +59 -0
- package/dist/feature-adr-landing.js.map +1 -0
- package/dist/feature-adr-routing.d.ts +2 -2
- package/dist/feature-adr-routing.d.ts.map +1 -1
- package/dist/feature-adr-routing.js +7 -11
- package/dist/feature-adr-routing.js.map +1 -1
- package/dist/guard-promotion.d.ts +41 -0
- package/dist/guard-promotion.d.ts.map +1 -1
- package/dist/guard-promotion.js +218 -4
- package/dist/guard-promotion.js.map +1 -1
- package/dist/guard-volume.d.ts +108 -0
- package/dist/guard-volume.d.ts.map +1 -0
- package/dist/guard-volume.js +536 -0
- package/dist/guard-volume.js.map +1 -0
- package/dist/guard.d.ts +17 -0
- package/dist/guard.d.ts.map +1 -1
- package/dist/guard.js +92 -4
- package/dist/guard.js.map +1 -1
- package/dist/index.d.ts +24 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -5
- package/dist/index.js.map +1 -1
- package/dist/integration-apply.d.ts +25 -0
- package/dist/integration-apply.d.ts.map +1 -0
- package/dist/integration-apply.js +299 -0
- package/dist/integration-apply.js.map +1 -0
- package/dist/integration-evidence.d.ts +46 -0
- package/dist/integration-evidence.d.ts.map +1 -0
- package/dist/integration-evidence.js +44 -0
- package/dist/integration-evidence.js.map +1 -0
- package/dist/integration-probe-worker.d.ts +22 -0
- package/dist/integration-probe-worker.d.ts.map +1 -0
- package/dist/integration-probe-worker.js +334 -0
- package/dist/integration-probe-worker.js.map +1 -0
- package/dist/integrations-verify.d.ts +60 -0
- package/dist/integrations-verify.d.ts.map +1 -0
- package/dist/integrations-verify.js +194 -0
- package/dist/integrations-verify.js.map +1 -0
- package/dist/lesson-generalization.d.ts +29 -0
- package/dist/lesson-generalization.d.ts.map +1 -0
- package/dist/lesson-generalization.js +84 -0
- package/dist/lesson-generalization.js.map +1 -0
- package/dist/mutation-gate.d.ts +39 -36
- package/dist/mutation-gate.d.ts.map +1 -1
- package/dist/mutation-gate.js +111 -5
- package/dist/mutation-gate.js.map +1 -1
- package/dist/operations.d.ts +27 -0
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations.js +186 -7
- package/dist/operations.js.map +1 -1
- package/dist/patterns.d.ts +27 -1
- package/dist/patterns.d.ts.map +1 -1
- package/dist/patterns.js +211 -45
- package/dist/patterns.js.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +27 -5
- package/dist/plugin.js.map +1 -1
- package/dist/recommend.d.ts +4 -5
- package/dist/recommend.d.ts.map +1 -1
- package/dist/recommend.js +110 -45
- package/dist/recommend.js.map +1 -1
- package/dist/registry.d.ts +32 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +165 -9
- package/dist/registry.js.map +1 -1
- package/dist/run-records.d.ts +3 -0
- package/dist/run-records.d.ts.map +1 -1
- package/dist/run-records.js +18 -0
- package/dist/run-records.js.map +1 -1
- package/dist/score.d.ts +95 -0
- package/dist/score.d.ts.map +1 -1
- package/dist/score.js +274 -2
- package/dist/score.js.map +1 -1
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +20 -17
- package/dist/setup.js.map +1 -1
- package/dist/skill-selection.d.ts +72 -0
- package/dist/skill-selection.d.ts.map +1 -0
- package/dist/skill-selection.js +76 -0
- package/dist/skill-selection.js.map +1 -0
- package/dist/stem.d.ts +12 -0
- package/dist/stem.d.ts.map +1 -0
- package/dist/stem.js +89 -0
- package/dist/stem.js.map +1 -0
- package/dist/target-integrations.d.ts +65 -0
- package/dist/target-integrations.d.ts.map +1 -0
- package/dist/target-integrations.js +152 -0
- package/dist/target-integrations.js.map +1 -0
- package/dist/telemetry-vocabulary.d.ts +7 -0
- package/dist/telemetry-vocabulary.d.ts.map +1 -1
- package/dist/telemetry-vocabulary.js +29 -0
- package/dist/telemetry-vocabulary.js.map +1 -1
- package/dist/vector-tier.d.ts +6 -1
- package/dist/vector-tier.d.ts.map +1 -1
- package/dist/vector-tier.js +32 -7
- package/dist/vector-tier.js.map +1 -1
- package/package.json +7 -6
- package/sbom.json +772 -112
- package/src/amendment-trace.ts +12 -1
- package/src/codex-invoke.ts +138 -0
- package/src/compounding.ts +300 -1
- package/src/discrimination-gate.ts +183 -19
- package/src/eta.ts +590 -0
- package/src/event-chain.ts +41 -0
- package/src/feature-adr-checkpoints.ts +1 -1
- package/src/feature-adr-decision-recall.ts +652 -0
- package/src/feature-adr-landing.ts +109 -0
- package/src/feature-adr-routing.ts +7 -11
- package/src/guard-promotion.ts +245 -4
- package/src/guard-volume.ts +752 -0
- package/src/guard.ts +110 -4
- package/src/index.ts +73 -6
- package/src/integration-apply.ts +332 -0
- package/src/integration-evidence.ts +89 -0
- package/src/integration-probe-worker.ts +310 -0
- package/src/integration-receipts/claude-code/mcp/2.1.235.json +35 -0
- package/src/integrations-verify.ts +258 -0
- package/src/lesson-generalization.ts +115 -0
- package/src/mutation-gate.ts +165 -5
- package/src/operations.ts +207 -7
- package/src/patterns.ts +252 -43
- package/src/plugin.ts +27 -5
- package/src/recommend.ts +116 -46
- package/src/registry.ts +144 -11
- package/src/run-records.ts +23 -0
- package/src/score.ts +361 -3
- package/src/setup.ts +20 -17
- package/src/skill-selection.ts +111 -0
- package/src/stem.ts +87 -0
- package/src/target-integrations.ts +225 -0
- package/src/telemetry-vocabulary.ts +36 -0
- package/src/vector-tier.ts +44 -14
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/** Bounded, non-executing target registration verification. */
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { accessSync, constants, existsSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { delimiter, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { CLAUDE_MCP_RECEIPT, assessClaudeMcpEvidence } from './integration-evidence.js';
|
|
8
|
+
import { TARGET_INTEGRATION_POLICY } from './target-integrations.js';
|
|
9
|
+
export const INTEGRATION_PROBE_TIMEOUT_MS = 30_000;
|
|
10
|
+
export const INTEGRATION_PROBE_STREAM_MAX_BYTES = 1024 * 1024;
|
|
11
|
+
export const INTEGRATION_PROBE_AGGREGATE_MAX_BYTES = 2 * 1024 * 1024;
|
|
12
|
+
/** Resolve PATH once and reject a project-controlled probe binary before spawning it. */
|
|
13
|
+
export function resolveIntegrationExecutable(command, cwd, pathValue = process.env['PATH'] ?? '') {
|
|
14
|
+
const candidates = isAbsolute(command)
|
|
15
|
+
? [command]
|
|
16
|
+
: command.includes('/') || command.includes('\\')
|
|
17
|
+
? [resolve(cwd, command)]
|
|
18
|
+
: pathValue.split(delimiter).map((dir) => join(dir.length === 0 ? cwd : dir, command));
|
|
19
|
+
for (const candidate of candidates) {
|
|
20
|
+
if (!existsSync(candidate))
|
|
21
|
+
continue;
|
|
22
|
+
try {
|
|
23
|
+
accessSync(candidate, constants.X_OK);
|
|
24
|
+
const actual = realpathSync(candidate);
|
|
25
|
+
const rel = relative(realpathSync(cwd), actual);
|
|
26
|
+
if (rel === '' || (!rel.startsWith(`..${sep}`) && rel !== '..' && !isAbsolute(rel))) {
|
|
27
|
+
return { ok: false, errorCode: 'UNSAFE_EXECUTABLE' };
|
|
28
|
+
}
|
|
29
|
+
return { ok: true, path: actual };
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return { ok: false, errorCode: 'ENOENT' };
|
|
36
|
+
}
|
|
37
|
+
export const defaultIntegrationProcessPort = {
|
|
38
|
+
run(request) {
|
|
39
|
+
const executable = resolveIntegrationExecutable(request.command, request.cwd);
|
|
40
|
+
if (!executable.ok) {
|
|
41
|
+
return { status: null, signal: null, stdout: new Uint8Array(), stderr: new Uint8Array(), errorCode: executable.errorCode };
|
|
42
|
+
}
|
|
43
|
+
const workerPath = fileURLToPath(new URL('./integration-probe-worker.js', import.meta.url));
|
|
44
|
+
const result = spawnSync(process.execPath, [workerPath], {
|
|
45
|
+
input: JSON.stringify({
|
|
46
|
+
...request,
|
|
47
|
+
command: executable.path,
|
|
48
|
+
streamMaxBytes: INTEGRATION_PROBE_STREAM_MAX_BYTES,
|
|
49
|
+
aggregateMaxBytes: INTEGRATION_PROBE_AGGREGATE_MAX_BYTES,
|
|
50
|
+
}),
|
|
51
|
+
cwd: request.cwd,
|
|
52
|
+
timeout: request.timeoutMs + 5_000,
|
|
53
|
+
killSignal: 'SIGKILL',
|
|
54
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
55
|
+
encoding: 'utf8',
|
|
56
|
+
windowsHide: true,
|
|
57
|
+
});
|
|
58
|
+
if (result.error !== undefined || result.status !== 0) {
|
|
59
|
+
return {
|
|
60
|
+
status: result.status,
|
|
61
|
+
signal: result.signal,
|
|
62
|
+
stdout: new Uint8Array(),
|
|
63
|
+
stderr: new Uint8Array(),
|
|
64
|
+
errorCode: result.error?.code ?? 'PROCESS_ERROR',
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
let observation;
|
|
68
|
+
try {
|
|
69
|
+
observation = JSON.parse(result.stdout);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return { status: null, signal: null, stdout: new Uint8Array(), stderr: new Uint8Array(), errorCode: 'PROCESS_ERROR' };
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
status: observation.status,
|
|
76
|
+
signal: observation.signal,
|
|
77
|
+
stdout: Buffer.from(observation.stdoutBase64, 'base64'),
|
|
78
|
+
stderr: Buffer.from(observation.stderrBase64, 'base64'),
|
|
79
|
+
...(observation.errorCode !== undefined ? { errorCode: observation.errorCode } : {}),
|
|
80
|
+
};
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
function decodeBounded(observation) {
|
|
84
|
+
const stdout = Buffer.from(observation.stdout);
|
|
85
|
+
const stderr = Buffer.from(observation.stderr);
|
|
86
|
+
if (stdout.length > INTEGRATION_PROBE_STREAM_MAX_BYTES || stderr.length > INTEGRATION_PROBE_STREAM_MAX_BYTES ||
|
|
87
|
+
stdout.length + stderr.length > INTEGRATION_PROBE_AGGREGATE_MAX_BYTES)
|
|
88
|
+
return 'LIVE_PROBE_FAILED';
|
|
89
|
+
try {
|
|
90
|
+
const decoder = new TextDecoder('utf-8', { fatal: true });
|
|
91
|
+
return { stdout: decoder.decode(stdout), stderr: decoder.decode(stderr) };
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return 'LIVE_PROBE_FAILED';
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function parseClaudeVersion(text) {
|
|
98
|
+
return /(?:Claude Code\s+)?(\d+\.\d+\.\d+)/i.exec(text)?.[1];
|
|
99
|
+
}
|
|
100
|
+
function failed(options, reasonCode, remediation, runtimeVersion) {
|
|
101
|
+
return {
|
|
102
|
+
ok: false,
|
|
103
|
+
target: options.target,
|
|
104
|
+
component: options.component,
|
|
105
|
+
registrations: [],
|
|
106
|
+
reasonCode,
|
|
107
|
+
remediation,
|
|
108
|
+
...(runtimeVersion !== undefined ? { runtimeVersion } : {}),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function parseClaudeProjectRegistration(options, id, observation, runtimeVersion) {
|
|
112
|
+
if (observation.errorCode === 'ETIMEDOUT' || observation.signal === 'SIGKILL') {
|
|
113
|
+
return failed(options, 'LIVE_PROBE_TIMEOUT', `Claude mcp get exceeded ${INTEGRATION_PROBE_TIMEOUT_MS}ms`, runtimeVersion);
|
|
114
|
+
}
|
|
115
|
+
if (observation.errorCode !== undefined) {
|
|
116
|
+
return failed(options, 'LIVE_PROBE_FAILED', `Claude registration probe failed before a trustworthy observation (${observation.errorCode})`, runtimeVersion);
|
|
117
|
+
}
|
|
118
|
+
const text = decodeBounded(observation);
|
|
119
|
+
if (typeof text === 'string')
|
|
120
|
+
return failed(options, text, 'Claude registration output was oversized or invalid UTF-8', runtimeVersion);
|
|
121
|
+
if (observation.status !== 0)
|
|
122
|
+
return failed(options, 'POST_WRITE_REGISTRATION_NOT_OBSERVED', `claude mcp get ${id} did not observe the project registration`, runtimeVersion);
|
|
123
|
+
const output = `${text.stdout}\n${text.stderr}`;
|
|
124
|
+
const escapedId = id.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
125
|
+
// Claude 2.1.235 prints either `Name: <id>` or an exact `<id>:` heading depending on the
|
|
126
|
+
// installation channel. Require one whole attributable line; a prose mention is not evidence.
|
|
127
|
+
if (!new RegExp(`^(?:Name\\s*:\\s*${escapedId}|${escapedId}:)\\s*$`, 'im').test(output) ||
|
|
128
|
+
!/Scope\s*:\s*Project\b/i.test(output)) {
|
|
129
|
+
return failed(options, 'POST_WRITE_REGISTRATION_NOT_OBSERVED', `claude mcp get ${id} did not attribute the exact id to project scope`, runtimeVersion);
|
|
130
|
+
}
|
|
131
|
+
const pending = /pending approval/i.test(output);
|
|
132
|
+
return {
|
|
133
|
+
ok: true,
|
|
134
|
+
target: options.target,
|
|
135
|
+
component: options.component,
|
|
136
|
+
runtimeVersion,
|
|
137
|
+
evidenceVersion: CLAUDE_MCP_RECEIPT.runtimeVersion,
|
|
138
|
+
registrations: [{ id, scope: 'project', registered: true, approval: pending ? 'pending' : 'unknown', ready: !pending }],
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Verify only code-owned native list/get surfaces. Manifest command/URL fields
|
|
143
|
+
* are intentionally absent from this API, so they cannot be executed here.
|
|
144
|
+
*/
|
|
145
|
+
export function verifyTargetIntegration(options) {
|
|
146
|
+
const port = options.processPort ?? defaultIntegrationProcessPort;
|
|
147
|
+
if (options.target !== 'claude-code' || options.component !== 'mcp') {
|
|
148
|
+
const cell = TARGET_INTEGRATION_POLICY[options.target][options.component];
|
|
149
|
+
return failed(options, cell.reasonCode ?? 'NO_QUALIFYING_LIVE_RECEIPT', 'no qualifying exact-version live receipt exists for this target/component');
|
|
150
|
+
}
|
|
151
|
+
const versionObs = port.run({ command: 'claude', args: ['--version'], cwd: options.projectRoot, timeoutMs: INTEGRATION_PROBE_TIMEOUT_MS });
|
|
152
|
+
if (versionObs.errorCode === 'ENOENT')
|
|
153
|
+
return failed(options, 'TARGET_BINARY_UNAVAILABLE', 'install Claude Code and rerun dz integrations-verify');
|
|
154
|
+
if (versionObs.errorCode === 'ETIMEDOUT' || versionObs.signal === 'SIGKILL')
|
|
155
|
+
return failed(options, 'LIVE_PROBE_TIMEOUT', `Claude version probe exceeded ${INTEGRATION_PROBE_TIMEOUT_MS}ms`);
|
|
156
|
+
if (versionObs.errorCode !== undefined)
|
|
157
|
+
return failed(options, 'LIVE_PROBE_FAILED', `Claude version probe failed before a trustworthy observation (${versionObs.errorCode})`);
|
|
158
|
+
const versionText = decodeBounded(versionObs);
|
|
159
|
+
if (typeof versionText === 'string')
|
|
160
|
+
return failed(options, versionText, 'Claude version output was oversized or invalid UTF-8');
|
|
161
|
+
if (versionObs.status !== 0)
|
|
162
|
+
return failed(options, 'LIVE_PROBE_FAILED', 'Claude version probe exited non-zero');
|
|
163
|
+
const runtimeVersion = parseClaudeVersion(`${versionText.stdout}\n${versionText.stderr}`);
|
|
164
|
+
if (runtimeVersion === undefined)
|
|
165
|
+
return failed(options, 'LIVE_PROBE_FAILED', 'Claude version output did not contain an attributable semantic version');
|
|
166
|
+
const evidence = assessClaudeMcpEvidence(runtimeVersion);
|
|
167
|
+
if (!evidence.eligible)
|
|
168
|
+
return failed(options, evidence.reasonCode ?? 'RECEIPT_STALE', `expected Claude Code ${CLAUDE_MCP_RECEIPT.runtimeVersion}; rerun after a matching receipt is recorded`, runtimeVersion);
|
|
169
|
+
if ((options.phase ?? 'preflight') === 'preflight') {
|
|
170
|
+
const probeId = 'dz-registration-probe';
|
|
171
|
+
const probeRoot = mkdtempSync(join(tmpdir(), 'dz-claude-mcp-probe-'));
|
|
172
|
+
try {
|
|
173
|
+
writeFileSync(join(probeRoot, '.mcp.json'), `${JSON.stringify({
|
|
174
|
+
mcpServers: { [probeId]: { type: 'stdio', command: 'node', args: ['-e', 'process.exit(0)'] } },
|
|
175
|
+
}, null, 2)}\n`, { flag: 'wx', mode: 0o600 });
|
|
176
|
+
const observation = port.run({
|
|
177
|
+
command: 'claude', args: ['mcp', 'get', probeId], cwd: probeRoot, timeoutMs: INTEGRATION_PROBE_TIMEOUT_MS,
|
|
178
|
+
});
|
|
179
|
+
const parsed = parseClaudeProjectRegistration(options, probeId, observation, runtimeVersion);
|
|
180
|
+
return parsed.ok ? { ...parsed, registrations: [] } : parsed;
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
rmSync(probeRoot, { recursive: true, force: true });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const id = options.registrationId;
|
|
187
|
+
if (id === undefined)
|
|
188
|
+
return failed(options, 'LIVE_PROBE_FAILED', 'post-write verification requires an exact registration id', runtimeVersion);
|
|
189
|
+
return parseClaudeProjectRegistration(options, id, port.run({ command: 'claude', args: ['mcp', 'get', id], cwd: options.projectRoot, timeoutMs: INTEGRATION_PROBE_TIMEOUT_MS }), runtimeVersion);
|
|
190
|
+
}
|
|
191
|
+
export function runIntegrationsVerify(options) {
|
|
192
|
+
return verifyTargetIntegration(options);
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=integrations-verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrations-verify.js","sourceRoot":"","sources":["../src/integrations-verify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type LessonForm = 'specific' | 'class';
|
|
2
|
+
export type LessonMatchedForm = 'specific' | 'class' | 'both';
|
|
3
|
+
export interface LessonFormsInput {
|
|
4
|
+
readonly specific: string;
|
|
5
|
+
readonly classForm?: string;
|
|
6
|
+
readonly classAdvisory?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface LessonFormRankedHit<T> {
|
|
9
|
+
readonly key: string;
|
|
10
|
+
readonly value: T;
|
|
11
|
+
readonly matchedForm: LessonForm;
|
|
12
|
+
}
|
|
13
|
+
export interface MergedLessonFormHit<T> {
|
|
14
|
+
readonly key: string;
|
|
15
|
+
readonly value: T;
|
|
16
|
+
readonly matchedForm: LessonMatchedForm;
|
|
17
|
+
readonly score: number;
|
|
18
|
+
}
|
|
19
|
+
export declare function normalizeLessonForms(specific: string, classInput?: unknown): LessonFormsInput;
|
|
20
|
+
export declare function validateClassTemplate(specific: string, classTemplate: string): {
|
|
21
|
+
readonly ok: true;
|
|
22
|
+
} | {
|
|
23
|
+
readonly ok: false;
|
|
24
|
+
readonly reason: string;
|
|
25
|
+
};
|
|
26
|
+
export declare function lessonPairIdOf(specific: string, classTemplate: string, ts: string): string;
|
|
27
|
+
export declare function mergeLessonMatchedForms(left: LessonMatchedForm | undefined, right: LessonMatchedForm | undefined): LessonMatchedForm | undefined;
|
|
28
|
+
export declare function mergeLessonFormHits<T>(specificHits: readonly LessonFormRankedHit<T>[], classHits: readonly LessonFormRankedHit<T>[], limit: number): MergedLessonFormHit<T>[];
|
|
29
|
+
//# sourceMappingURL=lesson-generalization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lesson-generalization.d.ts","sourceRoot":"","sources":["../src/lesson-generalization.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC;AAC9C,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,OAAO,GAAG,MAAM,CAAC;AAE9D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,mBAAmB,CAAC,CAAC;IACpC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAClB,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAC;CAClC;AAED,MAAM,WAAW,mBAAmB,CAAC,CAAC;IACpC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAClB,QAAQ,CAAC,WAAW,EAAE,iBAAiB,CAAC;IACxC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAQ7F;AAUD,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,GACpB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAwBzE;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAM1F;AAED,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,iBAAiB,GAAG,SAAS,EACnC,KAAK,EAAE,iBAAiB,GAAG,SAAS,GACnC,iBAAiB,GAAG,SAAS,CAI/B;AAED,wBAAgB,mBAAmB,CAAC,CAAC,EACnC,YAAY,EAAE,SAAS,mBAAmB,CAAC,CAAC,CAAC,EAAE,EAC/C,SAAS,EAAE,SAAS,mBAAmB,CAAC,CAAC,CAAC,EAAE,EAC5C,KAAK,EAAE,MAAM,GACZ,mBAAmB,CAAC,CAAC,CAAC,EAAE,CAsB1B"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
export function normalizeLessonForms(specific, classInput) {
|
|
3
|
+
if (classInput === undefined || (typeof classInput === 'string' && classInput.trim() === '')) {
|
|
4
|
+
return { specific };
|
|
5
|
+
}
|
|
6
|
+
if (typeof classInput !== 'string') {
|
|
7
|
+
return { specific, classAdvisory: 'class form must be text; the specific lesson was kept' };
|
|
8
|
+
}
|
|
9
|
+
return { specific, classForm: classInput.trim() };
|
|
10
|
+
}
|
|
11
|
+
function normalizedText(value) {
|
|
12
|
+
return value.normalize('NFC').toLocaleLowerCase('und').replace(/\s+/gu, ' ').trim();
|
|
13
|
+
}
|
|
14
|
+
function escapedLiteral(value) {
|
|
15
|
+
return normalizedText(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s+/g, '\\s+');
|
|
16
|
+
}
|
|
17
|
+
export function validateClassTemplate(specific, classTemplate) {
|
|
18
|
+
const slot = /:([A-Za-z][A-Za-z0-9_-]*)/g;
|
|
19
|
+
const parts = [];
|
|
20
|
+
let slots = 0;
|
|
21
|
+
let cursor = 0;
|
|
22
|
+
for (const match of classTemplate.matchAll(slot)) {
|
|
23
|
+
const index = match.index ?? 0;
|
|
24
|
+
const literal = classTemplate.slice(cursor, index);
|
|
25
|
+
if (literal.includes(':'))
|
|
26
|
+
return { ok: false, reason: 'invalid :slot name' };
|
|
27
|
+
parts.push(escapedLiteral(literal), '(.+?)');
|
|
28
|
+
slots += 1;
|
|
29
|
+
cursor = index + match[0].length;
|
|
30
|
+
}
|
|
31
|
+
const tail = classTemplate.slice(cursor);
|
|
32
|
+
if (tail.includes(':'))
|
|
33
|
+
return { ok: false, reason: 'invalid :slot name' };
|
|
34
|
+
if (slots === 0)
|
|
35
|
+
return { ok: false, reason: 'class form must contain at least one :slot' };
|
|
36
|
+
parts.push(escapedLiteral(tail));
|
|
37
|
+
try {
|
|
38
|
+
return new RegExp(`^${parts.join('')}$`, 'u').test(normalizedText(specific))
|
|
39
|
+
? { ok: true }
|
|
40
|
+
: { ok: false, reason: 'class form does not syntactically cover the specific lesson' };
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return { ok: false, reason: 'class form contains an invalid template' };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export function lessonPairIdOf(specific, classTemplate, ts) {
|
|
47
|
+
const digest = createHash('sha256')
|
|
48
|
+
.update(`${specific}\u0000${classTemplate}\u0000${ts}`)
|
|
49
|
+
.digest('hex')
|
|
50
|
+
.slice(0, 16);
|
|
51
|
+
return `lesson-pair:${digest}`;
|
|
52
|
+
}
|
|
53
|
+
export function mergeLessonMatchedForms(left, right) {
|
|
54
|
+
if (left === undefined)
|
|
55
|
+
return right;
|
|
56
|
+
if (right === undefined || left === right)
|
|
57
|
+
return left;
|
|
58
|
+
return 'both';
|
|
59
|
+
}
|
|
60
|
+
export function mergeLessonFormHits(specificHits, classHits, limit) {
|
|
61
|
+
const merged = new Map();
|
|
62
|
+
const add = (hit, rank) => {
|
|
63
|
+
const current = merged.get(hit.key) ?? { value: hit.value, specific: false, classForm: false, score: 0 };
|
|
64
|
+
if (hit.matchedForm === 'specific')
|
|
65
|
+
current.specific = true;
|
|
66
|
+
else
|
|
67
|
+
current.classForm = true;
|
|
68
|
+
current.score += 1 / (60 + rank + 1);
|
|
69
|
+
merged.set(hit.key, current);
|
|
70
|
+
};
|
|
71
|
+
specificHits.forEach(add);
|
|
72
|
+
classHits.forEach(add);
|
|
73
|
+
const safeLimit = Number.isFinite(limit) ? Math.max(0, Math.trunc(limit)) : merged.size;
|
|
74
|
+
return [...merged.entries()]
|
|
75
|
+
.sort((a, b) => b[1].score - a[1].score || a[0].localeCompare(b[0]))
|
|
76
|
+
.slice(0, safeLimit)
|
|
77
|
+
.map(([key, hit]) => ({
|
|
78
|
+
key,
|
|
79
|
+
value: hit.value,
|
|
80
|
+
matchedForm: hit.specific && hit.classForm ? 'both' : hit.classForm ? 'class' : 'specific',
|
|
81
|
+
score: hit.score,
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=lesson-generalization.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lesson-generalization.js","sourceRoot":"","sources":["../src/lesson-generalization.ts"],"names":[],"mappings":"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"}
|
package/dist/mutation-gate.d.ts
CHANGED
|
@@ -77,6 +77,10 @@ export interface MutationObservation {
|
|
|
77
77
|
readonly rebaselineExitCode?: number | null;
|
|
78
78
|
/** named no-exit reason for the restored-tree attribution run, when it produced none. */
|
|
79
79
|
readonly rebaselineFailureReason?: string;
|
|
80
|
+
/** parsed failing files from a RED restored-tree run; absent when no red rebaseline ran. */
|
|
81
|
+
readonly rebaselineAttribution?: BaselineAttribution;
|
|
82
|
+
/** bounded log proving an internal runner failure received at most one retry. */
|
|
83
|
+
readonly internalAttemptLog?: string;
|
|
80
84
|
}
|
|
81
85
|
export interface MutationEntryResult {
|
|
82
86
|
readonly id: string;
|
|
@@ -102,6 +106,26 @@ export interface MutationEntryResult {
|
|
|
102
106
|
/** human sentence for the report line — names the undefended property on a green suite. */
|
|
103
107
|
readonly detail: string;
|
|
104
108
|
}
|
|
109
|
+
export type InternalRunnerAttemptOutcome = 'completed' | 'runner-internal-error';
|
|
110
|
+
export interface InternalRunnerAttempt {
|
|
111
|
+
readonly attempt: 1 | 2;
|
|
112
|
+
readonly outcome: InternalRunnerAttemptOutcome;
|
|
113
|
+
readonly detail: string;
|
|
114
|
+
}
|
|
115
|
+
export interface InternalRunnerRetryResult<T> {
|
|
116
|
+
/** The completed attempt's value. null means both attempts threw internally. */
|
|
117
|
+
readonly value: T | null;
|
|
118
|
+
readonly attempts: readonly InternalRunnerAttempt[];
|
|
119
|
+
/** Closed by construction: a runner receives either zero retries or exactly one. */
|
|
120
|
+
readonly internalRetries: 0 | 1;
|
|
121
|
+
/** Named reason consumed by the existing no-exit → INCONCLUSIVE arm. */
|
|
122
|
+
readonly failureReason?: `runner-internal-error: ${string}`;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Run one internal runner invocation. Only a THROWN internal error is retried; normal green/red
|
|
126
|
+
* observations and ordinary no-exit observations are values and therefore never retried.
|
|
127
|
+
*/
|
|
128
|
+
export declare function runWithOneInternalRetry<T>(runner: () => T): InternalRunnerRetryResult<T>;
|
|
105
129
|
export interface ParsedRegistry {
|
|
106
130
|
readonly registry: MutationRegistry | null;
|
|
107
131
|
/** every defect found — ANY error makes the registry unusable (a half-valid registry that runs
|
|
@@ -215,50 +239,29 @@ export interface RunFailureClassification {
|
|
|
215
239
|
* and never silently passes on output it cannot read.
|
|
216
240
|
*/
|
|
217
241
|
export declare function classifyRunFailure(rawOutput: string): RunFailureClassification;
|
|
242
|
+
export type BaselineAttributionSource = 'node-test' | 'vitest' | 'unparseable';
|
|
243
|
+
export interface BaselineAttribution {
|
|
244
|
+
readonly parsedFrom: BaselineAttributionSource;
|
|
245
|
+
/** Package-relative failing paths, in first-seen order. */
|
|
246
|
+
readonly failingFiles: readonly string[];
|
|
247
|
+
/** Failing paths that match a registry file exactly (or by package-relative suffix). */
|
|
248
|
+
readonly covered: readonly string[];
|
|
249
|
+
/** Failing paths with no matching registry file. */
|
|
250
|
+
readonly extraneous: readonly string[];
|
|
251
|
+
}
|
|
252
|
+
/** Parse the failing FILE paths already exposed by supported node --test and vitest shapes. */
|
|
253
|
+
export declare function attributeBaselineRedness(rawOutput: string, registryFiles: readonly string[]): BaselineAttribution;
|
|
254
|
+
export type BaselineFailureReason = 'runner-internal-error' | 'runner-no-exit' | 'extraneous-red-in-allowlist' | 'baseline-red-covered-files' | 'baseline-red-files-unparseable';
|
|
218
255
|
export interface BaselineResult {
|
|
219
256
|
readonly ok: boolean;
|
|
220
257
|
readonly detail: string;
|
|
258
|
+
readonly reason?: BaselineFailureReason;
|
|
221
259
|
}
|
|
222
260
|
/**
|
|
223
261
|
* A RED baseline in the scratch copy is a SETUP error, never a mutation result: every subsequent
|
|
224
262
|
* "red under mutation" would be noise, and every "green" a lie about an unrunnable copy.
|
|
225
263
|
*/
|
|
226
|
-
export declare function classifyBaseline(exitCode: number | null, runFailureReason?: string): BaselineResult;
|
|
227
|
-
/**
|
|
228
|
-
* Classify one entry's observation.
|
|
229
|
-
*
|
|
230
|
-
* PRECEDENCE LATTICE (checked top to bottom; each verdict is justified by what it invalidates
|
|
231
|
-
* BELOW it — a verdict may only outrank another when it makes that other's evidence meaningless):
|
|
232
|
-
* 1. NOT_APPLIED — nothing was mutated, so no run observation means anything;
|
|
233
|
-
* 2. MUTATION_UNPARSEABLE — the mutated file is not even syntactically a module; a non-parsing
|
|
234
|
-
* file never RUNS, so no load- or count-signal can exist (which is
|
|
235
|
-
* why it sits above MUTATION_LOAD_FATAL);
|
|
236
|
-
* 3. MUTATION_LOAD_FATAL — the run happened, but its own output says a test FILE died at
|
|
237
|
-
* load: the redness is structural and every count from that same run
|
|
238
|
-
* is non-attributable — so it outranks every count-based verdict AND
|
|
239
|
-
* the flaky-rebaseline check (a structurally-broken run needs no
|
|
240
|
-
* attribution analysis);
|
|
241
|
-
* 4. INCONCLUSIVE (no exit) — the run produced nothing to classify at all;
|
|
242
|
-
* 5. RECEIPT_MISMATCH — the harness declared its own receipt contract broken, which
|
|
243
|
-
* invalidates BOTH the green reading and every count-based reading
|
|
244
|
-
* of the same run;
|
|
245
|
-
* 6. UNDEFENDED (exit 0) — the disjoint GREEN arm: mutually exclusive with every red-based
|
|
246
|
-
* verdict below;
|
|
247
|
-
* 7. INCONCLUSIVE (unrecognised output) — red, but the shape is unreadable: counts parsed out of
|
|
248
|
-
* unrecognised output must not reach BELOW_MIN/OVER_FAILING/PROVEN;
|
|
249
|
-
* 8. INCONCLUSIVE (flaky rebaseline) — red, readable, but not attributable;
|
|
250
|
-
* 9. BELOW_MIN → 10. OVER_FAILING — reliable-count contract checks, both failing;
|
|
251
|
-
* 11. PROVEN — applied, red, behavioural, attributable, within bounds.
|
|
252
|
-
*
|
|
253
|
-
* THE DROP DECISION (SPEC §Reporting, decided here + justified): `failing < observed` but still
|
|
254
|
-
* `>= minFailing` is a LOUD WARNING, not a failure. Two reasons, both load-bearing:
|
|
255
|
-
* • the count is a BEST-EFFORT secondary parsed from runner output — making it verdict-deciding
|
|
256
|
-
* would let a count-parse failure flip red/green, which the design decisions forbid;
|
|
257
|
-
* • `observed` is a historical measurement, `minFailing` is the entry's declared CONTRACT. A
|
|
258
|
-
* contract violation fails (BELOW_MIN — only ever on a reliable count); history drifting down
|
|
259
|
-
* while the contract still holds is the early warning, reported loudly so a human re-pins
|
|
260
|
-
* `observed` or investigates — silently normalising it would erase the signal.
|
|
261
|
-
*/
|
|
264
|
+
export declare function classifyBaseline(exitCode: number | null, runFailureReason?: string, attribution?: BaselineAttribution): BaselineResult;
|
|
262
265
|
export declare function classifyMutationOutcome(obs: MutationObservation): MutationEntryResult;
|
|
263
266
|
/** Exit contract: 0 all proven · 1 any entry failed (or red baseline) · (2 = usage/setup, CLI-side). */
|
|
264
267
|
export declare function mutationGateExitCode(results: readonly MutationEntryResult[], baselineOk: boolean): number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mutation-gate.d.ts","sourceRoot":"","sources":["../src/mutation-gate.ts"],"names":[],"mappings":"AAsBA,oFAAoF;AACpF,MAAM,WAAW,qBAAqB;IACpC,wDAAwD;IACxD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,2FAA2F;IAC3F,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,iEAAiE;IACjE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,QAAQ,CAAC,QAAQ,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACvE,iEAAiE;IACjE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,oFAAoF;IACpF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC/B,mFAAmF;IACnF,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,6EAA6E;IAC7E,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAC5C,QAAQ,CAAC,OAAO,EAAE,SAAS,qBAAqB,EAAE,CAAC;CACpD;AAED,MAAM,MAAM,eAAe,GACvB,QAAQ,GACR,YAAY,GACZ,kBAAkB,GAClB,aAAa,GACb,WAAW,GACX,sBAAsB,GACtB,qBAAqB,GACrB,cAAc,GACd,cAAc,CAAC;AAEnB,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,qBAAqB,CAAC;IACtC,uFAAuF;IACvF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,mGAAmG;IACnG,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,uFAAuF;IACvF,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;;;;;OASG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC;;;;;OAKG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC;;;;OAIG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,gGAAgG;IAChG,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,yFAAyF;IACzF,QAAQ,CAAC,uBAAuB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"mutation-gate.d.ts","sourceRoot":"","sources":["../src/mutation-gate.ts"],"names":[],"mappings":"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"}
|
package/dist/mutation-gate.js
CHANGED
|
@@ -19,6 +19,39 @@
|
|
|
19
19
|
// 3. Never mutate the working tree (enforced by the executor: scratch copy only).
|
|
20
20
|
// 4. The gate has its own discrimination proof (the deliberately-undefended fixture in
|
|
21
21
|
// harness-cli/test/fixtures/mutation-gate-undefended — excluded from any real registry).
|
|
22
|
+
function internalRunnerErrorHead(error) {
|
|
23
|
+
const raw = error instanceof Error ? error.message : String(error);
|
|
24
|
+
const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() || 'unknown internal runner error';
|
|
25
|
+
return Array.from(firstLine).slice(0, 160).join('');
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Run one internal runner invocation. Only a THROWN internal error is retried; normal green/red
|
|
29
|
+
* observations and ordinary no-exit observations are values and therefore never retried.
|
|
30
|
+
*/
|
|
31
|
+
export function runWithOneInternalRetry(runner) {
|
|
32
|
+
const attempts = [];
|
|
33
|
+
for (const attempt of [1, 2]) {
|
|
34
|
+
try {
|
|
35
|
+
const value = runner();
|
|
36
|
+
attempts.push({ attempt, outcome: 'completed', detail: `attempt ${attempt}: completed` });
|
|
37
|
+
return { value, attempts, internalRetries: attempt === 1 ? 0 : 1 };
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
const head = internalRunnerErrorHead(error);
|
|
41
|
+
attempts.push({
|
|
42
|
+
attempt,
|
|
43
|
+
outcome: 'runner-internal-error',
|
|
44
|
+
detail: `attempt ${attempt}: runner-internal-error: ${head}`,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
value: null,
|
|
50
|
+
attempts,
|
|
51
|
+
internalRetries: 1,
|
|
52
|
+
failureReason: 'runner-internal-error: persistent after 2/2 attempts',
|
|
53
|
+
};
|
|
54
|
+
}
|
|
22
55
|
// ── Registry parsing — declarative DATA, validated loudly ─────────────────────────────────────
|
|
23
56
|
const SAFE_ID = /^[a-z0-9][a-z0-9-]{0,79}$/;
|
|
24
57
|
/** same shape as discrimination-gate's path rule: package-relative, no traversal, no metacharacters. */
|
|
@@ -355,17 +388,81 @@ export function classifyRunFailure(rawOutput) {
|
|
|
355
388
|
evidence: 'output matches neither node --test (TAP + # duration_ms) nor vitest (RUN v / Test Files) — no classifier for this runner; structural-vs-behavioural redness cannot be told apart, so the verdict may not be PROVEN',
|
|
356
389
|
};
|
|
357
390
|
}
|
|
391
|
+
function normaliseReportedFile(raw) {
|
|
392
|
+
let value = raw.trim().replace(/^['"]|['"]$/g, '').replace(/:\d+(?::\d+)?$/, '');
|
|
393
|
+
value = value.replace(/\\/g, '/');
|
|
394
|
+
const testSegment = value.lastIndexOf('/test/');
|
|
395
|
+
if (testSegment >= 0)
|
|
396
|
+
value = value.slice(testSegment + 1);
|
|
397
|
+
if (!value.includes('/') || !/\.(?:test|spec)\.[cm]?[jt]sx?$/.test(value))
|
|
398
|
+
return null;
|
|
399
|
+
return value;
|
|
400
|
+
}
|
|
401
|
+
/** Parse the failing FILE paths already exposed by supported node --test and vitest shapes. */
|
|
402
|
+
export function attributeBaselineRedness(rawOutput, registryFiles) {
|
|
403
|
+
const output = stripSgr(rawOutput);
|
|
404
|
+
const files = [];
|
|
405
|
+
const add = (raw) => {
|
|
406
|
+
const file = normaliseReportedFile(raw);
|
|
407
|
+
if (file !== null && !files.includes(file))
|
|
408
|
+
files.push(file);
|
|
409
|
+
};
|
|
410
|
+
const vitestMatches = [...output.matchAll(/^\s*FAIL\s+(\S+)/gm)];
|
|
411
|
+
for (const match of vitestMatches)
|
|
412
|
+
add(match[1] ?? '');
|
|
413
|
+
const tapMatches = [...output.matchAll(/^not ok \d+\s+-\s+(.+)$/gm)];
|
|
414
|
+
for (const match of tapMatches)
|
|
415
|
+
add(match[1] ?? '');
|
|
416
|
+
for (const match of output.matchAll(/^\s*location:\s*['"]([^'"]+)['"]\s*$/gm)) {
|
|
417
|
+
add(match[1] ?? '');
|
|
418
|
+
}
|
|
419
|
+
const parsedFrom = files.length === 0
|
|
420
|
+
? 'unparseable'
|
|
421
|
+
: vitestMatches.length > 0 ? 'vitest' : 'node-test';
|
|
422
|
+
if (parsedFrom === 'unparseable') {
|
|
423
|
+
return { parsedFrom, failingFiles: [], covered: [], extraneous: [] };
|
|
424
|
+
}
|
|
425
|
+
const normalisedRegistry = registryFiles
|
|
426
|
+
.map((file) => normaliseReportedFile(file) ?? file.replace(/\\/g, '/'));
|
|
427
|
+
const covered = files.filter((file) => normalisedRegistry.some((registryFile) => file === registryFile || file.endsWith(`/${registryFile}`) || registryFile.endsWith(`/${file}`)));
|
|
428
|
+
const extraneous = files.filter((file) => !covered.includes(file));
|
|
429
|
+
return { parsedFrom, failingFiles: files, covered, extraneous };
|
|
430
|
+
}
|
|
358
431
|
/**
|
|
359
432
|
* A RED baseline in the scratch copy is a SETUP error, never a mutation result: every subsequent
|
|
360
433
|
* "red under mutation" would be noise, and every "green" a lie about an unrunnable copy.
|
|
361
434
|
*/
|
|
362
|
-
export function classifyBaseline(exitCode, runFailureReason) {
|
|
435
|
+
export function classifyBaseline(exitCode, runFailureReason, attribution) {
|
|
363
436
|
if (exitCode === 0)
|
|
364
437
|
return { ok: true, detail: 'baseline suite green in the scratch copy' };
|
|
365
438
|
if (exitCode === null) {
|
|
366
|
-
|
|
439
|
+
const internal = runFailureReason?.startsWith('runner-internal-error:') === true;
|
|
440
|
+
return {
|
|
441
|
+
ok: false,
|
|
442
|
+
reason: internal ? 'runner-internal-error' : 'runner-no-exit',
|
|
443
|
+
detail: `baseline INCONCLUSIVE — suite produced no exit code (${runFailureReason ?? 'unknown timeout/spawn failure'}) — the copy is not runnable; do not read this as a mutation result`,
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
if (attribution === undefined || attribution.parsedFrom === 'unparseable') {
|
|
447
|
+
return {
|
|
448
|
+
ok: false,
|
|
449
|
+
reason: 'baseline-red-files-unparseable',
|
|
450
|
+
detail: `baseline suite RED (exit ${exitCode}) in the UNMUTATED scratch copy — failing files: unparseable from runner output — the broken copy cannot prove anything`,
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
const failing = `failing files: ${attribution.failingFiles.join(', ')}`;
|
|
454
|
+
if (attribution.extraneous.length > 0 && attribution.covered.length === 0) {
|
|
455
|
+
return {
|
|
456
|
+
ok: false,
|
|
457
|
+
reason: 'extraneous-red-in-allowlist',
|
|
458
|
+
detail: `baseline suite RED (exit ${exitCode}) in the UNMUTATED scratch copy — ${failing} — extraneous red in the testCommand allowlist; the registry entries themselves are not disproven`,
|
|
459
|
+
};
|
|
367
460
|
}
|
|
368
|
-
return {
|
|
461
|
+
return {
|
|
462
|
+
ok: false,
|
|
463
|
+
reason: 'baseline-red-covered-files',
|
|
464
|
+
detail: `baseline suite RED (exit ${exitCode}) in the UNMUTATED scratch copy — ${failing} — broken-copy baseline redness touches registry-covered files; fix the copy before evaluating mutations`,
|
|
465
|
+
};
|
|
369
466
|
}
|
|
370
467
|
// ── Classification (rules 1 + 2, and the drop decision) ───────────────────────────────────────
|
|
371
468
|
/**
|
|
@@ -403,7 +500,7 @@ export function classifyBaseline(exitCode, runFailureReason) {
|
|
|
403
500
|
* while the contract still holds is the early warning, reported loudly so a human re-pins
|
|
404
501
|
* `observed` or investigates — silently normalising it would erase the signal.
|
|
405
502
|
*/
|
|
406
|
-
|
|
503
|
+
function classifyMutationOutcomeWithoutAttemptLog(obs) {
|
|
407
504
|
const e = obs.entry;
|
|
408
505
|
const minFailing = e.minFailing ?? 1;
|
|
409
506
|
const base = {
|
|
@@ -499,12 +596,15 @@ export function classifyMutationOutcome(obs) {
|
|
|
499
596
|
// not come back green, so the suite is flaky and an unrelated neighbour may be what went red.
|
|
500
597
|
// Not attributable ⇒ INCONCLUSIVE (a failure, never a pass).
|
|
501
598
|
if (obs.rebaselineExitCode !== undefined && obs.rebaselineExitCode !== 0) {
|
|
599
|
+
const failing = obs.rebaselineAttribution === undefined || obs.rebaselineAttribution.parsedFrom === 'unparseable'
|
|
600
|
+
? 'failing files: unparseable from runner output'
|
|
601
|
+
: `failing files: ${obs.rebaselineAttribution.failingFiles.join(', ')}`;
|
|
502
602
|
return {
|
|
503
603
|
...base,
|
|
504
604
|
applied: true,
|
|
505
605
|
verdict: 'INCONCLUSIVE',
|
|
506
606
|
drop: false,
|
|
507
|
-
detail: `suite red under the mutation BUT the restored baseline did not reproduce green (${obs.rebaselineExitCode === null ? `no exit code: ${obs.rebaselineFailureReason ?? 'unknown timeout / spawn failure'}` : `exit ${obs.rebaselineExitCode}`}) —
|
|
607
|
+
detail: `suite red under the mutation BUT the restored baseline did not reproduce green (${obs.rebaselineExitCode === null ? `no exit code: ${obs.rebaselineFailureReason ?? 'unknown timeout / spawn failure'}` : `exit ${obs.rebaselineExitCode}`}) — mutation did not revert / flaky restored-tree route; ${failing}; the redness is not attributable to the protection`,
|
|
508
608
|
};
|
|
509
609
|
}
|
|
510
610
|
if (obs.failingCount !== null && obs.failingCount < minFailing) {
|
|
@@ -540,6 +640,12 @@ export function classifyMutationOutcome(obs) {
|
|
|
540
640
|
: `suite red under the mutation (${obs.failingCount === null ? 'failing count unavailable — exit code is the verdict' : `${obs.failingCount} failing`}) — the test discriminates`,
|
|
541
641
|
};
|
|
542
642
|
}
|
|
643
|
+
export function classifyMutationOutcome(obs) {
|
|
644
|
+
const result = classifyMutationOutcomeWithoutAttemptLog(obs);
|
|
645
|
+
if (obs.internalAttemptLog === undefined)
|
|
646
|
+
return result;
|
|
647
|
+
return { ...result, detail: `${result.detail}; ${obs.internalAttemptLog}` };
|
|
648
|
+
}
|
|
543
649
|
/** Verdicts that fail the gate. INCONCLUSIVE and NOT_APPLIED fail (inconclusive ≠ pass). */
|
|
544
650
|
const FAILING_VERDICTS = new Set(['UNDEFENDED', 'RECEIPT_MISMATCH', 'NOT_APPLIED', 'BELOW_MIN', 'MUTATION_UNPARSEABLE', 'MUTATION_LOAD_FATAL', 'OVER_FAILING', 'INCONCLUSIVE']);
|
|
545
651
|
/** Exit contract: 0 all proven · 1 any entry failed (or red baseline) · (2 = usage/setup, CLI-side). */
|