Skip to main content

WorktreesService

Struct WorktreesService 

Source
pub struct WorktreesService { /* private fields */ }
Expand description

Hosts the cross-window WorktreesRegistry as a DaemonService.

Implementations§

Source§

impl WorktreesService

Source

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).

Source

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.

Source

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.

Source

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.

Source

pub fn start_menu_refresh(&self)

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.

Source

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.

Source

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

Source§

fn name(&self) -> &'static str

Stable identifier used to route control-socket envelopes to this service (the envelope’s 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,

Handles an operation routed to this service, returning a JSON payload.
Source§

fn subscribe( &self, op: &str, _payload: &Value, ) -> Option<Box<dyn ServiceStream>>

Opens a push subscription for a streaming op, or returns None when op is not one this service streams (the default for every service). Read more
Source§

fn menu(&self) -> MenuSnapshot

Cheap snapshot of the service’s tray submenu, polled by the menu-bar shell. Must not block.
Source§

fn menu_action<'life0, 'life1, 'async_trait>( &'life0 self, action_id: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Performs a tray menu action previously surfaced by menu, identified by its MenuAction::id.
Source§

fn status<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = ServiceStatus> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Structured status for daemon status aggregation.
Source§

fn shutdown<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Gracefully stops the service, draining in-flight work. Called once on daemon shutdown.
Source§

impl Default for WorktreesService

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more