Repository navigation
F6b: one-click merge job with a required-check wait - #161
Merged
Merged
Conversation
prepare-merge now pushes a rebased head whose review is still clear over exactly the PR head it rewrote, before running command checks. A schema v18 marker (tasks.push_in_progress) is written before the first external write and cleared only after a read of the branch settles the outcome; while it exists, merge, runner admission, status changes, rebases and other pushes refuse. Preparation and a tracked startup job settle an interrupted push. GitBranchPusher gains an exact expected-head lease and a branch read; remotePair reports the task PR's branch. Part of #22 (F6a). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Push settlement reads now abort only on shutdown, so a cancel can no longer strand the marker; the in-run read is bounded by the remaining preparation deadline. Startup settlement runs under the verified runner lock and closes the PRs of a task it closed. After a push, preparation waits (bounded) until GitHub's PR API reports the pushed head, and a move seen then is reviewed from the pushed head. Adds regressions for each, plus production push wiring and server startup wiring tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GitHub can report a moved base while its PR API still shows the pre-push head. The visibility wait now ends only when the head changes, so the review is never refreshed back onto a head the branch no longer holds. The test remote's branch read honours its abort signal, so a cancel that stopped push settlement would fail a test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The visibility wait starts no inspection after 30 seconds, so it lasts at most 30 seconds plus one inspection. Say so in the code and the docs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A cancel requested while no preparation runs and a push marker remains now starts the tracked settlement job, so the task and its PRs close without a restart. After a refused push, preparation waits for GitHub's PR API to report the branch's new head before reviewing it, as it does after a push. Renames settleAtStartup to settlePendingPush and corrects the architecture doc's not-built list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A preparation that fails waiting for GitHub to show the pushed head now binds its failure to the post-push task state, so it is not reported as stale at once. The startup-settlement server test now builds the state a crash leaves (a push marker plus a pending cancel) instead of having the fake job cancel the task itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Repeating a cancel that waits on an unsettled push now reads the branch again instead of refusing, so the task can close without a restart. Adds push-boundary regressions for a pending cancel and a changed snapshot, and documents the retry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After a push or a refusal, a moved PR head is now reviewed by tracing history from the reviewed head and its rewrite lineage, nearest first, so an earlier rewrite codeboost already pushed is reviewed against its own base instead of the original one. Any failure after the push or refusal is now bound to the post-push task state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds regressions for a plan revision applied at the push boundary (only the task state version changes, so that check is load-bearing), for the visibility wait ending at the preparation deadline, for the settlement read's deadline bound, and for shutdown aborting a startup settlement. States in the docs that a killed push may still land later, harmlessly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds regressions for a cancel during the next preparation's push settlement, attempt admission and reassignment refusing while a push is in flight, and shutdown starting no branch read. The push-boundary cancel test now cancels through the coordinator while the push reads the remote, as production does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A failed push's settlement read can take up to 30 seconds. The failure now adopts the task state version that clearing the marker produced only when nothing else wrote during the read; otherwise it stays bound to the claim's version, so a plan revision applied meanwhile marks it stale. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Success, refusal and settlement now share one rule: the version clearing the push marker produced counts only when nothing else wrote since the claim; otherwise a later failure stays bound to the claim's version, so a plan revision applied while the push was in flight marks it stale. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After the pushed head appears, preparation now requires the task state, review version and snapshot to be exactly what the push left before any cmd: check runs, so a plan revision or review edit applied during the push fails it instead of being adopted. The publish guard returns to its rebase-only form: publishing cannot overlap a push by status, which the docs now state. 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>
…serve settlement - The push claim uses the task state version captured before the asynchronous trust refresh, so a reassignment during it refuses. - Production returns the last guard as the pusher's synchronous finalizer, closing the gap between validation and the Git push. - A settlement read keeps the process-settlement reserve inside its limit and does not start with less than that reserve left. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When the preparation's own deadline ends the push, less than Git's settlement reserve is left, so no branch read starts and the marker stays, as for an unreadable branch. The docs, the code comment and the test now say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A pre-merge rebase moves the PR's base past base commits the pre-PR already-fixed check never saw. Merge inspections of a runner-published PR now scan from the base of the snapshot the last clear check ran on, excluding the ledger's owned commits. Adds the merge-handoff design. Part of #22 (F6b). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With a runner, Merge PR now starts one merge job and returns. The job prepares (refresh, rebase, push, cmd: checks), waits up to 30 minutes for GitHub's required checks on the pushed head (pending or missing waits, a failure stops at once), then hands the exact pair to the guarded merge. A failing or slow check, or GitHub's refusal, leaves the task approved, merge blocked; review needs and other blockers end the job for a person. Jobs live in schema v19 merge_jobs, so their progress never moves the task state version that readiness is bound to; startup fails an interrupted job. The merge status offers Merge PR while only blockers the job resolves remain, and the browser follows the job. Part of #22 (F6b). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The job passes its signal into the guarded merge, so shutdown or a cancel stops it as it stops a click. A task cancelled while the job runs ends the job cancelled however the job learns of it. Adds regressions for the queued outcome, a busy refusal, retry after a blocked job, startup settlement and shutdown wiring; corrects the design doc. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The #22 acceptance cases through the job now run with a runner-published PR, so the guarded merge checks the readiness real preparation records. Another blocker now outranks a failing check, so checks failing on a collaborator's head end the job failed rather than blocked. Shutdown stops a merge job in its first synchronous step, before requests drain. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Openings now record the clear already-fixed check they followed (schema v19), and the merge's scan starts at that check's base, so a re-publish from a rebased base cannot skip earlier base commits. Once the job's merge attempt exists, its recorded state decides the job's outcome, so a refusal that lands during shutdown still blocks. Adds server regressions for the cancel wiring and the settlement hook that closes PRs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The job saves its merge action ID before the guarded merge starts, so a restart settles it from its merge attempt. A pre-v19 opening scans from the latest clear check. A merge conflict caused by a moved base no longer disables Merge PR: the job's rebase resolves it. Moves a misplaced comment back to its method. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
F6b is the first code to move a task to approved, merge blocked; start now accepts it as it accepts a task in review, so an amended plan can fix what blocked the merge. The cancel tests now cancel the task before telling the job, and the server test's preparation holds a real push marker, as production does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Revert starting an amended plan from approved, merge blocked: a blocked task always has runner commits, so the existing guard refuses that start from review too; the doc now points to the scope-amendment flow. Blocking now ends the job and moves the task in one Store transaction. Adds regressions that a retried job is never decided by an earlier refused attempt, in process or at startup. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The merge's already-fixed scan now starts at the base-branch tip the PR's own pre-PR check scanned through, the tightest sound start. The check's 250-commit cap still fails closed for long-lived PRs, as it did before any rebase; the docs say so, and that failing required checks keep Merge PR available. Adds tests that blocking advances the task state version and that a click during a running preparation is refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
Merge idempotency, legacy scan safety, deadline enforcement, and keyboard-focus handling have unresolved defects.
4 open findings
What changed in this PR
Adds F6b’s durable one-click merge workflow for runner-published pull requests.
Changes:
- Adds merge-job preparation, required-check polling, guarded merge handoff, cancellation, and recovery.
- Extends schema v19 with merge-job state and already-fixed scan bindings.
- Adds browser status reporting, documentation, and regression coverage.
| File | Description |
|---|---|
web/server.ts |
Wires merge jobs into API and shutdown. |
web/public/app.js |
Displays and polls merge-job progress. |
runner/merge-job.ts |
Implements the merge-job lifecycle. |
runner/merge.ts |
Adds abort handling and scan ranges. |
runner/store.ts |
Adds schema v19 persistence. |
runner/production.ts |
Settles interrupted jobs at startup. |
github/merge.ts |
Supports bounded already-fixed ranges. |
test/merge-job.test.ts |
Tests merge-job lifecycle behavior. |
test/runner-publishing.test.ts |
Tests server integration and cancellation. |
test/pre-merge.test.ts |
Tests acceptance scenarios end-to-end. |
test/merge-task-pr.test.ts |
Tests published-PR scan ranges. |
test/merge.test.ts |
Tests merge adapter inputs. |
test/runner-production.test.ts |
Tests startup recovery. |
test/browser/review.spec.ts |
Tests browser job progression. |
test/store.test.ts |
Updates migration expectations. |
test/runner-lifecycle-store.test.ts |
Updates schema expectations. |
test/publish.test.ts |
Updates schema expectations. |
test/planning-drafts.test.ts |
Updates schema expectations. |
docs/implementation/merge-handoff.md |
Documents the new workflow. |
docs/implementation/guarded-merge.md |
Updates guarded-merge behavior. |
docs/architecture.md |
Records F6b architecture status. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
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>
…uarded-merge refusal reason exactly Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… say when each can land Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In one process the guarded merge saves its outcome and the job reads it in one turn, so a queue poll or plan import can only land while GitHub's merge command is in flight. Hold the gateway's merge in the removed/failed and plan-revision tests, run the poll and import while it is held, and assert the guarded merge rejects. Label the fulfilled path's failed/removed branch defensive. Observe a moved HEAD in the new-snapshot test, as production's load does. Plan writes are refused, not delayed, during a merge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sive Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he timed-out blocked job through the poll Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A local HEAD observation during the guarded merge advances the task's state version, so the job's preparation no longer applies. The queue correlation, queue boundary and authorization checks reuse the review loaded before the second pass, see only that blocker, and the job now words their refusal as what merging does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A merge job needs a runner, and a runner server's review load reads the recorded snapshot head, so no HEAD observation records a snapshot during a job. Rename the tests that reach their state that way as defensive, correct the observeLocalHead docstring, and correct the handoff doc, blockMergeJob and guarded merge comments to match. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d one A completed run with no start time (or the zero time) counted as the latest, so a skipped or neutral run could hide a newer failure and read as passing. Only a run not yet completed counts as the latest without a start time; a completed one counts at its completion time, or as the oldest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The merge job tests' server never set runnerRepository, so review loads observed the user's HEAD, and a user-side commit restoring run.sh's mode made the task mergeable; a runner server never reads that commit. The tests now open the runner-owned repository and set runnerRepository before any load, as setUpRunner does, and reach a mergeable review by amending the plan to declare the run.sh change. The shared setup asserts the review reads the runner repository and that the snapshot never moves to the user's (moved) HEAD. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The click's review token was checked only at admission. A review change from another tab that landed while the job's first review load was pending made the job prepare and merge a review the person never saw. The job now ends failed, saving no prepare-merge action, when that load's token differs from the click's, as the direct merge refuses a review that changed during validation. The guarded merge keeps the current token: a rebase during preparation makes a new one, and readiness binds the review version. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every runner-mode merge status in the browser fixtures now goes through runnerMerge, which adds settledBeforeJobMerge as withMergeJob always does; many click answers and reviews omitted it. The poll back-off test for a changed check list now sends that list at revision 4, as production saves it, and the end at 6. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…moval Add latest-check regressions for commit statuses without a start time, and say in the docstring and merge-handoff doc that the rule covers statuses. Label the job-refresh removal in withMergeJobObservation as defensive. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A completed check run or commit status with neither a start nor a completion time was ordered as the oldest, so an older started success outranked a failure with no time and the check read as passing. Such an observation now ties with the latest, and the existing tie rule (the one that passes least counts) decides, so it can never make a check pass. Completion is read from the observed state, which replaces the explicit SUCCESS/FAILURE/ERROR list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A completed run or status with no start time is dropped only when another of its kind provably started after its completion; otherwise it ties with the latest, so the run that passes least counts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e its assertion cannot distinguish Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…st title Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mchwang
marked this pull request as ready for review
October 17, 2026 01:39
mchwang
added a commit
that referenced
this pull request
Oct 17, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> # Conflicts: # docs/implementation/merge-handoff.md # github/merge.ts # runner/merge-job.ts # runner/merge.ts # runner/store.ts # test/browser/review.spec.ts # test/merge-job.test.ts # test/merge-task-pr.test.ts # test/merge.test.ts # test/runner-production.test.ts # test/runner-publishing.test.ts # web/public/app.js # web/server.ts
This was referenced Oct 18, 2026
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
F6b of #22, on
mainafter F6a (#153, merged as209707e): one-click merge. With a runner, Merge PR starts one merge job and returns; the job prepares, waits for GitHub's required checks, and hands the exact pair to the guarded merge. Design:docs/implementation/merge-handoff.md.runner/merge-job.ts): admits the click (fresh token, mergeable task, issue trust read from GitHub, nothing busy; a definite refusal is saved and replayed to a resend), records its ownprepare-mergeaction and runs preparation (refresh, rebase, push,cmd:checks); then inspects the PR (fresh) at once and with backoff (10 s → 60 s) for up to 30 minutes. Pending or missing checks and mergeability GitHub is still computing wait; a failing check or the 30-minute limit ends the jobblocked; any other blocker (PR moved, readiness lost, already-fixed) or a failed GitHub read ends itfailedand outranks a failing check. No inspection starts after the limit, and one running into it is stopped. Then it saves a merge action ID and calls the guarded merge (MergeCoordinator.merge, which now takes the job's abort signal).merged,queued,submitted,blocked(task → approved, merge blocked, in one transaction with the job),review-required,failed,cancelled. Once the job's own merge attempt exists, its recorded state decides the outcome.merge_jobskeeps job progress off the task, so the readiness preparation records stays bound while the job waits. Startup fails a job that had not asked GitHub to merge, or settles it from its own merge attempt.withMergeJob, which words them as what merging does and sends the codes a successful job settles);GET /api/mergereturns the task's latest job (saved refusals excluded); a refused click's 409 carries the latest job;prepare-merge,startandresumeare refused while a job runs; cancel stops the job, and its PRs close when the task ends cancelled; shutdown stops the job in its first synchronous step.revisionand a per-tasksequence, never by time); a click stays pending across renders; a job that reached the merge drops the blockers it settled, any other ending keeps them; the details dialog lists waiting checks. In runner mode the Merge button isaria-disabled, never disabled, so it keeps focus; when the job ends, only the merge control and banner change ("Refresh to see the result"), so selections and input survive.F6c (common CI / T9) comes next.
Validation
Exact pair: base
209707e(main, F6a #153 squash-merged; its tree equals F6a's final head7ffd084, so the branch merged it with-s oursafter checking), head23cadee(current head).209707e..23cadee(round 97; every changed line of all 22 files read); 10 changed unit test files 920 of 920 (no Errors or Unhandled); F6b / merge-key / merge-action / merge-request / direct-Merge browser tests 53 of 53; mutation checks on the newest rules each failed a testtestand real-Docker: passed on23cadeed440544: independent review clean (round 68); CItestand real-Docker passedfc7834c: independent review clean (round 60); CItestand real-Docker passed82661d6: independent review clean (round 45); CI and real-Docker passed7b1b5fa: independent review clean (round 44); CItestand real-Docker passed445081f(after merging F6a withmain): independent review clean (round 41); full non-Docker suite (CI's command) 2,220 of 2,220; browserreview.spec.ts+plans.spec.ts100 of 101. The one failure ("keeps focus on the Merge button when a merge-job click is refused") failed every time: since Run review history work in an abortable worker thread #170 the review loads asynchronously and the test pressed Enter before the page offered the merge.79ed50cwaits for "Merge PR"; 5 of 5 runs then passed79ed50c,7fe156d,7b1b5fa) change tests onlytestand real-Docker: passed on7b1b5fa8c3b171: independent review clean (round 34); CI and real-Docker passed; Copilot round 4 found three issues, fixed inda0204a1874e6a(F6a749a977merged in): full non-Docker suite (CI's command), local: 75 files, 2,113 of 2,113 passed;npm run typecheckpassed. Later commits changerunner/merge.ts'squeue-headmessage, tests and docs onlytestand real-Docker: passed on7b1b5fa2c5ccc3: independent review clean (round 29); CI and real-Docker passedcbacd19(base old F6a9e1f695): independent review clean (round 28); 8 focused files, 679 of 679 passed777c9b5: full non-Docker suite 75 files / 2,079 passed; browser suite 139 of 139; CI and real-Docker passedKnown limitations
pull-request-opening.md).failedrather than retrying (fails closed; documented and tested).Review ledger
4a3d23efailed; a cancel without the job's abort could strand it; untested claims (queued, busy, retry, startup and shutdown wiring); a non-existentmergingresponse in the doc7cc6ab27cc6ab279b7fd579b7fd5e9575aae9575aafailed; a misplaced commentffb995effb995e922007f(start change later reverted)922007fdef4b0adef4b0a19d5abc(scan starts at the pre-PR check's scanned tip; limit documented)19d5abc19d5abc18fc56818fc568latestMergeJob; stalestart()docstring727f279727f279prepare-mergeperforms; two doc claims (limit vs merge time,merge_jobsrows)db189aedb189ae841e395841e395fc57d20,76c81f276c81f2368495c368495caria-disabledMerge button kept the primary look9e0ee399e0ee3912340911234091ab74aecab74aecbe4f391be4f391f6eb190f6eb190887f0cd,7302d687302d68github/merge.ts; the one-transaction block claim untested (also: a job that already ended no longer moves the task)b3ba376b3ba3764ea87244ea8724329327832932782e3e860(unit test onclose(); server test narrowed)2e3e860777c9b5777c9b5777c9b5018025601802567180aad7180aadcbacd19cbacd192c5ccc3f3bc3e1(withmain22e76a8) with no conflicts; architecture doc now names schema 192c5ccc32c5ccc31b81ee91b81ee97604a79d61da9857b7ccbd61da988b365861874e6a749a9771874e6afailed; real preparation endsreview-required(the test's fakes kept items approved)ad00767(test through real preparation; doc corrected)ad007678c3b1718c3b1718c3b171da0204ac265333a734646c2653332a9534d2a9534d1ac194ca752482(withmain49f03db: #170 made review loads and actions async, #171, #172)1ac194c…3b74ef7cancelled; a reload failure after the job was saved answered an error the page could not follow; a click answered with an already-ended job showed nothing to do7717bf8,0605a85,3b74ef7,445081f445081f445081f79ed50c79ed50c7fe156d(fails with 2 requests when the guard is removed)7fe156dstart()in a unit test; a resent click attaches a second.finallyto a running job7b1b5faawaitsstart(); second nit declined (below)7b1b5fa7b1b5fasubmittingafter an unsaved outcome; (2) a failed admission load ignored a resend admitted meanwhile; (3) a failed Retry lost its key while an earlier job was shown; (4) a held full review response overwrote a newer job observation; (5) a trust-read failure ignored a saved resend, and shutdown replaced the read's own timeout or gh answer14c2942(1, 2, 5),fbde7d4(3, 4); each regression fails with its fix reverted; threads answered82661d67ffd084(shutdown push guard)82661d682661d6(fresh pass)0eb8a21(logged, documented, tested)0eb8a2145bff7a(run never rejects; always logs, quoted; settle tested)45bff7arunning.finallycallback could throw (unhandled rejection exits Node); shutdown log untestedcffdf4cmain96cdee5209707e; PR retargeted tomainfirst, merged with-s oursafter the tree checkcffdf4ccffdf4cmerge_jobslayout misdescribed; pre-v19 scan could start on another branch's check; publish during a job (not real)715329b,f684da4,b721ca6,d1f99fcd1f99fc94c3557(start/resume refused during a job),25444dd25444dd25444ddd2de1b3,c1a329dcffdf4cde892bf,fbfe4ce; threads answered and resolvedfbfe4cefbfe4ce105ea74105ea74a76b453(server sendssettledBeforeJobMerge)a76b4530857fa2(pinned on a runner server)0857fa20857fa29d468289d46828f83b657,3a2251a9d46828536a18d,3fbf1cd(per-jobrevision); threads answered and resolved3a2251afc7834cfc7834c536a18d,3fbf1cdandfc7834ceach failed a testfc7834cqueuedrecordedsubmitted; a ready inspection after the limit mergedd49aeb7; threads answered and resolvedd49aeb7cd78734cd78734cd787340ef10400ef1040084b5df084b5df674b986,d440544d440544d440544queueddropped its queue so polling stopped; a resent click replaying an older job replaced a newer running oned3cbaa8; threads answered and resolvedd3cbaa8…835d0a4f899661,7fcf94e,bd77939,84c4ab1d3cbaa8835d0a4(one shared ended-job-and-attempt helper); threads answered and resolved835d0a4…51a9b3cd490184,c4414d3,51a9b3c,42d4f67ca91302…2d65a23fea2785,ca91302,371fd3e…c4acbf5,d1c0959,32ff4ec,2d65a23,57c7786dba9e7c…82b857fretryaction, server status intowithMergeJob, timing defaults, poll back-off, draft on failed click, preparing banner, foreign ledger entries, ended-job update, store refusals; defensive labels where unreachabledba9e7c,1a0aa0f,f717220,5ec39ee,00298e4,abdcc65,f9bfa8d,82b857f0c7171c…98db23f250abad,986a517,365a720,cda7852,84d93f0,38e54cc,03f11fd,98db23f69b3e05…f63fecc69b3e05,4465a5d,b1808ef,355a794,f63feccf63feccd945617(runner-owned repository setup),98ce3f7d945617eab5d2e(job endsfailedwhen the review changed after the click),27427128028cba…5f9350d8028cba,9f8f01f,c0d0a4b,5f9350d5f9350d23cadee23cadeeDeclined: round 62's overlapping merge poll chains in one generation, a defect that predates this PR (#48) and that stacked #175 fixes for both queue and job polls. Round 43's second
.finally. Both callbacks run back to back; the first one'staskCancelled→actIfOwedreserves#runningsynchronously, so the second sees the work running and does nothing (round 44 traced and confirmed this). Every regression added from round 9 on was shown to fail with its fix removed (mutation probes). Round 5's finding was accepted, then shown by round 6 to rest on a false premise; its change was reverted and the doc corrected.Review-lesson audit
AGENTS.mdAGENTS.mdGate status
209707e/ head23cadeetestand real-Docker: passed on23cadee23cadee(requested after round 97 was clean and exact-head CI passed): no open issues; both round 10 findings resolvedmainmissing; predates this PR)mainafter this PR merges🤖 Generated with Claude Code