Inspiration

When a flight is disrupted, an operations desk has minutes to decide who gets rebooked, who is recoverable, and who needs priority handling. They have almost certainly handled the same shape of disruption before — same cause, same delay, same number of missed connections.

But that knowledge sits in people's heads and walks out the door with them. Traditional systems store records. They do not store judgement.

I wanted to find out whether a database could hold the judgement instead: not "what happened", but "what we did about it, and whether it worked".

What it does

A new disruption arrives. The agent builds a structured summary, embeds it, and runs a hybrid vector search over past disruptions — semantic similarity and a hard status = 'resolved' filter, in one indexed operation. It retrieves the closest past cases along with the outcomes those cases produced, then proposes typed, reviewable actions that cite the specific precedent they lean on.

A human approves, edits, or rejects. When the case closes, the outcome is embedded in the same transaction as the state write and becomes retrievable precedent for the next disruption.

The loop closing is the product. Everything else supports it.

Rejected proposals are memory too. An agent that records what humans overruled is more useful than one that only records its successes — an overrule is the strongest signal in the dataset.

How I built it

CockroachDB is the memory layer, and the reason this works as one system:

  • Distributed vector indexing (C-SPANN) on VECTOR(1024) embeddings, with a prefix column on case_status. This is the key technical decision: the structured filter is pushed into the vector index as a span rather than applied afterwards to an approximate-nearest-neighbour result set. EXPLAIN proves it — prefix spans: [/'resolved' - /'resolved'].
  • Vectors and transactional rows in one system. No ETL, no consistency gap between what the agent remembers and what actually happened.
  • Managed MCP Server for a runtime read-only analyst: an ops lead asks a question in English, the model writes SQL, and it executes through MCP — never through the application's connection pool.
  • Agent Skills found three real defects, described below.

AWS: Lambda Function URL for orchestration (no API Gateway, no VPC, scale-to-zero), Bedrock for Titan Text Embeddings V2 and Nova Lite via the Converse API, S3 for the static console.

TypeScript throughout. The frontend is Vue 3 + PrimeVue loaded from vendored UMD builds — no bundler, so deployment stays a plain file sync and nothing between the repo and the demo can fail to compile.

Challenges I ran into

Real embeddings scored worse than a fake one. My offline feature-hashing embedder beat Titan on the retrieval benchmark. The cause was not the model — I had accidentally tuned the summary text toward token overlap, which is what lexical matching is unbeatable at. Rewriting the summary to lead with an operational signature recovered it. I kept the finding in the README rather than quietly deleting the benchmark.

The agent quietly abandoned passengers. On one case it rebooked one of four people who had missed connections. The cause was my own prompt: "prefer a small number of high-value actions over exhaustive coverage." I replaced it with an explicit coverage invariant plus a deterministic check that recomputes unhandled passengers on every read, so a human rejecting the only rebooking action surfaces the gap immediately.

The analyst was confidently wrong, twice. Asked which cause had the worst outcome, it wrote a correct query, ordered it DESC, and read the top row — reporting the best cause as the worst. Later, asked which case caused the most trouble, it ranked by counting rows in a sampled child table and put a case with 62 misconnections ahead of one with 113. Both are fixed by prompt rules, but the real defence is architectural: every query and the rows it returned are shown beside the answer, so the answer arrives as a claim with its evidence attached rather than something to trust.

A Function URL returning 403 with a textbook-correct resource policy. Since October 2025 the invoke permission requires both lambda:InvokeFunctionUrl and lambda:InvokeFunction — and the docs note this applies even when the auth type is NONE.

Accomplishments that I'm proud of

The precedent is visible at the moment of decision. Each action group shows the past disruptions it leans on as a chip reading ZZ4381 · 5/5 · worked, tinted by outcome and clickable through to the full case. A reviewer can act on that. They cannot act on a UUID.

And the Agent Skills findings were real, not cosmetic: no 40001 retry handling anywhere (a correctness bug that only appears under concurrency), an index on created_at serving a query that orders by scheduled_dep — a full scan on the hottest read — and stale statistics reporting 52 rows against 84 actual.

What I learned

That the honest claim is usually the stronger one. This project cannot claim its analyst is reliable — no prompt makes a model's arithmetic dependable. It can claim the analyst is checkable, which is a different and more defensible property, and it is the one the UI is actually built around.

Also that a memory system's weakest point is often its prose. 81 resolutions in my seed data shared just 13 distinct outcome notes, three of them repeated fifteen times. Two precedents opened side by side read identically — which is precisely the moment the whole premise is supposed to land. Composing each note from the case's own facts took it to 62 distinct strings.

What's next

Multi-region, which is where CockroachDB earns its keep and a single-region demo cannot show. Feeding approve/edit/reject decisions back into retrieval ranking, so precedent that humans consistently overrule sinks. And a true read-only role for the MCP server — today read-only is enforced by my client's tool allowlist, because the platform requires Cluster Operator and puts all twelve tools on the wire regardless.

Built With

Share this project:

Updates

Submission history