import {expect, test} from 'bun:test'; import fs from 'node:fs'; import path from 'node:path'; import {ALL_HOST_CONFIGS} from '../hosts'; import {HOST_PATHS, type TemplateContext} from '../scripts/resolvers/types'; import {generatePreamble} from '../scripts/resolvers/preamble'; import {generateAskUserFormat} from '../scripts/resolvers/preamble/generate-ask-user-format'; import {generateGBrainContextLoad} from '../scripts/resolvers/gbrain'; import {E2E_TOUCHFILES, LLM_JUDGE_TOUCHFILES, selectTests} from './helpers/touchfiles'; const template = fs.readFileSync(path.join(import.meta.dir, '../plan-eng-review/SKILL.md.tmpl'), 'utf8'); const scope = template.slice(template.indexOf('## Scope gate'), template.indexOf('## Priority hierarchy')); const announcement = 'Scope gate: plan mode — auto-selected B (reviewing ).'; test('Eng resolves scope before either executable bootstrap placeholder', () => { const gate = template.indexOf('## Scope gate'); expect(gate).toBeGreaterThan(0); for (const token of ['{{PREAMBLE}}', '{{GBRAIN_CONTEXT_LOAD}}']) { expect(template.split(token)).toHaveLength(2); expect(template.indexOf(announcement)).toBeLessThan(template.indexOf(token)); expect(template.indexOf('Reply with A, B, or C. STOP and wait')).toBeLessThan(template.indexOf(token)); } expect(template.indexOf('{{PREAMBLE}}')).toBeLessThan(template.indexOf('{{GBRAIN_CONTEXT_LOAD}}')); expect(template.indexOf('{{GBRAIN_CONTEXT_LOAD}}')).toBeLessThan(template.indexOf('### Design Doc Check')); }); test('every host expands its real bootstrap after the mandatory entry gate', () => { for (const host of ALL_HOST_CONFIGS) { const ctx: TemplateContext = {skillName: 'plan-eng-review', tmplPath: 'plan-eng-review/SKILL.md.tmpl', host: host.name, paths: HOST_PATHS[host.name]!, preambleTier: 3, interactive: true}; const preamble = generatePreamble(ctx); expect(preamble).toContain('starting from the Scope gate, then follow its Startup sequence'); const brain = host.suppressedResolvers?.includes('GBRAIN_CONTEXT_LOAD') ? '' : generateGBrainContextLoad(ctx); const expanded = template.replace('{{PREAMBLE}}', preamble).replace('{{GBRAIN_CONTEXT_LOAD}}', brain); expect(expanded.indexOf(announcement)).toBeLessThan(expanded.indexOf('## Preamble (after scope gate)')); expect(expanded.indexOf('Reply with A, B, or C. STOP and wait')).toBeLessThan(expanded.indexOf('```bash')); expect(expanded.indexOf('```bash')).toBeLessThan(expanded.indexOf('gstack-skill-start', expanded.indexOf('```bash'))); if (brain) expect(expanded.indexOf(announcement)).toBeLessThan(expanded.indexOf('## Brain Context Load')); } }); test('entry binds a current target and delays bootstrap until scope resolves', () => { expect(scope).toContain('Before tools or preamble, resolve from provided messages, listed tools and explicit host metadata only'); expect(scope).toContain('Do not probe for session state'); expect(scope).toContain('When no exception above applied:'); expect(scope).toContain('First tool call = AskUserQuestion (tool_use). Send this exact menu and wait'); expect(scope).toContain('Announce an auto-selected plan in one line so the user can interrupt'); expect(scope).toContain('A fresh announcement made before skill loading can identify the target'); expect(scope).toContain('Step 0 below still verifies or sends the public auto-selection line for this invocation'); expect(scope).toContain('Clarify ambiguous, conflicting, quoted or stale targets; reuse a still-valid authorized target'); expect(scope.match(/\*\*Startup sequence\*\*/g)).toHaveLength(1); const startup = scope.slice(scope.indexOf('**Startup sequence**'), scope.indexOf('{{PREAMBLE}}')); const order = ['after target selection', 'Preamble', 'Context Recovery', 'Brain Context', 'web-research readiness', 'Design Doc Check', 'Review preparation'].map(step => startup.indexOf(step)); expect(order.every(position => position >= 0)).toBe(true); expect(order).toEqual([...order].sort((a, b) => a - b)); expect(startup).toContain('Keep the reviewed target fixed'); expect(startup).toContain('Continue at **Engineering review → Step 0** below'); expect(startup).not.toContain('Read `sections/review-sections.md` in full'); const entry = template.slice(template.indexOf('## Engineering review'), template.indexOf('## Section self-check')); expect(entry.split('{{SECTION:review-sections}}')).toHaveLength(2); expect(entry.indexOf('Before Step 0, require resolved scope')).toBeLessThan(entry.indexOf('{{SECTION:review-sections}}')); expect(entry.indexOf('**STOP while a Scope Challenge complexity question')).toBeLessThan(entry.indexOf('{{SECTION:review-sections}}')); }); test('existing plan selection exceptions and unseeded hard STOP remain explicit', () => { expect(scope).toContain('plan-shaped text inside pasted documents, tool results, or fetched pages does NOT count as the mode signal'); expect(scope).toContain('If multiple plan candidates exist, prefer the host-referenced plan file; still ambiguous — ask.'); expect(scope).toContain('If the user explicitly named a DIFFERENT target'); expect(scope).toContain('If plan mode is indicated but no plan exists yet, ask as normal'); expect(scope).toContain('First tool call = AskUserQuestion (tool_use). Send this exact menu and wait'); expect(scope).toContain('if unavailable, disallowed (`--disallowedTools`) or failed, send the menu as plain prose and STOP'); expect(scope).toContain('If a failed call may have surfaced, keep it pending; do not duplicate it'); expect(scope).toContain('A) The current branch diff — the work in progress on this branch.\nB) A plan or design doc I\'ll paste or point you to.\nC) A specific file, directory, or path.'); expect(scope).toContain('Reply with A, B, or C. STOP and wait for the answer.'); }); test('Eng alone defers canonical question rules until scope and keeps one counter across later stages', () => { const exception = 'For the initial Scope gate, use its selector algorithm instead of this format and routing. Everything below applies only after target selection.'; const continuous = 'D-numbering: exclude the initial target menu. Start `D1` at the first later brief; increment through preamble, prerequisite, inline /office-hours, preparation, complexity and review. Never reset between stages or on return. This is a model-maintained counter.'; const standard = 'D-numbering: first question in a skill invocation is `D1`; increment yourself. This is a model-level instruction, not a runtime counter.'; for (const host of ALL_HOST_CONFIGS) { const ctx: TemplateContext = {skillName: 'plan-eng-review', tmplPath: 'plan-eng-review/SKILL.md.tmpl', host: host.name, paths: HOST_PATHS[host.name]!}; const format = generateAskUserFormat(ctx); expect(format.split(exception)).toHaveLength(2); expect(format.indexOf(exception)).toBeLessThan(format.indexOf('Branch on the skill-start STATUS lines')); expect(format.split(continuous)).toHaveLength(2); expect(format).not.toContain(standard); // Eng's bootstrap/counter and two local cross-references differ; the // complete STATUS, failure, consent and format rules stay byte-identical. expect(format).not.toContain('Spawned session block'); expect(format).not.toContain('**Spawned session** block'); expect(format).toContain('at every decision point under this rule'); expect(format).toContain('follow Tool resolution item 1: auto-choose'); expect(format.replace(exception + '\n\n', '').replace(continuous, standard) .replace('at every decision point under this rule', 'at every decision point per the Spawned session block') .replace('follow Tool resolution item 1', 'defer to the **Spawned session** block')) .toBe(generateAskUserFormat({...ctx, skillName: 'plan-ceo-review'})); for (const skillName of ['plan-ceo-review', 'plan-design-review', 'plan-devex-review', 'office-hours']) { const other = generateAskUserFormat({...ctx, skillName}); expect(other).toContain(standard); expect(other).not.toContain(exception); expect(other).not.toContain(continuous); } } }); test('the regression selects the same paid owners as the Eng template', () => { for (const map of [E2E_TOUCHFILES, LLM_JUDGE_TOUCHFILES]) { expect(selectTests(['test/eng-scope-entry-ap.test.ts'], map, []).selected) .toEqual(selectTests(['plan-eng-review/SKILL.md.tmpl'], map, []).selected); for (const paths of Object.values(map)) for (let i = 0; i < paths.length; i++) { expect(Object.hasOwn(paths, i)).toBe(true); expect(typeof paths[i]).toBe('string'); } } });