Skip to content

fix(agents): render recent runs oldest-to-newest on the Agents page - #31645

Open
aniketkatkar97 wants to merge 3 commits into
mainfrom
recent-runs-sort-order-bug
Open

fix(agents): render recent runs oldest-to-newest on the Agents page#31645
aniketkatkar97 wants to merge 3 commits into
mainfrom
recent-runs-sort-order-bug

Conversation

@aniketkatkar97

@aniketkatkar97 aniketkatkar97 commented Aug 17, 2026

Copy link
Copy Markdown
Member

Describe your changes:

No linked issue — reported directly. Please link one if you would like the metadata gate to pass.

Recent runs are supposed to read chronologically left → right (oldest leftmost, newest rightmost). The legacy Ant badge row IngestionRecentRuns was fixed to do that in #23572, but the newer ServiceAgents surfaces never got the same treatment: they render whatever order the API returns, and the backend serves pipeline statuses newest-first (IngestionPipelineRepository uses OrderBy.DESC plus a reverseOrder() comparator).

So on the Agents tab both surfaces read right → left, with the latest run on the left:

  • Agent card run dotsbuildRecentRuns only did .filter().slice(0, 5), no sort, and AgentCard gave full opacity to index === 0.
  • Run history drawer railuseAgentRuns did res.data.map(mapPipelineStatusToRun) with no sort, and the drawer defaulted its selection to runs[0].

This aligns both with the IngestionRecentRuns convention, so ordering is one convention across the app.

Type of change:

  • Bug fix

High-level design:

Order is normalised once, at the data boundary (mapper + hook); the two renderers that assumed index 0 was the latest were adjusted to the last index.

File Change
utils/agentsDataMapper.ts buildRecentRuns sorts descending by timestamp, takes the newest 5, then reverses.
components/AgentCard.component.tsx Latest-run highlight moves from index === 0 to the last index; adds data-run-status.
hooks/useAgentRuns.ts Sorts the raw PipelineStatus[] ascending by timestamp before mapping.
components/RunHistoryDrawer.component.tsx Defaults selection to runs.at(-1); scrolls the rail to keep the selected card in view.

Decisions worth calling out:

  • Window before reversing. Capping an already-ascending list would keep the five oldest runs, so the newest-5 window is taken first and reversed after.
  • Never sort in place. pipeline.pipelineStatuses is shared, and mapPipelineToAgent, AgentsStatusWidgetUtils and useEntityLogs all read [0] as the latest run. filter runs before sort so the sort operates on its copy. There is a regression test asserting the caller's array is not reordered.
  • Sort the raw statuses, not the mapped runs. AgentRun.startedAt is an already-formatted display string and cannot be ordered, so useAgentRuns sorts PipelineStatus[] before mapping.
  • scrollLeft, not scrollIntoView. The rail fits four or five of the ten cards, so the newest run is now outside the initial scroll window. The drawer body is overflow-y-auto, so scrollIntoView would also move it vertically.
  • Sorted defensively rather than trusting the API order, so an unsorted or mixed-order response still renders correctly.

Alternatives rejected: reversing at render time in each component (leaves the two surfaces free to drift apart again, which is how this bug happened), and flex-row-reverse on the rail (breaks tab order and does not fix the underlying data order).

