diff --git a/test/ceo-workflow-clarity.test.ts b/test/ceo-workflow-clarity.test.ts index a8331f03a..670ada8b4 100644 --- a/test/ceo-workflow-clarity.test.ts +++ b/test/ceo-workflow-clarity.test.ts @@ -28,88 +28,70 @@ const sources = [ }, ]; +const ordered = (text: string, anchors: string[]) => { + const positions = anchors.map(anchor => text.indexOf(anchor)); + expect(positions.every(position => position >= 0), `missing one of ${JSON.stringify(anchors)}`).toBe(true); + expect(positions).toEqual([...positions].sort((a, b) => a - b)); +}; + for (const source of sources) describe(`CEO clarity routing — ${source.label}`, () => { const main = compact(source.main); const section = compact(source.section); const decisions = main.split('### 0D.')[1]!.split('### 0E.')[0]!; const mode = main.split('### 0E.')[1]!.split('### 0F.')[0]!; - test('pending proposals stay out of approved work and settled means exact authority', () => { - expect(main).toContain('**Required choice:** unanswered. Resolve a choice only when continuing would change scope, hide a blocker or produce the wrong output'); - expect(main).toContain('**Pending:** unapproved; keep in Proposed, not tasks or accepted work'); - expect(main).toContain('Status is `unresolved` or `reopened`'); - expect(main).toContain('**Settled:** an answer, direct instruction or authorized auto-decision resolves this exact choice and scope; a recommendation does not'); + test('decision statuses keep pending work unapproved and settle only on exact authority', () => { + ordered(main, ['**Required choice:**', '**Pending:**', '**Settled:**']); + expect(main).toMatch(/\*\*Pending:\*\* unapproved/i); + expect(main).toMatch(/\*\*Settled:\*\*[^*]*a recommendation does not/i); }); - test('admin menus return locally while prescribed scope menus still require the full approval cycle', () => { - expect(decisions).toContain('Skip steps 1–4; this approves no plan changes. Resume that menu\'s next step'); - expect(decisions).toContain('0E owns mode selection; 0H owns document approval'); - expect(decisions).toContain('If an admin answer requests a plan change, use the Plan decision route for that change before resuming'); - expect(decisions).toContain('0G proposals and section findings use this route even with prescribed menus'); - expect(decisions).toContain('0D returns to its caller, not to mode selection'); - expect(decisions).toContain('For mode changes, follow 0E\'s **Mode change** instruction'); - expect(decisions).not.toContain('0D never restarts mode selection'); - expect(decisions).toContain('**Pre-question checkpoint:**'); - expect(decisions).toContain('**STOP for the actual answer, even for a lone option.**'); - expect(decisions).toContain('**Post-answer checkpoint:**'); + test('admin menus return locally while plan decisions run the full approval cycle', () => { + ordered(decisions, ['**Admin question:**', '**Plan decision:**', '**1. Check sources', '**2. Record the pending choice.**', '**3. Compare and save', '**Pre-question checkpoint:**', '**4. Ask, record the answer', '**Post-answer checkpoint:**']); + expect(decisions).toMatch(/this approves no plan changes/i); + expect(decisions).toMatch(/0D returns to its caller, not to mode selection/i); + expect(decisions.split('**4. Ask')[1]!.split('**Post-answer checkpoint:**')[0]).toMatch(/stop for the actual answer/i); }); - test('a mode change waits for authority and resumes without discarding earlier answers', () => { + test('a mode change waits for an answer and selecting a mode approves nothing', () => { const change = mode.split('**Mode change:**')[1]!.split('Selecting a mode')[0]!; - expect(change).toContain('Pause and ask with the four-mode menu; keep the mode until answered'); - expect(change).toContain('repeat the handoff/provenance record'); - expect(change).toContain('complete newly applicable Step 0 work in route order, reusing completed work and scope answers'); - expect(change).toContain('Then resume the paused step. If unchanged, resume directly'); - expect(mode).toContain('Selecting a mode does not approve changes'); + expect(change).toMatch(/pause and ask[^.]*keep the mode until answered/i); + expect(mode).toMatch(/selecting a mode does not approve changes/i); expect(mode).toContain('| SCOPE EXPANSION / SELECTIVE EXPANSION | 0F → 0G → 0H (including its spec review loop) → 0I |'); expect(mode).toContain('| HOLD SCOPE | 0G → 0I |'); expect(mode).toContain('| SCOPE REDUCTION | 0G |'); }); - test('scope limits count reused deliverables but mode recommendations count only changed files', () => { - expect(main).toContain('Count all deliverables, including reused code, against scope limits'); - expect(main).toContain('0E counts changed files, excluding unchanged reuse, to recommend a mode'); - expect(main).toContain('Neither count approves changes'); - expect(mode).toContain('For >15 planned changed files, recommend SCOPE REDUCTION'); - const routes = ['For >15 planned changed files', 'If categories overlap or are unclear', 'Otherwise: a new product/system']; - const positions = routes.map(route => mode.indexOf(route)); - expect(positions.every(position => position >= 0)).toBe(true); - expect(positions).toEqual([...positions].sort((a, b) => a - b)); + test('scope limits and mode recommendations keep their numeric thresholds and route order', () => { + expect(main).toMatch(/including reused code, against scope limits/i); + expect(main).toMatch(/excluding unchanged reuse/i); + expect(main).toMatch(/neither count approves changes/i); + ordered(mode, ['For >15 planned changed files', 'If categories overlap or are unclear', 'Otherwise: a new product/system']); + expect(mode).toMatch(/>15 planned changed files, recommend SCOPE REDUCTION/); expect(main).toContain('more than 8 files or more than 2 new classes/services'); }); - test('coverage and kind-only scoring retain approvals and define the completion fraction', () => { + test('coverage scoring keeps its scale and the completion fraction keeps its format', () => { expect(decisions).toContain('**Same work, different coverage:**'); expect(decisions).toContain('10 = all edge cases, 7 = happy path, 3 = shortcut'); - expect(decisions).toContain('mode selection and Add/Defer/Skip or Defer/Keep'); - expect(decisions).toContain('No score does not waive approval checkpoints'); - expect(section).toContain('Select answered questions scored for coverage under 0D that offered a 10/10 option'); - expect(section).toContain('Exclude unscored mode/scope choices and unanswered questions'); - expect(section).toContain('Count a reopened choice only once, using its latest answered option'); - expect(section).toContain('Y is the number of eligible questions; X is how many selected the 10/10 option'); + expect(decisions).toMatch(/no score does not waive approval checkpoints/i); + expect(section).toMatch(/count a reopened choice only once/i); expect(section).toContain('Report X/Y, or `N/A` when Y is zero'); }); test('section findings check reopening evidence before reusing prior answers', () => { const gate = section.split('**Resolve.**')[1]!.split('**Apply.**')[0]!; - const paths = ['1. This section needs a new choice', '2. An exact prior answer covers it', '3. A non-blocking choice belongs to a later section']; - const positions = paths.map(path => gate.indexOf(path)); - expect(positions.every(position => position >= 0)).toBe(true); - expect(positions).toEqual([...positions].sort((a, b) => a - b)); - expect(gate).toContain('0D\'s Plan decision route through its post-answer save'); - expect(gate).toContain('Resolve critical risks now'); - expect(gate).toContain('No path selects the mode again'); - expect(source.section.match(/\*\*Decision gate\.\*\*/g)).toHaveLength(11); + ordered(gate, ['1. This section needs a new choice', '2. An exact prior answer covers it', '3. A non-blocking choice belongs to a later section']); + expect(gate).toMatch(/no path selects the mode again/i); + const sections = source.section.match(/^### Section \d+:/gm)!.length; + expect(sections).toBeGreaterThan(0); + expect(source.section.match(/\*\*Decision gate\.\*\*/g)).toHaveLength(sections); }); test('Section 1 publishes current dispositions, not a second initial mode handoff', () => { const opening = section.split('### Section 1: Architecture Review')[1]!.split('Evaluate and diagram:')[0]!; - expect(opening).toContain('Publish **Current scope** in chat before the architecture analysis'); - expect(opening).toContain('Retain 0E\'s selected mode, rationale and preference attribution'); - expect(opening).toContain('each governing row\'s ID, disposition and answer reference'); - expect(opening).toContain('including scope decisions after 0E'); - expect(opening).toContain('Distinguish accepted, deferred, rejected and pending work'); - expect(opening).toContain('This is a scope update, not another mode handoff; do not ask or log the mode again'); - expect(opening).not.toContain('using the Step 0E mode-handoff format'); + expect(opening).toContain('**Current scope**'); + expect(opening).toMatch(/accepted, deferred, rejected and pending/i); + expect(opening).toMatch(/not another mode handoff; do not ask or log the mode again/i); }); });