Skip to content

feat(server): stat-polling file tracker - #487

Merged
16bit-ykiko merged 3 commits into
mainfrom
feat/stat-polling-file-tracker
Jul 6, 2026
Merged

feat(server): stat-polling file tracker#487
16bit-ykiko merged 3 commits into
mainfrom
feat/stat-polling-file-tracker

Conversation

@16bit-ykiko

@16bit-ykiko 16bit-ykiko commented Jul 5, 2026

Copy link
Copy Markdown
Member

clice now notices changes the client never tells it about. A new FileTracker polls the filesystem with plain stat (portable, no fd limits, no event storms — the same trade-off clangd makes) and feeds the existing event-driven invalidation engine:

  • CDB poll loop (default 3s): watches compile_commands.json — including the case where none exists yet at startup and the build system generates one later. A change must stay stable for two consecutive ticks before the database is reloaded (half-written-file guard). The reload diffs entries by canonical command hash and emits one CDBChanged event with the added/removed/changed file sets.
  • Workspace sweep loop (default 30s): stats every file the dependency graph knows, skipping open buffers. A (mtime, size) suspect is confirmed by content hash before DiskChanged is emitted, so touch stays silent; a stat failure on a known file emits DiskRemoved once. The first sweep only seeds the baseline (no startup storm).

Polling only marks dirty and emits events — it never needs to be complete. A missed change means derived state stays stale for one more poll period at worst; correctness is anchored by the pull side's two-layer deps validation at compile/index time.

User-visible behavior

  1. Rewriting compile_commands.json while the server runs (add/remove entries, change flags) takes effect without a restart; a database generated after startup is discovered automatically ("opened the editor before running cmake" now works).
  2. Files changed on disk behind the server's back (e.g. git checkout, code generators) are picked up: open files get correct diagnostics on their next request, closed files are reindexed.
  3. touch (mtime-only change) does not trigger reindexing.
  4. If a save hook rewrites a file as it lands (disk no longer matches the buffer at didSave), the file is additionally treated as a disk change, removing the "I see my old buffer, my dependents see the new disk" split.

Configuration

New [tracker] section in clice.toml:

[tracker]
cdb_poll_seconds = 3        # compile_commands.json poll interval; 0 disables
workspace_poll_seconds = 30 # workspace sweep interval; 0 disables

Notes

  • The engine handles CDBChanged with a uniform full dependency-graph rescan rather than per-entry incremental surgery: entry additions, removals and flag changes all funnel into one code path, and since CDB changes are the only rescan trigger, a persistent scan cache would never be warm anyway.
  • Entry identity for the CDB diff is the canonical command hash (frontend profile) plus the entry directory: entry reordering and codegen-only flag changes (-g, -fPIC, ...) deliberately do not count as changes; -O* does (it defines __OPTIMIZE__).
  • DiskRemoved is conservative: the file loses its includer role and context choices through it are re-checked, but index shards are kept so last-known content still serves navigation.
  • Integration tests disable both loops and drive deterministic single ticks through an internal test-only request; one live test keeps the real loop covered.
  • Client-side file watching (didChangeWatchedFiles) can later be added as just another event producer; deliberately out of scope here, as are directory-level new-file discovery and moving stats off the event loop (perf log line records sweep duration to decide if/when that is needed).

@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds CDB diffing and reload preservation, a FileTracker for polling compile_commands.json and workspace files, new invalidation paths for disk/CDB events, server wiring for polling, and tests covering the new flows.

Changes

File Tracker and CDB Diff Feature

