agentic-workflow-manager 9.4.0 → 9.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,6 +7,7 @@ const fs_1 = __importDefault(require("fs"));
7
7
  const os_1 = __importDefault(require("os"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const run_1 = require("../../../src/commands/sensors/run");
10
+ const status_1 = require("../../../src/commands/sensors/status");
10
11
  const verdict_1 = require("../../../src/commands/sensors/verdict");
11
12
  function mkTmp() {
12
13
  return fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-sensors-'));
@@ -14,10 +15,18 @@ function mkTmp() {
14
15
  // Define stable mock before jest.mock hoisting
15
16
  const mockRunCommand = jest.fn();
16
17
  const mockRunStructuredCommand = jest.fn();
18
+ const mockResolveSensorProject = jest.fn((startCwd) => {
19
+ const actual = jest.requireActual('../../../src/commands/sensors/project');
20
+ return actual.resolveSensorProject(startCwd);
21
+ });
17
22
  jest.mock('../../../src/commands/sensors/exec', () => ({
18
23
  runCommand: (...args) => mockRunCommand(...args),
19
24
  runStructuredCommand: (...args) => mockRunStructuredCommand(...args),
20
25
  }));
26
+ jest.mock('../../../src/commands/sensors/project', () => {
27
+ const actual = jest.requireActual('../../../src/commands/sensors/project');
28
+ return { ...actual, resolveSensorProject: (startCwd) => mockResolveSensorProject(startCwd) };
29
+ });
21
30
  const { ok, exited, timedOut } = require('./exec-fixtures');
22
31
  const MANIFEST = {
23
32
  pack: 'js-ts',
@@ -39,6 +48,7 @@ describe('runSensors', () => {
39
48
  fs_1.default.writeFileSync(path.join(tmpDir, '.awm', 'sensors.json'), JSON.stringify(MANIFEST));
40
49
  mockRunCommand.mockReset();
41
50
  mockRunStructuredCommand.mockReset();
51
+ mockResolveSensorProject.mockClear();
42
52
  });
43
53
  afterEach(() => { fs_1.default.rmSync(tmpDir, { recursive: true }); });
44
54
  const load = () => require('../../../src/commands/sensors/run');
@@ -282,6 +292,22 @@ describe('runSensors v2 lifecycle contract', () => {
282
292
  expect(result).toEqual(expect.objectContaining({ overall: 'pass' }));
283
293
  expect(mockRunStructuredCommand).toHaveBeenCalledWith(expect.objectContaining({ executable: 'live-eslint' }), expect.any(Object));
284
294
  });
295
+ it('rebinds a missing v2 registryRoot through the unique configured source without mutation', async () => {
296
+ const manifestPath = path_1.default.join(project, '.awm', 'sensors.json');
297
+ const manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf8'));
298
+ delete manifest.registryRoot;
299
+ fs_1.default.writeFileSync(manifestPath, JSON.stringify(manifest));
300
+ const before = fs_1.default.readFileSync(manifestPath, 'utf8');
301
+ const inventoryPath = path_1.default.join(home, 'registries.json');
302
+ const inventoryBefore = fs_1.default.readFileSync(inventoryPath, 'utf8');
303
+ const { runSensors } = require('../../../src/commands/sensors/run');
304
+ const result = await runSensors({ cwd: project, fast: true });
305
+ expect(result).toMatchObject({ overall: 'pass', mode: 'project-sensors', reason: 'legacy-rebound' });
306
+ expect(result.source).toMatchObject({ kind: 'legacy-rebound', registry: 'baseline' });
307
+ expect(mockRunStructuredCommand).toHaveBeenCalledWith(expect.objectContaining({ executable: 'live-eslint' }), expect.any(Object));
308
+ expect(fs_1.default.readFileSync(manifestPath, 'utf8')).toBe(before);
309
+ expect(fs_1.default.readFileSync(inventoryPath, 'utf8')).toBe(inventoryBefore);
310
+ });
285
311
  it('scopes applicability detection and execution to packageRoot for a monorepo manifest', async () => {
286
312
  // Move the fixture project into a subdirectory ("cli") and point the
287
313
  // manifest at it via packageRoot — mirrors a monorepo where the
@@ -388,15 +414,92 @@ describe('runSensors — not_certified + auto-discovery', () => {
388
414
  expect(out.overall).toBe('not_certified');
389
415
  expect(out.sensors).toEqual([]);
390
416
  });
391
- it('discovers .awm/sensors.json in a parent directory (walk-up)', async () => {
417
+ it('uses the same stable schema-unsupported reason as status', async () => {
418
+ tmpDir = mkTmp();
419
+ fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.git'));
420
+ fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.awm'));
421
+ fs_1.default.writeFileSync(path_1.default.join(tmpDir, '.awm', 'sensors.json'), JSON.stringify({ schemaVersion: 99 }));
422
+ const status = await (0, status_1.computeSensorStatus)(tmpDir);
423
+ const run = await (0, run_1.runSensors)({ cwd: tmpDir });
424
+ expect(status).toMatchObject({ mode: 'invalid', reason: 'schema-unsupported' });
425
+ expect(run).toMatchObject({ mode: 'invalid', reason: 'schema-unsupported' });
426
+ });
427
+ it('resolves the same legacy project and execution cwd from root and nested CWD', async () => {
392
428
  tmpDir = mkTmp();
429
+ fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.git'));
393
430
  fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.awm'));
394
431
  fs_1.default.writeFileSync(path_1.default.join(tmpDir, '.awm', 'sensors.json'), JSON.stringify({ pack: 'test', sensors: { noop: { cmd: 'echo ok', fast: true } } }));
395
432
  const nested = path_1.default.join(tmpDir, 'a', 'b');
396
433
  fs_1.default.mkdirSync(nested, { recursive: true });
397
- const out = await (0, run_1.runSensors)({ cwd: nested });
398
- expect(out.overall).toBe('not_certified');
399
- expect(out.sensors.length).toBe(1);
434
+ const rootRun = await (0, run_1.runSensors)({ cwd: tmpDir });
435
+ const nestedRun = await (0, run_1.runSensors)({ cwd: nested });
436
+ expect(nestedRun).toEqual(rootRun);
437
+ expect(rootRun.overall).toBe('not_certified');
438
+ expect(rootRun.sensors).toHaveLength(1);
439
+ expect(mockResolveSensorProject).toHaveBeenCalledWith(tmpDir);
440
+ expect(mockResolveSensorProject).toHaveBeenCalledWith(nested);
441
+ expect(mockRunCommand).toHaveBeenNthCalledWith(1, 'echo ok', expect.objectContaining({ cwd: tmpDir }));
442
+ expect(mockRunCommand).toHaveBeenNthCalledWith(2, 'echo ok', expect.objectContaining({ cwd: tmpDir }));
443
+ });
444
+ it.each([
445
+ ['native-gate', 'Release-blocking CI is authoritative.'],
446
+ ['opt-out', 'This repository intentionally has no local gate.'],
447
+ ])('does not dispatch v3 %s declarations', async (mode, declarationReason) => {
448
+ tmpDir = mkTmp();
449
+ fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.git'));
450
+ fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.awm'), { recursive: true });
451
+ fs_1.default.writeFileSync(path_1.default.join(tmpDir, '.awm', 'sensors.json'), JSON.stringify({ schemaVersion: 3, mode, reason: declarationReason }));
452
+ const out = await (0, run_1.runSensors)({ cwd: tmpDir, all: true });
453
+ expect(out).toMatchObject({
454
+ overall: 'not_certified', mode, reason: `${mode}-declared`,
455
+ declarationReason,
456
+ projectRoot: tmpDir, manifestPath: path_1.default.join(tmpDir, '.awm', 'sensors.json'),
457
+ });
458
+ expect(out.sensors).toEqual([]);
459
+ expect(mockRunCommand).not.toHaveBeenCalled();
460
+ expect(mockRunStructuredCommand).not.toHaveBeenCalled();
461
+ });
462
+ it('reports bounded candidates and a remedy when v2 source resolution is ambiguous', async () => {
463
+ const previousHome = process.env.AWM_HOME;
464
+ const home = mkTmp();
465
+ try {
466
+ process.env.AWM_HOME = home;
467
+ const pack = {
468
+ schemaVersion: 2, name: 'js-ts', description: 'fixture', detects: ['package.json'],
469
+ coverage: { schemaVersion: 1, classes: { lint: { description: 'lint', detectors: [{ sensor: 'lint' }], remedy: { summary: 'fix', command: 'awm sensors init' } } } },
470
+ sensors: { lint: { applicability: { allFiles: ['package.json'] }, variants: [{
471
+ id: 'eslint-10', priority: 1, certifiedRange: '>=10 <11',
472
+ requirements: { tool: 'eslint', toolRange: '>=10 <11', runtime: 'node', runtimeRange: '>=0' },
473
+ assets: [], formatter: 'generic', probe: { kind: 'package-script-present' },
474
+ command: { executable: 'eslint', resolution: 'node-modules-bin', args: ['.'] },
475
+ }] } },
476
+ };
477
+ const registries = ['alpha', 'beta'];
478
+ fs_1.default.writeFileSync(path_1.default.join(home, 'registries.json'), JSON.stringify(registries.map(name => ({ name, remote: `https://example.test/${name}.git` }))));
479
+ for (const name of registries) {
480
+ const packDir = path_1.default.join(home, 'registries', name, 'sensor-packs', 'js-ts');
481
+ fs_1.default.mkdirSync(packDir, { recursive: true });
482
+ fs_1.default.writeFileSync(path_1.default.join(packDir, 'pack.json'), JSON.stringify(pack));
483
+ }
484
+ tmpDir = mkTmp();
485
+ fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.git'));
486
+ fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.awm'), { recursive: true });
487
+ fs_1.default.writeFileSync(path_1.default.join(tmpDir, '.awm', 'sensors.json'), JSON.stringify({ schemaVersion: 2, pack: 'js-ts', sensors: {} }));
488
+ const out = await (0, run_1.runSensors)({ cwd: tmpDir });
489
+ expect(out).toMatchObject({
490
+ overall: 'not_certified', mode: 'source-ambiguous', reason: 'multiple-compatible-registries',
491
+ remedy: 'configure-one-logical-registry',
492
+ source: { kind: 'source-ambiguous', candidates: ['alpha', 'beta'] },
493
+ });
494
+ expect(out.source.candidates).toHaveLength(2);
495
+ }
496
+ finally {
497
+ if (previousHome === undefined)
498
+ delete process.env.AWM_HOME;
499
+ else
500
+ process.env.AWM_HOME = previousHome;
501
+ fs_1.default.rmSync(home, { recursive: true, force: true });
502
+ }
400
503
  });
401
504
  });
402
505
  describe('detectPackDrift', () => {
@@ -46,6 +46,26 @@ describe('computeSensorStatus', () => {
46
46
  expect(result.overall).toBe('NOT_CONFIGURED');
47
47
  expect(result.pack).toBeNull();
48
48
  });
49
+ it('resolves the Git-bounded project manifest from a nested CWD', async () => {
50
+ fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.git'));
51
+ fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.awm'), { recursive: true });
52
+ fs_1.default.mkdirSync(path_1.default.join(tmpDir, 'node_modules', '.bin'), { recursive: true });
53
+ fs_1.default.writeFileSync(path_1.default.join(tmpDir, 'node_modules', '.bin', 'tsc'), '');
54
+ fs_1.default.writeFileSync(path_1.default.join(tmpDir, '.awm', 'sensors.json'), JSON.stringify({
55
+ pack: 'js-ts', sensors: { typecheck: { cmd: 'npx tsc --noEmit', fast: true } },
56
+ }));
57
+ const nestedCwd = path_1.default.join(tmpDir, 'packages', 'app');
58
+ fs_1.default.mkdirSync(nestedCwd, { recursive: true });
59
+ await expect((0, status_1.computeSensorStatus)(nestedCwd)).resolves.toMatchObject({
60
+ overall: 'READY',
61
+ pack: 'js-ts',
62
+ mode: 'project-sensors',
63
+ reason: 'legacy-v1',
64
+ projectRoot: tmpDir,
65
+ manifestPath: path_1.default.join(tmpDir, '.awm', 'sensors.json'),
66
+ checks: { typecheck: { ok: true } },
67
+ });
68
+ });
49
69
  it.each([
50
70
  ['valid manifest', 'READY', () => {
51
71
  installLocalBin('eslint');
@@ -218,17 +238,24 @@ describe('computeSensorStatus', () => {
218
238
  fs_1.default.writeFileSync(path_1.default.join(packageDir, 'node_modules', '.bin', 'eslint'), '');
219
239
  fs_1.default.writeFileSync(path_1.default.join(packageDir, 'eslint.config.awm.mjs'), 'export default []');
220
240
  fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.awm'), { recursive: true });
221
- fs_1.default.writeFileSync(path_1.default.join(tmpDir, '.awm', 'sensors.json'), JSON.stringify({
222
- schemaVersion: 2, pack: 'js-ts', packageRoot: 'cli', registryRoot: registry,
241
+ const manifestPath = path_1.default.join(tmpDir, '.awm', 'sensors.json');
242
+ fs_1.default.writeFileSync(manifestPath, JSON.stringify({
243
+ schemaVersion: 2, pack: 'js-ts', packageRoot: 'cli',
223
244
  sensors: { lint: {
224
245
  enabled: true, variantId: 'eslint-10', command: { executable: 'eslint', resolution: 'node-modules-bin', args: ['.', '--config', 'eslint.config.awm.mjs'] },
225
246
  assets: ['eslint.config.awm.mjs'],
226
247
  initializedCompatibility: { state: 'certified', reason: 'range-and-probe', variantId: 'eslint-10', toolVersion: '10.4.1', runtimeVersion: process.versions.node, certifiedRange: '>=10.0.0 <11.0.0', evidence: [] },
227
248
  } },
228
249
  }));
250
+ const manifestBefore = fs_1.default.readFileSync(manifestPath);
251
+ const inventoryPath = path_1.default.join(home, 'registries.json');
252
+ const inventoryBefore = fs_1.default.readFileSync(inventoryPath);
229
253
  const result = await (0, status_1.computeSensorStatus)(tmpDir);
230
254
  expect(result.checks.lint).toMatchObject({ ok: true });
231
255
  expect(result.overall).toBe('READY');
256
+ expect(result.source).toMatchObject({ kind: 'legacy-rebound', registry: 'baseline' });
257
+ expect(fs_1.default.readFileSync(manifestPath)).toEqual(manifestBefore);
258
+ expect(fs_1.default.readFileSync(inventoryPath)).toEqual(inventoryBefore);
232
259
  expect(runCommand).not.toHaveBeenCalled();
233
260
  expect(runStructuredCommand).not.toHaveBeenCalled();
234
261
  }
@@ -304,4 +331,81 @@ describe('computeSensorStatus', () => {
304
331
  fs_1.default.rmSync(home, { recursive: true, force: true });
305
332
  }
306
333
  });
334
+ it.each([
335
+ ['native-gate', 'Release-blocking CI is authoritative.', 'native-gate-declared'],
336
+ ['opt-out', 'This repository intentionally has no local gate.', 'opt-out-declared'],
337
+ ])('reports v3 %s without dispatching sensors', async (mode, declarationReason, diagnosticReason) => {
338
+ fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.git'));
339
+ fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.awm'), { recursive: true });
340
+ fs_1.default.writeFileSync(path_1.default.join(tmpDir, '.awm', 'sensors.json'), JSON.stringify({ schemaVersion: 3, mode, reason: declarationReason }));
341
+ await expect((0, status_1.computeSensorStatus)(tmpDir)).resolves.toMatchObject({
342
+ overall: 'NOT_CONFIGURED', mode, reason: diagnosticReason,
343
+ declarationReason,
344
+ projectRoot: tmpDir, manifestPath: path_1.default.join(tmpDir, '.awm', 'sensors.json'),
345
+ });
346
+ expect(runCommand).not.toHaveBeenCalled();
347
+ expect(runStructuredCommand).not.toHaveBeenCalled();
348
+ });
349
+ it('reports a v2 source-unavailable state instead of accepting a missing pack', async () => {
350
+ const previousHome = process.env.AWM_HOME;
351
+ const home = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-status-missing-source-home-'));
352
+ try {
353
+ process.env.AWM_HOME = home;
354
+ fs_1.default.writeFileSync(path_1.default.join(home, 'registries.json'), JSON.stringify([]));
355
+ fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.awm'), { recursive: true });
356
+ fs_1.default.writeFileSync(path_1.default.join(tmpDir, '.awm', 'sensors.json'), JSON.stringify({
357
+ schemaVersion: 2, pack: 'js-ts', registryRoot: path_1.default.join(home, 'gone'), sensors: {},
358
+ }));
359
+ await expect((0, status_1.computeSensorStatus)(tmpDir)).resolves.toMatchObject({
360
+ overall: 'DEGRADED', pack: 'js-ts', mode: 'source-unavailable',
361
+ reason: 'no-compatible-registry', remedy: 'install-registry-or-run-awm-update',
362
+ projectRoot: tmpDir, manifestPath: path_1.default.join(tmpDir, '.awm', 'sensors.json'),
363
+ });
364
+ }
365
+ finally {
366
+ if (previousHome === undefined)
367
+ delete process.env.AWM_HOME;
368
+ else
369
+ process.env.AWM_HOME = previousHome;
370
+ fs_1.default.rmSync(home, { recursive: true, force: true });
371
+ }
372
+ });
373
+ it('reports bounded candidates and a remedy when v2 source resolution is ambiguous', async () => {
374
+ const previousHome = process.env.AWM_HOME;
375
+ const home = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-status-ambiguous-home-'));
376
+ try {
377
+ process.env.AWM_HOME = home;
378
+ const pack = {
379
+ schemaVersion: 2, name: 'js-ts', description: 'fixture', detects: ['package.json'],
380
+ coverage: { schemaVersion: 1, classes: { lint: { description: 'lint', detectors: [{ sensor: 'lint' }], remedy: { summary: 'fix', command: 'awm sensors init' } } } },
381
+ sensors: { lint: { applicability: { allFiles: ['package.json'] }, variants: [{
382
+ id: 'eslint-10', priority: 1, certifiedRange: '>=10 <11',
383
+ requirements: { tool: 'eslint', toolRange: '>=10 <11', runtime: 'node', runtimeRange: '>=0' },
384
+ assets: [], formatter: 'generic', probe: { kind: 'package-script-present' },
385
+ command: { executable: 'eslint', resolution: 'node-modules-bin', args: ['.'] },
386
+ }] } },
387
+ };
388
+ const registries = ['alpha', 'beta'];
389
+ fs_1.default.writeFileSync(path_1.default.join(home, 'registries.json'), JSON.stringify(registries.map(name => ({ name, remote: `https://example.test/${name}.git` }))));
390
+ for (const name of registries) {
391
+ const packDir = path_1.default.join(home, 'registries', name, 'sensor-packs', 'js-ts');
392
+ fs_1.default.mkdirSync(packDir, { recursive: true });
393
+ fs_1.default.writeFileSync(path_1.default.join(packDir, 'pack.json'), JSON.stringify(pack));
394
+ }
395
+ fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.git'));
396
+ fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.awm'), { recursive: true });
397
+ fs_1.default.writeFileSync(path_1.default.join(tmpDir, '.awm', 'sensors.json'), JSON.stringify({ schemaVersion: 2, pack: 'js-ts', sensors: {} }));
398
+ await expect((0, status_1.computeSensorStatus)(tmpDir)).resolves.toMatchObject({
399
+ overall: 'DEGRADED', mode: 'source-ambiguous', reason: 'multiple-compatible-registries',
400
+ remedy: 'configure-one-logical-registry', candidates: ['alpha', 'beta'],
401
+ });
402
+ }
403
+ finally {
404
+ if (previousHome === undefined)
405
+ delete process.env.AWM_HOME;
406
+ else
407
+ process.env.AWM_HOME = previousHome;
408
+ fs_1.default.rmSync(home, { recursive: true, force: true });
409
+ }
410
+ });
307
411
  });
@@ -22,7 +22,7 @@ describe('checkCurrentness', () => {
22
22
  now: () => 1_700_000_000_000,
23
23
  fetch: jest.fn().mockResolvedValue({
24
24
  ok: true,
25
- text: async () => JSON.stringify({ 'dist-tags': { latest: '1.2.3' } }),
25
+ text: async () => JSON.stringify({ version: '1.2.3' }),
26
26
  }),
27
27
  readPreferences: () => ({ pins: {} }),
28
28
  listRegistries: () => [{ name: 'baseline', remote: 'https://example.test/team/repo.git', contentRoot: '/registry' }],
@@ -50,6 +50,16 @@ describe('checkCurrentness', () => {
50
50
  expect(JSON.stringify(result)).not.toContain('secret');
51
51
  expect(result.components[1].source).toBe('https://example.test/team/repo.git');
52
52
  });
53
+ it('reads npm currentness from the bounded latest metadata endpoint', async () => {
54
+ const fetch = jest.fn().mockResolvedValue({
55
+ ok: true,
56
+ text: async () => JSON.stringify({ version: '1.2.3' }),
57
+ });
58
+ const { checkCurrentness } = require('../../../src/core/currentness/check');
59
+ const result = await checkCurrentness(root, deps({ fetch }));
60
+ expect(fetch).toHaveBeenCalledWith('https://registry.npmjs.org/agentic-workflow-manager/latest', expect.objectContaining({ signal: expect.any(AbortSignal) }));
61
+ expect(result.components[0]).toEqual(expect.objectContaining({ status: 'current', latest: '1.2.3' }));
62
+ });
53
63
  it('marks an older pinned registry as pinned-behind with the unpin remedy', async () => {
54
64
  const { checkCurrentness } = require('../../../src/core/currentness/check');
55
65
  const result = await checkCurrentness(root, deps({
@@ -78,7 +88,7 @@ describe('checkCurrentness', () => {
78
88
  cliVersion: () => '1.9007199254740992.0',
79
89
  fetch: jest.fn().mockResolvedValue({
80
90
  ok: true,
81
- text: async () => JSON.stringify({ 'dist-tags': { latest: '1.9007199254740993.0' } }),
91
+ text: async () => JSON.stringify({ version: '1.9007199254740993.0' }),
82
92
  }),
83
93
  }));
84
94
  expect(result.components[0]).toEqual(expect.objectContaining({
@@ -151,7 +161,7 @@ describe('checkCurrentness', () => {
151
161
  ])('fails closed for %s', async (name, git) => {
152
162
  const { checkCurrentness } = require('../../../src/core/currentness/check');
153
163
  const result = await checkCurrentness(root, deps(name === 'malformed npm semver'
154
- ? { fetch: jest.fn().mockResolvedValue({ ok: true, text: async () => JSON.stringify({ 'dist-tags': { latest: 'latest' } }) }) }
164
+ ? { fetch: jest.fn().mockResolvedValue({ ok: true, text: async () => JSON.stringify({ version: 'latest' }) }) }
155
165
  : { git }));
156
166
  expect(result.components.some((c) => c.status === 'unverifiable')).toBe(true);
157
167
  });
@@ -59,6 +59,11 @@ test('preserves actionable no-manifest preflight JSON when verify-sensors is pip
59
59
  expect(result.status).toBe(1);
60
60
  expect(JSON.parse(result.stdout)).toMatchObject({
61
61
  status: 'not_configured',
62
+ mode: 'missing',
63
+ reason: 'manifest-absent',
64
+ projectRoot: project,
65
+ manifestPath: path_1.default.join(project, '.awm', 'sensors.json'),
66
+ remedy: expect.any(String),
62
67
  checks: expect.arrayContaining([expect.objectContaining({
63
68
  id: 'sensors-execution', ok: false,
64
69
  detail: 'sensor verdict was not_certified; no sensor established an empirical pass',
@@ -17,12 +17,6 @@ const bin = path_1.default.join(cliDir, 'dist/src/index.js');
17
17
  const fixtureRoot = path_1.default.join(cliDir, 'tests/fixtures/sensor-compatibility');
18
18
  const projectFixture = path_1.default.join(fixtureRoot, 'project');
19
19
  const registryFixture = path_1.default.join(fixtureRoot, 'registry');
20
- const noFollowUnavailableOnNativeWindows = process.platform === 'win32'
21
- && typeof fs_1.default.constants.O_NOFOLLOW !== 'number';
22
- // Other native platforms exercise coverage through testWithNoFollow; this
23
- // branch preserves the deliberate Windows fail-closed behavior instead.
24
- const testWithNoFollow = noFollowUnavailableOnNativeWindows ? test.skip : test;
25
- const testWithoutNoFollowOnNativeWindows = noFollowUnavailableOnNativeWindows ? test : test.skip;
26
20
  function hashTree(root) {
27
21
  const hash = crypto_1.default.createHash('sha256');
28
22
  const walk = (directory) => {
@@ -97,7 +91,7 @@ test.each(['linux', 'darwin', 'win32'])('keeps injected resolver semantics consi
97
91
  fs_1.default.rmSync(fixture.root, { recursive: true, force: true });
98
92
  }
99
93
  });
100
- testWithNoFollow('compiled binary dispatches coverage and emits parseable JSON on the native CI platform', () => {
94
+ test('compiled binary dispatches coverage and emits parseable JSON on the native CI platform', () => {
101
95
  const fixture = createFixture();
102
96
  try {
103
97
  const report = json(runCli(fixture, 'coverage', '--json'));
@@ -107,7 +101,7 @@ testWithNoFollow('compiled binary dispatches coverage and emits parseable JSON o
107
101
  fs_1.default.rmSync(fixture.root, { recursive: true, force: true });
108
102
  }
109
103
  });
110
- testWithNoFollow('compiled sensors run returns nonzero while preserving parseable not_certified JSON', () => {
104
+ test('compiled sensors run returns nonzero while preserving parseable not_certified JSON', () => {
111
105
  const fixture = createFixture();
112
106
  try {
113
107
  fs_1.default.rmSync(path_1.default.join(fixture.project, '.awm', 'sensors.json'));
@@ -119,7 +113,7 @@ testWithNoFollow('compiled sensors run returns nonzero while preserving parseabl
119
113
  fs_1.default.rmSync(fixture.root, { recursive: true, force: true });
120
114
  }
121
115
  });
122
- testWithNoFollow('compiled status reports static READY without writing or executing the project sensor (R6, R6.2)', () => {
116
+ test('compiled status reports static READY without writing or executing the project sensor (R6, R6.2)', () => {
123
117
  const fixture = createFixture();
124
118
  try {
125
119
  const localBin = path_1.default.join(fixture.project, 'node_modules', '.bin', 'eslint');
@@ -139,7 +133,7 @@ testWithNoFollow('compiled status reports static READY without writing or execut
139
133
  fs_1.default.rmSync(fixture.root, { recursive: true, force: true });
140
134
  }
141
135
  });
142
- testWithNoFollow('legacy coverage stays unverified, init migrates explicitly, and version drift is visible (R7.2, R7.8)', () => {
136
+ test('legacy coverage stays unverified, init migrates explicitly, and version drift is visible (R7.2, R7.8)', () => {
143
137
  const fixture = createFixture();
144
138
  try {
145
139
  const before = [hashTree(fixture.project), hashTree(fixture.awmHome)];
@@ -167,14 +161,3 @@ testWithNoFollow('legacy coverage stays unverified, init migrates explicitly, an
167
161
  fs_1.default.rmSync(fixture.root, { recursive: true, force: true });
168
162
  }
169
163
  });
170
- testWithoutNoFollowOnNativeWindows('coverage fails closed when native Windows cannot prevent symlink dereference', () => {
171
- const fixture = createFixture();
172
- try {
173
- const result = runCli(fixture, 'coverage', '--json');
174
- expect(result.status).toBe(1);
175
- expect(`${result.stdout ?? ''}${result.stderr ?? ''}`).toContain('platform cannot guarantee no symlink dereference');
176
- }
177
- finally {
178
- fs_1.default.rmSync(fixture.root, { recursive: true, force: true });
179
- }
180
- });
@@ -12,9 +12,6 @@ const cliDir = path_1.default.resolve(__dirname, '../..');
12
12
  const bin = path_1.default.join(cliDir, 'dist/src/index.js');
13
13
  const fixture = path_1.default.join(cliDir, 'tests/fixtures/sensor-coverage/js-ts-gap');
14
14
  const registryFixture = path_1.default.join(cliDir, 'tests/fixtures/sensor-coverage/registry');
15
- const noFollowUnavailableOnNativeWindows = process.platform === 'win32'
16
- && typeof fs_1.default.constants.O_NOFOLLOW !== 'number';
17
- const testWithNoFollow = noFollowUnavailableOnNativeWindows ? test.skip : test;
18
15
  const hashTree = (root) => {
19
16
  const hash = crypto_1.default.createHash('sha256');
20
17
  const walk = (directory) => {
@@ -71,32 +68,26 @@ test('compiled CLI reports static and read-only empirical coverage without leaki
71
68
  const result = (0, child_process_1.spawnSync)(process.execPath, [bin, 'sensors', 'coverage', '--json'], {
72
69
  cwd: project, encoding: 'utf8', env: { ...process.env, AWM_HOME: awmHome, AWM_NO_UPDATE_CHECK: '1' },
73
70
  });
74
- if (noFollowUnavailableOnNativeWindows) {
75
- expect(result.status).toBe(1);
76
- expect(`${result.stdout ?? ''}${result.stderr ?? ''}`).toContain('platform cannot guarantee no symlink dereference');
77
- }
78
- else {
79
- expect(result.status).toBe(0);
80
- const report = JSON.parse(result.stdout ?? '');
81
- expect(report.static.classes).toEqual(expect.arrayContaining([
82
- expect.objectContaining({ id: 'formatting', status: 'unverifiable' }),
83
- expect.objectContaining({ id: 'project-style-conventions', status: 'unverifiable' }),
84
- ]));
85
- expect(report).toMatchObject({ schemaVersion: 2, empirical: {
86
- status: 'partial', classes: [expect.objectContaining({ defectClass: 'lint-errors', occurrences: 2, recurrent: true })],
87
- unclassified: { occurrences: 1 },
88
- } });
89
- expect(result.stdout).not.toContain('fixture-secret-description');
90
- expect(result.stdout).not.toContain('private-lint-signature');
91
- expect(result.stdout).not.toContain('windows secret');
92
- }
71
+ expect(result.status).toBe(0);
72
+ const report = JSON.parse(result.stdout ?? '');
73
+ expect(report.static.classes).toEqual(expect.arrayContaining([
74
+ expect.objectContaining({ id: 'formatting', status: 'unverifiable' }),
75
+ expect.objectContaining({ id: 'project-style-conventions', status: 'unverifiable' }),
76
+ ]));
77
+ expect(report).toMatchObject({ schemaVersion: 2, empirical: {
78
+ status: 'partial', classes: [expect.objectContaining({ defectClass: 'lint-errors', occurrences: 2, recurrent: true })],
79
+ unclassified: { occurrences: 1 },
80
+ } });
81
+ expect(result.stdout).not.toContain('fixture-secret-description');
82
+ expect(result.stdout).not.toContain('private-lint-signature');
83
+ expect(result.stdout).not.toContain('windows secret');
93
84
  expect([hashTree(project), hashTree(awmHome)]).toEqual(before);
94
85
  }
95
86
  finally {
96
87
  fs_1.default.rmSync(tmp, { recursive: true, force: true });
97
88
  }
98
89
  });
99
- testWithNoFollow('compiled coverage keeps static verdict stable while --min changes only recurrence emphasis', () => {
90
+ test('compiled coverage keeps static verdict stable while --min changes only recurrence emphasis', () => {
100
91
  const { tmp, project, awmHome } = runWithFixture();
101
92
  try {
102
93
  const before = [hashTree(project), hashTree(awmHome)];
@@ -115,7 +106,7 @@ testWithNoFollow('compiled coverage keeps static verdict stable while --min chan
115
106
  fs_1.default.rmSync(tmp, { recursive: true, force: true });
116
107
  }
117
108
  });
118
- testWithNoFollow('informative states exit zero; malformed contract exits non-zero (R2.7, R2.9)', () => {
109
+ test('informative states exit zero; malformed contract exits non-zero (R2.7, R2.9)', () => {
119
110
  const run = (mutatePack) => {
120
111
  const { tmp, project, awmHome } = runWithFixture(mutatePack);
121
112
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-workflow-manager",
3
- "version": "9.4.0",
3
+ "version": "9.5.0",
4
4
  "main": "dist/src/index.js",
5
5
  "bin": {
6
6
  "awm": "./dist/src/index.js"