# Intake Triage AI

**Scores inbound legal leads, drafts the intake memo, and routes the file to the right attorney — in under a minute.**

Built for US plaintiff-side and small/mid-size law firms by Mike Rodgers (RIG).
Slug: `intake-triage`.

---

## What this is

A law firm pays $150–$300 for a lead through Google Local Services Ads, and then the lead sits in a queue in *arrival
order* until a human gets to it. The best case in the month gets the same position as the worst. About 40% of
accident-related form fills land after hours, when nobody is reading anything.

Intake Triage AI reads every inbound intake the moment it lands and does three things:

1. **Scores it 0–100** across seven weighted factors, showing its arithmetic on every point.
2. **Drafts the intake memorandum** — allegations, liability picture, medical posture, coverage, deadlines, valuation,
   next actions.
3. **Routes it** to an attorney by practice area, live caseload, resolution rate, language and tier rules — or to a
   referral partner with a fee agreement if it is outside the firm's practice.

Plus the two things that stop a claim from becoming a malpractice claim: a **statute-of-limitations flag** with a
days-remaining number per state and practice area, and a **Rule 4.2 conflict block** when the claimant is already
represented.

---

## Repository contents

```
intake-triage/
├── app/                     the working application
│   ├── index.html           single-page app shell
│   ├── styles.css           all styling, no framework
│   ├── data.js              practice areas, SOL tables, attorney roster, liability signal patterns
│   ├── seed.js              30 synthetic demo intakes (generated on the GPU fleet, hand-verified)
│   ├── engine.js            the scoring / valuation / routing / memo engine — pure functions, no network
│   ├── app.js               UI controller
│   ├── serve.py             local static server + GPU-fleet proxy
│   └── icon.svg
├── landing.html             sales page
├── icon.svg                 512×512 hand-authored SVG
├── icon-256.png             raster export
├── icon-512.png             raster export
├── screenshot.png           app screenshot used by the landing page
├── GTM.md                   ICP, triggers, prospect archetypes, 5-email sequence, 3 LinkedIn posts, objections
├── PRICING.md               packages, replacement cost, ROI arithmetic, operator margin math, contract terms
└── README.md                this file
```

No build step. No `npm install`. No dependencies beyond a Python 3 standard library for the optional proxy.

---

## How to run it

### Fastest — just open it

```bash
open ~/Developer/rig-offerings/intake-triage/app/index.html
open ~/Developer/rig-offerings/intake-triage/landing.html
```

Everything works from `file://` **except** the "Polish with GPU fleet" button, which needs the proxy below.

### Full — with the GPU fleet connected

```bash
cd ~/Developer/rig-offerings/intake-triage/app
python3 serve.py            # http://127.0.0.1:8787
python3 serve.py 8850       # or pick a port
```

On start it probes all five fleet nodes and prints which are up:

```
Intake Triage AI  ->  http://127.0.0.1:8787/
probing fleet ...
   blackwell   UP       Qwen/Qwen3-Coder-30B-A3B-Instruct
   rig-96gb    UP       qwen3-coder:30b
   rig-36gb    UP       qwen3-coder:30b
   rig-28gb    UP       qwen3-coder:30b
   rig-256gb   UP       laguna-262k:latest
```

The header pill in the app turns green and shows which node it is talking to.

**Endpoints the proxy exposes**

| Route | Purpose |
|---|---|
| `GET /fleet/status` | which nodes are up, what model each is serving (cached 25s) |
| `POST /fleet/chat` | OpenAI-compatible chat completion, routed to the first healthy node |

Node list lives at the top of `app/serve.py`. Change it there.

---

## What to try in the app

| Action | What it demonstrates |
|---|---|
| Click any lead | Full score breakdown — every factor, every note, traced to the caller's own words |
| **Triage Queue** | Scores all 30 intakes and reports the elapsed time (measured: 8.07 ms total, 0.27 ms per lead) |
| Filter **Declined** | The reject path: expired statutes, represented claimants, out-of-practice matters |
| Sort **Statute Risk** | Reorders the queue by days remaining on the limitations period |
| **Intake Memo** tab | The deterministic memo, then **Polish with GPU fleet** for the attorney-ready narrative |
| **Routing** tab | Assignment reasoning + live roster capacity bars |
| **+ New Intake** → **Fill sample** | Type a narrative and watch the liability language move the score |
| **Settings** | Move the tier thresholds or toggle a practice area — the whole queue rescores live |
| **Export** | CSV of every scored lead with tier, valuation, routing and flags |
| **Reset** | Restores the 30-intake demo queue |

