Repository navigation
fix(history): fetch actionable executions separately so terminal rows cannot starve them - #684
Conversation
… cannot starve them - The History view loaded one list of the 100 newest executions across all twelve statuses, so 100+ newer failed/expired/canceled rows pushed an older pending approval out before any filter ran (#632). - Fetch the actionable statuses (pending, notified, scheduled, approved, running, paused) at MaxListLimit and the terminal statuses at DefaultListLimit; truncated now fires per class. - Bound the stale-expiry sweep to 100 rows per request: the actionable fetch can now return far more stale rows, and Lambda sweeps synchronously. - Make MockConfigStore.GetExecutionsByStatuses honour statuses, limit, ordering and the clean-completed exclusion like the real store, so two calls do not return the same canned rows twice. - Not fixed here: far-future ramp steps sort first inside the actionable class (#680); provider/account/date and scope pushdown is the next PR. Refs #632
|
Warning Review limit reached
This review includes 6 billable files and costs up to $1.50.
Reviews can continue after your included limit without a manual trigger. An admin must approve usage-based billing. Or wait 55 minutes for your next included review. View limit detailsLimit details: You’ve used the included review currently available. Your 83 included PR review attempts over the past 7 days set your current allowance at 1 review per hour. Review configuration: ⚙️ Run configuration
📒 Files selected for processing (6)
Comment |
|
Independent full-PR review (cc-cli-w2, Claude Opus 5.5; did not write this PR) at exact head Verdict: CLEAN for merge; no blocking findings. There is one low-severity actionable item (UI copy), which can be fixed here or explicitly deferred to the frontend PR. I also have two non-blocking recommendations. What I checkedNo row lost or duplicated between the two queries. The mock is faithful to the real store.
The only difference is tie order: the store's is unspecified and the mock's is stable. Scope and leak. Other Reproduced
FindingsA1 (low, actionable or explicitly deferred). R1 (non-blocking). A listing error on the actionable class logs, skips the class and leaves R2 (non-blocking). Add a behavioural test with more than 100 actionable rows (for example, 150 pending plus 1 older pending) to pin the 1000 cap by outcome rather than by the mock-argument assertion. Known residual, confirmed (tracked in #680). The actionable class is ordered by Note. A row that changes from actionable to terminal between the two queries can appear in both (an audit-gap completed row) or briefly in neither. This only affects a single response, and the handler is read-only, so it isn't a concern. The SQL prefilter (provider/account/date/scope pushdown) is #632 PR2 and isn't in this PR, so its superset property is reviewed there. |
|
CI follow-up for
Every other check passed: unit, integration, E2E, security, gosec and Terraform validate. The rest of my review verdict stands. These are mechanical fixes, and a new head needs only a delta re-review. |
…come - handler_history.go: build the merged execution list in a fresh slice instead of appending onto a re-sliced one (gocritic appendAssign). - mocks/stores.go: range the canned rows by index (gocritic rangeValCopy). - handler_history_truncation_test.go: failedExecutionsAtCap takes no parameter, as every caller passed DefaultListLimit (unparam). - Add a test that more than DefaultListLimit pending rows are all returned and not flagged truncated, so the 1000 actionable cap is pinned by outcome and not only by the limit argument (review note R2). Refs #632
|
Delta re-review (cc-cli-w2, independent; did not write this PR) of Verdict: CLEAN, no actionable findings in the delta. My earlier review of
CI at
|
Why
GET /api/historyloaded one list of the 100 newest executions across all twelve statuses, then applied provider/account/date and scope filters in Go. 100+ newer terminal rows (failed, expired, canceled, ...) therefore pushed an older pending approval off the list before any filter ran, and the Approval Queue under-reported (#632, umbrella #248). #630 only added thetruncatedflag.What
internal/api/handler_history.go: executions are fetched as two disjoint classes. Actionable (pending, notified, scheduled, approved, running, paused) atMaxListLimit; terminal (completed, partially_completed, failed, expired, canceled, legacy canceled) atDefaultListLimit. A listing error on one class is logged and that class skipped; the other still renders.truncatedfires per class.historyExecutionStatusesis removed (no other Go reference).internal/mocks/stores.go:MockConfigStore.GetExecutionsByStatusesnow honours statuses, limit, ordering and the clean-completed exclusion like the real store, otherwise two calls would return the same canned rows twice. Three truncation tests that fed 100pendingrows now feedfailedrows (same intent: a full terminal class yields the flag).types.goandopenapi.yamlwording fortruncated/executions_limit.Verification (local, Go, non-race, fixture/mock based; no real database)
handler_history_actionable_starvation_test.go(A to E) through the realgetHistorypath. A (150 newer failed rows + 1 older pending) FAILS on the old handler ("older pending row must survive 150 newer failed rows", total_pending 0 vs 1) and passes now.go test ./internal/apipasses;go vetandgofmtclean;go build ./...clean.Known limits (deliberately not in this PR)
truncatedstays honest because it fires at 1000.executions_limitstays 100 (terminal cap), so the existing frontend notice can print "100" when only the actionable class hit its cap (wording follow-up in feat(frontend): Load more on the History view, Approval Queue from all loaded pages #676). Comments mentioninghistoryExecutionStatusesremain infrontend/src/history.ts,frontend/src/types.tsand migration 000089.Refs #632 (umbrella #248). Not
Closes: PR2 is still required.🤖 Generated with Claude Code