Skip to content

Rehydrate omits overrides and route_decisions (same gap as #149's snapshots) #153

Description

@P3st83

Found while reviewing #149. That PR reconstructs persisted snapshots into SnapshotArtefact on reload, which is correct, but the same rehydrate gap exists for two more collections, and there are two minor items from the same scan.

The rehydrate gap is wider than snapshots

_rehydrate_workspace in packages/coordinator-py/chap_coordinator/coordinator.py rebuilds members, tasks, whispers, deliberations, handoffs, audit into their dataclasses, and its exclusion set is:

if k not in {"members", "tasks", "whispers", "deliberations", "handoffs", "audit"}

But _snapshot_workspace persists via asdict(ws), which serialises every collection, and the Workspace dataclass also has typed overrides, snapshots, and route_decisions (types.py). So on reload, overrides and route_decisions are injected into Workspace(**ws_kwargs) as raw dicts, not OverrideArtefact / RouteDecisionArtefact.

  • snapshots is the live crash fix: rehydrate persisted snapshot artefacts before rollback #149 fixes (control.rollback reads snap.include).
  • overrides and route_decisions are the same omission. They are latent today, because nothing in the coordinator reads them back as typed objects after a reload (only len(ws.overrides) at coordinator.py:838; route_decisions is only ever written). But they come back as raw dicts, they diverge from the TypeScript restore() (which rebuilds all three into Maps), and they will fail the moment any attribute/to_dict() read is added, or if chap-analytics reads a restored store.

Suggested fix (extends #149): reconstruct all three, so the exclusion set becomes {members, tasks, overrides, whispers, deliberations, handoffs, snapshots, route_decisions, audit}, matching what the TypeScript restore() reconstructs.

Minor items from the same scan

  • audit.submit_to_scitt is not in _READ_ONLY_METHODS (coordinator.py), so dispatching it appends an entry to the very chain it is submitting. It reads and submits; it should not mutate the log.
  • control.supersede skips the paused-assignee check that task.create enforces (coordinator.py if ws.members[assignee].paused), so a successor task can be created for a paused participant.

Related cross-language divergences of the or-vs-?? family are in #152 (control.snapshot/control.rollback empty list) and #151 (handoff empty acceptance).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions