95b6ed57c2
ISSUES CLOSED: #303
88 lines
2.9 KiB
Markdown
88 lines
2.9 KiB
Markdown
# Diff Review
|
|
|
|
The diff review module builds reviewable diff artifacts from `SpecChangeSet`
|
|
records, grouping changes by resource and producing unified diff text for
|
|
CLI display.
|
|
|
|
## Overview
|
|
|
|
When a plan accumulates changes through tool executions, the diff review
|
|
system transforms the raw `ChangeEntry` records into a structured
|
|
`ReviewArtifact` suitable for human review before applying changes.
|
|
|
|
## Domain Models
|
|
|
|
### DiffEntry
|
|
|
|
A single diff entry for CLI review. Contains:
|
|
|
|
| Field | Type | Description |
|
|
|---|---|---|
|
|
| `resource_id` | `str` | Resource ULID |
|
|
| `resource_name` | `str` | Human-readable resource name |
|
|
| `path` | `str` | File path |
|
|
| `change_type` | `str` | Operation type (create/modify/delete/rename) |
|
|
| `before_content` | `str \| None` | Content before the change |
|
|
| `after_content` | `str \| None` | Content after the change |
|
|
| `before_hash` | `str \| None` | SHA-256 before |
|
|
| `after_hash` | `str \| None` | SHA-256 after |
|
|
| `file_mode` | `int \| None` | File permission mode |
|
|
| `is_binary` | `bool` | Whether the file is binary |
|
|
| `is_rename` | `bool` | Whether this is a rename/move |
|
|
| `rename_from` | `str \| None` | Original path for renames |
|
|
| `rename_to` | `str \| None` | Destination path for renames |
|
|
| `diff_text` | `str` | Unified diff text |
|
|
| `truncated` | `bool` | Whether the diff text was truncated |
|
|
|
|
### ResourceDiffGroup
|
|
|
|
Groups diff entries for a single resource. Provides summary counts
|
|
via properties: `added`, `modified`, `deleted`, `renamed`.
|
|
|
|
### ReviewArtifact
|
|
|
|
Complete diff review artifact for a plan. Contains grouped diff entries,
|
|
total change count, and truncation status.
|
|
|
|
## DiffBuilder
|
|
|
|
`DiffBuilder` takes a `SpecChangeSet` and optional content maps to produce
|
|
a `ReviewArtifact`.
|
|
|
|
```python
|
|
from cleveragents.domain.models.core.diff_review import DiffBuilder
|
|
|
|
builder = DiffBuilder(max_diff_size=50_000, context_lines=3)
|
|
artifact = builder.build(
|
|
changeset,
|
|
before_contents={"src/main.py": "old content"},
|
|
after_contents={"src/main.py": "new content"},
|
|
resource_names={"RES001": "my-resource"},
|
|
)
|
|
```
|
|
|
|
### Features
|
|
|
|
- **Unified diff generation** using `difflib.unified_diff`
|
|
- **Binary detection** via null byte checking
|
|
- **Rename detection** using `SequenceMatcher` path similarity (>0.6)
|
|
combined with matching content hashes
|
|
- **Truncation** when total diff text exceeds `max_diff_size` (applies
|
|
uniformly to all entry types including renames)
|
|
- **Stable ordering** by resource ID, then path within each group
|
|
|
|
## DiffSerializer
|
|
|
|
Renders a `ReviewArtifact` to various output formats:
|
|
|
|
- `to_plain(artifact)` — plain text for terminal output
|
|
- `to_json(artifact)` — structured JSON for programmatic use
|
|
- `to_rich(artifact)` — rich markup with `[bold]`, `[green]`, `[red]` tags
|
|
|
|
## Constants
|
|
|
|
| Name | Default | Description |
|
|
|---|---|---|
|
|
| `DEFAULT_MAX_DIFF_SIZE` | 50,000 | Maximum total diff text characters |
|
|
| `RENAME_SIMILARITY_THRESHOLD` | 0.6 | Minimum path similarity for rename detection |
|