test: fold per-incident replay series into their detector owners (D)

Twelve detector families move into one owner test each: 73 incident files
become describe blocks in ceo-section-loading-fixture (stale-fill race),
model-overlays, coverage-audit-evidence, autoplan-phase-observer,
native-auto-decide, outside-voice-evidence, eng-first-review,
plan-count-completion, plan-count-file-permission, ceo-mode-option,
plan-scope-selection and plan-count-prerequisite. Each block keeps its original
code and fixture, so every case still runs; only tests asserting the incident
file's own touchfile registration are dropped (41). Touchfile lists that named
an incident now name its owner.
This commit is contained in:
garrytan committed 2026-09-29 06:37:21 +00:00
1 parent 6415690a18
commit 0489bc9fe8
89 files changed
+8091 -8373

No files matched your search

+665
View File
@@ -207,7 +207,672 @@ test('AD v2 path-only heading must agree with full menu target and current metad
});
import {E2E_TOUCHFILES,selectTests} from './helpers/touchfiles';
import fs_batching_permission_at from 'node:fs';
import os_batching_permission_at from 'node:os';
import path_batching_permission_at from 'node:path';
import captured_batching_permission_at from './fixtures/batching-permission-at.json';
import fixture_design_crop_gutter_ap from './fixtures/design-crop-gutter-ap.json';
import previous_design_crop_gutter_ap from './fixtures/plan-count-crop-ak.json';
import capturedAd_plan_count_permission_ac from './fixtures/plan-count-permission-ad.json';
import capturedAe_plan_count_permission_ac from './fixtures/plan-count-permission-ae.json';
import capturedAh_plan_count_permission_ac from './fixtures/plan-count-permission-ah.json';
import { currentFilePermissionBinding } from './helpers/plan-count-file-permission';
import exact_plan_count_quoted_frame_ak from './fixtures/plan-count-quoted-frame-ak.json';
import owned_plan_count_quoted_frame_ak from './fixtures/plan-count-owned-permission-v.json';
test('AD v2 cropped target fixture selects all existing file-permission consumers',()=>{
expect(selectTests(['test/fixtures/plan-count-permission-target-ad-v2.json'],E2E_TOUCHFILES,[]).selected)
.toEqual(selectTests(['test/plan-count-file-permission.test.ts'],E2E_TOUCHFILES,[]).selected);
});
describe('batching-permission-at', () => {
const fs = fs_batching_permission_at;
const os = os_batching_permission_at;
const path = path_batching_permission_at;
const captured = captured_batching_permission_at;
function renderPermissionScreen(expected: string, paths: Pick<typeof path, 'dirname' | 'basename'> = path): string {
// The capture is already laid out at the runner's 120 columns. Replacing its
// path must reflow that menu line, otherwise the PTY hard-wraps words in half.
return captured.screen.split('\n').map(original => {
const line = original.replaceAll(path.posix.dirname(captured.expectedPath), paths.dirname(expected))
.replaceAll(path.posix.basename(captured.expectedPath), paths.basename(expected));
if (line === original || line.length <= 120) return line;
const indent = /^ */.exec(line)![0], lines: string[] = []; let current = indent;
for (const word of line.trim().split(/\s+/)) {
if (indent.length + word.length > 120) throw Error('Fixture path exceeds the permission panel width');
if (current.length > indent.length && current.length + 1 + word.length > 120) { lines.push(current); current = indent; }
current += (current.length > indent.length ? ' ' : '') + word;
}
return [...lines, current].join('\n');
}).join('\n');
}
function fixture(){
const dir=fs.mkdtempSync(path.join(os.tmpdir(),'batch-permission-')),cwd=path.join(dir,'cwd'),config=path.join(dir,'.claude'),expected=path.join(dir,'report.md');fs.mkdirSync(cwd);fs.writeFileSync(expected,'original');
const recorder=createFilePermissionRecorder(cwd,config,expected)!;const startedAt=Date.now()-1000;
const screen=renderPermissionScreen(expected);
const transcript:any={status:'ready',calls:[],assistantMessages:[{sessionId:'synthetic-epoch',text:'Reviewing',timestamp:new Date().toISOString()}]};
const record=(name:string,id:string,extra={})=>recordFilePermission(JSON.stringify({hook_event_name:name,tool_name:'Edit',session_id:'synthetic-epoch',tool_use_id:id,cwd,transcript_path:path.join(config,'projects','owned','synthetic-epoch.jsonl'),tool_input:{file_path:expected},...extra}),recorder.file,cwd,config,expected);
const read=()=>currentFilePermissionEpoch(recorder.file,expected,cwd,config,startedAt,transcript,screen);
return{dir,cwd,config,expected,recorder,screen,transcript,record,read,close(){recorder.dispose();fs.rmSync(dir,{recursive:true,force:true})}};
}
test('retained retry has a valid permission panel and real previous completion without a pending ID',()=>{
expect(classifyPlanCountFrame(captured.screen)).toBe('permission');expect(captured.priorCompletedEdit[0]!.name).toBe('Edit');expect(captured.priorCompletedEdit[1]!.isError).toBe(false);
expect(captured.pendingEditId).toBeNull();expect(captured.provenance.originalOutcome).toBe('timeout');expect(captured.provenance.paidOutcomeReclassified).toBe(false);
const guard=createPlanCountPermissionGuard();expect(guard(captured.screen,captured.lastMatchedDisplayCompletion)).toBe('grant');expect(guard(captured.screen,captured.lastMatchedDisplayCompletion)).toBe('handled');
});
test('a substituted long fixture path reflows the menu without splitting permission words', () => {
const prefix = ' always allow access to ', suffix = ' for this ';
const directory = '/' + 'x'.repeat(120 - prefix.length - suffix.length - 3 - 1);
const rawLine = `${prefix}${directory}${suffix}session`;
expect(`${rawLine.slice(0, 120)}\n${rawLine.slice(120)}`).toContain('ses\nsion');
for (const paths of [path.posix, path.win32]) {
const expected = paths.join(directory, 'report.md');
const screen = renderPermissionScreen(expected, paths);
const menu = screen.slice(screen.indexOf(' Do you want to make this edit'));
expect(menu.split('\n').every(line => line.length <= 120)).toBe(true);
expect(menu).toContain(paths.dirname(expected));
expect(menu).toContain('edit to report.md?');
expect(menu).toMatch(/1\. Yes[\s\S]+2\. Yes,[\s\S]+3\. No/);
expect(createPlanCountPermissionGuard()(screen, captured.lastMatchedDisplayCompletion)).toBe('grant');
}
});
test('synthetic hook epochs release only the later exact request after its predecessor succeeds',()=>{
const f=fixture();try{const guard=createPlanCountPermissionGuard(),input=()=>guard(f.screen,captured.lastMatchedDisplayCompletion,f.read());
expect(input()).toBe('handled');f.record('PreToolUse','first');expect(input()).toBe('grant');expect(input()).toBe('handled');
f.record('PostToolUse','first');expect(input()).toBe('handled');f.record('PreToolUse','first');expect(input()).toBe('handled');
f.record('PreToolUse','second');expect(input()).toBe('grant');expect(input()).toBe('handled');f.record('PostToolUse','first');expect(input()).toBe('handled');
}finally{f.close()}
});
for(const reason of ['failed','no-result','foreign-session','foreign-path','other-tool','sidechain'])test(`a later matching menu cannot replace ${reason} predecessor evidence`,()=>{
const f=fixture();try{const guard=createPlanCountPermissionGuard(),input=()=>guard(f.screen,'',f.read());f.record('PreToolUse','first');expect(input()).toBe('grant');
if(reason==='failed')f.record('PostToolUseFailure','first');else if(reason!=='no-result')f.record('PostToolUse','first',reason==='foreign-session'?{session_id:'foreign'}:reason==='foreign-path'?{tool_input:{file_path:path.join(f.dir,'foreign','report.md')}}:reason==='other-tool'?{tool_name:'Read'}:{agent_id:'child'});
f.record('PreToolUse','second');expect(input()).toBe('handled');
}finally{f.close()}
});
test.skipIf(process.platform==='win32')('real fake CLI observes two file epochs without imposing terminal report validation',async()=>{
const dir=fs.mkdtempSync(path.join(os.tmpdir(),'batch-permission-pty-')),fake=path.join(dir,'fake-claude'),worker=path.join(dir,'worker.ts'),events=path.join(dir,'events.jsonl'),output=path.join(dir,'output.json'),expected=path.join(dir,'report.md');fs.writeFileSync(expected,'original');
const screen=renderPermissionScreen(expected);
fs.writeFileSync(fake,`#!${process.execPath}\n`+String.raw`
import * as fs from 'node:fs';import * as path from 'node:path';
const item=JSON.parse(process.env.FILE_EPOCH_CASE);const log=e=>fs.appendFileSync(item.events,JSON.stringify(e)+'\n');
const sid='epoch-main';const nativePath=path.join(process.env.CLAUDE_CONFIG_DIR,'projects','epoch',sid+'.jsonl');fs.mkdirSync(path.dirname(nativePath),{recursive:true});
const native=(role,content,extra={})=>fs.appendFileSync(nativePath,JSON.stringify({cwd:process.cwd(),sessionId:sid,isSidechain:false,timestamp:new Date().toISOString(),message:{role,content},...extra})+'\n');
native('assistant',[{type:'text',text:'Reviewing fixture.'}]);log({type:'start',pid:process.pid,cwd:process.cwd()});
const settings=JSON.parse(process.argv[process.argv.indexOf('--settings')+1]);
if(settings.hooks.PreToolUse[0].matcher!=='^ExitPlanMode$')throw Error('Exit recorder changed');
const hook=async(name,id)=>{
const entries=(settings.hooks[name]??[]).filter(h=>h.matcher==='^(Write|Edit)$');
if(entries.length!==1)throw Error('Expected exactly one caller-owned file recorder');
for(const entry of entries){
const event={hook_event_name:name,tool_name:'Edit',session_id:sid,tool_use_id:id,cwd:process.cwd(),transcript_path:nativePath,tool_input:{file_path:item.activePlan?path.join(process.cwd(),'PLAN.md'):item.expected,old_string:'old',new_string:'new'}};
const p=Bun.spawn(['bash','-c',entry.hooks[0].command],{stdin:new Blob([JSON.stringify(event)]),stdout:'pipe',stderr:'pipe'});
const [code,out,err]=await Promise.all([p.exited,new Response(p.stdout).text(),new Response(p.stderr).text()]);if(code||out||err)throw Error('hook was not silent');log({type:'hook',name,id});
}
};
let stage='startup';const paint=()=>process.stdout.write('\x1b[2J\x1b[H'+item.screen.replaceAll('__ACTIVE_PLAN_PATH__',path.join(process.cwd(),'PLAN.md')).replaceAll('\n','\r\n'));
process.stdin.setRawMode?.(true);process.stdin.on('data',async data=>{
const input=data.toString();log({type:'input',stage,input});
if(stage==='startup'){stage='first';await hook('PreToolUse','first');paint();return;}
if(stage==='old-pane'||stage==='done'){log({type:'unexpected'});return;}
if(input!=='1\r')throw Error('default permission input changed');
if(stage==='first'){stage='old-pane';await hook('PostToolUse','first');if(item.intervening){await hook('PreToolUse','automatic');await hook('PostToolUse','automatic');}paint();setTimeout(async()=>{await hook('PreToolUse','second');stage='second';paint();},3200);return;}
stage='done';await hook('PostToolUse','second');
const q={header:'Finding',question:'Apply this repair?',options:[{label:'Fix'},{label:'Keep'}]};
native('assistant',[{type:'tool_use',name:'AskUserQuestion',id:'finding',input:{questions:[q]}}]);native('user',[{type:'tool_result',tool_use_id:'finding',content:'Answered'}],{toolUseResult:{answers:{[q.question]:'Fix'}}});
process.stdout.write('\x1b[2J\x1b[HCompletion summary\r\n');
});process.on('SIGINT',()=>process.exit(0));process.stdin.resume();process.stdout.write('FILE_EPOCH_READY\r\n');
`);fs.chmodSync(fake,0o755);
fs.writeFileSync(worker,`import {runPlanSkillCounting} from ${JSON.stringify(pathToFileURL(path.join(import.meta.dir,'helpers/claude-pty-runner.ts')).href)};const o=await runPlanSkillCounting({skillName:'plan-eng-review',slashCommand:'/plan-eng-review',followUpPrompt:'Review this disposable batching fixture.',permissionPlanPath:${JSON.stringify(expected)},startupReadyMarker:'FILE_EPOCH_READY',isLastStep0AUQ:()=>false,isReviewAUQ:()=>true,reviewCountCeiling:2,timeoutMs:28000,env:{FILE_EPOCH_CASE:${JSON.stringify(JSON.stringify({events,expected,screen}))}}});await Bun.write(${JSON.stringify(output)},JSON.stringify(o));`);
const child=Bun.spawn([process.execPath,worker],{env:{...process.env,BROWSE_TERMINAL_BINARY:fake,EVALS_HERMETIC:'1'},stdout:'pipe',stderr:'pipe'});const killer=setTimeout(()=>child.kill('SIGKILL'),33000);
try{const[code,out,err]=await Promise.all([child.exited,new Response(child.stdout).text(),new Response(child.stderr).text()]);expect(code,out+err).toBe(0);
const o=JSON.parse(fs.readFileSync(output,'utf8'));expect(o.outcome,JSON.stringify(o)).toBe('completion_summary');expect(o.reviewCount).toBe(1);expect(fs.readFileSync(expected,'utf8')).toBe('original');
const rows=fs.readFileSync(events,'utf8').trim().split('\n').map(l=>JSON.parse(l));expect(rows.filter(e=>e.type==='input').map(e=>e.input)).toEqual(['/plan-eng-review\r','1\r','1\r']);expect(rows.some(e=>e.type==='unexpected')).toBe(false);
expect(()=>process.kill(rows[0].pid,0)).toThrow();expect(fs.existsSync(rows[0].cwd)).toBe(false);
}finally{clearTimeout(killer);child.kill('SIGKILL');if(fs.existsSync(events)){const first=JSON.parse(fs.readFileSync(events,'utf8').split('\n')[0]!);try{process.kill(first.pid,'SIGKILL');}catch{}}fs.rmSync(dir,{recursive:true,force:true});}
},35000);
});
describe('design-crop-gutter-ap', () => {
const fs = fs_batching_permission_at;
const os = os_batching_permission_at;
const path = path_batching_permission_at;
const fixture = fixture_design_crop_gutter_ap;
const previous = previous_design_crop_gutter_ap;
function replay(change:(f:any)=>void=()=>{},input:any=fixture){
const dir=fs.mkdtempSync(path.join(os.tmpdir(),'design-gutter-ap-'));
const expected=path.join(dir,'report.md'),record=path.join(dir,'record.json');
const f:any={expected,record,cwd:input.cwd,config:input.config,startedAt:input.startedAt,
screen:input.screen.replaceAll(path.dirname(input.hook.expected),dir).replaceAll(path.basename(input.hook.expected),'report.md'),
state:{...structuredClone(input.hook),expected},before:input.ownedBefore,transcript:structuredClone(input.transcript),fileKind:'file'};
try{
change(f);fs.writeFileSync(record,JSON.stringify(f.state));
if(f.fileKind==='file')fs.writeFileSync(expected,f.before);
if(f.fileKind==='directory')fs.mkdirSync(expected);
if(f.fileKind==='symlink'){const target=path.join(dir,'other.md');fs.writeFileSync(target,f.before);fs.symlinkSync(target,expected);}
const epoch=currentFilePermissionEpoch(record,f.expected,f.cwd,f.config,f.startedAt,f.transcript,f.screen);
const guard=createPlanCountPermissionGuard();
return {epoch,first:guard(f.screen,'',epoch),second:guard(f.screen,'',epoch)};
}finally{fs.rmSync(dir,{recursive:true,force:true});}
}
test('exact five-column current gutter supplies one owned Edit epoch and one grant',()=>{
const lines=fixture.screen.split('\n');expect(lines[0]).toMatch(/^ {5}\S/);expect(lines[1]).toBe(' 62 ');
expect(fixture.ownedBefore.split('\n')[60]!.endsWith(lines[0]!.slice(5))).toBe(true);
const r=replay();expect(r.epoch).toEqual({pendingId:fixture.hook.pendingId,completedId:fixture.hook.completedId,completedIds:fixture.hook.completedIds});
expect(r.first).toBe('grant');expect(r.second).toBe('handled');
});
test('prior six-column public crop remains exact and one-time',()=>{
expect(previous.screen.split('\n')[0]).toMatch(/^ {6}\S/);expect(previous.screen.split('\n')[1]).toBe(' 82 ');
const r=replay(()=>{},previous);expect(r.epoch?.pendingId).toBe(previous.hook.pendingId);expect(r.first).toBe('grant');expect(r.second).toBe('handled');
// Keep the existing six-space acceptance even when the adjacent numeric
// row has a different padding; the unchanged original-line guard remains.
expect(replay(f=>{f.screen=' '+f.screen;}).epoch?.pendingId).toBe(fixture.hook.pendingId);
});
test('padding and line-number width derive the continuation column together',()=>{
const padded=replay(f=>{f.screen=' '+f.screen;f.screen=f.screen.replace(/^ 62 $/m,' 62 ');});
expect(padded.epoch?.pendingId).toBe(fixture.hook.pendingId);
const relocated=replay(f=>{
f.before='Earlier unchanged line\n'.repeat(38)+f.before;
f.screen=' '+f.screen;
f.screen=f.screen.replace(/^ ([1-9]\d*)( | [+-])/gm,(_:string,n:string,g:string)=>' '+(Number(n)+38)+g);
});
expect(relocated.epoch?.pendingId).toBe(fixture.hook.pendingId);
});
test.each([0,3])('a native numbered-row padding of %d derives a matching non-six gutter',padding=>{
const r=replay(f=>{
f.screen=' '.repeat(padding+4)+f.screen.slice(5);
f.screen=f.screen.replace(/^ 62 $/m,' '.repeat(padding)+'62 ');
});
expect(r.epoch?.pendingId).toBe(fixture.hook.pendingId);expect(r.first).toBe('grant');
});
test('an ordinary numbered unchanged row remains a numbered row, not a wrapped continuation',()=>{
const r=replay(f=>{
f.screen=' 61 '+f.before.split('\n')[60]+'\n'+f.screen.slice(f.screen.indexOf('\n')+1);
});
expect(r.epoch?.pendingId).toBe(fixture.hook.pendingId);expect(r.first).toBe('grant');
});
const negatives:Array<[string,(f:any)=>void]>=[
['four-space gutter with five-column numbered row',f=>{f.screen=f.screen.slice(1);}],
['seven-space gutter with five-column numbered row',f=>{f.screen=' '+f.screen;}],
['tab cannot substitute for a native space gutter',f=>{f.screen='\t'+f.screen.slice(1);}],
['wrong preceding file line',f=>{f.screen=f.screen.replace(/^ 62 $/m,' 63 ');}],
['changed continuation content',f=>{f.screen=f.screen.replace('f2 with icon','foreign with icon');}],
['stale before-file bytes',f=>{f.before=f.before.replace('f2 with icon','changed with icon');}],
['quoted continuation',f=>{f.screen=f.screen.replace(/^ {5}/,' > ');}],
['two unnumbered continuation rows',f=>{f.screen=f.screen.split('\n')[0]+'\n'+f.screen;}],
['next row is an addition, not unchanged context',f=>{f.screen=f.screen.replace(/^ 62 $/m,' 62 +');}],
['zero next line',f=>{f.screen=f.screen.replace(/^ 62 $/m,' 00 ');}],
['missing current file',f=>{f.fileKind='missing';}],
['directory instead of current file',f=>{f.fileKind='directory';}],
['required source line beyond the bounded prefix',f=>{f.before='x'.repeat(65537)+f.before;}],
['foreign displayed directory',f=>{f.screen=f.screen.replace(path.dirname(f.expected)+' for this session',path.join(path.dirname(f.expected),'foreign')+' for this session');}],
['foreign hook target',f=>{f.state.expected+='.foreign';}],
['foreign hook cwd',f=>{f.state.cwd+='.foreign';}],
['foreign native session',f=>{f.transcript={status:'ready',calls:[],assistantMessages:[{sessionId:'foreign',text:'Current review',timestamp:new Date(f.startedAt).toISOString()}]};}],
['missing pending request',f=>{f.state.pendingId=null;}],
['completed request cannot reopen',f=>{f.state.completedId=f.state.pendingId;}],
['stale request timestamp',f=>{f.state.timestamp=new Date(f.startedAt-1).toISOString();}],
['missing menu footer',f=>{f.screen=f.screen.replace('Esc to cancel · Tab to amend','');}],
['one-time action changed',f=>{f.screen=f.screen.replace('❯ 1. Yes','❯ 1. Yes, always allow');}],
];
test.each(negatives)('%s cannot obtain a grant',(_,change)=>{
const r=replay(change);expect(r.epoch).not.toBeTruthy();expect(r.first).not.toBe('grant');
});
test.skipIf(process.platform==='win32')('symlink cannot provide the original line',()=>{expect(replay(f=>{f.fileKind='symlink';}).epoch).toBeNull();});
});
describe('plan-count-crop-ak', () => {
const fs = fs_batching_permission_at;
const os = os_batching_permission_at;
const path = path_batching_permission_at;
const fixture = previous_design_crop_gutter_ap;
function replay(change: (f: any) => void = () => {}) {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'plan-crop-ak-'));
const expected = path.join(dir, 'report.md'), record = path.join(dir, 'record.json');
const f: any = { expected, record, screen: fixture.screen.replaceAll(path.dirname(fixture.hook.expected), dir)
.replaceAll(path.basename(fixture.hook.expected), 'report.md'), state: { ...structuredClone(fixture.hook), expected },
before: fixture.ownedBefore, cwd: fixture.cwd, config: fixture.config, startedAt: fixture.startedAt,
transcript: structuredClone(fixture.transcript), fileKind: 'file' };
try {
change(f); fs.writeFileSync(record, JSON.stringify(f.state));
if (f.fileKind === 'file') fs.writeFileSync(expected, f.before);
if (f.fileKind === 'directory') fs.mkdirSync(expected);
if (f.fileKind === 'symlink') { const other = path.join(dir, 'other.md'); fs.writeFileSync(other, f.before); fs.symlinkSync(other, expected); }
const epoch = currentFilePermissionEpoch(record, f.expected, f.cwd, f.config, f.startedAt, f.transcript, f.screen);
return { epoch, screen: f.screen, guard: createPlanCountPermissionGuard(), run: () => createPlanCountPermissionGuard()(f.screen, '', epoch) };
} finally { fs.rmSync(dir, { recursive: true, force: true }); }
}
test('actual owned bare continuation uses the preceding original line and grants once', () => {
const r = replay();
expect(r.epoch?.pendingId).toBe(fixture.hook.pendingId);
expect(r.guard(r.screen, '', r.epoch)).toBe('grant');
expect(r.guard(r.screen, '', r.epoch)).toBe('handled');
});
test('harmless preceding line number relocation keeps the exact content binding', () => {
const r = replay(f => {
f.before = 'Added unchanged line\n' + f.before;
f.screen = f.screen.replace(/^ {0,3}82 +$/m, ' 83 ');
});
expect(r.epoch?.pendingId).toBe(fixture.hook.pendingId);
});
const negatives: Array<[string, (f: any) => void]> = [
['wrong preceding line', f => { f.screen = f.screen.replace(/^ {0,3}82 +$/m, ' 83 '); }],
['unrelated prose continuation', f => { f.screen = f.screen.replace(/^.*\n/, ' Apply this edit now\n'); }],
['source quotation continuation', f => { f.screen = f.screen.replace(/^.*\n/, ' > Example: apply this edit now\n'); }],
['foreign continuation suffix', f => { f.screen = f.screen.replace('f2 (~7.6:1)', 'foreign (~7.6:1)'); }],
['five-space unnumbered gutter', f => { f.screen = f.screen.slice(1); }],
['seven-space unnumbered gutter', f => { f.screen = ' ' + f.screen; }],
['no following unchanged numbered row', f => { f.screen = f.screen.replace(/^ {0,3}82 +$/m, ' 82 +'); }],
['two arbitrary continuation rows', f => { f.screen = f.screen.split('\n')[0] + '\n' + f.screen; }],
['missing original file', f => { f.fileKind = 'missing'; }],
['directory in place of original file', f => { f.fileKind = 'directory'; }],
['required original line beyond the bounded prefix', f => { f.before = 'x'.repeat(65537) + f.before; }],
['foreign displayed directory', f => { f.screen = f.screen.replace(path.dirname(f.expected) + ' for this session', path.join(path.dirname(f.expected), 'foreign') + ' for this session'); }],
['foreign hook expected path', f => { f.state.expected += '.foreign'; }],
['no current native request', f => { f.state.pendingId = null; }],
['completed native request', f => { f.state.completedId = f.state.pendingId; }],
['stale native timestamp', f => { f.state.timestamp = new Date(f.startedAt - 1).toISOString(); }],
['future native timestamp', f => { f.state.timestamp = new Date(Date.now() + 60000).toISOString(); }],
['foreign session transcript', f => { f.transcript.assistantMessages[0].sessionId = 'foreign'; }],
['unavailable native transcript', f => { f.transcript.status = 'error'; }],
['missing complete menu footer', f => { f.screen = f.screen.replace('Esc to cancel · Tab to amend', ''); }],
['selected policy change', f => { f.screen = f.screen.replace('❯ 1. Yes', '❯ 1. Yes, always allow'); }],
['unrelated prompt prepended', f => { f.screen = '☐ Review this example\n' + f.screen; }],
['historical pane is not current viewport', f => { f.screen = 'Waiting for current tool'; }],
];
test.each(negatives)('%s cannot supply an owned epoch', (_, change) => {
const r = replay(change); expect(r.epoch).not.toBeTruthy(); expect(r.run()).not.toBe('grant');
});
test.skipIf(process.platform === 'win32')('a symlink cannot supply current original content', () => {
expect(replay(f => { f.fileKind = 'symlink'; }).epoch).toBeNull();
});
});
describe('plan-count-permission-ac', () => {
const captured = capturedAc;
const capturedAd = capturedAd_plan_count_permission_ac;
const capturedAe = capturedAe_plan_count_permission_ac;
const capturedAh = capturedAh_plan_count_permission_ac;
function fixture() {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'count-permission-ac-'));
const cwd = path.join(dir, 'cwd'), config = path.join(dir, '.claude');
const expected = path.join(dir, 'report.md'), file = path.join(dir, 'state.json');
fs.mkdirSync(cwd); const startedAt = Date.now() - 1000;
const screen = captured.rows[0]!.screen
.replace('../gstack-e2e-plan-design-3vPM9g/gstack-test-plan-design.md', expected)
.replaceAll('gstack-test-plan-design.md', 'report.md');
const transcript: any = { status: 'ready', calls: [], assistantMessages: [{ sessionId: 'main', text: 'Reviewing' }] };
const record = (name: string, id: string, delta: object = {}) => recordFilePermission(JSON.stringify({
hook_event_name: name, tool_name: 'Edit', session_id: 'main', tool_use_id: id, cwd,
transcript_path: path.join(config, 'projects', 'owned', 'main.jsonl'),
tool_input: { file_path: expected, old_string: 'PRIVATE_OLD', new_string: 'PRIVATE_NEW' }, ...delta,
}), file, cwd, config, expected);
const epoch = () => currentFilePermissionEpoch(file, expected, cwd, config, startedAt, transcript, screen);
return { dir, cwd, config, expected, file, screen, record, epoch,
close: () => fs.rmSync(dir, { recursive: true, force: true }) };
}
test('all five actual stalled screens already identify an edit permission', () => {
for (const row of captured.rows) {
expect(classifyPlanCountFrame(row.screen), row.attempt).toBe('permission');
expect(createPlanCountPermissionGuard()(row.screen), row.attempt).toBe('grant');
expect(row.hook.pendingId).not.toBeNull();
expect(row.transcriptSessions).toEqual([row.hook.sessionId]);
}
});
test('an intervening successful edit does not erase the exact previous grant completion', () => {
const f = fixture(); try {
const guard = createPlanCountPermissionGuard(), input = () => guard(f.screen, '', f.epoch());
f.record('PreToolUse', 'granted'); expect(input()).toBe('grant');
f.record('PostToolUse', 'granted'); expect(input()).toBe('handled');
f.record('PreToolUse', 'automatic'); f.record('PostToolUse', 'automatic');
// Old pane is still inert, even after two successful results.
expect(input()).toBe('handled');
f.record('PreToolUse', 'next'); expect(input()).toBe('grant'); expect(input()).toBe('handled');
expect(fs.readFileSync(f.file, 'utf8')).not.toContain('PRIVATE_');
} finally { f.close(); }
});
test('an unrelated success cannot substitute for failed or missing prior approval completion', () => {
for (const outcome of ['PostToolUseFailure', 'missing', 'foreign', 'other-path', 'sidechain']) {
const f = fixture(); try {
const guard = createPlanCountPermissionGuard(), input = () => guard(f.screen, '', f.epoch());
f.record('PreToolUse', 'granted'); expect(input()).toBe('grant');
if (outcome === 'PostToolUseFailure') f.record(outcome, 'granted');
else if (outcome !== 'missing') f.record('PostToolUse', 'granted', outcome === 'foreign'
? { session_id: 'foreign' } : outcome === 'sidechain' ? { agent_id: 'child' }
: { tool_input: { file_path: path.join(f.dir, 'other.md') } });
f.record('PreToolUse', 'automatic'); f.record('PostToolUse', 'automatic');
f.record('PreToolUse', 'next'); expect(input(), outcome).toBe('handled');
f.record('PreToolUse', 'granted'); expect(input(), outcome).toBe('handled');
} finally { f.close(); }
}
});
test('success history rejects malformed, foreign, replayed, and pending IDs', () => {
const f = fixture(); try {
f.record('PreToolUse', 'first'); f.record('PostToolUse', 'first'); f.record('PreToolUse', 'next');
const original = JSON.parse(fs.readFileSync(f.file, 'utf8'));
for (const completedIds of [['foreign:first'], ['main:../escape'], ['main:next'],
['main:first', 'main:first'], Array(129).fill('main:first'), ['main:unseen'], 'main:first']) {
fs.writeFileSync(f.file, JSON.stringify({ ...original, completedIds }));
expect(f.epoch()).toBeNull();
}
} finally { f.close(); }
});
test('success history is bounded by the existing 128-request recorder limit', () => {
const f = fixture(); try {
for (let i = 0; i < 127; i++) { f.record('PreToolUse', `id${i}`); f.record('PostToolUse', `id${i}`); }
f.record('PreToolUse', 'last'); expect(f.epoch()?.completedIds?.length).toBe(127);
expect(fs.statSync(f.file).size).toBeLessThan(64 * 1024);
f.record('PreToolUse', 'overflow'); expect(f.epoch()).toBeNull();
} finally { f.close(); }
});
test('cropped actual panes bind their full directory and basename to the current native epoch', () => {
const rows = captured.rows.filter(row => [4, 5].includes(row.job) && !/^ {0,3}Edit file$/m.test(row.screen));
expect(rows).toHaveLength(2);
for (const row of rows) {
const f = fixture(); try {
const screen = row.screen.replaceAll(path.dirname(row.hook.expected), path.dirname(f.expected))
.replaceAll(path.basename(row.hook.expected), path.basename(f.expected));
const read = (value = screen) => currentFilePermissionEpoch(f.file, f.expected, f.cwd, f.config,
0, { status: 'ready', calls: [], assistantMessages: [{ sessionId: 'main', text: 'Reviewing', timestamp: new Date().toISOString() }] }, value);
f.record('PreToolUse', 'current');
expect(read()?.pendingId, row.attempt).toBe('main:current');
const guard = createPlanCountPermissionGuard();
expect(guard(screen, '', read())).toBe('grant');
expect(guard(screen, '', read())).toBe('handled');
for (const [name, changed] of [
['foreign', screen.replace(path.dirname(f.expected), path.join(f.dir, 'foreign'))],
['remedy', screen.replace(/always\s+allow\s+access\s+to/, 'remove files from')],
['footer', screen.replace('Esc to cancel · Tab to amend', '')],
['yes policy', screen.replace(/❯\s*1\.\s*Yes/, '❯ 1. Yes, change policy')],
['AUQ', '☐ Finding\n' + screen],
['quoted', '> Example:\n' + screen],
['wrapped path', screen.replace(path.dirname(f.expected), path.dirname(f.expected) + '\n/other')],
['path spaces', screen.replace(path.dirname(f.expected), path.dirname(f.expected) + ' space')],
]) {
expect(changed, name).not.toBe(screen);
expect(read(changed), name).toBeNull();
}
expect(read(screen.replace(path.dirname(f.expected), path.join(f.dir, 'foreign')))).toBeNull();
f.record('PostToolUse', 'current'); expect(read()).toBeNull();
f.record('PreToolUse', 'current'); expect(read()).toBeNull();
} finally { f.close(); }
}
});
test('a later exact owned binding wins over an earlier same-basename block', () => {
const f = fixture(); try {
f.record('PreToolUse', 'current');
const transcript: any = {status:'ready', calls:[], assistantMessages:[{sessionId:'main', text:'Reviewing'}]};
const foreign = {file:path.join(f.dir,'foreign-state.json'), expected:path.join(f.dir,'other','report.md')};
const owned = {file:f.file, expected:f.expected};
for (const bindings of [[foreign, owned], [owned, foreign]]) {
const selected = currentFilePermissionBinding(bindings, f.cwd, f.config, 0, transcript, f.screen);
expect(selected?.binding).toBe(owned);
expect(selected?.epoch.pendingId).toBe('main:current');
}
const blocked = currentFilePermissionBinding([foreign, {...foreign, expected:path.join(f.dir,'another','report.md')}],
f.cwd, f.config, 0, transcript, f.screen);
expect(blocked).toBeNull();
expect(createPlanCountPermissionGuard()(f.screen, '', blocked)).toBe('handled');
const otherScreen = f.screen.replaceAll('report.md', 'OTHER.md');
const unrelated = currentFilePermissionBinding([foreign, owned], f.cwd, f.config, 0, transcript, otherScreen);
expect(unrelated).toBeUndefined();
expect(createPlanCountPermissionGuard()(otherScreen, '', unrelated)).toBe('grant');
} finally { f.close(); }
});
// Exact current screens plus content-free native identity from full AD/AE runs.
// The replay projections do not assert these pending writes ever completed.
const cases = [...capturedAd.rows, capturedAe, capturedAh].map(row => ({
p: row, screen: row.screen, binding: {expected: row.state.expected, state: row.state},
observation: {transcript: {status: row.transcriptStatus, calls: [],
assistantMessages: row.transcriptSessions.map(sessionId => ({sessionId}))}},
}));
function adEpoch(c: any, screen = c.screen, state = c.binding.state, delta: any = {}) {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'permission-crop-replay-'));
const file = path.join(dir, 'state.json');
try {
// Captures contain POSIX paths. Project their filesystem identity onto the
// replay host without changing the captured fixture or its menu rendering.
const nativeState = { ...state, cwd: path.resolve(state.cwd), expected: path.resolve(state.expected),
transcriptPath: path.resolve(state.transcriptPath) };
const replayScreen = screen.replaceAll(path.posix.dirname(c.binding.expected),
path.dirname(path.resolve(c.binding.expected)));
fs.writeFileSync(file, JSON.stringify(nativeState));
return currentFilePermissionEpoch(file, path.resolve(delta.expected ?? c.binding.expected), path.resolve(delta.cwd ?? c.p.cwd),
path.resolve(delta.config ?? c.p.config), delta.startedAt ?? c.p.startUnix * 1000,
delta.transcript ?? c.observation.transcript, replayScreen);
} finally { fs.rmSync(dir, {recursive:true, force:true}); }
}
for (const c of cases) {
test(`actual AD captured current permission ${c.p.pid} returns its exact pending epoch`, () => {
expect(classifyPlanCountFrame(c.screen)).toBe('permission');
expect(adEpoch(c)).toEqual({pendingId:c.binding.state.pendingId, completedId:c.binding.state.completedId,
completedIds:c.binding.state.completedIds});
const guard = createPlanCountPermissionGuard();
expect(guard(c.screen, '', adEpoch(c))).toBe('grant');
expect(guard(c.screen, '', adEpoch(c))).toBe('handled');
});
test(`AD isolating the rejected rendering guard ${c.p.pid} preserves native identity`, () => {
// These are explicitly normalized controls; the actual captured screen is unchanged above.
const normalized = c.p.pid === capturedAe.pid ? c.screen.replace(/^[╌─━]{3,}[ \t]*\n/, '')
: c.p.pid === 1332470 ? c.screen.replace('3. Nohift+tab)', '3. No') : c.screen.replace(/^ {4,5}\+/, ' 99 +');
expect(normalized).not.toBe(c.screen);
expect(adEpoch(c, normalized)?.pendingId).toBe(c.binding.state.pendingId);
});
}
for (const c of cases) {
test(`AD crop ${c.p.pid} rejects foreign, quoted, incomplete and policy-changing menus`, () => {
const directory = path.dirname(c.binding.expected);
const changes: [string, string][] = [
['foreign directory', c.screen.replace(directory, path.join(directory, 'foreign'))],
['split directory', c.screen.replace(directory, directory + '\n/foreign')],
['quoted', '> Example:\n' + c.screen],
['AUQ', '☐ Review\n' + c.screen],
['code fence', '```\n' + c.screen],
['missing footer', c.screen.replace('Esc to cancel · Tab to amend', '')],
['policy on selected Yes', c.screen.replace('❯ 1. Yes', '❯ 1. Yes, always allow')],
['selected No', c.screen.replace('❯ 1. Yes', ' 1. Yes').replace(' 3. No', ' ❯ 3. No')],
['arbitrary No suffix', c.screen.replace(/3\. No(?:hift\+tab\))?/, '3. No; run another command')],
['another hint', c.screen.replace(/3\. No(?:hift\+tab\))?/, '3. No(shift+enter)')],
['foreign option action', c.screen.replace(/always\s+allow\s+access\s+to/, 'delete files from')],
['unrecognized cropped prose', c.screen.replace(/^.*\n/, 'arbitrary text\n')],
];
for (const [name, screen] of changes) {
expect(screen, name).not.toBe(c.screen);
expect(adEpoch(c, screen), name).not.toBeTruthy();
expect(createPlanCountPermissionGuard()(screen, '', adEpoch(c, screen)), name).not.toBe('grant');
}
});
test(`AD crop ${c.p.pid} leaves unrelated-basename permission policy unchanged`, () => {
const screen = c.screen.replace(path.basename(c.binding.expected), 'OTHER.md');
expect(adEpoch(c, screen)).toBeUndefined();
// This is intentionally the existing caller policy for unrelated fixture permissions.
expect(createPlanCountPermissionGuard()(screen, '', adEpoch(c, screen))).toBe('grant');
});
test(`AD crop ${c.p.pid} cannot replace missing, stale, completed or foreign native identity`, () => {
const original = c.binding.state;
for (const [name, state, delta] of [
['foreign session', {...original, sessionId:'foreign'}, {}],
['wrong native transcript', {...original, transcriptPath:path.join(c.p.config, 'projects', 'foreign', 'other.jsonl')}, {}],
['completed request', {...original, completedId:original.pendingId}, {}],
['no pending request', {...original, pendingId:null}, {}],
['unseen pending request', {...original, pendingId:original.sessionId + ':other'}, {}],
['stale timestamp', {...original, timestamp:new Date(c.p.startUnix * 1000 - 1).toISOString()}, {}],
['future timestamp', {...original, timestamp:new Date(Date.now() + 60_000).toISOString()}, {}],
['mixed sessions', original, {transcript:{status:'ready', calls:[], assistantMessages:[{sessionId:original.sessionId},{sessionId:'foreign'}]}}],
['unready transcript', original, {transcript:{...c.observation.transcript,status:'unavailable'}}],
] as const) {
expect(adEpoch(c, c.screen, state, delta), name).toBeNull();
}
});
}
test('AD crop fixture selects the exact existing permission regression callers', () => {
expect(selectTests(['test/fixtures/plan-count-permission-ad.json'], E2E_TOUCHFILES).selected.sort()).toEqual(
selectTests(['test/fixtures/plan-count-permission-ac.json'], E2E_TOUCHFILES).selected.sort());
});
test('AE crop admits one native divider only and preserves its exact existing caller selection', () => {
const c = cases.find(item => item.p.pid === capturedAe.pid)!;
const firstLine = c.screen.slice(0, c.screen.indexOf('\n') + 1);
for (const screen of [firstLine + c.screen, 'unrelated prose\n' + c.screen,
firstLine + 'Example:\n' + c.screen.slice(firstLine.length),
c.screen.replace(firstLine, firstLine.trimEnd() + ' extra action\n')]) {
expect(adEpoch(c, screen)).toBeNull();
expect(createPlanCountPermissionGuard()(screen, '', adEpoch(c, screen))).not.toBe('grant');
}
expect(selectTests(['test/fixtures/plan-count-permission-ae.json'], E2E_TOUCHFILES).selected.sort()).toEqual(
selectTests(['test/fixtures/plan-count-permission-ad.json'], E2E_TOUCHFILES).selected.sort());
});
test('AH wrapped crop admits four or five spaces with the same owned native epoch', () => {
const c = cases.find(item => item.p.pid === capturedAh.pid)!;
expect(c.screen.startsWith(' + ')).toBe(true);
for (const screen of [c.screen, ` ${c.screen}`, c.screen.replace(/^ \+/, ' -')]) {
expect(adEpoch(c, screen)?.pendingId).toBe(c.binding.state.pendingId);
const guard = createPlanCountPermissionGuard();
expect(guard(screen, '', adEpoch(c, screen))).toBe('grant');
expect(guard(screen, '', adEpoch(c, screen))).toBe('handled');
}
// Cleaning the unselected No paint residue does not establish missing identity.
const noOnly = c.screen.replace('3. Nohift+tab)', '3. No');
expect(noOnly).not.toBe(c.screen);
expect(adEpoch(c, noOnly)?.pendingId).toBe(c.binding.state.pendingId);
});
test('AH continuation crop rejects prose, unsupported gutters and malformed numbered context', () => {
const c = cases.find(item => item.p.pid === capturedAh.pid)!;
for (const [name, screen] of [
['four-space prose', c.screen.replace(/^.*\n/, ' Apply this edit now\n')],
['four-space quoted prose', c.screen.replace(/^.*\n/, ' > Example\n')],
['three-space gutter', c.screen.slice(1)],
['six-space gutter', ` ${c.screen}`],
['no numbered rows', c.screen.replace(/^\s*\d+\s+(?=[+\- ])/gm, ' +')],
['one numbered row', c.screen.replace(/^(\s*\d+\s+)(?=[+\- ])/gm,
(prefix, _group, offset) => offset === c.screen.indexOf(' 79 ') ? prefix : ' +')],
]) {
expect(screen, name).not.toBe(c.screen);
expect(adEpoch(c, screen), name).toBeNull();
expect(createPlanCountPermissionGuard()(screen, '', adEpoch(c, screen)), name).not.toBe('grant');
}
expect(selectTests(['test/fixtures/plan-count-permission-ah.json'], E2E_TOUCHFILES).selected.sort()).toEqual(
selectTests(['test/fixtures/plan-count-permission-ae.json'], E2E_TOUCHFILES).selected.sort());
});
});
describe('plan-count-quoted-frame-ak', () => {
const fs = fs_batching_permission_at;
const os = os_batching_permission_at;
const path = path_batching_permission_at;
const exact = exact_plan_count_quoted_frame_ak;
const native = capturedAc;
const owned = owned_plan_count_quoted_frame_ak;
const quote=(s:string)=>s.split('\n').map(row=>'> '+row).join('\n');
// A PTY transports bytes, not command-sized stdin events. Share the framing
// code with the fake CLI so fragmented grants exercise the same receiver.
function commandBuffer(){
let pending='';
return (chunk:string)=>{
pending+=chunk;const commands:string[]=[];let end:number;
while((end=pending.indexOf('\r'))!==-1){commands.push(pending.slice(0,end+1));pending=pending.slice(end+1);}
return commands;
};
}
test('fake CLI preserves command bytes across fragmented and coalesced PTY input',()=>{
const expected=['/plan-ceo-review\r','1\r'];
for(const chunks of [expected,['/plan-ceo-review\r','1','\r'],[...expected.join('')],[expected.join('')]]){
const receive=commandBuffer();expect(chunks.flatMap(receive)).toEqual(expected);
}
const receive=commandBuffer();
expect(receive('1')).toEqual([]);expect(receive('\r2\rtrailing')).toEqual(['1\r','2\r']);
expect(receive('\r')).toEqual(['trailing\r']); // no unexpected bytes are discarded
});
test('the exact wholly quoted AK pane is handled so the dispatcher sends no fallback',()=>{
const screen=quote(exact.screen);
expect(classifyPlanCountFrame(screen)).toBe('permission');
expect(createPlanCountPermissionGuard()(screen,'',undefined)).toBe('handled');
});
test('quoted whole native frames remain inert across redraw, indentation, CRLF and terminal color',()=>{
for(const source of [exact.screen,native.rows[0]!.screen,owned.screen])for(const transform of [
(s:string)=>quote(s),(s:string)=>'\n'+quote(s)+'\n',(s:string)=>quote(s).replace(/^>/gm,' >'),
(s:string)=>quote(s).replaceAll('\n','\r\n'),(s:string)=>'\x1b[31m'+quote(s)+'\x1b[0m',
]){const s=transform(source),g=createPlanCountPermissionGuard();
const expected=classifyPlanCountFrame(s)==='permission'?'handled':null;
expect(g(s,'')).toBe(expected);expect(g(s,'⎿ Wrote 4 lines\n')).toBe(expected);}
});
test('quoted history cannot consume or authorize the current native grant',()=>{
const s=native.rows[0]!.screen,g=createPlanCountPermissionGuard(),q=quote(s);
expect(g(q,'')).toBe('handled');expect(g(s,q)).toBe('grant');expect(g(q,s)).toBe('handled');expect(g(s,q)).toBe('handled');
expect(createPlanCountPermissionGuard()(s,'',null)).toBe('handled');
expect(createPlanCountPermissionGuard()(s,'',{pendingId:'main:first',completedId:null})).toBe('grant');
expect(createPlanCountPermissionGuard()(q,'',{pendingId:'main:first',completedId:null})).toBe('handled');
});
test('unquoted current native frames retain their policy even with quote characters in diff text or history',()=>{
for(const s of native.rows.map(row=>row.screen))expect(createPlanCountPermissionGuard()(s,quote(s))).toBe('grant');
expect(createPlanCountPermissionGuard()('> An old note\n'+native.rows[0]!.screen,'')).toBe('grant');
expect(createPlanCountPermissionGuard()('No current pane',quote(exact.screen))).toBeNull();
expect(createPlanCountPermissionGuard()('> Ordinary quoted prose, without a permission menu')).toBeNull();
});
test.skipIf(process.platform==='win32')('real dispatcher ignores quoted pane then grants the fresh owned native request once',async()=>{
const dir=fs.mkdtempSync(path.join(os.tmpdir(),'count-quoted-frame-')),fake=path.join(dir,'fake-claude'),worker=path.join(dir,'worker.ts'),events=path.join(dir,'events.jsonl'),output=path.join(dir,'result.json'),report=path.join(dir,'report.md');
fs.writeFileSync(report,'original');
fs.writeFileSync(fake,`#!${process.execPath}\nconst receive=(${commandBuffer.toString()})();\n`+String.raw`
import fs from 'node:fs';import path from 'node:path';
const item=JSON.parse(process.env.QUOTED_FRAME_CASE),sid='quoted-frame-main',log=e=>fs.appendFileSync(item.events,JSON.stringify(e)+'\n');
const transcript=path.join(process.env.CLAUDE_CONFIG_DIR,'projects','owned',sid+'.jsonl');fs.mkdirSync(path.dirname(transcript),{recursive:true});
const native=(role,content,extra={})=>fs.appendFileSync(transcript,JSON.stringify({cwd:process.cwd(),sessionId:sid,isSidechain:false,timestamp:new Date().toISOString(),message:{role,content},...extra})+'\n');
native('assistant',[{type:'text',text:'Reviewing the owned fixture.'}]);log({type:'start',pid:process.pid,cwd:process.cwd()});
const settings=JSON.parse(process.argv[process.argv.indexOf('--settings')+1]);
const hook=async name=>{for(const entry of settings.hooks[name]??[]){if(entry.matcher!=='^(Write|Edit)$')continue;
const event={hook_event_name:name,tool_name:'Edit',session_id:sid,tool_use_id:'current',cwd:process.cwd(),transcript_path:transcript,tool_input:{file_path:item.report}};
const p=Bun.spawn(['bash','-c',entry.hooks[0].command],{stdin:new Blob([JSON.stringify(event)]),stdout:'pipe',stderr:'pipe'});
const [code,out,err]=await Promise.all([p.exited,new Response(p.stdout).text(),new Response(p.stderr).text()]);if(code||out||err)throw Error('Hook failed');}};
const pane=item.screen.replaceAll('PLAN.md',item.report),paint=s=>process.stdout.write('\x1b[2J\x1b[H'+s.replaceAll('\n','\r\n'));
let stage='startup';process.stdin.setRawMode?.(true);const dispatch=async input=>{
log({type:'input',stage,input});
if(stage==='startup'){stage='quoted';paint(item.quotedScreen);setTimeout(async()=>{await hook('PreToolUse');stage='current';paint(pane);},4200);return;}
if(stage!=='current'){log({type:'unexpected'});return;}
if(input!=='1\r')throw Error('One-time grant changed');stage='done';await hook('PostToolUse');
const q={header:'Finding',question:'Apply the reviewed fix?',options:[{label:'Fix'},{label:'Keep'}]};
native('assistant',[{type:'tool_use',name:'AskUserQuestion',id:'finding',input:{questions:[q]}}]);native('user',[{type:'tool_result',tool_use_id:'finding',content:'Answered'}],{toolUseResult:{answers:{[q.question]:'Fix'}}});paint('Done.\n');
};process.stdin.on('data',async data=>{const chunk=data.toString();log({type:'chunk',stage,input:chunk});for(const input of receive(chunk))await dispatch(input);});process.on('SIGINT',()=>process.exit(0));process.stdin.resume();
process.stdout.write('PTY_READY:'+item.events+'\x1b[2J\x1b[H');
`);fs.chmodSync(fake,0o755);
// Keep every physical terminal row inside the quote; adding a prefix to an
// already120-column capture would otherwise wrap an unquoted continuation.
const quotedScreen=exact.screen.split('\n').flatMap(row=>row.trimEnd().match(/.{1,116}/gu)??['']).map(row=>'> '+row).join('\n');
const args={skillName:'plan-ceo-review',slashCommand:'/plan-ceo-review',followUpPrompt:'Review the disposable fixture.',expectedPlanPath:report,reviewCountCeiling:1,timeoutMs:25000,startupReadyMarker:'PTY_READY:'+events,env:{QUOTED_FRAME_CASE:JSON.stringify({events,report,screen:owned.screen,quotedScreen})}};
fs.writeFileSync(worker,`import {runPlanSkillCounting} from ${JSON.stringify(pathToFileURL(path.join(import.meta.dir,'helpers/claude-pty-runner.ts')).href)};const result=await runPlanSkillCounting({...${JSON.stringify(args)},isLastStep0AUQ:()=>false,isReviewAUQ:()=>true});await Bun.write(${JSON.stringify(output)},JSON.stringify(result));`);
const child=Bun.spawn([process.execPath,worker],{env:{...process.env,BROWSE_TERMINAL_BINARY:fake,EVALS_HERMETIC:'1'},stdout:'pipe',stderr:'pipe'}),timer=setTimeout(()=>child.kill('SIGKILL'),30000);
try{const [code,out,err]=await Promise.all([child.exited,new Response(child.stdout).text(),new Response(child.stderr).text()]);expect(code,out+err).toBe(0);
const result=JSON.parse(fs.readFileSync(output,'utf8')),rows=fs.readFileSync(events,'utf8').trim().split('\n').map(s=>JSON.parse(s));
expect(result.outcome,JSON.stringify({result,rows})).toBe('ceiling_reached');expect(result.reviewCount).toBe(1);
const chunks=rows.filter(r=>r.type==='chunk');
expect(chunks.every(r=>['startup','current'].includes(r.stage))).toBe(true);
expect(chunks.map(r=>r.input).join('')).toBe('/plan-ceo-review\r1\r');
expect(rows.filter(r=>r.type==='input').map(r=>[r.stage,r.input])).toEqual([['startup','/plan-ceo-review\r'],['current','1\r']]);expect(rows.some(r=>r.type==='unexpected')).toBe(false);
expect(()=>process.kill(rows[0].pid,0)).toThrow();expect(fs.existsSync(rows[0].cwd)).toBe(false);
}finally{clearTimeout(timer);child.kill('SIGKILL');await child.exited;
if(fs.existsSync(events)){const first=JSON.parse(fs.readFileSync(events,'utf8').split('\n')[0]!);try{if(fs.readFileSync('/proc/'+first.pid+'/cmdline','utf8').split('\0').includes(fake))process.kill(first.pid,'SIGKILL');}catch{}}
fs.rmSync(dir,{recursive:true,force:true});}
},32000);
});