Files
command-center/agent/managed-skills/real-pdf-acroform-census/SKILL.md
T
Frank 8af40c402b chore(agent): commit the machine tier and the doctrine text the checkout was already running
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
2026-10-21 01:44:51 -07:00

36 lines
2.4 KiB
Markdown

---
name: real-pdf-acroform-census
description: "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
1. **Metadata**: `pdfinfo file.pdf` — note `Form:` line, producer (`macOS ... Quartz PDFContext` = Preview/Preview-era export), page size.
2. **Text layer**: `pdftotext -layout file.pdf -` — underscore rules (`____`) look "flat".
3. **Widget census (the decisive probe)** — never trust the looks:
```bash
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()))
```
Traps: a custom ENTRYPOINT script swallows `python` as argv — override with `--entrypoint python`. Mount a directory, not a single file (MuPDF atomic-save EBUSY).
4. **Fill live-fire**: pass the file through the fill step as-is with placeholder values; verify with `pdftotext -layout` that values land on the rules.
5. **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**: `pdfinfo` keeps printing `Form: AcroForm` on 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.