# Task Automation — Alignment Plan **Date:** 2026-06-25 **Source:** Code review of `/opt/projects/OnDeck/ondeck` against the Seubert task-automation meeting (Lorentz Hinrichsen, Luke Billman, Dawn Boland). **Status:** Planning — to be executed at a later date. --- ## Executive Summary The meeting reaffirmed the design for SHAPE task automation: tasks should only generate for **active SHAPE / SHAPE 2 clients with active policies**, the manager setup queue should be drivable **to zero**, and client active/inactive status changes should be **logged and surfaced in a daily briefing**. The Horizon data model and the core task mechanics already match the agreed design. **Tasks are policy-specific (not carrier-specific), loss-run tasks follow the policy renewal date, and clients are never auto-deactivated** — the advocate stays on the profile so returning clients resume cleanly. These need no change. Three decisions from the call are **not yet built**, and one **likely defect** can mask half the SHAPE population. In priority order: 1. **Generation is not gated on active policies.** Tasks generate for any client that *has* policies, regardless of whether those policies are Cancelled/Expired/Non-Renewed. The dead-policy filter only hides tasks at display time — they are still created. This contradicts the meeting's central rule: *"no active policies → no tasks of any kind."* 2. **No way to clear non-P&C-SHAPE accounts from the manager queue.** Client setup still *requires* a claims advocate before it can be completed, with no "NA / no advocate / other department" option. Accounts like surety/bond-shape (in-transit example) therefore cannot be cleared, so the queue can never reach zero. 3. **No status-change logging or daily briefing.** Client active↔inactive transitions are not recorded anywhere, and no briefing/digest feature exists. 4. **(Likely bug) `Shape2` vs `Shape 2` name mismatch.** The secondary designation is created/synced as `Shape2` (no space) but nearly every read query filters for `Shape 2` (with a space). If the stored name is `Shape2`, all SHAPE-2 clients are silently excluded from the manager queue, dashboard, and metrics. None of the proposed work changes the database schema meaningfully — it is logic-only — so it is safe to deploy in the agreed 7 PM–5 AM window. --- ## What already matches (no action needed) | Meeting decision | Code reality | |---|---| | Tasks are policy-specific, not carrier-specific | `Task.policyId` / `policyGroupId`; carrier only on `Policy.carrierName`; generation filters by `policyTypeFilter`, never carrier (`src/lib/sync/auto-generate.ts:179`) | | Loss-run tasks follow the policy renewal date | Negative `daysOffset` anchored to `policy.expirationDate + 1` (`auto-generate.ts:175-185`) | | Don't auto-mark clients inactive; advocate persists | No `isActive` flag on `Client`; nothing auto-deactivates; `claimsAdvocateId` persists | | Logic-only change, safe maintenance window | Auto-generation writes no schema; scheduled sync runs 02:00 (`0 2 * * *`) | --- ## Items to address (priority order) ### Priority 1 — Gate task generation on active policies **Problem:** `src/lib/sync/auto-generate.ts` selects clients/policies/groups by *existence* of policies, not status. Dead-policy filtering (`['Cancelled','Expired','Non-Renewed','Rewritten','Not taken']`) only runs at display time (`api/clients/[id]/tasks/route.ts:29`), so tasks for hibernating/departed clients are still created and merely hidden. **Target behavior:** A client with no active policy generates **no** client-, policy-, or group-level tasks. ### Priority 2 — Client-level "NA / no advocate" path **Problem:** Setup completion requires an advocate (`src/components/renewal-groups/setup-wizard.tsx:422-426`, disabled button at `:436`). A client leaves the manager queue only when **both** `claimsAdvocateId` and `setupCompletedAt` are set (`manager/setup/page.tsx:40-43`). There is no client-level NA concept (the existing `NA` is a *task* status). **Target behavior:** A manager can mark a client as NA / no-advocate / other-department, which clears it from the setup queue without assigning an advocate. ### Priority 3 — Status-change logging + daily briefing **Problem:** No client active↔inactive transition is written to `AuditLog`; `Notification.create` is never called; no briefing/digest/summary feature exists. **Target behavior:** When a client crosses the active/inactive line (e.g. a new policy reactivates them, or their last active policy lapses), log it; roll those events into a daily briefing for managers. ### Priority 4 (verify first) — `Shape2` vs `Shape 2` naming **Problem:** Created/synced as `Shape2` (`api/admin/sync-designations/route.ts:22,84,92`; `scripts/import-shape-tasks.ts:68`) but read as `Shape 2` across manager queue, dashboard, metrics, workload. Import scripts hedge with both forms; runtime read queries do not. **Target behavior:** One canonical name used consistently everywhere. --- ## Step-by-step plan (for later execution) ### Phase 0 — Verify the designation name (do this first; ~15 min) 1. Confirm the stored name with: `SELECT name FROM designations WHERE name ILIKE 'shape%';` 2. If it returns `Shape2`: either (a) rename the row to `Shape 2`, **or** (b) normalize every read filter to match the import scripts' `['Shape','Shape 2','Shape2']`. Pick one canonical form and apply it everywhere. 3. Re-check manager queue / dashboard / metrics counts before vs. after to confirm SHAPE-2 clients now appear. ### Phase 1 — Active-policy gate on task generation (Priority 1) 1. Define a single shared `ACTIVE_POLICY_STATUSES` (or reuse the existing `DEAD_STATUSES` exclusion) in one module so generation and display agree. 2. In `auto-generate.ts`: - Client-level query (`~:228-232`): require at least one **active** policy (status not in dead set), not merely any policy/group. - Policy-level generation (`~:149-221`): skip policies whose status is dead. - Group-level generation (`~:80-146`): skip groups with no active policies. 3. Write tests covering: all-dead-policies client → 0 tasks; mixed active/dead → tasks only for active; reactivation (dead → active policy added) → tasks resume. 4. Decide and document the policy on **already-generated** tasks for now-inactive clients (leave hidden vs. mark NA/cancelled). Confirm with Luke/Lorentz. ### Phase 2 — Client NA / no-advocate path (Priority 2) 1. Decide the data representation (recommend a nullable `Client.setupStatus` enum or a `clearedReason` + boolean, avoiding a heavy schema change). Confirm naming with stakeholders. 2. Update the setup wizard so a manager can choose "NA / no advocate / other department" instead of an advocate, and allow completion in that case (relax `setup-wizard.tsx:422-426` / the `canFinish` guard). 3. Update the manager setup queue (`manager/setup/page.tsx:38-43`, `api/clients/setup-queue/route.ts`) so NA-marked clients drop off the queue. 4. Ensure NA-marked clients are excluded from (or clearly distinguished in) generation per the Phase 1 rules. 5. Tests: NA client clears the queue; NA client generates no advocate-assigned tasks; queue can reach zero. ### Phase 3 — Status-change logging + daily briefing (Priority 3) 1. Add active↔inactive transition detection at the point policies change (post-sync and/or on policy create/cancel). Derive status from active-policy presence (consistent with Phase 1). 2. Write transitions to `AuditLog` (and/or `Notification`) with old/new status and trigger. 3. Build the daily briefing aggregation (new clients needing setup, status changes, queue size) and choose delivery: in-app notification first; Teams/email later (explicitly deferred in the meeting). 4. Schedule within the 7 PM–5 AM window; align with the existing 02:00 sync. 5. Tests: reactivation logs an event; lapse logs an event; briefing aggregates a day's events. ### Phase 4 — Non-SHAPE generation gate (follow-on to Priority 1/2) 1. Decide whether non-SHAPE clients should generate **zero** tasks (meeting implied yes) or only generic templates. 2. If zero: apply the SHAPE/SHAPE 2 designation gate (already used by the manager queue) to `auto-generate.ts` as well. 3. Tests: non-SHAPE client → 0 generated tasks. ### Cross-cutting - All changes are logic-only (Phase 2/3 may add a small column/enum — coordinate a `prisma db push` per `CLAUDE.md`). Deploy in the 7 PM–5 AM window. - After each phase: rebuild and restart per `CLAUDE.md` (`docker compose build horizon-app && docker compose up -d horizon-app`). - Validate against a sanitized/dev copy before production where possible. --- ## Open questions for Luke / Lorentz 1. For clients that go inactive, what happens to **tasks already generated** — leave hidden, auto-NA, or cancel? 2. Exact set of "NA" reasons for the setup queue (other department? surety/bond? no rep?) and whether each should still appear anywhere for reference. 3. Daily briefing delivery channel and recipients for v1 (in-app vs. Teams vs. email). 4. Should **non-SHAPE** clients generate *no* tasks at all, or just no SHAPE-specific tasks?