← all writing

[ ARCHITECTURE · 2026-07-12 · 6 MIN ]

The Context Layer Is an Event Store

by Burak Emre Kabakcı

[ no plate ]
FIG_01 [ cover · architecture ] © lobu v1

Tasman Analytics published How to build a context layer, the best writeup yet of a failure everyone running data agents has seen. The semantic layer is the governed what. Nobody wrote down the governed why. So the agent computes churn correctly, sees a spike in March, and invents an explanation, because the billing migration that wrote 1,900 fake cancellations was never stored anywhere a machine can read.

The numbers behind this are brutal. Anthropic found the same model went from at most 21% correct to over 95% on their internal analytics once it had the right skills around it: procedural routing, reference files, a governed data foundation. Then accuracy fell back toward 65% within a month as the underlying models drifted and nobody kept the skill docs current. In that system the limiting variable was the context, not the model.

Tasman’s conclusion is that no product can solve this, because the content does not exist yet. It lives in analysts’ heads and scrolled Slack threads. I agree that it is a workflow problem. I think the governed why wants one substrate, not four.

The governed why wants one substrate

Their design spreads the why across four homes: column caveats in dbt YAML, a business_events table in the warehouse, decision records as markdown in git, and verified queries pinned in the semantic layer. Four systems, four maintenance rituals, glued together by three MCP servers and the model’s context window.

I don’t think all four collapse into one product, and I want to be honest about which parts don’t. A dbt caveat belongs next to the model so the code and the caveat change in the same commit. A verified query belongs where routing uses it. Those have a source of truth and it isn’t Lobu. But they can be mirrored into one queryable place through connectors, the way you’d sync any source, so an agent reads one store instead of stitching three MCP servers together at answer time. Mirroring is not the same as owning. The authoritative copy stays where the authoring workflow lives.

What genuinely does want to live in one store is the part nobody owns today: the dated business events, the definition changelog, and the decisions. We took Tasman’s fictional company, Kelder Coffee, and built that overlay on Lobu without changing a line of Lobu. A business-event entity type holds the migration, the definition change, the courier strike, each with affected metrics and a link to the incident. The changelog is not a table anyone maintains. Events in Lobu are append-only and versioned by supersession, so saving churn v2 with a pointer to v1 is the changelog. Query the current definition or the full history, same store.

The live series is never materialized. A read-only connector runs the churn rollup live against the customer’s own Postgres, and a sandboxed script joins that series with the business events and the definition versions in plain JavaScript. The context doesn’t just annotate the numbers, it drives the query. March reads raw 550, and the billing-migration event carries a structured adjustment, so the script subtracts the ~500 artifact rows and reports 50 — the real cancellations that month still stand, the month isn’t thrown away. The churn definition’s v2 (a dunning-grace exclusion) is a WHERE fragment the script applies per month, so a month after the v2 boundary is counted at 38 where v1 would have said 50 — same warehouse, different number, because the definition changed the query and not just the label. Every deviation cites its source. No model in the loop.

That is a deterministic composition, and it proves seeded context can be composed into a sourced answer. It does not yet prove an agent retrieves the right context and writes the right query on its own — the repo ships that eval, WITH-context versus baseline, and it passes as a deterministic proxy; the live-agent run just needs a model key wired in. I’m calling that out rather than dressing the proxy up as the real thing.

The trust loop is a pinned verified query: store the approved answer, re-run the SQL later, diff the rows. When the warehouse moves under a definition, the rows change and the diff fires before someone repeats a stale number in a board deck. It is a result-drift canary, not a measure of whether an agent stayed accurate. Those are different failures and the honest version says so.

The runnable example is in the repo (link goes live when the PRs below land).

Capture is an ingestion problem, not a documentation problem

“The content does not exist yet” is the strongest line in their piece, and their fix is PR templates and incident runbooks. Asking humans to write documentation is how we got here.

The why gets spoken exactly once, in a Slack thread, at the moment the CFO agrees to exclude returns from revenue. Lobu’s watchers already sit in those threads. Capturing that sentence as a decision event, typed, dated, linked back to the thread, is ingestion. The human approves it instead of authoring it. That is the difference between a context layer that fills up and a wiki that rots.

Context has to be pushed, not searched

An agent that doesn’t know March is poisoned has no reason to search for “is March poisoned.” The jump from 21 to 95 comes from context sitting in the prompt before the first query gets written, not from a retrieval the agent thinks to run.

This one we had to fix in our own house. Entity-type and field descriptions were stored in Lobu and never rendered into the instructions agents receive, so we render them now, and we added a guidance event kind: org-level context that admins author and every agent sees on every request. The data model is just an event, which is the nice part, editing guidance means superseding it and the history of your own operating instructions is more memory. But storing it is the easy half. Rendering it, gating who can author it, capping how much lands in the prompt, and injecting it safely is the actual work, and that was a real branch, not a config flag. A prompt budget also means this can’t hold arbitrary org context, so selective retrieval doesn’t go away, it sits underneath.

One more thing nobody in this conversation mentions: access control. The moment real decisions go into a shared layer, “who may see this why” is the first question an enterprise asks. Lobu compiles per-connection and per-resource visibility straight into the query SQL, so a private connection’s data or a repo you’re not a member of never surfaces in a coworker’s answer. Arbitrary per-decision authorization is further out, but having the fence compiled into the read path rather than bolted on afterward is the part that’s genuinely hard to retrofit.

What’s honestly still missing is more than plumbing. Warehouse connectors in cloud needed real egress hardening before we could open them, and that landed. Lineage from a dbt manifest needs an SPI addition. Federated metric definitions from Cube or Snowflake are designed and not wired, and reflecting them means the metric compiler can’t assume Postgres dialect. Beyond our own code: the Slack-to-decision capture I described is the design, not a shipped demo, and the harder-than-storage question of whether a captured sentence is actually an authoritative, correctly-scoped decision is editorial judgment a human still makes. And the agent eval passes as a deterministic proxy today; the live-agent version, an actual model answering the March question with the context pushed versus without, needs a provider key wired in and is the honest remaining gap. So the honest claim is narrow: the governed why is a small, versioned, access-controlled event store, and it mirrors the semantic layer and the warehouse rather than replacing them. That is a component, not a fifth product you buy, and you can run it today.

Also worth reading: Tasman’s semantic vs. context essay, Yali Sassoon on why a semantic layer is not a context layer, and Snowflake’s agent context layer.