Skip to content

Support multiple Claude project dirs (eg windows+wsl), condense watcher warnings, add polling fallback for unwatchable dirs#38

Merged
wesm merged 3 commits intowesm:mainfrom
njt:condense-watcher-warnings
Feb 25, 2026
Merged

Support multiple Claude project dirs (eg windows+wsl), condense watcher warnings, add polling fallback for unwatchable dirs#38
wesm merged 3 commits intowesm:mainfrom
njt:condense-watcher-warnings

Conversation

@njt
Copy link
Copy Markdown
Contributor

@njt njt commented Feb 25, 2026

Summary

  • config.json can have multiple paths, to support Windows & WSL on the same box eg
 "claude_project_dirs": [
    "C:\\Users\\Nat\\.claude\\projects",
    "\\\\wsl.localhost\\Ubuntu-24.04\\home\\gnat\\.claude\\projects"
  ],
  • WatchRecursive now returns watched/unwatched counts silently instead of logging one warning per failed subdirectory
  • startFileWatcher prints a single condensed line per root dir (e.g. "Couldn't watch 15 directories under \wsl.localhost..., will poll every 2m")
  • Adds a 2-minute polling goroutine for unwatchable directories, supplementing the existing 15-minute periodic sync (wsl can't be watched from Windows)

Test plan

  • go vet -tags fts5 ./internal/sync/ ./cmd/agentsview/ passes
  • CGO_ENABLED=1 go test -tags fts5 ./internal/sync/ ./internal/config/ ./internal/server/ passes
  • Manual: run with WSL dirs configured, confirm one summary line instead of many warnings

🤖 Generated with Claude Code

@roborev-ci
Copy link
Copy Markdown

roborev-ci bot commented Feb 25, 2026

roborev: Combined Review (8c46d57)

Synthesis unavailable. Showing raw review outputs.

Agent: codex | Type: security | Status: done

Summary: I reviewed the 7-commit combined diff for injection, authz/authn, credential exposure, path traversal, unsafe parsing, dependency/CI/CD risk, data leakage, concurrency bypass, and security-relevant error handling. Under this repo’s stated localhost-only/single-user assumptions, I did not find any new security vulnerabilities introduced by these changes.

No issues found.

Agent: gemini | Type: security | Status: skipped (quota)

Review skipped — agent quota exhausted.

Agent: codex | Type: default | Status: done

Reviewing this commit range now with a code-review focus. I’ll inspect the changed files directly to validate behavior, look for regressions, and map findings to concrete file/line references.
I confirmed repo history; next I’m checking the exact commit range content in the workspace so findings align with the real code, not just a partial hunk view.
I’ve loaded the main diff and now I’m drilling into engine.go and main.go around changed logic paths to verify subtle behavior (especially watcher fallback and multi-dir precedence).
The current branch has moved on, so I’m switching to reading the exact files at commit 8c46d57 to avoid mixing in unrelated later refactors.
I have enough context from the target snapshot and am now comparing against the pre-range baseline to catch behavior changes that could introduce regressions.
I’m now validating two suspicious areas: error handling in single-session OpenCode sync and watcher fallback behavior, to ensure any findings are concrete and not style-only.
Findings

  1. Medium - SyncPaths can miss valid files when configured directories overlap or one directory is a parent of another.
    Reference: internal/sync/engine.go:160, internal/sync/engine.go:187, internal/sync/engine.go:213, internal/sync/engine.go:255
    In each per-dir loop, once isUnder(...) matches, invalid shape checks return false immediately instead of trying later configured dirs. That can suppress live updates for valid files in another configured root.
    Suggested fix: inside each per-dir loop, change “shape mismatch” paths from return ... false to continue, and only return false after all configured roots are exhausted.

  2. Medium - syncSingleOpenCode now swallows real parse/DB errors and may return misleading “not found”.
    Reference: internal/sync/engine.go:1100, internal/sync/engine.go:1118
    On any ParseOpenCodeSession error it continues, then may end with opencode session ... not found. This hides actionable failures (corrupt DB, permission issues, schema issues).
    Suggested fix: only continue on true “not found” (errors.Is(err, sql.ErrNoRows)), and return/log first non-not-found error if no directory succeeds.

  3. Low (testing gap) - coverage is narrow for multi-dir behavior.
    Reference: internal/sync/engine_integration_test.go:1243, internal/config/config_test.go:269
    Tests added for Claude multi-dir and Claude precedence, but no tests for overlapping-dir classification, non-Claude multi-dir SyncPaths, or OpenCode single-session multi-dir error handling.
    Suggested fix: add integration tests for overlapping roots and OpenCode error propagation across multiple dirs.

No security issues stood out under the repository’s localhost-only assumptions.

Brief summary
These commits add multi-directory config support (JSON arrays + resolve precedence), pass multi-dir slices through main and sync.Engine, add startup missing-dir warnings, and introduce watcher “unwatched dir” polling fallback, with tests updated plus a new Claude multi-dir integration test.

Agent: gemini | Type: default | Status: skipped (quota)

Review skipped — agent quota exhausted.

Note: gemini review skipped (agent quota exhausted)

@njt njt changed the title Condense watcher warnings, add polling fallback for unwatchable dirs Support multiple Claude project dirs (eg windows+wsl), condense watcher warnings, add polling fallback for unwatchable dirs Feb 25, 2026
@wesm
Copy link
Copy Markdown
Owner

wesm commented Feb 25, 2026

working on this

Nat Torkington and others added 2 commits February 25, 2026 10:14
- Add multi-directory array fields (claude_dirs, codex_dirs, etc.) to
  config with config.json loading and ResolveDirs precedence merging
- Warn on startup when configured directories are missing
- Change sync Engine to accept multi-directory slices instead of
  single strings
- Wire multi-directory support through main entry point
- Condense repeated fsnotify watcher warnings into periodic summaries
- Add polling fallback for directories that can't be watched (e.g.
  network mounts)
- Add multi-directory Claude sync integration test
In classifyOnePath, pattern-mismatch inside an isUnder match now
continues to the next configured directory instead of returning
false immediately. This prevents nested/overlapping same-agent
directories from short-circuiting classification.

In syncSingleOpenCode, track the last parse error across dirs and
return it when all dirs fail, instead of a generic "not found".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@wesm wesm force-pushed the condense-watcher-warnings branch from 8c46d57 to d87df64 Compare February 25, 2026 16:18
@roborev-ci
Copy link
Copy Markdown

roborev-ci bot commented Feb 25, 2026

roborev: Combined Review (d87df64)

Summary Verdict: The changes successfully implement multi-directory support and fallback polling, but one medium-severity issue requires attention regarding
the polling fallback mechanism when watcher creation fails.

Medium

Polling fallback does not start when the watcher cannot be created.
Files: [main.go:149](/home/roborev/.roborev/clones/wesm/agentsview/cmd/agentsview/main.go
:149), main.go:257

startUnwatchedPoll only starts when len(unwatchedDirs) > 0 , but startFileWatcher returns nil unwatched dirs when sync.NewWatcher(...) fails. In that case, the new 2-minute fallback never runs, so updates fall back to the 15-minute periodic sync only.
Suggested fix: When watcher creation fails,
return a signal to force polling (e.g., all resolved dirs as unwatched, or a separate watcherUnavailable bool).


Synthesized from 4 reviews (agents: codex, gemini | types: default, security)

@wesm
Copy link
Copy Markdown
Owner

wesm commented Feb 25, 2026

thank you!

When sync.NewWatcher fails entirely (e.g. no inotify/kqueue),
startFileWatcher returned nil unwatchedDirs, so the 2-minute
polling fallback never started. The only fallback was the
15-minute periodic sync.

Return a non-empty unwatchedDirs slice on watcher failure so
startUnwatchedPoll kicks in.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@roborev-ci
Copy link
Copy Markdown

roborev-ci bot commented Feb 25, 2026

roborev: Combined Review (59d5f3d)

Summary Verdict: The code review identified one medium-severity issue related to error handling
in the file watcher fallback mechanism.

Medium

  • Ignored watcher traversal errors can silently disable the new fallback behavior
    • File: [cmd/agentsview/main.go:297](/home/roborev/.roborev/clones/wesm/agentsview/
      cmd/agentsview/main.go:297)
    • Description: startFileWatcher ignores the third return value from WatchRecursive (watched, uw, _ := ...). If WalkDir returns an error for a root (e.g., transient IO/permission/
      race), that root may end up unwatched without being added to unwatchedDirs, so the 2m poller may not start for that condition.
    • Suggested fix: Handle err explicitly, log it, and treat that root as unwatched (append to unwatchedDirs).

Synthesized from 4 reviews (agents: codex, gemini | types: default, security)

@wesm wesm merged commit de36280 into wesm:main Feb 25, 2026
6 checks passed
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.

2 participants