Know whether the sorts in your case can set the job before you pull type.
SortProof is an offline command-line tool for handset letterpress printers. Give it the real UTF-8 copy for each composition stage and a CSV inventory of movable type. It returns a deterministic pull list when the job fits, or names every missing sort and quantity before composition begins.
READY: 13 sorts / 18 pieces fit the declared inventory.
Reports: sortproof-report
Next: pull type using sortproof-report/pull-list.csv.
No account, network service, font file, or shop-management system is involved at runtime.
- printers checking a form before pulling type;
- teaching shops preparing student jobs;
- community studios working from a shared case inventory;
- maintainers who need both a human report and stable JSON/CSV output.
In letterpress usage, a sort is one individual piece of type. SortProof models quantities of those pieces, not digital-font coverage.
SortProof requires Python 3.11 or newer. The simplest isolated installation uses uv:
uv tool install "https://github.com/KanadeK/sortproof/releases/download/v0.1.0/sortproof-0.1.0-py3-none-any.whl"
sortproof versionThe second command must print sortproof 0.1.0. A source archive and demo bundle are also attached to the v0.1.0 Release.
Download and extract sortproof-demo-v0.1.0.zip from the Release, then run this from the extracted directory:
sortproof compile examples/ready/job.toml examples/ready/inventory.csv --out sortproof-reportOpen sortproof-report/report.html or use sortproof-report/pull-list.csv at the case. The output directory must be new or empty; choose another name when rerunning.
The ready example proves two important rules:
front.txtandback.txtare in one stage, so their counts are added because both forms remain composed together;envelopesis a later stage, so its type is assumed to be redistributed and reused. Each glyph therefore needs the largest stage count, not the sum of every stage.
job.toml names the job and groups files that are composed simultaneously:
name = "Open house card"
[[stages]]
name = "cards"
files = ["copy/front.txt", "copy/back.txt"]
[[stages]]
name = "envelopes"
files = ["copy/envelope.txt"]Relative copy paths resolve from the manifest directory. Copy is read as UTF-8, normalized to NFC, and counted by non-whitespace Unicode code point.
inventory.csv records physical locations in pull order:
glyph,quantity,case,compartment
H,8,Upper,H
e,36,Lower,e
e,12,Reserve,e-2
!,4,Figures,exclamationThe same glyph may occupy multiple locations; its quantities are pooled and allocated in CSV order. A glyph must normalize to one non-whitespace code point. Use normal CSV quoting for a comma or quotation-mark glyph.
Every valid job writes exactly four files:
| File | Purpose |
|---|---|
report.json |
Schema-versioned job, stage, demand, allocation, and shortage evidence |
report.md |
Human-readable summary with shortages first |
pull-list.csv |
Non-zero pulls plus an explicit MISSING row per shortfall; formula-like location labels are neutralized for spreadsheet use |
report.html |
Standalone, script-free report with input-derived content escaped |
The report contract documents every JSON field and ordering rule.
| Exit | Meaning | Reports |
|---|---|---|
0 |
Inventory is sufficient (READY) |
Written |
1 |
Valid job is short or missing sorts (BLOCKED) |
Written |
2 |
Usage, input schema, UTF-8, file, or output error (ERROR) |
Not written for invalid input |
Try the other supplied examples with a fresh output directory each time:
sortproof compile examples/boundary/job.toml examples/boundary/inventory.csv --out boundary-report
sortproof compile examples/blocked/job.toml examples/blocked/inventory.csv --out blocked-report
sortproof compile examples/invalid/job.toml examples/invalid/inventory.csv --out invalid-reportboundary succeeds with decomposed/composed é and a quoted comma glyph. blocked exits 1 but writes exact shortage evidence. invalid exits 2 because the inventory header is wrong and does not create invalid-report.
| Message or symptom | Cause | Fix |
|---|---|---|
header must be exactly ... |
Inventory columns are missing, renamed, or reordered | Make the first row exactly glyph,quantity,case,compartment |
must be valid UTF-8 |
A TOML, CSV, or copy file uses another encoding | Re-save that named file as UTF-8 |
glyph must normalize to exactly one... |
A row contains whitespace or multiple code points | Put one physical sort on each row; inventory a precomposed ligature such as fi explicitly |
BLOCKED |
Declared stock is below peak demand | Read pull-list.csv; add the MISSING quantities or revise the copy |
output directory is not empty |
SortProof refuses to replace existing evidence | Choose a new/empty --out directory |
| Counts seem too low across stages | SortProof assumes redistribution between stages | Put forms that stay composed together in the same stage |
Errors identify the file plus field or CSV row and include a repair action. Expected input failures do not print a traceback.
- One normalized Unicode code point equals one sort. Multi-code-point sorts and automatic ligature substitution are not modeled; a precomposed ligature such as
fican be listed explicitly. - Whitespace and spacing material are ignored.
- There is no line width, set width, leading, furniture, imposition, lockup geometry, makeready, press, ink, paper, or labor planning.
- All type is assumed to be redistributed between stages. Simultaneously composed forms belong in the same stage.
- Results are only as accurate as the supplied copy and inventory. They are planning evidence, not a physical or typographic safety guarantee.
- v0.1.0 loads inputs into memory and targets small-shop jobs, not book-scale corpora.
uv sync --locked --dev
uv run --no-sync python scripts/check.pyThe single check command formats nothing and proves formatting, lint, strict types, unit/integration behavior, branch coverage, examples, package archives, clean wheel installation, the installed entry point, and the README's main commands. CI runs the same command on Windows and Ubuntu.
The design decision, rejected candidates, and bounded comparison with nearby projects are recorded in docs/research.md. SortProof does not claim that no comparable project can exist.
MIT © 2026 KanadeK