Skip to content

Tarinoi Consistency Model

Version 0.0.1 — 2026-05-21

Tarinoi is an eventually consistent distributed system supporting both real-time online collaboration and asynchronous offline work as first-class citizens. This document describes the data lifecycle, reconciliation model, integrity guarantees, failure modes, and semantic integrity model. It is intended for technical Tarinoi users.

Data lifecycle

All Tarinoi data originates on clients. When a client wants to mutate a resource, it stores a snapshot of the shared base along with the mutated resource in its client-side database. If the client is online, this triggers a near-immediate sync cycle. If it is offline, sync is deferred until it returns online.

When the server receives data, it emits notifications to clients, which also triggers sync cycles on them.

Sync cycle and reconciliation model

A sync cycle is pull → reconcile → push. Reconciliation is through a three-way rebase on a shared base snapshotted by the client prior to mutating a resource. Conflict policy is server wins.

Reasoning: A server-wins policy produces rare edge cases where an offline client loses concurrent edits to the same fields as an online user. These edge cases are avoidable through coordination and have a narrow blast radius. The alternative — merge-based conflict resolution — introduces structural problems (stale references, invalid scripting constructs) in the more common case where concurrent edits affect semantically related but non-overlapping fields. Server-wins handles that case cleanly.

Data layers

Data is layered: changes first appear in a buffer layer from which they are committed to a project layer. This commit triggers a flush to a filesystem Git repository, and if the customer has linked it to a remote, a push to the remote. Each of these phases has distinct history, persistence, and integrity guarantees.

Consistency model

The source of truth is the server-side ACID database that clients sync against. Clients are inconsistent with the server and each other between sync cycles; offline use may result in significant drift between a client and the server.

The data unit Tarinoi operates on is the document. With each sync cycle, the system converges to consistency at the document level. The full set of consistency guarantees is in the Guarantees table below.

Active documents

A document has an is_active flag derived by the database from three independent flags:

FlagEffect on clients
is_tombstoneHard-delete the document
is_archivedHide the document
is_movedHide the document; coordinates move atomicity (see below)

If any of these flags is set, is_active is false. The guarantee "only one active document per project, per layer" means no two documents with the same document_id in the same project and layer may simultaneously have is_active = true.

Contention resolution

If concurrent sync cycles conflict — two clients push changes that intersect — latest push to server wins. The blast radius is bounded to a few seconds of concurrent edits, and contention of this kind only occurs in intensive real-time sessions.

Commit resolution

Commit from the buffer layer to the project layer occurs in a single ACID transaction.

Move atomicity

The primary key for a document row is (layer_id, collection_id, document_id). A move of document d1 from collection c1 to c2 is executed in a single database transaction that writes two rows:

  1. Destination record: document_id: d1, collection_id: c2, moved_from: c1, payload: <payload>
  2. Signpost record: document_id: d1, collection_id: c1, is_moved: true, moved_to: c2, payload: null

The signpost record has is_active = false (because is_moved = true). Its purpose is to allow clients performing optimistic sync to recognize that a document they previously held in c1 has moved to c2, rather than treating it as a deletion.

When the change is flushed from the buffer layer to the project layer, the system verifies that both the destination record and the signpost record are included in the same flush transaction.

Mutation history

Tarinoi maintains a project's commit history in its associated Git repository. No other mutation history guarantees are offered.

Reasoning: Offline-first architecture permits clients to diverge from the server for an arbitrary amount of time. Reconciling mutation logs across that divergence would be fragile and hard to reason about. Reconciling state through rebase against a shared base is simpler and more robust. Users should commit frequently so that meaningful mutations are captured in the Git log.

Guarantees

Tarinoi implements the following integrity guarantees on project data:

GuaranteeDescriptionEnforcement mechanism
Active document identityOnly one active document with a given ID exists in a project layerDatabase constraint
Commit/revert atomicityPartial commits or reverts between the project layer and buffer layer will never occurSingle database transaction
Move atomicityA moved document always has both a destination record in the target collection and a signpost record in the source collectionSingle database transaction; database constraint on active document identity
Bulk operation atomicityBulk changes are accumulated in the staging layer and applied to the buffer layer atomicallyFlush from staging layer to buffer layer in a single database transaction