---

## How the score is built

Seven factors, 100 points total. Weights are in `WEIGHTS` at the top of `engine.js` and are meant to be recalibrated
per firm.

| Factor | Max | What moves it |
|---|---|---|
| Practice Area Fit | 22 | The firm's own acceptance weight for the claimed matter type |
| Liability Clarity | 16 | 16 regex signal patterns over the caller's narrative — "rear-ended", "ran the red light", "the driver apologized", "cited", "wrong way", surveillance, witnesses, commercial defendant; minus admissions of shared fault, distracted driving, hit-and-run, pre-existing injury, low impact |
| Damages Severity | 20 | Treatment ladder (hospitalization → surgery → ER+PT → chiropractor → none), objective vs soft-tissue injury language, client-stated damages |
| Timeliness / SOL | 14 | Days since incident against a per-state, per-practice-area limitations table, plus administrative deadlines (EEOC 300 days, comp notice windows) |
| Collectability | 12 | Named carrier vs unknown vs uninsured, commercial/institutional defendant, hit-and-run |
| Claimant Engagement | 8 | Channel quality, contact-record completeness, narrative length, language |
| Conflicts & Exclusivity | 8 | Currently represented (hard block), prior counsel (lien exposure) |

**Hard rules override the score.** `hasRepresentation` → Tier D, "Do Not Contact", routing blocked under Model Rule 4.2.
Expired statute → Tier D, "Decline — statute", partner review required.

**Valuation** is a base case value per practice area, modulated by damages and liability, blended with the client's own
estimate when present, then multiplied by a calibrated sign-probability logistic to produce an expected fee value. The
probability ceiling is deliberately capped at 0.80 — a high triage score is a strong lead, never a signed client.

---

## What is real vs what is stubbed

### Real — this code actually does the thing

- **The scoring engine.** All seven factors, all thresholds, hard-rule overrides, and the full explainable breakdown.
  Pure functions in `engine.js`, deterministic, no network. Run it a thousand times and get the same answer.
- **The statute-of-limitations math.** Per-state, per-practice-area table with administrative-deadline overlays.
  Computes real days-remaining and flags anything inside the warning window. Demo dates are stored as *offsets*, so the
  arithmetic stays live no matter when you open it.
- **Conflict and Rule 4.2 blocking.** Represented claimants are hard-blocked from routing and from the contact path.
- **The routing engine.** Real assignment against a 7-attorney roster with live caseload, resolution rate, language and
  tier profile. Tier A files weight closer quality over capacity; Tier C files weight capacity. Out-of-practice matters
  route to a referral partner with a fee share.
- **The valuation model.** Case value, contingency fee, sign probability, expected fee value — all computed, all shown.
- **The memo generator.** Full structured intake memorandum built locally from the scored record.
- **The AI polish layer.** Genuinely calls the RIG GPU fleet (`blackwell` → Qwen3-Coder-30B on 3× RTX PRO 6000, falling
  through to the LAN nodes) via `serve.py`, and rewrites the structured record into a 5-paragraph attorney-ready
  narrative. Verified working end to end: ~2,900–3,400 characters in 25–60 seconds.
- **Every UI interaction.** Filters, sorts, search, tabs, new-intake form with live scoring, settings that rescore the
  whole queue in real time, CSV export, localStorage persistence, reset.
- **The demo data.** 30 intakes generated on the fleet across three nodes, then hand-verified for schema validity,
  jurisdiction sanity, and quality distribution (7 Tier A / 9 Tier B / 4 Tier C / 10 declined).

### Stubbed / simulated — be honest about this in any demo

- **No CRM integration.** There is no Litify, Filevine, Clio Grow, Lawmatics, SmartAdvocate or CasePeer connector in
  this repo. The landing page and PRICING.md list them as part of the paid setup, which is accurate — they are setup
  work, not shipped code.
- **No live intake ingestion.** No webhook receiver, no email parser, no call-transcript pipeline. Leads come from
  `seed.js` or from the New Intake form.
