@principles/pd-cli 1.147.10 → 1.147.12
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/commands/runtime-internalization-run-once.d.ts.map +1 -1
- package/dist/commands/runtime-internalization-run-once.js +16 -2
- package/dist/commands/runtime-internalization-run-once.js.map +1 -1
- package/dist/commands/runtime-recovery-failed-tasks.d.ts.map +1 -1
- package/dist/commands/runtime-recovery-failed-tasks.js +82 -6
- package/dist/commands/runtime-recovery-failed-tasks.js.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/runtime-internalization-run-once.ts +16 -2
- package/src/commands/runtime-recovery-failed-tasks.ts +83 -6
- package/src/index.ts +6 -3
- package/tests/commands/cli-help-snapshot.test.ts +7 -1
- package/tests/commands/runtime-internalization-run-once-rollout-parity.test.ts +550 -0
- package/tests/commands/runtime-internalization-run-once.test.ts +21 -2
- package/tests/commands/runtime-recovery-failed-tasks.test.ts +136 -9
|
@@ -8,15 +8,23 @@ vi.mock('../../src/resolve-workspace.js', () => ({
|
|
|
8
8
|
resolveWorkspaceDir: vi.fn().mockReturnValue('/fake/workspace'),
|
|
9
9
|
}));
|
|
10
10
|
|
|
11
|
-
vi.mock('@principles/core/runtime-v2', () =>
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
vi.mock('@principles/core/runtime-v2', async (importOriginal) => {
|
|
12
|
+
// Keep the real kind guards (isPeerRunnerKind / isDiagnosticianStageKind):
|
|
13
|
+
// the per-kind execution guidance under test must exercise the actual
|
|
14
|
+
// classification, not a test-local reimplementation. Only the I/O-bearing
|
|
15
|
+
// service factory is replaced.
|
|
16
|
+
const actual = await importOriginal<typeof import('@principles/core/runtime-v2')>();
|
|
17
|
+
return {
|
|
18
|
+
...actual,
|
|
19
|
+
createRecoverySweepService: vi.fn().mockResolvedValue({
|
|
20
|
+
service: {
|
|
21
|
+
detectFailedTasks: mockDetectFailedTasks,
|
|
22
|
+
recoverFailedTask: mockRecoverFailedTask,
|
|
23
|
+
},
|
|
24
|
+
close: mockServiceClose,
|
|
25
|
+
}),
|
|
26
|
+
};
|
|
27
|
+
});
|
|
20
28
|
|
|
21
29
|
import { handleRuntimeRecoveryFailedTasks } from '../../src/commands/runtime-recovery-failed-tasks.js';
|
|
22
30
|
|
|
@@ -179,6 +187,7 @@ describe('pd runtime recovery failed-tasks command contract', () => {
|
|
|
179
187
|
maxAttempts: 3,
|
|
180
188
|
isExhausted: false,
|
|
181
189
|
status: 'failed',
|
|
190
|
+
inputRef: null,
|
|
182
191
|
},
|
|
183
192
|
]);
|
|
184
193
|
mockRecoverFailedTask.mockResolvedValue(null);
|
|
@@ -198,4 +207,122 @@ describe('pd runtime recovery failed-tasks command contract', () => {
|
|
|
198
207
|
reason: expect.stringContaining('no longer failed or concurrently modified'),
|
|
199
208
|
});
|
|
200
209
|
});
|
|
210
|
+
|
|
211
|
+
// ── PRI-674 review P2: per-kind execution guidance ────────────────────────
|
|
212
|
+
// Guidance must name commands that actually support the task kind:
|
|
213
|
+
// internalization → run-once; diagnostician parent / diag_* stage → diagnose run.
|
|
214
|
+
|
|
215
|
+
describe('per-kind execution nextAction (review P2)', () => {
|
|
216
|
+
it('diagnostician parent task points at pd diagnose run', async () => {
|
|
217
|
+
mockDetectFailedTasks.mockResolvedValue([
|
|
218
|
+
{ taskId: 'diagnosis_pain-001', taskKind: 'diagnostician', attemptCount: 1, maxAttempts: 3, isExhausted: false, status: 'failed', inputRef: 'pain-001' },
|
|
219
|
+
]);
|
|
220
|
+
|
|
221
|
+
await handleRuntimeRecoveryFailedTasks({ workspace: '/fake/workspace', confirm: true, json: true });
|
|
222
|
+
|
|
223
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string);
|
|
224
|
+
const nextAction: string = output.tasks[0].nextAction;
|
|
225
|
+
expect(nextAction).toContain('pd diagnose run --task-id diagnosis_pain-001');
|
|
226
|
+
expect(nextAction).not.toContain('internalization run-once');
|
|
227
|
+
// Summary must not claim a single internalization command executes everything
|
|
228
|
+
expect(output.nextAction).toContain('pd diagnose run');
|
|
229
|
+
expect(output.nextAction).not.toContain('run-once to execute recovered tasks');
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('diag_* stage task resolves parent from inputRef linkage', async () => {
|
|
233
|
+
mockDetectFailedTasks.mockResolvedValue([
|
|
234
|
+
{ taskId: 'diag_rootcause-diagnosis_pain-001', taskKind: 'diag_rootcause', attemptCount: 1, maxAttempts: 3, isExhausted: false, status: 'failed', inputRef: 'diagnosis_pain-001' },
|
|
235
|
+
]);
|
|
236
|
+
|
|
237
|
+
await handleRuntimeRecoveryFailedTasks({ workspace: '/fake/workspace', confirm: true, json: true });
|
|
238
|
+
|
|
239
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string);
|
|
240
|
+
const nextAction: string = output.tasks[0].nextAction;
|
|
241
|
+
expect(nextAction).toContain('diagnosis_pain-001');
|
|
242
|
+
expect(nextAction).toContain('pd diagnose run --task-id diagnosis_pain-001');
|
|
243
|
+
expect(nextAction).not.toContain('run-once');
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('diag_* stage task falls back to diag_<stage>-<parent> ID convention when inputRef is null', async () => {
|
|
247
|
+
mockDetectFailedTasks.mockResolvedValue([
|
|
248
|
+
{ taskId: 'diag_router-diagnosis_pain-007', taskKind: 'diag_router', attemptCount: 1, maxAttempts: 3, isExhausted: false, status: 'failed', inputRef: null },
|
|
249
|
+
]);
|
|
250
|
+
|
|
251
|
+
await handleRuntimeRecoveryFailedTasks({ workspace: '/fake/workspace', confirm: true, json: true });
|
|
252
|
+
|
|
253
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string);
|
|
254
|
+
const nextAction: string = output.tasks[0].nextAction;
|
|
255
|
+
expect(nextAction).toContain('diagnosis_pain-007');
|
|
256
|
+
expect(nextAction).toContain('pd diagnose run --task-id diagnosis_pain-007');
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('diag_* stage task with no inputRef and non-conventional ID reports unresolved parent instead of a guessed command', async () => {
|
|
260
|
+
mockDetectFailedTasks.mockResolvedValue([
|
|
261
|
+
{ taskId: 'weird-stage-id', taskKind: 'diag_distiller', attemptCount: 1, maxAttempts: 3, isExhausted: false, status: 'failed', inputRef: null },
|
|
262
|
+
]);
|
|
263
|
+
|
|
264
|
+
await handleRuntimeRecoveryFailedTasks({ workspace: '/fake/workspace', confirm: true, json: true });
|
|
265
|
+
|
|
266
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string);
|
|
267
|
+
const nextAction: string = output.tasks[0].nextAction;
|
|
268
|
+
expect(nextAction).toContain('could not be resolved');
|
|
269
|
+
expect(nextAction).toContain('pd diagnose status --task-id weird-stage-id');
|
|
270
|
+
expect(nextAction).not.toContain('pd diagnose run --task-id');
|
|
271
|
+
expect(nextAction).not.toContain('run-once');
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('internalization task points at run-once with its runner kind', async () => {
|
|
275
|
+
mockDetectFailedTasks.mockResolvedValue([
|
|
276
|
+
{ taskId: 'pi-task-1', taskKind: 'scribe', attemptCount: 1, maxAttempts: 3, isExhausted: false, status: 'failed', inputRef: null },
|
|
277
|
+
]);
|
|
278
|
+
|
|
279
|
+
await handleRuntimeRecoveryFailedTasks({ workspace: '/fake/workspace', confirm: true, json: true });
|
|
280
|
+
|
|
281
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string);
|
|
282
|
+
const nextAction: string = output.tasks[0].nextAction;
|
|
283
|
+
expect(nextAction).toContain('pd runtime internalization run-once --runner scribe');
|
|
284
|
+
expect(nextAction).not.toContain('pd diagnose run');
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it('mixed recovery summary splits guidance per executor instead of one command for all', async () => {
|
|
288
|
+
mockDetectFailedTasks.mockResolvedValue([
|
|
289
|
+
{ taskId: 'diagnosis_pain-001', taskKind: 'diagnostician', attemptCount: 1, maxAttempts: 3, isExhausted: false, status: 'failed', inputRef: 'pain-001' },
|
|
290
|
+
{ taskId: 'diag_rootcause-diagnosis_pain-001', taskKind: 'diag_rootcause', attemptCount: 1, maxAttempts: 3, isExhausted: false, status: 'failed', inputRef: 'diagnosis_pain-001' },
|
|
291
|
+
{ taskId: 'pi-task-1', taskKind: 'dreamer', attemptCount: 1, maxAttempts: 3, isExhausted: false, status: 'failed', inputRef: null },
|
|
292
|
+
{ taskId: 'pi-task-2', taskKind: 'rollout_reviewer', attemptCount: 1, maxAttempts: 3, isExhausted: false, status: 'failed', inputRef: null },
|
|
293
|
+
]);
|
|
294
|
+
|
|
295
|
+
await handleRuntimeRecoveryFailedTasks({ workspace: '/fake/workspace', confirm: true, json: true });
|
|
296
|
+
|
|
297
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string);
|
|
298
|
+
expect(output.recoveredCount).toBe(4);
|
|
299
|
+
const summary: string = output.nextAction;
|
|
300
|
+
expect(summary).toContain('diagnostician parent task(s) (diagnosis_pain-001)');
|
|
301
|
+
expect(summary).toContain('pd diagnose run --task-id <taskId>');
|
|
302
|
+
expect(summary).toContain('diag_* stage task(s)');
|
|
303
|
+
expect(summary).toContain('internalization task(s) (pi-task-1, pi-task-2)');
|
|
304
|
+
expect(summary).toContain('run-once --runner <kind>');
|
|
305
|
+
// No stale single-command claim
|
|
306
|
+
expect(summary).not.toContain('Run pd runtime internalization run-once to execute recovered tasks');
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it('guidance matches real command registrations (help wiring contract)', async () => {
|
|
310
|
+
// cli-7-test-wiring: the commands we recommend must actually be
|
|
311
|
+
// registered with the flags we name. Check the built CLI's help.
|
|
312
|
+
const { execFileSync } = await import('node:child_process');
|
|
313
|
+
const { getBuiltPdCliPath } = await import('../helpers/pd-cli-path.js');
|
|
314
|
+
const cliPath = getBuiltPdCliPath();
|
|
315
|
+
|
|
316
|
+
const diagnoseHelp = execFileSync('node', [cliPath, 'diagnose', 'run', '--help'], { encoding: 'utf8' });
|
|
317
|
+
expect(diagnoseHelp).toContain('--task-id');
|
|
318
|
+
expect(diagnoseHelp).toContain('--workspace');
|
|
319
|
+
|
|
320
|
+
const runOnceHelp = execFileSync('node', [cliPath, 'runtime', 'internalization', 'run-once', '--help'], { encoding: 'utf8' });
|
|
321
|
+
expect(runOnceHelp).toContain('--runner');
|
|
322
|
+
// run-once must NOT advertise diagnostician support: the supported list
|
|
323
|
+
// comes from SUPPORTED_RUNNERS (peer runners only)
|
|
324
|
+
expect(runOnceHelp).toMatch(/dreamer/);
|
|
325
|
+
expect(runOnceHelp).not.toMatch(/diag_|diagnostician/);
|
|
326
|
+
});
|
|
327
|
+
});
|
|
201
328
|
});
|