Layer / File(s) Summary
CDB reload/diff core and contracts
src/command/command.{h,cpp}, tests/unit/command/cdb_diff_tests.cpp, tests/unit/command/command_tests.cpp
Adds CDBDiff, reload_and_diff, snapshot hashing, reload failure preservation, and unit tests for added/removed/changed detection and corrupt-file handling.
Pinned context hash validation
src/server/context/context_resolver.{h,cpp}
Adds shared command-hash validation for saved contexts and orphaned pinned choices.
Invalidation event model and cascades
src/server/workspace/invalidator.{h,cpp}, src/syntax/dependency_graph.{h,cpp}, tests/unit/server/invalidator_tests.cpp
Extends FileEvent and DirtySet, adds cascade helpers, rewrites event handling for save/disk/CDB changes, adds all_files(), and covers the new behavior in unit tests.
FileTracker implementation
src/server/workspace/file_tracker.{h,cpp}, src/server/workspace/workspace.{h,cpp}, src/server/workspace/config.{h,cpp}, docs/clice.toml, tests/unit/server/file_tracker_tests.cpp
Introduces polling config, CDB discovery, FileTracker tick logic for CDB and workspace sweeps, and unit tests for debounce, force, discovery, and disk-state transitions.
Server wiring, poll endpoint, and background tasks
src/server/service/master_server.{h,cpp}, src/server/service/lsp_client.cpp, src/server/protocol/extension.h
Adds the tracker member and background tasks, wires workspace load and dispatch changes, and exposes the internal poll request with typed params/result.
Integration tests for tracker behavior
tests/integration/features/test_file_tracker.py, tests/integration/features/test_header_reindex.py, tests/integration/utils/client.py, tests/integration/utils/wait.py
Adds integration coverage for CDB and workspace polling, rewrite detection, polling helpers, and divergent save reindexing.

Estimated code review effort: 4 (Complex) | ~75 minutes

Possibly related PRs

  • clice-io/clice#368: Adds DependencyGraph::all_files() and related graph traversal work used by workspace polling.
  • clice-io/clice#391: Touches the same invalidation path in src/server/workspace/invalidator.cpp for save-related event handling.
  • clice-io/clice#406: Modifies MasterServer wiring and load_workspace() around workspace state setup.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: a server-side stat-polling file tracker.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/stat-polling-file-tracker

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3011e3bc24

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/server/workspace/invalidator.cpp
Comment thread src/server/workspace/invalidator.cpp
Comment thread src/server/workspace/invalidator.cpp Outdated
Comment thread src/server/workspace/invalidator.cpp
Comment thread src/server/workspace/invalidator.cpp
Comment thread src/server/workspace/file_tracker.cpp

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (3)
src/server/workspace/workspace.cpp (1)

213-221: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Subdirectory scan order is filesystem-dependent, making CDB discovery non-deterministic.

When more than one direct subdirectory contains a compile_commands.json (e.g. build/ and build-debug/), directory_iterator order isn't guaranteed, so the discovered path can differ across OSes/filesystems or even between runs. Consider collecting candidates and picking deterministically (e.g., lexicographic sort, or preferring a directory literally named build).

♻️ Proposed fix for deterministic ordering
-    std::error_code ec;
-    for(llvm::sys::fs::directory_iterator it(workspace_root, ec), end; it != end && !ec;
-        it.increment(ec)) {
-        if(it->type() == llvm::sys::fs::file_type::directory_file) {
-            if(auto found = try_candidate(it->path()); !found.empty()) {
-                return found;
-            }
-        }
-    }
-    return {};
+    std::error_code ec;
+    std::vector<std::string> subdirs;
+    for(llvm::sys::fs::directory_iterator it(workspace_root, ec), end; it != end && !ec;
+        it.increment(ec)) {
+        if(it->type() == llvm::sys::fs::file_type::directory_file) {
+            subdirs.push_back(it->path());
+        }
+    }
+    llvm::sort(subdirs);
+    for(auto& dir: subdirs) {
+        if(auto found = try_candidate(dir); !found.empty()) {
+            return found;
+        }
+    }
+    return {};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/workspace/workspace.cpp` around lines 213 - 221, The subdirectory
scan in the workspace CDB lookup is non-deterministic because
llvm::sys::fs::directory_iterator does not guarantee a stable order. Update the
logic around the workspace_root scan and try_candidate to first collect all
direct subdirectory candidates, then choose one deterministically before
returning a compile_commands.json path. Prefer a stable rule such as
lexicographic ordering or an explicit preference for a directory named build so
workspace discovery behaves consistently across runs and filesystems.
src/server/service/lsp_client.cpp (2)

498-522: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

Test-only endpoint is unconditionally registered/reachable.

clice/internal/poll is wired into register_extensions() with no build-time or runtime gate, so any connected client can invoke it in production, despite the comment marking it "TEST-ONLY... not a stable API". LSP method dispatch isn't restricted to advertised capabilities, so this is reachable outside test harnesses.

Consider gating registration behind a debug/test-only build flag or an opt-in server option so it isn't part of the shipped surface.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/service/lsp_client.cpp` around lines 498 - 522, The test-only
`clice/internal/poll` request is being registered unconditionally in
`register_extensions()`, which exposes it to production clients. Update the
registration around `peer.on_request("clice/internal/poll", ...)` so it only
happens behind a debug/test build flag or an explicit opt-in server option. Keep
the handler itself unchanged, but ensure `LspClient`/extension setup does not
advertise or accept this endpoint unless the test-only gate is enabled.

