@productbrain/cli 0.1.0-beta.4239 → 0.1.0-beta.4250

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.
@@ -0,0 +1,29 @@
1
+ import { Command } from 'commander';
2
+ export interface QuestionCreateOptions {
3
+ domain?: string;
4
+ blocks?: string[];
5
+ }
6
+ export declare function runQuestionCreateVerb(text: string, options: QuestionCreateOptions): Promise<void>;
7
+ export interface QuestionAdoptOptions {
8
+ domain?: string;
9
+ blocks?: string[];
10
+ }
11
+ export declare function runQuestionAdopt(entryId: string, options: QuestionAdoptOptions): Promise<void>;
12
+ export interface QuestionAssignOptions {
13
+ toRole?: string;
14
+ toPerson?: string;
15
+ }
16
+ export declare function runQuestionAssign(entryId: string, options: QuestionAssignOptions): Promise<void>;
17
+ export declare function runQuestionSnooze(entryId: string): Promise<void>;
18
+ export declare function runQuestionDecline(entryId: string, options: {
19
+ reason: string;
20
+ }): Promise<void>;
21
+ export interface QuestionAnswerOptions {
22
+ entry?: string;
23
+ }
24
+ export declare function runQuestionAnswer(entryId: string, text: string | undefined, options: QuestionAnswerOptions): Promise<void>;
25
+ export declare function runQuestionForceClose(entryId: string, options: {
26
+ reason: string;
27
+ }): Promise<void>;
28
+ export declare function createQuestionCommand(): Command;
29
+ //# sourceMappingURL=questions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"questions.d.ts","sourceRoot":"","sources":["../../src/commands/questions.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuFpC,MAAM,WAAW,qBAAqB;IAAG,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE;AAS7E,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBvG;AAID,MAAM,WAAW,oBAAoB;IAAG,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE;AAE5E,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBpG;AAID,MAAM,WAAW,qBAAqB;IAAG,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE;AAE7E,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBtG;AAID,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CActE;AAID,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBpG;AAID,MAAM,WAAW,qBAAqB;IAAG,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;AAEzD,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoChI;AAID,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBvG;AAID,wBAAgB,qBAAqB,IAAI,OAAO,CA8C/C"}
@@ -0,0 +1,285 @@
1
+ import { Command } from 'commander';
2
+ import { getConfigOrGuide } from '../lib/config.js';
3
+ import { kernelCall, kernelCallWithSession } from '../lib/client.js';
4
+ import { CLIError, ErrorCode } from '../lib/errors.js';
5
+ import { runCliCommand } from '../lib/runner.js';
6
+ import { readSession } from '../lib/session.js';
7
+ import { formatQuestionCreate, formatQuestionAdopt, formatQuestionAssignConfirm, formatQuestionAssignReassign, formatQuestionSnooze, formatQuestionDecline, formatQuestionAnswer, formatQuestionForceClose, } from '../formatters/questions.js';
8
+ /**
9
+ * `pb question` — routed-questions connector surface (WP-638 S3, §4.4 of the routed-questions
10
+ * design spec: docs/superpowers/specs/2026-08-10-routed-questions-design.md). Modeled on
11
+ * `conflicts.ts`'s read/write split: every verb here is a workspace WRITE (kernelCallWithSession
12
+ * + a session gate, matching conflicts.resolve/shape.agree). `list` (a plain read) ships in S4
13
+ * with its own read model (ackGateFacts.ts) — §4.1's slice-boundary fix.
14
+ *
15
+ * A "why it matters" tip (§3.3): a question created/adopted below the 80-char nudge threshold
16
+ * already carries the nudge text in the mutation's own return — appended verbatim, never
17
+ * reworded here.
18
+ */
19
+ function requireQuestionSession() {
20
+ const session = readSession();
21
+ if (!session) {
22
+ throw new CLIError('No active session.', {
23
+ code: ErrorCode.SESSION_REQUIRED,
24
+ category: 'session',
25
+ guidance: 'Run `pb session start` before acting on a routed question.',
26
+ });
27
+ }
28
+ return session;
29
+ }
30
+ // ─── Human-id resolution (R43) ──────────────────────────────────────────────────────────────
31
+ // The question write verbs declare entryId/toRoleId as v.id('entries') — a raw Convex doc id,
32
+ // never a string parser of their own. `chain.getEntry` is the real, already-registered
33
+ // gateway route that resolves either form (STD-102) — this command routes through it instead
34
+ // of inventing a private parser, satisfying R43 without touching the verbs' argument shapes.
35
+ const HUMAN_ID_PATTERN = /^[A-Za-z]+-\d+$/;
36
+ async function resolveEntryRef(raw, label) {
37
+ if (!HUMAN_ID_PATTERN.test(raw))
38
+ return raw;
39
+ const entry = await kernelCall('chain.getEntry', { entryId: raw });
40
+ if (!entry) {
41
+ throw new CLIError(`${label} '${raw}' not found.`, {
42
+ code: ErrorCode.VALIDATION_FAILED,
43
+ category: 'validation',
44
+ guidance: 'Check the id — this workspace has no entry matching it.',
45
+ });
46
+ }
47
+ return entry._id;
48
+ }
49
+ // `chain.getEntry`'s `id` param is `v.id('entries')` — it can resolve a role/domain id (both
50
+ // live in `entries`) but NOT a `people`-table id (e.g. `askerPersonId`, `ownerPersonId`), which
51
+ // throws an ArgumentValidationError. Caught here so a name-resolution failure (wrong table, or
52
+ // any other lookup miss) never breaks the write confirmation it decorates — degrades to the raw
53
+ // id/undefined, never a crash. Bug found live (S3 build's own live-proof run against the seeded
54
+ // preview): the first `answer` proof call surfaced exactly this cross-table mismatch.
55
+ async function displayName(id) {
56
+ if (!id)
57
+ return undefined;
58
+ try {
59
+ const entry = await kernelCall('chain.getEntry', { id });
60
+ return entry?.name;
61
+ }
62
+ catch {
63
+ return undefined;
64
+ }
65
+ }
66
+ // The role/team-lead NAME needed for §7.7's confirmation string requires a second read
67
+ // (chain.getEntry) after the write. PRRT_kwDORS4ils6YnqKe (round 2): runCliCommand's JSON mode
68
+ // serializes whatever fn() RETURNS, so bundling the resolved name into that return value
69
+ // (the earlier shape) leaked a `{result, roleOrLeadName}` wrapper into JSON-mode/scripted
70
+ // output instead of the flat mutation result. Fixed the way formatters/shape.ts's own
71
+ // `truncated` aux state does it: a closure variable OUTSIDE runCliCommand, set inside fn(),
72
+ // read by formatPretty — fn() returns ONLY the real result.
73
+ export async function runQuestionCreateVerb(text, options) {
74
+ const config = await getConfigOrGuide(() => runQuestionCreateVerb(text, options));
75
+ if (!config)
76
+ return;
77
+ requireQuestionSession();
78
+ let roleOrLeadName = 'someone';
79
+ await runCliCommand({
80
+ fn: async () => {
81
+ const resolvedBlocks = options.blocks ? await Promise.all(options.blocks.map((b) => resolveEntryRef(b, 'Blocked entry'))) : undefined;
82
+ const result = await kernelCallWithSession('question.create', {
83
+ text,
84
+ domainSlug: options.domain,
85
+ blocks: resolvedBlocks,
86
+ });
87
+ roleOrLeadName = (await displayName(result.suggestedRoleId ?? result.ownerRoleId)) ?? (result.suggestedRoleId ?? result.ownerRoleId ?? 'someone');
88
+ return result;
89
+ },
90
+ formatPretty: (result) => formatQuestionCreate(result, roleOrLeadName),
91
+ });
92
+ }
93
+ export async function runQuestionAdopt(entryId, options) {
94
+ const config = await getConfigOrGuide(() => runQuestionAdopt(entryId, options));
95
+ if (!config)
96
+ return;
97
+ requireQuestionSession();
98
+ let roleOrLeadName = 'someone';
99
+ await runCliCommand({
100
+ fn: async () => {
101
+ const resolvedEntryId = await resolveEntryRef(entryId, 'Entry');
102
+ const resolvedBlocks = options.blocks ? await Promise.all(options.blocks.map((b) => resolveEntryRef(b, 'Blocked entry'))) : undefined;
103
+ const result = await kernelCallWithSession('question.adopt', {
104
+ entryId: resolvedEntryId,
105
+ domainSlug: options.domain,
106
+ blocks: resolvedBlocks,
107
+ });
108
+ roleOrLeadName = (await displayName(result.suggestedRoleId ?? result.ownerRoleId)) ?? (result.suggestedRoleId ?? result.ownerRoleId ?? 'someone');
109
+ return result;
110
+ },
111
+ formatPretty: (result) => formatQuestionAdopt(result, roleOrLeadName),
112
+ });
113
+ }
114
+ export async function runQuestionAssign(entryId, options) {
115
+ const config = await getConfigOrGuide(() => runQuestionAssign(entryId, options));
116
+ if (!config)
117
+ return;
118
+ requireQuestionSession();
119
+ const isReassign = Boolean(options.toRole || options.toPerson);
120
+ let targetName;
121
+ await runCliCommand({
122
+ fn: async () => {
123
+ const resolvedEntryId = await resolveEntryRef(entryId, 'Entry');
124
+ const resolvedToRoleId = options.toRole ? await resolveEntryRef(options.toRole, 'Role') : undefined;
125
+ const result = await kernelCallWithSession('question.assign', {
126
+ entryId: resolvedEntryId,
127
+ toRoleId: resolvedToRoleId,
128
+ toPersonId: options.toPerson,
129
+ });
130
+ // PRRT_kwDORS4ils6YnqKM (round 2): people are a separate table from entries —
131
+ // chain.getEntry cannot resolve a personId (throws ArgumentValidationError, caught by
132
+ // displayName() below and degraded to undefined). The mutation already resolves the
133
+ // person server-side (a plain ctx.db.get) when --to-person names one — prefer that
134
+ // real name over a client-side lookup that structurally cannot succeed for a person.
135
+ targetName = options.toPerson ? (result.ownerPersonName ?? result.ownerPersonId) : ((await displayName(result.ownerRoleId)) ?? result.ownerRoleId);
136
+ return result;
137
+ },
138
+ formatPretty: () => (isReassign ? formatQuestionAssignReassign(entryId, targetName ?? 'the target') : formatQuestionAssignConfirm(entryId)),
139
+ });
140
+ }
141
+ // ─── snooze ──────────────────────────────────────────────────────────────────────────────────
142
+ export async function runQuestionSnooze(entryId) {
143
+ const config = await getConfigOrGuide(() => runQuestionSnooze(entryId));
144
+ if (!config)
145
+ return;
146
+ requireQuestionSession();
147
+ let leadName;
148
+ await runCliCommand({
149
+ fn: async () => {
150
+ const resolvedEntryId = await resolveEntryRef(entryId, 'Entry');
151
+ const result = await kernelCallWithSession('question.snooze', { entryId: resolvedEntryId });
152
+ leadName = result.status === 'escalated' ? await displayName(result.ownerRoleId) : undefined;
153
+ return result;
154
+ },
155
+ formatPretty: (result) => formatQuestionSnooze(result, leadName),
156
+ });
157
+ }
158
+ // ─── decline ─────────────────────────────────────────────────────────────────────────────────
159
+ export async function runQuestionDecline(entryId, options) {
160
+ if (!options.reason?.trim()) {
161
+ throw new CLIError('A reason is required — say why it isn\'t yours (or why you\'re declining it).', {
162
+ code: ErrorCode.VALIDATION_FAILED,
163
+ category: 'validation',
164
+ guidance: 'Pass --reason "<why>".',
165
+ });
166
+ }
167
+ const config = await getConfigOrGuide(() => runQuestionDecline(entryId, options));
168
+ if (!config)
169
+ return;
170
+ requireQuestionSession();
171
+ let roleOrLeadName;
172
+ await runCliCommand({
173
+ fn: async () => {
174
+ const resolvedEntryId = await resolveEntryRef(entryId, 'Entry');
175
+ const result = await kernelCallWithSession('question.decline', {
176
+ entryId: resolvedEntryId,
177
+ reason: options.reason,
178
+ });
179
+ roleOrLeadName = await displayName(result.suggestedRoleId ?? result.ownerRoleId);
180
+ return result;
181
+ },
182
+ formatPretty: (result) => formatQuestionDecline(entryId, result, roleOrLeadName),
183
+ });
184
+ }
185
+ export async function runQuestionAnswer(entryId, text, options) {
186
+ // §7.7's two distinct declared refusal strings — matches the MCP tool's equivalent
187
+ // tool-boundary check (packages/mcp-server/src/tools/question.ts's handleQuestionAnswer).
188
+ if (text === undefined && options.entry === undefined) {
189
+ throw new CLIError('Give the answer as text, or point at an entry with --entry <id>.', {
190
+ code: ErrorCode.VALIDATION_FAILED,
191
+ category: 'validation',
192
+ guidance: 'pb question answer <id> "<text>" OR pb question answer <id> --entry <entryId>',
193
+ });
194
+ }
195
+ if (text !== undefined && options.entry !== undefined) {
196
+ throw new CLIError('Use either answer text or --entry, not both.', {
197
+ code: ErrorCode.VALIDATION_FAILED,
198
+ category: 'validation',
199
+ guidance: 'pb question answer <id> "<text>" OR pb question answer <id> --entry <entryId>',
200
+ });
201
+ }
202
+ const config = await getConfigOrGuide(() => runQuestionAnswer(entryId, text, options));
203
+ if (!config)
204
+ return;
205
+ requireQuestionSession();
206
+ await runCliCommand({
207
+ fn: async () => {
208
+ const resolvedEntryId = await resolveEntryRef(entryId, 'Entry');
209
+ const resolvedAnswerEntryId = options.entry ? await resolveEntryRef(options.entry, 'Answer entry') : undefined;
210
+ // PRRT_kwDORS4ils6YnqKM (round 2): the mutation already resolves the asker's display
211
+ // name server-side (answerCore does a plain ctx.db.get on facet.createdByPersonId) —
212
+ // no client-side people lookup is attempted (chain.getEntry cannot reach the people
213
+ // table at all, unlike the role/domain lookups elsewhere in this file).
214
+ return await kernelCallWithSession('question.answer', {
215
+ entryId: resolvedEntryId,
216
+ text,
217
+ answerEntryId: resolvedAnswerEntryId,
218
+ });
219
+ },
220
+ formatPretty: (result) => formatQuestionAnswer(entryId, result, result.askerName),
221
+ });
222
+ }
223
+ // ─── force-close ─────────────────────────────────────────────────────────────────────────────
224
+ export async function runQuestionForceClose(entryId, options) {
225
+ if (!options.reason?.trim()) {
226
+ throw new CLIError('A reason is required to force-close a question.', {
227
+ code: ErrorCode.VALIDATION_FAILED,
228
+ category: 'validation',
229
+ guidance: 'Pass --reason "<why>".',
230
+ });
231
+ }
232
+ const config = await getConfigOrGuide(() => runQuestionForceClose(entryId, options));
233
+ if (!config)
234
+ return;
235
+ requireQuestionSession();
236
+ await runCliCommand({
237
+ fn: async () => {
238
+ const resolvedEntryId = await resolveEntryRef(entryId, 'Entry');
239
+ return kernelCallWithSession('question.forceClose', {
240
+ entryId: resolvedEntryId,
241
+ reason: options.reason,
242
+ });
243
+ },
244
+ formatPretty: (result) => formatQuestionForceClose(entryId, result),
245
+ });
246
+ }
247
+ // ─── command registration ────────────────────────────────────────────────────────────────────
248
+ export function createQuestionCommand() {
249
+ const command = new Command('question').description('Routed questions (WP-638) — a question with no owner routes itself: to the owning role when clear, to the team lead when ambiguous, up the chain until someone owns it. ' +
250
+ 'Domains are discovered by using this tool (an unresolvable --domain refusal lists the real slugs — never hardcoded). ' +
251
+ '`adopt` attaches routing to a tension already captured via `pb capture "TEN: ..."` — the real birth path for most questions. ' +
252
+ '`--blocks <entry-id>` (repeatable, on create/adopt) records what a question is holding up.');
253
+ command.command('create <text>')
254
+ .description('Create a new routed question from text')
255
+ .option('--domain <slug>', 'Domain slug (see the refusal for the live list if omitted/unknown)')
256
+ .option('--blocks <entryId...>', 'Entry ids this question is blocking (repeatable)')
257
+ .action(runQuestionCreateVerb);
258
+ command.command('adopt <entry-id>')
259
+ .description('Attach routing to a tension already captured (e.g. via `pb capture "TEN: ..."`)')
260
+ .option('--domain <slug>', 'Domain slug (see the refusal for the live list if omitted/unknown)')
261
+ .option('--blocks <entryId...>', 'Entry ids this question is blocking (repeatable)')
262
+ .action(runQuestionAdopt);
263
+ command.command('assign <id>')
264
+ .description('Confirm ("take it", no flags) or reassign (--to-role/--to-person) ownership')
265
+ .option('--to-role <roleId>', 'Reassign to this role (human ROL-nnn form or a raw entry id)')
266
+ .option('--to-person <personId>', 'Reassign to this person')
267
+ .action((entryId, opts) => runQuestionAssign(entryId, opts));
268
+ command.command('snooze <id>')
269
+ .description('Defer a routed/escalated question (max 2, then it escalates to the team lead)')
270
+ .action(runQuestionSnooze);
271
+ command.command('decline <id>')
272
+ .description('Pass on a suggested question (never closes it), or — as owner/team lead — decline an owned one')
273
+ .requiredOption('--reason <reason>', 'Why it isn\'t yours, or why you\'re declining it')
274
+ .action((entryId, opts) => runQuestionDecline(entryId, opts));
275
+ command.command('answer <id> [text]')
276
+ .description('Close the loop — creates the answering entry itself unless --entry is given')
277
+ .option('--entry <entryId>', 'Point at an already-written entry instead of supplying text')
278
+ .action((entryId, text, opts) => runQuestionAnswer(entryId, text, opts));
279
+ command.command('force-close <id>')
280
+ .description('Team-Lead-only escape hatch for a question that cannot otherwise be resolved')
281
+ .requiredOption('--reason <reason>', 'Why this is being force-closed')
282
+ .action((entryId, opts) => runQuestionForceClose(entryId, opts));
283
+ return command;
284
+ }
285
+ //# sourceMappingURL=questions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"questions.js","sourceRoot":"","sources":["../../src/commands/questions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=questions.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"questions.test.d.ts","sourceRoot":"","sources":["../../src/commands/questions.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,174 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ const { getConfigOrGuide, kernelCall, kernelCallWithSession, readSession, runCliCommand } = vi.hoisted(() => ({
3
+ getConfigOrGuide: vi.fn(),
4
+ kernelCall: vi.fn(),
5
+ kernelCallWithSession: vi.fn(),
6
+ readSession: vi.fn(),
7
+ runCliCommand: vi.fn(async ({ fn }) => fn()),
8
+ }));
9
+ vi.mock('../lib/config.js', () => ({ getConfigOrGuide }));
10
+ vi.mock('../lib/client.js', () => ({ kernelCall, kernelCallWithSession }));
11
+ vi.mock('../lib/runner.js', () => ({ runCliCommand }));
12
+ vi.mock('../lib/session.js', () => ({ readSession }));
13
+ import { createQuestionCommand, runQuestionCreateVerb, runQuestionAdopt, runQuestionAssign, runQuestionSnooze, runQuestionDecline, runQuestionAnswer, runQuestionForceClose, } from './questions.js';
14
+ /**
15
+ * `pb question` command surface tests (WP-638 S3). Mirrors commands/shape.test.ts's shape:
16
+ * every verb here is a workspace WRITE (session-attributed, kernelCallWithSession), and
17
+ * human-id (TEN-nnn) args are resolved via `chain.getEntry` (a plain read, kernelCall) before
18
+ * the write — the R43 resolution layer this file builds instead of a private parser.
19
+ */
20
+ describe('pb question command surface (WP-638 S3)', () => {
21
+ beforeEach(() => {
22
+ vi.clearAllMocks();
23
+ getConfigOrGuide.mockResolvedValue({ apiKey: 'pb_sk_test', siteUrl: 'https://example.test' });
24
+ readSession.mockReturnValue({ sessionId: 'session-1' });
25
+ // Default: chain.getEntry resolves any human id to itself as _id — tests that need a
26
+ // DIFFERENT resolved doc id override with mockResolvedValueOnce.
27
+ kernelCall.mockImplementation(async (_fn, args) => ({ _id: args.entryId ?? args.id }));
28
+ kernelCallWithSession.mockResolvedValue({ entryId: 'TEN-100', facetId: 'facet-1', status: 'unrouted', routingReason: 'single_domain_owner', domainSlugs: [] });
29
+ });
30
+ it('exposes all seven write subcommands, and none of them is `list` (S4 ships list)', () => {
31
+ const command = createQuestionCommand();
32
+ const names = command.commands.map((item) => item.name());
33
+ expect(names).toEqual(['create', 'adopt', 'assign', 'snooze', 'decline', 'answer', 'force-close']);
34
+ expect(names).not.toContain('list');
35
+ });
36
+ it('requires an active session before every write verb (workspace write, session-attributed)', async () => {
37
+ readSession.mockReturnValue(null);
38
+ await expect(runQuestionCreateVerb('a question', {})).rejects.toMatchObject({ code: 'SESSION_REQUIRED' });
39
+ await expect(runQuestionAdopt('TEN-1', {})).rejects.toMatchObject({ code: 'SESSION_REQUIRED' });
40
+ await expect(runQuestionAssign('TEN-1', {})).rejects.toMatchObject({ code: 'SESSION_REQUIRED' });
41
+ await expect(runQuestionSnooze('TEN-1')).rejects.toMatchObject({ code: 'SESSION_REQUIRED' });
42
+ await expect(runQuestionDecline('TEN-1', { reason: 'not mine' })).rejects.toMatchObject({ code: 'SESSION_REQUIRED' });
43
+ await expect(runQuestionAnswer('TEN-1', 'the answer', {})).rejects.toMatchObject({ code: 'SESSION_REQUIRED' });
44
+ await expect(runQuestionForceClose('TEN-1', { reason: 'stuck' })).rejects.toMatchObject({ code: 'SESSION_REQUIRED' });
45
+ expect(kernelCallWithSession).not.toHaveBeenCalled();
46
+ });
47
+ it('create: sends text/domainSlug/blocks straight through when no --blocks are human ids', async () => {
48
+ await runQuestionCreateVerb('a question', { domain: 'delivery' });
49
+ expect(kernelCallWithSession).toHaveBeenCalledWith('question.create', { text: 'a question', domainSlug: 'delivery', blocks: undefined });
50
+ });
51
+ it('create: resolves human-id --blocks targets via chain.getEntry before the write (R43)', async () => {
52
+ kernelCall.mockResolvedValueOnce({ _id: 'raw-doc-id-1' });
53
+ await runQuestionCreateVerb('a question', { blocks: ['TEN-42'] });
54
+ expect(kernelCall).toHaveBeenCalledWith('chain.getEntry', { entryId: 'TEN-42' });
55
+ expect(kernelCallWithSession).toHaveBeenCalledWith('question.create', { text: 'a question', domainSlug: undefined, blocks: ['raw-doc-id-1'] });
56
+ });
57
+ it('create: a raw (non human-id-shaped) --blocks target passes through unresolved, no lookup', async () => {
58
+ await runQuestionCreateVerb('a question', { blocks: ['j5712abcxyz'] });
59
+ expect(kernelCall).not.toHaveBeenCalled();
60
+ expect(kernelCallWithSession).toHaveBeenCalledWith('question.create', { text: 'a question', domainSlug: undefined, blocks: ['j5712abcxyz'] });
61
+ });
62
+ it('adopt: resolves the target entryId via chain.getEntry (R43) then calls question.adopt', async () => {
63
+ kernelCall.mockResolvedValueOnce({ _id: 'raw-doc-id-1' });
64
+ kernelCallWithSession.mockResolvedValueOnce({ entryId: 'TEN-42', facetId: 'facet-1', status: 'unrouted', routingReason: 'single_domain_owner', domainSlugs: [] });
65
+ await runQuestionAdopt('TEN-42', {});
66
+ expect(kernelCall).toHaveBeenCalledWith('chain.getEntry', { entryId: 'TEN-42' });
67
+ expect(kernelCallWithSession).toHaveBeenCalledWith('question.adopt', { entryId: 'raw-doc-id-1', domainSlug: undefined, blocks: undefined });
68
+ });
69
+ it('assign: bare form sends no toRoleId/toPersonId ("take it")', async () => {
70
+ kernelCallWithSession.mockResolvedValueOnce({ entryId: 'TEN-1', status: 'routed' });
71
+ await runQuestionAssign('TEN-1', {});
72
+ expect(kernelCallWithSession).toHaveBeenCalledWith('question.assign', { entryId: 'TEN-1', toRoleId: undefined, toPersonId: undefined });
73
+ });
74
+ it('assign: --to-role resolves via chain.getEntry, --to-person passes through raw', async () => {
75
+ kernelCallWithSession.mockResolvedValueOnce({ entryId: 'TEN-1', status: 'routed', ownerRoleId: 'ROL-2', ownerPersonId: 'person-1' });
76
+ await runQuestionAssign('TEN-1', { toRole: 'ROL-2', toPerson: 'person-1' });
77
+ expect(kernelCall).toHaveBeenCalledWith('chain.getEntry', { entryId: 'ROL-2' });
78
+ // Default mock echoes the human id back as _id (beforeEach) — the point under test is
79
+ // that toRole goes through resolveEntryRef (a chain.getEntry call happened) while
80
+ // toPerson does NOT (no matching chain.getEntry call for 'person-1').
81
+ expect(kernelCallWithSession).toHaveBeenCalledWith('question.assign', { entryId: 'TEN-1', toRoleId: 'ROL-2', toPersonId: 'person-1' });
82
+ expect(kernelCall).not.toHaveBeenCalledWith('chain.getEntry', { entryId: 'person-1' });
83
+ });
84
+ // PRRT_kwDORS4ils6YnqKM (round 2 review): people are a separate table from entries —
85
+ // chain.getEntry cannot resolve a personId. --to-person alone must render via the
86
+ // mutation's own server-resolved ownerPersonName, never attempt a chain.getEntry({id})
87
+ // lookup on a people-table id.
88
+ it('assign: --to-person alone renders via the mutation\'s server-resolved ownerPersonName, never chain.getEntry({id})', async () => {
89
+ kernelCallWithSession.mockResolvedValueOnce({ entryId: 'TEN-1', status: 'routed', ownerPersonId: 'person-1', ownerPersonName: 'Sylvie' });
90
+ await runQuestionAssign('TEN-1', { toPerson: 'person-1' });
91
+ expect(kernelCall).not.toHaveBeenCalledWith('chain.getEntry', { id: 'person-1' });
92
+ });
93
+ // PRRT_kwDORS4ils6YnqKe (round 2 review): fn() must return the FLAT mutation result — the
94
+ // exact value runCliCommand serializes to stdout in JSON/non-TTY mode. An earlier shape
95
+ // wrapped it as {result, roleOrLeadName}/{result, targetName}/etc., breaking scripted
96
+ // consumers expecting entryId/status at the top level.
97
+ it('create/adopt/assign/snooze/decline/answer: runCliCommand receives fn() returning the FLAT mutation result, never a {result, ...} wrapper', async () => {
98
+ const flatCreate = { entryId: 'TEN-1', facetId: 'facet-1', status: 'unrouted', routingReason: 'single_domain_owner', domainSlugs: [] };
99
+ kernelCallWithSession.mockResolvedValue(flatCreate);
100
+ await runQuestionCreateVerb('a question', {});
101
+ await expect(runCliCommand.mock.calls.at(-1)[0].fn()).resolves.toEqual(flatCreate);
102
+ const flatSnooze = { entryId: 'TEN-1', status: 'routed', snoozeCount: 1, snoozeUntil: Date.now() };
103
+ kernelCallWithSession.mockResolvedValue(flatSnooze);
104
+ await runQuestionSnooze('TEN-1');
105
+ await expect(runCliCommand.mock.calls.at(-1)[0].fn()).resolves.toEqual(flatSnooze);
106
+ const flatAnswer = { entryId: 'TEN-1', status: 'processed', askerName: 'Mathijs' };
107
+ kernelCallWithSession.mockResolvedValue(flatAnswer);
108
+ await runQuestionAnswer('TEN-1', 'the answer', {});
109
+ await expect(runCliCommand.mock.calls.at(-1)[0].fn()).resolves.toEqual(flatAnswer);
110
+ });
111
+ it('snooze: calls question.snooze with the resolved entryId', async () => {
112
+ kernelCallWithSession.mockResolvedValueOnce({ entryId: 'TEN-1', status: 'routed', snoozeCount: 1, snoozeUntil: Date.now() });
113
+ await runQuestionSnooze('TEN-1');
114
+ expect(kernelCallWithSession).toHaveBeenCalledWith('question.snooze', { entryId: 'TEN-1' });
115
+ });
116
+ it('decline: requires --reason client-side before calling the gateway', async () => {
117
+ await expect(runQuestionDecline('TEN-1', { reason: '' })).rejects.toMatchObject({ code: 'VALIDATION_FAILED' });
118
+ expect(kernelCallWithSession).not.toHaveBeenCalled();
119
+ });
120
+ it('decline: forwards the reason', async () => {
121
+ kernelCallWithSession.mockResolvedValueOnce({ entryId: 'TEN-1', status: 'processed' });
122
+ await runQuestionDecline('TEN-1', { reason: 'not mine' });
123
+ expect(kernelCallWithSession).toHaveBeenCalledWith('question.decline', { entryId: 'TEN-1', reason: 'not mine' });
124
+ });
125
+ it('answer: neither text nor --entry is a client-side refusal, §7.7\'s exact string', async () => {
126
+ await expect(runQuestionAnswer('TEN-1', undefined, {})).rejects.toMatchObject({
127
+ code: 'VALIDATION_FAILED',
128
+ message: 'Give the answer as text, or point at an entry with --entry <id>.',
129
+ });
130
+ expect(kernelCallWithSession).not.toHaveBeenCalled();
131
+ });
132
+ it('answer: both text and --entry is a client-side refusal, §7.7\'s exact string', async () => {
133
+ await expect(runQuestionAnswer('TEN-1', 'the answer', { entry: 'DEC-5' })).rejects.toMatchObject({
134
+ code: 'VALIDATION_FAILED',
135
+ message: 'Use either answer text or --entry, not both.',
136
+ });
137
+ expect(kernelCallWithSession).not.toHaveBeenCalled();
138
+ });
139
+ it('answer: text form forwards text, no answerEntryId', async () => {
140
+ kernelCallWithSession.mockResolvedValueOnce({ entryId: 'TEN-1', status: 'processed', answerEntryId: 'DEC-9', answerPending: true });
141
+ await runQuestionAnswer('TEN-1', 'the answer', {});
142
+ expect(kernelCallWithSession).toHaveBeenCalledWith('question.answer', { entryId: 'TEN-1', text: 'the answer', answerEntryId: undefined });
143
+ });
144
+ it('answer: --entry form resolves the target via chain.getEntry, forwards no text', async () => {
145
+ kernelCallWithSession.mockResolvedValueOnce({ entryId: 'TEN-1', status: 'processed', answerEntryId: 'DEC-9' });
146
+ await runQuestionAnswer('TEN-1', undefined, { entry: 'DEC-9' });
147
+ expect(kernelCall).toHaveBeenCalledWith('chain.getEntry', { entryId: 'DEC-9' });
148
+ // Default mock echoes the human id back as _id (beforeEach).
149
+ expect(kernelCallWithSession).toHaveBeenCalledWith('question.answer', { entryId: 'TEN-1', text: undefined, answerEntryId: 'DEC-9' });
150
+ });
151
+ it('force-close: requires --reason client-side before calling the gateway', async () => {
152
+ await expect(runQuestionForceClose('TEN-1', { reason: '' })).rejects.toMatchObject({ code: 'VALIDATION_FAILED' });
153
+ expect(kernelCallWithSession).not.toHaveBeenCalled();
154
+ });
155
+ it('force-close: forwards the reason', async () => {
156
+ kernelCallWithSession.mockResolvedValueOnce({ entryId: 'TEN-1', status: 'processed' });
157
+ await runQuestionForceClose('TEN-1', { reason: 'stuck forever' });
158
+ expect(kernelCallWithSession).toHaveBeenCalledWith('question.forceClose', { entryId: 'TEN-1', reason: 'stuck forever' });
159
+ });
160
+ it('parses the Commander create surface end to end', async () => {
161
+ const command = createQuestionCommand();
162
+ command.exitOverride();
163
+ await command.parseAsync(['create', 'a real question', '--domain', 'delivery'], { from: 'user' });
164
+ expect(kernelCallWithSession).toHaveBeenCalledWith('question.create', { text: 'a real question', domainSlug: 'delivery', blocks: undefined });
165
+ });
166
+ it('parses the Commander force-close surface end to end', async () => {
167
+ kernelCallWithSession.mockResolvedValueOnce({ entryId: 'TEN-1', status: 'processed' });
168
+ const command = createQuestionCommand();
169
+ command.exitOverride();
170
+ await command.parseAsync(['force-close', 'TEN-1', '--reason', 'stuck'], { from: 'user' });
171
+ expect(kernelCallWithSession).toHaveBeenCalledWith('question.forceClose', { entryId: 'TEN-1', reason: 'stuck' });
172
+ });
173
+ });
174
+ //# sourceMappingURL=questions.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"questions.test.js","sourceRoot":"","sources":["../../src/commands/questions.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ export interface CreateAdoptResult {
2
+ entryId: string;
3
+ facetId: string;
4
+ status: 'unrouted' | 'escalated';
5
+ suggestedRoleId?: string;
6
+ ownerRoleId?: string;
7
+ routingReason: string;
8
+ domainSlugs: string[];
9
+ nudge?: string;
10
+ }
11
+ /** `roleName`/`leadName` are resolved by the caller (one `chain.getEntry` lookup) — this
12
+ * formatter never resolves ids itself, matching formatters/shape.ts's "no math here" rule.
13
+ * The name is workspace-controlled entry/person data (a role name, a person's displayName),
14
+ * never sanitized upstream — every interpolation here goes through singleLine() first
15
+ * (BET-269 C3: terminal escaping) so a name containing control/ANSI sequences can't inject
16
+ * extra terminal output when this pretty-mode confirmation prints (round 2 review,
17
+ * PRRT_kwDORS4ils6YnqK-). */
18
+ export declare function formatQuestionCreate(result: CreateAdoptResult, roleOrLeadName: string): string;
19
+ export declare function formatQuestionAdopt(result: CreateAdoptResult, roleOrLeadName: string): string;
20
+ export interface AssignResult {
21
+ entryId: string;
22
+ status: 'routed' | 'unrouted' | 'escalated' | 'processed';
23
+ ownerRoleId?: string;
24
+ ownerPersonId?: string;
25
+ ownerPersonName?: string;
26
+ }
27
+ export declare function formatQuestionAssignConfirm(entryId: string): string;
28
+ export declare function formatQuestionAssignReassign(entryId: string, targetName: string): string;
29
+ export interface SnoozeResult {
30
+ entryId: string;
31
+ status: 'routed' | 'escalated';
32
+ snoozeCount?: number;
33
+ snoozeUntil?: number;
34
+ ownerRoleId?: string;
35
+ }
36
+ export declare function formatQuestionSnooze(result: SnoozeResult, leadName?: string): string;
37
+ export interface DeclineResult {
38
+ entryId: string;
39
+ status: 'unrouted' | 'escalated' | 'processed';
40
+ suggestedRoleId?: string;
41
+ ownerRoleId?: string;
42
+ dependentsCount?: number;
43
+ isRoot?: boolean;
44
+ }
45
+ /**
46
+ * `isRoot` disambiguates two branches that otherwise share the identical
47
+ * `{status:'escalated', ownerRoleId}` shape: a suggestee's PASS (§3.7a, never sets `isRoot` —
48
+ * it is never the owner/lead) vs an owner/lead's decline (§3.7b, always sets `isRoot`
49
+ * explicitly — `true` at the root, `false` below it). Checking `isRoot !== undefined` is what
50
+ * tells the two apart; `ownerRoleId` alone cannot.
51
+ */
52
+ export declare function formatQuestionDecline(entryId: string, result: DeclineResult, roleOrLeadName?: string): string;
53
+ export interface AnswerResult {
54
+ entryId: string;
55
+ status: 'processed';
56
+ answerEntryId?: string;
57
+ answerPending?: boolean;
58
+ askerPersonId?: string;
59
+ askerName?: string;
60
+ idempotent?: boolean;
61
+ }
62
+ /**
63
+ * PRRT_kwDORS4ils6YnqK0 (round 2): does NOT mention `pb question list --mine` — S3 ships no
64
+ * `list` subcommand (createQuestionCommand() exposes only the seven write verbs; `list` is
65
+ * S4's own vertical, §4.1's slice-boundary fix). Pointing users at an unshipped command here
66
+ * would be a broken-command dead end; S4 restores the full §7.7 wording when `list` lands.
67
+ */
68
+ export declare function formatQuestionAnswer(entryId: string, result: AnswerResult, askerName?: string): string;
69
+ export interface ForceCloseResult {
70
+ entryId: string;
71
+ status: 'processed';
72
+ idempotent?: boolean;
73
+ }
74
+ export declare function formatQuestionForceClose(entryId: string, result: ForceCloseResult): string;
75
+ //# sourceMappingURL=questions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"questions.d.ts","sourceRoot":"","sources":["../../src/formatters/questions.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,UAAU,GAAG,WAAW,CAAC;IACjC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;8BAM8B;AAC9B,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAQ9F;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAO7F;AAED,MAAM,WAAW,YAAY;IAAG,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,QAAQ,GAAG,UAAU,GAAG,WAAW,GAAG,WAAW,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE;AAEpL,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAExF;AAED,MAAM,WAAW,YAAY;IAAG,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,QAAQ,GAAG,WAAW,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE;AAEnJ,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAQpF;AAED,MAAM,WAAW,aAAa;IAAG,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,UAAU,GAAG,WAAW,GAAG,WAAW,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE;AAE9L;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,CAQ7G;AAED,MAAM,WAAW,YAAY;IAAG,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE;AAEzL;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAKtG;AAED,MAAM,WAAW,gBAAgB;IAAG,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE;AAEhG,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAE1F"}