# ChangeSet Model Reference The ChangeSet domain model captures all file changes made by tools during the Execute phase. It is the foundation for plan diff, review, and apply workflows. ## Overview | Model | Purpose | |---|---| | `ChangeOperation` | Enum of operation types (CREATE, MODIFY, DELETE, RENAME) | | `ChangeEntry` | Single recorded mutation with content hashes and metadata | | `SpecChangeSet` | Collection of entries for a plan, with computed summaries | | `ChangeSetStore` | Protocol for persisting and querying changesets | | `InMemoryChangeSetStore` | In-memory implementation for M1 milestone | ## ChangeOperation ```python from cleveragents.domain.models.core.change import ChangeOperation ChangeOperation.CREATE # "create" — new file ChangeOperation.MODIFY # "modify" — content changed ChangeOperation.DELETE # "delete" — file removed ChangeOperation.RENAME # "rename" — file moved/renamed ``` ## ChangeEntry Each `ChangeEntry` represents a single tool-caused mutation. ### Fields | Field | Type | Description | |---|---|---| | `entry_id` | `str` | Auto-generated ULID uniquely identifying this entry | | `plan_id` | `str` | ULID of the plan that owns this change | | `resource_id` | `str` | ULID of the resource affected | | `tool_name` | `str` | Namespaced tool name (e.g. `builtin/file-write`) | | `operation` | `ChangeOperation` | Type of change | | `path` | `str` | Repo-relative file path | | `before_hash` | `str \| None` | SHA-256 of file before change (`None` for create) | | `after_hash` | `str \| None` | SHA-256 of file after change (`None` for delete) | | `before_mode` | `int \| None` | File mode before change | | `after_mode` | `int \| None` | File mode after change | | `timestamp` | `datetime` | UTC timestamp of the change | ### ULID Fields The `entry_id` and `plan_id` fields use [ULIDs](https://github.com/ulid/spec) (Universally Unique Lexicographically Sortable Identifiers). ULIDs are 128-bit identifiers that encode a timestamp and random component, making them sortable by creation time while remaining globally unique. ``` 01ARZ3NDEKTSV4RRFFQ69G5FAV └─────────┘└────────────┘ timestamp randomness (48 bit) (80 bit) ``` ### Example ```python from cleveragents.domain.models.core.change import ( ChangeEntry, ChangeOperation, ) entry = ChangeEntry( plan_id="01HXYZ123456789ABCDEF", resource_id="01HXYZ000000000000001", tool_name="builtin/file-write", operation=ChangeOperation.CREATE, path="src/models/user.py", after_hash="e3b0c44298fc1c149afbf4c8996fb924...", ) ``` ## SpecChangeSet A `SpecChangeSet` groups all `ChangeEntry` records for a single plan execution. ### Fields | Field | Type | Description | |---|---|---| | `changeset_id` | `str` | Auto-generated ULID for the changeset | | `plan_id` | `str` | ULID of the plan | | `entries` | `list[ChangeEntry]` | Ordered list of change entries | | `created_at` | `datetime` | UTC timestamp when created | ### Computed Properties | Property | Type | Description | |---|---|---| | `creates` | `int` | Count of CREATE entries | | `modifies` | `int` | Count of MODIFY entries | | `deletes` | `int` | Count of DELETE entries | | `renames` | `int` | Count of RENAME entries | | `paths_changed` | `set[str]` | Unique file paths affected | | `resources_involved` | `set[str]` | Unique resource IDs | ### Example ```python from cleveragents.domain.models.core.change import ( ChangeEntry, ChangeOperation, SpecChangeSet, ) cs = SpecChangeSet( plan_id="01HXYZ123456789ABCDEF", entries=[ ChangeEntry( plan_id="01HXYZ123456789ABCDEF", resource_id="01HXYZ000000000000001", tool_name="builtin/file-write", operation=ChangeOperation.CREATE, path="src/new.py", ), ], ) print(cs.creates) # 1 print(cs.paths_changed) # {'src/new.py'} print(cs.summary()) # {'total': 1, 'creates': 1, ...} ``` ## ChangeSetStore The `ChangeSetStore` protocol defines the interface for changeset persistence: ```python class ChangeSetStore(Protocol): def start(self, plan_id: str) -> str: ... def record(self, changeset_id: str, entry: ChangeEntry) -> None: ... def get(self, changeset_id: str) -> SpecChangeSet | None: ... def get_for_plan(self, plan_id: str) -> list[SpecChangeSet]: ... def summarize(self, changeset_id: str) -> dict: ... ``` ### InMemoryChangeSetStore The `InMemoryChangeSetStore` is the M1 implementation that stores changesets in a plain Python dict. It is suitable for single-process tests and the M1 runtime. ```python from cleveragents.domain.models.core.change import ( ChangeEntry, ChangeOperation, InMemoryChangeSetStore, ) store = InMemoryChangeSetStore() cs_id = store.start("01HXYZ123456789ABCDEF") store.record(cs_id, ChangeEntry( plan_id="01HXYZ123456789ABCDEF", resource_id="01HXYZ000000000000001", tool_name="builtin/file-write", operation=ChangeOperation.CREATE, path="src/new.py", )) cs = store.get(cs_id) print(store.summarize(cs_id)) ```