Commit Graph

18 Commits

Author SHA1 Message Date
Levi Neuwirth bcec61e020 feat(highlight): grammars for python/go/typescript/javascript/toml/zig
These five languages already had LSP configs (basedpyright, gopls,
tsserver, taplo, zls) but shipped no tree-sitter grammar, so they
rendered with no lexical color. Fill the gap — 8 BUILTIN_LANGUAGES
entries across 6 crates:

- python (`tree-sitter-python`, root `module`), go (`tree-sitter-go`),
  toml (`tree-sitter-toml-ng`), zig (`tree-sitter-zig`, +`.zon`) — each a
  single self-contained highlights query.
- JavaScript/TypeScript family: `tree-sitter-javascript` parses both
  `.js` and `.jsx`; `tree-sitter-typescript` ships two grammars
  (LANGUAGE_TYPESCRIPT, LANGUAGE_TSX). The four entries — javascript,
  javascriptreact, typescript, typescriptreact — mirror the LSP filetype
  map so tsserver enables the JSX parser. Highlights inherit: the TS
  query is a ~5-capture delta over JavaScript and JSX is a further
  delta, so the entries compose base-first (js → jsx → ts), the same
  pattern as `cuda` over C/C++ (typescript resolves ~22 capture classes,
  typescriptreact ~24).

Each grammar's name equals its existing `pmacs.lsp.config.<name>` key,
so grammar detection (which wins over the filetype map) resolves the id
the server keys off — the file now gets BOTH highlighting and the right
server. No lsp.lua change needed. All crates ride `tree-sitter-language
0.1` with tree-sitter dev-only — no second core in the graph.

Bite-verified acceptance:
- gap_grammars_load_and_parse — each grammar's ABI accepted by the 0.26
  core; a snippet parses without error at its root (covers both TS
  grammars, incl. JSX).
- typescript_highlights_compose_the_javascript_base — the compiled
  typescript/typescriptreact queries resolve >= 15 captures, not just the
  ~5-capture TS delta (the JS base is really composed in).
- builtin_languages_include_gap_grammars /
  gap_grammar_extensions_resolve — entry presence + extension detection
  across all 8 ids.
- m4_gap_grammars_align_with_lsp_configs — through the loaded runtime,
  each path's grammar id matches an existing LSP config. Bite-verified
  against pre-feature src/syntax.rs.

