fix(daemon): reject projectless watch roots - #559
Conversation
|
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 95d194ab1a
ℹ️ 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".
| let auto_init_candidate = project_path.as_deref().or(original_cwd); | ||
| let auto_init_root = auto_init_candidate | ||
| .filter(|candidate| !initialized && crate::config::load_sync_config(candidate).auto_init) | ||
| .and_then(crate::worktree::git_worktree_root); |
There was a problem hiding this comment.
Check auto-init config at the Git root
When discovery-mode serve starts in a subdirectory of an uninitialized Git repository, this filter loads configuration from that subdirectory before git_worktree_root resolves the repository root. A root-level .tracedecay/config.json with sync.auto_init=false is therefore missed, the default true is used, and the daemon is allowed to initialize a repository whose configuration explicitly opts out. Resolve the Git root first and evaluate its sync configuration before setting allow_init.
Useful? React with 👍 / 👎.
| let telemetry_timings = timings | ||
| || project_path | ||
| .as_deref() | ||
| .is_some_and(|path| crate::config::load_telemetry_config(path).timings); |
There was a problem hiding this comment.
Preserve timings after initialize-root routing
For a projectless CWD that is later routed to a real project through MCP initialize.roots, this expression permanently sets the handshake's timings flag to false unless --timings was passed. apply_daemon_initialize_route updates only the project path and allow_init, while run_daemon_connection_with_timings passes this false value as an override; consequently the routed project's telemetry.timings setting—including its default true—can never enable response timing annotations for this flow. The routed project configuration needs to be applied after initialize-root resolution rather than treating the initially absent path as an opt-out.
Useful? React with 👍 / 👎.
| if root.is_dir() | ||
| && root.join(".git").exists() | ||
| && TraceDecay::has_initialized_store_with_options(&root, &open_options).await |
There was a problem hiding this comment.
Apply the watcher cap after rejecting stale rows
code_projects_seen_within(window, cap) applies its SQL LIMIT before these new validity checks, so recent stale or projectless rows still consume the entire watcher budget. If the newest watch_max_projects rows are invalid, initialized Git projects that are only slightly older are never examined or watched after daemon startup, even though no watcher slots were actually used. Fetch enough candidates to fill the cap after filtering, or move equivalent validity filtering ahead of the limit.
Useful? React with 👍 / 👎.
| } else { | ||
| crate::config::resolve_path_with_discovery(None) | ||
| original_cwd.and_then(crate::config::discover_project_root) | ||
| }; |
There was a problem hiding this comment.
Retain registry-only initialized CWD routing
Using only synchronous filesystem discovery makes an initialized profile-sharded project invisible when its local enrollment marker is missing but its store remains resolvable through the global registry—a supported state handled by discover_project_root_with_identity. For a non-Git project, or a Git project with TRACEDECAY_SYNC_AUTO_INIT=false, auto_init_root cannot restore the path, so a no---path client now sends a projectless handshake and project tools fail unless that MCP host happens to provide usable initialize roots. Previously the CWD fallback was still sent to the daemon, which could resolve the registered store; preserve a daemon-side identity lookup for this case rather than dropping the path outright.
Useful? React with 👍 / 👎.
Summary
tracedecay serveprojectless when the current directory is neither an initialized project nor an auto-init Git rootWhy
Starting
servefrom a non-project directory could promote that directory into daemon project routing. Once recorded, daemon startup could also seed a Git watcher for the arbitrary directory, causing unnecessary discovery and indexing work.This change preserves projectless discovery until a real project root is available and prevents stale registry metadata from admitting non-Git or uninitialized watch roots.
Tests
cargo test --lib daemon::git_watch::testscargo test --lib serve::testscargo test --test daemon_suite git_watchcargo test --test mcp_suite no_explicit_pathcargo test --test mcp_suite explicit_initialized_path_ignores_initialize_rootscargo test --test mcp_suite serve_with_reachable_daemon_proxies_before_opening_explicit_projectnpm run lint:commit -- --from upstream/master --to HEAD