> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vinmake.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Typewriter to posted journal entry

> The technical golden path from schema composition and PDF approval through direct intake handoff, two accounting gates and the ledger writer.

# Typewriter to posted journal entry

The Typewriter golden path is **compose → save → sign → hand off → intake approval → draft journal proposal → posting approval → post**.
It shares the intake and ledger tail with uploaded documents, but it does not upload a PDF through the Clerk extraction graph.

## 1. Compose a payload

**Artifact, location, job.** The artifact is the schema-shaped document payload in the browser and preview request; its location is transient application state; its job is to render a document before anything is stored.
`POST /typewriter/preview` persists nothing and deliberately accepts a partial payload so the preview can show blanks as a person types.

Preview is not validation, approval or evidence.
The server refuses an unattributable request, but it does not turn preview HTML into the stored document.

## 2. Save exact PDF bytes and an immutable version

**Artifact, location, job.** The evidence artifact is the rendered PDF at `document-pdfs/<doc_type>/<uuid>.pdf` in the private `documents` object-storage bucket; its job is the exact document a signer will attest to.
The service hashes those exact bytes with SHA-256 before it records the version.

**Artifact, location, job.** The version artifact is a `typewriter_documents` row in Postgres; its job is to bind the schema payload, `pdf_path`, `pdf_hash`, document group, version, author and `PENDING` status.
The author identity and UUID come from the verified session; the request body cannot choose them.

Save refuses undeclared schema fields, unreadable numeric values, unsupported currency values and a service caller that cannot supply the session UUID required by the row-level policy.
A correction creates a new version with `supersedes_id`; it never edits the prior PDF or row.
If the database insert fails after storage, the service attempts to delete the object, but a process death in that interval can leave an unreferenced object; the source does not claim a cross-service atomic transaction.

## 3. Sign the stored hash

**Artifact, location, job.** The artifact is an append-only `document_approvals` trail in Postgres; its job is to record which designated human approved which step and when, against the saved version's exact PDF hash.
Before recording a step, the service reads the stored PDF and recomputes its SHA-256.
The database approval function rechecks the next step, designation and hash under lock, then records the step and changes the document to `APPROVED` only when the chain is complete.

The approval gate refuses the document author, a viewer without the required designation, an incomplete or missing chain, a superseded version and stored bytes that no longer match `pdf_hash`.
Signing does not create an intake row and does not approve accounting.

## 4. Hand the approved payload directly to intake

**Artifact, location, job.** The artifact is an `intake_queue` row in Postgres with `payload={fields, lines}` and `drafted_by='typewriter'`; its job is to place the approved composition in the same human review state machine used by Clerk output.
`submit_to_clerk` derives the idempotency key as `typewriter:<pdf_hash>` and preserves the document author's `created_by_identity` as the queue `submitter` even when another person clicks Submit.

Despite the route name, this handoff does **not** run `agents/clerk/graph.py`.
There is no second classification, extraction, Cognee text publication or Teable master-resolution pass here; the schema payload is projected directly into a `pending` queue row.
That is why “no Clerk flags” cannot be read as “the Clerk verified it.”

The handoff refuses a document that is not `APPROVED` and a version that is no longer the group's head.
A retry returns the existing intake id with `outcome='duplicate'` instead of inserting a second row.
A different live version in the same group produces a visible duplicate link and can move the new row to `flagged`; it is not auto-selected or auto-merged.
Every created, duplicate or failed attempt receives a `document_handoffs` audit row.

<Warning>
  This service flow is traceable in backend source, including the direct queue insert and handoff audit.
  The known Typewriter handoff fix is merged but was not deployed at the time of this review, and the authenticated deployed flow could not be exercised.
  The deployed Compose-to-Submit handoff is therefore unverified, not claimed operational from source alone.
</Warning>

## 5. Pass the intake gate

**Artifact, location, job.** The artifact is the same queue row changed to `active`, an `intake_events` decision and an `intake_launch_outbox` job in Postgres; their job is to record human acceptance and durably request a Bookkeeper launch.
The reviewer judges whether the composed business document belongs in the books; the prior PDF signature is not a substitute for that accounting decision.

The intake gate refuses the document author because that identity is the row's `submitter`, refuses the Typewriter drafting identity as a maker, refuses a person who resubmitted edited fields and refuses a duplicate group with another accepted or posted member.
Reject writes no journal entry.
Approve can return with the outbox deferred or failed, which means accepted but not yet proposed; it must not be reported as posted.

## 6. Draft a journal proposal and stop

**Artifact, location, job.** The artifact is a balanced Bookkeeper proposal in the durable LangGraph checkpoint; its job is to translate the active Typewriter payload through deterministic posting rules for a posting checker to inspect.
The Bookkeeper reads `intake_queue.payload`, existing Teable master data and the chart of accounts; it does not write any of those sources.

It refuses a non-active row, missing base currency, unmapped accounts or amounts, an unsupported accounting effect and imbalance.
It does not use a suspense fallback.
The graph stops before `post`, so a drafted proposal is not a draft row in the ledger and cannot become one without the second accounting gate.

## 7. Approve posting with a different person

**Artifact, location, job.** The artifact is the server-signed posting mandate; its job is to bind the exact organization, intake record, posting checker and verdict that may resume the Bookkeeper.
The checker must be distinct from the Typewriter author, any resubmitting editor and the intake approver; an agent cannot approve posting.
Together with the Typewriter author-versus-signer rule, the ordinary golden path has four distinct people.

The Bookkeeper refuses a missing or forged mandate, a checkpoint that disagrees with it, substituted lines, a changed source row and a forged success status.
It re-reads the active queue row and re-derives the proposal at post time rather than trusting the paused checkpoint.

## 8. Post one immutable accounting fact

**Artifact, location, job.** The artifact is one `journal_entries` row with its lines and `event_stream` event in the canonical Postgres ledger, linked back by `intake_queue.result_entry_id`; its job is the posted accounting fact and its trace to the approved document.
`ledger_bridge.post_and_mark` creates the ledger and applicable subledger facts, changes the queue row from `active` to `posted` and records the result id in one transaction.

`LedgerService.post_entry` is the only writer and enforces all 18 GL invariants before the write.
It refuses imbalance, invalid accounts or currencies, missing dimensions, a closed period, a conflicting replay and any attempt to mutate a previously posted entry.
If the intake transition cannot complete, the whole transaction rolls back.

## Verified at source

Composition, persistence, approval and handoff are in `langgraph_chat/devcenter/typewriter_api.py` and `langgraph_chat/devcenter/typewriter_service.py`; the PDF renderer is in `langgraph_chat/renderer/`; and the approval-chain boot assertion is in `typewriter_api.py`.
Queue and gate behavior are in `agents/clerk/queue.py`, `devcenter/intake_api.py`, `devcenter/intake_service.py` and `devcenter/intake_validation.py`.
Proposal, mandate and posting behavior are in `agents/bookkeeper/intake_source.py`, `agents/bookkeeper/graph.py`, `agents/bookkeeper/post_auth.py`, `agents/bookkeeper/ledger_bridge.py` and `backend/app/services/ledger_service.py`.
