Skip to content

chore(repo): add author-migration skill - #36413

Merged
FrozenPandaz merged 17 commits into
masterfrom
nxc-4618
Jul 24, 2026
Merged

chore(repo): add author-migration skill#36413
FrozenPandaz merged 17 commits into
masterfrom
nxc-4618

Conversation

@leosvelperez

@leosvelperez leosvelperez commented Jul 20, 2026

Copy link
Copy Markdown
Member

Current Behavior

There is no repo guidance for authoring migrations. Each new migration is written by copying whatever sibling looks closest, which reproduces stale patterns (wrong dist path shapes, backdated versions, missed packageJsonUpdates groups) and misses conventions that only live in reviewers' heads.

Expected Behavior

A Claude Code skill at .claude/skills/author-migration/ covers the authoring flow end to end: decomposing a change into migration needs, version and requires gating, scaffolding, implementation canon (codemods, config edits, dependency updates, prompt and hybrid migrations), spec requirements, docs, and a pre-PR checklist. Companion files document the nx migrate runtime contract, deprecated patterns with recognition signatures, and entry/spec/doc templates.

The guidance was validated through scenario testing: child agents authored migrations at the parent commits of 16 shipped migration PRs (plus a no-skill control round), their output was graded against what actually shipped, and the skill was amended after each round.


View session information ↗

@leosvelperez leosvelperez self-assigned this Jul 20, 2026
@nx-cloud

nx-cloud Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit 247372e

Command Status Duration Result
nx run-many -t check-imports check-lock-files c... ✅ Succeeded 3s View ↗
nx affected --targets=lint,test,build,e2e,e2e-c... ✅ Succeeded <1s View ↗
nx-cloud record -- pnpm nx-cloud conformance:check ✅ Succeeded 56s View ↗
nx build workspace-plugin ✅ Succeeded <1s View ↗
nx-cloud record -- nx sync:check ✅ Succeeded 18s View ↗
nx-cloud record -- nx format:check ✅ Succeeded 6s View ↗

☁️ Nx Cloud last updated this comment at 2026-07-23 08:57:25 UTC

@netlify

netlify Bot commented Jul 20, 2026

Copy link
Copy Markdown

Deploy Preview for nx-docs ready!

Name Link
🔨 Latest commit 247372e
🔍 Latest deploy log https://app.netlify.com/projects/nx-docs/deploys/6a61d5e78f6232000915bec5
😎 Deploy Preview https://deploy-preview-36413--nx-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Jul 20, 2026

Copy link
Copy Markdown

Deploy Preview for nx-dev ready!

Name Link
🔨 Latest commit 247372e
🔍 Latest deploy log https://app.netlify.com/projects/nx-dev/deploys/6a61d5e7171ea80008b764cb
😎 Deploy Preview https://deploy-preview-36413--nx-dev.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Authoring a first-party migration involves conventions that are easy to
get wrong and expensive to discover from source each time: entry naming
and versioning, the migrations.json runtime contract (version gating,
requires, implementation resolution, prompt/documentation handling),
packageJsonUpdates group semantics, return-value channels, idempotency
requirements, and the colocated file set including agentic prompts.

The skill encodes those conventions with pointers to the source of truth
and live exemplars, plus templates for the migrations.json entry, spec
file, agentic prompt runbook, and documentation file, and a pre-PR
checklist. It triggers when writing or editing migrations,
packageJsonUpdates entries, migration prompts or docs, porting upstream
framework migrations, or deciding whether a breaking change needs a
migration.
…sting

The skill was exercised against four shipped-migration scenarios (swc-cli
bump, typescript-eslint v8 rules, Angular 22 buildable workspace, jest
verify-typecheck), re-run after amendments, and compared against no-skill
control runs in history-isolated clones. The gaps that surfaced are folded
back into the guidance:

- requires cannot express an OR of package names; recognize umbrella vs
  scoped installs and gate in code (paired packageJsonUpdates groups).
- Preserve jsonc comments with jsonc-parser instead of readJson/updateJson,
  and wire new runtime deps into package.json and ng-package.json.
