From a9a1e98e846d886871f8fe9fbefe1c97c825532f Mon Sep 17 00:00:00 2001 From: garrytan Date: Wed, 30 Sep 2026 05:55:32 +0000 Subject: [PATCH] test(pty): fake PTY session driver with an injectable clock through the runner launch seam The three plan-skill runners take an optional PtyDriver (launch, now, monotonic, sleep); omitted, they use the real launcher and clocks exactly as before. test/helpers/pty/fake-session.ts feeds scripted frames through that seam, and claude-pty-runner.runners.unit.test.ts runs observation, counting and floor for success, deadline timeout, permission prompt and plan-ready outcomes with no CLI or real timers. These cases must stay green unchanged through the W4 split and the runPtySession extraction. Touchfiles: every entry that lists claude-pty-runner.ts or pty-screen.ts now also lists test/helpers/pty/**. --- .../claude-pty-runner.runners.unit.test.ts | 186 ++++++++++++++++++ test/helpers/claude-pty-runner.ts | 163 ++++++++------- test/helpers/pty/fake-session.ts | 149 ++++++++++++++ test/helpers/touchfiles-data.ts | 40 ++-- test/test-of-test-ratchet.test.ts | 1 + 5 files changed, 451 insertions(+), 88 deletions(-) create mode 100644 test/helpers/claude-pty-runner.runners.unit.test.ts create mode 100644 test/helpers/pty/fake-session.ts diff --git a/test/helpers/claude-pty-runner.runners.unit.test.ts b/test/helpers/claude-pty-runner.runners.unit.test.ts new file mode 100644 index 000000000..ab32b091f --- /dev/null +++ b/test/helpers/claude-pty-runner.runners.unit.test.ts @@ -0,0 +1,186 @@ +/** + * The three plan-skill runners driven through the fake PTY session driver + * (scripted frames, injectable clock, no CLI or model). Each runner is run + * for success, deadline timeout, a permission prompt and plan-ready, and its + * launch request is checked for the model pin and skill seeding. + * + * Value: protects=runner outcomes across the runPtySession extraction; fails_when=the session loop reorders poll, permission, deadline or terminal handling; why_new=the runner loops had no deterministic driver; seam=PtyDriver + */ +import { describe, expect, test } from 'bun:test'; +import * as fs from 'node:fs'; +import * as os from 'node:os'; +import * as path from 'node:path'; +import { runPlanSkillObservation, runPlanSkillCounting, runPlanSkillFloorCheck } from './claude-pty-runner'; +import { createFakePtyDriver, type FakeFrame, type FakeSessionContext } from './pty/fake-session'; +import { FORCING_FLOOR_DEVEX } from '../fixtures/forcing-finding-seeds'; + +const IDLE = 'Claude Code\n> '; +const PLAN_READY = 'Here is the plan.\nReady to execute?\n❯ 1. Yes, and auto-accept edits\n 2. Yes, and manually approve edits\n 3. No, keep planning'; +const PERMISSION = 'Create file\n/tmp/unowned-report.md\n────────────────\n 1 # Report\n────────────────\n' + + 'Do you want to create unowned-report.md?\n❯ 1. Yes\n 2. Yes, and switch to accept edits (auto-approve file edits and common file commands) for this session\n 3. No\nEsc to cancel · Tab to amend'; +const QUESTION = { header: 'Architecture', question: 'The plan repeats a custom generator in each service. Should we use the built-in generator?', + multiSelect: false, options: [{ label: 'Use built-in', description: 'Remove the custom generator.' }, { label: 'Keep custom', description: 'Keep the maintenance burden.' }] }; +const TTHW = { header: 'TTHW target', question: 'D2 — Which TTHW target should this journey be measured against?\nThe SDK quickstart has eight steps and an unbounded wait for an emailed API key.', + multiSelect: false, options: [{ label: 'A) Champion (< 2 min)', description: 'Puts key and database questions on the table; not reachable via docs alone.' }, + { label: 'B) Competitive (2-5 min) (recommended)', description: 'Shows which gaps docs polish closes; still blocked by emailed key and local Postgres.' }] }; +const render = (q: typeof QUESTION) => ['☐ ' + q.header, q.question, + ...q.options.flatMap((o, i) => [`${i ? ' ' : '❯'} ${i + 1}. ${o.label}`, o.description]), + 'Enter to select · ↑/↓ to navigate · Esc to cancel'].join('\n'); + +/** Native JSONL writer for the fake session's isolated config dir. */ +function journal(ctx: FakeSessionContext, sessionId: string, role: 'user' | 'assistant', content: unknown, extra: object = {}) { + const file = path.join(ctx.configDir!, 'projects', 'owned', `${sessionId}.jsonl`); + fs.mkdirSync(path.dirname(file), { recursive: true }); + fs.appendFileSync(file, JSON.stringify({ type: role, cwd: ctx.launch.cwd, sessionId, isSidechain: false, + timestamp: new Date(ctx.now).toISOString(), message: { role, content }, ...extra }) + '\n'); +} +const floorSession = (ctx: FakeSessionContext) => ctx.launch.extraArgs![ctx.launch.extraArgs!.indexOf('--session-id') + 1]!; + +async function withConfigDir(run: (configDir: string) => Promise): Promise { + const configDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pty-fake-config-')); + const saved = { run: process.env.EVALS_RUN_ID, dir: process.env.GSTACK_EVAL_DIR }; + delete process.env.EVALS_RUN_ID; delete process.env.GSTACK_EVAL_DIR; + try { return await run(configDir); } finally { + if (saved.run !== undefined) process.env.EVALS_RUN_ID = saved.run; + if (saved.dir !== undefined) process.env.GSTACK_EVAL_DIR = saved.dir; + fs.rmSync(configDir, { recursive: true, force: true }); + } +} + +describe('runPlanSkillObservation through the fake driver', () => { + const observe = (frames: FakeFrame[]) => withConfigDir(async () => { + const fake = createFakePtyDriver({ frames: [{ screen: IDLE }, ...frames] }); + const obs = await runPlanSkillObservation({ skillName: 'plan-eng-review', timeoutMs: 20_000, model: 'fake-model', driver: fake.driver }); + return { obs, fake }; + }); + + test('success: a rendered question is asked', async () => { + const { obs, fake } = await observe([{ onInput: '/plan-eng-review\r', screen: render(QUESTION) }]); + expect(obs.outcome).toBe('asked'); + expect(fake.sent).toEqual(['/plan-eng-review\r']); + expect(fake.closes()).toBe(1); + expect(fake.launches).toHaveLength(1); + expect(fake.launches[0]).toMatchObject({ permissionMode: 'plan', seedSkills: true, model: 'fake-model', env: { GSTACK_PLAN_MODE: 'active' } }); + }); + + test('deadline: no terminal frame times out on the injected clock', async () => { + const { obs, fake } = await observe([{ onInput: '/plan-eng-review\r', screen: 'Thinking…' }]); + expect(obs.outcome).toBe('timeout'); + expect(obs.summary).toBe('no terminal outcome within 20000ms'); + expect(obs.elapsedMs).toBe(20_000); + expect(fake.closes()).toBe(1); + }); + + test('permission prompt: never answered and never counted as a question', async () => { + const { obs, fake } = await observe([{ onInput: '/plan-eng-review\r', screen: PERMISSION }]); + expect(obs.outcome).toBe('timeout'); + expect(obs.proseAUQEverObserved).toBe(false); + expect(fake.sent).toEqual(['/plan-eng-review\r']); + }); + + test('plan-ready: the native confirmation ends the observation', async () => { + const { obs } = await observe([{ onInput: '/plan-eng-review\r', screen: PERMISSION }, { afterMs: 4000, screen: PLAN_READY }]); + expect(obs.outcome).toBe('plan_ready'); + }); +}); + +describe('runPlanSkillCounting through the fake driver', () => { + const count = (frames: FakeFrame[], seedTranscript = true) => withConfigDir(async configDir => { + const fake = createFakePtyDriver({ configDir, frames: [ + { screen: IDLE, effect: ctx => { if (seedTranscript) journal(ctx, 'count', 'assistant', [{ type: 'text', text: 'Ready.' }]); } }, + ...frames] }); + const obs = await runPlanSkillCounting({ skillName: 'plan-eng-review', slashCommand: '/plan-eng-review', + followUpPrompt: '# Plan: fake counting fixture', isLastStep0AUQ: () => false, isReviewAUQ: () => true, + reviewCountCeiling: 8, timeoutMs: 60_000, model: 'fake-model', driver: fake.driver }); + return { obs, fake }; + }); + const asked = (ctx: FakeSessionContext) => journal(ctx, 'count', 'assistant', [{ type: 'tool_use', id: 'finding', name: 'AskUserQuestion', input: { questions: [QUESTION] } }]); + const answered = (ctx: FakeSessionContext) => journal(ctx, 'count', 'user', [{ type: 'tool_result', tool_use_id: 'finding', content: 'Answered.' }], + { toolUseResult: { answers: { [QUESTION.question]: 'Use built-in' } } }); + + test('success: one answered native review question, then the completion summary', async () => { + const { obs, fake } = await count([ + { onInput: '/plan-eng-review\r', screen: render(QUESTION), effect: asked }, + { onInput: '1', screen: '## Completion Summary\nOne finding resolved.\n> ', effect: answered }, + ]); + expect(obs.outcome).toBe('completion_summary'); + expect(obs.reviewCount).toBe(1); + expect(fake.sent).toEqual(['/plan-eng-review\r', '1']); + expect(fake.closes()).toBe(1); + expect(fake.launches[0]).toMatchObject({ permissionMode: 'plan', seedSkills: true, model: 'fake-model', observeScreen: true, observePlanReady: true }); + }); + + test('deadline: the work window closes with the cleanup reserve kept', async () => { + const { obs, fake } = await count([{ onInput: '/plan-eng-review\r', screen: 'Thinking…' }]); + expect(obs.outcome).toBe('timeout'); + expect(obs.summary).toContain('no terminal outcome within 60000ms total budget'); + expect(obs.elapsedMs).toBeLessThanOrEqual(55_000); + }); + + test('permission prompt: granted with the default pick, then plan-ready', async () => { + const { obs, fake } = await count([ + { onInput: '/plan-eng-review\r', screen: PERMISSION }, + { onInput: '1\r', screen: PLAN_READY }, + ]); + expect(obs.outcome).toBe('plan_ready'); + expect(fake.sent).toEqual(['/plan-eng-review\r', '1\r']); + }); + + test('plan-ready: needs a complete native transcript', async () => { + const ready = await count([{ onInput: '/plan-eng-review\r', screen: PLAN_READY }]); + expect(ready.obs.outcome).toBe('plan_ready'); + expect(ready.obs.reviewCount).toBe(0); + const missing = await count([{ onInput: '/plan-eng-review\r', screen: PLAN_READY }], false); + expect(missing.obs.outcome).toBe('transcript_unavailable'); + }); +}); + +describe('runPlanSkillFloorCheck through the fake driver', () => { + const delivered = (ctx: FakeSessionContext) => journal(ctx, floorSession(ctx), 'user', + 'plan-devex-review\n/plan-devex-review\nPLAN.md'); + const floor = (frames: FakeFrame[]) => withConfigDir(async configDir => { + const fake = createFakePtyDriver({ configDir, frames: [{ screen: IDLE }, ...frames] }); + const obs = await runPlanSkillFloorCheck({ skillName: 'plan-devex-review', slashCommand: '/plan-devex-review', + followUpPrompt: FORCING_FLOOR_DEVEX, timeoutMs: 30_000, model: 'fake-model', driver: fake.driver }); + return { obs, fake }; + }); + + test('success: the deterministic seeded finding is observed without an answer', async () => { + const { obs, fake } = await floor([ + { onInput: '/plan-devex-review PLAN.md\r', screen: 'Reviewing PLAN.md', effect: delivered }, + { afterMs: 2000, screen: render(TTHW), effect: ctx => journal(ctx, floorSession(ctx), 'assistant', + [{ type: 'tool_use', id: 'tthw', name: 'AskUserQuestion', input: { questions: [TTHW] } }]) }, + ]); + expect(obs.outcome).toBe('auq_observed'); + expect(obs.auqObserved).toBe(true); + expect(obs.targetDelivery?.status).toBe('ready'); + expect(fake.sent).toEqual(['/plan-devex-review PLAN.md\r']); + expect(fake.closes()).toBe(1); + expect(fake.launches[0]).toMatchObject({ permissionMode: 'plan', seedSkills: true, model: 'fake-model', observeScreen: true, observeSetupQuestions: true }); + }); + + test('deadline: a delivered target with no question times out', async () => { + const { obs } = await floor([{ onInput: '/plan-devex-review PLAN.md\r', screen: 'Thinking…', effect: delivered }]); + expect(obs.outcome).toBe('timeout'); + expect(obs.summary).toBe('no qualifying finding question within 30000ms'); + }); + + test('permission prompt: an unowned permission is neither granted nor counted', async () => { + const { obs, fake } = await floor([ + { onInput: '/plan-devex-review PLAN.md\r', screen: PERMISSION, effect: delivered }, + { afterMs: 6000, screen: PLAN_READY }, + ]); + expect(obs.outcome).toBe('plan_ready'); + expect(obs.auqObserved).toBe(false); + expect(fake.sent).toEqual(['/plan-devex-review PLAN.md\r']); + }); + + test('plan-ready: reaching approval without a finding is the regression outcome', async () => { + const { obs } = await floor([ + { onInput: '/plan-devex-review PLAN.md\r', screen: 'Reviewing PLAN.md', effect: delivered }, + { afterMs: 2000, screen: PLAN_READY }, + ]); + expect(obs.outcome).toBe('plan_ready'); + expect(obs.summary).toBe('agent reached plan_ready without a qualifying finding question'); + }); +}); diff --git a/test/helpers/claude-pty-runner.ts b/test/helpers/claude-pty-runner.ts index 2bcc77fd3..d554b06e0 100644 --- a/test/helpers/claude-pty-runner.ts +++ b/test/helpers/claude-pty-runner.ts @@ -216,6 +216,21 @@ export interface ClaudePtySession { close(): Promise; } +/** + * The runners' launch seam: how a session starts, and the clock the runner + * loop reads. Omitted in production (real launcher, Date.now, + * performance.now, Bun.sleep); tests pass the fake driver from + * test/helpers/pty/fake-session.ts. + */ +export interface PtyDriver { + launch(opts: ClaudePtyOptions): Promise; + /** Wall clock, Date.now() semantics. */ + now(): number; + /** Monotonic clock, performance.now() semantics. */ + monotonic(): number; + sleep(ms: number): Promise; +} + /** Let a numbered menu apply its selection before confirming it. */ export async function selectPtyNumberedOption( session: Pick, @@ -3688,11 +3703,15 @@ export async function runPlanSkillObservation(opts: { * buffer (case-sensitive). Results land in obs.tokensObserved. Use for * consumption asserts that must survive the 2KB evidence tail. */ trackTokens?: string[]; + /** Launch seam and clock; tests pass the fake driver. Default: real launcher and clocks. */ + driver?: PtyDriver; }): Promise { - const startedAt = Date.now(); + const driver = opts.driver ?? { launch: launchClaudePty, now: () => Date.now(), + monotonic: () => performance.now(), sleep: (ms: number) => Bun.sleep(ms) }; + const startedAt = driver.now(); const budgetMs = opts.timeoutMs ?? 180_000; const deadlineAt = startedAt + budgetMs; - const screenDeadlineAt = performance.now() + budgetMs; + const screenDeadlineAt = driver.monotonic() + budgetMs; // Explicitly identify only a new seeded plan-mode session. Caller-owned // resume/session arguments retain their existing behavior. const scopeSessionId = opts.initialPlanContent && opts.inPlanMode !== false && @@ -3701,7 +3720,7 @@ export async function runPlanSkillObservation(opts: { const readAutoDecisionState = opts.autoDecisionState ? bindAutoDecisionState(opts.autoDecisionState, opts.env, opts.skillName) : undefined; const saveSnapshot = createPlanCountSnapshotWriter(); - const session = await launchClaudePty({ + const session = await driver.launch({ permissionMode: opts.inPlanMode === false ? null : 'plan', cwd: opts.cwd, timeoutMs: (opts.timeoutMs ?? 180_000) + 30_000, @@ -3727,7 +3746,7 @@ export async function runPlanSkillObservation(opts: { observation: { state: 'plan_skill_preflight_timeout', summary, scopeSessionId, startedAt, deadlineAt, viewportError } }); return { outcome: 'timeout', summary, evidence: session.visibleText().slice(-2000), - elapsedMs: Date.now() - startedAt, + elapsedMs: driver.now() - startedAt, proseAUQEverObserved: false, waitingEverObserved: false, scopeGateQuestionObserved: false, scopeGateAutoSelectObserved: false, ...(opts.trackTokens?.length ? { tokensObserved: Object.fromEntries(opts.trackTokens.map(t => [t, false])) } : {}), @@ -3736,7 +3755,7 @@ export async function runPlanSkillObservation(opts: { }; // Entry deadline → boot → owned paste/receipt/ack → slash → observation. // Setup consumes the existing case budget; cleanup has its separate grace. - if (!opts.initialPlanContent) await Bun.sleep(Math.min(8000, Math.max(0, deadlineAt - Date.now()))); + if (!opts.initialPlanContent) await driver.sleep(Math.min(8000, Math.max(0, deadlineAt - driver.now()))); if (opts.initialPlanContent) { const seed = `Keep this draft plan as context. Briefly acknowledge receipt, then wait for my next message containing a slash command. Do not start the review or call tools yet.\n\n${opts.initialPlanContent}`; try { @@ -3749,12 +3768,12 @@ export async function runPlanSkillObservation(opts: { return await preflightTimeout(`Plan seed submission failed: ${error.message}`); } } - if (Date.now() >= deadlineAt) return await preflightTimeout('Boot or seed preflight exhausted the existing case budget'); - const commandStartedAt = Date.now(); + if (driver.now() >= deadlineAt) return await preflightTimeout('Boot or seed preflight exhausted the existing case budget'); + const commandStartedAt = driver.now(); const since = session.mark(); session.send(`/${opts.skillName}\r`); - const start = Date.now(); + const start = driver.now(); let lastJudgeAt = 0; let lastJudgeVerdict: PtyStateVerdict | null = null; // High-water marks: did we EVER see a prose-AUQ surface or a judge @@ -3793,8 +3812,8 @@ export async function runPlanSkillObservation(opts: { }; const JUDGE_AFTER_MS = 60_000; const JUDGE_INTERVAL_MS = 30_000; - while (Date.now() < deadlineAt) { - await Bun.sleep(Math.min(2000, Math.max(0, deadlineAt - Date.now()))); + while (driver.now() < deadlineAt) { + await driver.sleep(Math.min(2000, Math.max(0, deadlineAt - driver.now()))); const visible = session.visibleSince(since); if (session.exited()) { @@ -3802,7 +3821,7 @@ export async function runPlanSkillObservation(opts: { outcome: 'exited', summary: `claude exited (code=${session.exitCode()}) before reaching a terminal outcome`, evidence: visible.slice(-2000), - elapsedMs: Date.now() - startedAt, + elapsedMs: driver.now() - startedAt, ...highWaterFlags(), }; } @@ -3811,7 +3830,7 @@ export async function runPlanSkillObservation(opts: { outcome: 'exited', summary: `claude rejected /${opts.skillName} as unknown command (skill not registered in this cwd)`, evidence: visible.slice(-2000), - elapsedMs: Date.now() - startedAt, + elapsedMs: driver.now() - startedAt, ...highWaterFlags(), }; } @@ -3829,7 +3848,7 @@ export async function runPlanSkillObservation(opts: { proseAUQEverObserved = true; logPtySnapshot(visible, { testName: opts.skillName, - elapsedMs: Date.now() - start, + elapsedMs: driver.now() - start, tag: 'prose-auq-surfaced', }); } @@ -3848,7 +3867,7 @@ export async function runPlanSkillObservation(opts: { scopeTools = []; scopeTranscript = readPlanCountTranscript(session.hermeticConfigDir, path.resolve(opts.cwd ?? process.cwd()), event => scopeTools.push(event)); - nativePolledAt = Date.now(); + nativePolledAt = driver.now(); if (!scopeGateAutoSelectObserved) scopeGateAutoSelectObserved = nativeSeededPlanSelection(scopeTranscript, scopeTools, { seed: opts.initialPlanContent, skillName: opts.skillName, sessionId: scopeSessionId, commandStartedAt, }); @@ -3861,7 +3880,7 @@ export async function runPlanSkillObservation(opts: { const obs: PlanSkillObservation = { ...classified, evidence: visible.slice(-2000), - elapsedMs: Date.now() - startedAt, + elapsedMs: driver.now() - startedAt, ...highWaterFlags(), }; // Capture the plan file path on any outcome where one may have been @@ -3880,13 +3899,13 @@ export async function runPlanSkillObservation(opts: { // and writes). Only an unclassified frame may use owned native auto-decision evidence. if (scopeSessionId) { nativeAutoDecide = findNativeAutoDecision(scopeTranscript, scopeTools, { - skillName: opts.skillName, sessionId: scopeSessionId, commandStartedAt, now: Date.now(), proseQuestionObserved: proseAUQEverObserved, + skillName: opts.skillName, sessionId: scopeSessionId, commandStartedAt, now: driver.now(), proseQuestionObserved: proseAUQEverObserved, stateEvidence: readAutoDecisionState?.(), }); if (nativeAutoDecide) return { outcome: 'auto_decided', summary: 'owned native session completed the saved-preference auto-decision', - evidence: visible.slice(-2000), elapsedMs: Date.now() - startedAt, + evidence: visible.slice(-2000), elapsedMs: driver.now() - startedAt, ...highWaterFlags(), }; } @@ -3896,9 +3915,9 @@ export async function runPlanSkillObservation(opts: { // working, or hung?" Treat 'waiting' as 'asked' (model surfaced a // question via prose the regex couldn't reassemble). Snapshot the // visible buffer at each judge call when GSTACK_PTY_LOG=1. - const elapsed = Date.now() - start; - if (elapsed > JUDGE_AFTER_MS && Date.now() - lastJudgeAt > JUDGE_INTERVAL_MS) { - lastJudgeAt = Date.now(); + const elapsed = driver.now() - start; + if (elapsed > JUDGE_AFTER_MS && driver.now() - lastJudgeAt > JUDGE_INTERVAL_MS) { + lastJudgeAt = driver.now(); logPtySnapshot(visible, { testName: opts.skillName, elapsedMs: elapsed, tag: 'judge-tick' }); lastJudgeVerdict = judgePtyState(visible, { testName: opts.skillName }); if (lastJudgeVerdict.state === 'waiting' && !pendingSeededCompletion) { @@ -3908,7 +3927,7 @@ export async function runPlanSkillObservation(opts: { outcome: 'asked', summary: `LLM judge: ${lastJudgeVerdict.reasoning} (state=waiting after ${Math.round(elapsed / 1000)}s)`, evidence: visible.slice(-2000), - elapsedMs: Date.now() - startedAt, + elapsedMs: driver.now() - startedAt, ...highWaterFlags(), }; } @@ -3930,7 +3949,7 @@ export async function runPlanSkillObservation(opts: { ? ` (last LLM judge: ${lastJudgeVerdict.state} — ${lastJudgeVerdict.reasoning})` : ''), evidence: finalVisible.slice(-2000), - elapsedMs: Date.now() - startedAt, + elapsedMs: driver.now() - startedAt, ...highWaterFlags(), }; } @@ -3942,7 +3961,7 @@ export async function runPlanSkillObservation(opts: { ? ` (last LLM judge: state=${lastJudgeVerdict.state} — ${lastJudgeVerdict.reasoning})` : ''), evidence: finalVisible.slice(-2000), - elapsedMs: Date.now() - startedAt, + elapsedMs: driver.now() - startedAt, ...highWaterFlags(), }; } catch (error) { @@ -4125,7 +4144,11 @@ export async function runPlanSkillCounting(opts: { env?: Record; /** Override the spawned model. Defaults via launchClaudePty's chain. */ model?: string; + /** Launch seam and clock; tests pass the fake driver. Default: real launcher and clocks. */ + driver?: PtyDriver; }): Promise { + const driver = opts.driver ?? { launch: launchClaudePty, now: () => Date.now(), + monotonic: () => performance.now(), sleep: (ms: number) => Bun.sleep(ms) }; if (opts.requireNativePicker && !opts.pickAUQ) throw Error('Native picker binding requires a declared picker'); if (opts.bindDesignBoardState && (opts.skillName !== 'plan-design-review' || !opts.pickAUQ)) @@ -4134,8 +4157,8 @@ export async function runPlanSkillCounting(opts: { throw Error('Eng test-plan approval requires the Eng caller and its explicit report'); if (opts.isCollectionComplete && opts.expectedPlanPath) throw Error('Collection-only completion cannot replace the final report contract'); - const budgetStarted = performance.now(); - const startedAt = Date.now(); + const budgetStarted = driver.monotonic(); + const startedAt = driver.now(); const defaultPick = opts.defaultPick ?? 1; const timeoutMs = opts.timeoutMs ?? 1_500_000; if (opts.startupReadyMarker !== undefined && !opts.startupReadyMarker.length) { @@ -4149,12 +4172,12 @@ export async function runPlanSkillCounting(opts: { throw new RangeError('Plan counting timeout must exceed the 5000ms cleanup reserve'); } const workDeadline = budgetStarted + timeoutMs - cleanupReserveMs; - const remainingWork = () => Math.max(0, workDeadline - performance.now()); + const remainingWork = () => Math.max(0, workDeadline - driver.monotonic()); async function waitForWork(ms: number): Promise { const remaining = remainingWork(); if (remaining <= 0) return false; const clipped = ms >= remaining; - await Bun.sleep(Math.min(ms, remaining)); + await driver.sleep(Math.min(ms, remaining)); // A clipped wait cannot finish the requested interval. Timers may wake // just before the fractional deadline; that is no license to advance. return !clipped && remainingWork() > 0; @@ -4169,7 +4192,7 @@ export async function runPlanSkillCounting(opts: { ]; let session: ClaudePtySession; try { - session = await launchClaudePty({ + session = await driver.launch({ permissionMode: 'plan', cwd: fixture.cwd, // Stop new output at the work cutoff so screen drain cannot consume @@ -4210,7 +4233,7 @@ export async function runPlanSkillCounting(opts: { let administrativeCount = 0; let isFirstAUQ = true; const saveSnapshot = createPlanCountSnapshotWriter(); - let lastCheckpointAt = Date.now(); + let lastCheckpointAt = driver.now(); let viewport = ''; const capture = (observation: object) => { @@ -4243,7 +4266,7 @@ export async function runPlanSkillCounting(opts: { ? `exitCode=${session.exitCode()}\n--- post-command evidence (last 3KB) ---\n${clean(visible).slice(-3000)}` + `\n--- full-session evidence, including startup (last 6KB) ---\n${clean(session.visibleText()).slice(-6000)}` : visible.slice(-3000), - elapsedMs: Date.now() - startedAt, + elapsedMs: driver.now() - startedAt, fingerprints, transcript, step0Count, @@ -4272,7 +4295,7 @@ export async function runPlanSkillCounting(opts: { observedOutput = session.mark(); if (opts.approveEngTestPlanEdits) { if (!session.startAutoplanArtifactEditApproval) throw Error('Owned Eng test-plan approval hook unavailable'); - session.startAutoplanArtifactEditApproval(Date.now()); + session.startAutoplanArtifactEditApproval(driver.now()); } session.send(`${opts.slashCommand}\r`); } @@ -4281,10 +4304,10 @@ export async function runPlanSkillCounting(opts: { await session.waitForOutput(observedOutput, Math.min(2000, remainingWork())); if (remainingWork() <= 0) break; const coalesceMs = session.rawOutput().length > observedOutput - ? 250 : 250 - (performance.now() - lastObservationAt); + ? 250 : 250 - (driver.monotonic() - lastObservationAt); if (coalesceMs > 0 && !await waitForWork(coalesceMs)) break; observedOutput = session.mark(); - lastObservationAt = performance.now(); + lastObservationAt = driver.monotonic(); const visible = viewport = await session.currentScreen(); if (remainingWork() <= 0) break; transcript = session.hermeticConfigDir @@ -4301,9 +4324,9 @@ export async function runPlanSkillCounting(opts: { // Native approval owns this Edit. Never answer its repaint or count a // metadata-only pending request; resume only after its actual result. if (boundary === 'pending') { - if (Date.now() - lastCheckpointAt >= 30_000) { - lastCheckpointAt = Date.now(); - const saved = capture({ state: 'artifact_pending', elapsedMs: Date.now() - startedAt, + if (driver.now() - lastCheckpointAt >= 30_000) { + lastCheckpointAt = driver.now(); + const saved = capture({ state: 'artifact_pending', elapsedMs: driver.now() - startedAt, fingerprints, step0Count, reviewCount, administrativeCount, transcript, artifactStatus: status }); if (saved.artifactError) console.error(`PTY artifact write failed: ${saved.artifactError}`); } @@ -4316,7 +4339,7 @@ export async function runPlanSkillCounting(opts: { for (const [callIndex, call] of transcript.calls.entries()) { const signature = `${call.sessionId}:${call.toolUseId}`; if (!call.answered || countedCalls.has(signature)) continue; - const fp = nativePlanCallFingerprint(call, Date.now() - startedAt, !boundaryFired); + const fp = nativePlanCallFingerprint(call, driver.now() - startedAt, !boundaryFired); const phase = planCountQuestionPhase(fp, boundaryFired, opts.isLastStep0AUQ, opts.isFirstReviewAUQ, opts.isSetupAUQ, opts.isCompletionHandoffAUQ, opts.isArtifactGenerationAUQ); if (phase.administrative) { fp.preReview = false; @@ -4340,9 +4363,9 @@ export async function runPlanSkillCounting(opts: { } // An outer test timeout/cancellation may prevent a terminal snapshot. // Keep bounded-cadence evidence without adding a timer to clean up. - if (Date.now() - lastCheckpointAt >= 30_000) { - lastCheckpointAt = Date.now(); - const saved = capture({ state: 'in_progress', elapsedMs: Date.now() - startedAt, + if (driver.now() - lastCheckpointAt >= 30_000) { + lastCheckpointAt = driver.now(); + const saved = capture({ state: 'in_progress', elapsedMs: driver.now() - startedAt, fingerprints, step0Count, reviewCount, administrativeCount, transcript }); if (saved.artifactError) console.error(`PTY artifact write failed: ${saved.artifactError}`); } @@ -4520,7 +4543,7 @@ export async function runPlanSkillCounting(opts: { // findings often reuse the same Add to plan / Defer / Skip menu. if (opts.requireNativePicker && !newlyMatched) continue; const capturedSeen = opts.requireNativePicker ? new Set(seen) : seen; - const fp = capturePlanCountQuestion(visible, capturedSeen, Date.now() - startedAt, !boundaryFired, pending, planningDirectory); + const fp = capturePlanCountQuestion(visible, capturedSeen, driver.now() - startedAt, !boundaryFired, pending, planningDirectory); if (!fp) continue; const boundNativeTab = pending && fp.nativeCall === pending && fp.nativeQuestionIndex !== undefined; if (opts.requireNativePicker && !boundNativeTab) continue; @@ -4572,7 +4595,7 @@ export async function runPlanSkillCounting(opts: { // Keep the exact frame/transcript that the throwing caller observed; // awaiting a redraw here would erase that ordering evidence. const saved = capture({ state: 'threw', error: error instanceof Error ? error.message : String(error), - elapsedMs: Date.now() - startedAt, fingerprints, step0Count, reviewCount, administrativeCount, transcript, + elapsedMs: driver.now() - startedAt, fingerprints, step0Count, reviewCount, administrativeCount, transcript, pendingQuestion: readPendingQuestion(session.pendingQuestionFile, fixture.cwd, session.hermeticConfigDir, startedAt, transcript) }); if (saved.artifactDir) console.error(`Full PTY artifacts: ${saved.artifactDir}`); @@ -4721,8 +4744,12 @@ export async function runPlanSkillFloorCheck(opts: { env?: Record; /** Override the spawned model. Defaults via launchClaudePty's chain. */ model?: string; + /** Launch seam and clock; tests pass the fake driver. Default: real launcher and clocks. */ + driver?: PtyDriver; }): Promise { - const startedAt = Date.now(); + const driver = opts.driver ?? { launch: launchClaudePty, now: () => Date.now(), + monotonic: () => performance.now(), sleep: (ms: number) => Bun.sleep(ms) }; + const startedAt = driver.now(); const timeoutMs = opts.timeoutMs ?? 600_000; const dxContext = opts.devexSetupContext; if (dxContext !== undefined && (opts.skillName !== 'plan-devex-review' || opts.productType !== 'sdk-documentation' || @@ -4745,7 +4772,7 @@ export async function runPlanSkillFloorCheck(opts: { const sessionId = randomUUID(); let session: ClaudePtySession; try { - session = await launchClaudePty({ + session = await driver.launch({ permissionMode: 'plan', cwd: fixture.cwd, timeoutMs: timeoutMs + 60_000, @@ -4780,14 +4807,14 @@ export async function runPlanSkillFloorCheck(opts: { let floorFailed = false; let floorError: unknown; try { - await Bun.sleep(8000); // boot grace + auto-trust handler window + await driver.sleep(8000); // boot grace + auto-trust handler window const since = session.mark(); - const commandStartedAt = Date.now(); + const commandStartedAt = driver.now(); session.send(`${opts.slashCommand} PLAN.md\r`); const deliveryOptions = { seed: fixture.seed, sessionId, slashCommand: opts.slashCommand, startedAt: commandStartedAt }; let targetDelivery = readPlanFloorTarget(session.hermeticConfigDir, fixture.cwd, - { ...deliveryOptions, now: Date.now() }); + { ...deliveryOptions, now: driver.now() }); const saveSnapshot = createPlanCountSnapshotWriter(); let nativeCandidates: NativePlanQuestionCall[] = []; let validatedPendingQuestion: ReturnType; @@ -4805,7 +4832,7 @@ export async function runPlanSkillFloorCheck(opts: { const input = readPendingWriteInput(binding.file, binding.expected, fixture.cwd, session.hermeticConfigDir, startedAt); return input ? [input] : []; }), - questionDiagnostics: { sampledAt, parentSessionId: sessionId, recorderStatus, recorderStatusAt: Date.now(), nativeCandidates, validatedPendingQuestion }, + questionDiagnostics: { sampledAt, parentSessionId: sessionId, recorderStatus, recorderStatusAt: driver.now(), nativeCandidates, validatedPendingQuestion }, ...(dxContext ? { setupContextReplies: [...dxReplies.values()] } : {}), artifactError } }); if (artifacts.artifactError) { artifactError ??= artifacts.artifactError; @@ -4816,9 +4843,9 @@ export async function runPlanSkillFloorCheck(opts: { const checkpoint = () => { const state = JSON.stringify([pendingQuestionRecorderStatus(session.pendingQuestionFile, fixture.cwd, session.hermeticConfigDir), targetDelivery.status, nativeCandidates, validatedPendingQuestion, pendingQuestion, [...dxReplies.values()]]); - if (state === lastCheckpointState && Date.now() - lastCheckpointAt < 15_000) return; - lastCheckpointState = state; lastCheckpointAt = Date.now(); - capture({ state: 'in_progress', elapsedMs: Date.now() - startedAt }); + if (state === lastCheckpointState && driver.now() - lastCheckpointAt < 15_000) return; + lastCheckpointState = state; lastCheckpointAt = driver.now(); + capture({ state: 'in_progress', elapsedMs: driver.now() - startedAt }); }; captureBeforeClose = (error) => { if (floorFailed && session.hermeticConfigDir) { @@ -4826,7 +4853,7 @@ export async function runPlanSkillFloorCheck(opts: { transcript = readPlanCountTranscript(session.hermeticConfigDir, fixture.cwd, event => publicTools.push(event)); } if (!finished) capture({ state: floorFailed ? 'threw' : 'in_progress', error: floorFailed ? String(error) : undefined, - captureReason: 'before_cleanup', elapsedMs: Date.now() - startedAt }); + captureReason: 'before_cleanup', elapsedMs: driver.now() - startedAt }); }; const finish = (observation: PlanSkillFloorObservation): PlanSkillFloorObservation => { const artifacts = capture(observation); @@ -4834,11 +4861,11 @@ export async function runPlanSkillFloorCheck(opts: { return { ...observation, targetDelivery, ...artifacts }; }; - const start = Date.now(); + const start = driver.now(); const deadlineAt = start + timeoutMs; - const screenDeadlineAt = performance.now() + timeoutMs; - while (Date.now() - start < timeoutMs) { - await Bun.sleep(2000); + const screenDeadlineAt = driver.monotonic() + timeoutMs; + while (driver.now() - start < timeoutMs) { + await driver.sleep(2000); const visible = session.visibleSince(since); if (session.exited()) { @@ -4847,7 +4874,7 @@ export async function runPlanSkillFloorCheck(opts: { outcome: 'exited', summary: `claude exited (code=${session.exitCode()}) before a qualifying finding`, evidence: visible.slice(-3000), - elapsedMs: Date.now() - startedAt, + elapsedMs: driver.now() - startedAt, }); } if (isRejectedSlashCommand(visible, opts.slashCommand)) { @@ -4856,13 +4883,13 @@ export async function runPlanSkillFloorCheck(opts: { outcome: 'exited', summary: `claude rejected ${opts.slashCommand} as unknown command`, evidence: visible.slice(-3000), - elapsedMs: Date.now() - startedAt, + elapsedMs: driver.now() - startedAt, }); } if (targetDelivery.status !== 'ready') { targetDelivery = readPlanFloorTarget(session.hermeticConfigDir, fixture.cwd, - { ...deliveryOptions, now: Date.now() }); + { ...deliveryOptions, now: driver.now() }); if (targetDelivery.status !== 'ready') { viewport = await session.currentScreen(screenDeadlineAt); checkpoint(); @@ -4883,10 +4910,10 @@ export async function runPlanSkillFloorCheck(opts: { call.sessionId === sessionId && !call.answered && !call.failed && publicTools.filter(event => event.sessionId === sessionId && event.kind === 'use' && event.name === 'AskUserQuestion' && event.toolUseId === call.toolUseId && Date.parse(event.timestamp) >= commandStartedAt && - Date.parse(event.timestamp) <= Date.now() && isDeepStrictEqual(event.input?.questions, call.questions)).length === 1) : []; + Date.parse(event.timestamp) <= driver.now() && isDeepStrictEqual(event.input?.questions, call.questions)).length === 1) : []; nativeCandidates = currentCalls.slice(); validatedPendingQuestion = hook; - sampledAt = Date.now(); + sampledAt = driver.now(); if (hook?.sessionId === sessionId && !transcript.calls.some(call => call.toolUseId === hook.toolUseId)) currentCalls.push(hook); const activeReply = currentCalls.length === 1 && dxReplies.get(`${currentCalls[0]!.sessionId}:${currentCalls[0]!.toolUseId}`); if (activeReply) { @@ -4922,7 +4949,7 @@ export async function runPlanSkillFloorCheck(opts: { if (permissionIsActiveRender) continue; const questionViewport = dxContext && pendingQuestion ? planFloorDXPane(viewport, pendingQuestion)! : viewport; - const fp = pendingQuestion && capturePlanCountQuestion(questionViewport, new Set(), Date.now() - start, true, pendingQuestion, planningDirectory); + const fp = pendingQuestion && capturePlanCountQuestion(questionViewport, new Set(), driver.now() - start, true, pendingQuestion, planningDirectory); if (fp && pendingQuestion) { const index = fp.nativeQuestionIndex ?? 0; const question = pendingQuestion.questions[index]!; @@ -4947,7 +4974,7 @@ export async function runPlanSkillFloorCheck(opts: { // a generic idle prompt, permission, tool result or quoted example. floorReview = undefined; const message = transcript.assistantMessages.filter(m => m.sessionId === sessionId && - Date.parse(m.timestamp) >= commandStartedAt && Date.parse(m.timestamp) <= Date.now()).at(-1); + Date.parse(m.timestamp) >= commandStartedAt && Date.parse(m.timestamp) <= driver.now()).at(-1); const compact = (text: string) => text.replace(/\s+/g, ''); if (!currentCalls.length && message && isProseAUQVisible(viewport) && isProseAUQVisible(message.text) && !/^\s*(?:>|`{3,}|~{3,})/m.test(message.text) && compact(viewport).includes(compact(message.text))) @@ -4971,7 +4998,7 @@ export async function runPlanSkillFloorCheck(opts: { } catch (error) { return finish({ auqObserved: false, outcome: 'assessment_error', summary: `Finding assessment failed: ${error instanceof Error ? error.message : String(error)}`, - evidence: viewport, elapsedMs: Date.now() - startedAt }); + evidence: viewport, elapsedMs: driver.now() - startedAt }); } assessed.set(key, floorAssessment); } @@ -4983,7 +5010,7 @@ export async function runPlanSkillFloorCheck(opts: { if (floorAssessment.kind === 'finding') return finish({ auqObserved: true, outcome: 'auq_observed', summary: `Current ${floorReview.candidate.transport} question addresses the owned seeded finding: ${floorAssessment.reason}`, - evidence: viewport, elapsedMs: Date.now() - startedAt, + evidence: viewport, elapsedMs: driver.now() - startedAt, }); } @@ -4999,7 +5026,7 @@ export async function runPlanSkillFloorCheck(opts: { outcome: 'silent_write', summary: `Write/Edit to ${target} fired before any AskUserQuestion`, evidence: visible.slice(-3000), - elapsedMs: Date.now() - startedAt, + elapsedMs: driver.now() - startedAt, }); } } @@ -5017,7 +5044,7 @@ export async function runPlanSkillFloorCheck(opts: { outcome: 'plan_ready', summary: 'agent reached plan_ready without a qualifying finding question', evidence: visible.slice(-3000), - elapsedMs: Date.now() - startedAt, + elapsedMs: driver.now() - startedAt, }); } } @@ -5029,7 +5056,7 @@ export async function runPlanSkillFloorCheck(opts: { ? `no qualifying finding question within ${timeoutMs}ms` : `seeded target delivery unavailable within ${timeoutMs}ms: ${targetDelivery.reason ?? targetDelivery.status}`, evidence: session.visibleSince(since).slice(-3000), - elapsedMs: Date.now() - startedAt, + elapsedMs: driver.now() - startedAt, }); } catch (error) { floorFailed = true; diff --git a/test/helpers/pty/fake-session.ts b/test/helpers/pty/fake-session.ts new file mode 100644 index 000000000..724880af5 --- /dev/null +++ b/test/helpers/pty/fake-session.ts @@ -0,0 +1,149 @@ +/** + * Test-only fake PTY session driver. Feeds scripted frames on an injectable + * clock through the runners' launch seam (`PtyDriver`), so the plan-skill + * runners can be exercised without a CLI, a PTY, a model or real timers. + * + * A frame is shown when its trigger fires: `onInput` (the runner sent a + * matching input) or `afterMs` of fake time since the previous frame. Shown + * frames accumulate in the raw/visible history; the latest one is the + * current viewport. Never import this from production helpers. + */ +import type { ClaudePtyOptions, ClaudePtySession, PtyDriver } from '../claude-pty-runner'; + +export interface FakeSessionContext { + /** Options the runner passed to `driver.launch`. */ + launch: ClaudePtyOptions; + /** Current fake wall clock (ms since epoch). */ + now: number; + /** Isolated CLAUDE_CONFIG_DIR stand-in, or null. */ + configDir: string | null; + sent: readonly string[]; +} + +export interface FakeFrame { + /** Viewport text shown by this frame; also appended to the session history. */ + screen: string; + /** Show when the runner sends this exact input (string) or a matching one (RegExp). */ + onInput?: string | RegExp; + /** Otherwise show after this much fake time since the previous frame. Default 0. */ + afterMs?: number; + /** Side effect when shown, e.g. appending native transcript records. */ + effect?: (ctx: FakeSessionContext) => void; + /** End the fake process with this exit code when shown. */ + exit?: number; +} + +export interface FakePtyScript { + frames: FakeFrame[]; + /** Directory exposed as `session.hermeticConfigDir`. Default null. */ + configDir?: string | null; + /** Fake wall-clock start. Default: the real clock when the driver is created. */ + startAt?: number; + /** Extra session fields (e.g. pending hook files) merged into the fake session. */ + session?: Partial; +} + +export interface FakePtyDriver { + driver: PtyDriver; + /** Every input the runner sent, in order. */ + sent: string[]; + /** Every launch request, in order. */ + launches: ClaudePtyOptions[]; + /** Number of `close()` calls. */ + closes(): number; + now(): number; +} + +export function createFakePtyDriver(script: FakePtyScript): FakePtyDriver { + const startAt = script.startAt ?? Date.now(); + const configDir = script.configDir ?? null; + const sent: string[] = []; + const launches: ClaudePtyOptions[] = []; + let now = startAt; + let closes = 0; + let next = 0; + let lastShownAt = startAt; + let history = ''; + let screen = ''; + let lastMark = 0; + let exitCode: number | null = null; + let launched: ClaudePtyOptions | undefined; + + const context = (): FakeSessionContext => ({ launch: launched!, now, configDir, sent }); + const show = (frame: FakeFrame) => { + next++; + lastShownAt = now; + screen = frame.screen; + history += (history ? '\n' : '') + frame.screen; + frame.effect?.(context()); + if (frame.exit !== undefined) exitCode = frame.exit; + }; + const dueAt = (frame: FakeFrame | undefined) => + !launched || !frame || frame.onInput !== undefined || exitCode !== null ? Infinity : lastShownAt + (frame.afterMs ?? 0); + const showDue = () => { while (dueAt(script.frames[next]) <= now) show(script.frames[next]!); }; + const advance = (ms: number) => { + const target = now + Math.max(0, ms); + while (dueAt(script.frames[next]) <= target) { now = Math.max(now, dueAt(script.frames[next])); show(script.frames[next]!); } + now = target; + }; + + const session: ClaudePtySession = { + send(data: string) { + if (exitCode !== null) return; + sent.push(data); + const frame = script.frames[next]; + const trigger = frame?.onInput; + if (trigger !== undefined && (typeof trigger === 'string' ? trigger === data : trigger.test(data))) show(frame!); + showDue(); + }, + sendKey(key) { + const keys = { Enter: '\r', Up: '\x1b[A', Down: '\x1b[B', Esc: '\x1b', Tab: '\t', ShiftTab: '\x1b[Z', CtrlC: '\x03' }; + session.send(keys[key]); + }, + rawOutput: () => history, + visibleText: () => history, + currentScreen: async () => screen, + currentScreenFrame: async () => ({ text: screen, rawEnd: history.length, styledText: [] }), + mark: () => (lastMark = history.length), + visibleSince: (marker?: number) => history.slice(marker ?? lastMark), + async waitForOutput(since: number, timeoutMs: number) { + if (history.length > since || exitCode !== null) return; + const until = now + timeoutMs; + while (history.length <= since && exitCode === null && now < until) advance(Math.min(until, dueAt(script.frames[next])) - now); + }, + async waitForAny(patterns, opts) { + const until = now + (opts?.timeoutMs ?? 60_000); + for (;;) { + if (exitCode !== null) throw new Error(`claude exited (code=${exitCode}) before any pattern matched.`); + const visible = opts?.since !== undefined ? history.slice(opts.since) : history; + for (const matched of patterns) { + const index = typeof matched === 'string' ? visible.indexOf(matched) : visible.search(matched); + if (index >= 0) return { matched, index }; + } + if (now >= until) throw new Error(`Timed out after ${opts?.timeoutMs ?? 60_000}ms waiting for fake output`); + advance(Math.min(opts?.pollMs ?? 250, until - now)); + } + }, + async waitFor(pattern, opts) { await session.waitForAny([pattern], opts); }, + pid: () => undefined, + exited: () => exitCode !== null, + exitCode: () => exitCode, + hermeticConfigDir: configDir, + async close() { closes++; }, + ...script.session, + }; + + const driver: PtyDriver = { + async launch(opts) { + launches.push(opts); + launched = opts; + lastShownAt = now; + showDue(); + return session; + }, + now: () => now, + monotonic: () => now - startAt, + async sleep(ms: number) { advance(ms); }, + }; + return { driver, sent, launches, closes: () => closes, now: () => now }; +} diff --git a/test/helpers/touchfiles-data.ts b/test/helpers/touchfiles-data.ts index 10acaa314..24a7a71d7 100644 --- a/test/helpers/touchfiles-data.ts +++ b/test/helpers/touchfiles-data.ts @@ -196,7 +196,7 @@ export const E2E_TOUCHFILES: Record = { "test/fixtures/plan-scope-target-aw.json", - 'test/fixtures/auto-decide-saved-ai.json', 'test/fixtures/auto-decide-retry-ai.json','bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-ceo-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-plan-ceo-plan-mode.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/plan-scope-selection.ts', 'test/helpers/native-auto-decide.ts', 'test/fixtures/auto-decide-current-declaration-6aef.json', 'test/fixtures/auto-decide-explanatory-mode-043a.json', 'test/fixtures/auto-decide-explanatory-mode-749df.json', 'test/fixtures/auto-decide-structured-77.json', 'test/helpers/auto-decision-state.ts', 'test/fixtures/auto-decide-state-cab3.json', 'bin/gstack-question-log', 'bin/gstack-question-preference', 'test/helpers/fake-plan-seed.ts', 'test/helpers/plan-seed-submission.ts', 'test/fixtures/plan-seed-cli.ts', 'test/fixtures/native-auto-decide-ag.json', 'test/fixtures/eng-seeded-completion-ai.json', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**', + 'test/fixtures/auto-decide-saved-ai.json', 'test/fixtures/auto-decide-retry-ai.json','bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-ceo-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-plan-ceo-plan-mode.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/plan-scope-selection.ts', 'test/helpers/native-auto-decide.ts', 'test/fixtures/auto-decide-current-declaration-6aef.json', 'test/fixtures/auto-decide-explanatory-mode-043a.json', 'test/fixtures/auto-decide-explanatory-mode-749df.json', 'test/fixtures/auto-decide-structured-77.json', 'test/helpers/auto-decision-state.ts', 'test/fixtures/auto-decide-state-cab3.json', 'bin/gstack-question-log', 'bin/gstack-question-preference', 'test/helpers/fake-plan-seed.ts', 'test/helpers/plan-seed-submission.ts', 'test/fixtures/plan-seed-cli.ts', 'test/fixtures/native-auto-decide-ag.json', 'test/fixtures/eng-seeded-completion-ai.json', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**', 'test/fixtures/design-scope-announcement-ao.json', 'test/fixtures/design-scope-declaration-ak.json', @@ -217,7 +217,7 @@ export const E2E_TOUCHFILES: Record = { "test/fixtures/plan-scope-recovery-av.json", - 'test/fixtures/auto-decide-saved-ai.json', 'test/fixtures/auto-decide-retry-ai.json','bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-eng-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-plan-eng-plan-mode.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/plan-scope-selection.ts', 'test/fixtures/design-plan-scope-ag.json', 'test/fixtures/design-scope-selection-aj.json', 'test/helpers/native-auto-decide.ts', 'test/fixtures/auto-decide-current-declaration-6aef.json', 'test/fixtures/auto-decide-explanatory-mode-043a.json', 'test/fixtures/auto-decide-explanatory-mode-749df.json', 'test/fixtures/auto-decide-structured-77.json', 'test/helpers/auto-decision-state.ts', 'test/fixtures/auto-decide-state-cab3.json', 'bin/gstack-question-log', 'bin/gstack-question-preference', 'test/helpers/fake-plan-seed.ts', 'test/fixtures/native-auto-decide-ag.json', 'test/fixtures/eng-seeded-completion-ai.json', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**', + 'test/fixtures/auto-decide-saved-ai.json', 'test/fixtures/auto-decide-retry-ai.json','bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-eng-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-plan-eng-plan-mode.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/plan-scope-selection.ts', 'test/fixtures/design-plan-scope-ag.json', 'test/fixtures/design-scope-selection-aj.json', 'test/helpers/native-auto-decide.ts', 'test/fixtures/auto-decide-current-declaration-6aef.json', 'test/fixtures/auto-decide-explanatory-mode-043a.json', 'test/fixtures/auto-decide-explanatory-mode-749df.json', 'test/fixtures/auto-decide-structured-77.json', 'test/helpers/auto-decision-state.ts', 'test/fixtures/auto-decide-state-cab3.json', 'bin/gstack-question-log', 'bin/gstack-question-preference', 'test/helpers/fake-plan-seed.ts', 'test/fixtures/native-auto-decide-ag.json', 'test/fixtures/eng-seeded-completion-ai.json', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**', 'test/fixtures/design-scope-announcement-ao.json', 'test/fixtures/design-scope-declaration-ak.json', @@ -242,7 +242,7 @@ export const E2E_TOUCHFILES: Record = { "test/fixtures/plan-scope-recovery-av.json", "test/fixtures/design-scope-checkpoint-at.json", - 'test/fixtures/auto-decide-saved-ai.json', 'test/fixtures/auto-decide-retry-ai.json','bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-design-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-plan-design-plan-mode.test.ts', 'test/skill-e2e-design.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/plan-scope-selection.ts', 'test/fixtures/design-plan-scope-ag.json', 'test/fixtures/design-scope-selection-aj.json', 'test/helpers/native-auto-decide.ts', 'test/fixtures/auto-decide-current-declaration-6aef.json', 'test/fixtures/auto-decide-explanatory-mode-043a.json', 'test/fixtures/auto-decide-explanatory-mode-749df.json', 'test/fixtures/auto-decide-structured-77.json', 'test/helpers/auto-decision-state.ts', 'test/fixtures/auto-decide-state-cab3.json', 'bin/gstack-question-log', 'bin/gstack-question-preference', 'test/helpers/fake-plan-seed.ts', 'test/fixtures/native-auto-decide-ag.json', 'test/fixtures/eng-seeded-completion-ai.json', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**', + 'test/fixtures/auto-decide-saved-ai.json', 'test/fixtures/auto-decide-retry-ai.json','bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-design-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-plan-design-plan-mode.test.ts', 'test/skill-e2e-design.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/plan-scope-selection.ts', 'test/fixtures/design-plan-scope-ag.json', 'test/fixtures/design-scope-selection-aj.json', 'test/helpers/native-auto-decide.ts', 'test/fixtures/auto-decide-current-declaration-6aef.json', 'test/fixtures/auto-decide-explanatory-mode-043a.json', 'test/fixtures/auto-decide-explanatory-mode-749df.json', 'test/fixtures/auto-decide-structured-77.json', 'test/helpers/auto-decision-state.ts', 'test/fixtures/auto-decide-state-cab3.json', 'bin/gstack-question-log', 'bin/gstack-question-preference', 'test/helpers/fake-plan-seed.ts', 'test/fixtures/native-auto-decide-ag.json', 'test/fixtures/eng-seeded-completion-ai.json', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**', 'test/fixtures/design-scope-announcement-ao.json', 'test/fixtures/design-scope-declaration-ak.json', @@ -260,7 +260,7 @@ export const E2E_TOUCHFILES: Record = { "test/fixtures/plan-scope-target-aw.json", - 'test/fixtures/auto-decide-saved-ai.json', 'test/fixtures/auto-decide-retry-ai.json','bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-devex-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-plan-devex-plan-mode.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/plan-scope-selection.ts', 'test/helpers/native-auto-decide.ts', 'test/fixtures/auto-decide-current-declaration-6aef.json', 'test/fixtures/auto-decide-explanatory-mode-043a.json', 'test/fixtures/auto-decide-explanatory-mode-749df.json', 'test/fixtures/auto-decide-structured-77.json', 'test/helpers/auto-decision-state.ts', 'test/fixtures/auto-decide-state-cab3.json', 'bin/gstack-question-log', 'bin/gstack-question-preference', 'test/helpers/fake-plan-seed.ts', 'test/helpers/plan-seed-submission.ts', 'test/fixtures/plan-seed-cli.ts', 'test/fixtures/native-auto-decide-ag.json', 'test/fixtures/eng-seeded-completion-ai.json', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**', + 'test/fixtures/auto-decide-saved-ai.json', 'test/fixtures/auto-decide-retry-ai.json','bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-devex-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-plan-devex-plan-mode.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/plan-scope-selection.ts', 'test/helpers/native-auto-decide.ts', 'test/fixtures/auto-decide-current-declaration-6aef.json', 'test/fixtures/auto-decide-explanatory-mode-043a.json', 'test/fixtures/auto-decide-explanatory-mode-749df.json', 'test/fixtures/auto-decide-structured-77.json', 'test/helpers/auto-decision-state.ts', 'test/fixtures/auto-decide-state-cab3.json', 'bin/gstack-question-log', 'bin/gstack-question-preference', 'test/helpers/fake-plan-seed.ts', 'test/helpers/plan-seed-submission.ts', 'test/fixtures/plan-seed-cli.ts', 'test/fixtures/native-auto-decide-ag.json', 'test/fixtures/eng-seeded-completion-ai.json', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**', 'test/fixtures/design-scope-announcement-ao.json', 'test/fixtures/design-scope-declaration-ak.json', @@ -285,7 +285,7 @@ export const E2E_TOUCHFILES: Record = { "test/fixtures/plan-scope-recovery-av.json", "test/fixtures/design-scope-checkpoint-at.json", - 'test/fixtures/auto-decide-saved-ai.json', 'test/fixtures/auto-decide-retry-ai.json','bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-ceo-review/**', 'plan-eng-review/**', 'plan-design-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/preamble.ts', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-plan-mode-no-op.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/plan-scope-selection.ts', 'test/helpers/native-auto-decide.ts', 'test/fixtures/auto-decide-current-declaration-6aef.json', 'test/fixtures/auto-decide-explanatory-mode-043a.json', 'test/fixtures/auto-decide-explanatory-mode-749df.json', 'test/fixtures/auto-decide-structured-77.json', 'test/helpers/auto-decision-state.ts', 'test/fixtures/auto-decide-state-cab3.json', 'bin/gstack-question-log', 'bin/gstack-question-preference', 'test/helpers/fake-plan-seed.ts', 'test/fixtures/native-auto-decide-ag.json', 'test/fixtures/eng-seeded-completion-ai.json', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**', + 'test/fixtures/auto-decide-saved-ai.json', 'test/fixtures/auto-decide-retry-ai.json','bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-ceo-review/**', 'plan-eng-review/**', 'plan-design-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/preamble.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-plan-mode-no-op.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/plan-scope-selection.ts', 'test/helpers/native-auto-decide.ts', 'test/fixtures/auto-decide-current-declaration-6aef.json', 'test/fixtures/auto-decide-explanatory-mode-043a.json', 'test/fixtures/auto-decide-explanatory-mode-749df.json', 'test/fixtures/auto-decide-structured-77.json', 'test/helpers/auto-decision-state.ts', 'test/fixtures/auto-decide-state-cab3.json', 'bin/gstack-question-log', 'bin/gstack-question-preference', 'test/helpers/fake-plan-seed.ts', 'test/fixtures/native-auto-decide-ag.json', 'test/fixtures/eng-seeded-completion-ai.json', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**', 'test/fixtures/design-scope-announcement-ao.json', 'test/fixtures/design-scope-declaration-ak.json', @@ -309,7 +309,7 @@ export const E2E_TOUCHFILES: Record = { 'test/fixtures/auto-decide-recommendation-361c.json', 'test/fixtures/auto-decide-target-361c.json', - 'test/fixtures/native-auto-decide-ag.json', 'test/helpers/native-auto-decide.ts', 'test/fixtures/auto-decide-current-declaration-6aef.json', 'test/fixtures/auto-decide-explanatory-mode-043a.json', 'test/fixtures/auto-decide-explanatory-mode-749df.json', 'bin/gstack-skill-start', 'bin/gstack-skill-end', 'office-hours/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-office-hours-auto-mode.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', + 'test/fixtures/native-auto-decide-ag.json', 'test/helpers/native-auto-decide.ts', 'test/fixtures/auto-decide-current-declaration-6aef.json', 'test/fixtures/auto-decide-explanatory-mode-043a.json', 'test/fixtures/auto-decide-explanatory-mode-749df.json', 'bin/gstack-skill-start', 'bin/gstack-skill-end', 'office-hours/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-office-hours-auto-mode.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/fixtures/pty-companion-cli.ts', 'lib/fs-atomic.ts', 'test/helpers/plan-skill-questions.ts', 'test/fixtures/eng-auq-validation-error.json', 'test/fixtures/bash-directory-permission.json', 'test/fixtures/design-tasks-bash-permission.json', 'test/fixtures/read-permission.json', 'test/fixtures/ceo-split-e5-numbered-description-491.json', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/skill-census.ts', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/owned-claude-transcript.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/plan-count-file-permission.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-count-transcript.ts', 'test/helpers/plan-floor-review.ts', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts', 'test/helpers/pty-screen.ts'], 'office-hours-phase4-fork': [ 'bin/gstack-skill-start', 'bin/gstack-skill-end', 'office-hours/**', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/question-tuning.ts', 'test/helpers/llm-judge.ts', 'test/skill-e2e-office-hours-phase4.test.ts' ], 'llm-judge-recommendation': ['codex/**', 'test/helpers/llm-judge.ts', 'test/llm-judge-recommendation.test.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'codex/SKILL.md.tmpl', 'scripts/resolvers/review.ts'], @@ -328,7 +328,7 @@ export const E2E_TOUCHFILES: Record = { 'test/fixtures/autoplan-public-narration-ad.json', 'test/fixtures/auto-decide-completed-mode-f359.json', 'test/helpers/plan-count-transcript.ts', 'test/fixtures/plan-count-cross-cwd-ancestry-0bcd.json', - 'test/fixtures/auto-decide-saved-ai.json', 'test/fixtures/auto-decide-retry-ai.json','bin/gstack-skill-start', 'bin/gstack-skill-end', 'bin/gstack-session-kind', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-preamble-bash.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'plan-ceo-review/**', 'bin/gstack-question-preference', 'bin/gstack-config', 'bin/gstack-slug', 'hosts/claude/hooks/question-preference-hook.ts', 'hosts/claude/hooks/spawned-directive.ts', 'lib/is-conductor.ts', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-auto-decide-preserved.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/native-auto-decide.ts', 'test/fixtures/auto-decide-current-declaration-6aef.json', 'test/fixtures/auto-decide-explanatory-mode-043a.json', 'test/fixtures/auto-decide-explanatory-mode-749df.json', 'test/fixtures/auto-decide-structured-77.json', 'test/helpers/auto-decision-state.ts', 'test/fixtures/auto-decide-state-cab3.json', 'bin/gstack-question-log', 'test/helpers/fake-plan-seed.ts', 'test/helpers/plan-seed-submission.ts', 'test/fixtures/plan-seed-cli.ts', 'test/fixtures/native-auto-decide-ag.json', 'test/fixtures/eng-seeded-completion-ai.json', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**', + 'test/fixtures/auto-decide-saved-ai.json', 'test/fixtures/auto-decide-retry-ai.json','bin/gstack-skill-start', 'bin/gstack-skill-end', 'bin/gstack-session-kind', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-preamble-bash.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'plan-ceo-review/**', 'bin/gstack-question-preference', 'bin/gstack-config', 'bin/gstack-slug', 'hosts/claude/hooks/question-preference-hook.ts', 'hosts/claude/hooks/spawned-directive.ts', 'lib/is-conductor.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-auto-decide-preserved.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/native-auto-decide.ts', 'test/fixtures/auto-decide-current-declaration-6aef.json', 'test/fixtures/auto-decide-explanatory-mode-043a.json', 'test/fixtures/auto-decide-explanatory-mode-749df.json', 'test/fixtures/auto-decide-structured-77.json', 'test/helpers/auto-decision-state.ts', 'test/fixtures/auto-decide-state-cab3.json', 'bin/gstack-question-log', 'test/helpers/fake-plan-seed.ts', 'test/helpers/plan-seed-submission.ts', 'test/fixtures/plan-seed-cli.ts', 'test/fixtures/native-auto-decide-ag.json', 'test/fixtures/eng-seeded-completion-ai.json', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**', 'test/helpers/plan-count-fixture.ts', 'test/fixtures/pty-companion-cli.ts', 'lib/fs-atomic.ts', 'test/fixtures/auto-decide-mode-selector-749df.json', 'lib/redact-engine.ts', 'lib/redact-patterns.ts', 'test/helpers/ceo-finding-fixture.ts', 'test/helpers/owned-claude-transcript.ts', 'test/helpers/plan-skill-questions.ts', 'test/fixtures/eng-auq-validation-error.json', 'test/fixtures/bash-directory-permission.json', 'test/fixtures/design-tasks-bash-permission.json', 'test/fixtures/read-permission.json', 'test/fixtures/ceo-split-e5-numbered-description-491.json', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/skill-census.ts', 'scripts/resolvers/tasks-section.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/plan-count-file-permission.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-floor-review.ts', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-scope-selection.ts'], @@ -344,7 +344,7 @@ export const E2E_TOUCHFILES: Record = { 'test/helpers/auq-native-capture.ts', 'test/helpers/hermetic-env.ts', 'test/helpers/eval-store.ts', 'lib/claude-bin.ts', 'lib/eval-model.ts', - 'scripts/resolvers/tasks-section.ts', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/native-auto-decide.ts', 'test/helpers/owned-claude-transcript.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/plan-count-file-permission.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-count-transcript.ts', 'test/helpers/plan-floor-review.ts', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/plan-skill-questions.ts', 'test/helpers/pty-screen.ts', 'test/helpers/pty-trust-dialog.ts', 'test/helpers/skill-census.ts'], + 'scripts/resolvers/tasks-section.ts', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/native-auto-decide.ts', 'test/helpers/owned-claude-transcript.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/plan-count-file-permission.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-count-transcript.ts', 'test/helpers/plan-floor-review.ts', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/plan-skill-questions.ts', 'test/helpers/pty-screen.ts', 'test/helpers/pty-trust-dialog.ts', 'test/helpers/skill-census.ts'], 'plan-ceo-mode-routing': [ 'lib/claude-public-transcript.ts', @@ -358,7 +358,7 @@ export const E2E_TOUCHFILES: Record = { 'test/fixtures/ceo-mode-pending-submit.json', "test/fixtures/plan-count-cross-cwd-ancestry-0bcd.json", - "test/fixtures/ceo-mode-colon-at.json", 'plan-ceo-review/**', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/helpers/ceo-mode-option.ts', 'test/fixtures/ceo-expansion-disposition-77.json', 'test/helpers/plan-count-transcript.ts', 'test/fixtures/autoplan-public-narration-ad.json', 'test/helpers/plan-count-fixture.ts', 'test/fixtures/ceo-prerequisite-n-call.json', 'test/fixtures/eng-prerequisite-77.json', 'test/skill-e2e-plan-ceo-mode-routing.test.ts', 'test/fixtures/ceo-hold-posture-l.json', 'test/fixtures/ceo-mode-labels-l.json', 'test/fixtures/ceo-checkbox-l.screen.txt', 'test/fixtures/ceo-mode-prerequisite-o-calls.json', 'test/fixtures/ceo-mode-prerequisite-q-call.json', 'test/fixtures/ceo-preview-u-call.json', 'test/fixtures/ceo-preview-u-screen.txt', 'test/fixtures/design-preview-v-screen.txt', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/fixtures/ceo-mode-preview-aa-screen.txt', 'test/fixtures/ceo-count-mode-preview-aa-screen.txt', 'test/fixtures/ceo-expansion-auq-ac.json', 'test/helpers/plan-count-pending-question.ts', 'test/fixtures/ceo-barless-submit-ac.json', 'test/fixtures/pending-question-completion-ad.json', 'test/fixtures/ceo-mode-posture-ad.json', 'test/fixtures/ceo-mode-full-ad.json', 'test/fixtures/ceo-prerequisite-ad-v2.json', 'test/fixtures/ceo-hold-posture-ag.json', + "test/fixtures/ceo-mode-colon-at.json", 'plan-ceo-review/**', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/helpers/ceo-mode-option.ts', 'test/fixtures/ceo-expansion-disposition-77.json', 'test/helpers/plan-count-transcript.ts', 'test/fixtures/autoplan-public-narration-ad.json', 'test/helpers/plan-count-fixture.ts', 'test/fixtures/ceo-prerequisite-n-call.json', 'test/fixtures/eng-prerequisite-77.json', 'test/skill-e2e-plan-ceo-mode-routing.test.ts', 'test/fixtures/ceo-hold-posture-l.json', 'test/fixtures/ceo-mode-labels-l.json', 'test/fixtures/ceo-checkbox-l.screen.txt', 'test/fixtures/ceo-mode-prerequisite-o-calls.json', 'test/fixtures/ceo-mode-prerequisite-q-call.json', 'test/fixtures/ceo-preview-u-call.json', 'test/fixtures/ceo-preview-u-screen.txt', 'test/fixtures/design-preview-v-screen.txt', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/fixtures/ceo-mode-preview-aa-screen.txt', 'test/fixtures/ceo-count-mode-preview-aa-screen.txt', 'test/fixtures/ceo-expansion-auq-ac.json', 'test/helpers/plan-count-pending-question.ts', 'test/fixtures/ceo-barless-submit-ac.json', 'test/fixtures/pending-question-completion-ad.json', 'test/fixtures/ceo-mode-posture-ad.json', 'test/fixtures/ceo-mode-full-ad.json', 'test/fixtures/ceo-prerequisite-ad-v2.json', 'test/fixtures/ceo-hold-posture-ag.json', "test/fixtures/ceo-hold-commitment-ar.json", 'test/fixtures/pty-companion-cli.ts', 'lib/fs-atomic.ts', 'test/helpers/ceo-finding-fixture.ts', 'test/helpers/plan-skill-questions.ts', 'test/fixtures/eng-auq-validation-error.json', 'test/fixtures/bash-directory-permission.json', 'test/fixtures/design-tasks-bash-permission.json', 'test/fixtures/read-permission.json', 'test/fixtures/ceo-split-e5-numbered-description-491.json', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/skill-census.ts', 'test/helpers/owned-claude-transcript.ts', 'scripts/resolvers/tasks-section.ts', 'test/fixtures/ceo-expansion-pacing-77.json', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/native-auto-decide.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/plan-count-file-permission.ts', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-floor-review.ts', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts'], @@ -371,7 +371,7 @@ export const E2E_TOUCHFILES: Record = { "test/fixtures/plan-scope-recovery-av.json", - "test/fixtures/design-scope-checkpoint-at.json",'plan-design-review/**', 'test/fixtures/plans/ui-heavy-feature.md', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-plan-design-with-ui.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', + "test/fixtures/design-scope-checkpoint-at.json",'plan-design-review/**', 'test/fixtures/plans/ui-heavy-feature.md', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-plan-design-with-ui.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', "scripts/resolvers/preamble/generate-preamble-bash.ts", "scripts/resolvers/preamble/generate-completion-status.ts", @@ -392,11 +392,11 @@ export const E2E_TOUCHFILES: Record = { 'lib/eval-model.ts' ], 'ship-section-loading': [ 'ship/**', 'scripts/resolvers/sections.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/auq-sdk-capture.ts', 'test/helpers/session-runner.ts', 'test/skill-e2e-ship-section-loading.test.ts', - 'scripts/resolvers/testing.ts', 'test/helpers/agent-sdk-runner.ts', 'test/helpers/auq-native-capture.ts', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/llm-judge.ts', 'test/helpers/native-auto-decide.ts', 'test/helpers/owned-claude-transcript.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/plan-count-file-permission.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-count-transcript.ts', 'test/helpers/plan-floor-review.ts', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/plan-skill-questions.ts', 'test/helpers/pty-screen.ts', 'test/helpers/pty-trust-dialog.ts', 'test/helpers/skill-census.ts'], + 'scripts/resolvers/testing.ts', 'test/helpers/agent-sdk-runner.ts', 'test/helpers/auq-native-capture.ts', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/llm-judge.ts', 'test/helpers/native-auto-decide.ts', 'test/helpers/owned-claude-transcript.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/plan-count-file-permission.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-count-transcript.ts', 'test/helpers/plan-floor-review.ts', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/plan-skill-questions.ts', 'test/helpers/pty-screen.ts', 'test/helpers/pty-trust-dialog.ts', 'test/helpers/skill-census.ts'], 'plan-ceo-section-loading': [ 'test/fixtures/ceo-fill-lifetime.json', 'plan-ceo-review/**', 'scripts/resolvers/sections.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/auq-sdk-capture.ts', 'test/helpers/session-runner.ts', 'test/helpers/ceo-section-loading-fixture.ts', 'test/skill-e2e-plan-ceo-review-section-loading.test.ts', 'test/fixtures/ceo-section-loading-l-report.md', 'test/fixtures/ceo-section-loading-q-report.md', 'test/fixtures/ceo-section-r-rejected-report.md', 'test/fixtures/ceo-section-s-trace-report.md', 'test/fixtures/ceo-section-u-report.md', 'test/fixtures/ceo-section-y-report.md', 'test/fixtures/ceo-section-aa-report.md', 'test/fixtures/sdk-stale-table-ad-v3.json', 'test/fixtures/sdk-ordering-ae.json', 'test/fixtures/sdk-columnar-af.json', 'test/fixtures/sdk-order-b-ag.json', 'test/fixtures/sdk-schedule-continuation-ah.json', 'test/fixtures/sdk-original-order-ai.json', 'test/fixtures/sdk-compact-sequence-aj.json', "test/fixtures/sdk-reported-coordination-ar.md", "test/fixtures/sdk-ordered-schedule-ar.md", - 'scripts/resolvers/review.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/tasks-section.ts', 'test/helpers/agent-sdk-runner.ts', 'test/helpers/auq-native-capture.ts', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/llm-judge.ts', 'test/helpers/native-auto-decide.ts', 'test/helpers/owned-claude-transcript.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/plan-count-file-permission.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-count-transcript.ts', 'test/helpers/plan-floor-review.ts', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/plan-skill-questions.ts', 'test/helpers/pty-screen.ts', 'test/helpers/pty-trust-dialog.ts', 'test/helpers/skill-census.ts'], + 'scripts/resolvers/review.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/tasks-section.ts', 'test/helpers/agent-sdk-runner.ts', 'test/helpers/auq-native-capture.ts', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/llm-judge.ts', 'test/helpers/native-auto-decide.ts', 'test/helpers/owned-claude-transcript.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/plan-count-file-permission.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-count-transcript.ts', 'test/helpers/plan-floor-review.ts', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/plan-skill-questions.ts', 'test/helpers/pty-screen.ts', 'test/helpers/pty-trust-dialog.ts', 'test/helpers/skill-census.ts'], // Data-driven behavioral guard for the 'plan'/'prompt' carves (eng, design, // devex, office-hours + future PR2 carves). One file iterating CARVE_GUARDS; // the selector sets GSTACK_CARVE_SKILL= to scope cost to the changed @@ -412,7 +412,7 @@ export const E2E_TOUCHFILES: Record = { "scripts/resolvers/preamble/generate-preamble-bash.ts", "scripts/resolvers/preamble/generate-completion-status.ts", - 'test/carve-section-loading*.test.ts', 'test/helpers/carve-section-case.ts', 'test/carve-section-loading-browse.test.ts', 'test/carve-section-loading-codex.test.ts', 'test/carve-section-loading-design-consultation.test.ts', 'test/carve-section-loading-design-html.test.ts', 'test/carve-section-loading-design-shotgun.test.ts', 'test/carve-section-loading-document-release.test.ts', 'test/carve-section-loading-land-and-deploy.test.ts', 'test/carve-section-loading-plan-design-review.test.ts', 'test/carve-section-loading-plan-devex-review.test.ts', 'test/carve-section-loading-plan-eng-review.test.ts', 'test/carve-section-loading-qa.test.ts', 'test/carve-section-loading-retro.test.ts', 'test/carve-section-loading-review.test.ts', 'test/carve-section-loading-setup-gbrain.test.ts', 'test/carve-section-loading-spec.test.ts', 'test/fixtures/design-html-section-complete.md', 'scripts/resolvers/testing.ts', 'test/helpers/carve-plan-fixture.ts', 'test/fixtures/carve-existing-repository/**', 'scripts/resolvers/review.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'test/helpers/agent-sdk-runner.ts', 'test/helpers/auq-native-capture.ts', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/capture-parity-baseline.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/llm-judge.ts', 'test/helpers/native-auto-decide.ts', 'test/helpers/owned-claude-transcript.ts', 'test/helpers/parity-harness.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/plan-count-file-permission.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-count-transcript.ts', 'test/helpers/plan-floor-review.ts', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/plan-skill-questions.ts', 'test/helpers/pty-screen.ts', 'test/helpers/pty-trust-dialog.ts', 'test/helpers/skill-census.ts'], + 'test/carve-section-loading*.test.ts', 'test/helpers/carve-section-case.ts', 'test/carve-section-loading-browse.test.ts', 'test/carve-section-loading-codex.test.ts', 'test/carve-section-loading-design-consultation.test.ts', 'test/carve-section-loading-design-html.test.ts', 'test/carve-section-loading-design-shotgun.test.ts', 'test/carve-section-loading-document-release.test.ts', 'test/carve-section-loading-land-and-deploy.test.ts', 'test/carve-section-loading-plan-design-review.test.ts', 'test/carve-section-loading-plan-devex-review.test.ts', 'test/carve-section-loading-plan-eng-review.test.ts', 'test/carve-section-loading-qa.test.ts', 'test/carve-section-loading-retro.test.ts', 'test/carve-section-loading-review.test.ts', 'test/carve-section-loading-setup-gbrain.test.ts', 'test/carve-section-loading-spec.test.ts', 'test/fixtures/design-html-section-complete.md', 'scripts/resolvers/testing.ts', 'test/helpers/carve-plan-fixture.ts', 'test/fixtures/carve-existing-repository/**', 'scripts/resolvers/review.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'test/helpers/agent-sdk-runner.ts', 'test/helpers/auq-native-capture.ts', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/capture-parity-baseline.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/llm-judge.ts', 'test/helpers/native-auto-decide.ts', 'test/helpers/owned-claude-transcript.ts', 'test/helpers/parity-harness.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/plan-count-file-permission.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-count-transcript.ts', 'test/helpers/plan-floor-review.ts', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/plan-skill-questions.ts', 'test/helpers/pty-screen.ts', 'test/helpers/pty-trust-dialog.ts', 'test/helpers/skill-census.ts'], // Per-finding AskUserQuestion count + review-report-at-bottom assertion. // Each test drives its skill end-to-end; touchfiles include preamble + @@ -435,7 +435,7 @@ export const E2E_TOUCHFILES: Record = { 'test/fixtures/ceo-report-permission-fb10.json', 'test/fixtures/plan-floor-permission-fb10.json', 'test/helpers/plan-count-file-permission.ts', 'test/fixtures/plan-edit-cropped-permission-1579.json', 'test/fixtures/plan-count-permission-target-ad-v2.json', 'test/fixtures/design-crop-gutter-ap.json', 'test/fixtures/plan-count-crop-ak.json', 'test/fixtures/plan-count-long-edit-0bcd.json', 'test/fixtures/plan-count-cropped-wrap-6714.json', 'scripts/resolvers/learnings.ts', - "test/fixtures/plan-scope-recovery-av.json", 'bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-eng-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-eng-finding-floor.test.ts', 'test/fixtures/eng-batching-t-calls.json', 'test/fixtures/eng-scope-y-calls.json', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/fixtures/eng-binding-z-calls.json', 'test/fixtures/eng-binding-retry-z-calls.json', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-artifacts.ts', + "test/fixtures/plan-scope-recovery-av.json", 'bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-eng-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-eng-finding-floor.test.ts', 'test/fixtures/eng-batching-t-calls.json', 'test/fixtures/eng-scope-y-calls.json', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/fixtures/eng-binding-z-calls.json', 'test/fixtures/eng-binding-retry-z-calls.json', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-artifacts.ts', "test/fixtures/eng-injected-export-aq.json", "test/fixtures/eng-library-hooks-aq.json", "scripts/resolvers/preamble/generate-preamble-bash.ts", @@ -448,7 +448,7 @@ export const E2E_TOUCHFILES: Record = { 'test/fixtures/plan-floor-routing-361c.json', - 'test/fixtures/ceo-report-permission-fb10.json', 'test/fixtures/plan-floor-permission-fb10.json', 'test/helpers/plan-count-file-permission.ts', 'test/fixtures/plan-edit-cropped-permission-1579.json', 'test/fixtures/plan-count-permission-target-ad-v2.json', 'test/fixtures/design-crop-gutter-ap.json', 'test/fixtures/plan-count-crop-ak.json', 'test/fixtures/plan-count-long-edit-0bcd.json', 'test/fixtures/plan-count-cropped-wrap-6714.json', 'bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-ceo-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-ceo-finding-floor.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-artifacts.ts', + 'test/fixtures/ceo-report-permission-fb10.json', 'test/fixtures/plan-floor-permission-fb10.json', 'test/helpers/plan-count-file-permission.ts', 'test/fixtures/plan-edit-cropped-permission-1579.json', 'test/fixtures/plan-count-permission-target-ad-v2.json', 'test/fixtures/design-crop-gutter-ap.json', 'test/fixtures/plan-count-crop-ak.json', 'test/fixtures/plan-count-long-edit-0bcd.json', 'test/fixtures/plan-count-cropped-wrap-6714.json', 'bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-ceo-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-ceo-finding-floor.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-artifacts.ts', 'test/fixtures/pty-companion-cli.ts', 'lib/fs-atomic.ts', 'test/helpers/plan-skill-questions.ts', 'test/fixtures/eng-auq-validation-error.json', 'test/fixtures/bash-directory-permission.json', 'test/fixtures/design-tasks-bash-permission.json', 'test/fixtures/read-permission.json', 'test/fixtures/ceo-split-e5-numbered-description-491.json', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/skill-census.ts', 'scripts/resolvers/tasks-section.ts', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/native-auto-decide.ts', 'test/helpers/owned-claude-transcript.ts', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-count-transcript.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts'], @@ -464,7 +464,7 @@ export const E2E_TOUCHFILES: Record = { 'test/fixtures/ceo-report-permission-fb10.json', 'test/fixtures/plan-floor-permission-fb10.json', 'test/helpers/plan-count-file-permission.ts', 'test/fixtures/plan-edit-cropped-permission-1579.json', 'test/fixtures/plan-count-permission-target-ad-v2.json', 'test/fixtures/design-crop-gutter-ap.json', 'test/fixtures/plan-count-crop-ak.json', 'test/fixtures/plan-count-long-edit-0bcd.json', 'test/fixtures/plan-count-cropped-wrap-6714.json', "test/fixtures/plan-scope-recovery-av.json", - "test/fixtures/design-scope-checkpoint-at.json",'bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-design-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-design-finding-floor.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-artifacts.ts', + "test/fixtures/design-scope-checkpoint-at.json",'bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-design-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-design-finding-floor.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-artifacts.ts', "scripts/resolvers/preamble/generate-preamble-bash.ts", @@ -479,7 +479,7 @@ export const E2E_TOUCHFILES: Record = { 'test/fixtures/plan-floor-routing-361c.json', - 'test/fixtures/ceo-report-permission-fb10.json', 'test/fixtures/plan-floor-permission-fb10.json', 'test/helpers/plan-count-file-permission.ts', 'test/fixtures/plan-edit-cropped-permission-1579.json', 'test/fixtures/plan-count-permission-target-ad-v2.json', 'test/fixtures/design-crop-gutter-ap.json', 'test/fixtures/plan-count-crop-ak.json', 'test/fixtures/plan-count-long-edit-0bcd.json', 'test/fixtures/plan-count-cropped-wrap-6714.json','bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-devex-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-devex-finding-floor.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-artifacts.ts', + 'test/fixtures/ceo-report-permission-fb10.json', 'test/fixtures/plan-floor-permission-fb10.json', 'test/helpers/plan-count-file-permission.ts', 'test/fixtures/plan-edit-cropped-permission-1579.json', 'test/fixtures/plan-count-permission-target-ad-v2.json', 'test/fixtures/design-crop-gutter-ap.json', 'test/fixtures/plan-count-crop-ak.json', 'test/fixtures/plan-count-long-edit-0bcd.json', 'test/fixtures/plan-count-cropped-wrap-6714.json','bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-devex-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-devex-finding-floor.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-artifacts.ts', 'test/fixtures/pty-companion-cli.ts', 'lib/fs-atomic.ts', 'test/helpers/plan-skill-questions.ts', 'test/fixtures/eng-auq-validation-error.json', 'test/fixtures/bash-directory-permission.json', 'test/fixtures/design-tasks-bash-permission.json', 'test/fixtures/read-permission.json', 'test/fixtures/ceo-split-e5-numbered-description-491.json', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/skill-census.ts', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/native-auto-decide.ts', 'test/helpers/owned-claude-transcript.ts', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-count-transcript.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts'], // Multi-finding batching regression — periodic tier complement to the @@ -516,7 +516,7 @@ export const E2E_TOUCHFILES: Record = { "test/fixtures/plan-scope-recovery-av.json", "test/fixtures/batching-permission-at.json", - "test/fixtures/eng-declared-retry-at.json", 'test/fixtures/design-crop-gutter-ap.json', 'bin/gstack-config', 'bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-eng-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/helpers/plan-count-transcript.ts', 'test/fixtures/autoplan-public-narration-ad.json', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/eval-store.ts', 'test/helpers/plan-count-fixture.ts', 'test/fixtures/ceo-prerequisite-n-call.json', 'test/fixtures/eng-prerequisite-77.json', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-eng-multi-finding-batching.test.ts', 'test/fixtures/ceo-checkbox-l.screen.txt', 'test/fixtures/eng-devex-s-first-calls.json', 'test/fixtures/eng-devex-s-retry-calls.json', 'test/helpers/plan-count-file-permission.ts', 'test/fixtures/plan-edit-cropped-permission-1579.json', 'test/fixtures/plan-count-edit-permission-t.json', 'test/fixtures/eng-batching-t-calls.json', 'test/fixtures/ceo-preview-u-call.json', 'test/fixtures/ceo-preview-u-screen.txt', 'test/fixtures/design-preview-v-screen.txt', 'test/fixtures/plan-count-owned-permission-v.json', 'test/fixtures/ceo-questionless-w-native.json', 'test/fixtures/eng-scope-y-calls.json', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/fixtures/eng-binding-z-calls.json', 'test/fixtures/eng-binding-retry-z-calls.json', 'test/fixtures/plan-count-permission-ac.json', 'test/fixtures/plan-count-permission-ad.json', 'test/fixtures/plan-count-permission-ae.json', 'test/fixtures/plan-count-permission-target-ad-v2.json', 'test/fixtures/eng-count-ad-v2.json', 'test/fixtures/eng-first-category-af.json', 'test/fixtures/plan-count-permission-ah.json', + "test/fixtures/eng-declared-retry-at.json", 'test/fixtures/design-crop-gutter-ap.json', 'bin/gstack-config', 'bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-eng-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/helpers/plan-count-transcript.ts', 'test/fixtures/autoplan-public-narration-ad.json', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/eval-store.ts', 'test/helpers/plan-count-fixture.ts', 'test/fixtures/ceo-prerequisite-n-call.json', 'test/fixtures/eng-prerequisite-77.json', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-eng-multi-finding-batching.test.ts', 'test/fixtures/ceo-checkbox-l.screen.txt', 'test/fixtures/eng-devex-s-first-calls.json', 'test/fixtures/eng-devex-s-retry-calls.json', 'test/helpers/plan-count-file-permission.ts', 'test/fixtures/plan-edit-cropped-permission-1579.json', 'test/fixtures/plan-count-edit-permission-t.json', 'test/fixtures/eng-batching-t-calls.json', 'test/fixtures/ceo-preview-u-call.json', 'test/fixtures/ceo-preview-u-screen.txt', 'test/fixtures/design-preview-v-screen.txt', 'test/fixtures/plan-count-owned-permission-v.json', 'test/fixtures/ceo-questionless-w-native.json', 'test/fixtures/eng-scope-y-calls.json', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/fixtures/eng-binding-z-calls.json', 'test/fixtures/eng-binding-retry-z-calls.json', 'test/fixtures/plan-count-permission-ac.json', 'test/fixtures/plan-count-permission-ad.json', 'test/fixtures/plan-count-permission-ae.json', 'test/fixtures/plan-count-permission-target-ad-v2.json', 'test/fixtures/eng-count-ad-v2.json', 'test/fixtures/eng-first-category-af.json', 'test/fixtures/plan-count-permission-ah.json', 'test/fixtures/plan-count-crop-ak.json', @@ -540,7 +540,7 @@ export const E2E_TOUCHFILES: Record = { 'test/fixtures/plan-count-cropped-wrap-6714.json', "test/fixtures/plan-count-cross-cwd-ancestry-0bcd.json", - 'test/fixtures/design-crop-gutter-ap.json', 'bin/gstack-config', 'plan-ceo-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'bin/gstack-question-preference', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/helpers/plan-count-transcript.ts', 'test/fixtures/autoplan-public-narration-ad.json', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/eval-store.ts', 'test/helpers/plan-count-fixture.ts', 'test/fixtures/ceo-prerequisite-n-call.json', 'test/fixtures/eng-prerequisite-77.json', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-ceo-split-overflow.test.ts', 'test/fixtures/ceo-checkbox-l.screen.txt', 'test/helpers/plan-count-file-permission.ts', 'test/fixtures/plan-edit-cropped-permission-1579.json', 'test/fixtures/plan-count-edit-permission-t.json', 'test/fixtures/plan-count-owned-permission-v.json', 'test/fixtures/ceo-questionless-w-native.json', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/fixtures/plan-count-permission-ac.json', 'test/fixtures/plan-count-permission-ad.json', 'test/fixtures/plan-count-permission-ae.json', 'test/fixtures/plan-count-permission-target-ad-v2.json', 'test/fixtures/plan-count-permission-ah.json', + 'test/fixtures/design-crop-gutter-ap.json', 'bin/gstack-config', 'plan-ceo-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'bin/gstack-question-preference', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/helpers/plan-count-transcript.ts', 'test/fixtures/autoplan-public-narration-ad.json', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/eval-store.ts', 'test/helpers/plan-count-fixture.ts', 'test/fixtures/ceo-prerequisite-n-call.json', 'test/fixtures/eng-prerequisite-77.json', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-ceo-split-overflow.test.ts', 'test/fixtures/ceo-checkbox-l.screen.txt', 'test/helpers/plan-count-file-permission.ts', 'test/fixtures/plan-edit-cropped-permission-1579.json', 'test/fixtures/plan-count-edit-permission-t.json', 'test/fixtures/plan-count-owned-permission-v.json', 'test/fixtures/ceo-questionless-w-native.json', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/fixtures/plan-count-permission-ac.json', 'test/fixtures/plan-count-permission-ad.json', 'test/fixtures/plan-count-permission-ae.json', 'test/fixtures/plan-count-permission-target-ad-v2.json', 'test/fixtures/plan-count-permission-ah.json', 'test/fixtures/plan-count-crop-ak.json', @@ -1096,7 +1096,7 @@ export const E2E_TOUCHFILES: Record = { 'test/skill-e2e-arm-benchmark.test.ts', 'ship/SKILL.md', 'test/helpers/e2e-gate.ts'], - 'office-hours-section-loading': [ 'office-hours/**', 'bin/gstack-office-hours-review', 'lib/office-hours-review.ts', 'lib/fs-atomic.ts', 'scripts/resolvers/review.ts', 'scripts/resolvers/sections.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/carve-guards.ts', 'test/helpers/auq-sdk-capture.ts', 'test/helpers/office-hours-completion.ts', 'test/helpers/llm-judge.ts', 'test/helpers/session-runner.ts', 'test/skill-e2e-office-hours-section-loading.test.ts', 'test/helpers/agent-sdk-runner.ts', 'test/helpers/auq-native-capture.ts', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/capture-parity-baseline.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/native-auto-decide.ts', 'test/helpers/owned-claude-transcript.ts', 'test/helpers/parity-harness.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/plan-count-file-permission.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-count-transcript.ts', 'test/helpers/plan-floor-review.ts', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/plan-skill-questions.ts', 'test/helpers/pty-screen.ts', 'test/helpers/pty-trust-dialog.ts', 'test/helpers/skill-census.ts'], + 'office-hours-section-loading': [ 'office-hours/**', 'bin/gstack-office-hours-review', 'lib/office-hours-review.ts', 'lib/fs-atomic.ts', 'scripts/resolvers/review.ts', 'scripts/resolvers/sections.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/carve-guards.ts', 'test/helpers/auq-sdk-capture.ts', 'test/helpers/office-hours-completion.ts', 'test/helpers/llm-judge.ts', 'test/helpers/session-runner.ts', 'test/skill-e2e-office-hours-section-loading.test.ts', 'test/helpers/agent-sdk-runner.ts', 'test/helpers/auq-native-capture.ts', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/capture-parity-baseline.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/native-auto-decide.ts', 'test/helpers/owned-claude-transcript.ts', 'test/helpers/parity-harness.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/plan-count-file-permission.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-count-transcript.ts', 'test/helpers/plan-floor-review.ts', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/plan-skill-questions.ts', 'test/helpers/pty-screen.ts', 'test/helpers/pty-trust-dialog.ts', 'test/helpers/skill-census.ts'], 'plan-devex-peer-comparison-classification': [ diff --git a/test/test-of-test-ratchet.test.ts b/test/test-of-test-ratchet.test.ts index 1f6f94152..612238465 100644 --- a/test/test-of-test-ratchet.test.ts +++ b/test/test-of-test-ratchet.test.ts @@ -139,6 +139,7 @@ const BASELINE = [ 'test/helpers/budget-override.test.ts', 'test/helpers/capture-parity-baseline.test.ts', 'test/helpers/claude-pty-runner.scope-gate-floor.unit.test.ts', + 'test/helpers/claude-pty-runner.runners.unit.test.ts', // W4 fake-driver regressions: the PTY harness is itself the subject (plan-mode paid evals stand on it) 'test/helpers/e2e-gate.unit.test.ts', 'test/helpers/eval-store.test.ts', 'test/helpers/hermetic-env.test.ts',