ADR 0003: Do Not Adopt A Runtime Graph Database Yet
- Status: accepted no-adoption decision after bounded spike
- Date: 2026-08-20
Context
The P6 backlog proposed a runtime graph database. k0smos already stores the relevant operational relationships as indexed foreign keys in tenant SQL databases. Adding a graph store would create a second, eventually consistent projection with its own credentials, queues, reconciliation, tenant filters, backup expectations, monitoring, and failure modes.
The first candidate was an operational impact traversal from Client to Project and Ticket, followed by Calendar and Deadline links. It is representative of the currently requested relationship depth and uses real table and foreign-key names from those four modules.
doc/public/en/development/graphify.md describes an optional repository-analysis artifact generated
from source code. It is not a runtime application datastore, is not canonical
business data, and has no bearing on this decision.
Spike
The analysis-only OperationalImpactSqlProbe executes one bounded SQL CTE. It
accepts a positive client identifier, fixes the traversal to three levels, caps
results at 500, and never exposes arbitrary SQL or a general graph-query API.
The executable tests prove deterministic ordering, correct relationship
expansion, result/depth bounds, and absence of nodes belonging only to another
client.
The reproducible local benchmark used SQLite in memory with the same indexed relationship shape:
| Measure | Result |
|---|---|
| Clients | 200 |
| Projects | 1,000 |
| Tickets | 10,000 |
| Calendar events | 3,000 |
| Deadlines | 1,000 |
| Measured iterations | 250 |
| Rows per traversal | 75 |
| p50 | 0.480 ms |
| p95 | 0.571 ms |
| maximum | 2.152 ms |
This synthetic result is not a production SLO. It is sufficient for the architecture gate because the candidate is a shallow traversal over direct, indexed relationships and the SQL implementation is both small and fast. A graph adapter cannot improve the demonstrated use case enough to offset a new projection and its consistency/operations cost.
Decision
Do not add a runtime graph database, provider-neutral graph contracts, Neo4j
adapter, projection messages, graph credentials, or graph-specific health and
rebuild operations in 0.26.0.
SQL remains canonical and adequate for the only named query. If the query is promoted into the product, it must live in the application/module boundary that owns its authorization, use the tenant's existing SQL connection, apply the caller's module ACL before execution, and retain explicit depth/result bounds. The spike itself remains test/benchmark support and is deliberately not a core runtime service.
Reopen the decision only when a named product use case supplies all of:
- a relationship traversal that is materially less maintainable in SQL or misses an agreed p95 target on a production-shaped dataset;
- at least two stable projection consumers, or an explicit approved exception to the core-promotion rule;
- an authorization model for every returned node/edge and a zero-foreign-tenant test;
- measured projection throughput, lag, rebuild/recovery time, storage, and operational cost;
- a provider-neutral spike followed, not preceded, by any Neo4j commitment.
Consequences
Discovery use case — 2026-09-13
The public navigator adds three named product traversals: visitor need through an approved concept and documented feature to content; topic through a related concept to published articles; and content through linked documents to approved supporting evidence. Discovery owns indexed SQL nodes/assertions/edges and bounded traversal (maximum depth three, node and result limits), with tenant and public eligibility checked at every hop. This is a functional SQL graph, not adoption of a graph database or a provider-neutral graph framework.
The frozen 54-query IT/EN relevance fixture measures graph-case nDCG@10 of 0.70973 with documents/vectors and 0.94620 with reviewed graph paths (four synthetic cases). The 10,000-content / 100,000-point / 200,000-edge fixture initially missed the serving budgets: graph/retrieval p95 1,262.46/4,769.42 ms. Query-plan inspection identified scans in concept/edge frontiers and point identity resolution. Ordered indexes and bounded exact-term SQL preserve the original global seed order and MySQL collation semantics; the repeated fixture now records 31.03 ms graph / 367.09 ms retrieval p95, excluding embedding.
SQL is in-memory SQLite with actual migrations, canonical leaf reads are
synthetic, and Qdrant reuses one real vector. MySQL separately passes semantic,
collation, concurrent-review and index down/up checks. These observations are
not production-owner SQL or real-model capacity measurements. Keep SQL: there
is still no second stable consumer or complete supported-engine cost/recovery
comparison. All five reopening criteria remain applicable. The original
operational-impact timings do not establish Discovery's SLO. Detailed methods,
resource context and failed/passing artifacts live in
modules/Discovery/src/EVALUATION.Discovery.md and
tools/benchmarks/evidence/discovery-scale-remediation-20260913.json.
Operational consequences
- No external graph service is required by this decision.
- Existing tenant isolation, database backup, and module ACL boundaries remain authoritative.
- No external graph index, cross-store graph write or arbitrary Cypher surface is introduced. Discovery's SQL projection still requires freshness and recovery checks against canonical owners.
- The original graph investigation is complete with a measured no-adoption result. Discovery has a functional SQL graph meeting its warm synthetic serving budgets; external-engine adoption requires all reopening gates.