Skip to main content

The Claim contract

The Claim document in packages/shared/src/claim.ts is the contract between the generator, the database, the claim screens, the chat tools and every embedding host. It is a zod schema, so the same definition gives us the TypeScript type, runtime validation and a JSON Schema.

Change it there, then follow the type errors. Nothing else in the codebase is allowed to assume a shape the schema does not declare.

The field-by-field reference is generated from the schema.

Its shape​

The sections follow the Davies DGPH-34 claims-processing ontology, in the order a claim moves through it:

Two conventions run through all of it:

  • Money is a plain number in the claim's currency (GBP or USD). Formatting happens at the edge, in the region's locale.
  • Dates are YYYY-MM-DD strings. Comparisons are string comparisons, which is why the format is strict.

Documents carry a summary that stands in for the file. The assistant "reads" a document by reading its summary; it cannot open a PDF.

Eight required fields, honest defaults for the rest​

Only eight fields are required:

id, lineOfBusiness, title, status, currency, claimant, incident, policy

Every other section carries a .default(). Parsing fills it with an empty value: no reserves, no diary, investigation pending, litigation none. Two things follow from that.

zod's output type stays complete. Claim (the parsed type) has every section, so no tool, prompt or component has to handle undefined. Making the contract optional did not require a single consumer to change.

Absence reads as absence. The defaults are deliberately empty rather than plausible. A host that does not send a diary gets a claim with no diary entries, and the tools report "no diary recorded" rather than inventing a fact. Dates a host does not record are empty strings rather than a sentinel date, so anything derived from them is visibly missing rather than plausibly wrong.

Input type versus output type

z.input<typeof Claim> is what a host or the generator must supply. z.infer<typeof Claim> is what every consumer receives. The generated Claim schema page documents the input shape, with each default.

Who reads and writes it​

PartyRelationship
Generator (api/src/generator/)Writes claims. The full JSON Schema goes into the prompt; the reply is validated with Claim.parse and depth-checked.
Stores (api/src/claims/)Hold one document per claim as JSON text (D1) or JSONB (Neon), plus a few denormalised listing columns. They parse on the way out.
Chat tools (api/src/capabilities/)Read it. Every tool gets the full parsed claim and today's date.
Claim brief (api/src/chat/context.ts)Summarises it into the system prompt.
Claim screens (frontend/src/claimpilot/)Render it, through deriveClaimView.
Embedding hostsMap their own claim onto it in the browser and hand it to the element.

Demo metadata​

metadata belongs to the generator: provenance (generatedBy, generatedAt, taxonomyVersion), scenarioNotes, and metadata.demo:

  • hint: the one-line tip shown on the claim screen,
  • showcases: the capability ids the claim is built to demonstrate,
  • gotchas: the traps planted in the record, in plain words.

A host supplies none of it.

Changing the contract​

See the step-by-step guide, Change the claim shape. In short:

  1. Edit claim.ts. Give any new section a .default() unless every host can genuinely supply it.
  2. Run npm run typecheck and follow the errors through the fixtures, tools, claim brief and screens.
  3. Run npm test. The shared contract tests check that every fixture still parses and that the required core is still exactly what hosts are told it is.
  4. Regenerate the reference: npm run docs:reference.
  5. If the change adds a required field, existing stored claims will fail to parse. Regenerate or migrate them.