Uh oh!
There was an error while loading. Please reload this page.
[Bugfix #844] Gate NeedsAttention PR rows on porch pr-gate - #845
Conversation
Before: every open PR appeared in Needs Attention the instant it was created, even while CMAP reviews were still running — reviewers arrived before the AI comments did. After: a PR with a tracked builder is surfaced only once that builder reaches the porch `pr` gate (blocked === 'PR review'). PRs without an associated builder (human-authored, externally opened) fall back to the GitHub reviewDecision and are surfaced only when reviewStatus is REVIEW_REQUIRED. Adds buildItems export and 6 regression tests covering the scenarios called out in the issue.
Three small refinements after the iter-1 CMAP review: 1. waitingSince now uses the builder's blockedSince (not pr.createdAt) for affiliated PRs — the wait-time chip now measures "how long since the human became the bottleneck," not "how long since the PR was opened while CMAP was still running." Falls back to pr.createdAt for unaffiliated PRs. 2. Builders stuck at the pr gate whose PR is missing from `prs` (cache miss, pagination, transient API failure) now surface via the builder loop instead of disappearing silently. Tracks an emittedPrGateIssueIds set during PR iteration and only dedupes when the PR was actually emitted. 3. gateKindClass now handles 'PR review' → 'attention-kind--pr' so the builder-loop fallback row from (2) renders with the same PR styling as the PR-loop emit. 3 new regression tests for these behaviors. 9/9 tests pass.
waleedkadous
commented
May 24, 2026
Excellent work — thank you. Approving. Two things I particularly liked:
The regression test for the double-emission invariant is the kind of test I'd hope someone would write but rarely see. Nice. Please merge. |
Summary
Fixes#844
Root Cause
packages/dashboard/src/components/NeedsAttentionList.tsx:42-56iterated every open PR fromoverview.pendingPRsand emitted an attention row unconditionally. There was no cross-reference against the builder's porch gate state, so a PR appeared in Needs Attention the moment it was created — before the automated CMAP reviews (Gemini / Codex / Claude) had run. Reviewers arrived ahead of the AI review comments.Fix
buildItemsnow consultsOverviewBuilder.blocked/blockedSinceto decide whether a PR is genuinely waiting on a human:prGateSince=Map<issueId, blockedSince>for builders withblocked === 'PR review'(the human-readable label for the porchprgate, mapped inoverview.ts:GATE_LABELS).linkedIssuematches a pr-gate builder (prGateSince.has(...)), ORreviewStatus === 'REVIEW_REQUIRED'(the human-authored / externally opened case — no porch gate to wait on, so fall back to GitHub'sreviewDecision).waitingSinceuses the builder'sblockedSincefor affiliated PRs (the "human became bottleneck" moment), falling back topr.createdAtfor unaffiliated PRs.emittedPrGateIssueIds; the builder loop only dedupes pr-gate builders whose PR was actually emitted. This protects against a stuck builder disappearing if its PR is missing fromprs(cache miss, pagination, transient API failure).gateKindClassgains a'PR review' → 'attention-kind--pr'case so the builder-loop fallback row from (4) renders with the same PR styling.The
linkedIssueplumbing already existed onOverviewPR(populated byparseLinkedIssueinoverview.ts); no API/types change was needed.Note on issue suggestion
The issue suggested dropping the
continueat line 62, but doing so would create duplicate rows (one from the PR loop, one from the builder loop) for the same builder. Kept the skip and conditioned it on the emitted-set so a missing PR doesn't silently hide the gate signal.Test Plan
pnpm test NeedsAttentionList→ 9/9 passedporch checkpasses (build + tests)Test scenarios covered
blocked === null) → excluded ✓prgate → included ✓prgate → emitted exactly once (dedupe holds) ✓REVIEW_REQUIRED→ included ✓APPROVED→ excluded ✓linkedIssue(no matching builder) treated as unaffiliated ✓waitingSince === builder.blockedSince(notpr.createdAt) ✓CMAP Review (PR-845, 3-way)
waitingSinceas a non-blocking observation.waitingSincesemantics, invisible stuck builders, missinggateKindClasscase.All three of Gemini's points addressed in iter-2 commit
7ec0fc9c.