pmacs/docs
Levi Neuwirth db24abb64e
feat(lsp): D3 --- the file watcher stops sleeping and walks once per scan (#233)
Implements docs/lsp-file-watch-d3-framing.md revision 4, approved
2026-08-11 with the four rulings adopted as proposed: the honest bar
(absent at idle, one attributable job per concurrently due group), no
exclusions by default, server root_uri -> cwd -> attachment fallback,
and constants rather than config keys.

pmacs.fs.walk_tree: the whole recursive tree as ONE cancellable job
(JobKind::FsWalkTree, reply reuses ReplyKind::ReadDir --- identical
payload shape, so the Lua boundary needs no second conversion). Names
are base-relative; symlinks recorded, never traversed; an unreadable
subdirectory skips its subtree (scan_tree's pcall behaviour); only the
root failing to open fails the walk; the cancel token is polled once
per directory. Eight Rust unit tests, including flat-directory entry
parity with read_dir_blocking and the two review-round cancellation
cases (empty-tree pre-cancel; mid-walk via the cfg(test) entry hook).

The watcher itself is rewritten as the framed group scheduler. No
sleeps anywhere: one process.after-tick subscription (installed once
and guarded --- pmacs.hook.remove does not exist) drives every
(server, base) group's deadline off monotonic_ms, autosave's Q#AS2
idiom. The old design held one pool thread per sleeping watcher and
allocated 1 sleep + D read_dir jobs per watcher per tick --- 1,326
per tick for rust-analyzer's six watchers on this 220-directory
checkout. At idle there is now NO running job, which is also the
strongest witness in the suite: activity_summary settles to None, and
that assertion is unwritable under the old design.

The scheduler is the framing's state machine, all three review rounds
included: single-flight per group with generation-checked completions;
deadlines advanced from completion; the round-3 three-arm completion
partition (success / stale-or-retired / live non-success, with the
failure latch and quiet cancellation); joins wake the group, queue
exactly one follow-up mid-walk, and never reset the backoff curve;
per-watcher baselines --- the first snapshot whose WALK STARTED after
the join; membership captured at scan start; per-member cancellation
recheck at emit through the preserved _after_scan_for_tests seam;
backoff 250ms x2 to a 4s cap, reset by any emitted change; retirement
cancels the in-flight walk cooperatively.

Verification: eighteen acceptance tests. The six #234 tests are
byte-unchanged and green. Ten witnesses cover the framing's plan (the
review rounds added the fallback-determinism and root-boundary pair,
making twelve):
idle absence (and never a sleep purpose), one walk job per scan on a
twelve-directory fixture, join-wakes plus the registration epoch,
queued baseline for a mid-walk join (driven by saturating the worker
pool so the walk genuinely queues), single-flight under a withheld
completion pump, retirement and rebaseline through the fake's
unregister/re-register triggers, live cancel via pmacs._async._cancel
on the queued job, live failure with the once-per-error latch and the
preserved-snapshot recovery (DELETED for the pre-failure file is only
derivable from the retained snapshot), backoff shape from seam
timestamps, and the configured-root base.

Every witness was mutation-tested. Two findings from the bites:

- Retirement is DOUBLE-ENFORCED (unregister path and post-scan sweep)
  and biting either copy alone is masked by the other; only biting
  both goes red. Kept deliberately: the sweep covers seam-cancelled
  members, the unregister path covers idle groups whose next deadline
  is seconds away.
- The first idle probe was VACUOUS: it read pmacs.async instead of
  pmacs._async, errored, and the unwrap_or_default made every sample
  read as "absent". The probe now expects rather than defaults, so a
  broken probe is a red test, not a green lie.

One environmental fact, recorded in the lane: an empty stray /tmp/.git
(since removed) made project detection root every markerless tempdir
fixture at /tmp, which under Q#D3-3 the watcher then faithfully
watched. A markerless-fixture red that looks like a watcher bug may be
an ancestor marker.

A pre-commit review round found four blockers, all fixed here:

- walk_tree checked cancellation only inside its entry loops, which an
  EMPTY tree never enters --- a pre-cancelled queued walk returned an
  empty SUCCESS, which the success arm would commit and diff into a
  deletion storm. Cancellation is now checked before opening and
  before returning, cancellation outranks a missing-root error, and a
  unit test pins both.
- The neither-root-nor-cwd attachment fallback was still pairs-order
  nondeterministic --- the exact accident D3 set out to remove, behind
  a comment claiming otherwise. It now takes the lexicographically
  smallest attachment directory. Verified at the spawn sites: every
  server spawned with an attached file gets cwd = root, so the arm is
  defensive and unreachable through production spawning --- which is
  also why it carries no through-the-server witness.
- A base at the filesystem root joined as //path (and file:////path in
  URIs). Both join sites now go through join_under, the root-aware
  idiom dired's handler already uses, and the dir-of capture for a
  root-level file ("" from the match) normalizes to "/".
