pdfpythonverified

real form fill td1 skill

Fill the real CRA TD1 (Personal Tax Credits Return) from human labels: discovers each field from tooltips and widget geometry, writes values with appearances, leaves other fields empty.

rec_fcffc2c5a47f48a7a41bd50c7338fc2a · banked 2026-09-04 · by neruva
SKILL.md
Certificate

A program checked this on cases it had never seen. You can run it.

Download the checker
Model alone
see evidence
With this skill
see evidence
Near-misses it rejects
5/5
Signed
ed25519
What was checked
  • Output built to the specification is accepted.
  • Each of these deliberate breaks is rejected: extra_field, wrong_value, missing_page, no_appearance, stringify_date.
  • An empty file is rejected.
What was not
  • Anything a person would call taste: layout, tone, whether it looks good. No program can check that, and this one does not claim to.
  • Behaviour outside the specification's clauses.
  • Inputs the checker was never given. See the evidence line for the corpus.
Scorecard

Fill the real CRA TD1 tax form from human labels, finding each field from its tooltip and position.

The model on its own
19%16 unseen cases
The same model with this skill
94%deepseek-chat
A frontier model on its own
100%same cases
Cost per task with the skill
0.342 cents79.4x cheaper than the frontier model
Smallest model measured
not measured below DeepSeek-chat yet
Cost to build it once
$0.50
Checked by
hand-written, reads the stored field values back out of the filed PDF
Where it does not apply
  • Forms whose fields carry no tooltips, such as the IRS W-9, are not solved by this rung.
  • Reads a blank form and fills it. It does not read a scan.

Every figure comes from one run, kept in the repository as probe6b_pdf-fill_td1_result.json.

Evidence

  • real CRA TD1 (canada.ca), 16 held-out fills, best-of-2: deepseek-chat cold 0.188 @0.47c -> 0.938 with rung @0.34c (CI +0.562..+0.938); Sonnet cold 1.000 @27.17c; 80x cheaper per success
  • forged by claude-sonnet-5, 2026-09-04

Use it

# in Claude Code (MCP tools from neruva-mcp)
rung_search(q="real_form_fill_td1_rungs")
rung_install(id="rec_fcffc2c5a47f48a7a41bd50c7338fc2a", dir="~/.claude/skills")
# the skill folder is now loaded like any other skill

Usage guide

What the model reads to call the skill. This is the whole interface.

Show the guide
list_form_fields(pdf_path)
  Returns a list of dicts describing every AcroForm field in the PDF:
  {"name": <internal field name>, "tooltip": <TU text>, "type": <FT code>, "value": <current V>}.
  Example:
    fields = list_form_fields("td1.pdf")

find_fields_by_tooltip(fields_info, substring, case_sensitive=False, search_name_too=True)
  Filters fields_info (from list_form_fields) to those whose tooltip (or
  internal name, if search_name_too) contains `substring`. Returns a list
  of matching field dicts (possibly empty).
  Example:
    matches = find_fields_by_tooltip(fields, "date of birth")

find_best_field(fields_info, candidates, case_sensitive=False)
  Tries each string in `candidates` (most specific first) against
  find_fields_by_tooltip and returns the single field dict it thinks is the
  best match, or None if nothing matched. Useful when the exact tooltip
  wording is uncertain.
  Example:
    f = find_best_field(fields, ["date of birth", "birth"])

fill_pdf_form(input_pdf, output_pdf, field_values, generate_appearances=True)
  Fills the named AcroForm fields (dict of internal field name -> value)
  in input_pdf, storing them in /V, generating appearance streams by
  default, keeping every other field's value untouched, keeping all pages,
  and writes the result to output_pdf. Returns output_pdf.
  Example:
    fill_pdf_form("td1.pdf", "out.pdf", {"topmostSubform[0].Page1[0].f1_02[0]": "Nguyen"})

Code

143 lines of python, hashed and signed below.

Show the code
def list_form_fields(pdf_path):
    """
    List all AcroForm fields in a PDF along with their tooltip (/TU),
    internal name (/T), type (/FT) and current value (/V).

    Parameters:
        pdf_path (str): path to the PDF file.

    Returns:
        list[dict]: one dict per field with keys:
            "name"    -> internal field name (str)
            "tooltip" -> tooltip / alternate text, "" if none (str)
            "type"    -> field type code, e.g. "/Tx", "/Btn" (str)
            "value"   -> current /V value, "" if none (str)
    """
    from pypdf import PdfReader

    reader = PdfReader(pdf_path)
    fields = reader.get_fields()
    result = []
    if not fields:
        return result
    for name, f in fields.items():
        tooltip = f.get("/TU", "") or ""
        ftype = f.get("/FT", "") or ""
        value = f.get("/V", "") or ""
        result.append({
            "name": name,
            "tooltip": str(tooltip),
            "type": str(ftype),
            "value": str(value),
        })
    return result


