Skip to content

Commit 79417f3

Browse files
🔄 synced local 'skyvern-frontend/src/' with remote 'skyvern-frontend/src/'
## Summary - [SKY-7719](https://linear.app/skyvern/issue/SKY-7719/workflow-run-timeline-missing-steps-until-page-refresh) Fixes a race condition where the workflow run timeline would display incomplete step data until the page was manually refreshed. The timeline now properly synchronizes with the workflow run query to ensure it always shows the most up-to-date information. ## Problem The workflow run timeline and the workflow run details were polling independently at 5-second intervals. When a workflow completed, the workflow run query would detect the finalized status and stop polling - but this would also stop the timeline's independent polling. If the timeline's last fetch occurred before the workflow completed, it would be left displaying stale, incomplete data. Users had to manually refresh the page to see all steps. ## What Changed - Added synchronization mechanism between timeline query and workflow run query in `useWorkflowRunTimelineQuery.ts` - Timeline now tracks `dataUpdatedAt` from the workflow run query using `useEffect` and `useRef` - Whenever the workflow run query receives new data, the timeline query is automatically invalidated and refetches - Removed independent `refetchInterval` from timeline query since it now follows the workflow run query's polling schedule - Added detailed comments explaining the synchronization strategy ## Screenshots ### Before (without refresh) <img width="1506" height="1260" alt="image" src="https://github.com/user-attachments/assets/fa7b383e-a371-4d53-86fd-e8b57875c3aa" /> ### Before (after refresh) <img width="1484" height="1243" alt="image" src="https://github.com/user-attachments/assets/84ab7a69-4f36-4558-9139-01e279cb21f8" /> ### After the fix (no refresh needed) <img width="1509" height="1255" alt="image" src="https://github.com/user-attachments/assets/0db021b0-0879-412f-a483-d2e6b83b1c4b" /> ## Test plan - [ ] Start a workflow run and observe the timeline updating in real-time - [ ] Verify that when the workflow completes, the timeline shows all steps without requiring a page refresh - [ ] Test with both fast-completing and long-running workflows - [ ] Verify timeline updates correctly when navigating away and back to the workflow run page - [ ] Confirm no unnecessary re-renders or duplicate API calls 🤖 Generated with [Claude Code](https://claude.ai/code) <!-- ELLIPSIS_HIDDEN --> ---- > [!IMPORTANT] > Fixes race condition in `useWorkflowRunTimelineQuery.ts` by synchronizing timeline query with workflow run query using `useEffect` and `useRef`. > > - **Synchronization**: > - Added synchronization between timeline query and workflow run query in `useWorkflowRunTimelineQuery.ts` using `useEffect` and `useRef`. > - Timeline query invalidates and refetches when `dataUpdatedAt` changes in workflow run query. > - **Polling**: > - Removed independent `refetchInterval` from timeline query; now follows workflow run query's schedule. > - **Comments**: > - Added detailed comments explaining synchronization strategy in `useWorkflowRunTimelineQuery.ts`. > > <sup>This description was created by </sup>[<img alt="Ellipsis" src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=Skyvern-AI%2Fskyvern-cloud&utm_source=github&utm_medium=referral)<sup> for 58783f1d33e2a898d400f2d762f4366afe334d02. You can [customize](https://app.ellipsis.dev/Skyvern-AI/settings/summaries) this summary. It will automatically update as commits are pushed.</sup> <!-- ELLIPSIS_HIDDEN -->
1 parent ee1cb01 commit 79417f3

File tree

1 file changed

+30
-4
lines changed

1 file changed

+30
-4
lines changed

‎skyvern-frontend/src/routes/workflows/hooks/useWorkflowRunTimelineQuery.ts‎

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
import { useEffect, useRef } from "react";
12
import { getClient } from "@/api/AxiosClient";
23
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
34
import { statusIsNotFinalized } from "@/routes/tasks/types";
4-
import { keepPreviousData, useQuery } from "@tanstack/react-query";
5+
import {
6+
keepPreviousData,
7+
useQuery,
8+
useQueryClient,
9+
} from "@tanstack/react-query";
510
import { WorkflowRunTimelineItem } from "../types/workflowRunTypes";
611
import { useWorkflowRunWithWorkflowQuery } from "./useWorkflowRunWithWorkflowQuery";
712
import { useGlobalWorkflowsQuery } from "./useGlobalWorkflowsQuery";
@@ -10,11 +15,32 @@ import { useFirstParam } from "@/hooks/useFirstParam";
1015
function useWorkflowRunTimelineQuery() {
1116
const workflowRunId = useFirstParam("workflowRunId", "runId");
1217
const credentialGetter = useCredentialGetter();
18+
const queryClient = useQueryClient();
1319
const { data: globalWorkflows } = useGlobalWorkflowsQuery();
14-
const { data: workflowRun } = useWorkflowRunWithWorkflowQuery();
20+
const { data: workflowRun, dataUpdatedAt } =
21+
useWorkflowRunWithWorkflowQuery();
1522
const workflow = workflowRun?.workflow;
1623
const workflowPermanentId = workflow?.workflow_permanent_id;
1724

25+
// Track when workflow run data was last updated
26+
const prevDataUpdatedAtRef = useRef<number>(dataUpdatedAt);
27+
28+
// Refetch timeline whenever the workflow run query gets new data.
29+
// This keeps the timeline perfectly synchronized with the workflow run status,
30+
// ensuring we never miss updates (e.g., when workflow completes).
31+
useEffect(() => {
32+
if (
33+
dataUpdatedAt !== prevDataUpdatedAtRef.current &&
34+
workflowPermanentId &&
35+
workflowRunId
36+
) {
37+
queryClient.invalidateQueries({
38+
queryKey: ["workflowRunTimeline", workflowPermanentId, workflowRunId],
39+
});
40+
}
41+
prevDataUpdatedAtRef.current = dataUpdatedAt;
42+
}, [dataUpdatedAt, workflowPermanentId, workflowRunId, queryClient]);
43+
1844
return useQuery<Array<WorkflowRunTimelineItem>>({
1945
queryKey: ["workflowRunTimeline", workflowPermanentId, workflowRunId],
2046
queryFn: async () => {
@@ -33,8 +59,8 @@ function useWorkflowRunTimelineQuery() {
3359
)
3460
.then((response) => response.data);
3561
},
36-
refetchInterval:
37-
workflowRun && statusIsNotFinalized(workflowRun) ? 5000 : false,
62+
// No independent refetchInterval - timeline follows workflow run query's timing
63+
// via the useEffect above that invalidates on dataUpdatedAt changes
3864
placeholderData: keepPreviousData,
3965
refetchOnMount:
4066
workflowRun && statusIsNotFinalized(workflowRun) ? "always" : false,

0 commit comments

Comments
 (0)