Files
cleveragents-core/docs/reference/plan_apply.md
T
2026-02-25 10:05:10 +00:00

6.6 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/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 error details and recovery hints via agents plan errors <id>
  2. Review conflict details via agents plan status <id>
  3. Re-run execute phase after resolving conflicts: agents plan execute <id>
  4. Or fix validation issues and retry apply

See Error Recovery Reference for the full error classification, retry policy, and recovery hint system.

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}")