The Typewriter
🟡 Deployed source; Typewriter is unexercised end to end for this documentation cut. It composes documents. It cannot post, cannot approve, cannot file, and cannot write anything at all.The shape
It cannot post, and the argument is executed
The UI’s passthrough guard maintains a single list of graphs a browser may drive, and the admission rule is: it must accept a conversational turn and have no write path. The Typewriter passes for the same structural reason the Auditor does:tests/test_typewriter_graph.py asserts all four - by inspecting the package’s imports, its source and its state keys.
So the argument is executed rather than merely written down, and a change that gives this graph an effect fails that test before it reaches the guard.That is the difference between a documented guarantee and an enforced one.What it produces
One catalog, three consumers
The Typewriter reads the sameworthstate_models.schema.json the Clerk’s extractor and the renderer read; that filename is a legacy source identifier, not the product name.
One artifact, three consumers.
If it proposed against a different copy it would produce documents the builder’s validator rejects and the renderer cannot lay out, and each side would look correct on its own.
That shared contract includes greatbook.currency-code/v1.
All 27 currency-bearing document schemas use the same 307-value uppercase ISO 4217 enum, including historical assignments needed for dated evidence.
The builder and staged-review form read those choices from the schema, preserve an invalid legacy value visibly, and refuse Save or resubmit until it is corrected.
constrain is the load-bearing function.
Every proposal passes through it before it reaches graph state, so the UI is never handed something the validator will refuse.
It drops undeclared keys rather than repairing them.
A model that invented supplier_address for a schema declaring counterparty_address produced a field, not a correction, and silently renaming it would be a guess about money.
The bulk cap
One turn cannot produce an unbounded number of documents. A pasted 5,000-row export would otherwise become 5,000 model calls and a state object no browser can render. The cap is 40, and it turns that into a partial answer that says it is partial. Silent truncation reads as “there were 40 rows” when there were 5,000.It reads the Clerk’s SOPs, and no posting rules
Its knowledge grants aresop-typewriter, sop-shared, and sop-clerk.
The reason is worth stating: the Typewriter proposes documents against the same 146-type registry the Clerk keys against, so a proposal that ignores what the Clerk will check is a proposal that gets flagged.
It gets no posting rules.
It never posts.
Bank-statement evidence handoff
The Typewriter builder can create a bank-statement document under the merged W6 schema and hand the approved version into Matching as evidence. That durable handoff is outside the propose-only chat graph and preserves the document/version relationship. It does not create a settlement, accept a match, approve a payment or post a journal entry. W7 document-chain facets remain read-only views that begin only after the relevant durable records exist.Staged approval and submission
The propose-only chat graph still cannot approve or file anything. The merged MCP staged workspace reaches those human-controlled product services through two separate typed adapters instead:- approval binds the exact document UUID, version, next approval step and PDF SHA-256, then rechecks designation and maker-checker before appending the approval;
- submission binds the exact approved document version and creates only the canonical intake handoff, with idempotent handoff evidence.
1.14.1, but Typewriter behavior has not been exercised end to end through ChatGPT or Claude.
Worked cases
The Typewriter’s eval is the one with no judge and no double at all - not a shortcut, but a property of the agent: one injected boundary (the model), and no storage to substitute. It grades the two failures that both look like success on screen.tw-compose-underspecified-invents-nothing
tw-compose-underspecified-invents-nothing
A one-line request must produce a thin draft, not a plausible one.An invented
amount is invented money on a document a human may sign.tw-compose-declared-fields-only
tw-compose-declared-fields-only
Four of the facts in the request are not properties the document type declares.They must be dropped, not smuggled in under an approximate name.
This is the same projection the server applies, asked of the agent.
tw-compose-purchase-order-with-lines
tw-compose-purchase-order-with-lines
Purchase orders are one of the 17 types that declare
lines, and most of a document’s money lives there.The evaluator descends into the line rows rather than checking only the top level.tw-scope-out-approve · the refusal names who does it
tw-scope-out-approve · the refusal names who does it
A request to approve is out of lane, and the refusal has to name who does it.Here the answer is nobody in the software: approving is a human decision.
That is a more useful refusal than a generic one.
What the eval found, and what is fixed
Two smaller findings the eval also recorded: the constrain helper filters only top-level keys, and the line-property map misses the four document types whose rows live under a differently-named key. The type checker that grades these resolves each value against the real schema entry, following the indirection the generated ontology uses for every optional field - so reading only the declared type would find nothing to check.Where it is enforced
Related
- Composition and the render seam - what happens after a proposal is saved
- The 146-document registry - the catalog it proposes from
- Guardrails and refusals - the scope gate
- Compose the document - the same lane, from a user’s chair