The enum that fit no one exactly
To Do, In Progress, Test, Pending, Done — a reasonable default, and also not quite anyone's actual process. A QA-heavy team wants a separate "In Review" before "Test." A support team wants "Waiting on Customer." A shop running its own compliance process wants a "Needs Sign-off" step that has nothing to do with any of the built-in five. For years the fix was renaming things in spreadsheets or just tolerating a status column that didn't map to reality. The status set was a fixed Go enum, hardcoded in the frontend — changing it meant changing the product, not configuring it.
What changed: statuses are data now, not code
// TaskStatus is an organization-scoped, user-configurable per-assignee status.
//
// Historically the per-assignee status (user_tasks.status) was a fixed enum
// hardcoded in the frontend: TODO / IN_PROGRESS / TEST / PENDING / DONE. This
// model makes that set dynamic per organization while preserving full backward
// compatibility.
Every organization now owns its own set of TaskStatus rows — add one, delete one, reorder them, recolor them, without touching a line of code. The migration path was designed so nothing already in the database needed to change: the five default statuses keep the exact same keys the old enum used (TODO, IN_PROGRESS, TEST, PENDING, DONE), so every existing task's status is still valid the moment the new system turns on.
The one status that can't be renamed away from meaning "done"
// The terminal "completed" status keeps the reserved key "DONE" forever (only
// its label is editable). Every done-detection path in the backend that
// compares against 'DONE'/'done'/'completed' therefore keeps working.
You can rename the label — call it "Shipped," "Closed," "تحویل شد," whatever matches your team's language — but the underlying key DONE is protected: it can't be deleted, and its IsDone flag can't be unset. That's not an arbitrary restriction; every piece of backend logic that needs to know "is this task actually finished" — reporting, burndown charts, delivery dashboards — checks against that one stable key. Free-form custom statuses without a protected terminal state would break every one of those the first time someone renamed "Done" to something the done-detection code didn't recognize.
Translated by default, not by afterthought
Labels datatypes.JSON `json:"labels"` // {"en":"To Do","fa":"انجام نشده",...}
var SupportedLangs = []string{"en", "fa", "es", "fr", "de", "ar", "zh"}
A status isn't one string — it's a label map, one entry per supported UI language. Adding a custom status means providing a translation for each of the seven languages WKFGo ships in, which is more upfront work than typing one English word, but it's what keeps a bilingual team (or a seven-language one) from seeing a raw untranslated key on half their boards. This mirrors the same discipline the rest of WKFGo's i18n system enforces.
Setting it up
1. Open your organization's status settings. Each status has a key, a position (drag to reorder), a color, and a label per language.
2. Add a status for a real gap in your process — the test is whether it maps to an actual decision point your team makes, not whether it sounds official. "Needs Sign-off" earns its place if someone genuinely has to approve before work continues; a status nobody ever selects is clutter.
3. Leave DONE alone. Rename its label to match your team's vocabulary if you like — leave the key and the "is done" flag as they are.
Common mistakes
Creating a status that duplicates an existing one under a different name. "In Review" and "Pending Review" both existing on the same board just fragments where work actually sits — pick one.
Forgetting a language when adding a custom status. An untranslated label falls back to the raw key or an empty string for that locale's users — exactly the raw-dotted-path problem WKFGo's i18n completeness test guards against elsewhere in the app.
Trying to make a second status "terminal." Only DONE drives done-detection logic; a custom status with a green color and a checkmark icon still isn't "done" to any report that counts completed work — if you need a second completion state, it likely means the underlying workflow needs its own separate lifecycle, not a status flag.
FAQ
Do existing tasks break when we customize statuses?
No — the default five keep their original keys, so historical data and any automation built against TODO/IN_PROGRESS/etc. keeps working unchanged.
Can different projects in the same organization use different statuses? Statuses are organization-scoped, not per-project — the set is shared across all of an organization's projects, keeping cross-project reporting consistent.
What happens to tasks in a status that gets deleted?
Only DONE is protected from deletion; deleting any other status requires migrating the tasks currently in it to a different one first.