def find_fields_by_tooltip(fields_info, substring, case_sensitive=False, search_name_too=True):
    """
    Find fields whose tooltip (or optionally internal name) contains a substring.

    Parameters:
        fields_info (list[dict]): output of list_form_fields().
        substring (str): text to search for.
        case_sensitive (bool): whether the match is case sensitive. Default False.
        search_name_too (bool): also search the internal field name if the
            tooltip doesn't match. Default True.

    Returns:
        list[dict]: subset of fields_info whose tooltip/name contains substring.
    """
    matches = []
    needle = substring if case_sensitive else substring.lower()
    for f in fields_info:
        tip = f.get("tooltip", "") or ""
        name = f.get("name", "") or ""
        hay_tip = tip if case_sensitive else tip.lower()
        hay_name = name if case_sensitive else name.lower()
        if needle in hay_tip:
            matches.append(f)
        elif search_name_too and needle in hay_name:
            matches.append(f)
    return matches


def find_best_field(fields_info, candidates, case_sensitive=False):
    """
    Try a list of candidate substrings (in order of specificity) against a
    field list and return the single best-matching field.

    For each candidate substring, looks for fields whose tooltip or name
    contains it. Returns the first candidate that yields exactly one match.
    If a candidate yields multiple matches, the one with the shortest
    tooltip text is chosen (assumed to be the most specific/simple match).

    Parameters:
        fields_info (list[dict]): output of list_form_fields().
        candidates (list[str]): substrings to try, most specific first.
        case_sensitive (bool): case sensitivity for matching. Default False.

    Returns:
        dict or None: the matching field dict, or None if no candidate matched.
    """
    for cand in candidates:
        matches = find_fields_by_tooltip(fields_info, cand, case_sensitive=case_sensitive)
        if len(matches) == 1:
            return matches[0]
        elif len(matches) > 1:
            matches_sorted = sorted(matches, key=lambda f: len(f.get("tooltip", "") or ""))
            return matches_sorted[0]
    return None


def fill_pdf_form(input_pdf, output_pdf, field_values, generate_appearances=True):
    """
    Fill an AcroForm PDF's fields with given values, storing them as /V
    (so any compliant reader shows them) and generating visual appearances
    for text fields when possible. Leaves all other fields untouched
    (i.e. empty, if they were empty before). Preserves all pages.

    Parameters:
        input_pdf (str): path to the source PDF.
        output_pdf (str): path to write the filled PDF to.
        field_values (dict): mapping of internal field name -> value to set.
            For checkboxes/radio buttons, use the "on"/"off" export value
            strings (e.g. "/Yes", "/Off") as found in the PDF.
        generate_appearances (bool): if True (default), ask pypdf to build
            appearance streams for the new values (auto_regenerate=False).
            If False, sets the AcroForm /NeedAppearances flag instead and
            relies on the viewer to render values.

    Returns:
        str: the output_pdf path that was written.
    """
    from pypdf import PdfReader, PdfWriter
    from pypdf.generic import NameObject, BooleanObject

    reader = PdfReader(input_pdf)
    writer = PdfWriter()
    writer.append(reader)

    for page in writer.pages:
        writer.update_page_form_field_values(
            page, field_values, auto_regenerate=not generate_appearances
        )

    try:
        acro = writer._root_object["/Root"]["/AcroForm"] if "/Root" in writer._root_object else writer._root_object["/AcroForm"]
    except Exception:
        acro = None
    if acro is None:
        try:
            acro = writer._root_object.get("/AcroForm")
        except Exception:
            acro = None
    if acro is not None:
        try:
            acro[NameObject("/NeedAppearances")] = BooleanObject(not generate_appearances)
        except Exception:
            pass

    with open(output_pdf, "wb") as f:
        writer.write(f)
    return output_pdf

Certificate

Code sha256 47f211e87b3bc2c5e0118fcec88294b02cdd84b992cb23f13d2680a40d004e5c
Signature ed25519, present
Last re-checked
passing

Re-checked today. The signature was recomputed from the code served by the API, and the certificate matched.

A certificate proves the code has not changed. Re-running the check proves it still works today, on today's libraries.