Inspiration

Institutions keep a durable, structured, adversarially-useful record about you. Your internet provider knows your account number, your service address history, every billing period, and the policy version in force on the day you called. You keep nothing comparable about them.

The price is paid in tail obligations: the $1,800 deposit promised "within 30 days of inspection" that quietly was not returned; the cancellation confirmed in writing on 15 May and billed again for June. Each is worth a few hundred dollars and about four hours of reconstruction, which is exactly why they go unresolved. The problem is evidentiary, not motivational.

What it does

Provenance keeps one thing: your open obligations with the companies you deal with, held as versioned beliefs grounded in immutable evidence, projected into transactional state, with prospective triggers so that a deadline passing is itself an event.

When a forwarded invoice arrives four months after a confirmed termination, it does not summarise it. It admits the bytes as immutable evidence, types the content as a counterparty claim rather than a fact, detects that it is mutually exclusive with the canonical service_terminated belief, reopens the closed case in one serializable transaction, and drafts a reply whose every factual sentence carries a support id.

The deployed app shows USD 2,020.00 outstanding across four relationships, summed by the API from rows the Kernel wrote. There is no login: the web app holds its token server-side.

How we built it

Models are extraordinary at interpreting ambiguous evidence and catastrophic at being the authority on what is true, so the two jobs are split by architecture rather than by prompt.

Gemini agent graphs read untrusted evidence and emit typed MemoryProposal objects. They hold no SQL write credential — enforced by database grant, checked by lint. A deterministic, model-free Memory Kernel validates each proposal and commits beliefs, obligations, state transitions and outbox records in one SERIALIZABLE CockroachDB transaction, or commits nothing.

Six levels stay apart, each its own table: Artifact → Evidence → Claim → Belief → Commitment → State. A forwarded invoice does not make $186 owed; it makes $186 claimed, by a party with a financial interest, about a period after a termination that party confirmed in writing. Every qualifier there is a column. Ordinary RAG has one level — the chunk — and settles the contradiction by cosine similarity.

Built with: Gemini via the Gemini Developer API and the GenAI SDK (google-genai 1.60.0) — gemini-3.6-flash for semantic resolution and contradiction characterisation, gemini-3.5-flash-lite for extraction and bulk structured output. Google Cloud: Cloud Run (two services, us-east4, deployed and serving), Cloud Build, Artifact Registry, Secret Manager. Python 3.12, FastAPI, Pydantic, psycopg, the MCP SDK. Next.js 15 and React 19 on the front. CockroachDB Cloud: 26 tables, 5 agent-safe views, 5 SQL roles.

Challenges we ran into

Model ids frozen from documentation were all wrong. An earlier model canon was written from a docs page; every one of the four ids turned out to be un-invocable. The probe written to prevent a repeat then produced two wrong verdicts of its own — both defects in the probe, not the models. Everything is settled by invocation now, and the transcript is committed.

A passing frontend suite could not see what mattered. Fixtures and types written from the same reading of the spec agreed with each other, while nine live routes returned 500 against the real API. Types are not evidence. We now narrow contracts to what the API actually builds, which turns that class of bug into a compile error.

Capacity is a runtime condition, not a bug. On the final day gemini-3.7-flash answered 0 of 3 two-word prompts with 503 UNAVAILABLE. Because both tiers were probed and both clear the 3.5 floor, moving Tier R to gemini-3.6-flash was a one-line environment change — and the live agent graph went from PASS 31 / CANNOT RUN 12 to PASS 34 / CANNOT RUN 8, with the Tier R resolver passing against a live model for the first time.

Accomplishments that we're proud of

Two Cloud Run services serving now, with an unauthenticated GET /v1/version a judge can curl for fixture_mode, db_ok and the running revision's git_sha. A route sweep against that deployment reports 50 discovered, 0 broken. The hermetic unit lane runs 2,955 passed, 0 failed; the frontend

  1. Both Gemini tiers have been invoked live, leaving 37 attributed agent_runs rows.

What we would actually defend is the reporting discipline. Six of the API's 47 port methods are unbound, and each answers 501 NOT_IMPLEMENTED naming the subsystem it waits on — never 500, and never an empty list, because a read returning [] would render as "memory did nothing on this case" and be believed. CANNOT RUN is recorded distinctly from FAIL everywhere.

What we learned

Absence is not emptiness, and that difference has to live in the types rather than in someone's memory. A hardcoded verdict beside a computed fact is worse than no verdict: our first rehearsal printed PASS next to git_sha equals HEAD: NO. A retrieval suite whose headline check was a literal Verdict.PASS would have reported the same pass at recall 0.00.

And a stale transcript is its own kind of lie. Evidence has to be re-measured, not quoted — several numbers in this repository moved within a single day.

What's next for Provenance

Compose the retrieval pipeline: all eight stages exist and the ANN statement is proved against the corpus, but no module runs them end to end, so retrieval is not yet on the live path. Build the trace assembler over rows already persisted, which unbinds the two trace reads. Make the live read path produce a typed StateProof, which is what currently blocks intent creation and therefore blocks approving an outbound draft.

Built With

  • artifact-registry
  • cloud-build
  • cloud-run
  • cockroachdb
  • fastapi
  • google-cloud
  • google-gemini
  • google-genai
  • mcp
  • nextjs
  • playwright
  • psycopg
  • pydantic
  • python
  • react
  • secret-manager
  • typescript
  • vitest
Share this project:

Updates

Submission history