Skip to content
GitHub
Decisions

Overview

Architecture Decision Records (ADRs)

This directory contains Architecture Decision Records for the Spectral project. ADRs document significant technical decisions, capturing the context, trade-offs, and alternatives so engineers — human and agent — understand why things are the way they are.

Format

Every ADR is a markdown file named NNN-short-title.md following _template.md in this directory. Copy the template when starting a new ADR; do not freestyle the structure. Uniform shape is what makes a growing corpus browsable.

Statuses

  • Accepted (YYYY-MM-DD) — the decision is in effect.
  • Accepted (supersession planned) — the decision is in effect but a replacement is planned.
  • Accepted (partially superseded by ADR-NNN) — the decision is in effect; one or more sub-decisions have been replaced by a later ADR while the rest remain authoritative. Whole-ADR supersession is a different state — see “Retired” below.
  • Rejected — the proposal was considered and declined. Rejected ADRs stay in the repository as history; deletion is rare and must be called out by the successor ADR.
  • Retired (folded into ADR-NNN) — the ADR was wholly superseded; its file has been deleted from the filesystem; the historical context worth preserving lives as a bottom-anchored addendum on the superseding ADR. The index entry remains so the supersession trail stays browsable. Git history preserves the original file’s full text at the commit that retired it.

Discipline rule

Any decision that shapes more than one epic or more than one file deserves an ADR. This is a default, not a ceiling — write an ADR whenever the reasoning is worth preserving for a future reader who joins after the context has evaporated.

  • Prefer one ADR per cohesive decision. If a decision bundles several related sub-decisions (as ADR-012 does with four dev-tooling choices), keep them together rather than splitting into separate ADRs that always have to be read as a set.
  • Link ADRs from the Linear issue that prompted them and from the Codex page that documents the resulting system behavior. ADRs are why; Codex is what; Linear is when.
  • Accepted ADRs are not edited while in force. Decision integrity is the point of the rule. The narrow exception: at a partial-supersession event, the superseded segment(s) of the prior ADR are removed (replaced with a one-line pointer to the superseder); the supersession’s documented rationale lives in the new ADR’s addendum. Other in-force decisions in the prior ADR remain unchanged. The intent is precision about what is currently authoritative — leaving superseded content in place produces the worst of both worlds, where a reader inside the prior ADR sees content that no longer governs.
  • At full supersession, retire the prior ADR. Write the superseder; distill what a future reader of the new ADR genuinely needs to understand why we’re here, not there into a bottom-anchored addendum on the new ADR (reference to the prior ADR by name + brief bullets); delete the prior file; sweep live references in the repo and re-point them at the successor. Git history preserves the full archaeological record.
  • Whole-ADR supersession only. A new ADR addressing one D# of a multi-D ADR is a new ADR that supersedes that specific D, not the whole prior ADR; the original’s other Ds remain authoritative and the original file stays. Reflect partial supersession in the prior ADR’s index status (see Statuses above) and remove the superseded segment from the prior ADR per the rule above (replace with a one-line pointer using the format: **D# — superseded by [ADR-NNN](NNN-...md).** [one-sentence note].).
  • Discipline reminders for day-to-day work live in AGENTS.md at the repo root.

Adding a new ADR

  1. Pick the next unused number (ls docs/decisions/ and take the highest + 1; numbers skip over retired-and-deleted ADRs).
  2. Copy _template.md to NNN-short-title.md and rename.
  3. Fill in Context, Decision, Alternatives Considered, and Consequences.
  4. If this ADR supersedes a prior one: add a Supersedes: line to the new ADR’s header; distill the prior ADR’s historical context worth preserving into a bottom-anchored addendum on the new ADR; delete the prior ADR file; sweep live references in the repo and re-point them at the new ADR; update the prior ADR’s index entry to Retired (folded into ADR-NNN).
  5. Update the Index and the supersession graph below.
  6. Land the ADR in the same change as the code it governs when possible — the ADR and its first consumer ship together.

Index

