Skip to content

Stand in for watched files when the client lacks them - #652

Draft
Firehed wants to merge 10 commits into
pipeline/4-holders-declare-pathsfrom
pipeline/5-stand-in-watcher
Draft

Firehed wants to merge 10 commits into
pipeline/4-holders-declare-pathsfrom
pipeline/5-stand-in-watcher

Conversation

@Firehed

@Firehed Firehed commented Sep 29, 2026

Copy link
Copy Markdown
Owner

Last of five stacked PRs, on top of the watched paths. Its tree is identical to #646, the total view.

What was wrong

[LSP] makes workspace/didChangeWatchedFiles the way a server learns of changes on disk, and leaves supporting it optional for the client. With a client that does not, nothing told the server a file changed. ALE is one: it declares no such capability and never sends the notification.

What this does

PollingFileWatcher stands in for the notification with a client that did not declare support, and does nothing otherwise.

  • Before each message it looks at the paths the holders named in the previous PR.
  • It reports one event per file created, deleted, or changed, as the client would, through the same InvalidatableInterface the notification's handler calls.
  • StatReader reads modification times. BeforeMessageInterface is how the server loop calls the watcher.

Behaviour changes

With a client that lacks watched-file support, a file created, deleted, or regenerated on disk is seen on the next request.

Two things that are deliberate

  • The filesystem reports whole seconds. A path last looked at during the second it was modified is looked at again, since an unchanged time proves nothing within its own second.
  • A directory's modification time changes for any entry, including an editor's swap file. The watcher compares PHP file names and reports nothing when none changed.

Measured

On openemr Cost
First look, once about 600 ms
Each message after about 8 ms
A client that sends watched-file events nothing

#647 records the option to throttle it.

Enforcement edits

Each at Eric's direction, classified by docs/architecture/enforcement-edits.md.

Edit Class
phpstan.neon filesystem allowlist: add StatReader Loosen
phpstan.neon invalidate() allowlist: add PollingFileWatcher Loosen
deptrac.yaml: add the Message layer Tighten
deptrac.yaml: Root may use Filesystem, Message, and Watch; Watch may use Cache, Capability, Domain, Filesystem, and Message Loosen

🤖 Generated with Claude Code

@codecov

codecov Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.66%. Comparing base (cfba211) to head (a0145d3).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@                         Coverage Diff                         @@
##             pipeline/4-holders-declare-paths     #652   +/-   ##
===================================================================
  Coverage                               99.65%   99.66%           
- Complexity                               1917     1948   +31     
===================================================================
  Files                                     140      144    +4     
  Lines                                    4923     5004   +81     
===================================================================
+ Hits                                     4906     4987   +81     
  Misses                                     17       17           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@Firehed
Firehed added this pull request to stack #653 September 29, 2026 17:17

@Firehed Firehed left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The overall mechanics on this feel suspicious, even if they may work today. I haven't closely reviewed the main PollingFileWatcher yet which needs direct focus; that aside, the "stamping" utilities seem too spread out and poorly encapsulated.

Comment thread docs/vim-ale.md

## Changes on Disk

ALE's own LSP client does not send `workspace/didChangeWatchedFiles`.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claim should specify a version/commit/etc since ALE may fix this in the future.

Comment thread src/Server.php
// support; the events invalidate cached workspace state (RFC 1 §5.2, §5.3).
$watchedFilesRegistrar = new WatchedFilesRegistrar(new TransportClientConnection($transport));
$lifecycleHandler = new LifecycleHandler($negotiator, [$watchedFilesRegistrar]);
$fileWatcher = new PollingFileWatcher(

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This setup should happen in the DI container if possible. May defer if it turns into a wiring nightmare.

Comment thread src/Server.php
private readonly LifecycleHandler $lifecycleHandler,
array $handlers,
private readonly MessageScopedInterface $messageScope,
private readonly ?BeforeMessageInterface $beforeMessage = null,

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this being optional seems like a recipe for bugs.


final readonly class PathStamp
{
public function __construct(

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought the initial design had agreed on using a cheap hash rather than just time and size.

@Firehed
Firehed force-pushed the pipeline/5-stand-in-watcher branch from ad05b95 to 9e9a68c Compare September 29, 2026 20:01
Firehed and others added 10 commits September 29, 2026 14:11
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Firehed
Firehed force-pushed the pipeline/5-stand-in-watcher branch from 9e9a68c to a0145d3 Compare September 29, 2026 21:11
@Firehed
Firehed marked this pull request as draft September 30, 2026 00:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant