A file is read into words once, and the words are what travels - #34
Merged
Merged
Conversation
The owner sent a price list in the conversation. It was read on arrival, the text was kept, and the assistant proposed filing it under the business. Every attempt came back "conversion failed", so the assistant told the owner the PDF could not be added and asked for a different format. `add_file_to_business` had encoded the kept text back into bytes and handed them to `addDocument` under the file's own name, `….pdf`, with `contentType: "text/plain"`. `readUpload` decides what a file is from its name, so it sent a plain-text "PDF" to `env.AI.toMarkdown`. The platform's supported-formats table has no plain text, so the converter answered `format: "error"`, `unwrapConversion` threw `conversion failed`, and the loop handed the model `Failed: conversion failed`. Deterministic, so "again"; account-independent, so the fresh account was a coincidence — the main account had only ever filed files through the console's Add data screen, which sends real bytes. The covering test mocked `addDocument` wholesale, so it proved the tool's intent and nothing about what the reader did next. A mock at the door hides what the door does. - `IngestInput` is now `IngestFile | IngestText`. Words already read travel as `text`, are never converted and never archived (there is no original to archive; a copy of the text under the file's name would be an archive of something that is not the file). They are recorded as `text/plain` at the text's own byte size, under the owner's filename. - The tool hands on `text: file.text`. - `a-file-is-read-once.test.ts` runs the REAL `ingestDocument` with only the platform faked, and its converter throws the moment it is reached. It also proves bytes still go to the reader unchanged, and holds the whole of the assistant's tools to never re-encoding kept text into a body. 956 tests, lint and typecheck clean. Workers AI cannot be exercised from this machine (wrangler is not logged in), so the live proof is the owner's own deployment after this lands. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NDkiHs276KQhSWxQnHQdLg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What the owner saw
On a fresh account, a price-list PDF sent in the conversation was read fine on arrival — the assistant knew the file by name — and then every attempt to file it under the business came back:
The mechanism, from the code
add_file_to_businesshad the kept text and handed it toaddDocumentencoded back into bytes, under the file's own name….pdf, withcontentType: "text/plain":readUploaddecides what a file is from its name, so it sent a plain-text "PDF" toenv.AI.toMarkdown. Cloudflare's supported formats table has no plain text, so the converter answeredformat: "error"→unwrapConversionthrewconversion failed→ the loop gave the modelFailed: conversion failed→ "conversion error".addDocumentwholesale, so it proved the tool's intent and nothing about what the reader did next.The change
IngestInputis nowIngestFile | IngestText. Words already read travel astext, are never converted and never archived (there is no original to archive), and are recorded astext/plainat the text's own byte size under the owner's filename. The tool hands ontext: file.text. Bytes from the console's Add data screen and from Telegram go through the reader exactly as before.Tests — 956 passing (+5)
a-file-is-read-once.test.tsruns the realingestDocumentwith only the platform faked, and itstoMarkdownthrows the moment it is reached:.pdfname are filed without a second reading;tools.ts).The old test's mock now throws if handed bytes, and says where the real check lives.
Not proved here
Workers AI cannot be exercised from the build machine (
wrangler whoami: not logged in; no token in the environment), so the live proof is the owner's deployment after this lands: send the PDF in the conversation, say yes to filing it, and the prices should follow.🤖 Generated with Claude Code
https://claude.ai/code/session_01NDkiHs276KQhSWxQnHQdLg