* docs: retire the long-lines lane, the QoL arc having closed at #223 Rule 4, applied in its stated order: durable facts to `docs/agent-handoff.md` FIRST, then the lane goes. The ledger is for volatile branch/checkpoint/recovery state, and none of that survives a merge worth keeping. What moved to the handoff, because it is architecture rather than bookkeeping: - The GPU is not a grid consumer, so it could not inherit `view_left`; `code_scroll_left` is its own, in pixels, local viewport state with no wire and no version bump. - The work was ONE transform and ONE clip, written before any consumer moved. glyphon honors `TextBounds` so the text layers clip themselves; the manual quad and squiggle renderers do not, and nothing needed them to before this stage because no code-relative x could be negative. - Washes crop rather than drop — the same boundary Stage 4's review caught the TUI painter getting wrong. - Both lifecycle resets are observed PRE-MOTION, because a later cursor motion repairs the offset anyway. - `pmacs_protocol:📜:follow_left`: the follow rule beside `classify`, called by both frontends. The approved exception to Stage 5's scope, recorded in the framing doc §1.2a. - The two witnesses that exist because mutation testing found the TESTS wrong rather than the code — the unwitnessed glyph layer, and the completion predicate a far-off-left test could not catch. "Test a boundary AT the boundary" is the transferable part. The `## Long lines (QoL arc)` block is removed whole; nothing else in the ledger referenced it. NOT REMOVED, and not an oversight: the QoL Stage 1 (#219) and Stage 2 (#220) blocks. Rule 4 would now reach them too — the arc they belong to is closed — but Stage 2 carries a live named follow-on, capability-aware keymap resolution (Q#Z3 option A, deliberately deferred), plus the "first eager state consumer" restore-seam finding. Those need re-homing before their blocks can go, which is a judgment beyond retiring the lane that was asked for. Flagged rather than done. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai * docs: complete the retirement — Stages 1 and 2, and the stale anchors The previous commit removed the long-lines lane and stopped short. Two things were still wrong. STALE ANCHORS. The handoff was dated 2026-08-06 with `main` at `db1bbe9` — five merges behind. Now 2026-08-08 at `9a26ac8`, with the arc's five merges named in the ancestry chain, and the same in active-work's canonical-base record and recovery floor (a tree at `db1bbe9` no longer passes; it would lack the whole arc). **The recovery path was re-exercised, not SHA-swapped.** That file warns that advancing the base is exactly when the commands are most likely to have rotted and that a swapped SHA reads identically to a verified one — so: fresh clone into an empty directory, `githubsucks` alias added, `git fetch --prune`, `9a26ac8` confirmed an ancestor of `githubsucks/main`, and a worktree recovered with the three-argument form. All four steps clean. STAGE 1 AND STAGE 2 BLOCKS. Keeping them left false live planning standing: "Stage 3 is long-line wrap/scroll, which is a design round: no horizontal viewport exists at all" — written before #221–#223 built one. A merged lane that still describes the future is worse than no lane. Re-homed first, per Rule 4's order: - **FG-INV is a CONSUMER contract**, and it lives on the protocol type because that is where consumer authors read it. It had been a doc comment on a PRIVATE PRODUCER FIELD, which is why the one consumer never honored it. - **Seven tests covered that flag and all seven tested the producer.** None asserted a consumer acts on it. "Add a test for the flag" had already been done — §5's enforcement/documentation drift in a second register. - **`install_state_dirs` is the eager-state-consumer seam.** Builtins and `init.lua` run before it, so `pmacs.state.read` at module load returns nothing, always. `saveplace` and `recentf` escape it only because both read lazily. Any future eager consumer belongs at the same seam. - **A GPU-only binding cannot be expressed**: `Scope` has no frontend identity and `FrontendEvent` no command-invocation variant. #220 shipped commands without bindings for that reason, not preference. - **Capability-aware keymap resolution** is now a named §6 backlog item: CROSS-CUTTING, NOT STARTED, needs its own framing. It says so explicitly, and says not to start it as a half-lane attached to another stage's branch — which is how it would arrive by accident. No implementation, no lane, no design. What is deliberately NOT preserved: the recovery commands for `full-grid-resync` and `gui-zoom`. Those branches are merged; a recovery command for a branch nobody should check out is the kind of stale instruction this ledger exists to avoid. The framing docs remain on disk as the historical record. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai * docs: the live schema range is v6..=v22, and #224 gets its own lane Two review findings against the retirement. THE SCHEMA RANGE. The canonical-base paragraph still said `v6..=v21`. The upper bound moved to **v22 at #221**, which added `InstanceMessage::LineWrapFacts` — so that line had been wrong for two merges, including the one this branch is retiring the lane for. Verified against `pmacs-protocol/src/message.rs` rather than carried forward: `SUPPORTED_PROTOCOL_VERSIONS` is `6..=22`, `PROTOCOL_VERSION` is 22, and `ADVERTISED_PROTOCOL_VERSION` is **20** and did not move. The paragraph now says so, and says the advertised constant must not be edited to chase the range — it is a permanent baseline, and the session version is settled one message later by the frontend's counter-offer. It also now states which claims it governs: historical `v21` statements elsewhere describe a stage as it landed and are correct there. Only this current-state paragraph tracks the live range, so only this one goes stale when the range moves. A LANE FOR #224. This file requires a lane for **every open PR**, and the PR that retires other lanes is not exempt. Added with the branch, the ref-not-a-SHA recovery command, the docs-only scope, and the verification — including that the recovery path was re-exercised rather than SHA-swapped, and that the full gate suite is deliberately not re-run for a change that cannot reach it. The entry is honest about its own lateness: it was written AFTER the PR existed, which is the standing correction from #171 and #215 being missed again, and it took review asking. Back-dating the block to look compliant would have destroyed the only evidence that the practice still slips. It carries its own retirement instruction — next absorption after #224 merges. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai * docs: scope the two provenance paragraphs that claimed to be current Both said "this line is the head-of-`main` anchor". Neither was, and a provenance note that claims currency is worse than a plainly historical one — it disagrees with the real anchor at the top of the file while looking authoritative. The bottom-panel paragraph keeps its `v6..=v21` facts, which are correct for the stage they describe. What changes is scope: "a current session" becomes "a session at that anchor", and the closing clause now says these statements describe the historical `6c9e765` anchor, with the live range pointed at "Repository authority" in `docs/active-work.md` (`v6..=v22` since #221; advertised baseline still v20). The counter-offer mechanism is called out as still current independent of which numbers it carries, since that part did not go stale. The second was MY inconsistency, introduced earlier on this branch. I rewrote that bullet's opening to "Beneath the QoL arc, at `db1bbe9`" and left its closing claiming to be the head-of-`main` anchor, so the bullet contradicted itself. It now names what it actually is — the `db1bbe9` ancestry chain — and points at the top of the file. Both cross-references name the file they point into. "Repository authority" is a section of `docs/active-work.md`, not of this one, and an unqualified "above" would resolve to nothing here. Noted, not acted on: the bottom-panel arc has its own currency drift in this file (§1 says Arc 7 COMPLETE at #213, two later paragraphs still call Stage 3 the remaining step). Pre-existing, unrelated to the QoL retirement, and not this PR's to fix. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .github/workflows | ||
| audit | ||
| builtin | ||
| docs | ||
| pmacs-gpu | ||
| pmacs-protocol | ||
| proptest-regressions | ||
| scripts | ||
| src | ||
| tests | ||
| .gitignore | ||
| AGENTS.md | ||
| CHANGELOG.md | ||
| CLAUDE.md | ||
| COHERENCE.md | ||
| Cargo.lock | ||
| Cargo.toml | ||
| LICENSE-APACHE | ||
| LICENSE-MIT | ||
| README.md | ||
| TEST_IMPROVEMENT.md | ||
| build.rs | ||
| rust-toolchain.toml | ||
| rustfmt.toml | ||
README.md
Pmacs
Parallel Emacs --- a Rust-cored, Lua-scripted editor in the Emacs tradition.
Pmacs runs the editor's hot path (rope, buffers, views, async runtime, process supervision) in Rust, and exposes the rest --- commands, keymaps, hooks, packages --- through an embedded Lua VM. The design follows Emacs in shape (configurable, introspectable, programmable from inside) but discards the single-threaded substrate; workers, message bus, and a coroutine-based async surface are core primitives, not bolt-ons.
The editor is partitioned into a long-lived instance (the daemon that owns buffers, processes, and language services) and thin frontends that attach over a typed protocol (currently v20). Two frontends ship today:
- a TUI (crossterm cell grid), attachable locally over a Unix
socket or remotely over SSH, with reconnect-on-drop modeled on
mosh; and - pmacs-gpu, a GPU frontend (wgpu + winit + glyphon) that renders from a semantic projection of editor state --- style spans, decorations, inlay adornments --- rather than a character grid, and edits optimistically against a local CRDT replica for latency-free typing.
Buffers are optionally CRDT-backed (loro, behind --features crdt),
so multiple frontends --- TUI and GPU, local and remote --- can edit
the same buffers concurrently with live cursor/selection presence.
Status
v1.1.0 --- stable core, active development, and the first release with prebuilt binaries. The v1.0 gate (the instance/frontend partition, the Lua surface, and a REPL package audited to use zero direct Rust core access) shipped some time ago. Development since has expanded the semantic frontend protocol from v6 through v21, brought the GPU frontend near input/render parity with the TUI, and completed the LSP, editing, persistence, themes, and terminal arcs. Recent work added major modes and modeline detection, a typed configuration registry, composable statuslines, multi-language syntax processing, cross-frontend tab-width parity, a directory browser, a describe/list command family, and a bottom panel on both frontends.
Current direction lives in COHERENCE.md (the product-coherence thesis
and its audited priority order) and docs/agent-handoff.md (durable
project state). docs/roadmap-2026-07.md is a historical planning
snapshot and is no longer the authority.
Public contributions are open: use, evaluate, file issues, and send pull requests.
Highlights
Editing & UI. CUA-style region editing plus Emacs kill/yank and kill-ring
bindings; linear undo/redo; query-replace; incremental substring and regex
search (C-s / C-r / C-M-s); comment, auto-indent, auto-pair, transpose,
case, line, and region operations; line-number gutter with absolute, relative,
and hybrid modes; diagnostic signs; context menu; OS clipboard integration
(OSC 52 in the TUI, native in the GPU); minibuffer completion with persisted
history; buffer-list and compilation modes; self-navigable help. Named ui.*
theme faces, live GPU font selection, and composable per-window statusline
providers keep chrome and modelines runtime-configurable. Saves are atomic
(temp + rename + parent fsync, mode-preserving).
Language intelligence. The async LSP client provides diagnostics,
rename with prepareRename, cross-file definitions, hover, signature help,
references, document symbols, code actions, formatting, semantic tokens, and
inline inlay hints. Preconfigured servers cover Rust, C/C++, Python, Go,
JavaScript/TypeScript, Lua, Bash, TOML, Zig, Dockerfile, CMake, JSON, and YAML.
Bundled tree-sitter grammars include those languages plus Markdown, Make, and
CUDA; nested Markdown fences and frontmatter use multi-language injections,
and locals-query processing distinguishes shadowed builtins. Bounded Emacs
and Vim modelines join extensions, exact filenames, and shebangs in one
fresh-load language decision. That decision initializes the buffer's major
mode, drives syntax/LSP/pairing/comment behavior, and enables mode-scoped
keymaps. A persistent project-symbol index (.pmacs/index.json) rides the
same worker infrastructure.
Collaboration & frontends. With --features crdt, buffers are CRDT-backed
and any number of frontends attach to one daemon and edit concurrently; peers
see each other's cursors and selections as translucent washes. The TUI and GPU
frontends both host owned full-screen terminal sessions; protocol-v19 terminal
frames preserve the fixed-cell VT screen while each frontend owns its
scroll/selection/input context. The GPU frontend also provides a live minimap,
wavy diagnostic squiggles, a status band, and optimistic local editing that
rebases in-flight edits through authoritative frames. Buffer text, syntax,
diagnostics, carets, hits, and minimap geometry now share one eight-column tab
projection without mutating source bytes.
Extensibility. The pmacs.* Lua namespaces cover buffers, windows,
commands, global/mode/buffer keymaps, hooks, themes, statusline providers,
tree-sitter, LSP stores, async workers, and a PTY-aware process supervisor.
The typed, introspectable pmacs.config registry supports global and
buffer-local values, listeners, startup-only settings, and describe-setting.
A package manager installs from git (github:owner/repo,
version/branch/commit pins) with transitive dependency resolution and a
SHA-256 lockfile. Pmacs is also an MCP client: packages can spawn MCP
servers and consume their tools, resources, and prompts --- AI integrations
are packages over a transport, not a built-in feature. The bundled REPL
package is written entirely against the public Lua API.
Running
Single-process TUI:
pmacs [FILE] # TUI; -nw reserved for when a GUI default lands
GPU frontend (one command; the root binary starts or reuses the daemon):
pmacs --gpu # default instance; no initial file
pmacs --gpu README.md # default instance; open one file
pmacs --gpu --socket NAME FILE # named instance; bare NAME →
# <runtime>/pmacs/NAME.sock
pmacs --gpu -- --leading-dash # `--` ends option parsing
pmacs --gpu requires the root pmacs binary to be built with the
crdt feature. It discovers a sibling pmacs-gpu binary first, then
falls back to pmacs-gpu on PATH. When FILE is present, the daemon
loads or creates it and completes startup hooks before the GPU window
appears. Closing the window detaches only that frontend; the daemon
remains available for later GPU or TUI attaches.
Daemon + attached TUI frontends:
pmacs --daemon --socket NAME # foreground daemon
pmacs --attach --socket NAME # TUI frontend; F12 detaches
pmacs --attach user@host # remote TUI over SSH
For debugging an already-running daemon, the low-level GPU command stays available and never auto-starts or replaces anything:
pmacs-gpu --attach /absolute/path/to/pmacs.sock
pmacs --attach also understands ssh:user@host/instance,
local:/path.sock, and bare hostnames (treated as SSH). See
pmacs --help for the full matrix.
User configuration is plain Lua at
$XDG_CONFIG_HOME/pmacs/init.lua (default ~/.config/pmacs/init.lua),
loaded after the builtin runtime so plain assignments override
defaults --- keybindings, pmacs.lsp.config, theme overrides, and
package installs all live there.
Install
Download an archive from the
releases page, unpack
it, and put both binaries somewhere on your PATH.
Keep pmacs and pmacs-gpu together. pmacs --gpu looks for
pmacs-gpu beside itself first and only then falls back to a PATH
lookup, so an unpacked release is self-contained as long as the two
stay in the same directory.
Verify a download:
sha256sum -c SHA256SUMS --ignore-missing
| platform | built on | notes |
|---|---|---|
| Linux x86_64 | Ubuntu 22.04 | requires glibc ≥ 2.35 — Ubuntu 22.04+, Debian 12+. RHEL 9 (glibc 2.34) is not supported yet. |
| macOS arm64 | macOS 15 | Apple Silicon only; Intel is not built yet. Binaries are unsigned and not notarized, so Gatekeeper will quarantine them until you allow them explicitly. |
Releases carry binaries only — there is no in-place update, rollback, or package-manager distribution yet. Build from source for any platform not listed, and see "Runtime dependencies" below for what the editor assumes is present.
Build
Builds on the toolchain pinned in rust-toolchain.toml (Rust
1.95.0, edition 2024); rustup selects it automatically.
# Coherent root + GPU release build. The package-qualified feature keeps
# the separate pmacs-gpu package feature-free while enabling CRDT in pmacs.
cargo build --release --workspace --features pmacs/crdt
target/release/pmacs --gpu README.md # one-command managed GPU file launch
cargo run --release -- --version # default-run selects the pmacs binary
cargo test --workspace # unit + integration tests (all crates)
cargo fmt --check
cargo clippy --workspace --all-targets -- -D warnings # incl. pmacs-gpu
Feature matrix
Cargo features fall into two independent axes. Do not use
--all-features — it enables both Lua flavors at once, which cannot
build (see below).
| Feature | Axis | Notes |
|---|---|---|
luajit |
Lua flavor | Default. LuaJIT backend via mlua (vendored). |
lua54 |
Lua flavor | Lua 5.4 fallback for hosts without LuaJIT (big-endian, …). |
crdt |
Buffer | Opt-in CRDT-backed buffer mode (adds the loro dep). v1.0 builds enable it; orthogonal to the flavor. |
Exactly one Lua flavor must be enabled — luajit or lua54, never
both (and never neither). They map to mlua's mutually-exclusive Lua
backends, so --all-features (or --features luajit,lua54, or
--no-default-features with no flavor) fails in the mlua-sys build
script with "You can enable only one of the features: …". That check
lives in a dependency cargo builds first, so pmacs can't replace it with a
friendlier error — the fix is to build a specific flavor. Supported build
lines:
cargo build --release # luajit (default)
cargo build --release --no-default-features --features lua54
cargo build --release --features crdt # luajit + crdt
cargo build --release --no-default-features --features lua54,crdt
CI, cargo hack, and distro tooling should iterate the flavors
explicitly (--no-default-features --features <flavor>[,crdt]) rather
than reaching for --all-features. Both flavors pass the full test suite;
CI runs the matrix on every push.
Release-only perf gates (M5 keystroke-to-render, M6 ingest/RSS/cancel
and scrollback navigation/search) are #[ignore]'d during normal
test runs and exercised in CI under dedicated jobs. The GPU frontend
has headless render tests (offscreen wgpu, pixels read back) that run
in CI under lavapipe and skip gracefully on machines without a Vulkan
adapter (PMACS_REQUIRE_GPU=1 turns a missing adapter into a hard
failure).
Runtime requirements
The pmacs binary depends on a small set of POSIX command-line tools
at runtime. The dependency exists because the project enforces
#![forbid(unsafe_code)] everywhere, including in tests; calls that
would otherwise need unsafe (PTY raw-mode setup, signal name
translation) are routed through trampolines that exec these tools.
-
/bin/sh(POSIX shell). Used for the PTY raw-mode trampoline:/bin/sh -c 'stty raw -echo </dev/tty 2>/dev/null; exec "$@"' --configures the controlling TTY's line discipline before exec'ing the actual subprocess. Required by the REPL package and any other caller that spawns a process in raw PTY mode. -
stty(coreutils). The line-discipline configurator invoked by the trampoline above. -
coreutilsmore broadly. The M6 process-supervisor tests spawncat,yes, andwhich; absent these the test suite (not the editor itself) degrades.whichis also used by the M6.5 shell-locator helper to findbash/zsh/fishfor per-shell integration tests. The M7.2 fetcher's timeout test usessleep. -
setsid(util-linux, Linux only, optional). The process teardown-deadlock test usessetsid --forkto orphan a grandchild, which is the only way to reproduce that deadlock without depending on shell&semantics (they differ betweenbashanddash). The test skips whensetsidis absent, so a minimal or BusyBox environment still runscargo test --lib; setPMACS_REQUIRE_SETSID=1to make that skip a failure, as CI does on Linux. -
/bin/bash(optional, Linux only). The signal diagnostic's job-control corroboration test needs a terminal whose foreground process group is not the spawned leader, whichbash -mproduces by running a foreground job in its own process group. The path matters: the test spawns/bin/bashdirectly rather than resolvingbashonPATH, and skips when that path is absent. SetPMACS_REQUIRE_BASH=1to make the skip a failure, as CI does on Linux.It is deliberately not armed on macOS, which ships bash 3.2 but where a non-interactive
bash -mwas measured in CI to keep the terminal on the leader — so the divergence the test needs never happens there. The divergent case is pinned on every platform by injecting the foreground group instead. -
git(added in M7.2). Required for any package operation: the package fetcher shells out togitto clone, fetch, and resolve refs, with a deterministic environment (GIT_TERMINAL_PROMPT=0,GIT_CONFIG_NOSYSTEM=1,LC_ALL=C, inheritedGIT_*variables stripped). Authentication for private repositories rides the user's existing git configuration (credential helpers, SSH agent), so packagers do not need a separate auth story. Pre-M7 builds without package operations do not need git. -
tar(added in M7.3). Required forpmacs.packages.install: the installer materializes a snapshot viagit archive --format=tarpiped intotar -x -C <dest>, which keeps the on-disk install directory self-contained (no.gitlinkage back to the bare cache, no working-tree state). GNU tar and bsdtar both work. Pre-M7 builds and any path that doesn't callpmacs.packages.install{...}do not need tar.
Distribution packagers should ensure these are runtime dependencies
of the pmacs package. On a typical Linux distribution, busybox or
GNU coreutils plus a shell of any kind satisfies the requirement; on
macOS the system shell and /usr/bin/stty are both standard.
The Lua VM (LuaJIT or Lua 5.4) is statically vendored via mlua's
vendored feature, so there is no external Lua dependency at
runtime.
The GPU frontend additionally needs a Vulkan-capable driver stack (any real GPU driver, or lavapipe for software rendering); its font (JetBrains Mono, OFL-licensed) is bundled into the binary.
Layout
The workspace has three first-party crates:
src/ pmacs — the core + TUI + daemon
rope.rs persistent byte-sequence backing every buffer
buffer.rs buffer + view chain + undo/redo
editor_core.rs cursor + commands + edit dispatch
crdt.rs loro-backed CRDT buffer state (feature `crdt`)
daemon.rs instance side of the frontend partition
attach.rs frontend side; transports + reconnect
semantic_render.rs semantic-frame producer (StyleSpans, Decorations, …)
lsp.rs language-server client
diag.rs, highlight.rs diagnostic + syntax/semantic-token rendering
syntax.rs tree-sitter integration
search.rs incremental search (substring + regex)
minibuffer.rs prompt, completion, persisted history
menu.rs context-menu model
file_io.rs atomic saves + external-modification detection
async_runtime.rs worker pool + message bus
process.rs PTY-aware process supervisor
ansi.rs ECMA-48 parser
project.rs, project_index.rs project detection + symbol index
packages/ resolver, fetcher, installer, lockfile, loader
mcp.rs MCP client (packages speak to MCP servers)
lua_bindings/ pmacs.* Lua surface installers
text_view.rs cell-grid renderer
frontend.rs crossterm TUI
main.rs entry point (TUI / daemon / attach modes)
pmacs-protocol/ wire types + framing codec shared by all frontends
pmacs-gpu/ the GPU frontend (wgpu + winit + glyphon)
builtin/ Lua runtime shipped with the binary
commands/default.lua named commands for every editor primitive
keymaps/default.lua default key bindings
hooks/default.lua built-in hook definitions
menus/default.lua context-menu items
runtime/ async, lsp, syntax, mcp, fs runtimes
packages/repl/ the bundled REPL package
docs/ design notes, framing docs, and the roadmap
tests/ integration tests (acceptance gates per milestone)
License
Dual-licensed under either of:
- MIT License (LICENSE-MIT)
- Apache License, Version 2.0 (LICENSE-APACHE)
at your option.