Address the pdf-auditor findings that survived the 2026-05-12 PDF stack overhaul (pdfme → react-pdf). Items C-2/C-3 (tiptap-to-pdfme bugs) were resolved when that 571-LOC bridge was deleted; remaining items: - **M-7 wrong-port brand fallback** — replace `'Port Nimara'` defaults in PDF-rendering services. `reports.service` and `expense-export` throw when the port row is missing (the job is FK-keyed on a real port, so absence = broken state, must not stamp a competitor brand). `record-export` uses `'(port)'` as the visible placeholder. - **M-2 silent field drift in fill-eoi-form** — promote the always-silent catch in `setText` / `setCheckbox` to log a structured warning per missing field (mirroring the existing `setBerthRange` pattern). A re-cut template with drifted AcroForm field names now surfaces in ops logs instead of shipping with empty values. - **M-3 form not flattened** — `fillEoiFormFields` now flattens the AcroForm before save. Documenso pathway flattens server-side; this brings the in-app pathway to parity, so the signer can't edit pre-filled yacht dimensions / address / berth number after the fact. - **M-1 PDF metadata** — set Title / Author / Subject / Lang / Producer / Creator on the generated EOI PDF for downstream readers and a11y tooling. - **M-4 noisy berth-range warnings** — downgrade per-mooring warn to debug; emit a single summary warn per call when any passthrough occurred. Multi-berth EOIs with archived/legacy moorings no longer spam the log on every render. - **M-6 source PDF sha pinning** — pin `assets/eoi-template.pdf` sha256 via `EXPECTED_EOI_SHA256` (exported for tests); `loadEoiTemplatePdf` warns once per process when the bytes drift without an explicit hash bump. Documented the intentional-update workflow in `assets/README.md`. Tests updated in `tests/unit/pdf/fill-eoi-form.test.ts` to reflect flatten + metadata (form fields are gone after flatten; pdf-lib has no getLanguage so we assert the other setters round-trip). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
assets/
Server-side runtime assets bundled by Next.js (via outputFileTracingIncludes
in next.config.ts). These files are read with fs.readFile from
process.cwd() at runtime, so they are NOT served as public URLs — use
public/ for that.
eoi-template.pdf
The source PDF used by the in-app EOI generation pathway
(src/lib/pdf/fill-eoi-form.ts). It must be the same PDF that the
Documenso EOI template uploads, so both pathways produce equivalent
documents.
The PDF must contain AcroForm fields with these exact names (mirroring the
Documenso template's formValues keys — see
docs/eoi-documenso-field-mapping.md):
| Field name | Type | Filled with |
|---|---|---|
Name |
Text | EoiContext.client.fullName |
Email |
Text | EoiContext.client.primaryEmail |
Address |
Text | street, city, country |
Yacht Name |
Text | EoiContext.yacht.name |
Length |
Text | EoiContext.yacht.lengthFt |
Width |
Text | EoiContext.yacht.widthFt |
Draft |
Text | EoiContext.yacht.draftFt |
Berth Number |
Text | EoiContext.berth.mooringNumber |
Lease_10 |
Checkbox | always false (legacy default — Purchase, not Lease) |
Purchase |
Checkbox | always true |
The fill path flattens the AcroForm after writing values, so the recipient can't edit pre-filled values (yacht dimensions, address, berth number) after the fact. Documenso pathway flattens server-side; the in-app pathway brings the artifact to parity.
Expected sha256
The source PDF's sha256 is pinned to guard against silent template swaps (an unreviewed asset swap would change legal output without a code diff):
ba495fd88d99ebe4b7f61acbe397fb2f1cd116e1e1f1b217de93106915c7c44b
scripts/check-eoi-template-sha.ts verifies this at boot of the in-app
pathway; the function exposes the expected hash via EXPECTED_EOI_SHA256
so tests can re-check after a deliberate template revision.
To intentionally update the template:
- Drop the new PDF as
eoi-template.pdf. - Run
shasum -a 256 assets/eoi-template.pdf. - Update the hash in this README and in
src/lib/pdf/fill-eoi-form.ts(search forEXPECTED_EOI_SHA256).
Override path
In dev/test, set EOI_TEMPLATE_PDF_PATH=/abs/path/to/your/template.pdf to
point at a different file (e.g. a fixture).
How to extract this PDF
The legacy flow uploads this PDF to Documenso template ID 8. To get the exact bytes:
- In Documenso, open the EOI template.
- Download the source PDF.
- Drop it here as
eoi-template.pdf.