Skip to content

test(browser): let a late publication land after its page closed - #2565

Merged
kalvinnchau merged 3 commits into
mainfrom
mtoohey/flaky-late-publication-after-close
Nov 6, 2026
Merged

kalvinnchau merged 3 commits into
mainfrom
mtoohey/flaky-late-publication-after-close

Conversation

@matt2e

@matt2e matt2e commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Summary

The policy relay fixture accepts each publication on a timer after its frame arrives, and if the page closes in between, the acceptance still calls relay.publish. Its delivery check then throws "event must traverse an explicit production channel REQ" from a timer, after the test body has already passed. This branch makes the nested-replies hook journey wait for its reaction to reach the relay before it ends. It also makes the relay skip only that delivery check for an acceptance whose community has no open socket, so the fault can't hit any other journey that ends right after an optimistic publish.

test(threads): wait for the reaction to reach the relay before the hook journey ends

Problem

WebKit's nested-replies.spec.mjs:434 "branch hooks land on each reply's avatar across sides and row shapes" failed on attempt 1 on branch codex/security-history-records, and the same SHA passed on rerun. Every test step passed. The failure was a fixture fault thrown from a timer after the test body ended:

Error: event must traverse an explicit production channel REQ
  at Object.publish (policy-relay.mjs:799)
  at acceptPublication (fixture.mjs:1916)
  at Timeout._onTimeout (policy-relay.mjs:618)

Cause

The journey ends by reacting 👍 to the emoji reply. The UI shows the reaction chip optimistically, so toBeVisible passes before the signed kind-7 publish reaches the relay. The app fixture then closes the page. If the frame reaches the relay just before the close, its acceptance timer fires after the sockets have closed, and relay.publish faults. In the trace, the 👍 click took 1.67 s to land (158701 to 160369 ms). toBeVisible passed at 160400 ms and the page closed at 160568 ms.

Fix

  • After the chip shows, the journey polls app.report.publications for the kind-7 event tagged with the reply's id, as message-actions.spec.mjs does.
  • The fixture records the publication and calls relay.publish in the same synchronous step, so once the report has the event, delivery has already run while the page was open.
  • The file's other journeys aren't exposed in the same way: each either already waits for its final send or doesn't publish at the end.

Validation

Local Linux workstation, Playwright 1.63.0, --no-deps --retries=0:

  • Adversarial ordering, using temporary, uncommitted changes:

    • the app teardown waited until the relay had received the kind-7 frame before closing
    • the relay held that frame's acceptance for 600 ms
    • the worker stayed alive for 1 s after close, as a CI worker does for its next test

    Without the fix: 10/10 failed (5 WebKit, 5 Chromium) with the CI stack. With the fix: 10/10 passed.

  • nested-replies.spec.mjs, "branch hooks land", both engines, --workers=4 --repeat-each=15: 30/30 passed.

  • Full nested-replies.spec.mjs, both engines: 18/18 passed.

test(browser): accept a publication without a delivery check once its page has closed

Problem

Found in code review. The previous commit fixed one journey, but the risk is fixture-wide. For kinds 9, 7, 5 and 40003, an acceptance that fires after the page closed calls relay.publish, whose requireSubscriber check throws because every socket has closed. Any journey that ends right after an optimistic publish can hit this.

Fix

  • When the acceptance timer fires and no socket in the publishing community is open (readyState 1), publish skips the delivery check for that community, for the duration of that acceptance only.
  • The acceptance itself still runs: the fixture records and stores the event, as a relay accepts a frame it already received.
  • If any socket in the community is still open, the check is unchanged. It is also unchanged for every publish outside an acceptance.
  • tests/integration/policy-relay.test.mjs adds a case with mocked timers:
    • a kind-7 frame whose socket closes before the timer fires is accepted, and no fault is recorded
    • the same frame with a socket still open in the community, but no channel REQ, still throws
    • a direct publish with no socket open still throws
  • The explicit wait in nested-replies.spec.mjs stays, so that journey still confirms its reaction reached the relay before it ends.

Validation

  • node --test tests/integration/policy-relay.test.mjs: 2/2 passed. Against the previous relay, the new case fails with the CI error.
  • nested-replies.spec.mjs and message-actions.spec.mjs, both engines, --workers=4: 38/38 passed.
  • biome check --error-on-warnings and git diff --check are clean.
  • Rebased on main f4805605f5: tsc --noEmit, biome and git diff --check are clean. node --test tests/integration/policy-relay.test.mjs passes 2/2. nested-replies.spec.mjs and message-actions.spec.mjs in full, WebKit and Chromium, --workers=4 --retries=0: 38/38 passed.

Only test code changes. No assertions are loosened, and no browser cases are added or removed. A green run doesn't prove the flake is gone. Hosted CI covers broad validation.

Coordination

🤖 Generated with Claude Code

matt2e and others added 2 commits November 5, 2026 02:26
…ok journey ends

## Problem

WebKit's `nested-replies.spec.mjs:434` "branch hooks land on each
reply's avatar across sides and row shapes" failed on attempt 1 on
branch codex/security-history-records, and the same SHA passed on
rerun. Every test step passed. The failure was a fixture fault thrown
from a timer after the test body ended:

- run 41727340697 (webkit 9/14),
  https://github.com/block/buzz-app/actions/runs/41727340697/job/126236093768

    Error: event must traverse an explicit production channel REQ
      at Object.publish (policy-relay.mjs:799)
      at acceptPublication (fixture.mjs:1916)
      at Timeout._onTimeout (policy-relay.mjs:618)

At that SHA, fixture.mjs:1916 is the `relay.publish` for kinds 7, 5 and
40003.

## Cause

The journey ends by reacting 👍 to the emoji reply. The UI shows the
reaction chip optimistically, so `toBeVisible` passes before the signed
kind-7 publish reaches the relay. The test then measures the hook and
ends, and the `app` fixture closes the page. If the frame reaches the
policy relay just before close, its acceptance timer fires after the
sockets have closed. `relay.publish` then finds no channel REQ and
faults.

The trace shows this order. The 👍 click took 1.67 s to land (158701 to
160369 ms). `toBeVisible` passed at 160400 and the page closed at
160568.

Locally the publish is late on every run. The broker logs "Socket
connection interrupted" for each kind-7 publication, because the frame
usually doesn't reach the relay before close at all. The fault needs
the narrower ordering seen in CI: the frame lands, then the page closes
before the acceptance timer fires.

## Fix

After the chip shows, poll `app.report.publications` for the kind-7
event tagged with the reply's id, as `message-actions.spec.mjs` does
for its reaction and this file's first journey does for its final
send. The fixture records the publication and calls `relay.publish` in
the same synchronous step, so once the report has it, delivery has run
while the page was still open.

The other journeys in this file are not exposed in this way. The first
already waits for its final send. The touch journey opens and
dismisses the emoji picker without reacting. The crowded-branch
journeys only make trial clicks, and the linked-reply journey only
follows a link.

Only the test changes (+11 lines). No assertions are loosened, and no
product code changes or browser cases are added or removed. It merges
cleanly with draft #2177 (which edits the crowded-branch journey) and
with #2422 and #2256.

## Validation

Local Linux workstation, Playwright 1.63.0, `--no-deps --retries=0`:

- Adversarial ordering, using temporary, uncommitted changes: the
  `app` teardown waited until the relay had received the kind-7 frame
  before closing, the relay held that frame's acceptance for 600 ms,
  and the worker stayed alive for 1 s after close, as a CI worker does
  for its next test. Without the fix: 10/10 failed (5 WebKit,
  5 Chromium) with the CI stack (`publish` from `acceptPublication`
  from `Timeout._onTimeout`). With the fix: 10/10 passed, and the
  acceptance ran while the socket was still open.
- `bin/pnpm test:browser tests/browser/nested-replies.spec.mjs
  --project webkit --project chromium --no-deps --workers=4
  --repeat-each=15 --grep "branch hooks land"`: 30/30 passed.
