Skip to content
Back to skills

Merging To Main

ASecurity

Keyword-gated by name. Merge a ready branch to main via PR.

  • 5 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 26, 2026
developmentpythongoshellbashnodedebugginggit

Works with

  • cli

Security analysis

A100/100

Scanned October 7, 2026

npx -y skills add dbc-oduffy/coordinator-claude --skill merging-to-main --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Merging To Main?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Merging To Main
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/dbc-oduffy-merging-to-main/badge)](https://www.skillsdirectory.com/skills/dbc-oduffy-merging-to-main)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: merging-to-main
description: Keyword-gated by name. Merge a ready branch to main via PR.
argument-hint: "[--force] [--force-merge-active-branch]"
version: 2.0.0
allowed-tools: ["Read","Write","Edit","Bash","Grep","Glob","Agent","Skill","AskUserQuestion","TaskCreate","TaskUpdate","TaskGet","TaskList"]
---

# Merging to Main

## Overview

Merge a work or feature branch to main via PR, gated by local validation — GitHub Actions is not in
the loop. `merge-assemble` computes the gates and
directives (`d0`-`d8`). What follows is what it cannot precompute: your judgment calls, plus the
steps depending on live PR/merge state.

**Announce at start:** "I'm using the coordinator:merging-to-main skill to merge this branch to main."

On a PowerShell host, invoke the `.exe` launcher by absolute path through the call operator
(Shape W) — ladder and shapes: `${CLAUDE_PLUGIN_ROOT}/snippets/resolve-coordinator-bin.md`.
Compute and apply are one verb (`brief` is removed): `merge-assemble apply [--session-id <id>] [--force] [--decisions-file <path>]`, resolved per that ladder. Resolve every `judgment_points[]` entry it returns, via `--decisions-file`, before its gated directive(s) proceed. `--force` bypasses only the node ceremony hard-gate (`d0`).

**First Officer Doctrine:** EM may refuse to merge and alert the PM on a branch with known issues.

---

## Step 1: Test Suite Gate

Pre-authorized Tier-U ceremony — grant first: `tier-u-grant-cli grant ceremony
"merging-to-main implicit Tier-U grant for the pre-merge unscoped project test suite" --ceremony
merging-to-main`.

`d0` (`node --test "${CLAUDE_PLUGIN_ROOT}/tests/plugin-ecosystem/run.js"` — the plugin root, never the repo root; halt-on-fail) runs first, then detect and run
the project's own test runner (`pnpm test`/`npm test`, `pytest`/`python3 -m pytest`, `/validate`, or
project-specific from `CLAUDE.md`/`package.json`). **This is the most expensive step in the whole
ceremony** — a machine-wide event (magnitude: `python3 coordinator/tests/_spawn_budget.py`). Fail on either →
halt: _"Test suite failed. Fix first, or use `/merging-to-main --force` to bypass for hotfixes."_

**`--force`** (skill flag, distinct from `apply`'s): skips this step including the Tier-U grant and
`d0`. Log: _"Force-merge requested — test suite gate bypassed."_

**Accepted red.** A suite that ran red on a baseline predating the branch, with the PM's merge
order: run it (never `--force`, which skips the run), write the red set to
`state/audits/<date>-<branch>-red-set.md`, cite that path in the PR body's verification line, and
merge. A failure not in that set, or a red set the branch introduced, still halts.

---

## Step 2: Pre-flight

Commit only paths this session touched, on the commit invocation itself (detail: wiki).

On a work/feature branch → continue. On main with unpushed commits ahead of `origin/main` →
auto-recover via `merge-recovery-and-tag-cut recovery-branch` (cuts a fresh `work/<host>/<date>`
branch off the pre-sync state, pushes, resets main, prints `BRANCH=<name>`), then continue there. On main with nothing unpushed → abort:
_"Already on main with nothing to merge. Switch to a work or feature branch first."_

Resolve the branch via `coordinator-current-branch`, decide unpushed state with `coordinator-invoke push.outstanding '{}'`, and push with `--set-upstream` when it reports commits ahead.

---

## Step 3: Release Surface (your call)

`d6`/`d3`/`d5`/`d1`/`d2` are directives (illegal-path scan, coverage gate, portability sweep,
tag-prefix resolution, release-tag plan). `d2` only plans the tag: nothing is created or pushed
until Step 7. Judgment:

**Ship verdict (`ship_verdict`).** EM stages one line, PM confirms or overrides; don't merge on
`hold`/`split` without PM redirect. `/staff-session vp-product` gives a structured second opinion.

```markdown
**Ship verdict:** [ship | ship-behind-flag | hold | split | spike-only] — [one-sentence rationale]
```

Verdict meanings: wiki § Ship verdict table.

**Release-note framing.** Prefer the latest `state/week-changelog/*-pending-release.md`
accumulator; absent, draft inline by impact (Added/Changed/Fixed/Deps/Internal; template: wiki).
When a tag is being cut, run `merge-release-notes-derive missed-releases <tag> $ENTRY_PATHS` first:
each entry it names shipped in an earlier release that never flipped, so leave it out of these notes.
Prepend to a repo-root `CHANGELOG.md` if one exists, committed before Step 5. `tasks/`/`tmp/`-only
merges get an "Internal" line only.

**Demo path** (user-visible merges) — append a Demo Path section (template: wiki) to the PR body.

**Version-bump (`version_bump_final`).** `version_bump` is a proposal only — confirm/override
before `d2` plans the tag (mode detail: wiki).

**Portability (`portability_disposition`).** Empty sweep report continues silently; non-empty needs
a per-finding PM disposition (options: wiki). Not a merge blocker;
`COORDINATOR_OVERRIDE_PORTABILITY=1` skips it for a one-off.

---

## Step 4: UE-specific checks (`project_type: game-dev`, `project_subtypes: unreal`)

Otherwise skip. Detection table (all five checks): wiki. UBT and reverse-drift gates have live
producers — non-zero halts with remediation (or the matching `COORDINATOR_OVERRIDE_*`). Plugin-
version-matrix, structural-index-schema, and install-path touches need eyeball diff-path
classification — no producer yet
<!-- engine-gap: field=merge.touched_path_classes producer=unknown memo=engine-gap-markers-name-a-memo-that-was-never-filed.md -->.
A schema bump needs `schema-migration-auditor` dispatched, the Staff Engineer review before merge.

---

## Step 5: Create PR

Compose the PR body via `merge-gate-and-pr pr-body --ship-verdict "$SHIP_VERDICT" --summary
"$SUMMARY" --release-notes "$RELEASE_NOTES" --verification "$VERIFICATION" --risk "$RISK" --links
"$LINKS" --commit-range main..HEAD` (`d4`), then `gh pr create --base main --head "$BRANCH" --title
"$TITLE" --body "$BODY"`. Pass `--release-notes` and `--demo-path` without headings of their own, version headings at `###`
or lower. Exit 2 `unrecognized arguments` (older engine): rerun without `--summary`,
`--verification`, `--risk`, `--links`.

If a version bump was suggested but not yet PM-confirmed, surface it in the PR body: _"Suggested
bump: patch ({old} → {new}) — confirm before tagging."_

---

## Step 6: Local Validation Confirmation

Confirm Step 1's run passed on the exact head being merged (re-run `/validate` if commits landed
since). A failure blocks merge — _"Validation failed on {test}. Fix and re-run `/merging-to-main`,
or investigate via `coordinator:systematic-debugging`."_

