fix(board): stop truncating workflows longer than 12 columns

`normalizeWorkspaceStatuses` capped the list at 12 with a bare `slice(0, 12)`.
Nothing in the UI enforced that cap, so authoring a 13th column appeared to
work and the column silently vanished on the next normalize — no error, no
warning, no way to tell why.

Nothing structural depends on the number. The board already scrolls
horizontally and the sidebar groups by status, so extra columns cost nothing;
the cap was the only thing standing in the way.

This matters for teams mirroring an external workflow. A Jira project with
Open / In development / Code review / Deploy DEV / Ready for QA / In QA /
Deploy STG / Ready for STG / In STG / Deploy PROD / Done / Blocked / Reopened
is already 13 states before counting anything project-specific, so a 1:1 board
was simply not expressible.

Raised to 64 and documented as what it actually is: a corruption guard, so a
damaged payload still cannot explode the board. Same shape as
MAX_PERSISTED_PROJECT_GROUP_DEPTH, which carries the same kind of comment.

Tests cover both directions — a 20-column workflow survives normalize, and a
500-entry payload is still clamped.
This commit is contained in:
Eugenio Valeiras
2026-07-30 23:02:09 -07:00
committed by OrcaWin
parent aa6f945001
commit 8fb76a22c0
2 changed files with 31 additions and 1 deletions
+24
View File
@@ -10,6 +10,30 @@ import {
} from './workspace-statuses'
describe('workspace status visuals', () => {
it('keeps workflows longer than a dozen columns', () => {
// Why: mirroring an external workflow (Jira, Linear) routinely needs more
// than a dozen states. The old cap of 12 dropped the extra ones on the
// next normalize, with no error and nothing in the UI preventing them.
const authored = Array.from({ length: 20 }, (_, index) => ({
id: `state-${index}`,
label: `State ${index}`
}))
const statuses = normalizeWorkspaceStatuses(authored)
expect(statuses).toHaveLength(20)
expect(statuses.at(-1)).toMatchObject({ id: 'state-19', label: 'State 19' })
})
it('still truncates absurd payloads so corrupted data cannot explode the board', () => {
const corrupted = Array.from({ length: 500 }, (_, index) => ({
id: `state-${index}`,
label: `State ${index}`
}))
expect(normalizeWorkspaceStatuses(corrupted).length).toBeLessThanOrEqual(64)
})
it('keeps the default workflow order', () => {
expect(cloneDefaultWorkspaceStatuses().map((status) => status.id)).toEqual([
'todo',
+7 -1
View File
@@ -9,7 +9,13 @@ export { DEFAULT_WORKSPACE_STATUSES } from './workspace-status-defaults'
const WORKSPACE_STATUS_GROUP_PREFIX = 'workspace-status:'
const MAX_STATUS_LABEL_LENGTH = 32
const MAX_WORKSPACE_STATUSES = 12
// Why: corruption guard only — nothing in the board or the sidebar depends on
// this number, and the renderer never enforces it, so a low cap just truncated
// legitimate columns on the next normalize. Teams mirroring an external
// workflow (Jira, Linear) routinely need more than a dozen states; the board
// already scrolls horizontally and groups by status, so the extra columns cost
// nothing structurally.
const MAX_WORKSPACE_STATUSES = 64
type WorkspaceStatusNormalizationOptions = {
migrateDefaultWorkflowStatuses?: boolean
migrateLegacyDefaultStatusVisuals?: boolean