forked from cleveragents/cleveragents-core
9f2e7e88b0
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.
147 lines
4.2 KiB
Markdown
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="...")
|
|
```
|