pmacs/builtin/runtime
Levi Neuwirth ed3033c1fb
fix(lsp): carry the GlobPattern form so plain-string globs can match, and cancel superseded watchers (#233)
Implements D1 and D2 from docs/lsp-file-watcher-framing.md (revision 1,
approved 2026-08-10). The user ruled that the walking survives this
lane; D3 gets its own framing.

D1. resolve_watcher now returns (base, pattern, form) and the watch
record carries the form. Per LSP, a plain-string glob matches the
file's ABSOLUTE path while a RelativePattern's pattern is relative to
its base --- and resolve_watcher discarded the distinction, so every
downstream consumer matched relatively. Real servers send absolute
globs: rust-analyzer was never told about any file change (all six of
its globs absolute), and gopls saw go.mod but never a .go edit. The
match subject is chosen in start_file_watcher --- form "absolute"
matches base .. "/" .. rel, form "relative" matches rel unchanged.
scan_tree still walks in relative terms; only the string handed to the
matcher changes.

D2. register_file_watchers now cancels the outgoing record list before
file_watchers[skey][reg.id] = recs drops the only reference to it. The
cancel loop is factored into cancel_watch_records, shared with
unregister_file_watchers, so the two paths cannot diverge.
rust-analyzer registers the same id twice with no unregister between
--- previously 12 concurrent pollers, 6 permanently uncancellable.

Verification, per the framing's plan. Three new fake-LSP modes and
tests beside m4_24, each mutation-tested against the defect it names:

- filewatchabs registers a plain-string absolute glob whose relative
  reading matches nothing. Red before D1 (reverting the match subject
  fails exactly this test), green after.
- filewatchflat registers a RelativePattern without a leading **/
  (*.txt at the base) --- F2's guard. Matching every form absolutely
  fails exactly this test, so the obvious wrong fix cannot land green.
  It also pins that a base-level pattern does not match into
  subdirectories.
- filewatchrereg registers the same id twice (rust-analyzer's shape).
  The witness is observable polling, not table shape: f.old exists on
  disk before either .new event lands, so a leaked watcher at the same
  250ms cadence reports it before the second positive. Reverting D2
  fails exactly this test, the leaked .old event visible in .received.

m4_24 stayed green under all three mutations --- the framing's F1
finding (the existing test is insensitive to D1 in both directions),
confirmed rather than assumed. It is kept unchanged.

The framing doc records the approval and the answered ruling; the
active-work lane moves to IMPLEMENTED with the verification results.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 23:29:12 +02:00
..
async.lua fix(workers): a safe display-text boundary for purpose and handler names 2026-08-10 14:51:54 +02:00
autosave.lua feat(config): typed configuration registry with buffer-local scope 2026-07-21 18:29:18 -04:00
comment.lua feat(lean4): editing surface + Stage 1 acceptance (Q#LN5, LN6, LN17) 2026-07-25 09:59:48 -04:00
compile.lua fix(process): pmacs.process.spawn REQUIRES a purpose — review blocker 2026-08-10 14:51:54 +02:00
completion.lua fix(completion): address TUI-validation findings (LSP query gaps, scoping, prefix keys, window scope) 2026-07-07 16:54:14 -04:00
desktop.lua fix(persistence): reliable daemon gate, unarm, per-pane after-load 2026-07-08 22:27:24 -04:00
dired.lua refactor(panel): one shared rule for the adopter `display` vocabulary 2026-08-01 20:25:27 -04:00
editops.lua fix(config): reject wrongly-typed spec fields; make trim-on-save buffer-aware 2026-07-21 18:29:18 -04:00
fold.lua feat(fold): Arc 6 Stage 1 — instance fold engine (headless) 2026-07-23 12:14:00 -04:00
fs.lua wip(stage2a): the Lua half — hooks, LSP subscribers, applier origin 2026-07-29 18:09:03 -04:00
help.lua fix(help): forwarders must work programmatically, not only from M-x 2026-07-31 20:31:00 -04:00
indent.lua fix(edit): PR #109 round 1 — shared search invalidation, daemon anchor clear, bounded indent scan 2026-07-10 15:46:36 -04:00
killring.lua feat(edit): editing-conveniences pack (editops) 2026-07-12 15:33:17 +01:00
lean.lua fix(process): pmacs.process.spawn REQUIRES a purpose — review blocker 2026-08-10 14:51:54 +02:00
lean_abbrev.lua feat(lean4): the Unicode input method (Arc 8 Stage 4b) 2026-07-26 16:24:06 -04:00
lean_input.lua fix(lean4): count fan-outs where a claim cannot skip the count 2026-07-26 17:15:24 -04:00
linewrap.lua feat(view): horizontal scroll, text and decorations together 2026-08-07 22:43:17 +02:00
listview.lua fix(listview): ids must also be unique and not NaN 2026-08-05 23:06:25 +02:00
lsp.lua fix(lsp): carry the GlobPattern form so plain-string globs can match, and cancel superseded watchers (#233) 2026-08-10 23:29:12 +02:00
mcp.lua M9 ship gate 2026-05-09 15:04:23 -04:00
pair.lua feat(typed-edit): the typed-edit consumer chain (Arc 8 Stage 4a) 2026-07-26 13:00:03 -04:00
recentf.lua fix(persistence): symlink confinement, real test-inertness, view_top restore 2026-07-08 18:21:46 -04:00
saveplace.lua fix(persistence): symlink confinement, real test-inertness, view_top restore 2026-07-08 18:21:46 -04:00
syntax.lua feat(lean4): editing surface + Stage 1 acceptance (Q#LN5, LN6, LN17) 2026-07-25 09:59:48 -04:00
terminal.lua fix(buffer): fan out generated writes, and clear the history that exists 2026-07-26 13:43:22 -04:00
typed_edit.lua fix(typed-edit): close round-8 review on the consumer chain 2026-07-26 13:39:33 -04:00
welcome.lua feat(help): the discovery command family — P4 Stage 1 2026-07-31 19:04:48 -04:00
window.lua feat(window): adopter placement opt-in and the Stage 1 acceptance suite 2026-07-24 14:10:45 -04:00
zoom.lua docs(zoom): the ties both round up, they do not oppose 2026-08-06 17:50:03 +02:00