Uh oh!
There was an error while loading. Please reload this page.
fix(e2e): poll for the tutor turn's rows instead of reading once (#477) - #500
Conversation
The persistence assert read `messages` a single time, immediately after the reply text rendered. That treats "the reply is on screen" as "the rows are committed", and they are not the same signal. The server side is fine: stream_agent_turn persists inside on_complete and only THEN yields `done` (services/chat_stream.py:372-393), so the write does precede the end of the turn. But the composer renders off the streamed TOKENS, so both UI assertions can pass while `done` is still in flight — leaving a window where the one-shot read sees only the 4 seeded rows. That is exactly the observed failure (expected 6, received 4, UI assertions green). Replaces the read with a bounded expect.poll on the row count, the idiom events.spec.ts already uses for its fire-and-forget rollup. Root fix rather than a retry, per the #388 zero-flake policy. Note: a ~1-in-6 race is not reproducible on demand, so this is verified by the persist-before-done contract above plus repeated green cycles, not by a watched-red test. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This pull request has been ignored for the connected project Preview Branches by Supabase. |
Deploying with |
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs | frontend-staging | 749c76e | Commit Preview URL Branch Preview URL | Jul 31 2026, 05:55 PM |
Warning Review limit reached
Next review available in:51 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe tutor E2E test now polls the database for the expected persisted user and assistant messages before validating their roles and encrypted contents. ChangesTutor persistence validation
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Code review: polling the count and then re-querying for the content assertions decoupled two checks the original single read had joined. A duplicate-persistence regression could land a row in that gap and still slice two valid-looking rows off the end — exactly what this journey exists to catch. Capture inside the predicate, matching events.spec.ts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
AndresL230
commented
Jul 31, 2026
Code reviewFound 1 issue, fixed in 749c76e.
A duplicate-persistence regression could land a row in that gap; the poll's exact https://github.com/SaplingLearn/Sapling/blob/749c76e/frontend/e2e/tutor.spec.ts#L88-L101 Checked and cleared: the poll is not weaker than the original assert (same exact 🤖 Generated with Claude Code |
Part of #477.
Cause
The persistence assert read
messagesonce, immediately after the reply text rendered — treating "the reply is on screen" as "the rows are committed". Those are different signals.The server side is correct:
stream_agent_turnpersists insideon_completeand only then yieldsdone(backend/services/chat_stream.py:372-393), so the write does precede the end of the turn. But the composer renders off the streamed tokens, so both UI assertions can pass whiledoneis still in flight — leaving a window where the one-shot read sees only the 4 seeded rows. That matches the reported failure exactly (expected 6, received 4, UI assertions green).So this is a test-side assumption, not a product regression — worth stating, since the alternative reading (rows should already be there) would have pointed at a real persistence bug.
Fix
Bounded
expect.pollon the row count, the idiomevents.spec.tsalready uses for its fire-and-forget rollup. Root fix rather than a retry, per the #388 zero-flake policy.Honest limitation
A ~1-in-6 race is not reproducible on demand, so this is not verified by a watched-red test. It rests on the persist-before-
donecontract above plus green cycles. What the change does guarantee: the assert can no longer fail because it read too early — it now waits for the condition it depends on, or fails after 5s.Gates
tsc clean; full local cycle green (Playwright 37/37, oracles 0 findings).
🤖 Generated with Claude Code
Summary by CodeRabbit