agentic-workflow-manager 9.4.1 → 9.6.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.
Files changed (57) hide show
  1. package/dist/src/commands/preflight/checks.js +58 -27
  2. package/dist/src/commands/sensors/bootstrap.js +171 -0
  3. package/dist/src/commands/sensors/compatibility/contract.js +18 -15
  4. package/dist/src/commands/sensors/compatibility/manifest.js +75 -2
  5. package/dist/src/commands/sensors/compatibility/materialize.js +256 -54
  6. package/dist/src/commands/sensors/compatibility/pack-source.js +61 -17
  7. package/dist/src/commands/sensors/compatibility/safe-file.js +286 -0
  8. package/dist/src/commands/sensors/compatibility/source.js +75 -0
  9. package/dist/src/commands/sensors/coverage/index.js +2 -2
  10. package/dist/src/commands/sensors/coverage/resolve.js +35 -29
  11. package/dist/src/commands/sensors/detection.js +31 -0
  12. package/dist/src/commands/sensors/exec.js +116 -11
  13. package/dist/src/commands/sensors/index.js +51 -10
  14. package/dist/src/commands/sensors/init.js +17 -168
  15. package/dist/src/commands/sensors/migrate.js +209 -0
  16. package/dist/src/commands/sensors/project.js +107 -0
  17. package/dist/src/commands/sensors/run.js +66 -47
  18. package/dist/src/commands/sensors/status.js +97 -56
  19. package/dist/src/core/init/steps.js +24 -11
  20. package/dist/src/core/secure-fs/native-bridge.js +209 -0
  21. package/dist/tests/commands/preflight/preflight.test.js +31 -0
  22. package/dist/tests/commands/sensors/bootstrap-observational.test.js +24 -0
  23. package/dist/tests/commands/sensors/bootstrap.test.js +175 -0
  24. package/dist/tests/commands/sensors/compatibility/contract.test.js +38 -0
  25. package/dist/tests/commands/sensors/compatibility/manifest.test.js +90 -0
  26. package/dist/tests/commands/sensors/compatibility/materialize.test.js +410 -0
  27. package/dist/tests/commands/sensors/compatibility/pack-source.test.js +249 -0
  28. package/dist/tests/commands/sensors/compatibility/safe-file.test.js +52 -0
  29. package/dist/tests/commands/sensors/compatibility/source.test.js +121 -0
  30. package/dist/tests/commands/sensors/coverage/index.test.js +20 -0
  31. package/dist/tests/commands/sensors/coverage/resolve.test.js +36 -34
  32. package/dist/tests/commands/sensors/exec.test.js +86 -0
  33. package/dist/tests/commands/sensors/index.test.js +54 -0
  34. package/dist/tests/commands/sensors/init-pack-unavailable.test.js +21 -84
  35. package/dist/tests/commands/sensors/init.test.js +56 -490
  36. package/dist/tests/commands/sensors/migrate.test.js +513 -0
  37. package/dist/tests/commands/sensors/project.test.js +102 -0
  38. package/dist/tests/commands/sensors/run.test.js +107 -4
  39. package/dist/tests/commands/sensors/status.test.js +106 -2
  40. package/dist/tests/core/init/orchestrator.test.js +3 -1
  41. package/dist/tests/core/secure-fs/native-bridge.test.js +733 -0
  42. package/dist/tests/integration/preflight-json-pipe.e2e.test.js +5 -0
  43. package/dist/tests/integration/sensor-bootstrap.e2e.test.js +165 -0
  44. package/dist/tests/integration/sensor-compatibility.e2e.test.js +14 -23
  45. package/dist/tests/integration/sensor-coverage.e2e.test.js +15 -24
  46. package/dist/tests/integration/sensor-structured-run.e2e.test.js +3 -1
  47. package/dist/tests/structural/native-release-artifacts.test.js +22 -0
  48. package/dist/tests/structural/release-flags-are-reachable-from-ci.test.js +10 -0
  49. package/dist/tests/structural/release-registry-provenance.test.js +4 -0
  50. package/dist/tests/structural/sensor-portability-contract.test.js +44 -0
  51. package/package.json +6 -2
  52. package/prebuilds/darwin-arm64/secure_fs.node +0 -0
  53. package/prebuilds/darwin-x64/secure_fs.node +0 -0
  54. package/prebuilds/linux-arm64/secure_fs.node +0 -0
  55. package/prebuilds/linux-x64/secure_fs.node +0 -0
  56. package/prebuilds/win32-arm64/secure_fs.node +0 -0
  57. package/prebuilds/win32-x64/secure_fs.node +0 -0
