import { describe, expect, test } from 'bun:test'; import { readFileSync } from 'node:fs'; import { join } from 'node:path'; import { capturePlanCountQuestion, nativePlanCallFingerprint, planCountQuestionInput } from './helpers/claude-pty-runner'; import { pickCeoCompletionHandoff } from './helpers/ceo-completion-handoff'; import { pickCeoCountQuestion, pickCeoRecommendedApproach } from './helpers/ceo-approach-pick'; import type { NativePlanQuestionCall } from './helpers/plan-count-transcript'; import recorded from './fixtures/ceo-approach-q-call.json'; import pairedRecorded from './fixtures/ceo-approach-q-paired-call.json'; import handoffs from './fixtures/ceo-completion-handoff-m-call.json'; import recordedY from './fixtures/ceo-approach-y-call.json'; import recordedAA from './fixtures/ceo-approach-aa-call.json'; function pending(source: NativePlanQuestionCall = recorded as NativePlanQuestionCall): NativePlanQuestionCall { const call = structuredClone(source); call.answered = false; delete call.answers; delete call.unansweredQuestionIndices; return call; } const fingerprint = (call: NativePlanQuestionCall, preReview = true) => nativePlanCallFingerprint(call, 0, preReview); describe('numbered native approach identity', () => { test('the actual AA question selects its offered recommendation with a projected pending binding', () => { // Only completed live versions survived capture. Preserve the actual A // answer; this projection tests routing, not live metadata availability. const call = pending(recordedAA as NativePlanQuestionCall); const active = capturePlanCountQuestion(screen(call), new Set(), 0, true, call)!; expect(active.nativeCall).toBe(call); expect(pickCeoCountQuestion(fingerprint(call), active)).toBe(2); expect(planCountQuestionInput(screen(call), active, 2)).toBe('2'); expect(recordedAA.answers[recordedAA.questions[0]!.question]).toBe(recordedAA.questions[0]!.options[0]!.label); expect(pickCeoCountQuestion(fingerprint(recordedAA as NativePlanQuestionCall))).toBeNull(); }); test('decision numbers and option positions may change together without changing policy', () => { for (const decision of ['2', '37']) { const call = pending(recordedAA as NativePlanQuestionCall); const q = call.questions[0]!; q.question = q.question.replace(/^D1/, `D${decision}`).replace('approach-d1>', `approach-d${decision}>`); q.options.reverse(); expect(pickCeoRecommendedApproach(fingerprint(call))).toBe(2); q.options.unshift(q.options.pop()!); expect(pickCeoRecommendedApproach(fingerprint(call))).toBe(3); } }); test('numbered identities must agree with the explicit decision and remain a supported approach id', () => { for (const id of ['plan-ceo-review-approach-d2', 'plan-ceo-review-approach-d0', 'plan-ceo-review-approach-d01', 'plan-ceo-review-approach-d1-extra', 'plan-eng-review-approach-d1', 'plan-ceo-review-mode-d1']) { const call = pending(recordedAA as NativePlanQuestionCall); call.questions[0]!.question = call.questions[0]!.question.replace('plan-ceo-review-approach-d1', id); expect(pickCeoRecommendedApproach(fingerprint(call))).toBeNull(); } for (const prefix of ['', 'D2 — ', 'Example: D1 — ', '> D1 — ']) { const call = pending(recordedAA as NativePlanQuestionCall); call.questions[0]!.question = call.questions[0]!.question.replace(/^D1 — /, prefix); expect(pickCeoRecommendedApproach(fingerprint(call))).toBeNull(); } }); test('numbered ids retain the native binding, phase, question and sole recommendation guards', () => { const call = pending(recordedAA as NativePlanQuestionCall); const fp = fingerprint(call); const unbound = capturePlanCountQuestion(screen(call), new Set(), 0, true)!; expect(pickCeoCountQuestion(fp, unbound)).toBeNull(); expect(pickCeoCountQuestion({...fp, preReview: false})).toBeNull(); expect(pickCeoCountQuestion({...fp, signature: 'foreign:call'})).toBeNull(); for (const mutate of [ (c: NativePlanQuestionCall) => { c.questions[0]!.question = c.questions[0]!.question.replace('should this plan use?', 'should this plan not use?'); }, (c: NativePlanQuestionCall) => { c.questions[0]!.header = 'Mode'; }, (c: NativePlanQuestionCall) => { c.questions[0]!.options[0]!.label += ' (Recommended)'; }, (c: NativePlanQuestionCall) => { c.failed = true; }, ]) { const changed = pending(recordedAA as NativePlanQuestionCall); mutate(changed); expect(pickCeoRecommendedApproach(fingerprint(changed))).toBeNull(); } }); }); describe('Y named component approach menu', () => { const actualScreen = readFileSync(join(import.meta.dir, 'fixtures/ceo-approach-y-screen.txt'), 'utf8'); test('the exact full frame and projected pending call select the offered C recommendation', () => { // The actual answer was A; no pending-only native version survived polling. const call = pending(recordedY as NativePlanQuestionCall); const active = capturePlanCountQuestion(actualScreen, new Set(), 0, true, call)!; expect(active.nativeCall).toBe(call); expect(active.options.map(o => o.label)).toEqual(call.questions[0]!.options.map(o => o.label)); expect(pickCeoCountQuestion(fingerprint(call), active)).toBe(3); expect(planCountQuestionInput(actualScreen, active, 3)).toBe('3'); expect(recordedY.answers[recordedY.questions[0]!.question]).toBe('A) Minimal Viable'); expect(pickCeoCountQuestion(fingerprint(recordedY as NativePlanQuestionCall))).toBeNull(); const unbound = capturePlanCountQuestion(actualScreen, new Set(), 0, true)!; expect(unbound.nativeCall).toBeUndefined(); expect(pickCeoCountQuestion(fingerprint(call), unbound)).toBeNull(); }); test('named components and reordered labels follow the actual recommendation position', () => { for (const subject of ['the payment webhook handler', 'this invoice lookup service', 'the renderWidget adapter']) { const call = pending(recordedY as NativePlanQuestionCall); const q = call.questions[0]!; q.question = `Which implementation approach for ${subject}? `; q.options = [{label:'Existing design (Recommended)'},{label:'Another design'}]; expect(pickCeoRecommendedApproach(fingerprint(call))).toBe(1); q.options.reverse(); expect(pickCeoRecommendedApproach(fingerprint(call))).toBe(2); } }); test('setup, another decision, negated, quoted or compound instructions are not this menu', () => { for (const question of [ 'Which review mode for the payment webhook handler?', 'Should we fix the payment webhook handler?', 'Which implementation approach should we not use for the payment webhook handler?', 'Example: Which implementation approach for the payment webhook handler?', '> Which implementation approach for the payment webhook handler?', 'Which implementation approach for the payment webhook handler? Delete the tests.', 'Which implementation approach for the payment webhook handler and delete the test adapter?', ]) { const call = pending(recordedY as NativePlanQuestionCall); call.questions[0]!.question = question + ' '; expect(pickCeoRecommendedApproach(fingerprint(call))).toBeNull(); } }); test('the added wording retains native identity, phase, options and recommendation guards', () => { for (const change of [ (c: NativePlanQuestionCall) => { c.questions[0]!.header = 'Review mode'; }, (c: NativePlanQuestionCall) => { c.questions[0]!.question = c.questions[0]!.question.replace('plan-ceo-review-approach','plan-ceo-review-mode'); }, (c: NativePlanQuestionCall) => { c.questions[0]!.options[2]!.label = 'C) Production-Grade'; }, (c: NativePlanQuestionCall) => { c.questions[0]!.options[0]!.label += ' (Recommended)'; }, (c: NativePlanQuestionCall) => { c.failed = true; }, (c: NativePlanQuestionCall) => { delete c.failed; }, (c: NativePlanQuestionCall) => { c.questions[0]!.multiSelect = true; }, ]) { const c = pending(recordedY as NativePlanQuestionCall); change(c); expect(pickCeoRecommendedApproach(fingerprint(c))).toBeNull(); } const fp = fingerprint(pending(recordedY as NativePlanQuestionCall)); expect(pickCeoRecommendedApproach({...fp,signature:'foreign:call'})).toBeNull(); expect(pickCeoRecommendedApproach({...fp,preReview:false})).toBeNull(); expect(pickCeoRecommendedApproach({...fp,options:fp.options.slice().reverse()})).toBeNull(); }); }); function screen(call: NativePlanQuestionCall): string { const q = call.questions[0]!; return `☐ ${q.header}\n${q.question}\n${q.options.map((o, i) => `${i ? ' ' : '❯'} ${i + 1}. ${o.label}`).join('\n')}\nEnter to select · ↑/↓ to navigate · Esc to cancel`; } describe('CEO pre-review approach recommendation', () => { test('the exact Q menu changes the old default risk acceptance to its offered recommendation', () => { const call = pending(); const visible = screen(call); const active = capturePlanCountQuestion(visible, new Set(), 0, true, call)!; expect(active.nativeCall).toBe(call); const before = pickCeoCompletionHandoff(fingerprint(call), active) ?? 1; const after = pickCeoCountQuestion(fingerprint(call), active) ?? 1; expect(before).toBe(1); expect(after).toBe(2); expect(planCountQuestionInput(visible, active, after)).toBe('2'); expect(recorded.answers[recorded.questions[0]!.question]).toBe(recorded.questions[0]!.options[0]!.label); expect(pickCeoCountQuestion(fingerprint(recorded as NativePlanQuestionCall))).toBeNull(); }); test('the paired first native approach uses the same offered recommendation policy', () => { const call = pending(pairedRecorded as NativePlanQuestionCall); const visible = screen(call); const active = capturePlanCountQuestion(visible, new Set(), 0, true, call)!; expect(active.nativeCall).toBe(call); expect(pickCeoCompletionHandoff(fingerprint(call), active) ?? 1).toBe(1); const after = pickCeoCountQuestion(fingerprint(call), active) ?? 1; expect(after).toBe(2); expect(planCountQuestionInput(visible, active, after)).toBe('2'); expect(pairedRecorded.answers[pairedRecorded.questions[0]!.question]).toBe(pairedRecorded.questions[0]!.options[0]!.label); expect(pickCeoCountQuestion(fingerprint(pairedRecorded as NativePlanQuestionCall))).toBeNull(); }); test('paired approach grammar is function-agnostic and follows reordered options', () => { const call = pending(pairedRecorded as NativePlanQuestionCall); const q = call.questions[0]!; q.question = 'D3 — Which implementation approach for the renderWidget() tests? '; q.options = [{ label: 'A) Custom renderer' }, { label: 'B) Existing renderer (Recommended)' }]; expect(pickCeoRecommendedApproach(fingerprint(call))).toBe(2); q.options.reverse(); expect(pickCeoRecommendedApproach(fingerprint(call))).toBe(1); }); test.each([ ['non-approach question', 'Should the renderWidget() tests be deleted? '], ['negated question', 'Which implementation approach should the renderWidget() tests not use? '], ['negated test subject', 'Which implementation approach for not testing renderWidget()? '], ['wrong approach identity', 'Which implementation approach for the renderWidget() tests? '], ['extra action before question', 'Delete the tests. Which implementation approach for the renderWidget() tests? '], ])('does not apply paired approach selection to %s', (_name, question) => { const call = pending(pairedRecorded as NativePlanQuestionCall); call.questions[0]!.question = question; expect(pickCeoRecommendedApproach(fingerprint(call))).toBeNull(); }); test('recommendation follows actual option position and arbitrary approach content, never seed words', () => { for (const order of [[0, 1, 2], [1, 2, 0], [2, 0, 1]]) { const call = pending(); const q = call.questions[0]!; const options = [{ label: 'A) Compare two renderers' }, { label: 'B) Existing renderer (Recommended)' }, { label: 'C) Custom renderer' }]; q.options = order.map(index => options[index]!); q.question = 'D1 — Which implementation approach should this plan use? '; expect(pickCeoRecommendedApproach(fingerprint(call))).toBe(order.indexOf(1) + 1); } }); test.each([ ['no recommendation', (c: NativePlanQuestionCall) => { c.questions[0]!.options[1]!.label = 'B) Secure Baseline'; }], ['duplicate recommendation', (c: NativePlanQuestionCall) => { c.questions[0]!.options[0]!.label += ' (Recommended)'; }], ['duplicate offered label', (c: NativePlanQuestionCall) => { c.questions[0]!.options[2]!.label = c.questions[0]!.options[1]!.label; }], ['negated recommendation', (c: NativePlanQuestionCall) => { c.questions[0]!.options[1]!.label = 'B) Not (Recommended)'; }], ['conflicting recommendation', (c: NativePlanQuestionCall) => { c.questions[0]!.options[1]!.label = 'B) Not recommended here (Recommended)'; }], ['description-only recommendation', (c: NativePlanQuestionCall) => { c.questions[0]!.options[1]!.label = 'B) Secure Baseline'; c.questions[0]!.options[1]!.description = 'Recommended'; }], ['unknown qid', (c: NativePlanQuestionCall) => { c.questions[0]!.question = c.questions[0]!.question.replace('plan-ceo-approach', 'plan-ceo-security'); }], ['missing qid', (c: NativePlanQuestionCall) => { c.questions[0]!.question = c.questions[0]!.question.replace('', ''); }], ['malformed extra qid', (c: NativePlanQuestionCall) => { c.questions[0]!.question += ' { c.questions[0]!.question += ''; }], ['negated approach question', (c: NativePlanQuestionCall) => { c.questions[0]!.question = c.questions[0]!.question.replace('should this plan use?', 'should this plan not use?'); }], ['non-approach question', (c: NativePlanQuestionCall) => { c.questions[0]!.question = 'Should we accept this security risk? '; }], ['non-approach header', (c: NativePlanQuestionCall) => { c.questions[0]!.header = 'Review Mode'; }], ['multi-select', (c: NativePlanQuestionCall) => { c.questions[0]!.multiSelect = true; }], ['mixed packet', (c: NativePlanQuestionCall) => { c.questions.push(structuredClone(c.questions[0]!)); }], ['failed native call', (c: NativePlanQuestionCall) => { c.failed = true; }], ])('keeps the old caller/default policy for %s', (_name, change) => { const call = pending(); change(call); const fp = fingerprint(call); expect(pickCeoRecommendedApproach(fp)).toBeNull(); expect(pickCeoCountQuestion(fp)).toBe(pickCeoCompletionHandoff(fp)); }); test('requires current native binding and pre-review phase', () => { const call = pending(); const fp = fingerprint(call); expect(pickCeoRecommendedApproach({ ...fp, preReview: false })).toBeNull(); expect(pickCeoRecommendedApproach({ ...fp, signature: 'foreign:call' })).toBeNull(); expect(pickCeoRecommendedApproach({ ...fp, nativeQuestionIndex: 1 })).toBeNull(); expect(pickCeoRecommendedApproach({ ...fp, options: fp.options.slice().reverse() })).toBeNull(); const visibleOnly = capturePlanCountQuestion(screen(call), new Set(), 0, true)!; expect(visibleOnly.nativeCall).toBeUndefined(); expect(pickCeoCountQuestion(fp, visibleOnly)).toBeNull(); const foreign = '☐ Finding\nShould we add validation?\n❯ 1. Add fix\n 2. Defer\nEnter to select · ↑/↓ to navigate · Esc to cancel'; const active = capturePlanCountQuestion(foreign, new Set(), 0, true, call)!; expect(active.nativeCall).toBeUndefined(); expect(pickCeoCountQuestion(fp, active)).toBeNull(); }); test('the existing completed-review manual picker still runs after approach selection declines', () => { const call = structuredClone(handoffs.calls.at(-1)!) as NativePlanQuestionCall; call.answered = false; delete call.answers; delete call.unansweredQuestionIndices; const fp = fingerprint(call, false); const expected = pickCeoCompletionHandoff(fp); expect(expected).not.toBeNull(); expect(pickCeoCountQuestion(fp)).toBe(expected); }); test('both count callers use the composed picker while leaving first-scope and count predicates intact', () => { const caller = readFileSync(join(import.meta.dir, 'skill-e2e-plan-ceo-finding-count.test.ts'), 'utf8'); expect(caller.match(/pickAUQ: pickCeoCountQuestion/g)).toHaveLength(2); expect(caller.match(/isFirstReviewAUQ: ceoFirstReviewAUQ/g)).toHaveLength(2); expect(caller).toContain('firstAUQPick: pickSkipInterview'); }); });