# refactor: replace per-operation intents with page saves · clo/snowbound

[View on GitCafe](https://git.cafe/clo/snowbound/commit/cc5b6a1bca8a59e2c06208c9086e7b002f0e95df)

Repository: [clo/snowbound](https://git.cafe/clo/snowbound)

Visibility: public

Requested revision: cc5b6a1bca8a59e2c06208c9086e7b002f0e95df

Requested commit: cc5b6a1bca8a59e2c06208c9086e7b002f0e95df

Commit: cc5b6a1bca8a59e2c06208c9086e7b002f0e95df

Tree: 29e15310e4918861a3a9608f78a186567619185b

Author: clover caruso

Committer: clover caruso

## Message

```
refactor: replace per-operation intents with page saves

The notebook replica no longer records typed editing operations. The editor
saves whole page models through Replica::save, which diffs the model against
the stored page, writes the difference into the working image and queues one
Operation::Page { before, after, author }; create_page, pages and delete_pages
remain the explicit section operations. The per-operation modules (paragraph,
formatting, outline, tree) and their conflict-review entry points are gone,
replaced by review_page.

Saves coalesce the way OneNote autosave does: a save for the same page as the
newest unattempted intent replaces its model under the same id. An attempted or
conflicting intent is never rewritten, and neither is the intent a running
synchronization step has selected but not yet recorded an attempt for; that
window would otherwise publish a stale model over an acknowledged save.

Reconciliation of a page intent against a changed remote page is a three-way
merge that picks each field per side: outline layout (position; width, its
user flag and the wrap reservation as one group), paragraph style, lists, tags,
parent and level, text content, and non-text content such as table cells. A
local text edit re-applies over a remote formatting-only change of the same
paragraph when its own formatting outside the replaced range is untouched.
Child order takes the remote list and re-places the paragraphs the local side
moved (the members outside a longest common subsequence with the stored
order); a paragraph both sides moved, or that the local side moved and the
remote removed or reparented, is a conflict rather than a silent loss.
Paragraphs reparented under a locally added paragraph, as a split of a parent
does, are placed under it.

The cache schema is version 12 and any other version is refused, naming both
versions, for caches and recovery archives alike; migrations return once the
application is usable end to end. The offline-v4 corpus and the migration
tests are removed.

Tests are re-expressed on page saves through tests/support/model_ops.rs and
split into one binary per subject (sync, sync_worker, sync_paragraph,
sync_formatting, sync_outline with tree, sync_page, sync_pages, sync_model)
sharing tests/support/server.rs. Tests whose subject was a per-operation
mechanism (typed rebases, retired-format attribute confirmation, insertion
placement review, tree attempt bookkeeping) are deleted. The tree schedule
driver and the offline_paragraph/offline_tree fuzz targets are replaced by a
page-model schedule (tests/support/model_schedule.rs, fuzz target
offline_model). The per-operation document workload of the smb_offline_client
example and its Python history check are removed pending a page-model design.

The native reconciliation exporters still write the paragraph-edit manifests;
the outline-edit and paragraph-edit corpus records and their Python
expectations reflect the per-operation runs and are regenerated when the
native gates rerun on intents.

Verification: cargo fmt, cargo clippy --workspace --all-features --all-targets,
cargo nextest run --workspace --all-features (431 passed, 32 skipped, 1
expectation fixed afterwards and rerun), doctests, fuzz crate check, Python
lane (162 passed).

Assisted-by: claude-fable-5.1

```

## Parents

- [2e2f9466aec89c2ab7fc03378e3cbf975ffd7f9d](https://git.cafe/clo/snowbound/commit/2e2f9466aec89c2ab7fc03378e3cbf975ffd7f9d?format=markdown)

[Source at this commit](https://git.cafe/clo/snowbound/tree/cc5b6a1bca8a59e2c06208c9086e7b002f0e95df?format=markdown)
