pub struct RegistryEntry {Show 30 fields
pub name: String,
pub short_id: String,
pub legacy_provider: String,
pub cwd: String,
pub project_root: String,
pub session_id: Option<String>,
pub claude_session_uuid: Option<String>,
pub harness: Option<String>,
pub harness_session_id: Option<String>,
pub messaging_socket_path: Option<String>,
pub codex_session_id: Option<String>,
pub gemini_session_id: Option<String>,
pub mcp_channel_id: Option<String>,
pub host_mode: Option<String>,
pub cc_session_id: Option<String>,
pub status: AgentStatus,
pub last_message_at: Option<String>,
pub created_at: String,
pub pid: Option<u32>,
pub pid_start_time: Option<u64>,
pub log_path: Option<String>,
pub last_reconciled_at: Option<String>,
pub inside_leg: Option<InsideLegReport>,
pub exited_at: Option<String>,
pub mux: Option<MuxRef>,
pub screen_state: Option<ScreenStateReport>,
pub crown_level: Option<u32>,
pub crown_scope: Option<String>,
pub crown_grantor: Option<String>,
pub legacy_claude_short_id: Option<String>,
}Expand description
One registry row (design schema v6). Optional fields default to None and
are preserved across update_registry because the whole row round-trips
through this typed struct.
Fields§
§name: String§short_id: StringDaemon-set PTY field. Python’s AgentEntry now mirrors it as
short_id: str = "" (ab-b946b59c) so a real PTY row in a mixed registry
is Python-readable and round-trips losslessly; skip_serializing_if
still drops it when empty so a Rust-authored exec/ask row stays slim and
a round-tripped Python row omits it (default-to-empty on read, ab-e5a57efa;
Codex P1, PR #364). A real daemon PTY agent always has a non-empty
short_id, so it still serializes for those rows; conversely a one-shot
ask row always has an empty short_id (no worker-socket identity). That
exclusivity is what RegistryEntry::is_one_shot_ask keys on – a
non-empty short_id on an ask row, or an empty one on a PTY row, is a
producer bug. (Python mirrors with a str default, not Option, because
a "short_id": null would fail this String field’s deserialize.)
legacy_provider: Stringv10 backfill-only (x-880e): the removed on-disk provider key. Deserialized
under its old name so a legacy row’s identity survives the read, but NEVER
serialized – RegistryEntry::backfill_harness_aliases moves it into
harness at load. This is the Rust mirror of Python’s load_registry
popping provider. harness is the sole on-disk identity axis.
cwd: String§project_root: StringDaemon-set PTY field, mirrored in Python’s AgentEntry as
project_root: str = "" (ab-b946b59c; see short_id): default on read,
skip-when-empty on write.
session_id: Option<String>On disk this is Rust-set only (Python’s session_id is a computed
@property, excluded from its serialized rows): skip when absent so
Python can read a Rust-written row (Codex P1). When a Rust PTY row DOES
record one, Python’s load_registry drops the key before constructing the
entry and recomputes the same projection from the *_session_id fields
(ab-b946b59c).
claude_session_uuid: Option<String>The FULL claude session UUID – the stream-json --resume target,
distinct from the 8-hex jobId in short_id. v10 (x-880e): a load-derived
in-memory alias only. skip_serializing keeps it off disk (harness_session_id
is the sole persisted session id); backfill_harness_aliases populates it
from harness_session_id on load, so the ~30 daemon read sites need no churn.
A post-load mutation of this field is synced back into harness_session_id
at the write choke point (AC6-FR). [stream-json host lane node]
harness: Option<String>Canonical harness identity (x-ec59), mirroring Python’s AgentEntry:
harness is the harness name (identity only – provider stays
load-bearing for dispatch) and harness_session_id is the worker’s own
session id in its harness’s store. Both additive-optional, back-filled
from the legacy per-provider fields at load via
RegistryEntry::backfill_harness_aliases so a Rust reader of a legacy
row and a Python reader of a Rust-minted canonical row both resolve.
Skip-when-None keeps a Rust-authored row slim; Python’s asdict always
emits the key, so a Python row round-trips fine.
harness_session_id: Option<String>§messaging_socket_path: Option<String>Daemon-set PTY field, mirrored in Python’s AgentEntry (ab-b946b59c):
skip when absent (Codex P1).
codex_session_id: Option<String>§gemini_session_id: Option<String>§mcp_channel_id: Option<String>§host_mode: Option<String>Hosting mode: absent/None == "exec" (one-shot, the default for every
pre-existing row), Some("interactive") == a long-lived drivable TUI
(fno agents host/promote). Skip-when-None so a Rust-authored exec
row omits the key; Python’s missing-key coercion then maps the absence
back to "exec". (Python itself always emits the key via asdict – as
"exec" or "interactive" – and Rust reads the concrete value fine, so
both directions agree.) Consumers must read it via
RegistryEntry::host_mode_or_default, never the raw Option, so the
absent==exec rule lives in one place. [interactive-drive node]
cc_session_id: Option<String>Daemon-set PTY field, mirrored in Python’s AgentEntry (ab-b946b59c):
skip when absent (Codex P1).
status: AgentStatus§last_message_at: Option<String>§created_at: String§pid: Option<u32>Daemon-set PTY field, mirrored in Python’s AgentEntry as
pid: Optional[int] (ab-b946b59c): skip when absent so a round-tripped
Python row stays slim and Python-readable (Codex P1).
pid_start_time: Option<u64>The worker process’s start time, captured alongside pid at spawn, used
to detect PID reuse: a liveness/reap/signal decision treats pid as “our
worker” only if the live process’s start time still matches this
(ab-d19e6458). Per-host, per-boot value (Linux: /proc/<pid>/stat field
22 in clock ticks; macOS: kinfo_proc start timeval in microseconds) —
only ever compared for equality against a fresh read of the SAME pid, so
the unit/epoch difference across platforms is irrelevant. Daemon-set PTY
field, mirrored in Python’s AgentEntry (ab-b946b59c); skip when absent.
log_path: Option<String>§last_reconciled_at: Option<String>Timestamp of the most recent reconcile probe (finding #1 High): the
reconcile sweep orders entries by ASC last_reconciled_at so a
budget-exhausted sweep stays fair across a large registry. Daemon-set,
mirrored in Python’s AgentEntry (ab-b946b59c); skip when absent (Codex P1).
inside_leg: Option<InsideLegReport>Latest inside-leg report for this row’s claude pane (inside-out E3,
contract v2). None for every non-inside-leg row (the default for every
pre-existing row, and for any provider/lane that does not run a hook).
Skip-when-None so a row without a report stays slim and a stale reader
rejects via the v5 schema bump rather than silently dropping it. Mirrored
in Python’s AgentEntry as inside_leg: Optional[dict] (X3 / ab-b946b59c).
exited_at: Option<String>When the dead-row GC first observed this row’s backing process as gone
(ISO 8601 UTC), stamped by the GC sweep on the first tick it sees the row
terminal/dead and cleared again if the row re-registers live (x-b1aa). It
anchors the config.agents.dead_row_grace window: a row is reaped only
once now - exited_at is strictly past the grace. Deliberately NOT set at
the status->Exited transition (reconcile re-stamps last_reconciled_at on
every probe, so that field can’t anchor a stable clock); the GC’s
first-observation stamp is stable until the row is reaped or resurrected.
Daemon-set, mirrored in Python’s AgentEntry as exited_at; skip when
absent so a pre-GC row round-trips losslessly (additive-optional, no
schema bump).
mux: Option<MuxRef>The mux hosting ref for a pane-substrate agent (4a-G2): Some means
this row’s PTY is a pane in mux.session, and pane-exit facts /
live-inject / sideline badges all key on it. None for every daemon
worker, bg-thread, and headless row. One live ref per row (mux XOR
worker XOR bg) - see MuxRef and validate_single_live_ref.
Skip-when-None so a pre-mux row stays slim; a stale reader rejects
via the v6 schema bump rather than silently dropping the ref. Mirrored
in Python’s AgentEntry as mux: Optional[dict] (X3).
screen_state: Option<ScreenStateReport>Latest screen-manifest verdict for this row’s mux pane (v7, the
fallback rung under the hook). Daemon-scrape-set, and mutually
exclusive with a live inside_leg authority BY THE WRITER (the sweep
skips hook-bearing rows; the inside-leg store clears this field on the
capability flip) - readers still treat inside_leg as unconditionally
senior, defense in depth. Skip-when-None so an unscraped row stays
slim and a stale reader rejects via the v7 bump rather than silently
dropping a verdict. Mirrored in Python’s AgentEntry as
screen_state: Optional[dict] (X3).
crown_level: Option<u32>Crown fields (US9, v11): who holds an orchestrator crown and at what
altitude. The Python spawn path is the sole writer (grantor-stamped,
never self-declared); the daemon only custodies them so a spawn-stamped
crown round-trips losslessly across a read-modify-write - the same X3
passthrough treatment as inside_leg/screen_state. Skip-when-None
keeps a Rust-authored uncrowned row slim; Python’s asdict always emits
the keys, so a crowned Python row round-trips fine. Crown liveness ==
this row’s liveness (no separate lifecycle).
crown_scope: Option<String>§crown_grantor: Option<String>§legacy_claude_short_id: Option<String>v9 backfill-only (x-1b1e): the removed claude_short_id. Deserialized
(under its old key) so a legacy row’s jobId survives the read, but NEVER
serialized – RegistryEntry::backfill_short_id moves it into
short_id at load and clears it, so it never round-trips. This is the
Rust mirror of Python’s load_registry popping claude_short_id from the
raw row. Not part of identity; no consumer reads it directly.
Implementations§
Source§impl RegistryEntry
impl RegistryEntry
Sourcepub fn backfill_harness_aliases(&mut self)
pub fn backfill_harness_aliases(&mut self)
Two-way sync of harness/harness_session_id with the legacy
per-provider identity fields (x-ec59), the Rust mirror of Python’s
harness_identity.sync_harness_aliases + the registry harness back-fill.
Applied at load so a Rust reader of a legacy row and a Python reader of a
Rust-minted canonical row both resolve. harness adopts provider when
absent (provider is always set; harness is identity-only, never gates the
read). Then canonical wins: a set harness_session_id syncs the matching
legacy key (a conflicting legacy value is overwritten, never leaked);
otherwise the first present legacy value back-fills harness_session_id.
The claude legacy key is claude_session_uuid (the registry’s identity),
NOT the manifest’s claude_session_id.
Sourcepub fn backfill_short_id(&mut self) -> Option<String>
pub fn backfill_short_id(&mut self) -> Option<String>
v9 transport-key backfill (x-1b1e), the Rust mirror of Python’s
load_registry popping the removed claude_short_id into short_id.
Applied at load, before validate_single_live_ref: a legacy row’s
jobId (deserialized into legacy_claude_short_id) moves into an empty
short_id and the transient is cleared so it never round-trips. A
conflicting pair (both set, different values – the drift this removal
kills) KEEPS short_id and returns the legacy value so the caller can
warn once; it never silently prefers the legacy value.
Sourcepub fn transport_short(&self) -> Option<&str>
pub fn transport_short(&self) -> Option<&str>
The provider transport key (v9, x-1b1e), or None when this row has
none: the non-empty short_id. For claude it is the jobId (claude attach/logs <jobId>); for a daemon PTY row the worker-socket key. The
single accessor consumers use to reach a session’s wire handle, so no
verb re-implements the empty-string guard. [x-1b1e transport extraction]
Sourcepub fn harness_name(&self) -> &str
pub fn harness_name(&self) -> &str
The row’s harness name as a required-string view (x-880e). The single
accessor every RegistryEntry consumer uses instead of the raw identity
field, so the provider->harness migration touches one place. harness is
set on load by RegistryEntry::backfill_harness_aliases; during the
migration window a not-yet-backfilled fresh row falls back to the legacy
provider. Collapses to harness-only once provider is removed.
Sourcepub fn host_mode_or_default(&self) -> &str
pub fn host_mode_or_default(&self) -> &str
The hosting mode with the absent==exec rule applied in one place.
None on disk (and the legacy rows that predate the field) read as
HOST_MODE_EXEC; an explicit value passes through. Reconcile/liveness
and the spawn path must use this, never the raw Option, so a missing
key can never be mistaken for a non-exec mode. [interactive-drive node]
Sourcepub fn is_interactive(&self) -> bool
pub fn is_interactive(&self) -> bool
True when this row is a long-lived interactive host (vs a one-shot exec
session). The reconcile branch keys off this: an exec worker that exited
is normal; an interactive worker is expected to stay live until /quit.
Sourcepub fn is_one_shot_ask(&self) -> bool
pub fn is_one_shot_ask(&self) -> bool
True when this row is a one-shot ask agent the daemon does NOT manage as
a worker process: empty short_id (no worker-socket identity) AND no
recorded pid. Such an agent has no process whose liveness could make it
live – its terminal status is exited, and its post-run value is
resumability (a recorded provider session id), surfaced separately from
status via the session_id projection. Only PTY agents (spawn/host/
promote) carry a non-empty short_id + pid and can be live; this is the
invariant documented on the short_id field (“a real daemon PTY agent
always has a non-empty short_id”). Reconcile uses this to settle a
finished ask to exited by process-liveness alone, never consulting
session-file reachability for status. [plan ab-70faa65b, Locked Decision #1]
Trait Implementations§
Source§impl Clone for RegistryEntry
impl Clone for RegistryEntry
Source§fn clone(&self) -> RegistryEntry
fn clone(&self) -> RegistryEntry
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more