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
- Both Gemini tiers have been invoked live, leaving 37 attributed
agent_runsrows.
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
Log in or sign up for Devpost to join the conversation.