# Operation Head (Web) — Cross-Role Handoffs

> The cross-role touchpoints this role fires into / receives from. The OH is the **terminal node** of the approval cascade — it **receives** the level-3 step from the Store Manager and gives the **final** decision (`Done` or send-back). It never fills. Cascade mechanics live in `FOUNDATION_SPEC §4`; the derived-Pending rule in `FOUNDATION_SPEC §4` + `PROJECT_PLAN §9 #5`.

---

## Upstream input (what arrives at the Operation Head)

| From | Trigger | What the OH receives | Surfaces on |
|---|---|---|---|
| **Store Manager** | SM **approves** at level 2 (`SM-CHK-APPROVE`) | Instance state → `SM_APPROVED`; the OH `ApprovalStep[3]` becomes the current pending step; OH notified (in-app + companion push) | `OH-CHK-OVERVIEW` (row reaches the OH step) → `OH-CHK-APPROVE`; `OH-DASH` Pending KPI |
| **Any filler (MON/SCM/CS/TL)** via the cascade | A checklist climbs the chain | While not yet `OH_APPROVED`, the instance reads **Pending** to the OH (derived, §9 #5) — for daily/weekly/monthly/quarterly/yearly alike | `OH-DASH` Pending counts · `OH-CHK-OVERVIEW` Status column |
| **Overdue sweep** | `now() > dueAt` and not submitted | Instance → `OVERDUE`; escalates to the top of the chain (OH) | `OH-REP-OVERDUE`, `OH-DASH` Overdue KPI |
| **Reports roll-up** | items recorded across all GZ | G items feed the org Positive report; A items + issue photos feed the org Negative report | `OH-REP-POSITIVE`, `OH-REP-NEGATIVE` |
| **The maintenance track (CL-2)** owned by `maintenance-tl-web` + `technician-mobile` | An A-item is routed → assigned → fixed/returned → closed/outsourced; the parent checklist stays **Held** | OH **observes** every Work-Order across all GZ (state, technician, SLA) and the **Held** state on its checklists — **read/monitor only**, no actions | `OH-WO-OVERVIEW` (org-wide WO table + state KPIs · drilldown drawer); `OH-CHK-OVERVIEW` (Held badge while WOs open) |

## Downstream output (what the Operation Head fires)

| To | Trigger | What fires | Lands on |
|---|---|---|---|
| **The checklist (terminal)** | OH **approves** (`OH-CHK-APPROVE` → `POST …/approve`) | Instance state → `OH_APPROVED` = **Done**; `ApprovalLog` + `ApprovalStep[3].APPROVE`; instance **drops off every Pending list** (OH/SM/TL). No further step exists | `*-CHK-STATUS` (filler sees "Operation Head Approved · Done"), `*-CHK-OVERVIEW` |
| **The original filler** | OH **sends back** (`OH-CHK-APPROVE` → `POST …/sendback`, reason required) | Instance state → `SENT_BACK`; returns to the **original `fillerId`** (Ramesh/etc.), **not** the Store Manager (§9 #3); filler notified (FCM + in-app) | filler's `MON-CHK-STATUS` (red banner + Re-fill); on re-submit the cascade **restarts from TL** (§9 #8) |
| **Org management (config)** | OH edits the RBAC matrix / onboards users / creates Game Zones | `RolePermission` / `User` / `GameZone` writes — effective on the next request (no redeploy for RBAC, L4) | every role's resolved `/me` nav + permissions; SM's GZ scope; auto-assign eligibility (certs) |
| **The auto-assign engine (CL-1)** | OH **authors / assigns / edits** a checklist template (`OH-CHK-UPLOAD` → `OH-CHK-TPL-ASSIGN` / `OH-CHK-TPL-EDIT`) | `ChecklistTemplate` write with `fillRoleCode + rideId + gameZoneId` + sections→items (test method, G/A, requires-photo-on-A); the Assign mapping is the **definition** auto-assign reads | the **auto-assign engine** generates instances at roster-publish / period-rollover (`FOUNDATION_SPEC §3`) for every matching rostered filler across the template's GZ scope → lands on the filler's `*-CHK-TODAY`; edits version the template (next cycle only) |

## The closed loop (where OH sits)

```
SM publishes roster ─auto-assign─▶ filler fills (MON/SCM/CS/TL) ─submit─▶ TL approve ─▶ SM approve
                                                                                              │
                                                                                              ▼
                                                                         ┌─────────  OH step (level 3)  ─────────┐
                                                                         │  OH-CHK-OVERVIEW → OH-CHK-APPROVE     │
                                                                         │   ├─ Approve  → OH_APPROVED = DONE    │
   filler's MON-CHK-STATUS  ◀────────── send-back (to original filler) ──┤   └─ Send back (reason) → SENT_BACK  │
   (red banner, Re-fill, restart-from-TL)                                └──────────────────────────────────────┘
   (until OH approves, OH/SM/TL all see PENDING — derived)
```

## Contract notes for the BE dev
- The **approve** handler at the OH level is **terminal**: on success it (1) transitions `SM_APPROVED → OH_APPROVED`, (2) writes `ApprovalLog` + sets `ApprovalStep[3].action = APPROVE`, (3) removes the instance from the derived-Pending set (it is now `Done`). **No notification "up" — there is no level above OH.** A filler-facing "your checklist is Done" notification is optional (P1).
- The **send-back** handler at the OH level is **identical in shape** to TL/SM send-back: set `state = SENT_BACK`, write `ApprovalLog` with the required `reason`, notify the **original `fillerId`** (not SM, not TL). Implement the send-back-to-filler once in the cascade engine; all three approver levels call it.
- **Derived Pending is a read concern only:** `OH-DASH` + `OH-CHK-OVERVIEW` call the shared `renderStatusForViewer(instance, viewerRole=OH)` — anything `< OH_APPROVED` (and not `NOT_STARTED`) renders **Pending**. Never write "Pending" to `state`.
- **`If-Match` matters at the OH step:** SM might still be moving instances; an OH approve/sendback on a stale version returns **409** so two approvers can't double-apply. The UI refetches the timeline.
- **OH scope = all (L5):** the cascade engine resolves the OH approver as "the user with the OH role" — there is one OH across all Game Zones. No GZ filter restricts which instances reach the OH step.
- Do **not** expose any fill/submit or photo-upload endpoint to OH — the resolver denies them. This pack consumes none of them; it is a viewer + approver + admin only.
- **Template authoring is consumed by auto-assign, not dispatched here (CL-1):** `OH-CHK-UPLOAD` writes `ChecklistTemplate` + the Assign mapping; the **auto-assign engine** (`FOUNDATION_SPEC §3`) reads `fillRoleCode + rideId + gameZoneId` and generates instances at roster-publish / period-rollover. Implement template selection once in the engine; OH/SM/TL authoring all feed it (scoped: OH=all, SM=own GZ, TL=own team). Edits are **versioned** — new items apply to the next cycle, never to already-generated instances.
- **OH is an observer of the Work-Order track (CL-2):** `OH-WO-OVERVIEW` consumes **read-only** `GET /work-orders` (all GZ). OH does **not** route/assign/fix/return/outsource — those mutations live in `maintenance-tl-web` (`route`/`assign`/`review`/`outsource`) + `technician-mobile` (`start`/`done`/`return`). **Single-track hold:** keep the parent `ChecklistInstance` in `HELD` while any WO is open; resume the approval cascade to Done only when **all** WOs close. **Only `OUTSOURCED` WOs reach the Store Manager** — internally-fixed (`DONE`) close at the maintenance level.
