ISSUES CLOSED: #155
6.4 KiB
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:
# 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/deletedapply_validations_run: Number of validation checks executedapply_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:
- The plan transitions to
ERROREDprocessing state error_messageis set to"Merge failed: <details>"error_detailsincludes:merge_conflict: Description of the conflictsandbox_rollback: Set to"pending"for cleanup
Recovery steps:
- Review conflict details via
agents plan status <id> - Re-run execute phase after resolving conflicts:
agents plan execute <id> - 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)
Validation-Gated Apply
The apply pipeline gates commits on validation results. Before any
file changes are committed, apply_with_validation_gate() checks
the validation summary (either from the plan or supplied externally)
and blocks apply if any required validations have failed.
Outcomes
| Outcome | Description |
|---|---|
applied |
All required validations passed; changes committed |
constrained |
Required validations failed; apply blocked |
already_applied |
Plan was already in a terminal applied state |
blocked_empty |
ChangeSet was empty and allow_empty not set |
Flow
apply_with_validation_gate(plan_id)
|
+-- Plan in terminal state? --> already_applied / constrained
|
+-- Empty ChangeSet? --> blocked_empty (unless allow_empty)
|
+-- required_failed > 0? --> constrained (with actionable message)
|
+-- All passed --> persist_apply_summary() --> complete_apply()
--> ApplyResult(outcome="applied")
ApplyResult Model
| Field | Type | Description |
|---|---|---|
outcome |
ApplyOutcome |
One of the outcomes above |
plan_id |
str |
Plan ULID |
message |
str |
Human-readable result message |
files_changed |
int |
Number of files changed during apply |
validations_total |
int |
Total validations evaluated |
validations_passed |
int |
Required validations that passed |
validations_failed |
int |
Required validations that failed |
External Validation Summary
You can supply a validation summary from an external source (e.g. a CI pipeline) to override the plan's stored summary:
result = service.apply_with_validation_gate(
plan_id,
validation_summary={"total": 3, "required_passed": 2, "required_failed": 1},
)
Service API
PlanApplyService
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="...")
# Validation-gated apply
from cleveragents.application.services.plan_apply_service import ApplyOutcome, ApplyResult
result = service.apply_with_validation_gate(plan_id, allow_empty=False)
if result.outcome == ApplyOutcome.APPLIED:
print(f"Applied: {result.files_changed} files changed")
elif result.outcome == ApplyOutcome.CONSTRAINED:
print(f"Blocked: {result.message}")