Skip to main content

Module process

Module process 

Source
Expand description

Process Broker — Phase A of the process-tracking consolidation.

See docs/specs/REPORT_PROCESS_ARCHITECTURE_STATE_AND_RETHINK_2026_07_22.md for the full rationale. AgentMux answers “is this agent/process alive and what’s it doing” six different, only-partially-overlapping ways today (blockcontroller::CONTROLLER_REGISTRY, process_tracker, the reactive handler’s own registration list, pidregistry, TurnActivityTracker’s turn-active bookkeeping, and watchdog.rs’s PTY-idle timers). This module is the consolidation point for the two callers that most directly motivated it (the Agent pane’s process badge and the Swarm pane’s block discovery/overview), modeled on broker::scheduler’s Credential Broker (same crate, sibling module — same “one broker, register your own backing store” pattern applied to a second domain) and on agentmux-srv::reducer’s existing discipline of computing state through one arbiter with explicit transition logic rather than letting every caller mutate a shared map directly (see lifecycle_from below, which mirrors reducer/lifecycle.rs::handle_register’s shape: pure function, explicit rule per legal state, no bare field copy).

Phase A scope: read-side only. status()/list() compute a normalized ProcessStatus by querying today’s existing sources (blockcontroller for lifecycle — authoritative, has an entry for every controller type; process_tracker for OS-process detail — only populated for subprocess/persistent controllers today) and cache the result, single-flight-guarded per call. This closes the concrete, reported bug directly: agent.tracked-blocks’s .chain() of process_tracker + the reactive handler’s registration list (two structurally different registries unioned with no reconciliation) is replaced by blockcontroller::get_all_controllers(), which is already authoritative for “which blocks exist” across every controller type.

Deferred to later phases (see the report’s §6 open questions): migrating each Controller impl to register with this broker directly at spawn (closing the coverage gap at the write side, not just papering over it at the read side); consolidating the two independent activity-summary pipelines; moving the Swarm pane’s client-side-only fine-grained activity chip server-side. None of that is touched here.

Structs§

ProcessBroker
ProcessStatus
The broker’s public read shape — one struct every consumer (Agent pane, Swarm pane, future consumers) reads instead of composing raw registry data themselves.

Enums§

Lifecycle
Coarse “is it alive” question, normalized from BlockControllerRuntimeStatus’s turn_active/shellprocstatus/shellprocexitcode fields into one enum — the Pod-phase-equivalent signal in the Kubernetes phase/status/probe analogy the report draws in §4.

Constants§

EVENT_STATUS_CHANGED
MPS event name for a ProcessStatus change. Scoped block:<id>, same convention as agent:process-added/controllerstatus. Consumers that want the unified signal subscribe to this instead of the two separate events the old .chain()-based discovery relied on.
EVENT_TRACKED_BLOCKS_CHANGED
MPS event name for “list()/list_agent_panes() membership may have changed” — published whenever blockcontroller::register_controller or delete_controller runs. Unscoped (no block:<id> — see emit_tracked_blocks_changed’s doc comment for why).

Statics§

GLOBAL 🔒

Functions§

compute_status 🔒
Compute a block’s current ProcessStatus from today’s underlying sources. Pure with respect to its two inputs — the only I/O is the two read calls themselves (both cheap in-memory lock acquisitions, not blocking I/O — process_tracker’s registry and blockcontroller’s CONTROLLER_REGISTRY are both parking_lot/std sync locks guarding plain maps).
global
lifecycle_from 🔒
Pure reducer over one block’s raw controller status: decides Lifecycle via explicit per-case rules, not a bare field copy. Isolated from I/O so the transition rules are unit-testable without a real controller instance — mirrors reducer/lifecycle.rs::handle_register’s shape (compute the legal next state from the current observation, rather than trusting the caller’s intent unconditionally).
set_global