Files
freemo 9f2e7e88b0 feat(plan): add diff review and apply integration
Add PlanApplyService with diff(), artifacts(), persist_apply_summary(),
handle_merge_failure(), and guard_empty_changeset() methods.

- plan diff: renders changeset in rich/plain/json/yaml formats
- plan artifacts: shows plan metadata, changeset summary, sandbox refs
- persist_apply_summary: stores files_changed/validations_run in plan metadata
- handle_merge_failure: transitions plan to error state with conflict details
- guard_empty_changeset: blocks apply on empty changeset (--allow-empty override)

CLI: plan diff and plan artifacts commands with --format flag.
Tests: 20 Behave scenarios, 8 Robot integration tests, 4 ASV benchmarks.
Docs: docs/reference/plan_apply.md with CLI reference.

Implements D0b.apply from the implementation plan.
2026-02-21 10:22:18 -05:00

147 lines
4.2 KiB
Markdown

# Plan Diff & Apply Reference
This document describes the diff review and apply integration features
added in D0b.apply.
## CLI Commands
### `agents plan diff <plan_id>`
Show the ChangeSet produced during Execute as a unified diff, grouped
by resource path.
**Options:**
| Flag | Description |
|------|-------------|
| `--format`, `-f` | Output format: `rich` (default), `plain`, `json`, `yaml` |
**Examples:**
```bash
# Rich output with coloured operation labels
agents plan diff 01HXYZ...
# Plain unified-diff output
agents plan diff 01HXYZ... --format plain
# Machine-readable JSON
agents plan diff 01HXYZ... --format json
```
**Output fields (JSON/YAML):**
| Field | Type | Description |
|-------|------|-------------|
| `changeset_id` | string | ULID of the ChangeSet |
| `plan_id` | string | ULID of the plan |
| `total_changes` | int | Number of change entries |
| `summary` | object | `{creates, modifies, deletes, renames, paths_changed, resources_involved}` |
| `entries` | list | Per-file entries with `path`, `operation`, hashes, timestamps |
### `agents plan artifacts <plan_id>`
Show plan artifacts: ChangeSet metadata, sandbox references, file
change list, and validation results.
**Options:**
| Flag | Description |
|------|-------------|
| `--format`, `-f` | Output format: `rich` (default), `plain`, `json`, `yaml` |
**Output fields (JSON/YAML):**
| Field | Type | Description |
|-------|------|-------------|
| `plan_id` | string | Plan ULID |
| `phase` | string | Current lifecycle phase |
| `processing_state` | string | Current processing state |
| `changeset_id` | string | ChangeSet ULID (null if Execute not complete) |
| `sandbox_refs` | list | Active sandbox reference IDs |
| `changeset_summary` | object | Summary counts from SpecChangeSet |
| `files_changed` | list | `{path, operation}` per changed file |
| `validation_summary` | object | Validation results (if available) |
| `apply_summary` | object | Files changed count, validations run (after apply) |
## Apply Integration
### Empty ChangeSet Guard
The apply pipeline checks whether the ChangeSet has any entries before
proceeding. If the ChangeSet is empty, apply is blocked with a clear
message:
```
Plan <id> has an empty ChangeSet. No changes to apply. Use --allow-empty to override.
```
Set `--allow-empty` to bypass this check for plans that intentionally
produce no file changes (e.g. validation-only plans).
### Apply Summary Persistence
When apply completes, the following metadata is stored in the plan:
- `apply_files_changed`: Number of files written/modified/deleted
- `apply_validations_run`: Number of validation checks executed
- `apply_completed_at`: ISO-8601 timestamp of apply completion
This metadata is visible via `agents plan status` and `agents plan artifacts`.
### Merge Failure Handling
When a sandbox merge fails during apply:
1. The plan transitions to `ERRORED` processing state
2. `error_message` is set to `"Merge failed: <details>"`
3. `error_details` includes:
- `merge_conflict`: Description of the conflict
- `sandbox_rollback`: Set to `"pending"` for cleanup
**Recovery steps:**
1. Review conflict details via `agents plan status <id>`
2. Re-run execute phase after resolving conflicts: `agents plan execute <id>`
3. Or fix validation issues and retry apply
### Processing State Flow
```
Execute/COMPLETE
|
v
Apply/QUEUED --> Apply/PROCESSING --> Apply/APPLIED (terminal success)
|
+--> Apply/ERRORED (merge failure)
+--> Apply/CONSTRAINED (invariant violation)
```
## Service API
### `PlanApplyService`
```python
from cleveragents.application.services.plan_apply_service import PlanApplyService
service = PlanApplyService(
lifecycle_service=lifecycle,
changeset_store=store, # optional InMemoryChangeSetStore
)
# Generate diff
diff_text = service.diff(plan_id, fmt="json")
# Get artifacts
artifacts = service.artifacts(plan_id, fmt="json")
# Guard empty changeset
service.guard_empty_changeset(plan_id, allow_empty=False)
# Persist apply summary
service.persist_apply_summary(plan_id, files_changed=5, validations_run=3)
# Handle merge failure
service.handle_merge_failure(plan_id, conflict_details="...")
```