Repository navigation
Conversation
A render while a merge poll was in flight called scheduleMergePoll and
started a second poll chain in the same generation, so an older poll's
answer ("queued") or error could land after a newer one ("merged").
The page now tracks the poll in flight by generation and sequence. A
render while it runs starts no new poll; only the latest poll's answer
or error is applied, and only while its generation is current. An error
thrown while applying a current answer is still reported and polling
continues, and the merge click clears the old poll timer.
Four browser regressions cover a re-render during a held poll, a held
error, an older generation's late poll, and an unusable current answer;
each fails when its guard is removed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟢 Approval recommended
No blocking issues were identified in the focused polling changes, recovery paths, or regression coverage.
0 open findings
What changed in this PR
Addresses the browser portion of #48 by preventing overlapping merge poll chains and stale responses from overwriting current UI state.
Changes:
- Tracks poll ownership by generation and sequence.
- Adds regressions for ordering, stale responses, and error recovery.
- Documents polling guarantees and validation limits.
| File | Description |
|---|---|
web/public/app.js |
Guards poll ownership, settlement, and timer cleanup. |
test/browser/issue-48-stale-errors.spec.ts |
Tests held errors and unusable-response recovery. |
test/browser/issue-48-poll-ordering.spec.ts |
Tests single-chain polling and cross-generation isolation. |
docs/implementation/merge-queue.md |
Documents polling behavior and regression evidence. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…nswer (#48) The item selection re-renders and clears the banner, so the banner check could not see a late applied answer; the control shows "Merged" if it was applied. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Part of #48: the browser part. The server part merged as #171. This PR is stacked on F6b (#161,
codex/f6b-merge-handoff), which rewrites merge polling inweb/public/app.js.Defects
Both were reproduced on F6b at 2a9534d:
renderMerge()callsscheduleMergePoll()on every render. While a poll was in flight its timer was already cleared, so any render (for example selecting another item) started a second poll chain in the samemergeGeneration. Nothing ordered the two answers, so an older "queued" answer could be applied after a newer "merged" one, and the control went back to "Merge queued".Fix
One merge poll chain at a time (
web/public/app.js):mergePollInFlight = { generation, sequence }is set when a poll starts.scheduleMergePoll()starts nothing while a poll of the current generation is in flight; that poll reschedules itself when it settles.docs/implementation/merge-queue.mddescribes the single poll chain.Traced, not tested:
/api/merge.Tests
test/browser/issue-48-poll-ordering.spec.ts:test/browser/issue-48-stale-errors.spec.ts:Break checks:
app.js, the specs fail with 2 polls where 1 is expected.F6b has since merged
main(#170 madeload()/act()async). 63b5525 merges that F6b (445081f) in: the specs nowawaitthe review service, and themerge-queue.mdconflict keeps main's #48 regression list with this PR's browser sentence. F6b's new click answers (mergeRefreshRequired, an already-ended job) do not interact with the guard: the click raises the generation, so an older in-flight poll neither applies nor blocks the job's poll.Validation
Current head 8bdeaf9, base codex/f6b-merge-handoff cffdf4c (#161, now based on
mainafter F6a #153 merged as 209707e). This PR's diff has been byte-identical since fb8915b; the later merges brought in #161 changes only.plans.spec.ts,review.spec.tsand the four #48 specstestEarlier checkpoints: d21cb0e on 7b1b5fa (CI
testpass; Copilot 2 clean); ec2e2e4 on 2a9534d (browser 102/102, CItestpass).Review record
settle()applying every answer, the test fails.settle()applying every answer, the new check alone fails. Removing the generation check fails three existingplans.spec.tstests.settle()accepts it; a full response that keeps a newer job record starts no second applied chain.Review-lesson audit
A second poll chain started by a render: covered by AGENTS.md "Never apply a background response without proving it is still current", and the race "Poll starts, then a user action completes, then the old poll returns".
The swallowed error and stopped polling: covered by "Preserve the original timeout, cancellation, and shutdown reason through every layer. Do not replace actionable errors…".
Specs that did not assert their interleaving, or could hang: covered by "A race regression must assert that the interleaving it names actually happened" (added in Keep merge stop reasons and GitHub answers through every layer (#48) #171).
The merge click timer and docs: covered by "Treat every behavioural claim … as something to verify".
An assertion on a banner that a re-render had already cleared (round 6): covered by "A race regression must assert that the interleaving it names actually happened" and the Design system and plan format #1 rule of E2E tests.
No new rules.
🤖 Generated with Claude Code