1# Coordinating cloud edits through separate files
2
3**Draft specification and investigation, 2026-10-11.**
4[#120](https://shale.paperclover.net/snowbound/issues/120). This is separate from
5shipping Android. Nothing in this document implements or enables a new storage
6mode. Existing local/native and direct SMB publication retain their existing
7OneNote-compatible exclusion. The wire examples and ownership policies below
8are proposals for review, not frozen public APIs.
9
10Clover's direction is a notebook's `.snowbound/` directory carrying coordination
11between Snowbound clients, with one owner process publishing the real `.one`
12files. Other builds exchange separate files instead of racing on those files.
13Generic cloud mounts cannot be assumed to implement reliable distributed locks.
14This should cover Android/Linux provider-backed notebooks and improve iCloud
15coordination. The selected direction is automatic takeover with retained branches
16and canonical repair. On 2026-10-11 Clover chose “whichever is the more resilient
17even if it's more complicated.” Resilience means keeping recoverable history and allowing an
18isolated capable device to progress; it does not mean promising exclusive cloud
19writes. The selection below is a design contract, not an implemented protocol.
20
21## Selected contract
22
23```text
24Android / Linux / iOS editor
25 │ existing edits, locally durable SQLite queue
26 ▼
27.snowbound/ unique immutable request segments + blobs
28 │ validated complete bytes and causal dependencies
29 ▼
30branch owner existing merge and revision append
31 ├── immutable decision/checkpoint evidence ──► other Snowbound replicas
32 └── ordinary .one / .onetoc2 files ─────────► eventual provider projection
33```
34
35A single application publisher reduces ordinary write/write contention. It
36cannot prevent a provider from delivering an old queued upload after ownership
37has moved. A successful local flush, a successful handoff, and a unique current
38logical owner are three different facts. The design must retain enough immutable
39evidence to repair the mutable file after a rollback even if every client has
40already received a historical publication receipt.
41
42Required safety properties:
43
44- An acknowledged local edit remains in retained local storage until its content
45 and outcome are recoverable elsewhere under an explicit retention contract.
46- Seeing a request, seeing a receipt, or seeing a matching file stamp does not
47 authorize deletion of the only recoverable request/base/blob.
48- Missing, stale, incomplete, repeated, or reordered provider observations never
49 silently delete notes, grant exclusive ownership, retire a branch, or
50 establish a complete list of devices.
51- Every accepted request has a stable identity, complete validated payload,
52 known causal base, and a recorded disposition. Retried transport does not
53 repeat the user edit or create duplicate conflict pages.
54- Normal edits use existing ops and append normal revisions. A projection is
55 never rebuilt by diffing or recreating a page model.
56- A dependency that has not arrived means wait. Concurrent incompatible content
57 means the existing merge produces conflict pages or a preserved recovery
58 branch. A missing dependency is not a content conflict.
59- Locked protected content, unavailable providers, and absent owners retain
60 encrypted pending edits. No network condition turns a protected edit into a
61 cleartext file.
62- Guarantees identify their observation point: local durability, owner custody,
63 file publication, and provider/cloud visibility are distinct.
64
65These requirements depend on at least one retained complete copy of each
66necessary object eventually being delivered. No file protocol preserves data
67when every durable copy is destroyed, or proves delivery when the provider
68permanently withholds it. Eventual projection repair additionally requires that
69old uploads eventually stop, required objects are eventually delivered, a
70capable reconciler keeps running, and necessary keys/versions remain available.
71An unbounded stream of stale overwrites can keep the canonical path regressing
72forever. Retained history preserves recoverability; it does not make that path
73monotonic under an adversarial delivery schedule.
74
75## Storage model and observations
76
77Assume independent client views of one provider-backed directory tree. Reads and
78listings may be stale; files may become visible before their bytes are complete;
79notifications and uploads may reorder; a complete old `.one` image may replace a
80newer one. A provider may omit known children, return unknown metadata, rename a
81file while changing its opaque handle, or reject a nominally supported operation.
82Clients can remain offline indefinitely. Process clocks are unrelated. A process
83can stop at any instruction and later resume with old state.
84
85There is no assumed atomic directory rename, distributed exclusive create,
86compare-and-swap, reliable distributed advisory lock, complete listing, or
87multi-file transaction. Independent objects with unguessably unique identities
88reduce collisions; they do not turn a provider's filename API into exclusive
89creation. Object integrity is checked after reading complete bytes. Local
90app-private SQLite ownership is a different boundary and can still serialize
91processes sharing that same local replica.
92
93Android is one transport, not the definition of the protocol. SAF `rw` opens
94provide seeking when supported; that says nothing about remote exclusion or
95upload ordering. Provider handles/IDs remain opaque, and returned rename handles
96must be retained. iCloud coordination/versions remain useful host capabilities,
97but they do not establish distributed fencing for the generic mode.
98[Android ContentResolver](https://developer.android.com/reference/android/content/ContentResolver),
99[DocumentsContract](https://developer.android.com/reference/android/provider/DocumentsContract).
100
101A provider that cannot retain separately named byte objects, enumerate them
102again, or represent the selected notebook subtree cannot carry this transport
103without another adapter. Unsupported capabilities must be observed in disposable
104provider fixtures; a provider name is not evidence of support.
105
106## Ownership choices and their actual guarantees
107
108| Candidate | Progress while original owner is absent | Logical ownership | Physical `.one` protection |
109| --- | --- | --- | --- |
110| Fixed designated publisher | Other clients edit/queue; publication waits | One configured owner under non-duplicated local state | No cross-provider upload fencing; late old uploads still require repair |
111| Explicit handoff | New owner proceeds after validated handoff dependencies | Old process durably stops admitting/publishing; successor receives state | Provider may still deliver old process's earlier uploads |
112| Timeout/heartbeat takeover | Available to isolated clients | Stale views can authorize two writers | Unsafe as a strict single-writer rule |
113| Fixed-member majority consensus | Majority can proceed; minority queues | Possible with durable acceptor promises and valid membership changes | Consensus does not make a generic provider reject stale uploads |
114| Branch-preserving recovery takeover | Isolated clients can preserve new branches | Cannot promise one global writer/order without additional agreement | Mutable file remains an eventually repairable projection |
115| Fenced server/provider | Depends on service availability | Service validates owner epoch for each accepted write | Can reject stale owner writes when checks cover the actual storage destination |
116
117No recommendation here may describe a wall-clock lease as sufficient fencing.
118Heartbeat objects are discovery/liveness hints only. A process that sees no other
119owner cannot distinguish an absent owner from a partition or stale listing.
120
121Majority agreement is a real alternative, not impossible consensus: fixed
122acceptors can persist promises, choose one logical history, and recover after
123restart. Its availability cost matters for a phone and one laptop. With two
124members, a majority requires both. Membership changes and acceptor-state loss
125need a complete protocol; a directory full of claims or votes is not one.
126[Paxos Made Simple](https://www.microsoft.com/en-us/research/uploads/prod/2016/12/paxos-simple-Copy.pdf).
127
128Fencing must be checked by the resource receiving the write. A local owner epoch
129inside `.snowbound` cannot force a generic mount to reject a queued older `.one`
130upload; the provider does not interpret that epoch. Chubby's sequencers address
131precisely delayed operations by requiring validation at the recipient.
132[The Chubby lock service](https://research.google.com/archive/chubby-osdi06.pdf).
133
134**Selected policy: branch-preserving automatic takeover.** Prefer one discovered
135publisher during ordinary connected operation. An isolated capable client may
136start a fresh recovery branch from its retained validated base, without waiting
137for the preferred device or pretending to revoke it. A delay/heartbeat can decide
138when to attempt recovery, but cannot authorize deletion, invalidate another
139branch, or establish exclusive ownership. Explicit handoff remains the efficient
140connected path; a fixed publisher is not a prerequisite for progress. Do not add
141majority membership to the first version: its loss of offline availability buys
142logical serialization, while leaving the demonstrated physical rollback problem.
143
144| Resilience dimension | Selected guarantee / cost |
145| --- | --- |
146| Local edit custody | Existing durable queue plus retained exact dependencies; takeover never discards an absent device's work |
147| Isolated-device progress | A capable unlocked device can retain decisions and attempt publication from its complete local closure; missing ancestry/keys still block interpretation |
148| Accepted-history recovery | All valid accepted branches and pending requests survive reconciliation, including losing projection versions |
149| Canonical availability | Ordinary `.one` remains readable when valid; stale uploads can regress it, and repair can be delayed |
150| Eventual convergence | Requires complete eventual delivery, stale writes to cease, compatible keys/versions and a running reconciler; reproducible selection rules must pass the gates below |
151| Physical exclusion | Not available on this storage model; no timeout, handoff receipt or sidecar epoch supplies it |
152| Storage/backup cost | Sidecar recovery history is essential for registered notebooks; initial version has no automatic GC |
153
154“One owner writes the real file” becomes **one logical publisher per branch**,
155with one locally serialized process owning each live branch incarnation. Peers
156send requests to it while it is reachable. A takeover opens a separate branch;
157it does not seize the old branch's namespace or credentials. During partitions
158there can be several physical publishers, plus provider-delayed uploads from
159retired ones. On rejoin, discovered publishers stop adding independent work,
160retain their unfinished attempts and coordinate through a reconciler. Its
161preference/order is a contention reduction rule, not a global lock. No generic
162file-only protocol can guarantee both isolated takeover and exclusive physical
163writes under these assumptions.
164
165Accepted branch outcomes are never revoked because another branch was preferred.
166They may become conflict/recovery alternatives in the integrated result. A
167publication receipt names its branch and covered input identities; it cannot
168claim a globally final order or that every offline branch has been seen. The
169registered notebook's recovery authority is the validated retained history,
170including pending closures, rather than whichever `.one` snapshot arrived last.
171Bare-file imports and intentional restores remain explicit operations below.
172
173## Identities and duplicate processes
174
175Notebook identity must be independent of mount path, provider document ID,
176display name, host registration, and filename case normalization. Existing
177OneNote file/root identities remain the content identities. A new coordination
178notebook identity distinguishes separate imported copies and ownership histories;
179how deliberate notebook copies inherit or reset this identity remains a
180migration decision, not an inference from their path.
181
182Use distinct roles:
183
184| Identity | Lifetime / purpose |
185| --- | --- |
186| Local installation/device identity | Retained private registration and diagnosis; not sufficient owner authorization |
187| Process/writer incarnation | Fresh random identity for each process run; disjoint message namespace |
188| Local edit identity | Existing durable replica edit ID plus its originating replica/writer identity |
189| Request segment identity | Stable once sealed, retries resend identical bytes |
190| Owner epoch / branch identity | Unique branch incarnation plus exact parent frontier; no globally increasing epoch or revocation implied |
191| Decision identity | Immutable owner result naming its predecessor and accepted request identities |
192| Blob/checkpoint identity | Validated immutable bytes, content digest and exact byte length |
193
194Previously sealed/outstanding records keep their original request, edit and
195segment identities after every restart. A fresh process incarnation names only
196new records. Retrying an old outbox may write its original namespace/bytes; it
197does not rekey old edits into the new process namespace. Otherwise deduplication
198would turn an ordinary restart into a second user action.
199
200Two processes on one installation must not publish the same live branch because
201they share a device ID. A locally owned publisher database/socket serializes
202processes sharing reliable private storage. Every process run uses its own new
203branch incarnation for newly admitted decisions, referencing retained predecessor
204history and unresolved attempts. Recovery can confirm an old prepared attempt
205with its original identities; it cannot generate a second outcome for it merely
206because the process restarted.
207
208Copied private profiles on separate devices therefore create distinct new
209branches, not competing continuations of one immutable namespace. Already sealed
210outbox objects retain their original identity and bytes. Two different decisions
211claiming the same immutable identity or contradictory dispositions for one exact
212prepared transaction remain a protocol contradiction: quarantine/preserve the
213evidence and stop automatic interpretation of the affected history. Valid
214independent branch claims are expected recovery inputs. A handoff records the
215old branch's retirement before its successor is eligible; stale cached retirement
216cannot be assumed visible elsewhere.
217
218## Proposed object namespace
219
220Names illustrate roles; this is not a frozen layout or extension:
221
222```text
223Notebook/
224 Notes.one
225 Notebook.onetoc2
226 .snowbound/
227 protocol-<notebook-id>-<object-id>
228 writers/<incarnation>/segments/<bucket>/<segment-id>
229 owners/<epoch-id>/decisions/<bucket>/<decision-id>
230 handoffs/<handoff-id>
231 blobs/<digest-prefix>/<blob-id>
232 checkpoints/<checkpoint-id>
233 recovery/<recovery-id>
234```
235
236Every published object is immutable. A transport retry keeps its identity and
237bytes. Correcting an object creates a distinct object referencing what it
238supersedes. Mutable convenience indexes or heartbeats may accelerate discovery
239but cannot authorize publication, retirement, or data loss. No implementation
240may overwrite one shared `owner.json` or `inbox.json` as the coordination truth.
241
242Per-process directories avoid ordinary client/client name collisions. Unique
243logical object IDs also appear inside the envelope. Two provider documents with
244one display name are not automatically one object; the same object under two
245names/handles is one logical object only after matching its validated identity
246and bytes. Different bodies claiming one immutable identity are a contradiction
247and are preserved/quarantined. This cannot retroactively undo side effects that
248already occurred before the contradiction became visible.
249
250No one file per keystroke: transport sealed batches from the existing queue into
251segments bounded by encoded bytes and editing bursts. The durable local receipt
252remains immediate; the provider upload can coalesce multiple edits. Individual
253edit IDs remain present for receipts/dedup. Attachments and large bodies are
254separate immutable blobs; transport does not repeatedly embed them in every
255segment. Exact segmentation thresholds require measurements, not a public
256extension promise.
257
258## Framing, validation, and wire examples
259
260Before accepting an object, check magic/version, bounded envelope/body lengths,
261notebook identity, declared kind, complete record digest, sender/object identity,
262and all required dependencies. Integrity covers the entire encoded envelope and
263body, including identities, owner epoch, predecessor/base/dependency references,
264kind and version. A body-only digest permits metadata mixups and is insufficient.
265A trailing digest or excluded self-digest field must have an unambiguous encoding
266rule. Digests prove byte consistency, not permission to edit. If access/authentication is added, it covers the envelope and dependencies
267as well as payload bytes. Provider ACLs and section passwords remain separate.
268
269Do not trust size/mtime metadata as completeness. A parseable JSON prefix or an
270apparently complete `.one` header is not enough; an object references its exact
271complete body length/digest. A readiness marker may arrive before the body and
272merely adds a dependency to wait for. Atomic rename is unnecessary for acceptance
273only if incomplete objects are rejected and complete objects stay immutable.
274
275Illustrative request envelope (symbolic IDs/digests, not executable wire data):
276
277```json
278{
279 "protocol": 1,
280 "kind": "edit-segment",
281 "notebook": "N",
282 "id": "writer-R/segment-S",
283 "writer": "R",
284 "predecessor": "writer-R/segment-before-S",
285 "base": {"section": "OneNote-file-identity", "checkpoint": "C", "decisions": ["D"]},
286 "edits": [{"replica": "Q", "id": 42, "author": "Alice", "opsObject": "O"}],
287 "dependencies": ["C", "D", "O", "blob-B"],
288 "body": {"length": 1234, "sha256": "complete-body-digest"},
289 "recordDigest": "digest-of-envelope-and-body-excluding-this-field"
290}
291```
292
293Illustrative decision and publication receipt:
294
295```json
296{
297 "protocol": 1,
298 "kind": "owner-decision",
299 "id": "D-next",
300 "ownerEpoch": "E",
301 "predecessor": "D",
302 "accepted": ["writer-R/segment-S"],
303 "dispositions": [{"replica": "Q", "id": 42, "result": "merged-or-conflict-retained"}],
304 "section": "OneNote-file-identity",
305 "baseCheckpoint": "C",
306 "resultCheckpoint": "C-next",
307 "revisions": {"page-space": "sealed-revision-identity"},
308 "dependencies": ["D", "C", "C-next", "writer-R/segment-S"],
309 "recordDigest": "digest-of-envelope-and-body-excluding-this-field"
310}
311```
312
313```json
314{
315 "protocol": 1,
316 "kind": "projection-receipt",
317 "id": "P",
318 "decision": "D-next",
319 "section": "OneNote-file-identity",
320 "observed": {"header": "OneNote-stamp-header", "length": 5678},
321 "checkpoint": "C-next",
322 "guarantee": "owner-local-file-confirmed",
323 "recordDigest": "digest-of-envelope-and-body-excluding-this-field"
324}
325```
326
327The examples omit separate payload bytes. `recordDigest` is symbolic: the codec
328must define exact bytes and self-field exclusion before implementation. Every
329object kind requires the same metadata integrity, not only edit segments.
330
331Version 1 is illustrative. The actual serialization must reuse existing
332`onestore::op::Edit`/`Op` semantics, with an explicit compatibility boundary
333around them. Existing serde representation is not independently promised stable
334forever. Do not invent a second page-diff language or CRDT. Live Share's bounded
335op batch plus named revisions demonstrates an existing reusable approach; its
336network membership/timeout assumptions do not transfer automatically to cloud
337files.
338
339## Causal bases, merges, and replay identity
340
341A segment names its exact input base and prior writer segment. A client editing
342without the owner continues its own local causal chain. The owner waits for a
343complete base/checkpoint, referenced blobs, and required predecessor decisions
344before interpreting positional text ops. An old base is not permission to apply
345UTF-16 offsets directly to a changed page.
346
347Reuse the current queue/merge machinery: `PendingEdit` carries author and ops;
348`Replica` keeps its base, seals batches, rebases against changed remote state,
349and makes conflict pages for incompatible operations. Existing
350`resolve::merge` discovers common revision ancestry and deterministic merged
351revision identities for retained competing section versions. Where no shared
352valid ancestry exists, preserve the foreign/unreadable branch for recovery.
353
354Deduplication is two related checks:
355
3561. Request identity and encoded content digest identify a transport retry.
3572. Stored named revision identities/outcomes identify work already incorporated
358 into a section/checkpoint, including after owner restart or request replay.
359
360A numeric edit ID alone is local to one replica. A file stamp alone does not
361prove every request's effect is present. A page's visible text matching expected
362text also does not prove correct history, attachments, or membership outcome.
363
364An unmergeable request or section branch remains retained with its original
365request/base/blobs and exact error. Custody acknowledgement of
366`retained-unresolved` is distinct from semantic acceptance and from file
367publication. It cannot appear in an accepted edit list as though the note had
368merged. A recoverable conflict outcome names the actual stored conflict page;
369a retained foreign/unreadable branch names recovery evidence instead.
370
371Owner decisions impose an order within their branch. Concurrent branches remain
372explicit inputs to integration; never choose the newest wall clock or discard
373content because its owner ID sorts later. Stable reconciliation ordering chooses
374presentation and replay order among already validated inputs. It is neither
375consensus nor permission to edit. The same request accepted independently by two
376branches still denotes one user action; section revision identity alone is not a
377complete cross-branch request deduplication scheme.
378
379## Owner state and write/ack ordering
380
381Proposed durable states, persisted before their corresponding externally
382observable promise:
383
384| State | Required evidence | Permitted action |
385| --- | --- | --- |
386| Discovered | Candidate object handles | Read/validate; no ack |
387| Complete | Exact body validated | Wait for dependencies or stage locally |
388| Ready | Causal base/blobs/ownership validated | Prepare existing ops/merge |
389| Retained unresolved | Original request/base/blob closure and exact failure retained | Report custody only; preserve for later merge/review |
390| Prepared | Stable request disposition, named revisions, reconstructible input/base retained | Attempt normal append |
391| Attempt uncertain | Append/header/flush result ambiguous | Inspect/confirm exact evidence; never blindly reapply |
392| File confirmed locally | Named revisions/stamp confirmed at owner's actual adapter boundary | Record historical local publication evidence |
393| Recoverable result | Immutable input/decision/checkpoint closure available under custody contract | Publish decision/result receipt |
394| Receipted | Request identity maps to durable outcome and recovery closure | Client may advance display/status; retention rules still apply |
395
396The exact linearization between durable prepared decision, file append, and
397immutable result publication needs implementation tests. The invariant is
398stronger than a particular file ordering: no externally acknowledged request
399may depend solely on mutable `.one` bytes or a volatile owner map. A crash at any
400point must leave either an unacknowledged retained request or a recoverable
401recorded outcome. Checkpoints are raw section bytes/named append artifacts,
402not projected page models.
403
404An owner can prepare and retain a reproducible named transaction before an
405append; after an ambiguous append it uses revision evidence and existing
406`CommitState`/confirmation logic. It does not regenerate new revision IDs and
407retry the user action. Appending successfully and crashing before writing the
408receipt must converge to the same outcome when the original request returns.
409
410If the provider exposes the receipt before its referenced checkpoint/decision,
411the reader waits. If the `.one` file is behind the receipt, the reader obtains
412or reconstructs the referenced section from its recovery closure; it does not
413claim the mutable provider path already holds it. If the file is ahead, it can
414read validated revisions while separately resolving incomplete decision history.
415
416Once a client has validated a selected decision/checkpoint, a stale bare `.one`
417observation must not downgrade its known accepted history. Keep that history
418and recovery dependencies locally while repairing the projection. An explicit
419intentional restore is a new recorded operation, not a stale-file inference.
420
421An owner-unavailable client still reads, navigates, edits and uses Undo from its
422private replica. A capable client may start a recovery branch; otherwise requests
423wait for a capable publisher without blocking local use. Publishing status cannot
424report another device's custody merely because local creation of a provider
425object succeeded.
426
427## Explicit handoff, recovery takeover, and late uploads
428
429Proposed explicit handoff sequence:
430
4311. Old owner stops admitting new work for the epoch and persists that stop in
432 its retained local owner state. It retains requests arriving later.
4332. Seal/record admitted decisions and their complete recovery closure; publish
434 an immutable handoff naming successor, last decision, checkpoints and pending
435 uncertainty. No time-based claim of a complete device/request census.
4363. Successor validates the chain, retrieves every required dependency, resolves
437 uncertain attempts, and records acceptance in its own durable state.
4384. Successor publishes only under the selected handoff policy. Old owner never
439 resumes that epoch after a restart; a failed handoff remains recoverable.
4405. Both retain historical recovery evidence, because old provider uploads can
441 arrive after all application steps have completed.
442
443A receipt from the successor is useful evidence for operator recovery; it does
444not prove every cloud cache drained. Waiting an arbitrary duration is not a
445fence. A fixed owner with no ownership transitions avoids one source of late
446writes but still needs rollback repair for reordered uploads from its own run.
447
448Automatic takeover keeps each candidate's decisions in its own immutable branch.
449Before admitting work it durably records the exact validated parent frontier,
450base/checkpoint closure, its fresh incarnation and any uncertain attempts. Missing
451dependencies remain pending; a candidate cannot claim complete recovery from an
452incomplete listing. It cannot overwrite another owner's mailbox/checkpoint or
453claim exclusive authority based on silence. A branch may progress with a complete
454known subset while clearly retaining that subset's identity; it must not label
455it the complete notebook history.
456
457An old process resuming after takeover must inspect discovered branch/handoff
458state before new publication, preserve its old outbox, and join reconciliation
459when it can. A stale view can still hide that state, so this check is best effort
460contention control. The safety mechanism is retained branches and attempts,
461not an assertion that the old process can always be stopped. Ordinary canonical
462`.one` files may regress or produce provider conflict versions until reconciliation
463and delivery settle.
464
465A quorum alternative can choose an ownership/decision chain if acceptor
466membership and durable promises are implemented. It still needs immutable
467recovery and projection repair unless the actual provider enforces epochs on
468writes. Do not add a quorum merely to hide this physical fencing limitation.
469
470## Reconciliation and canonical repair
471
472The selected policy needs a decision DAG, not a fabricated globally monotonic
473epoch. Each branch decision names its predecessor, exact causal input frontier,
474prepared transaction/outcome and complete recovery closure. An integration
475decision names every covered source request/branch outcome and retained unresolved
476alternative. Its references can arrive in any order. Validation waits for the
477referenced closure; it never invents missing content from visible filenames.
478
479```text
480retained seed ──► A decisions ──► A checkpoint ──┐
481 └► B decisions ──► B checkpoint ──┼► integration plan / checkpoint
482 └► offline pending queue ─────────┘ │
483 ordinary append / repair
484 ▼
485 mutable .one path
486```
487
488A candidate implements this sequence durably:
489
4901. Gather validated known branch frontiers, pending requests, external versions
491 and uncertain attempts. Follow references, preserving exact source bytes;
492 listing completeness is never asserted. Keep the last valid local view if a
493 newly observed frontier is incomplete, malformed, foreign or unsupported.
4942. Resolve exact prepared-attempt identities before admitting a retry. Build a
495 coverage map from `(originating replica, edit ID)` to source request digest,
496 branch dispositions, named revisions and recovery closure. Identical transport
497 retries are one action. Independently rebased outcomes of that action are not
498 automatically identical: retain both, select one semantic application and
499 record the other as an alternate or retained-unresolved. Selecting one outcome
500 cannot discard its alternate's dependent descendants or reinterpret their ops
501 against the chosen base. Require validated causal translation/rebase; otherwise
502 retain the complete alternate-and-descendant closure unresolved. Contradictory
503 bytes for one immutable identity halt automatic interpretation of that history.
5043. Form a reproducible integration plan for the **complete known input set**.
505 Its identity covers notebook lineage, named seed/base, source frontier and
506 coverage, merge/codec version, stable causal processing order and all outcome
507 dependencies. Dependency order comes first, then a specified byte order of
508 immutable source identities for concurrent inputs. Clocks, directory order,
509 local arrival order and largest epoch are excluded. A newly discovered input
510 makes a new plan; it cannot make the old plan evidence disappear.
5114. For incomparable histories, choose the same retained primary checkpoint under
512 that ordering, then rebase the remaining original inputs through existing
513 ops/retained-version merge. Preserve conflict pages and every opaque/unmergeable
514 version. Do not integrate “whatever this device's mutable file currently is”
515 as an unspecified primary. If normal incremental integration depends on
516 arrival order, reproduce the plan from its named retained raw base in a
517 disposable section and append normal transactions. No page-model reconstruction
518 or whole-page diff/save shortcut is allowed. This may cost more I/O.
5195. Persist exact prepared revisions, input coverage, result raw section/catalog
520 checkpoint and external blobs before issuing a recoverability promise. An
521 integration checkpoint is a result over source inputs, not another independent
522 edit. Subsequent branches carry inherited coverage; replaying integrations as
523 new source edits must not multiply conflicts. Same plan/outcome retries reuse
524 their identities. Existing named-revision dedup alone does not solve every
525 independently rebased or differently ordered plan.
5266. Confirm/publish using the actual adapter's append and ambiguity rules. If a
527 valid projection has rolled back, retain those bytes, import unknown external
528 revisions as separate inputs, then restore the chosen raw checkpoint or append
529 repair revisions as the adapter requires. Copying a validated raw checkpoint
530 repairs a materialized store; it is not recreating notes from a page model.
531 Retain the checkpoint even after local confirmation. Receipt coverage names
532 the integrated inputs and retained alternatives, not a globally complete view.
5337. Recheck after publication and on later observations. A delayed stale upload
534 starts another repair with the same retained history; it does not reapply user
535 edits. Once a fresh replica sees the chosen checkpoint and the same inputs,
536 another reconciliation must produce no extra edit/conflict revisions. A
537 publisher that cannot satisfy these steps retains work without claiming a
538 recoverable integrated outcome.
539
540Stable ordering is a proposed integration mechanism to validate, not a claim that
541today's `resolve::merge` is commutative. The existing merge keeps the current
542branch as the main page when edits overlap. Its deterministic revision names help
543retry an original branch, but neither equal names nor presence of both texts
544proves equal primary/conflict placement, causal coverage or byte-identical files.
545Plan equivalence must compare source/outcome coverage, main and conflict page
546identities/content, tombstones, catalog and blob closure. Physical allocation,
547header bookkeeping and encoding can differ without a semantic disagreement;
548such differences must not trigger endless republishing.
549
550The implementation gate is stronger than static sorted input testing: two fresh
551replicas receiving the same branches in opposite orders, including intermediate
552integration checkpoints and new edits based on them, must converge to equivalent
553covered outcomes without duplicate conflict pages or silently repeated requests.
554Test at least three concurrent branches, ancestor/descendant arrivals, one request
555accepted on two branches, namespace operations and protected/opaque payloads.
556If the current merge cannot satisfy this, retain unresolved alternatives and fix
557the integration algorithm before claiming automatic convergence. Recovery custody
558can remain safe while automatic integration is unavailable.
559
560Eventual repair is conditional. For a finite settled input set, its complete
561closures must eventually reach a compatible unlocked reconciler; stale uploads
562must eventually stop replacing the path; and a successful repair must eventually
563be visible. A deterministic plan alone cannot guarantee these properties.
564Repeated infinite old uploads are an explicit liveness counterexample. No finite
565number of quiet reads proves that the last stale upload has arrived. Lack of
566quiescence must consume retry/backoff budget, not user history or retained bases.
567
568## Authority and disaster recovery
569
570The `.one` files remain the interoperable materialized section format. For an
571explicitly registered coordinated notebook, validated retained sidecar history
572and its complete closures are authoritative for accepted/pending work when
573mutable files regress. Registration binds notebook lineage, section identities,
574protocol version and initial recovery closure; an arbitrary adjacent `.snowbound`
575folder has no authority over a bare import or a different section at the same
576path. This changes the backup contract for the selected mode, not the `.one`
577format or native/SMB modes.
578
579Distinguish three recovery operations:
580
581| Operation | Meaning |
582| --- | --- |
583| Open a registered coordinated notebook | Reconcile validated selected history/retained branches with its section identities and observed projection |
584| Import a bare `.one`/notebook copy | Treat validated bytes as an import base; no historical receipts or missing sidecar authority inferred |
585| Restore a coordinated notebook backup | Restore a consistent closure or explicitly import/reconcile incomplete histories; never reset edit IDs/epochs silently |
586
587Durable custody is always qualified by its adapter: a private SQLite transaction
588is locally retained; a provider close is not a cloud durability receipt. A client
589must not evict its only outbox because the owner merely closed a provider file.
590Cross-device verified copies or an explicit retained owner-private closure have
591their own failure domains, which the recovery contract must name.
592
593A portable backup of a coordinated notebook needs the section bytes plus every
594retained immutable object required by accepted decisions and pending requests.
595Copying only `.one` is a valid interoperable snapshot, but not proof that
596already receipted cloud edits are recoverable. Copying only `.snowbound` may lack
597the seed images or external payloads needed for reconstruction.
598
599The mode must define when a deliberately restored older `.one` is an intentional
600user restore versus provider rollback. Timestamps cannot distinguish them.
601A deliberate restore needs a new explicit operation/epoch/disposition preserving
602the previous accepted branch, rather than deleting its evidence. Exact copy and
603restore UI semantics remain owner-facing design work after evidence; no current
604build is changed by this draft.
605
606## External writers, replacement, and repair
607
608On each projection change validate store/header/checksums, section root/file
609identity, and revision ancestry. The owner compares the actual base stamp at the
610moment of normal publication using available adapter guards; coordination mode
611never falls back by silently dropping an existing lock.
612
613A matching identity with extra external revisions is an import/merge candidate.
614The existing tolerant reader and retained-version merge can preserve content.
615Record the imported exact base/version so later requests have stable ancestry.
616A different section at the same filename is foreign content, not a new base for
617old queued ops. Preserve it and stop automatic destructive reconciliation.
618Unreadable/partially repaired images do not replace the last validated base.
619A repair must record the chosen input and retained rejected alternatives.
620
621Concurrent OneNote edits on a generic cloud mount bypass the coordination
622protocol. This mode cannot guarantee safe live interchange with such writers.
623Interoperable native filesystem/direct SMB mode remains the route for OneNote
6242010 side-by-side editing. A user opening a resulting `.one` offline still gets
625a normal OneNote file; that does not make its cloud upload fenced.
626
627On iCloud, continue reading unresolved `NSFileVersion` conflicts as recovery
628inputs. Multi-file coordination can reduce ordinary competing application
629writes and give them retained request history; it cannot promise the operating
630system will create zero conflict versions, nor retire those versions before
631complete recoverability is established.
632
633## Namespace, TOC, moves, deletes, and Undo
634
635A notebook has multiple files: section groups, `.onetoc2`, recycle-bin sections,
636external payload folders, and section files. A single-section protocol is not
637a notebook protocol. Namespace operations are owner-processed intentions named
638by stable section/page identities and expected causal state; paths are results.
639Only the publisher changes shared canonical namespace/TOC in this mode.
640
641Create/rename/move/delete outcomes reference all files and object identities
642they changed. Provider creation and rename return actual handles; do not infer
643success from a requested filename or overwrite an unrelated same-name item.
644A provider may expose TOC before a new section or source deletion before a
645moved section arrives. Consumers wait for the decision's full closure and keep
646last valid local catalog state.
647
648Multi-file changes use resumable intent records, not fictitious atomicity.
649For page/section moves, establish destination durability/recoverability before
650retiring the source, validate expected source stamp and queued generation under
651actual writer coordination, and make retries identity-stable. Without physical
652transaction/fencing, prefer a preserved duplicate to loss. Do not mix this
653unsettled transport implementation with the separately investigated
654cross-notebook move work.
655
656Delete is a durable tombstone/outcome, never merely disappearance in a listing.
657Retain the historical deleted branch or recycle-bin closure required by Undo
658and offline rejoin. An old already-accepted create must not recreate a deleted
659object when replayed. A genuinely unseen edit based before deletion receives an
660explicit merge/conflict/recovery disposition; its content is never dropped by a
661membership timeout. Undo is a new conditional intention against actual latest
662content, not blindly replaying an old whole-image inverse over later edits.
663
664## Protected content and blobs
665
666The existing protected replica queue seals edits/payloads under keys derived
667from the section key; payload names use keyed digests. Reuse those semantics
668when transporting protected data, with fresh nonces and authenticated envelope
669context as appropriate. A plain SHA-256 of private attachment bytes can leak
670cross-notebook equality; choose protected blob identifiers consistently with
671the existing keyed queue. No cleartext ops, attachment previews, page titles, or
672raw protected payloads may be written into `.snowbound` as a transport shortcut.
673
674An owner cannot semantically merge protected requests without the required
675section key. Proposed behavior is retain encrypted requests and wait for an
676unlocked capable publisher; an encrypted raw-transaction path must preserve
677existing validation and cannot pretend it can rebase unknown plaintext. Whether
678an always-on publisher may retain unlocked keys and for how long is a policy
679question for later design, not an implicit background service requirement.
680Password changes/rekeying need explicit key-epoch dependencies, retained old
681keys/requests policy, and safe receipts before old material is retired.
682
683Unprotected pictures/attachments/video remain byte payloads associated with
684existing ops. Blobs arrive before or after requests; missing blob means wait.
685Validate exact length/digest, reject oversized declarations before allocation,
686bound concurrent reads, and stream where the existing storage APIs permit.
687Preserve external `_onefiles` identities and original bytes. A raw section
688checkpoint does not automatically include external payload files.
689
690## Retention, compaction, and device rejoin
691
692The first implementation retains immutable logs/checkpoints without automatic
693deletion. Storage use is real, so measure it and design compaction; finite retention
694must not be disguised as a correctness proof.
695
696No “all devices have seen it” rule can be inferred from current listings or
697heartbeats. An offline device may hold unpublished edits indefinitely. Deleting
698an accepted request after observing `.one` alone is unsafe because that file
699can later roll back. Receipt existence alone is also insufficient if the
700receipt's reconstructible dependencies are removed.
701
702A compacted checkpoint needs complete section/catalog/blob state, accepted
703request identity/outcome coverage, selected history and retained branch/tombstone
704coverage, plus its exact predecessor closure or an explicit permanent cutoff.
705Only a verified replacement recovery closure can justify retiring old objects.
706Keep dedup/tombstone evidence needed to recognize old retries after compaction.
707
708For an offline client older than retained ancestry, retain its private queue and
709import a fresh base without discarding it. If exact rebase is unavailable, preserve
710its old base/ops as a recovery branch and require an explicit content outcome.
711A destructive administrative reset establishes a new notebook lineage and keeps
712an export of displaced data; it does not make stale requests silently valid.
713An app uninstall destroying the sole private outbox remains an actual loss
714boundary unless that outbox reached retained custody elsewhere.
715
716## Scale and discovery cost
717
718A long notebook cannot relist a flat object directory on every keypress. Segment
719existing sealed batches, partition by writer/owner and bounded buckets, cache
720validated handles/objects locally, follow known dependency/predecessor references,
721and use notifications as hints plus periodic reconciliation. Mutable indexes
722may reduce scans but cannot establish absence or completeness.
723
724SAF opaque document IDs make filename-derived constant-time lookup unavailable
725in general. Resolve actual handles from queried parents and cache them by
726validated object identity. Bucket size and deep directory cost need real provider
727measurements; a huge hierarchy can cost more than a flat list. Unknown new writer
728discovery still needs enumeration, so “bounded lookup” cannot mean a complete
729new-client discovery guarantee without scanning any provider state.
730
731Measure at least: edits per sealed segment; object and request count; bytes
732including retained bases/blobs; cold-open queries; warm sync queries; maximum
733listing size; incomplete dependency backlog; latency until local durability,
734owner custody and visible remote result; and time to rebuild after projection
735rollback. Record provider/API/build identity. A host memory microbenchmark is
736not a Google Drive/iCloud listing benchmark.
737
738## Status and receipt meanings
739
740These are engineering states, not proposed public UI strings:
741
742| Evidence | What is known | What is not known |
743| --- | --- | --- |
744| Local queue durable | This installation can recover the edit after process death | Provider/owner received it |
745| Provider create/close succeeded | This adapter completed local object I/O | Another device or cloud service has it durably |
746| Owner intake durable | Owner has validated recoverable request/dependencies under stated custody | Canonical `.one` contains it |
747| Owner result recoverable | Accepted outcome can be reconstructed from retained closure | Every client has downloaded it |
748| Owner file locally confirmed | Normal append/confirmation completed at owner adapter | Cloud globally ordered/durable, no later rollback |
749| Other client observed result | That client's validated state includes decision/checkpoint | All devices saw it or GC is safe |
750| Historical published receipt | A publication was confirmed at an earlier point | Its effect remains in today's mutable projection |
751
752Current `EditStatus::Published` is historical by design. Adding transport states
753must preserve that meaning and the existing local receipt boundary. Current
754mobile `sb_section_poll` coarse saved/saving/offline values cannot silently
755encode these extra guarantees; UI flow/copy changes require a separate approved
756mapping after protocol behavior exists.
757
758## Fault matrix and practical investigation
759
760Lab evidence lives outside source under
761`target/multi-file-sync-spec-20261011/`. It is a bounded investigation, not a
762production backend or commercial-provider certification. The Rust lab uses the
763existing `onestore::Transaction::commit`, `notebook::Replica`, receipt logic,
764retained-version merge, and cold parser. Provider delivery is a deterministic
765simulation; logical drop/reopen is not an OS-kill durability test. The separate
766Python model explores object framing/dependencies and ownership rules only.
767
768### Recorded results
769
770The following are observed experiment outcomes. The requirements and future
771implementation gates elsewhere in this draft are not proven by these tests.
772Native-corpus Rust run 7 completed with exit 0, and transport-model run 3
773completed with exit 0. Reports and raw `.one` outputs remain in the ignored lab
774folder; the verification manifest binds their hashes and the exact lab sources.
775
776| Experiment | Observed outcome | Evidence |
777| --- | --- | --- |
778| Heartbeat/lease takeover with stale view | Counterexample found: two independently valid append results; winning snapshot omits ALPHA | `run-7/report.json` → `lease_takeover_split_writer` |
779| Explicit handoff, then old provider upload | Counterexample found: exact ALPHA BETA becomes ALPHA RIGHT after delayed snapshot delivery, despite no concurrent application writers | `clean_handoff_late_old_upload` |
780| Retained independent section branches | Existing merge yields ordinary text exactly ALPHA BETA | `independent_immutable_branches_recover` |
781| Late-upload rollback with retained checkpoint | Existing retained-version merge recovers BETA | `late_upload_recovered_from_immutable_checkpoint` |
782| Concurrent overlapping changes | Main page retains BOB RIGHT; actual conflict page retains ALICE RIGHT | `overlap_retains_actual_conflict_page` |
783| Append succeeds, reply lost, logical close/reopen | One publication total; existing receipt settles without another append | `owner_append_then_crash_before_ack` |
784| Historical Published receipt, then projection rollback | Counterexample found: queue empty and historical receipt remains Published while ALPHA is absent | `historical_receipt_does_not_repair_projection_rollback` |
785| Resumed old-owner branch and new-owner branch | Existing merge retains OLD and NEW; strict owner uniqueness not established | `old_owner_resumes_branch_preservation` |
786| Old offline branch retained through rejoin | Existing merge can import its ALPHA content | `offline_rejoin_request_retention` |
787| Original branch redelivered after merge | For each recovery case: three same-database reopen/retries plus one genuinely fresh replica seeded from merged bytes; zero extra publications, identical output | Recovery case `proof` fields |
788| Generated minimal section seed | Current cold parse succeeds for both same-file/root branches; exact existing resolve returns “Object space has no active default revision” | `run-7/generated-seed-diagnostic.json` |
789| Abstract transport model | 737 truncated prefixes and 737 single-byte mutations rejected; 120 arrival permutations converge once with omission/duplicates; schema/cycle/contradiction checks pass | `visibility-run-3/visibility-report.json` |
790| Abstract deep input | 1,101-object dependency chain traversed iteratively; nested 1,100-level JSON rejected without recursion crash | Same transport report |
791
792The generated-seed diagnostic is specifically the current `create_section`
793helper's output and those exact edits. Current-image parsing does not certify
794all historical ancestry candidates or OneNote conformance. The existing sync
795fallback keeps an unmergeable version whole; this result is not evidence that
796all supported production inputs fail to merge. It does mean that a protocol
797must represent retained-unresolved custody separately from accepted content.
798The native-corpus success demonstrates existing section-version merge/dedup,
799not a new request/owner-decision/tombstone implementation.
800
801Failed investigations remain preserved: run 1 (missing lab dependency), run 3
802(lab type-alias error), runs 2/4/5 (generated-seed refusal), and run 6 (text oracle
803looked only at ordinary page-list content, excluding the conflict page). Final
804run 7 asserts actual conflict-page text through the existing section conflict
805reader and preserves the earlier evidence. No failed run is a product gate.
806
807Rerun from the repository root on a clean **new output directory**, using the
808shared target and one build slot; never reuse a run folder or point the lab at an original notebook:
809
810```sh
811nice -n 10 env CARGO_BUILD_JOBS=2 CARGO_TARGET_DIR="$PWD/target" cargo run --offline \
812 --manifest-path target/multi-file-sync-spec-20261011/lab/Cargo.toml -- \
813 "$PWD/target/multi-file-sync-spec-20261011/run-new"
814python3 target/multi-file-sync-spec-20261011/lab/visibility.py \
815 target/multi-file-sync-spec-20261011/run-new \
816 target/multi-file-sync-spec-20261011/visibility-run-new
817```
818
819The ignored standalone manifest currently has absolute path dependencies into
820`workspaces/android-host-plan`; the verification manifest records them. The lab
821links the repository's existing test remote and private merge sources directly,
822without copying or changing them. The fixture is a disposable read of
823`corpus/conflict-page/native/initial/notebook/synthetic.one`; its input hash is
824recorded. No commercial provider, emulator, or OneNote process ran in this lab.
825
826### Resilience policy follow-up
827
828The 2026-10-11 follow-up under
829`target/multi-file-sync-resilience-20261011/` tests the selected policy's recovery
830primitives and exposes limits of the current merge. Run 2 completed with exit 0
831in 2.4 seconds after the initial compilation populated the shared cache. The
832first attempt failed to compile a lab-only immutable binding used by
833`Section::conflicts()`; its exact source/log/result remain preserved. Neither
834attempt changes production code.
835
836| Experiment | Observed outcome |
837| --- | --- |
838| Same overlapping ALICE/BOB branches, opposite primary bases | Both alternatives survive, but main/conflict placement differs; three replays of each original branch add zero publications and preserve exact bytes |
839| Cross-deliver those opposite integration artifacts directly to current merge | Both directions publish again, flip the primary text and retain two conflict pages, including an alternative repeating current main text; naïve integration-result exchange is not convergence |
840| Sort the same complete two-branch set before choosing a retained primary | Both arrival orders produce the same main/conflict identities and text; raw bytes and some active revision identities still differ |
841| Five successive stale canonical replacements, alternating seed/A/B images | Every finite round repairs to exact ALPHA BETA using retained originals/checkpoint; repair costs 2/1/1/2/1 publications, and settled same-database/fresh-replica retries add zero |
842| Fresh local recovery with a truncated canonical image | Cold parser rejects the image; retained raw source sections recover exact ALPHA BETA; observing only B lacks ALPHA and cannot establish complete history |
843
844Ten SQLite files pass integrity checks. Each saved valid `.one` is cold parsed
845through the existing store/index/document reader with checksum/current-revision
846validation; actual conflict pages are inspected separately from the ordinary
847page. The immutable archive stays unchanged across repair. These are disposable
848copies of the prior native-corpus fixture and actual production
849`Replica`/`resolve::merge`/transaction calls; provider replacements, visibility,
850source ordering and logical database reopen are simulated by the lab.
851
852The sorted-set result proves only a bounded presentation property for this
853complete two-branch fixture. It does not prove equivalent opaque/protected
854content, downstream causal translation, arbitrary incremental DAG convergence,
855namespace operations, request coverage deduplication, cloud custody or OS-crash
856durability. The opposite-integration result is why the selected algorithm must
857track source coverage and normalize from retained inputs instead of feeding
858integration artifacts back as new independent edits. No automatic convergence
859claim is justified by this investigation.
860
861Rerun from a clean new output directory with one granted build slot:
862
863```sh
864nice -n 10 env CARGO_BUILD_JOBS=2 CARGO_TARGET_DIR="$PWD/target" cargo run --offline \
865 --manifest-path target/multi-file-sync-resilience-20261011/lab/Cargo.toml -- \
866 "$PWD/target/multi-file-sync-resilience-20261011/run-new"
867```
868
869The ignored standalone lab links the current source in
870`workspaces/sync-resilience` and reads three bound `.one` inputs from the original
871run 7 without modifying them. Its verification manifest binds lab/dependency
872sources, inputs, raw outputs, SQLite integrity checks, draft architecture review
873and document checks. No commercial provider, emulator or OneNote process runs
874in this follow-up. Native OneNote cold-open remains an implementation gate.
875
876| Fault | Required result / investigation oracle |
877| --- | --- |
878| Prefix/partial body, invalid length/digest | Reject/wait; last valid state untouched |
879| Reordered/duplicate/omitted listings | Same logical requests applied once; omission never deletion |
880| Missing base/blob/predecessor | Wait for dependency; do not apply positional ops to wrong base |
881| Same immutable ID, different bytes | Preserve contradiction; stop automatic authority selection |
882| Owner unavailable | Local editing/Undo/reopen works; requests retained |
883| Owner crash after append before ack | Original named outcome recognized after reopen; no repeated edit |
884| Lease takeover while old view is stale | Expected counterexample: both writers eligible; never call this fenced |
885| Graceful handoff followed by old queued upload | Expected counterexample: canonical `.one` can regress |
886| Old owner resumes after takeover | Preserve both decision/section branches; merge/recover without silent loss |
887| Published receipt then `.one` rollback | Historical receipt cannot alone restore removed effect |
888| Independent retained branches | Actual merge retains both changes, retry idempotent |
889| Overlapping retained branches | Actual conflict page preserves both alternatives |
890| Retried old branch after owner restart | No duplicate revisions/conflict pages from original immutable input |
891| Offline rejoin after delete/compaction | Dedup/tombstones honored; unseen edits retained for explicit outcome |
892| Invalid/foreign/repaired `.one` | Preserve bytes; never rebase old requests onto a different section |
893| Close/flush/upload ambiguity | Keep attempt evidence and request; do not promise remote durability |
894| Protected request without owner key | Encrypted queue stays recoverable; no plaintext transport fallback |
895| Attachment missing/partial | Wait; no receipt claiming complete recoverability |
896| TOC arrives before section / move halfway | Keep catalog/intent recoverable; prefer duplicate over loss |
897
898The Android host/provider plan and its two DocumentsProvider fixtures are at
899`target/android-wave-20261011/host/`. They are specified, not yet implemented or
900run. They test the real platform boundary separately from these simulations.
901Commercial provider observations, native OneNote cold-open of new protocol
902outputs, large-notebook scale, protected transport, namespace recovery, and actual
903process-kill/crash tests are implementation gates, not claims made by this draft.
904
905## Implementation boundary and review decisions
906
907`onestore` remains unaware of providers, coordination folders and ownership.
908The data-layer protocol belongs toward `notebook`, reusing queue, merge,
909recovery and normal append operations. Platform hosts adapt document/file APIs,
910retained local directories and actual I/O/error callbacks. Android rendering and
911native IME/accessibility can ship independently of this protocol.
912
913The owner has selected resilient branch-preserving takeover and retained recovery
914authority for registered coordinated notebooks. Before production protocol code,
915validate the reconciliation/receipt algorithm against the gates above and define:
916
917- Explicit registration, bare import, intentional restore and duplicate-copy flows
918 that preserve the stated lineage/custody boundaries.
919- Protected-key custody and unsupported-client migration/escape behavior.
920- Measured provider capability floor and storage cost. Initial automatic GC is
921 excluded; later compaction requires its own recovery proof.
922
923Unknown protocol/required-op versions must leave remote data untouched and keep
924local edits recoverable. Enabling coordination requires an explicit migration
925record and closure; old builds that ignore `.snowbound` can still edit `.one`
926uncoordinated. A sidecar marker cannot stop those binaries. Rollout therefore
927needs an honest compatibility boundary and an opt-in/import path, not a claim
928that an unsupported build will obey a new marker.
929
930The next concrete deliverable after the draft is a narrow transport/owner lab
931against real provider fixtures, then the selected policy implemented behind an
932explicit storage mode and tested with actual notes. No timeout election or
933full-image save shortcut should land as an Android dependency.