pub struct WorktreesService { /* private fields */ }Expand description
Hosts the cross-window WorktreesRegistry as a DaemonService.
Implementations§
Source§impl WorktreesService
impl WorktreesService
Sourcepub fn new() -> Self
pub fn new() -> Self
Creates the service with an empty registry. Cheap — no I/O and no task;
the daemon calls start_menu_refresh to begin
off-thread menu caching, while tests use the bare service (menu computed
inline on demand).
Sourcepub fn load_polling_prefs(&self, path: PathBuf)
pub fn load_polling_prefs(&self, path: PathBuf)
Seeds the per-repo PR-poll enable set from the persisted 0600 prefs file
and remembers path so later set-polling changes persist
back to it (#1376). Called once by the daemon at startup, before any window
subscribes — so seed_polling needs no
bump. Best-effort throughout: a missing file is the first-run default (no
repos enabled), and a corrupt/unreadable one is logged and treated as
empty rather than wedging the service — the user simply re-enables. The
path is stored regardless, so the next change rewrites a clean file.
Sourcepub fn load_pr_cache(&self, path: PathBuf)
pub fn load_pr_cache(&self, path: PathBuf)
Seeds the resolved PR-badge cache from the persisted 0600 file and
remembers path so each poll persists back to it (#1389, fix 4). Called
once by the daemon at startup, before any window subscribes and before the
poller spawns, so restored badges render on the first tree snapshot and the
poller can skip its immediate re-poll for verdicts still fresh.
Best-effort throughout (the load_polling_prefs
contract): a missing file is the cold-start default, and a corrupt/unreadable
one is logged and treated as empty — the poller simply re-resolves. The path
is stored regardless, so the next poll rewrites a clean file. Restores both
the badges (into pr_cache) and the
PrWarmStart the poller reads at spawn.
Sourcepub fn rate_limit_cache(&self) -> Arc<RateLimitCache>
pub fn rate_limit_cache(&self) -> Arc<RateLimitCache>
A handle to the GitHub rate-limit snapshot cache (#1375), so the daemon can
share it with the ServiceRegistry
for the built-in status op to read.
Starts the background task that recomputes the tray menu snapshot every
[menu_refresh_interval] off the main thread — git enrichment is
blocking disk I/O — and stores it in menu_cache, so
the macOS tray’s menu() serves a cache instead of running git on the GUI
event loop. Idempotent, and a no-op outside a tokio runtime (mirroring the
Snowflake keep-alive heartbeat), so unit tests that build a bare service
keep computing the menu inline.
Sourcepub fn start_pr_poller(&self)
pub fn start_pr_poller(&self)
Starts the background task that keeps PR check badges fresh (#1337).
This is the half of the badge nothing else can do. Badges used to be resolved extension-side on repo-expand, so they were recomputed only when a repo node’s children were rebuilt — and the streamed snapshot carries worktree topology, not CI. While CI ran and no window opened or closed, nothing re-asked GitHub and a badge stayed wrong indefinitely.
The loop resolves every (repo, branch) pair in one gh api graphql call
(cost 1, independent of repo/worktree/window count), writes the cache the
tree snapshot reads, and bumps the registry’s change-notify only when a
verdict actually moved — so the server’s diff pushes to every open window
exactly when CI state changes, and never otherwise.
Cadence adapts: [pr_poll_interval] (~10 s) while a badge is pending and
fresh, escalating to [PENDING_MAX_INTERVAL] once a pending phase runs long
(#1389, fix 5) and doubling to [MAX_PR_POLL_INTERVAL] once everything is
terminal; it polls nothing at all while no window is registered.
It spends a gh call only when the watch set grows — a target added or
an upstream pushed (#1389, fixes 1/3) — or the backoff elapses; a pure
removal (window close, VS Code/daemon shutdown, TTL reap, lease lapse) never
fetches. A change-notify storm is debounced ([pr_debounce_interval],
#1389 fix 2) into one fetch, restored badges survive a restart (#1389 fix
4), and the cadence is capped when the shared GitHub budget is strained
(#1389 fix 6).
Idempotent, and a no-op outside a tokio runtime (mirroring
start_menu_refresh and the Snowflake keep-alive
heartbeat), so unit tests build a bare service that never spawns gh.
Sourcepub fn start_rate_limit_poller(&self)
pub fn start_rate_limit_poller(&self)
Starts the background task that keeps the GitHub API rate-limit reading fresh (#1375).
The daemon’s PR-badge poller shells out to gh, spending the same GitHub
budget as every other tool sharing the user’s token; when that drains, gh
rate-limits machine-wide with no warning until commands start failing. This
loop polls gh api rate_limit — an endpoint GitHub documents (and this
project verified) as exempt, spending nothing against any budget — so
daemon status, the JSON payload, and the tray can show the used-percentage
trend and warn before exhaustion, at zero cost to the budget watched.
A plain fixed cadence ([rate_limit_poll_interval], ~60 s): no adaptive
backoff and no window-gating, because the endpoint is free and a current
reading is wanted whenever an operator checks status. Idempotent, and a
no-op outside a tokio runtime (mirroring start_pr_poller
and start_menu_refresh), so unit tests build a
bare service that never spawns gh.
Trait Implementations§
Source§impl DaemonService for WorktreesService
impl DaemonService for WorktreesService
Source§fn name(&self) -> &'static str
fn name(&self) -> &'static str
service field) and to label its status/menu.Source§fn handle<'life0, 'life1, 'async_trait>(
&'life0 self,
op: &'life1 str,
payload: Value,
) -> Pin<Box<dyn Future<Output = Result<Value>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn handle<'life0, 'life1, 'async_trait>(
&'life0 self,
op: &'life1 str,
payload: Value,
) -> Pin<Box<dyn Future<Output = Result<Value>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn subscribe(
&self,
op: &str,
_payload: &Value,
) -> Option<Box<dyn ServiceStream>>
fn subscribe( &self, op: &str, _payload: &Value, ) -> Option<Box<dyn ServiceStream>>
None when
op is not one this service streams (the default for every service). Read moremenu,
identified by its MenuAction::id.