498-522: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Potential reentrancy hazard: internal poll hook vs. live background poll loop.

tick_workspace() yields internally between stat batches, and FileTracker (per its header) keeps no reentrancy guard around its baseline map. If a client invokes clice/internal/poll with loop="workspace" while workspace_poll_task() is concurrently mid-sweep (e.g. background polling is enabled, as in test_cdb_polling_loop_live), the two coroutines could interleave and corrupt/duplicate tracker state. The integration test suite avoids this by disabling polling intervals whenever the poll hook is used, but nothing in the code itself prevents concurrent invocation.

Consider adding a simple in-flight guard in FileTracker (e.g., a bool flag rejecting/serializing overlapping tick_workspace()/tick_cdb() calls) for defense-in-depth.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/service/lsp_client.cpp` around lines 498 - 522, The internal poll
handler in lsp_client.cpp can overlap with the background workspace polling loop
and reenter FileTracker state updates. Add a simple in-flight guard in
FileTracker around tick_workspace() and tick_cdb() so overlapping calls are
rejected or serialized, and make the clice/internal/poll request path honor that
guard when dispatching on loop="workspace" or loop="cdb". Use the existing
tick_workspace, tick_cdb, and workspace_poll_task flow to locate the call sites
and keep the baseline/tracker state safe from concurrent access.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/server/workspace/file_tracker.cpp`:
- Around line 111-149: FileTracker::tick_workspace currently yields between
batches, so concurrent invocations can interleave and mutate the shared baseline
twice during the same sweep. Add an in-flight guard in tick_workspace() (or
equivalent serialization around workspace_poll_task callers) so only one sweep
runs at a time; if a second call arrives while one is active, skip or queue it
rather than touching baseline concurrently.

In `@src/server/workspace/invalidator.cpp`:
- Around line 199-227: The CDBChanged handling in invalidator.cpp performs
scan_dependency_graph() synchronously inside MasterServer::dispatch(), which
blocks the event loop and stalls request handling. Move the workspace rescan
work out of the dispatch path for the CDBChanged case—e.g. schedule it on a
separate worker/task queue or another serialized background path—then apply the
dep_graph, path_to_module, and context_epoch updates only after the scan
completes.
- Around line 182-198: The DiskRemoved handling in invalidator::apply is
rebuilding the entire dependency reverse map on every single removal, which
makes batched deletions too expensive. Update the FileEvent::Kind::DiskRemoved
case to only call workspace.dep_graph.clear_includes(path_id) and mark the
reverse map dirty, then trigger workspace.dep_graph.build_reverse_map() once
after the event loop in apply() when any removals occurred. Use the existing
workspace.dep_graph and dirty handling in apply to preserve correctness while
avoiding repeated full rebuilds.

In `@tests/unit/server/file_tracker_tests.cpp`:
- Around line 88-144: In WorkspaceTickStateMachine, the rewrite cases still use
same-size content, so the FileTracker fast path may miss the change when mtime
does not advance enough. Update the test around tick_workspace() to force a
detectable rewrite by using different-length payloads for the
changed/touched/reborn writes, or by waiting at least MTIME_GRANULARITY before
rewriting, so the DiskChanged and DiskRemoved behavior is exercised reliably.

