← Back to AAI for MerusCase

Audit Case

The integrity sweep. For every upload on a case, AAI re-downloads the PDF, re-reads the applicant name out of the file natively, and verifies the PDF actually matches the case applicant. Catches misfiles, wrong filenames, and wrong activity tags. Nothing is ever auto-corrected — the attorney sees every proposed fix before any write.

The upload IDs, case numbers, applicant names, and filenames in this page are fictitious demonstration data. Your install runs against your real case.

On this page

What it is

At the prompt:

aaicase> audit case Roe

After resolving the case and counting its uploads, the skill stops and asks before doing anything:

“Found 14 uploads on Roe #9739. Auditing each one will cost this will use API budget. Proceed?”

If approved, AAI walks through every upload, one at a time, and re-binds each PDF by reading the applicant name directly out of the file. At the end you get a findings table broken into three buckets: critical (wrong case), warning (right case but wrong filename or tag), and clear (everything matches). No correction happens without the attorney’s explicit approval.

Why this skill exists

Workers’ Comp practice generates thousands of PDFs across hundreds of cases, and the failure mode that hurts most is the same one across firms: the wrong PDF lands on the wrong case. A medical report for Roe gets filed on Doe. A defense letter named after the right person ends up linked to the wrong case file. An activity gets tagged “QME Report” when the upload is actually a subpoena.

These mistakes don’t reveal themselves at file time. They reveal themselves at trial, at a depo, or when a client realizes the firm doesn’t have what it should — or has something it shouldn’t. audit case is the only systematic way to find these before someone else does.

Bind by PDF content — not by filename, not by tag

AAI does not trust filenames to determine what a PDF is or who it belongs to. It downloads the PDF, reads it natively (Claude vision — not text extraction), and pulls the applicant name from inside the document.

This is the architectural rule the whole “code-guarded” claim rests on. A filename like 2026-05-08-da-letter-qme-panel-strike-roe.pdf says “this is for Roe.” The PDF inside might say something different. The skill believes the PDF, not the filename. Every binding decision in the system — mail processing, audits, re-filing — works the same way.

That’s why the misfile sweep is even possible. If filenames were truth, there would be nothing to check.

No caching, ever

Every audit run downloads and re-reads every PDF. The skill explicitly refuses to use a previously-stored binding for this purpose. Caching would mask the very problem the audit exists to detect.

If AAI bound a PDF last week as belonging to “Roe,” and someone later replaced the upload with a different PDF, the cached binding is now a lie. The audit’s job is to catch exactly that scenario, so it always re-binds from scratch.

This is by design and not configurable. It is also the source of the skill’s cost — every audit re-pays the binding cost for every upload.

