From ee5a4fe4641d790664932c8f53b37226871dcc93 Mon Sep 17 00:00:00 2001 From: Rassl Date: Tue, 28 Jul 2026 11:46:17 +0700 Subject: [PATCH] fix: show human review as Sent once dispatched, not Sending MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A human review runs for as long as a human takes, so a PENDING/RUNNING StakworkRun rendered "Sending…" with a spinner for that entire duration — implying the dispatch was still in progress when it had already completed. The spinner is now scoped to the POST itself (sub-second). Once a run exists, running and completed both read "Sent", which is the only fact the operator needs. Polling is unchanged: onCompleted still refreshes the list when the workflow's results land as new Reviews. The sent state is now genuinely disabled rather than only click-guarded. Co-Authored-By: Claude Opus 5 (1M context) --- src/components/admin/review-row.tsx | 32 +++++++++++++++++------------ src/lib/__tests__/reviews.test.tsx | 13 +++++++----- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/components/admin/review-row.tsx b/src/components/admin/review-row.tsx index c9de0a2..3cfb34f 100644 --- a/src/components/admin/review-row.tsx +++ b/src/components/admin/review-row.tsx @@ -583,6 +583,14 @@ export function ReviewRow({ } ) + // A human review takes as long as it takes — showing its run as "Sending…" + // for that whole time reads as though the dispatch is still going. Once the + // run exists, the only fact the operator needs is that it was sent, so an + // in-flight run and a completed one look the same here. Polling continues + // regardless: onCompleted is what surfaces the workflow's results. + const humanReviewSent = + humanReviewStatus === "COMPLETED" || humanReviewInFlight + // ── Merge-specific interactive state ──────────────────────────────────────── const [checkedSources, setCheckedSources] = useState>(new Set()) const [canonicalId, setCanonicalId] = useState("") @@ -778,38 +786,36 @@ export function ReviewRow({