Overview
Architecture Decision Records (ADRs)
How we keep decisions and domain docs
Two forward design genres. ADRs capture a researched decision — the choice, why, and the rejected alternatives — written only when a choice warranted weighing options. The Codex (apps/docs-codex/) documents the domain and its behaviors — the broad design; most of it has no ADR. Both are authored before code and seed the backlog.
One current version. Each ADR states the decision as it stands now. When a decision changes, rewrite it in place — no supersession chains, no per-decision tombstones, no narrative of how it changed. Git holds the past.
Keep the why, drop the changelog. An ADR keeps its rationale and rejected alternatives, so settled questions stay settled. It does not record how we got here.
Realization lives in the tracker, not the docs. ADRs and the Codex are the design; Linear / the milestone says how much is built. No status field on ADRs — a doc can legitimately run ahead of the code during a build window.
Two checkpoints. At authoring, validate against the live corpus (coherence; no contradiction with other current decisions). At close — folded into “verify before closing” — reconcile the governing ADR and the Codex pages you touched against the built code: matches → it stands; the build taught us better → rewrite the doc; the doc was right → fix the code.
Reversed → rewrite. Abandoned → delete.
Format
Every ADR is a markdown file named NNN-short-title.md holding the decision, its rationale, and the alternatives considered. The number is a stable handle for cross-references, not a chronology.
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.
- A decision changes → rewrite its ADR in place to the new current state. A decision is abandoned → delete its ADR. There are no
Supersedes:/Amends:/Status:headers, no supersession pointers, no “Retired/folded into” notes — in the ADR, the index, or anywhere. Git holds the past. (See §“How we keep decisions” above.) - Discipline reminders for day-to-day work live in
AGENTS.mdat the repo root.
Adding a new ADR
- Pick the next unused number (
ls docs/decisions/and take the highest + 1; numbers skip over retired-and-deleted ADRs). - Copy
_template.mdtoNNN-short-title.mdand rename. - Fill in Context, Decision, Alternatives Considered, and Consequences.
- If this decision replaces a prior one, don’t write a “superseding” ADR: rewrite the existing ADR in place to the new current state, or — if the prior decision is wholly abandoned — delete its file and sweep live references to the current decision. No
Supersedes:header, no addendum, no tombstone. - Add a row to the Index below (number + linked title). Remove the row of any ADR you deleted.
- Land the ADR in the same change as the code it governs when possible — the ADR and its first consumer ship together.
Index
| # | Title | Summary |
|---|---|---|
| 001 | Three-context architecture (worlds, platform, core) | The package boundary is the context boundary; inter-context isolation + Clean-Architecture layers enforced by the architecture validator. |
| 006 | API versioning and RESTful conventions | RESTful conventions for the API surface — resource shape, status codes, error envelope, router structure. |
| 007 | LangGraph agent architecture | LangGraph is the agent orchestration framework; the closed-over-DI tool-factory pattern. |
| 012 | Dev tooling — Biome, git-cliff, tiered commit hooks, ruff TD rules | Biome, git-cliff release notes, tiered commit hooks, ruff TODO-comment rules. |
| 015 | SystemCard protocol with authority and authority version | The SystemCard protocol carries an authority + authority_version convention across the context boundary. |
| 018 | Memory system — three tiers, world signal path | A three-tier agent-memory architecture with a signal path into Worlds. |
| 022 | Eval generation architecture | How evaluation sets are generated and structured. |
| 025 | System card authority basis | The basis on which a system card asserts authority. |
| 026 | World model version as unit of authority | A published world-model version is the unit of decision authority. |
| 029 | US Individual Tax Preparation as the first world-model domain | The first dogfood domain is US individual (1040) tax preparation. |
| 030 | Authority version as metadata-only across the context boundary | Authority version crosses the context boundary as metadata only, not a typed dependency. |
| 031 | Single-library Python package with per-app leaf namespaces | One installable library package (spectral) under src/spectral/; apps are leaf namespaces. |
| 032 | Storage topology — single Supabase project, three application schemas | One Supabase project; three application schemas plus core; forward-only migrations. |
| 033 | Tenancy enforcement layering | App-layer tenancy filtering is primary; RLS is the backstop; operator access is assumed-identity act-as. |
| 034 | Frontend data access via API proxy | Frontends reach data only through the API proxy; no direct browser→Supabase SDK. |
| 035 | LLM stack — in-process control plane | The in-process LLM control plane and the call boundary. |
| 036 | Observability stack — OTel substrate; three-stream LLM | OpenTelemetry substrate; a three-stream model for LLM observability. |
| 037 | Secrets management — provisioning script | Secrets resolve through the provisioning-script seam; BYOK reserved. |
| 038 | Embedding model + hybrid retrieval | The embedding model choice and hybrid (dense + lexical) retrieval. |
| 039 | Supabase Auth confirmation + hardening | Supabase Auth is the auth substrate, with confirmation + hardening measures. |
| 040 | Disaster recovery — Supabase-native managed backups + PITR | DR is Supabase’s managed backups + PITR on a regenerable alpha; no self-run pipeline. |
| 041 | Connection pooling architecture | psycopg3 async; session-pooler vs direct-connection strategy. |
| 042 | Data retention — four-state lifecycle | A four-state data-retention lifecycle. |
| 043 | Agent conversation persistence | Conversation-persistence patterns via the LangGraph checkpointer. |
| 044 | Event substrate — LISTEN/NOTIFY + outbox | The event substrate: a transactional outbox with LISTEN/NOTIFY delivery. |
| 045 | Test Supabase instance lifecycle | How the test Supabase instance is reset and seeded across the suite (no DB mocks). |
| 046 | Foundational alpha decisions — subdomains, frontend, auth pattern, dev tooling, Supabase | Subdomain layout, TanStack Start, Pattern-A JWKS auth, build/dev orchestration, supply-chain pin, Supabase. |
| 047 | Operations as a separate deployable | The operations console is a separate deployable surface. |
| 049 | Container build strategy — repo Dockerfiles, debian-slim-trixie base, one deployed app container | Build from repo Dockerfiles (digest + lockfile pinned); the deployed runtime is one collapsed app container; backup container retired. |
| 050 | TanStack Start + frontend SPA discipline | TanStack Start frontends render in SPA mode at alpha; SPA discipline. |
| 051 | Python type checker — ty primary, ruff ANN backfill | ty is the primary type checker; ruff ANN rules backfill annotations. |
| 052 | Cloudflare edge — DNS, TLS, proxied hostnames, WAF posture | Cloudflare is the edge: DNS, TLS, all product hostnames proxied (incl. api.), per-key rate limiting + WAF, codex Pages-Function JWKS, cookie discipline. |
| 053 | CD pipeline — fast-forward production-branch deploy | A fast-forward push of production fires the reusable GitHub-Actions deploy engine; one container deploy; /health smoke go/no-go; forward-fix rollback. |
| 054 | DLQ + retry semantics | Dead-letter-queue and retry semantics for the event substrate. |
| 055 | Curation → Worlds interface contract | The interface contract from curation into Worlds. |
| 058 | World Agent memory + agent-memory primitives | World Agent memory and the agent-memory primitives. |
| 060 | Agent tool invocation, framework-layer composition, and LLM-mediated error handling | The cross-cutting agent tool-invocation contract — error taxonomy, LLM-mediated handling, approval payload, DI composition. |
| 061 | LLM testing strategy | The cassette-based record/replay testing strategy for LLM calls. |
| 062 | CI secrets handling | CI secrets handling, fork-PR safety, and Environment scoping. |
| 063 | Inter-context access pattern — no SQL grants | Inter-context access is mediated, never via cross-schema SQL grants. |
| 064 | Notification-shaped reads — event-driven local replica | Notification-shaped reads served from an event-driven local replica. |
| 065 | Contract surface — admission rules and payload pattern | spectral.core admission rules + the opaque-payload contract pattern (producer-typed payloads, consumer ACL). |
| 066 | Snapshot-based contract testing with syrupy | Contract tests use syrupy snapshots over model_json_schema(). |
| 068 | Pragmatic methods on kernel value types | Kernel value types may carry pragmatic methods (clarifies the admission rule). |
| 069 | Trigger-driven spectral.core re-audit | The killer-test kernel re-audit is trigger-driven (not scheduled) — audit-candidate signals, the procedure, and tracked records. |
| 070 | Inter-context mechanism selection — simplest-fit ladder | A simplest-fit ladder for choosing the inter-context mechanism. |
| 071 | Flat application module placement | Application modules sit flat under the context, not nested by feature. |
| 074 | Retire the scan pipeline | The scan pipeline and its tournament/holdout machinery are retired. |
| 075 | Retire customer-facing eval generation | Customer-facing eval generation is retired; evals are an internal world asset. |
| 076 | Platform pillar as decision-module host | The platform pillar hosts decision modules. |
| 077 | Decision API surface — /api/decide and MCP | The decision API surface: /api/decide plus the MCP binding. |
| 078 | Retire the Spectral Agent | The standalone Spectral Agent is retired; the surviving topology is the single World Agent. |
| 079 | Retire the memory-tier signal path to Worlds | The T3-memory→Worlds signal path is retired. |
| 080 | Decision-module integrity — content hash and operator approval | Module integrity via content hash + operator approval. |
| 081 | World Agent role expansion and session boundary | The World Agent’s expanded role and its session boundary. |
| 082 | System Card and World Model Card under the in-band shift | The System Card / World Model Card model under the in-band decision-support shift. |
| 083 | Decision-module execution sandbox — layered defense, language-agnostic | Layered, language-agnostic sandbox defense for executing decision modules. |
| 084 | Multi-tenant noisy-neighbor handling | Rate limiting and per-tenant observability for noisy-neighbor handling. |
| 085 | Module store consistency — content-addressed storage with event-driven projection | Content-addressed bundles on the core.storage object store; commit-then-signal upload; honor-or-refuse; event-projected routing. |
| 086 | Tenancy hierarchy — org and domain | The org/domain tenancy hierarchy and naming. |
| 087 | Subject-identity delegation | JWT shape, act claim, lifetimes, revocation, and issuance for subject-identity delegation. |
| 088 | MCP surface — tool shape, resource shape, auth, decision-module binding | The MCP surface specifics. |
| 089 | Action discoverability endpoint | OpenAPI 3.x action discovery per (org, domain), with version pinning. |
| 090 | World Agent positioning and the world-model evolution workflow | World Agent positioning and the world-model evolution workflow. |
| 091 | API versioning — date-based, key-pinned, additive-only | API versioning is date-based, key-pinned, and additive-only. |
| 092 | read:actions scope — action-registry read access | The read:actions scope for action-registry reads; module approval is operator-only. |
| 097 | Architecture-validator rule registry | The canonical slugged rule set for the architecture validator + kernel dependency-direction discipline. |
| 098 | Domain and world as distinct entities with a one-to-one link | Domain and world are distinct entities under a 1:1 link; the active version is world-owned. |
| 099 | Core infrastructure zone | In-kernel infrastructure implementations of core contracts (the core/<area>/infrastructure/ zone). |
| 100 | Rule-provenance model — four axes and authoritative-source taxonomy | The rule-provenance model: four axes and the authoritative-source taxonomy. |
| 101 | Alpha agent topology — one agent (the World Agent) | One agent (the World Agent), serving operators directly. |
| 102 | LLM execution stack — LangGraph orchestration + LangChain structured-step | LangGraph orchestration + LangChain with_structured_output; DSPy deferred post-alpha. |
| 103 | Decision-authority positioning and the alpha product vocabulary | Decide-and-prove positioning; Workspace/Ruleset vocabulary; “world” internal-only; lifecycle labels; verify-at-review; publish-and-deploy. |
| 104 | Action registry and per-action module deployment | A world declares a real action set; one content-addressed module per action; the wildcard catch-all retired. |
| 105 | Tenancy authority & ownership model | Owner owns tenancy (operator participates, never owns); ≥1-owner invariant; self-service org provisioning + cold-start REST contract. |
| 106 | Rule-outcome model and winner_takes_all aggregation | A rule emits a static four-state outcome; severity is a sort/suppression axis under winner_takes_all. |
| 107 | The action’s canonical input ontology, reconciled at publish | An action carries a persisted canonical input ontology; a publish-time reconciliation converges its rules’ divergent input names onto it (published inputs frozen, enshrined-unpublished rules freely rewritten). |
| 108 | Behavioral-completeness publish gate | An independent behavioral spec → discriminating tests → predicate-to-pass → mutation gate enforces behavioral completeness at publish. |
| 109 | Cloudflare hosting topology — one app container, predicate in-process, Supabase locked | One Cloudflare app container (API + workers entrypoints), predicate in-process, World Agent in workers, outbox over the session pooler, generation-stamped deploy. |
| 110 | Provisioning by config resolution + GitHub Actions deploy — no Terraform | provision.sh resolves an environment (infra/environments.toml + 1Password) into a GitHub Environment; GitHub Actions deploys and reconciles vendor resources via wrangler + CLIs. |
TA-N spike identifiers
References to “TA-1,” “TA-3,” “TA-5,” “TA-19,” etc. inside ADRs in the 032–065 range 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 mapping is preserved in the corresponding Linear issues and in git 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.