Also included: ServiceAgentsDeploymentSummary.spec.ts was missing from the impact-map.json entry that covers src/components/ServiceAgents/**, so it only ran when the spec itself was edited. It now carries the ordering coverage, so it is mapped. (ServiceAgentsPauseResume.spec.ts and ServiceAgentsRefresh.spec.ts have the same gap but are unrelated to this change and were left alone.)

Tests:

Use cases covered

  • On a service's Agents tab, an agent with several completed runs shows its run dots oldest → newest left to right, with only the rightmost (latest) dot at full opacity.
  • Opening "View run history" shows the run cards oldest → newest left to right, opens on the newest (rightmost) run, and scrolls it into view.
  • Clicking an older dot still opens the drawer on that specific run.
  • An agent with more than five completed runs shows the five newest, not the five oldest.
  • Consumers that read pipelineStatuses[0] as the latest run (status pill, current run id, last-run timestamp) are unaffected.

Unit tests

  • I added unit tests for the new/changed logic.
  • Files updated:
    • utils/agentsDataMapper.test.ts — ascending order from a newest-first response; ordering from an unsorted response; newest-five window; caller's array not reordered while currentRunId/lastRunAt still resolve to the newest run.
    • components/AgentCard.test.tsx — dot order via data-run-status; only the rightmost dot undimmed.
    • components/RunHistoryDrawer.test.tsx — cards oldest-first, rightmost selected; initialRunId still wins over the default.
  • yarn test src/components/ServiceAgents13 suites / 222 tests passed.
  • Reverting only the four source files → 3 suites / 10 tests failed, confirming the assertions catch the bug rather than describing the new behaviour.

Backend integration tests

  • Not applicable (no backend changes).

Ingestion integration tests

  • Not applicable (no ingestion changes).

Playwright (UI) tests

  • I added Playwright E2E tests.
  • Files updated: playwright/e2e/Features/ServiceAgentsDeploymentSummary.spec.ts — new Service Agents recent run ordering describe with two tests. Extends the existing mockAgentsTab harness to support a multi-run history and adds the pipelineStatus route the drawer reads (previously unmocked).
  • Local run against a dev server: 8 passed.
  • With the fix reverted, both new tests fail with Expected substring: "Partial Success" / Received string: "FailedNov 15, 2023..." on the first card — i.e. the exact reported symptom.

Manual testing performed

  1. Started a Vite dev server for this worktree against a local backend on :8585.
  2. Opened a database service's Agents tab; confirmed the dots read oldest → newest left to right and only the rightmost is at full opacity.
  3. Clicked "View run history"; confirmed the rail reads oldest → newest, opens on the rightmost card, and scrolls it into view.
  4. Clicked an older dot; confirmed the drawer opens on that run and scrolls to it.

UI screen recording / screenshots:

Ordering change only; the Playwright assertions above pin the before/after (leftmost card was Failed — the newest run — and is now Partial Success, the oldest). Happy to attach a recording if reviewers want one.

Checklist:

  • I have read the CONTRIBUTING document.

  • My PR title is Fixes <issue-number>: <short explanation> — no issue exists yet.

  • My PR is linked to a GitHub issue via Fixes #<issue-number> above — no issue exists yet.

  • I have commented on my code, particularly in hard-to-understand areas.

  • For JSON Schema changes: not applicable.

  • For UI changes: see the note above in place of a recording.

  • I have added tests (unit / integration / Playwright as applicable) and listed them above.

  • I have added a test that covers the exact scenario we are fixing.

🤖 Generated with Claude Code

Greptile Summary

The PR normalizes recent agent runs into chronological display order while preserving newest-run selection and the newest-five window.

  • Sorts completed card runs oldest-to-newest without mutating shared pipeline status arrays.
  • Sorts drawer history oldest-to-newest and defaults selection to the newest, rightmost run.
  • Adds unit and Playwright coverage for ordering, highlighting, selection, and impact-map execution.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
openmetadata-ui/src/main/resources/ui/src/components/ServiceAgents/utils/agentsDataMapper.ts Sorts completed statuses into an oldest-to-newest newest-five window without mutating the source array.
openmetadata-ui/src/main/resources/ui/src/components/ServiceAgents/hooks/useAgentRuns.ts Normalizes API-provided pipeline history by timestamp before mapping it into drawer view models.
openmetadata-ui/src/main/resources/ui/src/components/ServiceAgents/components/AgentCard.component.tsx Moves latest-run emphasis to the rightmost dot and exposes run status for UI assertions.
openmetadata-ui/src/main/resources/ui/src/components/ServiceAgents/components/RunHistoryDrawer.component.tsx Selects the final chronological run by default and horizontally positions the selected history card.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ServiceAgentsDeploymentSummary.spec.ts Adds end-to-end coverage for chronological card and drawer ordering with newest-first mocked responses.
.github/playwright/impact-map.json Maps ServiceAgents source changes to the deployment-summary Playwright suite.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  API[Pipeline statuses: newest first] --> Sort[Sort by timestamp]
  Sort --> Window[Keep newest runs]
  Window --> Order[Present oldest to newest]
  Order --> Card[Agent card dots]
  Order --> Drawer[Run-history rail]
  Drawer --> Latest[Select rightmost newest run]
Loading

Reviews (2): Last reviewed commit: "Merge branch 'main' into recent-runs-sor..." | Re-trigger Greptile

aniketkatkar97 and others added 2 commits August 17, 2026 19:31
The agent card's run dots and the run-history drawer's rail rendered their
runs in whatever order the API returned. The backend serves pipeline
statuses newest-first (IngestionPipelineRepository uses OrderBy.DESC), so
both surfaces read right-to-left, with the latest run on the left.

The legacy IngestionRecentRuns badge row already normalises to ascending
and treats the last element as the latest. This aligns the two newer
ServiceAgents surfaces with that convention:

- buildRecentRuns takes the newest five statuses, then reverses them.
  Windowing before reversing matters: capping an already-ascending list
  would keep the five oldest runs. The sort runs on filter's copy because
  every other consumer reads pipelineStatuses[0] as the latest run.
- AgentCard highlights the last dot instead of the first, and exposes
  data-run-status so order is assertable without parsing a translated
  title.
- useAgentRuns sorts the raw PipelineStatus list by timestamp before
  mapping. AgentRun.startedAt is an already-formatted display string, so
  the mapped runs cannot be ordered.
- RunHistoryDrawer defaults its selection to the newest (now last) run and
  scrolls the rail to keep the selected card in view, since the rail only
  fits four or five of the ten cards. It drives scrollLeft rather than
  calling scrollIntoView, which would also scroll the drawer vertically.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`ServiceAgentsDeploymentSummary.spec.ts` was absent from the mapping that
covers `src/components/ServiceAgents/**`, so a change to that directory
only ran the spec when the spec itself was edited. It now also carries the
recent-run ordering coverage, which is exactly the kind of regression the
mapping exists to catch.

`ServiceAgentsPauseResume.spec.ts` and `ServiceAgentsRefresh.spec.ts` have
the same gap but are unrelated to this change and are left alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 17, 2026 14:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

Copy link
Copy Markdown
Contributor

❌ PR checklist incomplete

This PR cannot be merged until the following are addressed on its linked issue:

  • No GitHub issue is linked. Link an issue in the Development section of the PR (or add Fixes #12345 to the description). For a same-org cross-repo issue, add Fixes open-metadata/<repo>#123 to the description.

The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically.

Maintainers can bypass this check by adding the skip-pr-checks label.

@github-actions github-actions Bot added safe to test Add this label to run secure Github workflows on PRs UI UI specific issues labels Aug 17, 2026
@github-actions

github-actions Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

✅ Playwright Results — workflow succeeded

Validated commit 5631196463e2183b1ab3579493294dd8650372d6 in Playwright run 32053713194, attempt 1.

✅ 622 passed · ❌ 0 failed · 🟡 1 flaky · ⏭️ 0 skipped · 🧰 0 lifecycle flaky

Performance

Blocking targets: ✅ met · Optimization targets: 🟡 in progress

Shard-job maxima below are not the full workflow wall time; the linked run includes build, fixture, planning, and reporting.

🕒 Full workflow signal wall (to summary) 56m 32s

⏱️ Max setup 3m 43s · max shard execution 18m 39s · max shard-job elapsed before upload 23m 18s · reporting 7s

🌐 218.33 requests/attempt · 2.72 app boots/UI scenario · 17.16% common-shard skew

Optimization targets still in progress:

  • Common shard skew was 17.16% (convergence target: at most 15%).
  • Browser traffic was 218.33 requests per attempt (convergence target: fewer than 200).
  • Application boot ratio was 2.72 per UI scenario (1782 boots / 654 scenarios; convergence target: at most 1).
Shard Passed Failed Flaky Skipped Lifecycle failed Lifecycle flaky
✅ Shard chromium-01 144 0 0 0 0 0
✅ Shard chromium-02 137 0 0 0 0 0
✅ Shard chromium-03 126 0 0 0 0 0
✅ Shard data-asset-rules-01 61 0 0 0 0 0
✅ Shard domain-isolation-01 14 0 0 0 0 0
✅ Shard global-state-01 34 0 0 0 0 0
✅ Shard ingestion-01 28 0 0 0 0 0
🟡 Shard ingestion-02 36 0 1 0 0 0
✅ Shard reindex-01 2 0 0 0 0 0
✅ Shard search-01 11 0 0 0 0 0
✅ Shard search-rbac-01 29 0 0 0 0 0
🟡 1 flaky test(s) (passed on retry)
  • Pages/IngestionLogStreamLive.spec.tsLive logs arrive over SSE while the agent runs, with no polling (shard ingestion-02, 1 retry)

📦 Download artifacts

How to debug locally
# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip    # view trace

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ UI Checkstyle passed — lint findings in changed files

🔍 ESLint findings in this PR's files — 0 error(s), 17 warning(s)

Errors block the build. Warnings do not yet — they are rules whose backlog is still
being worked down, listed so this PR does not add to it. See docs/ui-code-quality-gate.md.

0 error(s), 17 warning(s) across 6 changed file(s).

Count Rule
6 i18next/no-literal-string
4 sonarjs/cyclomatic-complexity
4 sonarjs/no-duplicate-string
1 sonarjs/expression-complexity
1 jsx-a11y/control-has-associated-label
1 react/no-array-index-key
All findings
Location Rule Message
🟡 src/components/ServiceAgents/components/AgentCard.component.tsx:73:4 sonarjs/cyclomatic-complexity {"message":"Function has a complexity of 28 which is greater than 10 authorized.","cost":18,"secondaryLocations":[{"line":73,"column":3,"endLine":73,"endColumn"
🟡 src/components/ServiceAgents/components/AgentCard.component.tsx:163:14 sonarjs/expression-complexity Reduce the number of conditional operators (6) used in the expression (maximum allowed 3).
🟡 src/components/ServiceAgents/components/AgentCard.component.tsx:221:19 jsx-a11y/control-has-associated-label A control must be associated with a text label.
🟡 src/components/ServiceAgents/components/AgentCard.test.tsx:26:15 i18next/no-literal-string disallow literal string:

AgentOverflowMenu

🟡 src/components/ServiceAgents/components/AgentCard.test.tsx:31:41 i18next/no-literal-string disallow literal string:

StatusPill

🟡 src/components/ServiceAgents/components/AgentCard.test.tsx:35:41 i18next/no-literal-string disallow literal string:

ProgressBar

🟡 src/components/ServiceAgents/components/AgentCard.test.tsx:39:41 i18next/no-literal-string disallow literal string:

Metric

🟡 src/components/ServiceAgents/components/AgentCard.test.tsx:150:29 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 4 times.
🟡 src/components/ServiceAgents/components/AgentCard.test.tsx:253:33 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 4 times.
🟡 src/components/ServiceAgents/components/RunHistoryDrawer.component.tsx:176:4 sonarjs/cyclomatic-complexity {"message":"Function has a complexity of 14 which is greater than 10 authorized.","cost":4,"secondaryLocations":[{"line":176,"column":3,"endLine":176,"endColumn
🟡 src/components/ServiceAgents/components/RunHistoryDrawer.component.tsx:272:57 i18next/no-literal-string disallow literal string:
{run.startedAt} ({getUtcOffsetLabel()}) ·{' '} {t('message.ran-for-duration', { du
🟡 src/components/ServiceAgents/components/RunHistoryDrawer.component.tsx:350:26 react/no-array-index-key Do not use Array index in keys
🟡 src/components/ServiceAgents/components/RunHistoryDrawer.test.tsx:25:41 i18next/no-literal-string disallow literal string:

RunStepRow

🟡 src/components/ServiceAgents/components/RunHistoryDrawer.test.tsx:159:28 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 4 times.
🟡 src/components/ServiceAgents/utils/agentsDataMapper.test.ts:785:25 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 3 times.
🟡 src/components/ServiceAgents/utils/agentsDataMapper.ts:147:69 sonarjs/cyclomatic-complexity {"message":"Function has a complexity of 14 which is greater than 10 authorized.","cost":4,"secondaryLocations":[{"line":147,"column":68,"endLine":147,"endColum
🟡 src/components/ServiceAgents/utils/agentsDataMapper.ts:267:72 sonarjs/cyclomatic-complexity {"message":"Function has a complexity of 12 which is greater than 10 authorized.","cost":2,"secondaryLocations":[{"line":267,"column":71,"endLine":267,"endColum

Fix locally (fast - only checks files changed in this branch):

make ui-checkstyle-changed

@github-actions

Copy link
Copy Markdown
Contributor

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 66%
66.85% (79919/119536) 51.25% (48781/95172) 52.23% (14601/27954)

@sonarqubecloud

Copy link
Copy Markdown

Copilot AI review requested due to automatic review settings August 17, 2026 17:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@gitar-bot

gitar-bot Bot commented Aug 17, 2026

Copy link
Copy Markdown
Code Review ✅ Approved

Normalizes Agent recent runs to render oldest-to-newest chronologically across the UI and updates related tests. No issues found.

Options

Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source

@aniketkatkar97 aniketkatkar97 removed the safe to test Add this label to run secure Github workflows on PRs label Aug 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

safe to test Add this label to run secure Github workflows on PRs UI UI specific issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants