Code conventions¶
The rules that keep the codebase clean with several people working in parallel. They
originate in the skeleton proposal (2026-09-01,
frozen); this page is their home as current truth (D28: a frozen proposal is cited for
history only). Enforcement: tach.toml for import boundaries, pre-commit for lint, the
test suite for the rest.
Created 2026-09-03. Updated 2026-09-03.
- New stage = new package under
stages/, one PR with its service, its contract types, and mirrored tests. Never a new function bolted into an existing stage. - Stages never import each other. Only the shared contracts and
runs/. Enforced by tach in CI. - Deterministic stages cannot import an LLM client (compose, chunk, normalize). Also tach-enforced; this is what keeps them testable with zero mocks.
- Stage functions are pure; idempotency is decided in one place per category.
Derivations (pure functions of stored input plus versions) go through
run_stage(), which computes the run key and appends a run. Whether the key is stage-supplied over an open set of version axes, and whether mutations (dedup, compose) are a named second category idempotent by unique index plus row lock, is open. - Every table inherits
OrgScopedBase.organization_idis part of identity, never a filter convention (invariant 8). - No native Postgres enums on evolving status columns: text plus a check constraint.
Registry-validated vocabularies (
entity_type,claim_type) get no check constraint at all; the functions layer validates them, so adding a type is not DDL. - Version bumps add modules, never edit them:
prompts/v2.pynext tov1.py;ontology/v0.pyis never edited oncev1.pyexists. Whether prompts live in the repo or in Langfuse is open; the never-edit rule holds either way. - One PR = one stage or one cross-cutting concern. A PR touching two stages' services means the boundary is leaking: split it or fix the boundary.
- No job designed to exceed about 10 minutes. Work that grows with input size shards (per chunk batch, per source), never stretches. Keeps the Lambda path open and bounds what a retry loses.
Naming: plural snake_case tables, _id for references, _at for timestamps; kind for a
closed set the code switches on, *_type for a registry-validated vocabulary. The
storage-level conventions (supersession columns, caller-derived ids, composite org keys)
are owned by the data model doc (design/data-model.md, arriving with feat/graph-data-model).