git-workflows
Reusable git delivery workflows derived from local slash commands (commit, push, PR, release notes, and GitHub Actions failure triage with worktree-based fixes).
SKILL.md
Full skill instructions
Git Workflows (from local commands)
This skill turns the repo's former local command templates into reusable git delivery workflows.
When to use this skill
Use this skill when the user asks to:
- run a commit workflow (conventional-changelog style)
- push changes with safe branching and open a PR
- generate release notes from git history / GitHub context
- publish a version from
main(including bilingual README Recent updates on minor bumps) - analyze the latest failed GitHub Actions workflow, attempt a fix in an isolated worktree, and submit a PR
- move your own uncommitted changes onto a clean branch when the current workspace also holds someone else's edits
- check IDE icon configuration consistency across document components and source files
Source of truth
All detailed steps and constraints live in:
references/source-commands.md
This skill describes how to apply them consistently across agents/editors without duplicating implementation details.
Execution rules
- Read the requested command template from
references/source-commands.md. - Follow it as workflow steps, not as a loose summary.
- Keep actions safe by default:
- avoid destructive git operations unless explicitly requested
- avoid committing secrets
- keep changes minimal and localized
- If a workflow implies external side effects (push/PR/release), request explicit confirmation right before performing the side effect.
Repo-specific gotchas (CloudBase-MCP)
- Sync dependencies before building on
main. A stalenode_modulesshows up as a type error in code that is actually correct (seen:@cloudbase/manager-node5.8.6 installed vs 5.8.8 locked →Property 'ExternalStorage' does not exist on type 'CreateEnvParams'). Verify withnpm pack <pkg>@<locked-version>and read the.d.tsbefore touching code. pnpm installfails inside the sandbox withERR_PNPM_CODEBUDDY_BROKER_DENY(symlink ENOENT), and the failed run deletes the package directory that was previously there. Re-run the same command with sandbox disabled before concluding anything about the build.npm run buildsucceeds even when the last step is blocked. The build'srm -rf dist/typesis refused by the safe-delete hook (325 files > threshold), so the log ends with aSAFE_DELETE_BULK_CONFIRM_REQUIREDline anddist/types/is left behind. Judge the build by whether webpack printedERROR in, not by that trailing message.- Release-note scope is
git log <previous-tag>..HEAD, including merge commits. A PR number lower than the current version does not mean it already shipped — PRs often merge after the tag that "should" have contained them. CHANGELOG.md's## Unreleasedis not rotated on release in this repo; publish a GitHub Release and leave the changelog alone unless asked.- Version bump surface:
mcp/package.json+config/source/**skill versions (node scripts/sync-skill-versions.mjs --version X.Y.Z) +config/.claude/skillsmirror (npm run sync:claude-skills-mirror) + optional README bullets (patch: only for clearly user-visible capability, and bothREADME.mdandREADME.zh-CN.md, verified withnpm run check:readme-sync).
Redacting internal references from a pushed PR
Public-repo PR metadata — the title, the body, each commit headline, each commit message — is as public as the diff, yet it never appears in the diff, so no file-layer guard covers it. npm run check:internal-refs enforces this in CI (see internal-docs-guard.yml). When a reference has already been pushed, fix it in place:
- Enumerate every source before editing anything.
gh pr view <n> --json commitstruncates headlines — read messages from git instead (git log --format='%s%n%b' origin/main..<branch>) and the body viagh api repos/<owner>/<repo>/pulls/<n> --jq .body. Body and commit messages are separate fixes; doing only one is the usual miss. - Amend the message, then prove nothing else moved:
git commit --amend -F -, followed bygit diff --stat <old-sha> HEAD— it must be empty. - Force-push with
--force-with-lease. The repo rule says "no--force", but rewriting an already-pushed message has no alternative; the lease form still refuses to clobber a concurrent update. - Rewrite the body with
gh pr edit <n> --body-file -. - Clear local-only commits carrying the same problem: back up first (
git diff > /tmp/residual.patch), thengit reset --hard origin/main. The content is normally already in the PR it was split out of, so nothing is lost — verify each file is covered by a branch before resetting.
A --force-with-lease push re-triggers CI, and this repo's Publish MCP Package to pkg.pr.new job runs live cloud integration tests that flake on timeouts (seen: cloud-function create/call at 60s). Read the failed test name before assuming the rewrite broke something — a message-only amend cannot.
Recovering a stacked PR whose base was squash-merged
When PR B is stacked on branch A (base = A's branch) and A is squash-merged into main, A's commits on main get new shas. A's branch is no longer an ancestor of main, but B's base pointer still points at it — so B turns CONFLICTING even though nothing in it changed. Any PR merged into A's branch in the meantime (a deeper stack) makes the collision certain.
- Pre-flight the conflict set before touching anything:
git merge-tree --write-tree <A's-branch> <B's-branch>lists the conflicting paths directly. Cheap, and it tells you which files need a decision. - Rebase onto
main, dropping the already-merged commit:git rebase --onto origin/main <A's-original-commit>. Dropping it is the point — its content is onmain, and replaying it would re-apply an already-merged change. Do not rebase onto A's stale branch. - Resolve by file type (these three recur):
CHANGELOG.md— keep both sides; bullet order is irrelevant. Re-align blank-line spacing if the merge changed it.package.json— take the union; the addedscriptsentries come from different PRs and almost never truly conflict.config/source/editor-config/compat-baseline.json— takemain's side (git checkout --oursduring rebase), then recompute after the rebase finishes withnode scripts/update-compat-baseline.mjs. Sanity check: the recomputed diff should match the original commit's line count (a version bump touching 3 skills produced 23±, i.e. the same 46 lines).
- Fold the recomputed baseline into the commit that caused it —
git commit --fixup=<sha>thenGIT_SEQUENCE_EDITOR=true git rebase -i --autosquash <main-sha>. It belongs to the version bump, not a standalone commit.git commit --fixupsometimes printsnothing to commitand still squashes correctly afterwards — judge bygit log, not that message. - Retarget the base and rewrite the description:
gh pr edit <n> --base main; the PR now carries the whole stack, so its title and body must cover every commit in it, not just the top one. - Verify on the rebased content, not just by "no conflict markers": run the version check, the compat diff, and the unit tests. If the PR adds a CI gate, running that gate against
origin/mainon this branch is the strongest available check — it exercises the gate against real input.
Trap: a paths-filtered workflow can silently not run at all
After a force-push (synchronize) while the base was still the stale stacked branch, the workflows that carry a paths: filter (compat-check.yml, check-prompts-sync.yml) did not trigger at all — only the unfiltered internal-docs-guard.yml and the push-triggered Sync to CNB ran. gh pr checks does not list checks that never ran, so the PR looked clean while two checks had simply been skipped.
- Judge with the API, not the UI:
gh api "repos/<owner>/<repo>/actions/runs?branch=<branch>" --jq '.workflow_runs[] | "\(.name) \(.event) \(.conclusion)"'. A workflow missing from that list was skipped, not passed. - Fix:
gh pr close <n>thengh pr reopen <n>. Thereopenedevent recomputes against the new base and triggers the filtered workflows normally.
When the PR is not stacked and main simply moved
A plain PR turns CONFLICTING the same way — usually on config/source/editor-config/compat-baseline.json alone, since every branch that regenerates it rewrites the same lines. git merge origin/main is fine here: the squash on merge erases the extra merge commit, and there is no already-merged commit to drop. Watch the direction — during a merge main is --theirs (git checkout --theirs …); during a rebase it is --ours.
Then regenerate the whole chain on top of the merge rather than hand-resolving the generated side:
node scripts/generate-prompts-data.mjs, thennode scripts/generate-prompts.mjsnode scripts/sync-claude-skills-mirror.mjsnode scripts/build-compat-config.mjsnode scripts/update-compat-baseline.mjs
Merging main typically brings changes under config/source/skills/** as well, and those propagate into every IDE mirror and every generated surface — recomputing only the baseline leaves compat-diff or prompts-sync red. Close with node scripts/diff-compat-config.mjs (Has blocking diff: NO), node scripts/check-prompts-sync.mjs, and node scripts/sync-claude-skills-mirror.mjs --check.
Isolating your own changes from a shared dirty workspace
A workspace can hold several people's uncommitted work at once, sometimes on a branch whose PR already merged. Committing there means either shipping their work or fighting the index. Move your files onto a clean branch instead.
- Split by ownership before anything else.
git status --porcelainlists the whole workspace;git diff --stat -- <your paths>againstgit diff --stat -- . ':(exclude)<your paths>'separates yours from theirs. Trace each of your files back to the edit that produced it — a workspace is not yours just because you were the last one in it. - Snapshot your files as a patch, not a stash.
git diff -- <your paths> > /tmp/<name>.patch, then confirm the set withgrep -E '^diff --git' /tmp/<name>.patch. A patch travels between worktrees; a stash is per-worktree and easy to strand. - Base the new branch on the fetched
origin/main.git fetch originthengit worktree add -b <new-branch> .worktrees/<dir> origin/main. Never base it on the current branch — if that branch's PR was squash-merged, its commits are already onmainunder new shas. - Give the new worktree
node_modules. A fresh worktree has none. Check scripts that import only node builtins still run, butsync-skill-versions.mjsand vitest die withMODULE_NOT_FOUND:ln -sfn <main-worktree>/node_modules node_modules. - Apply, then check what is staged.
git apply --check <patch>beforegit apply <patch>;git addonly your paths;git status --porcelain | grep -v '^M 'must come back empty, because anything left unstaged or untracked silently misses the PR. - Clean the original workspace with
git restore <your paths>only. Notgit checkout -- ., notgit stash— either takes the other person's work with it. Their files must stay modified exactly as they were. - Rule out a duplicate before starting.
git worktree listcan show aprunableentry whose branch overlaps yours;gh pr list --head <branch> --state allsays whether it already merged. An overlapping branch is more often the precedent to extend than parallel work.
Run the repo's gates in the new worktree, not the original one — the new tree is the only one being pushed.
Command mapping
See references/command-catalog.md.
