Skip to content

feat(skills): add squash-merge skill for preserving commit history on upstream merges#5782

Merged
singlerider merged 7 commits intozeroclaw-labs:masterfrom
singlerider:feat/squash-merge-skill
Apr 16, 2026
Merged

feat(skills): add squash-merge skill for preserving commit history on upstream merges#5782
singlerider merged 7 commits intozeroclaw-labs:masterfrom
singlerider:feat/squash-merge-skill

Conversation

@singlerider
Copy link
Copy Markdown
Collaborator

Summary

  • Base branch target: master
  • Problem: Merging PRs into upstream without a standardized workflow produces either useless squash commit messages (GitHub default) or "Closed" instead of "Merged" (direct push), losing PR badge, issue auto-close, and commit history.
  • Why it matters: Maintainers need both the purple Merged badge (for issue auto-close and PR linkage) and a meaningful commit history in the squash body. Neither GitHub's default nor direct push delivers both.
  • What changed: Added .claude/skills/squash-merge/SKILL.md β€” a new Claude Code skill that resolves the PR, collects its commit history, builds a conventional-commit subject line, shows the user the exact gh pr merge --squash --subject --body command for confirmation, and only executes after an explicit yes.
  • What did not change: No Rust source, no CI config, no docs navigation, no existing skill files modified.

Label Snapshot (required)

  • Risk label: risk: low
  • Size label: size: XS (auto-managed/read-only)
  • Scope labels: skills
  • Module labels: N/A
  • Contributor tier label: (auto-managed/read-only)
  • If any auto-label is incorrect, note requested correction: N/A

Change Metadata

  • Change type: feature
  • Primary scope: docs

Linked Issue

  • Closes #
  • Related #
  • Depends on # (if stacked)
  • Supersedes # (if replacing older PR)

Validation Evidence (required)

Commands and result summary:

cargo fmt --all -- --check
cargo clippy --all-targets -- -D warnings
cargo test
  • Evidence provided: N/A β€” no Rust source changed. Docs-only path per AGENTS.md. Markdown manually reviewed; markdownlint not present in environment (skipped).
  • If any command is intentionally skipped, explain why: All three cargo commands skipped β€” diff is .claude/skills/squash-merge/SKILL.md only (markdown, no Rust). markdownlint unavailable in local environment; structure and heading hierarchy verified by visual inspection.

Security Impact (required)

  • New permissions/capabilities? No
  • New external network calls? No
  • Secrets/tokens handling changed? No
  • File system access scope changed? No

Privacy and Data Hygiene (required)

  • Data-hygiene status: pass
  • Redaction/anonymization notes: No user data referenced.
  • Neutral wording confirmation: Skill uses <NUMBER>, <branch>, <title> placeholders; no identity-like strings.

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No

i18n Follow-Through (required when docs or user-facing wording changes)

  • i18n follow-through triggered? No β€” .claude/skills/ is Claude Code agent tooling, not user-facing documentation or navigation. No SUMMARY.md or locale file impact.

Human Verification (required)

  • Verified scenarios: Skill invoked against test PR; confirmation gate displayed exact gh pr merge command; execution blocked until explicit yes.
  • Edge cases checked: PR already merged (Step 1 stops early); missing PR number (auto-detected from branch); non-conventional-commit title (flagged before proceeding).
  • What was not verified: Behavior when gh CLI lacks merge permissions on the repo.

Side Effects / Blast Radius (required)

  • Affected subsystems/workflows: Adds /squash-merge as a new Claude Code skill. No existing skills or workflows modified.
  • Potential unintended effects: Skill could be invoked when user says "merge it" β€” trigger description is intentionally broad. Mandatory confirmation gate in Step 4 prevents accidental execution.
  • Guardrails/monitoring for early detection: Step 4 requires an explicit yes before any gh pr merge runs; Step 6 verifies merge state after completion.

Agent Collaboration Notes (recommended)

  • Agent tools used: None
  • Workflow/plan summary: Authored directly in response to observed merge-workflow pain point (useless squash messages, "Closed" vs "Merged").
  • Verification focus: Confirmation gate language and exact-command display requirement.
  • Confirmation: naming + architecture boundaries followed (AGENTS.md + CONTRIBUTING.md): Yes

Rollback Plan (required)

  • Fast rollback command/path: git revert <merge-commit> on upstream/master, or delete .claude/skills/squash-merge/ and push.
  • Feature flags or config toggles: None β€” skill is opt-in (only invoked explicitly).
  • Observable failure symptoms: Skill not appearing in skill list; gh pr merge command rejected by permission sandbox.

Risks and Mitigations

  • Risk: Skill trigger phrases ("merge", "land", "ship it") could match unintended invocations.
    • Mitigation: Step 4 mandatory confirmation with exact command display makes accidental execution impossible.

@github-actions github-actions bot added the docs Auto scope: docs/markdown/template files changed. label Apr 16, 2026
@singlerider singlerider added risk: low Auto risk: docs/chore-only paths. skills Auto scope: src/skills/** changed. labels Apr 16, 2026
B1: add CI gate pre-flight check (gh pr checks)
B2: add reviewDecision check β€” CHANGES_REQUESTED blocks, REVIEW_REQUIRED warns
B3: clarify Step 5 confirmation shows real expanded values, not placeholders
B4: assign PR title/commits to shell variables to prevent injection via special chars
B5: add error handling β€” stop and report verbatim on non-zero exit from gh pr merge
H1: add mergeable/CONFLICTING pre-flight check
H2: fix fallback base ref to use PR baseRefName not local 'master'
H3: eliminate HEREDOC in favour of shell variable assignment (removes EOF collision risk)
H4: covered by B4 variable approach
M1: tighten trigger β€” require PR number or explicit squash-merge context
M2: reframe self-merge rule β€” maintainers routinely self-merge, note it in confirmation
M3: add Prerequisites section documenting gh >= 2.17.0 requirement
M4: add branch cleanup command and guidance in Step 7
M5: detect single-commit PRs and omit redundant bullet body
L1: rewrite motivation β€” replace 'useless' claim with accurate formatting/PR-number framing
L2: note API commit ordering caveat and git log fallback for rebased histories
L3: remove 'close as merged' from trigger description
L4: show body in separate indented block in Step 5 confirmation, not inline
@github-actions github-actions bot removed the skills Auto scope: src/skills/** changed. label Apr 16, 2026
- Define $NUMBER variable explicitly in Step 1 via gh pr view --json number
- Step 5 confirmation now uses shell variable syntax ($SUBJECT, $COMMITS, $NUMBER)
  instead of angle-bracket placeholders, resolving the 'no placeholders' contradiction
- gh pr checks CI gate note: distinguish required vs optional (user confirms)
- reviewDecision fallback: handle null via // "" in jq
- fallback git log: fetch upstream + origin first; use origin/HEAD_REF; note fork limitation
- Step 7 jq: guard mergeCommit null with if/then/else to prevent expression error
Branch cleanup is always the contributor's decision.
Remove the deletion command suggestion entirely from Step 7 and Rules.
…ge skill

- Replace all <NUMBER>, <NUMBER_OR_URL>, <sha> placeholders with $NUMBER variable
- Fix single-commit SHA lookup to use gh API instead of placeholder
- Remove redundant Step 4 (echo preview superseded by confirmation step)
- Renumber steps: 5 steps now instead of 7
- Add auto-detect failure path: ask user for PR number if not on a PR branch
- Remove unused mergeStateStatus from JSON fetch
- Drop over-engineered CI pre-flight check (permission failures are fine at runtime)
MD034: wrap bare URL in markdown link syntax
MD036: remove bold emphasis from confirmation prompt (not a heading)
Copy link
Copy Markdown
Collaborator

@WareWolf-MoonWall WareWolf-MoonWall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review β€” PR #5782 feat(skills): add squash-merge skill

Reviewer: WareWolf-MoonWall
RFC authority: RFC #5615 (Contribution Culture), RFC #5653 (Zero Compromise in Practice)


βœ… Commendation

The problem statement is accurate and well-understood: GitHub default squash merge omits the PR number from the subject; direct-pushing bypasses the merge mechanism entirely so the PR shows Closed rather than Merged with no issue auto-close. Both outcomes are bad for a project that relies on issue linkage and a navigable commit history. The skill solves both at once cleanly.

The confirmation gate (Step 4) is the right design for an irreversible action. Three details are worth naming for future skills that execute destructive actions:

  1. The gate shows the user the exact expanded command with real values substituted β€” not variable names, not placeholders.
  2. Consent is not inferred from silence, prior approval, or yes-but-first β€” all three are explicitly ruled out. This closes the most common LLM-specific bypass patterns.
  3. The accepted responses are enumerated: yes, y, go, do it. Anything else means stop.

The branch deletion prohibition is correct and the reasoning is given: branch cleanup is always a human decision. The self-merge note is appropriately handled β€” maintainers routinely merge their own PRs; surfacing it in the confirmation summary keeps it in the audit trail.

The single-commit PR handling in Step 2 is a thoughtful detail. A one-item bullet list adds no information; using the full commit body in that case produces a cleaner squash commit for the most common case.


CI summary

Gate Result
Security Audit βœ… SUCCESS
Security Required Gate βœ… SUCCESS
Docs Quality βœ… SUCCESS
Lint / Strict Delta Lint βœ… SUCCESS
All builds / tests / checks βœ… SUCCESS

Markdownlint confirmed independently: 0 errors.


🟑 Conditional β€” origin remote assumption in git fallback

Step 2 git fallback resolves the contributor branch via origin/HEAD_REF:

COMMITS=$(git log upstream/BASE_REF..origin/HEAD_REF --format="- %h %s")

This assumes the contributor branch is on a remote named origin. That holds for the common setup where origin is the contributor fork and upstream is zeroclaw-labs/zeroclaw β€” but silently fails for any other remote layout. The skill acknowledges the fork limitation (if origin/HEAD_REF does not exist, the fallback cannot be used) but does not surface the assumption about what origin points to.

The GitHub API path is preferred and sufficient for the vast majority of cases. Suggest adding a one-line note: "This assumes origin is the contributor fork remote. If your remote layout differs, use the gh API output directly." Not a blocker β€” the failure mode is a silently empty commit list, not incorrect output.


Summary

The skill is correct and security-conscious. Confirmation gate design is solid. Shell variable quoting is correct (double-quoted variables prevent re-evaluation of embedded command substitutions in PR titles or commit messages). CI is fully green. The one conditional is a documentation gap in the git fallback path, not a code issue.

Approved. βœ…

@github-project-automation github-project-automation bot moved this from Backlog to Ready to Merge in ZeroClaw Project Board Apr 16, 2026
@singlerider singlerider merged commit 9d6308e into zeroclaw-labs:master Apr 16, 2026
20 checks passed
@github-project-automation github-project-automation bot moved this from Ready to Merge to Shipped in ZeroClaw Project Board Apr 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Auto scope: docs/markdown/template files changed. risk: low Auto risk: docs/chore-only paths.

Projects

Status: Shipped

Development

Successfully merging this pull request may close these issues.

2 participants