From 870209cca9775b9933f6acafd3401aa017e7ba78 Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Fri, 24 Jul 2026 21:34:08 -0400 Subject: [PATCH 1/2] test(buffer): capture the CRDT undo no-op-with-op case MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Found during #155's sweep and recorded there as one line of prose with an uncommitted proptest seed. This turns it into something that cannot be lost and that states what is actually known. Reproduced on main @ e745068 with PROPTEST_CASES=2000 against rope_matches_crdt_projection_after_arbitrary_edits, then reduced by hand to five lines: replacing a byte range with IDENTICAL bytes is a textual no-op but a real CRDT operation, so undoing it advances the CRDT version while leaving the materialized text unchanged. `undo_crdt_mode` derives an empty replacement edit and still attaches the op `crdt.undo()` produced, which trips the proptest's "a no-op edit must have crdt_op = None" invariant. The test is `#[ignore]`d rather than asserted-as-correct or left as a seed, because the resolution is a judgement call I should not make silently: - the raw proptest seed is NOT committed, since it would fail the suite on every run for a case whose correct outcome is undecided; - the deterministic reduction reproduces without any seed, so nothing is lost by leaving the seed out. What the doc comment records, so the next reader need not redo it: content stays correct (rope and CRDT projection agree either side); both `crdt_op` consumers read the field unconditionally and do not short-circuit on an empty range, so replicas still converge; and `EditorCore::undo` never seeks `edit.range.start`, so no cursor jumps. The open question is whether the invariant is simply mis-scoped — it was written for the forward `apply_edit` short-circuit, which returns before producing an op, and CRDT-mode undo/redo never reach that path. One artifact is arbitrary either way: `derive_replacement_edit` reports the empty range at the buffer END rather than at the edit site. Co-Authored-By: Claude Opus 5 (1M context) --- src/buffer.rs | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/src/buffer.rs b/src/buffer.rs index 304918b..7f048d5 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -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! { // Smaller proptest case count than the default (64) to keep // CI overhead modest; the per-op invariant check is the From adf10cc9c3a0304267f19035e4ae9147c5975e57 Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Sat, 25 Jul 2026 18:16:31 -0400 Subject: [PATCH 2/2] docs: record the inline-math slice (#158) as landed Brings the three required docs current after #158 merged, and discharges the follow-up that framing named for itself. COHERENCE.md section 16 audits the claim that the GPU frontend exceeds the TUI "under real divergence pressure" without a privileged frontend emerging. Inline math is the sharpest instance of that so far -- the GPU typesets $...$ while the TUI shows LaTeX source, and the TUI fallback is a named deferral. Section 25 makes that update ride the PR, so the enumerated list gains the case along with what keeps it inside the rule: the slice reserves no protocol version and adds no wire surface, so the divergence is presentational and both frontends read the same model. docs/inline-math-framing.md carried a licence error the slice framing flagged in its own section 9 and deliberately did not fix in-branch, since the parent is a merged document. Latin Modern Math is under the GUST Font License, not the OFL; the row now says so and records the ~717 KiB bundled size. docs/agent-handoff.md records the landing and re-anchors section 1 to d152120. The bullet leads with the facts a fresh agent would otherwise have to rediscover: the whole slice lives in pmacs-gpu because pmacs-gpu depends only on pmacs-protocol and never on pmacs; the v0 subset is 34 Greek symbols, sub/superscript and \frac; an unsupported command fails the WHOLE span back to source, so most inline spans in a real paper still show LaTeX by design; and math is suppressed while the caret is inside its span. docs/active-work.md removes the merged lane per its own update protocol and adds a Closed entry. Four things there are reusable beyond this arc: a stale frontend binary is invisible from the source tree, so diagnose with strings on the binary rather than by re-reading a checkout that is already current; the dangerous integration was the one that did NOT conflict, so decide from the shared-file set rather than from whether git complained; integration is proved by predicting the other side's test-count delta and checking it; and m4_5_basedpyright has no timeout, hangs forever, and is intermittent, so an earlier clean sweep proves nothing. It also corrects a claim I recorded on main: the branch's missing CI was not an unidentified cause -- a conflicting PR builds no merge ref, so no pull_request run is created. Docs only; no code changes. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HZjWMjwPXhPbt9upku9mCk --- COHERENCE.md | 13 ++- docs/active-work.md | 180 +++++++++--------------------------- docs/agent-handoff.md | 43 +++++++-- docs/inline-math-framing.md | 2 +- 4 files changed, 93 insertions(+), 145 deletions(-) diff --git a/COHERENCE.md b/COHERENCE.md index 95761b2..9fe85f0 100644 --- a/COHERENCE.md +++ b/COHERENCE.md @@ -1272,9 +1272,16 @@ its asks are already practiced.** exclusive by construction; every other per-frontend-kind pair in the dispatcher remains two adjacent `if`s that a reader must notice are alternatives. -- The GPU frontend exceeds the TUI (minimap, squiggles, typography) - without the TUI losing the model — the "no privileged frontend" rule - is holding under real divergence pressure. +- The GPU frontend exceeds the TUI (minimap, squiggles, typography, + and since #158 rendered inline math) without the TUI losing the + 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 negotiation is per-bit rather than a first-class declared capability diff --git a/docs/active-work.md b/docs/active-work.md index 9a913e6..e031b4a 100644 --- a/docs/active-work.md +++ b/docs/active-work.md @@ -14,11 +14,11 @@ backlog. machine-local: `origin` may name this canonical URL, a release mirror, or something else, and therefore has no authority by name alone. - Canonical base at this snapshot: - `githubsucks/main` @ `8c86d34` (the dired framing #164 atop find-file - #162, COHERENCE.md #163, Lean 4 Stage 1 #160, 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 GPU initial-target doc refresh #152; protocol - v20). + `githubsucks/main` @ `d152120` (the bottom-panel landed-doc refresh #156 + atop the inline-math slice #158, dired Stage 1 #165, the GPU terminal + input fix #166, Lean 4 Stage 2 #161, the dired framing #164, + COHERENCE.md #163, find-file #162, Lean 4 Stage 1 #160, and the minimap + blank-slab fix #159; protocol v20). - On the transfer source, `origin/main` named a release mirror at `d3fa632` and lagged badly. On the current destination, `origin` names the canonical URL. This difference is why all recovery begins by @@ -52,137 +52,9 @@ git worktree list 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. -## 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 - ..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 5–11, 14–16; 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) - Stage 1 **merged as #160** (`main` @ `0827dd1`, 2026-07-25, one review @@ -599,6 +471,46 @@ git worktree add --track \ ## Closed since the last snapshot +- **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`, 2026-07-24, after two review rounds). Window placement, window parameters, TUI side windows, the divider, and the adopter `display` diff --git a/docs/agent-handoff.md b/docs/agent-handoff.md index 21352e9..d873063 100644 --- a/docs/agent-handoff.md +++ b/docs/agent-handoff.md @@ -1,7 +1,8 @@ # Agent handoff — cross-machine continuity -**Last updated: 2026-07-25, after find-file (#162) landed — the dired -arc's Stage 0 — following COHERENCE.md (#163), Lean 4 Stage 1 (#160), the +**Last updated: 2026-07-25, after the inline-math slice (#158) landed — +the first mathematical typesetting in pmacs — following 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 inline-math re-scout (#154), the vterm PTY-flake fix (#153), and the GPU initial-target doc refresh (#152); and before that GPU @@ -26,11 +27,13 @@ commands, read `docs/active-work.md` immediately after this file. ## 1. Where the project stands (2026-07-25) -- `main` @ `2af1ab3` (find-file #162 atop COHERENCE.md #163, Lean 4 Stage 1 - #160, minimap blank-slab #159, bottom-panel Stage 1 #155, inline-math - re-scout #154, vterm PTY-flake #153, and doc refresh #152). Protocol - unchanged at **v20**. The bullets below describe the arcs in their own - terms; this line is the head-of-`main` anchor. +- `main` @ `d152120` (the bottom-panel landed-doc refresh #156 atop the + inline-math slice #158, dired Stage 1 #165, the GPU terminal input fix + #166, Lean 4 Stage 2 #161, the dired framing #164, COHERENCE.md #163, + 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 + head-of-`main` anchor. - **`COHERENCE.md` is now required reading and a required framing input — #163.** It carries the product-coherence thesis, an audited scorecard, per-concern gaps, and §20's priority order, and it is the @@ -39,6 +42,32 @@ commands, read `docs/active-work.md` immediately after this file. interaction islands added, config-registry adoption, background-work attribution. Its §2 grades the golden journey **broken at step 3** (`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 `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 diff --git a/docs/inline-math-framing.md b/docs/inline-math-framing.md index 0b6bfb4..a2ce9c8 100644 --- a/docs/inline-math-framing.md +++ b/docs/inline-math-framing.md @@ -237,7 +237,7 @@ candidates, ranked: | 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 | | **Cambria Math** | Full | Proprietary | Ships with Office; unavailable on Linux | | **Libertinus Math** | Full | OFL | Derivative of Latin Modern, wider |