# Tester user guide — Unified Insurance Solution Pack (powered by TurfAI)

This guide explains the whole demo insurer for people who test it: every flow, every role and what it does, who
approves what and when, every TurfAI workflow, and a catalogue of scenarios with the result to expect. It is written
from the code (task kinds, decision handlers, router, mappers, workflows and UI labels as of P16.5, sim date
2026-11-01 onwards). Button labels are quoted exactly as they appear in the UI.

Setup, environment variables and commands are in the root [README](../README.md); the full design is in
[`brd/unified_app_plan.md`](../brd/unified_app_plan.md).

## Contents

1. [What the system is](#1-what-the-system-is)
   - [The principle](#11-the-principle-turfai-decides-people-approve-the-app-orchestrates)
   - [Starting it](#12-starting-it)
   - [Portal and console](#13-portal-and-console)
   - [Personas](#14-personas)
   - [The simulated clock](#15-the-simulated-clock)
   - [Reset demo](#16-reset-demo)
   - [Demo scopes (cost control)](#17-demo-scopes-cost-control)
   - [The autonomy level (autopilot)](#18-the-autonomy-level-autopilot)
   - [Sample documents](#19-sample-documents)
2. [Roles and their tasks](#2-roles-and-their-tasks)
3. [End-to-end flows](#3-end-to-end-flows)
   - [New business](#31-new-business-apply--underwriting--issuance)
   - [Billing](#32-billing-collection-dunning-grace-lapse-reconciliation)
   - [Renewals / retention and servicing](#33-renewals--retention-and-servicing)
   - [Claims](#34-claims-intake--fraud--settlement--siu--sign-off)
   - [Complaints](#35-complaints)
   - [Showing the autonomy ladder](#36-showing-the-autonomy-ladder)
   - [The claim TAT clock and penal interest](#37-the-claim-tat-clock-and-penal-interest)
4. [Who approves what, when (master table)](#4-who-approves-what-when)
5. [The TurfAI workflows](#5-the-turfai-workflows)
6. [Scenario catalogue](#6-scenario-catalogue)
   - [Claims variations and duplicate claims](#64-claims-variations-and-negative-cases)
7. [Tips, gotchas and known bugs](#7-tips-gotchas-and-known-bugs)
   - [Rehearsing a duplicate claim without TurfAI](#rehearse-a-duplicate-claim-without-turfai)
   - [Rehearsing the documents loop and the sign-off overrides](#rehearse-the-documents-loop-and-the-sign-off-overrides)
8. [Glossary](#8-glossary)

---

## 1. What the system is

A demo life insurer ("TurfLife") that runs TurfAI's eight Life Solution Packs — Underwriting, Issuance & Servicing,
Billing, Renewals, Claims Intake, Fraud & SIU, Claims Settlement, Complaints — as **one connected company**. A policy
applied for on the portal is underwritten, issued, billed, renewed, serviced, claimed on and complained about, and each
pack hands its result to the next.

### 1.1 The principle: TurfAI decides, people approve, the app orchestrates

- **Every insurance judgement is made inside a TurfAI workflow**: rating, requirement lists, issuance checks,
  collection instructions, dunning stages, reconciliation matching, renewal outreach, servicing answers, claim
  triage, fraud scoring, SIU dispositions, coverage and valuation, complaint classification and deadlines, and every
  letter or message a customer reads.
- **The app never decides.** It only
  1. *orchestrates* — routes events to workflows, branches on the routing fields TurfAI returns, holds human approvals,
     and runs the simulated clock;
  2. *plays the insurer's systems* — its Postgres database is the CRM / policy administration system; the
     deterministic non-AI systems are mocks: rating engine (premium tables), payment rail (NACH / UPI AutoPay / card
     debits), bank settlement feed, AML / PEP / sanctions list, hospital records service, revival quote;
  3. *shows it* — the customer portal and the operations / RM console.
- **Humans approve — at HITL.** Almost every TurfAI result lands as a *human task* in a role's inbox. At the default
  **autonomy level**, HITL (human in the loop), nothing is issued, lapsed, paid, repudiated, sent to a customer or
  closed until the right person chooses an option. The level (Console → Clock, [§1.8](#18-the-autonomy-level-autopilot))
  decides which *clean* gates go straight through: at **Semi-auto** the orchestrator's **autopilot** closes a clean
  standard underwriting memo, a complaint acknowledgement and a renewal outreach itself; at **Autonomous** also a clean
  settlement sign-off up to a pay-out limit and an unsubstantiated SIU disposition. The autopilot never judges: it
  applies TurfAI's own recommendation, and only when TurfAI's routing fields say the case is clean *and* the insurer's
  System checks found 0 mismatches *and* the level permits that gate. Every other gate waits for a person at every level.
- **Push model.** The worker sends TurfAI the full context as signed webhook JSON and polls for the result; TurfAI
  never calls back into the app. Each result is checked against a strict *contract* before the app uses it.
- **The insurer double-checks the arithmetic.** Before using a TurfAI result the app recomputes every deterministic
  fact it can (ages, dates, amounts, windows, copies of routing fields) and shows disagreements on the task as
  *System checks* (see [§5.14](#514-system-checks-lineage-runs-and-retries)).

### 1.2 Starting it

Follow the [README](../README.md): `npm install`, fill `.env.local`, provision the workflows, then

```bash
npm run demo:reset   # fresh demo data, clock at 2026-11-01
npm run dev          # web on http://localhost:3100 + the worker
```

Without the worker nothing reaches TurfAI: pages render, but submitted work waits.

| URL | What |
|---|---|
| <http://localhost:3100> | Landing page — pick a storyline |
| <http://localhost:3100/portal> | Customer portal |
| <http://localhost:3100/console> | Operations / RM console (Inbox) |
| `/console/policies` | Policy 360 — "Find a policy" by policy number or customer name; one timeline per policy / proposal |
| `/console/complaints` | Complaints register and root causes across the book |
| `/console/runs` | Runs — every TurfAI execution, its inputs, contract and status |
| `/console/dashboards` | Pack dashboards (all eight packs) |
| `/console/clock` | Simulated clock, advance controls, **Reset demo…** |

### 1.3 Portal and console

**Customer portal** (`/portal`). The **Act as** menu (top bar) picks which customer is using the portal. When the
insurer knows a customer has died (a claim was reported), the portal acts as that customer's **nominee** (e.g. A2's
son Karthik, B's wife Anita). Sections:

| Portal page | What the customer does there |
|---|---|
| **Apply** | Fill the proposal form, attach / upload documents, optionally **Pay the first premium now**, submit; follow the decision; answer a request for more information ("Send to underwriting") |
| **My policy** | Schedule, status, risk commencement, free-look end, nominee, premium paid-to date, policy documents |
| **Ask about my policy** | Ask a question, request an endorsement (nominee, address, phone / e-mail, premium mode), a premium-paid certificate, a free-look return, or a revival |
| **Payments & mandate** | See dues and messages; **Pay ₹… now**; **Re-register mandate**; **I'm having difficulty paying**; **Ask to revive this policy** / **Pay ₹… and revive** |
| **Offers** | Answer a revised underwriting offer, or a renewal / retention offer |
| **Report a claim** | Report a death (form + plain-text documents), follow the claim, read the decision letter. While a claim on the selected policy is not closed, the page says "You already have claim CLM-… (\<status\>) on this policy — its progress is shown above" and folds the form behind **Report this death again** (a second report is still allowed — [§3.4](#34-claims-intake--fraud--settlement--siu--sign-off)). A claim waiting for documents ("More documents needed": asked at triage or at settlement) shows what is still needed — the claims officer's note and TurfAI's list — and an **Add documents** form on the claim card (same upload list and **Use sample documents** as Report a death); the documents go to the **same** claim and TurfAI checks / re-assesses it ([§3.4](#34-claims-intake--fraud--settlement--siu--sign-off)) |
| **Raise a complaint** | Raise a complaint, follow the case, its deadline and the response |

**Operations / RM console** (`/console`). The **Work the inbox as** menu picks a role; the badge shows its open
tasks. The Inbox has **Open** / **Decided** views and an **All / Storyline / Background** filter (storyline personas
vs the background book). In the **Decided** view a task the autopilot closed carries an **Autopilot** badge next to
its status ([§1.8](#18-the-autonomy-level-autopilot)). Clicking a task opens the **task drawer**: TurfAI's contract
rendered for that kind of task, **System checks**, the case panels (claim, SIU, billing, …), **Lineage** (the TurfAI
run, the triggering event, the source documents as chips), the **Decision** card and the full payload. The drawer is
laid out so the decision is never out of reach:

- **Sticky header with jump chips.** Under the title a row of chips — **Summary · Checks · Recommendation** (or the
  case block: Claim, SIU case, Billing, …) **· Lineage · Decision · Payload** — is built from the sections actually on
  the page; a click scrolls that section under the header. The **Decision** chip is highlighted.
- **The Decision card** is the accented box (primary border, sparkle icon) with a state badge: **Your call** (open,
  yours to decide), **Decided**, **Decided by Autopilot**, **Cancelled**, or **Awaiting \<role\>** when the task belongs
  to another console role ("This task belongs to the Underwriter — switch role to work it").
  **Claim** reserves the task for you; **Release** gives it back. Deciding an open task claims it automatically. The
  options are radio buttons; choosing one shows the extra fields it needs and a short "what happens next" text; then
  **Submit: \<option\>**. On the underwriting memo, the option TurfAI recommends carries a small **TurfAI** sparkle
  marker. On the **settlement sign-off** and the **SIU disposition** — whose single option signs TurfAI's result off as
  it is ("Sign off TurfAI's recommendation / disposition") — the card opens with a line saying what that result is,
  e.g. *"TurfAI recommends: Admit the claim — pay ₹27,67,173 to Karthik Iyer (son)"*, *"Repudiate the claim — s.45
  non-disclosure; refund premiums of …"* or *"Substantiated — send repudiation grounds to Claims Settlement"*
  (*"TurfAI recommended: …"* once decided). It is read straight from TurfAI's routing in the task payload.
- **The sticky "Decide:" bar** at the bottom of the drawer shows one button per option (the recommended one with the
  TurfAI sparkle) while the Decision card is off-screen: clicking a button selects that option and scrolls the card —
  with its form open — into the middle of the view; the bar fades as soon as the card is in view and is not rendered
  for decided / cancelled tasks. For another role's task it says **Switch to \<role\> to decide**; for a held
  settlement sign-off it shows the held text ("Sign-off held: …") and no buttons.
- **Other claims on this policy.** On a **Claim triage** — and on a **Settlement sign-off** when the policy has
  another claim — the drawer opens with an amber box "Other claims on this policy" (badge *insurer's claims
  records*); its chip **Other claims** is the first in the header row. It lists the insurer's other claims on the
  policy, warns when TurfAI did not link an open one, and links to each claim's Policy 360 and open task
  ([§2.7](#27-claims-officer)).
- Results: **Saved** (with the number of events / follow-on tasks, shown on the decided card), or an error —
  "Decision not accepted" (a rule refused it; the message says why), "The task changed — the drawer now shows its
  current state" (someone else, a newer TurfAI run or the autopilot got there first), "Not allowed for this role".

A **storyline panel** (floating, collapsible, on every page) is the presenter guide: current beat, whom to act as, a
link to the right page, which TurfAI pack is working, and what to point out.

### 1.4 Personas

All people are fictional; amounts are INR. Customer ids, policies and dates come from `fixtures/personas/*.json`
and plan §15.

| Persona | Starting state (seed) | What it demonstrates |
|---|---|---|
| **A1 · Arjun Rao** (b. 1994-06-14, 32, non-smoker) | Draft application PRP-2026-000101: TL-SECURE-TERM, ₹1,00,00,000, 30 years, monthly ₹1,404, NACH mandate NACH-MND-88319, nominee Divya Rao (spouse). KYC, ITR and proposal form on file | Straight-through: apply → standard → issued `POL-LIFE-2026-000101` → 12-01 premium collected → 12-02 bank feed matched. Also the requirements path (send without ITR / KYC) |
| **R1 · Kavya Menon** (b. 1985-02-20, 41) | Draft application: ₹50,00,000, 20 years, annual. KYC, medical exam and lab report on file | Rated case: underwriter rates +50 % → Kavya accepts ₹17,346 on Offers → `POL-LIFE-2026-000102` |
| **A2 · Meena Iyer** (b. 1965-04-02) | Endowment POL-LIFE-2019-000311, ₹25,00,000, RCD 2019-03-01, vested bonus ₹3,40,000, policy loan ₹60,000 + interest; died 2026-10-25 | Clean death claim (11-02, nominee son Karthik): fraud low → settled for sum assured + bonus − loan |
| **B · Vikram Sethi** (b. 1982-01-17) | Term POL-LIFE-2025-000877, ₹40,00,000, RCD 2025-09-10, proposal disclosed no conditions; died 2026-11-03 | Early claim (11-08, wife Anita): hospital records show hypertension + diabetes since 2020 → fraud refers → SIU substantiated → s.45 repudiation → Anita complains (11-25) |
| **C · Lakshmi Narayanan** (b. 1976-08-09) | Endowment POL-LIFE-2021-000452, ₹15,00,000, RCD 2021-06-15, ₹4,200 monthly NACH due on the 15th | Billing to claim: 11-15 debit fails → 11-20 retry fails → 11-22 hardship → retention outreach → 12-01 lapse → 2027-01 revival with health declaration → monthly premiums → 2028-05-20 death in grace → SIU screen clears → ₹16,74,300 settled (in-grace premium deducted) |
| **D · Prakash Kulkarni** (b. 1968-12-01) | Term POL-LIFE-2018-000129, ₹50,00,000, lapsed 2025-08-04; claim repudiated (not in force); complaint received 2026-10-28 and acknowledged | Stalled complaint: on 11-07 the clock raises at-risk, TurfAI escalates before the 14-day breach (its escalate-to: the Head of Grievance Redressal); 11-08 the Compliance & grievance desk records the resolution |
| **Background book** (`bg-…`) | ~40 in-force policies (mixed products, modes, agents) | Dashboards, the 12-02 bank feed's exception lines (short / over / duplicate / unmatched), the fraud agent-cluster indicator. By default their premiums are *not* sent to TurfAI (see [§1.7](#17-demo-scopes-cost-control)) |
| **New sign-ups** (`new-<n>`, `CUST-9NNNNN`) | Created by testers: **Act as → New customer…** | Apply as an entirely new customer: sign-up form → draft application `PRP-<year>-9NNNNN` → Apply. Listed under "Signed up on the portal" in the Act as menu |

### 1.5 The simulated clock

Every component reads time from one simulated clock (`clock.sim_now`), never the wall clock, and every TurfAI run is
given `as_of_date` = the simulated business date. **The clock only moves forward.**

On **Console → Clock**: **+1 day**, **+7 days**, **+30 days**, **To next event (date)**, and **Advance to date** →
**Advance**. Every day in between is scanned in order. Before each day the clock waits (up to 5 minutes) for the
storylines' in-flight TurfAI work to finish, so a multi-day jump does not overtake a run; if it times out you see
"Advance stopped early" with the reason.

What the daily scan does (no judgement — it compares dates held in the CRM):

| Scanner | When | Result |
|---|---|---|
| Billing schedule | a premium-paying policy has no future due | next instalment row created |
| Premium due | a scheduled due's date ≤ today | `premium.due` → TurfAI `billing_collect` |
| Dunning step | the next dunning action date ≤ today | `dunning.step_due` → mock rail retry and / or TurfAI `billing_dun` |
| Grace end | grace end ≤ today on an unpaid due | `grace.ends` (timeline fact) |
| Grace expiry | grace end < today on an unpaid due | `billing.grace_expired` + **Grace expired** task for the collections officer (the app never lapses by itself) |
| Renewal window | a scheduled due enters a window (annual 90 / 60 / 30 / 7 days before, half-yearly 30 / 7, quarterly 15 / 7, monthly 7) | `renewal.window_open` → TurfAI `renewal_action` |
| Complaint risk | a complaint's escalation trigger date ≤ today | `complaint.at_risk` → TurfAI `complaint_handle` re-check |
| Free look | free-look end = today | `policy.free_look_closed` (timeline fact) |
| Settlement feed | a bank feed is dated today (only 2026-12-02 is seeded) | `settlement_feed.arrived` → TurfAI `billing_reconcile` |
| Bonus declaration | 31 March | reversionary bonus vests on participating policies |

**Why dates matter.** Cover is judged on the *date of death*; contestability (s.45) runs 3 years from the later of
RCD and last revival; grace is 15 days for monthly and 30 days for other modes; complaint clocks run from the
received date (acknowledge 3 days, resolve 14 days, escalate on day 10); revival quotes are valid 5 days. Play the
storylines in date order: A1 + R1 on 11-01 → A2 on 11-02 → D on 11-07 / 11-08 → B from 11-08 and C from 11-15
interleaved → A1's December premium (12-01 / 12-02) → C's 2027–28 beats last. Once the clock has passed a beat's
date, that beat cannot be replayed without a reset.

### 1.6 Reset demo

**Console → Clock → Reset demo…** opens "Reset the demo?" → **Reset demo**. It is the same as
`npm run demo:reset`: every proposal, policy change, payment, claim, complaint, task, sign-up and TurfAI run made
during the demo is deleted, the database is re-seeded, the clock goes back to 01 Nov 2026, and TurfAI runs still in
flight are abandoned (their results are never applied). It only works on the `uip` database on `localhost:5544`.

### 1.7 Demo scopes (cost control)

TurfAI runs cost tokens. `reference_data['demo.settings']` limits which billing / renewal events go to TurfAI:

| Setting | Values | Default (setting absent — what `demo:reset` leaves) |
|---|---|---|
| `turfai_billing_scope` | `all` (whole book) · `personas` (storyline customers and sign-ups — anyone not `bg-…`) · `none` | `personas` |
| `turfai_renewal_scope` | same; when absent it follows the billing scope | `personas` |

So by default background policies' premiums are not collected through TurfAI and simply run to grace expiry (you will
see their **Grace expired** tasks under the **Background** filter). With billing scope `none` the bank feed is not
reconciled either. There is no console toggle for the scopes (§13.1 K10); they are changed by SQL only. The same
row's `autonomy` key is the autonomy level, which *does* have a console control — next section.

**TurfAI focus (test one storyline faster).** A clock jump waits, day by day, for every storyline persona's TurfAI
runs, so moving A1 from issuance to its 01-Dec due also waits on C's renewal windows, D's complaint re-check, bank
feeds and so on. In the storyline panel, **Focus TurfAI on this storyline** stores `turfai_focus_persona` in the same
row. While it is on, only that persona's events (and bank feeds with a line on its policies) go to TurfAI. The other
storylines' events are **kept, not dropped**: they wait unprocessed, the clock does not wait for them, and their tasks
are not held. The panel shows how many are waiting. **Turn focus off** (or focus another storyline) and the worker
catches up on them, routing on the CRM as it is then. Reset demo clears the focus. Background customers are not
affected; the scopes above still govern them. While focused, the other storylines' dues still pass their grace dates
without a TurfAI collection, so expect their **Grace expired** tasks. Reset before running those storylines properly.

### 1.8 The autonomy level (autopilot)

A demo setting (`reference_data['demo.settings'].autonomy = {level, payout_limit}`, plan D30) that says which **clean**
TurfAI results the orchestrator may close itself by applying TurfAI's own recommendation. Absent / unreadable → HITL.

**Setting it.** **Console → Clock → Autonomy level** card: three radios with their meaning —

| Radio | Meaning (as shown) |
|---|---|
| **HITL** | Every TurfAI result waits for a human. (The default; what `demo:reset` leaves.) |
| **Semi-auto** | Clean standard underwriting memos, complaint acknowledgements and renewal outreach go straight through. |
| **Autonomous** | Also clean pay-outs up to the pay-out limit and unsubstantiated SIU dispositions. |

Under the radios an amber note (also at the foot of the storyline panel) reads: *"Autonomous — demo only: the specs
have a claims officer approve every claim; the autopilot never repudiates, never signs a substantiated fraud case and
never pays above the limit."* (P16.5, D-12 — copy only; the level behaves exactly as described here.)

— then **Pay-out limit (INR, Autonomous only)** (whole rupees, a ₹ preview beside it; default `5000000` = ₹50,00,000;
"a clean settlement with net payable up to ₹… is signed off automatically; anything above it waits for the claims
officer"), the help line **Never automatic at any level:** issuance review, lapse, hardship / collections, billing
mismatch, reconciliation, servicing, revival, claim triage, SIU screen / investigation, complaint resolution /
escalation, regulator notice, run failure, every customer task — and **Save autonomy level**. Under the button: *"Applies to the next TurfAI
result; open tasks are not re-evaluated; a demo reset returns to HITL."* Saving shows "Autonomy: Semi-auto · pay-out
limit ₹50,00,000 — Saved. The next TurfAI result the worker maps runs at this level; tasks already open keep waiting
for their role." (or "Already set — nothing changed."); an invalid limit is refused with "Not saved". The save takes
the demo lock, so it is refused while a clock advance or a reset is running. The console frame (not the portal) shows
the current level as the badge **Autonomy: HITL / Semi-auto / Autonomous** at the right end of the console tab row (Inbox … Clock); it links to the
card (`/console/clock#autonomy`).

**What the autopilot does** (`apps/worker/src/autonomy.ts`). Right after a mapper has written a TurfAI result into
the CRM and raised one of the five gate tasks below, the worker reads the level and evaluates the gate's checks on the
task payload — TurfAI's `routing.*` fields plus the insurer's recomputed **System checks**. Every check must pass and
the level must permit the gate; then the task is decided as `system:autopilot` with the option TurfAI recommended,
inside the same transaction, and the follow-on events fire exactly as if a person had clicked:

| Gate (task kind) | From level | Checks (all must pass) | Decision applied |
|---|---|---|---|
| Underwriting memo (`uw_memo`) | Semi-auto | `decision_type standard` · no referral (`refer_to` empty) · no outstanding requirements · `compliance_pass true` · loading 0 % · no exclusions · no over-insurance · 0 system-check mismatches | **Accept (standard)** → `uw.approved` → `policy_issue` |
| Complaint acknowledgement (`complaint_acknowledgement`) | Semi-auto | acknowledgement required · no human review required · not escalated · TurfAI's draft present · 0 mismatches | **Send TurfAI's acknowledgement** → `complaint.acknowledged` + resolution task (owner stays unassigned — a `system:*` actor is never made the case owner) |
| Renewal hand-off (`renewal_handoff`) | Semi-auto | stage hand-off (not a follow-up) · sendable (consented channel + draft) · not escalated · handed off to the RM (not a broker) · cadence not stopped · 0 mismatches | **Send TurfAI's draft** → `renewal.outreach_sent` + customer offer |
| SIU disposition sign-off (`siu_disposition`) | Autonomous | TurfAI's outcome `unsubstantiated` · sign-off offered · 0 mismatches | **Sign off TurfAI's disposition** → `fraud.disposition` + `fraud.cleared` |
| Settlement sign-off (`claim_signoff`) | Autonomous | a paying decision (admit / rider / partial) · Fraud & SIU cleared (TurfAI's `fraud_cleared` or the claim's fraud status) · not an early claim · not provisional · no investigation required · TurfAI's compliance sign-off passed (`compliance_signoff.routing.pass true`, decision-ready; a sign-off that still fails after the one retry, or none at all, blocks) · net payable ≤ pay-out limit · sign-off not held · no other open or settled claim on the policy (the insurer's claims records, read when the sign-off is raised and again when `fraud.cleared` releases it; a claim closed as a duplicate does not count) · 0 mismatches — 10 checks | **Sign off TurfAI's recommendation** → `claim.approved` |

Everything else is never automatic (the list on the card). A rated / exclusion / counter-offer / decline /
requirements memo, an escalated complaint, a repudiation, a substantiated disposition — all wait for their role at
every level. The autopilot also respects the task engine's own rules: when the engine refuses the decision (e.g. the
customer opted out, the sign-off is held) the task stays open with a `crm_changes` `autopilot_skipped` note.

**Where you see it.**

- **Inbox → Decided**: an **Autopilot** badge beside the status of the row.
- **Task drawer**: fact "Decided by: Autopilot"; the Decision card's badge reads **Decided by Autopilot** and the card
  says "Decided by Autopilot · Semi-auto · 8 checks passed on \<date\> — TurfAI's recommendation (Accept (standard))
  applied by the orchestrator; nothing was judged in the app." followed by the **Autopilot checks** chips (one per
  check, e.g. `decision_type standard`, `no referral`, `compliance_pass true`, `loading 0 %`, `0 system-check mismatches`).
- **Task drawer, open task**: when the level permitted the gate but the autopilot left the task to a person, the
  Decision card opens with one muted line — *"Left to you by the autopilot (Semi-auto): referred to senior ·
  compliance_pass false"* (the failing checks; "skipped — \<reason\>" when the task engine refused the decision).
- **Storyline panel** ("Show the story and every beat"): a beat the autopilot completed carries the subtitle
  *"auto-approved — TurfAI's recommendation applied by the autopilot"* (A1 memo, A2 sign-off, B acknowledgement, C
  outreach; R1's rated memo and B's substantiated disposition never do).
- **Policy 360 timeline**: the decision event (`uw.approved`, `claim.approved`, …) is listed with source
  `system:autopilot`, and the CRM change rows name `system:autopilot` as the actor.
- **Dashboards**: Underwriting, Renewals, Fraud & SIU, Claims Settlement and Complaints cards have an **Auto-approved
  share** tile — decided tasks of the pack's autopilot gate decided by `system:*` ÷ decided tasks of that gate (dashes
  until one has been decided).
- **Audit trail**: every save writes a `crm_changes` row on subject `demo:settings` (table `reference_data`, row
  `demo.settings`, actor `<role>:console.demo`, `{autonomy: {from, to}}`); an autopilot decision has `decided_by`
  `system:autopilot`, a decision note "Auto-approved by the autopilot (semi_auto): TurfAI recommended accept; …" and
  event `source` `system:autopilot`; and the task payload carries an `autopilot` stamp `{level, rule, option,
  eligible, checks, evaluated_at}` (+ `payout_limit` on a sign-off). The stamp is written whenever the level permits
  the gate — with `eligible: false` and the failing checks when a person had to decide (the Decision card's "Left to
  you by the autopilot" line; the full stamp is in the drawer's **Payload** section), and `skipped {code, message}`
  when the engine refused. At HITL nothing is stamped.

**Not retroactive.** The level is read when a TurfAI result is *mapped*. Raising it does not touch tasks already in
an inbox, and lowering it does not undo anything. The one re-evaluation: a held settlement sign-off is re-checked when
`fraud.cleared` releases it (at the level current at that moment). **Reset demo** drops the setting → HITL.

### 1.9 Sample documents

Testers do not need the storyline files on their machine. The two upload forms that take a scenario's evidence offer
it in one click:

- **Portal → Report a claim**: **Use sample documents** next to the documents list (and **Use sample email** inside
  the intimation e-mail section when the set has an e-mail — C).
  The **Add documents** form on a claim waiting for documents offers the same sets (P16.2).
- **Console → SIU investigation → Submit findings**: **Use sample documents** below the file inputs (findings file and
  evidence uploads) — only for the **SIU investigator** role; other roles see no findings samples.

**Report a death opens pre-filled.** When the Act as persona has a claim sample set, the "Report a death" fields (date
of death, cause, place, your name, relationship, phone, e-mail) open filled from that persona's sample claim form,
marked **Sample details** with a **Clear** button (back to the plain defaults: the nominee's name and a date of death the insurer already knows). Every field stays
editable and the policy selection is yours; **Use these documents** on a set fills the fields again from that set's
claim form and attaches its documents, so a storyline claim is **Use sample documents → Use these documents → Submit
claim** with nothing typed. A set that would take the upload list over 10 documents is refused with a message (remove
some of your files first); a sample e-mail never overwrites text you typed in the intimation e-mail box without asking.

The button opens "Sample documents for this customer": one card per set with its title, where it fits in the
storyline, a summary, the **Expected** outcome and the file list. **Use these documents** adds the files to the form's
upload list as ordinary entries marked **Demo sample text** — the kind (claim) or slot (findings / evidence) can still
be changed, each entry can be removed or read ("Show the sample text"), and your own files can still be added. Using
another set replaces the samples of the first one (your own files stay). On the findings form the set's relied-on
documents are pre-ticked when they are on the task (using another set or removing the set's file takes those ticks
away again; ticks you made stay), and if you also choose a findings file it replaces the sample findings (the form says
so). TurfAI reads the text exactly as if you had uploaded the file.

**Only the current customer's documents.** On the portal the list holds only the documents of the persona selected
in **Act as**; in the console, only those of the customer whose claim the open SIU investigation is about. The server
decides this from the Act as selection / the task on every request — another persona's set or file is simply "not
found" (also for downloads).

**Download to edit.** Every file has a **Download** link (a `.txt` attachment). Download it, change what you want to
test (a name, a date, the conclusion line) and upload your copy with "choose files" instead of the sample.

| Act as (portal) / task (console) | Form | Set (as shown) | Expected outcome |
|---|---|---|---|
| A2 · Karthik Iyer | Report a claim | **Clean death claim — Meena Iyer** | S6: intake fast-track, fraud low (cleared), settlement admits ₹25,00,000 + bonus − loan for the claims officer's sign-off |
| A2 · Karthik Iyer | Report a claim | **Death certificate that does not match (C2)** | C2 — run it **instead of** the clean claim, on a fresh `demo:reset`: intake may hold / ask for documents; if registered, fraud expected to find document forensics → high → SIU. Reported *after* the clean claim it is a second report of the same death — that is the separate case "C2 after the clean claim" ([§6.4](#64-claims-variations-and-negative-cases)) |
| A2 · Karthik Iyer | Report a claim | **Report the same death again (S6 duplicate)** | After the clean claim CLM-2026-000311 is registered (or further: awaiting sign-off / fraud) — click **Report this death again** first, the form is folded while 311 is open: the same five files and details again → intake links it as a duplicate of CLM-2026-000311 (letter *duplicate link*) → the claims officer's triage offers **Link as duplicate** (C3 in [§6.4](#64-claims-variations-and-negative-cases)) |
| A2 · Karthik Iyer | Report a claim | **Incomplete death claim — claim form and death certificate only (P16.2)** | C14 documents loop, on a fresh `demo:reset`: intake asks for the missing KYC / bank proof / medical attendant's certificate, or holds the claim for the claims officer, who asks for them — the claim then waits for documents on the portal |
| A2 · Karthik Iyer | Add documents (on the claim card; every claim set is offered on both forms) | **The documents asked for — KYC, bank proof, medical attendant's certificate (P16.2)** | C14: added to the same claim reference; TurfAI `claim_intake` checks it again with all five documents → registered (fast track) or a new triage task |
| B · Anita Sethi | Report a claim | **Early death claim — Vikram Sethi** | S8: intake investigate; fraud high → SIU case SIU-2026-000877; settlement held for the fraud outcome |
| C · Ramesh Narayanan | Report a claim | **Death in the grace period after revival — Lakshmi Narayanan** (incl. the intimation e-mail) | S15: in force (death in grace); fraud medium → SIU screen cleared → settled ₹16,74,300 |
| D · Sunita Kulkarni | Report a claim | **Claim on a lapsed policy — Prakash Kulkarni** | Policy not in force on the date of death → adjudicate → repudiated (not in force) |
| SIU investigation on B's claim | Submit findings | **Findings: non-disclosure substantiated** | S9: substantiated disposition with s.45 grounds → settlement re-runs → repudiation (premiums refunded) |
| SIU investigation on B's claim | Submit findings | **Findings without a conclusion line (C8)** | C8: no outcome → the SIU lead can only return the case |
| SIU investigation on background claim CLM-2026-001007 | Submit findings | **Findings: inconclusive, enquiries open** | C9: inconclusive disposition recommending an extension |

**No sample documents** (no button): **A1**, **R1**, **N1** and new sign-ups (`new-<n>`) — they have no claim in their
storylines; **Apply** and **Requirements** keep their own generated **Use a sample** documents ([§7.1](#71-working-with-the-demo)).
The constructed fraud scenario `s5_siu_findings.txt` (UNSUBSTANTIATED, C10) is not offered: no seeded customer owns
its policy (it is about claim CLM-2026-000743). Changing only the conclusion line of B's findings does not make an
unsubstantiated case: a conclusion that contradicts F1–F9 gets no outcome from TurfAI (C8's result).

The files are the repo's fixtures, unchanged (`fixtures/documents/<persona>/`, `fixtures/documents/samples/`,
`turfai/fixtures/claim_intake/docs/`, `turfai/fixtures/fraud_dispose/docs/`).

---

## 2. Roles and their tasks

The console roles come from `HUMAN_TASK_ROLES`; the task kinds, owners and options from `TASK_KINDS`
(`packages/db/src/tasks/kinds.ts`); what each option does from the decision handlers
(`packages/db/src/tasks/handlers/*.ts`). "Note (required)" means the note field must be filled for that option.
Ten console roles plus the customer (the earlier Finance, SIU and Head of Grievance Redressal desks were merged into
Collections & finance, SIU lead and Compliance & grievance). TurfAI's own routing words are unchanged — a reconciliation
exception is still "for review by finance", a dunning case is still "routed to a collections officer", an escalation
still names "the Head of Grievance Redressal" — those name the *route*; the table names the *desk* that works it.

| Role (console label) | Task kinds (inbox label) |
|---|---|
| Underwriter | Underwriting memo |
| Customer (portal) | Counter-offer · Renewal / retention offer · Revival payment |
| Servicing officer | Issuance review · Servicing escalation · Revival request |
| Collections & finance | Grace expired · Collections review · Billing check · Reconciliation exception |
| Relationship manager | Renewal hand-off |
| Claims officer | Claim triage · Settlement sign-off |
| SIU lead | SIU screen · SIU disposition sign-off |
| SIU investigator | SIU investigation |
| Complaint owner | Complaint acknowledgement · Complaint resolution |
| Compliance & grievance | Claim triage (screening hit) · Complaint escalation (whoever TurfAI escalates to) · Regulator notice sign-off |
| System (ops) | System — run failure |

### 2.1 Underwriter — "Underwriting memo" (`uw_memo`)

Raised by the `uw_assess` mapper for every assessment (first application and every requirements answer). Shows
TurfAI's memo: headline, decision and loading, rating factors with debits / credits and the manual sections they cite
(UWM-n), requirements with status (outstanding → satisfied → not required), non-disclosure flags, KYC / AML,
financial underwriting (aggregate cover, income multiple, justified sum, over-insurance), the drafted letter, and
System checks (age, aggregate cover, income multiple, justified sum, over-insurance, loading recomputed from the
manual). The option TurfAI recommends carries a small **TurfAI** marker; Rated pre-fills TurfAI's loading /
exclusions; Request requirements pre-fills TurfAI's outstanding items.

| Option (button) | Fields | Rule (refused otherwise) | What happens |
|---|---|---|---|
| **Accept (standard)** | — | no loading, exclusions or changed terms | proposal `approved` → `uw.approved` (standard) → TurfAI `policy_issue` runs |
| **Rated / exclusion** | Loading %, Exclusions (one per line) | loading > 0 and / or ≥ 1 exclusion; sum assured and term unchanged | proposal `offer_pending`; the rating engine re-prices; customer **Counter-offer** task on portal Offers. Nothing issues until the customer accepts |
| **Counter-offer** | Sum assured (₹), Term (years), optional loading / exclusions | sum assured and / or term changed; sum assured not above the proposed one | as Rated — re-priced offer to the customer |
| **Decline** | Note (required) | no terms | proposal `declined`; nothing issued |
| **Request requirements** | Requirements (one per line) | at least one item | proposal `requirements_pending`; the customer sees the list on Apply and answers with documents |

Every option emits `uw.approved {decision}`; only `standard` routes to issuance. **The customer letter** is TurfAI's
draft and is attached only when your decision is the one TurfAI wrote it for (same option as its recommendation, same
loading / exclusions / sum / term, same requirement list). Otherwise no letter goes out and the form tells you why
before you submit (e.g. "the decided terms are not the ones TurfAI's letter states: loading 75 % vs TurfAI's 50 %").
The app never writes or edits a letter.

At **Semi-auto** or above a *clean* memo — standard, no referral, no outstanding requirements, compliance pass, loading 0, no
exclusions, no over-insurance, 0 System-check mismatches — is accepted by the autopilot before it reaches the inbox
([§1.8](#18-the-autonomy-level-autopilot)); it appears under **Decided** with the Autopilot badge and issuance starts
at once. Anything else waits for you as at HITL.

### 2.2 Customer (portal) tasks

| Task | Where | Options | What happens / refusals |
|---|---|---|---|
| **Counter-offer** (`counter_offer`) | Offers → "Revised offer from underwriting" | **Accept** / **Accept and pay ₹…** → **Confirm acceptance**; **Decline** → **Confirm decline** (optional reason) | Accept → `uw.offer_accepted` → TurfAI `policy_issue`. If the first premium was not paid at Apply, accepting pays the quoted premium; if a smaller amount was paid at Apply, a top-up is collected; if more was paid than the revised premium, accepting is refused ("a refund of the excess is not supported"). Decline → proposal `offer_declined` → `uw.offer_declined`; nothing else happens |
| **Renewal / retention offer** (`renewal_offer`) | Offers ("We'd like to help you keep your cover" / renewal reminder) | **Accept** / **Decline** (+ optional note) | `renewal.offer_accepted` / `renewal.offer_declined` + an RM follow-up hand-off. Changes no billing or policy status. Refused once the touch is closed / superseded |
| **Revival payment** (`revival_payment`) | Payments → **Pay ₹… and revive** | pay (channel) | Settles every arrear due, the interest, sets the policy `revived` with last revival date = today → `policy.revived`; billing resumes. Due by the quote's valid-to date |

Only the customer who owns the proposal / policy can answer.

### 2.3 Servicing officer

**Issuance review** (`issuance_review`) — raised by `policy_issue` when TurfAI flags the issuance `non_standard`, or
any recomputed issuance fact (policy number, sum assured, term, premium, mode, grace days, loading, exclusions,
issue / RCD / free-look / maturity dates, status vs first premium receipt) disagrees with the contract.

| Option | Rule | What happens |
|---|---|---|
| **Confirm issuance** | the CRM holds a confirmed first premium with a receipt; the stored plan's next due and free-look end are not in the past | `issuance.confirmed`, then the policy is written with the **insurer's recomputed plan** (never TurfAI's values): policy, nomination, mandate, documents, billing schedule, `policy.issued` |
| **Reject** | Note (required) | proposal `issuance_rejected` → `issuance.rejected`; nothing issued. The plan says "re-run the issuance from the current date", but there is no UI to re-run it |

**Servicing escalation** (`servicing_review`) — raised by `policy_service` when TurfAI declined a question (out of
scope), held a change, a generated certificate / applied change disagrees with the insurer's figures, or the CRM
could not write an applied change. Options depend on the case:

| Option | When offered | What happens |
|---|---|---|
| **Reply to the customer** | always | "Your reply to the customer (required)" goes to portal Ask beside TurfAI's message → `servicing.resolved (replied)` |
| **Apply the change** | a held change with a CRM record action, and TurfAI's values equal the customer's request | writes TurfAI's validated change (nominee, address, contact, premium mode, free-look cancellation) → `servicing.resolved (applied)`; refused if the values cannot be written or differ from the request |
| **Decline** | always | "Reason, shown to the customer (required)" → `servicing.resolved (declined)`; nothing changes |

**Revival request** (`revival_review`) — every revival request is held for the officer (TurfAI never revives).

| Option | Rule | What happens |
|---|---|---|
| **Approve the revival** | policy still `lapsed`; quote not expired (valid 5 days); the billing schedule still gives the same total | request `approved` → `servicing.revival_approved` + customer **Revival payment** task for the quoted total. Otherwise refused ("the revival quote expired…", "the billing schedule changed since the quote…") — the customer must ask again |
| **Decline the revival** | "Reason, shown to the customer (required)" | `servicing.resolved (revival declined)`; policy stays lapsed |

### 2.4 Collections & finance — collections tasks

**Collections review** (`collections_review`) — raised by `billing_dun` when TurfAI routes the case to a
collections officer (hardship, final stage, past grace). One open per due.

| Option | Fields / rule | What happens |
|---|---|---|
| **Agree a payment arrangement** | Note (required); optional "Pay by" date, on or before the grace end | arrangement recorded on the due; automated reminders stay stopped → `billing.hardship_resolved (arrangement)` |
| **Resume reminders** | refused if the due is paid or no ladder step is left before the grace end | dunning restarts at the next ladder step; on that date the clock fires `dunning.step_due` → TurfAI `billing_dun` again |
| **Close the case — no change to the policy** | Note (required) | `billing.hardship_resolved (closed)`; nothing changes |

**Grace expired** (`grace_expiry`) — raised by the clock the day after grace ends with the premium unpaid.

| Option | Rule | What happens |
|---|---|---|
| **Lapse the policy** | refused when the due has been paid meanwhile, or the policy is not in force / in grace / revived | policy `lapsed` as of the grace end (last covered day), due `lapsed`, open collections reviews for the due cancelled → `policy.lapsed`; open renewal touches are superseded |
| **Close the case — no change to the policy** | Note (required); **only** when the due is already paid | no event |

**Billing check** (`billing_review`) — raised when TurfAI's billing contract disagrees with the
insurer's recomputed facts (amount, grace end, due date, mandate validity, next dunning date, …) or the rail refused a
debit. The CRM already used the insurer's values. Option **Acknowledge** — records that you saw it; no event.

### 2.5 Collections & finance — "Reconciliation exception" (`reconciliation_exception`)

Raised by `billing_reconcile` for every feed line TurfAI flagged (TurfAI marks it "for review by finance"; the same
Collections & finance desk works it). Nothing is applied to a due until that decision.
Options are narrowed to the line's state:

| Option | Offered for | What happens |
|---|---|---|
| **Collect the balance** | short | part payment stays against the due, which stays open for the balance (collection / reminders carry on) |
| **Write off the shortfall** | short | Note (required); due settled with the part payment, shortfall written off → also `payment.received` |
| **Apply to a premium due** | unmatched (and held lines) | "Premium due id (the due you traced the remitter to)"; the due must be unpaid and its outstanding must equal the cash → due settled, `payment.received` |
| **Refund** | any | refund instruction `RFND-<bank ref>` for the excess (over) or the whole credit |
| **Hold in suspense** | over / duplicate / unmatched | Note (required); amount parked in premium suspense |
| **Confirm the rail debit** | held lines whose debit reference names a rail debit for the same amount still awaiting bank confirmation | the rail payment is marked bank-confirmed and the held entry merged into it |

Every decision emits `reconciliation.resolved`. A line whose figures the insurer could not reproduce is **held**
(cash recorded unapplied) with the differences listed.

### 2.6 Relationship manager — "Renewal hand-off" (`renewal_handoff`)

Raised by `renewal_action` for every renewal (cadence) or retention (lapse-risk) touch TurfAI hands off, unless TurfAI
stops the cadence. The task shows TurfAI's priority, outreach draft, channel, cross-sell and System checks.

| Option | Rule | What happens |
|---|---|---|
| **Send TurfAI's draft** | only offered when TurfAI's channel is one the customer consented to; refused if the customer opted out of renewal contact | the draft goes out exactly as written; your optional note travels beside it → `renewal.outreach_sent` + customer **Renewal / retention offer** on portal Offers |
| **Record the outcome** | Outcome: Renewed — premium paid · Will pay before the deadline · Payment arrangement discussed (collections officer decides) · Will revive after lapse · Customer declined · No response · Not contactable | touch `closed` → `renewal.outcome`; an unanswered customer offer is cancelled |
| **Withhold (do not send)** | "Why it is withheld (required)" | nothing sent → `renewal.outcome (withheld)` |

When the customer answers the offer, a **follow-up** hand-off arrives with only **Record the outcome**. A touch is
closed automatically (superseded, tasks cancelled) when the premium is collected / paid, or the policy lapses or is
revived. Renewals never change billing or policy status. At **Semi-auto** or above the autopilot sends TurfAI's draft
itself when the hand-off is sendable, not escalated, addressed to the RM (not a broker), the cadence is not stopped and
System checks found 0 mismatches ([§1.8](#18-the-autonomy-level-autopilot)); follow-ups always wait for the RM.

### 2.7 Claims officer

**Claim triage** (`claim_triage`) — two forms:

- *Triage exception* (gate): TurfAI held the claim, asked for requirements, flagged a duplicate / beneficiary /
  screening issue, did not register it, or the insurer's recomputed facts disagree. Claim status `triage_review`.
  Owned by **Compliance** instead when TurfAI routes it to the compliance officer (AML / PEP / sanctions hit).
  The task carries the insurer's other claims on the policy (`insurer_open_claims` / `insurer_settled_claims`: status,
  date of death, claimant, reported on, net payable and TurfAI's settlement recommendation when known), and its title
  names a possible duplicate first — "Triage exception — \<name\> · possible duplicate of CLM-… (status)" — whether
  TurfAI named it or only the insurer's records show it open. A report on a policy where a claim is already
  **settled** always comes here, even when TurfAI's routing is clean (TurfAI is only sent the *open* claims): title
  "possible duplicate of settled claim CLM-… (Settled)", hold reason "… — on the insurer’s claims records; TurfAI
  checks open claims only"; it can be registered (with the note), sent for documents, or linked as a duplicate of the
  settled claim (the officer's decision on the insurer's records — TurfAI's INT-5 is unchanged).
- *Notice*: TurfAI registered the claim on a non-clean route — `investigate` ("Early claim registered with fraud
  priority") or `adjudicate` ("Claim registered for adjudication (not in force on the date of death)"). Fraud scoring
  and settlement already started.

| Option | Offered | What happens |
|---|---|---|
| **Register the claim** | triage exception | claim `registered` → `claim.triage_confirmed` + `claim.registered` → fraud scoring and settlement start. When another claim on the policy is open or settled, the form warns (box "Another claim on this policy") "Another claim on this policy is open: CLM-… (status) — say why it is a separate claim." (for a settled one: "Another claim on this policy was settled: CLM-… — say why it is a separate claim.") and the note **Why it is a separate claim (required)** is required; the claims recorded are kept as `registered_despite_open_claim` on the change log and the event |
| **Ask for documents** | triage exception | "Documents still needed — shown to the nominee (required)" → claim `requirements_pending`; portal "More documents needed" with your note, TurfAI's outstanding documents and an **Add documents** form. When the nominee adds them (same claim reference, document ids continue the claim's sequence): claim back to `intimated` → `claim.documents_received` → TurfAI `claim_intake` re-runs with all the claim's documents and re-triages it — registered (fraud ∥ settlement start) or a **new** Claim triage task; the acknowledgement letter is replaced. No second claim is created ([§3.4](#34-claims-intake--fraud--settlement--siu--sign-off)) |
| **Link as duplicate** | when TurfAI's routing names `duplicate_of` **or** the insurer's claims records show another **open or settled** claim on the policy | claim `duplicate`, linked to the claim shown under "Links to CLM-…" — TurfAI's duplicate (while it is still open or settled), else the insurer's open claim, else a settled one ("— already paid on the insurer's records"); with more than one, pick it in **Claim to link to** (TurfAI's preselected). The targets are the claims table *now*, not as at the task was raised. Recorded as `linked_from` `turfai` / `insurer_records` and `linked_claim_status`. Re-checked on submit (TurfAI's target too): refused when the claim is not on this policy, or ended unpaid since (repudiated / duplicate / withdrawn / closed — "… it ended unpaid and is no longer a claim to link to"). With no target left the form says "No other open or settled claim on this policy to link to now — register this claim or ask for documents." The nominee's portal shows the claim as "Linked to your earlier claim" |
| **Acknowledge** | notice | nothing changes; records that you saw TurfAI's routing |

**"Other claims on this policy"** (box at the top of the triage drawer; chip **Other claims**). Display only — the
duplicate judgement stays TurfAI's (INT-5); the box puts the insurer's own claims records beside it:

- read from the claims table when the drawer opens (not as at the task was raised): one row per other claim on the
  policy that is open or settled *now*: reference (→ its Policy 360), current status, **Date of
  death**, **Claimant**, **Reported on**, **TurfAI recommends** / **Decision** and **Net payable** when known, a
  **TurfAI's duplicate** badge on the claim TurfAI linked, and **Open its task** when that claim has an open task
  (e.g. its Settlement sign-off).
- a claim the task named when it was raised (or TurfAI's `duplicate_of`) that is no longer open or settled: a row with
  its status now and "no longer open — not a claim to link to (was … when this task was raised)"; a TurfAI
  `duplicate_of` the insurer has no claim for on this policy: "Not on this policy’s claims records". When the records
  changed since, a line "Shown as the claims records are now. When this task was raised they showed: …".
- TurfAI linked the claim: "TurfAI linked this claim as a duplicate of CLM-…" followed by its INT-5 detail.
- The insurer's records show an open claim but TurfAI's `duplicate_of` is empty: a red warning "The insurer's records
  show an open claim on this policy — TurfAI did not link it." with TurfAI's own words underneath, verbatim (the INT-5
  check detail, a failed INT-2 check, triage reasons citing a duplicate — e.g. a different date of death on the
  certificate), or "TurfAI gave no reason on its duplicate check.", then "Compare the claims below before you decide."
- The **Register the claim** warning and the link targets use the same live list.
- The **Claim** block's **Duplicate of** shows both values: "TurfAI \<ref | —\> · insurer \<refs | —\>" (e.g.
  "TurfAI — · insurer CLM-2026-000311").

On a **Settlement sign-off** the same box appears when another claim on the policy exists, read live from the claims
table: an open one adds "Another claim on this policy is open — check it is not the same death before you sign this
one off."; claims already closed as duplicates of this one are listed as "CLM-…-2 · closed as a duplicate of this
claim". At **Autonomous** such a sign-off is never auto-approved: the autopilot's check "other claim on the policy:
CLM-… (status)" fails and the card says it was left to you ([§1.8](#18-the-autonomy-level-autopilot)).

**Settlement sign-off** (`claim_signoff`) — raised by `claim_settle` with TurfAI's recommendation (decision,
valuation lines, letter, compliance checklist), the insurer's own figures and the claim's TAT clock
([§3.7](#37-the-claim-tat-clock-and-penal-interest)). Due on the clock start + 15 days (45 when investigation is
required) — the clock start is the last document's date, not the intimation. Three options — the officer approves or
overrides, always:

| Option | Note | What happens |
|---|---|---|
| **Sign off TurfAI's recommendation** | optional | signs off *whatever TurfAI recommended* (table below). The form states **Written to the claim: ₹…** (TurfAI's `routing.net_payable` — the figure written to the claim); when the insurer's recomputed net payable differs, a red line beside it: "The insurer's recomputed net payable is ₹… (policy admin + billing) — TurfAI's figure is the one written." Under it the penal interest: "Penal interest: none — Day 9 of 15 · 6 days left." or, once the clock is breached, "Penal interest due with the payment: ₹… — Breached by N days (…); (bank rate + 2 %) × the written amount × N days / 365, recorded with the sign-off." When the task carries **System-check mismatches**, a red box lists them (`fact`: TurfAI *x*, insurer *y*) with the checkbox "I have checked the N system-check mismatches and sign off TurfAI's figures anyway (required)" — the server refuses an approve with mismatches without it, and records `acknowledged_mismatches` |
| **Ask for documents** | "Documents the nominee must send — shown on the portal (required)" | overrides TurfAI: claim `settlement_requirements` (decision `requirements`) → `claim.settlement_requirements`. Nothing is paid and TurfAI's settlement letter is **not** sent; the portal shows your note ("More documents needed") with **Add documents**. When the nominee adds them: claim `under_assessment` → `claim.settlement_reassess` (documents_received) → TurfAI `claim_settle` re-runs with all the claim's documents → a new sign-off. The TAT clock is paused meanwhile and restarts from the last document |
| **Return for re-assessment** | "Why you disagree — sent to TurfAI with the re-assessment (required)" | you disagree with TurfAI: claim `under_assessment` → `claim.settlement_reassess` (officer_return, note, by) → TurfAI `claim_settle` re-runs with `officer_review` (your note, who returned it, when, TurfAI's previous recommendation). TurfAI must address the note in its synthesis (the first rationale point starts "Officer's note: …"), but the settlement rules still apply — the note is context, not a rule override, and the letter never mentions it. The new sign-off shows "Re-assessed on the officer's return (\<you\>, \<date\>): \<note\>" above TurfAI's recommendation |

Every option records the actor and the note on the change log and the event. **Sign off TurfAI's recommendation**
does:

| TurfAI decision | After sign-off |
|---|---|
| admit · admit with rider · partial (suicide clause) | claim `settled` for the net payable → `claim.approved`; settlement letter + discharge voucher filed for the nominee; policy `claimed`, mandate cancelled, unpaid dues cancelled |
| repudiate | claim `repudiated` → `claim.repudiated`; decision letter with grievance rights filed for the nominee; policy `terminated`, mandate cancelled, unpaid dues cancelled |
| requirements | claim `settlement_requirements` → `claim.settlement_requirements` (requirements letter); the nominee answers with **Add documents** → settlement re-runs |
| investigate | **never approvable** — "Settlement held — awaiting the fraud outcome"; settlement re-runs when fraud clears or SIU grounds arrive |

**The sign-off is held** (the options are disabled, a "Sign-off held" box explains why) by `settlementSignoffHold`:

| TurfAI decision | Held while fraud status is | Released by |
|---|---|---|
| admit / admit with rider / partial suicide clause | anything except cleared · unsubstantiated · released | `fraud.cleared` (fraud score clear, SIU screen clear, unsubstantiated or released disposition) |
| repudiate | pending · referred · under investigation · inconclusive | a decided fraud outcome |
| requirements | never held | — |
| investigate | always | a new settlement run |

A held sign-off refuses **every** option (Ask for documents and Return for re-assessment too) — Fraud & SIU
clearing releases it. A newer settlement run cancels the open sign-off ("a newer settlement assessment … superseded
this sign-off").

At **Autonomous** the autopilot signs off a *clean paying* recommendation itself — admit / rider / partial, Fraud &
SIU cleared, not an early claim, not provisional, no investigation required, TurfAI's compliance sign-off passed, net
payable within the pay-out limit, not held, 0 System-check mismatches ([§1.8](#18-the-autonomy-level-autopilot)). A held sign-off is re-evaluated when
`fraud.cleared` releases it. Repudiations, requirements and anything above the limit always wait for the officer. The
autopilot only ever uses **Sign off TurfAI's recommendation** — never Ask for documents or Return for re-assessment.

### 2.8 SIU lead — "SIU screen" (`siu_screen`)

Raised when `fraud_score` puts the claim in the **medium band** (`human_review`), or TurfAI said clear but the
insurer's recompute disagreed.

| Option | Field | What happens |
|---|---|---|
| **Clear the claim** | "Screening note — why the claim is cleared (required)" | fraud status `cleared` → `fraud.screen_decided` + `fraud.cleared`: the held sign-off is released, or settlement re-runs after `investigate` |
| **Refer to SIU investigation** | "Referral note for the investigator (required)" | SIU case `SIU-<yr>-<n6>` opened, fraud status `referred` → `fraud.referred` → SIU investigator task; settlement stays held |

### 2.9 SIU investigator — "SIU investigation" (`siu_investigation`)

Raised on `fraud.referred` (from `fraud_score` high band or an SIU screen referral), and again when the SIU lead
extends or returns the case. One option, **Submit findings**:

- "Findings document (.txt, required)", "Evidence uploads (.txt, optional, up to 5)" — plain text, ≤ 50,000 characters
  each; tick the claim / proposal documents relied on; optional "Note to the SIU lead".
- Files the findings and evidence on the case → case `findings_submitted` → `siu.findings_submitted` → TurfAI
  `fraud_dispose` drafts the disposition.

**The findings document must end with a line that starts `Investigator conclusion:`** followed by SUBSTANTIATED,
UNSUBSTANTIATED or INCONCLUSIVE (the disposition rule FDR-1: the human conclusion governs). Recommended shape —
numbered lines TurfAI can cite, then the conclusion (the B set **Findings: non-disclosure substantiated** under
**Use sample documents** — [§1.9](#19-sample-documents); file `turfai/fixtures/fraud_dispose/docs/b_siu_findings.txt`,
abridged):

```text
TURFLIFE SIU — INVESTIGATOR FINDINGS, CASE SIU-2026-000877 (claim CLM-2026-000877, policy POL-LIFE-2025-000877)
Submitted by: SIU investigator; submitted on 18-11-2026
F1 Policy record verified: risk commencement 10-09-2025, no revival, 14 monthly premiums paid by NACH. Death on 03-11-2026 is 13.8 months after risk commencement.
F2 Cause of death verified with the treating cardiologist: acute anterior wall STEMI; death natural.
F3 Treating physician's statement: under his care from 14-03-2020 for essential hypertension and type 2 diabetes, continuous medication.
F6 Proposal (signed 02-09-2025): C7 chronic illness "No", C12 current medication "No".
F8 Medical officer opinion: the conditions were material to the risk; disclosure would have required an APS and a rated acceptance.
Investigator conclusion: SUBSTANTIATED — material non-disclosure at proposal. Recommend that repudiation grounds under section 45 be passed to Claims Settlement for its decision.
```

Other examples: **Findings without a conclusion line (C8)** (B) and **Findings: inconclusive, enquiries open** (the
background claim CLM-2026-001007) under **Use sample documents**; `s5_siu_findings.txt` (UNSUBSTANTIATED) is only in the
repo folder (not offered — §1.9). Without an explicit conclusion line (or with one that contradicts the findings)
TurfAI returns no outcome and the lead can only return the case. Each sample file has a **Download** link to edit it
and upload your own version.

### 2.10 SIU lead — "SIU disposition sign-off" (`siu_disposition`)

Raised by `fraud_dispose` with TurfAI's disposition (outcome, basis, per-indicator findings, s.45 repudiation grounds,
underwriting feedback). Due 2 days after the draft. Options depend on TurfAI's outcome:

| TurfAI outcome | Options offered |
|---|---|
| substantiated / unsubstantiated | **Sign off TurfAI's disposition** · **Return to the investigator** |
| inconclusive | **Release to settlement** · **Extend the investigation** · **Return to the investigator** |
| no conclusion | **Return to the investigator** only |

| Option | Note | What happens |
|---|---|---|
| **Sign off TurfAI's disposition** | optional | case closed with the outcome; claim fraud status = outcome → `fraud.disposition`. *Substantiated*: settlement re-runs with the repudiation grounds; TurfAI's underwriting feedback item is filed for Underwriting. *Unsubstantiated*: also `fraud.cleared` |
| **Release to settlement** | required | (inconclusive only) fraud status `released` → `fraud.disposition` + `fraud.cleared` |
| **Extend the investigation** | required | case `extended` → `siu.investigation_extended` → new investigator task (due on TurfAI's extend-until date) with the open lines |
| **Return to the investigator** | required | case `returned` → `siu.findings_returned` → new investigator task (due in 7 days) with your note; no conclusion recorded |

(Return / Extend failed with "The task could not be updated" before P12.8; fixed.)

At **Autonomous** the autopilot signs off an **unsubstantiated** disposition itself (sign-off offered, 0 System-check
mismatches — [§1.8](#18-the-autonomy-level-autopilot)); substantiated, inconclusive and no-conclusion drafts always
wait for the SIU lead.

### 2.11 Complaint owner (grievance officer)

**Complaint acknowledgement** (`complaint_acknowledgement`) — raised when TurfAI says an acknowledgement is required.
Option **Send TurfAI's acknowledgement**: sends TurfAI's draft verbatim, DL-ACK met today, you become the case owner,
status `acknowledged` → `complaint.acknowledged` + a **Complaint resolution** task. Refused if a newer TurfAI run
superseded the task. Also allowed on a case escalated before its acknowledgement went out (then no resolution task —
the escalation task carries the case). At **Semi-auto** or above the autopilot sends the acknowledgement itself when
TurfAI requires one, asks for no human review, did not escalate, drafted the text, and System checks found 0
mismatches ([§1.8](#18-the-autonomy-level-autopilot)); the resolution task then arrives with no owner yet — the
complaint owner who works it becomes the owner.

**Complaint resolution** (`complaint_resolution`) — raised after the acknowledgement (or directly when none is
required); due on the primary clock.

| Option | Fields | What happens |
|---|---|---|
| **Record the resolution** | "Decision on the complaint": Decision upheld, with reasons · Decision reversed · Partly reversed · Explained / information given; "Written response sent to the complainant (required)" | status `resolved`, primary clock met → `complaint.resolved` → TurfAI re-checks the case (within TAT). The complainant sees your response verbatim. Every other open case task is cancelled |
| **Add a case note** | "Case note (required)" | note on the case → `complaint.note_added`; the same task comes back |

The pack never resolves a complaint — only this human decision does.

### 2.12 Compliance & grievance — "Complaint escalation" (`complaint_escalation`)

Raised when TurfAI's at-risk re-check escalates the case before the breach; it supersedes the owner's resolution
task. Always lands with the Compliance & grievance desk; the task title, case panel and portal note name whoever TurfAI
escalated to (Head of Grievance Redressal, Head of Compliance, the owner's team lead). Same two options as Complaint resolution (**Record the resolution**, **Add a case note**).

### 2.13 Compliance & grievance — the other tasks

- **Claim triage** on a screening hit (see §2.7).
- **Complaint escalation** (§2.12).
- **Regulator notice sign-off** (`complaint_notice_signoff`) — raised when TurfAI drafted a regulator / Ombudsman
  notice. Option **Sign off TurfAI's notice draft** (optional note): the draft is signed off verbatim →
  `notice.signed_off`. Filing it is outside the demo; Compliance cannot return a draft (K16).

### 2.14 System (ops) — "System — run failure" (`system`)

Raised when a TurfAI run still failed after its one automatic re-trigger (invalid contract or failed run), or when
writing a valid contract into the CRM failed.

| Option | What happens |
|---|---|
| **Retry** | mapper failure → the same valid run is mapped again; otherwise a fresh TurfAI run with the same inputs (same as-of date) |
| **Dismiss** | closes the task; nothing else |

---

## 3. End-to-end flows

Legend for the step tables: **Who** = portal persona, console role or *system* (worker / clock / mock); **TurfAI** =
the workflow that runs (40–250 s); **Event** = the outbox event that moves the flow on.

### 3.1 New business: apply → underwriting → issuance

```mermaid
flowchart TD
  A[Customer: Apply or New customer sign-up] -->|proposal.submitted| UW[TurfAI uw_assess]
  UW --> M[Underwriter: Underwriting memo]
  UW -->|Semi-auto or above, clean standard memo, 0 mismatches| AP[Autopilot: Accept standard]
  AP --> PI[TurfAI policy_issue]
  M -->|Accept standard| PI
  M -->|Rated / exclusion or Counter-offer| CO[Customer: Counter-offer on Offers]
  M -->|Decline| DEC[Proposal declined]
  M -->|Request requirements| RQ[Customer: Apply - Send to underwriting]
  RQ -->|proposal.requirements_submitted| UW
  CO -->|Accept| PI
  CO -->|Decline| OD[Offer declined - end]
  PI -->|clean, in force| ISS[Policy issued - mandate, schedule, documents]
  PI -->|non_standard or mismatch| IR[Servicing officer: Issuance review]
  PI -->|pending_first_premium| PFP[Proposal waits for first premium]
  IR -->|Confirm issuance| ISS
  IR -->|Reject| REJ[Issuance rejected - end]
```

| # | Who | What | TurfAI | Event | Result |
|---|---|---|---|---|---|
| 1a | Portal persona (A1 / R1) | **Apply**: pre-filled draft; tick the documents on file to send and / or upload; optionally **Pay the first premium now (₹…)**; accept the declaration; **Submit application** | — | `proposal.submitted` | proposal `submitted`; AML screening of the applicant (mock) |
| 1b | New customer | **Act as → New customer…** → "Your details" (**Fill sample details** for a fictional applicant) → **Sign up and start my application** → Apply. Unanswered health questions show as "Choose…" | — | — | customer `CUST-9NNNNN`, draft `PRP-<year>-9NNNNN`, mandate request |
| 1c | Portal | Documents: plain-text uploads only; choose what each file is; **No files? Use a demo sample written from your details:** **KYC**, **ITR / Form 16**, **Medical exam report**, **Lab report** (labelled "Demo sample text"). If no proposal form is sent, one is generated from the answers | — | — | files `DOC-<PRP>-APP<n>-<KIND>` |
| 2 | system | underwriting assessment | `uw_assess` | → `uw.recommendation_ready` | proposal `uw_review`; **Underwriting memo** for the underwriter |
| 2a | Autopilot (autonomy level Semi-auto or above, [§1.8](#18-the-autonomy-level-autopilot)) | a *clean* standard memo (no referral, no requirements outstanding, compliance pass, loading 0, no exclusions, no over-insurance, 0 System-check mismatches) is accepted in the same transaction | — | `uw.approved {standard}` (source `system:autopilot`) | memo **Decided by Autopilot**; continues at step 6 with no console touch |
| 3 | Underwriter | decide (see §2.1) | — | `uw.approved {decision}` | see below |
| 3a | | **Accept (standard)** | `policy_issue` | | proposal `approved` |
| 3b | | **Rated / exclusion** / **Counter-offer** | — | | proposal `offer_pending`; rating engine re-prices; customer **Counter-offer** |
| 3c | | **Decline** | — | | proposal `declined` |
| 3d | | **Request requirements** | — | | proposal `requirements_pending` |
| 4 | Customer (Offers) | **Accept (and pay ₹…)** → **Confirm acceptance**, or **Decline** → **Confirm decline** | `policy_issue` on accept | `uw.offer_accepted` / `uw.offer_declined` | `offer_accepted` → issuance; `offer_declined` → end |
| 5 | Customer (Apply, "More information needed") | tick "Documents on file for this application that were not sent with it" and / or upload ("Or upload new documents", samples available) → **Send to underwriting** | `uw_assess` again (all documents sent so far) | `proposal.requirements_submitted` | proposal `submitted` → new memo task (the old one stays decided). One answer per request |
| 6 | system | issuance | `policy_issue` | | three routes — below |
| 6a | | clean + first premium received | | `policy.issued` | policy in force; nomination, mandate, policy schedule / certificate documents, first premium receipt, billing schedule |
| 6b | | TurfAI `non_standard`, or any recomputed fact differs | | | proposal `issuance_review` → **Issuance review** (servicing officer): **Confirm issuance** → issued with the recomputed plan; **Reject** → `issuance_rejected` |
| 6c | | TurfAI `pending_first_premium` and no mismatch | | | proposal `pending_first_premium`; nothing issued (no portal path to pay late — K16) |

Policy numbers: A1 `POL-LIFE-2026-000101`, R1 `POL-LIFE-2026-000102`, sign-ups `POL-LIFE-<year>-9NNNNN`. Risk
commences on the issue date; free-look end = RCD + 30 days; next due = RCD + one premium period.

**What TurfAI's rules imply** (manual `turfai/fixtures/reference/uw_manual.json`):

- **UWM-10 KYC in all cases**: no KYC document (Aadhaar / PAN) on file → `requirements` (a clear AML screening does
  not replace it). With a UWM-9 referral the referral stays and KYC is listed outstanding.
- **UWM-11** every requirement is `satisfied`, `outstanding` or `not_required` (a requirement whose trigger does not
  apply is never "satisfied").
- **UWM-2 non-medical limits** (inclusive): age 18–35 ₹1,00,00,000 · 36–45 ₹40,00,000 · 46–55 ₹20,00,000 · 56–65
  none. Above → medical examination + lab panel required.
- **UWM-7 financial proof**: sum proposed above ₹50,00,000 → latest 2 years' ITR / Form 16. Justified sum = income
  multiple (25 / 20 / 15 / 10 by age band) × annual income; aggregate cover above it = over-insurance → counter-offer
  or more proof.
- **UWM-1** rating starts at 100 %; loading = total − 100 rounded up to 25; 101–150 mild, 151–250 substandard.
  **UWM-9** suspected non-disclosure → refer to the medical underwriter, never standard. **UWM-8** aggregate above
  ₹2,00,00,000 retention → reinsurance referral.

`refer` and `postpone` recommendations have no matching button: the underwriter picks the decision (a TurfAI
requirements letter can go with **Request requirements** on a refer).

### 3.2 Billing: collection, dunning, grace, lapse, reconciliation

```mermaid
flowchart TD
  D[Clock: premium.due] --> BC[TurfAI billing_collect]
  BC -->|debit| RAIL[Mock rail presents the debit]
  BC -->|block_remandate| F1[collection.failed - mandate blocked]
  RAIL -->|success| OK[Due collected]
  RAIL -->|failed| F2[collection.failed]
  F1 --> DUN[TurfAI billing_dun]
  F2 --> DUN
  DUN -->|next step on ladder| STEP[Clock: dunning.step_due - retry or reminder]
  STEP --> DUN
  DUN -->|human_route| CR[Collections & finance: Collections review]
  DUN -->|lapse_risk_signal| LR[billing.lapse_risk to TurfAI renewal_action]
  H[Customer: I'm having difficulty paying] --> DUN
  P[Customer: Pay now] -->|payment.received| DUN
  GE[Clock: grace expired, unpaid] --> GX[Collections & finance: Grace expired]
  GX -->|Lapse the policy| LAP[Policy lapsed]
  FEED[Clock: settlement_feed.arrived] --> REC[TurfAI billing_reconcile]
  REC -->|matched| SET[Due settled / bank-confirmed]
  REC -->|short / over / duplicate / unmatched| FX[Collections & finance: Reconciliation exception]
```

| # | Who | What | TurfAI | Event | Result |
|---|---|---|---|---|---|
| 1 | Clock | premium due date reached (persona policies only, by default) | `billing_collect` | `premium.due` | TurfAI bills the instalment and instructs `debit` or `block_remandate` |
| 2 | Mock rail | presents the debit only if the contract is valid and the mandate is valid (active, not expired, same policy). Outcome is scenario-driven: only C's 2026-11-15 attempts 1 and 2 fail (insufficient funds); everything else succeeds | — | `collection.succeeded` / `collection.failed` | success → due `collected`, payment row; failure → dunning |
| 3 | system | mandate blocked (missing / expired / inactive / mismatch) | — | `collection.failed (mandate_blocked)` | dunning stage 0 with the re-mandate path; Payments pre-selects **Re-register mandate** |
| 4 | system | dunning stage | `billing_dun` | | due status `in_dunning` / `escalated` / `lapsed_risk` / `collected`, next action on the ladder, customer message; policy `in_grace` while unpaid inside grace |
| 5 | Clock | ladder step (monthly: day 0 failed-debit notice, day 5 retry, day 10 reminder with pay link, day 13 final reminder, day 15 grace-end review; other modes 0 / 7 / 15 / 25 / 30) | `billing_dun` (after the rail's retry result on a retry step) | `dunning.step_due` | next stage |
| 6 | Customer | **I'm having difficulty paying** → "Tell us what has happened" → **Send** | `billing_dun` | `customer.hardship_reported` | TurfAI flags hardship; typically routes to a collections officer and signals lapse risk |
| 7 | Collections & finance | **Collections review** (§2.4) | — | `billing.hardship_resolved` | arrangement / reminders resumed / closed |
| 8 | system | TurfAI's lapse-risk signal | `renewal_action` (trigger lapse_risk) | `billing.lapse_risk` | RM **Renewal hand-off** (retention) — §3.3 |
| 9 | Customer | **Pay ₹… now** (method) → **Pay ₹…** | `billing_dun` if the due was in dunning | `payment.received` | due `paid`, policy back to `in_force`; dunning closes the case; open renewal touches superseded |
| 10 | Customer | **Re-register mandate** → method, "Account, UPI ID or card (masked)" → **Register mandate** | `billing_collect` (if a due is open) | `mandate.registered` | the unpaid due is presented on the new mandate |
| 11 | Clock | day after grace end, unpaid | — | `billing.grace_expired` | **Grace expired** task |
| 12 | Collections & finance | **Lapse the policy** | — | `policy.lapsed` | policy `lapsed` (lapsed on = grace end); revival possible later (§3.3) |
| 13 | Clock | 2026-12-02 bank settlement feed | `billing_reconcile` (batches of ≤ 8 lines) | `settlement_feed.arrived` (`settlement_feed.next_batch` for the rest) | matched lines settle / bank-confirm dues; every flagged line → **Reconciliation exception** (TurfAI: "for review by finance") for the Collections & finance desk; arithmetic disagreements → held line / **Billing check** |

Any disagreement between a billing contract and the insurer's recompute → **Billing check** (`billing_review`); the
CRM keeps the insurer's values.

### 3.3 Renewals / retention and servicing

**Renewals / retention**

| # | Who | What | TurfAI | Event | Result |
|---|---|---|---|---|---|
| 1 | Clock | renewal window opens (monthly: within 7 days of every due — so persona policies get a touch each month, e.g. A1 from 2026-11-24 for the 12-01 due) | `renewal_action` (cadence) | `renewal.window_open` | `renewal_actions` row; unless TurfAI stops the cadence → RM **Renewal hand-off** |
| 1b | system | lapse-risk signal from dunning | `renewal_action` (lapse_risk, with the lapse context) | `billing.lapse_risk` | retention hand-off, due by the lapse deadline (grace end) |
| 2 | RM | **Send TurfAI's draft** / **Record the outcome** / **Withhold (do not send)** | — | `renewal.outreach_sent` / `renewal.outcome` | customer offer on portal Offers (when sent) |
| 3 | Customer | Offers → **Accept** / **Decline** | — | `renewal.offer_accepted` / `_declined` | RM follow-up hand-off (Record the outcome only) |
| 4 | system | premium collected / paid, policy lapsed or revived | — | — | open touch `superseded`; its tasks cancelled |

A newer touch on the same policy supersedes an older one still open.

**Servicing (portal Ask about my policy)**

Pick the **Policy** and **What do you need?** — A question about my policy · Change my details or nominee
(endorsement) · A premium-paid certificate · Return my policy in the free-look period · Revive a lapsed policy —
fill the fields (for an endorsement **What to change**: Nominee · Address · Phone / e-mail · How often I pay (premium
mode); nominee shares must total 100 %; a certificate asks the **Financial year**), optionally attach your documents
→ **Ask** / **Send request**. Event `servicing.requested` → TurfAI `policy_service` (Request Resolver, then the Q&A
agent or the Endorsement & Service agent).

| TurfAI result | App does | Human task |
|---|---|---|
| answered (question) | answer with clause citations on the Ask page | — |
| generated (certificate) | certificate stored as a policy document (held if its totals disagree with the billing schedule) | Servicing escalation if held |
| applied / drafted with a record action (nominee, address, contact, premium mode, free-look cancellation) | change written; confirmation stored as a policy document | Servicing escalation if the CRM cannot write it or values disagree |
| declined (out of scope) | — | **Servicing escalation**: Reply / Decline |
| held (other) | — | **Servicing escalation**: Apply the change (when allowed) / Reply / Decline |
| held, revival (always) | — | **Revival request** |

Premium-mode changes are recorded but not applied at the anniversary; a free-look refund is recorded without a finance
approval / payout (K16).

**Revival** (C's 2027 beat):

| # | Who | What | TurfAI | Event | Result |
|---|---|---|---|---|---|
| 1 | Customer | Payments on a lapsed policy → **Ask to revive this policy** (opens Ask with *Revive a lapsed policy*); attach the health declaration file (C has `revival_health_declaration.txt`; "A revival needs a declaration of good health") → **Send request** | `policy_service` | `servicing.requested` | mock billing produces the revival quote (arrears + 9 % p.a. simple interest, valid 5 days); TurfAI restates it and holds the request |
| 2 | Servicing officer | **Revival request** → **Approve the revival** / **Decline the revival** | — | `servicing.revival_approved` / `servicing.resolved` | customer **Revival payment** |
| 3 | Customer | Payments → **Pay ₹8,485 and revive** (C on 2027-01-10) | — | `policy.revived` | arrears dues collected, policy `revived`, last revival date set (contestability re-opens from it), billing resumes |

### 3.4 Claims: intake → fraud ∥ settlement → SIU → sign-off

```mermaid
flowchart TD
  R[Nominee: Report a claim] -->|claim.intimated| CI[TurfAI claim_intake]
  CI -->|fast_track, clean| REG[Registered]
  CI -->|investigate / adjudicate, clean| REGN[Registered + Claim triage notice]
  CI -->|hold / requirements / duplicate / screening / mismatch / claim already settled on the policy| TR[Claims officer or Compliance: Claim triage]
  TR -->|Register the claim| REG
  TR -->|Ask for documents| RP[Documents requested - clock paused]
  RP -->|Nominee: Add documents - same claim, claim.documents_received| CI
  TR -->|Link as duplicate - TurfAI or an open / settled claim on the insurer records| DUP[Duplicate - closed]
  REGN --> REG
  REG -->|claim.registered| FS[TurfAI fraud_score]
  REG -->|claim.registered| CS[TurfAI claim_settle]
  FS -->|low: clear| FC[fraud.cleared]
  FS -->|medium: human_review| SC[SIU lead: SIU screen]
  FS -->|high: refer| SIU[SIU case + SIU investigator task]
  SC -->|Clear the claim| FC
  SC -->|Refer to SIU investigation| SIU
  SIU -->|Submit findings| FD[TurfAI fraud_dispose]
  FD --> LEAD[SIU lead: SIU disposition sign-off]
  LEAD -->|Return / Extend| SIU
  LEAD -->|Sign off unsubstantiated / Release| FC
  LEAD -->|Sign off substantiated| GR[fraud.disposition with s.45 grounds]
  GR --> CS
  CS --> SO[Claims officer: Settlement sign-off]
  FC -->|releases hold / re-runs settlement after investigate| SO
  SO -->|Sign off: admit| ST[Settled - policy claimed]
  SO -->|Sign off: repudiate| RE[Repudiated - policy terminated]
  SO -->|Sign off: requirements, or Ask for documents| SR[Documents requested for settlement - clock paused]
  SR -->|Nominee: Add documents - same claim, claim.settlement_reassess| CS
  SO -->|Return for re-assessment - note as officer_review| CS
```

**The documents loop (P16.2).** A claim waiting for documents is never re-reported: the nominee adds them to the
**same** claim on the portal (**Add documents** on the claim card), and TurfAI is re-triggered with what changed —
asked at triage (`requirements_pending`) → `claim_intake` re-runs (registered, or a new Claim triage task); asked at
settlement (`settlement_requirements`, by TurfAI's requirements decision or the officer's **Ask for documents**) →
`claim_settle` re-runs (a new Settlement sign-off). Any other status refuses the form ("Documents not added"); a double
submit files nothing twice. The TAT clock is paused while documents are requested and restarts from the last document
([§3.7](#37-the-claim-tat-clock-and-penal-interest)).

| # | Who | What | TurfAI | Event | Result |
|---|---|---|---|---|---|
| 1 | Portal (the policyholder persona; the nominee acts once the death is known) | **Report a claim** → "Report a death" (while a claim on the policy is not closed: the notice "You already have claim CLM-… (…) on this policy — its progress is shown above" and **Report this death again** to unfold the form; refused only while the first claim is still "Received", C4): Policy, Name of the insured, Date of death, Cause of death, Place of death, claimant name / relationship / e-mail / phone, optional intimation e-mail text, plain-text documents (choose each kind: claim form, death certificate, your ID, bank proof, medical attendant's certificate, hospital summary, …) — or **Use sample documents** for the persona's storyline set ([§1.9](#19-sample-documents); the fields open pre-filled from the persona's sample claim form) → **Submit claim** | — | `claim.intimated` | claim `CLM-<year>-<policy n6>` (`-2`, `-3` for later ones), status `intimated`; claimant screened (mock AML); customer marked deceased |
| 2 | system | intake | `claim_intake` | | acknowledgement letter filed for the nominee; routing below |
| 2a | | `fast_track` + registered, no human route / beneficiary review / duplicate, **0 mismatches, and no other claim on the policy already settled** | | `claim.registered` | registered automatically |
| 2b | | `investigate` (early claim / anomaly — "fraud priority") or `adjudicate` (not in force on the date of death), otherwise clean | | `claim.registered` | registered + **Claim triage** notice (**Acknowledge**) |
| 2c | | anything else: `hold` (screening hit → Compliance; screening pending), `requirements` (incomplete, policy not found, duplicate), beneficiary review, not registered, any recomputed fact differs, or a claim on the policy is already settled (never auto-registered, whatever TurfAI's routing) | | | `triage_review` → **Claim triage** gate: **Register the claim** (note required when another claim on the policy is open or settled) / **Ask for documents** / **Link as duplicate** (when TurfAI named the duplicate **or** the insurer's records show another open or settled claim on the policy) ([§2.7](#27-claims-officer)) |
| 3 | system | on registration: fraud status `pending`, hospital records requested from the mock records service (B's outpatient history is filed as claim documents); billing stops | `fraud_score` ∥ `claim_settle` (in parallel) | `claim.registered` | |
| 4 | system | fraud scoring (six indicators; bands 0–2 low, 3–4 medium, ≥ 5 high) | `fraud_score` | | low + 0 mismatches → `cleared` (`fraud.cleared`); medium, or clear with mismatches → **SIU screen** (`fraud.screen_required`); high → SIU case + **SIU investigation** (`fraud.referred`) |
| 5 | system | settlement (decision order: not in force → repudiate; incomplete → requirements; s.45 grounds → repudiate; suicide in window → partial; exclusion → repudiate; early claim and fraud not cleared → investigate; ADB → admit with rider; else admit) | `claim_settle` | | claim `pending_signoff` (+ **Settlement sign-off**, possibly held) or `awaiting_fraud_outcome` (investigate, held). A failed compliance checklist re-triggers the run once |
| 6 | SIU lead | **SIU screen** → **Clear the claim** / **Refer to SIU investigation** | — | `fraud.screen_decided` + `fraud.cleared` / `fraud.referred` | |
| 7 | SIU investigator | **SIU investigation** → **Submit findings** | `fraud_dispose` | `siu.findings_submitted` | case `disposition_proposed` → **SIU disposition sign-off** |
| 8 | SIU lead | §2.10 | — | `fraud.disposition` (+ `fraud.cleared` unless substantiated) / `siu.findings_returned` / `siu.investigation_extended` | substantiated → settlement re-runs with grounds; unsubstantiated / released → hold released or settlement re-runs; return / extend → back to step 7 |
| 9 | system | settlement re-run (after `investigate` when fraud clears; with repudiation grounds after a substantiated disposition; after the nominee's documents; with the officer's note after a return) | `claim_settle` | `fraud.cleared` / `fraud.disposition` / `claim.settlement_reassess` (documents_received / officer_return) | new sign-off; the old one is cancelled |
| 10 | Claims officer | **Settlement sign-off** → **Sign off TurfAI's recommendation** (with the mismatch acknowledgement when the task has mismatches) | — | `claim.approved` / `claim.repudiated` / `claim.settlement_requirements` | settled → policy `claimed`; repudiated → policy `terminated`; both: mandate cancelled, unpaid dues cancelled; letter `DOC-<claim>-LETTER` on the portal; a repudiated claim shows a "raise a complaint" link. The TAT clock stops; a breached clock's penal interest is recorded with the decision and on `claim.approved` |
| 10a | Claims officer | **Ask for documents** (note required) | — | `claim.settlement_requirements` | claim `settlement_requirements`, clock paused; the nominee's note on the portal → **Add documents** → step 9 |
| 10b | Claims officer | **Return for re-assessment** (note required) | `claim_settle` with `officer_review` | `claim.settlement_reassess` (officer_return) | claim `under_assessment` → step 9 → a new sign-off showing your note |
| 11 | Nominee (portal) | **Add documents** on a claim "More documents needed" | `claim_intake` (asked at triage) / `claim_settle` (asked at settlement) | `claim.documents_received` / `claim.settlement_reassess` (documents_received) | same claim: `intimated` → re-triage (step 2) / `under_assessment` → step 9 |

Valuation (checked by the insurer): gross = sum assured + bonuses on file; deductions = policy loan principal +
interest to the date of death + the premium unpaid in grace on the date of death; on an s.45 non-disclosure
repudiation the premiums received are refunded (s.45(4)). Sign-off due: 15 days from the clock start (the last
document's date, else the intimation), 45 days when investigation is required —
[§3.7](#37-the-claim-tat-clock-and-penal-interest).

### 3.5 Complaints

```mermaid
flowchart TD
  C[Customer / nominee: Raise a complaint] -->|complaint.raised| H1[TurfAI complaint_handle mode new]
  H1 -->|acknowledgement required| ACK[Complaint owner: Complaint acknowledgement]
  H1 -->|none required| RES[Complaint owner: Complaint resolution]
  H1 -->|notice drafted| NS[Compliance: Regulator notice sign-off]
  ACK -->|Send TurfAI's acknowledgement| RES
  RES -->|Add a case note| RES
  CLK[Clock: escalation trigger day 10] -->|complaint.at_risk| H2[TurfAI complaint_handle mode at_risk]
  H2 -->|escalate| ESC[Compliance & grievance: Complaint escalation]
  RES -->|Record the resolution| DONE[Resolved]
  ESC -->|Record the resolution| DONE
  DONE -->|complaint.resolved| H3[TurfAI re-check: within TAT]
```

| # | Who | What | TurfAI | Event | Result |
|---|---|---|---|---|---|
| 1 | Portal | **Raise a complaint** → "What is it about?" (Claim rejected · Claim delayed or unanswered · Premium, payment or refund · Policy service (documents, changes, no reply) · Policy sold to me wrongly · Something else), About (policy / claim), Subject, What happened?, What would you like us to do?, name, relationship, contact → **Raise complaint**. One open complaint per claim / policy at a time | — | `complaint.raised` | status "Received — being registered" |
| 2 | system | classify (grievance or query, category, severity), set clocks DL-ACK (3 days) / DL-GRV (14 days) / DL-OMB, open the case, route it, draft the acknowledgement, regulator notice if needed | `complaint_handle` (new) | | **Complaint acknowledgement** (or **Complaint resolution**); **Regulator notice sign-off** when a notice is drafted |
| 3 | Complaint owner | **Send TurfAI's acknowledgement** | — | `complaint.acknowledged` | DL-ACK met; owner assigned; **Complaint resolution** |
| 4 | Complaint owner | **Add a case note** (optional, any number) / **Record the resolution** | — / re-check | `complaint.note_added` / `complaint.resolved` | resolved; the complainant sees the response |
| 5 | Clock | escalation trigger (day 10 from receipt) reached on an open, unescalated case; re-raised daily until escalated | `complaint_handle` (at_risk) | `complaint.at_risk` | TurfAI re-checks the clocks; escalates before the breach → **Complaint escalation** (supersedes the owner's task) |
| 6 | Compliance & grievance | **Record the resolution** / **Add a case note** | re-check | `complaint.resolved` | resolved; every other case task cancelled |
| 7 | system | re-check after resolution | `complaint_handle` (at_risk) | `complaint.resolved` | "Resolved within TAT (TurfAI re-check)" or late |

There is no console capture form for contact-centre complaints and no breach-date re-check (K16).

### 3.6 Showing the autonomy ladder

The demo script for the autonomy level ([§1.8](#18-the-autonomy-level-autopilot)): the same Loop 1 twice — once with
the underwriter in the loop, once with the autopilot — and back. Start from **Reset demo** (level HITL); the clock stays
on 2026-11-01 throughout. Three TurfAI runs (steps 1, 4 and 7 — ≈ 1–4 min each); nothing else costs tokens.

| # | Who | What | TurfAI | Event | Result |
|---|---|---|---|---|---|
| 1 | A1 (portal) → Underwriter | §3.1 steps 1a–3a at **HITL**: Arjun applies (pay the first premium now); the memo lands in the **Underwriter** inbox — open it: badge **Your call**, the sticky **Decide:** bar; **Accept (standard)** → **Submit** | `uw_assess`, then `policy_issue` | `uw.approved` (source `human:underwriter`, actor `underwriter:console.demo`) | `POL-LIFE-2026-000101` in force. Point out: the human gate, the frame badge **Autonomy: HITL** |
| 2 | Console → Clock | **Autonomy level** card → **Semi-auto** → **Save autonomy level** | — | — | "Autonomy: Semi-auto · pay-out limit ₹50,00,000 — Saved …"; the frame badge reads **Autonomy: Semi-auto**; the `demo.settings` change is logged |
| 3 | New customer (portal) | **Act as → New customer…** → **Fill sample details** → **Sign up and start my application** → **Apply**: keep the defaults (TL-SECURE-TERM, ₹50,00,000, 25 years, monthly), **Use a demo sample** for **KYC** (the sample applicants are 29–38, so no medicals or ITR are required under UWM-2 / UWM-7), optionally **Pay the first premium now**, accept the declaration, **Submit application** | — | `proposal.submitted` | customer `CUST-9NNNNN`, proposal `PRP-2026-9NNNNN` submitted. Do not touch the console |
| 4 | system | **Console → Runs** (refreshes itself): `uw_assess` running → completed → mapped; a `policy_issue` run appears right after it, with no console touch | `uw_assess` → `policy_issue` | `uw.approved {standard}` (source `system:autopilot`) → `policy.issued` | proposal `approved` → `POL-LIFE-2026-9NNNNN` in force; on the portal **My policy** shows the schedule and the mandate |
| 5 | Underwriter (console) | **Inbox → Decided**: the new memo row carries the **Autopilot** badge; open it: fact "Decided by: Autopilot", card badge **Decided by Autopilot**, "Decided by Autopilot · Semi-auto · 8 checks passed …" and the **Autopilot checks** chips; **Policy 360** → the policy's timeline lists `uw.approved · system:autopilot` | — | — | the audit trail of a decision no person made |
| 6 | Console → Dashboards | **Underwriting** card → **Auto-approved share** tile | — | — | **1 of 2** (50 %): Arjun's memo by the underwriter, the sign-up's by the autopilot (1 of 1 if step 1 was skipped) |
| 7 | Console → Clock → New customer | **HITL** → **Save autonomy level** (badge back to **Autonomy: HITL**); a second **New customer…** sign-up and application as in step 3 (**Fill sample details** now offers the next sample applicant) | `uw_assess` | `uw.recommendation_ready` | the memo **waits in the Underwriter inbox** (**Your call**); nothing is issued until someone decides — the level applies to the next mapped result, and the first sign-up's policy is untouched |

If TurfAI's memo for the sign-up is **not clean** — e.g. `compliance_pass: false`, a requirement listed outstanding, a
refer, or a System-check mismatch (K18 variance) — the autopilot leaves it for the underwriter exactly as at HITL; the
Decision card opens with *"Left to you by the autopilot (Semi-auto): …"* naming the failing check(s) (the stamp
`autopilot.eligible: false` is in the drawer's **Payload**). Say so and decide it by hand; the ladder is about *clean*
cases.

**Autonomous is demo only.** The ladder is shown at Semi-auto; if you go on to Autonomous, say what the amber note on
the card and the storyline panel says: the specs have a claims officer approve every claim, so Autonomous exists to
show the autopilot's checks, not as a mode an insurer would run. Even there it never repudiates (only a paying
decision qualifies), never signs a substantiated fraud case (only an unsubstantiated SIU disposition) and never pays
above the pay-out limit.

**Rehearsal without TurfAI** (recorded contracts, worker allowed to run): `npm run demo:reset` → set the level on the
Clock page → `npm run replay:a1-autopilot -w @uip/worker`. At Semi-auto / Autonomous it prints "POL-LIFE-2026-000101
issued by the autopilot" and steps 5–6 can be shown on Arjun's memo (Auto-approved share **1 of 1**); at HITL it prints
"memo left open" and the memo waits in the Underwriter inbox. `demo:reset` afterwards (the setting goes with it).

### 3.7 The claim TAT clock and penal interest

TurfAI's `claim_settle` computes the settlement clock on its recommendation (SET-11) and the penal interest (SET-9).
The insurer keeps the same clock itself (P16.4) — dates, a status and arithmetic, no judgement — recomputes TurfAI's
figures as **System checks** (`compliance.tat_clock` start / elapsed / days left / status / breached / delay,
`valuation.penal_interest`), and shows it in the console:

| Rule | Value |
|---|---|
| Clock start | the date the **last** claim document was filed (the insurer's own letters do not count), else the intimation date — the badge's tooltip says which ("(last document received)" / "(intimation — no document date on file)") |
| Applicable | **15 days**; **45 days** when investigation is required — an early claim (SET-3, within the contestability window per policy admin) or a Fraud & SIU referral (referred, under investigation, substantiated, unsubstantiated, inconclusive, released) |
| Paused | while documents are requested (`requirements_pending`, `settlement_requirements`): "Paused — documents requested (15-day clock)", no interest. Documents added restart it from the new last document |
| Running | elapsed = sim date − clock start; "Day 9 of 15 · 6 days left" |
| Stopped | at the decision (`settled` / `repudiated`, the decision date): badge "TAT 15d · Met" or "TAT 15d · Breached" |
| Status | green "On time" · amber "Due soon" (≤ 3 days left) · red "Breached" (elapsed > applicable: "Breached by 3 days (day 18 of 15)") · "Paused". A stopped clock is green or red, never amber |
| No clock | duplicate, withdrawn or closed claims |
| Penal interest | only when breached: (bank rate 6.75 % + 2 %) × the **written amount** (TurfAI's net payable) × delay days / 365, rounded to the rupee. The bank rate is `settlement_rules` SET-9 in the reference data |

Where it shows:

- **Policy 360** — a TAT column on the claims table and the badge (status, days left / delay, penal interest, "from …
  · due …") on the claim block.
- **Claim triage** and **Settlement sign-off** drawers — the badge on the claim block; on the sign-off, the penal
  interest line under **Written to the claim** ([§2.7](#27-claims-officer)).
- **Dashboards → Claims Settlement** — the **Claims TAT** tile: open claims on the clock with counts **On time** /
  **Due soon** / **Breached** / **Paused**, and "Penal interest accrued ₹…" on the breached ones whose current
  recommendation pays (not the indicative figure of a claim awaiting the fraud outcome).

**Recorded at sign-off.** **Sign off TurfAI's recommendation** records the clock (`tat_clock`, incl. `delay_days`) and,
on a paying decision, `penal_interest_due` — computed by the server at the sim date, not taken from the form — on the
claim's decision, the change log and the `claim.approved` event. Example (P16.4 check): A2's claim, clock start 11-02,
due 11-17, signed off with the clock at 11-20 → day 18 of 15, breached by 3 days → 8.75 % × ₹27,67,173 × 3 / 365 =
**₹1,990**. The sign-off due date on the task is clock start + 15 (or 45) days.

The dashboard's **Decided within TAT** metric uses the same clock: a decided claim counts as within TAT when the clock
recorded at sign-off (else the claims system's stopped clock) met its 15 / 45 days — **Median days to decision** still
counts intimation → decision.

To see the colours without a TurfAI run, replay a claim and advance the clock — but pause the worker first
([§7.1](#71-working-with-the-demo)).

---

## 4. Who approves what, when

"Autopilot from level" = the lowest autonomy level at which the orchestrator closes a *clean* instance of the gate itself by applying TurfAI's recommendation ([§1.8](#18-the-autonomy-level-autopilot)); **never** = always a person, at every level.

| Decision | Role | Task kind (inbox label) | Triggered by | Options | Held / refused until | Emits → next | Autopilot from level ([§1.8](#18-the-autonomy-level-autopilot)) |
|---|---|---|---|---|---|---|---|
| Underwriting decision | Underwriter | `uw_memo` (Underwriting memo) | every `uw_assess` result | Accept (standard) · Rated / exclusion · Counter-offer · Decline · Request requirements | terms must fit the option; decline needs a note | `uw.approved` → `policy_issue` (standard) / customer Counter-offer / requirements on Apply / end | **Semi-auto** — standard, no referral, no outstanding requirements, compliance pass, loading 0, no exclusions, no over-insurance, 0 mismatches → Accept (standard) |
| Accept revised terms | Customer (portal Offers) | `counter_offer` | underwriter Rated / Counter-offer | Accept · Decline | first premium must equal the quote (top-up allowed; overpayment refused) | `uw.offer_accepted` → `policy_issue` · `uw.offer_declined` → end | never |
| Answer requirements | Customer (portal Apply) | — (form) | underwriter Request requirements | Send to underwriting | one answer per request | `proposal.requirements_submitted` → `uw_assess` | never |
| Non-standard issuance | Servicing officer | `issuance_review` | `policy_issue` non_standard or mismatch | Confirm issuance · Reject | confirm needs a received first premium and a plan not yet stale | `issuance.confirmed` + `policy.issued` · `issuance.rejected` | never |
| Hardship / final dunning | Collections & finance | `collections_review` | `billing_dun` routes to a collections officer | Agree a payment arrangement · Resume reminders · Close the case | arrangement pay-by ≤ grace end; resume needs a ladder step left; notes on arrangement / close | `billing.hardship_resolved` (→ `billing_dun` again on resume) | never |
| Lapse after grace | Collections & finance | `grace_expiry` | clock: day after grace end, unpaid | Lapse the policy · Close the case | lapse refused if paid; close only if paid | `policy.lapsed` | never |
| Billing mismatch | Collections & finance | `billing_review` | billing contract vs insurer facts; rail refusal | Acknowledge | — | none | never |
| Reconciliation exception | Collections & finance | `reconciliation_exception` | `billing_reconcile` flagged line / held line | Collect the balance · Write off the shortfall · Apply to a premium due · Refund · Hold in suspense · Confirm the rail debit | options per line state; apply needs an unpaid due for exactly the amount | `reconciliation.resolved` (+ `payment.received`) | never |
| Renewal / retention outreach | RM | `renewal_handoff` | `renewal_action` hand-off; customer answer (follow-up) | Send TurfAI's draft · Record the outcome · Withhold (do not send) | send only on a consented channel, not opted out; follow-up = Record the outcome only | `renewal.outreach_sent` (+ customer offer) · `renewal.outcome` | **Semi-auto** — hand-off (not follow-up), sendable, not escalated, handed off to the RM (not a broker), cadence not stopped, 0 mismatches → Send TurfAI's draft |
| Renewal offer | Customer (portal Offers) | `renewal_offer` | RM sent the draft | Accept · Decline | touch still open | `renewal.offer_accepted` / `_declined` + RM follow-up | never |
| Servicing escalation | Servicing officer | `servicing_review` | `policy_service` declined / held / unwritable | Reply to the customer · Apply the change · Decline | apply only for a writable change equal to the request | `servicing.resolved` | never |
| Revival | Servicing officer | `revival_review` | every revival request | Approve the revival · Decline the revival | lapsed, quote valid (5 days), schedule unchanged | `servicing.revival_approved` (+ customer Revival payment) · `servicing.resolved` | never |
| Revival payment | Customer (portal Payments) | `revival_payment` | revival approved | Pay ₹… and revive | by the quote's valid-to date | `policy.revived` | never |
| Claim triage | Claims officer (Compliance on a screening hit) | `claim_triage` | `claim_intake` exception or non-clean auto-registration | Register the claim · Ask for documents · Link as duplicate · Acknowledge | link when TurfAI named the duplicate or the insurer's records show another open or settled claim on the policy (never to one that ended unpaid); register over another open / settled claim on the policy needs a note; a claim already settled on the policy always holds a new report here; acknowledge only on a registered claim | `claim.triage_confirmed` (+ `claim.registered` → fraud ∥ settlement) | never |
| SIU screen | SIU lead | `siu_screen` | `fraud_score` medium band / clear with mismatches | Clear the claim · Refer to SIU investigation | fraud status must be pending; note required | `fraud.cleared` / `fraud.referred` | never |
| Investigation findings | SIU investigator | `siu_investigation` | `fraud.referred`; lead return / extend | Submit findings | findings .txt required; case open / returned / extended | `siu.findings_submitted` → `fraud_dispose` | never |
| SIU disposition | SIU lead | `siu_disposition` | `fraud_dispose` draft | Sign off TurfAI's disposition · Release to settlement · Extend the investigation · Return to the investigator | options per TurfAI outcome; superseded by a newer run | `fraud.disposition` (+ `fraud.cleared`) → `claim_settle` re-run · investigator task | **Autonomous** — outcome unsubstantiated, sign-off offered, 0 mismatches → Sign off |
| Settlement sign-off | Claims officer | `claim_signoff` | `claim_settle` result | Sign off TurfAI's recommendation · Ask for documents · Return for re-assessment | held until Fraud & SIU clear (pay) / decide (repudiate) — a hold refuses every option; investigate never; superseded by a newer run; approve with System-check mismatches needs the acknowledgement; both overrides need a note | `claim.approved` / `claim.repudiated` / `claim.settlement_requirements` → policy closed (approve); `claim.settlement_requirements` (Ask for documents); `claim.settlement_reassess` → `claim_settle` re-run (Return) | **Autonomous** — paying decision, Fraud & SIU cleared, not early / provisional / investigate, TurfAI's compliance sign-off passed, net payable ≤ pay-out limit, not held, no other open or settled claim on the policy, 0 mismatches → Sign off |
| Complaint acknowledgement | Complaint owner | `complaint_acknowledgement` | `complaint_handle` new (ack required) | Send TurfAI's acknowledgement | superseded by a newer run | `complaint.acknowledged` + resolution task | **Semi-auto** — acknowledgement required, no human review, not escalated, draft present, 0 mismatches → Send |
| Complaint resolution | Complaint owner | `complaint_resolution` | after the acknowledgement | Record the resolution · Add a case note | resolution needs decision + written response | `complaint.resolved` → re-check · `complaint.note_added` | never |
| Escalated complaint | Compliance & grievance | `complaint_escalation` | at-risk re-check escalated | Record the resolution · Add a case note | as above | `complaint.resolved` → re-check | never |
| Regulator notice | Compliance | `complaint_notice_signoff` | `complaint_handle` drafted a notice | Sign off TurfAI's notice draft | superseded by a newer run | `notice.signed_off` | never |
| Run failure | System (ops) | `system` | run invalid / failed after one re-trigger; mapper failure | Retry · Dismiss | — | `run.retry_requested` (fresh run or re-map) | never |

---

## 5. The TurfAI workflows

Thirteen activities named `UIP · <pack> · <name>` (12 pack workflows + `smoke`), defined in
`turfai/src/workflows/*.ts`. Every run receives `request_id`, `as_of_date` (sim date), `market` (INR, IN-IRDAI), the
relevant `product_config` slice and the insurer's rules as reference data with section ids (UWM-, BCR-, DGR-, REC-,
RNR-, SVR-, INT-, FRR-, FDR-, SET-, CMR-n) — so every judgement cites a rule. Documents travel as text; citations
are `{doc_id, locator}`. Data Shield (PII masking) is on for every LLM step. Judgement steps run on
`gemini-2.5-pro`, the rest on `gemini-2.5-flash`. A run typically takes 40–250 s (measured live: `uw_assess` 88–180 s,
`policy_issue` ~56 s); long underwriting and reconciliation batches take longer.

### 5.1 `uw_assess` — UIP · 3 Underwriting · Assess proposal

- **Trigger**: `proposal.submitted`; `proposal.requirements_submitted` (same inputs, all documents sent so far).
- **Reads**: the proposal (applicant, plan, sum, term, mode, disclosures, medical, financial), documents as text,
  existing cover, the AML screening result, the underwriting manual (`uw_manual`).
- **Steps**: Submission Parser → Underwriting Analyst (pro) → Coverage-Gap Detector → Compliance Reviewer (pro) →
  Contract.
- **Decides**: `decision.type` standard · rated · exclusion · counter_offer · requirements · refer (senior / medical /
  reinsurer) · postpone · decline; rating factors and total rating %, loading, exclusions, requirements with
  satisfied / outstanding / not_required, reinsurance referral, compliance (incl. KYC ok / document_missing / hit),
  memo synthesis, one applicant letter (acceptance / counter_offer / requirements / decline / postpone).
- **Follow-up**: **Underwriting memo** (always). Never binds cover.

### 5.2 `policy_issue` — UIP · 4 Issuance · Issue policy

- **Trigger**: `uw.approved` with decision standard; `uw.offer_accepted`.
- **Reads**: the approved recommendation and the underwriter's decision, the rating-engine quote, policy number from
  policy admin, nomination, mandate request, first-premium receipt.
- **Steps**: Policy Assembler (pro) → Contract(policy) → Certificate & Kit → Contract(documents).
- **Decides**: issued policy (status in_force / pending_first_premium, `non_standard` + review reasons), mandate,
  policy schedule and certificate text.
- **Follow-up**: clean → policy issued automatically; non_standard or mismatch → **Issuance review**;
  pending_first_premium → proposal parked.

### 5.3 `policy_service` — UIP · 4 Servicing · Service policy request

- **Trigger**: `servicing.requested` (portal Ask).
- **Reads**: policy record, this policy's documents, product wording (clause ids), billing summary (so payment
  questions are grounded), servicing rules (SVR-n), the request and attachments, the revival quote for a revival.
- **Steps**: Request Resolver → branch "Question?" → Policy Q&A Agent (pro) or Endorsement & Service Agent (pro) →
  Contract(qna) / Contract(service).
- **Decides**: type qna / endorsement / certificate / free_look / revival; status answered / declined / applied /
  drafted / generated / held; record action (replace nomination, update address / contact, change premium mode,
  cancel free look).
- **Follow-up**: §3.3 table — **Servicing escalation** or **Revival request** when declined / held; revival is always
  held.

### 5.4 `billing_collect` — UIP · 8 Billing · Bill and collect premium

- **Trigger**: `premium.due` (in scope); `mandate.registered` with an open due.
- **Reads**: the due, mandate, policy, customer consent, billing rules (BCR-n).
- **Steps**: Billing & Invoicing Agent → Collection Agent (pro) → Contract(billing_event).
- **Decides**: `instruction` debit / block_remandate, block reason, re-mandate request.
- **Follow-up**: the mock rail debits (or not); failure → `billing_dun`; mismatch / rail refusal → **Billing check**.
  Never moves money itself.

### 5.5 `billing_dun` — UIP · 8 Billing · Dun failed premium

- **Trigger**: `collection.failed`, `dunning.step_due`, `customer.hardship_reported`, and recovery events on a due
  already in dunning (`collection.succeeded`, `payment.received`).
- **Reads**: the due with the collection attempts, dunning history, consent, hardship signals, dunning rules (DGR-n),
  the ladder and grace days.
- **Steps**: Dunning Agent (pro) → Contract(billing_event) → Dunning Communications → Contract(communications).
- **Decides**: status in_dunning / escalated / lapsed_risk / collected; stage; next action retry_debit / reminder /
  final_reminder / grace_end_review / none; hardship flag; human route (collections officer); lapse-risk signal;
  customer messages.
- **Follow-up**: **Collections review** when routed; `billing.lapse_risk` → `renewal_action`. Never lapses a policy.

### 5.6 `billing_reconcile` — UIP · 8 Billing · Reconcile settlement feed

- **Trigger**: `settlement_feed.arrived` (unless billing scope `none`); `settlement_feed.next_batch`.
- **Reads**: one feed batch (≤ 8 bank lines), open expected premiums, dues already settled, reconciliation rules.
- **Steps**: Reconciliation Agent (pro) → Contract(reconciliation).
- **Decides**: one item per line — match state matched / short / over / duplicate / unmatched, exception type
  (shortfall / unapplied / duplicate) flagged for finance.
- **Follow-up**: matched settles / bank-confirms; every flagged line → **Reconciliation exception**. Nothing flagged is
  applied.

### 5.7 `renewal_action` — UIP · 7 Renewals · Renewal / retention action

- **Trigger**: `renewal.window_open` (cadence, in scope, due still scheduled); `billing.lapse_risk` (lapse_risk).
- **Reads**: policy, holdings, consent, persistency history, trigger + lapse context, renewal rules (RNR-n), cross-sell
  catalogue, servicing roster.
- **Steps**: Renewal Detection & Timing → Renewal Analyst → Coverage-Gap & Cross-Sell (pro) → Outreach & Hand-off →
  Contract(renewal_action).
- **Decides**: window, priority (critical / high / medium / low), stop cadence, outreach draft + channel, hand-off
  (rm / broker), cross-sell.
- **Follow-up**: **Renewal hand-off** (unless stop cadence). Never prices, sends or closes.

### 5.8 `claim_intake` — UIP · 1 Claims Intake · Intake and triage death claim

- **Trigger**: `claim.intimated`; `claim.documents_received` (the nominee added documents to a claim asked for them at
  triage — the same claim, re-run with all its documents, only while it is still `intimated`).
- **Reads**: the intimation and documents, the policy-admin record with billing facts for in-grace-on-date-of-death,
  the claims system's other open claims on the policy, the AML / PEP / sanctions result, intake rules (INT-n).
- **Steps**: Intake & Capture → Validation (pro) → Screening → Triage (pro) → Contract(claim_intake_record) →
  Acknowledgement Drafter → Contract(acknowledgement).
- **Decides**: validation (policy found, in force on the date of death with basis premium_paid / in_grace / lapsed /
  outside_term, completeness, claimant is nominee, duplicate), screening clear / hit / pending, triage decision
  fast_track / investigate / adjudicate / requirements / hold, route (claims_settlement / siu = fraud priority only /
  compliance / hold), early claim, acknowledgement letter.
- **Follow-up**: §3.4 step 2. Intake never refers to SIU and never rejects.

### 5.9 `fraud_score` — UIP · 5 Fraud · Score claim and refer

- **Trigger**: `claim.registered` (every claim).
- **Reads**: claim, policy record, the underwriting proposal and disclosures (+ any revival health declaration),
  claim and proposal documents, other cover on the life, agent / branch early-claim statistics, contestability
  parameters, fraud rubric (FRR-n).
- **Steps**: Fraud Scoring Agent (pro) → Referral / Triage Agent → Contract(fraud_assessment) → Contract(referral) →
  branch "Refer to SIU?" → SIU Case Agent → Contract(siu_case).
- **Decides**: six indicators — early_claim +3, health_habit_mismatch +3, financial_anomaly +2, document_forensics
  2 / 3 / 5 (critical = showstopper), nominee_anomaly +1, agent_branch_cluster +2; score, band, recommendation clear /
  human_review / refer, SIU case stub and investigation plan. An isolated non-critical indicator never auto-refers.
- **Follow-up**: clear → `fraud.cleared`; human_review → **SIU screen**; refer → **SIU investigation**. Never declares
  fraud.

### 5.10 `fraud_dispose` — UIP · 5 Fraud · Dispose SIU case

- **Trigger**: `siu.findings_submitted`.
- **Reads**: the SIU case and fraud assessment, the investigator's findings document and evidence, claim / policy
  facts for the s.45 window, original documents, disposition rules (FDR-n), fraud rubric, underwriting manual.
- **Steps**: Disposition Agent (pro) → Contract(disposition).
- **Decides**: outcome substantiated / unsubstantiated / inconclusive (or none), action repudiation_grounds /
  release, next step, per-indicator findings, s.45 repudiation grounds (material non-disclosure / fraudulent
  misstatement / document fraud), inconclusive next lines, underwriting feedback.
- **Follow-up**: **SIU disposition sign-off** (always needs the lead). Never repudiates.

### 5.11 `claim_settle` — UIP · 2 Claims Settlement · Settle death claim

- **Trigger**: `claim.registered` (in parallel with fraud scoring); `fraud.cleared` when the last decision was
  investigate; `claim.settlement_reassess` (reason `documents_received` after the nominee's **Add documents**, or
  `officer_return` after **Return for re-assessment**); `fraud.disposition` substantiated (with repudiation grounds).
- **Reads**: the registered claim + intake contract, claim documents, policy record (bonuses, loan, unpaid in-grace
  premium, premiums received), the current fraud status (never assumed clear), repudiation grounds, settlement rules
  (SET-n, ADB-n), `last_document_date` (the clock start), and `officer_review` (`{note, returned_by, returned_on,
  previous_recommendation}` on a re-run after the officer's return, null otherwise — TurfAI addresses the note in its
  synthesis but still applies the rules).
- **Steps**: Evidence Collector → Coverage Analyst (pro) → Claims Valuator (pro) → Settlement Drafter (pro) →
  Contract(settlement_recommendation) → Compliance Reviewer (pro) → Contract(compliance_signoff).
- **Decides**: decision admit / admit_with_rider / partial_suicide_clause / repudiate (basis policy_not_in_force /
  s45_non_disclosure / s45_document_fraud / excluded_peril) / investigate / requirements; valuation; letter
  (discharge voucher / repudiation / requirements); TAT clock (15 / 45 days); compliance pass / kickback.
- **Follow-up**: **Settlement sign-off**. Only recommends — no auto-pay. The mapper recomputes the TAT clock and
  penal interest as System checks ([§3.7](#37-the-claim-tat-clock-and-penal-interest)).

### 5.12 `complaint_handle` — UIP · 6 Complaints · Complaint handling

- **Trigger**: `complaint.raised` (mode new); `complaint.at_risk` (mode at_risk, on an open case with no run in
  flight); `complaint.resolved` (at_risk re-check).
- **Reads**: the complaint, linked policy / claim / billing context, the existing case on a re-check, complaint rules
  (CMR-n, DL-ACK / DL-GRV / DL-OMB).
- **Steps**: branch "New complaint?" → Complaint Classification (pro) → Deadline & Case (pro) → Acknowledge & Routing →
  Regulatory Notice & Compliance (pro) → Contract(complaint_new); re-check path: Deadline & Case (re-check) → … →
  Contract(complaint_at_risk).
- **Decides**: grievance or query, category (claim_delay / claim_repudiation / mis_sale / servicing / premium /
  other), severity, deadlines and their states (on_track / at_risk / breached / met), case status, escalation,
  queue / owner, acknowledgement draft, regulator / Ombudsman notice draft, resolution within TAT, root-cause
  feedback area.
- **Follow-up**: §3.5. Never resolves.

### 5.13 `smoke`

A one-agent test workflow (policy status) used to prove trigger → LLM → contract → poll before the packs were built.
Not used by the app.

### 5.14 System checks, lineage, Runs and retries

- **System checks.** Mappers recompute every deterministic fact they can from the CRM and product_config (ages, dates,
  contestability anchor, grace on the date of death, premiums, valuation arithmetic, clocks, routing-field copies) and
  list disagreements on the task: "System checks · N mismatches" — `fact`: TurfAI *x*, insurer systems *y*, with the
  basis. The CRM always keeps the insurer's values. A mismatch never changes TurfAI's decision but it stops automatic
  steps: a claim is not auto-registered, a fraud "clear" goes to the SIU screen, an issuance goes to review, a
  certificate / change is held, a reconciliation line is held.
- **Lineage.** Every task shows the TurfAI run (workflow, execution id, duration, contract valid / invalid — click it
  for the run page), the triggering event, and document chips for the documents TurfAI was actually given plus those
  it cited. Clicking a chip opens the document with the cited line highlighted. Clock / billing-raised tasks say "Raised
  by the insurer's systems (clock / billing scanner) — no TurfAI run involved".
- **Runs page** (`/console/runs`): every execution with status (pending / triggered / queued / running / completed /
  failed …), "Contract valid" / "Contract invalid", mapped, attempt, duration, polls, token usage; filters **All
  workflows** / **All statuses**. A run's page shows "Inputs sent", the contract with "Validation errors", and "Raw
  execution outputs". Busy pages refresh every 3 s.
- **Retries.** An invalid contract does not fail the TurfAI run, so the worker re-triggers once automatically; if the
  retry is also invalid (or the run failed), a **System — run failure** task appears (Retry / Dismiss). TurfAI
  occasionally returns invalid contracts (seen on `complaint_handle`, `claim_intake`) and ~2 % of runs fail inside
  TurfAI ("LLM output format validation failed: expected json"). `claim_settle` with a failed compliance checklist is
  also re-triggered once. Stale results are ignored: a run older than the one already applied, or on a closed case,
  is recorded in history only.

---

## 6. Scenario catalogue

Start each block from a fresh **Reset demo** unless stated. "Expect" describes what the app does for sure, and what
TurfAI is expected to return under its rules; TurfAI is an LLM, so its exact wording, and occasionally its judgement,
varies (K18) — the human gate then decides.

### 6.1 Storyline beats (happy paths)

| # | Setup | Steps | Expected result |
|---|---|---|---|
| S1 | 11-01 | A1: Apply, keep KYC + ITR + proposal ticked, tick **Pay the first premium now (₹1,404)**, declaration, **Submit application** | `uw_assess` run on Runs; memo "Recommend accept at standard rates"; requirements satisfied / not required |
| S2 | after S1 | Underwriter: open the memo, click a lineage chip, **Accept (standard)** → **Submit** | `policy_issue`; `POL-LIFE-2026-000101` in force, nominee Divya Rao, mandate NACH-MND-88319 on My policy (or an Issuance review to confirm first) |
| S3 | after S2 | Clock → **Advance to date** 2026-12-01, then 2026-12-02 | 12-01 due collected (`NACH-DEBIT-2026-120101`); 12-02 feed line Matched on Policy 360 → Payments; background exception lines in the Collections & finance inbox |
| S4 | 11-01 | R1: Apply **without** paying; underwriter **Rated / exclusion**, Loading 50, no exclusions | Counter-offer on Kavya's Offers at ₹17,346 |
| S5 | after S4 | R1 Offers: **Accept and pay ₹17,346** → confirm | `policy_issue` → `POL-LIFE-2026-000102` |
| S6 | 11-02 | Acting as A2 (Karthik): **Report a claim** (fields pre-filled from A2's sample claim form) → **Use sample documents → Clean death claim — Meena Iyer** → **Submit claim** (files: `fixtures/documents/a2`) | intake fast-track, fraud low (cleared), settlement admit; claims officer **Sign off TurfAI's recommendation**: ₹25,00,000 + bonus − loan; policy claimed |
| S7 | 11-07 → 11-08 | Advance to 11-07 | D's complaint escalated (to the Head of Grievance Redressal) → Compliance & grievance **Complaint escalation**; 11-08 **Record the resolution** (upheld) |
| S8 | 11-08 | Acting as B (Anita): **Report a claim** (fields pre-filled from B's sample claim form: 03 Nov 2026, Heart attack, Aravali Heart Institute, Gurugram, Anita Sethi, spouse) → **Use sample documents → Early death claim — Vikram Sethi** → **Submit claim** (files: `fixtures/documents/b`, without the outpatient extract — the records service files it) | intake investigate (fraud priority) + triage notice; fraud high (early_claim + health_habit_mismatch, citing the proposal) → SIU case `SIU-2026-000877`; settlement "held — awaiting the fraud outcome" |
| S9 | 11-18 | SIU investigator on B's case: **Submit findings** → **Use sample documents → Findings: non-disclosure substantiated** (DOC-B-OPD-HISTORY and DOC-B-HOSP-SUMMARY are pre-ticked; file `turfai/fixtures/fraud_dispose/docs/b_siu_findings.txt`); SIU lead: **Sign off TurfAI's disposition**; claims officer: sign off | substantiated → settlement re-run → repudiate (s.45 non-disclosure, premiums refunded) → claim repudiated, policy terminated, letter on the portal; underwriting feedback filed |
| S10 | 11-25 → 12-03 | Anita: "raise a complaint" link on the decision; 11-26 owner **Send TurfAI's acknowledgement**; 12-03 **Record the resolution** | resolved within TAT (re-check) |
| S11 | 11-15 → 11-20 | Advance to 11-15, then 11-20 | C's debit fails (insufficient funds) → dunning; retry fails |
| S12 | 11-22 | C: **I'm having difficulty paying** (job loss); RM: **Send TurfAI's draft** | hardship flagged, collections review, retention offer on C's Offers |
| S13 | 12-01 | Collections & finance: **Grace expired** → **Lapse the policy** | policy lapsed on 11-30 |
| S14 | 2027-01-05 → 01-10 | C: **Ask to revive this policy** with the health declaration; servicing officer **Approve the revival**; 01-10 **Pay ₹8,485 and revive** | policy revived; billing resumes |
| S15 | to 2028-05-28 | Pay ₹4,200 monthly to Apr 2028, leave 15 May unpaid; 05-27 acting as C (Ramesh), report the 05-20 death (fields pre-filled from C's sample claim form) → **Use sample documents → Death in the grace period after revival — Lakshmi Narayanan** (files: `fixtures/documents/c` + the intimation e-mail); SIU lead **Clear the claim**; claims officer sign off | fraud medium (early claim since revival) → screen cleared → settled ₹16,74,300 (in-grace ₹4,200 deducted) |

### 6.2 Underwriting variations

| # | Setup | Steps | Expected result |
|---|---|---|---|
| U1 | A1, 11-01 | Untick the KYC document, submit | TurfAI `requirements` with KYC outstanding (UWM-10); memo pre-fills **Request requirements** |
| U2 | after U1 | Underwriter **Request requirements** (keep the pre-fill) → A1 Apply "More information needed": tick the KYC on file (or **KYC** sample) → **Send to underwriting** | `uw_assess` re-runs; new memo, KYC satisfied; standard |
| U3 | A1, 11-01 | Untick the ITR | requirements: financial proof (sum above ₹50,00,000) |
| U4 | New customer, age 36–45, sum assured ₹50,00,000, KYC sample only | Submit | requirements: medical exam + lab (above the ₹40,00,000 non-medical limit). Answer with **Medical exam report** + **Lab report** samples (clean values) → standard |
| U5 | New customer | Disclose a condition / treatment in the health questions (or upload evidence contradicting a "No") | rated or refer (UWM-4 debits / UWM-9 suspected non-disclosure → medical underwriter); possibly APS required |
| U6 | any memo | Rated with a loading different from TurfAI's | accepted; the form says no letter will be attached (terms differ from TurfAI's letter) |
| U7 | memo | **Accept (standard)** with a loading typed, or **Counter-offer** with a higher sum assured | refused ("Decision not accepted") |
| U8 | memo | **Decline** without a note | refused (note required) |
| U9 | R1 applied without paying | Underwriter **Accept (standard)** (instead of Rated) | `policy_issue` → pending_first_premium; proposal parked with no portal path to pay (K16) — or an issuance review, which **Confirm issuance** refuses without a receipt |
| U10 | Counter-offer cheaper than what was paid at Apply | customer **Accept** | refused ("a refund of the excess is not supported") |
| U11 | counter-offer | customer **Decline** → **Confirm decline** | proposal "Offer declined"; nothing issued |
| U12 | requirements answered once | try to send again | refused ("already sent"); a second request from the underwriter allows a new answer |

### 6.3 Billing variations

| # | Setup | Steps | Expected result |
|---|---|---|---|
| B1 | C in dunning (after 11-15) | C: **Pay ₹4,200 now** | due paid, policy in force; dunning closes; renewal touch superseded |
| B2 | C hardship task | **Agree a payment arrangement** with Pay by after the grace end | refused ("a later date would need a revival") |
| B3 | C hardship task | **Resume reminders** after the last ladder step | refused ("no automated dunning step is left") |
| B4 | Grace expired task on a paid due | **Lapse the policy** | refused ("already paid — close the task") |
| B5 | 12-02 feed | Collections & finance: open each exception (short / over / duplicate / unmatched) and try each option | options narrowed per state; **Apply to a premium due** refused unless the due is unpaid and equals the amount |
| B6 | C | **Re-register mandate** while a due is open | `billing_collect` presents the due on the new mandate |
| B7 | A1 issued | advance to 11-24 | renewal window (7 days before 12-01) → `renewal_action` → RM hand-off (unless TurfAI stops the cadence); collected on 12-01 → hand-off superseded |
| B8 | RM hand-off whose TurfAI channel the customer did not consent to | open the task | `outreach.channel` mismatch in System checks and no **Send TurfAI's draft** option |

### 6.4 Claims variations and negative cases

| # | Setup | Steps | Expected result |
|---|---|---|---|
| C1 | A1 issued 11-01 (RCD 2026-11-01) | Report a death dated **before** 2026-11-01 | TurfAI intake: not in force (`outside_term`) → adjudicate. The insurer recompute agrees (P16.1: a death before the RCD is `outside_term`, checked first), so with no other mismatch the claim is registered for adjudication with a Claim triage notice (no `validation.policy_in_force` mismatch). Settlement is then expected to repudiate (policy not in force); its in-grace check reads the same cover (no premium deducted). *Insurer side verified in unit tests; not re-run live* |
| C2 | A2, 11-02 — **instead of** S6's clean claim, on a fresh `demo:reset` (reported after the clean claim it is the next row, C2a) | Acting as A2: **Report a claim** (fields pre-filled) → **Use sample documents → Death certificate that does not match (C2)** (or download the A2 death certificate, edit name / DOB / date and upload it; file `fixtures/documents/samples/a2_death_certificate_mismatch.txt`) | intake may hold / ask for documents (policy match, INT-2); if registered, fraud expected to find document_forensics (critical weight 5 → high) → refer to SIU |
| C2a | **C2 after the clean claim** — S6 done to "Assessed" (CLM-2026-000311 awaiting sign-off) | Acting as A2: **Report a claim** → **Report this death again** → **Use sample documents → Death certificate that does not match (C2)** → **Submit claim** | **Confirmed live (user test 2026-11-02):** `CLM-2026-000311-2`; TurfAI held it / asked for documents, `policy_found` false, no `duplicate_of` (the certificate does not match the life assured, so INT-5 does not see the same death); System checks: insurer `duplicate_of` CLM-2026-000311 vs TurfAI — → 10 mismatches. *(Since P16.1 the insurer's duplicate check uses INT-5's key — same policy + life assured + date of death, the date the run used: certified, else stated. When TurfAI takes the certificate's 21-10-2026 rather than the stated 25-10-2026, the insurer no longer names 311 as a `duplicate_of` either — the `death_event.date_of_death` mismatch against the stated date remains, so expect fewer mismatches; 311 still shows in **Other claims** with **Link as duplicate**.)* The insurer's records show 311 open: Claim triage "Triage exception — … · possible duplicate of CLM-2026-000311 (Awaiting the claims officer’s sign-off)"; **Other claims** box with 311 (sign-off status, ₹27,67,173, admit, **Open its task**), the red "TurfAI did not link it" warning with TurfAI's reason; **Duplicate of** "TurfAI — · insurer CLM-2026-000311"; **Link as duplicate** ("Links to CLM-2026-000311 (…) — open on the insurer's records") → -2 closed `duplicate` (`linked_from insurer_records`). **Register the claim** instead shows the warning and needs **Why it is a separate claim (required)** — and starts fraud ∥ settlement on -2. If TurfAI does link it, the view is C3's |
| C3 | **S6 duplicate** — S6 done to registered or further (best: "Assessed", 311 awaiting sign-off) | Acting as A2: the portal shows "You already have claim CLM-2026-000311 (Assessed) on this policy — its progress is shown above" with the form folded → **Report this death again** → **Use sample documents → Report the same death again (S6 duplicate) → Use these documents** → **Submit claim** | `CLM-2026-000311-2`; TurfAI's INT-5 links it (`duplicate_of` CLM-2026-000311, *duplicate link* letter) → Claim triage titled "… possible duplicate of CLM-2026-000311 (…)", box "TurfAI linked this claim as a duplicate of CLM-2026-000311." + INT-5 detail, **Duplicate of** "TurfAI CLM-2026-000311 · insurer CLM-2026-000311" → **Link as duplicate** → -2 closed as duplicate; portal card "Linked to your earlier claim" (the notice still names 311); 311's sign-off box lists "CLM-2026-000311-2 · closed as a duplicate of this claim". If TurfAI misses it, the case is C2a's (Link from the insurer's records). The duplicate key is the same policy + life assured + **date of death** (INT-5; the insurer recompute uses the same key since P16.1): the same death re-reported matches; a second report on the policy with **another** date of death is not a duplicate on either side (no `duplicate_of` mismatch) — it still shows in **Other claims** and the officer can still **Link as duplicate**. A nominee who only has a missing document for 311 does not need a second report: while 311 waits for documents its card offers **Add documents** (C14 / C15) |
| C4 | claim still "Received — TurfAI is checking it" | Report again | refused ("A claim on this policy is already being checked") |
| C5 | claimant not the registered nominee | Report with another claimant name | beneficiary review / nominee anomaly → triage exception; fraud nominee_anomaly +1 |
| C6 | triage exception | **Ask for documents** without a note | refused (note required); with a note → claim `requirements_pending`, portal "More documents needed" with your note and **Add documents** (C14) |
| C7 | sign-off while fraud pending | try **Sign off TurfAI's recommendation** | disabled, "Sign-off held: held until Fraud & SIU clear the claim (fraud status pending)" |
| C8 | B's case | **Submit findings** → **Use sample documents → Findings without a conclusion line (C8)** (file `fixtures/documents/samples/b_siu_findings_no_conclusion.txt`) | TurfAI no conclusion → only **Return to the investigator** (note required) → new investigator task |
| C9 | inconclusive disposition — on the CLM-2026-001007 SIU task **Use sample documents → Findings: inconclusive, enquiries open** (only on that background claim's task; file `turfai/fixtures/fraud_dispose/docs/bg1007_siu_findings.txt`) | SIU lead **Extend the investigation** / **Release to settlement** | extend → new investigator task due on TurfAI's extend-until date; release → fraud released → settlement released / re-run |
| C10 | unsubstantiated disposition (`s5_siu_findings.txt` style — not offered as a sample, §1.9; write findings that clear the case and end `Investigator conclusion: UNSUBSTANTIATED`) | **Sign off TurfAI's disposition** | fraud unsubstantiated + cleared → settlement admit becomes approvable |
| C11 | SIU screen | **Clear the claim** without a note | refused (note required) |
| C12 | settled claim | Payments on the policy | "No further premiums are collected after the policyholder's death"; mandate cancelled |
| C13 | **settled sibling** — S6 signed off (CLM-2026-000311 settled) | Acting as A2: **Report a claim** (no notice — 311 is closed) → **Use sample documents → Report the same death again (S6 duplicate)** → **Submit claim** | never auto-registered, even on a clean TurfAI routing (TurfAI is sent open claims only): Claim triage "… possible duplicate of settled claim CLM-2026-000311 (Settled)"; box lists 311 as settled; options **Register the claim** (warning "Another claim on this policy was settled: CLM-2026-000311 — say why it is a separate claim.", note required → `registered_despite_open_claim` status settled) / **Ask for documents** / **Link as duplicate** ("Links to CLM-2026-000311 (Settled) — already paid on the insurer's records" → -2 closed `duplicate`, `linked_claim_status settled`; the officer's call, TurfAI is unchanged) |
| C14 | **Incomplete claim → documents loop at triage** (Intake scenario #5 "incomplete → requirements") — fresh `demo:reset`, 11-02; worker paused, `npm run replay:a2-requirements -w @uip/worker`, worker resumed ([§7.1](#rehearse-the-documents-loop-and-the-sign-off-overrides)); or live: acting as A2 **Report a claim** → **Use sample documents → Incomplete death claim — claim form and death certificate only (P16.2)** | Claims officer: Claim triage → **Ask for documents** (note: what is missing) → acting as A2 on **Report a claim**: the claim card "More documents needed" → **Add documents** → **Use sample documents → The documents asked for — KYC, bank proof, medical attendant's certificate (P16.2)** → **Send documents** | the triage claim is `requirements_pending`, TAT clock "Paused — documents requested"; after Send documents: "N documents added to CLM-2026-000311 — TurfAI is checking the claim again", same claim back to `intimated`, **one live** `claim_intake` re-run with all the claim's documents → fast track / registered (fraud ∥ settlement then start — more live runs) or a **new** Claim triage task on the same reference (e.g. a document still missing; P16.2 check: the KYC left out on purpose → `requirements` again → new triage task). Never a second claim; the acknowledgement letter is replaced. The replayed first intake cites documents not on file (recorded contract) |
| C15 | **Settlement requirements** (Settlement scenario #6 "missing post-mortem → requirement letter, clock paused") — a claim whose sign-off TurfAI recommends `requirements`, or any sign-off where the officer chooses **Ask for documents** | **Sign off TurfAI's recommendation** (requirements) or **Ask for documents** (note required) → nominee: **Add documents** → **Send documents** | claim `settlement_requirements` ("More documents needed": TurfAI's missing documents / your note), TurfAI's settlement letter not sent on an override, TAT clock **paused**; after Send documents: "TurfAI is re-assessing the settlement", claim `under_assessment` → `claim.settlement_reassess` (documents_received) → `claim_settle` re-run (live) → a new sign-off; the clock restarts from the new last document. *Proven by tests with recorded contracts (s6 missing post-mortem / A2), not run live* |
| C16 | **Return for re-assessment** — A2's claim awaiting sign-off (S6, or `UIP_REPLAY_STOP=signoff npm run replay:a2-duplicate -w @uip/worker`) | Claims officer: **Return for re-assessment**, e.g. "loan interest looks high — confirm the net payable" | without a note: refused; with it: claim `under_assessment`, **one live** `claim_settle` run with `officer_review` → a new sign-off with "Re-assessed on the officer's return (…): …" and TurfAI's first rationale point "Officer's note: …". **Confirmed live (P16.3, exec 2510, 2m 55s):** the recommendation stays admit ₹27,67,173 — TurfAI explained the loan interest comes from the policy record; the letter does not mention the note |
| C17 | **Approve with mismatches** — `UIP_REPLAY_STOP=signoff UIP_REPLAY_SETTLE=mismatch npm run replay:a2-duplicate -w @uip/worker` (worker paused; TurfAI's net payable patched +₹1,000) | **Sign off TurfAI's recommendation** without / with the acknowledgement checkbox | the form shows **Written to the claim: ₹27,68,173** with the red "The insurer's recomputed net payable is ₹27,67,173 …" line and the mismatch box; without the tick: refused; with it: settled for TurfAI's figure, `acknowledged_mismatches` recorded |
| C18 | **TAT breach** — a replayed A2 sign-off (C17 setup, or without `UIP_REPLAY_SETTLE`), worker **paused**, Clock → **Advance to date** 2026-11-20 | Policy 360 / the sign-off drawer, then **Sign off TurfAI's recommendation** | red "TAT 15d · Breached", "Breached by 3 days (day 18 of 15)", penal interest **₹1,990** (8.75 % × ₹27,67,173 × 3 / 365; with `UIP_REPLAY_SETTLE=mismatch` the written amount is ₹27,68,173 → ₹1,991); after sign-off `penal_interest_due` (1990) and `tat_clock.delay_days` 3 on the claim and `claim.approved`. A claim asked for documents shows "Paused — documents requested"; the Claims TAT tile counts it under **Paused**. Advancing past ~11-19 with the worker running starts live `complaint_handle` runs ([§7.1](#71-working-with-the-demo)) |

### 6.5 Complaints and servicing

| # | Setup | Steps | Expected result |
|---|---|---|---|
| K1 | any customer | **Raise a complaint**, then raise another about the same matter | second refused ("You already have an open complaint …") |
| K2 | open complaint, not resolved | advance the clock past day 10 | at-risk re-check escalates → **Complaint escalation** for Compliance & grievance (title names TurfAI's escalate-to) |
| K3 | resolution task | **Add a case note** | note on the case; the same task returns |
| K4 | Ombudsman / regulator matter | raise a complaint that names the Ombudsman / regulator | TurfAI drafts a notice → Compliance **Regulator notice sign-off** |
| V1 | any policy | Ask a question about premiums / clauses | answered with citations (or Servicing escalation if out of scope) |
| V2 | any policy | Change nominee with shares not totalling 100 % | the insurer's record system refuses to write it ("nominee shares total N%, not 100%"): TurfAI is expected to hold / decline it; **Apply the change** on the escalation is refused |
| V3 | revival approved | wait more than 5 days, then approve / pay | approve refused ("the revival quote expired …"); ask again |

### 6.6 Autonomy ladder

| # | Setup | Steps | Expected result |
|---|---|---|---|
| L1 | fresh reset, Clock → **Semi-auto** → Save | **New customer…** sign-up with sample details and a sample KYC → Submit application ([§3.6](#36-showing-the-autonomy-ladder)) | `uw_assess` → memo **Decided by Autopilot** (8 checks) → `policy_issue` → `POL-LIFE-2026-9NNNNN` in force with no console touch; Decided view badge; Policy 360 event source `system:autopilot`; Dashboards → Underwriting → Auto-approved share counts it |
| L2 | as L1 | R1 (Kavya) applies at Semi-auto | TurfAI rates her (+50 %, or refers) → not clean → the memo waits for the underwriter as at HITL; the Decision card says "Left to you by the autopilot (Semi-auto): …" with the failing checks (payload `autopilot.eligible: false`) |
| L3 | Semi-auto; open memo in the inbox | switch to **HITL** → Save | the open memo stays open (not re-evaluated); the next `uw_assess` result waits too. Reset demo → badge **Autonomy: HITL** |
| L4 | **Autonomous**, limit ₹20,00,000 | A2's claim (S6) | settlement admit ₹25,00,000 + bonus − loan exceeds the limit → the sign-off waits for the claims officer ("net payable … ≤ limit" fails); with the default ₹50,00,000 the clean admit is signed off by the autopilot once fraud clears |
| L5 | no TurfAI | `npm run demo:reset` → Clock → Semi-auto → `npm run replay:a1-autopilot -w @uip/worker` | "POL-LIFE-2026-000101 issued by the autopilot"; the same badges / tile as L1 on Arjun's memo |

---

## 7. Tips, gotchas and known bugs

### 7.1 Working with the demo

- **TurfAI takes minutes.** Each run takes 40–250 s; a claim runs three workflows (intake, then fraud ∥ settlement).
  The Inbox, Runs, Apply, Ask, Payments, Report a claim, Raise a complaint pages and the storyline panel refresh
  themselves every 3 s while work is in flight; Policy 360, Offers, My policy and Dashboards do not — reload them.
- **Watch Console → Runs** to see what is pending, running, invalid or failed.
- **The clock only goes forward.** Plan the order of beats; to replay, **Reset demo**. Do not drive the clock from
  the CLI while the console is in use (K13).
- **Plain-text uploads only** (`.txt`) everywhere — Apply, requirements, claims, SIU findings. Choose what each file
  is; it matters (the kind code goes into the document id that TurfAI sees).
- **"Use a sample" buttons** (KYC, ITR / Form 16, Medical exam report, Lab report) write a document from the
  applicant's own details, labelled "Demo sample text" in the UI; TurfAI reads it as ordinary evidence. Medical / lab
  samples are clean (BP 122/80, HbA1c 5.3 %).
- **No files needed for claims and SIU findings.** **Use sample documents** on Report a claim and on Submit findings
  attaches the current Act as persona's storyline documents (console: the claim's customer), and "Report a death" opens
  with the persona's claim-form details already filled; **Download** a file to edit it and upload your own
  ([§1.9](#19-sample-documents)).
- **Sign-off signs TurfAI's recommendation.** **Sign off TurfAI's recommendation** on a claim does exactly what
  TurfAI recommended — admit, *repudiate*, or ask for documents — for the amount shown as **Written to the claim**.
  Read the decision before you click. To disagree, use **Ask for documents** or **Return for re-assessment** (both
  need a note); a return starts a live `claim_settle` run. With System-check mismatches the approve needs the
  acknowledgement tick ([§2.7](#27-claims-officer)).
- **Documents go to the same claim.** For a claim "More documents needed", the nominee uses **Add documents** on its
  card — not **Report this death again**. **Send documents** starts a live `claim_intake` (asked at triage) or
  `claim_settle` (asked at settlement) run; a registration after the intake re-run starts fraud ∥ settlement too.
- **Pause the worker before advancing the clock in a "no live run" check.** The daily scan raises
  `complaint.at_risk` on D's seeded complaint from its escalation day **11-07** onwards (re-raised daily until the case
  is escalated), and a running worker sends it to TurfAI `complaint_handle` — the billing / renewal
  scopes ([§1.7](#17-demo-scopes-cost-control)) do not cover complaints. P16.4's screenshots triggered two such runs
  by advancing to 11-19 and 11-20 (the TAT red / paused views need about 11-19 or later). Pause the worker (`kill -STOP …`, below), advance, look, then
  **Reset demo** before resuming it — resuming would send the queued events.
- **Registering over a TurfAI hold.** **Register the claim** on a triage exception overrides TurfAI's hold /
  requirements decision; fraud scoring and settlement start at once and still apply their own rules.
- **Customer letters are TurfAI's.** The app never edits them; an underwriter decision TurfAI did not draft for goes
  out without a letter.
- **Roles are a switcher, not security** (K8). Anyone can act as any role; customer tasks are answered only on the
  portal by the owning customer.
- **Background tasks.** With the default scope, background policies reach grace expiry and raise Grace expired
  tasks; use the **Storyline** filter to hide them.
- **Variance is expected** (K18): e.g. `uw_assess` may recommend refer where the storyline expects rated +50 %, or
  issuance may ask for a review. The app shows TurfAI's result faithfully; follow the human gate.
- **Mismatches can be TurfAI arithmetic.** A System checks mismatch (e.g. justified sum ₹6,02,50,000 vs ₹6,00,00,000)
  is shown for the human to judge; it does not block the decision except where §5.14 says so — but any mismatch keeps
  the autopilot off the task.
- **Reset puts the autonomy level back to HITL.** Check the **Autonomy:** badge in the console frame before a demo; a
  level saved earlier is gone after **Reset demo** / `demo:reset`.
- **The level applies to the next mapped TurfAI result, not to open tasks.** Raising it does not clear an inbox;
  lowering it does not undo an autopilot decision. Set it *before* the customer submits ([§3.6](#36-showing-the-autonomy-ladder)).
- **A second report of the same death is allowed.** While a claim is open the portal folds "Report a death" behind
  **Report this death again**; submitting still starts a TurfAI intake. The claims officer decides at the triage
  gate — read the **Other claims** box first ([§2.7](#27-claims-officer)).
- **Find the decision fast.** In the task drawer use the **Decision** chip in the header or the **Decide:** bar at the
  bottom — a bar button picks the option and brings the form into view.

#### Rehearse a duplicate claim without TurfAI

`npm run replay:a2-duplicate -w @uip/worker` plays A2's duplicate
situation on the dev DB from recorded contracts (0 TurfAI runs): 11-02 the clean claim CLM-2026-000311 → registered
→ settlement admit ∥ fraud clear → awaiting sign-off; 11-04 the same death reported again with the *Report the same
death again* set → CLM-2026-000311-2 in Claim triage. Options (environment variables):

- `UIP_REPLAY_DUP=insurer` (default) — TurfAI links nothing, the insurer's records show 311 open (the C2a view:
  "TurfAI did not link it", Link as duplicate from the insurer's records); `UIP_REPLAY_DUP=turfai` — TurfAI's
  recorded duplicate contract, `duplicate_of` CLM-2026-000311 (the C3 view).
- `UIP_REPLAY_STOP=signoff` — stop with 311 awaiting sign-off, before the second report (to show the portal notice
  and **Report this death again**).

**The dev worker must not run while it replays** — it would send the pending intake / fraud / settlement runs to
TurfAI before the script completes them. The script checks this itself and refuses to start (exit 2, nothing
written) when CLM-2026-000311 already exists (no fresh reset), when a TurfAI run is in flight, or when this repo's
worker process is running and not paused ("the dev worker is running (pid …) — pause it first: kill -STOP …"). It
quiets the TurfAI billing / renewal scopes while it replays and puts them back as they were at the end (other demo
settings, e.g. the autonomy level, untouched):

1. `npm run demo:reset` (with the worker running or not).
2. Stop or pause the worker: either stop `npm run dev` (Ctrl+C) and start only the web with
   `npm run dev -w @uip/web`, or pause the worker process in place:
   `kill -STOP $(pgrep -f '^\S*node .*loader\.mjs src/main\.ts')` (the worker's node child of `tsx watch`; `pgrep` alone shows the pid).
3. `npm run replay:a2-duplicate -w @uip/worker` (e.g. `UIP_REPLAY_DUP=turfai npm run replay:a2-duplicate -w @uip/worker`).
   It prints the second claim's triage task title and warns if any run is left pending (normally none).
4. Resume the worker: `kill -CONT $(pgrep -f '^\S*node .*loader\.mjs src/main\.ts')`, or start `npm run dev` again. Do not keep
   it paused across a `demo:reset`; if `tsx watch` restarted the worker (a code change) pause the new process.

Then work the triage in the console. **Link as duplicate** needs no TurfAI; **Register the claim**, a portal
**Submit claim** after `UIP_REPLAY_STOP=signoff`, or signing off 311 start live TurfAI runs once the worker is back.

#### Rehearse the documents loop and the sign-off overrides

The same script (same guard and worker-pause steps 1–4 above) has two more modes for Phase 16:

- `npm run replay:a2-requirements -w @uip/worker` — on 11-02 Karthik reports 311 with the *Incomplete death claim*
  set (claim form + death certificate only); the recorded intake is patched to TurfAI `requirements` (KYC and bank
  proof outstanding) → a Claim triage task, and it stops there. Then: **Ask for documents** → portal **Add documents**
  ([§6](#64-claims-variations-and-negative-cases) C14). The re-run after **Send documents** is live (worker resumed).
- `UIP_REPLAY_STOP=signoff npm run replay:a2-duplicate -w @uip/worker` — 311 awaiting sign-off (admit ₹27,67,173),
  for **Ask for documents** (no TurfAI until the nominee sends documents), **Return for re-assessment** (one live
  `claim_settle` run) and the TAT clock (C16, C18).
- `UIP_REPLAY_STOP=signoff UIP_REPLAY_SETTLE=mismatch npm run replay:a2-duplicate -w @uip/worker` — the same, with
  TurfAI's net payable +₹1,000 so the sign-off carries System-check mismatches: the acknowledgement checkbox and both
  figures beside **Written to the claim** (C17). Approving it needs no TurfAI.

Recorded settlement contracts are re-timed to the replay's dates, so the TAT System checks agree. To show a red /
paused clock, advance the clock **with the worker still paused** (tip above), then `demo:reset`.

### 7.2 Known open items a tester may hit (plan §13.1)

| # | What you will see |
|---|---|
| K1 | ~2 % of runs fail inside TurfAI ("expected json"); absorbed by the automatic re-trigger, else a System task |
| K10 | no console toggle for the billing / renewal scope (SQL only); the autonomy level has one (Clock page, [§1.8](#18-the-autonomy-level-autopilot)) |
| K11 | a `completed_with_errors` run with a valid contract may still show as not finished on a few pages |
| K13 | the CLI clock bypasses the console's settle / lock — avoid mixing |
| K16 | *(documents for a claim waiting for them: fixed in P16.2 — **Add documents**, §3.4)* Compliance cannot return a notice draft; no breach-date complaint re-check; no console form for contact-centre complaints; premium-mode change recorded but never applied; free-look refund without finance approval; late first premium (`pending_first_premium`) unreachable from the portal; reset does not run the clock-start-day scan |
| K17 | the expired-mandate billing scenario cannot be shown live |
| K18 | TurfAI judgement variance |
| — | Rejected issuance (`issuance_rejected`) has no UI to re-run issuance |
| — | *[Fixed in P16.2: a claim put on "Ask for documents" — at triage or at sign-off — now takes the nominee's documents on the same claim (**Add documents**) and TurfAI re-checks / re-assesses it; no second claim is needed.]* |

### 7.3 Known bugs (verified in code)

1. *[Fixed in P16.1: `coverOnDate` returns `outside_term` (not in force) for a date of death before the RCD, checked
   before every other branch; settlement's in-grace check follows — §6 C1.]*
   **Insurer in-force check ignores the risk-commencement date.** `coverOnDate` in
   `apps/worker/src/mappers/claim-intake.ts:46-60` treats a death after maturity as `outside_term`, and checks lapse /
   paid-to / grace, but never checks a date of death **before** the RCD. For a new policy (paid-to date after the
   death) it returns in force (`premium_paid`). TurfAI's intake rules (INT-3: "outside_term when d is before risk
   commencement") say not in force, so the claim gets a `validation.policy_in_force` / `in_force_basis` mismatch and
   is held in triage instead of being registered for adjudication. The same function feeds settlement's in-grace
   deduction check (`claim-settle.ts:72`).
2. *[Fixed in P15.2: **Link as duplicate** is now also offered from the insurer's own open claims on the policy, the
   title names the possible duplicate first, and Register over another open claim needs a note; P15.3 adds the
   **Other claims** box, the "TurfAI did not link it" warning, and keeps a re-report on a policy with a settled claim
   out of auto-registration — §2.7.]*
   **"Link as duplicate" only when TurfAI flags the duplicate.** The triage options were
   `routing.duplicate_of ? [register, request_documents, link_duplicate] : [register, request_documents]`
   (`claim-intake.ts:297`) and the decision handler refuses without it. The insurer's own duplicate check
   (`claim-intake.ts:130`: any other open claim on the policy, regardless of date of death) only produces a mismatch
   — the officer then cannot link the duplicate, only register it or ask for documents. Conversely a second claim
   with a different date of death is flagged as a duplicate mismatch by the insurer although TurfAI's rule (INT-5:
   same policy, life and date of death) says it is not one. *[That half fixed in P16.1: the insurer's duplicate check now uses
   INT-5's key — §6 C3.]*

---

## 8. Glossary

| Term | Meaning |
|---|---|
| **APS** | Attending physician's statement — the treating doctor's report, called for by the manual for some conditions (UWM-4) |
| **Arrears** | Unpaid regular premiums of a lapsed policy, paid with interest on revival |
| **Autonomy level** | Demo setting (Console → Clock): HITL · Semi-auto · Autonomous — which clean TurfAI gates the orchestrator may close itself ([§1.8](#18-the-autonomy-level-autopilot)). **Autonomous is demo only**: the specs have a claims officer approve every claim; the autopilot never repudiates, never signs a substantiated fraud case and never pays above the limit |
| **Autopilot** | The orchestrator applying TurfAI's own recommendation to a clean gate task when the autonomy level permits it; `decided_by` / event source `system:autopilot`; never a judgement of its own |
| **Contestability / s.45** | Insurance Act 1938 s.45: within 3 years of the later of RCD and last revival the insurer may repudiate for material non-disclosure or fraud; after that it cannot. "Early claim" = death inside this window |
| **Contract** | The validated JSON output of a TurfAI workflow (`Contract-<id>` node); the app uses nothing else |
| **Counter-offer** | Revised terms (loading, exclusions, lower sum assured or other term) the applicant must accept before issue |
| **DL-ACK / DL-GRV / DL-OMB** | Complaint clocks: acknowledge (3 days), resolve the grievance (14 days), Ombudsman |
| **Documents loop** | A claim waiting for documents takes them on the same claim (portal **Add documents**) and TurfAI re-checks it (intake) or re-assesses it (settlement) — [§3.4](#34-claims-intake--fraud--settlement--siu--sign-off) |
| **Duplicate claim** | A second report of the same death on the same policy. TurfAI's INT-5 decides it (same policy, life and date of death, against the open claims it is sent); the triage gate also offers **Link as duplicate** from the insurer's own open claims on the policy; the linked claim is closed as `duplicate` ("Linked to your earlier claim" on the portal) ([§2.7](#27-claims-officer)) |
| **Dunning** | The reminder / retry ladder after a failed premium debit |
| **Endorsement** | A recorded change to a policy (nominee, address, contact, premium mode) |
| **Exclusion** | A cause of death not covered, added at underwriting |
| **Free look** | 30 days after issue in which the policyholder may return the policy |
| **Grace period** | Days after the due date during which cover continues unpaid: 15 (monthly) / 30 (other modes). A death in grace is paid, less the unpaid premium |
| **HITL** | Human in the loop — the default autonomy level: every TurfAI result waits for a person |
| **ITR / Form 16** | Income tax return / salary certificate — financial proof above ₹50,00,000 (UWM-7) |
| **Justified sum** | Income multiple for the age band × annual income; cover above it is over-insurance |
| **KYC / AML / PEP** | Know your customer (identity / age proof document: Aadhaar / PAN); anti-money-laundering, politically exposed person and sanctions screening (mock list) |
| **Lapse** | The policy ends for non-payment after grace (only by a collections officer's decision here) |
| **Loading** | Extra premium (%) for higher risk |
| **Mandate / NACH / UPI AutoPay** | Standing authority for automatic premium debits (National Automated Clearing House; UPI AutoPay) |
| **Mismatch / System checks** | A deterministic fact the insurer recomputed that disagrees with TurfAI's contract |
| **Non-medical limit (NML)** | Sum assured up to which no medical examination is needed (UWM-2) |
| **Nominee** | The person named to receive the claim money |
| **Pay-out limit** | INR ceiling (default ₹50,00,000) on the net payable the autopilot may sign off at Autonomous; above it the claims officer decides |
| **Penal interest** | Interest the insurer owes on a claim settled after its TAT: (bank rate + 2 %) × the amount paid × delay days / 365 (SET-9; 8.75 % in the demo) — [§3.7](#37-the-claim-tat-clock-and-penal-interest) |
| **RCD** | Risk commencement date — cover starts; here the issue date |
| **Reconciliation** | Matching the bank's settlement feed lines to expected premiums (matched / short / over / duplicate / unmatched) |
| **Repudiation** | The insurer's refusal of a claim (policy not in force, s.45 non-disclosure / fraud, excluded peril), always with a letter and grievance rights |
| **Revival** | Re-instating a lapsed policy by paying arrears + interest with a health declaration; re-opens contestability |
| **Sample documents** | A persona's ready-made storyline evidence on Report a claim / Submit findings (**Use sample documents**, **Download**), shown only for the current Act as persona or the console task's customer ([§1.9](#19-sample-documents)) |
| **SIU** | Special Investigation Unit — screens medium-band claims, investigates referrals, signs off dispositions |
| **Suicide clause** | Suicide within 12 months of the anchor → only a limited payment (partial_suicide_clause) |
| **Suspense** | Holding account for cash that cannot yet be applied to a due |
| **TAT** | Turn-around time (claims 15 / 45 days from the last document, paused while documents are requested — [§3.7](#37-the-claim-tat-clock-and-penal-interest); complaints 14 days) |
| **Triage** | Intake's first routing of a claim: fast track, investigate, adjudicate, requirements, hold |
| **UWM / BCR / DGR / REC / RNR / SVR / INT / FRR / FDR / SET / CMR** | Section ids of the insurer rules TurfAI cites (underwriting manual, billing, dunning, reconciliation, renewals, servicing, intake, fraud rubric, fraud disposition, settlement, complaints) |
