# Git Reconciliation Engine Specification

**Feature ID**: `git_reconciliation_engine`  
**Version**: 1.0.0  
**Status**: Outstanding  
**Created**: 2026-03-07

---

## Overview

A periodic git reconciliation engine that ensures feature worktrees remain semantically compatible with the mainline branch throughout the multi-agent orchestration lifecycle. The engine runs between every phase transition (before planning, before building, before QA, before merge) to detect and resolve divergence.

---

## Problem Statement

In parallel multi-agent development:

- Multiple features work in isolated worktrees simultaneously
- Mainline branch evolves independently as features merge
- Feature worktrees can drift from mainline, causing merge conflicts at promotion time
- Late-stage conflict detection blocks `ready_to_merge` transitions and wastes agent work

**Goal**: Detect and resolve mainline divergence proactively at phase boundaries, ensuring worktrees are always merge-ready.

---

## Requirements

### 1. Git Diff Engine

**Location**: `apps/control-plane/src/application/services/git-reconciliation-service.ts`

**Core Operation**: `reconcileWithMainline(featureId: string, baseRef: string): Promise<ToolResponse<ReconciliationResult>>`

#### 1.1 Divergence Detection

**Input**:

- `featureId`: Feature identifier
- `baseRef`: Mainline branch name (from `policy.yaml` `worktree.base_branch`)

**Process**:

1. Resolve feature worktree path from `state.md`
2. Execute `git fetch origin <baseRef>` to update remote tracking
3. Compute diff: `git diff <baseRef>...HEAD` (three-dot diff shows changes since common ancestor)
4. Classify result:
   - **No divergence**: Worktree is up-to-date or ahead-only (fast-forward merge possible)
   - **Non-conflicting divergence**: Mainline has new commits, but no overlapping file changes
   - **Conflicting divergence**: Mainline and worktree modified the same files/lines

**Output**: `ReconciliationResult`

```typescript
type ReconciliationResult = {
  status: 'up_to_date' | 'merged' | 'conflict_detected';
  divergence_detected: boolean;
  files_changed_mainline: string[];
  files_changed_worktree: string[];
  conflicting_files: string[];
  merge_base: string; // SHA of common ancestor
  mainline_head: string; // SHA of current mainline HEAD
  worktree_head: string; // SHA of current worktree HEAD
};
```

#### 1.2 Non-Conflicting Merge

**Trigger**: `divergence_detected === true` AND `conflicting_files.length === 0`

**Process**:

1. **Pull remote mainline changes**:

   ```bash
   git fetch origin <baseRef>
   git merge --ff-only origin/<baseRef>  # Fast-forward if possible
   ```

   If fast-forward fails (worktree has diverged):

   ```bash
   git rebase origin/<baseRef>  # Rebase worktree commits onto mainline
   ```

2. **Pull local mainline changes**:

   ```bash
   # Fetch from local repository's mainline branch
   git fetch <repo_root> <baseRef>
   git merge --ff-only FETCH_HEAD
   ```

   If fast-forward fails:

   ```bash
   git rebase FETCH_HEAD
   ```

3. **Verify merge success**:
   - No conflict markers: `git diff --check`
   - Working tree clean: `git status --porcelain`

4. **Update state metadata**:
   - Patch `state.md`: `last_reconciliation_at`, `last_reconciliation_sha`
   - Return `{ status: 'merged' }`

5. **Handle rebase conflicts** (unexpected):
   - Abort: `git rebase --abort`
   - Fall through to conflict resolution path

**Note**: No merge commits are created. Changes are integrated via fast-forward or rebase to maintain linear history.

#### 1.3 Conflicting Merge

**Trigger**: `conflicting_files.length > 0`

**Process**:

1. Transition feature to `blocked` status with reason `mainline_conflict`
2. Record conflict metadata in `state.md`:
   ```yaml
   conflicts:
     - type: mainline_divergence
       detected_at: <timestamp>
       merge_base: <sha>
       mainline_head: <sha>
       conflicting_files: [...]
   ```
3. Invoke reconciliation agent (see §2)
4. Return `{ status: 'conflict_detected' }`

---

### 2. Reconciliation Agent

**Role**: `reconciler` (new agent role)

**Trigger**: Conflicting divergence detected by git diff engine

