3.2 KiB
Activity Replication
github.com/tutti-os/tutti/packages/agent/activity-replication is the official
versioned JSON contract for uploading canonical agent activity snapshots to a
cloud projection.
The root package defines the wire batch, mutation, typed entity key, scopes,
five projection snapshots, validation, and acknowledgement/rejection
semantics. Canonical activity vocabulary comes from
store-sqlite/canonical; this module does not define a second set of turn or
interaction lifecycle values.
The conformance subpackage exposes backend-neutral fixtures through two
interfaces that match the direction of data flow:
ProjectionBuilderseeds canonical state and builds the official mutations; the Tutti SQLite adapter and tsh local builder runProjectionFixtures.Sinkapplies official mutations and reads the final projection; tsh-server MySQL runsSinkFixtures.
The sink fixtures verify that:
- an already committed mutation retried after its HTTP response was lost is accepted as a duplicate and returns its original cursor;
- a stale snapshot is an acknowledged no-op and does not block later ordered mutations;
- reusing a mutation ID for a different identity is a permanent rejection;
- schema rejection reports the failing mutation and transaction IDs.
The contract owns no database queries, room authorization, transport,
WebSocket behavior, or GUI-derived state. runtimeOperation,
runtimeOperationEvent, and submitClaim are retained only as entity names
for decoding tombstone deletes; new upserts are invalid.
Turn.identityAnchorTurnId is an optional, provider-neutral identity relation.
When present, the Turn inherits the ultimate external identity of another Turn
in the same canonical Session; absence means the Turn owns its identity. The
anchor is immutable, non-self-referential, and flattened to one hop by the
canonical store. It does not change lifecycle, cancellation, interaction,
provider-Turn binding, or message ownership. Cloud sinks must treat an omitted
field as “not asserted” and must not clear an anchor already stored by a newer
owner. This keeps old owners readable while limiting the new behavior to owners
that actually emit the relation.
The production module depends only on the nested, dependency-free
store-sqlite/canonical module. The SQLite conformance adapter lives in the
store module's tests, so importing this contract does not add the SQLite driver
or daemon to a consumer's module graph.
Consumers should validate an ordered batch with ValidateBatch, preserve the
original durable cursor for duplicate acknowledgements, count duplicate and
stale mutations in acceptedCount, and use SummarizeAcknowledgements to
produce the batch result. ProjectionEpoch is an explicit rebaseline fence:
when a cloud projection is destructively reset, its paired consumer release
must increment the epoch. Clients then treat acknowledgement rows from an
older epoch as untrusted and replay the canonical projection in dependency
order before relying on cloud-derived features. The cloud sink must be deployed
before clients that emit the new field; it rejects a different epoch rather
than applying a partially compatible projection. This is an intentional
fail-closed release fence, not a compatibility mode.