Skip to content

checkpoint: objects are saved by their class and attributes, pickle as the fallback - #40

Merged
endrix merged 1 commit into
mainfrom
feat/checkpoint-objects
Oct 1, 2026
Merged

endrix merged 1 commit into
mainfrom
feat/checkpoint-objects

Conversation

@endrix

@endrix endrix commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator

Follow-up to #39. A checkpoint refused any value that was not JSON, a File or a dataclass, so a run passing a task's own objects could not be resumed.

Encoding order (_checkpoint_runtime.encode):

  1. Builtins by exact type, with tags for tuple, set, frozenset, bytes, non-string-key dicts, Path, File/Resource, Enum members, datetime/date/time/timedelta, Decimal, UUID and complex. Because the match is exact, a Counter, a named tuple or an IntEnum is not flattened into its base.
  2. An object by name: {"$wfType": "object", "type": "module:Qualname", "state": {…}}, read back with cls.__new__ plus object.__setattr__ (frozen dataclasses and __slots__ included). It survives the class's code changing between the failure and the resume. This applies to classes findable by name that keep their attributes in a __dict__ or Python __slots__, are not built on a builtin container, and define no pickle hooks.
  3. Pickle as the fallback, for classes with their own __getstate__/__reduce__ and for subclasses of builtin containers.
  4. Not resumable otherwise (a lock, an open file, a generator, a lambda, a class defined in a function), with the value and reason named. A value the current code cannot read back fails the resume and names the checkpoint.

Also fixed: the overlay and run-record writers failed the run outright on such a token. They now write it as its repr (a dataclass as its fields).

Tests: round-trips for 30+ value kinds, each checked for both equality and exact type. Also: an object written by name rather than pickled, an object read back after its class changed, and a run passing objects that fails and resumes end to end. Full suite: 679 passed, 9 skipped.

https://claude.ai/code/session_015VK7fH1c4aKbexnq2QcuKU

…s the fallback

A checkpoint refused any value that was not JSON, a File or a dataclass,
so a run passing a task's own objects could not be resumed. Now an
object of a class that can be found by its name and keeps its
attributes in a __dict__ or Python __slots__ is written as its class
name and attributes, and read back by setting them on a bare object of
the class -- which survives the class's code changing between the
failure and its resume, the change a fix is. Pickle is the fallback,
for a class that says how it is saved itself or one built on a builtin
container. Builtins are matched by exact type, so a Counter, a named
tuple or an IntEnum is not flattened into its base; enums, datetimes,
Decimal, UUID and complex have their own tags. A lock, an open file, a
generator or a local class still make the checkpoint not resumable,
naming the value; a value the current code cannot read back fails the
resume, naming the checkpoint.

The overlay and run record writers failed the run outright on such a
token; they now write it as its repr (a dataclass as its fields).

Claude-Session: https://claude.ai/code/session_015VK7fH1c4aKbexnq2QcuKU
@endrix
endrix merged commit ba0c39a into main Oct 1, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant