From 6a5fefb2c5558397d37c473eed79ee4455261018 Mon Sep 17 00:00:00 2001 From: Danyang Chen Date: Wed, 28 Oct 2026 20:43:09 +0800 Subject: [PATCH] fix(workflow): list a failed live attempt once in Runs A failed attempt stays live and also has its own history row; runHistoryRows keyed a live row by its run id and a history row by task id plus time, so the two never matched and the Runs tab painted the same attempt twice. Key live rows by both, so the failed attempt shows once while a real retry still shows the earlier attempt and the current one. Fixes #1583 Docs-not-needed: the Runs tab still lists one row per attempt; no documented architecture changes. --- changelog.d/1583-workflow-runs-dedup.fixed.md | 1 + plugins/workflow/client.js | 6 +++- plugins/workflow/test/client.test.js | 30 +++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 changelog.d/1583-workflow-runs-dedup.fixed.md diff --git a/changelog.d/1583-workflow-runs-dedup.fixed.md b/changelog.d/1583-workflow-runs-dedup.fixed.md new file mode 100644 index 000000000..5c8cb0990 --- /dev/null +++ b/changelog.d/1583-workflow-runs-dedup.fixed.md @@ -0,0 +1 @@ +With no run active and the last attempt failed, the workflow **Runs** tab listed that same attempt twice: a phantom row for the live run plus its own history row. It now lists one row per attempt — the failed attempt appears once, marked `Failed` — while a real retry still shows the earlier attempt and the current one ([#1583](https://github.com/ThinkFlowLab/FlyBot/issues/1583)). diff --git a/plugins/workflow/client.js b/plugins/workflow/client.js index 750fa8026..21c1a8f61 100644 --- a/plugins/workflow/client.js +++ b/plugins/workflow/client.js @@ -693,7 +693,11 @@ window.__ModuleLoader__.load({ current: workflow.run?.id === run.id, })) for (const task of queued) rows.push({ task, status: 'queued', current: false }) - const seen = new Set(rows.map(row => row.id ?? `${row.task?.id ?? ''}|${row.at ?? ''}`)) + const seen = new Set() + for (const row of rows) { + if (row.id) seen.add(row.id) + seen.add(`${row.task?.id ?? ''}|${row.at ?? ''}`) + } for (const item of history) { const key = item.id ?? `${item.task?.id ?? ''}|${item.at ?? ''}` if (seen.has(key)) continue diff --git a/plugins/workflow/test/client.test.js b/plugins/workflow/test/client.test.js index 0746bcd4f..3d209dd62 100644 --- a/plugins/workflow/test/client.test.js +++ b/plugins/workflow/test/client.test.js @@ -931,6 +931,36 @@ test('run history does not invent a second running row when only one live run ex assert.equal(rows.filter(row => row.status === 'running').length, 1) }) +test('a failed live attempt is listed once, as failed, not as its own history row (#1583)', () => { + const task = { id: '#1422', title: 'PR 1422' } + const workflow = { + activeCount: 0, + queue: [], + runs: [{ id: 'run-a', live: true, status: 'failed', task, updatedAt: 1_700_000_000_000 }], + run: { id: 'run-a', live: true, status: 'failed', task, updatedAt: 1_700_000_000_000 }, + history: [{ task, status: 'failed', at: 1_700_000_000_000 }], + } + const rows = client.runHistoryRows(workflow) + assert.equal(rows.length, 1) + assert.equal(rows[0].status, 'failed') + assert.equal(rows.filter(row => row.status === 'running').length, 0) +}) + +test('a genuine retry still shows the failed attempt and the current one (#1583)', () => { + const task = { id: '#1422', title: 'PR 1422' } + const workflow = { + activeCount: 1, + queue: [], + runs: [{ id: 'run-b', live: true, status: 'running', task, updatedAt: 1_700_000_100_000 }], + run: { id: 'run-b', live: true, status: 'running', task, updatedAt: 1_700_000_100_000 }, + history: [{ task, status: 'failed', at: 1_700_000_000_000 }], + } + const rows = client.runHistoryRows(workflow) + assert.equal(rows.length, 2) + assert.deepEqual(rows.map(row => row.status), ['running', 'failed']) + assert.deepEqual(rows.map(row => row.current), [true, false]) +}) + test('the runs tab shows one running row per concurrent live run', async () => { const graph = delivery() graph.activeCount = 2