skill/ship/SKILL.md
Canonical push-to-dev and branch-policy enforcement for agents. Provides the push-to-dev workflow, branch naming, conflict handling, and release process guidance. Trigger with: /skill:ship push-to-dev
npx skillsauth add sorratheorc/sorraagents shipInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Canonical agent-side push-to-dev behaviour and branch-policy enforcement, with automated dev-to-main release execution.
Provide a deterministic, automated push-to-dev and release workflow for agents:
agents push completed feature branches into dev, and the release process
promotes tested changes from dev to main via a
gated, PR-based merge process.
dev integration branch.Triggers
Agents reference this skill explicitly in their prompts or via skill invocation:
/skill:ship <action>
Where <action> is one of:
| Action | Description |
|--------|-------------|
| check-unmerged-branches | Check for local branches not yet merged into dev and report their work item details |
| push-to-dev | Push the current feature branch into the dev integration branch |
| validate-branch <name> | Validate a branch name against the canonical pattern |
| generate-name <work-item-id> <short-desc> | Generate a canonical branch name |
| check-blocked <branch> | Check whether a branch is protected |
| release | Execute the automated dev → main release |
| help | Print the ship skill documentation |
skill/ship/scripts/ship.js — Push-to-dev behaviour module (exports: pushToDev, pushToBranch, validatePushTarget, validateForcePush, DEV_BRANCH, PROTECTED_BRANCHES, checkUnmergedBranches, and re-exports from git-helpers.js)skill/ship/scripts/git-helpers.js — Branch naming and policy helpers (exports: makeBranchName, validateBranchName, isBranchBlocked, BLOCKED_BRANCHS, BRANCH_NAME_PATTERN)skill/ship/scripts/check-unmerged-branches.js — Unmerged branch detection module (exports: checkUnmergedBranches, getUnmergedBranchNames, extractWorkItemId, getWorkItemStatus)skill/ship/scripts/run-release.js — Safe wrapper to invoke the release process (exports: runRelease, syncDevWithMain, parsePRUrl, waitForPRMerge; includes unmerged branches gating check, post-release dev sync)skill/ship/scripts/release/merge-dev-to-main.sh — Canonical release merge script (installed in the skill directory)// Push completed work into dev
import { pushToDev } from 'skill/ship/scripts/ship.js';
const result = pushToDev('origin');
if (!result.success) {
// handle failure — e.g., create a merge-conflict work item
}
// Generate a canonical branch name
import { makeBranchName, validateBranchName, isBranchBlocked } from 'skill/ship/scripts/git-helpers.js';
const branchName = makeBranchName('SA-001', 'fix-login-bug');
// Returns: 'wl-SA-001-fix-login-bug'
const validation = validateBranchName('wl-SA-001-fix-login-bug');
// Returns: { valid: true }
const blocked = isBranchBlocked('main');
// Returns: true
Before performing any operation that integrates branches (push-to-dev, release),
the ship skill automatically checks for local branches that are not yet merged
into dev. This is a gating step to prevent accidentally pushing when there
are unmerged feature branches that should be dealt with first.
The check works as follows:
git branch --no-merged dev to list all local branches not merged into dev.dev itself, protected branches (main, master), and the
current branch (the branch being worked on).wl-<work-item-id>-<slug> pattern,
extracts the work item ID and queries Worklog (wl) for its title, status, and stage.If unmerged branches are found, the operation is blocked with a report that shows:
import { checkUnmergedBranches } from 'skill/ship/scripts/check-unmerged-branches.js';
const report = checkUnmergedBranches();
if (report.hasUnmergedBranches) {
console.log(report.message);
// Example output:
// Found 2 local branch(es) not yet merged into 'dev':
//
// 1. Branch: wl-SA-001-fix-login-bug
// Work Item: Fix login bug (SA-001)
// Status: in_progress
// Stage: in_review
//
// Would you like to merge these branches into dev first before proceeding?
}
The report is also available as structured data:
// report.unmergedBranches is an array of:
// {
// branch: string, // The branch name
// workItemId: string|null, // Extracted work item ID (null if not a wl- branch)
// title: string|null, // Work item title from Worklog
// status: string|null, // Work item status
// stage: string|null, // Work item stage
// error?: string // Any error from querying Worklog
// }
Both pushToDev() and pushToBranch() (when targeting dev) automatically
run the unmerged branches check before executing the push. If unmerged branches
are found, the push is rejected with the report in the error message.
To bypass the gating check, resolve or merge the unmerged branches first.
The release wrapper (run-release.js) also runs the unmerged branches check
before executing the release script. If unmerged branches are found, the release
is aborted. To bypass, use the --skip-checks flag:
node skill/ship/scripts/run-release.js --skip-checks
Agents work in feature branches and push completed work into the dev integration branch. This is the canonical integration action.
makeBranchName(workItemId, shortDesc) from skill/ship/scripts/git-helpers.js.validateBranchName(name).checkUnmergedBranches() (also run automatically by pushToDev()).pushToDev() from skill/ship/scripts/ship.js. This:
git push origin HEAD:refs/heads/dev{ success, error?, command? }Agents MUST NOT push directly to:
mainmasterHEADUse isBranchBlocked(branch) or validatePushTarget(targetBranch) to check before any push operation.
When pushToDev() fails due to a non-fast-forward rejection (conflict):
{ success: false, error: "..." } — no force-push is attempted.All agent-created branches MUST follow the canonical pattern:
wl-<work-item-id>-<short-description>
Where:
wl- is a literal prefix<work-item-id> is the Worklog identifier (e.g., SA-0MPDZDPZB00121IE)<short-description> is a lowercase, hyphen-separated slugExamples:
wl-SA-0MPDZDPZB00121IE-branch-naming-policywl-SA-001-fix-login-bugBranch names are validated against: /^wl-[A-Z0-9]+(-[A-Z0-9]+)*-[a-z0-9]+(-[a-z0-9]+)*$/
A pre-push hook at .githooks/pre-push enforces this policy automatically. Before any git push, the hook checks the current branch against the blocked list and rejects the push if the branch is protected. To enable:
git config core.hooksPath .githooks
Force-push (git push --force / git push -f) is prohibited.
Execute the release using the canonical merge script:
node skill/ship/scripts/run-release.js
The script performs the following steps:
Unmerged branches check: Runs checkUnmergedBranches() to verify no local
branches are pending merge into dev. If unmerged branches are found, the
release is aborted with a report. Use --skip-checks to bypass.
Pre-flight checks: Verifies gh authentication, wl availability,
and a clean working tree.
CI verification: Checks that the dev-full-suite workflow is green
on the dev branch via the GitHub Actions API. This is a hard gate
— the script aborts if CI is not green (use --force to bypass in
exceptional circumstances).
Merge commit creation: Fetches latest dev and main, creates a
merge commit locally (dev → main with --no-ff).
PR creation: Pushes the merge commit to a temporary
release/dev-to-main-<timestamp> branch and creates a GitHub Pull
Request targeting main.
Status check wait & PR merge: Waits for required status checks to pass
on the PR (configurable timeout, default 10 minutes), then merges the PR
using gh pr merge --merge --delete-branch. When using --force, the PR
is merged immediately without waiting.
Audit logging: Records the merge commit hash, CI run IDs, PR URL, and approver identity in the worklog.
Sync dev with main: After the release is complete, the script
automatically switches back to the dev branch, merges origin/main into
it (fast-forward), and pushes the updated dev to origin. This ensures
dev stays in sync with main after each release.
The dev sync is performed by syncDevWithMain() which:
git fetch origin --prunegit checkout devgit merge origin/maingit push origin devFor repositories where the automated merge is not suitable, a human Release Manager can perform the release
manually. The Release Manager must follow the checklist and procedures in
docs/dev/release-process.md.
The human fallback supports three approaches:
| Approach | Description | When to use |
|----------|-------------|-------------|
| Automated script | Run node skill/ship/scripts/run-release.js manually | Script is available in the skill directory |
| Direct merge | git checkout main && git merge origin/dev --no-ff | No branch protection on main |
| Manual PR | Create a temp branch with merge result and open a PR | Want human review before merge |
The following must be verified before merging dev into main:
dev-full-suite is green on the current dev HEAD.dev-smoke is green on the current dev HEAD.dev and main.See docs/dev/release-tests.md for the
test commands to run locally.
skill/ship/scripts/run-release.js
to perform the dev → main merge.docs/dev/release-process.md.main or dev.--force.wl comment add.main branch is protected: agents must never push directly to main.The per-work-item merge workflow in AGENTS.md (step 6) describes PR-based merging into main. This skill's pushToDev() function handles the dev integration step that happens before the final PR to main:
pushToDev() to integrate into devdevdev → main via PR (see AGENTS.md step 6)release/dev-to-main-<timestamp> to main
(created and merged by the script).testing
Automated batch planning for intake_complete work items. Discovers all items in intake_complete status and invokes /plan for each sequentially, producing a summary report.
data-ai
Unified git management skill that orchestrates the full feature-branch lifecycle — create, commit, push, PR, merge, cleanup — for both AI agents and human operators.
development
Write tests, docs and code for a single, specific Worklog work item. Unlike the `implement` skill, this skill operates on exactly one work-item without using `wl next` for recursive dependency resolution or sub-task discovery. It is designed to be invoked by Ralph's per-child loop so that each child is implemented, audited, and remediated independently. Trigger on user queries such as: 'implement-single <work-item-id>', 'complete <work-item-id> (single)', or when Ralph delegates a single-child implement step.
testing
Run an iterative implement→audit loop for a target work item. Ralph is a launcher/orchestrator, not the normal Worklog implementation workflow.