Skip to content

Conversation

@Mbeaulne
Copy link
Collaborator

@Mbeaulne Mbeaulne commented Jan 8, 2026

Description

Added a task status list feature that allows users to view and navigate to individual tasks in a pipeline run. The implementation includes:

  • A collapsible status bar that shows task statuses grouped by their execution state
  • Ability to navigate to specific tasks in the pipeline graph
  • Automatic highlighting of tasks when selected
  • Task grouping by status with expandable sections
  • Integration with the existing status bar component

Type of Change

  • New feature
  • Improvement

Checklist

  • I have tested this does not break current pipelines / runs functionality
  • I have tested the changes on staging

Test Instructions

  1. Open any pipeline run with multiple tasks
  2. Click "View all" under the status bar
  3. Expand status groups and click on individual tasks to navigate to them
  4. Verify that the graph focuses on the selected task and highlights it

status_click.mov (uploaded via Graphite)

Additional Comments

This feature improves pipeline run debugging by making it easier to find and navigate to specific tasks, especially in complex pipelines with many nested components.

Copy link
Collaborator Author

Mbeaulne commented Jan 8, 2026

This stack of pull requests is managed by Graphite. Learn more about stacking.

@Mbeaulne Mbeaulne force-pushed the 01-08-status_click_through branch 2 times, most recently from a8d0d34 to 5f33bd2 Compare January 12, 2026 15:58
@Mbeaulne Mbeaulne changed the title status click through Add expandable task status list to pipeline run details Jan 12, 2026
@Mbeaulne Mbeaulne force-pushed the 01-08-status_click_through branch 11 times, most recently from 2c94e0d to 0a1f283 Compare January 12, 2026 21:31
@Mbeaulne Mbeaulne changed the title Add expandable task status list to pipeline run details Add task status list with node focus navigation in pipeline run view Jan 12, 2026
@Mbeaulne Mbeaulne force-pushed the 01-08-status_click_through branch 4 times, most recently from 26c3d2c to 8c37884 Compare January 12, 2026 21:56
Comment on lines 21 to 28
const handleClick = () => {
const { focus: _, ...rest } = search;
navigate({
to: pathname,
search: rest,
replace: true,
});
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical race condition: The global click handler fires during the capture phase before the task navigation click handlers. When a user clicks on a task in the status list:

  1. This handler fires first (capture phase) and removes the focus parameter
  2. Then the task item's click handler fires and sets a new focus parameter
  3. Both handlers call navigate() with replace: true, causing a navigation race

This will cause the focus highlighting to flicker or fail entirely when clicking on tasks from the status list.

Fix: Add a check to ignore clicks on task navigation elements:

const handleClick = (event: MouseEvent) => {
  // Don't clear focus if clicking on task navigation elements
  const target = event.target as HTMLElement;
  if (target.closest('[data-task-navigation]')) {
    return;
  }
  
  const { focus: _, ...rest } = search;
  navigate({
    to: pathname,
    search: rest,
    replace: true,
  });
};

And add data-task-navigation attribute to the task buttons in TaskStatusList.tsx.

Suggested change
const handleClick = () => {
const { focus: _, ...rest } = search;
navigate({
to: pathname,
search: rest,
replace: true,
});
};
const handleClick = (event: MouseEvent) => {
// Don't clear focus if clicking on task navigation elements
const target = event.target as HTMLElement;
if (target.closest('[data-task-navigation]')) {
return;
}
const { focus: _, ...rest } = search;
navigate({
to: pathname,
search: rest,
replace: true,
});
};

Spotted by Graphite Agent

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

@Mbeaulne Mbeaulne marked this pull request as ready for review January 14, 2026 21:13
@Mbeaulne Mbeaulne requested a review from a team as a code owner January 14, 2026 21:13
@Mbeaulne Mbeaulne force-pushed the 01-08-status_click_through branch 2 times, most recently from 4564ec1 to 84fe6c1 Compare January 15, 2026 15:48
@Mbeaulne Mbeaulne force-pushed the 01-08-status_click_through branch from 84fe6c1 to 9cace25 Compare January 15, 2026 15:54
Comment on lines +68 to +70
if (!componentSpec || !state || !details || !backendUrl) {
return [];
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: due to enabled condition, this extra check may not be required, unless it narrows down the typing.

},
enabled:
!!componentSpec && !!state && !!details && !!backendUrl && configured,
staleTime: 30000,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: use 30 * SECONDS for better readability

"rounded-2xl border-gray-200 border-2 wrap-break-word p-0 drop-shadow-none gap-2",
selected ? "border-gray-500" : "hover:border-slate-200",
(highlighted || highlightedState) && "border-orange-500!",
(highlighted || highlightedState || isFocusedByUrl) &&
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this condition becomes pretty complex - it makes sense to extract it into a dedicated variable to improve readability.

rootExecutionId?: string;
}

