You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Epic: the emailed-form workflow stops at step 2 — download is broken (#755), Word is unreadable (#763), and only the byte-upload parameter is genuinely missing (#762) #756
Rewritten 2026-08-28. Two premises in the original were wrong and would have sent an implementer to build things that already exist. Corrections are marked CORRECTION below. The workflow the owner asked for is unchanged and is tracked by the checklist.
The workflow
A form arrives by email. The agent should:
Find and read the message
Put the attachment in the instance file store as a real file
Read the form well enough to complete it
Save the completed output as a new file
Reply in the original thread with that file attached — ideally after a human has looked at it
Junior Comp Hawthorn Entry Form SUMMER 2026:2027.doc (application/msword)
Hawthorn Club Championships 2026.docx
Reply target: the original Kelly / Hawthorn Tennis thread
This is a high-frequency shape, not an edge case: club entries, school forms, permission slips, supplier forms, onboarding packs, insurance forms. The agent currently reads the email, understands the instructions, names the attachments — and stops exactly where the delegated work begins.
Priority — P2: correctness: three of the five steps are already shipped. What is left is one live defect (#755) and one missing parameter (#762), not a new product area. The one genuinely open question is #764.
gmail_download_attachment refuses the id it was given — #755
3. Read/fill the form
PDF only
inspect_pdf_form / fill_pdf_form / build_answer_sheet — lib/pdf-storage-tools.ts:66-92 (#712). No Word path — #763, #764
4. Save the result as a file
Reachable everywhere except from a tool
DO route takes contentBase64; upload_file does not — #762
5. Reply in-thread with the file
Shipped
gmail_replyattachment_file_ids — lib/connectors/gmail.ts:561 (#713). Draft-before-send is missing — #765
CORRECTION 1 — there IS a binary path; no tool exposes it
The original said "there is no obvious way for an external connector/tool to place an edited binary Word document back into the instance file store." That is wrong at the layer that matters, and building it from scratch would duplicate live code.
workers/api/src/agent-do-storage-routes.ts:150-154 — if (!body.name || (!body.content && !body.contentBase64)) … then const data = body.contentBase64 ? bytesFromBase64(body.contentBase64).slice().buffer : body.content;
workers/api/src/routes/storage.ts:167-175 forwards the request body verbatim, so contentBase64 already reaches the DO over HTTP today
workers/api/src/lib/connectors/gmail.ts:226 — the Gmail downloader uses it in production right now: contentBase64: base64UrlToBase64(base64url),
What is true is narrower: upload_file is text-only — workers/api/src/lib/storage-tools.ts:86, content: { type: "string", description: "File content (text)", required: true } — and so is MCP upload_agent_file (workers/mcp/src/storage-tools.ts:271). That is one parameter, tracked as #762, not an epic.
CORRECTION 2 — step 5 already ships
The original said "the missing binary download/edit/upload path means there may be no usable file ID to attach." The attach path itself is complete and tested: gmail_reply takes attachment_file_ids (connectors/gmail.ts:561), collectOutgoingAttachments (:327-347) resolves each id to real bytes with a 15MB total cap, and gmail.test.ts:409 covers it. Recipients come from the parent message and can never be model-supplied (connectors/gmail.ts:349-356).
What is genuinely missing at step 5 is the draft: both tools state "This really sends: there is no draft step and no undo" (connectors/gmail.ts:556, :570). Tracked as #765 — and it needs no new OAuth scope, because gmail.modify is already requested and recorded.
CORRECTION 3 — .doc and .docx are not one problem, and the user's file is the hard one
The original treated ".doc / .docx forms" as a single gap. They are different problems with different answers:
.docx
.doc
Container
ZIP of XML
OLE2 / CFB compound binary
Readable in a Worker
yes — the runtime already uses DecompressionStream twice (agent-storage-utils.ts:200, repo-ingest.ts:205) and already hand-rolls an archive walker (repo-ingest.ts:245, readTar)
realistically no
extractFileText handles text and PDF and nothing else — workers/api/src/agent-storage-utils.ts:141-149, catch-all return { text: "", status: "unsupported" };. So both formats land in the store contributing nothing, and read_file refuses them honestly (storage-tools.ts:365-369).
The user's actual attachment is the .doc. A complete .docx implementation would not have finished the reported task. Split as #763 (read a .docx) and #764 (the decision about how far Word fill goes, and whether .doc is in scope at all).
CORRECTION 4 — the "flat form" fallback already exists
build_answer_sheet (pdf-storage-tools.ts:85-92) already generates "a clean PDF of questions and their answers… send it alongside the original rather than attaching an empty form." It was built for flat-scan PDFs, but it is the same answer for a Word form with no structured fields — which is what a club form usually is. This may make the expensive option (true .docx fill) unnecessary; that is the open question in #764.
#755 → #762 → #763 is a complete, shippable chain that ends with the agent able to read an emailed .docx and reply with a generated answer sheet.#764 gates nothing except the "fill the original document in place" version of step 3; it must not gate the other four.
The question that needs the owner, not a developer
Is "the agent replies with a clean answer-sheet PDF alongside the original form" an acceptable outcome, or must the returned file be the original document with the values in it?
Answer it on #764. Everything downstream of it — whether .docx write support is built, whether .doc needs an external converter and whether that is acceptable for the owner's documents — follows from it, and it cannot be read out of the code.
Related history: #710 is the closed epic that built steps 1, 3 (PDF) and 5. It left one thing explicitly unverified — "Whether real-world club forms like the motivating one are AcroForms or flat scans… Not reproduced — needs the actual attachment." This issue is the answer, and it is worse than either branch #710 anticipated: they were not PDFs at all.
The workflow
A form arrives by email. The agent should:
Concrete case that motivated this:
1a00cff038273010, instancee17d538d-6940-4773-80f6-3715c1b2d2faJunior Comp Hawthorn Entry Form SUMMER 2026:2027.doc(application/msword)Hawthorn Club Championships 2026.docxThis is a high-frequency shape, not an edge case: club entries, school forms, permission slips, supplier forms, onboarding packs, insurance forms. The agent currently reads the email, understands the instructions, names the attachments — and stops exactly where the delegated work begins.
Priority —
P2: correctness: three of the five steps are already shipped. What is left is one live defect (#755) and one missing parameter (#762), not a new product area. The one genuinely open question is #764.Where each step actually stands — VERIFIED
gmail_search,gmail_read_message—lib/connectors/gmail.ts:524-549(#711)gmail_download_attachmentrefuses the id it was given — #755inspect_pdf_form/fill_pdf_form/build_answer_sheet—lib/pdf-storage-tools.ts:66-92(#712). No Word path — #763, #764contentBase64;upload_filedoes not — #762gmail_replyattachment_file_ids—lib/connectors/gmail.ts:561(#713). Draft-before-send is missing — #765CORRECTION 1 — there IS a binary path; no tool exposes it
The original said "there is no obvious way for an external connector/tool to place an edited binary Word document back into the instance file store." That is wrong at the layer that matters, and building it from scratch would duplicate live code.
workers/api/src/agent-do-storage-routes.ts:150-154—if (!body.name || (!body.content && !body.contentBase64)) …thenconst data = body.contentBase64 ? bytesFromBase64(body.contentBase64).slice().buffer : body.content;workers/api/src/routes/storage.ts:167-175forwards the request body verbatim, socontentBase64already reaches the DO over HTTP todayworkers/api/src/lib/connectors/gmail.ts:226— the Gmail downloader uses it in production right now:contentBase64: base64UrlToBase64(base64url),What is true is narrower:
upload_fileis text-only —workers/api/src/lib/storage-tools.ts:86,content: { type: "string", description: "File content (text)", required: true }— and so is MCPupload_agent_file(workers/mcp/src/storage-tools.ts:271). That is one parameter, tracked as #762, not an epic.CORRECTION 2 — step 5 already ships
The original said "the missing binary download/edit/upload path means there may be no usable file ID to attach." The attach path itself is complete and tested:
gmail_replytakesattachment_file_ids(connectors/gmail.ts:561),collectOutgoingAttachments(:327-347) resolves each id to real bytes with a 15MB total cap, andgmail.test.ts:409covers it. Recipients come from the parent message and can never be model-supplied (connectors/gmail.ts:349-356).What is genuinely missing at step 5 is the draft: both tools state "This really sends: there is no draft step and no undo" (
connectors/gmail.ts:556,:570). Tracked as #765 — and it needs no new OAuth scope, becausegmail.modifyis already requested and recorded.CORRECTION 3 —
.docand.docxare not one problem, and the user's file is the hard oneThe original treated "
.doc/.docxforms" as a single gap. They are different problems with different answers:.docx.docDecompressionStreamtwice (agent-storage-utils.ts:200,repo-ingest.ts:205) and already hand-rolls an archive walker (repo-ingest.ts:245,readTar)extractFileTexthandles text and PDF and nothing else —workers/api/src/agent-storage-utils.ts:141-149, catch-allreturn { text: "", status: "unsupported" };. So both formats land in the store contributing nothing, andread_filerefuses them honestly (storage-tools.ts:365-369).The user's actual attachment is the
.doc. A complete.docximplementation would not have finished the reported task. Split as #763 (read a.docx) and #764 (the decision about how far Word fill goes, and whether.docis in scope at all).CORRECTION 4 — the "flat form" fallback already exists
build_answer_sheet(pdf-storage-tools.ts:85-92) already generates "a clean PDF of questions and their answers… send it alongside the original rather than attaching an empty form." It was built for flat-scan PDFs, but it is the same answer for a Word form with no structured fields — which is what a club form usually is. This may make the expensive option (true.docxfill) unnecessary; that is the open question in #764.The children
gmail_download_attachmentrefuses the idgmail_read_messagehanded the model. Blocks step 2; nothing else can start.P1upload_filecannot write bytes; add a base64 parameter. Step 4.P2.docxcontributes nothing;extractFileTexthas no Word path. Step 3, read half.P2[decision]how far Word form handling goes;.docxvs.doc, and whetherbuild_answer_sheetis already the answer. Step 3, fill half — the only research problem here.P3P2#755 → #762 → #763 is a complete, shippable chain that ends with the agent able to read an emailed
.docxand reply with a generated answer sheet. #764 gates nothing except the "fill the original document in place" version of step 3; it must not gate the other four.The question that needs the owner, not a developer
Answer it on #764. Everything downstream of it — whether
.docxwrite support is built, whether.docneeds an external converter and whether that is acceptable for the owner's documents — follows from it, and it cannot be read out of the code.Related history: #710 is the closed epic that built steps 1, 3 (PDF) and 5. It left one thing explicitly unverified — "Whether real-world club forms like the motivating one are AcroForms or flat scans… Not reproduced — needs the actual attachment." This issue is the answer, and it is worse than either branch #710 anticipated: they were not PDFs at all.