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.7 KiB
2.7 KiB
name, description
| name | description |
|---|---|
| pdf-widget-layer-census | Census a real-world "flat" PDF before assuming it needs field injection: Quartz/Preview exports often already carry an AcroForm widget layer under underscore rules; also audits pdf-lib flatten residue. |
Real-PDF census (widget-first, then judge)
A PDF that looks flat (pdftotext shows ____ fill rules) may already contain a
full AcroForm widget layer — macOS Preview/Quartz exports write widgets with
transparent rects under drawn underscores. Injecting fields into such a file
duplicates/shadows widgets. Always census before touching the pipeline.
Steps
- Surface facts (host):
pdfinfo file.pdf # "Form: AcroForm" hints at a widget layer pdftotext -layout file.pdf - # label lines + underscore rules = intended fields - Widget census — authoritative check. With the franksign sidecar image,
the image ENTRYPOINT is
inject_fields.py; override it:podman run --rm --entrypoint python -v ~/Downloads:/in:z \ localhost/franksign-pdf-field-sidecar: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('widgets:', sum(1 for p in d.pages() for _ in p.widgets()), 'annots:', sum(1 for p in d.pages() for _ in p.annots())) " - Fill test: if widgets exist,
fillTemplate()(pdf-lib) consumes the file AS-IS — spec.json + sidecar are unnecessary for it. Flatten-verify below.
Flatten audit gotchas
pdfinfoprintsForm: AcroFormeven on correctly flattened pdf-lib output — pdf-libflatten()removes every widget/annotation but leaves the field dictionary. Truth = MuPDF widget enumeration (0), not the pdfinfo line.describeForm(bytes, specJson)inpackages/pdfparses the SPEC, not the PDF, for the field list — pass a spec or skip it; calling it spec-less throwsJSON Parse error.
Decision rule
- widgets > 0 → the widget layer IS the spec (names/types/rects, flip y to
bottom-left origin); NEVER run
inject_fields.pyover it. - widgets = 0 → genuine flat form: sidecar injection with a spec (position detection = issue #14 territory: underscore-run rects, label-derived names).
- Missing signature widget is normal ("please sign" is prose); franksign decision 19: the typed full name is the signature.
Privacy
Real documents are personal: scratch copies in /tmp, placeholder fill values,
destroy scratch (rm /tmp/* is fine OUTSIDE the repo; repo deletions still go
to .AGENTTRASH/). Never commit the document, a spike script touching it, or
its extracted text into the vault.