All PDF I/O through one typed model; raw canonical-PDF-library access banned.

PdfModel (sole PDF mutation surface)

Intent — Route all reads and writes of a complex file format through one typed model, with raw access to the underlying library banned by a lint, so the structure is compiler-checked and every mutation passes through a surface that encodes the format's invariants (our instance: PdfModel over the canonical PDF library).

SummaryAll PDF I/O through one typed model; raw canonical-PDF-library access banned.
TargetProduct · Canonical models & seams
Formtyped-ir
EnforcementHard (deterministic) · blocking — a raw-PDF-library ban-lint fails the build on any raw library call; the typed model is construction, the ban-lint is the counted sensor

Motivation — the failure it kills

The raw canonical PDF library is a minefield of silent, invisible-at-the-call-site failures. Forget SetModified() on an indirect object and the write is silently dropped on save. Call tagPointer.AddTag or dict.Put directly and you can corrupt the /StructTreeRoot, the exact class that produced the v172 corruption. There is no single place to enforce these invariants, so scattered raw calls make the same PDF bug class recur at every call site.

Why it's not just "use the library carefully" (or "code-review the PDF calls")

The library's sharp edges are invisible where they're used — a missing SetModified() looks like correct code, and reviewers miss it because nothing in the diff flags it. PdfModel makes the raw API unreachable: typed mutators encode the invariants (they can't forget SetModified()), and the raw-PDF-library ban-lint fails the build on any raw constructor, AddTag, dict.Put, or structRoot.AddKid. The distinction is a typed sole-seam whose raw alternative is lint-banned versus disciplined use of a raw API. The typed model is construction: the bug becomes unrepresentable. The ban-lint is the counted detection sensor that keeps every call site on the seam.

Mechanism

Read via PdfModel.Read(path); write via the typed mutators in Primitives/. A raw-PDF-library ban-lint fails the build on raw library constructors, tagPointer.AddTag, dict.Put, and structRoot.AddKid. Each typed mutator wires the SetModified() discipline and stamp emission so a new verb cannot land un-wired.

Prerequisites

Consequences & costs

Known uses

Related mechanisms