fix(task-board): open the review cycle when the PR link loses the race - #6700
Closed
pedrofrxncx wants to merge 3 commits into
Closed
fix(task-board): open the review cycle when the PR link loses the race#6700pedrofrxncx wants to merge 3 commits into
pedrofrxncx wants to merge 3 commits into
Conversation
added 3 commits
August 28, 2026 09:12
The dispatch client published the `starting-sandbox` run-status stage unconditionally before every `ensureSandbox`. An interactive Code Agent keeps its pod between turns (`interactive: true` skips the release-on-run-end), so the ensure was a warm resume — but the chat had already been told the machine was booting, on every single message. Move the publish into an `onColdStart` callback that `ensureSandbox` fires only past its live-pod fast path.
A card moved to In Review the moment its PR was opened, so it sat in the reviewers' lane for the whole time an agent was still working on it. In Review read as "waiting on a person" when nobody was waiting on anything. The lane could not simply be changed, because the review CYCLE — the boundary that decides which reviewer verdicts still count — was derived from the newest `status_changed → in_review` activity. Moving a card mid review would have reset that boundary and invalidated its own reviewer's verdict, which is the failure mode that stranded 13 prod cards holding an approval that could never merge. So the cycle stops riding on the lane. `review_cycle_started_at` is a column (migration 189, backfilled from the timeline so in-flight cycles survive the deploy), and `reviewCycleStart` reads it, falling back to the old activity scan only for cards stamped before it. With that decoupled, the lane is free to say what a person wants to read: agent opens a PR → review cycle opens, card STAYS In Progress reviewer decides → In Review (`parkReviewedCardForHuman`) hand-off / retries → In Review, unassigned `inReviewPhase` is the predicate every automatic path now takes instead of `status === "in_review"`, and the sweeper's work list mirrors it. Two regressions that decoupling opens are closed explicitly: a failed REVIEWER run no longer schedules a Super Agent retry or parks the card on To Do (the reviewer has its own budget), and `closesOwnReview` gates on the phase, so an author's run can't mark its own work Done while a reviewer is still reading it. Verified against real Postgres: migration up/down from an empty database, the backfill against a seeded pre-migration card, and the full unit + integration suite with no new failures.
A card went to In Review and stayed there for the whole reviewer run — the exact thing this branch exists to prevent. `advanceLinkedTasksToReviewOnThreadFinish` reads `listPrs` to decide repo-backed vs repo-less. A run that links its PR moments AFTER its thread goes terminal is read as repo-less and parked In Review. The link then lands on a card that is no longer In Progress, so `openReviewCycleIfInProgress` matched nothing and the cycle never opened at all. Observed on a real board at 52 seconds between the two: 15:42:47 thread finished, listPrs empty -> card moved to In Review 15:43:39 PR #423 linked -> update matched nothing, no cycle 15:49:37 sweeper dispatched the reviewer via the in_review lane branch The card therefore read In Review while an agent reviewed it, and with `review_cycle_started_at` NULL every verdict fell back to the legacy activity scan. The predicate now also matches a Super-Agent-owned card sitting In Review with NO cycle open, and takes it back to In Progress in the same statement. That is safe precisely because the cycle is null: a card whose cycle never opened has had no reviewer and no verdict, so there is nothing behind it to invalidate. The Super Agent guard keeps it off a card a person has taken over (`handTaskToHuman` clears the assignee), and one statement means the two columns cannot interleave with the sweeper's read. Real-Postgres tests: the late-link rescue, an In Review card with a cycle already open left alone (a verdict put it there), a human-owned card left alone, and a shipped card left alone. The test that asserted the old narrow rule is inverted rather than appended.
pedrofrxncx
force-pushed
the
feat/review-cycle-off-lane
branch
from
August 28, 2026 16:09
e42cb0d to
1750b36
Compare
Collaborator
Author
|
Folded into #6689 directly — the fix belongs in that PR, not stacked on it. Branch |
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.
Stacked on #6689 — merge into
feat/review-cycle-off-lane, notmain.The hole
Found by running #6689 on a live cluster. A card went to In Review and stayed there for the whole reviewer run — the exact thing the branch exists to prevent.
advanceLinkedTasksToReviewOnThreadFinishreadslistPrsto decide repo-backed vs repo-less. A run that links its PR moments after its thread goes terminal is read as repo-less and parked In Review. The link then lands on a card that is no longer In Progress, soopenReviewCycleIfInProgressmatches nothing and the cycle never opens at all.Observed at 52 seconds between the two:
Two consequences, both silent:
review_cycle_started_atstays NULL, so every verdict on that card falls back to the pre-189 activity scan.The fix
The predicate now also matches a Super-Agent-owned card sitting In Review with no cycle open, and takes it back to In Progress in the same statement.
Safe precisely because the cycle is null: a card whose cycle never opened has had no reviewer and no verdict, so there is nothing behind it to invalidate. Once a cycle IS open, the card is mid-review and
parkReviewedCardForHumanis what put it In Review — dragging it back would undo a verdict and re-stamp a boundary that verdicts stand on, so that case still returns null.The Super Agent guard keeps it off a card a person has taken over (
handTaskToHumanclears the assignee). One statement, so the two columns cannot interleave with the sweeper's read.Testing
Real Postgres (embedded), 27 pass / 0 fail in the file:
The test that asserted the old narrow rule (
never opens a cycle on a card that is not In Progress) is inverted, not appended.bun run --cwd=apps/api check,bun run lint(0 errors),bun run fmtclean. 36 pre-existing failures on the base branch, 36 here, zero regressions.Summary by cubic
Fixes a race that left a card In Review for the whole reviewer run, with
review_cycle_started_atNULL, when the PR link landed after the thread finished—so every verdict on that card fell back to the legacy activity scan.Bug Fixes
openReviewCycleIfInProgressnow also matches a Super-Agent-owned card sitting In Review with no cycle open, moving it back to In Progress and stamping the cycle in the same statement.Written for commit bfa845c. Summary will update on new commits.