2.4.4 Inline controls: workflow-aware status + assignee
Estimate: 14m · Depends on: 2.4.1, 2.3.6
The two write affordances the scope names — and the ONLY ones on the detail page (everything else is the Edit form). Both REUSE 2.3.6's components + gated actions; this Subtask wires them inline, it does not build new mutation primitives.
Status control (workflow-aware). The header status pill becomes a StatusPicker (2.3.6) that offers only the LEGAL next statuses for the current one — the project's transitions in restricted mode, any status in open mode (2.2's canTransition / the workflow context already in the getIssueDetail bundle). Selecting one calls 2.2.4's gated updateStatus via a detail-page Server Action (changeStatusAction — reuse 2.3.6's if its signature fits, else a thin sibling); an illegal/stale transition surfaces the typed error inline; success toasts + revalidatePaths the detail route so the pill + the activity trail reconcile.
Assignee control. The sidebar assignee field becomes an AssigneePicker (2.3.6, a workspace-member combobox) that reassigns via the now-status-free updateWorkItem (2.3.6's finding-#46 cleanup) through an updateAssigneeAction — assignee is a non-status patch field, so it rides the ordinary patch path (which still writes a revision). "Unassign" is supported (null assignee). Optimistic + toast + revalidate. Permission: any project member can change status/assignee in v1 (no per-field RBAC until Epic 6) — but the Server Actions still carry the explicit workspaceId/membership gate (finding #26).
Acceptance criteria
- Status control offers exactly the legal next statuses (restricted: transition rows; open: all); choosing one persists via 2.2.4's
updateStatus; an illegal/stale move surfaces the typed error inline and does not change the pill. - Assignee control reassigns (and unassigns) via the status-free
updateWorkItem; a revision row is written for the change. - Both controls are Server-Action-gated (workspace + membership); a forged cross-workspace target 404s; no client-trusted ids.
- Both reuse 2.3.6's
StatusPicker/AssigneePicker— no parallel components; the detail page and edit form share them. - Vitest (real Postgres) over the two actions: legal status change, illegal rejection, reassign + unassign, cross-workspace 404. Component test: the status control lists only legal targets. shell-a11y stays green with the controls rendered.
Context refs
- 2.3.6's
StatusPicker/AssigneePicker+changeStatusAction/ the status-freeupdateWorkItem(finding #46) — reuse, don't rebuild workItemsService.updateStatus(2.2.4) +workflowsService.canTransition(2.2.3) — the gated paths- 2.4.1's
IssueDetailDtoworkflow context (statuses + legal transitions) — the picker's option source motir-core/CLAUDE.md— Server Action / 4-layer / workspaceId-gate rules