Skip to main content

Observable state

Scope and identity

A query returns a bounded snapshot of account state. A Kotlin Flow observes that query; it is not an ordered event log or a promise to emit every database revision. This contract covers the authored Android facade over the Rust account runtime. The semantic type determines how the snapshot interprets its records.

Supported behavior

Committed snapshot

shared.observation.committed-snapshot — A newly collected account query must produce the current snapshot. After a committed mutation publishes a revision, an active observation reruns the complete query and can expose the updated result. Coalescing revisions may skip intermediate snapshots; it must not manufacture an uncommitted product state.

Publication occurs inside the native call's dispatched mutation boundary. A caller cancelled after the native commit must not prevent other collectors from learning that the state changed. Concurrent publishers retain the highest revision and cannot reopen a terminal observation signal.

Direct message lookup, message pages, search, context and conversation previews must use the owning message contract's effective-state rules. Several separate queries are not a single atomic multi-query snapshot while concurrent writes continue. Comparisons must use a stable point or the recorded revision.

messages.getMany(ids) / messages.observeMany(ids) resolve up to 512 unique message IDs in caller order, omit unknown IDs, and include deletion tombstones. Empty input returns an empty snapshot. Observations retain a copy of the input IDs and refresh their complete effective state, including older messages beyond the newest page. Duplicate and oversized inputs are rejected before the query. Apps split larger loaded windows into bounded groups; separate groups are separate snapshots and do not provide a shared revision barrier.

Observation closure

shared.observation.close — Closing an account terminates its observations. When close wins a pending query race, the observer must not report account closure as an ordinary query failure or emit a value from the closed owner. A new account lifecycle requires a new observation owner.

Observation callbacks may synchronously close their account or transport, including from completion handlers. Reentrant teardown must not destroy a native handle twice. Managed snapshots remain monotonic and terminal; a delayed snapshot from a stopped owner cannot overwrite a replacement transport's state.

Persistence semantics

The observation signal is process-local. SQLite state and revision are durable; collectors and their pending emissions are not. Reopening the account rebuilds observations from a fresh query rather than replaying an in-memory cache. Implementation details belong to the account-runtime architecture.

Observable behavior

Android renders complete returned values. It must not merge raw protocol tags, choose its own edit winner, or clear a durable draft solely because an observer was cancelled. UI selection, scroll position and transient loading/error state remain application-owned. Their full lifecycle contract is outside this first SDK contract set.

Verification and limits

The packed-AAR Android case checks initial and updated observations around message changes; a focused Kotlin case covers close winning a pending-query race. The message contract separately maps typed query and reopen checks. These cases are not an exhaustive scheduler/model check of every concurrent writer, collector cancellation or account switch.

A deterministic Kotlin dispatcher case also cancels the caller after its mutation commits but before the return continuation executes. Shutdown checks verify that the managed owner publishes its final native snapshot even when its observer never ran.

No execution report is bundled with this documentation. Linked test cases describe verification scope; their existence is not a passing result.

InvariantRequired environmentsExecution evidence
shared.observation.committed-snapshot
shared-boundary
android-device, android-jvmmissing
4 mapped test cases
  • com.softcose.softchat.MessageSdkContractTest#packedAarPublishesInitialAndCommittedMessageSnapshotsintegration; android, instrumentation:com.softcose.softchat.devicetest
  • com.softcose.softchat.sdk.AccountLifecyclePublicationTest#committedMutationPublishesWhenCallerIsCancelledBeforeDispatcherReturnfacade; sdk, gradle:library:test
  • com.softcose.softchat.sdk.AccountLifecyclePublicationTest#revisionObserverCanCloseAccountDuringCommittedMutationfacade; sdk, gradle:library:test
  • com.softcose.softchat.sdk.AccountRuntimeTest#exact message observation snapshots its ids and follows durable changesfacade; sdk, gradle:library:test
Shared requirements
shared.observation.close
shared-boundary
android-jvmmissing
8 mapped test cases
  • com.softcose.softchat.sdk.AccountRuntimeTest#account observations complete when close wins a pending query racefacade; sdk, gradle:library:test
  • com.softcose.softchat.sdk.AccountLifecyclePublicationTest#stoppedTransportPublishesTerminalSnapshotWithoutObserverExecutionfacade; sdk, gradle:library:test
  • com.softcose.softchat.sdk.AccountLifecyclePublicationTest#revisionObservationCompletionCanReenterAccountClosefacade; sdk, gradle:library:test
  • com.softcose.softchat.sdk.ManagedTransportPublicationTest#delayedSnapshotCannotReopenStoppedTransportfacade; sdk, gradle:library:test
  • com.softcose.softchat.sdk.ManagedTransportPublicationTest#delayedSnapshotCannotOverwriteReplacementTransportfacade; sdk, gradle:library:test
  • com.softcose.softchat.sdk.ManagedTransportPublicationTest#managedObserverCanSynchronouslyCloseTransportfacade; sdk, gradle:library:test
  • com.softcose.softchat.sdk.ManagedTransportPublicationTest#nativeCallRacingShutdownReturnsStoppedResultfacade; sdk, gradle:library:test
  • com.softcose.softchat.sdk.ManagedTransportCloseRaceTest#concurrentAccountCloseAndTerminalCollectorDoNotInvertStopLocksfacade; sdk, gradle:library:test