Skip to content

[P3/high] feat(forms): official IRS AcroForm output layer (subsystem + f1040 template)#22

Draft
ne-groomer[bot] wants to merge 1 commit into
mainfrom
groom/telos-ops-10-forms-layer
Draft

[P3/high] feat(forms): official IRS AcroForm output layer (subsystem + f1040 template)#22
ne-groomer[bot] wants to merge 1 commit into
mainfrom
groom/telos-ops-10-forms-layer

Conversation

@ne-groomer

@ne-groomer ne-groomer Bot commented Jul 12, 2026

Copy link
Copy Markdown

Priority: P3 · Complexity: high

Toward nousergon/telos-ops#10 (does not fully close it — see "Decision needed").

What

Greenfield src/telos/forms/ — the "official-form output at the back" of telos: transcribe the engine's computed line values into official fillable IRS AcroForm PDFs and assemble a print-and-mail package. Lives entirely outside src/telos/engine/ (imports nothing from it); filled output goes only under TELOS_DATA_DIR.

Module Role
paths.py template lookup (in-tree) + filled-output resolution (TELOS_DATA_DIR only, escape-guarded)
profile.py FormProfile{engine line key → AcroForm field} map + load-bearing verified gate
filler.py fill an AcroForm — unknown-field guard, XFA drop, NeedAppearances, exact Decimal formatting
package.py concatenate filled forms in filing order
introspect.py dump each field's fully-qualified name / type / rect / nearest printed label — the mapping aid

The engine's Form1040Result.lines is already keyed by 1040 line labels ("1a", "2b", "16"…) — those are exactly the profile's semantic keys, so this layer plugs onto the existing output with no engine change.

Validated (CI-green)

  • 43 new tests; telos.forms coverage 96%; full suite 477 passed / 98.41% total (gate 90). ruff check . clean. DCO-signed. No secrets; the committed f1040.pdf is a public-domain IRS work.
  • Full subsystem validated on a synthetic AcroForm fixture (field names controlled by the test, so read-back is meaningful) plus a mechanical round-trip on the REAL committed f1040.pdf — which is a hybrid AcroForm+XFA form (229 fields). Naive fillers get hybrid forms wrong; this drops /XFA so viewers render the AcroForm values, and rejects a mapped-but-absent field instead of silently no-op'ing.
  • Also fixes a pre-existing .gitignore defect: the !forms/templates/**/*.pdf negation carried an inline comment, which git treats as part of the pattern — so it never matched and the blank templates stayed ignored. (The issue assumed the exception already worked; it didn't.)

Decision needed (why this is a DRAFT, gate:decision)

The one piece intentionally not shipped is the verified semantic field map for f1040. Verified against the live artifact: the AcroForm has opaque positional field names (topmostSubform[0].Page1[0].f1_11[0]) and zero /TU tooltips, so which field is which 1040 line cannot be read from the PDF. A read-back test proves a value lands in a named field — never that the field is the semantically correct box. A mis-mapped dollar amount is a silent, high-stakes defect on a document mailed to the IRS, so I refuse to ship a guessed map (the same discipline as the params contract's refusal of uncited constants). profiles/f1040.yaml therefore ships verified: false with an empty map, and require_verified() blocks filing-grade use until it is confirmed.

The fork (pick one; then a tier groom can execute — the map is data-only YAML, no code rework):

  • Option A (recommended) — hand-author field_map from describe_fields() output (it already emits each field's nearest printed label), visually confirm each entry against the rendered form once, flip verified: true. What commercial tax software does; durable.
  • Option B — promote the nearest_text geometric heuristic into a validated auto-mapper subsystem (removes the hand step but is itself a design-bearing component needing its own validation across multi-column layouts, checkboxes, ~7 forms).

Validate locally

pip install -e ".[dev]" && ruff check . && pytest --cov --cov-report=term-missing

Groom-Run: 844608d4415945c6a5e710825122c5d4

…mplate)

Adds src/telos/forms/ — the "official-form output at the back" of the pipeline:
transcribe engine line values into fillable IRS PDFs and assemble a
print-and-mail package. Lives entirely outside src/telos/engine/ (imports
nothing from it) and writes filled output only under TELOS_DATA_DIR.

Modules:
- paths.py     template lookup (in-tree) + filled-output resolution (data dir only)
- profile.py   FormProfile: {engine line key -> AcroForm field} map + `verified` gate
- filler.py    fill an AcroForm — unknown-field guard, XFA drop, NeedAppearances
- package.py   concatenate filled forms in filing order
- introspect.py dump each field's name/type/rect/nearest printed label (mapping aid)

Robustness verified against the REAL f1040.pdf (hybrid AcroForm+XFA, 229 fields):
the filler drops /XFA so viewers render the values we set, and rejects a
mapped-but-absent field rather than silently no-op'ing. Full subsystem validated
on a synthetic AcroForm fixture (controlled field names) plus a mechanical
round-trip on the real template. 43 new tests; telos.forms coverage 96%; full
suite 477 passed / 98% total.

The one piece intentionally NOT shipped is the *verified semantic field map* for
f1040: the AcroForm has opaque positional field names and zero /TU tooltips, so
which field is which 1040 line cannot be read from the PDF and a read-back test
proves only that a value lands, never that it lands in the correct box. A
mis-mapped dollar amount is a silent, high-stakes defect on a form mailed to the
IRS, so profiles/f1040.yaml ships verified:false with an empty map and
require_verified() refuses filing-grade use until it is confirmed. See
forms/README.md for the two ways to establish that map (hand-authored+confirmed
vs geometric auto-mapper) — the one decision this needs from a human.

Also fixes a pre-existing .gitignore defect: the `!forms/templates/**/*.pdf`
negation carried an inline comment, which git treats as part of the pattern, so
it never matched and the blank templates stayed ignored.

Toward nousergon/telos-ops#10

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Signed-off-by: groom-bot <[email protected]>
@ne-groomer ne-groomer Bot added source:groom Opened by the backlog groomer gate:decision Draft: awaiting a human merge/scope decision (no external blocker) labels Jul 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gate:decision Draft: awaiting a human merge/scope decision (no external blocker) source:groom Opened by the backlog groomer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants