Plan: Task 3 compares deltas, not just plain text
Ruling R5, carried into the plan. The fixtures gained a delta field because text alone cannot tell a correct implementation from one that decoded the update and dropped the formatting -- and nothing else in the plan caught that either, so a plan whose whole purpose is proving wire compatibility could not see the loss of bold, italics or links. Carries the verified yrs API facts so the implementer builds against checked signatures rather than guessing, and requires the assertion be proven to bite by temporarily discarding attributes -- same discipline as the zero-imports gate, which turned out to be worth having.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011F5CivVm4CmL9ByfSSAaXA
1 file changed,
+33
−5
docs/superpowers/plans/2026-08-30-rust-core-wire-compat.md+33 −5
diff --git a/docs/superpowers/plans/2026-08-30-rust-core-wire-compat.md b/docs/superpowers/plans/2026-08-30-rust-core-wire-compat.md| index 4673ab1..bb2a90d 100644 |
| --- a/docs/superpowers/plans/2026-08-30-rust-core-wire-compat.md |
| +++ b/docs/superpowers/plans/2026-08-30-rust-core-wire-compat.md |
| @@ -297,10 +297,11 @@ fn fixture(name: &str) -> (Vec<u8>, String) { |
| } |
| fn check(name: &str) { |
| - let (update, expected) = fixture(name); |
| - let doc = Doc::new(); |
| + let (update, expected_text, expected_delta) = fixture(name); |
| + let doc = Doc::with_client_id(9001); // never Doc::new() — see Global Constraints |
| docs_core::apply_update(&doc, &update).expect("apply failed"); |
| - assert_eq!(docs_core::text_content(&doc), expected, "fixture {name}"); |
| + assert_eq!(docs_core::text_content(&doc), expected_text, "fixture {name} text"); |
| + assert_eq!(delta_of(&doc), expected_delta, "fixture {name} delta"); |
| } |
| #[test] |
| @@ -324,13 +325,40 @@ fn applies_concurrent() { |
| } |
| ``` |
| -Add the dev-dependency to `core/Cargo.toml`: |
| +Add the dev-dependency to `core/Cargo.toml` (exact pin, per Global Constraints): |
| ```toml |
| [dev-dependencies] |
| -serde_json = "1" |
| +serde_json = "=1.0.145" |
| ``` |
| +Take whatever exact version `cargo add --dev serde_json` resolves. |
| + |
| +**The delta comparison — amended during execution (ruling R5).** The fixtures |
| +now carry a `delta` field alongside `text`, because `text` alone cannot |
| +distinguish a correct implementation from one that decoded the update and |
| +dropped the formatting entirely. `fixture()` must therefore return three |
| +values, and the test needs a `delta_of(&Doc)` helper that produces the same |
| +attributed-run shape Yjs's `toDelta()` writes. |
| + |
| +Verified API facts to build it from — check each against the pinned `yrs` |
| +rather than trusting this list: |
| + |
| +- `Text::diff<T, D, F>(&self, txn: &T, compute_ychange: F) -> Vec<Diff<D>>` — |
| + `yrs-0.23.5 src/types/text.rs:420`, on the `Text` trait. |
| +- `Diff<T> { insert: Out, attributes: Option<Box<Attrs>>, ychange: Option<T> }` |
| + — `text.rs:1103`. |
| +- `YChange::identity` — `text.rs:1201`, the pass-through for `compute_ychange`. |
| +- `Attrs = HashMap<Arc<str>, Any>` — `types/mod.rs:882`. |
| + |
| +Attribute order must be normalised (sort by key) before comparison, or the |
| +test is flaky on `HashMap` iteration order. |
| + |
| +**The delta assertion must be proven to bite**, the same way the zero-imports |
| +gate was: temporarily make `delta_of` discard attributes, confirm the |
| +`formatted` fixture fails and the other three still pass, then revert. A |
| +comparison that cannot fail is not an assertion. |
| + |
| - [ ] **Step 2: Run the test to verify it fails** |
| ```bash |