Skip to main content

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

One pass, one terminal answer. There is no gate, because there is nothing to gate.

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

Each proposal is already constrained to the schema’s declared properties, with the document type pinned exactly as the save and preview paths pin it. So a proposal opened in the builder is byte-identical to one typed there, and the human’s Save is the write. Always the human’s.

One catalog, three consumers

The Typewriter reads the same worthstate_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 are sop-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.
Neither adapter gives the model a document editor, payload override, intake approval or posting path. The public MCP ingress and release source are current on registry 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.
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.
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.
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.
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

The Typewriter’s eval landed at a 36% baseline, and that is a feature of the harness rather than a problem with it: the eval reproduced two real agent defects on its first run.The numeric-field defect is fixed. One schema-aware reader now converts written number notation in both the proposal graph and the preview/save service, including header and line fields. It re-reads notation without guessing a unit, preserves ambiguous values for the human to see and lets the save path refuse them. The line-collection numeric score moved from 0.000 to 1.000 on all three recorded post-fix runs.An intermittent classification miss, roughly one turn in three, ends the turn with zero proposals.The remaining miss is still an agent defect the eval reproduces, not an eval bug. It is published because an eval that distinguishes a landed fix from a remaining failure is evidence the eval works.
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