Seven days of dogfooding our own memory layer

Five clients, two deterministic hooks, and one rule: if the loop is annoying enough to skip, the product does not work. Here is what had to be built before the clock could even start.

  • Build notes
  • Dogfooding

Why a dogfood needed building at all

The honest reason to dogfood is that the failure modes we care about only appear over days. A checkpoint that looks fine in a test looks different when it is the fourth one today and you are tired. Rehydration quality is invisible in a fixture and obvious when the slice you get back is missing the constraint you set on Monday.

So the target was not a demo. It was seven consecutive days of the real loop, against this repository, with the same published clients a customer installs.

Five clients, two of which are not JSON

Claude Code, Cursor, Claude Desktop, Codex, and Gemini CLI. Two are repo-scoped and committed; three are user-scoped, so their real config paths are written out in the docs rather than faked into the repository.

Codex takes TOML, not JSON. Pasting a JSON block into it is the single most likely setup failure, which is why it gets its own section in the client docs rather than a footnote. Client neutrality is a claim you have to pay for in documentation, not just in code.

The four non-Claude-Code clients get both operations through the MCP server’s instructions field, which names no vendor and is pinned by a test so it cannot drift.

Making the loop deterministic

Where the client allows it, the loop should not depend on remembering. Two hooks: SessionStart runs mneia brief and injects the slice, Stop runs mneia checkpoint.

Both shell out to the published CLI rather than reimplementing the wire protocol. That is the whole point of a dogfood - if we call an internal path, we are testing something a customer does not have.

The Stop hook is where the real engineering went, because a checkpoint that fires once per turn instead of once per task boundary is worse than no checkpoint at all. Four guards:

  • An active stop-hook flag returns immediately, so it cannot recurse into itself.
  • No session marker means the session never rehydrated, so it never checkpoints.
  • Fewer than six new transcript entries is a no-op - not every stop is a boundary.
  • The lock is claimed with an exclusive-create write, so a concurrent stop loses the race rather than doubling up. A lock older than ten minutes is taken over once, so a killed process cannot wedge the session.

The marker advances even after a failed attempt, because mneia checkpoint resumes from a server-side watermark. The marker is a debounce, never a record of what was captured - conflating those two is how you build something that silently drops sessions.

The smallest blocker was a .gitignore line

The project binding - which workspace and project this directory belongs to - is a property of the repository, not of one laptop. It is meant to be committed.

.gitignore swallowed the whole of .mneia/, so the one file every client already reads could not be versioned. Narrowing it to .mneia/* plus a negation for config.json was a two-character fix that had been quietly making the binding a per-machine setup step for everyone.

Credentials stay in ~/.mneia/credentials, outside the repository, and nothing committed references them. The binding file holds no secret - that separation is what makes committing it safe.

What it changed

The recurring lesson was that the interesting bugs were never in the ranking algorithm. They were in provenance - which session an item came from, and whether the answer could be trusted - and in the boundaries around the model call rather than the call itself.

The other lesson is older and keeps being true: the checkpoint is the surface where the confirmation happens, so its interaction quality has weight out of all proportion to its size. Confirm has to be one keypress. Edit must not require retyping the item. Prompt only for what genuinely needs a human. A clumsy prompt there does not merely annoy - people skip the review, and the record stops being worth trusting.