**System Prompt**: `config/agentic/orchestrator/prompts/reconciler.md`

**Objective**: Resolve conflicts by modifying worktree to conform to mainline semantics while preserving feature intent.

#### 2.1 Agent Context

**Inputs**:

- Feature spec (`spec.md`)
- Feature plan (`plan.json`)
- Conflict metadata from `state.md`
- Mainline diff: `git diff <merge_base>..<mainline_head> -- <conflicting_files>`
- Worktree diff: `git diff <merge_base>..<worktree_head> -- <conflicting_files>`
- Full conflict markers: `git merge origin/<baseRef>` (leave in conflicted state)

**Available Tools**:

- `repo.read_file` — read conflicted files with markers
- `repo.apply_patch` — apply resolution patches
- `repo.status` — check resolution progress
- `repo.diff` — verify resolution semantics
- `feature.log_append` — document resolution decisions

#### 2.2 Resolution Strategy

**Principle**: Mainline is authoritative. Worktree must adapt.

**Process**:

1. **Analyze conflicts**: For each conflicting file, determine:
   - What mainline changed (API signature, behavior, structure)
   - What worktree changed (feature implementation)
   - How to rebase worktree changes onto mainline semantics

2. **Identify plan impacts**: Determine if mainline changes invalidate plan assumptions:
   - API contracts changed (function signatures, types)
   - File structure changed (moved/renamed files in `allowed_areas`)
   - Dependencies changed (new required imports, removed exports)

3. **Delegate plan updates**: If plan modifications are required:
   - Document required changes: `feature.log_append`
   - Invoke planner agent with context:
     ```typescript
     {
       task: 'update_plan_for_mainline_changes',
       mainline_changes: [...],
       affected_plan_sections: [...],
       reconciliation_context: {
         merge_base: '...',
         mainline_head: '...',
         conflicting_files: [...]
       }
     }
     ```
   - Planner calls `plan.update` with revised plan
   - Reconciler waits for plan update completion

4. **Generate resolution patches**: Modify worktree to:
   - Accept all mainline structural changes
   - Rewrite feature code to work with new mainline APIs/contracts
   - Preserve feature intent from updated `plan.json`

5. **Apply patches**: Use `repo.apply_patch` to resolve conflicts

6. **Verify resolution**:
   - `git diff --check` (no conflict markers remain)
   - `gates.run` with `fast` profile (lint + typecheck pass)

7. **Commit resolution**:
   - Stage resolved files: `git add <conflicting_files>`
   - Commit: `git commit -m "chore: reconcile mainline conflicts at ${mainline_head}"`

8. **Transition feature**: Move from `blocked` back to previous phase (e.g., `building`)

**Failure Handling**:

- If reconciliation agent cannot resolve after `max_iterations_per_phase` attempts:
  - Transition feature to `failed` with reason `unresolvable_mainline_conflict`
  - Emit dashboard notification (see §9)
  - Require human intervention (`requires_human: true`)

---

### 3. Phase Boundary Integration

**Execution Points**: Reconciliation runs **before** each phase transition:

1. **Before Planning** (`pending → planning`):
   - Ensure worktree starts from latest mainline
   - Reconcile any drift since feature initialization
2. **Before Building** (`planning → building`):
   - Ensure plan was created against current mainline APIs
   - Reconcile any mainline changes that invalidate plan assumptions
3. **Before QA** (`building → qa`):
   - Ensure implementation is compatible with current mainline
   - Reconcile any mainline test infrastructure changes
4. **Before Merge** (`qa → ready_to_merge`):
   - Final reconciliation check before promotion
   - Guarantee merge will be conflict-free

**Implementation**: `apps/control-plane/src/supervisor/runtime.ts`

```typescript
async function transitionPhase(
  featureId: string,
  fromPhase: FeatureStatus,
  toPhase: FeatureStatus,
): Promise<ToolResponse> {
  // Run reconciliation before phase transition
  const reconcileResult = await gitReconciliationService.reconcileWithMainline(
    featureId,
    policy.worktree.base_branch,
  );

  if (reconcileResult.ok && reconcileResult.data.status === 'conflict_detected') {
    // Block transition, invoke reconciliation agent
    return fail(
      ERROR_CODES.MAINLINE_CONFLICT,
      `Cannot transition to ${toPhase}: mainline conflicts detected`,
      {
        retryable: true,
        requires_human: false,
        conflicting_files: reconcileResult.data.conflicting_files,
      },
    );
  }

  // Proceed with phase transition
  return await kernel.transitionStatus(featureId, toPhase);
}
```

