diff --git a/.changeset/lane-listview-sort-traits.md b/.changeset/lane-listview-sort-traits.md
new file mode 100644
index 0000000000..758ccb1489
--- /dev/null
+++ b/.changeset/lane-listview-sort-traits.md
@@ -0,0 +1,7 @@
+---
+"@runfusion/fusion": patch
+---
+
+summary: Board lanes and the list view now sort by each column's role, so renamed boards keep their card order.
+category: fix
+dev: `Lane` and `ListView` pass the resolved `isArchivedColumn`/`isHoldColumn`/`isCompleteColumn`/`isReviewColumn` traits to `sortTasksForDisplayColumn`, mirroring Board.tsx; previously they used the helper's legacy-id defaults.
diff --git a/packages/dashboard/app/components/Lane.tsx b/packages/dashboard/app/components/Lane.tsx
index 4615751b3b..6882b5685e 100644
--- a/packages/dashboard/app/components/Lane.tsx
+++ b/packages/dashboard/app/components/Lane.tsx
@@ -104,7 +104,27 @@ function LaneComponent(props: LaneProps) {
(grouped[task.column] ??= []).push(task);
}
for (const col of workflow.columns) {
- grouped[col.id] = sortTasksForDisplayColumn(grouped[col.id] ?? [], task_legacyKey(col.id));
+ /*
+ FNXC:WorkflowResolvedColumns 2026-07-31-08:20 (the two callers taskSorting.ts names as unconverted):
+ `sortTasksForDisplayColumn` defaults its four role questions to the LEGACY ids, and its own header
+ says the callers that do not resolve flags — Lane and ListView — keep today's behaviour. Today's
+ behaviour on a renamed board is: the hold lane loses its priority-then-FIFO queue order, the
+ complete lane loses completion-date sorting, and merging cards stop floating to the top of review.
+ Nothing fails; the cards are just in the wrong order.
+
+ Board.tsx already resolves exactly this from `column.flags`; mirrored here rather than answered a
+ second way. `doneSortMode` stays defaulted because this lane has no operator setting for it.
+ */
+ const isDoneLikeColumn = col.flags.complete === true && col.flags.archived !== true;
+ grouped[col.id] = sortTasksForDisplayColumn(
+ grouped[col.id] ?? [],
+ task_legacyKey(col.id),
+ undefined,
+ col.flags.archived === true,
+ col.flags.hold === true,
+ isDoneLikeColumn,
+ col.flags.mergeBlocker === true || col.flags.humanReview === true,
+ );
}
return grouped;
}, [tasks, workflow.columns]);
diff --git a/packages/dashboard/app/components/ListView.tsx b/packages/dashboard/app/components/ListView.tsx
index 216df7636e..6f0e822100 100644
--- a/packages/dashboard/app/components/ListView.tsx
+++ b/packages/dashboard/app/components/ListView.tsx
@@ -1166,7 +1166,18 @@ export function ListView({
for (const column of listColumns) {
const columnId = column.id;
if (!sortField) {
- groups[columnId] = sortTasksForDisplayColumn(groups[columnId], columnId as Column);
+ /* FNXC:WorkflowResolvedColumns 2026-07-31-08:22: same conversion as Lane.tsx — the sort's four
+ role questions default to legacy ids, so a renamed board sorted every lane generically. */
+ const isDoneLikeColumn = column.flags.complete === true && column.flags.archived !== true;
+ groups[columnId] = sortTasksForDisplayColumn(
+ groups[columnId],
+ columnId as Column,
+ undefined,
+ column.flags.archived === true,
+ column.flags.hold === true,
+ isDoneLikeColumn,
+ column.flags.mergeBlocker === true || column.flags.humanReview === true,
+ );
continue;
}
diff --git a/packages/dashboard/app/components/__tests__/Lane.test.tsx b/packages/dashboard/app/components/__tests__/Lane.test.tsx
index 41d21e9d06..5f7e6d0296 100644
--- a/packages/dashboard/app/components/__tests__/Lane.test.tsx
+++ b/packages/dashboard/app/components/__tests__/Lane.test.tsx
@@ -206,3 +206,69 @@ describe("Lane", () => {
expect(screen.queryByTestId("column-inline-feedback")).toBeNull();
});
});
+
+/*
+FNXC:WorkflowResolvedColumns 2026-07-31-08:30:
+Lane sorted every column with `sortTasksForDisplayColumn`'s LEGACY defaults, which taskSorting.ts's
+own header calls out: the callers that do not resolve flags keep today's behaviour. Today's behaviour
+on a renamed board is the wrong ORDER — the hold lane loses priority-then-FIFO, so a card an operator
+marked urgent no longer shows up next. Nothing throws; the queue is just wrong.
+
+The board below is the built-in vocabulary renamed and nothing else, which is why every case above
+still passes: `todo` satisfies the legacy default.
+*/
+const RENAMED_WORKFLOW: BoardWorkflowDefinition = {
+ id: "custom:renamed",
+ name: "Renamed",
+ columns: [
+ { id: "drafting", name: "Drafting", flags: { hold: true } },
+ { id: "building", name: "Building", flags: { countsTowardWip: true } },
+ { id: "checking", name: "Checking", flags: { humanReview: true } },
+ { id: "shipped", name: "Shipped", flags: { complete: true } },
+ ],
+};
+
+describe("Lane on a RENAMED board", () => {
+ /* Each case picks inputs where the ROLE order and the generic fallback order DISAGREE. My first
+ draft asserted urgent-first, which the generic sort also produces — it passed with the fix
+ reverted and proved nothing. */
+ function renderedIds() {
+ return screen.getAllByTestId(/^task-FN-/).map((node) => node.getAttribute("data-id"));
+ }
+
+ it("orders the hold lane by created-at, not task id", () => {
+ /* Equal priority: hold order is FIFO by createdAt, the generic fallback is task-id ascending. */
+ const tasks = [
+ mkTask({ id: "FN-1", column: "drafting", priority: "normal", createdAt: "2024-03-01T00:00:00.000Z" }),
+ mkTask({ id: "FN-9", column: "drafting", priority: "normal", createdAt: "2024-01-01T00:00:00.000Z" }),
+ ];
+
+ render();
+
+ expect(renderedIds()).toEqual(["FN-9", "FN-1"]);
+ });
+
+ it("orders the complete lane newest-first by completion time", () => {
+ /* Complete sorts by columnMovedAt DESC; the generic fallback would put FN-1 first on id. */
+ const tasks = [
+ mkTask({ id: "FN-1", column: "shipped", priority: "normal", columnMovedAt: "2024-01-01T00:00:00.000Z" }),
+ mkTask({ id: "FN-9", column: "shipped", priority: "normal", columnMovedAt: "2024-03-01T00:00:00.000Z" }),
+ ];
+
+ render();
+
+ expect(renderedIds()).toEqual(["FN-9", "FN-1"]);
+ });
+
+ it("floats a merging card to the top of the review lane", () => {
+ /* The "what is merging right now" ordering; the generic fallback ignores status entirely. */
+ const tasks = [
+ mkTask({ id: "FN-1", column: "checking", priority: "normal" }),
+ mkTask({ id: "FN-9", column: "checking", priority: "normal", status: "merging" }),
+ ];
+
+ render();
+
+ expect(renderedIds()).toEqual(["FN-9", "FN-1"]);
+ });
+});
diff --git a/scripts/lib/lane-wiring-baseline.json b/scripts/lib/lane-wiring-baseline.json
index 1e631f0633..9f071d7593 100644
--- a/scripts/lib/lane-wiring-baseline.json
+++ b/scripts/lib/lane-wiring-baseline.json
@@ -8,8 +8,6 @@
"packages/engine/src/project-engine.ts": 1,
"packages/engine/src/runtimes/in-process-runtime.ts": 1,
"packages/dashboard/src/routes/register-task-workflow-routes.ts": 1,
- "packages/dashboard/app/components/Lane.tsx": 1,
- "packages/dashboard/app/components/ListView.tsx": 1,
"packages/dashboard/app/hooks/useBlockerFanout.ts": 1,
"packages/cli/src/commands/dashboard-tui/app.tsx": 1,
"packages/cli/src/commands/dashboard-tui/bucket-mapping.ts": 1,