Skip to main content

Versioned state

Mesh should not treat agent memory, tool access, or persona configuration as loose markdown files on disk. Those files are fine as an export format, but they are the wrong source of truth for a multiplayer system that needs to be durable, auditable, recoverable, and perspective-isolated. See perspective.md for the RuntimeAgent perspective boundary, runtime/context-resolution.md for how that boundary applies to model context, and runtime/memory-retrieval.md for how eligible memory is selected and ranked.

Why not markdown as the canonical store

Markdown repos are convenient for a hacker workflow, but they have structural problems:
  • they are easy to mutate silently
  • they do not naturally preserve provenance
  • they do not give you a clean rollback story
  • they make branching and forking awkward
  • they spread important runtime state across files that are hard to query
  • they make it too easy to accidentally share state between agents that happen to use the same filesystem
That is acceptable for notes. It is not ideal for live agent state.

What Mesh should do instead

Mesh should store the canonical state in Postgres using append-only, versioned tables. The important pieces are:
  • self/profile state — persona, style, operating constraints, goals
  • tool grants — what capabilities an agent has been given
  • memory entries — durable remembered facts with provenance
  • prompt snapshots — the assembled material used for a turn
  • revisions — immutable history for every change
Every one of those belongs to a specific RuntimeAgent. Multi-agent hosting is operational multiplexing, not a shared state space. A human-readable markdown export can still exist, but only as a view over the structured state.

Perspective ownership

Memory belongs to the RuntimeAgent that learned it. If Lyra and Daedalus both interact with Victor, Lyra’s memory about Victor and Daedalus’s memory about Victor are separate state. The control plane may correlate the external identities for operational reasons, but that correlation does not make either agent’s memory eligible context for the other. The same rule applies to profile state, tool grants, and prompt snapshots: there is no deployment-global fallback state that an agent silently inherits because another RuntimeAgent already has it. A cross-connector identity link established in one perspective may organize that RuntimeAgent’s memory across Slack, Telegram, or GitHub. It does not bootstrap identity or memory in another perspective.

Provenance is part of memory

A durable memory entry must retain enough provenance for future context resolution to decide whether it is appropriate to reuse:
  • which RuntimeAgent learned it
  • which Actor or Actors it concerns inside that perspective
  • which Conversation and MessageEvents produced it
  • when it was learned
  • what audience could observe the source conversation
Perspective isolation answers “whose knowledge is this?” Audience provenance answers “where may that knowledge be reused?” Losing either makes later policy enforcement impossible.

Why append-only matters

Append-only state gives us:
  • auditability
  • rollback
  • forking
  • reproducibility
  • safe experimentation
  • the ability to inspect exactly what the runtime believed at a given turn
If an agent degrades over time, we can return to a known-good revision instead of trying to reconstruct intent from a pile of edited files.

Intended behavior

When a profile or tool grant changes:
  1. write a new revision owned by the RuntimeAgent
  2. preserve the prior version
  3. mark the new one as current for that RuntimeAgent
  4. assemble the runtime prompt from that agent’s current version plus context resolved entirely inside its perspective
That makes the live system predictable and the history inspectable.

Prompt snapshots

A prompt snapshot is the audit record of what one RuntimeAgent actually knew for one turn. It must never contain references sourced from another RuntimeAgent’s perspective merely because both are hosted by the same process. The snapshot should eventually record the agent id plus the source message, memory, and state revisions used to assemble it. See runtime/context-resolution.md.

Design principles

DB is canonical. Markdown is presentation. State is RuntimeAgent-owned. Co-residency is not shared cognition. Those are the right boundaries for a system that wants to be multiplayer from the start.

Current state

This document is the intent. What is actually built, as of 0019_run_ledger.sql:
  • persona — built (agent_persona_revisions): the operator-authored identity text rendered into every prompt. Append-only revisions with an explicit active flag (one per agent, enforced by a partial unique index), written by the admin API’s persona endpoints and edited on the agent’s profile page. Reverting activates an earlier revision; it never mints a copy, so history records what happened. The loader resolves the active revision into the serving agent and turn.Engine renders it — this one is behavior, not intent.
  • memory entries + revisions — built and live in both directions, keyed to runtime_agents(id), and actor-attributed as of 0018_actor_attributed_memory.sql. Operators author them through the admin API; the runtime loads the newest eligible live entries into each prompt and exposes four verbs (remember, recall, amend, forget). Each entry now distinguishes subject (subject_actor_id, who the claim is about), source (source_actor_id plus source_message_event_id, who supplied it and on what basis), and visibility (perspective or conversation, the hard eligibility filter). The two actor references are composite foreign keys into actors (runtime_agent_id, id), so cross-perspective attribution is rejected by the database rather than by convention. Both are nullable: an unresolved subject is a legitimate state, not a defect to paper over with a guess. memory_entry_revisions is an operation log, not merely a body history. Each row records its operation (create/amend/forget), the attributed state it produced, and who supplied the change. Every write — including forget, which soft-deletes and advances the revision counter — commits its entry and its revision row in one transaction, so a committed memory is never missing its history and a retired memory stays auditable. An agent-authored entry has source_type='agent', a NULL operator id, and provenance derived from the persisted inbound message’s graph record. Retrieval is lexical (PostgreSQL FTS) plus a current-subject boost and recency; the semantic and salience axes in runtime/memory-retrieval.md remain to be implemented.
  • prompt snapshots — built and live, keyed to runtime_agents(id). The turn engine records the assembled system prompt before provider execution and each durable model-call row references that snapshot, so the audit record and invocation cannot silently drift apart.
  • tool grants — not built. 0003 created tool_sets / tool_set_revisions speculatively; nothing ever read them and 0009 dropped them. They return designed against the tool contract, not resurrected — see the 0009 header for what a real grant schema has to carry.
  • profile state — there is no separate profile row. The profile is the runtime_agents row (display_name, the three model-role columns, settings) plus its persona revisions; 0009 dropped agent_profiles rather than keep a 1:1 surrogate that had to be kept in sync.
The first three bullets are the honest reason to read the migration headers rather than this file when asking “what can the system do today”: applied SQL is executable, and a design doc is not.