---

Nitpick comments:
In `@src/server/service/lsp_client.cpp`:
- Around line 498-522: The test-only `clice/internal/poll` request is being
registered unconditionally in `register_extensions()`, which exposes it to
production clients. Update the registration around
`peer.on_request("clice/internal/poll", ...)` so it only happens behind a
debug/test build flag or an explicit opt-in server option. Keep the handler
itself unchanged, but ensure `LspClient`/extension setup does not advertise or
accept this endpoint unless the test-only gate is enabled.
- Around line 498-522: The internal poll handler in lsp_client.cpp can overlap
with the background workspace polling loop and reenter FileTracker state
updates. Add a simple in-flight guard in FileTracker around tick_workspace() and
tick_cdb() so overlapping calls are rejected or serialized, and make the
clice/internal/poll request path honor that guard when dispatching on
loop="workspace" or loop="cdb". Use the existing tick_workspace, tick_cdb, and
workspace_poll_task flow to locate the call sites and keep the baseline/tracker
state safe from concurrent access.

In `@src/server/workspace/workspace.cpp`:
- Around line 213-221: The subdirectory scan in the workspace CDB lookup is
non-deterministic because llvm::sys::fs::directory_iterator does not guarantee a
stable order. Update the logic around the workspace_root scan and try_candidate
to first collect all direct subdirectory candidates, then choose one
deterministically before returning a compile_commands.json path. Prefer a stable
rule such as lexicographic ordering or an explicit preference for a directory
named build so workspace discovery behaves consistently across runs and
filesystems.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 075024d0-9f87-4263-9efd-7a75b56a0dab

📥 Commits

Reviewing files that changed from the base of the PR and between 403ffab and 3011e3b.

📒 Files selected for processing (23)
  • docs/clice.toml
  • src/command/command.cpp
  • src/command/command.h
  • src/server/protocol/extension.h
  • src/server/service/lsp_client.cpp
  • src/server/service/master_server.cpp
  • src/server/service/master_server.h
  • src/server/workspace/config.cpp
  • src/server/workspace/config.h
  • src/server/workspace/file_tracker.cpp
  • src/server/workspace/file_tracker.h
  • src/server/workspace/invalidator.cpp
  • src/server/workspace/invalidator.h
  • src/server/workspace/workspace.cpp
  • src/server/workspace/workspace.h
  • src/syntax/dependency_graph.cpp
  • src/syntax/dependency_graph.h
  • tests/integration/features/test_file_tracker.py
  • tests/integration/features/test_header_reindex.py
  • tests/integration/utils/client.py
  • tests/unit/command/cdb_diff_tests.cpp
  • tests/unit/server/file_tracker_tests.cpp
  • tests/unit/server/invalidator_tests.cpp

Comment thread src/server/workspace/file_tracker.cpp
Comment thread src/server/workspace/invalidator.cpp
Comment thread src/server/workspace/invalidator.cpp
Comment thread tests/unit/server/file_tracker_tests.cpp

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7b22e1d433

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/server/workspace/invalidator.cpp Outdated
Comment thread src/server/workspace/invalidator.cpp
Comment thread src/server/workspace/invalidator.cpp Outdated
Comment thread src/server/workspace/file_tracker.cpp
Comment thread src/server/workspace/file_tracker.cpp
Comment thread src/server/workspace/invalidator.cpp Outdated
Comment thread src/server/workspace/invalidator.cpp Outdated
Comment thread src/server/workspace/invalidator.cpp
Comment thread src/server/workspace/invalidator.cpp

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/server/context/context_resolver.cpp (2)

941-953: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Duplicate hash-validation lambda; not migrated to the new shared helper.

has_command here reimplements ContextResolver::entry_has_hash verbatim. Since this PR's stated purpose is to introduce shared command-hash validation, this call site should use the new member instead of its own local copy to avoid the two implementations drifting apart.

