The mechanical half, riding on the census in the previous commit.
* 342 in-process construction sites in 65 files now take
`new_with_roots` / `open_with_roots` with `iso::roots()`. The isolated
base is a pure function of `CARGO_TARGET_TMPDIR` — no counter, no
`OnceLock` — so two copies of the module in one binary agree instead of
racing, and the tree lives somewhere `cargo clean` owns rather than
leaking into `/tmp` once per run. It is shared deliberately:
materialization is content-gated and idempotent, so a per-test
directory would repeat it ~330 times per run for a byte-identical
result.
* `journey_acceptance` keeps the ambient `EditorState::open`, because
proving the production entry point has a caller is the whole of what
that ratchet is for. Rev 2's "isolated by the environment its binary is
launched with" was not a mechanism — cargo launches each test binary
with the caller's environment, and a binary cannot re-point its own
roots before its tests run. Each test is now a thin parent that
re-execs this binary for its own name with controlled roots, and the
child runs the body against production's call. Two pins guard it: the
child asserts all four roots resolve inside the controlled base, and
the suite asserts against its own source that it has not quietly taken
the seam. The parent also asserts the child ran `1 passed` — a stale
`--exact` filter would otherwise hollow the whole thing out silently.
* The shared spawners take all five storage variables.
`spawn_daemon_process_with_env` set `HOME` and `XDG_CONFIG_HOME` only;
`HOME` is a FALLBACK, so it isolates a root only while the matching
`XDG_*` is unset — the harness's apparent adequacy was a property of
one developer's environment. The PTY spawner backfills whichever of the
five its caller did not pin. The 10 direct `Command::new` daemon and
attach spawns get the same treatment.
Three suites had `mod common;` behind `#[cfg(feature = "crdt")]`;
`common::iso` is needed in every build, so those are ungated. Files that
already pull in `common` reach `iso` through a `use` rather than a second
`#[path]` declaration — loading one file as two modules is
`clippy::duplicate_mod`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lv428Fth9LRtffwJSsqH7T
Round 2 correctly found the Finding-2/3 fixes were unpinned (reverting
them left the suite green). Both are now bite-verified:
- **Kill-path purge (Finding 2).** Replaced the direct
`forget_buffer(id)` unit test with
`killing_a_buffer_through_the_real_path_purges_its_fold_store`, which
drives `pmacs.buffer.remove` — the production route through
`after_buffer_removed` — and asserts the store is gone via the dead id
(BufferIds never recycle). Mirrors config_registry's real-kill-path
test. Bite-verified: reverting the `after_buffer_removed` fold branch
turns it red.
- **close-all point move (Finding 3).** Added
`close_all_command_moves_point_to_enclosing_head`, which invokes the
`fold.close-all` command with the point inside the second of two
top-level fns and asserts the cursor landed on that fn's head-line
content end (and both folds exist). Bite-verified: reverting close_all's
`maybe_move_point` loop turns it red.
- Ledger: `docs/active-work.md` folding lane now records PR #142 OPEN +
the two landed review rounds (was "opens once the gate suite is green").
Correction to the round-1 gate report: the acceptance suite is **21**
tests (round 1 was 20, not 24 — a tally slip), green under default and
`--features crdt`. Full gate suite otherwise green (fmt, clippy
--workspace --all-targets, git diff --check).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q5BkezMppbpCgGAYk2ftxV
- **Finding 1 (bug):** a delete starting exactly at a fold's `end`
removed the `\n` that `end` names — the last hidden line's terminator —
but the strictly-after arm (`os >= e`) kept the fold, leaving a mid-line
end. `translate`'s after-arm is now `os > e || (os == e && old_len == 0)`
so a pure insert at `e` still stays outside while a delete at `e` falls
to the drop arm, symmetric with the head side. New unit test
`delete_starting_at_tail_boundary_drops_fold` (bite-verified).
- **Finding 2:** `pmacs.buffer.kill` didn't clean the fold registry.
Added `FoldRegistry::forget_buffer(id)` (id-keyed; the view died with the
buffer) and wired it into `after_buffer_removed`, mirroring the
keymap/config cleanup; the registry is now stashed as Lua app-data.
`forget(&mut Buffer)` is clarified as the revert/reload reset.
- **Finding 3:** `fold.close-all` now moves the invoking point to the head
when it closes a fold around it (Q#FD3); the data-API `fold` exemption
(programmatic, no invoking point) is named in the module doc.
- Nits: dropped the dead `!(both empty)` conjunct in `fold_state_msg`;
replaced the trivial fresh-registry assert; added coverage for the
stale-tree refuse via a fold command, the read-only-buffer rejection
(Q#FD11), and unfold normalizing an arbitrary range.
Gates green: fmt, clippy --workspace --all-targets, --lib (1786),
--features crdt (1962), folding_acceptance (24), m4 (skip basedpyright),
required-GPU, git diff --check.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q5BkezMppbpCgGAYk2ftxV
The fold engine behind `docs/folding-framing.md` (approved rev 5): a
per-buffer fold store, a structural tree-sitter fold source, the
state-aware Lua command + data-API surface with the Emacs hideshow
`C-c @` bindings, the dispatch-layer pre-edit unfold, and `FoldState`
production. No rendering — Stages 2 (grid) and 3 (GPU) consume the store.
- `src/fold.rs`: `FoldStore` (a buffer-attached `View` that translates
ranges on every edit and drops any whose head/tail the edit crosses,
provenance-blind — Q#FD6), the structural source (nearest block-like
node >= 2 source lines -> introducer<->body -> **derived head line**,
the line immediately above the first hidden line, so wrapped signatures
and `where` clauses stay visible per R3-1 -> **closer-aware tail**, a
closing-delimiter line stays visible per R2-5), injection-layer walk,
`(start, end]` containment, and the state-aware ops (close innermost
open / open outermost closed / org-TAB cycle). Stale/absent tree
refuses (Q#FD10).
- `src/lua_bindings/fold.rs`: `pmacs.fold.*` — explicit-buffer data API
(`fold`/`unfold`/`folds`/`toggle`) + interactive helpers, validation
(Q#FD11: document buffer, UTF-8 boundaries, >= 1 hidden line — Q#FD9
falls out of the last clause), point-moves-to-head (Q#FD3).
- `builtin/runtime/fold.lua`: `fold.toggle/close/open/close-all/open-all`
commands + the `C-c @` prefix set (Q#FD4).
- `src/editor_core.rs`: the six point-anchored edit primitives run the
pre-edit unfold keyed on the authenticated source's point (Q#FD5,
command path); `EditorCore` owns the shared `FoldRegistry`.
- `src/semantic_render.rs`: the `FoldState` producer —
authoritative-empty, diff-suppressed, baseline resets on
`BufferSnapshot` (Q#FD8); the "never emitted" pin split so
`BlockAdornments` stays unproduced.
- `tests/folding_acceptance.rs` (16) over real Rust/Python/markdown
grammars + `fold_state_producer_transitions` + 15 engine unit tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q5BkezMppbpCgGAYk2ftxV