const TaskStatusBar = ({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not related to the PR, but it bothers me: we have TWO aliases for the same component: TaskStatusBar and just StatusBar. We should fix it future

Comment on lines +140 to +174
{showViewAll && (
<CollapsibleTrigger asChild>
<InlineStack align="end">
<Button
variant="link"
size="inline-xs"
className="text-muted-foreground hover:text-foreground"
>
<InlineStack gap="1" blockAlign="center">
{isExpanded ? "Hide" : viewAllLabel}
<Icon
name="ChevronDown"
size="xs"
className={cn(
"transition-transform duration-200",
isExpanded && "rotate-180",
)}
/>
</InlineStack>
</Button>
</InlineStack>
</CollapsibleTrigger>
)}
</BlockStack>
{showViewAll && (
<CollapsibleContent>
<BlockStack className="mt-1 max-h-64 overflow-y-auto">
<TaskStatusList
tasks={taskStatuses}
rootExecutionId={rootExecutionId}
/>
</BlockStack>
</CollapsibleContent>
)}
</BlockStack>
</Collapsible>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this must be a separate component for simplified maintenance. StatusBar is used a lot across the app, and only in one place with taskStatuses (which changes behavior drastically). So my suggestion is to extract this into a separate component and use it next to the StatusBar in RunDetails.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reviewing, I think it definitely need to be a separate component. Also helpers from collectAllTaskStatuses need to be ingested by that new component in form of lazy useSuspenseQuery. useTaskNavigation must be also co-located with the new component.

return undefined;
};

export const useNodeFocus = () => {
Copy link
Collaborator

@maxy-shpfy maxy-shpfy Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The purpose of the hook is to "remove selection" when clicked outside of the selected node (click-away-to-dismiss behavior), if I read it correctly.

I would advice to change the name of the hook to be reflect the purpose.

const target = event.target;
if (
target instanceof HTMLElement &&
target.closest("[data-task-status-list]")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Magic string. Risk of divergence in future, if someone changes it in a different place.

* Hook for navigating to a task from the status list.
* Updates the URL with `?focus=nodeId` to trigger node highlighting.
*/
export const useTaskNavigation = ({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hook is used in one place only. I suggest to move it closer to the usage, so it follows "co-location" principle.

filter?: string;
};

const validateHomeSearch = (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how is it related to the PR?

* Recursively collects all LEAF tasks (non-subgraph tasks) with their execution statuses.
* This traverses into subgraphs to find the actual tasks.
*/
export const collectAllTaskStatuses = async (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this been used only in one place. I suggest to move this helper closer to the usage to satisfy our co-location principle.

Comment on lines +88 to +97
if (subgraphExecutionId && context.backendUrl) {
try {
[nestedDetails, nestedState] = await Promise.all([
fetchExecutionDetails(subgraphExecutionId, context.backendUrl),
fetchExecutionState(subgraphExecutionId, context.backendUrl),
]);
} catch {
// If fetching fails, continue without nested execution data
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's something we need to think about. We were fighting hard to reduce number of requests coming from the Pipeline Run page and managed to do that.

Now we introducing those queries back. I would suggest to rethink and try to find way to avoid this or at least make it "lazy" - component is "hidden" by default, so we can fetch data only when we "expand" it.

Also "state" wont be re-fetched, as fast as I understood
image.png

Copy link
Collaborator

@maxy-shpfy maxy-shpfy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UI and purpose seems to be pretty nice! I liked it.

But

We were fighting hard to reduce number of requests coming from the Pipeline Run page and managed to do that.

Now we introducing those queries back. I would suggest to rethink and try to find way to avoid this or at least make it "lazy" - component is "hidden" by default, so we can fetch data only when we "expand" it.

Also "state" wont be re-fetched leading to "stale" data displayed in the expanded form.

Screen Recording 2026-01-15 at 10.46.29 AM.mov (uploaded via Graphite)

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.

3 participants