@productbrain/cli 0.1.0-beta.1328 → 0.1.0-beta.1342
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/__tests__/promote-batch.test.d.ts +2 -0
- package/dist/__tests__/promote-batch.test.d.ts.map +1 -0
- package/dist/__tests__/promote-batch.test.js +185 -0
- package/dist/__tests__/promote-batch.test.js.map +1 -0
- package/dist/__tests__/session-close-loop.test.d.ts +2 -0
- package/dist/__tests__/session-close-loop.test.d.ts.map +1 -0
- package/dist/__tests__/session-close-loop.test.js +210 -0
- package/dist/__tests__/session-close-loop.test.js.map +1 -0
- package/dist/commands/promote.d.ts +4 -1
- package/dist/commands/promote.d.ts.map +1 -1
- package/dist/commands/promote.js +137 -68
- package/dist/commands/promote.js.map +1 -1
- package/dist/commands/session.d.ts.map +1 -1
- package/dist/commands/session.js +61 -7
- package/dist/commands/session.js.map +1 -1
- package/dist/formatters/__tests__/session.test.js +84 -6
- package/dist/formatters/__tests__/session.test.js.map +1 -1
- package/dist/formatters/session.d.ts +38 -1
- package/dist/formatters/session.d.ts.map +1 -1
- package/dist/formatters/session.js +125 -17
- package/dist/formatters/session.js.map +1 -1
- package/dist/generators/chain-classifier.d.ts.map +1 -1
- package/dist/generators/chain-classifier.js +4 -1
- package/dist/generators/chain-classifier.js.map +1 -1
- package/dist/generators/chain-classifier.test.js +25 -1
- package/dist/generators/chain-classifier.test.js.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/lib/errors.d.ts +2 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +2 -0
- package/dist/lib/errors.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promote-batch.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/promote-batch.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pb promote — batch (multi-ID) promotion (INS-1813).
|
|
3
|
+
* Accepts multiple entry IDs, promotes each independently, and a single failure
|
|
4
|
+
* does NOT abort the rest of the batch. Reports per-entry results and exits
|
|
5
|
+
* non-zero when any entry failed.
|
|
6
|
+
*/
|
|
7
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
8
|
+
import { runPromote } from '../commands/promote.js';
|
|
9
|
+
import { CLIError, ErrorCode } from '../lib/errors.js';
|
|
10
|
+
const kernelCallWithSessionMock = vi.fn();
|
|
11
|
+
const { MockMcpError } = vi.hoisted(() => {
|
|
12
|
+
class MockMcpError extends Error {
|
|
13
|
+
code;
|
|
14
|
+
details;
|
|
15
|
+
constructor(message, code, details) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = 'McpError';
|
|
18
|
+
this.code = code;
|
|
19
|
+
this.details = details;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return { MockMcpError };
|
|
23
|
+
});
|
|
24
|
+
vi.mock('../lib/client.js', () => ({
|
|
25
|
+
kernelCallWithSession: (...args) => kernelCallWithSessionMock(...args),
|
|
26
|
+
McpError: MockMcpError,
|
|
27
|
+
}));
|
|
28
|
+
vi.mock('../lib/config.js', () => ({
|
|
29
|
+
getConfigOrGuide: vi.fn(() => Promise.resolve({ apiKey: 'pb_sk_test', siteUrl: 'https://test.convex.site' })),
|
|
30
|
+
}));
|
|
31
|
+
vi.mock('../lib/runner.js', () => ({
|
|
32
|
+
isJsonMode: () => false,
|
|
33
|
+
runCliCommand: vi.fn(async ({ fn, formatPretty }) => {
|
|
34
|
+
const data = await fn();
|
|
35
|
+
process.stdout.write(formatPretty(data) + '\n');
|
|
36
|
+
}),
|
|
37
|
+
}));
|
|
38
|
+
let mockSession = { sessionId: 'sess-test' };
|
|
39
|
+
vi.mock('../lib/session.js', () => ({
|
|
40
|
+
readSession: () => mockSession,
|
|
41
|
+
}));
|
|
42
|
+
/** Route by fn + entryId so batches don't depend on call ordering. */
|
|
43
|
+
function installRoutes(opts = {}) {
|
|
44
|
+
const failIds = new Set(opts.failIds ?? []);
|
|
45
|
+
kernelCallWithSessionMock.mockImplementation((fn, args) => {
|
|
46
|
+
const id = args?.entryId ?? '';
|
|
47
|
+
if (fn === 'chain.getEntry') {
|
|
48
|
+
return Promise.resolve({ name: `Name ${id}`, collectionSlug: 'tensions', data: { description: 'A valid description' } });
|
|
49
|
+
}
|
|
50
|
+
if (fn === 'chain.detectSemanticConflicts')
|
|
51
|
+
return Promise.resolve([]);
|
|
52
|
+
if (fn === 'chain.commitEntry') {
|
|
53
|
+
if (failIds.has(id))
|
|
54
|
+
return Promise.reject(new Error(`commit failed for ${id}`));
|
|
55
|
+
return Promise.resolve({ _id: `j-${id}`, entryId: id, name: `Name ${id}`, versionId: 'v1', version: 1 });
|
|
56
|
+
}
|
|
57
|
+
return Promise.resolve({});
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function commitCallsFor(id) {
|
|
61
|
+
return kernelCallWithSessionMock.mock.calls.filter((c) => c[0] === 'chain.commitEntry' && c[1]?.entryId === id).length;
|
|
62
|
+
}
|
|
63
|
+
describe('runPromote — batch promotion (INS-1813)', () => {
|
|
64
|
+
beforeEach(() => {
|
|
65
|
+
vi.clearAllMocks();
|
|
66
|
+
mockSession = { sessionId: 'sess-test' };
|
|
67
|
+
});
|
|
68
|
+
it('promotes every ID in the batch and prints a receipt for each', async () => {
|
|
69
|
+
installRoutes();
|
|
70
|
+
const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
71
|
+
await runPromote({ entryIds: ['TEN-2', 'INS-3', 'DEC-4'] });
|
|
72
|
+
const output = writeSpy.mock.calls.map((c) => String(c[0])).join('');
|
|
73
|
+
writeSpy.mockRestore();
|
|
74
|
+
expect(commitCallsFor('TEN-2')).toBe(1);
|
|
75
|
+
expect(commitCallsFor('INS-3')).toBe(1);
|
|
76
|
+
expect(commitCallsFor('DEC-4')).toBe(1);
|
|
77
|
+
expect(output).toContain('Promoted TEN-2');
|
|
78
|
+
expect(output).toContain('Promoted INS-3');
|
|
79
|
+
expect(output).toContain('Promoted DEC-4');
|
|
80
|
+
});
|
|
81
|
+
it('does not abort the batch when one entry fails — the rest still promote', async () => {
|
|
82
|
+
installRoutes({ failIds: ['INS-3'] });
|
|
83
|
+
const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
84
|
+
let caught;
|
|
85
|
+
try {
|
|
86
|
+
await runPromote({ entryIds: ['TEN-2', 'INS-3', 'DEC-4'] });
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
caught = err;
|
|
90
|
+
}
|
|
91
|
+
const output = writeSpy.mock.calls.map((c) => String(c[0])).join('');
|
|
92
|
+
writeSpy.mockRestore();
|
|
93
|
+
// Every entry was attempted, including the ones after the failure.
|
|
94
|
+
expect(commitCallsFor('TEN-2')).toBe(1);
|
|
95
|
+
expect(commitCallsFor('INS-3')).toBe(1);
|
|
96
|
+
expect(commitCallsFor('DEC-4')).toBe(1);
|
|
97
|
+
// The successful ones still got promoted.
|
|
98
|
+
expect(output).toContain('Promoted TEN-2');
|
|
99
|
+
expect(output).toContain('Promoted DEC-4');
|
|
100
|
+
// The failure is surfaced per-entry...
|
|
101
|
+
expect(output).toContain('INS-3');
|
|
102
|
+
// ...and the batch exits non-zero so automation knows.
|
|
103
|
+
expect(caught).toBeInstanceOf(Error);
|
|
104
|
+
});
|
|
105
|
+
it('still works for a single ID passed via entryIds', async () => {
|
|
106
|
+
installRoutes();
|
|
107
|
+
const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
108
|
+
await runPromote({ entryIds: ['TEN-2'] });
|
|
109
|
+
const output = writeSpy.mock.calls.map((c) => String(c[0])).join('');
|
|
110
|
+
writeSpy.mockRestore();
|
|
111
|
+
expect(commitCallsFor('TEN-2')).toBe(1);
|
|
112
|
+
expect(output).toContain('Promoted TEN-2');
|
|
113
|
+
});
|
|
114
|
+
it('fails fast and preserves the category when a global precondition (auth) fails the batch', async () => {
|
|
115
|
+
// An expired/invalid key fails every id identically. The batch must NOT retry each id
|
|
116
|
+
// and relabel it as a per-entry VALIDATION_FAILED — it rethrows the real auth error
|
|
117
|
+
// immediately, just like the single-id path (Codex P2, PR #196).
|
|
118
|
+
kernelCallWithSessionMock.mockImplementation((fn, args) => {
|
|
119
|
+
const id = args?.entryId ?? '';
|
|
120
|
+
if (fn === 'chain.getEntry') {
|
|
121
|
+
return Promise.resolve({ name: `Name ${id}`, collectionSlug: 'tensions', data: { description: 'A valid description' } });
|
|
122
|
+
}
|
|
123
|
+
if (fn === 'chain.detectSemanticConflicts')
|
|
124
|
+
return Promise.resolve([]);
|
|
125
|
+
if (fn === 'chain.commitEntry') {
|
|
126
|
+
return Promise.reject(new CLIError('API key expired.', {
|
|
127
|
+
code: ErrorCode.AUTH_INVALID,
|
|
128
|
+
category: 'auth',
|
|
129
|
+
guidance: 'Run `pb login` to re-authenticate.',
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
132
|
+
return Promise.resolve({});
|
|
133
|
+
});
|
|
134
|
+
let caught;
|
|
135
|
+
try {
|
|
136
|
+
await runPromote({ entryIds: ['TEN-2', 'INS-3', 'DEC-4'] });
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
caught = err;
|
|
140
|
+
}
|
|
141
|
+
// The real auth category/code survives — not masked as validation.
|
|
142
|
+
expect(caught).toBeInstanceOf(CLIError);
|
|
143
|
+
expect(caught.category).toBe('auth');
|
|
144
|
+
expect(caught.code).toBe(ErrorCode.AUTH_INVALID);
|
|
145
|
+
// Fail fast: it aborted on the first id, never attempting the rest against a dead key.
|
|
146
|
+
expect(commitCallsFor('TEN-2')).toBe(1);
|
|
147
|
+
expect(commitCallsFor('INS-3')).toBe(0);
|
|
148
|
+
expect(commitCallsFor('DEC-4')).toBe(0);
|
|
149
|
+
});
|
|
150
|
+
it('fails fast on a session-category failure (closed/expired session dooms every id)', async () => {
|
|
151
|
+
kernelCallWithSessionMock.mockImplementation((fn, args) => {
|
|
152
|
+
const id = args?.entryId ?? '';
|
|
153
|
+
if (fn === 'chain.getEntry') {
|
|
154
|
+
return Promise.resolve({ name: `Name ${id}`, collectionSlug: 'tensions', data: { description: 'A valid description' } });
|
|
155
|
+
}
|
|
156
|
+
if (fn === 'chain.detectSemanticConflicts')
|
|
157
|
+
return Promise.resolve([]);
|
|
158
|
+
if (fn === 'chain.commitEntry') {
|
|
159
|
+
return Promise.reject(new CLIError('Session expired.', {
|
|
160
|
+
code: ErrorCode.SESSION_REQUIRED,
|
|
161
|
+
category: 'session',
|
|
162
|
+
guidance: 'Run `pb session start`.',
|
|
163
|
+
}));
|
|
164
|
+
}
|
|
165
|
+
return Promise.resolve({});
|
|
166
|
+
});
|
|
167
|
+
let caught;
|
|
168
|
+
try {
|
|
169
|
+
await runPromote({ entryIds: ['TEN-2', 'INS-3'] });
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
caught = err;
|
|
173
|
+
}
|
|
174
|
+
expect(caught.category).toBe('session');
|
|
175
|
+
expect(caught.code).toBe(ErrorCode.SESSION_REQUIRED);
|
|
176
|
+
expect(commitCallsFor('TEN-2')).toBe(1);
|
|
177
|
+
expect(commitCallsFor('INS-3')).toBe(0);
|
|
178
|
+
});
|
|
179
|
+
it('rejects when the batch is empty after trimming', async () => {
|
|
180
|
+
installRoutes();
|
|
181
|
+
await expect(runPromote({ entryIds: [' ', ''] })).rejects.toThrow('Entry ID is required');
|
|
182
|
+
expect(kernelCallWithSessionMock).not.toHaveBeenCalled();
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
//# sourceMappingURL=promote-batch.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promote-batch.test.js","sourceRoot":"","sources":["../../src/__tests__/promote-batch.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-close-loop.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/session-close-loop.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pb session close — finish-the-loop enforcement (TEN-2370 / ASM-40).
|
|
3
|
+
*
|
|
4
|
+
* Close fetches the SERVER wrapup truth (agent.getSessionWrapup) and:
|
|
5
|
+
* - reports committed vs still-draft from server state, not the local snapshot;
|
|
6
|
+
* - when drafts remain and --force is absent: prints the promote command, throws
|
|
7
|
+
* LOOP_UNFINISHED (→ non-zero exit), and DOES NOT close the session;
|
|
8
|
+
* - with --force, or with zero drafts: closes normally;
|
|
9
|
+
* - if the wrapup read fails: degrades to a normal close (never blocks on infra).
|
|
10
|
+
*/
|
|
11
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
12
|
+
import { runSessionClose } from '../commands/session.js';
|
|
13
|
+
import { ErrorCode } from '../lib/errors.js';
|
|
14
|
+
import { setOutputMode } from '../lib/runner.js';
|
|
15
|
+
const kernelCallMock = vi.fn();
|
|
16
|
+
vi.mock('../lib/client.js', () => ({
|
|
17
|
+
kernelCall: (...args) => kernelCallMock(...args),
|
|
18
|
+
kernelCallWithSession: vi.fn(),
|
|
19
|
+
}));
|
|
20
|
+
vi.mock('../lib/config.js', () => ({
|
|
21
|
+
getConfigOrGuide: vi.fn(() => Promise.resolve({ apiKey: 'pb_sk_test', siteUrl: 'https://test.convex.site' })),
|
|
22
|
+
readLocalProjectConfig: vi.fn(() => null),
|
|
23
|
+
}));
|
|
24
|
+
const clearSessionMock = vi.fn();
|
|
25
|
+
let mockSession = null;
|
|
26
|
+
vi.mock('../lib/session.js', () => ({
|
|
27
|
+
readSession: () => mockSession,
|
|
28
|
+
writeSession: vi.fn(),
|
|
29
|
+
clearSession: () => clearSessionMock(),
|
|
30
|
+
addCapturedEntry: vi.fn(),
|
|
31
|
+
}));
|
|
32
|
+
vi.mock('../commands/handshake.js', () => ({ runHandshake: vi.fn() }));
|
|
33
|
+
vi.mock('../lib/constants.js', () => ({ resolveAppUrl: () => 'https://work.productbrain.io' }));
|
|
34
|
+
function installRoutes(opts) {
|
|
35
|
+
kernelCallMock.mockImplementation((fn) => {
|
|
36
|
+
switch (fn) {
|
|
37
|
+
case 'agent.getSessionWrapup':
|
|
38
|
+
if (opts.wrapup instanceof Error)
|
|
39
|
+
return Promise.reject(opts.wrapup);
|
|
40
|
+
return Promise.resolve(opts.wrapup ?? { committed: [], drafts: [] });
|
|
41
|
+
case 'authorityDomains.readiness':
|
|
42
|
+
return opts.readiness !== undefined
|
|
43
|
+
? Promise.resolve(opts.readiness)
|
|
44
|
+
: Promise.reject(new Error('Admin access required.'));
|
|
45
|
+
case 'agent.recordWrapup':
|
|
46
|
+
return Promise.resolve({});
|
|
47
|
+
case 'agent.closeSession':
|
|
48
|
+
return Promise.resolve({ status: 'closed', workspaceSlug: 'acme' });
|
|
49
|
+
case 'setup.listFailedAuditReceipts':
|
|
50
|
+
return Promise.resolve({ total: 0, entries: [] });
|
|
51
|
+
default:
|
|
52
|
+
return Promise.resolve({});
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function calledWith(fn) {
|
|
57
|
+
return kernelCallMock.mock.calls.some((c) => c[0] === fn);
|
|
58
|
+
}
|
|
59
|
+
describe('runSessionClose — finish-the-loop enforcement', () => {
|
|
60
|
+
beforeEach(() => {
|
|
61
|
+
vi.clearAllMocks();
|
|
62
|
+
// Default to the human-terminal (pretty) context so the nudge-text assertions exercise
|
|
63
|
+
// the TTY path. Under the test runner's non-TTY default ('auto'), isJsonMode() resolves
|
|
64
|
+
// to JSON and the nudge is intentionally suppressed — the dedicated --json test covers that.
|
|
65
|
+
setOutputMode('pretty');
|
|
66
|
+
mockSession = {
|
|
67
|
+
sessionId: 'sess-test',
|
|
68
|
+
workspaceId: 'ws-1',
|
|
69
|
+
workspaceName: 'Product Brain',
|
|
70
|
+
startedAt: new Date(Date.now() - 5 * 60_000).toISOString(),
|
|
71
|
+
entriesCaptured: ['DEC-1', 'TEN-2'],
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
it('blocks close with a non-zero LOOP_UNFINISHED error when drafts remain and no --force', async () => {
|
|
75
|
+
installRoutes({
|
|
76
|
+
wrapup: { committed: [{ entryId: 'DEC-1', name: 'Decided X' }], drafts: [{ entryId: 'TEN-2', name: 'Tension Y' }] },
|
|
77
|
+
});
|
|
78
|
+
const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
79
|
+
let caught;
|
|
80
|
+
try {
|
|
81
|
+
await runSessionClose({});
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
caught = err;
|
|
85
|
+
}
|
|
86
|
+
const output = writeSpy.mock.calls.map((c) => String(c[0])).join('');
|
|
87
|
+
writeSpy.mockRestore();
|
|
88
|
+
expect(caught).toBeInstanceOf(Error);
|
|
89
|
+
expect(caught.code).toBe(ErrorCode.LOOP_UNFINISHED);
|
|
90
|
+
// Prints the exact promote command for the remaining draft.
|
|
91
|
+
expect(output).toContain('pb promote TEN-2');
|
|
92
|
+
// The session must NOT be torn down — the agent needs it open to promote.
|
|
93
|
+
expect(calledWith('agent.closeSession')).toBe(false);
|
|
94
|
+
expect(clearSessionMock).not.toHaveBeenCalled();
|
|
95
|
+
});
|
|
96
|
+
it('blocks close on a draft hidden beyond the cap — overflowDrafts is merged into the gate (TEN-2370)', async () => {
|
|
97
|
+
// The capped window shows only committed entries (drafts: []), but a still-draft entry
|
|
98
|
+
// exists past WRAPUP_ENTRY_CAP. The gate must NOT falsely certify the loop finished.
|
|
99
|
+
installRoutes({
|
|
100
|
+
wrapup: {
|
|
101
|
+
committed: [{ entryId: 'DEC-1', name: 'Decided X' }],
|
|
102
|
+
drafts: [],
|
|
103
|
+
overflowDrafts: [{ entryId: 'INS-16', name: 'Overflow insight' }],
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
107
|
+
let caught;
|
|
108
|
+
try {
|
|
109
|
+
await runSessionClose({});
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
caught = err;
|
|
113
|
+
}
|
|
114
|
+
const output = writeSpy.mock.calls.map((c) => String(c[0])).join('');
|
|
115
|
+
writeSpy.mockRestore();
|
|
116
|
+
expect(caught.code).toBe(ErrorCode.LOOP_UNFINISHED);
|
|
117
|
+
// The overflow draft is promotable — its id appears in the promote command.
|
|
118
|
+
expect(output).toContain('pb promote INS-16');
|
|
119
|
+
// And the session stays open so the agent can promote it.
|
|
120
|
+
expect(calledWith('agent.closeSession')).toBe(false);
|
|
121
|
+
expect(clearSessionMock).not.toHaveBeenCalled();
|
|
122
|
+
});
|
|
123
|
+
it('fails closed when the draft set is truncated (overflowUnscanned > 0) even with zero visible drafts', async () => {
|
|
124
|
+
installRoutes({
|
|
125
|
+
wrapup: { committed: [{ entryId: 'DEC-1', name: 'Decided X' }], drafts: [], overflowUnscanned: 3 },
|
|
126
|
+
});
|
|
127
|
+
const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
128
|
+
let caught;
|
|
129
|
+
try {
|
|
130
|
+
await runSessionClose({});
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
caught = err;
|
|
134
|
+
}
|
|
135
|
+
const output = writeSpy.mock.calls.map((c) => String(c[0])).join('');
|
|
136
|
+
writeSpy.mockRestore();
|
|
137
|
+
// Cannot certify the loop finished — must NOT close.
|
|
138
|
+
expect(caught.code).toBe(ErrorCode.LOOP_UNFINISHED);
|
|
139
|
+
expect(output).toContain('could not be read');
|
|
140
|
+
expect(calledWith('agent.closeSession')).toBe(false);
|
|
141
|
+
});
|
|
142
|
+
it('in --json mode the blocked gate writes no human nudge to stdout (still throws LOOP_UNFINISHED)', async () => {
|
|
143
|
+
installRoutes({
|
|
144
|
+
wrapup: { committed: [], drafts: [{ entryId: 'TEN-2', name: 'Tension Y' }] },
|
|
145
|
+
});
|
|
146
|
+
const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
147
|
+
setOutputMode('json');
|
|
148
|
+
let caught;
|
|
149
|
+
try {
|
|
150
|
+
await runSessionClose({});
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
caught = err;
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
setOutputMode('auto');
|
|
157
|
+
}
|
|
158
|
+
const output = writeSpy.mock.calls.map((c) => String(c[0])).join('');
|
|
159
|
+
writeSpy.mockRestore();
|
|
160
|
+
expect(caught.code).toBe(ErrorCode.LOOP_UNFINISHED);
|
|
161
|
+
// The human nudge must NOT corrupt stdout for JSON callers — only the structured
|
|
162
|
+
// error (emitted by the global handler on throw) reaches them.
|
|
163
|
+
expect(output).not.toContain('Finish the loop');
|
|
164
|
+
expect(output).not.toContain('Session NOT closed');
|
|
165
|
+
});
|
|
166
|
+
it('closes anyway when --force is passed even though drafts remain', async () => {
|
|
167
|
+
installRoutes({
|
|
168
|
+
wrapup: { committed: [], drafts: [{ entryId: 'TEN-2', name: 'Tension Y' }] },
|
|
169
|
+
});
|
|
170
|
+
const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
171
|
+
await expect(runSessionClose({ force: true })).resolves.toBeUndefined();
|
|
172
|
+
writeSpy.mockRestore();
|
|
173
|
+
expect(calledWith('agent.closeSession')).toBe(true);
|
|
174
|
+
expect(clearSessionMock).toHaveBeenCalled();
|
|
175
|
+
});
|
|
176
|
+
it('closes normally and reports server truth when every entry was committed', async () => {
|
|
177
|
+
installRoutes({
|
|
178
|
+
wrapup: { committed: [{ entryId: 'DEC-1', name: 'Decided X' }], drafts: [] },
|
|
179
|
+
});
|
|
180
|
+
const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
181
|
+
await runSessionClose({});
|
|
182
|
+
const output = writeSpy.mock.calls.map((c) => String(c[0])).join('');
|
|
183
|
+
writeSpy.mockRestore();
|
|
184
|
+
expect(calledWith('agent.closeSession')).toBe(true);
|
|
185
|
+
expect(clearSessionMock).toHaveBeenCalled();
|
|
186
|
+
expect(output).toContain('1 committed, 0 still draft');
|
|
187
|
+
});
|
|
188
|
+
it('degrades to a normal close when the wrapup read fails (never blocks on infra)', async () => {
|
|
189
|
+
installRoutes({ wrapup: new Error('gateway unreachable') });
|
|
190
|
+
const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
191
|
+
await expect(runSessionClose({})).resolves.toBeUndefined();
|
|
192
|
+
writeSpy.mockRestore();
|
|
193
|
+
// No server truth → no enforcement → close proceeds.
|
|
194
|
+
expect(calledWith('agent.closeSession')).toBe(true);
|
|
195
|
+
expect(clearSessionMock).toHaveBeenCalled();
|
|
196
|
+
});
|
|
197
|
+
it('surfaces the pending domain-tag proposal count when the readiness read succeeds', async () => {
|
|
198
|
+
installRoutes({
|
|
199
|
+
wrapup: { committed: [{ entryId: 'DEC-1', name: 'Decided X' }], drafts: [] },
|
|
200
|
+
readiness: { proposals: { pendingDomainTagCount: 4 } },
|
|
201
|
+
});
|
|
202
|
+
const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
203
|
+
await runSessionClose({});
|
|
204
|
+
const output = writeSpy.mock.calls.map((c) => String(c[0])).join('');
|
|
205
|
+
writeSpy.mockRestore();
|
|
206
|
+
expect(output).toContain('4 pending domain-tag proposal');
|
|
207
|
+
expect(output).toContain('pb authority-domains review');
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
//# sourceMappingURL=session-close-loop.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-close-loop.test.js","sourceRoot":"","sources":["../../src/__tests__/session-close-loop.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
* Thin client: governance checks live in Convex (BR-104).
|
|
5
5
|
*/
|
|
6
6
|
interface PromoteOptions {
|
|
7
|
-
|
|
7
|
+
/** Single entry id (back-compat: `pb promote <id>`). */
|
|
8
|
+
entryId?: string;
|
|
9
|
+
/** Batch of entry ids (INS-1813). Takes precedence when provided. */
|
|
10
|
+
entryIds?: string[];
|
|
8
11
|
message?: string;
|
|
9
12
|
}
|
|
10
13
|
export declare function runPromote(options: PromoteOptions): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"promote.d.ts","sourceRoot":"","sources":["../../src/commands/promote.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,UAAU,cAAc;IACtB,OAAO,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"promote.d.ts","sourceRoot":"","sources":["../../src/commands/promote.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,UAAU,cAAc;IACtB,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA8HD,wBAAsB,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA8FvE"}
|