Catch missing, swapped, leaked, duplicated, or unfinished personalized documents after generation and before delivery.
BatchSeal is an offline CLI that compares a recipient CSV with a folder of final PDF, DOCX, TXT, and EML artifacts. It proves that every recipient has one expected file, checks required markers, catches another recipient's isolation marker, finds leftover template tokens, and blocks duplicated output. It does not generate or send mail.
A merge job can exit successfully while attaching Bob's letter to Alice, omitting a
file, leaving {{ amount_due }} in a document, or emitting the same statement for
hundreds of recipients. Template-unit tests do not inspect the final batch as a set.
BatchSeal does.
recipient CSV + generated folder
|
v
completeness + isolation + residue checks
|
+-----+-----+
| |
PASS(0) FAIL(1)
|
JSON + CSV + HTML
uv sync --frozen --all-extras
uv run batchseal audit --manifest examples/good/recipients.csv --input examples/good/output --report-dir reports/goodExpected result:
BatchSeal PASS: 4 recipients, 4 artifacts, 0 errors
Reports: reports/good
Now run the deliberately broken batch:
uv run batchseal audit --manifest examples/bad/recipients.csv --input examples/bad/output --report-dir reports/badIt exits 1 and demonstrates every v0.1.0 finding from BS001 through BS009. The sample
contains only synthetic names and .test email addresses.
From the tagged source:
pipx install git+https://github.com/KanadeK/batchseal.git@v0.1.0
batchseal versionOr download the wheel from the latest GitHub Release:
pipx install batchseal-0.1.0-py3-none-any.whlBatchSeal requires Python 3.11 or newer; CI tests Python 3.11 through 3.14 across Windows, macOS, and Linux.
recipient_id,expected_file,name,email,required_terms,isolation_terms
alice,letters/alice.pdf,Alice,alice@example.test,Alice|ID-A,ID-A|alice@example.test
bob,letters/bob.docx,Bob,bob@example.test,Bob|ID-B,ID-B|bob@example.test| Column | Required | Meaning |
|---|---|---|
recipient_id |
yes | Unique row key serialized in reports; default required and isolation marker. |
expected_file |
yes | Relative .pdf, .docx, .txt, or .eml path below --input. |
name |
no | Display name retained in the manifest only. |
email |
no | Added to default isolation markers when explicit markers are absent. |
required_terms |
no | ` |
isolation_terms |
no | ` |
Use stable, unique customer or case IDs as isolation markers. Names such as John are
usually too common. Matching is Unicode-aware and case-insensitive.
| Code | Meaning |
|---|---|
| BS001 | Expected artifact is missing. |
| BS002 | Supported artifact is not assigned in the manifest. |
| BS003 | Multiple recipients map to one artifact. |
| BS004 | Artifact cannot be parsed. |
| BS005 | Artifact has no extractable text. |
| BS006 | Required recipient term is missing. |
| BS007 | Another recipient's isolation term appears. |
| BS008 | An unresolved {{...}}, <<...>>, or named placeholder remains. |
| BS009 | Distinct assigned artifacts have identical SHA-256 content. |
See the repair guide for the exact recovery path for each code.
Every audit writes:
batchseal-report.json— versioned machine contract (schema);batchseal-findings.csv— spreadsheet-friendly remediation queue;batchseal-report.html— self-contained human review page with no JavaScript.
Reports contain recipient IDs, relative paths, counts, stable codes, and artifact digests, but never document excerpts or matched recipient terms. Use opaque IDs and filenames if reports will be shared beyond the delivery team.
| Exit | Meaning |
|---|---|
| 0 | Audit completed and no errors were found. |
| 1 | Audit completed and the batch is not safe to deliver. |
| 2 | Manifest, input, command, or report configuration is invalid. |
- PDF inspection uses the text layer. Image-only scans need OCR before BatchSeal.
- Password-protected PDFs are rejected.
- DOCX body, header, and footer text are inspected; macros and embedded active content are never executed.
- EML recipient/subject headers, plain-text bodies, and supported PDF/DOCX/TXT attachments are inspected; HTML-only message bodies are outside v0.1.0.
- Each expected artifact and inspected DOCX XML member is limited to 25 MiB.
- A pass is a targeted batch QA result, not malware, DLP, or regulatory certification.
All processing is local. BatchSeal has no network code, telemetry, cache, mail sender, or mutation path for input documents. Read the threat model and security policy before regulated use.
Run the same release gate used by CI:
uv run python scripts/release_check.pyIt runs lint, format, typing, coverage, dependency audit, wheel/sdist build, clean-wheel
installation, and both example batches. If it stops, fix the first reported command and
rerun the gate; do not skip or weaken the failing check. For audit failures, follow
docs/repair-guide.md, regenerate, and rerun the original audit.
uv sync --frozen --all-extras
uv run pytest -q
uv run ruff check .
uv run ruff format --check .
uv run mypy src tests scripts
uv buildArchitecture and scope live in SPEC.md. The selection research and explicit non-goals are in docs/research.md. Contributions are described in CONTRIBUTING.md.
MIT © 2026 KanadeK