--- name: migration-skill-authoring description: This skill should be used when the user asks to create a new Montage migration skill for the montage-web-migration plugin (e.g. a future v4-to-v5), add or update content in an existing one (e.g. montage-v3-to-v4) after new breaking changes or codemods land, validate a migration skill, or says "마이그레이션 스킬 만들어줘", "마이그레이션 스킬 업데이트해줘", "v3-to-v4 스킬에 내용 추가해줘", "마이그레이션 스킬 검증해줘", "create/update/validate the migration skill". The pipeline (codemod analysis, ordering/idempotency derivation, authoring conventions, adversarial validation) is Workflow-orchestrated. --- # migration-skill-authoring Author or update consumer-facing migration skills in `.claude-plugin/montage-migration/skills/`. The pipeline: **analyze the codemods with a Workflow (never trust MIGRATION.md prose alone) → derive ordering and run-once constraints from source facts → author the four-file deliverable per convention → validate with a Workflow, fix, and re-run until clean**. `skills/montage-v3-to-v4/` is the reference implementation; all conventions live in `references/authoring-conventions.md`. ## Core principles 1. **Idempotency and ordering come from transform SOURCE, not docs.** The one migration fact that matters most — "does re-running corrupt code?" — is a rename-chain property of the transform (a rename VALUE appearing as another rename KEY). It must be traced in code, with file:line evidence. 2. **Codemods run strictly in sequence, each exactly once per tree.** Every skill encodes this with a state file, per-step verification, and defense-in-depth warnings for any non-idempotent transform. 3. **Every factual claim ships validated.** Grep patterns are empirically executed (macOS BSD grep included), rename tables are diffed against the transform maps, and consistency surfaces are cross-checked — by adversarial agents, before the skill ships. ## Mode A — Create a new version skill ### Prerequisites - The MIGRATION.md section for the target version is written. - The codemods exist in `packages/codemod/src/transforms//` and are registered in `MIGRATION_TRANSFORMS` in `packages/codemod/src/constants.ts`. If either is missing, stop and surface it — the skill is authored FROM these, never ahead of them. ### Step 1 — Analyze the codemods Enumerate the version's transforms from `constants.ts`, then run the analysis Workflow: ```js Workflow({ scriptPath: "/scripts/analyze-codemods.js", args: { repoRoot: "", migrationSection: "## .0.0", transforms: [ { key: "", files: "", extra: "" }, ... ] } }) ``` Resolve `` from the "Base directory for this skill" line announced when this skill loaded. If the result's `missing` array is non-empty (or `docs` is null when the docs agent was not skipped), re-run the analysis for those transforms before Step 2 — never derive ordering or idempotency from partial analysis. Write a pointed `extra` per transform: rename-chain suspects (any A→B where B is also renamed), map-file interactions with other transforms' outputs, whether it touches attribute names vs values, string literals vs identifiers. ### Step 2 — Derive the design from the analysis - **Canonical order**: MIGRATION.md section order, adjusted only by hard constraints the analysis surfaced. Record every constraint WITH its reason. - **Run-once machinery**: flag every non-idempotent transform (and every hand-migration-collision hazard) for defense-in-depth treatment — see the consistency surfaces table in `references/authoring-conventions.md`. - **Manual steps (M-sections)**: everything the codemods cannot reach, each with scan patterns and an unambiguous owner (exactly one phase fixes it; safety-net scans say so). ### Step 3 — Author the deliverable Follow `references/authoring-conventions.md` exactly: the four files (SKILL.md, `references/codemod-steps.md`, `references/manual-migrations.md`, `scripts/migration-workflow.js`), the state-file spec, the workflow script conventions, the writing conventions, and the grep pattern rules. Copy the montage-v3-to-v4 structure and adapt content — do not invent a new shape. ### Step 4 — Validate until clean ```js Workflow({ scriptPath: "/scripts/validate-migration-skill.js", args: { repoRoot: '', skillDir: '', migrationSection: '## .0.0', transformsDir: '', scope: 'full', // Mode A authors a whole skill, so everything is in scope. Mode B passes // 'delta' (the default) — see "Scoping the review" below. // optional — defaults derived from skillDir: // pluginRoot: '', // knownIssuesFile: '/known-issues.md', }, }); ``` **`repoRoot` must be the checkout you are editing.** When the work happens in a git worktree, that is the worktree path — the parent checkout holds an older copy of the same files at the same relative path, and a reviewer that reads it produces confident, entirely false findings (one run yielded three false `critical`s that way). The script pins the reviewers to absolute paths and makes each finding carry the `wc -l` of every file it cites; the verify phase re-runs `wc -l` and refutes on mismatch. That guard only works if the paths you pass are the ones you edited. The run returns **verified** findings, not raw reviewer output: four reviewers fan out, then per-file verifiers dedup across reviewers, re-rate severity, and REFUTE anything they cannot reproduce. Read the result accordingly: - `findings` — CONFIRMED only, already deduped and re-rated. This is the worklist. - `refuted` — dropped claims with the reason (`stale-file-read`, `accepted-trade-off`, or the observation that killed it). Skim it: a wrongly refuted finding is possible, and the reasons tell you whether a reviewer was reading the wrong tree. - `rawCount` vs `findings.length` — the gap is how much noise the verify phase absorbed. A large gap with many `stale-file-read` reasons means `repoRoot` was wrong. - `unassessedGroups` — nonzero means a verifier died and its findings were never judged; re-run before trusting a clean result. - `preExisting` — CONFIRMED `critical` findings the change neither introduced nor invalidated (empty under `scope: 'full'`, where everything is in `findings`). They do NOT block convergence, but they are corruption paths: fix them or add a `known-issues.md` entry deliberately, and tell the user which you chose. - `clean` — no CONFIRMED in-scope `critical`/`major`, all four reviewers returned, nothing unassessed. `preExisting` is excluded by design; a pre-existing corruption path would otherwise make every future change review unconvergeable. - `scope` / `diffBase` / `changedFiles` — the scope the run ACTUALLY used, which can differ from what was requested: an empty diff falls back to `scope: 'full'` (with `diffBase` and `changedFiles` returning `null`). Check these before reading a delta run as clean — a `'full'` here when you passed `'delta'` means the diff base was wrong, not that the change reviewed clean. #### Scoping the review `scope: 'delta'` (the default) validates **the change**, not the whole package. A Scope phase runs `git diff --unified=0 ` (default `HEAD`; pass the base branch when the change is already committed) and hands the reviewers the changed line ranges. They still READ every file — consistency checking is impossible otherwise — but may only report a finding that is anchored in a changed region, is unchanged text the change made wrong (`blast-radius`: a now-stale cross-reference, count, or duplicated statement — the highest-value category in update mode), or is `critical`. The verify phase re-checks the scope itself and refutes out-of-scope non-criticals with reason `out-of-scope`. The `structure` reviewer is exempt: its checks are mechanical and actionable wherever they fail. Use `scope: 'full'` for Mode A, and for a deliberate periodic audit of an existing skill. Do NOT use it for a routine Mode B update: a full audit re-surfaces the entire pre-existing backlog every round, which buries the change's own defects and prevents convergence (one M-section addition produced 21 confirmed findings across two rounds, none of them in the new section). When the diff comes back empty the run logs it and falls back to a full audit — check the `diffBase` you passed rather than trusting that result as a clean change review. Record deliberate trade-offs (a version bump the user deferred, a length budget knowingly exceeded) in `/known-issues.md`, one short entry each. Reviewers and verifiers read it and stop re-reporting them, which is what keeps successive runs comparable. Fix every `critical`/`major` finding, then RE-RUN the validation Workflow — a real fix can introduce a new real defect, so `critical`/`major` always warrants another pass. **Stop re-running once a round returns no `critical`/`major` and its `minor` findings are only heuristic refinement.** The validation reviewers are adversarial and will almost always surface _something_; `clean: true` is the ideal, not a required terminal state. Each round is expensive (four large sub-agents), so converge deliberately: - Fix `minor` findings that are genuine defects (a wrong rename-table entry, a broken grep that silently finds nothing, a state-schema contradiction, a claim that contradicts the transform source) — these are cheap and worth another pass with the `critical`/`major` fixes. - Do NOT loop for `minor` findings that just make an already-hedged heuristic incrementally tighter — "this scan pattern also misses `'wds-' + kind`", "widen this regex to also match `props.theme`", etc. The scan patterns are declared line-based heuristics ("a clean scan is 'nothing obvious', not proof of absence"); chasing every regex edge case with more regex contradicts that framing and never terminates. Apply the ones you judge worthwhile in a single final edit and do not re-validate solely to confirm them. - A `minor` you are deliberately not acting on (e.g. a version bump the user deferred) belongs in `known-issues.md` plus a one-line note in your report, not another round. Practically: expect ONE re-run after the first substantive fix pass, occasionally two if that pass surfaced new `critical`/`major`. If a round yields only heuristic-refinement `minor`s, you are done — stop and report. The structure reviewer already runs the mechanical checks (workflow-script syntax wrap + `node --check`, prettier); run them manually (procedure in the conventions doc) after your final edits instead of triggering a whole validation round just to confirm formatting. ### Step 5 — Ship Update the plugin `README.md`/`README.ko.md` skill listing, bump `.claude-plugin/montage-migration/.claude-plugin/plugin.json` (minor) and `.claude-plugin/marketplace.json`. Suggest a trigger test: `claude --plugin-dir .claude-plugin/montage-migration`. ## Mode B — Update an existing migration skill For new breaking changes, a new codemod, or corrections landing in a version that already has a skill: 1. **Impact analysis first.** Read the consistency surfaces table in `references/authoring-conventions.md` — a new step or M-section touches EVERY listed surface (SKILL.md, both references, the workflow script's `CODEMOD_STEPS` / `MANUAL_SCAN_SECTIONS` / `STATE_FILE_TEMPLATE`, both READMEs). Partial updates are how the files start contradicting each other. 2. **Analyze only what changed** — run `scripts/analyze-codemods.js` with just the new or modified transforms (`skipDocsAgent: true` if the CLI is untouched). A changed transform's idempotency must be re-traced from source, not assumed stable. For a step inserted anywhere but last, the `extra` MUST also ask: "does this transform behave correctly on code already migrated by the steps that canonically follow it?" — consumers who completed later steps will run it on a post-migration tree. 3. **Respect state-schema stability.** Consumers may be mid-migration with a live state file: never rename existing step keys or M-numbers (renamed transforms are the one exception — see the conventions doc); add new steps at the position the constraint analysis dictates with value `pending`. Then encode the semantics INTO the consumer deliverable, not just here: the consumer SKILL.md's Step 0 resume item and the workflow step-agent prompt must both state that a step key missing from an older state file is `pending` (so resuming consumers pick up the new step), and if the new step lands before steps consumers may have completed, the resume item must say the new step still runs and what to verify first. Add the new step's post-step verification grep to the consumer skill's "already migrated" preflight path and final verification, so consumers who FINISHED the migration get flagged when they invoke the skill again. 4. **Run the validation Workflow with `scope: 'delta'`** (Step 4 above, and its "Scoping the review" subsection). Delta scope constrains what reviewers may REPORT, never what they READ — they still load the whole package, because the consistency reviewer exists precisely for update-mode drift (renumbering a step leaves stale cross-references that reading the diff alone would never reveal). That drift is the `blast-radius` category and it survives scoping by design; what scoping drops is the pre-existing backlog that has nothing to do with your change. Apply Step 4's convergence rule: re-run after each `critical`/`major` fix pass, but stop once a round returns only heuristic-refinement `minor`s — do not loop toward `clean: true`. Reach for `scope: 'full'` only when you deliberately want a whole-package audit, and expect it not to converge on the change: budget it as separate work. 5. **Bump versions** (plugin.json minor for new content, patch for corrections) and update READMEs if behavior changed. ## Additional resources - **`references/authoring-conventions.md`** — the deliverable structure, consistency surfaces, sequencing/run-once design, workflow-script and writing conventions, verified CLI facts, grep pattern rules, mechanical validation. - **`scripts/analyze-codemods.js`** — analysis Workflow (per-transform facts + CLI/docs). - **`scripts/validate-migration-skill.js`** — validation Workflow (fact-check, consistency, quality, structure); re-run until `clean: true`.