Ripple: two #116 shebang tests used python as their "has-LSP-but-no-
grammar" example, which this PR invalidates. Updated both — the .py +
`#!/bin/sh` precedence test now asserts a python grammar tree (not "no
tree"), and the grammarless-language-is-silent gate test switches to
`ruby` (genuinely grammarless) via a test-local shebang mapping.

Gates: fmt; clippy -D warnings; test --lib; --features crdt;
m4_acceptance --skip basedpyright; GPU; full workspace sweep;
git diff --check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJ9FQ832QwftJXCD9LeFan
2026-07-14 17:29:54 +01:00
Levi Neuwirth 7646dda583 feat(highlight): filename detection + Dockerfile/Make/CMake grammars
Files identified by their whole basename — Dockerfile, Makefile,
CMakeLists.txt, rc dotfiles — had no detection path (extension-, then
shebang-keyed). Add a filename layer and the three grammars behind it.

- **Grammars** (BUILTIN_LANGUAGES): dockerfile via `tree-sitter-containerfile`
  (the ABI-current grammar; the old `tree-sitter-dockerfile` pins
  `tree-sitter ^0.20` and would fork the graph — containerfile rides
  `tree-sitter-language 0.1`, tree-sitter dev-only, like the others),
  make via `tree-sitter-make`, cmake via `tree-sitter-cmake`. All ship
  self-contained highlights (single fragment). Extensions:
  `.dockerfile`/`.containerfile`, `.mk`/`.make`, `.cmake`.
- **Filename layer**: `pmacs.parse.language_from_filename(name)` backed by
  an extensible `pmacs.parse.filenames` map, wired into the precedence
  chain in both syntax.lua (grammar) and lsp.lua (LSP): grammar-ext →
  filetype map → filename → shebang. A recognized extension still wins;
  the basename map only fires when the extension misses. Seeds the three
  filenames plus shell rc dotfiles (`.bashrc`/`.zshrc`/`PKGBUILD`/… →
  bash) — highlighting them against the grammar shipped in #115.
- **LSP**: `config.dockerfile` (docker-langserver --stdio) and
  `config.cmake` (cmake-language-server). Make has no server, so no
  `config.make` — grammar highlight only. Extension filetype fallbacks
  added for id stability.

Bite-verified acceptance:
- filename_grammars_load_and_parse — each grammar's ABI is accepted by
  the tree-sitter 0.26 core and parses a representative snippet without
  error (dockerfile/cmake root at source_file, make at makefile).
- builtin_languages_include_dockerfile_make_cmake /
  language_for_path_resolves_dockerfile_make_cmake_extensions — entry
  presence and extension detection.
- m4_filename_map_resolves_special_files — the basename map (incl. path
  form and dotfiles→bash), config.dockerfile/cmake commands, and no
  config.make. Bite-verified against pre-feature syntax.lua.
- m4_filename_extensionless_dockerfile_highlights — an extensionless
  `Dockerfile` resolves to dockerfile for LSP and gets a dockerfile parse
  tree; reachable only via the filename map. Bite-verified.

Gates: fmt; clippy -D warnings; test --lib; --features crdt;
m4_acceptance --skip basedpyright; GPU; full workspace sweep;
git diff --check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJ9FQ832QwftJXCD9LeFan
2026-07-14 16:40:50 +01:00
Levi Neuwirth 6fd7db81fe feat(highlight): shell/bash tree-sitter grammar for the shell family
Shell scripts already had LSP (bash-language-server + shellcheck/shfmt,
wired in builtin/runtime/lsp.lua), but no tree-sitter grammar, so their
text rendered without lexical color. Fill in the missing half.

- Bundle tree-sitter-bash (0.25) as a BUILTIN_LANGUAGES entry. Unlike
  cuda, bash's highlights.scm is self-contained (no `; inherits:`
  delta), so a single fragment suffices. The crate exports
  LANGUAGE/HIGHLIGHT_QUERY over tree-sitter-language 0.1 — shared ABI
  crate, no second tree-sitter in the graph.
- Extension set is wider than the `.sh`/`.bash` the LSP filetype map
  covered: `.zsh`/`.ksh`/`.ash` are close-enough dialects and `.bats`
  is bash. The grammar's language name is `bash`, matching the
  `pmacs.lsp.config.bash` key, so opening any of these also auto-attaches
  bash-language-server (shellcheck declines zsh, so `.zsh` diagnostics
  may be sparse; highlighting is unaffected). lsp.lua's filetype map is
  extended to the same set as the belt-and-suspenders fallback.
- Extensionless shebang scripts (`#!/bin/sh`) and rc dotfiles
  (`.bashrc`) are intentionally NOT covered: detection is extension-keyed
  and shebang/filename sniffing is a separate, deferred feature.

Bite-verified acceptance:
- bash_grammar_loads_and_parses_script — the 0.25 grammar's ABI is
  accepted by the 0.26 core (set_language succeeds at runtime) and a
  representative script (shebang, set, parameter expansion, function,
  if) parses without error, rooting at `program`.
- builtin_languages_include_bash / language_for_path_resolves_bash_
  extensions — entry presence and detection across the wider set.
- bash_highlights_compile_with_captures — the self-contained query
  compiles against the grammar with real capture classes.
- m4_12_default_bundle_wires_bash — through the loaded runtime,
  config.bash targets bash-language-server and both grammar detection
  and the filetype fallback resolve the new extensions to `bash`.

