Ingest .eml: legal matters are mostly email

↗ view on GitHub · Eli Ziff · 2026-07-28 · a2dd9938

Beaver had no email ingester at all -- .eml was outside
ALLOWED_DOCUMENT_TYPES, and the LAB arm works around that by wrapping the
message as a .docx with its content unchanged. Replaying the smoke run's
audit showed what "unchanged" costs: the fixture is a real
quoted-printable message, so soft line breaks land mid-digit and the
reader sees "$85,0=\n00". Three amounts in one client email were being
read wrong by both the model and the deterministic auditors.

Measured on the LAB fixture (deterministic, no model):
  money anchors     33 raw -> 33 decoded
  malformed anchors  2 raw ->  0 decoded   ($47,00 and $85,0)
  recovered                   $47,000, $85,000, $320

lib/emailText.ts parses RFC 5322 headers (with folding and RFC 2047
encoded-words) and MIME bodies: quoted-printable and base64 transfer
decoding, charset decoding via TextDecoder, multipart traversal that
prefers the plain-text rendition of an alternative and strips HTML only as
a fallback, and embedded message/rfc822. Part bodies are handled as latin1
so string offsets slice the Buffer byte-for-byte; charset decoding happens
strictly after transfer decoding, since both encodings yield bytes.

What it will not decode becomes a typed abstention rather than a guess:
not_an_email, unsupported_encoding, unsupported_charset, part_too_large,
nesting_too_deep, encrypted. Attachments are named with their type and
size instead of inlined -- an attachment is evidence that something exists
which this file does not contain, and the reader needs to know that.

Wired through textParserFor, which is the single seam both the production
read path and the local library path already share.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018bhP89cYSdEEjHNPnxacns
Repository eliziff/Beaver
Author Eli Ziff <eliasziff@gmail.com>
Authored
Parents 1b09de4a
Stats 5 files changed , +620 , -3
Part of Chat backend features and prompt/context-budget management

Capture this commit into my fork

Download a Markdown prompt that tells Claude how to port this exact commit into your working tree. Run it via claude -p < capture-commit-a2dd9938.md from inside the repo you want the change in.

⬇ Download capture-commit-a2dd9938.md