- Edit only files that locally declare an inherited setting; never mutate
  shared bases from one consumer's resolution.
- Install new deps at the generators' versions-constant, not the workspace's
  installed version.
- Match backtick-quoted object keys in codemods.
- Mandatory spec fixtures: adjacent multi-edit offsets and
  local-overrides-inherited precedence.
- Re-check .md claims against the final implementation and verify
  tool-behavior claims from source (also a checklist item).
- Build tree paths with joinPathFragments/posix helpers.
- Bump the repo's own pin when a migration bumps a package the repo uses.
- Handle every extends form the host resolver accepts (string, array,
  package specifiers, cycles).
- Scope discovery by project graph only for executor-derived file sets;
  scan the tree for name-matched user configs.
- Prompt runbooks must cover the bare no-options form.
A source-check of the skill against the migrate runtime and the cited
migrations surfaced two exemplar citations that contradict the rules they
illustrate and two runtime-contract claims that drift from the
implementation:

- annotate the vite dual-world exemplar: it returns a GeneratorCallback and
  uses a bare entry key, both of which the canon forbids; copy only its scan
- swap the storybook version-admission exemplar from the 23.1.0 group (a
  within-major ceiling raise) to 22.1.0 (a true major admission)
- state the real install cadence: once after the run by default, per
  migration only under --create-commits and agentic runs
- attribute runner selection to the run-time re-read of the unmerged
  migrations.json, not the fetch-phase fold
…nario testing

Four new test scenarios surfaced gaps the previous rounds missed:

- documentation .md is required on every entry, prompt-only migrations
  included; a prompt .md never doubles as documentation
- compatibility claims come from published package metadata
  (peerDependencies, engines), not upstream prose
- codemods must enumerate hand-written authoring shapes and classify
  files by resolved import bindings, not substring matches
- handles/covers claims in .md files need a backing spec case
- requires bounds asymmetry explained via the collection-time mechanism:
  entries gate destination-only (>=N), groups always carry both bounds
… skill

The skill called the deterministic half of a migration a codemod, a term the Nx docs and the migrate source never use for it: the documented vocabulary is migration generator, with generator-only, prompt-only, and hybrid as the kinds, and codemod in the repo refers to external npx-runnable tools.

Rename accordingly across SKILL.md, the templates, and the runtime contract. Codemod stays only for upstream tools (the Run upstream codemod treatment and the runbook rule), the old Codemod table row and section become Source transform (the kind names would over-claim there, since config edits and conditional dep changes are also migration generators), and one gloss binds the general-usage term to the documented one.
…t rules

Round-7 validation runs (jest, maven, nest, devkit scenarios) surfaced gaps
the skill text either caused or failed to prevent:

- forbid inferring a packageJsonUpdates group's requires posture from sibling
  groups; re-derive per admission (the ungated rspack 21.4.0 group next to its
  gated react twin is the counterexample)
- relax the entry-key canon to a soft convention: keys stay unique and
  slug-bearing but follow the file's dominant form; document that JSON.parse
  silently drops the earlier of two duplicate keys and that a version part in
  the key is a release-level hint, not tied to the version field
- tighten the version ask-step: a re-stamped version is still a train choice,
  and a run counts as non-interactive only when there is no channel to ask
- add migration-homing guidance and the collection-scope contract (installed
  versions resolve by node resolution from the workspace root)
- require materialized option values to land where the tool reads them, never
  as undeclared target options riding executor passthrough
- add two mandatory spec rules: a drift check for frozen copies of another
  module's canonical set, and case-by-case coverage when a migration
  statically replicates deleted runtime behavior
…view

A ten-reviewer audit of the skill (correctness against the migrate runtime,
internal coherence, published skill-authoring guidance, token efficiency,
steering quality) surfaced a set of verified issues:

- fix three stale or wrong facts: assertValidMigrationPaths now has a
  file-granular orphan check (only .md files and helpers escape it), the
  generated migrations file carries the full entry rather than package+name,
  and devkit glob is deprecated in place, not deleted
