From 9be198011daa2baebca8cb922abe4fe57e0074e2 Mon Sep 17 00:00:00 2001 From: "prompt.ac/@jeffrey" Date: Thu, 17 Sep 2026 18:47:23 -0700 Subject: [PATCH] Add opt-in single-window gate to Captutor Stage --- slab/captutor/lib/single-tab.mjs | 8 +++++--- slab/captutor/test/single-tab.test.mjs | 3 +++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/slab/captutor/lib/single-tab.mjs b/slab/captutor/lib/single-tab.mjs index 5d8cf042d7..563cad856a 100644 --- a/slab/captutor/lib/single-tab.mjs +++ b/slab/captutor/lib/single-tab.mjs @@ -1,14 +1,16 @@ // Read-only recording gate: never close or navigate unrelated tabs. -export async function assertSingleRecordingTab(cdp){ +export async function assertSingleRecordingTab(cdp,{singleWindow=false}={}){ const current=await cdp.send('Browser.getWindowForTarget'); const {targetInfos}=await cdp.send('Target.getTargets'); if(!Array.isArray(targetInfos))throw Error('Cannot inspect recording-window tabs'); - let count=0; + let count=0;const windows=new Set(); for(const t of targetInfos){ if(t.type!=='page')continue; const {windowId}=await cdp.send('Browser.getWindowForTarget',{targetId:t.targetId}); + windows.add(windowId); if(windowId===current.windowId)count++; } if(count!==1)throw Error(`Recording window has ${count} tabs; move the project tab into its own window before recording. Preserve all other tabs.`); - return {windowId:current.windowId,tabCount:count}; + if(singleWindow&&windows.size!==1)throw Error(`Stage sees ${windows.size} Chrome windows; close completed mission verification/preview windows before recording. Preserve unrelated work and pause capture if it cannot be isolated.`); + return {windowId:current.windowId,tabCount:count,windowCount:windows.size}; } diff --git a/slab/captutor/test/single-tab.test.mjs b/slab/captutor/test/single-tab.test.mjs index 43547194ff..a91fc3b2de 100644 --- a/slab/captutor/test/single-tab.test.mjs +++ b/slab/captutor/test/single-tab.test.mjs @@ -3,3 +3,6 @@ const mock=windows=>({send:async(method,p)=>{if(method==='Target.getTargets')ret test('one recording tab accepts other windows without mutation',async()=>assert.equal((await assertSingleRecordingTab(mock([1,2,2]))).tabCount,1)); test('extra recording tab fails without closing it',async()=>assert.rejects(assertSingleRecordingTab(mock([1,1])),/2 tabs/)); test('missing recording target fails closed',async()=>assert.rejects(assertSingleRecordingTab(mock([2])),/0 tabs/)); + +test('Stage rejects a second window without closing it',async()=>assert.rejects(assertSingleRecordingTab(mock([1,2]),{singleWindow:true}),/2 Chrome windows/)); +test('Stage accepts exactly one window and tab',async()=>assert.equal((await assertSingleRecordingTab(mock([1]),{singleWindow:true})).windowCount,1)); -- 2.51.2