This skill should be used when preparing a feature for production deployment. It enforces the complete feature lifecycle checklist, ensuring all artifacts are committed, documentation is updated, and learnings are captured before creating a PR. Version bumping happens automatically in CI.
From soleurnpx claudepluginhub jikig-ai/soleur --plugin soleurThis skill uses the workspace's default tool permissions.
Purpose: Enforce the full feature lifecycle before creating a PR, preventing missed steps like forgotten /compound runs and uncommitted artifacts. Version bumping is handled by CI at merge time via semver labels.
CRITICAL: No command substitution. Never use $() in Bash commands. When a step says "get value X, then use it in command Y", run them as two separate Bash tool calls -- first get the value, then use it literally in the next call. This avoids Claude Code's security prompt for command substitution.
If $ARGUMENTS contains --headless, set HEADLESS_MODE=true. Strip --headless from $ARGUMENTS before processing remaining args.
When HEADLESS_MODE=true:
skill: soleur:compound --headless (forward flag, no user prompt)Detect the current environment:
# Determine current branch and worktree
git rev-parse --abbrev-ref HEAD
git worktree list
pwd
Branch safety check (defense-in-depth): If the branch from the command above is main or master, abort immediately with: "Error: ship cannot run on main/master. Checkout a feature branch first." This is defense-in-depth alongside PreToolUse hooks -- it fires even if hooks are unavailable (e.g., in CI).
Load project conventions:
if [[ -f "CLAUDE.md" ]]; then
cat CLAUDE.md
fi
Identify the base branch (main/master) for comparison:
git remote show origin | grep 'HEAD branch'
Check that feature artifacts exist and are committed. Look for files related to the current feature branch name:
Get the current branch name:
git rev-parse --abbrev-ref HEAD
Extract the feature name from the result by stripping the feat-, feature/, fix-, or fix/ prefix. Then search for related artifacts using the Glob and Bash tools:
knowledge-base/project/brainstorms/*FEATURE*knowledge-base/project/specs/feat-FEATURE/spec.mdknowledge-base/project/plans/*FEATURE*git status --porcelain knowledge-base/If artifacts exist but are not committed: Stage and commit them.
If no artifacts exist: Note this in the checklist but do not block. Not all features go through the full brainstorm/plan cycle.
Check for evidence that /review ran on the current branch. This is defense-in-depth --
/one-shot already enforces review ordering, but direct /ship invocations bypass it.
Step 1: Check for review artifacts (legacy).
Search for todo files tagged as code-review findings:
grep -rl "code-review" todos/ 2>/dev/null | head -1 || true
Step 2: Check commit history for review evidence (legacy).
If Step 1 found nothing, check for the review commit pattern:
git log origin/main..HEAD --oneline | grep "refactor: add code review findings" || true
Step 3: Check for GitHub issues with code-review label (current).
If Steps 1 and 2 found nothing, check for review issues linked to this branch's PR. This requires two separate Bash calls (no command substitution):
Step 3a — get the current branch name:
git branch --show-current
Step 3b — get the PR number for that branch (use the branch name from Step 3a literally):
gh pr list --head <branch-name> --state open --json number --jq '.[0].number // empty'
Step 3c — if Step 3b returned a PR number, search for code-review issues referencing it:
gh issue list --label code-review --search "PR #<number>" --limit 1 --json number --jq '.[0].number // empty'
If gh fails or is unavailable, treat as no output (fail open on Signal 3).
Note: Three signals are checked, any one suffices:
todos/ grep): coupled to legacy review workflow (pre-#1329)gh issue list): coupled to review-todo-structure.md issue body template (**Source:** PR #<number>)If any step produced output: Review evidence found. Continue to Phase 2.
If no step produced output:
Headless mode: Abort with: "Error: no review evidence found on this branch. Run /review before /ship, or use /one-shot for the full pipeline."
Interactive mode: Present options via AskUserQuestion:
"No evidence that /review ran on this branch. How would you like to proceed?"
skill: soleur:review, then continue to Phase 2Why: Identified during #1129/#1131/#1134 implementation session when the /one-shot pipeline ran correctly but the gap was noted as a systemic risk for direct /ship invocations. See #1170.
Check if /compound was run for this feature. Use the feature name extracted in Phase 1:
git log --oneline --since="1 week ago" -- knowledge-base/project/learnings/
Also use the Glob tool to search knowledge-base/project/learnings/**/*FEATURE* (replacing FEATURE with the actual name).
If no recent learning exists: Check for unarchived KB artifacts before offering a choice.
Search for unarchived artifacts matching the feature name (excluding archive/ paths) using the Glob tool:
knowledge-base/project/brainstorms/*FEATURE*knowledge-base/project/plans/*FEATURE*knowledge-base/project/specs/feat-FEATURE/If unarchived artifacts exist: Do NOT offer Skip. List the found artifacts and explain that compound must run to consolidate and archive them before shipping. Then use skill: soleur:compound (or skill: soleur:compound --headless if HEADLESS_MODE=true). The compound flow will automatically consolidate and archive the artifacts on feat-* branches.
If no unarchived artifacts exist:
Headless mode: Auto-invoke skill: soleur:compound --headless without prompting.
Interactive mode: Offer the standard choice:
"No learnings documented for this feature. Run /compound to capture what you learned?"
skill: soleur:compoundCheck if new commands, skills, or agents were added in this branch.
Step 1 (separate Bash call): Get the merge base hash.
git merge-base HEAD origin/main
Step 2 (separate Bash call): Use the hash from Step 1 literally in this command.
git diff --name-status HASH..HEAD -- plugins/soleur/commands/ plugins/soleur/skills/ plugins/soleur/agents/
Replace HASH with the actual commit hash from Step 1. Do NOT use $() to combine these.
If new components were added:
bash scripts/sync-readme-counts.sh to auto-update counts in both README.md and plugins/soleur/README.mdplugins/soleur/README.mdknowledge-base/marketing/brand-guide.md exists, check for stale agent/skill counts and update themIf no new components: Run bash scripts/sync-readme-counts.sh --check to verify counts are still in sync. Fix if drifted.
First, verify that new source files have corresponding test files:
Find new source files added in this branch. First, get the merge base hash (reuse from Phase 3 if already obtained):
git merge-base HEAD origin/main
Then, in a separate Bash call, use the hash literally:
git diff --name-only --diff-filter=A HASH..HEAD
Replace HASH with the actual commit hash. Filter results for .ts, .js, .rb, .py files (excluding test/spec/config files).
For each new source file, check if a corresponding test file exists (e.g., foo.ts -> foo.test.ts or foo.spec.ts). Report any source files missing test coverage.
If test files are missing:
Headless mode: Continue without writing tests (CI gate catches missing coverage).
Interactive mode: Ask the user whether to write tests now or continue without them. Do not silently proceed.
Then run the project's test suite:
bun test
If tests fail: Stop and fix before proceeding.
Create a TodoWrite checklist summarizing the state:
Ship Checklist for [branch name]:
- [x/skip] Artifacts committed (brainstorm/spec/plan)
- [x/skip] Learnings captured (/compound)
- [x/skip] README counts synced (`bash scripts/sync-readme-counts.sh`)
- [x/skip] Tests pass
- [ ] Code review completed (Phase 5.5 gate)
- [ ] Push to remote
- [ ] Create PR with semver label
- [ ] PR is mergeable (no conflicts)
- [ ] CI checks pass
Defense-in-depth check that review ran before shipping. Phase 1.5 catches this earlier, but if context compaction erased Phase 1.5's check or the skill was invoked mid-flow, this gate is the second net.
Detection: Check for review evidence using the same three signals described in Phase 1.5 (Signal 1: todos/ grep, Signal 2: commit message grep, Signal 3: GitHub issues with code-review label). Run the same commands in the same order. See Phase 1.5 for full details and coupling notes.
If review evidence is found: Pass silently.
If no review evidence is found:
Headless mode: Abort with: "Error: no review evidence found on this branch. Run /review before /ship, or use /one-shot for the full pipeline."
Interactive mode: Display warning: "No code review was run before ship." Then invoke skill: soleur:review. After review completes, if findings include critical or high severity issues, resolve them before continuing to Phase 6.
Domain leaders are consulted at brainstorm time but not at ship time. The actual deliverables may have implications the brainstorm couldn't predict. This phase runs three conditional gates in parallel.
Trigger: PR matches ANY of: (a) touches files in knowledge-base/product/research/, knowledge-base/marketing/, or adds new workflow patterns (new AGENTS.md rules, new skill phases); (b) has a semver:minor or semver:major label; (c) title matches ^feat(\(.*\))?: pattern.
Detection: Run git diff --name-only origin/main...HEAD and check file paths against trigger (a). Run gh pr view --json labels,title and check against triggers (b) and (c). If any trigger matches, proceed to "If triggered."
If triggered:
knowledge-base/marketing/content-strategy.md: add the piece to the content pipeline table under the appropriate pillar AND insert it into the rolling quarterly calendar at the correct week. A GitHub issue without a content strategy entry is an orphan — it will be forgotten. Why: In #1173, a methodology blog post was created as issue #1176 but never added to the content strategy calendar, requiring a manual fix.Why: In #1173, a research sprint produced a novel methodology with compelling data, but no content was planned because the CMO was only consulted when the scope was "should we explore this?" — not when the actual content existed.
Trigger: PR modifies knowledge-base/marketing/brand-guide.md — specifically the Value Proposition Framings, Positioning, Tagline, or Voice sections. Also triggers if the PR modifies value prop findings or competitive positioning documents that inform website copy.
Detection: Run git diff --name-only origin/main...HEAD and check for brand-guide.md. If present, check git diff origin/main...HEAD -- knowledge-base/marketing/brand-guide.md for changes to positioning-related sections.
If triggered:
apps/web-platform/, docs/, or the Eleventy source directory) — do NOT use Playwright to fetch the rendered site when the source files are local. Prompt: "The brand guide's value proposition framings have been updated. Audit the website source templates for alignment: does the hero headline, subheadline, feature descriptions, and pricing page messaging match the updated framing recommendations? Identify specific copy that needs updating and propose replacements with file paths and line numbers."Why: In #1173, the brand guide was updated with a new primary framing ("Stop hiring, start delegating"), a memory-first A/B variant, and trust scaffolding recommendations — but the website still used the old framing. Brand guide changes that don't cascade to the website create a disconnect between strategy and execution.
Trigger: The PR or session involved signing up for new services, provisioning new tools, subscribing to APIs, or using paid external resources during implementation. Also triggers if the diff adds new entries to infrastructure configs, Terraform files, or references new SaaS tools not already in knowledge-base/operations/expenses.md.
Detection: Scan the session for: account creation actions (Playwright flows, CLI signups), new API key generation, new tool installations, new Terraform resources, or references to services not already tracked in the expense ledger. Also check git diff origin/main...HEAD for new domain names, new provider references in .tf files, or new environment variables suggesting new service integrations.
If triggered:
knowledge-base/operations/expenses.md. For any not already tracked, provide the service name, estimated cost, billing cycle, and category for the expense ledger."expenses.md.If not triggered: Skip silently.
Why: New tools and subscriptions adopted during implementation often go unrecorded in the expense ledger because they feel incidental to the engineering work. The COO gate ensures every new cost is tracked at ship time, not discovered months later during a financial review.
Trigger: The PR fixes a gate's detection logic (trigger conditions, assessment questions, or routing rules) AND the fix was motivated by a specific case that the gate missed.
Detection: Check if the PR modifies any of: Phase 5.5 gate trigger/detection sections in this file, assessment questions in brainstorm-domain-config.md, or domain routing rules in AGENTS.md. If yes, check the linked issue or brainstorm document for the original missed case (e.g., a PR number, feature name, or issue that exposed the gap).
If triggered:
If not triggered: Skip silently.
Why: In #1265, the CMO content gate was fixed to catch product features but the PWA feature itself was never assessed — the fix shipped without remediating the original gap. "Gate fixed" is not done — "gate fixed AND missed case remediated" is done.
Before creating or editing the PR, detect if the work resolves a GitHub issue. Check these sources (in order, stop at first match):
Branch name: Extract issue number from patterns like fix/123-description, feat/issue-123, fix-123, or any segment matching \b(\d+)\b after a fix or issue prefix.
Commit messages: Search recent branch commits for #N references:
git log origin/main..HEAD --oneline
Extract any #N references from the output.
User context: If the user mentioned an issue number earlier in the conversation, use it.
If an issue number is found, store it as ISSUE_NUMBER for use in the PR body below. If multiple are found, use all of them. If none are found, omit the Closes line from the PR body.
Important: Use Closes #N syntax (not Ref #N, not (#N) in the title). GitHub only auto-closes issues when the PR body contains a keyword (Closes, Fixes, or Resolves) followed by the issue reference.
Push the branch to remote. Get the branch name first:
git rev-parse --abbrev-ref HEAD
Then push in a separate Bash call, using the branch name literally:
git push -u origin BRANCH_NAME
Replace BRANCH_NAME with the actual branch name from the previous call.
Check for existing PR on this branch:
Check for an existing open PR using the branch name from above:
gh pr list --head BRANCH_NAME --state open --json number,isDraft --jq '.[0]'
Replace BRANCH_NAME with the actual branch name.
If an open PR exists:
The PR was likely created as a draft earlier in the workflow.
Headless mode: Auto-accept the generated PR title/body from diff analysis. Interactive mode: Confirm the PR title and body with the user before editing.
Update the PR. Pass the body as a multi-line string (no $() needed):
gh pr edit PR_NUMBER --title "the pr title" --body "## Summary
- bullet points
Closes #ISSUE_NUMBER
## Changelog
- changelog entries describing what changed
## Test plan
- checklist
Generated with [Claude Code](https://claude.com/claude-code)"
If ISSUE_NUMBER was detected, include the Closes #N line. If multiple issues, list each (Closes #N, Closes #M). If no issue was detected, omit the Closes line entirely.
Do not quote flag names -- write --title not "--title".
If the PR is a draft, mark it ready:
gh pr ready PR_NUMBER
Present the PR URL to the user.
If no open PR exists:
Fall through to creating a new PR. This handles cases where the user entered the pipeline through /plan or /work directly (skipping brainstorm/one-shot).
gh pr create --title "the pr title" --body "## Summary
- bullet points
Closes #ISSUE_NUMBER
## Changelog
- changelog entries describing what changed
## Test plan
- checklist
Generated with [Claude Code](https://claude.com/claude-code)"
If ISSUE_NUMBER was detected, include the Closes #N line. If no issue was detected, omit it.
Do not quote flag names -- write --title not "--title".
Present the PR URL to the user.
After the PR is created or updated, determine the appropriate semver label and apply it.
Step 1: Analyze the diff to determine bump type. Get the merge base hash (reuse from Phase 3 if already obtained):
git merge-base HEAD origin/main
Then, in a separate Bash call, check for new components:
git diff --name-status HASH..HEAD -- plugins/soleur/commands/ plugins/soleur/skills/ plugins/soleur/agents/
Replace HASH with the actual commit hash.
Step 1b: Check for app changes (in a separate Bash call):
git diff --name-only HASH..HEAD -- apps/web-platform/ | head -1
git diff --name-only HASH..HEAD -- apps/telegram-bridge/ | head -1
If apps/web-platform/ has changes, apply app:web-platform label. If apps/telegram-bridge/ has changes, apply app:telegram-bridge label:
gh pr edit PR_NUMBER --add-label app:web-platform
gh pr edit PR_NUMBER --add-label app:telegram-bridge
Only apply each label if the corresponding path has changes.
Step 2: Determine the bump type:
A status files in the diff above), OR new files added under apps/*/When ONLY app files changed (no plugin files), still apply semver:* based on app change significance — new files added means semver:minor, changes only means semver:patch.
Step 3: Apply the semver label to the PR:
gh pr edit PR_NUMBER --add-label semver:patch
Replace semver:patch with semver:minor or semver:major as appropriate. Replace PR_NUMBER with the actual PR number.
Step 4: Generate a ## Changelog section from the changes and update the PR body to include it. The changelog should describe what changed in user-facing terms (not file paths). If the PR body already has a ## Changelog section, update it. Include app changes alongside plugin changes — group by component if multiple components changed (e.g., "### Plugin", "### Web Platform").
Step 5: Validate consistency -- if new agents, skills, or commands were detected in Step 1 but the label is semver:patch, warn the user that the label may be incorrect. New components typically warrant semver:minor.
After pushing (or after any subsequent push), verify the PR has no merge conflicts with the base branch:
git fetch origin main
gh pr view --json mergeable,mergeStateStatus | jq '{mergeable, mergeStateStatus}'
If mergeable is MERGEABLE: Continue to Phase 7.
If mergeable is CONFLICTING:
Merge the base branch locally to surface conflicts:
git merge origin/main --no-commit --no-ff
Identify conflicted files:
git diff --name-only --diff-filter=U
Read each conflicted file and resolve. Common conflict patterns:
Stage resolved files and commit the merge:
git add <resolved files>
git commit -m "Merge origin/main -- resolve conflicts"
Push and re-verify:
git push
gh pr view --json mergeable | jq '.mergeable'
If still CONFLICTING after resolution: stop and ask the user for help.
If mergeable is UNKNOWN: Wait 5 seconds and re-check (GitHub may still be computing). After 3 retries, warn and continue.
After confirming mergeability, queue auto-merge and let GitHub handle waiting for CI:
gh pr merge <number> --squash --auto
Do NOT use gh pr checks --watch -- it exits immediately with "no checks reported" when CI hasn't registered yet, causing premature merge attempts.
If auto-merge fails to queue: Check gh api repos/{owner}/{repo} --jq '.allow_auto_merge' -- it must be true.
After auto-merge is queued, poll until the PR is merged. Do NOT ask "merge now or later?" -- auto-merge handles it.
gh pr view <number> --json state --jq .state
Poll every 10 seconds until state is MERGED.
If state becomes CLOSED (not MERGED): Auto-merge was cancelled due to a CI failure.
Read the failure details:
gh pr checks --json name,state,description | jq '.[] | select(.state != "SUCCESS")'
If the failure is in tests: investigate the failing test, fix locally, commit, push, re-queue auto-merge.
If the failure is in a flaky or unrelated check: Headless mode: abort the pipeline with a clear error message (do not auto-proceed past failed checks). Interactive mode: warn the user and ask whether to proceed or wait for a re-run.
CRITICAL: Do NOT use --delete-branch on merge. The guardrails hook blocks --delete-branch whenever ANY worktree exists in the repo -- not just the one for the branch being merged -- so the restriction applies unconditionally during parallel development. Merge with --squash only, then cleanup-merged handles branch deletion after removing the worktree.
If merged (either now or user says "merge PR" later in the session):
Version bump and release are automatic. The version-bump-and-release.yml GitHub Actions workflow reads the PR's semver:* label, computes the next version from the latest release tag, creates a GitHub Release with a vX.Y.Z tag, and posts to Discord. No committed files are modified — version is derived from git tags.
If the workflow did not fire (e.g., no semver label was set), run /release-announce manually as a fallback.
Verify all release/deploy workflows triggered by the merge. The push to main triggers release workflows based on path filters (e.g., web-platform-release.yml when apps/web-platform/** changed). These can fail for reasons unrelated to PR CI (Docker build failures, lockfile drift, deploy health mismatches). A failing release workflow means the old version keeps running in production — this is a silent outage.
Step 1: Get the merge commit SHA. Use the PR number from Phase 6:
gh pr view <number> --json mergeCommit --jq .mergeCommit.oid
Step 2: Wait 15 seconds for workflows to trigger, then list all runs on the merge commit:
gh run list --branch main --commit <merge-sha> --json databaseId,workflowName,status,conclusion
Step 3: For each run that is not yet completed, poll every 30 seconds:
gh run view <id> --json status,conclusion --jq '"\(.status) \(.conclusion)"'
Poll until all runs complete. Expected max wait: ~5 minutes for Docker builds + deploy verification.
Step 4: Check conclusions:
success: Report "Release verification: N/N workflows passed" and continue.failure: Report which workflow failed, fetch logs with gh run view <id> --log-failed | tail -n 50, and investigate. Do NOT silently proceed. If the failure is in the release/deploy pipeline, it must be fixed before ending the session — production is running stale code.If no workflows were triggered (the PR only touched files outside all path filters): Skip this step.
Why this matters: In #1293, PR #1275 added @playwright/test to package.json but didn't update package-lock.json. PR CI passed (it uses bun test, not npm ci), but the Docker build uses npm ci which requires lockfile sync. Five consecutive release runs failed silently — production stayed on v0.8.6 for hours while new PRs kept merging.
Post-merge validation of new or modified workflows. If the PR added or modified GitHub Actions workflow files (.github/workflows/*.yml), validate them by triggering each affected workflow via workflow_dispatch and polling for completion. This is mandatory — never leave validation as a manual step for the user.
Step 1: Detect new or modified workflow files in this PR. Use the merge base hash from Phase 3:
git diff --name-only --diff-filter=AM HASH..HEAD -- .github/workflows/
Note: --diff-filter=AM catches both Added and Modified files. A modified workflow is just as likely to break as a new one — both must be validated.
Step 2: For each affected workflow file, trigger it:
gh workflow run <workflow-filename>
If a workflow has a long expected runtime (>10 minutes), note this to the user and continue polling. Do not skip validation because the workflow is slow.
Step 3: Poll each triggered run until completion (check every 30 seconds):
gh run list --workflow <workflow-filename> --limit 1 --json databaseId,status,conclusion --jq '.[0]'
Poll until status is completed. Then check conclusion:
gh run view <id> --log | tail -50, and present the error to the user. Do NOT silently proceed.Step 4: Report summary: "Post-merge validation: N/N workflows passed" or "Post-merge validation: X/N workflows failed — [details]"
If no new or modified workflow files were detected: Skip this step.
Why this matters: The founder is a solo operator. Every "please run this manually" is a context switch. gh workflow run exists — use it. Modified workflows are equally risky — a prompt change, a new step, or a timeout bump can all cause failures that are invisible without a live run. Why AM not just A: In #1126, a modified workflow (new Steps 5.5/5.6 in growth audit) was merged without validation because the ship skill only checked for new files.
Clean up worktree and local branch:
Navigate to the repository root directory, then run bash ./plugins/soleur/skills/git-worktree/scripts/worktree-manager.sh cleanup-merged.
This detects [gone] branches (where the remote was deleted after merge), removes their worktrees, archives spec directories, deletes local branches, and pulls latest main so the next worktree branches from the current state.
If working from a worktree: Navigate to the main repo root first, then run cleanup.
If the session ends before cleanup runs: The next session will handle it automatically via the Session-Start Hygiene check in AGENTS.md. The cleanup-merged script is idempotent and safe to run at any time.
plugins/soleur/ must have a semver:patch, semver:minor, or semver:major label. CI uses this label to bump the version at merge time.plugin.json and marketplace.json versions are frozen sentinels (0.0.0-dev). Version is derived from git tags via GitHub Releases at build time.