Files
cleveragents-core/docs/reference/plan_apply.md
T
HAL9000 e7e7eedc4f docs: add git worktree sandbox guide, extend sandbox/plan-apply/context-tiers refs
- docs/modules/git-worktree-sandbox.md: new module guide for GitWorktreeSandbox
  (PR #5998) covering execute/apply phases, non-git fallback, conflict handling,
  and context hydration integration
- docs/reference/sandbox.md: add Strategy Selection section and Git Worktree
  Sandbox section documenting execute/apply phases and non-git fallback
- docs/reference/plan_apply.md: add Git Worktree Merge-Based Apply section with
  CLI output panels and fallback behaviour; update intro to reference PR #5998
- docs/reference/context_tiers.md: add Context Tier Hydration section documenting
  ContextTierHydrator (PR #4219), hydration algorithm, exclusion rules,
  configuration parameters, and LLMExecuteActor integration
- mkdocs.yml: add Git Worktree Sandbox to Modules nav
2026-04-28 09:25:34 +00:00

8.9 KiB

Plan Diff & Apply Reference

This document describes the diff review and apply integration features, including the git worktree merge-based apply workflow introduced in v3.5.0 (PR #5998).

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

Git Worktree Merge-Based Apply

For plans linked to a git-checkout resource, agents plan apply uses a git-native merge workflow instead of flat file copying:

  1. Execute phase writes LLM output to an isolated git worktree branch cleveragents/plan-<plan_id> (see Sandbox Infrastructure).
  2. Apply phase runs git merge --no-ff to merge the worktree branch into the project's current branch.
  3. The CLI displays spec-aligned output panels:
╭─ Apply Summary ─────────────────────────────────────────────╮
│  Plan:       01HXYZ...                                       │
│  Artifacts:  3 files                                         │
│  Changes:    +42 / -7                                        │
│  Project:    my-project                                       │
│  Applied at: 2026-04-10T14:23:00Z                           │
╰─────────────────────────────────────────────────────────────╯
╭─ Sandbox Cleanup ───────────────────────────────────────────╮
│  ✓ Worktree removed                                          │
│  ✓ Branch merged to main                                     │
╰─────────────────────────────────────────────────────────────╯
╭─ Next Steps ────────────────────────────────────────────────╮
│  Review changes: git diff HEAD~1                             │
│  Commit if satisfied: git commit --amend                     │
╰─────────────────────────────────────────────────────────────╯
✓ OK  Changes applied

For non-git projects, apply falls back to flat file copy (shutil.copy2) from the sandbox directory to the project directory.

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