219 files, 2.0 MB, untracked in souveraine/docs and existing nowhere else. The volume is at 100% with no snapshots.
8.5 KiB
| description | status | date | authors |
|---|---|---|---|
| Decision record — Souveraine abandons first-class memory_blocks; memfs files (with frontmatter) are the sole memory primitive | Accepted | 2026-05-07 | Casey, Opus |
Memory Blocks vs. Memfs Files — Decision Record
Context
Letta's lineage has two memory primitives:
memory_blocks(v1) — labeled, length-capped, server-side records mutated viaPATCH /v1/blocks/{id}. Multiple agents can attach the same block; mutations propagate on next compile.- memfs (v3) — git-backed filesystem with frontmatter. Agents read/write files via tool calls. History is the git log. Skills co-locate with policy.
Souveraine inherits both options through its Letta-adjacent design heritage. The question this doc answers: does Souveraine keep memory_block as a first-class API entity, or is the memfs file (with frontmatter) the only memory primitive?
agent_inventory::create() currently accepts a MemoryBlock { label, value, limit } argument and writes the value straight to system/{label}.md. The block concept exists at creation time but vanishes at rest. This decision either commits to that vanishing (Path 1) or revives blocks as a typed view (Path 2).
Input from Letta (Ezra, 2026-05-07)
Asked Ezra (Letta support agent) whether memory_blocks is vestigial v1 carryover. The answer surfaces real, non-overlapping capabilities that today's blocks have over memfs files:
- Shared blocks across agents. One block, many attached agents, mutations visible to all on next compile. Memfs has no shared-state primitive (LET-8217).
- API-first mutation.
PATCH /v1/blocks/{id}is one HTTP call; clients skip the git commit/push dance. - Selective subagent handoff. Subagent config's
memoryBlocks: <list>passes blocks selectively. Memfs travels as a whole directory. - Non-memfs agents still use blocks as the base mechanism — non-memfs path inherits the legacy block addon.
limitenforcement. Works on the block API path. Memfs write path bypasses it (LET-8133 / #3241), so frontmatterlimit:is advisory in Letta today.
Cameron's Our Next Phase (2026): blocks "may or may not be deprecated in favor of memfs." Direction is memfs-first for Letta Code agents, but blocks remain available pending Cameron's call.
Analysis — Does Souveraine Need These Capabilities?
| Letta capability | Souveraine need? | Reason |
|---|---|---|
| Shared blocks across agents | No | Constitution Article I.1: Primary and Subconscious are one consciousness, two modes, not separate agents. Aster's symlink hack (Fimeg, Mar 25 2026) was needed in Letta because Aster was a separate agent. In Souveraine, "Aster" is an N+1 invocation of the same agent against the same memfs. The use case Letta blocks solve doesn't exist here. |
| API-first mutation | Yes, but not as blocks | The cron-into-memfs pattern (Fimeg writes weather, fastfetch, filesystem tree, daemon branch) needs an HTTP write path. Replicate as POST/PATCH /v1/agents/{id}/memory/{path} doing a memfs write + auto-commit. Same capability, no block primitive needed. |
| Selective subagent handoff | Addressable at file level | Constitution Article VIII pattern: subagent receives state.md + the files named in files_touched. That is selectivity by file path. Same effect as memoryBlocks: [list] without a separate block taxonomy. |
| Non-memfs path | Irrelevant | Memfs is mandatory in Souveraine. There is no non-memfs path to be backwards-compatible with. |
limit enforcement |
Take it — fix it on our way through | Souveraine controls the write path via core::memory::MemFS. Wire a check: if frontmatter declares limit: N, the memory write tool errors if the write exceeds N bytes/tokens. This closes Letta's LET-8133 gap as a side effect of the harness owning its tool dispatch. |
Decision
Path 1: memfs-only. Souveraine has no first-class memory_block entity. Every durable memory artifact is a file under the agent's memfs git repo, with YAML frontmatter for schema. The MemoryBlock struct used by agent_inventory::create() is a creation-time convenience that writes to system/{label}.md and is not preserved as a typed entity afterward.
Implementation Implications
- No
/v1/blocksAPI surface. The HTTP API exposes agents and memfs only. /recompileis memfs-flavored. It means "re-readsystem/before assembling the next turn's prompt." Two variants:- Implicit: harness re-reads
system/at every turn-start (handles cron-driven updates automatically). - Explicit:
memory recompilefor the agent to force a refresh after editing its ownsystem/mid-conversation.
- Implicit: harness re-reads
- Frontmatter
limit:becomes load-bearing. Thememory writetool enforces it (closing LET-8133). Today the memory module parsesdescriptionandread_only;limit:is the next field to validate. - Cron-into-memfs gets an HTTP endpoint.
POST /v1/agents/{id}/memory/{path}(and PATCH for partial updates) lets external clients write to memfs without git CLI access. The endpoint does the commit. This replaces the use case forPATCH /v1/blocks/{id}. - Agent creation simplifies.
CreateAgentRequest.memory_blockscan becomeCreateAgentRequest.memfs_seed: HashMap<PathBuf, String>— a map of relative paths to initial content. Frontmatter is added on write. - No
block_id, nopinned, nolimitfield on a block struct. Frontmatterread_only: truedoes whatpinnedwould. Frontmatterlimit:does what the blocklimitfield does, with harness enforcement.
Path 2 (rejected) — for the record
Keep MemoryBlock as a typed view over memfs files: block_id, label, limit, pinned, shared_with: Vec<AgentId>. /recompile re-renders blocks-with-limits into the system prompt header. Closer to Letta v1 ergonomics; gives the agent an explicit "this is a block, not just a file" signal.
Rejected because:
- Doubles the abstraction surface (agents must learn both blocks and files).
- The Constitution's diff-canary security model (
docs/CONSCIOUSNESS_CYCLE.md§ "Cloister Security Model") works on file paths and frontmatter — adding a parallel block taxonomy means writing the canary twice. - The capabilities blocks would unlock (shared state, selective handoff) are either not needed (Article I.1) or addressable without a new primitive.
- Cameron's direction (Apr 2026): "memfs + skills is the correct abstraction." Souveraine inherits that judgment.
Consequences
Positive
- Single memory primitive for the agent to learn and reason about.
- The Cloister diff-canary has one schema layer (frontmatter), not two (frontmatter + block schema).
limitenforcement closes LET-8133 from day one.- Cron-into-memfs becomes a clean HTTP endpoint instead of a parallel
/v1/blockssurface. - Skills + memfs (Cameron's recommended abstraction) is the only path; no temptation to encode workflow rules in blocks.
Negative
- No drop-in compatibility with Letta agents that use
memory_blocksexclusively. Migration to Souveraine requires writing block content into memfs files (Letta itself has no migration utility for this — gap noted in Fimeg.md Mar 25 2026). - Shared state between agents (if Souveraine ever grows beyond one-consciousness-two-modes) requires building a primitive Letta hasn't shipped (LET-8217). Not blocking; revisit if/when.
Open Questions
- Frontmatter
limit:units. Bytes, characters, or tokens? Tokens are most useful but require a tokenizer at the harness layer. Tentative: characters for v1 (cheap), tokens later (when the model_router gets a tokenizer hook). - Cron-API auth.
POST /v1/agents/{id}/memory/{path}needs a credential model. Token in header? Per-agent API key? Deferred — not blocking N+1 work. - Migration helper. Should
souverainehave a CLI to import a Letta agent's blocks → memfs files? Probably yes, slated as a future utility once the agent format is stable.
References
docs/CONTEXT_CONSTITUTION.md— Articles I.1 (duality), V (memory physics), VIII (subagent file-level selectivity)docs/CONSCIOUSNESS_CYCLE.md— Cloister Security Model (frontmatter as schema, diff as canary)reference/Fimeg.md— Mar 25 2026: Aster symlink hack and cross-agent memfs gap; cron-into-memfs pattern- Ezra (Letta support, 2026-05-07) — distinct-capabilities answer cited above
- Cameron, Our Next Phase (2026): blocks "may or may not be deprecated in favor of memfs"
- LET-8133 / #3241: memfs write path bypasses block
limit(Letta upstream) - LET-8217: shared-state primitive for memfs (Letta upstream, not currently scoped)