SKILL.md
SKILL.mdBrowse 5 files
5,478 tokens
24,341 bytes
Token encoding: o200k_base
Snapshot 646e806
1---2name: multi-version-compliance3description: >4 Apply or review multi-version support compliance for first-party Nx5 plugins. Primary entry point: a Linear task ID (NXC-XXXX) from the6 "Multi-version supported across plugins" milestone — the task carries the7 resolved support window, findings, and "Needs human decision" items. Falls8 back to self-discovery when no task exists. Use when asked to "fix9 multi-version compliance for @nx/X", "do NXC-XXXX", "review this10 compliance PR", or when working on a branch / PR titled "multi-version11 support compliance for @nx/X". Covers the canonical shape12 (assertSupportedPackageVersion, all-generators-enforce-floor.spec.ts,13 peer dep alignment, requires-gate auditing, user-pin preservation,14 executor / inferred-plugin feature gating).15argument-hint: '[<NXC-XXXX> | @nx/<plugin> | review #<PR>]'16allowed-tools: Bash, Read, Edit, Write, Glob, Grep, Agent, mcp__linear-server__get_issue, mcp__linear-server__list_comments, mcp__linear-server__get_milestone, mcp__linear-server__list_issues17---18 19# Multi-version compliance for Nx plugins20 21## What this is22 23The `nx migrate --first-party-only` flag lets users upgrade Nx without24dragging the managed third-party ecosystems (Angular, Cypress, Playwright,25Jest, Vitest, ESLint, etc.) along. For that to be safe, every first-party26plugin must keep working across its declared support window — not silently27fall through to the latest install constants on older workspaces, not28silently break on newer ones.29 30**Source-of-truth split:**31 32| Source | Owns |33| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |34| Linear milestone "Multi-version supported across plugins" (project NXC-4072) | What's wrong per plugin, the resolved support window, open human decisions. Per-plugin tasks NXC-4381..NXC-4410 (P1–P29). |35| This skill | How to implement the canonical shape, code-level anti-patterns, gotchas, findings doc shape (no-task case). |36 37The skill is the gap-closer: it accepts a Linear task, parses it, drives38the fix. When no task exists for the plugin, fix mode runs discovery in39Phase 1–2 and produces a findings doc that mirrors a Linear task body —40so the user can file it as a new task before proceeding.41 42**Reference PRs (the canonical shape):**43 44- `#35587` — `@nx/angular` — merged. Set the precedent. Introduced45 `throwForUnsupportedVersion`.46- `#35642` — `@nx/playwright` — merged. Generalized the shared helpers47 into `@nx/devkit/internal`. Established executor / runtime feature-48 gating.49- `#35670` — `@nx/cypress` — merged. Added `excludeGenerators` to the50 parameterized test helper.51- `#35671` — `@nx/vitest` — open at time of writing. Demonstrates52 "drop phantom peer-range claim" and "declared floor < effective floor"53 patterns.54 55Before citing any PR by number, verify state — these go stale:56`gh pr view <N> --repo nrwl/nx --json state`. Verify any unmerged PR's57contents via `gh pr diff <N> --repo nrwl/nx`.58 59## Entry points60 61| Invocation | Mode | Behavior |62| ----------------------------------------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |63| `multi-version-compliance <NXC-XXXX>` | Fix (primary) | Fetch task, surface findings + decisions in Phase 2, wait for user OK before Phase 3 edits. |64| `multi-version-compliance` (no arg) | Ask for task ID | Prompt for NXC-XXXX. |65| `multi-version-compliance @nx/<plugin>` (bare plugin) | Fix (task lookup) | Look up the per-plugin task in milestone NXC-4072. If found, confirm with user and enter fix mode. If not found, run discovery in Phase 1–2 (rubric against code), present findings, suggest filing as a new task before any edits. |66| `multi-version-compliance review #<N>` | Review | Fetch PR, derive Linear task from branch name if possible, compare diff vs. task findings (or run pure code-level review if no task). |67 68**Stop-after-Phase-2 (audit-equivalent):** if you want findings without69edits, decline to approve at the end of Phase 2. The skill stops, no70branch, no commits.71 72**On a branch matching `nxc-NNNN` with no explicit arg:** before73asking the user, suggest "Use NXC-NNNN?" inferred from the branch name.74 75## Linear-fetching protocol76 77Before any code-level work in Linear-driven mode, the skill MUST:78 791. **Check Linear MCP availability.** If `mcp__linear-server__get_issue`80 isn't available (MCP server not installed / not connected), tell the81 user and fall through to the no-task discovery path (fix mode Phase 182 step 2). Don't pretend to fetch.832. **Fetch the task.** `mcp__linear-server__get_issue id="NXC-XXXX"`.84 If the call errors (invalid ID, network), halt and ask the user to85 verify the ID.863. **Verify shape.** Confirm:87 - Title matches `[multi-version][P##] \`@nx/<plugin>\` — multi-version support compliance`(per-plugin) or`[multi-version][W#] ...` (cross-cutting). If the pattern doesn't match, halt and ask the user to confirm this is the right task.88 - Status. `Done` → ask whether re-audit or follow-up. `Canceled` → halt and ask.894. **Read description sections.** Every per-plugin task has:90 - **Plugin:** — path, upstream support, peerDep declarations, per-major install map, paired secondaries.91 - **Needs human decision** — open items blocking implementation.92 - **Findings** — `(high|medium|low)` items with `[file:line]` and a suggested fix per item.93 - **Verification checklist** — Sections A (Support window declarations) / B (Generator inputs) / C (Generator outputs) / D (Migrations) / E (Runtime) / F (Out-of-window UX).945. **Fetch comments.** `mcp__linear-server__list_comments issueId="..."`.95 Audits attached as files / linked uploads may carry additional96 context.976. **Surface "Needs human decision" as a batch.** Restate every decision98 item in chat. The user can resolve all, defer some, or override.99 Block until the user has acknowledged the set — don't proceed silently.1007. **Translate findings → code changes.** Map each finding to a canonical101 pattern in `references/canonical-shape.md`. The Linear task's102 suggested fix is the authoritative scope; the skill verifies it103 conforms to the canonical shape and flags any deviation.1048. **Run the A–F checklist** against the final code state. The task's105 checklist is the agreed scope. The skill verifies code-level106 conformance.107 108**Default to the task's resolved support window.** Don't re-derive it109from code unless the user explicitly overrides. If the user overrides:110restate the new window and confirm before applying.111 112**Don't expand scope beyond the task's Findings without asking.** If you113spot a new issue mid-fix: stop, present it, ask whether to (a) add it to114this PR, (b) defer as a follow-up, or (c) update the Linear task as a115comment.116 117## Mode workflows118 119### Fix mode (primary)120 121**Phase 1 — Read.**122 1231. If a Linear task ID was provided, fetch it per the Linear-fetching124 protocol. If only a plugin name was provided, look up the per-plugin125 task in milestone NXC-4072.1262. **No task case.** If no task exists for this plugin: run discovery127 instead — apply the policy ladder for the support window128 (Rule 1: upstream LTS for Angular/React/ESLint/Next/Expo; Rule 2:129 N & N-1; widen to existing supported set if larger), inventory the130 plugin's code against the A–F rubric, find the effective floor by131 walking imports, classify all results as new findings. The skill is132 producing audit-quality output for a plugin that wasn't ticketed.1333. If on a branch matching `nxc-NNNN`, read recent commits to understand134 prior scope decisions.1354. Read `references/canonical-shape.md` and `references/anti-patterns.md`.136 137**Phase 2 — Align.**138 1395. **(task case)** Surface every "Needs human decision" item from the140 task as a batch. Wait for resolutions.1416. **(task case)** Restate the Findings list with severity tags. Confirm142 scope.1437. **(no-task case)** Surface findings discovered from the rubric144 inventory + decisions the rubric surfaces (floor raise/drop, peer145 declarations, optional-vs-required peer, one-sided gates, etc.).146 Suggest filing them as a new Linear task in milestone NXC-4072147 before proceeding to Phase 3.1488. **User OK gate.** Wait for explicit "proceed" before Phase 3.149 Declining stops the skill — no branch, no edits. (This is the150 audit-equivalent.)151 152**Phase 3 — Implement** (per `canonical-shape.md`).153 1549. Branch from `master` if needed using the repo's `nxc-NNNN` convention.15510. Order: any shared-helper extension lands first; plugin changes land156 after. Commit/PR titling defers to the user's conventions.15711. For each Finding category, apply the canonical pattern:158 - Section A → peer ranges + version map + install constants. Every159 third-party package the plugin **invokes at runtime** (TS import,160 executor spawning the CLI binary, or inferred-plugin emitting a161 target with `command: '<bin>'`) gets a peer entry. Default to162 `optional: true` via `peerDependenciesMeta` for gated surfaces163 (executor opt-in, inferred plugin gated on config file presence).164 Non-optional peers are reserved for packages every workspace using165 the plugin needs.166 - Section B → generator entry asserts, `keepExistingVersions`,167 fresh-install branch.168 - Section C → templates, schema stubs with runtime throws,169 version-map coverage.170 - Section D → `requires` gates per package per AND-semantics; split171 mixed entries; retain intentional pre-floor entries. **Default to172 bilateral bounds** (`>=N <M`) for cross-major `packageJsonUpdates`173 windows. One-sided windows (`<N` with no lower, `>=N` with no174 upper) need a justified reason (legacy cleanup, undefined source,175 v0→v1 bridge) — record the reason in the findings doc or as a code176 comment. Migration entries gate on the destination instead,177 usually `>=N` alone (checklist below).178 - Section E → executor and inferred-plugin feature gates.179 - Section F → below-floor throw via shared util.180 - **Cross-cutting:** if the fix changes runtime behavior, update any181 in-codebase docs (`astro-docs/`, `docs/`, inline `.md`) that182 describe the changed behavior. Docs that contradict the code are a183 correctness bug, not a PR-body concern.18412. If during implementation you spot something not in the task's185 Findings: stop, surface it, ask whether to (a) add to this PR, (b)186 defer as a follow-up, or (c) update the Linear task as a comment.187 188**Phase 4 — Tests** (same commit as Phase 3 usually).189 19013. Add `all-generators-enforce-floor.spec.ts` — parameterized via191 `assertGeneratorsEnforceVersionFloor`. This exercises every192 generator's floor assert and is the high-value spec.19314. Footgun: assert calls must be in place in every generator BEFORE194 running the parameterized spec, or every untouched generator fails195 and you'll restart.19615. Optional: a per-plugin `assert-supported-<pkg>-version.spec.ts`197 with the 5 canonical cases. The shared `assertSupportedPackageVersion`198 already has full coverage in devkit, so this is mostly symmetry199 across the PR series — skip unless the user asks.200 201**Phase 5 — Verify locally.**202 20316. `npx nx test <plugin> --testPathPattern="all-generators-enforce-floor"`204 (add `assert-supported-` if you added the optional wrapper spec).20517. `npx nx test <plugin> --testPathPattern="<modified-generator>"` per206 touched generator.20718. `npx nx format`.208 209**Phase 6 — Hand off.** Code changes complete. The user drives210commit/push/PR per their own conventions (loaded globally from211`~/.claude/memory/workflow/git/`). This skill does not enforce PR title,212body, commit shape, or related-issues format.213 214### Review mode215 2161. **Fetch PR.** `gh pr view <N> --repo nrwl/nx` and217 `gh pr diff <N> --repo nrwl/nx`. For a local branch:218 `git diff master...HEAD`.2192. **Derive the Linear task.** Branch name `nxc-NNNN` → `NXC-NNNN`. If220 no match: ask the user.2213. **Fetch the task** (if derivable). Compare diff vs. task Findings:222 every Finding addressed; nothing extra without justification. Flag223 scope drift.224 **If no task and the user has none:** skip task-comparison; run pure225 code-level review against `canonical-shape.md` and `anti-patterns.md`.2264. **Code-level checks.** Run the "Code-level verification (review-mode227 lens)" section of `canonical-shape.md`. Cross-reference228 `anti-patterns.md`. For each finding, anchor at `file:line` and cite229 which reference PR / file demonstrates the correct pattern.230 **Scope:** code, configs, migrations, and in-codebase docs that claim231 runtime behavior. NOT PR title / body / commit shape — those defer to232 the user's PR conventions.2335. **Classify each finding.**234 - **Only two inline categories:** `[blocker]` and `[non-blocker]`. No235 "open question," "ask," or other inline tags. Questions for the236 author surface in the closing "Open questions for author" block,237 drawn from non-blocker findings — list each question once.238 - **Severity is independent of scope-drift.** A finding can be both a239 blocker AND not in the Linear task. Flag it as a blocker in the240 code-level section AND list it under "in PR but not in Linear task"241 in scope drift. Don't hedge with "in this PR or follow-up?" — if242 it's a blocker, the answer is "this PR."243 - **Group related non-blockers.** When multiple non-blockers describe244 symptoms of one blocker (e.g., five symptoms of a single245 `version-utils.ts` duplication), list them as sub-bullets under246 the blocker with "(resolved when §X is fixed)" rather than as N247 separate top-level non-blockers.248 - **Be terse on passes.** A section with no findings gets a single249 summary line ("Pass — all 7 generator entries assert at first250 statement"), not a per-file enumeration. Detail is reserved for251 blockers and non-blockers. The reviewer's audience skims for252 actionable items; passing checks should not eat reading budget.2536. **Output.** Markdown checklist of blockers / non-blockers anchored at254 `file:line`, followed by the structured verdict block from255 `canonical-shape.md` §"Verdict template". The verdict block is the256 skimmable index — produce it, don't substitute a free-form prose257 summary. Do not post via `gh pr review` unless the user explicitly258 asks.259 260## Which references to load (context hygiene)261 262| Mode | Required | Optional |263| ---------------------------- | ------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |264| Fix | `canonical-shape.md`, `anti-patterns.md` | `gotchas.md` (effective floor, ecosystem lockstep, cypress inline tree), `examples.md` (when copying a pattern) |265| Review | `anti-patterns.md`, `canonical-shape.md` (especially the "Code-level verification" section) | `gotchas.md` (cross-plugin coordination, lockstep), `examples.md` (when citing) |266| "What is compliance?" answer | none | answer from SKILL.md alone |267 268References are ~100–500 lines each. Don't pull all of them just because269you're invoked. Match the load to the mode.270 271## Critical rules (apply in every mode)272 2731. **Linear task is the source of truth for scope** (ratified decisions274 and Findings).275 - (a) Don't produce a parallel scope document. The task IS the scope.276 Fix mode runs against the task as input — drift checks, new277 findings, and decisions feed back to the task (via comments or as278 deferred items), not into a competing source of truth.279 - (b) Don't expand a fix beyond the task's Findings without280 surfacing the new issue.281 - (c) Don't second-guess the task's resolved support window without282 an explicit user override.2832. **Do not create or duplicate shared helpers.** They live in284 `@nx/devkit/internal` (`assertSupportedPackageVersion`,285 `getInstalledPackageVersion`, `getDeclaredPackageVersion`,286 `throwForUnsupportedVersion`, `normalizeSemver`, `isNonSemverDistTag`)287 and `@nx/devkit/internal-testing-utils`288 (`assertGeneratorsEnforceVersionFloor`). Reject any local289 re-implementation (`cleanVersion`, `getInstalled<Pkg>VersionRuntime`,290 private `throwBelowFloor`, etc.). See `canonical-shape.md`.2913. **Above-ceiling is silent fallthrough.** Do not warn, do not throw,292 do not branch. Reject `throwAboveWindow`, `warnAboveCeiling`,293 `versions()` with `switch + throw default:`. The only throw is below294 the declared floor.2954. **`keepExistingVersions: true` is for generators only.** Migration296 generators (`src/migrations/`) are exempt — their job is to bump.297 Do not flag missing flags in migration code.2985. **Floor assert is the first statement in the function doing the299 actual work.** Wrapper/internal split (cypress, playwright): in300 `*Internal`. Single-function generators (angular): in the function301 itself. Not conditional, not inside an install branch, not after a302 tree read.3036. **Phase 1–2 never writes, never branches.** Discovery, finding304 classification, and decision-surfacing happen on the current branch305 with no edits. Any working artifact (e.g., a findings doc for a306 no-task case, multi-plugin scratch notes) goes in `tmp/` (gitignored)307 and stays uncommitted. No `TRIAGE-REPORT.md` / `AUDIT.md` at repo308 root. Branch creation and edits start at Phase 3, after the user OK.3097. **PR / commit conventions are out of scope.** Title format, body shape,310 commit-message structure, related-issues handling, push flags, etc.311 are governed by the user's global memory (`pr-creation-shorthand.md`,312 `push-conventions.md`, `explain-before-committing.md`,313 `chore-not-fix-non-prod.md`). Don't enforce or flag these from this314 skill — defer to whatever the user's conventions resolve to at PR time.315 316## Findings doc template (Phase 2 output, used when no Linear task exists)317 318When fix mode hits the no-task case (Phase 1 step 2), produce319`tmp/<plugin>-findings.md` shaped to mirror a Linear task body so the320user can file it as a new task in milestone NXC-4072 before proceeding321to Phase 3.322 323For plugins managing multiple primary packages, repeat the install-map324/ decisions / findings bullets per primary.325 326```md327# @nx/<plugin> — multi-version support compliance findings328 329> No Linear task in milestone NXC-4072. This doc is filing-ready —330> create the task with this body before proceeding to fix.331 332## Plugin333 334- Path: packages/<plugin>335- Upstream support: <official policy if any, else "no formal LTS">336- peerDep declarations: <list>337- Per-major install (`<file>` branches on installed `<package>` major):338 - v<N-1>: <constants>339 - v<N>: <constants> (default)340- Paired secondaries: <list of ecosystem-locked siblings>341 342## Needs human decision343 3441. <decision 1 — e.g., raise floor to vN.0.0 vs keep current>3452. <decision 2 — e.g., drop ^1.0.0 from peer (no v1 install lane)>346 347## Findings348 349- **(high) <one-line summary>** [file:line]350 _Suggested fix_: <one-line>351- **(medium) ...**352- **(low) ...**353 354## Verification checklist (A–F)355 356### A. Support window declarations357 358- [ ] peerDep ranges match the support window359- [ ] Version map / runtime branching covers every supported major360- [ ] Every third-party package the plugin **invokes at runtime** has a peerDep entry. "Invokes" = TS import/`require` OR executor spawns its CLI binary OR inferred plugin emits a target whose `command` invokes its CLI (look for `externalDependencies: ['<pkg>']` in emitted target inputs). Packages the generator installs for the user to consume independently (ESLint plugins loaded by the user's eslintrc, `@types/*`) don't need peer-declaration.361- [ ] Peers needed only when a user opts into a specific surface (executor opt-in, inferred plugin gated on config file presence, opt-in preset) are declared **optional** via `peerDependenciesMeta: { "<pkg>": { "optional": true } }`. Required-non-optional peers are reserved for packages every workspace using the plugin needs.362 363### B. Generator inputs364 365- [ ] Generators don't overwrite installed third-party versions366 - [ ] `addDependenciesToPackageJson` passes `keepExistingVersions=true` or branches on detected version367- [ ] Fresh-install path installs the latest supported version368 369### C. Generator outputs370 371- [ ] Templates compile and run on every supported version372- [ ] Generated `project.json` target shape valid on every major373- [ ] Default option values valid on every major374- [ ] Version map covers every managed third-party dep — no gaps375- [ ] Schema accepts union of options; runtime throws when inapplicable376 377### D. Migrations (migrations.json + packageJsonUpdates)378 379- [ ] Cross-major `packageJsonUpdates` declare `requires` per bumped package380- [ ] `packageJsonUpdates` `requires` windows are bilateral (`>=N <M`) by default. One-sided ranges (`<N` with no lower, `>=N` with no upper) are intentional (legacy cleanup, undefined source major, v0→v1 bridge) — flagged in "Needs human decision" or noted in the Findings.381- [ ] Migration entries gate on the destination: `requires` evaluates once at collection time against the version the package lands on in this run (installed only when the run does not bump it), so a bound meant as the source window (`>=9 <10` for "migrating from 9") skips whenever the run bumps past the cap (the storybook bug, #33613). Default is `>=N` alone; add an upper bound only when the migration is inapplicable at or above it (`next >=15.0.0 <16.0.0` on the next-15 instructions entry). Semantics: `.claude/skills/author-migration/SKILL.md`, `requires` section.382- [ ] A migration declares a gate only when its behavior depends on the touched package's version; conditions `requires` cannot express (an OR of alternative package names) get an in-body check instead383- [ ] Nx-only migrations have no third-party `requires`384- [ ] No silent gap in `packageJsonUpdates` across the support window385 386### E. Runtime387 388- [ ] Executors branch on installed version where behavior diverges389- [ ] Inferred plugin (createNodes/V2) parses configs across every major390 391### F. Out-of-window UX392 393- [ ] Below-floor: throws via shared util naming package + installed + floor; no silent fall-through394 395## Out-of-scope (deferred follow-ups)396 397- <e.g., consolidate ... across plugins — separate PR>398```399 400## References401 402See "Which references to load" near the top. Don't pull all of them.403 Discovery context
Discovered by repository scan. No exact path reference found in the snapshot’s root AGENTS.md.