Skip to content

fix(schedules): load live fields in schedule lists#368

Merged
jromualdez-scale merged 4 commits into
mainfrom
jerome/schedule-list-live-fields
Jul 21, 2026
Merged

fix(schedules): load live fields in schedule lists#368
jromualdez-scale merged 4 commits into
mainfrom
jerome/schedule-list-live-fields

Conversation

@jromualdez-scale

@jromualdez-scale jromualdez-scale commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

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_times empty 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

  • add an opt-in include_live query parameter to schedule listing
  • enrich authorized rows with live Temporal fields using a concurrency limit of 10
  • keep the existing database-only list behavior as the default for compatibility and performance
  • have the UI request enriched lists and remove the per-schedule detail-request fan-out
  • surface a notice when an active schedule still lacks next-run data

Test plan

  • Backend schedule service unit tests
  • Backend schedule authorization route unit tests
  • Frontend schedule hook tests
  • Frontend TypeScript typecheck
  • Frontend lint and formatting checks
  • Local smoke test confirms enriched lists return future action times

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_live query parameter. The previous approach silently dropped schedules from the "Upcoming" view when any per-schedule detail request failed in authenticated deployments.

  • Backend: list_schedules now accepts include_live=True, collects visible rows, then concurrently enriches up to MAX_LIVE_ENRICHMENT_ROWS (200) via a semaphore-bounded asyncio.gather(return_exceptions=True); rows beyond the cap are returned with live_data_available=None rather than a misleading False. A new live_data_available: bool | None field on the response schema distinguishes enrichment success, enrichment failure, and enrichment not requested.
  • Frontend: Both list hooks now pass include_live: true and staleTime: 30_000, replacing the removed useAgentRunScheduleDetailsForItems fan-out. The "Upcoming" tab shows a targeted status notice when any non-paused schedule has live_data_available === false, directing users to the Schedules tab for definitions that are still intact.
  • Tests: Four new service-layer tests cover the live path, semaphore concurrency bound, fan-out cap via monkeypatch, and the guarantee that all sibling tasks settle before re-raising an error.

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

Filename Overview
agentex/src/domain/services/agent_run_schedule_service.py Core enrichment logic: bounded gather with semaphore, MAX_LIVE_ENRICHMENT_ROWS cap, return_exceptions=True, and live_data_available tri-state field all implemented correctly
agentex/src/api/routes/agent_run_schedules.py Adds include_live query parameter with Annotated[bool, Query(...)] annotation and threads it through to the use case
agentex/src/api/schemas/agent_run_schedules.py Adds live_data_available: bool
agentex-ui/hooks/use-agent-run-schedules.ts Removes per-schedule detail fan-out; both list hooks now pass include_live:true and staleTime:30_000; AgentRunScheduleListItem export removed cleanly
agentex-ui/components/scheduled-tasks/scheduled-tasks-page.tsx Removes detailQueries fan-out; computes unavailableLiveDataCount from live_data_available===false and shows a targeted status notice on the upcoming tab
agentex-ui/lib/agent-run-schedules.ts Adds live_data_available: boolean
agentex/tests/unit/services/test_agent_run_schedule_service.py Four new tests cover include_live=True path, bounded concurrency, fan-out cap (via monkeypatch), and waiting for all rows before re-raising — solid coverage
agentex/tests/unit/api/test_agent_run_schedules_authz.py Updates authz route test to pass and assert include_live=True through the use case call
agentex-ui/hooks/use-agent-run-schedules.test.tsx New test verifies the hook passes include_live:true and that live_data_available is surfaced on the returned schedule object
agentex-ui/lib/schedule-utils.test.ts Test fixtures updated to include live_data_available: null to match the new field; no logic changes
agentex/openapi.yaml Adds include_live query parameter and live_data_available response field to the OpenAPI spec; nullable boolean uses anyOf correctly
agentex/src/domain/use_cases/agent_run_schedules_use_case.py Minimal pass-through change: adds include_live parameter to list_schedules and forwards it to the service

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
Loading
%%{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 false
Loading

Reviews (5): Last reviewed commit: "Merge branch 'main' into jerome/schedule..." | Re-trigger Greptile

Avoid per-schedule detail requests so authenticated clients receive consistent live fields for upcoming and skip actions.

Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown

✱ Stainless preview builds

This PR will update the agentex-sdk SDKs with the following commit messages.

openapi

feat(api): add include_live query parameter

python

feat(api): add include_live parameter to schedules list method

typescript

feat(api): add include_live parameter to schedules list method
agentex-sdk-openapi studio · code

Your SDK build had at least one "note" diagnostic.
generate ✅

⚠️ agentex-sdk-typescript studio · code

Your SDK build had at least one "warning" diagnostic.
generate ⚠️build ✅lint ✅test ✅

npm install https://pkg.stainless.com/s/agentex-sdk-typescript/781ad4813e8f93f4f79e2383f6fa6717216dda2a/dist.tar.gz
⚠️ agentex-sdk-python studio · code

Your SDK build had at least one "warning" diagnostic.
generate ⚠️build ✅lint ✅test ✅

pip install https://pkg.stainless.com/s/agentex-sdk-python/199fd6a633bc82626a4629ee1ac52a40e6f10271/agentex_client-0.20.0-py3-none-any.whl

This comment is auto-generated by GitHub Actions and is automatically kept up to date as you push.
If you push custom code to the preview branch, re-run this workflow to update the comment.
Last updated: 2026-07-21 23:07:55 UTC

@jromualdez-scale
jromualdez-scale marked this pull request as ready for review July 20, 2026 22:43
@jromualdez-scale
jromualdez-scale requested a review from a team as a code owner July 20, 2026 22:43
@jromualdez-scale
jromualdez-scale marked this pull request as draft July 20, 2026 22:44
Comment thread agentex/src/domain/services/agent_run_schedule_service.py Outdated
Comment thread agentex-ui/hooks/use-agent-run-schedules.ts
Comment thread agentex-ui/components/scheduled-tasks/scheduled-tasks-page.tsx
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
jromualdez-scale marked this pull request as ready for review July 20, 2026 23:21
@jromualdez-scale
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
jromualdez-scale marked this pull request as ready for review July 20, 2026 23:27
@jromualdez-scale
jromualdez-scale marked this pull request as draft July 20, 2026 23:27
@jromualdez-scale
jromualdez-scale marked this pull request as ready for review July 21, 2026 13:27
@jromualdez-scale
jromualdez-scale merged commit c045ad0 into main Jul 21, 2026
33 checks passed
@jromualdez-scale
jromualdez-scale deleted the jerome/schedule-list-live-fields branch July 21, 2026 23:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants