Skip to content

Regulation Lakehouse — the demo, end to end

An agent that answers questions about a company's regulations and HR records, built so that a superseded regulation cannot be the source of an answer.

Everything on these pages runs. The stack is four cloudcheflabs products plus Apache Polaris and a few ordinary source systems, installed from released tarballs; the corpus, the pipeline, the policies and the agent are all here in full. Nothing is elided — if a file is part of making this work, its contents are on one of these pages, because the products are closed-source and this documentation is the only copy you get.

Follow it and it works

These pages are written to be executed, not skimmed. Read in order, paste what is shown, and you end with the same cluster the screenshots came from.


Why this demo exists

A mid-sized company holds a few dozen governed regulations buried in hundreds of meeting notes, several versions of each, and filenames that lie about which version they are. An index that treats all of it alike answers "육아휴직 며칠?" with a number that was correct two years ago — and nothing in the answer marks it as wrong.

That is the failure this demo is built around, and it is worth being precise about why it is hard.

A superseded regulation is the best match for the question it used to answer. It is the best lexical match and the best semantic match. No recency weighting reliably beats a document that says exactly what was asked. Ranking is the wrong tool.

So the retriever does not rank the old version lower. It binds to a view where the expired text is absent:

WHERE v.status = 'EFFECTIVE'
  AND v.effective_from <= CURRENT_DATE
  AND (v.effective_to IS NULL OR v.effective_to > CURRENT_DATE)

And effective_from is the approval date, not the date the document's own 부칙 (supplementary provision) prints. HR-REG-003 v3 claims 2025-01-01 and was approved 2025-03-15; for those ten weeks the old version was still binding. A pipeline that trusts the document body gets this wrong and has no way to notice.

The test that matters

홍가은 has used 12 of 20 childcare days.

Source read Answer
HR-REG-003 v3 (current, 20 days) 8 days left ✓
HR-REG-003 v2 (superseded, 15 days) 3 days left ✗

A temporal-isolation failure produces a wrong number, not a wrong citation. So the end-to-end test asserts on arithmetic rather than on whether a document was named.


What is running

                          ┌──────────────────────────────┐
    user ──── question ──► │  Agent (claude-opus-5)       │
                          │  9 tools, runs as the caller │
                          └──────────────┬───────────────┘
                                         │  every call carries the asker's identity
                                         ▼
    ┌────────────────────────────────────────────────────────────────────┐
    │  Ontul — federation · semantic layer · ontology · IAM · Flow        │
    │                                                                     │
    │   semantic views      retrievers        object/link/action          │
    │   (temporal filter)   (hybrid search)   (typed entities + writes)   │
    └───┬──────────────────────┬───────────────────────┬─────────────────┘
        │                      │                       │
        ▼                      ▼                       ▼
    ┌────────────┐    ┌──────────────────┐    ┌────────────────────┐
    │  Iceberg   │    │   NeorunBase     │    │  ERP (PostgreSQL)  │
    │  the       │    │  vector · FTS ·  │    │  Approvals (MySQL) │
    │  ledger    │    │  graph · OLTP    │    │  Training (REST)   │
    │ (Polaris+S3)│   │  = derived serve │    └────────────────────┘
    └─────┬──────┘    └────────▲─────────┘
          │                    │
          │   Ontul Flow       │   CDC + changelog
          └────────────────────┘

    ShannonStore  S3 object storage — the original documents and the Iceberg data files
    Polaris       the Iceberg REST catalog
    kiok          the batch scheduler that runs the indexing DAG
    embed-svc     multilingual-e5-base, pinned and offline

Nothing is present for breadth:

  • No Kafka. The real trigger for a regulation taking effect is an approval row changing, and CDC already reads that.
  • No separate search engine. NeorunBase carries vectors, Korean full-text and the graph, so hybrid fusion happens in the engine rather than in application code — which is where most stacks get their ranking wrong — and the IAM row filter is written once instead of twice.
  • No cloud API for embeddings. The model runs in-network with HF_HUB_OFFLINE=1, because a regulation corpus is not something you post to a third party to find out what it says.

What it demonstrates, and where to read each part

Install The whole stack from four release tarballs — every compose file and Dockerfile in full
Corpus 446 files shaped like a real shared drive — name collisions, scanned PDFs and register defects, all deliberate
Schema The Iceberg ledger, the semantic views, and the NeorunBase serving layer
Pipeline Distributed indexing on a kiok DAG — extract, chunk, effective dates, embed, graph
CDC and Flow ERP → Iceberg, graph → serving, and the approval event stream
IAM and retrievers Why the same question returns different rows to different people
Ontology Objects, links, and a governed write
Agent Nine tools, the system prompt, and the chat window
Verification The scenario suite and what was actually measured

Measured, on one laptop

Taken on a 16 GB laptop with 10.7 GB given to Docker.

Source documents 446 files → 123 matched, 319 unmatched, 4 scan-only
Ledger 50 documents · 104 versions
Chunks 818, none duplicated
Vectors 818 × 768 dim
Graph 50 nodes · 116 edges
ERP CDC 5 tables, 1,214 rows, compared by value
One DAG run about 50 seconds

A note on the failure mode this demo kept finding

Almost every defect found while building this — in the pipeline and in the engine — had the same shape: something went wrong and the caller was told nothing.

An expired catalog token turned every query into a successful read of an apparently empty ledger. A Flow with an unrecognised snapshot mode ran healthily and delivered nothing. A local ingest and a distributed job both loaded the same chunks, doubling the index while every count-based check still passed. A CDC connector encoded every decimal as base64 and the row counts matched exactly.

That is why the checks on these pages compare values rather than counts, and why several of them assert against the ledger rather than against what a component reported about itself.