一键导入
migration
Create a labelled migration ticket + matching migration AgDR — required by the migration gate.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Create a labelled migration ticket + matching migration AgDR — required by the migration gate.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Review a PR for quality, security, and standards compliance. Invokes the Code Reviewer agent (Rex).
Review a technical design / migration AgDR / feature spec for architectural soundness BEFORE the Build phase. Invokes the Solution Architect agent (Tariq) — the non-code analog of /code-review.
Score a review agent (Rex/Hakim/Tariq) against a labeled PR corpus — ground-truth defect-set overlap, never a prose rubric. Headline metric is approve-precision.
Security-focused PR review for vulnerabilities and best practices. Invokes the Security Reviewer agent (Shield).
Record per-PR design-review approval (UI merge gate). ONLY on an explicit per-PR designer "approved".
Record per-PR CEO approval and merge in one turn. ONLY on an explicit per-PR "approved" — never on umbrella "go".
| name | migration |
| description | Create a labelled migration ticket + matching migration AgDR — required by the migration gate. |
| argument-hint | [<project>] |
| allowed-tools | Bash, Read, Write |
Migrations are high-blast-radius: data loss, downtime, lock contention, cross-service coordination. ApexYard treats them as a distinct class of change from code — a migration PR needs:
migration label (plus priority) that captures the plan.docs/agdr/AgDR-NNNN-migration-<slug>.md that captures the options considered, rollback plan, downtime estimate, cross-service consumers, data volume, testing plan, and observability.require-migration-ticket.sh (the matching PreToolUse hook) refuses to let you write to migration paths without both artefacts in place. This skill produces both in one pass.
Read the registry path via portfolio_registry, the per-project docs dir via portfolio_projects_dir, and the ideas backlog via portfolio_ideas_backlog — all from .claude/hooks/_lib-portfolio-paths.sh. Source the helper at the top of any bash block that touches those paths:
source "$(git rev-parse --show-toplevel)/.claude/hooks/_lib-read-config.sh"
source "$(git rev-parse --show-toplevel)/.claude/hooks/_lib-portfolio-paths.sh"
registry=$(portfolio_registry)
Defaults match today's single-fork layout (./apexyard.projects.yaml, ./projects, ./projects/ideas-backlog.md). Adopters in split-portfolio mode override the portfolio.{registry, projects_dir, ideas_backlog} keys in .claude/project-config.json. Don't hardcode literal apexyard.projects.yaml or projects/ paths in bash blocks — the helper resolves whichever mode the adopter is in. See docs/multi-project.md.
/migration # prompts for everything, creates ticket + AgDR in the current project
/migration curios-dog # explicitly target a registered project
backend/template.yamlaws_rds_* / aws_dynamodb_table resource in TerraformWrite on anything the migration-gate hook blocksBefore any gh issue create (or other tracker CLI), write this skill's name to the active-issue-skill marker so require-skill-for-issue-create.sh lets the command through. At skill entry:
# Resolve the ops-fork root the SAME way the hooks do (_lib-ops-root.sh):
# anchor on the .apexyard-fork marker (split-portfolio v2 — onboarding.yaml
# lives in the sibling portfolio repo, NOT the ops fork), falling back to the
# onboarding.yaml + apexyard.projects.yaml pair (single-fork v1).
ops_root="$PWD"; r="$PWD"
while [ "$r" != / ]; do
if [ -f "$r/.apexyard-fork" ] || { [ -f "$r/onboarding.yaml" ] && [ -f "$r/apexyard.projects.yaml" ]; }; then
ops_root="$r"; break
fi
r=${r%/*}
done
mkdir -p "$ops_root/.claude/session"
echo "migration" > "$ops_root/.claude/session/active-issue-skill"
Remove the marker on every exit path (success, early-exit, user cancel, error):
rm -f "$ops_root/.claude/session/active-issue-skill"
The clear-issue-skill-marker.sh SessionStart hook sweeps stale markers from killed sessions, but a clean exit should never leave one behind. See AgDR-0030.
If the user passed <project>, use that. Otherwise:
<ops_root>/workspace/<project>/, infer project from the pathLook up the project in apexyard.projects.yaml to get repo (the tracker) and workspace (for computing the AgDR path).
If the project isn't registered, stop — file one via /handover first, or pass the ticket as a plain ops-level change.
Ask each of the following. Each answer feeds both the issue body and the AgDR — the skill writes them into both so the user never retypes.
[Migration] <type>: <summary>schema | data | sql | orm (pick one; if it straddles, use the most invasive)staging | copy of prod | unit fixture | not tested. If "not tested", flag it in the AgDR and tell the user this is a blocker for prod apply (not for creating the ticket).none | seconds | minutes | hours. Plus reasoning: "why this much / this little?"P0 | P1 | P2 | P3. Default P1 for migrations (blast radius).Re-prompt if rollback plan is empty, affected tables is empty, or priority is missing — these are the three fields with no safe default.
Before creating anything, echo back a structured preview so the user can correct mistakes:
Ticket:
Tracker: <owner/repo>
Title: [Migration] <type>: <summary>
Labels: migration, <priority>
Body: (formatted — see below)
AgDR:
Path: docs/agdr/AgDR-NNNN-migration-<slug>.md
Next number: NNNN = max(existing AgDR ids in that dir) + 1
Ask "Create ticket + AgDR? [y/N]". Only proceed on explicit y.
Resolve the migration AgDR template via the portfolio helper so adopter overrides win when present:
source "$(git rev-parse --show-toplevel)/.claude/hooks/_lib-read-config.sh"
source "$(git rev-parse --show-toplevel)/.claude/hooks/_lib-portfolio-paths.sh"
template=$(portfolio_resolve_template agdr-migration.md) # → custom-templates/agdr-migration.md if present
cp "$template" "$resolved_agdr_path"
Single-fork adopters (no portfolio block) and adopters with no override fall straight through to templates/agdr-migration.md. Adopters who want a customised migration-AgDR shape drop their version at <private_repo>/custom-templates/agdr-migration.md. See templates/README.md for the path-mirroring convention.
Fill in:
id, timestamp, agent, model, trigger: user-prompt, status: draft, ticket — left as TBD until step 5 creates the issue and we know the number)AgDRs are written in the RIGHT repo:
| Where the migration runs | AgDR path |
|---|---|
| Inside a managed project's own code repo | workspace/<project>/docs/agdr/AgDR-NNNN-migration-<slug>.md |
| Against the apexyard framework itself (rare — only for framework-level data/config migrations) | docs/agdr/AgDR-NNNN-migration-<slug>.md in the ops fork |
For the NNNN id: scan the target docs/agdr/ directory for existing AgDR-\d+-.*\.md files, take the max id, increment. Zero-pad to 4 digits.
Title: [Migration] <type>: <summary>
Labels: migration (or whatever the project configures as its migration label — check .claude/project-config.json key migration_label, default migration), plus the priority label (P0 / P1 / P2 / P3).
Body — resolve the migration ticket body template via the portfolio helper so adopter overrides win when present:
source "$(git rev-parse --show-toplevel)/.claude/hooks/_lib-read-config.sh"
source "$(git rev-parse --show-toplevel)/.claude/hooks/_lib-portfolio-paths.sh"
ticket_template=$(portfolio_resolve_template tickets/migration.md) # → custom-templates/tickets/migration.md if present
Single-fork adopters (no portfolio block) and adopters with no override fall straight through to templates/tickets/migration.md. Adopters who want a customised migration-ticket shape drop their version at <private_repo>/custom-templates/tickets/migration.md. See templates/README.md for the path-mirroring convention.
Backward-compat fallback: if portfolio_resolve_template returns empty (template file missing — partial adopter setup), fall back to the inline heredoc body below and print a one-line WARN on stderr (WARN: tickets/migration.md template missing — using inline fallback).
Body (CommonMark, must include a ref to the AgDR so require-migration-ticket.sh can verify it) — the default templates/tickets/migration.md shape is reproduced below:
## Migration
**Type**: <type>
**Affected tables/entities**: <list>
**Estimated downtime**: <level> — <reasoning>
**Data volume**: <count or "unknown">
**Priority**: <priority>
## Rollback Plan
<rollback plan verbatim>
**Tested against**: <staging | copy of prod | unit fixture | not tested>
## Cross-Service Consumers
<list or "none">
**Deploy-order constraint**: <constraint or "none">
## Testing Plan
- Dev smoke: <command>
- Staging verify: <steps>
- Canary / phased rollout: <plan or "n/a">
## Observability
<what to watch during + after>
## Agent Decision Record
Migration AgDR: `<relative-path-to-AgDR>`
## Glossary
| Term | Definition |
|------|------------|
| <term> | <definition> |
---
Created by `/migration`. Do not edit the labels off this issue — the
migration-gate hook (`require-migration-ticket.sh`) verifies the
`migration` label is present before allowing edits to migration files.
Create via the tracker abstraction (tracker_create, #670 / AgDR-0072, extended
by the #709 creator sweep) so the ticket lands in this project's tracker —
GitHub, GitLab, or a custom CLI — per its tracker: block in
apexyard.projects.yaml. For a GitHub adopter this runs gh issue create
exactly as before.
# Resolve the tracker lib (it lives in the ops fork's hooks dir) by walking up
# from the cwd; source it.
tracker_lib="$(r="$PWD"; while [ -n "$r" ] && [ "$r" != / ]; do \
[ -f "$r/.claude/hooks/_lib-tracker.sh" ] && { echo "$r/.claude/hooks/_lib-tracker.sh"; break; }; \
r="${r%/*}"; done)"
# shellcheck source=/dev/null
. "$tracker_lib"
# $BODY was assembled above; hand it to tracker_create via a file (arbitrary
# markdown — never inline-interpolated). Labels pass as a comma-separated list.
body_file="$(mktemp)"
printf '%s\n' "$BODY" > "$body_file"
# tracker_create <owner/repo> <title> <body_file> [<labels_csv>] → {"ref","url"}.
result="$(tracker_create "<owner/repo>" "[Migration] <type>: <summary>" "$body_file" "migration,<priority>")"
rc=$?
rm -f "$body_file"
if [ "$rc" -eq 3 ]; then
echo "Tracker is 'none' (shape-only) — nothing was created in a tracker." >&2
echo "File this in your external system (e.g. Jira/Linear via MCP):" >&2
printf '%s\n' "$result"
exit 0
elif [ "$rc" -ne 0 ] || [ -z "$result" ]; then
echo "Migration ticket creation failed — check the tracker CLI / auth. Nothing was created." >&2
exit 1
fi
# Capture the reference + URL for the AgDR back-fill below.
ref="$(printf '%s' "$result" | jq -r '.ref')"
url="$(printf '%s' "$result" | jq -r '.url')"
The migration label is what require-migration-ticket.sh reads to gate edits
to migration files (Gate 3a). On GitHub it must already exist on the repo; on
GitLab it is auto-created when first applied.
Open the AgDR written in step 4 and update (using the ref / url captured
above):
ticket: <owner/repo>#${ref}${url}This lets a future reader land on the AgDR and find the ticket, and land on the ticket and find the AgDR.
Single-line output (using the ref / url captured above):
Migration ticket: ${url}
Migration AgDR: <relative-path-to-AgDR>
Next step: run /start-ticket <owner/repo>#${ref}, then begin editing the migration files
Do NOT automatically run /start-ticket — the user may have other context to set first, and the explicit handoff makes the workflow legible. The migration gate will block edits until the marker points at this ticket.
.claude/ or docs/ unless the migration IS against apexyard itself. For managed projects, the AgDR lives inside that project's repo (workspace/<project>/docs/agdr/), not in the ops fork.rm. The ticket is remote state. If anything errors after the ticket is created, the user has a ghost ticket to clean up; minimise that exposure./start-ticket — the migration-gate hook checks the active ticket has the migration label, so they need to declare it before touching migration files.| Hook | require-migration-ticket.sh (fires PreToolUse on Write/Edit to migration paths) |
|---|---|
| Gate 1 | Active ticket exists (same as require-active-ticket.sh) |
| Gate 2 | The active ticket on GitHub has the migration label |
| Gate 3 | The active ticket's body references an AgDR at docs/agdr/AgDR-\d+-.*migration.*\.md |
| Fail message | Points at this skill with the exact invocation to run |
The skill and the gate are two halves of the same mechanism: gate detects the situation and blocks, skill builds the artefacts needed to unblock.
Part of ApexYard — multi-project SDLC framework for Claude Code · MIT.