@@ -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',
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const crypto_1 = __importDefault(require("crypto"));
7
+ const child_process_1 = require("child_process");
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const os_1 = __importDefault(require("os"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const materialize_1 = require("../../src/commands/sensors/compatibility/materialize");
12
+ const pack_source_1 = require("../../src/commands/sensors/compatibility/pack-source");
13
+ const safe_file_1 = require("../../src/commands/sensors/compatibility/safe-file");
14
+ const native_bridge_1 = require("../../src/core/secure-fs/native-bridge");
15
+ const cliRoot = path_1.default.resolve(__dirname, '../..');
16
+ const bin = path_1.default.join(cliRoot, 'dist/src/index.js');
17
+ const fixtureRoot = path_1.default.join(cliRoot, 'tests/fixtures/sensor-compatibility');
18
+ function hashTree(root) {
19
+ const hash = crypto_1.default.createHash('sha256');
20
+ const walk = (directory) => {
21
+ for (const entry of fs_1.default.readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
22
+ const file = path_1.default.join(directory, entry.name);
23
+ hash.update(path_1.default.relative(root, file));
24
+ if (entry.isDirectory())
25
+ walk(file);
26
+ else if (entry.isFile())
27
+ hash.update(fs_1.default.readFileSync(file));
28
+ }
29
+ };
30
+ walk(root);
31
+ return hash.digest('hex');
32
+ }
33
+ function fixture() {
34
+ const root = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-bootstrap-e2e-'));
35
+ const project = path_1.default.join(root, 'project');
36
+ const awmHome = path_1.default.join(root, 'awm-home');
37
+ const registryRoot = path_1.default.join(awmHome, 'registries', 'baseline');
38
+ fs_1.default.cpSync(path_1.default.join(fixtureRoot, 'project'), project, { recursive: true });
39
+ fs_1.default.mkdirSync(path_1.default.join(project, 'node_modules', 'eslint'), { recursive: true });
40
+ fs_1.default.copyFileSync(path_1.default.join(project, 'eslint-tool-package.json'), path_1.default.join(project, 'node_modules', 'eslint', 'package.json'));
41
+ fs_1.default.mkdirSync(path_1.default.dirname(registryRoot), { recursive: true });
42
+ fs_1.default.cpSync(path_1.default.join(fixtureRoot, 'registry'), registryRoot, { recursive: true });
43
+ fs_1.default.writeFileSync(path_1.default.join(awmHome, 'registries.json'), JSON.stringify([{ name: 'baseline', remote: 'fixture' }]));
44
+ return { root, project, awmHome, registryRoot };
45
+ }
46
+ function run(fixture, ...args) {
47
+ return (0, child_process_1.spawnSync)(process.execPath, [bin, 'sensors', 'bootstrap', ...args], {
48
+ cwd: fixture.project,
49
+ encoding: 'utf8',
50
+ env: { ...process.env, AWM_HOME: fixture.awmHome, AWM_NO_UPDATE_CHECK: '1' },
51
+ });
52
+ }
53
+ function v2Manifest() {
54
+ return {
55
+ schemaVersion: 2,
56
+ pack: 'js-ts',
57
+ sensors: {
58
+ lint: {
59
+ enabled: true, variantId: 'eslint-10',
60
+ command: { executable: 'eslint', resolution: 'node-modules-bin', args: ['.'] },
61
+ assets: ['eslint.fixture.mjs'],
62
+ initializedCompatibility: {
63
+ state: 'certified', reason: 'fixture', variantId: 'eslint-10',
64
+ toolVersion: '10.4.1', runtimeVersion: '24.0.0', certifiedRange: '>=10 <11', evidence: [],
65
+ },
66
+ },
67
+ },
68
+ };
69
+ }
70
+ beforeAll(() => {
71
+ if (!fs_1.default.existsSync(bin))
72
+ throw new Error(`Sensor bootstrap E2E requires the compiled CLI at ${bin}; run npm run build before this test.`);
73
+ });
74
+ test('compiled bootstrap creates once and the exact second invocation is a byte-stable no-op', () => {
75
+ const subject = fixture();
76
+ try {
77
+ fs_1.default.rmSync(path_1.default.join(subject.project, '.awm'), { recursive: true, force: true });
78
+ const machineBefore = hashTree(subject.awmHome);
79
+ expect(run(subject, '--mode', 'native-gate', '--reason', 'fixture-gate').status).toBe(0);
80
+ const first = hashTree(subject.project);
81
+ expect(run(subject, '--mode', 'native-gate', '--reason', 'fixture-gate').status).toBe(0);
82
+ expect(hashTree(subject.project)).toBe(first);
83
+ expect(hashTree(subject.awmHome)).toBe(machineBefore);
84
+ expect(JSON.parse(fs_1.default.readFileSync(path_1.default.join(subject.project, '.awm', 'sensors.json'), 'utf8'))).toEqual({ schemaVersion: 3, mode: 'native-gate', reason: 'fixture-gate' });
85
+ }
86
+ finally {
87
+ fs_1.default.rmSync(subject.root, { recursive: true, force: true });
88
+ }
89
+ });
90
+ test('compiled project-sensors bootstrap materializes only selected assets and then no-ops', () => {
91
+ const subject = fixture();
92
+ try {
93
+ fs_1.default.rmSync(path_1.default.join(subject.project, '.awm'), { recursive: true, force: true });
94
+ fs_1.default.copyFileSync(path_1.default.join(subject.registryRoot, 'sensor-packs', 'js-ts', 'eslint.fixture.mjs'), path_1.default.join(subject.project, 'eslint.fixture.mjs'));
95
+ const machineBefore = hashTree(subject.awmHome);
96
+ const result = run(subject, '--mode', 'project-sensors');
97
+ if (result.status !== 0)
98
+ throw new Error(`project-sensors bootstrap failed: ${result.stdout}\n${result.stderr}`);
99
+ expect(`${result.stdout}${result.stderr}`).toContain('pack=js-ts');
100
+ expect(fs_1.default.existsSync(path_1.default.join(subject.project, 'eslint.fixture.mjs'))).toBe(true);
101
+ expect(fs_1.default.existsSync(path_1.default.join(subject.project, 'pack.json'))).toBe(false);
102
+ const first = hashTree(subject.project);
103
+ expect(run(subject, '--mode', 'project-sensors').status).toBe(0);
104
+ expect(hashTree(subject.project)).toBe(first);
105
+ expect(hashTree(subject.awmHome)).toBe(machineBefore);
106
+ }
107
+ finally {
108
+ fs_1.default.rmSync(subject.root, { recursive: true, force: true });
109
+ }
110
+ });
111
+ test('injected manifest publication failure compensates created assets without changing unrelated project or machine bytes', () => {
112
+ const subject = fixture();
113
+ try {
114
+ fs_1.default.rmSync(path_1.default.join(subject.project, '.awm'), { recursive: true, force: true });
115
+ const beforePackage = fs_1.default.readFileSync(path_1.default.join(subject.project, 'package.json'));
116
+ const beforeConfig = fs_1.default.readFileSync(path_1.default.join(subject.project, 'eslint.config.mjs'));
117
+ const beforeMachine = hashTree(subject.awmHome);
118
+ const source = (0, pack_source_1.resolvePackSource)('js-ts', { registries: [{ name: 'baseline', remote: 'fixture', contentRoot: subject.registryRoot }] });
119
+ (0, safe_file_1.setSecureFsForTests)({
120
+ withProjectLease: native_bridge_1.secureFs.withProjectLease,
121
+ readRegularFile: native_bridge_1.secureFs.readRegularFile,
122
+ writeProjectTransaction: ((root, destination, content, options) => {
123
+ if (destination === '.awm/sensors.json')
124
+ throw new Error('injected rename failure');
125
+ native_bridge_1.secureFs.writeProjectTransaction(root, destination, content, options);
126
+ }),
127
+ removeObservedProjectFile: native_bridge_1.secureFs.removeObservedProjectFile,
128
+ });
129
+ expect(() => (0, materialize_1.materializePortableSensors)({
130
+ projectRoot: subject.project, pack: 'js-ts', source,
131
+ sensors: { lint: { enabled: true, variantId: 'eslint-10', command: { executable: 'eslint', resolution: 'node-modules-bin', args: ['.'] }, assets: ['eslint.fixture.mjs'], initializedCompatibility: { state: 'certified', reason: 'fixture', variantId: 'eslint-10', toolVersion: '10.4.1', runtimeVersion: '24.0.0', certifiedRange: '>=10 <11', evidence: [] } } },
132
+ })).toThrow('injected rename failure');
133
+ expect(fs_1.default.readFileSync(path_1.default.join(subject.project, 'package.json'))).toEqual(beforePackage);
134
+ expect(fs_1.default.readFileSync(path_1.default.join(subject.project, 'eslint.config.mjs'))).toEqual(beforeConfig);
135
+ expect(fs_1.default.existsSync(path_1.default.join(subject.project, 'eslint.fixture.mjs'))).toBe(false);
136
+ expect(fs_1.default.existsSync(path_1.default.join(subject.project, '.awm', 'sensors.json'))).toBe(false);
137
+ expect(hashTree(subject.awmHome)).toBe(beforeMachine);
138
+ expect(fs_1.default.existsSync(path_1.default.join(subject.project, '.awm', 'sensors.json.tmp'))).toBe(false);
139
+ }
140
+ finally {
141
+ (0, safe_file_1.setSecureFsForTests)(undefined);
142
+ fs_1.default.rmSync(subject.root, { recursive: true, force: true });
143
+ }
144
+ });
145
+ test('compiled bootstrap migrates v2 once, preserves all non-manifest project bytes, and then no-ops', () => {
146
+ const subject = fixture();
147
+ try {
148
+ const manifest = path_1.default.join(subject.project, '.awm', 'sensors.json');
149
+ fs_1.default.writeFileSync(manifest, JSON.stringify(v2Manifest(), null, 2) + '\n');
150
+ const machineBefore = hashTree(subject.awmHome);
151
+ const assetBefore = fs_1.default.readFileSync(path_1.default.join(subject.project, 'eslint.config.mjs'));
152
+ expect(run(subject).status).toBe(0);
153
+ const first = hashTree(subject.project);
154
+ const migrated = JSON.parse(fs_1.default.readFileSync(manifest, 'utf8'));
155
+ expect(migrated).toMatchObject({ schemaVersion: 3, mode: 'project-sensors', source: { registry: 'baseline' } });
156
+ expect(migrated).not.toHaveProperty('registryRoot');
157
+ expect(fs_1.default.readFileSync(path_1.default.join(subject.project, 'eslint.config.mjs'))).toEqual(assetBefore);
158
+ expect(run(subject).status).toBe(0);
159
+ expect(hashTree(subject.project)).toBe(first);
160
+ expect(hashTree(subject.awmHome)).toBe(machineBefore);
161
+ }
162
+ finally {
163
+ fs_1.default.rmSync(subject.root, { recursive: true, force: true });
164
+ }
165
+ });
@@ -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) => {
@@ -48,6 +42,9 @@ function createFixture() {
48
42
  fs_1.default.copyFileSync(path_1.default.join(project, 'eslint-tool-package.json'), path_1.default.join(project, 'node_modules', 'eslint', 'package.json'));
49
43
  fs_1.default.mkdirSync(path_1.default.dirname(registryRoot), { recursive: true });
50
44
  fs_1.default.cpSync(registryFixture, registryRoot, { recursive: true });
45
+ // The fixture pack's certified probe requires this selected config to be
46
+ // present before bootstrap can prove compatibility and publish v3.
47
+ fs_1.default.copyFileSync(path_1.default.join(registryRoot, 'sensor-packs', 'js-ts', 'eslint.fixture.mjs'), path_1.default.join(project, 'eslint.fixture.mjs'));
51
48
  fs_1.default.writeFileSync(path_1.default.join(awmHome, 'registries.json'), JSON.stringify([{ name: 'baseline', remote: 'fixture' }]));
52
49
  return { root, project, awmHome, registryRoot };
53
50
  }
@@ -66,7 +63,8 @@ function runAwm(fixture, ...args) {
66
63
  });
67
64
  }
68
65
  function json(result) {
69
- expect(result.status).toBe(0);
66
+ if (result.status !== 0)
67
+ throw new Error(`CLI failed: ${result.stdout ?? ''}\n${result.stderr ?? ''}`);
70
68
  if (!(result.stdout ?? '').trim())
71
69
  throw new Error(`coverage emitted no JSON: stderr=${result.stderr ?? ''}`);
72
70
  return JSON.parse(result.stdout ?? '');
@@ -97,7 +95,7 @@ test.each(['linux', 'darwin', 'win32'])('keeps injected resolver semantics consi
97
95
  fs_1.default.rmSync(fixture.root, { recursive: true, force: true });
98
96
  }
99
97
  });
100
- testWithNoFollow('compiled binary dispatches coverage and emits parseable JSON on the native CI platform', () => {
98
+ test('compiled binary dispatches coverage and emits parseable JSON on the native CI platform', () => {
101
99
  const fixture = createFixture();
102
100
  try {
103
101
  const report = json(runCli(fixture, 'coverage', '--json'));
@@ -107,7 +105,7 @@ testWithNoFollow('compiled binary dispatches coverage and emits parseable JSON o
107
105
  fs_1.default.rmSync(fixture.root, { recursive: true, force: true });
108
106
  }
109
107
  });
110
- testWithNoFollow('compiled sensors run returns nonzero while preserving parseable not_certified JSON', () => {
108
+ test('compiled sensors run returns nonzero while preserving parseable not_certified JSON', () => {
111
109
  const fixture = createFixture();
112
110
  try {
113
111
  fs_1.default.rmSync(path_1.default.join(fixture.project, '.awm', 'sensors.json'));
@@ -119,9 +117,10 @@ testWithNoFollow('compiled sensors run returns nonzero while preserving parseabl
119
117
  fs_1.default.rmSync(fixture.root, { recursive: true, force: true });
120
118
  }
121
119
  });
122
- testWithNoFollow('compiled status reports static READY without writing or executing the project sensor (R6, R6.2)', () => {
120
+ test('compiled status reports static READY without writing or executing the project sensor (R6, R6.2)', () => {
123
121
  const fixture = createFixture();
124
122
  try {
123
+ fs_1.default.rmSync(path_1.default.join(fixture.project, '.awm', 'sensors.json'));
125
124
  const localBin = path_1.default.join(fixture.project, 'node_modules', '.bin', 'eslint');
126
125
  fs_1.default.mkdirSync(path_1.default.dirname(localBin), { recursive: true });
127
126
  fs_1.default.writeFileSync(localBin, 'this fixture must never be executed by status\n');
@@ -139,7 +138,7 @@ testWithNoFollow('compiled status reports static READY without writing or execut
139
138
  fs_1.default.rmSync(fixture.root, { recursive: true, force: true });
140
139
  }
141
140
  });
142
- testWithNoFollow('legacy coverage stays unverified, init migrates explicitly, and version drift is visible (R7.2, R7.8)', () => {
141
+ test('legacy coverage stays unverified, init migrates explicitly, and version drift is visible (R7.2, R7.8)', () => {
143
142
  const fixture = createFixture();
144
143
  try {
145
144
  const before = [hashTree(fixture.project), hashTree(fixture.awmHome)];
@@ -149,10 +148,13 @@ testWithNoFollow('legacy coverage stays unverified, init migrates explicitly, an
149
148
  id: 'lint-errors', status: 'unverifiable', detectors: expect.arrayContaining([expect.objectContaining({ compatibility: expect.objectContaining({ state: 'compatible-unverified' }) })]),
150
149
  })]));
151
150
  expect([hashTree(fixture.project), hashTree(fixture.awmHome)]).toEqual(before);
151
+ // v1 declarations are intentionally preserved; creation is explicitly
152
+ // restarted once the owner removes the legacy declaration.
153
+ fs_1.default.rmSync(path_1.default.join(fixture.project, '.awm', 'sensors.json'));
152
154
  const migrated = runCli(fixture, 'init', '--registry-root', fixture.registryRoot, '--pack', 'js-ts', '--no-configure');
153
155
  expect(migrated.status).toBe(0);
154
156
  const manifest = JSON.parse(fs_1.default.readFileSync(path_1.default.join(fixture.project, '.awm', 'sensors.json'), 'utf8'));
155
- expect(manifest).toMatchObject({ schemaVersion: 2, pack: 'js-ts', sensors: { lint: { variantId: 'eslint-10' } } });
157
+ expect(manifest).toMatchObject({ schemaVersion: 3, mode: 'project-sensors', pack: 'js-ts', source: { registry: 'baseline' }, sensors: { lint: { variantId: 'eslint-10' } } });
156
158
  const certified = json(runCli(fixture, 'coverage', '--json'));
157
159
  expect(certified.static.classes).toEqual(expect.arrayContaining([expect.objectContaining({
158
160
  id: 'lint-errors', status: 'covered', detectors: expect.arrayContaining([expect.objectContaining({ compatibility: expect.objectContaining({ state: 'certified' }) })]),
@@ -167,14 +169,3 @@ testWithNoFollow('legacy coverage stays unverified, init migrates explicitly, an
167
169
  fs_1.default.rmSync(fixture.root, { recursive: true, force: true });
168
170
  }
169
171
  });
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 {
@@ -107,8 +107,10 @@ test('compiled sensors run materializes a v2 registry command and passes its lit
107
107
  assertSuccessfulCli(initialized, 'init');
108
108
  const manifest = JSON.parse(fs_1.default.readFileSync(path_1.default.join(fixture.project, '.awm', 'sensors.json'), 'utf8'));
109
109
  expect(manifest).toMatchObject({
110
- schemaVersion: 2,
110
+ schemaVersion: 3,
111
+ mode: 'project-sensors',
111
112
  pack: 'fixture',
113
+ source: { registry: 'baseline' },
112
114
  sensors: { structured: { variantId: 'fixture-v1', command: { executable: 'node', resolution: 'path', args: ['fixture-sensor.mjs', fixture.literal] } } },
113
115
  });
114
116
  const result = runCli(fixture, 'run', '--fast');
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const child_process_1 = require("child_process");
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const CLI_ROOT = path_1.default.resolve(__dirname, '..', '..');
10
+ const REPO_ROOT = path_1.default.resolve(CLI_ROOT, '..');
11
+ describe('native release artifacts', () => {
12
+ it('keeps native prebuilds generated instead of versioned source', () => {
13
+ const gitignore = fs_1.default.readFileSync(path_1.default.join(REPO_ROOT, '.gitignore'), 'utf8');
14
+ const tracked = (0, child_process_1.execFileSync)('git', ['ls-files', '--', 'cli/prebuilds'], {
15
+ cwd: REPO_ROOT,
16
+ encoding: 'utf8',
17
+ }).trim();
18
+ expect(gitignore).toContain('cli/prebuilds/');
19
+ expect(gitignore).toContain('cli/native-artifacts/');
20
+ expect(tracked).toBe('');
21
+ });
22
+ });
@@ -24,6 +24,7 @@ const CLI_ROOT = path_1.default.resolve(__dirname, '..', '..');
24
24
  const REPO_ROOT = path_1.default.resolve(CLI_ROOT, '..');
25
25
  const RELEASE_ENTRY = path_1.default.join(CLI_ROOT, 'src', 'release', 'index.ts');
26
26
  const RELEASE_WORKFLOW = path_1.default.join(REPO_ROOT, '.github', 'workflows', 'release.yml');
27
+ const CI_WORKFLOW = path_1.default.join(REPO_ROOT, '.github', 'workflows', 'ci.yml');
27
28
  // Flags deliberadamente NO expuestos a CI, con la razón por la que no lo están. Sacar uno
28
29
  // de acá sin exponerlo en el workflow rompe el test a propósito.
29
30
  const NOT_FOR_CI = {
@@ -65,3 +66,12 @@ describe('flags del release: implementados <=> alcanzables desde CI', () => {
65
66
  expect(workflowSource).toMatch(/--force \{0\}/);
66
67
  });
67
68
  });
69
+ describe('matriz de compilacion nativa Windows', () => {
70
+ for (const workflowPath of [CI_WORKFLOW, RELEASE_WORKFLOW]) {
71
+ it(`${path_1.default.basename(workflowPath)} fija x64 en Windows Server 2022 y conserva ARM64 nativo`, () => {
72
+ const source = fs_1.default.readFileSync(workflowPath, 'utf8');
73
+ expect(source).toMatch(/- os: windows-2022\s+target: win32-x64/);
74
+ expect(source).toMatch(/- os: windows-11-arm\s+target: win32-arm64/);
75
+ });
76
+ }
77
+ });
@@ -18,4 +18,8 @@ describe('release registry provenance', () => {
18
18
  .toBeLessThan(workflow.indexOf('- name: Release'));
19
19
  expect(workflow).toMatch(/REGISTRY_TAG: v3\.4\.0[\s\S]*AWM_PUBLISHED_REGISTRY_TAG: v3\.4\.0/);
20
20
  });
21
+ it('normalizes npm pack JSON from both array and object output formats', () => {
22
+ expect(workflow).toContain('Array.isArray(packed) ? packed[0] : Object.values(packed)[0]');
23
+ expect(workflow).toContain('npm pack returned no tarball filename');
24
+ });
21
25
  });
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const fs_1 = __importDefault(require("fs"));
7
+ const path_1 = __importDefault(require("path"));
8
+ const commander_1 = require("commander");
9
+ jest.mock('@clack/prompts', () => ({ log: { error: jest.fn(), success: jest.fn(), info: jest.fn() } }));
10
+ jest.mock('picocolors', () => ({ green: (value) => value, yellow: (value) => value, red: (value) => value }));
11
+ const sensors_1 = require("../../src/commands/sensors");
12
+ const manifest_1 = require("../../src/commands/sensors/compatibility/manifest");
13
+ const root = path_1.default.resolve(__dirname, '../../..');
14
+ function bootstrapHelp() {
15
+ const program = new commander_1.Command().name('awm');
16
+ (0, sensors_1.registerSensorsCommand)(program);
17
+ const sensors = program.commands.find(command => command.name() === 'sensors');
18
+ return sensors.commands.find(command => command.name() === 'bootstrap').helpInformation();
19
+ }
20
+ describe('Publication B portable bootstrap contract', () => {
21
+ it('exposes the exact explicit bootstrap syntax', () => {
22
+ const help = bootstrapHelp();
23
+ expect(help).toContain('--mode <mode>');
24
+ expect(help).toContain('--reason <text>');
25
+ expect(help).toContain('--dry-run');
26
+ });
27
+ it('documents schema v3, explicit migration, dry-run, and environment separation', () => {
28
+ const reference = fs_1.default.readFileSync(path_1.default.join(root, 'docs/cli-reference.md'), 'utf8');
29
+ expect(reference).toContain('awm sensors bootstrap [--mode project-sensors|native-gate|opt-out] [--reason <text>] [--dry-run]');
30
+ expect(reference).toContain('schemaVersion: 3');
31
+ expect(reference).toContain('never writes');
32
+ expect(reference).toContain('each environment updates its own AWM installation separately');
33
+ });
34
+ it('keeps all documented v3 declarations free of machine registry paths', () => {
35
+ const reference = fs_1.default.readFileSync(path_1.default.join(root, 'docs/cli-reference.md'), 'utf8');
36
+ const examples = Array.from(reference.matchAll(/```json\s*\n([\s\S]*?)```/g), match => JSON.parse(match[1]));
37
+ const v3 = examples.filter(example => example?.schemaVersion === 3);
38
+ expect(v3.length).toBeGreaterThan(0);
39
+ v3.forEach(example => {
40
+ expect(() => (0, manifest_1.parseSensorManifest)(example, 'documented v3 declaration')).not.toThrow();
41
+ expect(JSON.stringify(example)).not.toMatch(/registryRoot|[A-Za-z]:\\\\|\/(?:Users|home|srv)\//);
42
+ });
43
+ });
44
+ });
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "agentic-workflow-manager",
3
- "version": "9.4.1",
3
+ "version": "9.6.0",
4
4
  "main": "dist/src/index.js",
5
5
  "bin": {
6
6
  "awm": "./dist/src/index.js"
7
7
  },
8
8
  "files": [
9
- "dist"
9
+ "dist",
10
+ "prebuilds"
10
11
  ],
11
12
  "repository": {
12
13
  "type": "git",
@@ -17,6 +18,8 @@
17
18
  "doc": "docs"
18
19
  },
19
20
  "scripts": {
21
+ "native:build": "node-gyp rebuild --directory native && node scripts/package-native.js",
22
+ "native:package-check": "node scripts/assert-native-package.js",
20
23
  "build": "tsc && chmod +x dist/src/index.js",
21
24
  "typecheck": "tsc --noEmit",
22
25
  "lint": "eslint . --config eslint.config.awm.mjs --cache --format json",
@@ -59,6 +62,7 @@
59
62
  "eslint": "10.8.1",
60
63
  "jest": "^30.2.0",
61
64
  "js-yaml": "4.1.0",
65
+ "node-gyp": "^11.5.0",
62
66
  "ts-jest": "^29.4.6",
63
67
  "ts-node": "^10.9.2",
64
68
  "typescript": "^5.9.3"