Merge canonical main (#172, #157) into the docs lane

Both conflicts were competing rewrites of the same anchor lines: #172
refreshed the canonical base and the handoff header while this branch
did the same for #165. Resolved by taking main's list, which is the more
accurate of the two (it names Lean 4 Stage 2 #161 properly), refreshing
it to the current tip `ccf29e3`, and keeping this branch's note that
lanes naming an older base have not been re-based.

#172 also removed the inline-math lane, so the stale-header note drops
from three back to two and now says who owes the remaining updates.
This commit is contained in:
Levi Neuwirth 2026-07-25 19:49:18 -04:00
commit 39233aa25f
5 changed files with 188 additions and 158 deletions

View File

@ -1272,9 +1272,16 @@ its asks are already practiced.**
exclusive by construction; every other per-frontend-kind pair in the exclusive by construction; every other per-frontend-kind pair in the
dispatcher remains two adjacent `if`s that a reader must notice are dispatcher remains two adjacent `if`s that a reader must notice are
alternatives. alternatives.
- The GPU frontend exceeds the TUI (minimap, squiggles, typography) - The GPU frontend exceeds the TUI (minimap, squiggles, typography,
without the TUI losing the model — the "no privileged frontend" rule and since #158 rendered inline math) without the TUI losing the
is holding under real divergence pressure. model — the "no privileged frontend" rule is holding under real
divergence pressure. Inline math is the sharpest case so far: the
GPU shapes `$…$` spans through a bundled MATH-table font while the
TUI shows the LaTeX source unchanged, and the TUI's distinct-face
fallback is a **named deferral rather than an oversight**. What
keeps it inside the rule is that the slice reserves no protocol
version and adds no wire surface — the divergence is presentational
only, and the semantic model both frontends read is identical.
Remaining, honestly small relative to the section's ambition: capability Remaining, honestly small relative to the section's ambition: capability
negotiation is per-bit rather than a first-class declared capability negotiation is per-bit rather than a first-class declared capability

View File

@ -5,14 +5,14 @@ landed on `main`. Read it after `docs/agent-handoff.md`. Remove completed
entries when their PR merges; do not let this become a second permanent entries when their PR merges; do not let this become a second permanent
backlog. backlog.
**Three lane headers below are stale on purpose**, pending the docs **Two lane headers below are stale on purpose**, pending the docs updates
updates their own lanes owe: the inline-math slice **#158 has merged** their own lanes owe: multi-root LSP affinity **#161 has merged** (the
(its lane still says PR #158 OPEN), multi-root LSP affinity **#161 has Lean 4 lane still says IN REVIEW; its continuation is PR #167) and GPU
merged** (the Lean 4 lane still says IN REVIEW; its continuation is PR terminal input **#166 has merged** (its lane still says IN REVIEW; PR
#167), and GPU terminal input **#166 has merged** (its lane still says IN #168 records it). Trust the canonical-base line below over a lane header:
REVIEW; PR #168 records it). Trust the canonical-base line below over a if a PR number appears in `git log --first-parent githubsucks/main`, it
lane header: if a PR number appears in `git log --first-parent has landed regardless of what its lane says. (The inline-math lane was
githubsucks/main`, it has landed regardless of what its lane says. here too until #172 removed it — that is the update those two owe.)
## Repository authority ## Repository authority
@ -23,14 +23,13 @@ githubsucks/main`, it has landed regardless of what its lane says.
machine-local: `origin` may name this canonical URL, a release mirror, machine-local: `origin` may name this canonical URL, a release mirror,
or something else, and therefore has no authority by name alone. or something else, and therefore has no authority by name alone.
- Canonical base at this snapshot: - Canonical base at this snapshot:
`githubsucks/main` @ `d152120` (the bottom-panel docs #156 atop the `githubsucks/main` @ `ccf29e3` (the CRDT undo repro #157 atop the
inline-math slice #158, dired Stage 1 #165, GPU terminal input #166, inline-math landed-doc refresh #172, the bottom-panel landed-doc
multi-root LSP affinity #161, the dired framing #164, refresh #156, the inline-math slice #158, dired Stage 1 #165, the GPU
find-file #162, COHERENCE.md #163, Lean 4 Stage 1 #160, the minimap terminal input fix #166, Lean 4 Stage 2 #161, the dired framing #164,
blank-slab fix #159, bottom-panel Stage 1 #155, the inline-math re-scout COHERENCE.md #163, find-file #162, Lean 4 Stage 1 #160, and the minimap
#154, the vterm PTY-flake fix #153, and the GPU initial-target doc blank-slab fix #159; protocol v20). **Lanes below that name an older
refresh #152; protocol v20). **Lanes below that name an older base have base have not been re-based; derive their integration surface from
not been re-based; derive their integration surface from
`git diff <their base>..main`.** `git diff <their base>..main`.**
- On the transfer source, `origin/main` named a release mirror at - On the transfer source, `origin/main` named a release mirror at
`d3fa632` and lagged badly. On the current destination, `origin` names `d3fa632` and lagged badly. On the current destination, `origin` names
@ -65,137 +64,9 @@ git worktree list
git status --short --branch git status --short --branch
``` ```
The `git log` command must expose `8c86d34` or a newer intentional main. The `git log` command must expose `d152120` or a newer intentional main.
If it does not, stop and repair the remote/fetch configuration. If it does not, stop and repair the remote/fetch configuration.
## Inline-math slice lane — PR #158 OPEN, main integrated
- Portable branch: `githubsucks/inline-math-slice`; worktree
`../pmacs-math-slice`. **PR #158**, base `main`.
- **Canonical `main` merged into the lane three times on 2026-07-25**,
every time merged rather than rebased, per the #135/#137 precedent: the
PR is awaiting review rounds and a rebase would break every review
anchor.
- First at `8c86d34` (28 commits behind). The conflict was
**pre-existing**, not introduced by the dired (#164) or Lean 4
ledger commits; it already conflicted against `main` @ `e745068`.
- Then at `46a1b8f`, after Lean 4 Stage 2 (#161) landed while this
branch's CI was still running. Same single conflict, same shape,
same resolution.
- Then at `b889873`, after the GPU terminal input fix (#166) landed.
**No conflict at all this time** — and that is exactly why it still
needed a real integration, see below.
- **The first two conflicts were this ledger and nothing else** — both
sides' lanes kept verbatim each time. That is the standing cost of a
long-lived PR here: every merge to `main` edits this file, so a branch
awaiting review re-conflicts on it and only on it. It is a docs
collision, never a code one, and it says nothing about integration
risk — do not read a `CONFLICTING` badge on this PR as a code signal
without checking which file `git merge-tree` names.
- **The inverse trap matters more, and #166 is the case in point: a
CLEAN `git merge-tree` is not a reason to skip integrating.** #166
landed 41 lines in `pmacs-gpu/src/main.rs`, the same heavily-rewritten
file as the first integration, and git merged it without a murmur
because the two edits sit in different regions (#166 is entirely in the
headless probe — `PMACS_GPU_PROBE_OBSERVE_MS`, `PROBE_INPUT_CHAR`,
`input_echo_observed` — while this lane rewrites the render path).
Merging the PR on the strength of that clean auto-merge would have
shipped a combination no gate had ever run. **Decide whether to
integrate from the shared-FILE set, not from whether git complained.**
- **First integration's surface** (derived from `git diff
<merge-base>..main`, not from another PR's file list):
`pmacs-gpu/src/main.rs` gained 72 lines on main from `e547a90` — the
minimap all-blank-slab divide-by-zero fix — and this lane rewrites
large parts of the same file. Git auto-merged it **textually**; a
clean auto-merge is not evidence the tree compiles (the folding-arc
lesson), so the full gate suite below is what actually discharges it.
- **Second integration's surface is code-disjoint.** #161 touched
`COHERENCE.md`, `builtin/runtime/lsp.lua`, `src/lua_bindings/mod.rs`,
and a new `tests/lsp_multi_root_acceptance.rs`; intersecting that
against this lane's own changed-file set leaves exactly one entry,
`docs/active-work.md`. No source file is touched by both sides, so
this one carries none of the first integration's semantic risk.
- **CI ran on this branch for the first time on 2026-07-25 and passed
all twelve** (Format, both Lints, GPU Render headless, all four Test
matrix jobs, M1/M4/M5/M6 gates) at `8b457de` — the first-integration
tip. Before that there were zero workflow runs since the PR opened on
2026-07-24, while every other open PR had a full run; not a fork and
not a trigger-config issue (the workflow fires on all `pull_request`
events), cause never identified. So the green run **validates the
first integration, including the `pmacs-gpu/src/main.rs` auto-merge,
on macOS and Linux both** — the platforms local gating could not
cover. The second and third integrations get their own CI run on the
push that carries them.
- Framing: `docs/inline-math-slice-framing.md` rev 3, approved after two
review rounds; parent arc framing merged as #154.
- State: parser, font bundle (GUST licence), MATH-table layout with the
measured height budget, currency-guarded detection, and the
`ChunkSource::MathBox` spacer substrate are implemented and
round-3-reviewed (review fixes at `cbf7782`: the exclusive-`end`
mapping bug its own test had pinned, script-marker whitespace, fallible
layout via `UncoverableGlyph`, real fraction gap-min constants —
flagship scale 0.867, fallback depth 5).
- Caret-driven suppression (the Q#MS5 gate over the effective caret and
Q#MS11 selection endpoints, chunk substitution before tab expansion,
the line-reuse predicate's third input, and the CursorByte /
optimistic-edit / Decorations refresh triggers), the draw pass
(per-glyph mini-buffers positioned by each shaped line's real
baseline, fraction-rule quads over the washes, the F8b family pin),
the Q#MS11 whole-rectangle wash widening, and the pixel acceptance
battery (criteria 511, 1416; 17 discharged by the differential
`cargo tree -e features` check — byte-identical with and without the
dependency line) are implemented on the branch tip.
- Clippy is CLEAN on the whole workspace at `-D warnings` — the draw
pass consumed every formerly-dead item.
- Verification **pre-integration** (at `14c1c01`, against the old base):
199 `pmacs-gpu` tests under `PMACS_REQUIRE_GPU=1`; 1,815 default +
1,992 CRDT library tests; M4 121; full workspace sweep green (isolated
`XDG_CONFIG_HOME`). Superseded by the post-integration run below —
those numbers describe a tree 28 commits behind.
- Verification after the **first** integration: `cargo fmt --check`
clean; strict workspace Clippy clean; 1,826 default + 2,003 CRDT
library tests; **202 `pmacs-gpu` tests under `PMACS_REQUIRE_GPU=1`**;
M4 121; isolated-`XDG_CONFIG_HOME` `--no-fail-fast` workspace sweep
3,208 across 91 suites, zero failures; `git diff --check` clean.
- Verification after the **third** integration (this is the set that
describes what the PR now proposes): fmt clean; `git diff --check`
clean; strict workspace Clippy clean; **1,829 default + 2,006 CRDT**
library tests; **202 `pmacs-gpu`** under `PMACS_REQUIRE_GPU=1`; M4 121;
**isolated-`XDG_CONFIG_HOME` `--no-fail-fast` sweep 3,224 across 92
suites, zero failures**.
- **Test-count reconciliation is the integration proof, not the pass.**
Run it against what the other side actually added, per merge:
- First: GPU 199 → **202**, and `e547a90` added exactly **3**
`pmacs-gpu` tests — the whole delta on main since the merge base.
Structurally spot-checked too: main's fix survives as
`(count > 0).then(|| MinimapLineShape {` (the deferred closure,
**not** the eager `then_some`) with its regression test.
- Third: #166 adds **3** library tests, **2** to
`vterm_stage3_acceptance`, and **0** to `pmacs-gpu`. Predicted lib
1,826 → 1,829, CRDT 2,003 → 2,006, GPU unchanged at 202 — and that
is exactly what ran. Suite count 91 → **92** is #161's new
`tests/lsp_multi_root_acceptance.rs` binary. All three sides'
markers confirmed live in `pmacs-gpu/src/main.rs`: #166's probe
symbols, this lane's `math_plan_for_line` / `math_gates_match` /
`cached_math_subs_for_slice` / `widen_over_math_chunks` / 21
`MathBox` references, and the first integration's minimap fix.
- **Ops trap, cost hours: `m4_5_basedpyright_initializes_and_negotiates_
encoding` does not time out — it hangs forever.** A `--workspace`
sweep parks on `m4_acceptance` with a live
`basedpyright/langserver.index.js` child and never advances (observed
stuck at 38 of 92 suites for 2h26m). The per-suite M4 gate already
carries `-- --skip basedpyright`; **the workspace sweep needs the same
flag** — `cargo test --workspace --no-fail-fast -- --skip
basedpyright` (libtest filters apply to every binary; verify it bit by
checking the run reports exactly 1 filtered out). Do not read a
long-running sweep as "slow": check whether the suite count is
advancing.
- Remaining: the user's review pass.
Named v0 approximations: the peer-caret half of acceptance 14 is
pinned at the mapping level (unit tests), not pixels; a soft-wrapped
spacer draws its box whole at the first run's origin; the fit budget
reads the bundled code face even under a custom `set_font` family
(the draw anchors to the real shaped baseline either way).
## Lean 4 lane (Arc 8) — Stage 1 MERGED; Stage 2 IN REVIEW (PR #161) ## Lean 4 lane (Arc 8) — Stage 1 MERGED; Stage 2 IN REVIEW (PR #161)
- Stage 1 **merged as #160** (`main` @ `0827dd1`, 2026-07-25, one review - Stage 1 **merged as #160** (`main` @ `0827dd1`, 2026-07-25, one review
@ -496,6 +367,47 @@ git worktree add --track \
undifferentiated `ReplyKind::FsUnit` and so must be keyed on undifferentiated `ReplyKind::FsUnit` and so must be keyed on
`JobKind::FsRename`. `JobKind::FsRename`.
- **Inline-math slice — MERGED as #158** (`main` @ `5aa9044`,
2026-07-25). Detect → parse → layout → draw for `$…$`, entirely inside
`pmacs-gpu`, no protocol change. Verified by the user's manual pass on
a real paper after the landing. What is worth carrying forward:
- **The v0 subset is 34 Greek symbols, sub/superscript, and `\frac`.**
An unsupported command fails the **whole span** back to source, so on
a real document most inline spans still show LaTeX. Widening the
symbol map is the highest-value next increment — ahead of display
math, which is also deferred.
- **A stale frontend binary is invisible from the source tree.** The
slice lives only in `pmacs-gpu`, so after it merged the feature was
absent until `cargo build --release -p pmacs-gpu` and a client
restart; the daemon needs neither. Diagnose with `strings` on the
binary (`Latin Modern Math`, `MathBox`) rather than by re-reading the
checkout, which was already current.
- **Main was integrated three times in one day** (`8c86d34`,
`46a1b8f`, `b889873`), merged not rebased to preserve review anchors.
Two conflicts, both this ledger and nothing else. **The dangerous
case was the one that did NOT conflict**: #166 auto-merged into
`pmacs-gpu/src/main.rs`, the file this lane rewrites, because the two
edits sat in different regions of it. Decide whether to integrate
from the shared-**file** set, never from whether git complained.
- **Integration was proved by test-count reconciliation**, not by a
green run: predict what the other side adds, then check the deltas.
GPU 199→202 matched `e547a90`'s 3; later lib 1,826→1,829 and CRDT
2,003→2,006 matched #166's 3, with GPU unchanged because #166 adds
none. Suites 91→92 was #161's new binary.
- **Why the branch had no CI for a day**: a conflicting PR builds no
merge ref, so no `pull_request` run is created. The ledger previously
recorded this cause as unidentified; it is not. Check `mergeable` and
confirm a run exists for the current head SHA.
- **`m4_5_basedpyright` has no timeout and hangs forever**, parking a
`--workspace` sweep (observed 2h26m at 38 of 92 suites). It is
**intermittent**, so an earlier clean sweep proves nothing. Sweep with
`cargo test --workspace --no-fail-fast -- --skip basedpyright` and
judge progress by whether the suite count advances.
- Named v0 approximations: the peer-caret half of acceptance 14 is
pinned at the mapping level, not pixels; a soft-wrapped spacer draws
its box whole at the first run's origin; the fit budget reads the
bundled code face even under a custom `set_font` family.
- **Bottom panel Stage 1 — MERGED as #155** (`main` @ `e745068`, - **Bottom panel Stage 1 — MERGED as #155** (`main` @ `e745068`,
2026-07-24, after two review rounds). Window placement, window 2026-07-24, after two review rounds). Window placement, window
parameters, TUI side windows, the divider, and the adopter `display` parameters, TUI side windows, the divider, and the adopter `display`

View File

@ -1,9 +1,11 @@
# Agent handoff — cross-machine continuity # Agent handoff — cross-machine continuity
**Last updated: 2026-07-25, after dired Stage 1 (#165) landed — the **Last updated: 2026-07-25, after the CRDT undo repro (#157) and the
directory view — following GPU terminal input (#166), multi-root LSP inline-math landed-doc refresh (#172) — following the bottom-panel
affinity (#161), the dired framing (#164), find-file (#162) — the dired landed-doc refresh (#156), the inline-math slice (#158), dired Stage 1
arc's Stage 0 — COHERENCE.md (#163), Lean 4 Stage 1 (#160), the (#165) — the directory view — the GPU terminal input fix (#166), Lean 4
Stage 2 (#161), the dired framing (#164), find-file (#162), the dired
arc's Stage 0, and COHERENCE.md (#163), Lean 4 Stage 1 (#160), the
minimap blank-slab fix (#159), bottom-panel Stage 1 (#155), the minimap blank-slab fix (#159), bottom-panel Stage 1 (#155), the
inline-math re-scout (#154), the vterm PTY-flake fix (#153), and the inline-math re-scout (#154), the vterm PTY-flake fix (#153), and the
GPU initial-target doc refresh (#152); and before that GPU GPU initial-target doc refresh (#152); and before that GPU
@ -28,11 +30,12 @@ commands, read `docs/active-work.md` immediately after this file.
## 1. Where the project stands (2026-07-25) ## 1. Where the project stands (2026-07-25)
- `main` @ `c8ec8f3` (dired Stage 1 #165 atop GPU terminal input #166, - `main` @ `ccf29e3` (the CRDT undo repro #157 atop the inline-math
multi-root LSP affinity #161, the dired framing #164, find-file #162, landed-doc refresh #172, the bottom-panel landed-doc refresh #156, the
COHERENCE.md #163, Lean 4 Stage 1 #160, minimap blank-slab #159, inline-math slice #158, dired Stage 1 #165, the GPU terminal input fix
bottom-panel Stage 1 #155, inline-math re-scout #154, vterm PTY-flake #166, Lean 4 Stage 2 #161, the dired framing #164, COHERENCE.md #163,
#153, and doc refresh #152). Protocol unchanged at **v20**. The bullets find-file #162, Lean 4 Stage 1 #160, minimap blank-slab #159,
bottom-panel Stage 1 #155). Protocol unchanged at **v20**. The bullets
below describe the arcs in their own terms; this line is the below describe the arcs in their own terms; this line is the
head-of-`main` anchor. head-of-`main` anchor.
- **`COHERENCE.md` is now required reading and a required framing input - **`COHERENCE.md` is now required reading and a required framing input
@ -43,6 +46,32 @@ commands, read `docs/active-work.md` immediately after this file.
interaction islands added, config-registry adoption, background-work interaction islands added, config-registry adoption, background-work
attribution. Its §2 grades the golden journey **broken at step 3** attribution. Its §2 grades the golden journey **broken at step 3**
(`pmacs .` exits 1). (`pmacs .` exits 1).
- **Inline math LANDED — #158** (`docs/inline-math-slice-framing.md` rev 3;
merge `5aa9044`). pmacs renders `$…$` as typeset mathematics in the GPU
frontend. **No protocol change (still v20); the whole slice lives in
`pmacs-gpu`**, because `pmacs-gpu` depends only on `pmacs-protocol` and
never on `pmacs` — a core-crate parser would have been unreachable from
where rendering happens.
- `math_parse.rs``math_layout.rs` → a `ChunkSource::MathBox` spacer
chunk → per-glyph mini-buffers drawn at the shaped line's real
baseline, with fraction rules as quads. Font is bundled Latin Modern
Math (~717 KiB) under the **GUST Font License** — not OFL.
- **The v0 subset is narrow and deliberately so**: Greek (34 entries),
sub/superscript, and `\frac`. Everything else — including relations
like `\geq`, fences, big operators, and all display math (`$$…$$`,
`\[…\]`) — is a named deferral, and an unsupported command degrades
the **whole span** back to source rather than rendering partially.
In a real paper most inline spans still show source; that is the
designed behaviour, not a defect.
- **Math is suppressed while the caret is inside its span**, so editing
always sees source. That gate reads the effective caret plus
selection endpoints and is fed by three separate refresh triggers;
it is the most delicate part of the slice.
- Selection and search washes cover the whole box rectangle, not
sub-ranges (sub-range washes are deferred).
- TUI shows the LaTeX source unchanged. That divergence is recorded
against `COHERENCE.md` §16, which audits the "no privileged
frontend" rule.
- **find-file LANDED — #162** (`docs/dired-framing.md` §10, Q#DR11; merge - **find-file LANDED — #162** (`docs/dired-framing.md` §10, Q#DR11; merge
`2af1ab3`; one review round). `C-x C-f` is the dired arc's **Stage 0**: `2af1ab3`; one review round). `C-x C-f` is the dired arc's **Stage 0**:
pmacs previously had no discoverable way to open a file by path — no pmacs previously had no discoverable way to open a file by path — no

View File

@ -237,7 +237,7 @@ candidates, ranked:
| Font | Table quality | License | Notes | | Font | Table quality | License | Notes |
|------|--------------|---------|-------| |------|--------------|---------|-------|
| **Latin Modern Math** | Full | OFL (GUST) | Reference, ships with TeX Live, most widely tested | | **Latin Modern Math** | Full | **GUST Font License** (not OFL) | Reference, ships with TeX Live, most widely tested; ~717 KiB. Bundled by #158 as `pmacs-gpu/fonts/latinmodern-math.otf` |
| **STIX Two Math** | Full | OFL | Broader Unicode coverage | | **STIX Two Math** | Full | OFL | Broader Unicode coverage |
| **Cambria Math** | Full | Proprietary | Ships with Office; unavailable on Linux | | **Cambria Math** | Full | Proprietary | Ships with Office; unavailable on Linux |
| **Libertinus Math** | Full | OFL | Derivative of Latin Modern, wider | | **Libertinus Math** | Full | OFL | Derivative of Latin Modern, wider |

View File

@ -2790,6 +2790,88 @@ mod tests {
} }
} }
/// Deterministic reduction of a `rope_matches_crdt_projection_
/// after_arbitrary_edits` failure found by raising the case
/// count (`PROPTEST_CASES=2000`) on `main` @ `e745068`.
///
/// **What happens.** Replacing a byte range with *identical*
/// bytes is a textual no-op but a real CRDT operation (a delete
/// plus an insert). Undoing it therefore advances the CRDT
/// version while leaving the materialized text unchanged, so
/// `undo_crdt_mode` derives an EMPTY replacement edit — and
/// still attaches the `crdt_op` that `crdt.undo()` produced.
/// That trips the proptest's `crdt_op` shape invariant, "a
/// no-op edit must have `crdt_op = None`".
///
/// **What was verified about the consequences**, so the next
/// reader does not have to redo it:
///
/// * content stays correct — rope and CRDT projection agree
/// before and after (asserted below);
/// * replicas stay converged — both `crdt_op` consumers
/// (`EditorCore::queue_daemon_origin_crdt_op` and the remote-op
/// path) read `edit.crdt_op` unconditionally and do **not**
/// short-circuit on an empty range, so the op is broadcast;
/// * the cursor does not jump — `EditorCore::undo` only clamps
/// to buffer length and never seeks `edit.range.start`.
///
/// **The open question** is therefore whether the *invariant* is
/// simply mis-scoped rather than the behavior being wrong. It
/// was written for the FORWARD `apply_edit` short-circuit, which
/// returns before ever producing an op; CRDT-mode undo/redo
/// never reach that path. One artifact is genuinely arbitrary
/// either way: `derive_replacement_edit` reports the empty range
/// at the buffer END rather than at the edit site.
///
/// Ignored, not deleted: it documents a real, reproducible
/// asymmetry that nothing else on `main` records, and un-ignoring
/// it is the first step of whichever resolution wins.
#[test]
#[ignore = "known pre-existing main behavior; see the doc comment \
for the verified consequences and the open question"]
fn crdt_undo_of_an_identity_replace_reports_a_no_op_edit_carrying_an_op() {
let mut buffer =
Buffer::new_with_crdt(BufferId::next(), "*identity-undo*", 1).expect("crdt");
buffer
.apply_edit(EditOp::Insert {
pos: 0,
bytes: b"hello",
})
.expect("seed insert");
// Replace one byte with the SAME byte.
let forward = buffer
.apply_edit(EditOp::Replace {
range: Range::new(1, 2),
bytes: b"e",
})
.expect("identity replace");
assert_eq!(forward.range, Range::new(1, 2));
assert_eq!(forward.inserted_len, 1);
let undone = buffer.undo().expect("undo");
assert!(
undone.range.is_empty() && undone.inserted_len == 0,
"the undo produced no textual change: {:?}/{}",
undone.range,
undone.inserted_len
);
assert!(
undone.crdt_op.is_some(),
"…yet it carries a version-advancing CRDT op — the invariant \
the proptest trips on"
);
// Content is unharmed in both projections.
let mut bytes = vec![0u8; buffer.len() as usize];
buffer.snapshot_rope().slice(0, buffer.len(), &mut bytes);
assert_eq!(String::from_utf8(bytes).expect("utf8"), "hello");
assert_eq!(
buffer.crdt_state().expect("crdt").materialize_string(),
"hello"
);
}
proptest! { proptest! {
// Smaller proptest case count than the default (64) to keep // Smaller proptest case count than the default (64) to keep
// CI overhead modest; the per-op invariant check is the // CI overhead modest; the per-op invariant check is the