The universal document reader. Download any upload on any case by ID, read it natively (Claude vision for PDFs and images, in-process conversion for Office formats), summarize what it is, extract deadlines and action items, and clean up the file. One command, no extraction libraries, no third-party tools, no persistence after the read.
At the prompt:
aaicase> read upload [UPLOAD-ID]
aaicase> read the QME report on Doe
aaicase> what's in upload [UPLOAD-ID]
The skill downloads the document, identifies the file type, picks the right read strategy, reads it through Claude natively, and produces a structured summary: document type, applicant name, ADJ#, date, content summary, any deadlines, any discrepancies with the case data, and any follow-up actions.
One step from the attorney’s point of view: “read the defense letter” → download, read, summarize in a single round trip.
Two input modes:
read upload [UPLOAD-ID] — goes straight to the download step.read the defense letter on Doe — the skill resolves the case, then scans the case’s activity feed for matching uploads, then asks which one if multiple match./activities/index/[CASE-ID], NOT /uploads/index. The global uploads index is capped at ~1,900 recent files and misses older uploads on long-running cases.
This is one of those Merus quirks worth knowing: the activities endpoint with the case ID as path parameter returns every activity (and therefore every upload reference) on the case, no matter how old. The global uploads index has a recency cap that makes it unsuitable for finding documents on cases more than a few months old.
This matters because the documents this skill is most often run against — QME reports, UR/IMR decisions, subpoenas, defense letters, medical records — are exactly the kinds of PDFs that text extraction breaks on:
Native vision reads them like a person reads them. Text extraction returns garbage from any of them — or worse, returns partial garbage that looks plausible and quietly omits critical content. The skill never takes that risk.
This is also a CI-guarded rule in the codebase: any commit that re-introduces a text-extraction dependency on PDFs fails the build.
For PDFs over 50 pages, the skill plans the read in windows rather than asking Claude to read 200 pages in one shot. The window strategy:
| Page count | Strategy |
|---|---|
| 1–50 | Single Read call covering the whole document. |
| 51–200 | 50-page windows: pages: "1-50", then pages: "51-100", etc. |
| 200+ | 75-page windows, with a TOC scan first to identify which ranges matter. |
The windows are read sequentially in the same session — not dispatched to sub-agents, not split into separate PDFs with pdfseparate. The Read tool’s pages parameter handles the slicing internally; the skill just plans the sequence and walks it.
Why sequential and not parallel: the answer being assembled depends on what was read before. A QME report’s pages 30–40 only make sense in context of pages 1–10. The sequential read lets each subsequent window’s extraction benefit from what was already understood.
Word and Excel files need conversion before they can be read — Claude has direct vision for PDFs but not for binary Office formats. The skill uses bin/merus-convert.mjs, an in-process Node helper that:
merus-convert.mjs --info returns sheet names, row counts, and column counts so the skill knows what it’s dealing with before reading.The conversion output lives in the same per-run temp dir as the downloaded source. Both files are deleted at the end of the read.
| Format | Strategy |
|---|---|
| JPEG, PNG, TIFF, WebP, GIF | Read directly via Claude vision. Same as PDF — native, no conversion. |
| RTF | Convert to plain text via the same in-process helper. |
| EML (email) | Parse RFC 5322 headers + body in-process; read as text. |
| .doc, .xls (legacy) | Reported to the user as “needs conversion” — no native Node parser for these formats. Recommend re-saving as .docx/.xlsx. |
For everything else (encrypted PDFs, password-protected files, unknown binary formats), the skill stops and tells the attorney what it found. It doesn’t guess at content.
After reading, the skill produces a structured summary:
If any of these surface a real issue (wrong applicant, missed deadline), the skill offers to create a task on the case rather than just reporting.
The skill keeps a library of extraction templates in skills/templates/ — specialized prompts for each document type that produce structured output tuned to the document’s schema:
| Template | Used for |
|---|---|
qme.md | QME and AME reports — WPI percentages, P&S date, apportionment |
subpoena.md | SDTs and Notice to Consumer (CCP 1985.3) |
ur-denial.md | Utilization Review decisions from carriers/UR vendors |
imr-decision.md | Independent Medical Review determinations from Maximus |
medical-records.md | PTP visit notes, hospital records, imaging reports |
generic.md | Anything not matching a specialized template |
The skill picks the template from what the cover page says, not from the filename. UR (carrier-side) and IMR (Maximus) decisions look superficially similar but have different deadlines (LC 4610 vs. LC 4610.6) — the right template is critical for the extracted deadline to be correct.
Every read creates a fresh temp directory:
TMPDIR=$(mktemp -d "${TMPDIR:-/tmp}/aai-doc-XXXXXX") && chmod 700 "$TMPDIR"
The download lands inside that directory. Any conversion outputs land in the same directory. At the end of the read, the directory is deleted unconditionally:
rm -rf "$TMPDIR"
No caching, no retention. Each read re-downloads from Merus, even if the same upload was read minutes ago. The reasoning is the same as audit case’s no-cache rule: a cached document is a stale document, and stale documents lie about what they currently say.
The Merus token is never exposed via the command line — the skill uses merus-fetch --download, which keeps the token in headers; never via curl, which would expose the token in ps output. This matters on multi-user machines.
READ — Upload #[ID]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
File: 2026-04-22-qme-report-sample.pdf
Type: PDF (12 pages, 2.4 MB)
Downloaded: /tmp/aai-doc-XYZ123/doc.pdf
Read plan: single window (≤50 pp)
Template: qme.md
EXTRACTED:
Document type: QME Report (LC 4060)
Applicant: Doe, Jane
ADJ: [ADJ-#]
Document date: 2026-04-22
QME physician: Dr. Sample, M.D. — Orthopedic Surgery
Key findings:
• P&S declared as of 2026-04-15
• WPI: 12% lumbar spine
• WPI: 5% right knee
• No apportionment to non-industrial causes
• Work restrictions: no lifting > 25 lbs, no repetitive bending
• Future medical: ongoing PT + ortho consults
Deadlines triggered:
⚠ QME objection: 8 CCR 31.5(a) — 30 days from report date
Deadline: 2026-05-22 (26 days remaining)
Follow-up actions:
1. File objection if rating is disputed (deadline above)
2. PD payment must initiate within 14 days of P&S (LC 4650(b))
— check benefits skill to verify
3. Update case comments with P&S date
Discrepancies with case data:
✓ Applicant matches Case [CASE-ID]
✓ ADJ matches case injury record
⚠ Body parts: report addresses lumbar + right knee, but case
application lists only lumbar. Consider amended application.
NEXT STEPS:
1. Run "deadlines on [CASE-ID]" to confirm the QME 30-day objection date
2. Want to create a task to file objection? (yes/no)
3. Run "audit on [CASE-ID]" to surface PD payment activities and
cross-reference against the QME WPI rating
The upload ID doesn’t exist, was deleted, or you don’t have permission. Verify the ID via the case’s activity feed (timeline on [case] shows upload IDs).
The skill stops — AAI doesn’t attempt password cracking. Get an unencrypted copy from the source (carrier, defense, opposing counsel) and re-upload.
For pure scanned PDFs, Claude vision still reads the visual content — the “no OCR layer” warning means text extraction would fail, but the vision read should work normally. Continue past the warning.
If the document is over 50 pages and the key content is past the first window, the skill may have stopped at the first window’s extraction. Ask for the specific page range: read upload [ID] pages 51-100.
The pure-Node converters don’t handle the binary .doc/.xls formats. Re-save as .docx/.xlsx in Word/Excel and re-upload.
The skill chose based on cover-page content. If it picked generic.md when the document is clearly a QME, the cover page may not have used standard QME language. Run the read again with an explicit template hint, or use audit [case] for case-level context that helps template selection.
The skill cleans up with rm -rf at the end of every run. If you see a leftover /tmp/aai-doc-* directory, the skill crashed mid-run. The directory is owner-locked (chmod 700), so it’s readable only by you; delete it manually.
Part of AAI for MerusCase — code-guarded AI case intelligence for California Workers’ Comp attorneys.