What got settled
Out of the gaps in 01-origin-and-requirements fell a specific, opinionated architecture — the one the register now calls, without much ceremony, the settled state:
- A single SQLite file, with an explicit edges table — graph-shaped, not a flat store of records.
- Hybrid retrieval: sqlite-vec for dense vectors, FTS5 for lexical search, fused by Reciprocal Rank Fusion.
- Bi-temporal supersession — nothing is ever mutated in place; a correction closes nothing and inserts a new assertion, and “what’s current” is derived by replaying the log rather than stored as a flag. (The full story of how this got validated, not just decided, is its own page: theme-bitemporal-supersession.)
- A human-approval write gate — Claude proposes, I approve, and nothing else touches the authoritative log. This turned out to be the single decision everyone — me, Claude reviewing the design later, the register itself — converged on as the most important one in the system. Full story: theme-human-approval-write-gate.
- The MicroServer as the authoritative store, replicated continuously via Litestream.
- One MCP server, six tools, and no more — a deliberately frozen surface rather than a tool added every time a new need appeared.
- Claude does the proposing; the local LLM doesn’t. The Stage 1 plan was reversed within two weeks — see below.
None of this is meant to be re-litigated casually. The register’s own line for it: not for re-litigation without a forcing function.
(One naming clash worth flagging early: mem-local’s own build has its own internal “Stage 2” — the eight-decisions session this page is about — which has nothing to do with Stage 2 of this six-part reconstruction timeline, i.e. this page itself. Same word, two different countings. Stage 6 runs into it again with mem-local’s own Path 2, so it seemed worth naming here rather than there.)
Eight decisions, four reversals
I went into the session that nailed this down expecting to ratify a stack of things I’d mostly already decided. I came out having reversed half of them — and the reversals were the actual content of the session, not noise around it.
The pattern was consistent enough to be funny in hindsight. Every reversal happened because a mundane fact about my actual setup showed up and killed a clean theoretical answer:
- Transport (Decision B). I’d settled on stdio-over-SSH — genuinely the most secure option, no network port to misconfigure. Then I mentioned wanting to reach the system from my phone and iPad. stdio physically can’t reach a mobile MCP client. The secure, elegant answer had been answering a question I didn’t actually have. It reversed twice before landing on Streamable HTTP with the server bound to localhost and the existing nginx doing the TLS fronting — off-LAN access deliberately deferred.
- Embedding location (Decision G). On paper, CPU on the always-on server is the simplest choice — no GPU driver surface to rot over the years. Then I described what that server actually is: a 2-core, 17-watt Xeon already running DNS for the whole house plus about ten other containers. Embedding pins cores while it runs, which in practice means the internet goes slow for everyone in the house during memory-system maintenance. We checked
nvidia-smiand found the Quadro P400 sitting almost idle. Not the fancy option — the option that doesn’t degrade DNS. - The fork base (Decision C). I’d narrowed the build to a promising SQLite-native project advertising exactly the bitemporal model I needed, on relationships as well as memories. Claude ran a verification pass instead of trusting the README, and it didn’t hold up — no embeddings at all, and its bitemporal support was edge-only and mutated records in place rather than appending, the opposite of the never-delete property the whole design rests on. The shortcut evaporated and the scary part — writing the append-only bitemporal SQL myself — came home to roost. (Full account: the verification-pass write-up.)
- The dashboard (Decision D). Reversed from retire to migrate, once the actual Google Sheet turned out to hold thirteen projects on clean keys — a working prototype of half the system, not something to throw away.
Four more (schema versioning, concurrency, hard deletion left open by design) shaped the build without reversing anything, and are recorded in full in the register rather than repeated here.
The lesson underneath the architecture
Every reversal came from the same pattern: a mundane fact showing up late, after I’d already reasoned out a clean answer for a situation that didn’t actually exist. The device list. The existing reverse proxy. What’s already running on the box. Which model’s already loaded in the GPU. A well-argued answer to the wrong constraints is still wrong. The fix isn’t cleverer architecture — it’s asking about the boring hardware first.
The local LLM’s role, reassigned
The Stage 1 plan — a local LLM confined to the offline write path, drafting proposals for me to approve — didn’t survive contact with the actual hardware. Two related decisions, both ruled around 29 June, reversed it:
- Decision T. Stage-1 proposing is done by Claude, via
propose_entry, in the same conversation I’m already having — not by a separate local model. The forcing function was concrete rather than philosophical: the RX 6600 XT was unproven for this job (no LLM had actually been run on it, and Decision G had already flagged ROCm’s driver fragility on AMD hardware for exactly this kind of workload), and demoting the local-LLM proposer removed an unproven dependency from the Stage 1 critical path. The local LLM wasn’t deleted from the architecture — it survives as a deferred privacy experiment, a possible future option for drafting proposals offline in sessions where the content shouldn’t go to a hosted model at all. That’s a real, if currently unbuilt, use case — just not the main path. - Decision U. The RX 6600 XT itself got reassigned to a different job entirely: Path 2, deliberate-invocation local compute over a read-only replica of the store — historical semantic search, chiefly, plus room for heavier exploratory work later. Two invariants keep it from quietly becoming something it isn’t: the replica is read-only, and Path 2 never joins the live MCP path. That second rule matters more than it looks — the moment historical search becomes automatic on the live path, the benefit of the eviction policy below is lost through the back door.
The eviction policy Decision U leans on is itself a same-day reversal from earlier in the project: Decision I originally kept every superseded entity’s embedding rather than discarding it, precisely so a future historical-search feature wouldn’t need to re-embed anything. It was struck the same day it was made, in favour of the current design — the live path evicts the vector on supersede, keeping one per live entity, while the underlying assertion text is retained permanently and can be re-embedded on demand. The practical effect is the same intent (nothing about history is unrecoverable) achieved a cheaper way (discard a pre-computed vector rather than carry every version of it forward forever), with the actual recomputation pushed out to Path 2 rather than done automatically on every write. Stage 6 covers where Path 2 stands today — still deferred, not yet built.
Why this is the model, not a fork of one
Two other SQLite-native projects were inspected and rejected as a fork base before the build went raw — memorygraphMCP (bitemporal, but shallow and mutating) and Basic Memory (rejected as a foundation for reasons that have nothing to do with quality: its whole identity is live, two-way Markdown editing, which I don’t need — I browse the store and edit it indirectly through the approval gate). Basic Memory turned out to be the best reference implementation for the retrieval layer anyway, which is its own small irony covered in theme-graph-not-vector-dump.
Sources: earlier, unpublished notes memory-system-design-decisions (the full “eight decisions, four reversals” reflection); mem-local e8 (settled-architecture cluster), e9 (Stage-0 rulings A–O.1); register decisions B, C, D, G, I, T, U (chat:ai-memory-system-oversight-2026-07-20 register text; ruled in Decision needed on attached documentation, 29 Jun 2026, and Design corpus consistency review for memory system build, 19 Jun 2026); Memory system build stage 0 oversight, 20 Jun 2026.