@principles/pd-cli 1.147.17 → 1.149.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/errors-list.d.ts.map +1 -1
- package/dist/commands/errors-list.js +9 -10
- package/dist/commands/errors-list.js.map +1 -1
- package/dist/commands/pain-list.d.ts +14 -0
- package/dist/commands/pain-list.d.ts.map +1 -1
- package/dist/commands/pain-list.js +28 -1
- package/dist/commands/pain-list.js.map +1 -1
- package/dist/commands/pain-record.d.ts +2 -0
- package/dist/commands/pain-record.d.ts.map +1 -1
- package/dist/commands/pain-record.js +71 -4
- package/dist/commands/pain-record.js.map +1 -1
- package/dist/commands/runtime-init.d.ts +3 -1
- package/dist/commands/runtime-init.d.ts.map +1 -1
- package/dist/commands/runtime-init.js +7 -47
- package/dist/commands/runtime-init.js.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/errors-list.ts +14 -13
- package/src/commands/pain-list.ts +37 -1
- package/src/commands/pain-record.ts +73 -4
- package/src/commands/runtime-init.ts +7 -45
- package/src/index.ts +2 -1
- package/tests/commands/errors-list.test.ts +5 -4
- package/tests/commands/pain-list.test.ts +35 -1
- package/tests/commands/pain-record-session-parser.test.ts +27 -0
- package/tests/commands/pain-record.test.ts +77 -0
- package/tests/commands/runtime-init-empty-workspace.test.ts +5 -13
- package/tests/commands/runtime-init.test.ts +6 -32
|
@@ -105,6 +105,39 @@ describe('listPains (PRI-640 host filter)', () => {
|
|
|
105
105
|
expect(result.pains[0]?.host).toBe('unknown');
|
|
106
106
|
});
|
|
107
107
|
|
|
108
|
+
it('PRI-743: byHost reports the full-table distribution independent of --host/--limit', async () => {
|
|
109
|
+
const { db } = makeWorkspace();
|
|
110
|
+
seedPain(db, { id: 1, source: 'user_correction', canonical: 'pain_oc_1', host: 'openclaw', created: '2026-09-01T10:00:00.000Z' });
|
|
111
|
+
seedPain(db, { id: 2, source: 'user_correction', canonical: 'pain_oc_2', host: 'openclaw', created: '2026-09-01T10:05:00.000Z' });
|
|
112
|
+
seedPain(db, { id: 3, source: 'tool_failure', canonical: 'pain_cx_1', host: 'codex', created: '2026-09-01T11:00:00.000Z' });
|
|
113
|
+
seedPain(db, { id: 4, source: 'manual', canonical: 'pain_null_1', host: null, created: '2026-09-01T12:00:00.000Z' });
|
|
114
|
+
seedPain(db, { id: 5, source: 'manual', canonical: 'pain_null_2', host: null, created: '2026-09-01T12:05:00.000Z' });
|
|
115
|
+
const dbPath = dbPathOf(db);
|
|
116
|
+
db.close();
|
|
117
|
+
|
|
118
|
+
// --limit narrows the row list but must NOT narrow the distribution.
|
|
119
|
+
const limited = await listPains(dbPath, { limit: 2 });
|
|
120
|
+
expect(limited.count).toBe(2);
|
|
121
|
+
expect(limited.byHost).toEqual({ openclaw: 2, codex: 1, unknown: 2 });
|
|
122
|
+
|
|
123
|
+
// --host narrows the row list but must NOT narrow the distribution.
|
|
124
|
+
const filtered = await listPains(dbPath, { limit: 10, host: 'codex' });
|
|
125
|
+
expect(filtered.count).toBe(1);
|
|
126
|
+
expect(filtered.byHost).toEqual({ openclaw: 2, codex: 1, unknown: 2 });
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('PRI-743: byHost is null (never guessed) on a pre-PRI-640 database', async () => {
|
|
130
|
+
const { db } = makePre640Workspace();
|
|
131
|
+
db.prepare(`INSERT INTO pain_events (session_id, source, score, reason, severity, origin, confidence, text, canonical_pain_id, runtime_task_id, created_at)
|
|
132
|
+
VALUES ('s1', 'tool_failure', 70, 'r', 'moderate', 'system_infer', NULL, NULL, 'pain_legacy', NULL, '2026-08-01T00:00:00.000Z')`).run();
|
|
133
|
+
const dbPath = dbPathOf(db);
|
|
134
|
+
db.close();
|
|
135
|
+
|
|
136
|
+
const result = await listPains(dbPath, { limit: 10 });
|
|
137
|
+
expect(result.byHost).toBeNull();
|
|
138
|
+
expect(result.warnings).toContain('host_kind_column_missing');
|
|
139
|
+
});
|
|
140
|
+
|
|
108
141
|
it('degrades observably on a pre-PRI-640 database without the host_kind column (rc-9)', async () => {
|
|
109
142
|
const { db } = makePre640Workspace();
|
|
110
143
|
db.prepare(`INSERT INTO pain_events (session_id, source, score, reason, severity, origin, confidence, text, canonical_pain_id, runtime_task_id, created_at)
|
|
@@ -152,11 +185,12 @@ describe('handlePainList (CLI contract)', () => {
|
|
|
152
185
|
await handlePainList({ json: true });
|
|
153
186
|
expect(exitSpy).not.toHaveBeenCalled();
|
|
154
187
|
const raw = logSpy.mock.calls.map((args) => String(args[0])).join('\n');
|
|
155
|
-
const parsed = JSON.parse(raw) as { count: number; pains: { host: string; painId: string }[]; hostFilter: unknown; warnings: string[] };
|
|
188
|
+
const parsed = JSON.parse(raw) as { count: number; pains: { host: string; painId: string }[]; hostFilter: unknown; warnings: string[]; byHost: { openclaw: number; codex: number; unknown: number } | null };
|
|
156
189
|
expect(parsed.count).toBe(2);
|
|
157
190
|
expect(parsed.pains.map((p) => `${p.painId}:${p.host}`).sort()).toEqual(['pain_cli_cx:codex', 'pain_cli_oc:openclaw']);
|
|
158
191
|
expect(parsed.hostFilter).toBeNull();
|
|
159
192
|
expect(parsed.warnings).toEqual([]);
|
|
193
|
+
expect(parsed.byHost).toEqual({ openclaw: 1, codex: 1, unknown: 0 });
|
|
160
194
|
});
|
|
161
195
|
|
|
162
196
|
it('--host filter is reflected in the JSON result', async () => {
|
|
@@ -86,6 +86,33 @@ describe('pd pain record --session (real Commander + real trajectory.db)', () =>
|
|
|
86
86
|
expect(result.stdout).toContain('--session');
|
|
87
87
|
}, 15_000);
|
|
88
88
|
|
|
89
|
+
it('PRI-743: --help registers --host with the openclaw|codex contract', async () => {
|
|
90
|
+
const result = await runBuiltCli(['pain', 'record', '--help']);
|
|
91
|
+
expect(result.status).toBe(0);
|
|
92
|
+
expect(result.stdout).toContain('--host <kind>');
|
|
93
|
+
// Commander wraps help text; normalize whitespace before asserting content.
|
|
94
|
+
const normalized = result.stdout.replace(/\s+/g, ' ');
|
|
95
|
+
expect(normalized).toContain('openclaw | codex');
|
|
96
|
+
expect(normalized).toContain('codex refuses');
|
|
97
|
+
}, 15_000);
|
|
98
|
+
|
|
99
|
+
it('PRI-743: an invalid --host value exits non-zero with a single structured JSON object', async () => {
|
|
100
|
+
const result = await runBuiltCli([
|
|
101
|
+
'pain', 'record',
|
|
102
|
+
'--reason', 'parser test pain',
|
|
103
|
+
'--host', 'claude',
|
|
104
|
+
'--workspace', tmpDir,
|
|
105
|
+
'--json',
|
|
106
|
+
]);
|
|
107
|
+
|
|
108
|
+
expect(result.status).not.toBe(0);
|
|
109
|
+
const trimmed = result.stdout.trim();
|
|
110
|
+
const parsed = JSON.parse(trimmed) as Record<string, unknown>;
|
|
111
|
+
expect(parsed.status).toBe('failed');
|
|
112
|
+
expect(parsed.reason).toBe('invalid_host_kind');
|
|
113
|
+
expect(typeof parsed.nextAction).toBe('string');
|
|
114
|
+
}, 15_000);
|
|
115
|
+
|
|
89
116
|
it('fails with a single JSON object and reason session_not_found for a nonexistent session (SPEC 12.1.4)', async () => {
|
|
90
117
|
const result = await runBuiltCli([
|
|
91
118
|
'pain', 'record',
|
|
@@ -399,6 +399,83 @@ describe('pd pain record', () => {
|
|
|
399
399
|
exitSpy.mockRestore();
|
|
400
400
|
});
|
|
401
401
|
|
|
402
|
+
// ── PRI-743: explicit --host attribution ──────────────────────────────────
|
|
403
|
+
|
|
404
|
+
it('PRI-743: --host openclaw records without the default-assumption disclosure', async () => {
|
|
405
|
+
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
406
|
+
const exitSpy = mockProcessExit();
|
|
407
|
+
|
|
408
|
+
await handlePainRecord({ reason: 'explicit openclaw', session: 'sess-oc', host: 'openclaw', json: true });
|
|
409
|
+
|
|
410
|
+
expect(lastRecordPainInput).toBeTruthy();
|
|
411
|
+
expect(lastRecordPainInput!.sessionId).toBe('sess-oc');
|
|
412
|
+
expect(lastRecordPainInput!.hostKind).toBe('openclaw');
|
|
413
|
+
expect(lastRecordPainInput!.provenance).toBe('host_context_bound');
|
|
414
|
+
|
|
415
|
+
const jsonOutput = JSON.parse(logSpy.mock.calls[0][0]) as { hostAttribution?: string; warnings?: string[] };
|
|
416
|
+
expect(jsonOutput.hostAttribution).toBe('openclaw');
|
|
417
|
+
expect((jsonOutput.warnings ?? []).some((w) => w.includes("defaulted to 'openclaw'"))).toBe(false);
|
|
418
|
+
|
|
419
|
+
logSpy.mockRestore();
|
|
420
|
+
exitSpy.mockRestore();
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
it('PRI-743: --host codex refuses loudly — the CLI cannot verify Codex lineage (rc-6)', async () => {
|
|
424
|
+
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
425
|
+
const exitSpy = mockProcessExit();
|
|
426
|
+
|
|
427
|
+
await handlePainRecord({ reason: 'codex mistake', session: 'sess-codex', host: 'codex', json: true });
|
|
428
|
+
|
|
429
|
+
// cli-1: exactly one JSON object on stdout
|
|
430
|
+
expect(logSpy).toHaveBeenCalledTimes(1);
|
|
431
|
+
const jsonOutput = JSON.parse(logSpy.mock.calls[0][0]) as { status: string; reason: string; nextAction: string };
|
|
432
|
+
expect(jsonOutput).toMatchObject({ status: 'failed', reason: 'codex_lineage_unverifiable_by_cli' });
|
|
433
|
+
expect(jsonOutput.nextAction).toContain('Codex ingestion');
|
|
434
|
+
// cli-2/cli-5: refused before any evidence acquisition or service mutation
|
|
435
|
+
expect(lastRecordPainInput).toBeNull();
|
|
436
|
+
expect(acquireTrajectoryEvidenceFromDb).not.toHaveBeenCalled();
|
|
437
|
+
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
438
|
+
|
|
439
|
+
logSpy.mockRestore();
|
|
440
|
+
exitSpy.mockRestore();
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it('PRI-743: omitting --host keeps the openclaw default but discloses the assumption (rc-9)', async () => {
|
|
444
|
+
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
445
|
+
const exitSpy = mockProcessExit();
|
|
446
|
+
|
|
447
|
+
await handlePainRecord({ reason: 'legacy path', session: 'sess-123', json: true });
|
|
448
|
+
|
|
449
|
+
expect(lastRecordPainInput).toBeTruthy();
|
|
450
|
+
expect(lastRecordPainInput!.hostKind).toBe('openclaw');
|
|
451
|
+
const jsonOutput = JSON.parse(logSpy.mock.calls[0][0]) as { hostAttribution?: string; warnings?: string[] };
|
|
452
|
+
expect(jsonOutput.hostAttribution).toBe('openclaw');
|
|
453
|
+
expect((jsonOutput.warnings ?? []).some((w) => w.includes("defaulted to 'openclaw'") && w.includes('--host codex'))).toBe(true);
|
|
454
|
+
|
|
455
|
+
logSpy.mockRestore();
|
|
456
|
+
exitSpy.mockRestore();
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
it('PRI-743: an invalid --host value fails loudly before any mutation (cli-5/cli-6)', async () => {
|
|
460
|
+
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
461
|
+
const exitSpy = mockProcessExit();
|
|
462
|
+
|
|
463
|
+
await handlePainRecord({ reason: 'typo host', session: 'sess-123', host: 'claude', json: true });
|
|
464
|
+
|
|
465
|
+
// cli-1: exactly one JSON object on stdout
|
|
466
|
+
expect(logSpy).toHaveBeenCalledTimes(1);
|
|
467
|
+
const jsonOutput = JSON.parse(logSpy.mock.calls[0][0]) as { status: string; reason: string; nextAction: string };
|
|
468
|
+
expect(jsonOutput).toMatchObject({ status: 'failed', reason: 'invalid_host_kind' });
|
|
469
|
+
expect(jsonOutput.nextAction).toContain('--host codex');
|
|
470
|
+
// cli-2/cli-5: execution stopped before any evidence acquisition or service mutation
|
|
471
|
+
expect(lastRecordPainInput).toBeNull();
|
|
472
|
+
expect(acquireTrajectoryEvidenceFromDb).not.toHaveBeenCalled();
|
|
473
|
+
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
474
|
+
|
|
475
|
+
logSpy.mockRestore();
|
|
476
|
+
exitSpy.mockRestore();
|
|
477
|
+
});
|
|
478
|
+
|
|
402
479
|
// 用例 C2 (PRI-642 rewrite): without session, no sentinel session, no
|
|
403
480
|
// placeholder evidence — an honest unbound Owner report (SPEC §7.4).
|
|
404
481
|
it('C2: submits an honest unbound report when no --session provided', async () => {
|
|
@@ -73,10 +73,6 @@ const EXPECTED_TRAJECTORY_TABLES = [
|
|
|
73
73
|
'exports_audit', 'evolution_tasks', 'evolution_events',
|
|
74
74
|
];
|
|
75
75
|
|
|
76
|
-
const EXPECTED_WORKFLOW_TABLES = [
|
|
77
|
-
'schema_version', 'subagent_workflows', 'subagent_workflow_events',
|
|
78
|
-
];
|
|
79
|
-
|
|
80
76
|
const EXPECTED_TRAJECTORY_INDEXES = [
|
|
81
77
|
'idx_assistant_turns_session_id',
|
|
82
78
|
'idx_assistant_turns_created_at',
|
|
@@ -110,11 +106,11 @@ describe('pd runtime init — empty workspace integration', () => {
|
|
|
110
106
|
// ── EMPTY-01: initialize empty workspace ───────────────────────────────────
|
|
111
107
|
|
|
112
108
|
describe('EMPTY-01: initialize empty workspace', () => {
|
|
113
|
-
it('returns ok=true with
|
|
109
|
+
it('returns ok=true with 2 initialized databases', () => {
|
|
114
110
|
const output = buildRuntimeInitOutput(tmpDir, true);
|
|
115
111
|
expect(output.ok).toBe(true);
|
|
116
112
|
expect(output.mode).toBe('confirm');
|
|
117
|
-
expect(output.databases).toHaveLength(
|
|
113
|
+
expect(output.databases).toHaveLength(2);
|
|
118
114
|
for (const db of output.databases) {
|
|
119
115
|
expect(db.status).toBe('initialized');
|
|
120
116
|
}
|
|
@@ -150,14 +146,10 @@ describe('pd runtime init — empty workspace integration', () => {
|
|
|
150
146
|
}
|
|
151
147
|
});
|
|
152
148
|
|
|
153
|
-
it('creates subagent_workflows.db
|
|
149
|
+
it('no longer creates subagent_workflows.db (legacy workflow store retired in PRI-737)', () => {
|
|
154
150
|
buildRuntimeInitOutput(tmpDir, true);
|
|
155
151
|
const wfDbPath = path.join(tmpDir, '.state', 'subagent_workflows.db');
|
|
156
|
-
expect(fs.existsSync(wfDbPath)).toBe(
|
|
157
|
-
const tables = getTableNames(wfDbPath);
|
|
158
|
-
for (const expected of EXPECTED_WORKFLOW_TABLES) {
|
|
159
|
-
expect(tables).toContain(expected);
|
|
160
|
-
}
|
|
152
|
+
expect(fs.existsSync(wfDbPath)).toBe(false);
|
|
161
153
|
});
|
|
162
154
|
|
|
163
155
|
it('pain_events table has canonical_pain_id and runtime_task_id columns', () => {
|
|
@@ -229,7 +221,7 @@ describe('pd runtime init — empty workspace integration', () => {
|
|
|
229
221
|
// Second initialization (should be idempotent)
|
|
230
222
|
const output2 = buildRuntimeInitOutput(tmpDir, true);
|
|
231
223
|
expect(output2.ok).toBe(true);
|
|
232
|
-
expect(output2.databases).toHaveLength(
|
|
224
|
+
expect(output2.databases).toHaveLength(2);
|
|
233
225
|
for (const db of output2.databases) {
|
|
234
226
|
expect(db.status).toBe('initialized');
|
|
235
227
|
}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* runtime-init tests — pd runtime init command (unit tests with mocked DB layer).
|
|
3
3
|
*
|
|
4
4
|
* Covers:
|
|
5
|
-
* - INIT-01: --dry-run (default) reports
|
|
6
|
-
* - INIT-02: --confirm calls all
|
|
5
|
+
* - INIT-01: --dry-run (default) reports 2 DBs as 'skipped', no real writes
|
|
6
|
+
* - INIT-02: --confirm calls all init functions, output shows 'initialized'
|
|
7
7
|
* - INIT-03: --json outputs single parseable JSON object (cli-1)
|
|
8
8
|
* - INIT-04: --dry-run + --confirm mutually exclusive (cli-4)
|
|
9
9
|
* - INIT-05: --confirm failure sets exitCode=1 with reason + nextAction (cli-6)
|
|
@@ -24,7 +24,6 @@ import * as fs from 'node:fs';
|
|
|
24
24
|
const mockState = vi.hoisted(() => {
|
|
25
25
|
return {
|
|
26
26
|
initTrajectorySchema: vi.fn(),
|
|
27
|
-
initWorkflowSchema: vi.fn(),
|
|
28
27
|
sqliteConnectionCtor: vi.fn(),
|
|
29
28
|
sqliteConnectionGetDb: vi.fn(),
|
|
30
29
|
sqliteConnectionGetWarnings: vi.fn(),
|
|
@@ -37,7 +36,6 @@ const mockState = vi.hoisted(() => {
|
|
|
37
36
|
|
|
38
37
|
vi.mock('principles-disciple', () => ({
|
|
39
38
|
initTrajectorySchema: mockState.initTrajectorySchema,
|
|
40
|
-
initWorkflowSchema: mockState.initWorkflowSchema,
|
|
41
39
|
}));
|
|
42
40
|
|
|
43
41
|
vi.mock('@principles/core/runtime-v2', async () => {
|
|
@@ -87,12 +85,6 @@ function setupDefaultConfirmMocks(): void {
|
|
|
87
85
|
warnings: [],
|
|
88
86
|
});
|
|
89
87
|
|
|
90
|
-
// initWorkflowSchema mock
|
|
91
|
-
mockState.initWorkflowSchema.mockReturnValue({
|
|
92
|
-
tables: ['schema_version', 'subagent_workflows', 'subagent_workflow_events'],
|
|
93
|
-
warnings: [],
|
|
94
|
-
});
|
|
95
|
-
|
|
96
88
|
// SchemaConformanceReadModel mock
|
|
97
89
|
mockState.schemaConformanceCtor.mockImplementation(function () {
|
|
98
90
|
return {
|
|
@@ -113,19 +105,18 @@ describe('pd runtime init', () => {
|
|
|
113
105
|
// ── INIT-01: dry-run (default) ─────────────────────────────────────────────
|
|
114
106
|
|
|
115
107
|
describe('INIT-01: dry-run (default)', () => {
|
|
116
|
-
it('reports
|
|
108
|
+
it('reports 2 DBs as skipped without calling init functions', () => {
|
|
117
109
|
const tmp = mkTmpDir();
|
|
118
110
|
try {
|
|
119
111
|
const output = buildRuntimeInitOutput(tmp, false);
|
|
120
112
|
expect(output.ok).toBe(true);
|
|
121
113
|
expect(output.mode).toBe('dry-run');
|
|
122
|
-
expect(output.databases).toHaveLength(
|
|
114
|
+
expect(output.databases).toHaveLength(2);
|
|
123
115
|
for (const db of output.databases) {
|
|
124
116
|
expect(db.status).toBe('skipped');
|
|
125
117
|
}
|
|
126
118
|
// No init functions should be called in dry-run mode
|
|
127
119
|
expect(mockState.initTrajectorySchema).not.toHaveBeenCalled();
|
|
128
|
-
expect(mockState.initWorkflowSchema).not.toHaveBeenCalled();
|
|
129
120
|
expect(mockState.sqliteConnectionCtor).not.toHaveBeenCalled();
|
|
130
121
|
} finally { rmTmpDir(tmp); }
|
|
131
122
|
});
|
|
@@ -136,12 +127,10 @@ describe('pd runtime init', () => {
|
|
|
136
127
|
const output = buildRuntimeInitOutput(tmp, false);
|
|
137
128
|
const stateDb = output.databases.find(d => d.name === 'state.db');
|
|
138
129
|
const trajDb = output.databases.find(d => d.name === 'trajectory.db');
|
|
139
|
-
const wfDb = output.databases.find(d => d.name === 'subagent_workflows.db');
|
|
140
130
|
expect(stateDb?.tables).toContain('tasks');
|
|
141
131
|
expect(stateDb?.tables).toContain('runs');
|
|
142
132
|
expect(trajDb?.tables).toContain('pain_events');
|
|
143
133
|
expect(trajDb?.tables).toContain('sessions');
|
|
144
|
-
expect(wfDb?.tables).toContain('subagent_workflows');
|
|
145
134
|
} finally { rmTmpDir(tmp); }
|
|
146
135
|
});
|
|
147
136
|
});
|
|
@@ -149,20 +138,19 @@ describe('pd runtime init', () => {
|
|
|
149
138
|
// ── INIT-02: --confirm ─────────────────────────────────────────────────────
|
|
150
139
|
|
|
151
140
|
describe('INIT-02: --confirm', () => {
|
|
152
|
-
it('calls all
|
|
141
|
+
it('calls all init functions and reports initialized status', () => {
|
|
153
142
|
const tmp = mkTmpDir();
|
|
154
143
|
try {
|
|
155
144
|
const output = buildRuntimeInitOutput(tmp, true);
|
|
156
145
|
expect(output.ok).toBe(true);
|
|
157
146
|
expect(output.mode).toBe('confirm');
|
|
158
|
-
expect(output.databases).toHaveLength(
|
|
147
|
+
expect(output.databases).toHaveLength(2);
|
|
159
148
|
for (const db of output.databases) {
|
|
160
149
|
expect(db.status).toBe('initialized');
|
|
161
150
|
}
|
|
162
151
|
expect(mockState.sqliteConnectionCtor).toHaveBeenCalledTimes(1);
|
|
163
152
|
expect(mockState.sqliteConnectionGetDb).toHaveBeenCalledTimes(1);
|
|
164
153
|
expect(mockState.initTrajectorySchema).toHaveBeenCalledWith(tmp);
|
|
165
|
-
expect(mockState.initWorkflowSchema).toHaveBeenCalledWith(tmp);
|
|
166
154
|
} finally { rmTmpDir(tmp); }
|
|
167
155
|
});
|
|
168
156
|
|
|
@@ -256,7 +244,6 @@ describe('pd runtime init', () => {
|
|
|
256
244
|
it('does not call any init functions on flag conflict', async () => {
|
|
257
245
|
await handleRuntimeInit({ dryRun: true, confirm: true, json: true });
|
|
258
246
|
expect(mockState.initTrajectorySchema).not.toHaveBeenCalled();
|
|
259
|
-
expect(mockState.initWorkflowSchema).not.toHaveBeenCalled();
|
|
260
247
|
expect(mockState.sqliteConnectionCtor).not.toHaveBeenCalled();
|
|
261
248
|
});
|
|
262
249
|
});
|
|
@@ -305,19 +292,6 @@ describe('pd runtime init', () => {
|
|
|
305
292
|
} finally { rmTmpDir(tmp); }
|
|
306
293
|
});
|
|
307
294
|
|
|
308
|
-
it('sets exitCode=1 and includes reason when subagent_workflows.db fails', () => {
|
|
309
|
-
const tmp = mkTmpDir();
|
|
310
|
-
try {
|
|
311
|
-
mockState.initWorkflowSchema.mockImplementation(() => {
|
|
312
|
-
throw new Error('locked');
|
|
313
|
-
});
|
|
314
|
-
const output = buildRuntimeInitOutput(tmp, true);
|
|
315
|
-
expect(output.ok).toBe(false);
|
|
316
|
-
expect(output.reason).toContain('subagent_workflows.db');
|
|
317
|
-
expect(output.reason).toContain('locked');
|
|
318
|
-
} finally { rmTmpDir(tmp); }
|
|
319
|
-
});
|
|
320
|
-
|
|
321
295
|
it('handler sets process.exitCode=1 on failure with --json', async () => {
|
|
322
296
|
const tmp = mkTmpDir();
|
|
323
297
|
try {
|