AFTER — #1527 (Move Forward + Change Status) and #1528 (Assign + assignee) designed together.
Both land in the same hero/action region of `TaskDetailPane.tsx`, so they get one design-system section (§IR) and one mount point: a `StaffActionBar` band directly under the hero card. The hero action row itself is untouched — that is how state 1 stays byte-for-byte today's pane.
No new colour: everything below is §2/§3 tokens already in the system.

## 1Ordinary client — `internal_role: null`

Nothing is added. No staff bar, no Move Forward, no Change Status, no Assign, and no assignee name anywhere — the payload does not carry `assigned_to_name` at all for this viewer, so the name is absent from the HTTP response, not merely hidden in the DOM (spec decision 7). Identical to the BEFORE capture.

#1527 Spec✦ Feature

# Ticket panel: Move Forward and a role-correct Change Status control

RARequested by Redmine Admin Updated 2 hours ago ✳ ✎ Edit ⏸ Pause
GET …/tasks/1527 → { status_id: 8, allowed_statuses: [] } no can_move_forward · no can_assign · no assigned_to_name key

### At a glance

Reference
  #1527
Stage
  Spec
Type
  Feature

## 2Developer, assigned to this ticket

One control, not two. The server sends `allowed_statuses` already narrowed to what this viewer may set — for a developer that is exactly `[next_status]`, so length < 2 ⇒ Change Status is not rendered and Move Forward is the only door. A one-option dropdown would be a menu offering a choice that does not exist, three clicks for what the button does in one. The label names the target stage, from `next_status_id` + `next_status_name`.

#1527 Spec✦ Feature

# Ticket panel: Move Forward and a role-correct Change Status control

RARequested by Redmine Admin ✳ ✎ Edit ⏸ Pause
⚙ Staff controls
→ Move to Design You are assigned to this ticket.
can_move_forward: true · next_status_id: 15 ("Design") allowed_statuses: [ {id:15, name:"Design"} ] → 1 option ⇒ no dropdown

### At a glance

Reference
  #1527
Stage
  Spec
Assigned to
  GWGuillaume W.

## 3Developer, not assigned — disabled + tooltip

The button is present and keyboard-focusable: `aria-disabled="true"` with the click handler short-circuited, never a bare `disabled` (which drops it out of the tab order, so the explanation becomes unreachable exactly for the people who need it most). The tooltip is the `aria-describedby` target, shown on hover and on `:focus-visible`. Below: the same button focused by keyboard, with the second reason shape — nothing left to advance to.

#1531 In development

# Backend: the progression rule and the move-forward route

RARequested by Redmine Admin ✳⏸ Pause
⚙ Staff controls
Only the developer assigned to this ticket can move it forward. → Move to QA
⚙ Staff controls — variant: no next stage (ticket is Shipped)
This ticket is at the end of the pipeline. → Move forward focus-visible ring — the tooltip opens on keyboard focus too
can_move_forward: false, next_status_id: 10 → “not assigned to you” can_move_forward: false, next_status_id: null → “end of the pipeline” allowed_statuses: [] in both cases ⇒ no Change Status control

### At a glance

Stage
  In development
Assigned to
  ALAlice L.

## 4CTO / platform admin — full set, backward included, plus Assign

Three controls on one row, in decreasing frequency of use: Move Forward (primary — the one verb used twenty times a day) → Change Status (the correction path, forward and backward) → Assign. The Change Status menu lists `allowed_statuses` verbatim: the frontend holds no status order, so it cannot decide what “forward” means and does not try. Backlog is absent because the server never puts it there.

#1527 Spec✦ Feature

# Ticket panel: Move Forward and a role-correct Change Status control

RARequested by Redmine Admin ✳✎ Edit⏸ Pause
⚙ Staff controls
→ Move to Design Spec Change status▾
GWAssigned to Guillaume W.▾

Assign menu open — `GET /projects/9/developers`. “Unassigned” is an explicit first option, separated by a hairline from the people.

Change Status menu (same anatomy as the existing `TransitionControl`, §TB.6): each target is a `TaskStatusPill`, selecting one opens the inline confirm step before the mutation fires. Backward targets are listed in the server's order, unmarked — a “backward” badge would need a status order in `src/`.
can_move_forward: true · can_assign: true · next_status_id: 15 allowed_statuses: 7, 15, 9, 10, 11, 12, 13, 14 → ≥ 2 options ⇒ dropdown renders

### At a glance

Reference
  #1527
Stage
  Spec
Assigned to
  GWGuillaume W.

## 5Backlog ticket, viewed by a CTO — the credit gate holds

No status control renders for anyone, including CTO and admin. Not a disabled Move Forward with an explanation — absent. Submit stays the only way out of the backlog, and the pane must not suggest a second door exists. The Assign control survives: assigning changes no status and costs no credit, so a CTO can line the work up before the client pays.

#1528 Backlog✦ Feature

# Ticket panel: Assign to Developer and assignee visibility

Complexity 4/10 ➤ Submit to the team ✳ ✎ Edit ⏸ Pause
⚙ Staff controls
—Unassigned▾ No developer is configured for this project.

Empty-state variant of the Assign control: `GET …/developers` legitimately returned `[]`. The trigger is `aria-disabled` (focusable, described by the note), never an empty menu that opens onto nothing.

Filing is free — nothing starts until you submit this ticket.

status_id: 16 → Move Forward and Change Status are not rendered can_move_forward: false · next_status_id: null · allowed_statuses: [] can_assign: true — assignment is not a status change

### At a glance

Stage
  Backlog
Assigned to
  Unassigned

## 6Server refusals — inline, never blanking the pane

Every error is a `role="alert"` paragraph inside the staff bar, under the control that produced it. The ticket, its description and its criteria stay on screen; the status pill never showed an unconfirmed stage, because the controls are pessimistic by design — there is no optimistic state to roll back. Each failure still invalidates the board and detail queries, so a pane made stale by someone else converges on the next paint.

⚙ Staff controls
→ Move to QA In development Change status▾
⚠ 403 — You're not allowed to make that change. The ticket has been refreshed.
⚠ 409 — Couldn't move this task. (the existing invalid-transition message, reused verbatim)
MDAssigned to Marc D.▾
⚠ 422 — Marc D. has no Redmine account linked yet. An admin can add one in the admin dashboard.
⚠ upstream — Couldn't change the assignee.