Phase 3 core memory substrate

Phase 3 — Memory Engine and Operator Platform — Decision Log

Python stack choices and ADR decisions for the Memory Engine and Operator Platform phase.

Phase 3 — Decision Log

Quick decisions during Phase 3. Promote durable choices to docs/adr/ and the public ADR index when they affect multiple phases.

Python stack (non-negotiable)

ConcernChoiceReason
Python version3.11Performance over 3.10; 3.12 not yet stable across ML libs
Web frameworkFastAPI 0.110+Native async, automatic OpenAPI, DI
ORMSQLAlchemy 2.0 (async)Type-safe async queries, pgvector support
MigrationsAlembicStandard SQLAlchemy companion
Configpydantic-settings v2Typed, validated .env support
Testingpytest + pytest-asyncioasyncio_mode="auto"
Lintingruff + mypy --strictSingle tool, fast
Task queueCelery 5 + Redis brokerIndustry standard
gRPCgrpcio + betterprotoDataclass codegen, mypy-friendly
HTTP clienthttpx (async)Better typing than aiohttp
Embeddingssentence-transformers 2.xHuggingFace model flexibility
Packagingpyproject.toml + uvFast installs

ADR register (Phase 3 — Accepted)

ADRTopicMilestoneStatus
ADR-0047Temporal validity columns3.1.1 foundationAccepted
ADR-0048Multi-label memory_labels3.1.1 / 3.C.4Accepted
ADR-0049memory_relationships graph3.1.1 foundationAccepted
ADR-0052Memory schema v2 expand (HNSW, quality columns)3.1.1Accepted
ADR-0053VectorStore + composite scoring v23.2.1 / 3.D.2Accepted
ADR-0054Write-path Presidio PII3.C.1Accepted
ADR-0055Exact/near dedup3.C.2Accepted
ADR-0056Temporal-interval conflict detection3.C.3Accepted
ADR-0057Write-path persistence orchestration3.C.5Accepted
ADR-0058FTS fallback trigger3.D.1Accepted
ADR-0059Hot cache read path (Redis ZSET)3.D.3Accepted
ADR-0060Memory tenant isolation + read-path PII guard3.E.1Accepted
ADR-0061Retrieval-quality CI benchmarks3.E.3Accepted

Planning aliases not used

Early roadmap tables reserved numbers that collide with shipped Phase 2.5 ADRs. Do not reuse these for new work:

Planning aliasActual shipped ADR / note
“ADR-0032 memory data model”Not published — memory expand is ADR-0052 (ADR-0032 is session data model)
“ADR-0033 embedding service”Not published — ADR-0033 is ClickHouse llm_traces
“ADR-0035 vector / IVFFlat”Superseded — HNSW via ADR-0052 / ADR-0053
“ADR-0036 / 0037 conflict / extraction”Not Phase 3 — conflict shipped as ADR-0056; extraction ADRs are Phase 3.5+
“ADR-0040 memory schema / management API”Occupied — ADR-0040 Anthropic adapter
“ADR-0041 vector store”Occupied — ADR-0041 capability registry; vector store is ADR-0053
“ADR-0042 write pipeline”Occupied — ADR-0042; write path is ADR-0054–0057

Always assign the next free number from web/content/docs/adr/meta.json when publishing a new ADR.

Log pivots in findings. When an ADR merges, add it under docs/adr.

Architectural decisions

DecisionRationale
Expand memories (000017) instead of greenfield CREATEFoundations already shipped in 000014–000016 (ADR-0047–0049)
HNSW (not IVFFlat) as starting ANN indexAvoid live IVFFlat→HNSW cutover; greenfield table
Defer category → primary_category renameZero runtime benefit until app writers; keep 000015 trigger
CI Postgres = pinned pgvector 0.8.6-pg16 digestMutable :pg16 tag is unsafe for migrate smoke reproducibility
Separate Python context assembly service (not inline in Go proxy)Memory ranking needs NumPy, tiktoken, complex caching — wrong language for hot-path Go binary
gRPC for context assembly (not HTTP)Typed contract, lower overhead than JSON for <50ms target; matches auth pattern
Graceful degradation on context timeoutIf assembly exceeds ~45ms, proxy continues with directive-only context
betterproto for Python gRPCDataclass stubs vs protobuf message objects
Celery + Redis for workersAt-least-once delivery; idempotent tasks required
MinIO for session archivesPostgres metadata only; bulk conversation content in object storage
PAT auth for dashboard (Phase 3)OAuth deferred; operators use same token model as SDK

Pending decisions (resolve during milestones)

  1. HNSW ef_search per query — tune in Track B load tests (schema defaults m=16, ef_construction=64).
  2. Embedding GPU in dev compose — CPU default; document optional GPU profile in ENVIRONMENT_VARIABLES.md.
  3. Dashboard chart library — Recharts vs Tremor; lock in 3.8.5 before analytics pages multiply.
Edit on GitHub

Last updated on