Integrity Enforcement Mechanisms

MechanismDescription
Cryptographic consistency validationBefore commit, client and server collection state is validated by comparing a rolling hash. If the hashes match, the commit proceeds. If they do not match, Tarinoi retries with randomized backoff (jitter). If hashes still differ after retrying, the system checks whether the server cursor has advanced: if it has, there is active contention — retries continue with progressively longer backoffs, then fail gracefully; if the cursor has not advanced, the client state is irreconcilable with the server, indicating a corrupt client. Tarinoi transitions to error correction mode.
Error correction modeA recovery UI presented when the client cannot be reconciled with the server (cursor not advancing, hashes still mismatched). Offers: resync a specific collection; download a snapshot of the local client database; or, as a last resort, wipe the local client database and redownload from the server. Under normal operation, users should never encounter this.

Failure modes and recovery mechanisms

Failure modeBlast radiusRecovery options
Network failure while push/pull/flush/batch operation is in flightNoneRetry when server available
Server failure while push/pull/flush/batch operation is in flightNoneRetry when server available
Client database corruption, server rejects data, client irreconcilable with serverUn-pushed changes on clientDelete local client DB, redownload from server. Manual recovery from downloaded copy of client DB may be possible
Server database corruptionUncommitted changes, changes since last PITR backupPITR on database, recover from filesystem or Git remote
Server filesystem corruption (no remote repo)Commit historyRe-flush from DB to filesystem, recreate and reconnect remote repository
Server filesystem corruption (remote repo connected)Commit history since last push to remote (usually none)Clone from remote, flush to filesystem from last cursor

Semantic Integrity Design

Tarinoi is designed to converge towards semantic integrity. These constraints are not mechanically enforced, but they are reflected in control points and facilities afforded in the UI.

  • Tarinoi uses semantically meaningful identifiers for cross-document references. These include entity references, function references, variable references, list references, and template references. These references should be treated as foreign keys, and their internal consistency is observed but not guaranteed. See the Glossary for definitions.
  • Cards and entities are templated. Tarinoi does not guarantee consistency between a template and the templated document if a user changes a template to make it incompatible with existing data.
  • Functional dialogues have meaningful structural characteristics. Certain sets of nodes represent player choices; others represent alternatives dependent on game state. Certain structures cannot be unambiguously interpreted as dialogues. Tarinoi does not guarantee or enforce semantic structural consistency for dialogues.
AspectMechanism
Variable, function, entity, and list reference integrityOn-demand and automatic consistency scans with correction tools, with opt-in or opt-out UIs. Users may choose not to apply them, intentionally leaving the project in a semantically inconsistent state.
Template/templated document consistency; orphaned dataOn-demand and automatic consistency scans with automatic cleanup tools. Users may choose not to apply them, intentionally leaving the project with orphaned data.
Semantic dialogue integrityAutomatically surface potential problems in playback; on-demand consistency scans surfacing problems.

Glossary

Declarations

Tarinoi's scripting layer supports four types of named declarations that can be referenced across a project:

DeclarationDescription
VariableA named value in the project's scripting scope
FunctionA callable scripting unit
EntityA game entity (e.g., a character or interactive object)
ListA named ordered collection of values

Declarations are referenced by human-set identifiers, not stable internal keys. When a variable, function, entity, or list is created, the author assigns it an identifier (e.g., player_health, on_enter_room). All cross-references within the project use that identifier directly.

This is a deliberate design decision. Tarinoi's authored data is consumed by the customer's game implementation, which must reference these declarations by name. Using stable internal keys would require every game implementation to maintain a key-to-name mapping, adding indirection to every customer's codebase. Human-set identifiers keep the scripting layer directly readable, portable to game engine code, and inspectable without tooling.

The trade-off is that renaming a declaration requires both a change in Tarinoi and a corresponding change in the game implementation. Tarinoi's consistency scan tools detect stale references within the project, but cannot detect references in external codebases.