stack-ship
Analyzes multiple stacks for merge readiness to ensure atomic, conflict-free deployments.
Install
mkdir -p .claude/skills/stack-ship && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/14701" && unzip -o skill.zip -d .claude/skills/stack-ship && rm skill.zipInstalls to .claude/skills/stack-ship
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Evaluate shippable stacks, review their changes, check for merge conflicts, and produce a smart ship plan. Uses parallel multi-agent review and conflict analysis to determine what to ship, in what groupings, and what to hold. Keywords ship, merge, plan, stacks, PRs, conflicts.Key capabilities
- →Discover shippable stacks with CI/approval state
- →Review each shippable stack's diff for risks
- →Test merge compatibility between stacks
- →Synthesize a prioritized merge plan
- →Execute the merge command after user approval
How it works
This skill discovers shippable stacks, performs parallel code reviews and conflict checks, then synthesizes a prioritized merge plan for the user.
Inputs & outputs
When to use stack-ship
- →Planning atomic merges for multiple PRs
- →Detecting CI-blocked stack conflicts
- →Reviewing shippable changes before merge
About this skill
/stack-ship
Smart ship planning: discover shippable stacks → parallel code review → conflict detection → prioritized merge plan → optional execution.
Mental Model
Merging many PRs blindly causes conflict pain and wastes CI minutes. This skill works smarter:
- Discover —
stackit merge status --json --allsurfaces every stack with CI/approval state - Review — Parallel agents review each shippable stack's diff for correctness, risks, and red flags
- Conflict check —
stackit merge ship --dry-run --stacks ...tests whether stacks can merge together atomically - Plan — Synthesize into: ship these together, hold these (minor concerns), fix these (blocking issues)
- Execute — Run the merge command after user approval
Execution
Step 1: Launch the Ship Workflow
Use the Workflow tool with the script below. Pass no args.
export const meta = {
name: 'stack-ship',
description: 'Discover, review, and plan shipping for stacks',
phases: [
{ title: 'Discover', detail: 'Get shippability status of all stacks' },
{ title: 'Review', detail: 'Review each shippable stack in parallel' },
{ title: 'Conflict Check', detail: 'Test merge compatibility between stacks' },
{ title: 'Plan', detail: 'Synthesize findings into a ship plan' },
],
}
const STACK_SCHEMA = {
type: 'object',
properties: {
stacks: {
type: 'array',
items: {
type: 'object',
properties: {
root_branch: { type: 'string' },
all_branches: { type: 'array', items: { type: 'string' } },
pr_count: { type: 'number' },
scope: { type: 'string' },
status: { type: 'string' },
author: { type: 'string' },
pr_title: { type: 'string' },
approval_ok: { type: 'boolean' },
github_ci_ok: { type: 'boolean' },
blocking_prs: { type: 'array', items: { type: 'object' } },
},
required: ['root_branch', 'all_branches', 'status'],
},
},
shippable_count: { type: 'number' },
pending_count: { type: 'number' },
blocked_count: { type: 'number' },
incomplete_count: { type: 'number' },
},
required: ['stacks', 'shippable_count', 'pending_count', 'blocked_count', 'incomplete_count'],
}
const REVIEW_SCHEMA = {
type: 'object',
properties: {
root_branch: { type: 'string' },
verdict: { type: 'string', enum: ['ship', 'hold', 'fix'] },
confidence: { type: 'string', enum: ['high', 'medium', 'low'] },
summary: { type: 'string' },
risks: { type: 'array', items: { type: 'string' } },
blocking_issues: { type: 'array', items: { type: 'string' } },
},
required: ['root_branch', 'verdict', 'confidence', 'summary', 'risks', 'blocking_issues'],
}
const CONFLICT_SCHEMA = {
type: 'object',
properties: {
all_compatible: { type: 'boolean' },
working_stacks: { type: 'array', items: { type: 'string' } },
conflicting_stacks: { type: 'array', items: { type: 'string' } },
notes: { type: 'string' },
},
required: ['all_compatible', 'working_stacks', 'conflicting_stacks'],
}
const PLAN_SCHEMA = {
type: 'object',
properties: {
ship_groups: {
type: 'array',
description: 'Ordered groups of stacks to ship. Each group can be merged atomically.',
items: {
type: 'object',
properties: {
stacks: { type: 'array', items: { type: 'string' }, description: 'Root branches in this group' },
strategy: { type: 'string', enum: ['ship', 'drain', 'next'], description: 'ship=atomic consolidation (3+ branches), drain=sequential bottom-up, next=fire-and-forget lowest' },
command: { type: 'string', description: 'Exact stackit command to run' },
reasoning: { type: 'string' },
},
required: ['stacks', 'strategy', 'command', 'reasoning'],
},
},
hold: {
type: 'array',
items: {
type: 'object',
properties: {
root_branch: { type: 'string' },
reason: { type: 'string' },
},
required: ['root_branch', 'reason'],
},
},
fix: {
type: 'array',
items: {
type: 'object',
properties: {
root_branch: { type: 'string' },
issues: { type: 'array', items: { type: 'string' } },
},
required: ['root_branch', 'issues'],
},
},
summary: { type: 'string' },
},
required: ['ship_groups', 'hold', 'fix', 'summary'],
}
// Phase 1: Discover
phase('Discover')
const status = await agent(
`Run: stackit merge status --json --all
Return the JSON output as a structured object. If the command fails or produces no output, return {"stacks":[],"shippable_count":0,"pending_count":0,"blocked_count":0,"incomplete_count":0}.`,
{ label: 'discover:status', schema: STACK_SCHEMA }
)
const shippable = status.stacks.filter(s => s.status === 'shippable')
const nonShippable = status.stacks.filter(s => s.status !== 'shippable')
log(`${status.stacks.length} stacks total — ${status.shippable_count} shippable, ${status.pending_count} pending, ${status.blocked_count} blocked, ${status.incomplete_count} incomplete`)
if (shippable.length === 0) {
return {
ship_groups: [],
hold: status.stacks.filter(s => s.status === 'pending').map(s => ({ root_branch: s.root_branch, reason: `Waiting: ${(s.blocking_prs || []).map(b => b.reason).join(', ') || 'CI or review pending'}` })),
fix: status.stacks.filter(s => s.status === 'blocked').map(s => ({ root_branch: s.root_branch, issues: (s.blocking_prs || []).map(b => b.reason) })),
summary: `No stacks are ready to ship. ${status.pending_count} pending CI/review, ${status.blocked_count} blocked.`,
status,
}
}
// Phase 2: Review each shippable stack in parallel
phase('Review')
const reviews = (await parallel(shippable.map(stack => () => {
const tip = stack.all_branches[stack.all_branches.length - 1]
const branchChain = stack.all_branches.join(' → ')
return agent(
`Review this stack for ship-readiness.
Stack: ${stack.root_branch}
Title: ${stack.pr_title || stack.root_branch}
Branches: ${branchChain}
PRs: ${stack.pr_count}
CI passing: ${stack.github_ci_ok}
Approved: ${stack.approval_ok}
To inspect the changes:
git log main..${tip} --oneline
git diff main...${tip} --stat
git diff main...${tip}
For PR details (if PRs exist):
gh pr list --head ${stack.root_branch} --json number,title,body,additions,deletions,reviewDecision,state
Look for:
1. Correctness bugs (nil dereferences, missing error handling, off-by-one)
2. Missing tests for changed behavior
3. Security issues (command injection, path traversal, hardcoded secrets)
4. Breaking changes to public interfaces
5. Incomplete implementation (TODOs without tracking, panics, unhandled cases)
6. Race conditions or resource leaks
Return verdict:
- "ship": No blocking concerns
- "hold": Minor issues worth noting but not blocking (will be flagged in plan)
- "fix": Has bugs or security issues that should be addressed before merging`,
{ label: `review:${stack.root_branch}`, schema: REVIEW_SCHEMA }
)
}))).filter(Boolean)
// Phase 3: Conflict check
phase('Conflict Check')
let conflictResult
if (shippable.length > 1) {
const roots = shippable.map(s => s.root_branch).join(',')
conflictResult = await agent(
`Test whether these stacks can merge together without conflicts.
Run: stackit merge ship --dry-run --stacks ${roots}
Parse the output. Determine:
1. Can all stacks be merged atomically?
2. Which stacks can be included (working set)?
3. Which stacks conflict (would be excluded)?
If the command errors or no stacks can be combined, use working_stacks=[] and conflicting_stacks=[${shippable.map(s => `"${s.root_branch}"`).join(',')}].`,
{ label: 'conflict:check', schema: CONFLICT_SCHEMA }
)
} else {
conflictResult = {
all_compatible: true,
working_stacks: shippable.map(s => s.root_branch),
conflicting_stacks: [],
notes: 'Single stack, no conflict check needed',
}
}
// Phase 4: Synthesize ship plan
phase('Plan')
const reviewSummary = reviews.map(r =>
`${r.root_branch}: verdict=${r.verdict} (${r.confidence} confidence) — ${r.summary}` +
(r.risks.length ? `\n Risks: ${r.risks.join('; ')}` : '') +
(r.blocking_issues.length ? `\n Blocking: ${r.blocking_issues.join('; ')}` : '')
).join('\n')
const plan = await agent(
`Create a smart ship plan.
SHIPPABLE STACKS:
${shippable.map(s => `- ${s.root_branch}: ${s.pr_count} PR(s), "${s.pr_title || s.root_branch}", CI=${s.github_ci_ok}, approved=${s.approval_ok}`).join('\n')}
CODE REVIEW RESULTS:
${reviewSummary}
CONFLICT ANALYSIS:
- All compatible: ${conflictResult.all_compatible}
- Working stacks (can merge together): ${conflictResult.working_stacks.join(', ') || 'none'}
- Conflicting stacks: ${conflictResult.conflicting_stacks.join(', ') || 'none'}
NON-SHIPPABLE STACKS (for context):
${nonShippable.map(s => `- ${s.root_branch}: ${s.status}`).join('\n') || 'none'}
Rules for the plan:
- Group compatible working stacks to ship atomically (prefer fewer merge operations)
- Use strategy="ship" (atomic consolidation) when a group has 3+ total branches across stacks
- Use strategy="drain" for smaller groups (1-2 branches per stack, sequential bottom-up)
- Conflicting stacks that can't merge together go in separate groups
- Move "hold" review stacks to the hold list with the specific concern
- Move "fix" review stacks to the fix list with the specific issues
- For each ship_group, generate the exact stackit command to run
Example commands:
stackit merge ship --stacks root1,root2 # atomic multi-stack
stackit merge ship --branch root1 # single stack ship
stackit merge drain --branch root1 # sequential bottom-up`,
{ label: 'plan:synthesize', schema: PLAN_SCHEMA }
)
return { ...plan, status, reviews }
Step 2: Present the Plan
After the workflow completes, format and sh
Content truncated.
When not to use it
- →When merging a single, independent PR
- →When the user wants to manually manage all merges
- →When the task is not related to stack-based development workflows
Limitations
- →Requires `stackit` and `gh` CLI tools.
- →Relies on `stackit merge status --json --all` for stack discovery.
- →Requires user approval before executing merge commands.
How it compares
This skill automates the complex process of evaluating and planning merges for multiple interdependent stacks, reducing manual effort and preventing merge conflicts, unlike a manual merge process.
Compared to similar skills
stack-ship side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| stack-ship (this skill) | 0 | 2mo | Review | Advanced |
| fix-pr | 1 | 5mo | Review | Intermediate |
| resolve-checks | 1 | 6mo | Review | Intermediate |
| agent-github-pr-manager | 1 | 6mo | Review | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
You might also like
fix-pr
AztecProtocol
Fix a failing PR by analyzing CI logs and fixing errors. Autonomous workflow that identifies failures, rebases, fixes issues, and pushes.
resolve-checks
flowglad
Resolve all failing CI checks and address PR review feedback on the current branch's PR. Runs tests locally, fixes failures, incorporates valid review comments, and resolves addressed feedback. Use when CI is red, after receiving PR feedback, or before merging.
agent-github-pr-manager
ruvnet
Agent skill for github-pr-manager - invoke with $agent-github-pr-manager
agent-github-modes
ruvnet
Agent skill for github-modes - invoke with $agent-github-modes
agent-pr-manager
ruvnet
Agent skill for pr-manager - invoke with $agent-pr-manager
git-pr-workflows-git-workflow
sickn33
Orchestrate a comprehensive git workflow from code review through PR creation, leveraging specialized agents for quality assurance, testing, and deployment readiness. This workflow implements modern g