- Full `nested-replies.spec.mjs`, both engines: 18/18 passed.
- `biome check` and `git diff --check` are clean.

A green run doesn't prove the flake is gone. Hosted CI covers broad
validation.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: mtoohey <[email protected]>
(cherry picked from commit ba5fd893f8fef8ec5ae8c5ec79b615a58cdfbad9)
… page has closed

## Problem

Code review of "test(threads): wait for the reaction to reach the relay
before the hook journey ends" (suggestion, `tests/browser/policy-relay.mjs`):
that commit fixed one journey, but the risk is fixture-wide. The policy
relay accepts each publication on a `setTimeout` after its frame
arrives. If the page closes in between, the acceptance still runs.
`acceptPublication` then calls `relay.publish` for kinds 9, 7, 5 and
40003, and `publish`'s `requireSubscriber` check throws "event must
traverse an explicit production channel REQ", because every socket has
closed. Any journey that ends right after an optimistic publish can hit
this. It fails from a timer after the test body passed, with the stack
`publish` ← `acceptPublication` ← `Timeout._onTimeout`.

## Fix

When the acceptance timer fires and no socket in the publishing
community is open (`readyState` 1), `publish` skips the delivery check
for that community for the duration of that acceptance. The acceptance
itself still runs: the fixture records and stores the event, as a relay
accepts a frame it already received. Journeys that interrupt the
connection may rely on that. With any socket in the community still
open, and for every `publish` outside an acceptance, the check is
unchanged.

`tests/integration/policy-relay.test.mjs` adds a case with mocked
timers:

- A kind-7 frame whose socket closes before the timer fires is
  accepted, and no fault is recorded.
- The same frame with a socket still open in the community, but no
  channel REQ, still throws.
- A direct `publish` with no socket open still throws.

The explicit wait in `nested-replies.spec.mjs` stays: that journey
should still confirm its reaction reached the relay before it ends.

## Validation

Local Linux workstation, Node `--test` and Playwright 1.63.0
(`--no-deps --retries=0`):

- `node --test tests/integration/policy-relay.test.mjs`: 2/2 passed.
  Against the previous relay, the new case fails with "event must
  traverse an explicit production channel REQ", the CI error.
- `nested-replies.spec.mjs` and `message-actions.spec.mjs`, WebKit and
  Chromium, `--workers=4`: 38/38 passed.
- `biome check --error-on-warnings` and `git diff --check` are clean.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: mtoohey <[email protected]>
(cherry picked from commit ede7da817ce67c5aa905be55fa06f770ec50c9d2)
@matt2e
matt2e requested a review from a team as a code owner November 5, 2026 02:48

@kalvinnchau kalvinnchau left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤖 One non-blocking documentation finding; no functional or security blocker found.

Comment thread tests/browser/nested-replies.spec.mjs Resolved
The comment above the reaction poll in the branch-hook journey still
mentioned the delivery fault after close, which this branch removed from
the policy relay. It now states what the poll asserts: the reaction chip
renders optimistically, so the poll confirms a signed kind-7 tagged with
the reply's id actually reached the relay.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: mtoohey <[email protected]>

@wesbillman wesbillman 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.

🤖 Star Lord — consolidated Guardians source review.

Reviewed head 84f61d58b6c7ff6a6c6597dc05bb5f72e0e4cf3f against base f4805605f56a2f60bd25b966464f07f0c3292a41.

No actionable findings; recommend approval of the reviewed change.

The late-acceptance exception is limited to the publishing community with no open socket, and resets in finally. Supported fixture callbacks deliver synchronously; the held-join promise delays only acknowledgement. Authentication, signature and event validation remain intact, and open-socket/direct-publication delivery checks remain strict. The timer-controlled regression covers those boundaries. The nested-replies wait confirms a signed reaction targeting the reply before continuing, preserving the geometry assertions. The earlier comment-wording feedback is addressed.

Source-only review: no tests, builds or app execution. The author's adversarial/repeat results and original CI trace were not independently reproduced. Rocket, Mantis, Groot and Star Lord completed their independent lanes and integration.

Hosted run 41967248121 at this head passed all 10 changed-journey repetitions per engine. Chromium command wall/summed execution/slowest repetition: 100.381/106.177/10.983 seconds; WebKit: 217.385/331.973/37.219 seconds. The sole selected file was nested-replies.spec.mjs. No comparable before-change timing was inspected, so these figures do not establish a cost improvement or prove all fixture callers.

@kalvinnchau
kalvinnchau merged commit 63eeda2 into main Nov 6, 2026
43 checks passed
@kalvinnchau
kalvinnchau deleted the mtoohey/flaky-late-publication-after-close branch November 6, 2026 16:17
wpfleger96 added a commit that referenced this pull request Nov 6, 2026
…leanup

* origin/main:
  test(mentions): serve the mentions fixture as a build so cold loads fit (#2776)
  test(browser): let a late publication land after its page closed (#2565)
  test(relay): wait for the verified owner before a guest edits an agent (#2413)
  test: wait for the companion default width measurement (#2750)
  test(browser): preserve conversation scroll while revealing actions (#2715)
  test(browser): wait for link hit-testing after thread close (#2792)
  perf(messages): skip micromark for plain prose bodies (#2780)
  perf(inbox): load the emoji name table on first use (#2774)
  perf(messages): cut main-thread work while a busy channel is open (#2729)
  Refine agent Attention cards, watch details, and status controls (#2717)
  fix(bestie): remove a Bestie from its agent card on any device (#2720)
  feat(agents): preview Builderlab agent card links with Add to Buzz (#2719)
tlongwell-block added a commit that referenced this pull request Nov 6, 2026
Co-authored-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Co-authored-by: Tyler Longwell <[email protected]>
Signed-off-by: Tyler Longwell <[email protected]>

* origin/main:
  test(messages): wait for the row measurement frame before thread catch-up dwell (#2837)
  Keep agent coordination history readable after work finishes (#2454)
  test(mentions): serve the mentions fixture as a build so cold loads fit (#2776)
  test(browser): let a late publication land after its page closed (#2565)
  test(relay): wait for the verified owner before a guest edits an agent (#2413)
  test: wait for the companion default width measurement (#2750)
  test(browser): preserve conversation scroll while revealing actions (#2715)
  test(browser): wait for link hit-testing after thread close (#2792)
  perf(messages): skip micromark for plain prose bodies (#2780)
  perf(inbox): load the emoji name table on first use (#2774)
  perf(messages): cut main-thread work while a busy channel is open (#2729)
  Refine agent Attention cards, watch details, and status controls (#2717)
  fix(bestie): remove a Bestie from its agent card on any device (#2720)
  feat(agents): preview Builderlab agent card links with Add to Buzz (#2719)
  Restore macOS Cmd-W window closing when no tab handles it (#2443)
  Make agent transcripts quieter and link finished replies to activity (#2424)
johnmatthewtennant added a commit that referenced this pull request Nov 6, 2026
…ve-enter

* origin/main: (21 commits)
  Make drafts and sent part of Messages (#2728)
  fix(canvas): render every Canvas within the save limit (#2839)
  test(messages): wait for the row measurement frame before thread catch-up dwell (#2837)
  Keep agent coordination history readable after work finishes (#2454)
  test(mentions): serve the mentions fixture as a build so cold loads fit (#2776)
  test(browser): let a late publication land after its page closed (#2565)
  test(relay): wait for the verified owner before a guest edits an agent (#2413)
  test: wait for the companion default width measurement (#2750)
  test(browser): preserve conversation scroll while revealing actions (#2715)
  test(browser): wait for link hit-testing after thread close (#2792)
  perf(messages): skip micromark for plain prose bodies (#2780)
  perf(inbox): load the emoji name table on first use (#2774)
  perf(messages): cut main-thread work while a busy channel is open (#2729)
  Refine agent Attention cards, watch details, and status controls (#2717)
  fix(bestie): remove a Bestie from its agent card on any device (#2720)
  feat(agents): preview Builderlab agent card links with Add to Buzz (#2719)
  Restore macOS Cmd-W window closing when no tab handles it (#2443)
  Make agent transcripts quieter and link finished replies to activity (#2424)
  fix(channels): wrap unread conversation stepping around the sidebar (#2682)
  fix(navigation): let bare buzz:// open Buzz without a broken-link notice (#2718)
  ...
ravarora2 added a commit that referenced this pull request Nov 6, 2026
Co-authored-by: BuzzPRReviewAssistant1 <a581a7e8115222acdc32ffb14bfce55d48c24478d98e155f8eba126b69b83c7b@buzz.block.builderlab.xyz>

* origin/main: (71 commits)
  test(activity): scope preview assertions to the request row (#2843)
  perf(search): fold people names once across palette keystrokes (#2570)
  fix(github): keep PR watches running when localStorage is full (#2699)
  Make drafts and sent part of Messages (#2728)
  fix(canvas): render every Canvas within the save limit (#2839)
  test(messages): wait for the row measurement frame before thread catch-up dwell (#2837)
  Keep agent coordination history readable after work finishes (#2454)
  test(mentions): serve the mentions fixture as a build so cold loads fit (#2776)
  test(browser): let a late publication land after its page closed (#2565)
  test(relay): wait for the verified owner before a guest edits an agent (#2413)
  test: wait for the companion default width measurement (#2750)
  test(browser): preserve conversation scroll while revealing actions (#2715)
  test(browser): wait for link hit-testing after thread close (#2792)
  perf(messages): skip micromark for plain prose bodies (#2780)
  perf(inbox): load the emoji name table on first use (#2774)
  perf(messages): cut main-thread work while a busy channel is open (#2729)
  Refine agent Attention cards, watch details, and status controls (#2717)
  fix(bestie): remove a Bestie from its agent card on any device (#2720)
  feat(agents): preview Builderlab agent card links with Add to Buzz (#2719)
  Restore macOS Cmd-W window closing when no tab handles it (#2443)
  ...

Signed-off-by: Ravneet Arora <[email protected]>
cheng-block added a commit to cheng-block/buzz-app that referenced this pull request Nov 6, 2026
…ime-hint

* upstream/main: (26 commits)
  Make drafts and sent part of Messages (block#2728)
  fix(canvas): render every Canvas within the save limit (block#2839)
  test(messages): wait for the row measurement frame before thread catch-up dwell (block#2837)
  Keep agent coordination history readable after work finishes (block#2454)
  test(mentions): serve the mentions fixture as a build so cold loads fit (block#2776)
  test(browser): let a late publication land after its page closed (block#2565)
  test(relay): wait for the verified owner before a guest edits an agent (block#2413)
  test: wait for the companion default width measurement (block#2750)
  test(browser): preserve conversation scroll while revealing actions (block#2715)
  test(browser): wait for link hit-testing after thread close (block#2792)
  perf(messages): skip micromark for plain prose bodies (block#2780)
  perf(inbox): load the emoji name table on first use (block#2774)
  perf(messages): cut main-thread work while a busy channel is open (block#2729)
  Refine agent Attention cards, watch details, and status controls (block#2717)
  fix(bestie): remove a Bestie from its agent card on any device (block#2720)
  feat(agents): preview Builderlab agent card links with Add to Buzz (block#2719)
  Restore macOS Cmd-W window closing when no tab handles it (block#2443)
  Make agent transcripts quieter and link finished replies to activity (block#2424)
  fix(channels): wrap unread conversation stepping around the sidebar (block#2682)
  fix(navigation): let bare buzz:// open Buzz without a broken-link notice (block#2718)
  ...

Signed-off-by: Cheng Peng <[email protected]>
Co-authored-by: Cursor <[email protected]>
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.

3 participants