fix(penpal): eliminate battery drain from filesystem watching#358
Conversation
Previously the watcher deep-watched all sources and comments directories for every discovered project at startup (~16K file descriptors across 114 projects), causing constant CPU activity from filesystem events and directory walks that drained battery even when idle. Now the watcher only deep-watches what the current tab needs: - FilePage: the single directory containing the viewed file - ProjectPage: the project's source and comment directories - WorkspacePage/Recent/InReview/Search: no deep watches (workspace roots are already watched for project discovery) The frontend calls POST /api/focus?project=X (project-level) or POST /api/focus?project=X&path=Y (file-level) on navigation, and DELETE /api/focus when moving to pages that don't need it. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ab8a9e9812
ℹ️ 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".
| for _, p := range projects { | ||
| w.watchProject(p) | ||
| if err := w.watcher.Add(p.Path); err != nil { | ||
| log.Printf("Warning: could not watch project root %s: %v", p.Path, err) |
There was a problem hiding this comment.
Rebuild focused watches after project refresh
After refreshAfterConfigChange rescans projects, Watcher.Refresh now only re-adds project roots and never reapplies the current focus watch set. If a user is already on a ProjectPage and adds/removes sources or worktrees, focusWatched remains stale, so filesystem changes under newly discovered source directories no longer emit files events until focus is changed away and back.
Useful? React with 👍 / 👎.
| w.cache.RescanWith(projects) | ||
| for _, proj := range projects { | ||
| w.watchProject(proj) | ||
| w.watcher.Add(proj.Path) | ||
| } |
There was a problem hiding this comment.
Re-apply project focus after auto-detected source rescan
When an auto-detect source directory (for example thoughts/) is created under a project root, this rescan path updates the cache but only adds shallow root watches. If that project is currently focused, the new source tree is never added to focusWatched, so subsequent edits inside that directory are missed until another explicit FocusProject call occurs.
Useful? React with 👍 / 👎.
…ommands Restore BOT-686 / goose-internal PR #358 parity that the better-doctor migration dropped: direct npmjs.org access is blocked by Cloudflare WARP, so npm install/view must be routed through Block's internal Artifactory proxy via --registry=<url>. This wires an optional, caller-supplied registry through the crate without baking in any Block-specific URL. - Add `npm_registry: Option<String>` to RunChecksOptions (default None). - Add `apply_npm_registry(command, registry)` helper that appends ` --registry=<url>` only to npm-backed commands (npm install / npm view); curl-pipe installers, auth commands, and the git-clonefile fix pass through unchanged. - Apply the registry to the displayed fix_command: threaded through run_checks_with_options -> collect_base_report -> check_single_ai_agent so the shown command matches what runs. - Apply the registry to the freshness probe: latest_npm now adds --registry <url>, threaded via populate_freshness -> fetch_version_info. - Apply the registry to the execute path: new execute_fix_with_options / execute_fix_streaming_with_options resolve the command, run it through apply_npm_registry, then shell out. The existing execute_fix / execute_fix_streaming delegate with None. Purely additive: every new field/param is Option-defaulting-None, so RunChecksOptions::default() reproduces today's exact commands and the staged Tauri app compiles and behaves unchanged. The caller (goose-internal) supplies the Block Artifactory URL. Not in this commit (follow-ups): per-OS/platform install recipes (amp's CLI installer) and per-package name overrides. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Motivation
Penpal was draining battery even when idle, showing up in macOS "Using Significant Energy" warnings. Investigation revealed the watcher was deep-watching all sources and comments directories for every discovered project at startup — ~16K file descriptors across 114 projects — causing constant CPU activity from filesystem events and directory walks.
Approach
Only deep-watch what the current tab actually needs:
The frontend calls
POST /api/focus?project=X(project-level) orPOST /api/focus?project=X&path=Y(file-level) on navigation, andDELETE /api/focuswhen moving to pages that don't need deep watching.Test plan
🤖 Generated with Claude Code