with one click
existing-pr-followup-worktree
// When a user asks for follow-up changes to work already under review, use a fresh worktree on the existing PR branch and update the same PR instead of creating a new one.
// When a user asks for follow-up changes to work already under review, use a fresh worktree on the existing PR branch and update the same PR instead of creating a new one.
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | existing-pr-followup-worktree |
| description | When a user asks for follow-up changes to work already under review, use a fresh worktree on the existing PR branch and update the same PR instead of creating a new one. |
| version | 1.0.0 |
| author | Hermes Agent |
| license | MIT |
| metadata | {"hermes":{"tags":["git","github","pr","worktree","review-feedback"]}} |
Use this when the user asks for additional fixes or refinements to a PR that already exists.
Core rule:
Important distinction learned from review follow-up work:
origin/main (or the PR base branch), create a fresh worktree from that merged tip, and open a new follow-up branch/PR for the requested additional work.origin/main.origin/main but is missing from the old PR branch, do not force the change onto the stale PR branch. Treat it as a new independent task: branch from latest origin/main, open a separate PR, and mention explicitly why the old PR branch was no longer the right base.origin/main after the old PR branched, switching to a new main-based branch is usually safer than backporting those structural assumptions into the old PR.origin/main, recreate or reapply only that subset on a fresh latest-main branch.main, not a PR that drags along the larger in-progress branch context.main-based branch, because that keeps the review diff small and avoids duplicating the parent PR's not-yet-merged code.origin/main with one fresh worktree per new child PR.git checkout origin/<parent-branch> -- <needed-paths...>), not as the base of the new independent PR, unless true parent-only dependencies exist.Reusing a stale local checkout or creating a second PR for review follow-up causes confusion:
Use GitHub CLI to confirm the PR number, head branch, and current status.
Example:
gh pr view <pr-number> --json number,headRefName,url,state,updatedAt
Record:
PR number
head branch name
PR URL
PR state (OPEN, MERGED, or CLOSED)
Important precondition check learned from real follow-up work:
Before doing any worktree/rebase/update flow, confirm the PR is still open.
Also confirm the remote head branch still exists.
A PR can already be merged while gh pr view still shows the historical headRefName and headRefOid.
In that case, git fetch origin <head-branch>:... or git worktree add ... origin/<head-branch> will fail because the remote branch has already been deleted.
Additional absorbed-by-main lesson from later corp-web-japan PR maintenance: before a requested rebase onto latest main + squash pass across old PRs, inspect the latest origin/main log first. If origin/main already contains the PR's feature/refactor as a merged commit (for example refactor: ... (#277) or (#278)), treat that PR as already absorbed rather than as an active rewrite target.
In that situation, recreating/pushing the old branch name at the latest origin/main tip is only bookkeeping; it does not reopen or meaningfully update the historical merged PR, and gh pr view can keep reporting the old merged headRefOid.
Practical rule: if the latest main tip already includes the PR's change, report that the PR has effectively been superseded by main, avoid claiming a normal in-place PR update happened, and focus any remaining rewrite/squash work only on still-open PRs whose diffs are not yet absorbed.
Recommended check sequence:
env -u GITHUB_TOKEN gh pr view <pr-number> --json state,headRefName,headRefOid,baseRefName,url
git ls-remote origin refs/heads/<head-branch>
How to interpret it:
OPEN and the remote head branch exists: continue with the normal existing-PR follow-up workflow.MERGED or CLOSED: do not treat it as an update-in-place target anymore.ls-remote returns nothing, re-check the PR metadata because the branch may have been deleted manually or the PR may have just been merged.From the repo root:
git fetch origin --prune
git worktree add .worktrees/<topic> origin/<pr-branch>
cd .worktrees/<topic>
git checkout -b <pr-branch>-local --track origin/<pr-branch> 2>/dev/null || git checkout <pr-branch>
Important path rule learned from real PR follow-up work:
feat/internal-mdx-list-demo-whitepaper-ux with worktree path .worktrees/internal-mdx-list-demo-whitepaper.worktrees/feat/internal-mdx-list-demo-whitepaper-uxterminal commands with absolute file-tool paths during follow-up edits<topic> path first, then attach it to the PR branch tip.Practical note:
git checkout <pr-branch> in the fresh worktree will fail. In that case, staying on the detached origin/<pr-branch> checkout is acceptable for a small follow-up.git push origin HEAD:<pr-branch>.gh pr ... commands that rely on the current branch can fail with errors like could not determine current branch. Prefer explicit branch/PR arguments, e.g. gh pr view <pr-branch> --json ... or gh pr view <pr-number> --json ... instead of assuming branch autodetection will work.main and not from some old local branch.patch, write_file, sometimes read_file) do not automatically follow a terminal command's workdir, and repo-root-relative file paths can silently hit the main checkout instead of the fresh PR worktree.git -C, push, status, rev-parse, diff), prefer an absolute worktree path over a repo-relative .worktrees/<topic> path; in practice the agent/session cwd can differ between calls, and a relative worktree path that worked earlier can later fail with No such file or directorywrite_file and read_file may accept absolute worktree paths while patch can still resolve against the repo root or fail on the same path stylepatch or similar edit behaves unexpectedly, stop and verify whether the change landed on the root checkout instead of the PR worktree before continuinggit -C <absolute-worktree-path> status --shortread_file output is a display format, not raw file bytes. It includes LINE|content prefixes, and repeated reads can sometimes return a dedup placeholder like File unchanged since last read... instead of the raw file body.read_file(...)["content"] directly back into write_file/scripted rewrites unless you first strip the line-number prefixes and confirm you are holding real source text rather than a dedup message.terminal or use write_file only with content you constructed explicitly, not blindly copied from read_file display output.maingit status --short
git -C .worktrees/<topic> status --short
If the root checkout changed but the PR worktree did not, stop and move the change onto the PR worktree before committing.git diff origin/main...HEAD only compares committed history. It does not include your current uncommitted edits, so after you start rewriting the PR scope it can misleadingly keep showing the old PR file list/stat even though your worktree has already changed direction.# What is currently uncommitted in the worktree?
git diff --stat
git diff --name-only
# What committed PR history currently differs from main?
git diff --stat origin/main...HEAD
# What would the final result look like if committed right now?
git diff --stat origin/main
git diff --name-only origin/main
origin/main...HEAD to inspect the already-pushed/committed PR scopegit diff to inspect your local follow-up editsgit diff origin/main when reducing or rewriting the PR scope and you need to see the combined final tree-vs-main result before committingorigin/main...HEAD, you can think the unwanted old scope is still present even after you already removed it locally.git branch --show-current
git status -sb
git rev-parse HEAD origin/<pr-branch>
If local HEAD and origin/<pr-branch> differ before you start, stop and understand why.
Important practical finding:
ahead/behind the remote PR branch, which makes it unsafe as the starting point for a new follow-up edit.git status -sb shows divergence like [ahead N, behind M], prefer creating a brand-new worktree directly from origin/<pr-branch> again, even if another PR-named worktree already exists.origin/<pr-branch> is usually the safest follow-up base. Make the edit there, commit, and push with git push origin HEAD:<pr-branch>.Edit only the files needed for the follow-up request.
Run the relevant checks before pushing.
Typical examples:
npm run test:run
npm run typecheck
Special case: the user asks you to remove forbidden-scope changes from an already-open PR branch
git fetch origin --prune
gh pr view <pr-number> --json headRefName
git worktree add .worktrees/<topic> origin/<pr-branch>
cd .worktrees/<topic>
git diff --name-only origin/main...HEAD -- 'forbidden/scope/**' > /tmp/forbidden-files.txt
while IFS= read -r f; do
git checkout origin/main -- "$f"
done < /tmp/forbidden-files.txt
git diff --name-only origin/main...HEAD -- 'forbidden/scope/**' should become empty after the revert commit.git diff --stat -- 'forbidden/scope/**' should reflect only your local revert before commit, then become clean after commit.origin/main...HEAD answers "does the PR still contain this forbidden scope?"git diff -- ... answers "what is currently uncommitted in the worktree?"xargs -a; prefer the portable while IFS= read -r f; do ...; done loop above.git add forbidden/scope
git commit -m "fix: revert forbidden-scope changes"
git push origin HEAD:<pr-branch>
When the follow-up request is a broad cleanup of repeated behavior on the existing PR branch (for example removing all target="_blank" / rel="noreferrer" patterns, or fully eliminating a wrapper component the user no longer wants), use an exhaustive search-and-verify loop instead of editing only the first example the user mentions:
src/ (and tests if relevant)Important follow-up nuance learned from wrapper-removal work:
/t/... sidebars versus public /... sidebars)page.tsx, do not respond to wrapper-removal follow-up by extracting those authored sections into the new concrete component. Keep route-owned hero/CTA markup in page.tsx, and extract only the repeated structural body that the user actually wants centralized (for example the sidebar markup and its link set)./... resource links vs preview /t/... resource links), prefer a single concrete component with a small links prop or variant prop over recreating multiple near-identical concrete components. This preserves the narrower abstraction level the user asked for while still avoiding repeated sidebar markup across routes.This is especially useful when the user phrases the request as "all links" or says a wrapper should disappear entirely. The cited example file is only a clue, not the full edit set.
git add <files>
git commit -m "fix: address PR feedback"
git push origin HEAD:<pr-branch>
Do not create a new PR here.
Important user-expectation nuance learned from active-review follow-up:
git push origin HEAD:<pr-branch> is rejected as non-fast-forward during PR follow-up, do not open a new PR or force-push blindly. First fetch and compare against origin/<pr-branch> because another follow-up worktree/session may already have advanced the same PR branch.
Recommended recovery flow:
git fetch origin --prune
git rev-parse HEAD origin/<pr-branch>
git rebase origin/<pr-branch>
Then resolve any conflicts and push again. This keeps the same PR branch linear while preserving already-pushed review follow-ups.git rebase origin/<pr-branch> after a non-fast-forward rejection can explode into add/add conflicts on files that were already rewritten on the remote PR branch.
git log --oneline --left-right HEAD...origin/<pr-branch> shows the remote side is a new squashed/rebuilt history while the local side still has the old stacked commitsgit rebase --abort if a rebase is in progressorigin/<pr-branch>git push origin HEAD:<pr-branch>origin/<pr-branch> and then rebased that detached HEAD onto origin/main before pushing, your local history may accidentally rewrite the PR's existing head commit(s), not just add your new follow-up commit.
git log --oneline --left-right HEAD...origin/<pr-branch>
shows your local side contains both your new follow-up commit and a rewritten copy of the old PR tip, while the remote side still has the original old PR tip.git fetch origin --prune
git rebase --onto origin/<pr-branch> <old-local-pr-tip> HEAD
where <old-local-pr-tip> is the local commit that used to correspond to the original PR tip before your new follow-up commit(s).git log --oneline --decorate -n 5 shows origin/<pr-branch> followed by only your intended new follow-up commit(s), rerun the targeted test, and push normally.git reset --soft <base-branch>, recommit once with the final conventional-commit message, then git push --force-with-lease origin HEAD:<pr-branch> and re-check PR/CI status.Use the same fresh-worktree principle, but rebase the PR branch tip onto origin/main instead of creating a merge commit.
Recommended flow:
git fetch origin --prune
git worktree add .worktrees/<topic> origin/<pr-branch>
cd .worktrees/<topic>
git rebase origin/main
Important practical findings:
git worktree add <path> origin/<pr-branch> often leaves you on a detached HEAD at the remote branch tip. That is acceptable for a rebase-only maintenance task.
After a detached-HEAD rebase succeeds, do not force-push immediately. First verify that the expected post-PR follow-up commit(s) still survived the rebase and that their key scoped changes are still present. A fast check is:
git log --oneline --decorate -n 5
git search-files-or-grep-for-the-target-pattern
For example, if a later follow-up changed a path prefix or removed a repeated pattern, re-check that exact pattern before pushing.
Then push with:
git push --force-with-lease origin HEAD:<pr-branch>
If conflicts occur, resolve them by preserving the existing PR's intended behavior unless the user explicitly asked to adopt the newer main behavior in that area. Rebase conflicts often happen because main has evolved policy/tests while the PR still represents a deliberate exception or targeted rollout.
Important special case: if a separate test-only PR was intentionally merged into origin/main before rebasing the implementation PR, and the implementation PR branch still contains overlapping test updates, prefer the latest-main test files during conflict resolution and keep only the implementation commits that are still unique to the PR. In practice:
git rev-list --oneline origin/main..HEAD after the rebase attempt--ours from the rebasing origin/main side (or otherwise restore the latest-main test version)mainSimilar split-PR special case: if you previously split an extract/refactor subset out of the original PR and that new PR has already merged into origin/main, rebasing the original PR can hit add/add or modify/delete conflicts against those now-upstream files.
origin/main under the final intended name/path.page.tsx or removal of the old superseded file/path).patch contents already upstream; this is normal and usually desirable.main, do not resurrect the old path just to preserve history. Preserve the final main-side extracted file and continue the rebase with the original PR's remaining unique behavior changes only.Same-file neighboring-refactor case: the current PR may refactor one section of a large route file while latest origin/main has already merged a different route-local refactor in a nearby section of that same file.
page.tsx plus companion structure tests, even though the PR scope is still valid.patch contents already upstream because its effect was already incorporated into the resolved tree. Treat that as normal if the intended final file content is still present.Stronger rewrite-on-main case: if the user explicitly asks not just for a mechanical rebase but for the PR to reflect the latest main implementation and recent merged follow-ups, a literal git rebase origin/main may be the wrong tool.
origin/main, inspect the current PR branch's intended final file set, and reconstruct that intended end state on top of latest main as a new commit sequence (often one clean commit is enough).git merge-base origin/main <pr-branch> that the PR branch now sits directly on the latest remote main tip (or that tip's exact ancestor if main advanced during work), and rerun local validation before pushing.main, do not keep the old stacked base chain by default.
origin/main.origin/main and compare origin/main...origin/<pr-branch> to see the branch's surviving direct-vs-main scope.git checkout origin/<pr-branch> -- <paths...> for the exact changed file set, then prune anything that latest main already absorbed differently).origin/main, force-push back to the same branch name, and explicitly change the PR base to main with:
gh pr edit <pr-number> --base main
main.git rev-parse HEAD
git merge-base HEAD origin/main
gh pr view <pr-number> --json baseRefName,headRefOid,updatedAt,url
git ls-remote origin refs/heads/<pr-branch>
Expect the PR base to be main, the remote head SHA to match your pushed commit, and the merge-base to equal the latest main tip (or that exact ancestor if main moved again during work).main has already absorbed some intermediate sections (for example platform/security, then whitepapers), do not blindly preserve the old mixed diff. First identify which sections are already on origin/main, then rebuild each PR so it keeps only its still-unique intended scope (for example whitepapers only, or later final CTA only) on top of latest main.origin/main, the surviving implementation diff can be correct while repository tests still fail because latest main independently changed shared content-file naming conventions (for example numeric id.mdx -> id-slug.mdx) or helper import paths in unrelated merged work.
ENOENT on old paths such as src/content/blog/21.mdx or stale import assertions like whitepaper-publication-records vs whitepapers/records, even though the PRโs own implementation files look correct.origin/main filesystem shape or current source imports directlyid-slug.mdx filenames or route-aligned helper import paths)src/content/demo/aip/1.mdx, src/content/events/1.mdx, src/content/use-cases/1.mdx, src/content/whitepapers/25.mdx, or src/content/blog/23.mdxid-slug.mdx filenames from the live tree rather than trying to revert the implementation back toward the old namingsrc/lib/publications/whitepapers/records.ts, update structure tests carefully according to the scope of that exact PR: a parent PR that has not yet moved the page import should still expect the old import path, while a later cleanup PR should expect the new route-aligned import pathcreateStandardPublicationPostLoadercreateGatedPublicationPostLoaderbuildRelatedPublicationItems or resolveRedirectablePublicationHreforigin/main already includes part of that branch's intent from an earlier sibling PR. In that case, a literal git rebase origin/main often collides because the PR is half superseded rather than fully independent.
Recommended recovery flow:
git rev-list --oneline origin/main..origin/<pr-branch> and git diff --stat origin/main...origin/<pr-branch> to see the branch's total residual scopeorigin/mainorigin/<pr-branch> into that clean worktree with git checkout origin/<pr-branch> -- <paths...>main)git push --force-with-lease origin HEAD:<pr-branch>
This is especially useful when the final desired result is not "replay every historical commit" but "same open PR branch, rewritten so only its surviving unique review scope remains on top of latest main."origin/main already contains earlier sibling PRs that localized adjacent sections (for example why, then design-elements), and the current PR localizes the next section in sequence (for example process), do not revive the old branch's transitional wrapper such as HomePagePreProcessSections or re-import older content-backed sections wholesale.
Safer reconstruction pattern:
origin/mainorigin/<pr-branch>origin/mainprocess section between the already-main design-elements section and the remaining shared post-process sections)origin/main, create a temporary local branch there, and then copy only the PR's intended scoped files from origin/<pr-branch> into that clean worktree with git checkout origin/<pr-branch> -- <paths...>. Verify the resulting diff with git diff --stat origin/main...HEAD and git diff --name-only origin/main...HEAD, run validation, commit once, then git push --force-with-lease origin HEAD:<pr-branch>. This is safer than trying to interactively prune old mixed branch history when the desired result is a clean single-scope PR.gh pr view <pr-number> --json files,commits and note the exact intended file setgit diff --stat origin/main..origin/<pr-branch>origin/mainorigin/<pr-branch> into the clean worktreeAdditional sibling-series maintenance lesson from later corp-web-japan publication-refactor PRs: when you are rebase/squash-rewriting several related open PRs in sequence, do not assume the remaining later PRs stay valid after you finish the earlier one(s).
origin/main advances during the batch (for example because PR #279 merged while you were still rewriting PR #280 and PR #281), re-fetch and re-audit every remaining open PR against the new origin/main tip before touching it.tests/mdx-redirect-contract.test.mjs start failing because latest main now contains the sibling's newer expectations.origin --prune again and record the new origin/main SHAgh pr view <pr-number> --json files for the intended file setorigin/mainorigin/main to spot any stale sibling scope that no longer belongsgit push --force-with-lease origin HEAD:<pr-branch>main as invalidating your earlier assumptions about the remaining sibling PRs' clean diff boundaries.tests/redirect-endpoints.test.mjs), do not assume the PR-branch copy is still valid on latest origin/main just because that file belongs to the intended scope.
origin/main immediately and inspect whether it reintroduces stale expectations unrelated to the PR's real behavior (for example asserting old redirect route files that latest main intentionally removed).Additional latest-main path-cleanup maintenance pattern from corp-web-japan PR 301:
origin/main has advanced with behavior changes in the same touched routes and tests before you finish review follow-up.gh pr view still shows the PR as open, but mergeStateStatus flips to DIRTY after newer main-only commits landgit rebase origin/main explodes across many route files and tests even though the PR's real intent is still just path cleanuporigin/mainsrc/content/**/<id>-<slug>.mdx guidance to fake numeric-only filenames such as <id>.mdx, or changing public-list route docs back to removed preview /t/... pathsnext available numeric filename29.mdx / 30.mdx/t/demo/acp or /t/demo/aipget-*-publication-post.ts or *-publication-records.tsorigin/main.git rev-list --oneline origin/main..HEAD
git diff --stat origin/main...HEAD
origin/main tip can leave GitHub showing no commit found on the pull request, and the PR may effectively become a closed/empty historical shell rather than an active reviewable PR.gh pr view may start reporting the PR as CLOSED, with gh pr checks returning no commit found on the pull request.main, and that rebasing to main removes the PR's independent diff.gh pr view <pr-number> --json files --jq '.files[].path'git diff --stat origin/main...origin/<pr-branch>git show origin/main:<path>)origin/mainsrc/app/page.tsx and related helpers/tests so only the current PR's still-unique route-local section remains.gh pr view <pr-number> --json files --jq '.files[].path'git diff --stat origin/main...origin/<pr-branch>git diff --stat origin/<older-pr-branch>..origin/<current-pr-branch>mainsrc/app/page.tsx or similar route files on latest-main shape, then patch only the minimal imports/sections needed for the PR's surviving scopeTopPageSections still exists on main but the PR intends to inline/remove only part of it, decide explicitly whether that PR should still depend on the container or should remove it as part of the rewritten final stateorigin/main and against the old PR branch so you can confirm the new diff kept the intended scope while dropping already-merged stale pieces.git reset --soft origin/main. On an old PR branch this can stage a huge mixed diff containing unrelated pre-main history plus later main-side changes, which is exactly what you are trying to avoid.origin/maingit diff --statgit push --force-with-lease origin HEAD:<pr-branch>PR contains unrelated commits follow-up:
git rev-list --oneline origin/main..origin/<pr-branch> and git diff --stat origin/main...origin/<pr-branch> to identify which commits/files are actually unrelated to the PR's intended scope.origin/main and do not branch-checkout the PR branch there if that branch name is already occupied by another local worktree.pr205-rewrite) and selectively copy only the intended paths from origin/<pr-branch> with git checkout origin/<pr-branch> -- <paths...>.git diff --stat origin/main...HEAD that only the intended scope remains, run validation, and then force-push the clean rewrite back to the original PR branch with git push --force-with-lease origin HEAD:<pr-branch>.origin/main and several files stop with conflict markers, it can be faster and safer to restore those conflicted files fully from origin/main first, then reapply only the PR's surviving scope on top, rather than hand-merging each conflict block.
git checkout origin/main -- <conflicted-files>git rebase --continuenpm run test:ci can fail immediately with environment/setup issues such as sh: eslint: command not found because that worktree has no local install.
npm install just to satisfy a local CI wrapper.test:ci wrapper was not runnable in that fresh worktree due to missing tooling, so the remaining confidence comes from targeted tests plus GitHub CI.origin/main has since absorbed adjacent sibling refactors that change the correct page section order and shared-shell boundaries.origin/mainAfter a rewrite-on-main like this, re-check repository tests that assert source structure, not just runtime behavior. Structure-oriented tests may need helper updates when the user intentionally changes page.tsx from inline JSX to semantic section composition.
Important final-tree review step from corp-web-japan internal demo follow-up work: even when the rebase itself is clean, do one more critical pass on the latest-main final tree before pushing. Look specifically for page-local demo hacks or magic values that only existed to force a preview state (for example sentinel dates or ad hoc query interpretation inside page.tsx). If you find them, prefer moving that behavior into a dedicated helper/resolver in src/lib/** so the route stays thin and the demo-specific state logic has an explicit name and contract.
Additional practical rebase pattern from PR 214 maintenance: when rebasing a stacked follow-up PR branch onto latest origin/main, the first implementation commit can conflict in structure-test helpers (for example tests/helpers/static-marketing-page-sources.mjs) because main has gained new sibling-section coverage since the PR branched. Resolve that first conflict by keeping the latest-main helper/test baseline and merging in the rebased PR's newly introduced section files/assertions together.
Shared primitive/component rebase pattern from PR 217 maintenance: if the conflict is in a shared list/page primitive that both main and the PR extended in different ways, do not choose one side wholesale. Inspect the latest-main file and the PR-head file side by side, keep the latest-main API additions that other routes now depend on (for example a new sidebarBasePath prop or normalized sidebar key handling), then layer the PR-only feature hook-up on top (for example initialVisibleCount plus the conditional ResourceListLoadMore branch). After resolving, verify the rebased tree still preserves both behaviors: the new main-side compatibility path for existing callers and the PR-side behavior for the targeted routes.
Shared type-contract drift pattern from the same PR 217 maintenance: after rebasing a PR that changed a broadly reused type or interface (for example adding id to a shared ResourceItem type), do not stop at the files that originally belonged to the PR. Latest main may now contain new helper layers, preview-item registries, or abstract repositories that were added after the PR branched and that still construct the old shape. CI and Vercel type-check failures can therefore surface in files the original PR never touched.
Recommended recovery flow:
npm run test:ci, and npm run build if preview deploy failed)id to ResourceItem required not only publication list mappers but also latest-main src/lib/resources/base-resource-publication.ts and src/lib/resources/resource-preview-items.ts to be updated before Verify/Vercel would pass.Important follow-on nuance from the same case: later follow-up commits in the same rebase can then conflict again in the same test file, but only because the test should reflect the repository state at that specific historical commit.
why section and its initial assertions; commit 2 changes only the Before card API; commit 3 changes only the After card API.Additional practical rebase pattern learned from PR follow-up work: sometimes an open PR branch contains several early "intermediate" commits that introduced a temporary helper or transitional architecture, while a later final commit on the same PR fully supersedes that intermediate path.
origin/main stops first on an old early commit, not on the final intended commitgit rev-list --oneline origin/main..origin/<pr-branch> to see the full branch commit stackorigin/mainorigin/<pr-branch> onto the latest-main worktree with git checkout origin/<pr-branch> -- <paths...>origin/maingit push --force-with-lease origin HEAD:<pr-branch>/t path helper, then refactored environment detection, then later replaced that whole approach with a cookie-based preview-mode toggle and server/client wrapper split. Replaying the old helper commits onto latest main produced noisy conflicts, but copying the final branch tip file set onto latest main yielded a clean and correct result.main has moved a callsite or prop contract since the old PR branched, make the smallest backward-compatible adjustment needed before pushing.In route-local/static-marketing refactor PRs, also rerun tests that read source files via helpers such as tests/helpers/static-marketing-page-sources.mjs; these helper-based tests are often what break after a rewrite-on-main because the source-of-truth path moves from shared content/container files to route-local section files.
Practical single-file test-conflict pattern from AI Crew PR follow-up work: when rebasing a small one-commit PR onto newer origin/main, the only conflict can be a structure-oriented test file that latest main already expanded for a sibling section while the PR adds assertions for its own still-unique section behavior. In that case:
rebase --continueCommon docs/memory conflict case: append-only markdown files such as .hermes/memories/*.md or skill SKILL.md files often conflict during rebase when both main and the PR added new bullets near the end of the same section.
ยง lines in memory files), then continue the rebase.skills-jk, where concurrent memory/skill updates are common and dropping one side silently loses durable knowledge.skills-jk sub-case: distinguish add/add conflicts on newly created skill files from content conflicts inside existing files.
In stacked PR chains, origin/main may already contain one or more earlier sibling PR commits from the same series. Sometimes Git prints a clean skipped previously applied commit <sha> warning, but do not rely on that. It can also stop with normal content or add/add conflicts even when the current patch is logically already upstream, especially if main contains a later follow-up commit that edited the same files after the sibling PR merged.
Before hand-merging, inspect git rebase --show-current-patch and the conflicted files. If the current patch is an already-upstream sibling change rather than this PR's unique change, prefer git rebase --skip instead of resolving the conflict manually.
A practical signal for this case: the conflict appears in files changed by a previously merged sibling PR, the patch title/message belongs to that older PR topic, and the conflict markers show HEAD/origin/main already contains the newer desired shape of that feature while the replayed patch is trying to reintroduce the older version. This commonly happens when a feature PR was branched from another open PR and later rebased after the parent PR merged.
If an early conflicted commit is resolved by manually rebuilding the branch into the final intended architecture, inspect later commits in the old PR before blindly replaying them. A later commit may only re-introduce an obsolete intermediate abstraction (for example a temporary wrapper or page-sections registry) that is no longer desired after the manual resolution.
In that case, prefer this sequence:
git show --stat <commit>git rebase --skipTypical signal that skipping is correct: the pending commit mainly adds a now-unwanted intermediate abstraction layer while your current resolved tree already passes the intended structure and verification checks.
In non-interactive agent environments, git rebase --continue may try to open Vim and hang. Prefer:
GIT_EDITOR=true git rebase --continue
after staging the resolved files.
After force-pushing, verify both the remote branch tip and the PR head SHA. gh pr view can lag briefly right after the push, so confirm with both:
git ls-remote origin refs/heads/<pr-branch>
gh pr view <pr-number> --json headRefOid,updatedAt,url
If they differ momentarily, wait a few seconds and re-check before concluding the push failed.
Important stale-worktree lesson from PR 190 follow-up work: after a force-push/rewrite of an open PR branch, any older local worktree that still has the same branch checked out can be left behind at the pre-push commit and become misleadingly stale.
git -C <old-worktree> rev-parse HEAD
git rev-parse origin/<pr-branch>
origin/<pr-branch> and continue there.Additional fresh-worktree sanity check from PR 233 follow-up: if a newly created follow-up worktree path is not recognized by Git (fatal: not a git repository) or only contains a partial subtree such as tests/, treat it as broken immediately.
rm -rf <bad-worktree>
git worktree prune
git fetch origin --prune
git worktree add <new-clean-path> origin/<pr-branch>
git -C <new-clean-path> rev-parse --show-toplevel
git -C <new-clean-path> status -sb
find <new-clean-path> -maxdepth 2 | sed -n '1,30p'
gh pr view <pr-number> --json number,headRefName,updatedAt,commits
gh pr checks <pr-number>
Important practical note:
gh pr checks <pr-number> returns a non-zero exit code not only for hard failures, but also while checks are still pending.no checks reported on the '<branch>' branch before GitHub attaches the new workflow runs to the PR head.no checks reported message as proof that your branch update failed.gh pr view <pr-number> --json headRefOid,updatedAt,url and/or git ls-remote origin refs/heads/<pr-branch>.gh pr checks <pr-number> after a short wait and classify the resulting checks as pass, pending, or fail.pull_request runs for the new head (gh pr checks keeps saying no checks reported on the '<branch>' branch, and querying check-runs/actions by the new head SHA returns zero results).headRefOid match your pushed SHAworkflow_dispatch support (for example ci.yml, preview-deploy workflow)gh workflow run ci.yml --ref <pr-branch>
gh workflow run deploy-preview.yml --ref <pr-branch> -f BRANCH=<pr-branch>
Confirm:
This splits one review cycle across multiple PRs.
This risks mixing stale local history into the PR.
Unless explicitly requested, this creates unnecessary cherry-picking or duplicate PRs.