---

### 4. Configuration

**Policy Settings** (`config/agentic/orchestrator/policy.yaml`):

```yaml
reconciliation:
  enabled: true
  base_branch: main # Inherited from worktree.base_branch
  check_before_phases:
    - planning
    - building
    - qa
    - ready_to_merge
  auto_merge_non_conflicting: true
  reconciler_max_iterations: 3
  reconciler_timeout_seconds: 600
```

**Agent Configuration** (`config/agentic/orchestrator/agents.yaml`):

```yaml
roles:
  reconciler:
    system_prompt: prompts/reconciler.md
    description: Resolves mainline divergence conflicts by adapting worktree to mainline semantics
    capabilities:
      - conflict_analysis
      - semantic_rebase
      - api_migration
```

---

### 5. MCP Tool Extensions

**New Tool**: `repo.reconcile_mainline`

**Input Schema** (`tools/schemas/input/repo.reconcile_mainline.schema.json`):

```json
{
  "type": "object",
  "properties": {
    "feature_id": { "type": "string" },
    "base_ref": { "type": "string" },
    "auto_merge_non_conflicting": { "type": "boolean", "default": true }
  },
  "required": ["feature_id", "base_ref"]
}
```

**Output Schema** (`tools/schemas/output/repo.reconcile_mainline.schema.json`):

```json
{
  "type": "object",
  "properties": {
    "status": { "enum": ["up_to_date", "merged", "conflict_detected"] },
    "divergence_detected": { "type": "boolean" },
    "files_changed_mainline": { "type": "array", "items": { "type": "string" } },
    "files_changed_worktree": { "type": "array", "items": { "type": "string" } },
    "conflicting_files": { "type": "array", "items": { "type": "string" } },
    "merge_base": { "type": "string" },
    "mainline_head": { "type": "string" },
    "worktree_head": { "type": "string" }
  },
  "required": ["status", "divergence_detected"]
}
```

**Authorization**: `orchestrator`, `reconciler`, `system` roles

---

### 6. State Schema Extensions

**`state.md` Frontmatter** (`schemas/state.schema.json`):

```yaml
# Existing fields...
last_reconciliation_at: '2026-03-07T10:30:00Z'
last_reconciliation_sha: 'abc123def456'
conflicts:
  - type: mainline_divergence
    detected_at: '2026-03-07T10:25:00Z'
    merge_base: 'xyz789'
    mainline_head: 'abc123'
    worktree_head: 'def456'
    conflicting_files:
      - src/core/kernel.ts
      - apps/control-plane/src/supervisor/runtime.ts
    resolution_status: pending | resolved | failed
    resolved_at: '2026-03-07T10:35:00Z'
    resolved_by: reconciler
```

---

### 7. Error Codes

**New Codes** (`apps/control-plane/src/core/error-codes.ts`):

```typescript
export const ERROR_CODES = {
  // ... existing codes
  MAINLINE_CONFLICT: 'mainline_conflict',
  RECONCILIATION_FAILED: 'reconciliation_failed',
  UNRESOLVABLE_CONFLICT: 'unresolvable_conflict',
  MERGE_BASE_NOT_FOUND: 'merge_base_not_found',
} as const;
```

---

### 8. Observability

**Metrics** (`performance-analytics-service.ts`):

- `reconciliation_checks_total` — count of reconciliation runs
- `reconciliation_conflicts_detected` — count of conflicts found
- `reconciliation_auto_merged` — count of non-conflicting merges
- `reconciliation_agent_invocations` — count of reconciler agent sessions
- `reconciliation_failures` — count of unresolvable conflicts

**Logs** (`.aop/features/<id>/logs/reconciliation.log`):

- Timestamp of each reconciliation check
- Divergence detection results
- Merge outcomes (success/conflict)
- Reconciler agent decisions

**Evidence** (`.aop/features/<id>/evidence/reconciliation/`):

