Skip to main content

The 146-document registry

What it guarantees

Every document type GreatBook knows about has exactly one record, describing what it is, how to recognise it, what to extract from it, and what it does to the books. That record is generated from the ontology assets, not hand-maintained. Four independent consumers read the same file, so none of them can hold a different opinion about what a supplier invoice is.

The shape of it

146 records, split by category: Each record carries:

One record, in full

Two things to notice. The worthstate_models.schema.json value is a legacy source filename emitted by the current generator, not a product or service name. The classification cues are bilingual. Vietnamese source documents say hoá đơn GTGT đầu vào, not “supplier VAT invoice”, and a registry that only carried English tokens would classify by luck. The definition states the accounting effect inline. CREATES AP + 133 is a claim the posting rules then have to honour, and a mismatch between what a document type says it does and what the posting produces is a real defect - it is exactly how the missing input-VAT split was found.

The accounting effect

The field that connects a document to the books. The largest bucket by far is none. That is the right shape: most documents in a business are evidence, and treating them as postable is how a book fills with entries nobody asked for.

Line items come from the document, not the table

Seventeen document types carry a lines array. The line shape is derived from the ontology’s own lines: {type: array, items: {$ref: LineItem}}, not from a line_table field on the record. No record has a line_table, so anything built on it is dead code - and something was, which is why every bill once extracted lines: []. Lines ride their own state key through the graph, never inside the flat field map, so validation can present them back under the schema’s own property name before the shape check.

Four consumers, one file

  • The Clerk classifies against the cues, loads the record, and extracts the declared fields against the referenced schema.
  • The Typewriter proposes documents from the same catalog, so a proposal opens in the builder as a document the Clerk will recognise.
  • The SOP corpus generates its per-document-type intake rulebooks from the registry, so an agent’s playbook cannot describe a document type that does not exist.
  • The renderer reads the same schema, and its Docker build asserts at build time that the schema parses and carries at least 146 types.
That build-time assertion is a small thing that prevents a specific bad day: a schema file missing from an image works in development and 500s in the container.

Regenerate, do not hand-edit

The registry is generated from the ontology. Editing it by hand produces a file that the next regeneration silently overwrites, and in the meantime an agent is working from a description of a document that does not match its schema.

Where it is enforced

The records wrap under the top-level key documents. The loader also tolerates a development stub shape and a bare list, which exists because a mismatch there once broke graph loading in a way that looked like a model failure rather than a file-format one.

The test that would fail if it broke

langgraph_chat/agents/tests/test_integration.py loads the real registry, builds the dependencies and compiles the graph. That is the guard against a file-shape regression reaching production disguised as something else. The renderer’s build-time assertion is the other end of it, and the renderer suite includes a gate that renders all 146 types.

What goes wrong without it

Two copies of the document catalog is the failure. The Clerk classifies a type the renderer cannot lay out, or the Typewriter proposes fields the builder’s validator rejects, and each side looks correct on its own. That is the hardest class of defect to diagnose, because there is no wrong line of code - only two files that used to agree.