Undo / Redo
The undo/redo system in ObjectStateRegistry is a git-like DAG of snapshots, not just a
linear stack. Snapshots capture all ObjectStates at a point in time.
Data model
_snapshots: Dict[str, Snapshot]: all snapshots (UUID → Snapshot)_timelines: Dict[str, Timeline]: named branches; each has ahead_id_current_timeline/_current_head: current branch and cursor for time travelSnapshots store
all_states(scope_id → StateSnapshot) plus parent links for branching
Core operations
record_snapshot(label, triggering_scope=None)(internal): create a new snapshot, prune unreachableundo(): move to parent snapshot in the current branchredo(): move to child if unique; otherwise staytime_travel_to_snapshot(id)/time_travel_to(index): jump arbitrarily in historycreate_branch(name)/switch_branch(name): multi-branch historyatomic(label)context manager: batch multiple changes into one undo stepexport_history_to_dict()/import_history_from_dict(): serialize/restore historysave_history_to_file(path)/load_history_from_file(path): JSON persistence
Behavior
Recording from a non-head position creates a new branch (preserves old future as an auto-branch).
Undo/redo/time travel rewires the live registry: registers/unregisters ObjectStates to match the snapshot and restores saved/live resolved values.
History size is bounded by
_max_history_size; unreachable snapshots are pruned.
Example
from objectstate import ObjectStateRegistry
# Single undo step for a grouped change
with ObjectStateRegistry.atomic("add item"):
ObjectStateRegistry.register(item_state)
parent_state.update_parameter("items", new_items)
# Undo/redo
ObjectStateRegistry.undo()
ObjectStateRegistry.redo()
# Persist history
history = ObjectStateRegistry.export_history_to_dict()
ObjectStateRegistry.import_history_from_dict(history)