Dynamic file-watch registration with a full snapshot-diff watcher.
- src/lsp.rs: did_change_watched_files(sid, &changes) notification;
capability workspace.didChangeWatchedFiles.dynamicRegistration=true
(mandatory — clangd/rust-analyzer/gopls only register dynamically).
- src/lua_bindings.rs: pmacs.lsp.did_change_watched_files binding.
- builtin/runtime/lsp.lua: client/(un)registerCapability handled in
the server-request pump (reply null; start/stop watchers). Brace-
expanding glob → anchored Lua pattern; recursive read_dir/stat
snapshot-diff poller emitting per-file created/changed/deleted
filtered by glob + WatchKind, batched into one notification;
self-cancels when the server dies or unregisters. luajit-safe
(kind_has() arithmetic, no 5.3 bitwise).
- pmacs_fake_lsp.rs: `filewatch` mode registers a **/*.txt watcher
and logs received changes to <base>/.received (disk side-channel —
the protocol stream is drained by the pump).
- tests/m4_acceptance.rs: m4_24 asserts create(1)/change(2)/
delete(3) for matching .txt only; non-matching .md filtered.
Bug caught in validation: `**/` → `(.*/)?` is not a valid Lua
pattern (no group quantifier) — matched nothing, zero events. Fixed
to `**/`→`.-`, `**`→`.*`; m4_24 surfaced it.
client/unregisterCapability cancels watcher records (code-reviewed);
not asserted in m4_24 — a "no further notifications" negative-timing
check is flaky; the create/change/delete + filter path is the
deterministic proof.
Gates: lib 1301/0, m4 79/0, m8_1 10/0, m8_9 26/0, m8_10 19/0,
m9_1 18/0, m5_8 5/0, m11_5 (--features crdt) 2/0; fmt + clippy
clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>