Repository navigation
test(e2e): give chat.spec's streaming check its own slot - #18038
Conversation
First Principles Review (Opus 5.5) — ⏭️ skippedRevision |
Design Review (Opus 5.5) — ✅ PASSDesign-level review of Design-Verdict: PASS Nothing to check. [DESIGN-REVIEWED] a237d88 Description read[DESCRIPTION-READ] b3a98f5b4fc2645c122b9add7a433546c5c1b0f58efbab023ab884a68f0f5bce That sha256 names everything this verdict read: the PR description, plus the 1 evidence file(s) the workflow collected from it and handed this review. It covers that evidence because the media strip replaces every attachment URL with the same placeholder, so an edit swapping one attachment for another would leave the description's captured text identical while what this review judged changed. Recompute it with |
UX Review (Opus 5.5) — ✅ PASSUX-level review of UX-Verdict: PASS Nothing to check. [UX-REVIEWED] a237d88 Description read[DESCRIPTION-READ] bf367ed69551a4c99376707cc4992f9fb0a51a3e65c91be4d84b8ba5e63b611c That sha256 names everything this verdict read: the PR description, plus the 3 evidence file(s) the workflow collected from it and handed this review. It covers that evidence because the media strip replaces every attachment URL with the same placeholder, so an edit swapping one attachment for another would leave the description's captured text identical while what this review judged changed. Recompute it with |
GPT 6.1 Review — ✅ no blocking findingsGPT 6.1 completed its review of This comment is updated in place on each push. Review detailsNo findings. False positive or not applicable? A repository writer can comment: |
Opus 5.5 Review — ✅ no blocking findingsReviewed Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
"displays streaming response" sent Hello into the seeded slot every chat spec opens. The spec before it leaves the first turn of a new ACP session running there (that turn waits out the 6 s MCP-init drain, as the stub reports no MCP server), so Hello was only queued. The assertion, .msg-content first(), matched the slot's first seeded prompt, which ChatPage hides as the pinned prompt's stand-in while Hello is queued. It passed only when its first poll or the queue drain won that race, so the zero-flaky ceiling failed the E2E job on unrelated pull requests. The spec now creates its own slot, waits until that slot is open and the gateway connected, sends Hello, and waits for its own prompt and then the stub's reply in the transcript. The reply wait is a named 17 s ceiling: the new session's fixed 6 s MCP-init drain plus 10x the ~1.1 s measured rest, under a 36 s timeout for this test alone. It closes the slot afterwards. Fixes #17857
0de0cff to
a237d88
Compare
KiroCrewCaptain
left a comment
There was a problem hiding this comment.
Approved on maintainer request after review: test (1 file). The streaming check now runs in a slot it creates through the API, waits for the connected slot row before sending, asserts the prompt and the stub's exact reply in that slot's transcript, and closes the slot afterwards (closing archives, deletes nothing). The 17 s reply ceiling is derived from the 6 s MCP-init drain plus measured headroom and is written down next to the constant.
Problem / Motivation
Goal: The offline E2E job stops failing unrelated pull requests on
chat.spec.ts › displays streaming response, because that spec now checks the reply to its own prompt instead of racing the turn the spec before it left running.E2E (stub ACP backend, offline)goes red on pull requests that touch no chat code:chat.spec.ts:69waits 5 s for.msg-contentto become visible, it stays hidden, the retry passes, and theMAX_FLAKY_SPECS = 0ceiling fails the job. Six sightings on 2026-10-07/08 (#17159, #17847, #17922, #17930, #17925, #17828), recorded in #17857.Why it matters
Since the zero-flaky ceiling landed (#17673), this one spec can fail the E2E job of any pull request, whatever its diff. Authors rerun or wait, and a real E2E regression is harder to see behind it.
Not a goal
chat.spec.tstests that share the seeded slot and match withgetByText(...).first()("sends a chat message and displays it", "shows typing indicator when sending message"). They have not flaked; the file-wide clean-up is DEFLAKE_PLAN scope F12._MCP_DRAIN_NO_REPORT_CEILINGinacp/session_handle.py). Making the stub report an MCP server so its sessions skip that drain is afake_acp_backend.pychange, logged as a follow-up.playwright.config.ts, its retries, orMAX_FLAKY_SPECSintest/test_playwright_e2e.py.What changed (motivation → approach → change)
The flake is a race between two specs on one shared slot, plus an assertion that never looked at this spec's own turn:
Hello. The slot is busy, soHellogoes to the queue, not the transcript..msg-content.first()matches the slot's first seeded prompt. WhileHellois queued, that row is the pinned prompt's stand-in, whichChatPagekeepsvisibility: hiddenwhile the pinned card shows its text.Helloqueued, the earlier prompt "Thinking…", the pinned card on the first seeded prompt.The spec now works in a slot of its own: it creates one through
POST /api/chat/slotsand opens it with?sid=. Before typing it waits until the sidebar row for that slot is the active one and the composer is not showing its offline placeholder, because until then a send can go nowhere or into a slot the page creates itself. It sendsHello, waits for that prompt in theChat messagestranscript, then for the stub's reply,pong from the fake ACP backend, and checks the slot is still the active one. The reply wait is a named ceiling,FIRST_REPLY_CEILING_MS= 17 s: a new session's first reply measured 7.05–7.10 s here, a fixed 6 s timer plus ~1.1 s that load moves, so the ceiling is the timer plus 10x the rest. The test runs under its own 36 s timeout, so that ceiling stays below half of it. AnafterEachcloses the slot the spec created, which archives it to history.Backwards compatibility
Compatible: one Playwright spec changes; no product code, config, fixture or gate file is touched.
Removes nothing: the spec still sends
Helloand asserts on the transcript; it asserts more (its own prompt, then the backend's reply), and the one slot it adds is closed after it.Tests
website/playwright/chat.spec.ts› "displays streaming response" now pins: the prompt this spec sent reaches the transcript (it was sent, not queued), and the stub's streamed reply follows it in the same slot.Proof, all local against an isolated harness gateway (
spawn_feature_gateway,minimalfixture, stub ACP backend), Playwright 1.58.2,--retries=0:caf10f90f4)active-slot-persistence…capabilities, thenchat.spec.ts), 1 workerKIROCREW_KIRO_BIN) +slowMo1000, same order9 × locator resolved to <div … user-bubble …>,unexpected value "hidden")Hello [[ERROR]](the stub fails the turn, no reply)getByText('pong from the fake ACP backend'), at the ceilingchat.spec.ts --repeat-each=30 --workers=1(CI's worker count)-g "displays streaming response" --repeat-each=40 --workers=4 --fully-parallel, three fresh gatewayschat.spec.ts --repeat-each=10 --workers=4 --fully-paralleltest/test_playwright_e2e.py::test_dashboard_playwright_suite(KIROCREW_E2E=1,CI=1, so retries on)In the 4-worker whole-file run, "navigates to chat page" and "stop resolves to Stop Failed on budget expiry" fail on the parent too (8 and 2 of 10, plus "stop mid-tool-call" 2 of 10): they share the seeded slot, and CI runs this suite with one worker.
Static:
test/test_flake_pattern_ratchet.py53 passed (a mutation adding anisVisiblebranch to this file reds it, so it reads the change);semgrep --config semgrep/0 findings; stricttsc --noEmiton the spec clean.scripts/local-gate.py --dry-runselects 754 backend cross-surface files and 1 frontend file (the spec itself, which Vitest does not run), so per the campaign's host budget the backend set is left to CI.Manual verification
N/A — the change is one Playwright spec; the runs above exercise it against a real gateway.
Related Issues
Fixes #17857
Part of #16536
Checklist
feat|fix|docs|style|refactor|perf|test|chore|ci|build|revert: ...)Contribution License Agreement
Contributions are accepted under the project's Apache-2.0 license; see the Licensing section of CONTRIBUTING.md.