You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
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.
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.pyif 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).
Found while reviewing #149. That PR reconstructs persisted
snapshotsintoSnapshotArtefacton 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_workspaceinpackages/coordinator-py/chap_coordinator/coordinator.pyrebuildsmembers,tasks,whispers,deliberations,handoffs,auditinto their dataclasses, and its exclusion set is:But
_snapshot_workspacepersists viaasdict(ws), which serialises every collection, and theWorkspacedataclass also has typedoverrides,snapshots, androute_decisions(types.py). So on reload,overridesandroute_decisionsare injected intoWorkspace(**ws_kwargs)as raw dicts, notOverrideArtefact/RouteDecisionArtefact.snapshotsis the live crash fix: rehydrate persisted snapshot artefacts before rollback #149 fixes (control.rollbackreadssnap.include).overridesandroute_decisionsare the same omission. They are latent today, because nothing in the coordinator reads them back as typed objects after a reload (onlylen(ws.overrides)atcoordinator.py:838;route_decisionsis only ever written). But they come back as raw dicts, they diverge from the TypeScriptrestore()(which rebuilds all three into Maps), and they will fail the moment any attribute/to_dict()read is added, or ifchap-analyticsreads 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 TypeScriptrestore()reconstructs.Minor items from the same scan
audit.submit_to_scittis 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.supersedeskips the paused-assignee check thattask.createenforces (coordinator.pyif 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.rollbackempty list) and #151 (handoff empty acceptance).