Skip to content

How Prologue works

Prologue’s job splits into three stages that run as two separate tools: the Extractor watches and correlates, the Interpreter reads the result and builds the model.

Extractor

JSON files

HTTP POST

yes

no

SQL Server

Change Data Capture

Time-window correlator

PostgreSQL

logical replication

HTTP reverse proxy

POST · PUT · DELETE

OpenTelemetry proxy

traces · metrics · logs

Capture

Mounted folder

Receiver

MongoDB

Heuristic model builder

LLM refinement

configured?

Rename, derive system name,

ask clarifying questions

ExtractionResult

+ generated .play

  • SQL Server — change data capture. The Extractor enables CDC on the database itself (you don’t need to); an optional table allowlist narrows which tables it watches, otherwise it captures every user table’s changes.
  • PostgreSQL — logical replication. The Extractor creates its own publication and replication slot on the configured database — nothing to prepare on the database side beyond wal_level = logical.
  • HTTP — a YARP reverse proxy sits in front of the system. It captures only state-changing methods (POST, PUT, DELETE) — method, path, response status, and the W3C traceparent trace id if one’s present — and forwards the request through unchanged. GET and other read traffic is invisible to Prologue by design; reads don’t produce facts to model.
  • OpenTelemetry — an OTLP proxy. It captures span, metric, and log metadata — service name and an allowlisted set of attribute keys, never full payloads — and forwards everything on to wherever your telemetry already goes, so nothing about your observability stack changes.

Every source captures metadata, not data — see Why Prologue for why that boundary is deliberate.

A single command rarely tells the whole story on its own — the interesting fact is usually the command and the database transactions it caused. The Extractor’s correlator groups observations within a configurable time window (correlation.windowMilliseconds, 2000ms by default) into one capture:

  • An HTTP command plus every database transaction, telemetry span, or log observed within [command occurred, command occurred + window] — or that shares the command’s trace id — becomes one capture. The command and its effects are read together.
  • Telemetry that carries a trace id but has no preceding command of its own groups into one capture per trace id — useful for background work that never went through the reverse proxy.
  • Everything else — a standalone database transaction, a bare metric — becomes its own capture.
  • A database schema change (a table or column appearing or disappearing) is recorded but is never itself evidence that groups other observations together — schema drift isn’t a business fact.

The correlator only emits a capture once nothing newer than the window could still join it — so a capture is never split across two runs just because the window hadn’t closed yet.

The Interpreter reads back whichever captures it’s pointed at — a folder of JSON files (batch mode) or MongoDB (service mode, for Studio) — and reconstructs them into an ExtractionResult: modules, features, and slices, each carrying the commands, events, read models, projections, and constraints the heuristics inferred from the evidence. See The extraction result for the exact shape.

Heuristics alone get you a structurally correct model with mechanical names. Point the llm section of cratis-prologue.json at a language model — Ollama, OpenAI, Azure OpenAI, any OpenAI-compatible endpoint, or Anthropic — and the Interpreter refines that structure into domain language: renaming commands, events, and read models, deriving a system name, and, when it’s genuinely uncertain about a decision that would materially change the model, asking you a clarifying question rather than guessing silently.