@productbrain/cli 0.1.0-beta.71 → 0.1.0-beta.72

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 (80) hide show
  1. package/dist/__tests__/audit.test.js +44 -44
  2. package/dist/__tests__/capture.test.js +37 -37
  3. package/dist/__tests__/constellation.test.js +14 -14
  4. package/dist/__tests__/context-strategy.test.js +8 -8
  5. package/dist/__tests__/fields.test.js +20 -20
  6. package/dist/__tests__/ingest.test.js +28 -28
  7. package/dist/__tests__/orient.test.js +8 -8
  8. package/dist/__tests__/promote.test.js +15 -15
  9. package/dist/__tests__/proposals.test.js +18 -18
  10. package/dist/__tests__/relate.test.js +14 -14
  11. package/dist/__tests__/session-touch.test.js +11 -11
  12. package/dist/__tests__/session.test.js +2 -2
  13. package/dist/__tests__/setup.test.js +7 -7
  14. package/dist/__tests__/update.test.js +21 -21
  15. package/dist/__tests__/workspace.test.js +20 -20
  16. package/dist/commands/accept.js +4 -4
  17. package/dist/commands/admin/cockpit.d.ts +4 -2
  18. package/dist/commands/admin/cockpit.d.ts.map +1 -1
  19. package/dist/commands/admin/cockpit.js +213 -6
  20. package/dist/commands/admin/cockpit.js.map +1 -1
  21. package/dist/commands/admin/index.d.ts.map +1 -1
  22. package/dist/commands/admin/index.js +2 -0
  23. package/dist/commands/admin/index.js.map +1 -1
  24. package/dist/commands/admin/inspect.d.ts +9 -0
  25. package/dist/commands/admin/inspect.d.ts.map +1 -1
  26. package/dist/commands/admin/inspect.js +19 -0
  27. package/dist/commands/admin/inspect.js.map +1 -1
  28. package/dist/commands/admin/inspect.test.js +20 -1
  29. package/dist/commands/admin/inspect.test.js.map +1 -1
  30. package/dist/commands/admin/manage.d.ts +8 -0
  31. package/dist/commands/admin/manage.d.ts.map +1 -0
  32. package/dist/commands/admin/manage.js +76 -0
  33. package/dist/commands/admin/manage.js.map +1 -0
  34. package/dist/commands/audit.js +4 -4
  35. package/dist/commands/brief.js +4 -4
  36. package/dist/commands/capture.js +6 -6
  37. package/dist/commands/chain-walk.js +2 -2
  38. package/dist/commands/changes.js +2 -2
  39. package/dist/commands/codex-prep.js +2 -2
  40. package/dist/commands/collections.js +5 -5
  41. package/dist/commands/constellation.js +2 -2
  42. package/dist/commands/context.js +2 -2
  43. package/dist/commands/cross-cut.js +2 -2
  44. package/dist/commands/doctor.js +3 -3
  45. package/dist/commands/doctor.test.js +1 -1
  46. package/dist/commands/fields.js +2 -2
  47. package/dist/commands/get.js +3 -3
  48. package/dist/commands/handshake.js +5 -5
  49. package/dist/commands/ingest.js +6 -6
  50. package/dist/commands/init.d.ts +1 -1
  51. package/dist/commands/init.js +1 -1
  52. package/dist/commands/orient.js +2 -2
  53. package/dist/commands/promote.js +4 -4
  54. package/dist/commands/proposals.js +2 -2
  55. package/dist/commands/reject.js +2 -2
  56. package/dist/commands/relate.js +3 -3
  57. package/dist/commands/search.js +2 -2
  58. package/dist/commands/session.js +7 -7
  59. package/dist/commands/setup.js +5 -5
  60. package/dist/commands/update.js +3 -3
  61. package/dist/commands/usage.d.ts +1 -1
  62. package/dist/commands/usage.js +4 -4
  63. package/dist/commands/verify.js +2 -2
  64. package/dist/commands/workspace.js +4 -4
  65. package/dist/generators/chain-rules.d.ts +3 -3
  66. package/dist/generators/chain-rules.js +3 -3
  67. package/dist/generators/chain-rules.test.js +2 -2
  68. package/dist/generators/portable-knowledge.js +1 -1
  69. package/dist/index.js +1 -1
  70. package/dist/index.js.map +1 -1
  71. package/dist/lib/activation.js +2 -2
  72. package/dist/lib/activation.test.js +3 -3
  73. package/dist/lib/client.d.ts +4 -4
  74. package/dist/lib/client.js +8 -8
  75. package/dist/lib/client.js.map +1 -1
  76. package/dist/lib/onboarding-path-b.js +8 -8
  77. package/dist/lib/onboarding-shared.js +2 -2
  78. package/dist/lib/onboarding.js +4 -4
  79. package/dist/lib/workspace-probe.js +2 -2
  80. package/package.json +1 -1
@@ -3,7 +3,7 @@
3
3
  * BET-182 Slice 2: CLI command + auto-fix.
4
4
  *
5
5
  * Verifies:
6
- * 1. JSON mode: correct structure on pipe (mock convexCall with fixture)
6
+ * 1. JSON mode: correct structure on pipe (mock kernelCall with fixture)
7
7
  * 2. TTY mode: PASS gates collapsed, FAIL gates expanded
8
8
  * 3. Exit codes: 0/1/2/3 for each verdict
9
9
  * 4. --gate flag: only specified gate runs
@@ -15,11 +15,11 @@
15
15
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
16
16
  import { runAudit } from '../commands/audit.js';
17
17
  import { setOutputMode } from '../lib/runner.js';
18
- const convexCallMock = vi.fn();
19
- const convexCallWithSessionMock = vi.fn();
18
+ const kernelCallMock = vi.fn();
19
+ const kernelCallWithSessionMock = vi.fn();
20
20
  vi.mock('../lib/client.js', () => ({
21
- convexCall: (...args) => convexCallMock(...args),
22
- convexCallWithSession: (...args) => convexCallWithSessionMock(...args),
21
+ kernelCall: (...args) => kernelCallMock(...args),
22
+ kernelCallWithSession: (...args) => kernelCallWithSessionMock(...args),
23
23
  }));
24
24
  vi.mock('../lib/config.js', () => ({
25
25
  getConfigOrGuide: vi.fn((_fn) => Promise.resolve({ apiKey: 'pb_sk_test', siteUrl: 'https://test.convex.site' })),
@@ -122,7 +122,7 @@ afterEach(() => {
122
122
  // ─── Tests ──────────────────────────────────────────────────────────────────
123
123
  describe('runAudit — JSON mode (non-TTY)', () => {
124
124
  it('outputs correct JSON structure on pipe', withTty(false, async () => {
125
- convexCallMock.mockResolvedValue(PASS_FIXTURE);
125
+ kernelCallMock.mockResolvedValue(PASS_FIXTURE);
126
126
  const exit = catchExit();
127
127
  await expect(runAudit({ entryIds: ['BET-182'] })).rejects.toThrow('process.exit');
128
128
  const parsed = JSON.parse(stdoutOutput);
@@ -135,10 +135,10 @@ describe('runAudit — JSON mode (non-TTY)', () => {
135
135
  expect(exit.code).toBe(0);
136
136
  }));
137
137
  it('calls chain.auditBet with correct entryId', withTty(false, async () => {
138
- convexCallMock.mockResolvedValue(PASS_FIXTURE);
138
+ kernelCallMock.mockResolvedValue(PASS_FIXTURE);
139
139
  catchExit();
140
140
  await expect(runAudit({ entryIds: ['BET-182'] })).rejects.toThrow('process.exit');
141
- expect(convexCallMock).toHaveBeenCalledWith('chain.auditBet', { entryId: 'BET-182' });
141
+ expect(kernelCallMock).toHaveBeenCalledWith('chain.auditBet', { entryId: 'BET-182' });
142
142
  }));
143
143
  it('strips Convex internals from JSON output', withTty(false, async () => {
144
144
  const fixtureWithInternals = {
@@ -146,7 +146,7 @@ describe('runAudit — JSON mode (non-TTY)', () => {
146
146
  _id: 'sys-internal-id',
147
147
  _creationTime: 123456,
148
148
  };
149
- convexCallMock.mockResolvedValue(fixtureWithInternals);
149
+ kernelCallMock.mockResolvedValue(fixtureWithInternals);
150
150
  catchExit();
151
151
  await expect(runAudit({ entryIds: ['BET-182'] })).rejects.toThrow('process.exit');
152
152
  const parsed = JSON.parse(stdoutOutput);
@@ -156,7 +156,7 @@ describe('runAudit — JSON mode (non-TTY)', () => {
156
156
  });
157
157
  describe('runAudit — TTY mode', () => {
158
158
  it('collapses PASS gates (shows count), expands FAIL gates', withTty(true, async () => {
159
- convexCallMock.mockResolvedValue(FAIL_FIXTURE);
159
+ kernelCallMock.mockResolvedValue(FAIL_FIXTURE);
160
160
  catchExit();
161
161
  await expect(runAudit({ entryIds: ['BET-200'] })).rejects.toThrow('process.exit');
162
162
  // FAIL gates should be expanded with detail
@@ -169,7 +169,7 @@ describe('runAudit — TTY mode', () => {
169
169
  expect(stdoutOutput).toContain('strategic-anchor');
170
170
  }));
171
171
  it('shows WARN gates expanded with hints', withTty(true, async () => {
172
- convexCallMock.mockResolvedValue(WARN_FIXTURE);
172
+ kernelCallMock.mockResolvedValue(WARN_FIXTURE);
173
173
  catchExit();
174
174
  await expect(runAudit({ entryIds: ['BET-201'] })).rejects.toThrow('process.exit');
175
175
  expect(stdoutOutput).toContain('WARN:');
@@ -178,7 +178,7 @@ describe('runAudit — TTY mode', () => {
178
178
  expect(stdoutOutput).toContain('Consider removing auto-link noise');
179
179
  }));
180
180
  it('--verbose expands all gates including PASS', withTty(true, async () => {
181
- convexCallMock.mockResolvedValue(PASS_FIXTURE);
181
+ kernelCallMock.mockResolvedValue(PASS_FIXTURE);
182
182
  catchExit();
183
183
  await expect(runAudit({ entryIds: ['BET-182'], verbose: true })).rejects.toThrow('process.exit');
184
184
  // PASS gates should show detail, not just count
@@ -191,25 +191,25 @@ describe('runAudit — TTY mode', () => {
191
191
  });
192
192
  describe('runAudit — exit codes', () => {
193
193
  it('exit 0 for pass verdict', withTty(false, async () => {
194
- convexCallMock.mockResolvedValue(PASS_FIXTURE);
194
+ kernelCallMock.mockResolvedValue(PASS_FIXTURE);
195
195
  const exit = catchExit();
196
196
  await expect(runAudit({ entryIds: ['BET-182'] })).rejects.toThrow('process.exit');
197
197
  expect(exit.code).toBe(0);
198
198
  }));
199
199
  it('exit 1 for fail verdict', withTty(false, async () => {
200
- convexCallMock.mockResolvedValue(FAIL_FIXTURE);
200
+ kernelCallMock.mockResolvedValue(FAIL_FIXTURE);
201
201
  const exit = catchExit();
202
202
  await expect(runAudit({ entryIds: ['BET-200'] })).rejects.toThrow('process.exit');
203
203
  expect(exit.code).toBe(1);
204
204
  }));
205
205
  it('exit 2 for warn verdict', withTty(false, async () => {
206
- convexCallMock.mockResolvedValue(WARN_FIXTURE);
206
+ kernelCallMock.mockResolvedValue(WARN_FIXTURE);
207
207
  const exit = catchExit();
208
208
  await expect(runAudit({ entryIds: ['BET-201'] })).rejects.toThrow('process.exit');
209
209
  expect(exit.code).toBe(2);
210
210
  }));
211
211
  it('exit 3 for error (entry not found)', withTty(false, async () => {
212
- convexCallMock.mockRejectedValue(new Error('Entry "BET-999" not found.'));
212
+ kernelCallMock.mockRejectedValue(new Error('Entry "BET-999" not found.'));
213
213
  const exit = catchExit();
214
214
  await expect(runAudit({ entryIds: ['BET-999'] })).rejects.toThrow('process.exit');
215
215
  expect(exit.code).toBe(3);
@@ -217,7 +217,7 @@ describe('runAudit — exit codes', () => {
217
217
  });
218
218
  describe('runAudit — --gate flag', () => {
219
219
  it('filters JSON output to only specified gate(s)', withTty(false, async () => {
220
- convexCallMock.mockResolvedValue(FAIL_FIXTURE);
220
+ kernelCallMock.mockResolvedValue(FAIL_FIXTURE);
221
221
  catchExit();
222
222
  await expect(runAudit({ entryIds: ['BET-200'], gate: ['field-completeness'] })).rejects.toThrow('process.exit');
223
223
  const parsed = JSON.parse(stdoutOutput);
@@ -226,7 +226,7 @@ describe('runAudit — --gate flag', () => {
226
226
  expect(parsed.gates[0].gate).toBe('field-completeness');
227
227
  }));
228
228
  it('filters TTY output to only specified gate(s)', withTty(true, async () => {
229
- convexCallMock.mockResolvedValue(FAIL_FIXTURE);
229
+ kernelCallMock.mockResolvedValue(FAIL_FIXTURE);
230
230
  catchExit();
231
231
  await expect(runAudit({ entryIds: ['BET-200'], gate: ['field-completeness'] })).rejects.toThrow('process.exit');
232
232
  // Should show the filtered gate
@@ -235,7 +235,7 @@ describe('runAudit — --gate flag', () => {
235
235
  expect(stdoutOutput).not.toContain('intentional-relations');
236
236
  }));
237
237
  it('supports multiple --gate values', withTty(false, async () => {
238
- convexCallMock.mockResolvedValue(FAIL_FIXTURE);
238
+ kernelCallMock.mockResolvedValue(FAIL_FIXTURE);
239
239
  catchExit();
240
240
  await expect(runAudit({ entryIds: ['BET-200'], gate: ['intentional-relations', 'field-completeness'] })).rejects.toThrow('process.exit');
241
241
  const parsed = JSON.parse(stdoutOutput);
@@ -247,51 +247,51 @@ describe('runAudit — --gate flag', () => {
247
247
  });
248
248
  describe('runAudit — --phase flag', () => {
249
249
  it('passes phase through to MCP call', withTty(false, async () => {
250
- convexCallMock.mockResolvedValue(PASS_FIXTURE);
250
+ kernelCallMock.mockResolvedValue(PASS_FIXTURE);
251
251
  catchExit();
252
252
  await expect(runAudit({ entryIds: ['BET-182'], phase: 'handoff' })).rejects.toThrow('process.exit');
253
- expect(convexCallMock).toHaveBeenCalledWith('chain.auditBet', { entryId: 'BET-182', phase: 'handoff' });
253
+ expect(kernelCallMock).toHaveBeenCalledWith('chain.auditBet', { entryId: 'BET-182', phase: 'handoff' });
254
254
  }));
255
255
  it('omits phase from MCP call when not specified', withTty(false, async () => {
256
- convexCallMock.mockResolvedValue(PASS_FIXTURE);
256
+ kernelCallMock.mockResolvedValue(PASS_FIXTURE);
257
257
  catchExit();
258
258
  await expect(runAudit({ entryIds: ['BET-182'] })).rejects.toThrow('process.exit');
259
- expect(convexCallMock).toHaveBeenCalledWith('chain.auditBet', { entryId: 'BET-182' });
259
+ expect(kernelCallMock).toHaveBeenCalledWith('chain.auditBet', { entryId: 'BET-182' });
260
260
  }));
261
261
  });
262
262
  describe('runAudit — --fix', () => {
263
263
  it('calls updateEntry for exact fixes and reruns audit once', withTty(false, async () => {
264
264
  // First call: failing audit
265
265
  // Second call (after fix): passing audit
266
- convexCallMock
266
+ kernelCallMock
267
267
  .mockResolvedValueOnce(FAIL_FIXTURE)
268
268
  .mockResolvedValueOnce({ ...PASS_FIXTURE, entryId: 'BET-200', entryName: 'Broken Bet' });
269
- convexCallWithSessionMock.mockResolvedValue({ id: 'result-id', warnings: [] });
269
+ kernelCallWithSessionMock.mockResolvedValue({ id: 'result-id', warnings: [] });
270
270
  const exit = catchExit();
271
271
  await expect(runAudit({ entryIds: ['BET-200'], fix: true })).rejects.toThrow('process.exit');
272
272
  // Should have called updateEntry for each exact fix
273
- expect(convexCallWithSessionMock).toHaveBeenCalledWith('chain.updateEntry', {
273
+ expect(kernelCallWithSessionMock).toHaveBeenCalledWith('chain.updateEntry', {
274
274
  entryId: 'BET-200',
275
275
  data: { problem: 'needs definition' },
276
276
  changeNote: 'Auto-fix via pb audit: set problem',
277
277
  });
278
- expect(convexCallWithSessionMock).toHaveBeenCalledWith('chain.updateEntry', {
278
+ expect(kernelCallWithSessionMock).toHaveBeenCalledWith('chain.updateEntry', {
279
279
  entryId: 'BET-200',
280
280
  data: { appetite: '6 weeks' },
281
281
  changeNote: 'Auto-fix via pb audit: set appetite',
282
282
  });
283
- // Should have rerun the audit (2 convexCall invocations total)
284
- expect(convexCallMock).toHaveBeenCalledTimes(2);
283
+ // Should have rerun the audit (2 kernelCall invocations total)
284
+ expect(kernelCallMock).toHaveBeenCalledTimes(2);
285
285
  // Exit code should reflect the rerun result (pass = 0)
286
286
  expect(exit.code).toBe(0);
287
287
  }));
288
288
  it('does not fix when no session is active', withTty(false, async () => {
289
289
  mockSession = null;
290
- convexCallMock.mockResolvedValue(FAIL_FIXTURE);
290
+ kernelCallMock.mockResolvedValue(FAIL_FIXTURE);
291
291
  catchExit();
292
292
  await expect(runAudit({ entryIds: ['BET-200'], fix: true })).rejects.toThrow('process.exit');
293
293
  // Should NOT have called updateEntry
294
- expect(convexCallWithSessionMock).not.toHaveBeenCalled();
294
+ expect(kernelCallWithSessionMock).not.toHaveBeenCalled();
295
295
  // Should still show the result
296
296
  const parsed = JSON.parse(stdoutOutput);
297
297
  expect(parsed.verdict).toBe('fail');
@@ -304,36 +304,36 @@ describe('runAudit — --fix', () => {
304
304
  { gate: 'strategic-anchor', status: 'pass', blocking: true, detail: 'OK' },
305
305
  ],
306
306
  };
307
- convexCallMock.mockResolvedValue(manualFixFixture);
307
+ kernelCallMock.mockResolvedValue(manualFixFixture);
308
308
  catchExit();
309
309
  await expect(runAudit({ entryIds: ['BET-200'], fix: true })).rejects.toThrow('process.exit');
310
310
  // No exact fixes → no updateEntry calls, no rerun
311
- expect(convexCallWithSessionMock).not.toHaveBeenCalled();
312
- expect(convexCallMock).toHaveBeenCalledTimes(1);
311
+ expect(kernelCallWithSessionMock).not.toHaveBeenCalled();
312
+ expect(kernelCallMock).toHaveBeenCalledTimes(1);
313
313
  }));
314
314
  it('stops after one rerun even if issues remain', withTty(false, async () => {
315
315
  // Both calls return failing audit (fix didn't resolve everything)
316
- convexCallMock
316
+ kernelCallMock
317
317
  .mockResolvedValueOnce(FAIL_FIXTURE)
318
318
  .mockResolvedValueOnce(FAIL_FIXTURE);
319
- convexCallWithSessionMock.mockResolvedValue({ id: 'result-id', warnings: [] });
319
+ kernelCallWithSessionMock.mockResolvedValue({ id: 'result-id', warnings: [] });
320
320
  const exit = catchExit();
321
321
  await expect(runAudit({ entryIds: ['BET-200'], fix: true })).rejects.toThrow('process.exit');
322
322
  // Should have run audit exactly 2 times (initial + 1 rerun), not 3
323
- expect(convexCallMock).toHaveBeenCalledTimes(2);
323
+ expect(kernelCallMock).toHaveBeenCalledTimes(2);
324
324
  expect(exit.code).toBe(1);
325
325
  }));
326
326
  });
327
327
  describe('runAudit — batch mode', () => {
328
328
  it('audits multiple bets sequentially', withTty(false, async () => {
329
- convexCallMock
329
+ kernelCallMock
330
330
  .mockResolvedValueOnce(PASS_FIXTURE)
331
331
  .mockResolvedValueOnce(FAIL_FIXTURE);
332
332
  const exit = catchExit();
333
333
  await expect(runAudit({ entryIds: ['BET-182', 'BET-200'] })).rejects.toThrow('process.exit');
334
334
  // Both auditBet calls should have been made
335
- expect(convexCallMock).toHaveBeenCalledWith('chain.auditBet', { entryId: 'BET-182' });
336
- expect(convexCallMock).toHaveBeenCalledWith('chain.auditBet', { entryId: 'BET-200' });
335
+ expect(kernelCallMock).toHaveBeenCalledWith('chain.auditBet', { entryId: 'BET-182' });
336
+ expect(kernelCallMock).toHaveBeenCalledWith('chain.auditBet', { entryId: 'BET-200' });
337
337
  // Exit code should be worst verdict (fail = 1)
338
338
  expect(exit.code).toBe(1);
339
339
  // Should have batch summary as last line of output
@@ -346,7 +346,7 @@ describe('runAudit — batch mode', () => {
346
346
  expect(batchSummary.failed).toBe(1);
347
347
  }));
348
348
  it('shows separator between entries in TTY mode', withTty(true, async () => {
349
- convexCallMock
349
+ kernelCallMock
350
350
  .mockResolvedValueOnce(PASS_FIXTURE)
351
351
  .mockResolvedValueOnce(WARN_FIXTURE);
352
352
  catchExit();
@@ -358,7 +358,7 @@ describe('runAudit — batch mode', () => {
358
358
  expect(stdoutOutput).toContain('BET-201');
359
359
  }));
360
360
  it('continues after error on one entry', withTty(false, async () => {
361
- convexCallMock
361
+ kernelCallMock
362
362
  .mockRejectedValueOnce(new Error('Entry "BET-999" not found.'))
363
363
  .mockResolvedValueOnce(PASS_FIXTURE);
364
364
  const exit = catchExit();
@@ -373,7 +373,7 @@ describe('runAudit — batch mode', () => {
373
373
  });
374
374
  describe('runAudit — error handling', () => {
375
375
  it('outputs JSON error on pipe when entry not found', withTty(false, async () => {
376
- convexCallMock.mockRejectedValue(new Error('Entry "BET-999" not found.'));
376
+ kernelCallMock.mockRejectedValue(new Error('Entry "BET-999" not found.'));
377
377
  const exit = catchExit();
378
378
  await expect(runAudit({ entryIds: ['BET-999'] })).rejects.toThrow('process.exit');
379
379
  expect(exit.code).toBe(3);
@@ -383,7 +383,7 @@ describe('runAudit — error handling', () => {
383
383
  expect(errParsed.entryId).toBe('BET-999');
384
384
  }));
385
385
  it('outputs human error in TTY when entry not found', withTty(true, async () => {
386
- convexCallMock.mockRejectedValue(new Error('Entry "BET-999" not found.'));
386
+ kernelCallMock.mockRejectedValue(new Error('Entry "BET-999" not found.'));
387
387
  const exit = catchExit();
388
388
  await expect(runAudit({ entryIds: ['BET-999'] })).rejects.toThrow('process.exit');
389
389
  expect(exit.code).toBe(3);
@@ -5,9 +5,9 @@
5
5
  */
6
6
  import { beforeEach, describe, expect, it, vi } from 'vitest';
7
7
  import { runCapture } from '../commands/capture.js';
8
- const convexCallWithSessionMock = vi.fn();
8
+ const kernelCallWithSessionMock = vi.fn();
9
9
  vi.mock('../lib/client.js', () => ({
10
- convexCallWithSession: (...args) => convexCallWithSessionMock(...args),
10
+ kernelCallWithSession: (...args) => kernelCallWithSessionMock(...args),
11
11
  }));
12
12
  vi.mock('../lib/config.js', () => ({
13
13
  getConfigOrGuide: vi.fn(() => Promise.resolve({ apiKey: 'pb_sk_test', siteUrl: 'https://test.convex.site' })),
@@ -19,12 +19,12 @@ vi.mock('../lib/session.js', () => ({
19
19
  }));
20
20
  describe('runCapture --link', () => {
21
21
  beforeEach(() => {
22
- convexCallWithSessionMock.mockReset();
22
+ kernelCallWithSessionMock.mockReset();
23
23
  mockSession = { sessionId: 'sess-test' };
24
24
  });
25
25
  it('creates entry then links to target with default relation type', async () => {
26
26
  // Prefix "TEN:" is extracted → skips classification, routes directly to tensions
27
- convexCallWithSessionMock.mockImplementation((fn) => {
27
+ kernelCallWithSessionMock.mockImplementation((fn) => {
28
28
  if (fn === 'chain.suggestLinksForCapture')
29
29
  return Promise.resolve([]);
30
30
  if (fn === 'chain.createEntry')
@@ -36,13 +36,13 @@ describe('runCapture --link', () => {
36
36
  const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
37
37
  await runCapture({ text: 'TEN: missing validation', link: 'BET-151' });
38
38
  // Verify classification was skipped (no resolveCollection call)
39
- expect(convexCallWithSessionMock).not.toHaveBeenCalledWith('chain.resolveCollection', expect.anything());
39
+ expect(kernelCallWithSessionMock).not.toHaveBeenCalledWith('chain.resolveCollection', expect.anything());
40
40
  // Verify prefix was stripped from entry name and routed to correct collection
41
- expect(convexCallWithSessionMock).toHaveBeenCalledWith('chain.createEntry', expect.objectContaining({
41
+ expect(kernelCallWithSessionMock).toHaveBeenCalledWith('chain.createEntry', expect.objectContaining({
42
42
  collectionSlug: 'tensions',
43
43
  name: 'missing validation',
44
44
  }));
45
- expect(convexCallWithSessionMock).toHaveBeenCalledWith('chain.createEntryRelation', {
45
+ expect(kernelCallWithSessionMock).toHaveBeenCalledWith('chain.createEntryRelation', {
46
46
  fromEntryId: 'TEN-710',
47
47
  toEntryId: 'BET-151',
48
48
  type: 'surfaces_tension_in',
@@ -51,7 +51,7 @@ describe('runCapture --link', () => {
51
51
  });
52
52
  it('uses custom relation type when --type provided', async () => {
53
53
  // Prefix "INS:" is extracted → skips classification, routes directly to insights
54
- convexCallWithSessionMock.mockImplementation((fn) => {
54
+ kernelCallWithSessionMock.mockImplementation((fn) => {
55
55
  if (fn === 'chain.suggestLinksForCapture')
56
56
  return Promise.resolve([]);
57
57
  if (fn === 'chain.createEntry')
@@ -62,8 +62,8 @@ describe('runCapture --link', () => {
62
62
  });
63
63
  const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
64
64
  await runCapture({ text: 'INS: discovered X', link: 'DEC-264', type: 'informed_by' });
65
- expect(convexCallWithSessionMock).not.toHaveBeenCalledWith('chain.resolveCollection', expect.anything());
66
- expect(convexCallWithSessionMock).toHaveBeenCalledWith('chain.createEntryRelation', {
65
+ expect(kernelCallWithSessionMock).not.toHaveBeenCalledWith('chain.resolveCollection', expect.anything());
66
+ expect(kernelCallWithSessionMock).toHaveBeenCalledWith('chain.createEntryRelation', {
67
67
  fromEntryId: 'INS-50',
68
68
  toEntryId: 'DEC-264',
69
69
  type: 'informed_by',
@@ -73,12 +73,12 @@ describe('runCapture --link', () => {
73
73
  });
74
74
  describe('runCapture --json', () => {
75
75
  beforeEach(() => {
76
- convexCallWithSessionMock.mockReset();
76
+ kernelCallWithSessionMock.mockReset();
77
77
  mockSession = { sessionId: 'sess-test' };
78
78
  });
79
79
  it('outputs valid JSON with id field and no other stdout', async () => {
80
80
  // Prefix "TEN:" is extracted → skips classification
81
- convexCallWithSessionMock.mockImplementation((fn) => {
81
+ kernelCallWithSessionMock.mockImplementation((fn) => {
82
82
  if (fn === 'chain.suggestLinksForCapture')
83
83
  return Promise.resolve([]);
84
84
  if (fn === 'chain.createEntry')
@@ -111,14 +111,14 @@ describe('runCapture --json', () => {
111
111
  });
112
112
  describe('runCapture description field routing', () => {
113
113
  beforeEach(() => {
114
- convexCallWithSessionMock.mockReset();
114
+ kernelCallWithSessionMock.mockReset();
115
115
  mockSession = { sessionId: 'sess-test' };
116
116
  });
117
117
  it('passes description key to createEntry for assumptions (TEN-1258 — remap happens in Convex)', async () => {
118
118
  // The CLI always sends `description` — the Convex write gate remaps it to `belief`
119
119
  // for the assumptions collection (TEN-1258). Verify the CLI routes correctly and
120
120
  // sends the text as description so the Convex remap can fire.
121
- convexCallWithSessionMock.mockImplementation((fn) => {
121
+ kernelCallWithSessionMock.mockImplementation((fn) => {
122
122
  if (fn === 'chain.suggestLinksForCapture')
123
123
  return Promise.resolve([]);
124
124
  if (fn === 'chain.createEntry')
@@ -127,7 +127,7 @@ describe('runCapture description field routing', () => {
127
127
  });
128
128
  const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
129
129
  await runCapture({ text: 'ASM: users won\'t pay before seeing value' });
130
- const createCall = convexCallWithSessionMock.mock.calls.find(([fn]) => fn === 'chain.createEntry');
130
+ const createCall = kernelCallWithSessionMock.mock.calls.find(([fn]) => fn === 'chain.createEntry');
131
131
  expect(createCall?.[1]).toMatchObject({
132
132
  collectionSlug: 'assumptions',
133
133
  name: "users won't pay before seeing value",
@@ -138,21 +138,21 @@ describe('runCapture description field routing', () => {
138
138
  });
139
139
  describe('runCapture classification guard', () => {
140
140
  beforeEach(() => {
141
- convexCallWithSessionMock.mockReset();
141
+ kernelCallWithSessionMock.mockReset();
142
142
  mockSession = { sessionId: 'sess-test' };
143
143
  });
144
144
  it('throws CLIError when classification requires review', async () => {
145
- convexCallWithSessionMock.mockResolvedValueOnce(null);
145
+ kernelCallWithSessionMock.mockResolvedValueOnce(null);
146
146
  await expect(runCapture({ text: 'Ambiguous capture' })).rejects.toThrow('Classification needs review before capture can continue.');
147
147
  });
148
148
  });
149
149
  describe('runCapture date inference', () => {
150
150
  beforeEach(() => {
151
- convexCallWithSessionMock.mockReset();
151
+ kernelCallWithSessionMock.mockReset();
152
152
  mockSession = { sessionId: 'sess-test' };
153
153
  });
154
154
  it('does not default date to today when no source date is present', async () => {
155
- convexCallWithSessionMock.mockImplementation((fn) => {
155
+ kernelCallWithSessionMock.mockImplementation((fn) => {
156
156
  if (fn === 'chain.suggestLinksForCapture')
157
157
  return Promise.resolve([]);
158
158
  if (fn === 'chain.resolveCollection')
@@ -163,7 +163,7 @@ describe('runCapture date inference', () => {
163
163
  });
164
164
  const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
165
165
  await runCapture({ text: 'Decision without explicit source date' });
166
- const createCall = convexCallWithSessionMock.mock.calls.find(([fn]) => fn === 'chain.createEntry');
166
+ const createCall = kernelCallWithSessionMock.mock.calls.find(([fn]) => fn === 'chain.createEntry');
167
167
  expect(createCall?.[1]).toMatchObject({
168
168
  collectionSlug: 'decisions',
169
169
  data: { description: 'Decision without explicit source date' },
@@ -172,7 +172,7 @@ describe('runCapture date inference', () => {
172
172
  writeSpy.mockRestore();
173
173
  });
174
174
  it('uses inferred source date when the capture text includes one', async () => {
175
- convexCallWithSessionMock.mockImplementation((fn) => {
175
+ kernelCallWithSessionMock.mockImplementation((fn) => {
176
176
  if (fn === 'chain.suggestLinksForCapture')
177
177
  return Promise.resolve([]);
178
178
  if (fn === 'chain.resolveCollection')
@@ -183,7 +183,7 @@ describe('runCapture date inference', () => {
183
183
  });
184
184
  const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
185
185
  await runCapture({ text: 'Decision from April 1, 2026 steering meeting' });
186
- const createCall = convexCallWithSessionMock.mock.calls.find(([fn]) => fn === 'chain.createEntry');
186
+ const createCall = kernelCallWithSessionMock.mock.calls.find(([fn]) => fn === 'chain.createEntry');
187
187
  expect((createCall?.[1]).data.date).toBe('2026-04-01');
188
188
  writeSpy.mockRestore();
189
189
  });
@@ -201,13 +201,13 @@ describe('runCapture WP-319 grounding', () => {
201
201
  v: 1,
202
202
  };
203
203
  beforeEach(() => {
204
- convexCallWithSessionMock.mockReset();
204
+ kernelCallWithSessionMock.mockReset();
205
205
  mockSession = { sessionId: 'sess-test' };
206
206
  delete process.env.PB_GROUNDING_MODE;
207
207
  });
208
208
  it('fires chain.suggestLinksForCapture in parallel and still calls createEntry', async () => {
209
209
  // Prefix-based route skips classification; grounding fires for TEN prefix
210
- convexCallWithSessionMock
210
+ kernelCallWithSessionMock
211
211
  .mockImplementation((fn) => {
212
212
  if (fn === 'chain.suggestLinksForCapture')
213
213
  return Promise.resolve([emptyGrounding]);
@@ -218,15 +218,15 @@ describe('runCapture WP-319 grounding', () => {
218
218
  const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
219
219
  const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
220
220
  await runCapture({ text: 'TEN: grounding test tension' });
221
- expect(convexCallWithSessionMock).toHaveBeenCalledWith('chain.suggestLinksForCapture', expect.objectContaining({
221
+ expect(kernelCallWithSessionMock).toHaveBeenCalledWith('chain.suggestLinksForCapture', expect.objectContaining({
222
222
  entries: expect.arrayContaining([expect.objectContaining({ name: 'grounding test tension' })]),
223
223
  }));
224
- expect(convexCallWithSessionMock).toHaveBeenCalledWith('chain.createEntry', expect.anything());
224
+ expect(kernelCallWithSessionMock).toHaveBeenCalledWith('chain.createEntry', expect.anything());
225
225
  writeSpy.mockRestore();
226
226
  stderrSpy.mockRestore();
227
227
  });
228
228
  it('does NOT block capture when grounding call fails', async () => {
229
- convexCallWithSessionMock
229
+ kernelCallWithSessionMock
230
230
  .mockImplementation((fn) => {
231
231
  if (fn === 'chain.suggestLinksForCapture')
232
232
  return Promise.reject(new Error('network error'));
@@ -238,13 +238,13 @@ describe('runCapture WP-319 grounding', () => {
238
238
  const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
239
239
  // Must not throw — grounding failure is fail-open
240
240
  await expect(runCapture({ text: 'TEN: grounding failure test' })).resolves.toBeUndefined();
241
- expect(convexCallWithSessionMock).toHaveBeenCalledWith('chain.createEntry', expect.anything());
241
+ expect(kernelCallWithSessionMock).toHaveBeenCalledWith('chain.createEntry', expect.anything());
242
242
  writeSpy.mockRestore();
243
243
  stderrSpy.mockRestore();
244
244
  });
245
245
  it('--suggest-only does NOT call createEntry and outputs grounding data', async () => {
246
246
  // In test env (non-TTY), isJsonMode() returns true → JSON path is taken.
247
- convexCallWithSessionMock
247
+ kernelCallWithSessionMock
248
248
  .mockImplementation((fn) => {
249
249
  if (fn === 'chain.suggestLinksForCapture')
250
250
  return Promise.resolve([groundingWithDuplicate]);
@@ -257,7 +257,7 @@ describe('runCapture WP-319 grounding', () => {
257
257
  });
258
258
  const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
259
259
  await runCapture({ text: 'TEN: duplicate candidate', suggestOnly: true });
260
- expect(convexCallWithSessionMock).not.toHaveBeenCalledWith('chain.createEntry', expect.anything());
260
+ expect(kernelCallWithSessionMock).not.toHaveBeenCalledWith('chain.createEntry', expect.anything());
261
261
  // JSON output contains suggestOnly flag and grounding report
262
262
  const jsonLines = stdoutOutput.split('\n').filter((l) => { try {
263
263
  JSON.parse(l);
@@ -274,7 +274,7 @@ describe('runCapture WP-319 grounding', () => {
274
274
  stderrSpy.mockRestore();
275
275
  });
276
276
  it('--suggest-only with --json outputs machine-readable object without calling createEntry', async () => {
277
- convexCallWithSessionMock
277
+ kernelCallWithSessionMock
278
278
  .mockImplementation((fn) => {
279
279
  if (fn === 'chain.suggestLinksForCapture')
280
280
  return Promise.resolve([emptyGrounding]);
@@ -287,7 +287,7 @@ describe('runCapture WP-319 grounding', () => {
287
287
  });
288
288
  const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
289
289
  await runCapture({ text: 'TEN: dry run json', json: true, suggestOnly: true });
290
- expect(convexCallWithSessionMock).not.toHaveBeenCalledWith('chain.createEntry', expect.anything());
290
+ expect(kernelCallWithSessionMock).not.toHaveBeenCalledWith('chain.createEntry', expect.anything());
291
291
  const jsonLines = stdoutOutput.split('\n').filter((l) => { try {
292
292
  JSON.parse(l);
293
293
  return true;
@@ -302,7 +302,7 @@ describe('runCapture WP-319 grounding', () => {
302
302
  stderrSpy.mockRestore();
303
303
  });
304
304
  it('banner is suppressed when grounding has no matches (zero-noise floor)', async () => {
305
- convexCallWithSessionMock
305
+ kernelCallWithSessionMock
306
306
  .mockImplementation((fn) => {
307
307
  if (fn === 'chain.suggestLinksForCapture')
308
308
  return Promise.resolve([emptyGrounding]);
@@ -325,7 +325,7 @@ describe('runCapture WP-319 grounding', () => {
325
325
  });
326
326
  it('PB_GROUNDING_MODE=off skips grounding call entirely', async () => {
327
327
  process.env.PB_GROUNDING_MODE = 'off';
328
- convexCallWithSessionMock
328
+ kernelCallWithSessionMock
329
329
  .mockImplementation((fn) => {
330
330
  if (fn === 'chain.createEntry')
331
331
  return Promise.resolve({ docId: 'doc-4', entryId: 'TEN-723' });
@@ -334,14 +334,14 @@ describe('runCapture WP-319 grounding', () => {
334
334
  const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
335
335
  const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
336
336
  await runCapture({ text: 'TEN: no grounding env off' });
337
- expect(convexCallWithSessionMock).not.toHaveBeenCalledWith('chain.suggestLinksForCapture', expect.anything());
338
- expect(convexCallWithSessionMock).toHaveBeenCalledWith('chain.createEntry', expect.anything());
337
+ expect(kernelCallWithSessionMock).not.toHaveBeenCalledWith('chain.suggestLinksForCapture', expect.anything());
338
+ expect(kernelCallWithSessionMock).toHaveBeenCalledWith('chain.createEntry', expect.anything());
339
339
  delete process.env.PB_GROUNDING_MODE;
340
340
  writeSpy.mockRestore();
341
341
  stderrSpy.mockRestore();
342
342
  });
343
343
  it('--json output stdout is clean (no grounding on stdout)', async () => {
344
- convexCallWithSessionMock
344
+ kernelCallWithSessionMock
345
345
  .mockImplementation((fn) => {
346
346
  if (fn === 'chain.suggestLinksForCapture')
347
347
  return Promise.resolve([groundingWithDuplicate]);