Gates green: fmt; clippy -D warnings; test --lib; --features crdt;
m4_acceptance --skip basedpyright; GPU; full workspace sweep;
git diff --check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJ9FQ832QwftJXCD9LeFan
2026-07-14 13:50:21 +01:00
Levi Neuwirth 3cbb9dedd0
Merge pull request #114 from levineuwirth/cuda-lsp
feat(lsp): CUDA support — clangd + bundled tree-sitter grammar
2026-07-14 11:04:09 +00:00
Levi Neuwirth 11075914f3 feat(lsp): CUDA support — clangd + bundled tree-sitter grammar
Opening a .cu/.cuh file previously resolved to no language, so no
server attached and there was no highlighting. Wire CUDA end to end,
mirroring the existing C/C++ path:

- Bundle tree-sitter-cuda (0.21) as a new BUILTIN_LANGUAGES entry
  claiming .cu/.cuh, with its own HIGHLIGHTS_QUERY. A dedicated grammar
  rather than reusing cpp: the C++ grammar errors on the
  <<<grid, block>>> kernel-launch syntax. The crate rides
  tree-sitter-language 0.1 (its tree-sitter dep is dev-only), so it
  shares the ABI crate with the other grammars — no second tree-sitter
  in the graph.
- pmacs.lsp.config.cuda targets clangd (the same binary that serves
  C/C++; language_id "cuda" so clangd enters its CUDA parse mode), and
  .cu/.cuh filetype fallbacks map to "cuda" to keep the LSP id stable
  if the grammar is ever dropped. LspStyleView layers clangd's CUDA
  semantic tokens on top, exactly as for C/C++.

Bite-verified acceptance:
- cuda_grammar_loads_and_parses_kernel_launch — proves the 0.21
  grammar's ABI is accepted by the 0.26 core (set_language succeeds at
  runtime, which the compile step cannot confirm) and that the entry
  wired the CUDA grammar, not a cpp fallback: the <<<...>>> launch
  parses without error, whereas the cpp grammar reports an error on the
  same source (verified out of band).
- builtin_languages_include_cuda / language_for_path_resolves_cuda_
  extensions — entry presence and .cu/.cuh detection.
- m4_12_default_bundle_wires_cuda — config.cuda targets clangd and the
  filetype + grammar detection resolve to "cuda" through the loaded
  runtime.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJ9FQ832QwftJXCD9LeFan