#TitleStatus
001Three-package bounded-context architectureAccepted — partially superseded by ADR-031 (supersedes ADR-011)
002Supabase for Database, Auth, and Infrastructure ServicesRetired (folded into ADR-046)
003LiteLLM for Multi-Provider LLM AbstractionRetired (folded into ADR-035)
004SQLite In-Memory for API Test DatabaseRetired (folded into ADR-045)
005Application Services over CQRSRetired (folded into README addendum)
006API Versioning Strategy and RESTful ConventionsAccepted (complemented by ADR-043)
007LangGraph Agent ArchitectureAccepted
008Migrate Scan Pipeline LLM Provider to Pydantic AIAccepted
009Operations Dashboard — Section, Not a Separate AppRetired (folded into ADR-047)
010psycopg3 + async DB migrationRetired (folded into ADR-041)
011Spectral is one bounded context organized into feature modulesRetired (folded into ADR-001)
012Dev tooling — Biome, git-cliff, tiered commit hooks, ruff TD rulesAccepted (mypy portion superseded by ADR-051)
013packages/core boundary definitionRetired (folded into ADR-065)
014EvaluationFramework as shared contractual typeAccepted
015SystemCard protocol with authority and authority versionAccepted
016Attribution metadata as opaque envelope in packages/coreRetired (folded into ADR-065)
017Event-driven signal path from Spectral to WorldsAccepted
018Memory system redesign — three tiers, world signal pathAccepted (World Agent tier vocabulary partially superseded by ADR-058)
019Complete rebuild of Spectral — net-new requirementsRetired (folded into README addendum)
020Tournament redesign — consistent scoring metricAccepted
021CI redesign from scratchRetired (folded into ADR-053)
022Eval generation architectureAccepted
023Holdout strategyAccepted
024packages/core governanceRetired (folded into ADR-065)
025System card authority basisAccepted
026World model version as unit of authorityAccepted
027Eval corpus as internal world assetAccepted
028Statistically unique EvalSets per requestAccepted
029US Individual Tax Preparation as the first world-model domainAccepted
030Authority version as metadata-only across the context boundaryAccepted
031Single-library Python package with per-app leaf namespacesAccepted (partially supersedes ADR-001; context renamed scanning→platform per ADR-041 D11)
032Storage topology — single Supabase project, three application schemasAccepted
033Tenancy enforcement layeringAccepted (partially supersedes ADR-002)
034Frontend data access via API proxyAccepted (partially supersedes ADR-002)
035LLM stack — pydantic-ai + in-process control planeAccepted (partially supersedes ADR-003)
036Observability stack — OTel substrate; three-stream LLMAccepted
037Secrets management — provisioning scriptAccepted (D1/D2 superseded by ADR-046; D5 partially superseded by ADR-073; D9 superseded by ADR-073)
038Embedding model + hybrid retrievalAccepted
039Supabase Auth confirmation + hardeningAccepted (confirms ADR-002 auth portion)
040Baseline DR + backup postureAccepted (D2/D7 partially superseded by ADR-072)
041Connection pooling architectureAccepted
042Data retention — four-state lifecycleAccepted
043Spectral Agent conversation persistenceAccepted (complements ADR-006)
044Event substrate — LISTEN/NOTIFY + outboxAccepted (D5 partially superseded by ADR-063)
045Test Supabase instance lifecycleAccepted
046Alpha hosting — Render PaaSAccepted (supersedes ADR-037 D1+D2)
047Operations as separate deployableAccepted (supersedes ADR-009)
048Deployment topology — six Render services + two PagesAccepted (D2 inter-context inheritance superseded by ADR-063)
049Container strategyAccepted
050TanStack Start + frontend SPA disciplineAccepted
051Python type checker — ty primary, ruff ANN backfillAccepted (supersedes ADR-012 mypy portion)
052Edge / CDN / DNS — CloudflareAccepted
053CD pipeline orchestrationAccepted
054DLQ + retry semanticsAccepted
055Curation → Worlds interface contractAccepted (D3 grant superseded by ADR-063; D3 Option A vs B sub-decision resolved by ADR-064)
056T3 Memory → Worlds routingAccepted (D3 grant superseded by ADR-063; D3 Protocol-vs-projection sub-decision resolved by ADR-064 D3 broadened)
057Failure cluster → Rule candidate signalAccepted
058World Agent memory + agent-memory primitivesAccepted (partially supersedes ADR-018 World Agent tier vocabulary)
059Operations Agent memory storageAccepted
060Agent tool invocation + framework-layer compositionAccepted
061LLM testing strategyAccepted
062CI secrets handlingAccepted
063Inter-context access pattern — no SQL grantsAccepted (§2 default-mechanism framing refined by ADR-070)
064Notification-shaped reads — event-driven local replicaAccepted (D3 broadened 2026-04-30 to cover T3 memory; partially supersedes ADR-055 D3 + ADR-056 D3)
065Contract surface — admission rules and payload patternAccepted (D1 superseded by ADR-068; D4 default-mechanism framing partially superseded by ADR-070; supersedes ADR-013, ADR-016, ADR-024; partially supersedes specific D#s of ADR-014, ADR-015, ADR-017, ADR-020, ADR-044, ADR-055, ADR-056, ADR-057, ADR-060, ADR-063, ADR-064 — per-decision list in ADR-065 Consequences)
066Snapshot-based contract testing with syrupyAccepted
067spectral.core killer-test re-audit cadenceAccepted (D1 + D2 superseded by ADR-069)
068Pragmatic methods on kernel value types (clarifies ADR-065 D1)Accepted (partially supersedes ADR-065 D1)
069Trigger-driven spectral.core re-auditAccepted (partially supersedes ADR-067 D1 + D2)
070Inter-context mechanism selection — simplest-fit ladderAccepted (partially supersedes ADR-065 D4 default-mechanism framing; refines ADR-063 §2 framing)
071Flat application module placementAccepted
072Cloudflare R2 for backups and Terraform stateAccepted (partially supersedes ADR-040 D2 + D7)
073Provisioning orchestrator — OpenTofu, setup.sh, 1Password, manual_stepAccepted (partially supersedes ADR-037 D5; supersedes ADR-037 D9)

Supersession graph

Nodes show each ADR by number and status. Arrows point from the superseded ADR to its successor.

The graph shows only ADRs that participate in supersession relationships. ADRs with no incoming or outgoing supersession edge (004, 005, 006, 007, 008, 010, 014, 015, 017, 019–023, 025–030, 032, 036, 038, 041–043, 045, 049, 050, 052, 053, 054, 057, 059, 060, 061, 062, 066, 071) are listed in the index above and are not redrawn here for legibility.

The retired style marks whole-ADR supersession events where the prior ADR file has been deleted from the filesystem and its historical context lives as an addendum on the successor. The superseded style is reserved for older retirements that have not yet had their historical context distilled into the successor (a transient state — convert to retired when the addendum lands). Partial supersession is a different state: the prior ADR remains accepted with one or more sub-decisions replaced; styled accepted and labeled in the index status column.

Relationship to the SWMS decision log

ADRs 013–028 were originally migrated from planning/swms-decisions.md (legacy numbering 023–038) as part of the 0.3.0 rebuild. The earlier entries in that document (SWMS ADR-001 through ADR-021) described the internal design of the World Model System; those decisions are captured in narrative form in the Codex under system-design/world-model-system/ rather than as standalone ADRs, because they describe the subsystem’s domain logic rather than project-wide architectural commitments. SWMS ADR-022 (three-package monorepo) is represented by ADR-001 in this repo.

Cross-references inside ADRs 013–028 that mention “the World Model System Codex” point at those Codex pages for the fuller design context.

TA-N spike identifiers

References to “TA-1,” “TA-3,” “TA-5,” “TA-19,” etc. inside ADRs 032–065 are spike identifiers from the 0.3.0 Tech Arch Review (TAR), which ran approximately TA-1 through TA-27. Each spike’s disposition produced one of the ADRs in this set. The TA-N → ADR-NNN mapping is preserved in the corresponding Linear issues and in git commit history; it is not reproduced here. A future reader landing on a TA-N reference inside an ADR can treat it as the spike-era identifier for that ADR’s disposition.

Retired ADR addenda

The following ADRs were wholly retired as part of the 0.3.0 rebuild. Their historical context worth preserving for future readers is captured below.

ADR-005 — Application Services over CQRS

ADR-005 (Accepted 2026-03-29; retired during the 0.3.0 rebuild) reversed an earlier CQRS introduction (command/query Pydantic models, handler classes, dispatcher routing) on grounds that, at v0.2 scale, CQRS added 16+ trivial query handlers, ~5-hop start-scan paths, per-request handler-graph construction, and ~1200 lines of dispatcher infrastructure for behavior that fit naturally in service methods.

Why a future reader should know about ADR-005:

  • The architecture style decision is now codified in Codex under system-design/foundations/architecture.mdx (Clean Architecture layering with thin application services in <context>.application.*); the Codex page is normative.
  • The driver was proportionality at the size we are: CQRS earns its complexity at scales where the read/write paths genuinely diverge. At Spectral’s scale (v0.2 then; alpha now), thin application services with a Result-returning use case handler are the right shape.
  • ADR-070’s simplest-fit ladder for inter-context mechanism selection inherits the same proportionality discipline: pick the simplest mechanism that meets the real need; escalate only when conditions warrant.

Git history at the commit retiring ADR-005 preserves the original text.

ADR-019 — Complete rebuild of Spectral — net-new requirements

ADR-019 (Accepted 2026-04-20; retired during the 0.3.0 rebuild) ratified the strategic decision to rebuild Spectral from scratch rather than incrementally migrate the v0.2 codebase: bounded-context introduction, memory-system redesign, evaluation-framework ownership transfer, tournament redesign, and CI overhaul collectively constituted a ground-up restructure, and incremental migration would have inherited known technical debt the new design rejects.

Why a future reader should know about ADR-019:

  • The rebuild is the world-as-is per AGENTS.md. There is no parallel v0.2 to migrate from; there is one codebase under active development against the 0.3.0 architecture.
  • The v0.2 codebase informed the rebuild as reference material — sound architectural patterns (Clean Architecture layering, Result type, pure functional verdict engine), domain vocabulary, and Codex docs — but no code was copied without explicit review.
  • The rebuild rationale itself (one-time strategic call) does not need restatement in newer ADRs; subsequent ADRs reference the v0.2 → 0.3.0 boundary by name where it matters.

Git history at the commit retiring ADR-019 preserves the original text.