- `<timestamp>-mainline-diff.patch` — mainline changes since merge base
- `<timestamp>-worktree-diff.patch` — worktree changes since merge base
- `<timestamp>-conflict-markers.txt` — files with conflict markers
- `<timestamp>-resolution.patch` — reconciler's resolution patch

---

## Acceptance Criteria

1. ✅ Reconciliation service detects divergence between worktree and mainline
2. ✅ Non-conflicting divergence auto-merges into worktree without agent intervention
3. ✅ Conflicting divergence blocks phase transition and invokes reconciler agent
4. ✅ Reconciler agent resolves conflicts by adapting worktree to mainline semantics
5. ✅ Reconciliation runs before planning, building, QA, and merge phases
6. ✅ `ready_to_merge` features are guaranteed conflict-free with mainline
7. ✅ Unresolvable conflicts transition feature to `failed` with `requires_human: true`
8. ✅ Dashboard displays exact conflicting files and diffs for human intervention
9. ✅ Reconciler agent delegates plan updates to planner agent when mainline changes invalidate plan
10. ✅ Time-based trigger runs every 5 minutes during long-running phases
11. ✅ Event-based trigger runs when checkpoint service detects worktree changes
12. ✅ Both remote and local mainline changes are pulled into worktree
13. ✅ No merge commits created (fast-forward or rebase for linear history)
14. ✅ All reconciliation operations are logged and produce evidence artifacts
15. ✅ Schema validation enforces reconciliation metadata structure
16. ✅ MCP tool `repo.reconcile_mainline` is registered and authorized

---

## 9. Dashboard Integration

### Conflict Notification UI

**Location**: `packages/web-dashboard/src/components/ConflictAlert.tsx`

**Trigger**: Feature transitions to `failed` with reason `unresolvable_mainline_conflict`

**Display Requirements**:

1. **Alert Banner**: Prominent notification in dashboard header
   - Icon: ⚠️ Merge Conflict
   - Message: "Feature `<feature_id>` has unresolvable conflicts with mainline"
   - Action: "View Details" button

2. **Conflict Detail Modal**:

   ```typescript
   interface ConflictDetailView {
     featureId: string;
     conflictingFiles: Array<{
       path: string;
       mainlineDiff: string; // git diff merge_base..mainline_head -- <file>
       worktreeDiff: string; // git diff merge_base..worktree_head -- <file>
       conflictMarkers: string; // Full file content with <<<< ==== >>>> markers
     }>;
     mergeBase: string;
     mainlineHead: string;
     worktreeHead: string;
     detectedAt: string;
     reconciliationAttempts: number;
   }
   ```

3. **Per-File Conflict View**:
   - **File path**: `src/core/kernel.ts`
   - **Mainline changes** (collapsible):
     ```diff
     + Added new parameter to transitionStatus()
     - Removed deprecated lockAcquire() method
     ```
   - **Worktree changes** (collapsible):
     ```diff
     + Feature implementation uses lockAcquire()
     + Added new helper function
     ```
   - **Conflict markers** (syntax-highlighted):
     ```typescript
     <<<<<<< HEAD (worktree)
     await lockAcquire(featureId, sessionId);
     =======
     await lockService.acquire(featureId, sessionId, ttl);
     >>>>>>> origin/main (mainline)
     ```

4. **Resolution Actions**:
   - **"Checkout Worktree"**: Open worktree in local editor
     ```bash
     cd .worktrees/<feature_id>
     ```
   - **"View Full Diff"**: Download complete diff bundle
   - **"Retry Reconciliation"**: Re-invoke reconciler agent (if user manually resolved)
   - **"Abandon Feature"**: Delete feature and cleanup worktree

### SSE Event Stream

**Event**: `conflict.unresolvable`

```typescript
{
  type: 'conflict.unresolvable',
  featureId: string,
  conflictingFiles: string[],
  timestamp: string,
  requiresHuman: true
}
```

**Dashboard Behavior**:

- Real-time notification appears immediately
- Browser notification (if permitted): "Merge conflict requires attention"
- Feature card in Kanban view shows red "Conflict" badge

### API Endpoints

**GET** `/api/features/:featureId/conflicts`

Response:

```json
{
  "ok": true,
  "data": {
    "featureId": "my_feature",
    "conflictingFiles": [
      {
        "path": "src/core/kernel.ts",
        "mainlineDiff": "...",
        "worktreeDiff": "...",
        "conflictMarkers": "..."
      }
    ],
    "mergeBase": "abc123",
    "mainlineHead": "def456",
    "worktreeHead": "ghi789",
    "detectedAt": "2026-03-07T10:30:00Z",
    "reconciliationAttempts": 3
  }
}
```

**POST** `/api/features/:featureId/conflicts/retry`

Triggers reconciler agent re-run after manual intervention.

---

## 10. Git Commands Reference

## 10. Git Commands Reference

```bash
# Fetch latest remote mainline
git fetch origin main

# Fetch local repository mainline
git fetch /path/to/repo main

# Find merge base (common ancestor)
git merge-base HEAD origin/main

# Three-dot diff (changes since divergence)
git diff origin/main...HEAD

# Fast-forward merge (no merge commit)
git merge --ff-only origin/main

# Rebase worktree onto mainline (linear history)
git rebase origin/main

# Abort failed rebase
git rebase --abort

# List conflicted files
git diff --name-only --diff-filter=U

# Check for remaining conflict markers
git diff --check

# Show mainline changes for specific file
git diff <merge_base>..<mainline_head> -- <file>

# Show worktree changes for specific file
git diff <merge_base>..<worktree_head> -- <file>
```

### Reconciler Agent Prompt Template

````markdown
# Reconciler Agent System Prompt

You are a reconciliation agent. Your role is to resolve conflicts between a feature worktree and the mainline branch.

## Guiding Principle

**Mainline is authoritative.** The worktree must adapt to mainline semantics while preserving feature intent.

## Your Task

1. Analyze the conflict: understand what mainline changed and why
2. Understand the feature intent from the plan
3. Determine if mainline changes require plan modifications:
   - API contracts changed (function signatures, types)
   - File structure changed (moved/renamed files)
   - Dependencies changed (imports, exports)
4. If plan updates are needed:
   - Document required changes with `feature.log_append`
   - Invoke planner agent with reconciliation context
   - Wait for planner to call `plan.update`
5. Rewrite the feature code to work with the new mainline APIs/structure
6. Apply resolution patches using `repo.apply_patch`
7. Verify resolution with `gates.run` (fast profile)
8. Document your decisions with `feature.log_append`

## Available Context

- Feature spec and plan
- Mainline diff since merge base
- Worktree diff since merge base
- Files with conflict markers
- Reconciliation metadata (merge base, heads, conflicting files)

## Planner Delegation

When mainline changes invalidate plan assumptions, invoke the planner agent:

```json
{
  "task": "update_plan_for_mainline_changes",
  "mainline_changes": ["API signature changed in kernel.ts", "..."],
  "affected_plan_sections": ["files.modify", "contracts.openapi"],
  "reconciliation_context": {
    "merge_base": "abc123",
    "mainline_head": "def456",
    "conflicting_files": ["src/core/kernel.ts"]
  }
}
```
````

The planner will update the plan, then you can proceed with code resolution.

## Success Criteria

- No conflict markers remain (`git diff --check` passes)
- Fast gates pass (lint + typecheck)
- Feature intent from plan is preserved
- Code works with current mainline APIs
- Plan is updated if mainline changes required it

## Failure Escalation

If you cannot resolve the conflict after {max_iterations} attempts, transition the feature to `failed` and request human intervention. The dashboard will display the exact conflicting files and diffs to the user.

````

---

## Dependencies

- Existing: `repo.read_file`, `repo.apply_patch`, `repo.status`, `repo.diff`
- Existing: `gates.run` (fast profile)
- Existing: `feature.log_append`, `feature.state_patch`
- Existing: `CheckpointService` (event emitter for worktree changes)
- New: `repo.reconcile_mainline` (MCP tool)
- New: `GitReconciliationService` (application service)
- New: `ReconciliationScheduler` (time-based trigger)
- New: Reconciler agent role and system prompt
- New: Dashboard conflict notification UI
- New: Planner agent delegation for plan updates

---

## Testing Strategy

### Unit Tests
- `git-reconciliation-service.spec.ts`:
  - Divergence detection (up-to-date, non-conflicting, conflicting)
  - Auto-merge non-conflicting changes
  - Conflict metadata recording
  - Error handling (merge base not found, git command failures)

### Integration Tests
- `reconciliation-workflow.spec.ts`:
  - Full reconciliation cycle (detect → merge/block → resolve)
  - Phase boundary integration (before planning, building, QA, merge)
  - Reconciler agent invocation and resolution
  - Unresolvable conflict escalation

### Manual Test Scenarios
1. **No divergence**: Feature worktree is up-to-date with mainline
2. **Fast-forward**: Mainline has new commits, worktree is behind but no conflicts
3. **Non-conflicting divergence**: Mainline and worktree changed different files
4. **Conflicting divergence**: Mainline and worktree changed same files/lines
5. **Unresolvable conflict**: Reconciler agent cannot resolve after max iterations

---

## Rollout Plan

### Phase 1: Core Service (Week 1)
- Implement `GitReconciliationService`
- Add `repo.reconcile_mainline` MCP tool
- Unit tests for divergence detection and auto-merge

### Phase 2: Phase Integration (Week 2)
- Integrate reconciliation checks into `supervisor/runtime.ts`
- Add reconciliation hooks before each phase transition
- Integration tests for phase boundary behavior

### Phase 3: Reconciler Agent (Week 3)
- Define reconciler role and system prompt
- Implement reconciler agent invocation on conflict detection
- Integration tests for conflict resolution workflow

### Phase 4: Observability & Dashboard (Week 4)
- Add reconciliation metrics to performance analytics
- Implement reconciliation logging and evidence artifacts
- Dashboard conflict notification UI with file-level diff display
- SSE event stream for real-time conflict alerts
- API endpoints for conflict details and retry actions

---

## Reconciliation Triggers

### 1. Time-Based Trigger

**Frequency**: Every 5 minutes during long-running phases

**Implementation**: `apps/control-plane/src/supervisor/reconciliation-scheduler.ts`

```typescript
class ReconciliationScheduler {
  private intervals = new Map<string, NodeJS.Timeout>();

  startPeriodicReconciliation(featureId: string, intervalMs = 300000) {
    const interval = setInterval(async () => {
      await this.reconciliationService.reconcileWithMainline(featureId, baseRef);
    }, intervalMs);

    this.intervals.set(featureId, interval);
  }

  stopPeriodicReconciliation(featureId: string) {
    const interval = this.intervals.get(featureId);
    if (interval) {
      clearInterval(interval);
      this.intervals.delete(featureId);
    }
  }
}
````

**Lifecycle**:

- Start: When feature enters `planning`, `building`, or `qa` phase
- Stop: When feature exits phase or reaches terminal state

### 2. Event-Based Trigger

**Source**: `CheckpointService` change detection

**Implementation**: `apps/control-plane/src/application/services/checkpoint-service.ts`

```typescript
// Existing checkpoint service emits event when changes detected
async function captureCheckpoint(featureId: string): Promise<ToolResponse> {
  const changes = await detectUncommittedChanges(featureId);

  if (changes.files_changed.length > 0) {
    // Emit event for reconciliation
    await eventBus.emit('worktree.changes_detected', {
      featureId,
      filesChanged: changes.files_changed,
      timestamp: new Date().toISOString(),
    });
  }

  // ... existing checkpoint logic
}
```

**Reconciliation Handler**:

```typescript
eventBus.on('worktree.changes_detected', async (event) => {
  await reconciliationService.reconcileWithMainline(event.featureId, policy.worktree.base_branch);
});
```

**Configuration** (`policy.yaml`):

```yaml
reconciliation:
  enabled: true
  triggers:
    time_based:
      enabled: true
      interval_ms: 300000 # 5 minutes
      active_phases: [planning, building, qa]
    event_based:
      enabled: true
      source: checkpoint_service
```

---

## References

- Git merge strategies: https://git-scm.com/docs/git-merge
- Three-dot diff: https://git-scm.com/docs/git-diff#Documentation/git-diff.txt-emgitdiffemltoptionsgtltcommitgtltcommitgt--ltpathgt82308203
- Conflict resolution: https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging

---

**Spec Status**: Ready for implementation  
**Estimated Effort**: 3-4 weeks (4 phases)  
**Priority**: High (blocks parallel multi-feature development at scale)