- close checklist and template drift: the pre-PR checklist now covers all
  seven mandatory spec cases, first-migration wiring, and the shipped-fix
  version re-stamp; the spec skeleton points at the single mandatory-case
  list instead of restating a subset; the prompt-only entry template gains
  the documentation key it was contradicting the canon by omitting
- state where entries live (the generators section) in SKILL.md and the
  template, and show the full new-file shape
- widen the treatment table's Nothing row to match the prose, add the
  ported-vs-source-transform tiebreaker, cover the next==latest train case,
  and give the between-branches classification gap a default
- sharpen undecidable wording (inferred-plugin scan trigger, generator-schema
  check for keepExistingVersions/skipFormat, ifPackageInstalled as a live but
  unused runtime gate) and align terminology (packageJsonUpdates group,
  target train)
- trim restated facts (landing-version semantics, tree-util import note,
  cli/schema bullet) and rephrase the rspack ungated-group citation as a
  historical fact so an upstream fix does not falsify it
…r-migration skill

The runtime contract missed the agentic validation flow: generator-only
migrations that produce changes get an agent validation step that receives
the captured generator output and any returned agentContext, and may apply
minor in-scope fixes. The skill now steers every migration with skipped or
residual work to return { nextSteps, agentContext }, not just hybrids, and
documents that nextSteps never reaches the agent while agentContext also
surfaces on stdout when running inside an outer agent.

Prompt-only entries get no spec file (nothing imports a prompt .md and no
harness exercises prompt content); the spec canon, checklist, layout, and
spec skeleton now say so. Every generators entry ships a documentation .md
set on its documentation key, and the docs-site wording follows the
key-driven rendering (nothing inferred from the implementation basename),
which also retires the docs-site-leak rationale for distinct prompt
filenames in favor of the documentation file owning the implementation's
basename.

Also corrects the overbroad claim that path validation misses any wrong
dist shape (it fails shapes whose stripped path misses the source tree)
and notes that prompt-runbook exemplars vary their heading names.
…migration skill

The skill restated checks the repo validators enforce: path resolution,
orphaned files, duplicate entry keys, the published dist shape, and
packageGroup membership. Section 5 now instructs running the validators
(plugin test/lint plus the conformance check) and the pre-PR checklist
keeps only the judgment items no validator covers, such as whether a
path names the right file, train and `requires` semantics, spec
coverage, and .md claim accuracy.

Target-train arithmetic moves from prose into
scripts/compute-target-versions.mjs, anchored on `npm view nx dist-tags`
alone; the git tag listing added nothing the dist-tags do not carry. The
script prints each candidate train with the recommended default marked
and guards the mid-promotion window where a stable `next` sits above
`latest`.

The local-registry walkthrough now includes the `npm adduser` login step
from CONTRIBUTING.md, and the `nx migrate` invocations run through
`npx`.
…gration skill

The spec skeleton's idempotency example had no arrange step, so both
reads returned null and the comparison passed for any non-idempotent
migration; the negative test used an undeclared variable and the
positive test carried only commented-out assertions. All three tests now
run as written: a shared arrange writes the trigger shape, the negative
test declares its fixture, and the idempotency test anchors on a
non-null read before comparing runs.

