State

Struct State 

Source
pub struct State {
    pub lifecycle: LifecyclePhase,
    pub processes: HashMap<u32, ProcessRecord>,
    pub windows: HashMap<String, WindowMirror>,
    pub pool: HashSet<String>,
    pub instance_registry: HashMap<String, u32>,
    pub next_instance_num: u32,
    pub backend_window_ids: HashMap<String, String>,
    pub event_version: u64,
    pub next_client_id: u64,
    pub monitors: Vec<Rect>,
    pub pending_hwnds: HashMap<u64, PendingHwnd>,
    pub just_promoted_labels: HashSet<String>,
}
Expand description

Top-level launcher state. Single Arc<Mutex> owned by the IPC server; passed into update(state, cmd, conn) for every incoming command.

Fields§

§lifecycle: LifecyclePhase§processes: HashMap<u32, ProcessRecord>

Keyed by PID. Multiple records per PID would be a bug — the reducer enforces unique-pid on insert.

§windows: HashMap<String, WindowMirror>

Read-only window mirror (Phase B.4). Keyed by label. Source of truth still lives in agentmux-cef::AppState.browsers / window_meta; this is a passive copy fed by host ReportWindow* commands. B.5 inverts the dependency: host queries this map instead of maintaining its own.

§pool: HashSet<String>

Phase B.4 follow-up — pre-warmed pool inventory. Tracked separately from windows because pool entries are not user-visible until promote. On promote the host emits ReportPoolWindowRemoved + ReportWindowOpened so the same label transitions atomically (from launcher’s perspective) from pool to windows. On pre-promote destroy: only ReportPoolWindowRemoved.

§instance_registry: HashMap<String, u32>

Phase B.5 — authoritative window instance registry. Maps label → sequential instance number (1 for “main”, 2 for the second window opened, etc.). Numbers are never reused within a launcher run — when a window closes the entry is removed but next_instance_num keeps advancing. Sole source of truth post-B.5e (host’s WindowInstanceRegistry was deleted in PR #584); host holds a passive shadow projection in agentmux-cef::AppState.shadow_instance_registry. Updated by the same reducer paths that mutate windows.

§next_instance_num: u32

Next instance number to assign. Starts at 2 — “main” is pre-seeded with 1 in Default (matching host’s WindowInstanceRegistry::new behavior so a synthetic main open wouldn’t collide).

§backend_window_ids: HashMap<String, String>

Phase B.5 (window_id_map step a) — authoritative label → backend window ID map. Mirrors host’s existing agentmux-cef::AppState.window_id_map. Populated by Command::ReportBackendWindowIdRegistered (sent from host when the frontend calls register_backend_window IPC after init); drained by ReportBackendWindowIdUnregistered on close. Will become host-side authoritative through the standard a→b→c→d→e ratchet.

§event_version: u64

Monotonic counter for Event.version. Bumped by bump_version().

§next_client_id: u64

Monotonic counter for client_id (returned in Registered events).

§monitors: Vec<Rect>

Phase B.9.1 (WRR) — current monitor topology, replaced wholesale on ReportMonitorTopologyChanged. Empty by default until the host’s wrr/wndproc.rs reports the first WM_DISPLAYCHANGE-equivalent (or its initial topology probe at startup). OffMonitor drift is suppressed when this is empty — we don’t know enough to classify yet.

§pending_hwnds: HashMap<u64, PendingHwnd>

Phase B.9.1 — HWNDs the reducer has seen via ReportHwndOpened but couldn’t yet associate with a WindowMirror. Three reasons an entry lives here transiently:

  1. The OS create event raced ahead of the host’s OnAfterCreatedReportWindowOpened chain.
  2. The host couldn’t determine label_hint at create time.
  3. The HWND belongs to a pool window not yet promoted. Drained on each ReportWindowOpened (we try to match a pending HWND by label_hint/timing). Anything still here after a follow-up event is classified as HwndWithoutBrowser.
§just_promoted_labels: HashSet<String>

Drift-storm fix (PR #708 round 3) — labels for which the host emitted ReportPoolWindowPromoted but the corresponding ReportWindowOpened hasn’t arrived yet. The actual host emit order on tear-off is ReportPoolWindowRemovedReportPoolWindowPromotedReportWindowOpened, so at promote-time the launcher has NO WindowMirror for the label — the mirror is created by ReportWindowOpened a few ms later. Without this set, the post-promote mirror is initialized with foregrounded_since_open: false, the open-transient drift detector then fires HiddenSinceOpen on every visible→hidden flicker during HWND repositioning, the host fans each event out across the bridge and the renderer’s V8 isolate crashes. ReportWindowOpened consumes the entry to initialize the new mirror with foregrounded_since_open: true. Removed on ReportWindowClosed if open never arrived (bounded leak). See docs/specs/ANALYSIS_DRIFT_STORM_RENDERER_CRASH_2026-05-06.md.

Implementations§

Source§

impl State

Source

pub fn bump_version(&mut self) -> u64

Bump and return the new event version. Always called inside the reducer when constructing an Event so version numbers stay strictly monotonic.

Strict (non-wrapping) add: Phase D’s GetSnapshot resync protocol relies on monotonicity (event.version > snapshot.version), and a wrap to 0 would silently break that contract. u64 at one event/ns would take 584 years to overflow — never going to happen in practice; if it ever does, the panic is the right failure mode. (gemini MEDIUM PR #574 round-1.)

Source

pub fn alloc_client_id(&mut self) -> u64

Bump and return the next client_id. Client IDs are stable per launcher run; not persisted across restart. Same strict- add reasoning as bump_version.

Trait Implementations§

Source§

impl Debug for State

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for State

Source§

fn default() -> Self

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

Auto Trait Implementations§

§

impl Freeze for State

§

impl RefUnwindSafe for State

§

impl Send for State

§

impl Sync for State

§

impl Unpin for State

§

impl UnwindSafe for State

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.

§

impl<T> Instrument for T

§

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

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

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, 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.
§

impl<T> WithSubscriber for T

§

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
§

fn with_current_subscriber(self) -> WithDispatch<Self>

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