Proposed fix
-    // Validate that `hash` names a real CDB entry of `entry_path`.
-    auto has_command = [&](llvm::StringRef entry_path, llvm::StringRef hash) {
-        if(!ws.cdb.has_entry(entry_path)) {
-            return false;
-        }
-        std::vector<std::string> rule_append, rule_remove;
-        ws.config.match_rules(entry_path, rule_append, rule_remove);
-        for(auto& cmd: ws.cdb.lookup(entry_path, {.remove = rule_remove, .append = rule_append})) {
-            if(canonical_command_hash(cmd.to_string_argv(), cmd.resolved.directory) == hash) {
-                return true;
-            }
-        }
-        return false;
-    };
+    // Validate that `hash` names a real CDB entry of `entry_path`.
+    auto has_command = [&](llvm::StringRef entry_path, llvm::StringRef hash) {
+        return ws.cdb.has_entry(entry_path) && entry_has_hash(entry_path, hash);
+    };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/context/context_resolver.cpp` around lines 941 - 953, The local
hash-checking lambda in ContextResolver is duplicating the new shared
command-hash logic instead of using it directly. Replace the inline has_command
implementation with a call to ContextResolver::entry_has_hash so the lookup and
canonical hash comparison stay centralized, and remove the duplicated
rule_append/rule_remove and lookup logic from this call site.

721-750: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Mirror the CDB-entry guard for host-pinned choices. drop_orphaned_choices() can keep a host pin alive when the host has been removed from the CDB but is still reachable through some include chain, especially in the common empty command_hash case. Add the same ws.cdb.has_entry(host_path) check used by validate_saved_context() before accepting the host branch.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/context/context_resolver.cpp` around lines 721 - 750, The
host-pinned branch in ContextResolver::drop_orphaned_choices should mirror the
CDB-entry validation used in validate_saved_context(). Add the same
workspace.cdb.has_entry(host_path) guard before treating a host-pinned saved
context as valid, especially when saved.command_hash is empty, so host-pinned
choices are dropped if the host entry no longer exists even if an include chain
still reaches it.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/server/context/context_resolver.cpp`:
- Around line 941-953: The local hash-checking lambda in ContextResolver is
duplicating the new shared command-hash logic instead of using it directly.
Replace the inline has_command implementation with a call to
ContextResolver::entry_has_hash so the lookup and canonical hash comparison stay
centralized, and remove the duplicated rule_append/rule_remove and lookup logic
from this call site.
- Around line 721-750: The host-pinned branch in
ContextResolver::drop_orphaned_choices should mirror the CDB-entry validation
used in validate_saved_context(). Add the same
workspace.cdb.has_entry(host_path) guard before treating a host-pinned saved
context as valid, especially when saved.command_hash is empty, so host-pinned
choices are dropped if the host entry no longer exists even if an include chain
still reaches it.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0abf54a4-8a70-44db-9926-f6b48635b3af

📥 Commits

Reviewing files that changed from the base of the PR and between 7b22e1d and 3b19e3f.

📒 Files selected for processing (9)
  • src/command/command.cpp
  • src/command/command.h
  • src/server/context/context_resolver.cpp
  • src/server/context/context_resolver.h
  • src/server/service/master_server.cpp
  • src/server/workspace/file_tracker.cpp
  • src/server/workspace/invalidator.cpp
  • tests/unit/command/cdb_diff_tests.cpp
  • tests/unit/command/command_tests.cpp
✅ Files skipped from review due to trivial changes (1)
  • src/server/context/context_resolver.h
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/unit/command/cdb_diff_tests.cpp
  • src/server/workspace/file_tracker.cpp
  • src/command/command.cpp

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3b19e3fb3b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/server/workspace/file_tracker.cpp
Comment thread src/server/workspace/file_tracker.cpp
Comment thread src/server/workspace/invalidator.cpp
Comment thread src/server/workspace/invalidator.cpp
@16bit-ykiko
16bit-ykiko merged commit a51c436 into main Jul 6, 2026
22 checks passed
@16bit-ykiko
16bit-ykiko deleted the feat/stat-polling-file-tracker branch July 6, 2026 13:52
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