agentic-workflow-manager 8.1.4 → 8.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/commands/preflight/checks.js +70 -1
- package/dist/src/commands/preflight/index.js +6 -5
- package/dist/src/commands/sensors/changed.js +15 -0
- package/dist/src/commands/sensors/compatibility/contract.js +17 -3
- package/dist/src/commands/sensors/compatibility/manifest.js +16 -4
- package/dist/src/commands/sensors/compatibility/timeout.js +21 -0
- package/dist/src/commands/sensors/exec.js +3 -1
- package/dist/src/commands/sensors/index.js +5 -13
- package/dist/src/commands/sensors/init.js +1 -0
- package/dist/src/commands/sensors/prepare.js +136 -0
- package/dist/src/commands/sensors/result.js +140 -0
- package/dist/src/commands/sensors/run.js +56 -335
- package/dist/src/commands/sensors/status.js +93 -21
- package/dist/src/commands/sensors/verdict.js +27 -0
- package/dist/tests/commands/preflight/preflight.test.js +50 -0
- package/dist/tests/commands/sensors/baseline.test.js +14 -0
- package/dist/tests/commands/sensors/changed-windows.test.js +3 -0
- package/dist/tests/commands/sensors/compatibility/contract.test.js +51 -0
- package/dist/tests/commands/sensors/compatibility/manifest.test.js +27 -11
- package/dist/tests/commands/sensors/compatibility/probe.test.js +13 -3
- package/dist/tests/commands/sensors/exec-fixtures.js +1 -1
- package/dist/tests/commands/sensors/exec.test.js +17 -0
- package/dist/tests/commands/sensors/index.test.js +46 -8
- package/dist/tests/commands/sensors/init.test.js +30 -1
- package/dist/tests/commands/sensors/prepare.test.js +88 -0
- package/dist/tests/commands/sensors/router.test.js +15 -1
- package/dist/tests/commands/sensors/run-changed.test.js +4 -4
- package/dist/tests/commands/sensors/run.test.js +29 -0
- package/dist/tests/commands/sensors/status-windows.test.js +1 -1
- package/dist/tests/commands/sensors/status.test.js +52 -20
- package/dist/tests/integration/preflight-json-pipe.e2e.test.js +79 -3
- package/dist/tests/integration/sensor-compatibility.e2e.test.js +40 -1
- package/dist/tests/structural/sensor-documentation-contract.test.js +33 -5
- package/dist/tests/structural/support-matrix-is-current.test.js +25 -0
- package/package.json +1 -1
|
@@ -53,7 +53,7 @@ describe('computeSensorStatus — Windows PATH resolution', () => {
|
|
|
53
53
|
// En win32 el usuario escribe `semgrep` y en disco existe `semgrep.cmd`.
|
|
54
54
|
fs_1.default.writeFileSync(path_1.default.join(pathDir, 'semgrep.cmd'), '@echo off\r\n');
|
|
55
55
|
const result = await (0, status_1.computeSensorStatus)(tmpDir);
|
|
56
|
-
expect(result.overall).toBe('
|
|
56
|
+
expect(result.overall).toBe('READY');
|
|
57
57
|
expect(result.checks.security.ok).toBe(true);
|
|
58
58
|
});
|
|
59
59
|
it('reporta ok:false en win32 cuando el binario no esta en PATH', async () => {
|
|
@@ -7,8 +7,11 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
7
7
|
const path_1 = __importDefault(require("path"));
|
|
8
8
|
const os_1 = __importDefault(require("os"));
|
|
9
9
|
const status_1 = require("../../../src/commands/sensors/status");
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
jest.mock('../../../src/commands/sensors/exec', () => ({
|
|
11
|
+
runCommand: jest.fn(),
|
|
12
|
+
runStructuredCommand: jest.fn(),
|
|
13
|
+
}));
|
|
14
|
+
const { runCommand, runStructuredCommand } = require('../../../src/commands/sensors/exec');
|
|
12
15
|
// `resolveOnPath` resuelve PATH en proceso (ya no invoca un shell — ver
|
|
13
16
|
// core/paths.ts). Por eso estos tests controlan un PATH aislado en vez de
|
|
14
17
|
// mockear `execSync`: ademas de reflejar el mecanismo real, los vuelve
|
|
@@ -43,13 +46,35 @@ describe('computeSensorStatus', () => {
|
|
|
43
46
|
expect(result.overall).toBe('NOT_CONFIGURED');
|
|
44
47
|
expect(result.pack).toBeNull();
|
|
45
48
|
});
|
|
49
|
+
it.each([
|
|
50
|
+
['valid manifest', 'READY', () => {
|
|
51
|
+
installLocalBin('eslint');
|
|
52
|
+
fs_1.default.writeFileSync(path_1.default.join(tmpDir, 'eslint.config.awm.mjs'), 'export default []');
|
|
53
|
+
fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.awm'), { recursive: true });
|
|
54
|
+
fs_1.default.writeFileSync(path_1.default.join(tmpDir, '.awm', 'sensors.json'), JSON.stringify({
|
|
55
|
+
pack: 'js-ts', sensors: { lint: { cmd: 'npx eslint . --config eslint.config.awm.mjs' } },
|
|
56
|
+
}));
|
|
57
|
+
}],
|
|
58
|
+
['missing tool', 'DEGRADED', () => {
|
|
59
|
+
fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.awm'), { recursive: true });
|
|
60
|
+
fs_1.default.writeFileSync(path_1.default.join(tmpDir, '.awm', 'sensors.json'), JSON.stringify({
|
|
61
|
+
pack: 'js-ts', sensors: { lint: { cmd: 'npx eslint .' } },
|
|
62
|
+
}));
|
|
63
|
+
}],
|
|
64
|
+
['absent manifest', 'NOT_CONFIGURED', () => { }],
|
|
65
|
+
])('reports %s as %s without dispatching sensor commands', async (_case, overall, setup) => {
|
|
66
|
+
setup();
|
|
67
|
+
await expect((0, status_1.computeSensorStatus)(tmpDir)).resolves.toMatchObject({ overall });
|
|
68
|
+
expect(runCommand).not.toHaveBeenCalled();
|
|
69
|
+
expect(runStructuredCommand).not.toHaveBeenCalled();
|
|
70
|
+
});
|
|
46
71
|
// Helper: simulate a tool installed locally (node_modules/.bin/<tool>)
|
|
47
72
|
function installLocalBin(tool) {
|
|
48
73
|
const binDir = path_1.default.join(tmpDir, 'node_modules', '.bin');
|
|
49
74
|
fs_1.default.mkdirSync(binDir, { recursive: true });
|
|
50
75
|
fs_1.default.writeFileSync(path_1.default.join(binDir, tool), '');
|
|
51
76
|
}
|
|
52
|
-
it('
|
|
77
|
+
it('reports READY for an operational legacy manifest when its declared npx tool is installed locally', async () => {
|
|
53
78
|
installLocalBin('tsc');
|
|
54
79
|
fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.awm'), { recursive: true });
|
|
55
80
|
fs_1.default.writeFileSync(path_1.default.join(tmpDir, '.awm', 'sensors.json'), JSON.stringify({
|
|
@@ -57,7 +82,7 @@ describe('computeSensorStatus', () => {
|
|
|
57
82
|
sensors: { typecheck: { cmd: 'npx tsc --noEmit', fast: true } }
|
|
58
83
|
}));
|
|
59
84
|
const result = await (0, status_1.computeSensorStatus)(tmpDir);
|
|
60
|
-
expect(result.overall).toBe('
|
|
85
|
+
expect(result.overall).toBe('READY');
|
|
61
86
|
expect(result.pack).toBe('js-ts');
|
|
62
87
|
expect(result.checks.typecheck.ok).toBe(true);
|
|
63
88
|
});
|
|
@@ -84,7 +109,7 @@ describe('computeSensorStatus', () => {
|
|
|
84
109
|
expect(result.checks.lint.ok).toBe(false);
|
|
85
110
|
expect(result.checks.lint.detail).toMatch(/missing config/i);
|
|
86
111
|
});
|
|
87
|
-
it('is
|
|
112
|
+
it('is READY when the declared npx tool and config are present without running a sensor command', async () => {
|
|
88
113
|
installLocalBin('eslint');
|
|
89
114
|
fs_1.default.writeFileSync(path_1.default.join(tmpDir, 'eslint.config.awm.mjs'), 'export default []');
|
|
90
115
|
fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.awm'), { recursive: true });
|
|
@@ -93,7 +118,10 @@ describe('computeSensorStatus', () => {
|
|
|
93
118
|
sensors: { lint: { cmd: 'npx eslint . --config eslint.config.awm.mjs --format json', fast: true } }
|
|
94
119
|
}));
|
|
95
120
|
const result = await (0, status_1.computeSensorStatus)(tmpDir);
|
|
121
|
+
expect(result.overall).toBe('READY');
|
|
96
122
|
expect(result.checks.lint.ok).toBe(true);
|
|
123
|
+
expect(runCommand).not.toHaveBeenCalled();
|
|
124
|
+
expect(runStructuredCommand).not.toHaveBeenCalled();
|
|
97
125
|
});
|
|
98
126
|
it('returns DEGRADED when a binary is missing', async () => {
|
|
99
127
|
fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.awm'), { recursive: true });
|
|
@@ -122,7 +150,7 @@ describe('computeSensorStatus', () => {
|
|
|
122
150
|
}));
|
|
123
151
|
installOnPath('semgrep');
|
|
124
152
|
const result = await (0, status_1.computeSensorStatus)(tmpDir);
|
|
125
|
-
expect(result.overall).toBe('
|
|
153
|
+
expect(result.overall).toBe('READY');
|
|
126
154
|
expect(result.checks.security.ok).toBe(true);
|
|
127
155
|
});
|
|
128
156
|
});
|
|
@@ -164,10 +192,10 @@ describe('computeSensorStatus', () => {
|
|
|
164
192
|
expect(result.checks.mutation.ok).toBe(true);
|
|
165
193
|
expect(result.checks.mutation.detail).toBe('disabled');
|
|
166
194
|
});
|
|
167
|
-
it('
|
|
168
|
-
// The manifest
|
|
169
|
-
//
|
|
170
|
-
//
|
|
195
|
+
it('degrades a v2 manifest whose live static compatibility has drifted', async () => {
|
|
196
|
+
// The manifest's initialization evidence remains durable diagnostic context.
|
|
197
|
+
// Status only checks its declared local executable; it never re-runs a project
|
|
198
|
+
// compatibility probe or calls a sensor command.
|
|
171
199
|
const previousHome = process.env.AWM_HOME;
|
|
172
200
|
const home = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-status-home-'));
|
|
173
201
|
try {
|
|
@@ -178,7 +206,7 @@ describe('computeSensorStatus', () => {
|
|
|
178
206
|
const variant = (id, range) => ({
|
|
179
207
|
id, priority: 10, certifiedRange: range,
|
|
180
208
|
requirements: { tool: 'eslint', toolRange: range, runtime: 'node', runtimeRange: '>=0.0.0' },
|
|
181
|
-
assets: ['eslint.config.awm.mjs'], formatter: 'eslint-llm', probe: { kind: '
|
|
209
|
+
assets: ['eslint.config.awm.mjs'], formatter: 'eslint-llm', probe: { kind: 'package-script-present' },
|
|
182
210
|
command: { executable: 'eslint', resolution: 'node-modules-bin', args: ['.'] },
|
|
183
211
|
});
|
|
184
212
|
fs_1.default.writeFileSync(path_1.default.join(registry, 'sensor-packs', 'js-ts', 'pack.json'), JSON.stringify({
|
|
@@ -188,6 +216,8 @@ describe('computeSensorStatus', () => {
|
|
|
188
216
|
fs_1.default.writeFileSync(path_1.default.join(tmpDir, 'package.json'), JSON.stringify({ devDependencies: { eslint: '^10.0.0' } }));
|
|
189
217
|
fs_1.default.mkdirSync(path_1.default.join(tmpDir, 'node_modules', 'eslint'), { recursive: true });
|
|
190
218
|
fs_1.default.writeFileSync(path_1.default.join(tmpDir, 'node_modules', 'eslint', 'package.json'), JSON.stringify({ version: '10.0.0' }));
|
|
219
|
+
fs_1.default.mkdirSync(path_1.default.join(tmpDir, 'node_modules', '.bin'), { recursive: true });
|
|
220
|
+
fs_1.default.writeFileSync(path_1.default.join(tmpDir, 'node_modules', '.bin', 'eslint'), '');
|
|
191
221
|
fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.awm'), { recursive: true });
|
|
192
222
|
fs_1.default.writeFileSync(path_1.default.join(tmpDir, '.awm', 'sensors.json'), JSON.stringify({
|
|
193
223
|
schemaVersion: 2, pack: 'js-ts', sensors: { lint: {
|
|
@@ -196,15 +226,17 @@ describe('computeSensorStatus', () => {
|
|
|
196
226
|
} },
|
|
197
227
|
}));
|
|
198
228
|
const result = await (0, status_1.computeSensorStatus)(tmpDir);
|
|
199
|
-
expect(result
|
|
200
|
-
expect(result.checks.lint
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const
|
|
206
|
-
expect(
|
|
207
|
-
expect(
|
|
229
|
+
expect(result).toMatchObject({ overall: 'DEGRADED', checks: { lint: { ok: false } } });
|
|
230
|
+
expect(result.checks.lint.detail).toMatch(/drift|eslint-10/i);
|
|
231
|
+
// Once the installed tool matches the initialized variant, a failed
|
|
232
|
+
// static probe is still evidence of degraded readiness — it is not
|
|
233
|
+
// a runtime probe and cannot be waived as merely inconclusive.
|
|
234
|
+
fs_1.default.writeFileSync(path_1.default.join(tmpDir, 'node_modules', 'eslint', 'package.json'), JSON.stringify({ version: '9.0.0' }));
|
|
235
|
+
const staticProbeFailure = await (0, status_1.computeSensorStatus)(tmpDir);
|
|
236
|
+
expect(staticProbeFailure).toMatchObject({ overall: 'DEGRADED', checks: { lint: { ok: false } } });
|
|
237
|
+
expect(staticProbeFailure.checks.lint.detail).toMatch(/probe-not-matched|unverifiable/i);
|
|
238
|
+
expect(runCommand).not.toHaveBeenCalled();
|
|
239
|
+
expect(runStructuredCommand).not.toHaveBeenCalled();
|
|
208
240
|
}
|
|
209
241
|
finally {
|
|
210
242
|
if (previousHome === undefined)
|
|
@@ -7,20 +7,96 @@ const child_process_1 = require("child_process");
|
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const os_1 = __importDefault(require("os"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
10
11
|
const cliRoot = path_1.default.resolve(__dirname, '../..');
|
|
11
12
|
const bin = path_1.default.join(cliRoot, 'dist', 'src', 'index.js');
|
|
12
|
-
test('preserves
|
|
13
|
+
test('preserves actionable no-manifest preflight JSON when verify-sensors is piped', () => {
|
|
13
14
|
const project = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-preflight-pipe-'));
|
|
14
15
|
try {
|
|
15
|
-
const result = (0, child_process_1.spawnSync)(process.execPath, [bin, 'preflight', '--json'], {
|
|
16
|
+
const result = (0, child_process_1.spawnSync)(process.execPath, [bin, 'preflight', '--verify-sensors', '--json'], {
|
|
16
17
|
cwd: project,
|
|
17
18
|
encoding: 'utf8',
|
|
18
19
|
env: { ...process.env, AWM_HOME: path_1.default.join(project, 'awm-home'), AWM_NO_UPDATE_CHECK: '1' },
|
|
19
20
|
});
|
|
20
21
|
expect(result.status).toBe(1);
|
|
21
|
-
expect(JSON.parse(result.stdout)).toMatchObject({
|
|
22
|
+
expect(JSON.parse(result.stdout)).toMatchObject({
|
|
23
|
+
status: 'not_configured',
|
|
24
|
+
checks: expect.arrayContaining([expect.objectContaining({
|
|
25
|
+
id: 'sensors-execution', ok: false,
|
|
26
|
+
detail: 'sensor verdict was not_certified; no sensor established an empirical pass',
|
|
27
|
+
remedy: expect.stringContaining('awm sensors init'),
|
|
28
|
+
})]),
|
|
29
|
+
});
|
|
22
30
|
}
|
|
23
31
|
finally {
|
|
24
32
|
fs_1.default.rmSync(project, { recursive: true, force: true });
|
|
25
33
|
}
|
|
26
34
|
});
|
|
35
|
+
function writeJson(file, value) {
|
|
36
|
+
fs_1.default.mkdirSync(path_1.default.dirname(file), { recursive: true });
|
|
37
|
+
fs_1.default.writeFileSync(file, JSON.stringify(value, null, 2));
|
|
38
|
+
}
|
|
39
|
+
function hashTree(root) {
|
|
40
|
+
const hash = crypto_1.default.createHash('sha256');
|
|
41
|
+
const walk = (dir) => {
|
|
42
|
+
for (const entry of fs_1.default.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
43
|
+
const file = path_1.default.join(dir, entry.name);
|
|
44
|
+
hash.update(path_1.default.relative(root, file));
|
|
45
|
+
if (entry.isDirectory())
|
|
46
|
+
walk(file);
|
|
47
|
+
else if (entry.isFile())
|
|
48
|
+
hash.update(fs_1.default.readFileSync(file));
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
walk(root);
|
|
52
|
+
return hash.digest('hex');
|
|
53
|
+
}
|
|
54
|
+
test('verify-sensors degrades parseably for a v2 sensor that exits 2 without findings and does not write the project', () => {
|
|
55
|
+
const root = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-preflight-verify-'));
|
|
56
|
+
const project = path_1.default.join(root, 'project');
|
|
57
|
+
const awmHome = path_1.default.join(root, 'awm-home');
|
|
58
|
+
const registry = path_1.default.join(awmHome, 'registries', 'baseline');
|
|
59
|
+
try {
|
|
60
|
+
fs_1.default.mkdirSync(project, { recursive: true });
|
|
61
|
+
fs_1.default.writeFileSync(path_1.default.join(project, 'AGENTS.md'), '# context\n', { encoding: 'utf8', flag: 'w' });
|
|
62
|
+
writeJson(path_1.default.join(project, 'package.json'), { name: 'preflight-verify-fixture', private: true });
|
|
63
|
+
writeJson(path_1.default.join(project, 'node_modules', 'fixture-sensor', 'package.json'), { name: 'fixture-sensor', version: '1.0.0' });
|
|
64
|
+
fs_1.default.writeFileSync(path_1.default.join(project, 'fixture-sensor.config.mjs'), 'export default {};\n');
|
|
65
|
+
fs_1.default.writeFileSync(path_1.default.join(project, 'fixture-sensor.mjs'), 'process.exit(2);\n');
|
|
66
|
+
writeJson(path_1.default.join(registry, 'sensor-packs', 'fixture', 'pack.json'), {
|
|
67
|
+
schemaVersion: 2, name: 'fixture', description: 'preflight exit-2 fixture', detects: ['package.json'],
|
|
68
|
+
sensors: { lint: { applicability: { allFiles: ['package.json'] }, variants: [{
|
|
69
|
+
id: 'fixture-v1', priority: 100,
|
|
70
|
+
requirements: { tool: 'fixture-sensor', toolRange: '>=1 <2', runtime: 'node', runtimeRange: '>=20', configFiles: ['fixture-sensor.config.mjs'] },
|
|
71
|
+
certifiedRange: '>=1 <2', command: { executable: 'node', resolution: 'path', args: ['fixture-sensor.mjs'] },
|
|
72
|
+
assets: ['fixture-sensor.config.mjs'], formatter: 'generic', probe: { kind: 'config-present' },
|
|
73
|
+
}] } },
|
|
74
|
+
coverage: { schemaVersion: 1, classes: {
|
|
75
|
+
'fixture-output': {
|
|
76
|
+
description: 'fixture output', detectors: [{ sensor: 'lint' }],
|
|
77
|
+
remedy: { summary: 'run fixture', command: 'awm sensors init --pack fixture' },
|
|
78
|
+
},
|
|
79
|
+
} },
|
|
80
|
+
});
|
|
81
|
+
writeJson(path_1.default.join(awmHome, 'registries.json'), [{ name: 'baseline', remote: 'fixture' }]);
|
|
82
|
+
writeJson(path_1.default.join(project, '.awm', 'sensors.json'), {
|
|
83
|
+
schemaVersion: 2, pack: 'fixture', packSelection: 'explicit', sensors: { lint: {
|
|
84
|
+
enabled: true, fast: true, variantId: 'fixture-v1', command: { executable: 'node', resolution: 'path', args: ['fixture-sensor.mjs'] }, assets: ['fixture-sensor.config.mjs'],
|
|
85
|
+
initializedCompatibility: { state: 'certified', reason: 'fixture', variantId: 'fixture-v1', toolVersion: '1.0.0', runtimeVersion: process.versions.node, certifiedRange: '>=1 <2', evidence: [] },
|
|
86
|
+
} },
|
|
87
|
+
});
|
|
88
|
+
const before = hashTree(project);
|
|
89
|
+
const result = (0, child_process_1.spawnSync)(process.execPath, [bin, 'preflight', '--verify-sensors', '--json', '--cwd', project], {
|
|
90
|
+
cwd: project, encoding: 'utf8', env: { ...process.env, AWM_HOME: awmHome, AWM_NO_UPDATE_CHECK: '1' },
|
|
91
|
+
});
|
|
92
|
+
expect(result.status).toBe(1);
|
|
93
|
+
expect(JSON.parse(result.stdout)).toMatchObject({
|
|
94
|
+
status: 'degraded',
|
|
95
|
+
checks: expect.arrayContaining([expect.objectContaining({ id: 'sensors-execution', ok: false, detail: expect.stringMatching(/lint.*exit 2/i) })]),
|
|
96
|
+
});
|
|
97
|
+
expect(hashTree(project)).toBe(before);
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
fs_1.default.rmSync(root, { recursive: true, force: true });
|
|
101
|
+
}
|
|
102
|
+
});
|
|
@@ -58,6 +58,13 @@ function runCli(fixture, ...args) {
|
|
|
58
58
|
env: { ...process.env, AWM_HOME: fixture.awmHome, AWM_NO_UPDATE_CHECK: '1' },
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
|
+
function runAwm(fixture, ...args) {
|
|
62
|
+
return (0, child_process_1.spawnSync)(process.execPath, [bin, ...args], {
|
|
63
|
+
cwd: fixture.project,
|
|
64
|
+
encoding: 'utf8',
|
|
65
|
+
env: { ...process.env, AWM_HOME: fixture.awmHome, AWM_NO_UPDATE_CHECK: '1' },
|
|
66
|
+
});
|
|
67
|
+
}
|
|
61
68
|
function json(result) {
|
|
62
69
|
expect(result.status).toBe(0);
|
|
63
70
|
if (!(result.stdout ?? '').trim())
|
|
@@ -81,7 +88,7 @@ test.each(['linux', 'darwin', 'win32'])('keeps injected resolver semantics consi
|
|
|
81
88
|
throw new Error('fixture must be a v2 pack');
|
|
82
89
|
const discovered = (0, discovery_1.discoverProjectEvidence)(fixture.project, parsed.pack, { platform: () => platform });
|
|
83
90
|
const probe = await (0, probe_1.runCompatibilityProbe)({ kind: 'version' }, { cwd: fixture.project, toolExecutable: 'eslint' }, async () => ({
|
|
84
|
-
code: 0, signal: null, timedOut: false, overflowed: false, stdout: 'eslint v10.4.1', stderr: '',
|
|
91
|
+
code: 0, signal: null, timedOut: false, overflowed: false, elapsedMs: 0, stdout: 'eslint v10.4.1', stderr: '',
|
|
85
92
|
}));
|
|
86
93
|
const result = (0, resolve_1.resolveSensorCompatibility)(parsed.pack.sensors.lint, { ...discovered, probe }, { pack: 'js-ts', sensor: 'lint' });
|
|
87
94
|
expect(result).toMatchObject({ state: 'certified', variantId: 'eslint-10', toolVersion: '10.4.1' });
|
|
@@ -100,6 +107,38 @@ testWithNoFollow('compiled binary dispatches coverage and emits parseable JSON o
|
|
|
100
107
|
fs_1.default.rmSync(fixture.root, { recursive: true, force: true });
|
|
101
108
|
}
|
|
102
109
|
});
|
|
110
|
+
testWithNoFollow('compiled sensors run returns nonzero while preserving parseable not_certified JSON', () => {
|
|
111
|
+
const fixture = createFixture();
|
|
112
|
+
try {
|
|
113
|
+
fs_1.default.rmSync(path_1.default.join(fixture.project, '.awm', 'sensors.json'));
|
|
114
|
+
const result = runCli(fixture, 'run', '--fast');
|
|
115
|
+
expect(result.status).toBe(1);
|
|
116
|
+
expect(JSON.parse(result.stdout ?? '')).toMatchObject({ sensors: [], overall: 'not_certified' });
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
fs_1.default.rmSync(fixture.root, { recursive: true, force: true });
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
testWithNoFollow('compiled status reports static READY without writing or executing the project sensor (R6, R6.2)', () => {
|
|
123
|
+
const fixture = createFixture();
|
|
124
|
+
try {
|
|
125
|
+
const localBin = path_1.default.join(fixture.project, 'node_modules', '.bin', 'eslint');
|
|
126
|
+
fs_1.default.mkdirSync(path_1.default.dirname(localBin), { recursive: true });
|
|
127
|
+
fs_1.default.writeFileSync(localBin, 'this fixture must never be executed by status\n');
|
|
128
|
+
fs_1.default.copyFileSync(path_1.default.join(fixture.registryRoot, 'sensor-packs', 'js-ts', 'eslint.fixture.mjs'), path_1.default.join(fixture.project, 'eslint.fixture.mjs'));
|
|
129
|
+
const initialized = runCli(fixture, 'init', '--registry-root', fixture.registryRoot, '--pack', 'js-ts', '--no-configure');
|
|
130
|
+
expect(initialized.status).toBe(0);
|
|
131
|
+
const before = hashTree(fixture.project);
|
|
132
|
+
const result = runAwm(fixture, 'sensors', 'status');
|
|
133
|
+
expect(result.status).toBe(0);
|
|
134
|
+
expect(result.stdout).toContain('READY');
|
|
135
|
+
expect(result.stderr).toBe('');
|
|
136
|
+
expect(hashTree(fixture.project)).toBe(before);
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
fs_1.default.rmSync(fixture.root, { recursive: true, force: true });
|
|
140
|
+
}
|
|
141
|
+
});
|
|
103
142
|
testWithNoFollow('legacy coverage stays unverified, init migrates explicitly, and version drift is visible (R7.2, R7.8)', () => {
|
|
104
143
|
const fixture = createFixture();
|
|
105
144
|
try {
|
|
@@ -15,7 +15,25 @@ const sensors_1 = require("../../src/commands/sensors");
|
|
|
15
15
|
const ROOT = path_1.default.resolve(__dirname, '../../..');
|
|
16
16
|
const read = (file) => fs_1.default.readFileSync(path_1.default.join(ROOT, file), 'utf8');
|
|
17
17
|
function documentedJson(files) {
|
|
18
|
-
return files.flatMap((file) =>
|
|
18
|
+
return files.flatMap((file) => {
|
|
19
|
+
const source = read(file);
|
|
20
|
+
return Array.from(source.matchAll(/```json\s*\n([\s\S]*?)```/g), (match) => {
|
|
21
|
+
const sectionStart = source.lastIndexOf('###', match.index ?? 0);
|
|
22
|
+
const leadIn = source.slice(Math.max(0, sectionStart), match.index ?? 0);
|
|
23
|
+
return {
|
|
24
|
+
value: JSON.parse(match[1]),
|
|
25
|
+
intentionallyIncomplete: /intentionally incomplete fragment/.test(leadIn),
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function documentedV2Manifests(files) {
|
|
31
|
+
return documentedJson(files)
|
|
32
|
+
.filter((example) => !example.intentionallyIncomplete
|
|
33
|
+
&& typeof example.value === 'object'
|
|
34
|
+
&& example.value !== null
|
|
35
|
+
&& example.value.schemaVersion === 2)
|
|
36
|
+
.map(example => example.value);
|
|
19
37
|
}
|
|
20
38
|
function helpFor(command) {
|
|
21
39
|
const program = new commander_1.Command().name('awm');
|
|
@@ -38,17 +56,27 @@ describe('R3 canonical sensor documentation', () => {
|
|
|
38
56
|
phrases.forEach((phrase) => expect(text).toContain(phrase));
|
|
39
57
|
});
|
|
40
58
|
test('parses every documented v2 sensor manifest example with the production parser', () => {
|
|
41
|
-
const examples =
|
|
42
|
-
.filter((value) => typeof value === 'object' && value !== null && value.schemaVersion === 2);
|
|
59
|
+
const examples = documentedV2Manifests(['docs/configuration.md', 'docs/cli-reference.md']);
|
|
43
60
|
expect(examples.length).toBeGreaterThan(0);
|
|
44
61
|
examples.forEach((example) => expect(() => (0, manifest_1.parseSensorManifest)(example, 'documented example')).not.toThrow());
|
|
45
62
|
});
|
|
46
63
|
test('keeps documented v2 sensor manifests portable across native platforms', () => {
|
|
47
|
-
const examples =
|
|
48
|
-
.filter((value) => typeof value === 'object' && value !== null && value.schemaVersion === 2);
|
|
64
|
+
const examples = documentedV2Manifests(['docs/configuration.md', 'docs/cli-reference.md']);
|
|
49
65
|
expect(examples.length).toBeGreaterThan(0);
|
|
50
66
|
examples.forEach((example) => expect(example).not.toHaveProperty('registryRoot'));
|
|
51
67
|
});
|
|
68
|
+
test('excludes only an explicitly incomplete timeout fragment, not invalid full v2 manifests', () => {
|
|
69
|
+
const examples = documentedJson(['docs/configuration.md']);
|
|
70
|
+
const incomplete = examples.find(example => example.intentionallyIncomplete);
|
|
71
|
+
const complete = documentedV2Manifests(['docs/configuration.md']);
|
|
72
|
+
expect(incomplete?.value).toMatchObject({ schemaVersion: 2, sensors: { test: { timeout: 600000 } } });
|
|
73
|
+
expect(() => (0, manifest_1.parseSensorManifest)(incomplete?.value, 'incomplete timeout fragment')).toThrow();
|
|
74
|
+
expect(complete).toHaveLength(1);
|
|
75
|
+
expect(() => (0, manifest_1.parseSensorManifest)(complete[0], 'complete documented manifest')).not.toThrow();
|
|
76
|
+
expect(() => (0, manifest_1.parseSensorManifest)({
|
|
77
|
+
schemaVersion: 2, pack: 'js-ts', sensors: { test: { enabled: true, variantId: 'npm-script' } },
|
|
78
|
+
}, 'invalid full manifest')).toThrow();
|
|
79
|
+
});
|
|
52
80
|
test('documents exact Commander flags', () => {
|
|
53
81
|
expect(helpFor('sensors coverage')).toContain('--min <count>');
|
|
54
82
|
expect(helpFor('ledger add')).toContain('--defect-class <id>');
|
|
@@ -24,6 +24,31 @@ const sensor_support_matrix_1 = require("../../scripts/sensor-support-matrix");
|
|
|
24
24
|
const SENSOR_FIXTURE_REGISTRY = path_1.default.join(__dirname, '..', 'fixtures', 'sensor-support-matrix', 'registry');
|
|
25
25
|
const CI_WORKFLOW_PATH = path_1.default.resolve(__dirname, '../../..', '.github', 'workflows', 'ci.yml');
|
|
26
26
|
describe('docs/support-matrix.md refleja el codigo', () => {
|
|
27
|
+
it('documents the bounded, empirical sensor gate contract (R3-R7, R10)', () => {
|
|
28
|
+
const root = path_1.default.resolve(__dirname, '../../..');
|
|
29
|
+
const cliReference = fs_1.default.readFileSync(path_1.default.join(root, 'docs', 'cli-reference.md'), 'utf8');
|
|
30
|
+
const configuration = fs_1.default.readFileSync(path_1.default.join(root, 'docs', 'configuration.md'), 'utf8');
|
|
31
|
+
const acceptance = fs_1.default.readFileSync(path_1.default.join(root, 'docs', 'testing', 'core-acceptance.md'), 'utf8');
|
|
32
|
+
const osMatrix = fs_1.default.readFileSync(path_1.default.join(root, 'docs', 'testing', 'os-matrix.md'), 'utf8');
|
|
33
|
+
for (const expected of [
|
|
34
|
+
'`execution.timeoutMs`', '`timeoutSource`', '`elapsedMs`',
|
|
35
|
+
'`requestedScope`', '`effectiveScope`', '`files`', '`scopeReason`',
|
|
36
|
+
'`project` → `pack` → `fallback`', '10,000 ms', '120,000 ms',
|
|
37
|
+
'`pass`', '`fail`', '`not_certified`', '`skipped`',
|
|
38
|
+
'`awm preflight --verify-sensors`', 'read-only', 'READY', 'not a health or certification claim',
|
|
39
|
+
])
|
|
40
|
+
expect(cliReference + configuration + acceptance).toContain(expected);
|
|
41
|
+
expect(acceptance).toContain('legacy manifest');
|
|
42
|
+
expect(acceptance).toContain('v2 manifest without new fields');
|
|
43
|
+
expect(acceptance).toContain('supported, unsupported, empty, and Git-error');
|
|
44
|
+
expect(acceptance).toContain('project, pack, and fallback');
|
|
45
|
+
expect(cliReference).toContain('`not_certified` with an empty sensor list');
|
|
46
|
+
expect(cliReference).toContain('`awm sensors init`');
|
|
47
|
+
expect(acceptance).toContain('established (for example `not_certified` and an empty list)');
|
|
48
|
+
expect(acceptance).toMatch(/must not fabricate a\s+sensor name, timeout, source, or elapsed time/);
|
|
49
|
+
expect(osMatrix).toContain('Ubuntu, macOS, and native Windows');
|
|
50
|
+
expect(osMatrix).toContain('shell-free');
|
|
51
|
+
});
|
|
27
52
|
it('el bloque generado esta al dia', () => {
|
|
28
53
|
const doc = fs_1.default.readFileSync(support_matrix_1.DOC_PATH, 'utf-8');
|
|
29
54
|
const expected = (0, support_matrix_1.spliceGenerated)(doc, (0, support_matrix_1.renderProviderTables)());
|