2.2.9 Restore default workflow — re-add missing default statuses + transitions
Estimate: 16m · Depends on: 2.2.5
After an admin edits a workflow (deletes statuses, removes transitions, renames things), they need a way to get the defaults back without recreating each one by hand. Add a "Restore defaults" capability that re-adds any missing default statuses and transitions from 2.2.2's DEFAULT_STATUSES / DEFAULT_TRANSITIONS.
Durable shape — ADDITIVE merge, never destructive. Restoring defaults re-adds the six default statuses (matched by key) that are missing and the default transitions whose endpoints now exist — but it NEVER deletes a custom status the admin added, never removes their custom transitions, and never overwrites a renamed default's label/color. It is idempotent: on a pristine default-seeded project it's a no-op; running it twice changes nothing the second time. A separate destructive "reset to factory" (wipe + reseed) is explicitly OUT of scope here — flagged as a possible follow-up if the user wants it, but additive-restore is the safe default.
workflowsService.restoreDefaultWorkflow({ userId, workspaceId, projectId }) — admin-gated (same assertProjectAdmin as 2.2.5). In one transaction: read the project's current statuses + transitions; for each default status whose key is absent, insert it (appended position via the fractional-index helper, category/label from the default; isInitial only if the project currently has NO initial status — otherwise leave the existing initial untouched); then for each default transition pair whose both endpoints now exist, insert it if absent (idempotent on the 2.2.1 unique constraint).
UI: a "Restore defaults" button in the workflow-settings header (admin-only), behind a confirm modal that states it re-adds the standard statuses & transitions and does NOT remove customizations. Optimistic + toast, same pattern as 2.2.5.
Acceptance criteria
workflowsService.restoreDefaultWorkflowre-adds missing default statuses (by key) + missing default transitions; idempotent (a second call is a no-op); never deletes or duplicates rows; admin-gated (NotProjectAdminErrorfor a non-owner).- Initial-status rule: if the project has no initial status, restore makes
todoinitial; if it already has one (even a custom status), the existing initial is left untouched (the partial-unique index from 2.2.1 is never violated). - A custom status the admin added survives the restore unchanged; a renamed default (e.g. "In Review" → "QA") is matched by key and NOT reverted.
- UI: admin-only "Restore defaults" button + confirm modal + success toast + revalidate; non-admins don't see it and a direct Server-Action call is rejected server-side.
- Vitest (real Postgres): delete
in_review+cancelledand a transition, add a customon_holdstatus, run restore → the two defaults + their transitions return,on_holdstays, no dupes; a second restore call is a no-op. - Quality gates green (tsc + eslint + prettier + vitest).
Context refs
lib/workflows/defaultWorkflow.ts(2.2.2) —DEFAULT_STATUSES/DEFAULT_TRANSITIONSsource of truthlib/services/workflowsService.ts(2.2.2/2.2.5) —seedDefaultWorkflow/createStatus/assertProjectAdmin/ the repo write methods to reuseapp/(authed)/settings/project/workflow/(2.2.5) — the settings UI + Server Actions to extendlib/workItems/positioning.ts—keyForAppendfor the re-added statuses' positions