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 a head_id

  • _current_timeline / _current_head: current branch and cursor for time travel

  • Snapshots store all_states (scope_id → StateSnapshot) plus parent links for branching

Core operations

  • record_snapshot(label, scope_id=None) (internal): create a new snapshot and prune unreachable history

  • time_travel_back(): move to the parent snapshot in the current branch

  • time_travel_forward(): move to the child if unique; otherwise stay

  • time_travel_to_snapshot(id) / time_travel_to(index): jump arbitrarily in history

  • create_branch(name) / switch_branch(name): multi-branch history

  • atomic(label) context manager: batch multiple changes into one undo step

  • export_history_to_dict() / import_history_from_dict(): serialize/restore history

  • save_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)

# Back/forward time travel
ObjectStateRegistry.time_travel_back()
ObjectStateRegistry.time_travel_forward()

# Persist history
history = ObjectStateRegistry.export_history_to_dict()
ObjectStateRegistry.import_history_from_dict(history)