Problem to Fix
After running the standard /start → /submit-for-review workflow over many PRs, the local integration branch (BRANCH_DEV, e.g. dev) accumulates a SHA-divergent
history from origin/dev. The two branches remain content-equivalent (same files at the same versions) but git's commit graph shows them as wildly divergent.
Concretely, in a project running CodeCannon since November 2025, the symptom today is:
$ git status
On branch dev
Your branch and 'origin/dev' have diverged,
and have 466 and 465 different commits each, respectively.
$ git diff origin/dev dev --shortstat
1 file changed, 27 insertions(+), 389 deletions(-) # one intentional local change
466 unique local commits vs 465 unique remote commits, but a near-empty content diff — the unmistakable fingerprint of parallel merge commits for the same logical PR,
repeated hundreds of times.
Why it Matters
git status and git pull become misleading and painful. Every interactive git pull on the integration branch attempts a merge instead of a fast-forward,
requiring conflict resolution that has no real conflicts.
- Risk of catastrophic force-push. If a user (or another tool) ever runs
git push origin dev --force-with-lease from local dev, they would rewrite the
production-adjacent branch with a parallel history. Branch protection helps but not all repos / setups have it.
- Confusing onboarding signal. A new contributor pulling the repo sees their local
dev immediately "ahead" of origin by hundreds of commits with no obvious cause,
and there's no good way to fix it short of git reset --hard origin/dev.
- Compounds across the toolchain. Same pattern likely affects
BRANCH_PROD via /deploy.
General Approach
The root cause is in Makefile.agents.mk:68-87, the merge: target:
merge:
@branch=$$(git rev-parse --abbrev-ref HEAD); \
...
gh pr merge --merge && \
git checkout $(INTEGRATION_BRANCH) && \
git pull origin $(INTEGRATION_BRANCH) && \
echo "PR merged into $(INTEGRATION_BRANCH)."
The trailing git pull is the problem. After gh pr merge --merge:
- origin/INTEGRATION_BRANCH has a fresh merge commit (created server-side by GitHub).
- Local INTEGRATION_BRANCH either:
- Is already behind (first iteration — fast-forward works), OR
- Has a different prior state from a previous iteration of this same bug (every subsequent iteration — pull creates a merge commit).
Once the divergence starts (could be a single hand-edit on local dev, or an interrupted merge, or an earlier CodeCannon version), every subsequent make merge makes it
worse: each iteration adds two distinct merge commits — one server-side and one client-side — for the same logical PR.
Suggested fix — replace the git pull with git fetch + git reset --hard:
gh pr merge --merge && \
git checkout $(INTEGRATION_BRANCH) && \
git fetch origin $(INTEGRATION_BRANCH) && \
git reset --hard origin/$(INTEGRATION_BRANCH) && \
echo "PR merged into $(INTEGRATION_BRANCH)."
This treats origin/$(INTEGRATION_BRANCH) as the canonical source of truth and makes local $(INTEGRATION_BRANCH) a perfect mirror after every merge. No divergence can accumulate.
--hard is safe here because the CodeCannon workflow explicitly forbids direct commits to the integration branch (all changes flow through feature branches + PRs). Any
local-only commits on INTEGRATION_BRANCH are by definition work that wasn't shipped — and git reflog preserves them for 90 days in case of accident.
The same fix should be applied in two other places that use git pull on the integration branch:
- skills/start.md Step 4 ("Create feature branch") — git checkout {{BRANCH_DEV}} && git pull origin {{BRANCH_DEV}} should be git fetch && git reset --hard
origin/{{BRANCH_DEV}} (or, if preserving "stop if user has uncommitted local dev work" is desired, use git pull --ff-only and fail loudly).
- skills/start.md Step 4 (Case B / resume) — same pattern.
- Any analogous step in skills/deploy.md that updates local BRANCH_PROD.
Using --ff-only is a slightly more conservative alternative — it would fail on a divergent local branch rather than silently rewriting it, surfacing the divergence to the user the first time it happens.
Complexity
Verification / QA effort: moderate
Manual repro is straightforward (run the workflow a few times against a test repo and observe git rev-list --count origin/dev..dev growing). Automated test would need to spin up a real GitHub-backed repo or mock gh pr merge behavior. Fix itself is one line; ensuring no regressions across trunk-mode (no BRANCH_DEV) vs two-branch vs
three-branch configurations requires testing each profile.
Acceptance Criteria
- After running /start → /submit-for-review against a clean local clone, git rev-list --count dev..origin/dev and git rev-list --count origin/dev..dev both return 0.
- Repeating the workflow 5 times in a row still leaves both counts at 0.
- git log --oneline dev origin/dev shows the same merge commits — no parallel SHAs for the same PR.
- In trunk mode (no BRANCH_DEV), the same invariant holds for BRANCH_PROD.
- Existing projects with already-divergent local branches can recover by running the fixed make merge once (the git reset --hard brings them in line) — no manual cleanup
script needed.
- skills/start.md and skills/deploy.md use the same fetch + reset (or pull --ff-only) pattern wherever they previously used git pull on an integration branch.
Problem to Fix
After running the standard
/start→/submit-for-reviewworkflow over many PRs, the local integration branch (BRANCH_DEV, e.g.dev) accumulates a SHA-divergenthistory from
origin/dev. The two branches remain content-equivalent (same files at the same versions) but git's commit graph shows them as wildly divergent.Concretely, in a project running CodeCannon since November 2025, the symptom today is:
$ git status
On branch dev
Your branch and 'origin/dev' have diverged,
and have 466 and 465 different commits each, respectively.
$ git diff origin/dev dev --shortstat
1 file changed, 27 insertions(+), 389 deletions(-) # one intentional local change
466 unique local commits vs 465 unique remote commits, but a near-empty content diff — the unmistakable fingerprint of parallel merge commits for the same logical PR,
repeated hundreds of times.
Why it Matters
git statusandgit pullbecome misleading and painful. Every interactivegit pullon the integration branch attempts a merge instead of a fast-forward,requiring conflict resolution that has no real conflicts.
git push origin dev --force-with-leasefrom localdev, they would rewrite theproduction-adjacent branch with a parallel history. Branch protection helps but not all repos / setups have it.
devimmediately "ahead" of origin by hundreds of commits with no obvious cause,and there's no good way to fix it short of
git reset --hard origin/dev.BRANCH_PRODvia/deploy.General Approach
The root cause is in
Makefile.agents.mk:68-87, themerge:target:The trailing git pull is the problem. After gh pr merge --merge:
Once the divergence starts (could be a single hand-edit on local dev, or an interrupted merge, or an earlier CodeCannon version), every subsequent make merge makes it
worse: each iteration adds two distinct merge commits — one server-side and one client-side — for the same logical PR.
Suggested fix — replace the git pull with git fetch + git reset --hard:
This treats
origin/$(INTEGRATION_BRANCH)as the canonical source of truth and makes local$(INTEGRATION_BRANCH)a perfect mirror after every merge. No divergence can accumulate.--hard is safe here because the CodeCannon workflow explicitly forbids direct commits to the integration branch (all changes flow through feature branches + PRs). Any
local-only commits on INTEGRATION_BRANCH are by definition work that wasn't shipped — and git reflog preserves them for 90 days in case of accident.
The same fix should be applied in two other places that use git pull on the integration branch:
origin/{{BRANCH_DEV}} (or, if preserving "stop if user has uncommitted local dev work" is desired, use git pull --ff-only and fail loudly).
Using
--ff-onlyis a slightly more conservative alternative — it would fail on a divergent local branch rather than silently rewriting it, surfacing the divergence to the user the first time it happens.Complexity
Verification / QA effort: moderate
Manual repro is straightforward (run the workflow a few times against a test repo and observe git rev-list --count origin/dev..dev growing). Automated test would need to spin up a real GitHub-backed repo or mock gh pr merge behavior. Fix itself is one line; ensuring no regressions across trunk-mode (no BRANCH_DEV) vs two-branch vs
three-branch configurations requires testing each profile.
Acceptance Criteria
script needed.