How it runs (under the hood)

  1. Resolve case. merus-search finds the case by name or applicant; if multiple matches, the attorney picks.
  2. Get the canonical applicant. One caseFiles/view fetch pulls the case’s applicant name, claim number, and status. This is the truth the audit compares against.
  3. Inventory uploads. A activities/index/[CASE-ID] fetch lists every activity with an attached upload. The audit only checks content-bearing activities.
  4. Confirm cost with the attorney. The skill stops here and asks before spending API budget.
  5. Sequential re-binding. For each upload, in sequence (never parallel): download to a per-run mktemp -d temp dir locked to chmod 700, run aai-bind to re-extract the applicant from the PDF natively, delete the temp file. Show progress: “Auditing upload N of TOTAL...”
  6. Fetch current Merus state per upload. A second pass pulls each upload’s current case_file_id and filename for comparison against the binding.
  7. Build the findings table. Each row gets the five checks below. No corrections yet — the audit only reports.
  8. Present + approve. The attorney sees every proposed correction and approves all, some, or none. They can edit any proposal (“for #9001 use a different filename”).
  9. Execute via sequencer. Approved corrections run through aai-sequence.mjs with full rollback support.
  10. Verify. Re-fetch each corrected upload’s state and confirm the fix landed.

Sequential, not parallel. The whole serial model exists to prevent upload IDs from crossing wires.

The five checks per upload

CheckWhat it catches
Applicant matchThe PDF’s bound applicant last name vs. the case’s applicant last name. Mismatch = critical: the PDF is about someone other than the case applicant.
Filename date prefixFilename starts with YYYY-MM-DD-. The firm convention is enforced; missing date prefix is a warning, not critical.
Filename applicantFilename (lowercased, alphanumeric only) contains the bound applicant’s last name. Catches wrong-applicant filenames even when the upload is on the right case.
Activity tag fitThe activity’s tag matches the bound document type, using a heuristic mapping (QME report → tag 47935, subpoena → 27960, UR/IMR → 47933, etc.). Mistagged activity = warning.
Activity description fitActivity description mentions either the bound applicant’s last name or the document type. Catches copy-paste errors from another case where the activity text was carried over without editing.

What the findings table looks like

AUDIT — ROE, JANE #9739
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Case applicant:  ROE, JANE        Claim [ADJ#]
Uploads audited: 14

CRITICAL (wrong case — PDF content doesn't match)
  ✗ Upload #[ID] — bound applicant DOE (≠ ROE)
        Filename:  2026-05-08-da-letter-qme-panel-strike-roe.pdf
        Activity:  [ID] [tag 47955 DA Letter]
        Suggested: move to DOE #9734, rename to
                   2026-05-06-subpoena-noncompliance-sample-copy-doe.pdf
                   re-tag activity to [27960 Subpoena]

WARNINGS (right case, but filename or tag issue)
  ⚠ Upload #[ID] — filename missing YYYY-MM-DD prefix
        Filename:  "AA PANEL STRIKE - ROE.pdf"
        Suggested: rename to 2026-05-08-aa-panel-strike-roe.pdf

  ⚠ Upload #[ID] — activity tag doesn't match document type
        Filename:  2026-04-22-da-letter-roe.pdf  (matches case)
        Activity:  [ID] [tag 47935 QME Report]
        Bound doc_type: "Defense Letter"
        Suggested: re-tag activity to [47955 DA Letter]

CLEAR (12 uploads)
  ✓ [list of upload IDs]

PROPOSED CORRECTIONS (4 actions across 2 uploads + 1 activity)
  A. Approve all
  1, 2, 3 — approve specific
  E. Edit any (e.g. "for #9001 use different filename")
  S. Skip — log findings without fixing
  Q. Quit

The attorney chooses. The audit will not move a single byte until they do.

How corrections happen (sequencer + rollback)

Most corrections are multi-step. Moving a misfiled upload to the right case also means moving the linked activity. Re-tagging an activity means soft-deleting the wrong-tagged activity and creating a new one with the correct tag — Merus doesn’t support in-place tag edits.

Every correction runs through aai-sequence.mjs: each step has an args and an undo_args. If any step in the sequence fails, the sequencer rolls back the prior steps. The case never ends up in a half-corrected state where the upload moved but the activity didn’t, or the activity got soft-deleted but the new one was never created.

The rollback machinery uses structured errorType values from merus-fetch, not English error message matching — the previous (fragile) approach. If the API emits a binding guard refusal, the sequencer sees errorType: "binding_guard" and knows to abort cleanly.

The merus-fetch binding guard

Even with attorney approval, the API layer has the final word. merus-fetch.mjs ships with a binding guard: any write that would move an upload to a case_file_id where the binding doesn’t match the new case’s applicant is refused at the API call, not just discouraged.

The flow is:

  1. Audit proposes: “Move upload #[ID] from Roe #9739 to Doe #9734.”
  2. Attorney approves.
  3. Sequencer issues: POST /uploads/edit/[ID] case_file_id=[DOE-CASE-ID].
  4. merus-fetch checks: does the stored binding for upload #[ID] match the applicant on the target case?
  5. If yes: the write proceeds.
  6. If no: the write is refused. The sequencer fails at this step. No undo needed because nothing was written.

This is “code-guarded” in the literal sense. Misfiles are not prevented by prompting (“please don’t do this”); they are refused at the API layer. The same guard also enforces filename conventions — if the proposed filename doesn’t contain the correct applicant’s last name, the write is refused.

Delete policy — what gets deleted, what doesn’t

Uploads are never deleted. Misfiled PDFs get reassigned to the correct case, not erased.

A deleted upload is gone; a reassigned upload moves to where it should have been. Reassignment is reversible (the binding history shows where the file used to live); a hard delete is not. The skill is built on the principle that integrity is restored by moving the PDF to its correct home, not by erasing it. This applies even when the misfiled upload looks like a clear duplicate — the duplicate gets flagged in the findings; the attorney decides whether to leave it, reassign it, or use Merus’s native deletion workflow outside AAI.

Activity tags can be soft-deleted inside a sequence. When an activity has the wrong tag and needs to be replaced (a “QME report” activity that should have been tagged as “Subpoena Response” because the PDF was misclassified), the audit proposes a two-step operation inside aai-sequence:

  1. deleted=1 on the wrong-tagged activity
  2. /activities/add with the correct tag + same upload_id

If either step fails, the sequence rolls back (the deleted=0 undo runs and the new activity gets cleaned up). The audit log shows both steps. This is the only place audit-case ever issues a delete-type write, and it’s only ever inside an atomic sequence with a verified undo path.

Tasks are never deleted. Tasks have audit-trail value — what was assigned, who closed it, when. Completion preserves the trail; deletion removes it. The audit completes stale tasks; it never deletes them.

Cost — this skill spends API budget

Each upload costs roughly API budget for the binding extraction (varies with PDF size). For a case with 50 uploads, expect the estimate. The skill always tells the attorney the estimated cost before starting and waits for confirmation.

The attorney can scope the run: “do all of them,” “just the recent 10,” “only the ones with weird filenames.” The audit will narrow the inventory to whatever subset is approved.

Audit case vs. audit [case]

Despite the similar names, these are different skills with different purposes:

audit caseaudit [case]
GoalFile integrityCase strategy
ReadsFirst few pages of every PDF (enough to bind the applicant)Pages 1–5 of every PDF (enough to extract findings)
FindsMisfiles, wrong filenames, wrong activity tagsMoney on the table, body-part mismatches, UR/LC 4610 violations, late objections
Writes backUpload reassignments, filename edits, activity re-tags (via sequencer)Case comments summary + action tasks
Time~5–10 min depending on upload count~4 min

Run audit case when you want to be sure the file is right. Run audit [case] when you want to be sure the case is right.

When to run it

Troubleshooting

“aai-bind returned UNKNOWN applicant”

The PDF is unreadable or has no clear applicant name (rare, but happens with cover sheets, blank pages, or heavily redacted documents). The audit flags it as a warning, not critical, and suggests manual review. Open the upload with read upload [id] and decide.

“PDF is encrypted or password-protected”

aai-bind cannot read the file. Flagged as warning. The fix is on the Merus side — obtain an unencrypted copy of the document.

The audit wants to reassign a duplicate upload

It will tell you it’s a duplicate in the findings. The reassignment proposal is still correct (move the duplicate to where it belongs); whether to keep the duplicate at all is a separate decision for the attorney.

The sequencer rolled back a correction

A later step in the same sequence failed, so the earlier step was undone. The case is back in the state it was before the correction started. The audit will tell you which step failed and why — usually a transient API issue (re-run) or a binding guard refusal (the binding doesn’t support the proposed move, which is the guard doing its job).

Attorney says “stop” mid-audit

The skill stops and shows partial findings. Progress through the current upload completes (the download was already paid for); the next upload is not started. No corrections are applied without explicit approval, so partial audits are safe to abandon.

Cost estimate seems high

The estimate is per upload, not per case. A 50-upload case is genuinely more expensive than a 10-upload case. Use the “just the recent 10” or “only the weird filenames” scopes to narrow the run if cost is a concern.

Part of AAI for MerusCase — code-guarded AI case intelligence for California Workers’ Comp attorneys.