Concepts
COMPASS stores one form: a Sache (a tracked question) holds Aussagen (dated statements that answer it). Everything else — rules, hypotheses, lessons, personas — is this same form applied to a specific job.
| Concept | What it is |
|---|---|
| Sache (subject) | A stable question COMPASS tracks the current answer to, e.g. "which test framework do we use". Identified by a kebab-case slug; optionally grouped under a bereich (area) and a stufe (level). |
| Aussage (statement) | One dated claim that answers a Sache. Every Aussage has an art (kind — see below), a herkunft (how it was obtained: measured, said, derived, decided, evidence), and a status. |
Fakt (fact, art=fakt) |
A measured or observed statement — typically written by a check (Prüfweg) after it runs. |
Regel (rule, art=regel) |
The statement type with teeth: at most one regel may hold (status=gilt) for a given Sache and subject at any time — enforced by the database, not by application code. |
Entscheidung (decision, art=entscheidung) |
A human call, recorded with its source (a decided question, an imported log entry, …). |
Hypothese (hypothesis, art=hypothese) |
A provisional statement that must carry a frist (deadline) and a pruefweg_slug (which check will confirm or refute it) — the contract rejects a hypothesis without both. |
| Lehre (lesson) | A separate record (compass_lehren, the "Hall of Lessons") for what went wrong, what went well, or a note from an agent to a human. A lesson can be turned into a rule, but only through an explicit human click — never automatically. |
| gilt_seit / gilt_bis | The validity interval: since when a statement has held, and until when (null = still holds). This is the time axis the domain cares about. |
| beobachtet_am / erfasst_am | The bi-temporal pair: when the underlying fact was actually observed, versus when COMPASS recorded it. The two can differ — a check can report on something that happened earlier. |
| Superseding, not deleting | There is no DELETE on statements. A new statement supersedes the old one (ersetzt points at the predecessor, which gets status=abgeloest and gilt_bis set to the successor's gilt_seit), or a statement is explicitly ended (POST /aussagen/{id}/beenden) when nothing replaces it. History is never erased. Why: see Set and supersede a rule. |
| Exactly one current rule per Sache | A partial unique index (uq_aussagen_eine_geltende_regel, on (kunde_slug, sache, subjekt_id) WHERE art='regel' AND status='gilt') makes a second concurrent rule for the same subject impossible to insert — the database refuses it, not just the API layer. |
| warum, check, deviation | GET /warum/{sache} answers "why does this rule hold" with its decision text and history. A Prüfweg (check) is a read-only, allow-listed command that verifies a rule against reality; a run that doesn't match what the rule expects is an Abweichung (deviation) with a determined cause (violation, tool missing, or unclear). |
| Mandant + key | Every row carries a kunde_slug (tenant) enforced by Postgres row-level security. A presented X-Compass-Key resolves to exactly one tenant and one scope: ro (read), rw (read and write), or bruecke (three narrow write paths only). |
| Persona + Brief | A Persona is an agent identity with a task, boundaries, a style, and a sichtfeld (visibility — which areas it may read). Its Brief is a deterministic Markdown document (no LLM involved) assembled from its rules, memory, and lessons, cut to fit a character budget. |
How a statement's lifetime works
A Sache never holds text directly — it holds a chain of Aussagen. Superseding
a rule keeps the old row (now abgeloest) and inserts a new one, linked by
ersetzt. The diagram below shows one Sache with two rule statements: the
first was superseded when the second was written.
Reading GET /gilt/{sache} at any moment returns the one row with
status=gilt — the query never has to reconcile competing answers, because
the database only ever allows one to exist.
Check a ledger independently
The single Python file core/scripts/ledger_pruefen.py recalculates a complete
exported tenant chain offline, without COMPASS or additional packages. Run
python3 ledger_pruefen.py kette.json --kopf kopf.json to compare it with a head
you kept separately; this also detects a truncated tail. A break reports the
first affected number. An empty chain or a missing head never reports success.
The ledger export itself is not yet part of the export endpoint; the input
format is defined for its future extension. The checker confirms the match
against your saved head, not its provenance or the truth of the stored statements.
Next
- New to COMPASS? Start with the Quickstart.
- Ready to change a rule? See Set and supersede a rule.
- Looking for a specific endpoint or tool? Jump to the Reference.