10 KiB
| title | state | created_at | updated_at |
|---|---|---|---|
| Split Pod metadata into a dedicated pod-store crate | closed | 2026-05-29T20:58:44Z | 2026-05-30T00:10:45Z |
Background
The current persistence design intentionally has two durable surfaces:
- append-only session/segment logs, which are the authority for conversation/history state and segment lineage;
- name-keyed Pod metadata, which is the authority for Pod-name attach/restore pointers and durable spawned-child bookkeeping.
That boundary has become blurry. The session-store crate is named and documented primarily as session persistence, but it also owns Pod metadata types, the PodMetadataStore trait, validation of Pod names, and the filesystem layout {sessions_root}/pods/{pod_name}/metadata.json. In addition, Pod metadata currently stores spawned_children and resolved_manifest_snapshot, while session logs also store Pod scope snapshots as LogEntry::Extension entries. This creates a risk that session-log authority, Pod-state authority, and runtime mirrors drift or become hard to reason about.
This happened because earlier implementation work treated session-store as a convenient place for every durable file under the sessions root. That shape should not be extended. The chosen direction for this ticket is to split the durable surfaces into separate crates/APIs: session-store remains the session/segment JSONL store, and a new pod-store crate owns Pod metadata, Pod-name validation, and the Pod metadata filesystem layout.
Decisions
- Introduce a dedicated
pod-storecrate for durable Pod metadata/state. - Move Pod metadata storage from
{sessions_root}/pods/{pod_name}/metadata.jsonto a top-level Pod-state root such as{data_dir}/pods/{pod_name}/metadata.json. - Do not provide backward compatibility or migration for the obsolete
{sessions_root}/podslayout. Existing old-layout Pod metadata may be ignored/lost by this change. - Redesign the Pod metadata API where needed instead of preserving awkward
session-store-shaped APIs. - Keep session logs as the authority for conversation/history replay and for Pod lifecycle notifications actually shown to the model.
- Remove
pod.scope/ effective-scope snapshots from the session-log authority. Parent effective scope during restore should be derived frompod-storedelegation state, not from a duplicate session extension. - Keep runtime mirrors such as sockets, lock-file allocations, and
spawned_pods.jsonas live runtime views, not durable authority.
Pod metadata may point at a (SessionId, SegmentId), but the session log store must not own Pod metadata types or the Pod metadata filesystem layout. If sharing ID types directly causes an undesirable dependency, introduce a small shared ID module/crate or otherwise keep the dependency narrow; do not let pod-store pull in session replay concerns just to name a session pointer.
Observed code points:
crates/session-store/src/lib.rsdocuments session persistence via append-only JSONL logs, but also exportspod_metadatatypes.crates/session-store/src/fs_store.rsstores segment logs under{root}/{session_id}/{segment_id}.jsonland Pod metadata under{root}/pods/{pod_name}/metadata.jsonin the sameFsStore.crates/session-store/src/pod_metadata.rssays metadata is a lightweight name-keyed pointer, butPodMetadataalso includesspawned_childrenandresolved_manifest_snapshot.crates/pod/src/pod.rswrites Pod metadata from run/restore/fork/compact paths (write_pod_metadata_active,write_pod_metadata_pending) and preserves existingspawned_childrenvia a read-modify-write helper.crates/pod/src/spawn/registry.rstreats durable spawned-child state as living in Pod metadata and runtimespawned_pods.jsonas a live mirror, while scope snapshots for resume live in the session log.crates/tui/src/pod_list.rsreads{store_dir}/pods/*/metadata.jsondirectly in some paths rather than using only thePodMetadataStoretrait.
Goal
Refactor the architectural boundary between session logs, Pod metadata/state, and runtime mirrors so the storage APIs, crate boundaries, and filesystem layout match their authority boundaries, without changing intended restore/attach semantics for newly written state.
Desired boundary
The resulting design should make these responsibilities explicit:
- Session log authority:
- conversation history and system prompt replay;
- segment lineage (
forked_from,compacted_from); - request config / usage / metrics / memory extension records;
- Pod lifecycle notifications and restore/reclaim notices only when they are appended to history as information shown to the model;
- filesystem layout under the session log root, e.g.
{data_dir}/sessions/{session_id}/{segment_id}.jsonland associated trace logs.
- Pod metadata authority, owned by
pod-store:- Pod-name validation and safe filesystem key rules;
- name-keyed active
(SessionId, SegmentId)pointer; - pending/new Pod state if needed before a session segment is materialized;
- resolved manifest snapshot needed for Pod-name restore when the source profile/manifest should not be re-evaluated;
- spawned-child registry state, because it is current parent-Pod state rather than conversation history;
- delegated child scope records and delegation/reclaim history needed to derive parent effective scope during restore;
- restore reconciliation state sufficient to detect children that are missing, stopped, or unreachable and to reclaim their delegated scope before continuing;
- filesystem layout under a Pod-state root, e.g.
{data_dir}/pods/{pod_name}/metadata.json, not below the session log root.
- Runtime mirrors:
- sockets, lock-file allocations, and
spawned_pods.jsonare live runtime views, not durable authority; - socket paths and callback addresses, if retained in durable metadata, must be documented as last-known runtime hints rather than proof of liveness.
- sockets, lock-file allocations, and
Acceptance criteria
- Create a new
pod-storecrate and move Pod metadata types/store traits/filesystem implementation into it. - Remove
pod_metadataexports and Pod metadata filesystem ownership fromsession-store; updatesession-storecrate/module docs so it describes session/segment logs rather than Pod metadata. - Move the durable Pod metadata layout out of
{sessions_root}/pods/{pod_name}/metadata.jsonto a Pod-state root such as{data_dir}/pods/{pod_name}/metadata.json. - Do not implement compatibility fallback or migration for
{sessions_root}/pods; tests should assert the old path is not read or written as an authority. - Redesign the Pod metadata API where useful. At minimum, avoid caller-side read-modify-write helpers that can silently drop unrelated fields; provide explicit update/merge operations or otherwise make field-preservation semantics safe and testable.
- Update construction/configuration paths so callers pass distinct roots or distinct store handles for session logs and Pod metadata; sharing the same higher-level data directory is allowed, but the session log store must not own the Pod metadata subdirectory.
- Update
pod,tui, and other callers to depend on/usepod-storefor Pod metadata instead of importing Pod metadata throughsession-storeor reading metadata files directly. - Remove direct filesystem reads of
pods/*/metadata.jsonoutside thepod-storeabstraction, especially in TUI Pod list/discovery paths. - Document the new boundary in code comments and/or crate/module docs, including why Pod metadata points to session IDs rather than being contained by the session store.
- Clarify the authority of
resolved_manifest_snapshot: it belongs to Pod-name restore state inpod-store; session JSONLSegmentStartconfig/system prompt remain the authority for replaying an existing segment. - Clarify the authority of
spawned_children: it belongs to Pod-state/durable child-registry state inpod-store; child lifecycle messages shown to the model remain session JSONL history. - Clarify delegated scope handling: delegated-scope records and delegation/reclaim history live in
pod-store; parent effective scope during restore is derived from outstandingpod-storedelegations. Remove the duplicatepod.scopesession-log extension/typed restore state unless a narrower non-duplicating replacement is proven necessary. - Add restore reconciliation behavior: when
pod-storerecords a delegated child that is missing, stopped, or unreachable at restore time, reclaim the delegated scope inpod-store/runtime state and append a system notification to the session history before any model request observes the resumed state. - Preserve intended durable behavior for newly written state:
- Pod-name restore resolves active metadata from
pod-storethen restores the session log fromsession-store; - session restore uses session log conversation/history plus
pod-storedelegation state for Pod-scope reconciliation; - runtime
spawned_pods.jsonremains a mirror; - stopped or unreachable child Pod metadata is not deleted merely because its socket is gone.
- Pod-name restore resolves active metadata from
- Add focused tests for the split, including active pointer updates preserving spawned children / manifest snapshot, spawned-child updates preserving active pointer / manifest snapshot, and discovery/restore behavior when one durable surface exists without the other.
- Add or update tests that verify Pod metadata is read/written under the new Pod-state root and not under the session log root.
- Run focused validation for
session-store,pod-store,pod, andtui, plus./tickets.sh doctorandgit diff --check. - Update any relevant docs or workflow notes if the persistence model changes.
Non-goals
- Do not redesign the session-log schema unless the split proves it is necessary.
- Do not preserve backward compatibility for obsolete
{sessions_root}/podsmetadata, and do not implement a permanent fallback or migration path. - Do not change live Pod registry lock semantics except where necessary to align with the clarified durable authority.
- Do not implement broader database storage or transactional storage in this ticket; if the boundary audit reveals a need for transactions, record it as a follow-up unless a minimal update API suffices.