Skip to content

Repository files navigation

SortProof

CI Release License: MIT

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.

Who it is for

  • 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.

Install

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 version

The second command must print sortproof 0.1.0. A source archive and demo bundle are also attached to the v0.1.0 Release.

60-second quick start

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-report

Open 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.txt and back.txt are in one stage, so their counts are added because both forms remain composed together;
  • envelopes is 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.

Input

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,exclamation

The 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.

Output and exit codes

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-report

boundary 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.

Troubleshooting

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 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.

Known limitations

  • One normalized Unicode code point equals one sort. Multi-code-point sorts and automatic ligature substitution are not modeled; a precomposed ligature such as can 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.

Development

uv sync --locked --dev
uv run --no-sync python scripts/check.py

The 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.

License

MIT © 2026 KanadeK

About

Prove a handset letterpress job fits the sorts in your cases before composition starts.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages