Skip to content

fix(task-board): name lanes after the board's columns, not Studio's - #6771

Merged
viktormarinho merged 1 commit into
mainfrom
fix/board-lane-naming
Aug 31, 2026
Merged

fix(task-board): name lanes after the board's columns, not Studio's#6771
viktormarinho merged 1 commit into
mainfrom
fix/board-lane-naming

Conversation

@viktormarinho

@viktormarinho viktormarinho commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Follow-up to the mirrored-board series. On a board whose columns come from a
tracker, three surfaces were still speaking Studio's vocabulary. Each one
misled differently.

A stranded card was masquerading as a column

The board draws a lane for any status no column accounts for (#6727), so a card
can't go invisible. But that lane was labelled with our translation — a
triage card on a mirrored board rendered as "Backlog", sitting next to the
tracker's own Backlog column. Two identical-looking columns, one of which does
not exist over there and nobody can act on.

laneHeader(status, t, columns) now answers naming and appearance together,
because they're the same question: a column the board owns is drawn with its
own name and, if it's one of ours, our icon. A status off the board gets
neither — it's labelled by its raw key and carries an off board badge with a
tooltip saying why it's on screen. The "new task here" button is gone for it
too: a card created in a non-column would be stranded the moment it existed.

laneLabel is now module-private. Naming a lane requires knowing whether the
board even has a column for it, and every caller that skipped that question
is the bug above.

You couldn't drag a card into an empty column

A lane's droppable was validated against Studio's STATUSES, which on a
mirrored board matches no column at all — so dropping onto an empty column
resolved to null and the card sprang back. Only dropping onto an existing card
worked, which made it look like an intermittent bug rather than a rule.

Extracted as dropLane, validated against the board's columns, with the two
ways of being over a lane (its droppable, or a card in it) gated by one rule.
That closes the mirror-image hole too: hovering a card in an off-board lane
used to resolve to that lane, so the drop appeared to work until the list
refetched and the server's rejection showed.

"Move to" was a list of ways to fail

The bulk menu and the task's status dropdown offered the canonical lanes
whoever's board it was. On a mirrored board every entry named a column the
server rejects. moveTargets takes the board's columns now; useBoardColumns
reads them off the same cached list without opening the board's SSE streams.

STATUSES is deleted — it had become a second copy of CANONICAL_COLUMN_KEYS
with no reader but a test.

Verification

Against a local board with five mirrored columns and one Studio-native card
stranded off it:

  • Lanes render the tracker's names; the stranded card's lane reads its raw
    status with the off board badge and no new-task button.
  • The status dropdown lists exactly the five columns — no canonical lanes.
  • A card dragged into a previously empty mirrored column landed and
    persisted (verified in Postgres). That run predates the dropLane
    extraction; the extraction itself is covered by unit tests, and the wiring
    is four lines.

Unit tests cover dropLane (4), laneHeader (3), moveTargets on a mirrored
board, and laneVisibility's new unplaced. bun run check, lint, fmt,
and knip are clean.

Still open

  • A renamed tracker column still reads as delete + create — the Agile API gives
    no stable column id, so the key is the name.
  • Bulk/import writers still leave board_column_org null.
  • in_review roles are settable but read by nothing yet.

Summary by cubic

Fixes the task board so lanes, drag targets, and the move menu follow the board's own columns instead of Studio's canonical statuses. On boards mirrored from a tracker, canonical names matched no real column, so stranded cards looked like phantom columns, drops into empty columns failed, and "Move to" listed statuses the server rejects.

Bug Fixes

  • A lane with no matching column now shows its raw status key with an "off board" badge and no "new task here" button.
  • Drops are validated against the board's columns, so mirrored empty columns accept cards and off-board lanes refuse them.
  • "Move to" and the status dropdown now list only the board's own columns.

Refactors

  • laneHeader replaces the public laneLabel, which is now module-private; naming a lane requires knowing whether the board has a column for it.
  • dropLane handles both ways of being over a lane — its droppable or a card in it — with one rule.
  • STATUSES is deleted; moveTargets and useBoardColumns read columns from the same cached query as the board.

Written for commit 582599a. Summary will update on new commits.

Review in cubic

A board mirrored from a tracker was still being read through Studio's own
vocabulary in three places, and each one misled in its own way:

- A status no column accounts for got OUR translation. A stranded `triage`
  card rendered as "Backlog" beside the tracker's real Backlog — a second
  column, identical in appearance, that nobody could act on because it does
  not exist over there. `laneHeader` gives an off-board lane neither our name
  nor our icon: it is labelled by its raw status and carries an "off board"
  badge that says why it is on screen.

- Dropping onto a lane's own droppable was validated against Studio's
  `STATUSES`, which on a mirrored board matches no column at all — so dragging
  a card into an empty Jira column resolved to null and the card sprang back.
  Validated against the board's columns now. Off-board lanes are deliberately
  not drop targets: a card can leave one, never be filed into one, and the
  "new task here" button is gone for the same reason.

- "Move to" and the status dropdown offered the canonical lanes whoever's
  board it was, so on a mirrored board every entry named a column the server
  rejects. `moveTargets` takes the board's columns.

`laneLabel` is now module-private. Naming a lane requires knowing whether the
board even HAS a column for it, and every caller that skipped that question is
the bug above; `laneHeader` is the way in. `STATUSES` goes with it — it had
become a second copy of `CANONICAL_COLUMN_KEYS` with no reader but a test.
@viktormarinho
viktormarinho merged commit 3986931 into main Aug 31, 2026
34 checks passed
@viktormarinho
viktormarinho deleted the fix/board-lane-naming branch August 31, 2026 14:55
decocms Bot pushed a commit that referenced this pull request Aug 31, 2026
PR: #6771 fix(task-board): name lanes after the board's columns, not Studio's
Bump type: patch

- decocms (apps/api/package.json): 4.305.2 -> 4.305.3
- @decocms/native (apps/native/package.json): 4.305.2 -> 4.305.3

Deploy-Scope: web
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.

1 participant