ADR-0003: Priority Number Mapping¶
Status¶
Accepted
Context¶
The Todoist API represents priority with inverted numbers compared to user expectations:
- API:
4= urgent,3= high,2= normal,1= low - User mental model (from Todoist's own UI labels):
p1= urgent,p2= high,p3= normal,p4= low
Mixing these two number spaces causes silent bugs. An agent or user
who assumes p1 means API value 1 creates a low-priority task when
they meant urgent. The bug does not fail any test: the task saves
successfully with the wrong priority, and the user only notices when
the task doesn't appear at the top of their list.
Two options:
- Match the API. Accept API numbers everywhere, rely on documentation and memory. Rejected because "my p1 task is showing as low priority" is the predictable failure mode, and it happens silently.
- Match the user. Convert at every boundary between display and API, so the rest of the codebase speaks one language.
Decision¶
Use the formula display = 5 - api_priority (equivalently,
api = 5 - display) at every boundary between the display layer and
the API layer. The formula is symmetric: applying it twice returns
the original value.
- User input. Always parses
p1throughp4as display numbers and converts to API numbers before the HTTP call. - User output. Always converts API numbers back to display numbers before rendering in any mode (Rich, Plain, or JSON).
- Internal storage. Logs, caches, and schema all standardize on display numbers. The API number exists only in the HTTP request/response body.
Consequences¶
Positive. Users and agents never see API numbers. The whole
codebase speaks one priority language. Every translation point is
localized to the API boundary (todoist-api-python call sites).
Negative. Every priority reference must go through the formula.
Missing a conversion is a latent bug that passes silently. Mitigation:
tests assert both directions at API boundary points, and the formula
is called out in CLAUDE.md as a known gotcha so anyone touching
priority code sees the rule.
Non-obvious. When writing a new command that handles priority,
the rule is never accept, display, or store raw API priority
numbers outside the translation layer. If you find yourself typing
priority=4 anywhere in core/ or cli/, that's almost certainly
a bug.