# Team Leader (Web) — Cross-Role Handoffs

> The cross-role touchpoints this role fires into / receives from. The TL is the **first approver** in the cascade (`PROJECT_PLAN §4b`, `FOUNDATION_SPEC §4`) — it **receives submissions** from fillers, **passes approvals up to the Store Manager**, and **sends back to the original filler**. It can also **fill** (then SM is its next approver). Cascade mechanics live in `FOUNDATION_SPEC §4`.

---

## Upstream input (what arrives at the Team Leader)

| From | Trigger | What the TL receives | Surfaces on |
|---|---|---|---|
| **Filler (MON / SCM / CS)** | A floor person **submits** a checklist (`*-CHK-SUBMIT` → `POST …/submit`) | Instance state → `SUBMITTED`; `ApprovalStep` rows (TL/SM/OH) created; **TL notified** (FCM + in-app). The instance enters the TL's verify queue | `TL-DASH` (verify queue) · `TL-CHK-OVERVIEW` (tab=active) · `TL-CHK-VERIFY` + a `CHECKLIST_SUBMITTED` notification |
| **Store Manager** | Roster (branch-wide) **published** by SM (`SM-ROSTER-PUBLISH`) | Auto-assign fires → `ChecklistInstance`s appear for the TL's team; the TL's team progress + overview populate | `TL-DASH`, `TL-CHK-OVERVIEW`, `TL-ROSTER-VIEW` |
| **Store Manager / Operation Head** | **Send-back** of an instance the **TL filled** (TL acting as filler) | If a higher approver rejects a TL-filled instance, it returns to **Priya as the original filler** to re-fill | `TL-CHK-FILL` re-entry + a `SENT_BACK` notification |
| **Overdue sweep** | `now() > dueAt` and not submitted on a team instance | Instance → `OVERDUE`; escalates up the chain — surfaces on the TL dashboard/overview | `TL-DASH` (overdue KPI), `TL-CHK-OVERVIEW` (Overdue tab) |
| **Maintenance TL / Technician (CL-2)** | The **last** open `WorkOrder` on a held instance closes (`DONE` internally / `OUTSOURCED`) | Single-track hold releases: instance `HELD → TL_APPROVED`, cascade resumes to SM; the Held banner clears on the TL's verify/overview | `TL-CHK-VERIFY` (Approve unlocks → already passed), `TL-CHK-OVERVIEW` / `TL-DASH` (Held count decrements) |

## Downstream output (what the Team Leader fires)

| To | Trigger | What fires | Lands on |
|---|---|---|---|
| **Store Manager (Rohit)** | Priya **Approves** a submission (`TL-CHK-VERIFY` → `POST …/approve`) | Instance state → `TL_APPROVED`; `ApprovalStep[1].APPROVE`; `ApprovalLog`; **SM notified** (FCM + in-app). The instance enters the SM's approval queue | `SM-CHK-APPROVE` / `SM-CHK-OVERVIEW` + a notification to Rohit |
| **Original filler (e.g. Ramesh)** | Priya **Sends back** with a reason (`TL-CHK-VERIFY` → `POST …/sendback`) | Instance state → `SENT_BACK`, reason logged; returns to the **original filler** (NOT the previous step, `§9 #3`); **filler notified** | `MON-CHK-STATUS` (red banner + Re-fill) + a `SENT_BACK` notification |
| **Store Manager (as next approver)** | Priya **submits** a checklist **she filled** (`TL-CHK-FILL` → `POST …/submit`) | Instance → `SUBMITTED`; because the filler is the TL, the next approver is **SM** (a TL cannot self-approve) | `SM-CHK-APPROVE` |
| **Up the chain (derived)** | a TL-approved instance climbs the cascade | While not yet OH-approved, the instance reads **Pending** to SM/OH per their level (derived, `§9 #5`); the TL sees the real sub-state climbing above its level | `OH-DASH` / `SM-CHK-OVERVIEW` Pending counts |
| **Team (auto-assign)** | Priya **publishes** the team roster (`TL-ROSTER` → `POST /roster/publish`) | Auto-assign creates `ChecklistInstance`s for the team's published entries (`FOUNDATION_SPEC §3`) | filler `*-CHK-TODAY` + `ROSTER_PUBLISHED` notifications |
| **Maintenance TL (Vikram) — CL-2** | Priya **Routes an A-item to Maintenance** (`TL-CHK-VERIFY` → `POST /work-orders`) | One `WorkOrder` (state `ROUTED`) per A-response; the inspection checklist goes **`HELD`** (single-track hold — cannot reach Done until every WO closes); **Maintenance TL notified** | `maintenance-tl-web` → `MTL-WO-QUEUE` / `MTL-WO-DETAIL` + a work-order notification |
| **Monitor mobiles (re-target) — CL-4** | Priya **Reassigns** a monitor mid-shift (`TL-ROSTER-REASSIGN` → `PATCH /roster-entries/:id/reassign`) | New ride's **`NOT_STARTED`** checklists auto-assign to the monitor; old ride re-targets to its current holder; **FCM `ride-changed`** to both → live `*-CHK-TODAY` update (ride-changed banner). In-progress work is never withdrawn | `staff-court-monitor-mobile` / `senior-court-monitor-mobile` / `cleaning-supervisor-mobile` → `*-CHK-TODAY` (live) |
| **Reports (roll-up)** | A items recorded by the team | Team **A** items + issue photos feed the Negative report (per ride/shift/day) | `TL-REP-NEGATIVE` (this pack) + SM/OH reports |

