--- name: upstream-merge description: This skill should be used when incorporating upstream git changes into a local fork while preserving local intent. It provides a structured workflow for analyzing divergence, categorizing conflicts, creating triage todos for each conflict, reviewing decisions one-by-one with the user, and executing all resolutions. Triggers on "merge upstream", "incorporate upstream changes", "sync fork", or when local and remote branches have diverged significantly. --- # Upstream Merge Incorporate upstream changes into a local fork without losing local intent. Analyze divergence, categorize every changed file, triage conflicts interactively, then execute all decisions in a single structured pass. ## Prerequisites Before starting, establish context: 1. **Identify the guiding principle** — ask the user what local intent must be preserved (e.g., "FastAPI pivot is non-negotiable", "custom branding must remain"). This principle governs every triage decision. 2. **Confirm remote** — verify `git remote -v` shows the correct upstream origin. 3. **Fetch latest** — `git fetch origin` to get current upstream state. ## Phase 1: Analyze Divergence Gather the full picture before making any decisions. **Run these commands:** ```bash # Find common ancestor git merge-base HEAD origin/main # Count divergence git rev-list --count HEAD ^origin/main # local-only commits git rev-list --count origin/main ^HEAD # remote-only commits # List all changed files on each side git diff --name-only $(git merge-base HEAD origin/main) HEAD > /tmp/local-changes.txt git diff --name-only $(git merge-base HEAD origin/main) origin/main > /tmp/remote-changes.txt ``` **Categorize every file into three buckets:** | Bucket | Definition | Action | |--------|-----------|--------| | **Remote-only** | Changed upstream, untouched locally | Accept automatically | | **Local-only** | Changed locally, untouched upstream | Keep as-is | | **Both-changed** | Modified on both sides | Create triage todo | ```bash # Generate buckets comm -23 <(sort /tmp/remote-changes.txt) <(sort /tmp/local-changes.txt) > /tmp/remote-only.txt comm -13 <(sort /tmp/remote-changes.txt) <(sort /tmp/local-changes.txt) > /tmp/local-only.txt comm -12 <(sort /tmp/remote-changes.txt) <(sort /tmp/local-changes.txt) > /tmp/both-changed.txt ``` **Present summary to user:** ``` Divergence Analysis: - Common ancestor: [commit hash] - Local: X commits ahead | Remote: Y commits ahead - Remote-only: N files (auto-accept) - Local-only: N files (auto-keep) - Both-changed: N files (need triage) ``` ## Phase 2: Create Triage Todos For each file in the "both-changed" bucket, create a triage todo using the template at [merge-triage-template.md](./assets/merge-triage-template.md). **Process:** 1. Determine next issue ID: `ls todos/ | grep -o '^[0-9]\+' | sort -n | tail -1` 2. For each both-changed file: - Read both versions (local and remote) - Generate the diff: `git diff $(git merge-base HEAD origin/main)..origin/main -- ` - Analyze what each side intended - Write a recommendation based on the guiding principle - Create todo: `todos/{id}-pending-p2-merge-{brief-name}.md` **Naming convention for merge triage todos:** ``` {id}-pending-p2-merge-{component-name}.md ``` Examples: - `001-pending-p2-merge-marketplace-json.md` - `002-pending-p2-merge-kieran-python-reviewer.md` - `003-pending-p2-merge-workflows-review.md` **Use parallel agents** to create triage docs when there are many conflicts (batch 4-6 at a time). **Announce when complete:** ``` Created N triage todos in todos/. Ready to review one-by-one. ``` ## Phase 3: Triage (Review One-by-One) Present each triage todo to the user for a decision. Follow the `/triage` command pattern. **For each conflict, present:** ``` --- Conflict X/N: [filename] Category: [agent/command/skill/config] Conflict Type: [content/modify-delete/add-add] Remote intent: [what upstream changed and why] Local intent: [what local changed and why] Recommendation: [Accept remote / Keep local / Merge both / Keep deleted] Reasoning: [why, referencing the guiding principle] --- How should we handle this? 1. Accept remote — take upstream version as-is 2. Keep local — preserve local version 3. Merge both — combine changes (specify how) 4. Keep deleted — file was deleted locally, keep it deleted ``` **Use AskUserQuestion tool** for each decision with appropriate options. **Record decisions** by updating the triage todo: - Fill the "Decision" section with the chosen resolution - Add merge instructions if "merge both" was selected - Update status: `pending` → `ready` **Group related files** when presenting (e.g., present all 7 dspy-ruby files together, not separately). **Track progress:** Show "X/N completed" with each presentation. ## Phase 4: Execute Decisions After all triage decisions are made, execute them in a structured order. ### Step 1: Create Working Branch ```bash git branch backup-local-changes # safety net git checkout -b merge-upstream origin/main ``` ### Step 2: Execute in Order Process decisions in this sequence to avoid conflicts: 1. **Deletions first** — Remove files that should stay deleted 2. **Copy local-only files** — `git checkout backup-local-changes -- ` for local additions 3. **Merge files** — Apply "merge both" decisions (the most complex step) 4. **Update metadata** — Counts, versions, descriptions, changelogs ### Step 3: Verify ```bash # Validate JSON/YAML files cat | python3 -m json.tool > /dev/null # Verify component counts match descriptions # (skill-specific: count agents, commands, skills, etc.) # Check diff summary git diff --stat HEAD ``` ### Step 4: Commit and Merge to Main ```bash git add # stage explicitly, not -A git commit -m "Merge upstream vX.Y.Z with [guiding principle] (vX.Y.Z+1)" git checkout main git merge merge-upstream ``` **Ask before merging to main** — confirm the user wants to proceed. ## Decision Framework When making recommendations, apply these heuristics: | Signal | Recommendation | |--------|---------------| | Remote adds new content, no local equivalent | Accept remote | | Remote updates content local deleted intentionally | Keep deleted | | Remote has structural improvements (formatting, frontmatter) + local has content changes | Merge both: remote structure + local content | | Both changed same content differently | Merge both: evaluate which serves the guiding principle | | Remote renames what local deleted | Keep deleted | | File is metadata (counts, versions, descriptions) | Defer to Phase 4 — recalculate from actual files | ## Important Rules - **Never auto-resolve "both-changed" files** — always triage with user - **Never code during triage** — triage is for decisions only, execution is Phase 4 - **Always create a backup branch** before making changes - **Always stage files explicitly** — never `git add -A` or `git add .` - **Group related files** — don't present 7 files from the same skill directory separately - **Metadata is derived, not merged** — counts, versions, and descriptions should be recalculated from actual files after all other changes are applied - **Preserve the guiding principle** — every recommendation should reference it