Local profile history¶
The optional native history feature in shared/profile-core stores immutable
account/settings operations and merges their causal history. It is a foundation
for continuous profiles. The optional Drive transport now verifies provider identity
and immutable files against this journal. A separate discovery catalog
now retains remote scan progress. Flutter discovery and initialized publication
and reviewed desktop/mobile enrollment are connected. Complete categories,
browser reconciliation and cross-client continuous sync remain unfinished. The
browser runs the same command contract through history::memory, a pure
in-memory journal exposed to WASM and parity tested against this SQLite journal;
see browser profiles.
Ownership and bounds¶
history::Worker owns one SQLite connection on a dedicated thread, with 32 pending
commands. Accepted writes finish even if their observer is cancelled. Close drains
accepted work; other worker clones must be dropped before it can finish. Never
open this synchronous journal from an iced handler or add a profile snapshot
connection to the mail cache. Input validation/encoding also belongs off the UI
thread.
The journal uses bundled SQLite 3.53.2, WAL/FULL, private new files/directories on
Unix and an exclusive companion lock retained until its connection closes. Resolve
symlinks before choosing the lock path; never remove a live lock file. This
checkpoint ports the dependency update from desktop db8c82a, not that commit's
entire mail-cache owning-worker migration.
Binding separates application namespace, verified provider principal, profile
UUID and generation. Its hash addresses local files; its input strings are not
authentication. Transport must verify identity and file ownership before importing anything;
the optional Drive implementation enforces both independently of this binding. A fresh journal generates its device UUID and retains it on
reopen. This is device-local state, not a portable database: installation/import
rebinding must prevent cloning that UUID or replaying queued uploads elsewhere.
Field/version observations contain at most 50 entries. Values/uploads return one
bounded operation. Each command applies at most 32 ready operations; callers
continue Drain while ready is nonzero. Missing-parent records stay durable and
block new local edits. Zero waiting records proves only that received ancestry
has applied; it does not establish a complete cloud listing. History compaction,
large-history performance and full storage bounds remain to verify. The existing
codec's 256-parent bound can require reconciliation before another local edit.
Edits and recovery¶
Generate one operation UUID and freeze the complete LocalEdit before submission.
After a lost reply, retry that same request. Reusing its UUID with different data
is rejected. Original remote bytes are retained exactly; a duplicate operation
with different bytes is rejected. Cycles and wrong bindings fail without partial
application. Unknown optional data remains in the original records; local field
replacements must preserve extensions from a reviewed version.
Capture expected_revision with or before the values shown for review. Do not
fetch a newer revision after displaying stale values and use it to overwrite
them. Per-target revisions allow unrelated changes, while changed reviewed
fields require a new review. Concurrent values remain explicit conflicts; a
resolution must identify every reviewed version. No wall-clock winner is chosen.
Account deletion hides its prior fields and prevents stale/concurrent edits from resurrecting it; re-adding uses a new account UUID. Profile deletion ends that generation. Concurrent duplicate tombstones retain their records without creating an unresolvable value conflict. Log insertion, causal dependencies, derived field versions and counters commit atomically.
Local operations keep exact upload bytes and SHA-256. Reserve a provider file ID
durably before sending; a retry cannot replace it. Confirm accepts only the
reserved identity/digest, but does not perform HTTP or prove Google committed it.
Transport must verify that exact owned immutable file before calling
it. Discovery checkpoints and acknowledged local application need separate
durable state; the journal's derived fields do not update mail accounts/settings.
ExportAcknowledgedRecord { expected_revision, after } returns one exact
original imported or confirmed-local operation, excluding unsent local records.
A partial covering index bounds this traversal. Import alone does not authenticate
an original: reconciliation additionally checks every returned record against the
completed provider inventory. Reopening an ongoing-sync owner restarts that
verification; normal cycles verify newly acknowledged records. Losing only the
catalog metadata cannot reuse proof from a surviving observation history.
Flutter integration and evidence¶
flutter/rust/src/profile_history.rs admits one active request/journal per native
workspace independently of mail/provider capacity. Switching bindings drains the
old owner first; stale close requests cannot close another binding. The production
bridge's owned request task retains admission through cancelled observations.
NativeProfileHistory uses background JSON encoding and decoding. Its future
controller must serialize dependent commands and fence profile/session changes.
Core tests cover independent stores and arrival order, conflicts/reviews, unknown data, missing parents/restart, tombstones, transactional failure, immutable IDs, upload reservations, queue cancellation and independent-process/symlink ownership. Native tests hold provider capacity while accessing history and check binding switch/reopen isolation. A shared actual Dart FFI scenario also runs on the isolated Android emulator. It copies synthetic records between two temporary stores with credentials locked; it is not a Google or Settings-control E2E test. See client testing and completion evidence.
There are no passwords or OAuth grants in this format. Local metadata SQLite is still unencrypted (R22). Credential protection, Google project configuration, live cross-client appData access, Apple execution and actual desktop/mobile/browser Settings flows remain open. Operational instructions live in AGENTS.md.