2.7 Work-item type + executor
Promote two pieces of planning metadata from PROSE to STRUCTURE: a work-item type (what KIND of work — code / design / test / content / research / review / decision / deploy / manual / chore) and an executor (WHO does it — coding_agent or human). Today the plan-seed loader stringifies these into each leaf’s description ("Type: code", "Executor: coding_agent"); this story makes them real work_item fields with a picker, a filter facet, and a type→executor default map.
The model (locked — see the module header for the full rationale):
type— a FIXEDWorkItemTypeenum (ten members), DISTINCT fromkind, carried only on executable LEAVES (task / subtask / bug). NULLABLE: every epic/story row + every legacy row staysnull. Fixed (not free text) so Story 7.6’s per-type prompt generator is a TOTAL function over it and the filter facet is a closed set.executor—coding_agent | human, with a type→executor DEFAULT (code/test/deploy→ coding_agent;manual/decision/review→ human;design/content/research/chore→ either, default coding_agent), seeded when a type is first chosen and OVERRIDABLE.- The justified deviation from the Jira mirror (Principle #11). Jira’s issue type IS the kind hierarchy (epic/story/task/sub-task/bug) and routes WHO executes via the assignee field (an AI agent "shows up as an assignee"). Motir splits the axes —
type(what work) +executor(who) — for one concrete use case: the Epic-7 AI dispatch layer routes bytype(prompt template) andexecutor(agent-dispatch vs human-assign). Recorded in 2.7.2.
Scope: the picker design under design/work-items/ (2.7.1); the taxonomy + default-map decision (2.7.2); the schema — enum + nullable column + executor field + migration + the default helper (2.7.3); the picker UI on the create modal + detail rail (2.7.4); the seed-loader mapping that stops emitting type/executor as prose (2.7.5); the Epic-6 FilterAST type facet (2.7.6); vitest (2.7.7); and the create-typed-item + filter-by-type E2E (2.7.8).
Out of scope (named so they land where they belong): the per-type PROMPT generation that keys off type (Story 7.6); the AI dispatch surface that routes by executor (Story 7.6 / 7.7); any change to the kind-parent grammar (type is orthogonal to kind and never affects parenting).
Verification
- Pull the Story branch; run
pnpm prisma migrate dev(clean — the new enum + nullable column + executor field apply with no drift) andpnpm db:seed. - The seed is now structured (the loader bridge). After re-seeding, open a few
PRODleaves: the type + executor are rendered as CHIPS from the structured fields, and the description body NO LONGER contains the "Type:" / "Executor:" prose lines (2.7.5 stopped emitting them). Epics and stories show NO type chip (typeis leaf-only →nullthere). - Create-modal + detail picker (the design-gated UI). Open the issue create modal: the type picker offers exactly the ten taxonomy members with their per-type hue (the
--el-type-*/IssueTypeIcontreatment), and choosing a type SEEDS the executor per the default map (e.g. pickingcodeselectscoding_agent; pickingmanualselectshuman), still OVERRIDABLE. Create acode/coding_agenttask and amanual/humanone; on the detail rail the type chip + executor indicator render and are editable inline. A type override persists; a kind that is epic/story shows no type control. - Filter by type (Epic-6 integration). On /issues, the filter builder now offers
typeas a facet over the shipped 6.1.1 FilterAST; filteringtype = manualreturns exactly the manual leaves, andtype = code OR type = testcomposes; a saved view round-trips thetypepredicate. pnpm test— 2.7.7 covers: the type→executor default helper for every enum member; the schema (enum values,typenullable, leaf-only enforcement); the picker default-seeding + override; the loader mapping (structured fields set, prose absent); and the FilterASTtypepredicate round-trip. New service/repo code respects the per-file coverage gate (motir-core/CLAUDE.md§ coverage).- The mirror deviation is honest. Confirm 2.7.2’s decision records the verified Jira mirror (issue-type = kind; executor routing = assignee, NOT a sub-type) and the concrete Epic-7-dispatch justification — the Principle-#11 paper trail.
- If every step holds, approve and merge the Story PR. If anything fails, comment with what didn’t work and Motir will produce a follow-up Subtask under the same Story.