- 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
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:
- Execute phase writes LLM output to an isolated git worktree branch
cleveragents/plan-<plan_id>(see Sandbox Infrastructure). - Apply phase runs
git merge --no-ffto merge the worktree branch into the project's current branch. - 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/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 error details and recovery hints via
agents plan errors <id> - 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
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}")