Selected Work

Case 01 / 04 · 2026

Portfolio Data Platform

Two pipelines over this site's own data: a private knowledge base compiled into a 45k-token attention budget behind six gates, and a Databricks medallion that turns the traffic this site produces into an answer about who actually read it — governed, versioned and replayable on both sides.

  • Databricks · Lakeflow
  • Delta Lake
  • Unity Catalog
  • Supabase · Postgres + RLS
  • GitHub Actions
  • Astro · Vercel

01 · The problem

This site has two data problems, and they point in opposite directions. It has to KNOW things — enough for an assistant to answer a recruiter properly, without ever touching the private work journal those facts come from. And it has to OBSERVE things — enough to tell someone who read for five minutes from a crawler that fetched one page and left, without keeping anything that identifies either of them.

Neither is really a chat problem or an analytics problem. Both are pipeline problems with a privacy constraint bolted to the front: a private source that must not leak, a transformation that has to be governed, an artifact somebody can review in a diff, and a gate that is allowed to say no.

So they are built as two pipelines over one system, and they meet in the middle. The assistant answers from a compiled corpus; every visit and every conversation lands in Postgres; a Databricks medallion refines that into gold; and the questions the assistant could not answer come back as the next page written into the corpus. The toggle below switches between the two halves.

02 · Architecture

Fig. 01 The whole loop. What the site knows answers a visitor; answering produces telemetry; the medallion refines that telemetry into gold; and what gold reports — together with the questions that got no answer — decides what gets written next. The two panels below are this diagram, zoomed in.

One site · two pipelines

The outbound half: a medallion lakehouse over this site's own production traffic — Supabase and Upstash to gold, Auto Loader and Lakeflow, deployed as an asset bundle and run every morning.

  • Databricks Lakeflow
  • Auto Loader
  • AUTO CDC · SCD-1
  • Unity Catalog Volumes
  • Asset Bundles
  • PySpark
Fig. 02 The medallion. An at-least-once extract lands immutable NDJSON in a Unity Catalog Volume; Auto Loader reads it into a typed bronze; silver filters, joins and deduplicates it; and gold recomputes the judgements — sessionized visits, a bot class, daily and per-page rollups.
Fig. 03 How it ships and how it runs. Everything is declared in one asset bundle; two independent extracts run in parallel; the pipeline refreshes once both have finished — not once both have succeeded — and the result leaves the workspace as an email rather than staying in a dashboard nobody opens.

Telemetry · in detail

  1. Derive at source only what is destroyed at source

    The raw IP and the user-agent exist only at the edge, so a crawler token and the webdriver flag have to be captured there or they are gone for good. Everything else — bot scoring, sessionization, channel resolution — belongs downstream, where a tuned heuristic can be replayed across all history instead of being stamped into rows that can never be backfilled.

  2. The landing zone is the system of record

    Raw NDJSON is kept immutably, so declaring a column later and running a full refresh reconstructs history rather than leaving it null before the change. That property is exactly why the format is NDJSON and not Parquet: Parquet fixes a schema at write time, which would push type inference into the one component that must never lose data.

  3. At-least-once extract, idempotent silver

    The watermark commits after the write, so a run that dies re-lands rows. Rather than chase exactly-once extraction, silver absorbs it: AUTO CDC keyed on the identity id and sequenced by the run that observed it, so a replay is deterministic right down to which file a row came from.

  4. Gold is a judgement, and it is allowed to change

    A visit is only classifiable given its neighbours — "never seen again" is decidable only in hindsight, and a dwell beacon lands in a later batch than its page view. So gold recomputes in full, a visitor who returns tomorrow reclassifies yesterday, and the bot score is the count of signals that fired rather than a probability, because there is no labelled data to fit one against.

View the Telemetry code

03 · How it works

  1. A private source that never crosses its boundary

    Neither pipeline is allowed to move the sensitive half. The work journal is not in the repository at all, so it cannot reach CI; message text is never replicated into the lake, so a deletion request stays mechanically satisfiable instead of chasing copies. In both cases the protection is that the data is absent — not that something is trusted to guard it.

  2. The artifact is the contract

    Build once where the source lives, then ship something reviewable. The corpus compiles locally into a generated prompt module and a byte-stable lockfile, so a change is a diff a human can read. The lake keeps immutable raw files, so a schema declared today can be replayed over everything landed before it. Both make a rebuild reproduce rather than approximate.

  3. A gate that is allowed to say no

    Six build gates and thirty golden cases stand between a corpus change and production. A single silver expectation on _rescued_data stands between a column changing on this site and a wrong dashboard the next morning. Each exits non-zero rather than warning, because the cheapest place to catch a leak or a drift is before anyone can read the result.

04 · Outcome

Both loops now run themselves. A new fact is one page in the wiki, and the next build either folds it in or refuses to ship — today that is 21 sources compiled to roughly 32k tokens against a 45k attention budget, with 30 of 30 cases passing. A visit is one beacon, and by the next morning it has been sessionized, classified and rolled up, with a digest in my inbox saying what yesterday actually was.

The honest framing matters as much as the architecture: neither half is load-bearing. This site does not need a lakehouse for forty rows a day, and the page says so rather than implying a scale it does not have. What I wanted was a complete, small instance of the discipline the rest of my work runs on — a private source, a governed transformation, a versioned artifact, a gate that can say no, and a feedback loop that decides what to build next — built twice, in opposite directions, over data I own and can therefore talk about freely.

Read the thinking behind it: I Built My Chatbot's Knowledge Like a Lakehouse Ask the assistant about this