Profile metadata format¶
shared/profile-core validates individual account/settings operations for the planned Google profiles. Its optional native local history now persists and merges these records. Flutter now has initialized profile publication and reviewed account/preferences enrollment. Desktop discovery and reviewed publication are connected; desktop/browser application and continuous reconciliation remain unfinished. Follow the implementation handover for the full lifecycle and remaining decisions. Passwords and OAuth grants have no representation in this metadata codec.
The initial so.shep.profile-operation major version 1 carries a configured application namespace, profile/generation/device/operation UUIDs, causal parent operation IDs and an atomic list of changes. Minor versions and unknown optional fields survive decoding and editing. Unknown major versions, required capabilities, actions, settings or security enums require an update; callers must keep their existing setup. Decoding validates structure only: the eventual worker must verify Google ownership, namespace binding, immutable operation identity, complete ancestry and local revisions separately. No wall-clock winner is defined.
Changes distinguish a complete account connection from its display name. Connection metadata includes explicit incoming and SMTP security/authentication, separate SMTP username/password policy and Sent policy/folder. Account and profile deletion records are explicit; a record cannot both delete and edit the same account, or combine profile deletion with other changes. Re-adding an account requires a new shared identity. Duplicate targets, parents, JSON keys, nil IDs and self-parent references are rejected. Native history applies tombstones and retains concurrent field versions for explicit resolution; reviewed Flutter account application has its own enrollment contract; desktop/browser application remains open.
profile_setup is a sole-change preparing/completed marker requiring initialization-v1. The history layer verifies its causal setup barrier; see publication.
causal-v1 is required. Account operations additionally require accounts-v1; settings require settings-v1. Initial named settings cover appearance, reply display, the boolean reply_include_original new-reply default, image policy, unified Inbox, cross-account moves, conversation grouping, badge preference, 0–4 preview lines, left/right swipes, sender pictures and tooltips. The reply default advances R97's former device-local setting into the common contract. Explicit false is a value; absence is not an observed default or consent. Older eight-field mobile reviews, requests and receipts retain their exact fields and revisions through retry/restart. A fresh snapshot exposes the local default true at revision zero until there is explicit intent or a checked review. Browser profile mapping supports the key, but its composer control and application remain open. Shortcuts, contacts, image exceptions, notification details, backup policy, category/enrollment toggles and platform settings mappings remain to implement. Unknown connection extensions remain read-only in the local account review adapter rather than being discarded.
Each record is at most 1 MiB, has at most 64 changes and 256 causal parents, and portable JSON nesting is bounded. Encoding stops at the byte bound. These are individual record bounds; there is no entire-history collection or mail-backup ceiling in this API. Native history preserves original operation bytes and reserved upload identities; normalized JSON returned by this codec is not a cryptographic signature or proof of an acknowledged Drive upload.
shared/mail-core::profiles maps account fields explicitly for both Rust desktop and Flutter. Legacy SMTP security resolves to TLS on 465 and STARTTLS otherwise; an empty outgoing username resolves to the incoming username. An existing UUID identity cannot be replaced during export. A legacy local ID needs a durably reviewed shared ID supplied by enrollment. Import produces a review candidate only: it cannot write SQLite, create credentials or send mail. Keep the complete operation alongside any eventual local mapping so optional fields are preserved. The extension guard rejects known device/secret field names, but is not a general secret detector; never serialize a whole local preferences object.
The native request validate_profile_operation runs with its own capacity-one background permit and returns encoded metadata without applying it. The WASM entry has the same validation contract, and ProfileHistory exposes the in-memory history journal to the browser worker. shared/profile-operation.json and the 28 cases in shared/profile-cases.json are fictional interoperability fixtures consumed by Rust, the native request/actual Dart FFI tests and the standalone WASM test. The WASM test runs in Node; browser Settings integration is described in browser profiles.
Use node scripts/clients/test_profile_codec.mjs with the pinned WASM tools described in client testing. Generated glue/binaries stay under ignored artifacts. Run cargo test -p shep-profile-core, the shared account mapping tests and Flutter native/FFI tests after codec changes. The disabled quality workflow records the same WASM gate; shared release stamping includes the new crate.