Files

164 lines
5.0 KiB
Markdown

# 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))
```