ADR-006 — Issue Queue State Machine + Delegation

ADR-006: Issue Queue State Machine + Delegation

Status: Accepted | Date: 2026-04-22 | Supersedes: v2 spec 10-admin-system.md §6 (Category E — Deferred banner) | Source: STAKEHOLDER-ANSWERS-2026-04-22.md §B; transcript QF-Backend-Review-Apr-22.md L65–89

Context

The Issue Queue was introduced in v2 (borrowed from Candidate B) as a student-to-admin communication channel for app-reported issues (bugs, content, account, other). The v2 spec as of Apr 15 defined columns and basic actions but left the state machine explicitly deferred — whether Resolved is terminal, whether backward transitions are allowed, and what "Delete" actually means were all flagged as TBD pending a working session.

Two complicating realities surfaced in the Apr 22 session:

  1. Mixed-content reports: students use the student-side "Report Issue" dialog for both genuine tech issues and non-technical concerns ("my mother is sick and my dog just died"). The queue must be able to distinguish actionable bugs from conversational / pastoral notes that belong with CS or teaching staff.
  2. Fresh desk overlap: in production, the same message stream will land in both the admin dashboard issue queue AND the Fresh desk inbox used by the CS team. The state machine must not assume the admin is the sole responder, but must also not block on Fresh desk integration (which is deferred).

Decision

1. State set — Open / In Progress / Resolved / Rejected; Archive is a view

State Meaning
Open New, unread, unassigned. Default state when an issue lands.
In Progress Admin has begun handling. May carry a delegated_to sub-state (see §2).
Resolved Handled. Kept in history. Terminal unless reopened.
Rejected Not a real issue (spam, misfire, duplicate). Kept in history. Terminal unless reopened.

"Closed" (present in the Apr 15 draft) is dropped — it had no distinct semantic from Resolved. Archive is a filter/view over Resolved + Rejected, not a fifth state.

2. Delegation — In Progress sub-state with fixed target list

On transition to In Progress, admin may set delegated_to ∈ {CS, IT, Teaching Staff}. Delegation:

3. Rename Delete → Reject

Per L77–79. "Delete" action becomes "Reject." No issue is hard-deleted in v2. Rejected issues remain queryable in the Archive view.

4. Legal transitions

         ┌─────────┐
   ┌────►│  Open   │◄────────────────┐
   │     └────┬────┘                 │
   │          │ Take / Delegate       │ Reopen
   │          ▼                       │
   │     ┌─────────────┐              │
   │     │ In Progress │──────────┐   │
   │     │  (+ dlg?)   │          │   │
   │     └─────┬───────┘          │   │
   │           │ Resolve          │ Reject
   │           ▼                  ▼
   │     ┌──────────┐       ┌──────────┐
   └─────┤ Resolved │       │ Rejected │
  Reopen └──────────┘       └──────────┘
              │ Reopen             │ Reopen
              └────────────────────┘
                      (→ Open)

Forward: Open → In Progress → Resolved | Rejected. Reject may also apply directly from Open (admin triages as non-issue on arrival). Backward / Reopen: Resolved → Open, Rejected → Open. Admin then re-triages. No direct Resolved ↔ Rejected swap — reopen through Open. In Progress → Open: allowed (undo taking).

5. Archive is a view, not a state

6. Fresh desk integration is deferred

No v2 spec or mockup work depends on Fresh desk. A future ADR (or amendment here) can specify routing if/when that integration is planned. Delegation to CS in v2 is tag-only.

Rationale

Consequences

First-order (10-admin-system.md §6)

Second-order

Third-order

What we explicitly preserve

Implementation notes

Mockup scope:

Production scope (not in this ADR):

Open questions

  1. Delegate target cleanup: when transitioning from In Progress → Resolved, is delegated_to cleared? Assume yes (terminal state, no ongoing delegation). Confirm in mockup pass.
  2. Bulk reject: supported or per-row only? Default to per-row; add bulk in a future ADR if volume demands it.
  3. Issue Queue report: which slices? (Type × status × time-to-resolve?) Tie to the full reporting working session still owed.