compute-target-versions.mjs now fails closed with the hand-compute
message on npm failures, non-JSON or null payloads, missing tags, and a
prerelease latest (which previously produced a backdated major option),
with a 30s timeout on the registry call. The comment claiming semver
resolves from the working directory was wrong (ESM resolves from the
script's own location) and is corrected along with the SKILL.md clause.

Guidance corrections: the fail-open canon distinguishes unparseable
files (skip and report in agentContext) from unrecognized ones (skip
silently) and the malformed-input spec case asserts the report; three
cited exemplars get caveats for their utils/versions imports; section 3
states entry values use the published dist-prefixed shape; the
agentContext note matches the runtime contract's outer-agent behavior;
section 5 cites the migration-groups conformance rule; the frontmatter
declares allowed-tools like the sibling skills.
The multi-version-compliance skill prescribed bilateral (>=N <M)
requires ranges at the migration-entry level and condemned all in-body
version guards. Both are wrong against the runner: entry gates evaluate
once at collection time against the version the package lands on in the
same run (installed only as fallback), so a bound encoding the source
window skips exactly when the run bumps past the cap (the shipped
storybook skip bug), and an OR of alternative package names cannot be
expressed in requires at all (the shipped eslint fix moved that check
into the migration body).

Scope the bilateral default to packageJsonUpdates windows, prescribe
destination gating for migration entries (lower bound alone unless the
migration is inapplicable at or above a cap, like the next-15
instructions entry), carve the inexpressible-OR case out of the in-body
anti-pattern, and refresh the stale reference to the pruned
update-typescript-eslint-v8.13.0 entry. The author-migration skill's own
"lower bound only" rule gets the same upper-bound nuance, and the two
skills now cross-link.
…tion skill

The migrations-json template opened with "replace the placeholders" while
its JSON blocks use concrete example values and no placeholders at all;
the intro now states that values are illustrative and the key sets and
path shapes are the contract. alwaysAddToPackageJson appeared in both
packageJsonUpdates examples unexplained, and the documentation template
never said the sample-code-changes section can be omitted for changes
with no code shape.

Migration documentation renders on nx.dev but sits outside every docs
validation: vale's scope is astro-docs/src/content only and the site
synthesizes migration pages from the colocated .md files at build time
without linting them (a shipped doc already fails the Nx.Headings
sentence-case rule). The documentation template's own Sample Code
Changes heading taught that violation; it is now sentence case, and the
template and section 6 point at the docs style guide with a note that
vale does not reach these files today.
@leosvelperez
leosvelperez marked this pull request as ready for review July 23, 2026 07:41
@leosvelperez
leosvelperez requested a review from a team as a code owner July 23, 2026 07:41
@leosvelperez
leosvelperez requested a review from JamesHenry July 23, 2026 07:41
…ngs in author-migration skill

The pre-PR checklist required "no upper bound on the entry's own gate",
contradicting the section 2 rule that permits one when the migration is
inapplicable at or above the cap (the next-15 instructions gate is a valid
instance). The checklist item now names the source-window case as the defect
and defers to section 2 for the valid exception.

The packageJsonUpdates ladder rules only covered rungs shipped together in
oldest-source-first key order. A rung backfilled at a later nx version sits
after the existing upper rungs, so its cohort lands on the intermediate major
with the upper groups permanently behind it. Document the choice between
re-offering the upper rungs (with landing-gated companion entries re-stamped)
and deliberately holding the cohort.
@FrozenPandaz
FrozenPandaz merged commit 205a32b into master Jul 24, 2026
25 checks passed
@FrozenPandaz
FrozenPandaz deleted the nxc-4618 branch July 24, 2026 15:31
FrozenPandaz pushed a commit that referenced this pull request Jul 29, 2026
## Current Behavior

There is no repo guidance for authoring migrations. Each new migration
is written by copying whatever sibling looks closest, which reproduces
stale patterns (wrong dist path shapes, backdated versions, missed
packageJsonUpdates groups) and misses conventions that only live in
reviewers' heads.

## Expected Behavior

A Claude Code skill at `.claude/skills/author-migration/` covers the
authoring flow end to end: decomposing a change into migration needs,
version and `requires` gating, scaffolding, implementation canon
(codemods, config edits, dependency updates, prompt and hybrid
migrations), spec requirements, docs, and a pre-PR checklist. Companion
files document the `nx migrate` runtime contract, deprecated patterns
with recognition signatures, and entry/spec/doc templates.

The guidance was validated through scenario testing: child agents
authored migrations at the parent commits of 16 shipped migration PRs
(plus a no-skill control round), their output was graded against what
actually shipped, and the skill was amended after each round.

<!-- polygraph-session-start -->
---
[View session information
↗](https://app.trypolygraph.com/orgs/6a061dcb561c062131116eca/sessions/nxc-4618-98faa68a)
<!-- polygraph-session-end -->

(cherry picked from commit 205a32b)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants