You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
F6a: push the rewritten head behind a durable marker - #153
F6a of #22: prepare-merge now pushes the rewritten head to the task's pull request.
When: after a rebase whose refreshed review has no blocker, before the cmd: checks run. A rewrite that made an approval stale stays local until a person reviews it.
What it may overwrite: exactly the PR head the rebase rewrote. GitBranchPusher.push gains expected: the lease is pinned to that commit, and any other tip refuses with nothing pushed, even a commit codeboost made. Production wires expected: from; MergeCoordinator.remotePair now reports the task PR's branch.
Durable marker (schema v18):tasks.push_in_progress is written before the first external write and cleared only after the outcome is known (push read back, refusal, or one branch read). While it exists, merge admission, runner admission, status changes, reassignment, rebases and another push refuse, and merge status names it. A cancel waits for it and closes the task when it clears.
Settling an unknown outcome: one branch read, bounded by 30 s and the preparation deadline; only shutdown aborts it. If it cannot run, the marker is settled by the next preparation, by a tracked job started under the verified runner lock at startup, or by that job when a cancel arrives while no preparation runs (and again on a repeated cancel). A task it closes has its PRs closed.
GitHub's PR API lag: after a push or refusal, preparation waits (at most 30 s plus one inspection, within the deadline) until the PR reports a new head. The pushed head continues only if the task, review and snapshot are exactly what the push left; any other head is reviewed through the rewrite lineage.
Trust: refreshed before the claim and revalidated immediately before the write, together with the task state version, review version, snapshot and cancel state.
F6b (next, stacked on this) adds the required-check wait, the already-fixed rerun and the one-click merge handoff. F6c adds common CI.
Validation
Exact pair: base 22e76a8bc90b59c8407bb78bee1c229b249b4936 (main), head 749a977c04b626b7c253310732f6c361ae16348c (current head; main merged in 143b5f6).
current head, independent full-diff review: clean on 22e76a8..749a977 (post-merge round 8; 11 focused files, 787 of 787 tests passed; the new regression fails with its fix removed)
intermediate checkpoint 57b7ccb: independent review clean (post-merge round 7); CI and real-Docker passed; Copilot round 5 returned 0 open findings
intermediate checkpoint f3bc3e1: independent review clean (post-merge round 3); CI and real-Docker passed
intermediate checkpoint 143b5f6 (the merge; 93af176 changes one doc line): full non-Docker suite (CI's command), local: 74 files, 2,047 of 2,048 passed; the failure (refreshes a pull request head that moves during final authorization, a 15 s timeout at load average ~120) passed alone. Browser suite: 134 of 137 passed under load; the 3 failures passed on rerun. npm run typecheck passed
current head, CI and real-Docker: pending
intermediate checkpoint 93af176: CI and real-Docker passed
Earlier validated head 9e1f695 (base ae9fc3d):
earlier head, full non-Docker suite (CI's command), local and run alone: 74 files, 2,028 passed; npm run typecheck and git diff --check passed
current head, independent full-diff review: clean on ae9fc3d..9e1f695; 6 focused files, 341 tests passed; 7 mutation probes all caught
current head, CI: push and pull-request runs passed; each ran 74 non-Docker files with 2,028 tests, 137 browser tests, and type checking
local load note for this round: two overlapping full-suite runs on 58e63e0 timed out in 10 and 20 tests under a host load average of 150–200; the deadline tests that failed by assertion passed alone, and the suite passed when run alone
Earlier validated head 87dd3bf:
full non-Docker suite (CI's command), local: 74 files, 2,025 passed; npm run typecheck and git diff --check passed
independent full-diff review: clean on ae9fc3d..87dd3bf; 8 focused files, 527 tests passed in the review
every new regression was shown to fail before its fix, or to fail under a mutation that removes the guarded behaviour
local load note: several full-suite runs on intermediate heads hit 15–30 s timeouts in review.test.ts and pre-merge.test.ts under a host load average near 200; each affected file passed alone and the full suite passed on rerun. These are recorded as infrastructure, not product, failures.
exact-head CI: push and pull-request runs passed; each ran 74 non-Docker files with 2,025 tests, 137 browser tests, and type checking
cancel aborted settlement reads; startup settlement did not close PRs; PR API lag; wrong comment; no wiring tests
a63e5cf
independent 2
a63e5cf
base move during lag ended the wait; fake read ignored its signal
b1eaaae
independent 3
b1eaaae
wait bound stated as 30 s; really 30 s plus one inspection
9e98ad7
independent 4
9e98ad7
stranded marker on cancel; refused path re-inspected without waiting; stale doc bullet
50ba765
independent 5
50ba765
no actionable defects; noted an unbound failure and a non-production test setup
e5ce306
independent 6
e5ce306
repeated cancel could not retry settlement; push-boundary checks untested
496d4b3
independent 7
496d4b3
refusal traced from the wrong head after an earlier landed push; failure after a push reported stale
d0639c9
independent 8
d0639c9
state-version check is load-bearing (plan revisions) and untested; settlement and wait bounds untested; doc overclaimed "not pushed"
6ab346d
independent 9
6ab346d
cancel during next preparation's settlement, attempt admission, shutdown reads untested; non-production cancel setup
0be7462
independent 10
0be7462
failed-push binding untested
7cc5477
independent 11
7cc5477
failed push bound after an awaited read adopted a concurrent plan revision
cedd594
independent 12
cedd594
same on success and refusal paths
a0ecaf0
independent 13
a0ecaf0
mid-push plan revision adopted before checks; unreachable publish guard
fc26627
independent 14
fc26627
stale pusher doc comments
365cda0
independent 15
365cda0
admission guards and busy settlement skip untested
87dd3bf
independent 16
87dd3bf
no actionable defects
—
Copilot 1
87dd3bf
claim version read after the trust refresh; guard ran before an awaited gap instead of as the pusher's finalizer; settlement read did not reserve Git's settlement time
58e63e0; three failing-before regressions; threads answered and resolved
independent 17
58e63e0
Copilot fixes correct; docs claimed a read after a deadline-ended push, but none starts
9e1f695
independent 18
9e1f695
no actionable defects
—
Copilot 2
9e1f695
0 open findings; the three round-1 findings resolved; summary notes the base advance must be integrated, re-reviewed and revalidated before merge
already recorded under Gate status as a merge prerequisite
merge main
143b5f6
merged 22e76a8 (#158, #154, #160) with no conflicts
—
independent post-merge 1
143b5f6
architecture.md still named schema version 17
93af176
independent post-merge 2
93af176
no actionable defects
—
Copilot 3
93af176
the push-settlement branch read could wait without bound on a process that escaped Git's group holding its output
f3bc3e1 (reads return failed after the bounded drain; the marker stays)
independent post-merge 3
f3bc3e1
no actionable defects (noted, not changed: git push itself still waits fail-closed for an escaped holder, per the process-group contract; no realistic trigger with HTTPS and the gh credential helper)
—
Copilot 4
f3bc3e1
allowUnsettledReturn also abandoned processes still in Git's group, which nothing owns
97b6305 (new boundEscapedOutput: bounds only an escaped output holder; group members waited for)
independent post-merge 4
97b6305
three comments still claimed every branch read is bounded
230e5b6
independent post-merge 5
230e5b6
reserve comment wrong about which calls are bounded; one inspection's settle time understated
33450c8
independent post-merge 6
33450c8
cat-file reads also waited without bound on an escaped holder
57b7ccb (bounded like ls-remote; stale rebaser doc line corrected)
independent post-merge 7
57b7ccb
no actionable defects
—
Copilot 4 (previously missed)
f3bc3e1
when a collaborator moved the branch between the push and its read-back, preparation failed instead of reviewing that head
749a977 (a moved settlement is reviewed, as after a refusal)
Copilot 5
57b7ccb
0 open findings
—
independent post-merge 8
749a977
no actionable defects
—
Declined: none. One noted out-of-scope item: F5's rebase-cleanup failure binding (merged in #149) reads the task state after an awaited cleanup, the pattern fixed here for pushes; recorded as a follow-up.
Review-lesson audit
lifecycle, cancellation, shutdown, deadline and race findings (rounds 1, 2, 4, 6, 9, 11–13, 15, Copilot 1, Copilot 3, Copilot 4 including its previously missed finding, post-merge 6): covered by Async jobs and polling and Required race regressions in AGENTS.md
untested-claim and fail-before findings (rounds 2, 8–10, 15): covered by Review readiness ("give each claim a test that fails if the claim is false") and the Design system and plan format #1 rule of tests
non-production test setups (rounds 5, 9): covered by Review readiness ("A test's setup must leave the state the production path would")
documentation claims (rounds 3, 4, 8, 14, 17, post-merge 1, 4, 5): covered by Review readiness (treat every behavioural claim as something to verify)
no new rule is needed: each finding is an instance of existing guidance
Gate status
independent exact-head review: clean on base 22e76a8bc90b59c8407bb78bee1c229b249b4936 / head 749a977c04b626b7c253310732f6c361ae16348c (earlier: clean on ae9fc3d / 9e1f695)
CI and real-Docker: pending on 749a977; passed on 57b7ccb, f3bc3e1, 93af176 and 9e1f695
Copilot review: rounds 1–5 addressed (round 5 on 57b7ccb: 0 open findings; round 4's previously missed finding fixed in 749a977); round 6 requested only after exact-head CI passes
base: main (22e76a8) merged in at 143b5f6; reviewed and revalidated on the new pair
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>
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>
…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>
Production readBranch ran Git without allowUnsettledReturn, so a process
that left Git's group while holding its output could hold the push
settlement read, startup recovery, a repeated cancel or shutdown without
bound. A read changes nothing, so it now returns failed after the
bounded drain, and the push marker stays.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Handle moved outcome after push read-back before rethrowing
runner/pre-merge.ts:336
The settlement result is discarded and the original push error is always rethrown. In production, if git push succeeds and a collaborator advances the branch before GitBranchPusher's mandatory read-back, remote.push throws for the mismatched head; this read then returns moved and clears the marker, but preparation reports failed without refreshing that collaborator head. The afterPush fixture does not reproduce the pusher's read-back, so its review-required regression misses this path. Route a settled moved outcome through the rewrite-lineage refresh path (with an accurate reason) instead of unconditionally rethrowing.
allowUnsettledReturn also stopped waiting for processes still in Git's
group, which a read has no durable ownership of. A new process-group
option, boundEscapedOutput, bounds only the wait for a process outside
the group that holds the output; group members are still waited for,
fail-closed. Branch reads use it. Tests cover both cases.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cat-file only reads, so a process that left Git's group but holds its
output no longer holds fetchCommits or the push's head check: the call
fails after the pipe drain. The reserve comment now lists the Git calls
that still wait (fetch, update-ref, push). The pre-merge-rebase doc no
longer says no production request starts the rebaser.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Handle abort race after checking but before listener registration
runner/pre-merge.ts:290
An abort can occur after the check on line 281 but before this listener is attached. AbortSignal does not replay that event, so cancellation or shutdown can be ignored until the backoff timer expires (up to 4 seconds). Check signal.aborted immediately after registering the listener, as the command-check path already does, and add the corresponding race regression.
…sh outcome
When the push's read-back fails because a collaborator moved the branch,
the settlement read now returns moved and preparation reviews that head
(review-required, attribution and approvals refreshed), as after a
refusal, instead of failing with the read-back error.
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
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
F6a of #22:
prepare-mergenow pushes the rewritten head to the task's pull request.cmd:checks run. A rewrite that made an approval stale stays local until a person reviews it.GitBranchPusher.pushgainsexpected: the lease is pinned to that commit, and any other tip refuses with nothing pushed, even a commit codeboost made. Production wiresexpected: from;MergeCoordinator.remotePairnow reports the task PR's branch.tasks.push_in_progressis written before the first external write and cleared only after the outcome is known (push read back, refusal, or one branch read). While it exists, merge admission, runner admission, status changes, reassignment, rebases and another push refuse, and merge status names it. A cancel waits for it and closes the task when it clears.F6b (next, stacked on this) adds the required-check wait, the already-fixed rerun and the one-click merge handoff. F6c adds common CI.
Validation
Exact pair: base
22e76a8bc90b59c8407bb78bee1c229b249b4936(main), head749a977c04b626b7c253310732f6c361ae16348c(current head;mainmerged in143b5f6).22e76a8..749a977(post-merge round 8; 11 focused files, 787 of 787 tests passed; the new regression fails with its fix removed)57b7ccb: independent review clean (post-merge round 7); CI and real-Docker passed; Copilot round 5 returned 0 open findingsf3bc3e1: independent review clean (post-merge round 3); CI and real-Docker passed93af176, independent full-diff review: clean on22e76a8..93af176(post-merge round 2), including the merge integration with Size the plan import request limit from the parser bound (#146) #158, Close out Lane G status records (#145) #154 and Read an oversized body before refusing it, and close on early refusals (#157) #160; 11 focused files, 774 of 775 tests passed, the one failure a known load-sensitive F5 deadline test that passed alone; 22 of 23 mutation probes killed (the survivor guards a case where continuing is safe)143b5f6(the merge;93af176changes one doc line): full non-Docker suite (CI's command), local: 74 files, 2,047 of 2,048 passed; the failure (refreshes a pull request head that moves during final authorization, a 15 s timeout at load average ~120) passed alone. Browser suite: 134 of 137 passed under load; the 3 failures passed on rerun.npm run typecheckpassed93af176: CI and real-Docker passedEarlier validated head
9e1f695(baseae9fc3d):npm run typecheckandgit diff --checkpassedae9fc3d..9e1f695; 6 focused files, 341 tests passed; 7 mutation probes all caught58e63e0timed out in 10 and 20 tests under a host load average of 150–200; the deadline tests that failed by assertion passed alone, and the suite passed when run aloneEarlier validated head
87dd3bf:npm run typecheckandgit diff --checkpassedae9fc3d..87dd3bf; 8 focused files, 527 tests passed in the reviewreview.test.tsandpre-merge.test.tsunder a host load average near 200; each affected file passed alone and the full suite passed on rerun. These are recorded as infrastructure, not product, failures.Review ledger
12e96baa63e5cfa63e5cfb1eaaaeb1eaaae9e98ad79e98ad750ba76550ba765e5ce306e5ce306496d4b3496d4b3d0639c9d0639c96ab346d6ab346d0be74620be74627cc54777cc5477cedd594cedd594a0ecaf0a0ecaf0fc26627fc26627365cda0365cda087dd3bf87dd3bf87dd3bf58e63e0; three failing-before regressions; threads answered and resolved58e63e09e1f6959e1f6959e1f695main143b5f622e76a8(#158, #154, #160) with no conflicts143b5f6architecture.mdstill named schema version 1793af17693af17693af176f3bc3e1(reads return failed after the bounded drain; the marker stays)f3bc3e1git pushitself still waits fail-closed for an escaped holder, per the process-group contract; no realistic trigger with HTTPS and theghcredential helper)f3bc3e1allowUnsettledReturnalso abandoned processes still in Git's group, which nothing owns97b6305(newboundEscapedOutput: bounds only an escaped output holder; group members waited for)97b6305230e5b6230e5b633450c833450c8cat-filereads also waited without bound on an escaped holder57b7ccb(bounded likels-remote; stale rebaser doc line corrected)57b7ccbf3bc3e1749a977(amovedsettlement is reviewed, as after a refusal)57b7ccb749a977Declined: none. One noted out-of-scope item: F5's rebase-cleanup failure binding (merged in #149) reads the task state after an awaited cleanup, the pattern fixed here for pushes; recorded as a follow-up.
Review-lesson audit
AGENTS.mdGate status
22e76a8bc90b59c8407bb78bee1c229b249b4936/ head749a977c04b626b7c253310732f6c361ae16348c(earlier: clean onae9fc3d/9e1f695)749a977; passed on57b7ccb,f3bc3e1,93af176and9e1f69557b7ccb: 0 open findings; round 4's previously missed finding fixed in749a977); round 6 requested only after exact-head CI passesmain(22e76a8) merged in at143b5f6; reviewed and revalidated on the new pair🤖 Generated with Claude Code