test(e2e): fix project-outcomes thread-panel flake — bridge seed race + mock relay ignoring filter.ids - #131
Merged
oscarlehuu merged 4 commits intoAug 11, 2026
Conversation
Author
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
This was referenced Aug 11, 2026
Merged
Wait for the E2E bridge before seeding project outcome threads and make mock history honor id filters.\n\nFixes #125 Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
devin-ai-integration
Bot
force-pushed
the
devin/1786366224-fix-125-project-outcomes-seed-race
branch
from
August 11, 2026 09:29
23a8a7b to
bfae5c1
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
It was a spec bug, not a product bug — specifically a test-setup race, not a superseded surface. The outcome-first Projects view from #95 mounts the in-place thread panel correctly; the spec was seeding its thread root before the E2E bridge existed, and swallowing the miss.
project-outcomes.spec.tsnavigates withwaitUntil: "domcontentloaded"and then immediately calls the bridge global with optional chaining:domcontentloadeddoes not imply the E2E bridge module has executed. When the bundle is still evaluating — which is what load does — the global is absent,?.turns the miss into a silent no-op, and the root event is never seeded. The panel's root fetch then finds no such event,thread.threadHeadisnull, andProjectOutcomeThreadPanelrenders its error branch, which carriesproject-in-flight-panelbut nomessage-thread-panel. That is exactly the observed shape: line 107 (project-in-flight-panelvisible) passes, line 108 (message-thread-panel) fails.Instrumenting the mock bridge and catching a natural failure captured the smoking gun — at seed time the bridge was
{ "available": false }, and at the failing root fetch the requested id was absent from the channel store while an unrelated kind-9 event was returned instead.Two changes:
desktop/tests/e2e/project-outcomes.spec.ts— wait for the bridge globals before using them, and fail loudly instead of silently skipping. This follows the convention already used in the repo (channel-dense-second-reach.spec.ts:35,active-turn-resilience.spec.ts:27) rather than adding a new helper. No assertion is relaxed, skipped, or deleted; the silent?.no-op is what hid this for days, so the seed now also asserts the returned event id.desktop/src/testing/e2eBridge.ts—emitMockHistorynow honorsfilter.ids(same exact/prefix semantics as the existing id-only path). It previously filtered onkinds/since/untilonly, so a filter carrying both#handidssilently degraded to "newest event in the channel". The panel's root fetch is{ ids, kinds, "#h", limit: 1 }, so without this the spec would stay latently flaky even with correct seeding — it would only pass while the seeded root happened to sort first undercreated_at DESC, id ASC. Demonstrated deterministically: a competing same-second event with id000…0wins the tiebreak overproject-outcome-thread, and the panel rendersUnable to load this thread.The only production caller of the combined#h+idsshape isProjectOutcomeThreadPanel.tsx:53, so the blast radius is contained.No product code changed. No
docs/change: this is test/harness-only and does not move shipped state, so the STATE.md anti-drift rule doesn't apply. NoDECISIONS.mdentry — no product decision was forced, so no D-number was consumed (leaving D-028/D-029/D-030 to #120).Related issue
Fixes #125.
Related but deliberately untouched: #109 (
Desktop Smoke E2E (4)dead lane, ~19 upstreamproject-*specs, owned by another session on a separate branch). This PR does not touch any upstreamproject-*spec and does not add or duplicate that session'sproject-plumbing<details>helper.Testing
Red -> green on the reported failure. The failure is load-dependent — it passes in isolation on
main, which is why it read as a hard break only when triaged inside the shard.RED, on unmodified
main,--repeat-each=20 --retries=0:Every failure identical:
with the error context showing the panel's error branch:
GREEN, same commands on this branch:
Plain reported command, from
desktop/:Gates (
. ./bin/activate-hermitfirst):Because
emitMockHistoryis shared channel-history code, a broader slice was run beyond the target spec: the sixproject-*.spec.tsfiles in one invocation (--retries=0) went from22 failed / 17 passedto21 failed / 18 passed, withproject-outcomes.spec.tsmoving into the passed column; the remaining failures are the preexisting shard failures tracked by #109 and are unaffected by this change either way. A 103-spec channel/thread slice ran83 passed / 20 failedon this branch; the identical slice onmain(35af74019, separate build, same workers and--retries=0) also ran83 passed / 20 failed, and the failing spec sets are identical (empty symmetric difference). So none of those 20 come from makingemitMockHistoryhonorfilter.ids.CI
Red checks on this PR are preexisting on the base commit.
mainrun31362178966is on35af74019— the exact commit this branch builds on — and already shows:Those are the same two shards red here, and shard 4 is the #109 lane.
channels.spec.ts:500is #110. Every individual failing test from shards 1 and 3 was additionally re-run locally against unmodifiedorigin/mainand reproduced there.No UI change, so no screenshots: the product surface is unchanged and already rendered correctly before this PR.
Link to Devin session: https://app.devin.ai/sessions/48126c669ed543be891e81905624328d
Requested by: @oscarlehuu