@principles/pd-cli 1.80.0 → 1.82.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/commands/diagnose.d.ts.map +1 -1
- package/dist/commands/diagnose.js +13 -1
- package/dist/commands/diagnose.js.map +1 -1
- package/dist/commands/pain-retry.d.ts +19 -0
- package/dist/commands/pain-retry.d.ts.map +1 -0
- package/dist/commands/pain-retry.js +480 -0
- package/dist/commands/pain-retry.js.map +1 -0
- package/dist/commands/runtime-internalization-run-once.d.ts.map +1 -1
- package/dist/commands/runtime-internalization-run-once.js +54 -8
- package/dist/commands/runtime-internalization-run-once.js.map +1 -1
- package/dist/index.js +22 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/diagnose.ts +15 -1
- package/src/commands/pain-retry.ts +547 -0
- package/src/commands/runtime-internalization-run-once.ts +60 -9
- package/src/index.ts +23 -1
- package/tests/commands/pain-retry.test.ts +767 -0
- package/tests/commands/runtime-internalization-run-once.test.ts +287 -15
|
@@ -535,7 +535,7 @@ describe('handleRuntimeInternalizationRunOnce', () => {
|
|
|
535
535
|
expect(text).toContain('resultRef: philosopher://run-phil-002');
|
|
536
536
|
});
|
|
537
537
|
|
|
538
|
-
it('successful dreamer
|
|
538
|
+
it('auto-enqueue: successful dreamer returns successor info by default', async () => {
|
|
539
539
|
mockWakeOnce.mockResolvedValue({
|
|
540
540
|
decision: 'would_lease',
|
|
541
541
|
taskId: 'task-dreamer-enq-001',
|
|
@@ -564,11 +564,11 @@ describe('handleRuntimeInternalizationRunOnce', () => {
|
|
|
564
564
|
|
|
565
565
|
const output = JSON.parse(consoleLogSpy.mock.calls[0][0]);
|
|
566
566
|
expect(output.enqueueDecision).toBe('successor_created');
|
|
567
|
-
expect(output.
|
|
567
|
+
expect(output.successorTaskIds![0]).toBe('task-phil-enq-001');
|
|
568
568
|
expect(output.successorKind).toBe('philosopher');
|
|
569
569
|
});
|
|
570
570
|
|
|
571
|
-
it('repeated
|
|
571
|
+
it('auto-enqueue: repeated run returns existing successorTaskId', async () => {
|
|
572
572
|
mockWakeOnce.mockResolvedValue({
|
|
573
573
|
decision: 'would_lease',
|
|
574
574
|
taskId: 'task-dreamer-enq-002',
|
|
@@ -597,11 +597,11 @@ describe('handleRuntimeInternalizationRunOnce', () => {
|
|
|
597
597
|
|
|
598
598
|
const output = JSON.parse(consoleLogSpy.mock.calls[0][0]);
|
|
599
599
|
expect(output.enqueueDecision).toBe('successor_exists');
|
|
600
|
-
expect(output.
|
|
600
|
+
expect(output.successorTaskIds![0]).toBe('task-phil-enq-002');
|
|
601
601
|
expect(output.successorKind).toBe('philosopher');
|
|
602
602
|
});
|
|
603
603
|
|
|
604
|
-
it('
|
|
604
|
+
it('auto-enqueue: no_successor does not set successor info', async () => {
|
|
605
605
|
mockWakeOnce.mockResolvedValue({
|
|
606
606
|
decision: 'would_lease',
|
|
607
607
|
taskId: 'task-dreamer-enq-003',
|
|
@@ -629,10 +629,10 @@ describe('handleRuntimeInternalizationRunOnce', () => {
|
|
|
629
629
|
|
|
630
630
|
const output = JSON.parse(consoleLogSpy.mock.calls[0][0]);
|
|
631
631
|
expect(output.enqueueDecision).toBe('no_successor');
|
|
632
|
-
expect(output.
|
|
632
|
+
expect(output.successorTaskIds).toEqual([]);
|
|
633
633
|
});
|
|
634
634
|
|
|
635
|
-
it('
|
|
635
|
+
it('auto-enqueue: failed run does not call commitNextTaskProposal', async () => {
|
|
636
636
|
mockWakeOnce.mockResolvedValue({
|
|
637
637
|
decision: 'would_lease',
|
|
638
638
|
taskId: 'task-dreamer-enq-004',
|
|
@@ -652,7 +652,7 @@ describe('handleRuntimeInternalizationRunOnce', () => {
|
|
|
652
652
|
expect(mockCommitNextTaskProposal).not.toHaveBeenCalled();
|
|
653
653
|
});
|
|
654
654
|
|
|
655
|
-
it('
|
|
655
|
+
it('auto-enqueue without --allow-test-double still blocked', async () => {
|
|
656
656
|
await handleRuntimeInternalizationRunOnce({ workspace: WS, runner: 'dreamer', runtime: 'test-double', enqueueNext: true });
|
|
657
657
|
|
|
658
658
|
expect(process.exitCode).toBe(1);
|
|
@@ -880,7 +880,7 @@ describe('handleRuntimeInternalizationRunOnce', () => {
|
|
|
880
880
|
expect(output.runnerResult.status).toBe('succeeded');
|
|
881
881
|
});
|
|
882
882
|
|
|
883
|
-
it('--runner scribe
|
|
883
|
+
it('auto-enqueue: --runner scribe creates artificer successor', async () => {
|
|
884
884
|
mockWakeOnce.mockResolvedValue({
|
|
885
885
|
decision: 'would_lease',
|
|
886
886
|
taskId: 'task-scribe-enq-001',
|
|
@@ -916,7 +916,7 @@ describe('handleRuntimeInternalizationRunOnce', () => {
|
|
|
916
916
|
|
|
917
917
|
const output = JSON.parse(consoleLogSpy.mock.calls[0][0]);
|
|
918
918
|
expect(output.enqueueDecision).toBe('successor_created');
|
|
919
|
-
expect(output.
|
|
919
|
+
expect(output.successorTaskIds![0]).toBe('task-artificer-enq-001');
|
|
920
920
|
expect(output.successorKind).toBe('artificer');
|
|
921
921
|
});
|
|
922
922
|
|
|
@@ -979,7 +979,7 @@ describe('handleRuntimeInternalizationRunOnce', () => {
|
|
|
979
979
|
expect(output.runnerResult.status).toBe('succeeded');
|
|
980
980
|
});
|
|
981
981
|
|
|
982
|
-
it('--runner artificer
|
|
982
|
+
it('auto-enqueue: --runner artificer returns successor decision', async () => {
|
|
983
983
|
mockWakeOnce.mockResolvedValue({
|
|
984
984
|
decision: 'would_lease',
|
|
985
985
|
taskId: 'task-artificer-enq-001',
|
|
@@ -1022,7 +1022,7 @@ describe('handleRuntimeInternalizationRunOnce', () => {
|
|
|
1022
1022
|
|
|
1023
1023
|
const output = JSON.parse(consoleLogSpy.mock.calls[0][0]);
|
|
1024
1024
|
expect(output.enqueueDecision).toBe('successor_created');
|
|
1025
|
-
expect(output.
|
|
1025
|
+
expect(output.successorTaskIds![0]).toBe('task-evaluator-enq-001');
|
|
1026
1026
|
expect(output.successorKind).toBe('evaluator');
|
|
1027
1027
|
});
|
|
1028
1028
|
|
|
@@ -1112,7 +1112,7 @@ describe('handleRuntimeInternalizationRunOnce', () => {
|
|
|
1112
1112
|
expect(output.runnerResult.status).toBe('succeeded');
|
|
1113
1113
|
});
|
|
1114
1114
|
|
|
1115
|
-
it('--runner evaluator
|
|
1115
|
+
it('auto-enqueue: --runner evaluator returns successor decision', async () => {
|
|
1116
1116
|
mockWakeOnce.mockResolvedValue({
|
|
1117
1117
|
decision: 'would_lease',
|
|
1118
1118
|
taskId: 'task-evaluator-enq-001',
|
|
@@ -1155,7 +1155,7 @@ describe('handleRuntimeInternalizationRunOnce', () => {
|
|
|
1155
1155
|
|
|
1156
1156
|
const output = JSON.parse(consoleLogSpy.mock.calls[0][0]);
|
|
1157
1157
|
expect(output.enqueueDecision).toBe('successor_created');
|
|
1158
|
-
expect(output.
|
|
1158
|
+
expect(output.successorTaskIds![0]).toBe('task-rollout-reviewer-enq-001');
|
|
1159
1159
|
expect(output.successorKind).toBe('rollout_reviewer');
|
|
1160
1160
|
});
|
|
1161
1161
|
|
|
@@ -1208,7 +1208,7 @@ describe('handleRuntimeInternalizationRunOnce', () => {
|
|
|
1208
1208
|
expect(output.runnerResult.status).toBe('succeeded');
|
|
1209
1209
|
});
|
|
1210
1210
|
|
|
1211
|
-
it('--runner rollout_reviewer
|
|
1211
|
+
it('auto-enqueue: --runner rollout_reviewer returns no_successor for prompt channel', async () => {
|
|
1212
1212
|
mockWakeOnce.mockResolvedValue({
|
|
1213
1213
|
decision: 'would_lease',
|
|
1214
1214
|
taskId: 'task-rollout-reviewer-enq-001',
|
|
@@ -1315,4 +1315,276 @@ describe('handleRuntimeInternalizationRunOnce', () => {
|
|
|
1315
1315
|
expect(output.nextAction).toBeTruthy();
|
|
1316
1316
|
expect(process.exitCode).toBe(1);
|
|
1317
1317
|
});
|
|
1318
|
+
|
|
1319
|
+
// === Default enqueue successor tests ===
|
|
1320
|
+
|
|
1321
|
+
it('default behavior (no --no-enqueue-next) auto-enqueues successor on runner success', async () => {
|
|
1322
|
+
mockWakeOnce.mockResolvedValue({
|
|
1323
|
+
decision: 'would_lease',
|
|
1324
|
+
taskId: 'task-dreamer-auto-001',
|
|
1325
|
+
taskKind: 'dreamer',
|
|
1326
|
+
});
|
|
1327
|
+
|
|
1328
|
+
mockRun.mockResolvedValue({
|
|
1329
|
+
status: 'succeeded',
|
|
1330
|
+
taskId: 'task-dreamer-auto-001',
|
|
1331
|
+
runId: 'run-auto-001',
|
|
1332
|
+
artifactId: 'pi-art-auto-001',
|
|
1333
|
+
resultRef: 'dreamer://run-auto-001',
|
|
1334
|
+
contextHash: 'ctx-auto',
|
|
1335
|
+
output: { valid: true, taskId: 'task-dreamer-auto-001', candidates: VALID_DREAMER_CANDIDATES, contextRefs: [], generatedAt: new Date().toISOString() },
|
|
1336
|
+
attemptCount: 1,
|
|
1337
|
+
});
|
|
1338
|
+
|
|
1339
|
+
mockCommitNextTaskProposal.mockResolvedValue({
|
|
1340
|
+
decision: 'successor_created',
|
|
1341
|
+
sourceTaskId: 'task-dreamer-auto-001',
|
|
1342
|
+
successorTaskId: 'task-phil-auto-001',
|
|
1343
|
+
successorKind: 'philosopher',
|
|
1344
|
+
});
|
|
1345
|
+
|
|
1346
|
+
// No enqueueNext specified — default should auto-enqueue
|
|
1347
|
+
await handleRuntimeInternalizationRunOnce({ workspace: WS, runner: 'dreamer', runtime: 'test-double', allowTestDouble: true, json: true });
|
|
1348
|
+
|
|
1349
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0]);
|
|
1350
|
+
expect(output.successorEnqueueAttempted).toBe(true);
|
|
1351
|
+
expect(output.successorTasksCreated).toBe(1);
|
|
1352
|
+
expect(output.successorTaskIds).toContain('task-phil-auto-001');
|
|
1353
|
+
expect(output.enqueueDecision).toBe('successor_created');
|
|
1354
|
+
expect(output.successorKind).toBe('philosopher');
|
|
1355
|
+
expect(mockCommitNextTaskProposal).toHaveBeenCalledWith('task-dreamer-auto-001');
|
|
1356
|
+
});
|
|
1357
|
+
|
|
1358
|
+
it('--no-enqueue-next (enqueueNext: false) skips successor enqueue', async () => {
|
|
1359
|
+
mockWakeOnce.mockResolvedValue({
|
|
1360
|
+
decision: 'would_lease',
|
|
1361
|
+
taskId: 'task-dreamer-skip-001',
|
|
1362
|
+
taskKind: 'dreamer',
|
|
1363
|
+
});
|
|
1364
|
+
|
|
1365
|
+
mockRun.mockResolvedValue({
|
|
1366
|
+
status: 'succeeded',
|
|
1367
|
+
taskId: 'task-dreamer-skip-001',
|
|
1368
|
+
runId: 'run-skip-001',
|
|
1369
|
+
artifactId: 'pi-art-skip-001',
|
|
1370
|
+
resultRef: 'dreamer://run-skip-001',
|
|
1371
|
+
contextHash: 'ctx-skip',
|
|
1372
|
+
output: { valid: true, taskId: 'task-dreamer-skip-001', candidates: VALID_DREAMER_CANDIDATES, contextRefs: [], generatedAt: new Date().toISOString() },
|
|
1373
|
+
attemptCount: 1,
|
|
1374
|
+
});
|
|
1375
|
+
|
|
1376
|
+
await handleRuntimeInternalizationRunOnce({ workspace: WS, runner: 'dreamer', runtime: 'test-double', allowTestDouble: true, enqueueNext: false, json: true });
|
|
1377
|
+
|
|
1378
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0]);
|
|
1379
|
+
expect(output.successorEnqueueAttempted).toBe(false);
|
|
1380
|
+
expect(output.nextAction).toContain('--no-enqueue-next');
|
|
1381
|
+
expect(mockCommitNextTaskProposal).not.toHaveBeenCalled();
|
|
1382
|
+
});
|
|
1383
|
+
|
|
1384
|
+
it('successor enqueue failure outputs partial_success with reason and nextAction', async () => {
|
|
1385
|
+
mockWakeOnce.mockResolvedValue({
|
|
1386
|
+
decision: 'would_lease',
|
|
1387
|
+
taskId: 'task-dreamer-fail-001',
|
|
1388
|
+
taskKind: 'dreamer',
|
|
1389
|
+
});
|
|
1390
|
+
|
|
1391
|
+
mockRun.mockResolvedValue({
|
|
1392
|
+
status: 'succeeded',
|
|
1393
|
+
taskId: 'task-dreamer-fail-001',
|
|
1394
|
+
runId: 'run-fail-001',
|
|
1395
|
+
artifactId: 'pi-art-fail-001',
|
|
1396
|
+
resultRef: 'dreamer://run-fail-001',
|
|
1397
|
+
contextHash: 'ctx-fail',
|
|
1398
|
+
output: { valid: true, taskId: 'task-dreamer-fail-001', candidates: VALID_DREAMER_CANDIDATES, contextRefs: [], generatedAt: new Date().toISOString() },
|
|
1399
|
+
attemptCount: 1,
|
|
1400
|
+
});
|
|
1401
|
+
|
|
1402
|
+
mockCommitNextTaskProposal.mockRejectedValue(new Error('database locked'));
|
|
1403
|
+
|
|
1404
|
+
await handleRuntimeInternalizationRunOnce({ workspace: WS, runner: 'dreamer', runtime: 'test-double', allowTestDouble: true, json: true });
|
|
1405
|
+
|
|
1406
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0]);
|
|
1407
|
+
expect(output.decision).toBe('partial_success');
|
|
1408
|
+
expect(output.successorEnqueueAttempted).toBe(true);
|
|
1409
|
+
expect(output.enqueueDecision).toBe('enqueue_failed');
|
|
1410
|
+
expect(output.enqueueReason).toContain('database locked');
|
|
1411
|
+
expect(output.nextAction).toContain('enqueue-successors');
|
|
1412
|
+
expect(output.successorTasksCreated).toBe(0);
|
|
1413
|
+
expect(output.successorTaskIds).toEqual([]);
|
|
1414
|
+
});
|
|
1415
|
+
|
|
1416
|
+
it('runner failure never enqueues successor (default behavior)', async () => {
|
|
1417
|
+
mockWakeOnce.mockResolvedValue({
|
|
1418
|
+
decision: 'would_lease',
|
|
1419
|
+
taskId: 'task-dreamer-nofail-001',
|
|
1420
|
+
taskKind: 'dreamer',
|
|
1421
|
+
});
|
|
1422
|
+
|
|
1423
|
+
mockRun.mockResolvedValue({
|
|
1424
|
+
status: 'failed',
|
|
1425
|
+
taskId: 'task-dreamer-nofail-001',
|
|
1426
|
+
errorCategory: 'execution_failed',
|
|
1427
|
+
failureReason: 'Runtime unavailable',
|
|
1428
|
+
attemptCount: 1,
|
|
1429
|
+
});
|
|
1430
|
+
|
|
1431
|
+
// Default behavior (no --no-enqueue-next)
|
|
1432
|
+
await handleRuntimeInternalizationRunOnce({ workspace: WS, runner: 'dreamer', runtime: 'test-double', allowTestDouble: true, json: true });
|
|
1433
|
+
|
|
1434
|
+
expect(mockCommitNextTaskProposal).not.toHaveBeenCalled();
|
|
1435
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0]);
|
|
1436
|
+
expect(output.successorEnqueueAttempted).toBeUndefined();
|
|
1437
|
+
});
|
|
1438
|
+
|
|
1439
|
+
it('JSON output is single parseable JSON with successor fields', async () => {
|
|
1440
|
+
mockWakeOnce.mockResolvedValue({
|
|
1441
|
+
decision: 'would_lease',
|
|
1442
|
+
taskId: 'task-dreamer-json-001',
|
|
1443
|
+
taskKind: 'dreamer',
|
|
1444
|
+
});
|
|
1445
|
+
|
|
1446
|
+
mockRun.mockResolvedValue({
|
|
1447
|
+
status: 'succeeded',
|
|
1448
|
+
taskId: 'task-dreamer-json-001',
|
|
1449
|
+
runId: 'run-json-001',
|
|
1450
|
+
artifactId: 'pi-art-json-001',
|
|
1451
|
+
resultRef: 'dreamer://run-json-001',
|
|
1452
|
+
contextHash: 'ctx-json',
|
|
1453
|
+
output: { valid: true, taskId: 'task-dreamer-json-001', candidates: VALID_DREAMER_CANDIDATES, contextRefs: [], generatedAt: new Date().toISOString() },
|
|
1454
|
+
attemptCount: 1,
|
|
1455
|
+
});
|
|
1456
|
+
|
|
1457
|
+
mockCommitNextTaskProposal.mockResolvedValue({
|
|
1458
|
+
decision: 'successor_created',
|
|
1459
|
+
sourceTaskId: 'task-dreamer-json-001',
|
|
1460
|
+
successorTaskId: 'task-phil-json-001',
|
|
1461
|
+
successorKind: 'philosopher',
|
|
1462
|
+
});
|
|
1463
|
+
|
|
1464
|
+
await handleRuntimeInternalizationRunOnce({ workspace: WS, runner: 'dreamer', runtime: 'test-double', allowTestDouble: true, json: true });
|
|
1465
|
+
|
|
1466
|
+
const rawOutput = consoleLogSpy.mock.calls[0][0];
|
|
1467
|
+
// Must be single parseable JSON
|
|
1468
|
+
const output = JSON.parse(rawOutput);
|
|
1469
|
+
expect(output).toHaveProperty('successorEnqueueAttempted');
|
|
1470
|
+
expect(output).toHaveProperty('successorTasksCreated');
|
|
1471
|
+
expect(output).toHaveProperty('successorTaskIds');
|
|
1472
|
+
// nextAction may be undefined when successor is successfully created
|
|
1473
|
+
// but must be present on partial_success / no_successor / skipped
|
|
1474
|
+
});
|
|
1475
|
+
|
|
1476
|
+
it('text output for auto-enqueue shows successor info', async () => {
|
|
1477
|
+
mockWakeOnce.mockResolvedValue({
|
|
1478
|
+
decision: 'would_lease',
|
|
1479
|
+
taskId: 'task-dreamer-text-001',
|
|
1480
|
+
taskKind: 'dreamer',
|
|
1481
|
+
});
|
|
1482
|
+
|
|
1483
|
+
mockRun.mockResolvedValue({
|
|
1484
|
+
status: 'succeeded',
|
|
1485
|
+
taskId: 'task-dreamer-text-001',
|
|
1486
|
+
runId: 'run-text-001',
|
|
1487
|
+
artifactId: 'pi-art-text-001',
|
|
1488
|
+
resultRef: 'dreamer://run-text-001',
|
|
1489
|
+
contextHash: 'ctx-text',
|
|
1490
|
+
output: { valid: true, taskId: 'task-dreamer-text-001', candidates: VALID_DREAMER_CANDIDATES, contextRefs: [], generatedAt: new Date().toISOString() },
|
|
1491
|
+
attemptCount: 1,
|
|
1492
|
+
});
|
|
1493
|
+
|
|
1494
|
+
mockCommitNextTaskProposal.mockResolvedValue({
|
|
1495
|
+
decision: 'successor_created',
|
|
1496
|
+
sourceTaskId: 'task-dreamer-text-001',
|
|
1497
|
+
successorTaskId: 'task-phil-text-001',
|
|
1498
|
+
successorKind: 'philosopher',
|
|
1499
|
+
});
|
|
1500
|
+
|
|
1501
|
+
await handleRuntimeInternalizationRunOnce({ workspace: WS, runner: 'dreamer', runtime: 'test-double', allowTestDouble: true, json: false });
|
|
1502
|
+
|
|
1503
|
+
const text = consoleLogSpy.mock.calls.map((c: string[]) => c[0]).join('\n');
|
|
1504
|
+
expect(text).toContain('successor: task-phil-text-001');
|
|
1505
|
+
expect(text).toContain('enqueue_attempted: true');
|
|
1506
|
+
expect(text).toContain('successors_created: 1');
|
|
1507
|
+
});
|
|
1508
|
+
});
|
|
1509
|
+
|
|
1510
|
+
// === Commander parser-level tests for --no-enqueue-next ===
|
|
1511
|
+
// These tests verify that Commander correctly maps --no-enqueue-next to opts.enqueueNext.
|
|
1512
|
+
// They exercise the real Commander parsing path, NOT the handler directly.
|
|
1513
|
+
// See ERR-063: previous code used opts.noEnqueueNext (always undefined) instead of opts.enqueueNext.
|
|
1514
|
+
|
|
1515
|
+
describe('Commander --no-enqueue-next parser wiring', () => {
|
|
1516
|
+
function buildRunOnceCommand(capturedOpts: Record<string, unknown>) {
|
|
1517
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
1518
|
+
const { Command } = require('commander') as typeof import('commander');
|
|
1519
|
+
const program = new Command();
|
|
1520
|
+
program.exitOverride(); // prevent process.exit during tests
|
|
1521
|
+
|
|
1522
|
+
const internalizationCmd = program.command('internalization');
|
|
1523
|
+
internalizationCmd
|
|
1524
|
+
.command('run-once')
|
|
1525
|
+
.description('Wake-and-run: lease the next PI task and execute it')
|
|
1526
|
+
.option('-w, --workspace <path>', 'Workspace directory')
|
|
1527
|
+
.option('--runner <kind>', 'Runner kind', 'dreamer')
|
|
1528
|
+
.option('--runtime <kind>', 'Runtime adapter kind', 'config')
|
|
1529
|
+
.option('--allow-test-double', 'Acknowledge test-double runtime')
|
|
1530
|
+
.option('--no-enqueue-next', 'Skip successor enqueue after successful runner')
|
|
1531
|
+
.option('--timeout-ms <ms>', 'Runner timeout', parseInt)
|
|
1532
|
+
.option('--json', 'Output raw JSON')
|
|
1533
|
+
.action(async (opts) => {
|
|
1534
|
+
Object.assign(capturedOpts, opts);
|
|
1535
|
+
});
|
|
1536
|
+
|
|
1537
|
+
return program;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
it('with --no-enqueue-next, Commander sets enqueueNext=false', async () => {
|
|
1541
|
+
const captured: Record<string, unknown> = {};
|
|
1542
|
+
const program = buildRunOnceCommand(captured);
|
|
1543
|
+
|
|
1544
|
+
await program.parseAsync([
|
|
1545
|
+
'node', 'pd', 'internalization', 'run-once',
|
|
1546
|
+
'--no-enqueue-next',
|
|
1547
|
+
'--workspace', '/tmp/test',
|
|
1548
|
+
'--runtime', 'test-double',
|
|
1549
|
+
'--allow-test-double',
|
|
1550
|
+
'--json',
|
|
1551
|
+
]);
|
|
1552
|
+
|
|
1553
|
+
expect(captured).toHaveProperty('enqueueNext', false);
|
|
1554
|
+
expect(captured).not.toHaveProperty('noEnqueueNext');
|
|
1555
|
+
});
|
|
1556
|
+
|
|
1557
|
+
it('without --no-enqueue-next, Commander sets enqueueNext=true (default)', async () => {
|
|
1558
|
+
const captured: Record<string, unknown> = {};
|
|
1559
|
+
const program = buildRunOnceCommand(captured);
|
|
1560
|
+
|
|
1561
|
+
await program.parseAsync([
|
|
1562
|
+
'node', 'pd', 'internalization', 'run-once',
|
|
1563
|
+
'--workspace', '/tmp/test',
|
|
1564
|
+
'--runtime', 'test-double',
|
|
1565
|
+
'--allow-test-double',
|
|
1566
|
+
'--json',
|
|
1567
|
+
]);
|
|
1568
|
+
|
|
1569
|
+
expect(captured).toHaveProperty('enqueueNext', true);
|
|
1570
|
+
});
|
|
1571
|
+
|
|
1572
|
+
it('opts has no noEnqueueNext property regardless of flag presence', async () => {
|
|
1573
|
+
const capturedWith: Record<string, unknown> = {};
|
|
1574
|
+
const programWith = buildRunOnceCommand(capturedWith);
|
|
1575
|
+
await programWith.parseAsync([
|
|
1576
|
+
'node', 'pd', 'internalization', 'run-once',
|
|
1577
|
+
'--no-enqueue-next', '--workspace', '/tmp/test',
|
|
1578
|
+
]);
|
|
1579
|
+
|
|
1580
|
+
const capturedWithout: Record<string, unknown> = {};
|
|
1581
|
+
const programWithout = buildRunOnceCommand(capturedWithout);
|
|
1582
|
+
await programWithout.parseAsync([
|
|
1583
|
+
'node', 'pd', 'internalization', 'run-once',
|
|
1584
|
+
'--workspace', '/tmp/test',
|
|
1585
|
+
]);
|
|
1586
|
+
|
|
1587
|
+
expect(capturedWith).not.toHaveProperty('noEnqueueNext');
|
|
1588
|
+
expect(capturedWithout).not.toHaveProperty('noEnqueueNext');
|
|
1589
|
+
});
|
|
1318
1590
|
});
|