## The closed loop (TL at the centre of the cascade)

```
 filler submits ──notify──▶ TL-CHK-VERIFY (Priya: review items + photos)
                                 │
                ┌────────────────┴─────────────────┐
          Approve ✓                          Send back (reason)
                │                                   │
                ▼                                   ▼
   SM-CHK-APPROVE (Rohit) ──▶ OH-CHK-APPROVE   MON-CHK-STATUS (original filler)
        approve                = DONE            red banner + Re-fill
                                                       │ re-fill + re-submit
                                                       ▼
                                          cascade RESTARTS from TL (§9 #8)
   (until OH approves, SM + OH see PENDING; TL sees the real sub-state above its level)
```

## Contract notes for the BE dev
- The **approve** handler is shared across cascade levels and **resolver-gated by level**: for a TL caller it transitions `SUBMITTED → TL_APPROVED`, writes `ApprovalStep[1].APPROVE` + `ApprovalLog`, and enqueues the **SM notification** (FCM fan-out to Rohit's `DeviceToken`s + an in-app `Notification`). The SM/OH packs reuse the same handler at levels 2/3.
- The **sendback** handler must set `state=SENT_BACK`, require a non-empty `reason` (422 otherwise), write the `ApprovalLog` with the reason, and notify the **original `fillerId`** — never the previous approver (`§9 #3`). After re-submit the cascade restarts from TL (`§9 #8`).
- **Do NOT** let a TL approve an instance they filled — when `fillerId == caller.id`, the approve grant for level 1 does not apply to that instance (it routes to SM). Enforce in the resolver, not the UI.
- **Derived Pending** is read-only: the same `renderStatusForViewer()` powers `TL-CHK-OVERVIEW`, `SM-CHK-OVERVIEW`, `OH-DASH`. Pending is never written to `state`.
- **Route-to-Maintenance (CL-2):** `POST /work-orders` is the **only** WO action the operations TL fires — it creates one `WorkOrder` per A-`ChecklistResponse` (`@@unique(responseId)`, idempotent) and sets the parent instance `HELD`. The Maintenance TL pack assigns/closes; the engine releases the parent (`HELD → TL_APPROVED`) only when **every** WO is `DONE`/`OUTSOURCED`. Only `OUTSOURCED` WOs reach the Store Manager (`SM-WO-OUTSOURCE`); `DONE` closes silently at maintenance.
- **Mid-shift reassign (CL-4):** `PATCH /roster-entries/:id/reassign` re-targets only **`NOT_STARTED`** instances (in-progress safety), requires `assertCertified(userId, newRideId)`, and pushes FCM `ride-changed` to both the reassigned monitor and the old ride's new holder so the mobile `*-CHK-TODAY` packs update live. Emits `AuditLog("ROSTER_REASSIGNED")`.
- **Trainee TL (CL-3):** does not fire approve / sendback / route / reassign / template-write — those grants resolve `allowed:false`. The Trainee assists (review, fill) but never produces these downstream events.
