An applicant's attorney opens AAI and types:
> draft a TD demand letter for Doe
That's the whole interaction the attorney has. They don't type aai-cite inspect 4650. They don't open a browser. They don't navigate to leginfo.legislature.ca.gov. They ask for the letter, and the assistant goes to work — pulling the applicant's case file from MerusCase, finding the missed TD payments, identifying which Labor Code sections need to be cited (LC 4650 for the timing rule, LC 5814 for the 25% delay penalty, LC 4453 for the rate cap), looking up each section's verbatim text, building the draft, and returning it. The attorney reviews, edits, signs, sends.
The lookups happen invisibly. The point of this post is what they look like from the inside.
What the assistant actually does between question and answer
When the assistant enters the letter-drafting skill, the first thing it does — before writing any prose — is run the citation inspector against each section it intends to cite. For a TD demand letter, that's at minimum LC 4650 (payment timing) and LC 5814 (penalty for delay). The shell command it fires looks like this:
node bin/aai-cite.mjs inspect 4650
And what comes back on its stdout is the live statute:
Labor Code §4650.
Amended by Stats. 2012, Ch. 363, Sec. 54. (SB 863) Effective January 1, 2013.
Effective: 2013-01-01
Div 4. · Part 2. · Ch 2. · Art 3.
Subsections: (a) (b) (c) (d) (g) (e) (f)
(a) If an injury causes temporary disability, the first payment of temporary
disability indemnity shall be made not later than 14 days after knowledge of
the injury and disability...
(d) If any indemnity payment is not made timely as required by this section,
the amount of the late payment shall be increased 10 percent and shall be
paid, without application, to the employee...
This is the operative text, with the effective date, the amendment history, and the Division/Part/Chapter/Article location. The assistant reads it the same way the attorney would — finds the subsection that matters for this letter (4650(d), the 10% increase), copies the language verbatim, and inserts it into the draft with proper attribution. The draft never says "shall be increased ten percent" when the statute says "10 percent." The verbatim quote is whatever the JSON returned.
Same for LC 5814. Same for LC 4453. The assistant inspects each section before quoting from it. No AI involvement at the lookup layer — it's a pure JSON read against the local index, milliseconds. The AI part is choosing which section to cite, which subsection answers the question, and where it lands in the letter's argument structure.
What it looks like when the attorney has a phrase in mind
Sometimes the attorney has already written the sentence and wants to anchor it to verbatim statutory language. The shape is:
> for the TD demand letter, I want to say "the late payment must be self-increased by ten percent" — find me the closest verbatim language from §4650
The assistant fires:
echo "the late payment must be self-increased by ten percent" | node bin/aai-cite.mjs suggest 4650
And gets back the highest-overlap passage from the actual section:
Closest passage in §4650. (44% word overlap)
"the amount of the late payment shall be increased 10 percent and shall be"
The assistant rewrites the sentence to use the verbatim language. The letter now quotes the statute as it reads, not as the attorney remembered it. The carrier can dispute whether 4650(d) applies to this particular missed payment, but they cannot dispute the quote. That distinction matters in practice — defense counsel routinely challenges paraphrased quotes on the wording, and a quote that matches the published text removes that argument cleanly.
What verify catches that the attorney would otherwise miss
Before showing the draft to the attorney, the skill prose instructs the assistant to pipe the whole draft through the verifier. Demonstrated on a draft with two typical mistakes:
$ echo 'Per §4650(d), the late payment "shall be increased ten percent."
We also cite §4560 for the rate cap.' | node bin/aai-cite.mjs verify
2 citation(s): 0 verified, 2 failed
✗ §4650(d) [quoted phrase not in statute: "shall be increased ten percent."]
suggested replacement (60% word overlap):
"late payment shall be increased 10 percent and shall be"
✗ §4560 [section not in knowledge base]
Two real classes of bug, both caught:
- The quote. The draft wrote "shall be increased ten percent" — close enough to sound right, wrong enough to be impeachable. The statute says "10 percent" (numeric, not spelled out). The verifier flagged the mismatch and offered the verbatim replacement.
- The section number. The draft cited §4560 — that's not a Labor Code section. (The drafter probably meant §4650.) The verifier flagged it as not in the knowledge base, so it can't be shipped.
These two failure modes are exactly the ones an attorney scanning their own draft is most likely to miss. The eye reads "ten percent" as the statute and moves on. The eye reads "§4560" and assumes someone typed it correctly. The verifier doesn't assume.
The discipline is enforced two layers up: a codified build check (check-aai-cite-triggers) refuses to ship any skill that produces legal output without invoking aai-cite, and the letter skill's own prose tells the assistant to verify the draft before showing it. The architectural guarantee is that the tool is wired in and reachable; the in-conversation guarantee is that the skill instructs the assistant to use it. If a draft slips through unverified, the verifier still runs cleanly when the attorney pipes the file through it manually.
How it doesn't work — the failures that actually ship a bad letter
The previous section is the happy path. The honest post is about the unhappy ones. Each of these is the kind of failure that produces a draft going out the door with a problem the attorney didn't catch. Some are limits of the verifier. The dangerous ones are limits of how the verifier is reached.
The right section, the wrong DOI version
The PD rating rule depends on date of injury. Injuries before January 1, 2013 are rated under LC 4660 (the old rule). Injuries on or after that date are rated under LC 4660.1 (the SB 863 rule). They produce different numbers — sometimes meaningfully different. The verifier handles both: inspect 4660 returns the old rule (and the section text literally says "This section shall only apply to injuries occurring before January 1, 2013"), inspect 4660.1 returns the new rule.
The verifier cannot tell which one applies to a given case. The skill prose says "use 4660.1 for post-2013 DOI." The assistant reads the case's DOI from MerusCase and picks. If the assistant picks wrong — or if the case has multiple injuries with DOIs straddling the 2013 cutover (which is more common than it sounds in cumulative-trauma cases) — the verifier still says "1 verified, 0 failed." The quote matches the statute returned. The statute returned is the wrong one for this case.
This is the most dangerous class of failure in the whole system: citation is verified, citation is wrong. The verifier guarantees the quote matches a section. The verifier does not guarantee the section is the right one. That second guarantee is doing for the attorney, every time, with the case in front of them.
The model invokes the tool and ignores the failure
The skill prose tells the assistant to pipe the draft through verify before showing the attorney. The skill prose tells the assistant to react to failures. The skill does not enforce the reaction. Verify prints to stdout with an exit code. A model that runs verify, sees "1 failed" in the output, and produces a draft anyway has produced an un-verified output the surrounding architecture thinks is verified.
There is no codified check that catches this. The architectural guarantee — enforced by the check-aai-cite-triggers build check — is that the skill references aai-cite. The runtime guarantee is that the model usually uses it. There is a wide gap between "tool is reachable from the skill" and "tool is consulted on every relevant draft." The gap is the discipline of the model that runs the skill on a given conversation.
This is the load-bearing honest disclosure of the whole system: AI-mediated verification is mediated by an AI. The model can skip the call. The model can read the failure and rationalize past it. The model can run verify on the draft before a hand-edit and forget to re-run after. The attorney's durable defense is to run aai-cite verify < draft.md themselves, before signing — the tool runs the same way for the attorney as for the assistant; the difference is who's applying the discipline.
The attorney writes "LC 4650" instead of "§4650"
The most common form in practice — LC 4650, LC 5814, LC 4453 — is invisible to the verifier. The citation regex matches § or Section, not LC. Demonstrated:
$ echo 'Per LC 4650(d), the late payment shall be increased.' \
| node bin/aai-cite.mjs verify
(no Labor Code citations found in text)
Zero citations seen. The drafter could have typed LC 4560 (nonexistent section) or LC 4650(z) (nonexistent subsection) or attached a wrong quote — the verifier reports "clean" because it didn't see anything to check. A skill that pipes the draft through verify expecting full coverage will not get it on an LC-form draft. The skill prose tells the model to use §; a model that defaults to LC (because that's how attorneys actually write) produces output the system thinks is verified and isn't.
This compounds with the previous failure: model invokes verify, verify sees no citations, model reports "verified — ready to send." Two silent failures stacked.
The model paraphrases the holding in a way the verifier doesn't see
The verifier checks quoted phrases. It does not check the prose around them. If a draft contains a verbatim quote from §4663(c) (correctly identified, correctly verified) embedded in a sentence that misrepresents what 4663(c) requires, the verifier has no opinion. It saw a quote. The quote matched. Exit 0.
The model could write "Per §4663(c), the QME must address apportionment with reference to the AMA Guides 6th Edition" — the quote is verifiable, the misrepresentation is in the paraphrase ("AMA Guides 6th" is wrong; California uses the 5th). Verify reports clean. The error is in the prose the verifier doesn't touch.
This is the structural limit of any quote-checker: it certifies that text matches a source. It does not certify that the conclusions drawn from the source are right. That work belongs to the attorney, every time. The verifier reduces the surface area of plausible-but-wrong content; it does not eliminate it.
The build is stale and nobody noticed
The freshness check fails the build only when the local index is 365+ days old. Below that — at 180-365 days, the "aging" range — the check warns but passes. Below 180 days, it doesn't warn at all.
The California Legislature passes amendments to the Labor Code every session. A statute that was amended six months ago and not yet rebuilt against the new pubinfo archive will return the old text on inspect. The verifier doesn't know it's old. The model doesn't know it's old. The attorney reads the inspect output, quotes it, and is now quoting last year's statute. Nothing in the system catches this — the only signal is "your build is 200 days old" in a check output the attorney never reads.
The fix is operational: rebuild the index more often than once a year, and the 180-day "aging" threshold should probably be 90, or the warn should be louder. The deeper fix is to make the staleness visible at inspect time — every inspect call could prepend "index extracted N days ago" so the attorney sees freshness next to the statute, not in a check output.
The local index disagrees with what the judge sees
The Legislature publishes corrections to the pubinfo archive between releases. A statute amended in 2025 might appear in pubinfo_2024.zip with one wording and in pubinfo_2025.zip with a tightened wording the Legislative Counsel cleaned up. A local index built from the 2024 archive returns the older form. The judge — looking it up live on the WCAB's system — sees the newer form. The attorney quotes the older form. The defense notices.
The verifier can't catch this either. It validates against the local index. If the local index is stale relative to the live statute, the verifier confirms agreement with stale data. The fix is the same fix as the previous failure: rebuild more often, surface freshness at inspect time, treat the build-from-source pattern as a recurring operation rather than a one-time setup.
The verifier mechanics — the small failures
These are real but smaller than the operational ones above. They produce ugly verify output, not bad letters. Listed for completeness:
- Punctuation-sensitive matches. A verbatim quote at the end of a sentence ("shall be increased 10 percent.") fails because the period isn't in the statute. The phrase is correct; the trailing punctuation is the drafter's. Verifier reports failure with no hint that the issue is punctuation.
- Stitched quotes fail. "the amount of the late payment shall be increased 10 percent...without application" fails because the verifier matches single substrings, not spans across an ellipsis. Both halves are verbatim; the join is not.
- Two citations, one quote, mis-pairing. "Per §4650(d) and §5814, [quote from §4650]" pairs the quote with §5814 because §5814 is closer to the quote. §5814 gets a failure for a §4650 quote; §4650 gets reported as a bare citation. The drafter has to read carefully to see the verifier confused the pair.
- Nested subsections. §4663(c)(1) fails the (1)-check because §4663(c) is one paragraph with no sub-numbering. Behavior is correct; the diagnostic doesn't tell the drafter "you probably meant §4663(c)."
What's getting fixed and what isn't
The failures above are different categories of work to fix, in roughly increasing order of difficulty:
- LC-prefix recognition. Small regex extension — add
LC\s*next to§andSectionin the citation pattern. This is a one-line change that closes the biggest blind spot in the verifier's coverage. - Punctuation tolerance at sentence-end. Strip trailing punctuation from the quoted phrase before substring match. Small, contained.
- Nested-subsection diagnostic. When (c)(1) fails because (c) has no (1), suggest "drop the (1)" in the failure report. Reuses logic already there.
- Freshness surfaced at inspect time. Every
inspectoutput could prepend "Index built N days ago from pubinfo_2025.zip" so freshness is visible next to the statute, not in a build check the attorney never sees. - Rebuild cadence policy. The 365-day fail threshold is too long for a system that quotes statute text. Tighten to 90 or 180. Operational, not a code change.
- Quote/citation pairing. Better heuristic — citation immediately preceding the colon or comma before the quote, not closest in absolute distance. Needs corpus testing on real drafts.
- Stitched-quote support. Span matching against the source text. Different algorithm. Not on the immediate path.
- DOI-aware section selection. The skill picks 4660 vs 4660.1 from context; a check that knows the DOI could refuse a 4660.1 quote on a pre-2013 case. Doable; the DOI is structured data on the case file. Not yet built.
- Model-discipline enforcement. The architectural fix is to wire verify into a post-processing step the skill cannot skip, not just into prose the model can ignore. This is a bigger change than the verifier — the skill executor would need a hook that intercepts every legal-output draft and runs verify before display. Tracked separately; not in the next pass.
- Paraphrase-around-quote. Not fixable by the verifier. The verifier checks quotes; the prose belongs to the attorney's review. The honest disclosure is that this layer always belongs to the attorney.
The first five items are the kind of work that lands in passes over the next few releases. The DOI-aware item is real work — building case-context into the verifier rather than treating it as a context-free statute lookup. The architectural-enforcement item is bigger still. Naming all of them is part of being honest about what the system does today versus what it could do.
The same inspect happens in skills the attorney doesn't think of as "drafting"
The letter-drafting skill is the obvious user. But the inspect lookup runs across the system anywhere a Labor Code section drives a calculation or a recommendation:
- The benefits skill (per-case TD/PD audit) inspects LC 4453, 4650, 5814, 4658 in the fetch block at the top of the skill — they're inspected up front so the audit reads from the live statute, not the model's memory of the rate cap or the penalty rule. LC 4656 is inspected later, before quoting any subsection. Wrong rate calculations on a TD audit are a real malpractice surface.
- The compare-cases skill (Benson allocation / apportionment review) inspects LC 4664, 4663, and 5500.5 — apportionment by prior award, apportionment by causation, and the cumulative-trauma allocation rule among carriers respectively.
- The deadlines skill (statute-of-limitations and procedural deadline check) inspects LC 5405 (1-year filing SOL) and LC 5410 (5-year outer limit) before quoting them, and inspects LC 4610.6 for the IMR appeal window.
- The settlement-value skill inspects LC 4660.1 (post-2013 PD rating), LC 4663/4664 (apportionment), LC 4659 (life pension), and LC 4658 (PD payment scale) before naming any of those sections in the settlement framework. The letter-drafting skill handles the pre-2013 cutover separately, directing an inspect of LC 4660 for the older rating rule when the date of injury calls for it.
The pattern is consistent across skills that produce output anchored to statutory text: the relevant sections get inspected at runtime, and the output is anchored to the published statute with the effective-date metadata available if anyone needs to ask "which version applied to a 2011 injury."
What "on disk" actually means
The California Legislature publishes the operative Code in a downloadable zip called pubinfo_NNNN.zip from their official distribution server. The current version is pubinfo_2025.zip. It contains the raw statute text the Legislative Counsel maintains as the canonical source of California law.
AAI ships with a build script (scripts/build-labor-code.mjs) that downloads that zip, parses it, and produces a JSON index of every Labor Code section keyed by section number. The result lives in reference/knowledge/ca-labor-code.json and gets installed with the package. Today's count: 2,631 sections, 4.5 MB on disk.
The build script is checked-in code, not a one-time data dump. When the Legislature publishes an updated zip (after a new statute takes effect), running node scripts/build-labor-code.mjs rebuilds the index from the new source. A codified check (check-knowledge-base-freshness) warns at build time if the local copy is more than a configurable number of days old. The system is honest about staleness — if you read a statute and the build is months behind, the check tells you that before the citation goes out.
This matters because the operative version of a statute changes. LC 4660.1 is a different statute today than it was in 2012. A static PDF would silently drift out of date; an LLM trained at some point in 2024 has the statute as it stood then. Neither of those is acceptable for a brief that the WCAB will read. The build-from-source pattern means the statute the assistant quotes is the statute the Legislature published, dated, with the effective-date metadata next to it.
If you want to use the tool directly
The shell commands the assistant fires are also available to the attorney directly, for the cases where a quick lookup is faster than a full REPL conversation:
# Show the full section
aai-cite inspect 4650
# Get the verbatim quote that matches a paraphrase
echo "the rate for permanent disability is capped" | aai-cite suggest 4453
# Verify a finished draft from a file
aai-cite verify < demand-letter.md
These are the same commands the assistant runs internally. There is no separate "attorney UI" and "assistant UI" — there is one tool, called the same way, returning the same output. An attorney who wants to do their own lookup mid-draft can. An attorney who'd rather say "draft a TD demand" and read the result can. Both paths converge on the same verified statute text.
The codified discipline behind it
There's a regression-guard the attorney doesn't see: check-prose-statute-refs-valid. This codified check runs at every build and verifies that every Labor Code citation across all skills and reference documents maps to a real section in the local index — currently 243 LC references validated.
This check validates section numbers — every "LC 4650" or "Section 4650" in skill prose must map to a real section in the local index. Subsection-level validity (4650(d) actually exists) is the job of the verify mode of the citation tool itself, run against finished drafts. The two checks compose: skills can't ship citing nonexistent sections, and finished briefs can be verified subsection-by-subsection before going out. A pull request that introduces a typo'd section number — a transposed digit, a wrong leading digit, a number that doesn't correspond to a real statute — fails the build check and can't ship to a customer install.
The check was added to the codebase after a typo in a skill citation went undiscovered for an extended stretch. The verifier was built so that class of bug can't ship again. The discipline is not "be careful when writing skills." It is "the build refuses to accept invalid section numbers."
What it doesn't do
- No federal statutes. This is California Labor Code only. ERISA, federal preemption analysis, Medicare Set-Aside guidelines — those are not in the local index.
- No California case law. The Labor Code is the statute. The cases that interpret it (Milpitas Unified, Escobedo, Benson, Acme Construction) are quoted by name in skill prose where load-bearing, but the case texts themselves are not indexed. Westlaw or Lexis is still the home for case-law research.
- No CCR Title 8 regulations. The 8 CCR sections (31.5(a) for the QME objection, Rule 10417 for DOR response) are cited in skill prose where load-bearing, but the regulation texts are not indexed. Extending the same build-from-source pattern to CCR Title 8 is doable using the same approach but is not currently shipped.
- No interpretation layer for ambiguous statutes. If a section's plain text is ambiguous, the verifier returns the text. The attorney does the interpretation. AAI does not pretend to know what LC 4660.1 means in a case where the body parts are disputed and the apportionment is contested.
The boundary is intentional. The Labor Code text is dispositive in a way case law isn't, and indexing dispositive text is a different operation from summarizing case law. AAI is built for the dispositive case first.
What was verified before this shipped
The local index is rebuilt from the official Legislature distribution on every build. The freshness check warns when the local copy is more than a few days old. The prose-statute-refs check verifies every LC citation in every skill against the local index. The cite-verifier-contract check (one of 30 codified checks) verifies that the verifier's own test fixtures still parse correctly.
When a Workers' Comp attorney quotes a statute in a brief, they sign their name to the accuracy of the quote. AAI's job is to make that quote accurate by construction — not by asking the attorney to be careful, but by refusing to let a wrong citation ship.