2026-07-14 10:58:36 +01:00
Levi Neuwirth a7d5a6fedf feat(process): group lifecycle + null stdin; buf:revision(); jump_back hook parity
Supervisor (Q#CM3, framing additions 1-2): ProcessSpec gains
stdin="null" (Stdio::null, no writer thread, immediate EOF) and
group=true — process_group(0) spawn, group-directed fatal signals,
liveness-probed TERM-to-KILL reap ledger (insert-if-absent arming,
per-tick kill(-pgid,0) probe, GROUP_TERM_GRACE=500ms), leader-exit
group TERM before the final drain with in-drain deadline enforcement
plus ESRCH quiescence window and absolute cancel cap, poll-based
cancellable readers (nix poll feature added), shutdown ledger
force-kill + probe-to-ESRCH, maybe_restart gated once shut_down.
Both options are pipe-mode-only and rejected at spawn under PTY.
Nine unit tests cover framing acceptance 34, including the
TERM-ignoring redirected survivor, the pipe-holding descendant tick
bound, and setsid-escapee resource reclamation via the per-runtime
active-reader counter.

Bindings (additions 3-4): buf:revision() exposes the edit revision
(bumped by edit/undo/redo — unit-pinned); pmacs.editor.jump_back now
fires buffer.after-switch exactly when the jump changed buffers,
matching pmacs.window.switch_buffer; pmacs.process.spawn parses the
stdin/group spec keys.

Deviation from the framing letter, called out for review: the
active-reader counter field is always present (one Arc + two atomics
per reader lifetime) rather than cfg(test)-gated — gating the field
would spread cfg attributes through every construction site; only
the probe accessor is test-gated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VoiEyuPjoBhvwACf8HAnLB
2026-07-13 14:36:20 +01:00
Levi Neuwirth 3b630bfee4 docs(features): document the Lua feature matrix; drop unreachable compile_error idea (F-002)
`--all-features` can't build pmacs — luajit and lua54 select mlua's
mutually-exclusive Lua backends. Document the model so generic tooling
(CI, cargo hack, distro packaging) doesn't trip over it:

- README §Build: a feature-matrix table (luajit default / lua54 fallback /
  orthogonal crdt), the supported build lines, and an explicit "don't use
  --all-features".
- src/lib.rs crate docs: a "Lua flavor features" section stating the
  exactly-one-flavor rule.
- Cargo.toml [features]: expanded comment on the mutual exclusivity.

CI already iterates the flavors explicitly (never --all-features), so no
CI change was needed.

The audit's suggested crate-local compile_error! for the wrong-flavor case
was investigated and rejected as unreachable: the flavor check lives in
the mlua-sys *build script*, which cargo compiles before the pmacs crate,
so a mis-set flavor (both or neither) fails there first and pmacs's own
compile_error! never evaluates — confirmed empirically for both cases. A
dependent crate can't preempt a dependency's build failure, so the docs
are the honest mitigation and they name mlua-sys as the actual error
surface.

Validated: fmt clean; clippy clean under both Lua flavors; both flavors
build.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014TXbAwk27agwhrNNrhLi2U
2026-07-03 19:54:35 -04:00
Levi Neuwirth 5c34b3c37a regex-search: smart-case multi-line find_all_regex (Q#RX1/RX2)
The regex sibling of find_all, on regex::bytes::Regex over the whole
buffer. Returns Option<Vec<ByteRange>>: Some for a valid pattern
(possibly empty), None when it fails to compile — so the caller can
tell an invalid pattern (show [invalid]) from a valid zero-match
search.

Smart-case mirrors the literal path: case-insensitive via a (?i)
prefix unless the pattern carries an uppercase letter. Multi-line is
free — the regex runs over the whole byte slice, so an explicit \n (or
(?s).) spans lines while `.` keeps its default. Zero-width matches
(a*, ^, $) are filtered. The regex crate (already transitive in the
lockfile) is promoted to a direct dependency; its linear-time engine
makes a pathological pattern slow at worst, never catastrophic.

Tests: pattern match, smart-case both ways, \n-spanning + dotall +
default-no-cross, invalid→None vs valid-zero→Some(empty), zero-width
filter.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 14:22:51 -04:00
Levi Neuwirth a706864e04 session 2: pmacs-gpu workspace + wgpu/winit/glyphon hello-world
Adds the pmacs-gpu binary crate to the workspace. wgpu 29.0 + winit
0.30 + glyphon 0.11 (cosmic-text 0.18 via re-export) + pollster +
env_logger; pmacs-protocol in the dep graph but not consumed yet
(session 3 wires the attach loop).

The binary opens an 800x200 window titled 'pmacs-gpu hello-world',
sets up wgpu against its surface, configures glyphon with the bundled
JetBrains Mono Regular, and renders 'hello, pmacs' once per redraw.
Close button or Escape exits. Resize re-configures the surface and
glyphon viewport. Surface acquisition matches wgpu 29's
CurrentSurfaceTexture enum (success/suboptimal render through; lost/
outdated re-configure; timeout/occluded skip the frame).

Bundled assets: pmacs-gpu/fonts/JetBrainsMono-Regular.ttf (268 KB)
and pmacs-gpu/fonts/OFL.txt. Font shipped as required by the SIL
Open Font License 1.1.

One finding surfaced during the move and absorbed under rule (iii)
of the framing pass (small / no structural change): the design doc
recorded JetBrains Mono as Apache 2.0; the actual license has been
OFL since the family's open-source release. Doc corrected in
docs/pmacs-gpu-design.md.

Gates: cargo fmt + cargo clippy --all-targets -D warnings clean for
the whole workspace; cargo test --lib still 1314 (pmacs main crate
untouched); m4_acceptance 83; m11_5_semantic_acceptance --features
crdt 2.

Visual confirmation pending — agent environment is headless, so
'window opens, text renders' is user-side validation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:37:26 -04:00
Levi Neuwirth a820e91389 session 1 commit 4/4: message envelopes moved to pmacs-protocol
The big move that completes session 1. Wire types moved from
src/protocol.rs to pmacs-protocol/src/message.rs:

- Input event family: Key, Modifiers, KeyEvent, MouseButton, MouseKind,
  MouseEvent, FrontendEvent (and its variants — Resize, KeyEvent,
  MouseEvent, Resume, Pause, Detach, ResizeAck, CrdtOp, Viewport).
- Instance-side message family: CursorState, InstanceSignal,
  GoodbyeReason, InstanceMessage (Hello/Cursor/CellDelta/CursorByte/
  CrdtOp/BufferSnapshot/Goodbye/PresenceUpdate + the SemanticFrame
  variants).
- SelectionSnapshot.
- SemanticFrame family components: StyleSpan, StyleSegment,
  DecorationKind, Decoration, DecorationSegment, AdornmentPlacement,
  AdornmentContent, InlineAdornment, BlockAdornment, ResourceBody.
- Handshake: PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS,
  is_supported_protocol_version, InstanceIdentity, InstanceCapabilities,
  FrontendCapabilities, NegotiatedCapabilities, negotiate_capabilities,
  Hello, AttachRequest.

What stays in src/protocol.rs:
- AttachTarget / AttachError / AttachTargetParseError /
  AttachTargetValidationError / AttachTargetError / AttachmentHandle
  (CLI / binding internals, not wire).
- crossterm_translate submodule (the crossterm ↔ pmacs-protocol-types
  translation layer; sits at the binding boundary, not on the wire).
- Existing tests (wire-format roundtrip + AttachTarget + crossterm
  translation), unchanged — they reach the moved types through the
  'pub use pmacs_protocol::*' re-export.

Mechanical rewrites inside the moved chunk: crate::buffer::BufferId →
crate::BufferId, crate::rope::Position → crate::Position,
crate::rope::CrdtOp → crate::CrdtOp (the message module is inside
pmacs-protocol; identity types live at the crate root).

Feature re-added on pmacs-protocol: 'crdt' (was removed in commit 3
as I'd thought CrdtOp was the only feature-gated thing — but
InstanceCapabilities::default and FrontendCapabilities::default both
call cfg!(feature = 'crdt') for their multi_frontend / crdt_replica /
semantic_render defaults). Re-added with a doc comment explaining why.
The parent pmacs crate's 'crdt' feature now activates
'pmacs-protocol/crdt' so the cfg!() check evaluates consistently in
both crates.

Full gate green: fmt, clippy --all-targets -D warnings, lib 1314,
m4_acceptance 83, m8_1/m8_9/m8_10 10/26/19, m9_1 18, m5_8 5,
m11_5_semantic_acceptance --features crdt 2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 09:55:27 -04:00
Levi Neuwirth 14341d958e session 1 commit 1/4: workspace + identity types moved to pmacs-protocol
Workspace skeleton: root Cargo.toml becomes a workspace with members
[".", "pmacs-protocol"]; [workspace.dependencies] pins serde,
postcard, thiserror so both crates use byte-identical versions (the
wire format depends on it). pmacs main package keeps its existing
shape (no file moves); it just gains pmacs-protocol as a path
dependency.

Identity types moved: BufferId (from buffer.rs), FrontendId + ByteRange
(from protocol.rs), Position type alias (from rope.rs). All four are
self-contained — no custom-type dependencies — so the first stage of
the move can land atomically without dragging cell/message types along.

src/buffer.rs / src/protocol.rs / src/rope.rs each gain a 'pub use
pmacs_protocol::...' re-export for the moved names, so existing
internal imports (crate::buffer::BufferId, crate::rope::Position, etc.)
continue to resolve unchanged. New consumers (pmacs-gpu, debug tools)
will depend on pmacs-protocol directly.

One visibility change: BufferId::from_raw was pub(crate); promoted to
pub with a doc note that it's not stable API for external consumers.
The (crate) restriction was advisory only — external deserialization
already worked via the derived Deserialize, so making it pub doesn't
widen the actual surface, just makes it honest.

Lib gate: 1314 passed, no regressions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 09:11:33 -04:00
Levi Neuwirth 0935efd454 M_B3: tree-sitter-cpp/c + dual-authority TUI styling
Drops the policy-A exclusivity that left grammar-backed languages
without LSP semantic refinement. Adds tree-sitter-c (.c/.h) and
tree-sitter-cpp (.cpp/.cc/.cxx/.hpp/...) to the bundle so the grid
TUI gets lexical highlighting (keywords / strings / operators) on
first open. The Lua attach in builtin/runtime/lsp.lua now pushes
LspStyleView whenever an LSP server is up, regardless of grammar
presence; with both views attached the cell-painter pipeline runs
SyntaxHighlightView first (lexical) then LspStyleView (semantic)
and their styles compose through crate::overlay::merge_styles. The
result is the VSCode / Zed "TextMate + LSP semantic tokens" model
on a terminal grid: keywords colored by tree-sitter, identifiers
refined by clangd's semantic tokens.

`.h` is ambiguous C / C++; the `c` BUILTIN_LANGUAGES entry claims it
to match the LSP filetype map's default. Users who want `.h` parsed
as C++ can override via Lua (extension → language map).

Note the tree-sitter-c / -cpp crates expose `HIGHLIGHT_QUERY`
(singular), matching tree-sitter-md's `HIGHLIGHT_QUERY_BLOCK`
convention; tree-sitter-rust / -lua use `HIGHLIGHTS_QUERY` (plural).
Same bundled highlights.scm either way.

Regression guard: builtin_languages_include_c_and_cpp asserts the
language entries exist and claim their canonical extensions. The
LspStyleView module doc rewritten to reflect dual-authority
composition; the existing headline test's comment updated (the
test fixture still attaches only LspStyleView directly, so its
asserted cells reflect the LSP authority alone — Lua-level
attach_buffer is what exercises composition end-to-end).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 20:39:07 -04:00
Levi Neuwirth 6113c53381 v1.0.0 release prep: CHANGELOG, version bump, README, MSRV
Roadmap steps #3–#6 (post-CI-green doc/version work; no source
change — the CI-validated tree at ed78465 is unchanged).