- The walk-count and scan-times probes defaulted on error, so two
  broken probes could compare equal and pass the retirement witness.
  Every probe now expects --- a broken probe is a red test, the same
  correction the vacuous idle probe forced.

A second pre-commit round found three more, all fixed here:

- Mid-walk cancellation was UNWITNESSED: both Rust cancel tests
  pre-cancelled and the acceptance test cancelled a queued walk, so
  deleting the internal polls left every test green. A cfg(test)
  entry hook now flips the token at an exact entry boundary and the
  witness asserts the walk stopped NEAR it (bound on entries
  processed), which is what discriminates the polls from the
  entry/exit checks. The retirement witness now holds a walk in
  flight across the unregister and asserts the job settles cancelled.
- The "unreachable fallback" claim was WRONG: pmacs.lsp.spawn may
  omit both cwd and root_uri, and ensure_server adopts such a live
  server for markerless files (root_uri and key_uri both nil). The
  lexicographic-minimum fallback now has a through-the-server
  witness: five sibling directories, the minimum opened last ---
  five, because with two the build's hash order coincided with the
  lexicographic answer and the first-pairs bite survived.
- The root-boundary joins gained a witness through exported
  production functions (the _deliver_status pattern): the matcher and
  URI builder driven at base "/", where reverting either join_under
  call makes the anchored glob refuse //hit and the URI grow a fourth
  slash. No fixture can walk / for real.

Verification totals after both rounds: eight walk_tree unit tests,
eighteen acceptance tests (six byte-unchanged, twelve witnesses), all
mutation-verified.

No wire change, no PROTOCOL_VERSION bump; walk_tree is an fs binding.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 14:59:28 +02:00
..
active-work.md feat(lsp): D3 --- the file watcher stops sleeping and walks once per scan (#233) 2026-08-11 14:59:28 +02:00
agent-handoff.md docs: absorb #234 and #227, discharge the hold, and rule D3 next 2026-08-11 09:59:26 +02:00
auto-indent-framing.md
…
auto-pairing-framing.md
…
autosave-recovery-framing.md
…
bottom-panel-framing.md docs: bottom panel framing (revision 4, amended) 2026-07-24 13:00:46 -04:00
bottom-panel-stage2-framing.md docs(bottom-panel): record framing revision 7 2026-07-29 22:37:41 -04:00
bottom-panel-stage3-framing.md review round 1: the crdt suite Stage 3 missed, and two stale explanations 2026-08-04 12:08:28 +02:00
ci-crdt-coverage-framing.md review round 2: assert the CRDT capability defaults, not their round-trip 2026-08-01 11:37:19 -04:00
ci-red-signatures.md docs: U9 --- the first red in this family with an in-run control 2026-08-10 20:18:45 +02:00
comment-toggle-framing.md
…
compile-mode-framing.md
…
config-registry-framing.md
…
context-menu-framing.md
…
cua-typeover-undo-framing.md
…
desktop-save-framing.md
…
destination-capture-framing.md docs: carry the four-plus-one dedication count into the framing 2026-08-10 14:11:43 +02:00
dired-framing.md docs: record dired Stage 1 (#165) as landed 2026-07-25 17:39:27 -04:00
dired-stage2-framing.md docs: dired Stage 2 framing rev 9 2026-07-28 21:09:11 -04:00
discovery-stage1-command-family-framing.md docs(discovery): finalize Stage 1 framing review 2026-07-31 18:28:35 -04:00
discovery-stage2-framing.md docs: record discovery Stage 2 framing approval 2026-08-10 13:52:32 +02:00
distribution-stage1-framing.md docs(release): a tag before the merge does nothing, silently 2026-08-01 14:46:27 -04:00
editing-conveniences-framing.md
…
folding-framing.md docs(folding): framing rev 5 — record approval + keybinding decision 2026-07-23 09:59:37 -04:00
folding-stage2-framing.md docs(folding): Stage 2 framing rev 4 — address review round 3 2026-07-23 17:25:55 -04:00
full-grid-resync-framing.md docs: the framing said "awaiting approval" after being approved 2026-08-06 15:22:03 +02:00
gate-protocol-build-framing.md docs: gate framing revision 5 --- pin both emitter pairs, correct print-plan 2026-08-09 18:11:14 +02:00
gate-script-framing.md build: scripts/gate — a target dir per worktree, and one gate suite (#225) 2026-08-09 09:43:33 +00:00
generated-buffer-immutability-framing.md merge: integrate main after PR 188 lands 2026-07-29 12:28:25 -04:00
git-integration-framing.md docs: narrow the copy-detection claim to the fixture it was measured on 2026-08-09 22:34:19 +02:00
gpu-attach-robustness-framing.md
…
gpu-chord-forwarding-framing.md
…
gpu-golden-harness-framing.md
…
gpu-horizontal-scroll-framing.md feat(gpu): horizontal scroll — QoL Stage 5, closing the long-lines arc (#223) 2026-08-08 10:55:49 +00:00
gpu-initial-target-framing.md test(journey): pin the destination commit, and land the doc updates 2026-07-26 17:25:22 -04:00
gpu-invocation-framing.md Record second GPU invocation review closure 2026-07-23 12:40:03 -04:00
gpu-minibuffer-framing.md
…
gpu-set-font-framing.md
…
gpu-terminal-input-framing.md docs: measure the CI coverage gap and record the vterm as-framed audit 2026-07-25 17:17:23 -04:00
gui-zoom-framing.md docs(zoom): the ties both round up, they do not oppose 2026-08-06 17:50:03 +02:00
horizontal-scroll-framing.md feat(view): horizontal scroll, text and decorations together 2026-08-07 22:43:17 +02:00
in-buffer-completion-framing.md
…
in-buffer-search-framing.md
…
inline-math-framing.md docs: record the inline-math slice (#158) as landed 2026-07-25 18:16:31 -04:00
inline-math-slice-framing.md fix(math): review round 3 — mapping bug, whitespace defect, real MATH gaps 2026-07-24 20:19:40 -04:00
journey-stage1a-framing.md feat(compile): make building discoverable — journey step 9 2026-07-30 21:13:12 -04:00
journey-stage1b1-compile-defaults-framing.md docs(journey): framing revision 2 — close review round 1 2026-07-30 18:40:34 -04:00
journey-stage1b2-lsp-guidance-framing.md docs(journey): framing revision 4 — close review round 3 2026-07-30 21:52:15 -04:00
journey-stage1b3-welcome-framing.md docs(journey): framing revision 4 — close review round 3 2026-07-31 14:55:57 -04:00
json-yaml-framing.md docs: absorption pass at c5f7501 — #209 landed, and six weeks of drift 2026-08-01 13:56:23 -04:00
keybindings.md feat(git): Stage 1 --- *git-status* and *git-diff*, no wire change 2026-08-09 14:43:21 +02:00
kill-ring-framing.md
…
latex-grammar-math-substrate-framing.md docs(inline-math): commit the parent inline-math framing 2026-07-23 14:06:26 -04:00
lean4-mode-framing.md fix(lean4): count fan-outs where a claim cannot skip the count 2026-07-26 17:15:24 -04:00
locals-query-processing-framing.md
…
long-lines-framing.md test: the report itself, at a real PTY — and §1.1 was wrong 2026-08-07 19:37:46 +02:00
lsp-file-watch-d3-framing.md feat(lsp): D3 --- the file watcher stops sleeping and walks once per scan (#233) 2026-08-11 14:59:28 +02:00
lsp-file-watcher-framing.md fix(lsp): read the glob form from the pattern, and stop a cancelled scan emitting 2026-08-11 09:00:15 +02:00
lsp-language-coverage-framing.md docs: LaTeX framing revision 3 --- step zero corrected the premise 2026-08-10 10:48:49 +02:00
lsp-panels-framing.md
…
lua-bindings-split-framing.md
…
macos-ci-signal-integrity-framing.md docs: record Stage 2 in the framing, and fence R3 off from R2's retirement 2026-08-05 13:51:40 +02:00
mcp-for-package-authors.md
…
mode-system-wiring-framing.md
…
modeline-detection-framing.md
…
multi-language-injections-framing.md
…
package-author-guide.md
…
package-manager-hardening-framing.md
…
packages.md
…
persistence-framing.md
…
pmacs-gpu-design.md
…
pmacs-gpu-diagnostic-parity-framing.md
…
pmacs-gpu-mouse-deferred-framing.md
…
pmacs-gpu-mouse-framing.md
…
pmacs-gpu-perline-reshape-framing.md
…
pmacs-gpu-phase-a-audit.md
…
pmacs-gpu-quad-backgrounds-audit.md
…
pmacs-gpu-quad-backgrounds-framing.md
…
pmacs-gpu-scroll-framing.md
…
pmacs-gpu-status-band-framing.md
…
pmacs-gpu-wavy-squiggles-framing.md
…
process-signal-diagnostic-completeness-framing.md docs(process): stop overclaiming the pre-kill sample; drop a dead comment 2026-07-30 14:41:50 -04:00
process-signal-tolerance-framing.md docs: frame the PTY terminate diagnostic (revision 4) 2026-07-25 21:12:51 -04:00
process-teardown-stdin-deadlock-framing.md fix(process): arm the setsid dependency, correct the orphan account 2026-07-29 22:54:06 -04:00
project.md
…
query-replace-framing.md
…
r8-fixture-boundary-framing.md fix(tests): bound the LSP fixture's project detection — retires R8 (#226) 2026-08-09 08:54:06 +00:00
reap-ledger-silent-failures-framing.md fix(process): scope the escalation claim to ticks, and pin the boundary 2026-07-30 20:41:03 -04:00
repository-audit-2026-07-03.md
…
resource-op-delete-guard-framing.md fix(lsp): close review gaps in workspace edit reporting 2026-07-29 12:29:50 -04:00
roadmap-2026-07.md
…
semantic-frontend-protocol.md
…
side-quest-backlog.md docs: absorption pass at c5f7501 — #209 landed, and six weeks of drift 2026-08-01 13:56:23 -04:00
statusline-segments-framing.md
…
tab-width-parity-framing.md
…
terminal-config-and-copy-mode-framing.md docs: correct the non-adopter attribution and two stale anchors 2026-07-26 16:45:12 -04:00
test-ambient-config-isolation-framing.md docs(framing): record the deliberate departure from the §7 branch plan 2026-07-31 19:46:33 -04:00
theme-faces-framing.md
…
tree-primitive-framing.md fix(listview): ids must also be unique and not NaN 2026-08-05 23:06:25 +02:00
ux-arc-framing.md
…
vterm-framing.md Merge main, and fold #173's a37 findings into the CI-coverage lane 2026-07-26 09:29:42 -04:00
web-grammars-html-css-framing.md docs+test(web): name and pin the @attribute retro-paint (review round 3) 2026-07-23 16:11:47 -04:00
worker-identity-framing.md fix(workers): a safe display-text boundary for purpose and handler names 2026-08-10 14:51:54 +02:00