Landing blocker for #61 and #63: the deployment checkout is 12 commits behind and `merge --ff-only` refuses while tracked paths are dirty. Discard is not available here (workspace policy: no `checkout`/`stash` on shared state), so the dirt is committed. - `agent/APPEND_SYSTEM.md`: the point-of-use / one-work-log / PR-link doctrine, already byte-identical to `origin/main` (`cmp` against the trunk blob: no difference). No new prose — the commit exists so the trunk merge can proceed. - `agent/config.yml` and `agent/managed-skills/**`: the machine-live tier the heartbeat owns (AGENTS.md § Live-sync tiers). `ALLOW_SECRETS=1` was used, deliberately and after auditing the staged diff by hand: the only credential-shaped strings in it are two copies of `AKIA0000000000000EXAMPLE` — the inert all-zero AWS example id — and they appear inside the two skill pages that teach that exact placeholder shape. This checkout is still running the pre-#61 hook; the merged gate (value-scoped placeholder filter, `71601f7`) exempts this value, so the refusal dies with this landing. No `--no-verify` was used: the gate ran and its verdict is recorded here. Refs #61
2.4 KiB
2.4 KiB
name, description
| name | description |
|---|---|
| real-pdf-acroform-census | Census a real-world PDF before running it through a flat→AcroForm pipeline: check for hidden widget layers, prove fill/flatten, avoid the pdfinfo ghost. |
Census recipe for a real-world PDF arriving at a flat-form pipeline (e.g. franksign's sidecar that injects AcroForm widgets). Run BEFORE assuming the document is flat.
Sequence
- Metadata:
pdfinfo file.pdf— noteForm:line, producer (macOS ... Quartz PDFContext= Preview/Preview-era export), page size. - Text layer:
pdftotext -layout file.pdf -— underscore rules (____) look "flat". - Widget census (the decisive probe) — never trust the looks:
Traps: a custom ENTRYPOINT script swallows
podman run --rm --entrypoint python -v ~/Downloads:/in:z <sidecar-image>:local -c " import pymupdf d = pymupdf.open('/in/file.pdf') for p in d.pages(): for w in p.widgets(): print(w.field_name, w.field_type_string, [round(v) for v in w.rect]) print('annots:', sum(1 for p in d.pages() for _ in p.annots()))pythonas argv — override with--entrypoint python. Mount a directory, not a single file (MuPDF atomic-save EBUSY). - Fill live-fire: pass the file through the fill step as-is with placeholder values; verify with
pdftotext -layoutthat values land on the rules. - Flatten audit: re-run the widget census on the output (expect 0) — do NOT read
pdfinfo.
Laws learned
- macOS Preview/Quartz exports (and most print-to-PDF paths) already embed AcroForm widgets under transparent rects over their drawn underscore rules. Field names are human-meaningful (
date_today, ...). Injecting widgets over them duplicates/shadows fields — the design-time injection stage must be skipped when a widget layer exists (widget-first detection). - pdf-lib
flatten()removes every widget but leaves the AcroForm field dictionary:pdfinfokeeps printingForm: AcroFormon a fully flattened file. Widget enumeration (MuPDF) is the only truth; pdfinfo is the ghost. - Underscore rules in the text layer are drawings, not fields; the text layer alone cannot tell flat from widget-bearing.
Verdicts
- Widgets present → fill as-is; detection spec = widget layer (names, types, rects; flip top-left→bottom-left origin if the pipeline spec uses bottom-left).
- Zero widgets → true flat form; needs position detection (underscore runs + nearest preceding label) before injection.