# todo.item The shape of one to-do item, shared by every `todo.*` capability, and the two checks that say whether one is acceptable. `Todo` is what is stored; `TodoDraft` is the part a person edits (the add and edit forms, and what todo.parse-quick-add produces); `Recurrence` says how an item repeats. `validateDraft` is for forms: it returns `valid` and one message per field that needs fixing, keyed by the field's name, so a form can show each message under its input and an API can return them as a validation error. It never throws. `validateTodo` is for stored items (read back from storage, or imported): the same rules plus the ones only a stored item has. ## The rules - **title**: judged after trimming, 1 to 200 characters, one line (no control characters). In a stored todo it must already be trimmed. - **notes**: optional, up to 2000 characters. - **priority**: `none`, `low`, `medium` or `high`. - **due**: optional, a real calendar date `YYYY-MM-DD`. A due date has no time of day: a to-do is due on a day. - **tags**: at most 10, each in the normal form todo.normalise-tags produces (lower-case ASCII letters and digits, runs joined by single hyphens), at most 30 characters, no duplicates. The first problem is reported. - **recurrence**: optional; frequency `daily`, `weekdays`, `weekly`, `monthly` or `yearly`, interval 1 to 999 (weekdays only 1), a real `anchor` date, and it needs a due date no earlier than the anchor. - **id** (stored): not blank. Ids are the caller's: pass one in. - **createdAt**, **completedAt** (stored): UTC timestamps `YYYY-MM-DDTHH:MM:SS[.fraction]Z`, as JavaScript's `toISOString` writes them. Offsets are refused so that timestamps compare as strings. `completedAt` is set exactly when `done` is true. - **order** (stored): the manual position, a whole number 0 or more. ## Decisions "Characters" are Unicode code points, which all three languages count the same way: 200 emoji is 200 characters, not 400 UTF-16 units. Trimming removes ASCII whitespace only (space, tab, CR, LF, VT, FF). The three standard libraries trim different sets of Unicode spaces, so a wider trim would store different titles in different services. The anchor is why month ends survive: a monthly todo anchored on 31 January is due 28 February, then 31 March, because every occurrence is counted from the anchor (todo.next-occurrence). Without it, the 28 February copy would drift to the 28th for ever. Messages are full sentences written for the person at the form, and the same in every language.