From 01ec12e77cc261defabda2e05746cd5423d34be8 Mon Sep 17 00:00:00 2001 From: weichen Date: Wed, 14 Oct 2026 03:57:38 +0000 Subject: [PATCH] Wait for settled material notices in browser checks Why: A failed task card can appear before its notification turn, and the second finding must be observed while work is still running. Evidence: The strengthened browser checks first exposed missing fixture notice text and a zero-width second-stage window; after fixture correction all six task-owned HTTP/Playwright scenarios passed. Frontend 265 unit tests and build passed. Impact: Test coverage only; no production UI change. Refs: U3 r0.4 independent review Important 5 --- e2e/work-checkpoints.playwright.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/e2e/work-checkpoints.playwright.ts b/e2e/work-checkpoints.playwright.ts index 20ba4a4..e2cdeaf 100644 --- a/e2e/work-checkpoints.playwright.ts +++ b/e2e/work-checkpoints.playwright.ts @@ -25,7 +25,19 @@ async function setup(request:any,scenario:Scenario='normal',awaitTask=true){ const out=await(await request.get(manifest.base+`/api/conversations/${conv.id}/cards`)).json() return out.cards.find((c:any)=>c.task)?.task.executionState }).toBe(scenario.endsWith('failure')?'failed':'completed') - return {project,conv,ids} + return {project,conv,ids,sendRunId:sendBody.run.id} +} +async function waitForFailureNotice(request:any,seed:any){ + await expect.poll(async()=>{ + const response=await request.get(manifest.base+`/api/conversations/${seed.conv.id}`) + if(!response.ok())return false + const current=await response.json() + const children=current.runs.filter((r:any)=>r.kind==='subagent') + const notices=current.messages.filter((m:any)=>m.role==='system'&&m.parts?.some((p:any)=>p.type==='text'&&p.text?.includes('后台子代理「'))) + const noticeRuns=current.runs.filter((r:any)=>r.kind==='conversation'&&r.id!==seed.sendRunId) + return children.length>0&¬ices.length>=children.length&¬iceRuns.length>=children.length&& + noticeRuns.every((r:any)=>['completed','failed','interrupted'].includes(r.status)) + }).toBe(true) } async function open(page:any,seed:any){ await page.addInitScript(({id}:{id:string})=>{localStorage.setItem('as.conv',id);localStorage.setItem('as.ui',JSON.stringify({theme:'light',rightCollapsed:true,fontSize:14}))},{id:seed.conv.id}) @@ -69,19 +81,21 @@ test('two sourced stages appear while the material task is still running',async( const cards=async()=>(await(await request.get(manifest.base+`/api/conversations/${seed.conv.id}/cards`)).json()).cards await expect.poll(async()=>{const card=(await cards()).find((c:any)=>c.task);return card?.task.executionState}).toBe('running') await expect(page.locator('[data-conversation-work]')).toContainText('上线六月') - await expect.poll(async()=>{const card=(await cards()).find((c:any)=>c.task);return card?.task.findings?.length}).toBeGreaterThan(1) + await expect.poll(async()=>{const card=(await cards()).find((c:any)=>c.task);return card?.task.executionState==='running'&&card.task.checkpointSeq>=2&&card.task.findings?.length>1}).toBe(true) await expect(page.locator('[data-conversation-work]')).toContainText('验收条件需要补齐') await expect.poll(async()=>{const card=(await cards()).find((c:any)=>c.task);return card?.task.executionState}).toBe('completed') }) for(const scenario of ['zero-failure','partial-failure'] as const){ test(`${scenario} shows the saved scope without a deliverable choice`,async({page,request})=>{ const seed=await setup(request,scenario) + await waitForFailureNotice(request,seed) await page.addInitScript(({id}:{id:string})=>{localStorage.setItem('as.conv',id);localStorage.setItem('as.ui',JSON.stringify({theme:'light',rightCollapsed:true,fontSize:14}))},{id:seed.conv.id}) await page.goto('/') const work=page.locator('[data-conversation-work]') await expect(work).toContainText('执行失败') if(scenario==='zero-failure')await expect(work).toContainText('暂无阶段结论记录') else await expect(work).toContainText('上线六月') + await expect(page.getByText('后台整理状态已回投工作卡;请按卡片中的结果继续。').last()).toBeVisible() await expect(page.locator('.hitl-choice')).toHaveCount(0) }) }