Skip to content

test(e2e): give chat.spec's streaming check its own slot - #18038

Merged
KiroCrewCaptain merged 1 commit into
mainfrom
refactor/df-e2e-chat-streaming-spec
Oct 8, 2026
Merged

KiroCrewCaptain merged 1 commit into
mainfrom
refactor/df-e2e-chat-streaming-spec

Conversation

@bolichen97

Copy link
Copy Markdown
Contributor

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:69 waits 5 s for .msg-content to become visible, it stays hidden, the retry passes, and the MAX_FLAKY_SPECS = 0 ceiling 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

  • The other chat.spec.ts tests that share the seeded slot and match with getByText(...).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.
  • The 6 s MCP-init drain every new stub session waits out (_MCP_DRAIN_NO_REPORT_CEILING in acp/session_handle.py). Making the stub report an MCP server so its sessions skip that drain is a fake_acp_backend.py change, logged as a follow-up.
  • playwright.config.ts, its retries, or MAX_FLAKY_SPECS in test/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:

Step What happens on a failing run
1 "sends a chat message" sends the first prompt of a new ACP session into the seeded slot every chat spec opens, and finishes as soon as the input clears. That first prompt waits out the 6 s MCP-init drain, so the turn is still running about 7 s later.
2 "displays streaming response" opens the same slot and sends Hello. The slot is busy, so Hello goes to the queue, not the transcript.
3 .msg-content.first() matches the slot's first seeded prompt. While Hello is queued, that row is the pinned prompt's stand-in, which ChatPage keeps visibility: hidden while the pinned card shows its text.
4 The spec passes only if its first poll beats that switch, or the queue drains inside the 5 s window. CI's failing snapshot shows exactly this state: Hello queued, 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/slots and 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 sends Hello, waits for that prompt in the Chat messages transcript, 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. An afterEach closes 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 Hello and 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, minimal fixture, stub ACP backend), Playwright 1.58.2, --retries=0:

Check Parent (caf10f90f4) This change
Fresh gateway, CI order (active-slot-persistence … capabilities, then chat.spec.ts), 1 worker red 1 of 3 green 3 of 3
Forced condition: backend start delayed 4 s (out-of-tree wrapper on KIROCREW_KIRO_BIN) + slowMo 1000, same order red 3 of 3, CI's exact call log (9 × locator resolved to <div … user-bubble …>, unexpected value "hidden") green 3 of 3
Negative control: prompt Hello [[ERROR]] (the stub fails the turn, no reply) — red at the reply wait, getByText('pong from the fake ACP backend'), at the ceiling
chat.spec.ts --repeat-each=30 --workers=1 (CI's worker count) — 331 passed, 0 flaky; this spec 30/30, 9.3–9.9 s
-g "displays streaming response" --repeat-each=40 --workers=4 --fully-parallel, three fresh gateways — 120 of 120 green
chat.spec.ts --repeat-each=10 --workers=4 --fully-parallel this spec red 1 of 10 this spec green 10 of 10
Full offline suite through test/test_playwright_e2e.py::test_dashboard_playwright_suite (KIROCREW_E2E=1, CI=1, so retries on) — 323 executed, 0 failed, 0 flaky, 0 skipped

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.py 53 passed (a mutation adding an isVisible branch to this file reds it, so it reads the change); semgrep --config semgrep/ 0 findings; strict tsc --noEmit on the spec clean. scripts/local-gate.py --dry-run selects 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

  • At most two commits (one is the norm), with a Conventional Commits title (feat|fix|docs|style|refactor|perf|test|chore|ci|build|revert: ...)
  • Existing tests pass and new tests added for new functionality
  • Self-review completed; code follows project style guidelines
  • Documentation updated (if applicable)
  • No secrets, credentials, or internal references in the diff

Contribution License Agreement

Contributions are accepted under the project's Apache-2.0 license; see the Licensing section of CONTRIBUTING.md.

@bolichen97
bolichen97 requested a review from a team as a code owner October 8, 2026 06:31
@github-actions github-actions Bot added the readiness: checking Automated validation is still running label Oct 8, 2026
@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

First Principles Review (Opus 5.5) — ⏭️ skipped

Revision a237d8823125da4a9a3acb202a05fd2e3348b80a ships no reviewable capability (docs, tests or generated files only), so there is nothing to inventory. Advisory — does not block merge.

@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Design Review (Opus 5.5) — ✅ PASS

Design-level review of a237d8823125da4a9a3acb202a05fd2e3348b80a — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

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 .github/scripts/pr-description-capture.sh over the same inputs; if it differs, the description or that evidence changed after this verdict was formed and any finding drawn from either is unproven.

@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

UX Review (Opus 5.5) — ✅ PASS

UX-level review of a237d8823125da4a9a3acb202a05fd2e3348b80a — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

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 .github/scripts/pr-description-capture.sh over the same inputs; if it differs, the description or that evidence changed after this verdict was formed and any finding drawn from either is unproven.

@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

GPT 6.1 Review — ✅ no blocking findings

GPT 6.1 completed its review of a237d8823125da4a9a3acb202a05fd2e3348b80a and found no blocking issues.

This comment is updated in place on each push.

Review details

No findings.
[GPT-REVIEWED] a237d88

False positive or not applicable? A repository writer can comment:
/ai-review override gpt a237d8823125da4a9a3acb202a05fd2e3348b80a: <one-sentence reason>

@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Opus 5.5 Review — ✅ no blocking findings

Reviewed a237d8823125da4a9a3acb202a05fd2e3348b80a — this comment is updated in place on each push.

Review details

No findings.

[OPUS-REVIEWED] a237d88

Verdict parsed from the review's SHA-scoped output markers for commit a237d8823125da4a9a3acb202a05fd2e3348b80a.

False positive or not applicable? A repository writer can comment:
/ai-review override fable a237d8823125da4a9a3acb202a05fd2e3348b80a: <one-sentence reason>

@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Oct 8, 2026
"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
@bolichen97
bolichen97 force-pushed the refactor/df-e2e-chat-streaming-spec branch from 0de0cff to a237d88 Compare October 8, 2026 08:10
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: passed Eligible automated validation passed for the current revision and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Oct 8, 2026
@KiroCrewCaptain
KiroCrewCaptain enabled auto-merge (squash) October 8, 2026 09:47

@KiroCrewCaptain KiroCrewCaptain left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@KiroCrewCaptain
KiroCrewCaptain merged commit 791b10a into main Oct 8, 2026
106 checks passed
@KiroCrewCaptain
KiroCrewCaptain deleted the refactor/df-e2e-chat-streaming-spec branch October 8, 2026 09:48
@github-actions github-actions Bot removed the readiness: passed Eligible automated validation passed for the current revision label Oct 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Flaky: chat.spec.ts › displays streaming response passes only on a retry in E2E (stub ACP backend, offline)

2 participants