Carry hasDescription on the work-item LIST row, so the /items row ⋯ menu can pick a leaf's Plan / Re-plan face by rule 2
The data half of MOTIR-2097's rule 2 — "a LEAF with a description shows Re-plan, not Plan" (Yue, 2026-08-04) — for the ONE surface that cannot satisfy it today.
Why it is its own card
MOTIR-2097 lands the whole rule from one shared function, and two of the three surfaces feed it everything it needs: the detail page holds item.descriptionMd, and the quick-view peek already carries descriptionMd on QuickViewData. The /items row ⋯ menu does not:
IssueRowData(app/(authed)/items/_components/issueRows.ts) carrieskind,hasChildrenandstatusCategory— enough for rules 1 and 3, which is exactly what MOTIR-2097 ships there — but no description signal.WorkItemListItemDto(lib/dto/workItems.ts) has nodescriptionMd.- The tree read's forest CTE projects a fixed column set that does NOT include
descriptionMd— stated inworkItemRepository.ts's own docblock (the same constraint that already forces the filter AST to joinwork_itemback onto the CTE for itsmatchedflag). So this is a repository-level change, not a mapper tweak.
That makes it a different SIZE and a different RISK from MOTIR-2097's pure-UI predicate work, so it ships as its own PR rather than inflating that one.
What to build
Carry a boolean hasDescription — NOT the description body — from the list/forest read through to IssueRowData:
- Project it in the forest CTE (a
descriptionMd IS NOT NULL AND descriptionMd <> ''expression, so the CTE's column set grows by one BOOLEAN and never by the body text — the tree read fans out over many rows and the body is large). - Add it to
WorkItemListItemDtoand the flat-list read on the same terms, so both list shapes agree. - Thread it through the row shapers in
issueRows.tsontoIssueRowData. - Feed it to the shared face function in
WorkItemRowActions, replacing the named degrade MOTIR-2097 leaves behind (that card is required to comment the degrade rather than hide it — delete the comment as you satisfy it).
Acceptance criteria
- A LEAF row (task / bug / subtask) with a description offers Re-plan in the ⋯ menu; the same row without one offers Plan / Expand. Rules 1 and 3 keep behaving exactly as MOTIR-2097 shipped them.
- The face is decided by the SAME shared function MOTIR-2097 extracted — this card adds an input, it does not add a second copy of the rule.
- An empty-string description counts as NO description (it is what an emptied editor writes).
- The tree/list reads do not select the description BODY.
Known ripples (from the auto-memory corpus)
Adding a field to a shared list DTO breaks exact-shape .toEqual assertions in route and mapper tests — run the WHOLE affected test directories, not the single file you edited, and expect fixtures across tests/work-items, tests/api and the tree/list component tests to need the new key.
Blocked by: MOTIR-2097 — it creates the shared function this one feeds.