feat(api)!: list runs by stack - #366
Open
aaaaahaaaaa wants to merge 1 commit into
Open
aaaaahaaaaa wants to merge 1 commit into
aaaaahaaaaa wants to merge 1 commit into
Conversation
A stack is one piece of work, so a listing shows its latest attempt and every filter reads that attempt: a stack whose first attempt failed and whose second succeeded is a success, which is what a reader means by "failed runs". Passing `root_run_id` asks for one stack's attempts instead, newest first. The narrowing is a grouped join rather than `DISTINCT ON`, so it runs on SQLite as well as Postgres, and it is scoped to the organisation alone: every attempt of a stack shares its target, its backfill and its org, so no other filter can change which attempt is the latest. Narrowing by the caller's filters would answer a different question, "the latest failed attempt" rather than "the stacks whose latest attempt failed". `RunResponse` gains `root_run_id` and `scheduled_for`. It gains no attempt count: a listing row is the stack's latest attempt, so `attempt` already is how many the stack took, and a second field would only duplicate it, or lie when an older attempt is fetched by id. In the app a row shows how many attempts its work took, the run page walks the stack's attempts, and the realtime path supersedes an earlier attempt's row instead of appending beside it, which would have broken the one-row-per-stack invariant the listing promises. By Digitl
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
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.
What
Phase 3 of the retry system, and the last: a stack reads as one piece of work in the API and the app, so a workload that eventually succeeded looks like a success.
Implements the retry spec section 9, following the phase 3 plan. Builds on #364 (core) and #365 (platform).
The rule
A listing shows each stack's latest attempt, and every filter reads that attempt. A stack whose first attempt failed and whose second succeeded is a success, which is what a reader means by "failed runs".
?root_run_id=asks for one stack's attempts instead, newest first.In the app: a row shows how many attempts its work took, the run page walks the stack's attempts, and the docs gain a Retrying section in the execution guide and a Retries section in the hooks guide.
For the reviewer
Three decisions that differ from the plan, each for a reason worth checking:
No
attemptsfield onRunResponse. The plan had one. A listing row is the stack's latest attempt, and attempts are consecutive, soattemptalready says how many the stack took. A second field would duplicate it in a listing and lie when an older attempt is fetched by id. Dropped rather than shipped as a near-synonym.The latest-attempt narrowing is a grouped join, not
DISTINCT ON.DISTINCT ONis Postgres-only and the store tests run on SQLite, so this reuses the idiom_advance_backfillalready established. It is also scoped to the organisation alone, deliberately: every attempt of a stack shares its target, its backfill and its org, so no other filter can change which attempt is latest. Narrowing by the caller's filters would quietly answer a different question, "the latest failed attempt" rather than "the stacks whose latest attempt failed".The realtime path needed fixing, which the plan missed. A new attempt arriving over the realtime subscription would have been appended beside its predecessor, showing both attempts of one stack until the next refetch and breaking the invariant the listing promises.
_upsertnow supersedes the earlier attempt's row.One existing API test needed its
SimpleNamespacerun stub extended with the two new fields.Not verified
The UI is typechecked and linted but not visually confirmed. The plan's live step, standing up a seeded instance on
:3100to see the attempt chip and the stack switcher render, was not run: it goes against the shared dev database and needs a login session. The changes are small (a conditional chip in one cell renderer, a conditional link list in the run page header), but they have not been seen in a browser.Verification
uv run ruff check,uv run ty check,uv run pytest: 2914 passed, 9 skipped. Frontend:pnpm exec nuxt typecheckexits 0,pnpm run lintreports 0 errors (4 pre-existing warnings).After this
The retry system is complete end to end: operations retry in place, runs retry as stacks, backfills and hooks report the stack's verdict, and the surfaces read it as one piece of work. Nothing retries until a component declares a policy, since there is deliberately no instance-wide default; that, and per-attempt spans in traces, are recorded as follow-ups in the spec.
By Digitl