symfonic.capabilities.memory.cycle_state¶
cycle_state ¶
What one consolidation cycle did, in three vocabularies.
Split from :mod:symfonic.capabilities.memory.consolidation so the record is
readable apart from the runtime that fills it, and because the combined
module went over its 300-line budget once the cycle ledger was added.
Three vocabularies, because three readers ask different questions:
- :attr:
ConsolidationState.mutations— what each phase reported. The capability's own terms, keyed by roster name. - :meth:
ConsolidationState.to_legacy_dict— the shippedConsolidationReport.to_dict()shape, so a dashboard written against the old report keeps working while the runtime under it changes. - :meth:
ConsolidationState.telemetry— the safe ledger. Integers and roster names, nothing else: no memory text, no embedding, no metadata, no phase argument. A consolidation reads a tenant's memories, so its telemetry is the one place a stray f-string would publish them.
ConsolidationState
dataclass
¶
ConsolidationState(scope_path: str, cycle: ConsolidationCycle, tenant_id: str = '', started_at: datetime = (lambda: datetime.now(UTC))(), finished_at: datetime | None = None, run_id: str = '', root_run_id: str = '', registered: tuple[str, ...] = (), phases_run: tuple[str, ...] = (), skipped: tuple[str, ...] = (), failed: tuple[str, ...] = (), deferred: tuple[str, ...] = (), mutations: dict[str, int] = dict(), ledger: dict[str, int] = dict(), counters: dict[str, int] = dict(), errors: tuple[str, ...] = (), committed: tuple[str, ...] = (), already_running: bool = False, lease_lost: bool = False)
What one cycle did — in this capability's terms and in legacy's.
status
property
¶
The cycle's final word.
running, clean, degraded -- and two more that are none of
those and must not be reported as any of them.
already_running: another holder had the scope, so this cycle never
started. Saying clean would make "somebody else is consolidating
this" indistinguishable from "there was nothing to consolidate", and a
scheduler reading a dashboard would conclude the cycle had run.
lease_lost: this cycle started, then lost the scope partway. Not
degraded either, though it is a kind of failure, because the two
want opposite responses: degraded is a phase that broke and wants
looking at, while lease_lost is a worker that correctly stood down
so another one could do the work properly. Alerting on the second is
alerting on the mechanism working. Ranked above degraded because a
cycle that loses its lease also collects the fence's error, and the
specific fact is the useful one.
telemetry ¶
The safe record of this cycle: integers, roster names, and a status.
Everything here is either a framework constant or a count. The scope
is not: tenant_id identifies whose consolidation this was, which
is what makes the model cost a phase spends attributable, and it is
already the key every other metric in the system carries.
Source code in src/symfonic/capabilities/memory/cycle_state.py
to_legacy_dict ¶
The shipped ConsolidationReport.to_dict() shape, plus this cycle.
Every legacy key is present with its legacy type, so a reader written
against the old report needs no change. The additions
(cycle, scope_path, skipped, committed) are new keys,
which a dict consumer ignores.
Source code in src/symfonic/capabilities/memory/cycle_state.py
already_running ¶
The record a worker that lost the lease returns.
A record rather than None: the caller needs to know why nothing
happened, and an absent answer reads exactly like a cycle that ran and
found nothing to do. Finished, because this worker is finished; empty
everywhere else, because it did nothing.
Source code in src/symfonic/capabilities/memory/cycle_state.py
merge_ledger ¶
Add one phase's counters into the cycle's, refusing anything unsafe.