---

## Step 7: Merge

Pre-merge quiet check: `merge-gate-and-pr active-branch-guard --pr "$PR"` halts if the PR's newest
commit is younger than 300 seconds. Override with the skill's own `--force-merge-active-branch`.

Merge via `gh pr merge` (no `--delete-branch`), merge commit (never squash). The head branch is
deleted only when its remote tip is an ancestor of the base — a merged PR merges a snapshot, not
the branch's later pushes; otherwise keep it and report the tip and its unmerged-commit count. Recovery recipes for
"base branch policy prohibits" and "head not up to date": wiki. **Merge conflicts** — do not force
through; offer the PM merge-main-in-and-resolve (recommended) or rebase; stop and wait.

**IF `d2` PLANNED A RELEASE TAG, CUT IT NOW, AT THE MERGE COMMIT:**

    merge-recovery-and-tag-cut cut-tag <tag> --pr <PR>
    git fetch origin --tags && git rev-list --count <tag>..origin/main

`cut-tag` resolves the PR's merge commit, tags it, and pushes. A refusal stops the ceremony: the
tag already exists elsewhere on origin, `gh` is missing, or the PR isn't merged. A non-zero count
also stops it. **Never force-push or retarget a tag.** Why: wiki § Tag-contains-release guard.

Once the merge lands, trigger the SCIP rebuild best-effort in the background (never blocks): `"$_py"
"${CLAUDE_PLUGIN_ROOT:-<content-root>/coordinator}/bin/scip-rebuild-at-ceremony.py" --ceremony
merge-to-main` (§ Plugin-local `coordinator/bin/`, `resolve-coordinator-bin.md`).

---

## Step 8: Post-Merge Re-Verify Shared Infra

After a conflict-resolved or concurrently-edited merge, confirm each touched file keeps a canonical
phrase from your change at `HEAD`; missing → re-apply, push a follow-up.

---

## Step 9: Local Cleanup

First run `<plugin-root>/bin/structural-index-refresh.py` (non-blocking, always exit 0; `COORDINATOR_STRUCTURAL_INDEX_REFRESH_OFF=1` disables) — report its line if it prints one.

Check out main (`COORDINATOR_OVERRIDE_BRANCH=1`), pull, delete the local branch (a follow-on
`/bug-blitz` cuts a fresh day branch from main). Clear any stray worktree (`git worktree remove <path>`).

---

## Step 10: Completion-Log Status Flip

Runs when a release tag was cut (`d2` landed); skip otherwise. `mkdir -p
archive/release-notes/`, then `d7` (`merge-release-notes-derive flip-tags <tag> <sha> <date>
$ENTRY_PATHS`) flips every matching entry to the earliest release tag whose history contains it. Best-effort `git mv` the pending-release accumulator to `archive/release-notes/`, scoped-commit
`$ENTRY_PATHS` + accumulator + release notes file, push to main.

---

## Step 11: Report

```
## Merged to Main
- **PR:** {url}
- **Merge commit:** {sha}
- **Branch deleted:** {branch} (local + remote)
- **Now on:** main @ {sha}
```

`d8` (`orphan-branch-sweep --format text --severity-min warning`, non-`OK` lines) surfaces other
in-flight branches: _"Multiple work branches in flight — verify these don't carry work intended for
this PR."_

**The auto-memory drain gate is gone from this ceremony; do not restore it** (wiki § Retired:
auto-memory drain gate).

## Red Flags

**Never:** squash commits; push directly to main. Concurrent-writer caveat: cap commit sweeps at
~6 and accept a moving target — don't loop trying to converge.

## Integration

**Called by:** `coordinator:finishing-a-development-branch` (Option 1); PM/EM, never `/workday-complete`.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.