Writes and deduplication
A perspective-bound graph store resolves the connector, conversation, external identity, and actor; inserts the message event; and records participation. The transactional store commits those related writes together. Foreign-perspective identifiers cannot be used to insert a message into another agent’s graph. Message bodies are immutable observations. Identity and conversation rows can be upserted as their metadata changes. A duplicate(conversation_id, external_message_id) returns the existing message without
replacing its body or moving its participation timestamp forward.
This is a logical append-only conversation contract, not a claim that every
database row is immutable. Configuration, leases, queue state, retention, and
current-state projections have their own update and cleanup rules.
Intake, execution, and delivery
Slack, Telegram, and phone connectors persist accepted intake before acknowledging it. Graph capture then precedes model execution. Durable intake describes the queue and the connectors that still use an in-process handoff. Runs record model calls, tool outcomes, budget usage, and ownership. Their checkpoints support restart recovery without repeating completed rounds. Unknown external effects are not automatically retried. The actual outbound message ID is learned from the connector send, so the canonical outbound event is recorded afterward. A durable reply boundary precedes the send. A crash in this interval can leave delivery uncertain; persistence does not make a remote send exactly once.Context reads
Every graph and memory read is scoped to the owning agent. Audience and lineage checks further restrict what can enter a turn. Search indexes are derived data; they cannot grant access to a row or replace canonical text. See the data model, context resolution, and memory privacy.Authoring a migration
Create a forward migration with a unique UTC timestamp prefix:Query generation and validation
internal/store is generated from db/queries and migrations. Edit the SQL, then
regenerate rather than modifying generated Go:
[]byte can encode a bytea literal instead of JSON.