- **No authentication, no multi-tenancy, no server-side persistence.** State lives in the browser's localStorage. This
  is a single-operator demo, not a hosted product.
- **The attorney roster and referral partners are fictional.** `Hartwell & Voss LLP` does not exist. Caseload numbers
  are static, not fed from a real system.
- **The statute-of-limitations table is a simplified default.** It covers 14 practice areas with per-state overrides
  for the largest jurisdictions and falls back to a default elsewhere. It is deliberately labeled as not legal advice
  in `data.js`, in the memo footer and on the landing page. A real deployment verifies it against the firm's actual
  filing jurisdictions with the firm's own counsel before go-live.
- **The valuation model is heuristic, not actuarial.** Base case values are informed estimates, not a regression on
  settlement data. Calibrating them against a firm's own closed-case history is explicitly part of the paid setup.
- **The sign-probability curve is uncalibrated.** The logistic is shaped to be conservative, but no real signed/declined
  dataset has been fit to it. Same caveat: that is setup work.
- **No SLA enforcement.** The app computes and displays an SLA deadline. Nothing pages anyone when it is breached.
- **No client has bought this.** There are no case studies, no reference customers, and no accuracy benchmark against a
  real firm's intake log. Every number in `PRICING.md` marked as an assumption is an assumption. The "12-attorney PI
  firm" is a reference model, not a customer.

### Business collateral status

`GTM.md` and `PRICING.md` are **drafts staged for approval**. Nothing has been sent, no list has been enriched through
Apollo, no LinkedIn post has been published. Gate-D applies to all of it.

---

## Fleet used to build this

| Node | Hardware | Endpoint | Used for |
|---|---|---|---|
| blackwell | 3× RTX PRO 6000 | `100.67.126.117:8000/v1` (vLLM, Qwen3-Coder-30B-A3B-Instruct) | GTM copy draft, 10 demo intakes, live AI memo polish |
| rig-96gb | LAN node | `100.102.142.84:11434/v1` (qwen3-coder:30b) | 14 demo intakes |
| rig-36gb | LAN node | `100.89.143.27:11434/v1` (qwen3-coder:30b) | ICP + prospect archetypes draft, 6 demo intakes |
| rig-28gb | LAN node | `100.76.209.22:11434/v1` (qwen3-coder:30b) | pricing draft |
| rig-256gb | LAN node | `100.91.39.12:11434/v1` (laguna-262k) | standby |

Fleet output was used as **raw material**. Every generated artifact was reviewed, re-checked for arithmetic, and
rewritten by hand where it was wrong — the first pricing draft, for example, claimed $1.5M in annual attorney-time cost
for a 12-attorney firm, which is nonsense. All arithmetic in the shipped `PRICING.md` and on the landing page has been
verified line by line.

---

## Verification performed

| Check | Result |
|---|---|
| App renders in Chromium at 1680×1000 | pass, screenshot captured |
| Console + page errors on load and through the full interaction path | **0** |
| Queue renders all 30 seeded intakes | pass |
| Tier filters (A / D) return the right subsets | 7 / 10 |
| Search across name, county, carrier, narrative | pass (`premises` → 4) |
| Statute-risk sort reorders the queue | pass |
| All four detail tabs render non-empty content | pass |
| New Intake form → scored, tiered and routed | pass (Rosa Delgado → 94.3, Tier A, Dana Whitfield) |
| Threshold slider rescores the whole queue live | pass (Tier A count 8 → 1 at threshold 90) |
| Practice-area toggle changes fit weights and rescores | pass |
| CSV export downloads | pass, 5.5 KB |
| localStorage persistence survives reload | pass (31 leads) |
| Triage Queue scores everything | pass, 31/31 |
| Engine throughput, 30 leads × 10 runs | 8.07 ms total, 0.269 ms/lead |
| AI memo polish through the fleet proxy | pass, 2,933 characters generated on `blackwell` |
| Landing page renders full-page at 1440px | pass, screenshot captured |
| Icon renders legibly at 512px and at 64px | pass |
| ROI arithmetic in PRICING.md and landing.html | recomputed in Python, all lines reconcile |

---

## License / status

Internal RIG offering. Not open source, not published, not sold. Decision-support software — not legal advice, and
attorney review is required on every intake.
