---
name: ao-issue-merge
description: "Merge or reconcile ao JSONL event streams after git worktree merges or branch rebases. Deduplicates events, preserves ordering, runs ao rebuild to validate the result."
category: utility
invokes: [ao-state, ao-usage]
invoked_by: [ao-git]
state_files:
  read: [issues/events.jsonl, issues/active.jsonl]
  write: [issues/events.jsonl, issues/active.jsonl]
---

# Issue Merge Skill

Merge and reconcile `ao` JSONL event streams after git worktree merges or parallel branch work.

## Problem

When working with git worktrees or parallel branches, `.agent/ops/issues/events.jsonl` and `active.jsonl` can diverge. Merging raw JSONL requires deduplication and ordering to produce a consistent event log.

## Goal

Produce a clean, deduplicated, chronologically ordered `events.jsonl` from two diverged copies, then run `ao rebuild` to regenerate `active.jsonl`.

## Invocation

1. **After git merge/rebase** that touched `.agent/ops/issues/`
2. **During `ao-git`** — when conflicts detected in `.agent/ops/issues/`
3. **Manually** — when user asks to reconcile issue stores

## Procedure

### Step 1: Identify Diverged Files

```bash
# Check if events.jsonl has git conflict markers (should never happen if stored as binary-safe JSONL)
grep -c "^<<<<<<< " .agent/ops/issues/events.jsonl || echo "No conflict markers"

# More likely: two separate events.jsonl files from different branches
# Collect both copies (e.g., from git stash or worktree)
```

### Step 2: Union and Deduplicate Events

Each line in `events.jsonl` is a JSON object with a unique `id` field. The merge algorithm:

1. Read all lines from BOTH copies
2. Parse each line as JSON
3. Deduplicate by `id` field — keep one copy of each event (they are immutable)
4. Sort by `ts` (timestamp) ascending, breaking ties by `id` lexicographically
5. Write merged result to `events.jsonl`

**Python one-liner (if Python available):**

```bash
python3 - <<'EOF'
import json, sys

files = sys.argv[1:]
seen = {}
for f in files:
    with open(f) as fh:
        for line in fh:
            line = line.strip()
            if not line:
                continue
            evt = json.loads(line)
            seen[evt["id"]] = evt

merged = sorted(seen.values(), key=lambda e: (e.get("ts", ""), e["id"]))
with open(files[0], "w") as out:
    for evt in merged:
        out.write(json.dumps(evt) + "\n")
print(f"Merged {len(merged)} unique events from {len(files)} files")
EOF
.agent/ops/issues/events.jsonl /path/to/other/events.jsonl
```

### Step 3: Rebuild Active Store

```bash
uv run ao rebuild
```

This regenerates `active.jsonl` from the merged `events.jsonl`.

### Step 4: Verify

```bash
uv run ao ls --limit 10
```

Spot-check that expected issues appear with correct status.

## Merge Rules

### Immutable Events
Once written, events are never modified. If both branches have an event with the same `id`, they are identical — keep either copy.

### New Events (Different IDs)
Include both — events from both branches are additive.

### Ordering
Sort by `ts` (ISO 8601 timestamp) ascending. If two events have the same timestamp, sort by `id` lexicographically for stability.

### No Counter File Required

ao derives the next issue number automatically from events — no `.counter` file maintenance needed after merge.
Simply run `ao rebuild` after merging events to update the active snapshot.

## Output

```
✅ Merged events.jsonl

Events:
- Local:  {N} events
- Remote: {M} events
- Merged: {K} unique events ({K-N-M} duplicates removed)

Rebuilding active.jsonl...
Active issues: {X}

Spot check (first 5):
{ao ls --limit 5 output}
```

## Error Handling

| Error | Recovery |
|-------|----------|
| Malformed JSON line | Skip line, warn user with line number |
| Missing `id` field | Skip line, warn |
| `ao rebuild` fails | Report error, show first 20 lines of events.jsonl for diagnosis |

## Completion Criteria

- [ ] `events.jsonl` contains all unique events from both branches
- [ ] Events are sorted by timestamp
- [ ] `ao rebuild` runs without errors
- [ ] `ao ls` returns expected issues

## Anti-Patterns

- ❌ Silently discarding events from either side
- ❌ Re-ordering events in a way that breaks causality (older timestamps first)
- ❌ Auto-committing without user review
- ❌ Ignoring `ao rebuild` errors after merge
