spec: Three-Way Merge Strategy — subplan result merging, conflict detection (v3.3.0) [AUTO-ARCH-8] #8737

Closed
HAL9000 wants to merge 1 commits from spec/three-way-merge-strategy-v3.3.0 into master
+144
View File
@@ -47095,3 +47095,147 @@ These architectural invariants must be maintained across all milestones:
8. **BDD tests**: All unit-level tests expressed as Behave/Gherkin scenarios. No xUnit-style tests.
9. **File size limit**: No source file exceeds 500 lines. Split into modules if approaching limit.
10. **Atomic commits**: One logical change per commit. No mixed concerns.
---
## Three-Way Merge Strategy (v3.3.0)
### Overview
When subplans complete, their results must be merged back into the parent plan. The Three-Way Merge Strategy combines non-conflicting changes from multiple subplans automatically, while surfacing conflicts to the user for resolution. The merge uses the parent plan's state at subplan spawn time as the common ancestor (base), and each subplan's result as a branch.
### Module Boundaries
- **Module**: `cleveragents.merge`
- **Layer**: Domain
- **Responsibilities**:
- Merging subplan results into the parent plan
- Detecting conflicts between subplan outputs
- Applying non-conflicting changes automatically
- Surfacing conflicts to the user for resolution
- Persisting merge results and conflict records
- **Public Interfaces**:
- `MergeStrategy` — abstract base for merge strategies
- `ThreeWayMergeStrategy` — concrete three-way merge implementation
- `MergeConflict` — value object representing a detected conflict
- `MergeResult` — value object representing the outcome of a merge
- **Forbidden Dependencies**: Must not import from `cleveragents.cli` or `cleveragents.tui`
### Data Models
#### MergeConflict
```python
@dataclass
class MergeConflict:
conflict_id: UUID
resource_path: str # The resource/file/artifact that has a conflict
conflict_type: Literal["content", "deletion", "creation", "rename"]
base_content: Optional[str] # Content at spawn time (common ancestor)
ours_content: Optional[str] # Content from subplan A
theirs_content: Optional[str] # Content from subplan B
subplan_ids: list[UUID] # Which subplans produced conflicting changes
resolution: Optional[MergeResolution] # Set when user resolves
```
#### MergeResolution
```python
@dataclass
class MergeResolution:
conflict_id: UUID
strategy: Literal["accept_ours", "accept_theirs", "manual", "llm_assisted"]
resolved_content: str
resolved_by: str
resolved_at: datetime
```
#### MergeResult
```python
@dataclass
class MergeResult:
plan_id: UUID
subplan_ids: list[UUID]
status: Literal["clean", "conflicts_detected", "conflicts_resolved", "failed"]
merged_changes: list[dict] # Non-conflicting changes applied
conflicts: list[MergeConflict] # Conflicts requiring resolution
applied_at: Optional[datetime]
```
### Three-Way Merge Algorithm
Given:
- **Base**: Parent plan state at subplan spawn time
- **Ours**: Subplan A result
- **Theirs**: Subplan B result (and any additional subplans)
Algorithm:
1. For each resource modified by any subplan:
a. If only one subplan modified it → apply that change (no conflict)
b. If multiple subplans modified it identically → apply once (no conflict)
c. If multiple subplans modified it differently → record as `MergeConflict`
d. If one subplan deleted it and another modified it → record as `MergeConflict`
2. Apply all non-conflicting changes to the parent plan
3. Return `MergeResult` with status and any conflicts
### Conflict Resolution Strategies
| Strategy | Description | When to Use |
|---|---|---|
| `accept_ours` | Accept the first subplan's version | When one subplan's change is clearly correct |
| `accept_theirs` | Accept the second subplan's version | When the other subplan's change is correct |
| `manual` | User provides the resolved content | When neither version is correct alone |
| `llm_assisted` | LLM proposes a resolution | When changes can be intelligently combined |
### CLI Interface Specification
#### `agents plan merge <plan-id>`
- Triggers merge of all completed subplan results into the parent plan
- Shows merge progress and any conflicts detected
- If conflicts exist, enters interactive conflict resolution mode
- Optional: `--strategy llm|manual` (default: llm for LLM-assisted resolution)
- Output: Summary of merged changes and any remaining conflicts
#### `agents plan conflicts <plan-id>`
- Lists all unresolved merge conflicts for a plan
- Optional: `--format table|json`
- Output: Table with columns: Conflict ID, Resource, Type, Subplans
#### `agents plan resolve <conflict-id> --strategy <strategy>`
- Resolves a specific merge conflict
- Required: `--strategy accept_ours|accept_theirs|manual|llm`
- For `--strategy manual`: prompts for resolved content
- Output: Confirmation with resolution details
### Integration Points
| Integration | Direction | Description |
|---|---|---|
| Subplan System | Called by | `SubplanExecutor` calls merge after all subplans complete |
| Plan Executor | Called by | Merge result is applied to parent plan state |
| Checkpoint System | Calls | Checkpoint created before merge is applied |
| CLI | Called by | `agents plan merge` and `agents plan resolve` commands |
### Error Handling
- `MergeConflictError(plan_id, conflicts)` — unresolved conflicts prevent merge completion
- `MergeStrategyError(strategy, reason)` — merge strategy failed
- `ConflictNotFoundError(conflict_id)` — conflict does not exist
- `ConflictAlreadyResolvedError(conflict_id)` — conflict was already resolved
### Cross-Cutting Concerns
- **Atomicity**: Merge is applied atomically — either all non-conflicting changes apply or none
- **Idempotency**: Re-running merge on the same subplan results is a no-op
- **Logging**: All merge operations logged at INFO level; conflicts logged at WARNING
- **LLM-Assisted Resolution**: Uses the plan's configured actor for LLM-assisted conflict resolution
---
**Automated by CleverAgents Bot**
Supervisor: Architecture | Agent: architecture-pool-supervisor
Worker: [AUTO-ARCH-8]