agentic-workflow-manager 9.20.4 → 9.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/commands/change/index.js +14 -3
- package/dist/src/commands/preflight/checks.js +31 -0
- package/dist/src/commands/sensors/compatibility/contract.js +56 -3
- package/dist/src/commands/sensors/compatibility/manifest.js +16 -2
- package/dist/src/commands/sensors/index.js +10 -1
- package/dist/src/commands/sensors/init.js +7 -1
- package/dist/src/commands/sensors/inputs-digest.js +315 -0
- package/dist/src/commands/sensors/prepare.js +10 -0
- package/dist/src/commands/sensors/result-cache.js +278 -0
- package/dist/src/commands/sensors/result.js +31 -12
- package/dist/src/commands/sensors/run.js +265 -8
- package/dist/src/commands/sensors/status.js +66 -7
- package/dist/src/commands/watch/init.js +3 -15
- package/dist/src/core/awm-gitignore.js +211 -0
- package/dist/src/core/changes/receipt.js +219 -9
- package/dist/src/core/diagnostics/checks.js +11 -0
- package/dist/tests/commands/change/new.test.js +1 -1
- package/dist/tests/commands/change/validate-unmeasured.test.js +246 -0
- package/dist/tests/commands/preflight/awm-gitignore.test.js +52 -0
- package/dist/tests/commands/preflight/sensors-inputs.test.js +110 -0
- package/dist/tests/commands/sensors/compatibility/contract.test.js +60 -0
- package/dist/tests/commands/sensors/compatibility/manifest.test.js +94 -0
- package/dist/tests/commands/sensors/init-gitignore.test.js +59 -0
- package/dist/tests/commands/sensors/inputs-digest.test.js +373 -0
- package/dist/tests/commands/sensors/result-cache.test.js +352 -0
- package/dist/tests/commands/sensors/run-cache.test.js +434 -0
- package/dist/tests/commands/sensors/run-slice.test.js +171 -0
- package/dist/tests/commands/sensors/status-inputs.test.js +271 -0
- package/dist/tests/commands/watch/init-gitignore.test.js +34 -0
- package/dist/tests/commands/watch/track-freeze.test.js +11 -7
- package/dist/tests/commands/watch/watch-init.test.js +3 -1
- package/dist/tests/core/awm-gitignore.test.js +58 -0
- package/dist/tests/core/changes/receipt.test.js +261 -2
- package/dist/tests/core/diagnostics/awm-gitignore.test.js +101 -0
- package/dist/tests/core/ledger/gitignore.test.js +6 -1
- package/dist/tests/structural/sensor-execution-optimization-docs-contract.test.js +102 -0
- package/dist/tests/structural/sensor-slice-decision-contract.test.js +63 -0
- package/package.json +2 -1
- package/prebuilds/darwin-arm64/secure_fs.node +0 -0
- package/prebuilds/darwin-x64/secure_fs.node +0 -0
- package/prebuilds/win32-arm64/secure_fs.node +0 -0
- package/prebuilds/win32-x64/secure_fs.node +0 -0
|
@@ -0,0 +1,271 @@
|
|
|
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 os_1 = __importDefault(require("os"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const status_1 = require("../../../src/commands/sensors/status");
|
|
10
|
+
/**
|
|
11
|
+
* RF-3.4 — status surfaces a non-blocking warning when a configured enabled
|
|
12
|
+
* sensor lacks effective `inputs` (not cacheable). Warnings must not degrade
|
|
13
|
+
* `overall`, and opt-out / native-gate must stay silent.
|
|
14
|
+
*/
|
|
15
|
+
describe('computeSensorStatus inputs warnings (RF-3.4)', () => {
|
|
16
|
+
let tmpDir;
|
|
17
|
+
let previousHome;
|
|
18
|
+
let home;
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-status-inputs-'));
|
|
21
|
+
home = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-status-inputs-home-'));
|
|
22
|
+
previousHome = process.env.AWM_HOME;
|
|
23
|
+
process.env.AWM_HOME = home;
|
|
24
|
+
fs_1.default.writeFileSync(path_1.default.join(home, 'registries.json'), JSON.stringify([]));
|
|
25
|
+
fs_1.default.mkdirSync(path_1.default.join(tmpDir, 'node_modules', '.bin'), { recursive: true });
|
|
26
|
+
fs_1.default.writeFileSync(path_1.default.join(tmpDir, 'node_modules', '.bin', 'eslint'), '');
|
|
27
|
+
fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.awm'), { recursive: true });
|
|
28
|
+
});
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
if (previousHome === undefined)
|
|
31
|
+
delete process.env.AWM_HOME;
|
|
32
|
+
else
|
|
33
|
+
process.env.AWM_HOME = previousHome;
|
|
34
|
+
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
35
|
+
fs_1.default.rmSync(home, { recursive: true, force: true });
|
|
36
|
+
});
|
|
37
|
+
function writeOnlyProject(sensors) {
|
|
38
|
+
fs_1.default.writeFileSync(path_1.default.join(tmpDir, '.awm', 'sensors.json'), JSON.stringify({
|
|
39
|
+
schemaVersion: 3,
|
|
40
|
+
mode: 'project-sensors',
|
|
41
|
+
sensors,
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
function projectSensor(extra = {}) {
|
|
45
|
+
return {
|
|
46
|
+
source: 'project',
|
|
47
|
+
enabled: true,
|
|
48
|
+
command: { executable: 'eslint', resolution: 'node-modules-bin', args: ['.'] },
|
|
49
|
+
formatter: 'exit-code',
|
|
50
|
+
...extra,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
it('warns when an enabled sensor lacks effective inputs (not cacheable)', async () => {
|
|
54
|
+
writeOnlyProject({ custom: projectSensor() });
|
|
55
|
+
const result = await (0, status_1.computeSensorStatus)(tmpDir);
|
|
56
|
+
expect(result.overall).toBe('READY');
|
|
57
|
+
expect(result.warnings).toEqual(expect.arrayContaining([
|
|
58
|
+
expect.objectContaining({
|
|
59
|
+
sensor: 'custom',
|
|
60
|
+
detail: expect.stringMatching(/not cacheable|no effective inputs|lacks inputs/i),
|
|
61
|
+
}),
|
|
62
|
+
]));
|
|
63
|
+
});
|
|
64
|
+
it('does not warn when effective inputs are present', async () => {
|
|
65
|
+
writeOnlyProject({ custom: projectSensor({ inputs: ['**/*.ts'] }) });
|
|
66
|
+
const result = await (0, status_1.computeSensorStatus)(tmpDir);
|
|
67
|
+
expect(result.overall).toBe('READY');
|
|
68
|
+
expect(result.warnings ?? []).toEqual([]);
|
|
69
|
+
});
|
|
70
|
+
it('does not warn for disabled sensors that lack inputs', async () => {
|
|
71
|
+
writeOnlyProject({ custom: projectSensor({ enabled: false }) });
|
|
72
|
+
const result = await (0, status_1.computeSensorStatus)(tmpDir);
|
|
73
|
+
expect(result.warnings ?? []).toEqual([]);
|
|
74
|
+
expect(result.checks.custom).toMatchObject({ ok: true, detail: expect.stringMatching(/disabled/i) });
|
|
75
|
+
});
|
|
76
|
+
it.each([
|
|
77
|
+
['native-gate', 'CI is authoritative.'],
|
|
78
|
+
['opt-out', 'No local gate.'],
|
|
79
|
+
])('emits no spurious warnings for v3 %s', async (mode, reason) => {
|
|
80
|
+
fs_1.default.writeFileSync(path_1.default.join(tmpDir, '.awm', 'sensors.json'), JSON.stringify({
|
|
81
|
+
schemaVersion: 3, mode, reason,
|
|
82
|
+
}));
|
|
83
|
+
const result = await (0, status_1.computeSensorStatus)(tmpDir);
|
|
84
|
+
expect(result.mode).toBe(mode);
|
|
85
|
+
expect(result.warnings ?? []).toEqual([]);
|
|
86
|
+
});
|
|
87
|
+
it('uses pack-variant inputs as effective inputs (no warning when only the pack declares them)', async () => {
|
|
88
|
+
const registry = path_1.default.join(home, 'registries', 'baseline');
|
|
89
|
+
fs_1.default.mkdirSync(path_1.default.join(registry, 'sensor-packs', 'js-ts'), { recursive: true });
|
|
90
|
+
fs_1.default.writeFileSync(path_1.default.join(home, 'registries.json'), JSON.stringify([
|
|
91
|
+
{ name: 'baseline', remote: 'https://example.test/baseline.git' },
|
|
92
|
+
]));
|
|
93
|
+
fs_1.default.writeFileSync(path_1.default.join(registry, 'sensor-packs', 'js-ts', 'pack.json'), JSON.stringify({
|
|
94
|
+
schemaVersion: 2, name: 'js-ts', description: 'fixture', detects: ['package.json'],
|
|
95
|
+
coverage: {
|
|
96
|
+
schemaVersion: 1,
|
|
97
|
+
classes: {
|
|
98
|
+
lint: {
|
|
99
|
+
description: 'lint',
|
|
100
|
+
detectors: [{ sensor: 'lint' }],
|
|
101
|
+
remedy: { summary: 'fix', command: 'awm sensors init' },
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
sensors: {
|
|
106
|
+
lint: {
|
|
107
|
+
applicability: { allFiles: ['package.json'] },
|
|
108
|
+
variants: [{
|
|
109
|
+
id: 'eslint-10', priority: 10, certifiedRange: '>=10 <11',
|
|
110
|
+
requirements: {
|
|
111
|
+
tool: 'eslint', toolRange: '>=10 <11',
|
|
112
|
+
runtime: 'node', runtimeRange: '>=0',
|
|
113
|
+
},
|
|
114
|
+
assets: [], formatter: 'eslint-llm',
|
|
115
|
+
probe: { kind: 'package-script-present', script: 'lint' },
|
|
116
|
+
command: { executable: 'eslint', resolution: 'node-modules-bin', args: ['.'] },
|
|
117
|
+
inputs: ['src/**/*.ts'],
|
|
118
|
+
}],
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
}));
|
|
122
|
+
fs_1.default.writeFileSync(path_1.default.join(tmpDir, 'package.json'), JSON.stringify({
|
|
123
|
+
scripts: { lint: 'eslint .' },
|
|
124
|
+
devDependencies: { eslint: '^10.0.0' },
|
|
125
|
+
}));
|
|
126
|
+
fs_1.default.mkdirSync(path_1.default.join(tmpDir, 'node_modules', 'eslint'), { recursive: true });
|
|
127
|
+
fs_1.default.writeFileSync(path_1.default.join(tmpDir, 'node_modules', 'eslint', 'package.json'), JSON.stringify({ version: '10.0.0' }));
|
|
128
|
+
fs_1.default.writeFileSync(path_1.default.join(tmpDir, '.awm', 'sensors.json'), JSON.stringify({
|
|
129
|
+
schemaVersion: 2, pack: 'js-ts',
|
|
130
|
+
sensors: {
|
|
131
|
+
lint: {
|
|
132
|
+
enabled: true, variantId: 'eslint-10',
|
|
133
|
+
command: { executable: 'eslint', resolution: 'node-modules-bin', args: ['.'] },
|
|
134
|
+
assets: [],
|
|
135
|
+
initializedCompatibility: {
|
|
136
|
+
state: 'certified', reason: 'range-and-probe', variantId: 'eslint-10',
|
|
137
|
+
toolVersion: '10.0.0', runtimeVersion: process.versions.node,
|
|
138
|
+
certifiedRange: '>=10 <11', evidence: [],
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
}));
|
|
143
|
+
const result = await (0, status_1.computeSensorStatus)(tmpDir);
|
|
144
|
+
expect(result.checks.lint).toMatchObject({ ok: true });
|
|
145
|
+
expect(result.warnings ?? []).toEqual([]);
|
|
146
|
+
});
|
|
147
|
+
it('warns for pack-bound enabled sensors when neither project nor pack variant provides inputs', async () => {
|
|
148
|
+
const registry = path_1.default.join(home, 'registries', 'baseline');
|
|
149
|
+
fs_1.default.mkdirSync(path_1.default.join(registry, 'sensor-packs', 'js-ts'), { recursive: true });
|
|
150
|
+
fs_1.default.writeFileSync(path_1.default.join(home, 'registries.json'), JSON.stringify([
|
|
151
|
+
{ name: 'baseline', remote: 'https://example.test/baseline.git' },
|
|
152
|
+
]));
|
|
153
|
+
fs_1.default.writeFileSync(path_1.default.join(registry, 'sensor-packs', 'js-ts', 'pack.json'), JSON.stringify({
|
|
154
|
+
schemaVersion: 2, name: 'js-ts', description: 'fixture', detects: ['package.json'],
|
|
155
|
+
coverage: {
|
|
156
|
+
schemaVersion: 1,
|
|
157
|
+
classes: {
|
|
158
|
+
lint: {
|
|
159
|
+
description: 'lint',
|
|
160
|
+
detectors: [{ sensor: 'lint' }],
|
|
161
|
+
remedy: { summary: 'fix', command: 'awm sensors init' },
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
sensors: {
|
|
166
|
+
lint: {
|
|
167
|
+
applicability: { allFiles: ['package.json'] },
|
|
168
|
+
variants: [{
|
|
169
|
+
id: 'eslint-10', priority: 10, certifiedRange: '>=10 <11',
|
|
170
|
+
requirements: {
|
|
171
|
+
tool: 'eslint', toolRange: '>=10 <11',
|
|
172
|
+
runtime: 'node', runtimeRange: '>=0',
|
|
173
|
+
},
|
|
174
|
+
assets: [], formatter: 'eslint-llm',
|
|
175
|
+
probe: { kind: 'package-script-present', script: 'lint' },
|
|
176
|
+
command: { executable: 'eslint', resolution: 'node-modules-bin', args: ['.'] },
|
|
177
|
+
}],
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
}));
|
|
181
|
+
fs_1.default.writeFileSync(path_1.default.join(tmpDir, 'package.json'), JSON.stringify({
|
|
182
|
+
scripts: { lint: 'eslint .' },
|
|
183
|
+
devDependencies: { eslint: '^10.0.0' },
|
|
184
|
+
}));
|
|
185
|
+
fs_1.default.mkdirSync(path_1.default.join(tmpDir, 'node_modules', 'eslint'), { recursive: true });
|
|
186
|
+
fs_1.default.writeFileSync(path_1.default.join(tmpDir, 'node_modules', 'eslint', 'package.json'), JSON.stringify({ version: '10.0.0' }));
|
|
187
|
+
fs_1.default.writeFileSync(path_1.default.join(tmpDir, '.awm', 'sensors.json'), JSON.stringify({
|
|
188
|
+
schemaVersion: 2, pack: 'js-ts',
|
|
189
|
+
sensors: {
|
|
190
|
+
lint: {
|
|
191
|
+
enabled: true, variantId: 'eslint-10',
|
|
192
|
+
command: { executable: 'eslint', resolution: 'node-modules-bin', args: ['.'] },
|
|
193
|
+
assets: [],
|
|
194
|
+
initializedCompatibility: {
|
|
195
|
+
state: 'certified', reason: 'range-and-probe', variantId: 'eslint-10',
|
|
196
|
+
toolVersion: '10.0.0', runtimeVersion: process.versions.node,
|
|
197
|
+
certifiedRange: '>=10 <11', evidence: [],
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
}));
|
|
202
|
+
const result = await (0, status_1.computeSensorStatus)(tmpDir);
|
|
203
|
+
expect(result.overall).toBe('READY');
|
|
204
|
+
expect(result.warnings).toEqual(expect.arrayContaining([
|
|
205
|
+
expect.objectContaining({
|
|
206
|
+
sensor: 'lint',
|
|
207
|
+
detail: expect.stringMatching(/not cacheable|no effective inputs|lacks inputs/i),
|
|
208
|
+
}),
|
|
209
|
+
]));
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
describe('awm sensors status CLI renders inputs warnings (RF-3.4)', () => {
|
|
213
|
+
let tmpDir;
|
|
214
|
+
let previousHome;
|
|
215
|
+
let home;
|
|
216
|
+
let previousCwd;
|
|
217
|
+
beforeEach(() => {
|
|
218
|
+
previousCwd = process.cwd();
|
|
219
|
+
tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-status-inputs-cli-'));
|
|
220
|
+
home = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-status-inputs-cli-home-'));
|
|
221
|
+
previousHome = process.env.AWM_HOME;
|
|
222
|
+
process.env.AWM_HOME = home;
|
|
223
|
+
fs_1.default.writeFileSync(path_1.default.join(home, 'registries.json'), JSON.stringify([]));
|
|
224
|
+
fs_1.default.mkdirSync(path_1.default.join(tmpDir, 'node_modules', '.bin'), { recursive: true });
|
|
225
|
+
fs_1.default.writeFileSync(path_1.default.join(tmpDir, 'node_modules', '.bin', 'eslint'), '');
|
|
226
|
+
fs_1.default.mkdirSync(path_1.default.join(tmpDir, '.awm'), { recursive: true });
|
|
227
|
+
fs_1.default.writeFileSync(path_1.default.join(tmpDir, '.awm', 'sensors.json'), JSON.stringify({
|
|
228
|
+
schemaVersion: 3,
|
|
229
|
+
mode: 'project-sensors',
|
|
230
|
+
sensors: {
|
|
231
|
+
custom: {
|
|
232
|
+
source: 'project',
|
|
233
|
+
enabled: true,
|
|
234
|
+
command: { executable: 'eslint', resolution: 'node-modules-bin', args: ['.'] },
|
|
235
|
+
formatter: 'exit-code',
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
}));
|
|
239
|
+
process.chdir(tmpDir);
|
|
240
|
+
});
|
|
241
|
+
afterEach(() => {
|
|
242
|
+
process.chdir(previousCwd);
|
|
243
|
+
if (previousHome === undefined)
|
|
244
|
+
delete process.env.AWM_HOME;
|
|
245
|
+
else
|
|
246
|
+
process.env.AWM_HOME = previousHome;
|
|
247
|
+
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
248
|
+
fs_1.default.rmSync(home, { recursive: true, force: true });
|
|
249
|
+
});
|
|
250
|
+
it('prints warning text in human-readable status output without failing READY', async () => {
|
|
251
|
+
const { Command } = require('commander');
|
|
252
|
+
const { registerSensorsCommand } = require('../../../src/commands/sensors/index');
|
|
253
|
+
const program = new Command();
|
|
254
|
+
program.exitOverride();
|
|
255
|
+
registerSensorsCommand(program);
|
|
256
|
+
const output = jest.spyOn(console, 'log').mockImplementation(() => undefined);
|
|
257
|
+
const exitSpy = jest.spyOn(process, 'exit').mockImplementation((() => undefined));
|
|
258
|
+
try {
|
|
259
|
+
await program.parseAsync(['node', 'awm', 'sensors', 'status']);
|
|
260
|
+
const rendered = output.mock.calls.flat().join('\n');
|
|
261
|
+
expect(rendered).toMatch(/READY/);
|
|
262
|
+
expect(rendered).toMatch(/custom/);
|
|
263
|
+
expect(rendered).toMatch(/not cacheable|no effective inputs|lacks inputs/i);
|
|
264
|
+
expect(exitSpy).not.toHaveBeenCalled();
|
|
265
|
+
}
|
|
266
|
+
finally {
|
|
267
|
+
output.mockRestore();
|
|
268
|
+
exitSpy.mockRestore();
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
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 os_1 = __importDefault(require("os"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const child_process_1 = require("child_process");
|
|
10
|
+
const init_1 = require("../../../src/commands/watch/init");
|
|
11
|
+
const awm_gitignore_1 = require("../../../src/core/awm-gitignore");
|
|
12
|
+
const git_1 = require("../../../src/core/tracks/git");
|
|
13
|
+
describe('initWatch managed awm gitignore (GI-2/GI-10)', () => {
|
|
14
|
+
let repo;
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
repo = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-watch-gi-'));
|
|
17
|
+
(0, child_process_1.execFileSync)('git', ['init'], { cwd: repo, stdio: 'pipe' });
|
|
18
|
+
fs_1.default.writeFileSync(path_1.default.join(repo, 'package.json'), JSON.stringify({ scripts: { test: 'jest' } }));
|
|
19
|
+
});
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
fs_1.default.rmSync(repo, { recursive: true, force: true });
|
|
22
|
+
});
|
|
23
|
+
it('writes the managed block instead of a bare .awm/ allow-nothing rule', () => {
|
|
24
|
+
(0, init_1.initWatch)(repo, 'rama');
|
|
25
|
+
const gi = fs_1.default.readFileSync(path_1.default.join(repo, '.gitignore'), 'utf8');
|
|
26
|
+
expect(gi).toContain(awm_gitignore_1.AWM_GITIGNORE_BEGIN);
|
|
27
|
+
expect(gi).toContain(awm_gitignore_1.AWM_GITIGNORE_END);
|
|
28
|
+
expect(gi).toContain('!.awm/sensors.json');
|
|
29
|
+
expect(gi).toContain('!.awm/profile.json');
|
|
30
|
+
const lines = gi.split('\n').map((l) => l.trim()).filter(Boolean);
|
|
31
|
+
expect(lines.filter((l) => l === '.awm' || l === '.awm/')).toEqual([]);
|
|
32
|
+
expect((0, git_1.isAwmGitignored)(repo)).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -301,13 +301,10 @@ describe('Supervisor.tick() — freeze de un track individual (R5.2/R6.3)', () =
|
|
|
301
301
|
}
|
|
302
302
|
function setUpGreenTrack() {
|
|
303
303
|
initCompactWatch(repo, BRANCH);
|
|
304
|
-
// `initWatch`
|
|
305
|
-
//
|
|
306
|
-
//
|
|
307
|
-
//
|
|
308
|
-
// commit, `isWorktreeClean` nunca sería `true` y el freeze jamás
|
|
309
|
-
// convergería — no por un bug del driver, sino por un repo de test
|
|
310
|
-
// que nunca terminó su propio setup.
|
|
304
|
+
// `initWatch` upserts the managed `# AWM:GITIGNORE` block but does not
|
|
305
|
+
// commit it — an untracked/modified `.gitignore` keeps the worktree
|
|
306
|
+
// dirty for R6.4's clean check. Commit the bootstrap side-effect so
|
|
307
|
+
// freeze can converge.
|
|
311
308
|
git(repo, 'add', '.gitignore');
|
|
312
309
|
git(repo, 'commit', '-qm', 'gitignore .awm');
|
|
313
310
|
const s = (0, store_1.readJournal)(repo, BRANCH).state;
|
|
@@ -526,6 +523,13 @@ describe('loop end-to-end: plan (reconcileTracks) + track (Supervisor.tick) cier
|
|
|
526
523
|
test('reconcileTracks real (plan) + Supervisor.tick real (track), en loop contra dos worktrees reales, cierran request -> drenaje -> persist -> observación -> FROZEN', async () => {
|
|
527
524
|
// --- lado TRACK: registrar el trabajo y drenarlo con un Supervisor REAL ---
|
|
528
525
|
initCompactWatch(trackWorktree, TRACK_BRANCH);
|
|
526
|
+
// `initWatch` migrates the seeded `.awm/` ignore into the managed
|
|
527
|
+
// `.awm/*` allowlist block. That (1) dirties `.gitignore` and (2)
|
|
528
|
+
// un-ignores `.awm/sensors.json`, which then shows up as untracked.
|
|
529
|
+
// Both must be committed or R6.4's clean-worktree gate never passes
|
|
530
|
+
// and this loop times out (same class of fixture bug as setUpGreenTrack).
|
|
531
|
+
git(trackWorktree, 'add', '.gitignore', '.awm/sensors.json');
|
|
532
|
+
git(trackWorktree, 'commit', '-qm', 'awm gitignore + sensors');
|
|
529
533
|
const ts0 = (0, store_1.readJournal)(trackWorktree, TRACK_BRANCH).state;
|
|
530
534
|
ts0.trackContext = { trackId: 'a', taskIds: ['T1'], planDigest: '', baseSha, planJournalId: 'j-plan' };
|
|
531
535
|
(0, store_1.writeJournal)(trackWorktree, TRACK_BRANCH, ts0);
|
|
@@ -154,7 +154,9 @@ describe('watch --init: plan-vs-repo mecanico', () => {
|
|
|
154
154
|
const out = (0, init_1.initWatch)(repo, 'rama');
|
|
155
155
|
expect(out.requiredVerifiers).toEqual(['test']);
|
|
156
156
|
expect((0, store_1.readJournal)(repo, 'rama').state.requiredVerifiers).toEqual(['test']);
|
|
157
|
-
|
|
157
|
+
const gi = fs_1.default.readFileSync(path_1.default.join(repo, '.gitignore'), 'utf8');
|
|
158
|
+
expect(gi).toContain('# AWM:GITIGNORE:BEGIN');
|
|
159
|
+
expect(gi).toContain('.awm/*');
|
|
158
160
|
expect(() => (0, init_1.initWatch)(repo, 'rama')).not.toThrow(); // idempotente
|
|
159
161
|
});
|
|
160
162
|
test('no escribe .gitignore cuando es un symlink', () => {
|
|
@@ -0,0 +1,58 @@
|
|
|
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 os_1 = __importDefault(require("os"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const awm_gitignore_1 = require("../../src/core/awm-gitignore");
|
|
10
|
+
describe('awm-gitignore managed block', () => {
|
|
11
|
+
let tmp;
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
tmp = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-gi-'));
|
|
14
|
+
});
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
fs_1.default.rmSync(tmp, { recursive: true, force: true });
|
|
17
|
+
});
|
|
18
|
+
it('renders the canonical managed block with versioned allowlist entries', () => {
|
|
19
|
+
expect((0, awm_gitignore_1.renderManagedBlock)()).toBe([
|
|
20
|
+
awm_gitignore_1.AWM_GITIGNORE_BEGIN,
|
|
21
|
+
'.awm/*',
|
|
22
|
+
'!.awm/sensors.json',
|
|
23
|
+
'!.awm/sensors.baseline.json',
|
|
24
|
+
'!.awm/profile.json',
|
|
25
|
+
'!.awm/context-budget.json',
|
|
26
|
+
'!.awm/context/',
|
|
27
|
+
'.awm/context/*',
|
|
28
|
+
'!.awm/context/index.json',
|
|
29
|
+
awm_gitignore_1.AWM_GITIGNORE_END,
|
|
30
|
+
'',
|
|
31
|
+
].join('\n'));
|
|
32
|
+
});
|
|
33
|
+
it('creates .gitignore with the managed block and is idempotent', () => {
|
|
34
|
+
(0, awm_gitignore_1.upsertAwmGitignore)(tmp);
|
|
35
|
+
const first = fs_1.default.readFileSync(path_1.default.join(tmp, '.gitignore'), 'utf8');
|
|
36
|
+
expect(first).toContain(awm_gitignore_1.AWM_GITIGNORE_BEGIN);
|
|
37
|
+
expect(first).toContain(awm_gitignore_1.AWM_GITIGNORE_END);
|
|
38
|
+
(0, awm_gitignore_1.upsertAwmGitignore)(tmp);
|
|
39
|
+
expect(fs_1.default.readFileSync(path_1.default.join(tmp, '.gitignore'), 'utf8')).toBe(first);
|
|
40
|
+
});
|
|
41
|
+
it('migrates bare .awm/ and .awm lines into the managed block without dropping unrelated rules', () => {
|
|
42
|
+
fs_1.default.writeFileSync(path_1.default.join(tmp, '.gitignore'), 'node_modules\n.awm/\ndist\n');
|
|
43
|
+
(0, awm_gitignore_1.upsertAwmGitignore)(tmp);
|
|
44
|
+
const text = fs_1.default.readFileSync(path_1.default.join(tmp, '.gitignore'), 'utf8');
|
|
45
|
+
expect(text).toContain('node_modules');
|
|
46
|
+
expect(text).toContain('dist');
|
|
47
|
+
expect(text).toContain(awm_gitignore_1.AWM_GITIGNORE_BEGIN);
|
|
48
|
+
const outside = text.slice(0, text.indexOf(awm_gitignore_1.AWM_GITIGNORE_BEGIN));
|
|
49
|
+
expect(outside.split('\n').map((l) => l.trim()).filter(Boolean)).not.toContain('.awm/');
|
|
50
|
+
expect(outside.split('\n').map((l) => l.trim()).filter(Boolean)).not.toContain('.awm');
|
|
51
|
+
});
|
|
52
|
+
it('refuses a symlink .gitignore', () => {
|
|
53
|
+
const target = path_1.default.join(tmp, 'ignore-target');
|
|
54
|
+
fs_1.default.writeFileSync(target, 'x\n');
|
|
55
|
+
fs_1.default.symlinkSync(target, path_1.default.join(tmp, '.gitignore'));
|
|
56
|
+
expect(() => (0, awm_gitignore_1.upsertAwmGitignore)(tmp)).toThrow(/symlink/i);
|
|
57
|
+
});
|
|
58
|
+
});
|