Skip to main content

Generating claims

Generation is CLI-only, by design: there are no in-app generation endpoints. Run everything from the repository root. The CLI reads GEMINI_API_KEY (and DATABASE_URL for Neon) from the environment or api/.dev.vars.

Run it​

npm run generate -- --lob auto --count 100 --concurrency 4 # into local D1 (the default)
npm run generate -- --lob property --count 100 --concurrency 4 --target neon
npm run generate -- --lob workers_compensation --count 100 --target d1-remote
npm run generate -- --lob auto --scenario auto_staged_collision_indicators --count 3
OptionDefaultMeaning
--lobautoauto, property, workers_compensation or general_liability
--count10How many claims to make
--concurrency3Model calls in flight
--scenario <id>every scenario for the line, in turnTarget one brief
--targetd1-locald1-local, d1-remote or neon

The count is spread across the line's scenarios, and workers pull from that queue. Each claim is written to the store as soon as it is made, so an interrupted run keeps what it finished. Re-running is safe: new ids never collide with what is stored.

Rough cost and time with Flash Lite: a few pence and one to two minutes per claim. At four in flight, 100 claims take about half an hour to 45 minutes.

Move data between stores​

npm run copy-claims -- --from neon --to d1-local
npm run copy-claims -- --from d1-local --to d1-remote

Every claim is upserted by id. The CLI reaches D1 through wrangler d1 execute (api/scripts/lib/stores.ts), because Worker bindings do not exist in plain Node; it reaches Neon through the Worker's own adapter.

How each claim is made​

generateClaims() in api/src/generator/generator.ts:

  1. System prompt: the taxonomy's data domains, the standards (dates in order, money that reconciles, a diary that tells the story), the hard minimum depth (8 diary entries, 6 documents, 5 tasks, 5 involvements, payments when money is paid), and the full JSON Schema of the Claim.
  2. User prompt: the scenario brief, its gotchas ("plant these in the record, not just in notes"), the capabilities to showcase, the demo-hint instruction, currency and jurisdiction, a variant number for variety, and a fixture of the same line of business as a depth-and-tone example.
  3. Reply in JSON mode, validated with zod, then depth-checked. A rejection is fed back verbatim and the claim regenerated, up to four attempts.
  4. Ids are CP-AUT-26nnnn, CP-PRO-…, CP-WCP-… or CP-GLB-…; metadata is stamped with the model and the time.

The schema goes in the prompt rather than as responseJsonSchema because Gemini's constrained decoding cannot take a schema this complex. See AI providers.

Add a scenario​

A scenario is a ScenarioBrief in api/src/generator/scenarios.ts:

api/src/generator/scenarios.ts
{
id: 'property_contractor_paid_twice',
lineOfBusiness: 'property',
title: 'Contractor paid twice for the same strip-out',
brief: 'Escape of water in a terraced house. Drying and strip-out by a panel contractor; the contractor invoices twice under different references and both are paid.',
showcases: ['fraud_check', 'data_insights', 'reserve_estimation'],
gotchas: [
'Two cleared payments to the same contractor for the same amount, three weeks apart, with different invoice references.',
'The second invoice describes the same rooms as the first.',
],
hint: 'Try Fraud check — one contractor has been paid twice for the same work.',
},
  • Gotchas must be discoverable in the record: in the diary, a document summary, the payments or the policy wording. A gotcha that exists only in metadata is invisible to the assistant.
  • showcases are capability ids from the shared catalogue.
  • hint is rewritten by the generator with the claim's specifics.

Generate a few with --scenario <id> and read them before generating in bulk. Then npm run docs:reference to add it to the scenarios reference.

Before a demo​

Read a sample from each scenario and delete anything weak. There is no delete endpoint; use SQL against the local store, deliberately, and never against a shared store as a habit.