agentleFS
sequant-iosequant-io/sequant/.claude/skills/merger/SKILL.md

merger

Skill1 starsChanged 3 months agoMITView on GitHub

Multi-issue integration and merge skill - handles post-QA integration of completed worktrees

  • Commits and pushes

1 other project uses a version of this.

Adopted by

Most recently taken up by

  • Other versions1 project uses a version of this.

    Word for word · 0

    None found.

    Edited · 0

    None found.

    Wrote their own version · 1

    1. Victorcorcos/skills★ 7same name

    How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.

  • In this repository19 other files it gives its agents.

The file

Show its rules and text

We split this skill into the rules it gives an agent

19 rules. About 0 are common, 13 are distinctive, and 6 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈13

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 6

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  • could be automatic: a setting could enforce it
  1. Very specific:

    When invoked as /merger <issue-numbers>, you:

    line 42
  2. Distinctive:

    Run post-merge smoketest with regression comparison against baseline

    line 49
  3. Very specific:

    Before running /merger, use sequant merge --check to catch cross-issue gaps at zero AI cost:

    line 54
  4. Distinctive:

    When processing multiple issues, determine the execution mode for validation checks:

    line 94
  5. Distinctive:

    Use the Read tool to check project settings:

    line 101
  6. Very specific:

    Orchestrator/MCP mode: when SEQUANT_ORCHESTRATOR is set, every locks checkout action is a no-op (exit 0, no file touched) and the …

    line 159
  7. Very specific:

    Session caching: If baseline has already been captured in this /merger invocation (e.g., when merging multiple issues), reuse the cached …

    line 170
  8. Distinctive:

    Before merging, check which issues the PR will actually close on merge — GitHub parses closing keywords anywhere in the body, so this can …

    line 241
  9. Very specific:

    Stop and report, naming the issue,: if the list contains any number not in $expected.

    line 287
  10. Distinctive:

    If overlapping files found:

    line 319
  11. Distinctive:

    If smoketest checks fail but are NOT regressions (same count as baseline), report as pre-existing:

    line 559
  12. Distinctive:

    If smoketest checks fail AND are regressions, report with diagnostic commands:

    line 572
  13. Distinctive:

    Rule: if merge ordering requires a PR whose issue number is not in the invocation's arg list, halt before merging anything and report it — …

    line 628
  14. Distinctive:

    If dependencies found, enforce merge order among the named set.

    line 661
  15. Distinctive:

    Treat the order in the manifest as the merge order — earlier entries land first.

    line 683
  16. Very specific:

    If every predecessor is in the named set but requested out of order (e.g.

    line 684
  17. Distinctive:

    Do not proceed with merge

    line 777
  18. Distinctive:

    Create a draft PR with conflicts marked

    line 781
  19. Distinctive:

    Keep integration branch for debugging

    line 786

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md
<!-- sequant:local-override -->

Local overrides (read this first). Before following any instruction below, check whether .claude/.local/skills/merger/overrides.md exists. If it does, read it and treat its contents as authoritative: its instructions take precedence over anything in this skill they conflict with. This is the supported way to tailor /merger without forking it — overrides.md lives under .claude/.local/, which sequant update and sync never overwrite.

Merger Skill

You are the "Merger Agent" for handling post-QA integration of completed worktrees.

Purpose

When invoked as /merger <issue-numbers>, you:

  1. Capture baseline metrics on main (build errors, test counts)
  2. Validate QA status for all specified issues
  3. Detect file conflicts between worktrees
  4. Generate integration branches for incompatible changes
  5. Respect dependency ordering
  6. Clean up worktrees after successful merge
  7. Run post-merge smoketest with regression comparison against baseline
  8. Provide detailed merge reports

Recommended Pre-Merge Check

Before running /merger, use sequant merge --check to catch cross-issue gaps at zero AI cost:

sequant run 10 11 12          # implement
sequant merge --check         # verify (deterministic checks)
/merger 10 11 12              # merge (this command)

sequant merge detects merge conflicts, template mirroring gaps, file overlaps, and residual patterns before /merger attempts the actual merge. See docs/reference/merge-command.md for details.

Usage

# Merge single issue
/merger 10

# Merge multiple issues (detects conflicts, creates integration if needed)
/merger 10 12

# Merge with dependency ordering
/merger 10 12 --order=dependency

# Dry run - show what would happen
/merger 10 12 --dry-run

# Force parallel validation of multiple issues (faster, higher token usage)
/merger 10 12 --parallel

# Force sequential validation (slower, lower token usage)
/merger 10 12 --sequential

# Skip post-merge smoketest
/merger 10 12 --skip-smoketest

# Force merge even if regression is detected (bypasses regression gate)
/merger 10 12 --force

Agent Execution Mode

When processing multiple issues, determine the execution mode for validation checks:

  1. Check for CLI flag override:
    • --parallel → Validate all issues in parallel (spawn agents simultaneously)
    • --sequential → Validate issues one at a time
  2. If no flag, read project settings: Use the Read tool to check project settings:
    Read(file_path=".sequant/settings.json")
    # Parse JSON and extract agents.parallel (default: false)
  3. Default: Sequential (cost-optimized)
ModeToken UsageSpeedBest For
Sequential1x (baseline)SlowerLimited API plans, 1-2 issues
Parallel~Nx (N=issues)~50% fasterUnlimited plans, batch merges

Workflow

Acquire the Checkout Lock (REQUIRED)

/merger runs branch-mutating git in the main checkout: git checkout main for the baseline in Step 0, then git checkout -b integrate/… and git merge on the integration path (Step 5), plus the mid-workflow git checkout main in Steps 6–7. Those verbs are global to the working tree, so before the first one you must claim the checkout lock (#901), exactly as /fullsolve does in its Phase 0.3. Without it a session on another issue can interleave a git checkout and land a merge on the wrong HEAD; and because the guard refuses a non-holder, an unparticipating /merger is itself refused when someone else holds the tree.

/merger operates on several issues, so identify the holder by the first issue number passed to it and use that same <first-issue> on the acquire and on every release below — acquire and release must name the same holder or the release is refused against your own lock (#906). Substitute the literal number (e.g. --issue=10 for /merger 10 12); a shell variable is invisible to the pre-tool.sh guard, which reads the command text before the shell expands it.

# Claim the shared working tree before the first branch-mutating verb.
npx sequant locks checkout acquire \
    --issue=<first-issue> \
    --command="/merger <issue-numbers>" \
    --skip-pid-check || true
# Belt-and-suspenders for a parent that launched us with it; the guard's real
# binding comes from watching the acquire command above (#906).
export SEQUANT_ISSUE=<first-issue>

--issue is mandatory (#906): it is what proves you are the holder on release, since the acquiring shell's PID is gone by the next block (--skip-pid-check). Stale recovery is therefore age-based only (the 6h SEQUANT_SKILL_LOCK_TTL_MS and the 24h SEQUANT_MAX_LOCK_AGE_MS ceiling), not same-host dead-PID recovery.

Release contract: release the checkout lock — npx sequant locks checkout release --issue=<first-issue> || true — on every path that exits /merger: happy-path completion (Step 8), the regression-gate halt (Step 7), and any error/abort in "Error Handling". Do NOT release on a branch that continues to a later branch-mutating verb (the #906 distinction): the integration git merge in Step 5 and the git checkout main in Steps 6–7 run under the lock and must stay protected until the run ends.

Orchestrator/MCP mode: when SEQUANT_ORCHESTRATOR is set, every locks checkout action is a no-op (exit 0, no file touched) and the pre-tool.sh checkout guard stands down, so the acquire/release calls are safe to run unconditionally.

Step 0: Baseline Capture (REQUIRED)

Purpose: Capture build error count and test pass/fail counts on main before any merge, so post-merge results can be compared to detect regressions.

Skip if: --skip-smoketest flag is set (no comparison needed if smoketest is skipped).

Session caching: If baseline has already been captured in this /merger invocation (e.g., when merging multiple issues), reuse the cached values instead of re-running build and tests.

# Check if baseline is already cached (multi-issue merge scenario)
if [[ -n "$BASELINE_BUILD_ERRORS" && -n "$BASELINE_TEST_FAILURES" ]]; then
  echo "♻️ Using cached baseline metrics (captured earlier in this session)"
  echo "  Build errors: $BASELINE_BUILD_ERRORS"
  echo "  Test failures: $BASELINE_TEST_FAILURES"
  echo "  Test passes: $BASELINE_TEST_PASSES"
else
  echo "📊 Capturing baseline metrics on main..."

  # Ensure we're on main with latest changes
  git checkout main
  git pull origin main

  # 1. Capture build error count
  build_output=$(npm run build 2>&1 || true)
  BASELINE_BUILD_ERRORS=$(echo "$build_output" | grep -c "error TS" || true)
  echo "  Baseline build errors: $BASELINE_BUILD_ERRORS"

  # 2. Capture test pass/fail counts
  test_output=$(npm test 2>&1 || true)

  # Parse vitest output format: "Tests  X passed | Y failed" or "Tests  X passed"
  # Use tail -1 to get the Tests line (not Test Files line) from vitest output
  BASELINE_TEST_PASSES=$(echo "$test_output" | grep -oE '[0-9]+ passed' | tail -1 | grep -oE '[0-9]+' || echo "0")
  BASELINE_TEST_FAILURES=$(echo "$test_output" | grep -oE '[0-9]+ failed' | tail -1 | grep -oE '[0-9]+' || echo "0")
  echo "  Baseline test passes: $BASELINE_TEST_PASSES"
  echo "  Baseline test failures: $BASELINE_TEST_FAILURES"

  echo "✅ Baseline captured"
fi

Output format (include in merge report):

### Baseline Metrics (main before merge)

| Metric | Count |
|--------|-------|
| Build errors (TS) | $BASELINE_BUILD_ERRORS |
| Test passes | $BASELINE_TEST_PASSES |
| Test failures | $BASELINE_TEST_FAILURES |

Step 1: Pre-Merge Validation

For each issue specified:

# Find the worktree for the issue. Resolve by branch (#899/#904) — grepping
# for "feature/$ISSUE" is a prefix match, so ISSUE=89 also hits feature/899-*.
worktree_path=$(npx sequant worktree resolve "$ISSUE") || echo "No worktree for #$ISSUE"

# Check PR status
gh pr list --head "feature/$ISSUE-*" --json number,state,title

# Verify worktree exists and has commits
git -C <worktree-path> log --oneline origin/main..HEAD

Validation checklist:

  • Worktree exists for the issue
  • PR exists (or will be created)
  • Changes have been committed
  • No uncommitted work

Step 1a: Closing-Reference Guard (#1197)

Before merging, check which issues the PR will actually close on merge — GitHub parses closing keywords anywhere in the body, so this can diverge from what the PR is supposed to close (a tracker issue mentioned in an AC table, or a multi-PR issue where only the last PR should close it):

# `gh pr view --json closingIssuesReferences` is not a recognized field on
# gh CLI ≤ 2.66 ("Unknown JSON field") — the REST PR payload doesn't carry
# it either. Query the GraphQL field directly; `{owner}`/`{repo}` resolve
# from the current repo the same way they do in REST `gh api` calls.
gh api graphql -f query='
  query($owner:String!,$repo:String!,$pr:Int!){
    repository(owner:$owner,name:$repo){
      pullRequest(number:$pr){closingIssuesReferences(first:10){nodes{number}}}
    }
  }' -F owner='{owner}' -F repo='{repo}' -F pr=<PR_NUMBER> \
  --jq '.data.repository.pullRequest.closingIssuesReferences.nodes[].number'

Work out which issues this PR is allowed to close. In refs mode, or when the issue carries the no-autoclose label, the answer is none: the PR's own issue must not close either.

# settings.json is JSONC (`sequant init` writes whole-line // comments), so
# strip those before jq. Only whole-line comments: values such as devUrl
# contain "//".
settings_json='{}'
if [ -f .sequant/settings.json ]; then
  settings_json=$(sed -E 's#^[[:space:]]*//.*$##' .sequant/settings.json)
fi
if link=$(printf '%s' "$settings_json" | jq -er '.run.prIssueLink // "closes"') &&
   no_close_label=$(printf '%s' "$settings_json" | jq -er '.run.prNoCloseLabel // "no-autoclose"'); then
  labels=$(gh api "repos/{owner}/{repo}/issues/<issue-number>" --jq '.labels[].name')
  if [ "$link" = "refs" ] || printf '%s\n' "$labels" | grep -qxF "$no_close_label"; then
    expected=""                 # nothing may close
  else
    expected="<issue-number>"   # only the PR's own issue
  fi
else
  # Fail closed: an unreadable settings file must not quietly mean "closes".
  echo "⚠️ Could not parse .sequant/settings.json; treating every closing reference as unexpected." >&2
  expected=""
fi

Stop and report, naming the issue, if the list contains any number not in $expected. In refs/label mode that includes <issue-number> itself; that is the case this guard exists for:

❌ PR #<PR_NUMBER> closes #<unexpected-number> on merge.
   Allowed to close here: #<issue-number> (or nothing, in refs/no-autoclose mode).
   Edit the PR body to change that reference to "Refs #<unexpected-number>"
   before merging.

A list that contains only numbers in $expected (or an empty list) is fine, so proceed to Step 2.

Step 2: Conflict Detection

Get files changed in each worktree:

# For each worktree
git -C <worktree-path> diff --name-only origin/main...HEAD

Find overlapping files:

# Compare file lists between worktrees
comm -12 <(sort files_issue1.txt) <(sort files_issue2.txt)

Step 3: Conflict Analysis

If overlapping files found:

  1. Semantic analysis: Are the changes compatible?
    • Additive changes (new functions) -> likely compatible
    • Same function modified -> likely incompatible
    • Same file, different sections -> may be compatible
  2. Generate merge preview:
    git merge-tree $(git merge-base main branch1) branch1 branch2

Step 4: Resolution Strategy

ScenarioAction
No conflictsMerge sequentially
Compatible changesAuto-merge with verification
Incompatible changesGenerate unified implementation in integration branch
True dependencyEnforce merge order

Step 5: Merge Execution

For clean merges (no conflicts):

# Merge PR first (without --delete-branch to avoid worktree lock conflicts)
gh pr merge <PR_NUMBER> --squash

# Only clean up worktree AFTER merge succeeds
# If merge fails, the worktree is preserved so work isn't lost
#
# Resolve by BRANCH, never by grepping the printed path (#899/#904): the path
# line contains the directory slug, so `grep "feature/$ISSUE"` with ISSUE=89
# also matches `feature/899-...` — and the next line force-removes whatever it
# matched. `worktree resolve` matches the branch's issue number exactly, and
# exits non-zero rather than guessing when nothing (or more than one) matches.
if worktree_path=$(npx sequant worktree resolve "$ISSUE"); then
  branch=$(git -C "$worktree_path" rev-parse --abbrev-ref HEAD)
  git worktree remove "$worktree_path" --force
  git branch -D "$branch" 2>/dev/null || true
fi

# Delete remote branch (previously handled by --delete-branch)
gh api repos/{owner}/{repo}/git/refs/heads/$(gh pr view <PR_NUMBER> --json headRefName --jq '.headRefName') -X DELETE 2>/dev/null || true

# State is tracked by the orchestrator runtime when available

For conflicting changes (integration branch):

# Create integration branch
git checkout -b integrate/<issue1>-<issue2>-<description> main

# Cherry-pick or merge each worktree's changes
git merge feature/<issue1>-* --no-commit
# Resolve conflicts...
git add .
git commit -m "feat: Integrate #<issue1> changes"

git merge feature/<issue2>-* --no-commit
# Resolve conflicts...
git add .
git commit -m "feat: Integrate #<issue2> changes"

# Run tests on integration branch
npm test
npm run build

# Create integration PR
gh pr create --title "feat: Integrate #<issue1> and #<issue2>" --body "..."

Step 6: Post-Merge Verification

After successful merge:

# Pull merged changes to main
git checkout main
git pull origin main

# Verify worktree was cleaned up
git worktree list  # Should not show the merged feature branch

# Remote branch is deleted explicitly after merge (see Step 5)

# REQUIRED: Verify state was updated (#305)
# The state should show status="merged" for the issue
# Use the Read tool to check state, then parse JSON
Read(file_path=".sequant/state.json")
# Verify the issue status shows "merged"

Step 6a: Worktree Cleanup After Merge (REQUIRED - #305)

After each successful merge, ensure the worktree is removed:

# Find and remove worktree for the issue.
# Resolve by branch, not by grepping the printed path — see the note in the
# clean-merge block above. This one force-removes, so a slug collision here
# destroys an unrelated issue's worktree.
if worktree_path=$(npx sequant worktree resolve "$ISSUE"); then
  echo "Removing worktree: $worktree_path"
  git worktree remove "$worktree_path" --force
else
  echo "No worktree found for #$ISSUE (already cleaned up)"
fi

# Verify worktree removal — resolve must now fail for this issue.
if npx sequant worktree resolve "$ISSUE" >/dev/null 2>&1; then
  echo "WARNING: Worktree still exists"
else
  echo "✅ Worktree removed"
fi

Why this matters: Leftover worktrees waste disk space and can cause confusion when re-running sequant run on the same issues. The state guard (#305) prevents re-execution, but the worktree should still be cleaned up.

Step 7: Post-Merge Smoketest with Regression Comparison

Skip if: --skip-smoketest flag is set or SEQUANT_MERGER_SKIP_SMOKETEST environment variable is true.

# Check skip flag
if [[ "$SKIP_SMOKETEST" == "true" ]]; then
  echo "⏭️ Smoketest skipped (--skip-smoketest flag set)"
  # Continue to output report
fi

After all merges complete and worktrees are cleaned up, verify main is healthy and compare against baseline:

# 1. Ensure we're on main with latest changes
git checkout main
git pull origin main

# 2. Build verification — capture post-merge error count
echo "Running build..."
post_build_output=$(npm run build 2>&1); build_exit=$?
POST_BUILD_ERRORS=$(echo "$post_build_output" | grep -c "error TS" || true)

# 3. Test suite — capture post-merge test counts
echo "Running tests..."
post_test_output=$(npm test 2>&1); test_exit=$?
POST_TEST_PASSES=$(echo "$post_test_output" | grep -oE '[0-9]+ passed' | tail -1 | grep -oE '[0-9]+' || echo "0")
POST_TEST_FAILURES=$(echo "$post_test_output" | grep -oE '[0-9]+ failed' | tail -1 | grep -oE '[0-9]+' || echo "0")

# 4. CLI health check (if sequant CLI is available)
echo "Running CLI health check..."
npx sequant doctor 2>&1 || true
doctor_exit=$?

# 5. Regression comparison against baseline
REGRESSION_DETECTED=false

BUILD_DELTA=$((POST_BUILD_ERRORS - BASELINE_BUILD_ERRORS))
TEST_FAIL_DELTA=$((POST_TEST_FAILURES - BASELINE_TEST_FAILURES))
TEST_PASS_DELTA=$((POST_TEST_PASSES - BASELINE_TEST_PASSES))

if [[ $BUILD_DELTA -gt 0 ]]; then
  echo "❌ REGRESSION: $BUILD_DELTA new build error(s) introduced"
  REGRESSION_DETECTED=true
fi

if [[ $TEST_FAIL_DELTA -gt 0 ]]; then
  echo "❌ REGRESSION: $TEST_FAIL_DELTA new test failure(s) introduced"
  REGRESSION_DETECTED=true
fi

Smoketest & Regression Output

Report results including regression comparison:

### Post-Merge Smoketest

| Check | Command | Result | Details |
|-------|---------|--------|---------|
| Build | `npm run build` | ✅ PASS / ❌ FAIL | [build output summary] |
| Tests | `npm test` | ✅ PASS (N/N) / ❌ FAIL | [test count or failure summary] |
| CLI Health | `npx sequant doctor` | ✅ PASS / ❌ FAIL / ⏭️ SKIP | [health check output] |

### Regression Check

| Metric | Baseline (main) | Post-merge | Delta | Status |
|--------|----------------|------------|-------|--------|
| Build errors | $BASELINE_BUILD_ERRORS | $POST_BUILD_ERRORS | +$BUILD_DELTA / 0 | ✅ No regression / ❌ REGRESSION |
| Test failures | $BASELINE_TEST_FAILURES | $POST_TEST_FAILURES | +$TEST_FAIL_DELTA / 0 | ✅ No regression / ❌ REGRESSION |
| Test passes | $BASELINE_TEST_PASSES | $POST_TEST_PASSES | +$TEST_PASS_DELTA | ✅ Tests added / ⚠️ Tests removed |

**Regression Result:** ✅ NO REGRESSIONS / ❌ REGRESSIONS DETECTED

Regression Gate

If regressions are detected:

if [[ "$REGRESSION_DETECTED" == "true" ]]; then
  if [[ "$FORCE_MERGE" == "true" ]]; then
    echo "⚠️ REGRESSION DETECTED but --force flag set. Proceeding with merge."
    echo "⚠️ Acknowledgment: Merging despite $BUILD_DELTA new build error(s) and $TEST_FAIL_DELTA new test failure(s)."
  else
    # Terminal halt — release the checkout lock before stopping (#901/#906).
    # This branch does NOT continue to Step 8, so the release lives here.
    npx sequant locks checkout release --issue=<first-issue> || true
    echo "❌ REGRESSION DETECTED — merge is blocked."
    echo ""
    echo "New build errors: $BUILD_DELTA"
    echo "New test failures: $TEST_FAIL_DELTA"
    echo ""
    echo "To override this gate, re-run with --force:"
    echo "  /merger <issues> --force"
    echo ""
    echo "To investigate:"
    echo "  npm run build 2>&1 | grep 'error TS'"
    echo "  npm test -- --verbose 2>&1"
    # Do NOT proceed — report regression and halt
  fi
fi

Regression gate behavior:

Scenario--force not set--force set
No regressions✅ Proceed✅ Proceed
New build errors❌ Block merge, report⚠️ Warn, proceed
New test failures❌ Block merge, report⚠️ Warn, proceed
Both❌ Block merge, report⚠️ Warn, proceed

Important: The regression gate does NOT trigger automatic rollback. It blocks further action and reports for human decision-making. Use --force only when you've confirmed the regressions are acceptable (e.g., known flaky tests).

Failure Handling (Non-Regression)

If smoketest checks fail but are NOT regressions (same count as baseline), report as pre-existing:

### ℹ️ Pre-existing Failures (not regressions)

| Check | Status | Notes |
|-------|--------|-------|
| Build | ⚠️ $BASELINE_BUILD_ERRORS errors | Same as baseline — pre-existing |
| Tests | ⚠️ $BASELINE_TEST_FAILURES failures | Same as baseline — pre-existing |

These failures existed on main before this merge. They are not caused by the merged PR(s).

If smoketest checks fail AND are regressions, report with diagnostic commands:

### ❌ Regressions Detected

| Check | Baseline | Post-merge | New Failures |
|-------|----------|------------|--------------|
| Build errors | $BASELINE_BUILD_ERRORS | $POST_BUILD_ERRORS | +$BUILD_DELTA |
| Test failures | $BASELINE_TEST_FAILURES | $POST_TEST_FAILURES | +$TEST_FAIL_DELTA |

**⚠️ Action Required:**
- Investigate new failures introduced by this merge
- Consider reverting the merge if regressions are critical
- Use `--force` to override if regressions are acceptable

**Diagnostic commands:**
\`\`\`bash
# Investigate new build errors
npm run build 2>&1 | grep "error TS"

# Run failing tests with verbose output
npm test -- --verbose 2>&1

# Check what the merge changed
git log --oneline -3
git diff HEAD~1 --stat
\`\`\`

Important: Regression detection does NOT trigger automatic rollback. It reports for human decision-making.

Step 8: Release the Checkout Lock (REQUIRED)

The merge is complete. Release the working-tree lock so other sessions can run branch-mutating git again (#901). Run this on the happy path — a successful merge and smoketest, or the --force proceed past a regression. It is the counterpart to the acquire in "Acquire the Checkout Lock" above.

npx sequant locks checkout release --issue=<first-issue> || true

On a failure exit instead, the release is done at the halt site — the regression gate (Step 7) or "Error Handling" — never both, so exactly one release runs per invocation.

<!-- BEGIN: named-set-boundary (#961) -->

Named-Set Boundary (REQUIRED)

/merger <issue-numbers> is the user's consent to merge exactly those issues — invoking it is the explicit authorization, the same way sequant merge/sequant merge --watch are explicit and human-triggered (#958's Non-Goals). That consent does not extend past the named set: never let Dependency Detection or Stacked PR Detection below pull another issue's PR into gh pr merge just because it happens to unblock or precede a named one.

Rule: if merge ordering requires a PR whose issue number is not in the invocation's arg list, halt before merging anything and report it — do not widen the merge silently, and do not treat the stacked-PR "Continue anyway? (y/N)" confirmation as license to include an unnamed predecessor. Offer the widened command for the user to re-run:

❌ Out-of-named-set dependency
   #101 depends on #100, but #100 was not in your /merger invocation.
   Halting before any merge.
   To include it: /merger 100 101

This rule governs both sections below.

<!-- END: named-set-boundary (#961) -->

Dependency Detection

Parse dependencies from issue body or comments:

<!-- In issue body -->
**Depends on**: #10

<!-- Or via label -->
Labels: depends-on/10
# Check issue for dependency markers
gh issue view <issue> --json body,labels | jq '.body, .labels[].name'

If dependencies found, enforce merge order among the named set. If a dependency's issue number falls outside the named set, apply the Named-Set Boundary rule above: halt and report instead of merging it.

Stacked PR Detection (#605)

PRs created with sequant run --stacked have a non-main base and a manifest line in the body (Part of stack: #N1 → #N2 → ...). They must merge in order — predecessor first. Merging out of order re-bases the dependent PR's diff against an unexpected commit and inflates the visible change set.

Detect stacked PRs:

# A PR with a non-main base + manifest is part of a stack
gh pr view <PR_NUMBER> --json baseRefName,body | \
  jq -r 'if (.baseRefName != "main" and (.body | contains("Part of stack:"))) then "STACKED" else "STANDALONE" end'

Enforce stack order before merging:

  1. Extract the stack manifest from each PR's body (Part of stack: #100 → #101 (this) → #102).
  2. Treat the order in the manifest as the merge order — earlier entries land first.
  3. If every predecessor is in the named set but requested out of order (e.g. /merger 102 100 101 for the stack above), warn before proceeding and recommend the manifest order.
  4. If a predecessor is not in the named set at all (e.g. /merger 102 alone for that stack), the Named-Set Boundary rule applies: halt and report — do not fall through to the "Continue anyway? (y/N)" prompt, since that prompt is for reordering named issues, not for merging an unnamed one.
  5. GitHub auto-updates the dependent PR's base when its predecessor merges, so once the order is correct no manual rebasing is needed.

Warning template:

⚠️  Stack order violation detected
   PR #102 is part of stack: #100 → #101 → #102
   You requested merge order: #102, #100, #101
   Recommended: merge in manifest order to preserve incremental diffs.
   Continue anyway? (y/N)

Output Format

Merge Report

## Merger Report: Issues #10, #12

### Pre-Merge Validation
| Issue | Worktree | PR | Status |
|-------|----------|-----|--------|
| #10 | feature/10-* | #15 | Ready |
| #12 | feature/12-* | #16 | Ready |

### Conflict Analysis
| File | #10 | #12 | Status |
|------|-----|-----|--------|
| `src/api/route.ts` | Modified | Modified | CONFLICT |
| `src/components/list.tsx` | - | Created | OK |

### Resolution
**Strategy:** Integration branch
**Branch:** integrate/10-12-api-merge
**PR:** #17

### Actions Taken
1. Captured baseline metrics on main
2. Created integration branch from main
3. Merged #10 changes (no conflicts)
4. Merged #12 changes (resolved 1 conflict in route.ts)
5. Tests passed (45 tests)
6. Build succeeded

### Cleanup
- Removed worktree: feature/10-*
- Removed worktree: feature/12-*
- Closed: PR #15, PR #16 (superseded by #17)

### Baseline Metrics (main before merge)
| Metric | Count |
|--------|-------|
| Build errors (TS) | 0 |
| Test passes | 42 |
| Test failures | 0 |

### Post-Merge Smoketest
| Check | Command | Result | Details |
|-------|---------|--------|---------|
| Build | `npm run build` | ✅ PASS | Compiled successfully |
| Tests | `npm test` | ✅ PASS (45/45) | All tests passing |
| CLI Health | `npx sequant doctor` | ✅ PASS | No issues detected |

### Regression Check
| Metric | Baseline (main) | Post-merge | Delta | Status |
|--------|----------------|------------|-------|--------|
| Build errors | 0 | 0 | 0 | ✅ No regression |
| Test failures | 0 | 0 | 0 | ✅ No regression |
| Test passes | 42 | 45 | +3 | ✅ Tests added |

**Regression Result:** ✅ NO REGRESSIONS

### Final Status
**Result:** SUCCESS
**Integration PR:** #17
**Issues to close on merge:** #10, #12

Error Handling

Before halting on any failure below, release the checkout lock (#901/#906) — the acquire in "Acquire the Checkout Lock" claimed the tree, and every failure here is a terminal exit that must hand it back before stopping:

npx sequant locks checkout release --issue=<first-issue> || true

If validation fails:

  • Report which issues failed validation
  • Suggest corrective actions
  • Do not proceed with merge

If merge conflicts cannot be resolved:

  • Document the conflicts
  • Create a draft PR with conflicts marked
  • Request manual intervention

If tests fail on integration branch:

  • Document failing tests
  • Keep integration branch for debugging
  • Do not merge

If worktree cleanup fails:

  • Log warning but continue
  • Manual cleanup may be needed

Configuration

Environment variables:

  • SEQUANT_MERGER_DRY_RUN - If true, only show what would happen
  • SEQUANT_MERGER_NO_CLEANUP - If true, keep worktrees after merge
  • SEQUANT_MERGER_FORCE - If true, proceed even with conflicts or regressions (bypasses regression gate)
  • SEQUANT_MERGER_SKIP_SMOKETEST - If true, skip post-merge smoketest (also skips baseline capture)

Output Verification

Before responding, verify your output includes ALL of these:

  • Baseline Metrics - Build errors, test passes/failures on main before merge (or "Skipped" if --skip-smoketest)
  • Pre-Merge Validation - Status of each issue/worktree/PR
  • Conflict Analysis - Table of overlapping files and status
  • Resolution Strategy - How conflicts were resolved (if any)
  • Actions Taken - Step-by-step log of what was done
  • Cleanup Status - Which worktrees/branches were removed
  • Post-Merge Smoketest - Build, test, and CLI health results (or "Skipped" if --skip-smoketest)
  • Regression Check - Baseline vs post-merge comparison table (or "Skipped" if --skip-smoketest)
  • Regression Gate - Whether regressions were detected and what action was taken
  • Final Status - SUCCESS/FAILURE with PR link

DO NOT respond until all items are verified.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.