mirror of
https://github.com/garrytan/gstack.git
synced 2026-08-31 18:30:39 +02:00
Reorder the pipeline to CEO -> Design (if UI scope) -> DX (if developer-facing scope) -> Eng. The old order (CEO -> Design -> Eng -> DX) let DX findings land AFTER the required gate signed off, so eng validated a stale plan. Accept-all semantics made explicit: every AskUserQuestion resolves to the recommended option; premises no longer pause the pipeline mid-run (clearly-wrong ones queue as User-Challenge items at the single Final Approval Gate). Eng's Codex voice now sees the DX consensus summary. New free static test pins the order; the chain E2E gains DX-between and Eng-terminal assertions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
71 lines
3.0 KiB
TypeScript
71 lines
3.0 KiB
TypeScript
/**
|
|
* /autoplan phase-order pin (free, static).
|
|
*
|
|
* The pipeline order is a deliberate design decision (2026-08-25, user-directed):
|
|
* CEO → Design (if UI scope) → DX (if developer-facing scope) → Eng, ALWAYS LAST.
|
|
* Eng is the required shipping gate — it must review the FINAL amended plan, so
|
|
* every other phase's amendments land before it. The original order buried Eng
|
|
* mid-pipeline (CEO → Design → Eng → DX), which let DX findings land AFTER the
|
|
* gate had signed off — the gate validated a stale plan.
|
|
*
|
|
* These assertions pin the template so a refactor can't silently restore the
|
|
* old order. The paid chain E2E (skill-e2e-autoplan-chain.test.ts) verifies the
|
|
* runtime behavior; this pins the source of truth for free on every PR.
|
|
*/
|
|
import { describe, test, expect } from 'bun:test';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
|
|
const ROOT = path.join(import.meta.dir, '..');
|
|
const read = (p: string) => fs.readFileSync(path.join(ROOT, p), 'utf-8');
|
|
|
|
describe('autoplan phase order (Eng always last)', () => {
|
|
const tmpl = read('autoplan/SKILL.md.tmpl');
|
|
|
|
test('Sequential Execution block names Eng as the terminal phase', () => {
|
|
const block = tmpl.split('## Sequential Execution')[1]?.split('---')[0] ?? '';
|
|
expect(block).toContain('Eng runs LAST, always');
|
|
expect(block).toMatch(/CEO → Design.*→ DX.*→ Eng/s);
|
|
// The old order must not resurface anywhere in the template.
|
|
expect(tmpl).not.toContain('CEO → Design → Eng → DX');
|
|
});
|
|
|
|
test('phase headings appear in the new order: 1, 2, 2.5, 3', () => {
|
|
const idx = (h: string) => {
|
|
const i = tmpl.indexOf(h);
|
|
expect(i).toBeGreaterThan(-1);
|
|
return i;
|
|
};
|
|
const p1 = idx('## Phase 1: CEO Review');
|
|
const p2 = idx('## Phase 2: Design Review');
|
|
const p25 = idx('## Phase 2.5: DX Review');
|
|
const p3 = idx('## Phase 3: Eng Review');
|
|
expect(p1).toBeLessThan(p2);
|
|
expect(p2).toBeLessThan(p25);
|
|
expect(p25).toBeLessThan(p3);
|
|
// No stale Phase 3.5 heading or transition marker survives.
|
|
expect(tmpl).not.toContain('Phase 3.5');
|
|
});
|
|
|
|
test('phase sections hand off in the new order', () => {
|
|
expect(read('autoplan/sections/dx-phase.md.tmpl')).toContain(
|
|
'Passing to Phase 3 (Eng Review',
|
|
);
|
|
expect(read('autoplan/sections/eng-phase.md.tmpl')).toContain(
|
|
'Passing to Phase 4 (Final Gate)',
|
|
);
|
|
// Eng's Codex voice sees every prior phase's consensus, DX included.
|
|
expect(read('autoplan/sections/eng-phase.md.tmpl')).toContain(
|
|
'DX: <insert DX consensus table summary',
|
|
);
|
|
});
|
|
|
|
test('single final gate: premises queue for the gate, never a mid-run stop', () => {
|
|
expect(tmpl).toContain('One exception class — never auto-decided');
|
|
expect(tmpl).not.toContain('Premise gate passed (user confirmed)');
|
|
const ceo = read('autoplan/sections/ceo-phase.md.tmpl');
|
|
expect(ceo).not.toContain('GATE: Present premises to user for confirmation');
|
|
expect(ceo).toContain('Final');
|
|
});
|
|
});
|