Skip to content

ADR Index

Architectural Decision Records for the Dazzle project. Agent-scannable: each line is a decision that prevents a wrong proposal.

  • 0001 — MkDocs Material for docs. No wiki, no Docusaurus.
  • 0002 — MCP = stateless reads. CLI = process/writes. Don't block conversation with long ops.
  • 0003 — No backward compat before v1.0. Delete freely, never create shims or wrappers.
  • 0004 — DSL optimized for AI agents. Precision and formal correctness over human ergonomics.
  • 0005 — RuntimeServices dataclass on app.state. No new module-level singletons.
  • 0006 — IR is immutable frozen Pydantic. Never mutate after parse.
  • 0007 — RBAC: static matrix + dynamic conformance + audit trail. Three independent layers.
  • 0008 — PostgreSQL is the sole database. No SQLite code paths.
  • 0009 — Scope rules compile to 6-type predicate algebra. Validated against FK graph at link time.
  • 0010 — permit = role gate (RBAC). scope = row filter (ABAC). Never mix.
  • 0011 — Server-side HTML rendering + HTMX. No SPA frameworks. Originally Jinja2; rendering pipeline is now typed Fragments — see ADR-0023, post-#1042.
  • 0012 — Alembic for schema migrations. No hand-rolled migration planners.
  • 0013 — One per-project KG with TOML seed. No separate knowledge systems.
  • 0014 — No from __future__ import annotations in FastAPI route files. Breaks OpenAPI.
  • 0015 — TigerBeetle for double-entry ledgers. Optional dependency, DSL ledger/transaction constructs.
  • 0016 — API Packs for vendor integration. TOML-driven mocks, webhook testing, DSL generation.
  • 0017 — All schema changes via Alembic, including framework entities. No raw DDL at startup.
  • 0018 — All file writes go to the project directory. Never write to the Python package directory.
  • 0019 — (Entity, Surface, Persona) triple is the atomic unit of verifiable behaviour.
  • 0020 — Lifecycle evidence predicates are orthogonal to state machines.
  • 0021 — Marketing pages via sitespec.yaml. No # dazzle:route-override on public paths.
  • 0022 — Don't put Alpine bindings on idiomorph-morphed elements. Server-render or use x-init helpers with direct DOM manipulation.
  • 0023 — Two-pattern template-emission model post-jinja2. Pattern A (framework writes HTML) uses f-strings + dazzle.render.html.esc; Pattern B (framework executes user-authored templates) uses string.Template. Choice is mechanical: who writes the template.
  • 0024 — No regex for DSL grammar. A regex parsing DSL is a signal for missing grammar, not an end solution. Lexical-shape regex (identifiers, numerics) is fine; matching call shapes / keywords / sub-expressions is not.
  • 0025 — Authorization is entity-level only. Field-level auth is not added; it would break the enumerable role×entity×operation security surface. A field with its own lifecycle is its own entity. Field sensitivity uses classify/pii(), not permit:.
  • 0026 — Subtype polymorphism uses TPT (table-per-type), flat hierarchy, immutable discriminator. Complex, potentially brittle — only justified when all three conditions hold: true IS-A, subtype-specific NOT NULL fields, polymorphic queries genuinely needed.
  • 0027 — No polymorphic_ref: keyword, now or planned. Rails-era ORM idiosyncrasy that breaks referential integrity, scope composition, and JOIN-based queries. Every classic use case (comments, attachments, tags, audit log, notifications, likes) fails a four-question interrogation that routes to per-target refs, event-stream entities, per-pair junctions, or TPT (ADR-0026). Union sugar ref X | Y | Z rejected outright.