- CHANGELOG: authored the [1.0.0] --- 2026-05-18 body. M7–M10 arc
  (third-party packages / fs API + dired-magit-outline / MCP /
  multi-frontend CRDT collaboration) over the 0.1.0 M1–M6 preview;
  the pulled-forward v0.2-prerequisite public APIs; SSH stderr
  Changed + Broken-pipe Fixed carried from Unreleased; Known
  limitations (per-frontend undo not persisted across reattach,
  Finding 4; macOS m6_5 REPL ctrl-c/exit-marker timing); project
  posture (forbid(unsafe_code), 1.95.0 pin, cross-flavor CI).
- Version: 0.1.0 -> 1.0.0 (Cargo.toml + Cargo.lock). Production
  version reporting already flows from CARGO_PKG_VERSION; verified
  `pmacs --version` -> `pmacs 1.0.0`, version-sensitive tests pass.
- README: Status -> v1.0.0 stable, contributions open; build line
  -> the rust-toolchain.toml-pinned 1.95.0.
- MSRV: rust-version 1.85 -> 1.95 to match the validated toolchain
  pin (was an unverified floor; pmacs is a pinned-toolchain app, so
  MSRV reflects the pinned/validated compiler).

Quiescent audit (#6): doc/version-only delta from CI-green ed78465;
build/version-tests/fmt verified clean on pinned 1.95.0; prose
reviewed accurate. SP-9 (macOS m6_5) logged in the gitignored
V0.2-PREREQUISITES.md.

Not in scope here / remaining: the recorded two-laptop manual
acceptance run (#7, operator) and the v1.0.0 tag (#8, operator).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 13:55:27 -04:00
Levi Neuwirth 45be65b026 M10.10 ship gate
Land the optimistic local-edit-application layer on top of the M10 CRDT
foundation: frontend-side rope replica with local edit application,
daemon-authoritative broadcast, and bidirectional cursor reconciliation.
Keystrokes feel instantaneous because the local replica answers next-render
queries before the daemon round-trip completes, while the daemon remains
the single source of truth for conflict resolution and broadcast to remote
replicas.

Architecture beats:
- BufferMirror (src/buffer_mirror.rs) holds a per-frontend rope replica
  with explicit cursor-staleness tracking. Every event that may move the
  active cursor or swap the active buffer marks the mirror stale; the
  next CursorByte from the daemon clears it.
- CrdtOpOrigin {OptimisticReplica(FrontendId), DaemonKey} routes broadcast.
  OptimisticReplica skips re-application on the originating frontend
  (already applied locally); DaemonKey broadcasts to all replicas including
  source -- covers Lua-driven and generated-buffer edits that bypass the
  optimistic path.
- Generated buffers (*help*, *workers*, *pmacs-instance*, *errors*) funnel
  apply_edit output through queue_daemon_origin_crdt_op so post-attach
  CRDT upgrades don't drop their edits.
- forbid(unsafe_code) preserved throughout; loro 1.12 added as the CRDT
  engine.

Audit posture: M10.10 shipped through six post-audit review rounds with
twenty-eight cumulative findings, most categorized as "incomplete
application of a prior round's mechanism." The audit doc records
grep-driven exhaustiveness as the standing countermeasure.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 16:28:46 -04:00
Levi Neuwirth 587a2a15de M9 ship gate
Land the Model Context Protocol (MCP) integration as a transport binding,
not a built-in feature. Six Lua functions plus userdata methods expose
the substance of three MCP feature areas (resources, tools, prompts), a
notification dispatcher, and a non-trivial AI-assistance example package
that meets the architectural ship gate (spec/pmacs-spec.tex:1572): zero
direct calls into the Rust core, zero special-cased MCP handling outside
the public API, source under 2000 lines of Lua.

The M9.5 -> M9.6 -> M9.7 -> M9.8 layered composition validates the claim
"AI is a transport binding, not a feature" -- pmacs-mcp-ai composes with
pmacs-mcp-prompts.render and inherits notification handling transitively
through M9.7's package, demonstrating that the AI domain is a layer
above MCP, not a thread woven through the core.

Subtask shape:
  M9.1 stdio transport + initialize handshake + restart policy
  M9.2 resources with in-flight + settled cache and per-uri invalidation
  M9.3 tools with isError-vs-JSON-RPC-error semantics + cancellation
  M9.4 prompts with required-argument validation
  M9.5 notification dispatcher (on_notification, off_notification)
  M9.6 tools-as-commands fixture package + 12 audit findings disposed
  M9.7 prompts-as-result-buffers fixture package + tree-sitter-md grammar
  M9.8 AI-assistance fixture package (363+ LoC; 17/17 acceptance tests)
  M9.9 formal package audit -- PASS on all three criteria
  M9.10 release: TRANSITION-M9.md + MCP-for-package-authors guide

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 15:04:23 -04:00
Levi Neuwirth 3a35d0b0f8 M8 ship gate 2026-05-07 16:55:14 -04:00
Levi Neuwirth c8d0d67615 Fix PTY final-output drain race 2026-05-04 09:44:30 -04:00
Levi Neuwirth 4da4b09d5d Initial commit: v0.1.0 2026-05-03 19:51:06 -04:00