Type · planning mistake (RECORD card — the correction is already applied) · Parent · MOTIR-1465, the planner-bug home · Discovered in · filing MOTIR-3170, 2026-08-19 · Repo · motir-meta (the deliverable is a notes.html entry; no product code)
MOTIR-3170: the planning / roadmap / plan-detail canvas draws a card at implemented or planning as To Do. Three files each keep a private six-member copy of the workflow status set and coerce anything outside it to todo.
Two cards were supposed to make a new status visible everywhere, and both enumerated their sweep correctly and incompletely:
implemented to STATUS_KEY_EL — the DOT map.STATUS_TONE consumers, and the enumeration is correct.The canvas is not one of the seven, because it does not consume STATUS_TONE. It has STATUS_META — a third, independent status vocabulary — and no card has ever counted it. A sweep enumerated over the callers of the symbol you are changing is structurally blind to a surface that re-implements it.
notes.html #29 already states the Totality rule for lookup layers:
Any metadata map, switch, validator, or renderer keyed off an enum/discriminant must be total over every value that enum can actually hold in the database.
That is exactly this defect's class, it is always-injected, and it did not fire — the order-beats-density shape (the third promote test: has this check already been written as a rule and failed since?). The reason it did not fire is worth stating precisely, because it is the new limb:
#29 presumes a CLOSED enum. STATUS_META IS declared Record<WorkItemStatus, StatusMeta> and IS total — over WorkItemStatus, a six-member union hand-authored in the same file. TypeScript reports nothing, a reviewer asking "is the map total?" gets yes, and the answer is worthless, because the union is the subset. The real discriminant is workflow_status, a TABLE: a project defines its own statuses, RoadmapNodeDto.status is typed string for exactly that reason, and defaultWorkflow alone has eight rows against the union's six.
So the check #29 asks — "is the map total over the enum?" — is unanswerable by inspection at the map. It has to be asked one level up: what defines this discriminant's membership, and can the compiler see it? When the answer is "a database row set", a closed Record is not totality; it is a subset with a totality certificate.
The second half is the degrade. toStatus does not fall through to a neutral render — it returns 'todo', the name of a real, specific state the card is definitively not in. A partial map that throws or blanks announces itself; one that resolves to a plausible sibling value never will. An open discriminant needs a fallback that carries the real value through, not one that picks a default member.
motir-core, 5 pts / 100 min — with the fix scoped as the general rule (carry statusLabel + statusCategory on the roadmap DTO; resolve key → category → neutral; delete all three literals) rather than as two more map rows. Its own repeat-defect section records the five occurrences.notes.html #334 — the totality certificate over a hand-authored union (motir-meta PR, branch docs/canvas-status-literal-lesson). #333 was taken by a sibling session's PR #270 while this was being written; the number here is the next free one and the count bump must be reconciled against whichever lands first.This card ships the LESSON only. Whether the limb belongs in the RULES tier is this record's close-out deliverable, and the evidence is assembled so it can be settled in one read rather than re-derived:
implemented (MOTIR-3003), planning (MOTIR-2425) and every custom status all fail the identical predicate in the identical three files. Not one incident in four costumes.Record<Union, …> is not totality — resolve by key with a fallback that preserves the value's own identity. Rule register, not narrative.If the close-out answers SHARPEN it is TWO cards, not one: a motir-meta W card and its (motir-ai) Mirror W<n> into SHARED_PLANNING_RULES sibling, blocked_by it, both under MOTIR-1464. Checked 2026-08-19 so the close-out does not have to — the open W series is W6 (MOTIR-2929 / MOTIR-2930), W7 + W7b (MOTIR-2953 / MOTIR-2954), W8 (MOTIR-2962 meta, MOTIR-2963 mirror), W9 (MOTIR-2987 meta, MOTIR-2989 mirror) and MOTIR-3072, and none of them touches the totality / lookup-layer region. So this would be a NEW W number, not an amendment to an existing one; read MOTIR-1464's child list for the next free one before filing. (Re-verify the pairing by reading MOTIR-1464's children — the keys above come from a SHARED_PLANNING_RULES text search, which finds the mirror halves reliably and the meta halves only when they name the constant.)
git show origin/main:notes.html | grep -c 'mistake-num">334<' returns 1, and the entry it opens is this lesson — matched by CONTENT (the phrase totality certificate), not by trusting the numeral.Record over a hand-authored union proves nothing when the discriminant's membership is defined by a table, and that the honest fallback preserves the value rather than picking a default member.Across the N mistakes count in notes.html matches the highest mistake-num present after the merge — reconciled against PR #270's bump rather than clobbering it.blocked_by-wired before it closes.git diff --name-only origin/main in motir-meta shows notes.html and nothing else.notes.html #29 — Totality rule for lookup layers, the rule this incident falsifies at its edgemotir-core/components/planning/WorkItemNode.tsx:59-98 — WorkItemStatus + STATUS_META, the closed union with the totality certificatemotir-core/lib/dto/workItems.ts:644 — RoadmapNodeDto.status: string, the open discriminant on the wiremotir-core/lib/workflows/defaultWorkflow.ts — eight statuses against the union's sixOpen — lesson authored as notes.html #334; the promote question is unanswered.