Project · Research Build · 2026
A SQLite-backed memory runtime that lets agents write raw notes privately, then promotes only validated, resolvable, canonical state into shared memory.
Multi-agent systems need some kind of shared state. The naive version is obvious: let every agent append whatever it thinks matters into a common store and hope the useful state rises above the noise. In practice, that fails in a very specific way. Bad memory is durable. A vague note becomes a tracked issue. A paraphrased lifecycle update closes the wrong row. A malformed result record survives long enough to mislead the next agent.
Governed Memory Layer is built around a stricter view: raw note text is not canonical state. A note is only a candidate write. Before it becomes durable shared memory, the system has to decide whether it is meaningful, what it refers to, whether the write is structurally safe, and how that write should project into the canonical tables everyone else reads.
The system is deliberately split into narrow stages. Agents can write notes into private working memory, but they cannot write canonical shared state directly. All durable memory changes flow through a governed promotion stack and then through a single persistence path.
The key architectural claim is not that the interpreter is always right. It is that shared state is never mutated on the basis of model text alone. The model can propose meaning. It cannot directly define canonical memory.
The most important interface in the system is the structured write request emitted after interpretation. From that point on, the pipeline becomes progressively more deterministic.
// Example: a note becomes a structured write request { "decision": "accept", "bucket": "issues", "target_id": "pandas_import_blocker", "operation": "resolve", "payload": {}, "reference_text": "the earlier blocker", "candidate_aliases": ["pandas error", "import blocker"], "confidence": 0.84, "rationale": "lifecycle update referring to an earlier open issue" }
The resolver then compares that request against current canonical context: open issues, active constraints, and active decisions.
If the reference clearly binds to one existing row, the write can commit. If it cannot safely identify the target, the system does not guess.
It preserves the write in pending_memory_events and retries later when fresh canonical state exists.
// Example: what gets durably recorded on commit { "event_id": "uuid", "bucket": "issues", "target_id": "pandas_import_blocker", "operation": "resolve", "payload_json": "{}", "raw_input": "The earlier blocker is fixed.", "source_ref": "{ matched_target_id, candidate_matches, resolution_reason }", "applied_successfully": 1 }
That ledger row is the durable source of truth. Canonical tables are downstream projections. This is why the system can recover from projection failures: the audit trail exists independently of whether the projector succeeded in that moment.
The runtime is not one giant memory object. It is a set of narrow modules, each responsible for one boundary in the note-to-memory path.
Agent-local scratch space for a single run. It stores timestamped agent and tool_result notes, tracks whether they have been processed, and intentionally does not enforce schema, identity, or canonical semantics.
The orchestrator. It pulls unpromoted notes, rebuilds fresh shared-memory context, runs interpretation, resolution, validation, and persistence, then retries pending writes after every successful canonical commit.
The resolver handles identity and lifecycle policy. The validator enforces deterministic structural rules such as known buckets, allowed operations, slug-like target IDs, and existence of lifecycle targets when needed.
The only official persistence entry point. It writes to the event ledger first, then asks the deterministic projector to update canonical tables, preserving the committed event even if projection later fails.
SharedMemoryWriter projects events into canonical tables with bucket-specific semantics. SharedMemory is the read API the rest of the system is supposed to consume, so agents read current belief from canonical state rather than raw note text.
Holds important writes that are not yet safe to commit. Instead of dropping ambiguous lifecycle updates or forcing a guess, the system preserves them as explicit pending work that can be retried later against fresher context.
The persistence layer is simple on purpose. SQLite is the backing store, connections run in WAL mode, and the database path can be overridden through AGENT_MEMORY_DB_PATH. The important design choice is not the database engine itself. It is the separation between ledger, pending queue, and canonical projections.
| Surface | Purpose | Why It Exists |
|---|---|---|
events_memory |
Append-only ledger of committed canonical writes | Durability and audit. Every committed canonical mutation is recorded here first, even if projection later fails. |
pending_memory_events |
Deferred writes that matter but are not yet safe to commit | Uncertainty handling. Ambiguous lifecycle updates become explicit pending state rather than silent corruption. |
shared_* tables |
Canonical state agents and tools are expected to read | Operational state. Current belief is stored as deterministic projections rather than free-form notes. |
Reference memory is embedded inside canonical issue, constraint, and decision rows through reference_memory_json. That stored alias and phrase history is what helps the resolver reconnect later notes like "that earlier blocker" back to the same canonical object instead of creating duplicates or closing the wrong row.
Each bucket has its own projection contract. Some behave like current state, some behave like append-only history, and some have lifecycle transitions that explicitly retire earlier rows.
| Bucket | Operations | Projection Rule |
|---|---|---|
plan |
upsert |
Single canonical row in practice, keyed by main; content is replaced and version increments. |
constraints |
upsert, invalidate |
Represents currently active restrictions; lifecycle moves rows between active and invalidated state. |
issues |
upsert, resolve |
Tracked problems and blockers; canonical rows stay open until a bound lifecycle write resolves them. |
decisions |
append, invalidate |
Choices accumulate over time; invalidation projects to superseded state rather than deleting history. |
results |
append |
Append-only measurements or outcome records; multiple rows may coexist for the same task or experiment. |
task_state |
upsert |
Latest state wins by task_id; optimized for current operational status rather than event history. |
learnings |
append |
Append-only reusable knowledge stored as active canonical rows. |
This mix is intentional. Plans and task state behave like current truth. Results and learnings behave like durable historical accumulation. Issues, constraints, and decisions sit in the middle, because they need lifecycle operations without losing identity over time.
Structural validation is comparatively easy. The difficult problem is preserving the identity of a thing across changing language. A system might capture an issue today as pandas_import_blocker, then receive tomorrow's note phrased only as "that earlier blocker." If the system binds too aggressively, it closes the wrong row. If it binds too weakly, it creates duplicates and leaves stale active state behind.
The resolver uses explicit target IDs, explicit snake_case slugs in note text, token overlap, interpreter-provided alias hints, and stored reference memory to score candidate matches. But when the match is still weak, the architecture chooses delay over false certainty.
When a lifecycle note cannot be bound confidently to an existing canonical row, the system does not force a guess. It records the unresolved write in the pending queue and retries later after canonical state changes. That turns ambiguity from a hidden failure mode into a visible, inspectable backlog.
The append-only ledger, reference memory, pending queue, and deterministic projector all exist to support that one choice. The system would be simpler without those parts, but it would also be far easier to silently corrupt shared state.
There are three operational paths worth understanding: normal commit, provisional deferral, and pending replay.
Working memory yields an unpromoted note. The pipeline interprets it, resolves identity, validates structure, writes an event into events_memory, projects canonical state through SharedMemoryWriter, then marks the event as successfully applied.
If a lifecycle write matters but cannot be safely attached to an existing canonical row, the pipeline does not reject it as noise and does not commit it as fact. It stores the serialized request in pending_memory_events so the uncertainty remains visible and recoverable.
After every successful canonical commit, the runtime runs a bounded retry loop over retryable pending items. A newly committed canonical row can make an older ambiguous note resolvable, which means pending work is not dead data. It is deferred resolution debt the system actively revisits.
The repository is intentionally concentrated on the note-to-memory boundary. It is a disciplined memory runtime, not a full production multi-agent platform.
The following are current scope limits, not accidental omissions: