Skip to content

Make conflict-bearing dry-runs valid #177

Description

@lloydrichards

What problem are we solving?

CLI dry-run currently creates an Apply with no decisions:

const result = yield* applyService.preview({
  apply: new Apply({ plan, decisions: [] }),
  repoRoot,
});

However, the Apply invariant requires one decision for every conflicted path. Both preview and execution call the same validation:

const actions = yield* materializeFrom(applyIntent);

// materializeFrom
yield* validateApplyIntent(apply);

Validation therefore rejects a conflict-bearing dry-run with Missing apply decision for conflicted path ....

Why does this matter?

Dry-run is meant to be the non-mutating view of what scaffolding would encounter. Conflicts are precisely where this view is most useful, but the current workflow represents the preview using execution intent that is invalid by definition.

flowchart LR
  P[Plan with conflict] --> D[Dry-run]
  D --> A[Apply with decisions: empty]
  A --> V{Apply invariant}
  V -->|missing decision| F[preview fails]
  style F stroke:#dc2626,stroke-width:2px
Loading

The problem is semantic rather than merely a missing default: a preview has not collected user execution decisions and should not pretend that it has.

Scope

  • Define how dry-run represents unresolved conflicts without mutating the repository.
  • Preserve the invariant that an executable Apply has complete and exact decisions.
  • Ensure dry-run output clearly distinguishes planned creates, modifications, unchanged paths, and unresolved conflicts.
  • Keep non-interactive CLI behavior deterministic.

Acceptance criteria

  • A dry-run succeeds when the Plan contains one or more conflicts.
  • Dry-run does not weaken the decision requirements for an executable Apply.
  • Conflict paths are visible in dry-run output as unresolved conflicts, not silently reported as successful writes.
  • Dry-run performs no repository writes and does not run Finalize commands.
  • A regression test exercises the CLI workflow with a conflict-bearing Plan.
  • Existing conflict-free dry-run behavior remains supported.

Blocked by

None - can start immediately.

Metadata

Metadata

Assignees

No one assigned

    Labels

    afkAutonomous slice that can be completed without human interactionbugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions