The decision
PAGS can fill a PDF form. It cannot fill a Word form, and the two Word formats are not one problem. This issue decides how far in-platform Word form handling goes, and records the answer — it is deliberately a design ticket, not an implementation ticket, because the honest options include "do not build this".
The motivating case (#756) is a club entry form that arrived as .doc and .docx. The user's goal is not "fill a Word document"; it is return the completed form to Kelly before the deadline. Keep that distinction — it is what makes option A viable.
Priority — P3: later: this is a design decision pending a call, and options A and B below deliver most of the user-visible outcome without it.
What exists today — VERIFIED
Three PDF form builtins, workers/api/src/lib/pdf-storage-tools.ts:66-92:
inspect_pdf_form (:68-73) — lists fillable fields, types and permitted values
fill_pdf_form (:74-84) — fills and saves a new file, returns its id
build_answer_sheet (:85-92) — "Generate a clean PDF of questions and their answers, saved to your files. This is the answer when a form is a flat scan with no fillable fields: send it alongside the original rather than attaching an empty form."
build_answer_sheet is the part that matters here, and #756 did not account for it. The platform already has a shipped, honest answer to "this form cannot be filled in place" — it just does not know that a .doc is such a form.
There is no Word equivalent of any of the three, and no ZIP reader in the tree (grep -rn "deflate-raw\|central directory\|centralDirectory" workers/api/src packages/*/src → nothing).
The two formats, and why the split is the whole decision
|
.docx |
.doc |
| Container |
ZIP of XML |
OLE2 / CFB compound binary |
| Read in a Worker |
tractable — see #763 |
not realistically |
| Write in a Worker |
tractable in principle: edit word/document.xml, re-zip |
no |
| Form fields |
content controls (<w:sdt>), legacy form fields, or — most club forms — plain underscores and table cells, no fields at all |
same, unreadable |
The user's actual attachment is the .doc. So even a complete .docx implementation would not have completed the reported task. Any plan that treats "Word forms" as one item is planning for the case that did not happen.
The third row is the one that usually decides it in practice: a form built by a tennis club in Word almost never has structured form fields. It has a table and blank lines. There is nothing to "fill" programmatically that is not layout reconstruction.
The options, cheapest first
A. Route Word forms to build_answer_sheet — no new format code at all.
Read the form's questions (requires #763 for .docx; for .doc requires C or nothing), call build_answer_sheet, reply with the generated PDF alongside the original attachment via gmail_reply's attachment_file_ids (connectors/gmail.ts:561, shipped in #713). Human-acceptable for a club: a filled answer sheet is a normal thing to receive. Cost: widening build_answer_sheet's description beyond "flat scan PDF", plus prompt guidance. This is the cheapest complete path to the user's actual outcome.
B. .docx read-only inspection. inspect_docx_form: list the visible questions/labels so the agent can ask the user for values and then take path A. Depends on #763.
C. .doc text via an external conversion step. The only way to read .doc at all. It puts the owner's documents through a third party, so it is a product decision, not an implementation choice — needs an explicit call, a named provider, and a consent story. My recommendation: do not build this. Instead, when a .doc arrives, say so plainly and offer path A driven by the user's own description of the fields, or ask them to re-send as PDF/.docx. An agent that says "I can't read a .doc; paste the questions or send it as a PDF and I'll fill it" is more useful than one that silently gets it wrong.
D. True .docx fill. Edit word/document.xml, re-zip, save as a new file. Only defensible if a survey of real forms shows structured fields are common. It is the largest option and the one most likely to produce a subtly corrupted document — the failure mode a club secretary discovers, not the platform.
My recommendation: A + B, decide C explicitly as "no", and gate D behind evidence.
The open question the owner should answer
Is "the agent returns a clean answer sheet PDF alongside the original form" an acceptable outcome, or must the returned file be the original document with values in it?
If the first: A + B, and this issue closes small. If the second: D becomes necessary for .docx and .doc is out of scope permanently. This cannot be decided from the code and should not be guessed. #710 left an adjacent question open — "Whether real-world club forms like the motivating one are AcroForms or flat scans… Not reproduced — needs the actual attachment" — and reality answered it worse than either branch: they were not PDFs at all.
Alternatives considered and rejected
- Convert
.doc/.docx → PDF and reuse fill_pdf_form. Rejected: conversion produces a flat PDF with no AcroForm fields, so fill_pdf_form refuses it by design and you land on build_answer_sheet anyway — which is option A, without the conversion.
- Have the model emit a whole new
.docx from scratch. Rejected: it discards the club's layout and produces a document that looks like a different form. build_answer_sheet is the same idea, honest about being a separate sheet.
- Ask the runner (
pags up) to open Word / LibreOffice locally. Rejected: it makes the capability depend on a local machine with an office suite, which the runner does not require today and which cloud-only agents (runtime: null) do not have at all.
Acceptance criteria
This ticket is done when the decision is recorded, not when code ships:
Regression risk
- Option A widens
build_answer_sheet's description, which is prompt surface for every agent that has the tool. A description that stops saying "flat scan" may see it used where fill_pdf_form should have been. Keep the "call inspect_pdf_form first" ordering explicit.
- Option D can emit a corrupt
.docx that opens with an error in Word. That is worse than not sending, and there is no in-Worker validator for it. If D is chosen, round-tripping through a real Word/LibreOffice open is a manual acceptance step and should be stated as one.
Parent: #756. Depends on #763 (reading a .docx at all) and #755 (getting the file into the store). Precedent: #712 built the PDF form tools.
The decision
PAGS can fill a PDF form. It cannot fill a Word form, and the two Word formats are not one problem. This issue decides how far in-platform Word form handling goes, and records the answer — it is deliberately a design ticket, not an implementation ticket, because the honest options include "do not build this".
The motivating case (#756) is a club entry form that arrived as
.docand.docx. The user's goal is not "fill a Word document"; it is return the completed form to Kelly before the deadline. Keep that distinction — it is what makes option A viable.Priority —
P3: later: this is a design decision pending a call, and options A and B below deliver most of the user-visible outcome without it.What exists today — VERIFIED
Three PDF form builtins,
workers/api/src/lib/pdf-storage-tools.ts:66-92:inspect_pdf_form(:68-73) — lists fillable fields, types and permitted valuesfill_pdf_form(:74-84) — fills and saves a new file, returns its idbuild_answer_sheet(:85-92) — "Generate a clean PDF of questions and their answers, saved to your files. This is the answer when a form is a flat scan with no fillable fields: send it alongside the original rather than attaching an empty form."build_answer_sheetis the part that matters here, and #756 did not account for it. The platform already has a shipped, honest answer to "this form cannot be filled in place" — it just does not know that a.docis such a form.There is no Word equivalent of any of the three, and no ZIP reader in the tree (
grep -rn "deflate-raw\|central directory\|centralDirectory" workers/api/src packages/*/src→ nothing).The two formats, and why the split is the whole decision
.docx.docword/document.xml, re-zip<w:sdt>), legacy form fields, or — most club forms — plain underscores and table cells, no fields at allThe user's actual attachment is the
.doc. So even a complete.docximplementation would not have completed the reported task. Any plan that treats "Word forms" as one item is planning for the case that did not happen.The third row is the one that usually decides it in practice: a form built by a tennis club in Word almost never has structured form fields. It has a table and blank lines. There is nothing to "fill" programmatically that is not layout reconstruction.
The options, cheapest first
A. Route Word forms to
build_answer_sheet— no new format code at all.Read the form's questions (requires #763 for
.docx; for.docrequires C or nothing), callbuild_answer_sheet, reply with the generated PDF alongside the original attachment viagmail_reply'sattachment_file_ids(connectors/gmail.ts:561, shipped in #713). Human-acceptable for a club: a filled answer sheet is a normal thing to receive. Cost: wideningbuild_answer_sheet's description beyond "flat scan PDF", plus prompt guidance. This is the cheapest complete path to the user's actual outcome.B.
.docxread-only inspection.inspect_docx_form: list the visible questions/labels so the agent can ask the user for values and then take path A. Depends on #763.C.
.doctext via an external conversion step. The only way to read.docat all. It puts the owner's documents through a third party, so it is a product decision, not an implementation choice — needs an explicit call, a named provider, and a consent story. My recommendation: do not build this. Instead, when a.docarrives, say so plainly and offer path A driven by the user's own description of the fields, or ask them to re-send as PDF/.docx. An agent that says "I can't read a .doc; paste the questions or send it as a PDF and I'll fill it" is more useful than one that silently gets it wrong.D. True
.docxfill. Editword/document.xml, re-zip, save as a new file. Only defensible if a survey of real forms shows structured fields are common. It is the largest option and the one most likely to produce a subtly corrupted document — the failure mode a club secretary discovers, not the platform.My recommendation: A + B, decide C explicitly as "no", and gate D behind evidence.
The open question the owner should answer
If the first: A + B, and this issue closes small. If the second: D becomes necessary for
.docxand.docis out of scope permanently. This cannot be decided from the code and should not be guessed. #710 left an adjacent question open — "Whether real-world club forms like the motivating one are AcroForms or flat scans… Not reproduced — needs the actual attachment" — and reality answered it worse than either branch: they were not PDFs at all.Alternatives considered and rejected
.doc/.docx→ PDF and reusefill_pdf_form. Rejected: conversion produces a flat PDF with no AcroForm fields, sofill_pdf_formrefuses it by design and you land onbuild_answer_sheetanyway — which is option A, without the conversion..docxfrom scratch. Rejected: it discards the club's layout and produces a document that looks like a different form.build_answer_sheetis the same idea, honest about being a separate sheet.pags up) to open Word / LibreOffice locally. Rejected: it makes the capability depend on a local machine with an office suite, which the runner does not require today and which cloud-only agents (runtime: null) do not have at all.Acceptance criteria
This ticket is done when the decision is recorded, not when code ships:
file:linegrounding..docis pinned by a test on the refusal string: it names the format, says it cannot be read, and offers the next step. Silence is the failure this issue exists to prevent.Regression risk
build_answer_sheet's description, which is prompt surface for every agent that has the tool. A description that stops saying "flat scan" may see it used wherefill_pdf_formshould have been. Keep the "callinspect_pdf_formfirst" ordering explicit..docxthat opens with an error in Word. That is worse than not sending, and there is no in-Worker validator for it. If D is chosen, round-tripping through a real Word/LibreOffice open is a manual acceptance step and should be stated as one.Parent: #756. Depends on #763 (reading a
.docxat all) and #755 (getting the file into the store). Precedent: #712 built the PDF form tools.