fix(schedules): load live fields in schedule lists#368
Merged
Conversation
Avoid per-schedule detail requests so authenticated clients receive consistent live fields for upcoming and skip actions. Co-authored-by: Cursor <cursoragent@cursor.com>
✱ Stainless preview buildsThis PR will update the openapi python typescript
|
jromualdez-scale
marked this pull request as ready for review
July 20, 2026 22:43
jromualdez-scale
marked this pull request as draft
July 20, 2026 22:44
Wait for bounded enrichment work to settle, preserve the previous refresh cadence, and distinguish unavailable live data from schedules with no future runs. Co-authored-by: Cursor <cursoragent@cursor.com>
jromualdez-scale
marked this pull request as ready for review
July 20, 2026 23:21
jromualdez-scale
marked this pull request as draft
July 20, 2026 23:22
The include_live list path fans out one Temporal describe per visible row. The route allows limit up to 1000, so a caller passing limit=1000&include_live=true could issue ~1000 describe RPCs in a single request (bounded only by the concurrency semaphore) and risk a gateway/client timeout. Bound the fan-out with MAX_LIVE_ENRICHMENT_ROWS: enrich up to the ceiling and serve the remainder DB-only. Deferred rows keep live_data_available=None (unknown) rather than a false False, so the client renders no spurious 'temporarily unavailable' notice. The ceiling sits well above the UI's per-agent page size, so interactive lists are always fully enriched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jromualdez-scale
marked this pull request as ready for review
July 20, 2026 23:27
jromualdez-scale
marked this pull request as draft
July 20, 2026 23:27
jromualdez-scale
marked this pull request as ready for review
July 21, 2026 13:27
danielmillerp
approved these changes
Jul 21, 2026
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.
Problem
The schedules UI loads persisted schedule definitions first, then sends one detail request per schedule to obtain live Temporal fields. In authenticated deployments, failures in that detail-request fan-out leave
next_action_timesempty without surfacing an error. As a result, valid schedules disappear from Upcoming and "Skip next run" is unavailable even though their Temporal clocks have future actions.Solution
include_livequery parameter to schedule listingTest plan
Made with Cursor
Greptile Summary
This PR moves live Temporal field enrichment from a per-schedule client-side fan-out to a single server-side batch operation gated by an opt-in
include_livequery parameter. The previous approach silently dropped schedules from the "Upcoming" view when any per-schedule detail request failed in authenticated deployments.list_schedulesnow acceptsinclude_live=True, collects visible rows, then concurrently enriches up toMAX_LIVE_ENRICHMENT_ROWS(200) via a semaphore-boundedasyncio.gather(return_exceptions=True); rows beyond the cap are returned withlive_data_available=Nonerather than a misleadingFalse. A newlive_data_available: bool | Nonefield on the response schema distinguishes enrichment success, enrichment failure, and enrichment not requested.include_live: trueandstaleTime: 30_000, replacing the removeduseAgentRunScheduleDetailsForItemsfan-out. The "Upcoming" tab shows a targeted status notice when any non-paused schedule haslive_data_available === false, directing users to the Schedules tab for definitions that are still intact.Confidence Score: 5/5
Safe to merge — the change is opt-in (default include_live=False preserves existing behavior), the fan-out ceiling and semaphore prevent overload, and all previously identified issues have been addressed.
All three concerns from the prior review round (return_exceptions=True, staleTime on enriched queries, live_data_available distinguishing enrichment failures from genuinely empty schedules) were addressed. The new logic is well-covered by four targeted service tests. The DB-only default path is unchanged, and the include_live=True path degrades gracefully per row rather than failing the whole request.
No files require special attention. agentex/src/domain/services/agent_run_schedule_service.py carries the most new logic but is thoroughly tested.
Important Files Changed
Sequence Diagram
%%{init: {'theme': 'neutral'}}%% sequenceDiagram participant UI as ScheduledTasksPage participant Hook as useAgentRunSchedules participant API as GET /agents/{id}/schedules participant Svc as AgentRunScheduleService participant DB as Postgres participant T as Temporal (xN semaphore10) UI->>Hook: mount / staleTime expires Hook->>API: "?include_live=true&limit=50" API->>Svc: "list_schedules(include_live=True)" Svc->>DB: list_by_agent_id() DB-->>Svc: rows (all) Note over Svc: auth filter to visible_rows Note over Svc: live_rows = visible_rows[:200] par asyncio.gather semaphore 10 Svc->>T: describe_schedule(id_1) T-->>Svc: ScheduleDescription Svc->>T: describe_schedule(id_2) T-->>Svc: ScheduleDescription or Exception end Note over Svc: live_data_available True or False per row Svc-->>API: AgentRunScheduleListResponse API-->>Hook: JSON run_schedules total Hook-->>UI: data staleTime 30s Note over UI: upcoming filter next_action_times not null Note over UI: notice if live_data_available is false%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% sequenceDiagram participant UI as ScheduledTasksPage participant Hook as useAgentRunSchedules participant API as GET /agents/{id}/schedules participant Svc as AgentRunScheduleService participant DB as Postgres participant T as Temporal (xN semaphore10) UI->>Hook: mount / staleTime expires Hook->>API: "?include_live=true&limit=50" API->>Svc: "list_schedules(include_live=True)" Svc->>DB: list_by_agent_id() DB-->>Svc: rows (all) Note over Svc: auth filter to visible_rows Note over Svc: live_rows = visible_rows[:200] par asyncio.gather semaphore 10 Svc->>T: describe_schedule(id_1) T-->>Svc: ScheduleDescription Svc->>T: describe_schedule(id_2) T-->>Svc: ScheduleDescription or Exception end Note over Svc: live_data_available True or False per row Svc-->>API: AgentRunScheduleListResponse API-->>Hook: JSON run_schedules total Hook-->>UI: data staleTime 30s Note over UI: upcoming filter next_action_times not null Note over UI: notice if live_data_available is falseReviews (5): Last reviewed commit: "Merge branch 'main' into jerome/schedule..." | Re-trigger Greptile