Files
command-center/agent/managed-skills/pdf-widget-layer-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

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

  1. Surface facts (host):
    pdfinfo file.pdf            # "Form: AcroForm" hints at a widget layer
    pdftotext -layout file.pdf -  # label lines + underscore rules = intended fields
    
  2. 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()))
    "
    
  3. 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

  • pdfinfo prints Form: AcroForm even on correctly flattened pdf-lib output — pdf-lib flatten() removes every widget/annotation but leaves the field dictionary. Truth = MuPDF widget enumeration (0), not the pdfinfo line.
  • describeForm(bytes, specJson) in packages/pdf parses the SPEC, not the PDF, for the field list — pass a spec or skip it; calling it spec-less throws JSON 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.py over 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.