Browse Source

merge: fix/drift-refresh — refresh 4 skills to current majors (verifier drift)

typescript-ops (TS 6 / Zod 4 / Valibot 1), vue-ops (Nuxt 4), mcp-ops
(fastmcp 3 + spec URL move), migrate-ops (Laravel 13, Python 3.14,
Node 26, TS 6, Go 1.26, PHP 8.5). All staleness verifiers green in
--offline and --live on 2026-07-05.

Re-run scripts/install.ps1 so the global ~/.claude copy picks these up.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
0xDarkMatter 3 weeks ago
parent
commit
d67c862b19

+ 24 - 2
skills/mcp-ops/SKILL.md

@@ -12,7 +12,7 @@ metadata:
 
 Comprehensive patterns for building, testing, and deploying Model Context Protocol servers in Python and TypeScript.
 
-> Ecosystem facts verified as of 2026-07.
+> Ecosystem facts verified as of 2026-07-05 (standalone FastMCP at major 3).
 
 ## MCP Architecture Quick Reference
 
@@ -142,6 +142,28 @@ uv add mcp[cli]
 # Or:       uv run mcp run server.py
 ```
 
+**Two Python FastMCPs — know which you're on.** The official `mcp` SDK bundles a frozen
+1.x-era FastMCP (`from mcp.server.fastmcp import FastMCP`, used in the samples above —
+stable, minimal). The standalone `fastmcp` package (gofastmcp.com) is where active
+development happens and is at **major 3**: same decorator surface, plus auth, proxying,
+OpenAPI generation, and a test client. To use it:
+
+```bash
+uv add fastmcp
+```
+
+```python
+from fastmcp import FastMCP   # standalone FastMCP 3 — not mcp.server.fastmcp
+
+mcp = FastMCP("my-server")    # v3: constructor is identity/behaviour only;
+                              # transport config moved to run()/serve time
+```
+
+FastMCP 3 breaking changes (from 2.x): 16 deprecated constructor kwargs removed
+(transport settings now passed at serve time), `ui=` replaced by `app=`,
+`ctx.set_state()`/`ctx.get_state()` are now async with session-scoped persistence, and
+the metadata namespace changed from `_fastmcp` to `fastmcp`.
+
 ## TypeScript SDK Quick Start
 
 ```typescript
@@ -328,7 +350,7 @@ The canonical fact set lives in [`assets/mcp-facts.json`](assets/mcp-facts.json)
 
 ## See Also
 
-- **MCP Specification**: https://spec.modelcontextprotocol.io
+- **MCP Specification**: https://modelcontextprotocol.io/specification/latest (the old spec.modelcontextprotocol.io subdomain no longer resolves)
 - **Python SDK**: https://github.com/modelcontextprotocol/python-sdk
 - **TypeScript SDK**: https://github.com/modelcontextprotocol/typescript-sdk
 - **Official MCP Servers**: https://github.com/modelcontextprotocol/servers

+ 5 - 5
skills/mcp-ops/assets/mcp-facts.json

@@ -3,9 +3,9 @@
   "schema": "claude-mods.mcp-ops.facts/v1",
   "as_of": "2026-07-05",
   "spec": {
-    "url": "https://spec.modelcontextprotocol.io",
-    "prose_token": "spec.modelcontextprotocol.io",
-    "_comment": "The MCP specification URL cited in See Also. --offline asserts the token is named in the skill prose; --live expects the URL to answer HTTP 200 (after redirects)."
+    "url": "https://modelcontextprotocol.io/specification/latest",
+    "prose_token": "modelcontextprotocol.io/specification",
+    "_comment": "The MCP specification URL cited in See Also (moved off the spec. subdomain, which stopped resolving by 2026-07). --offline asserts the token is named in the skill prose; --live expects the URL to answer HTTP 200 (after redirects)."
   },
   "packages": {
     "@modelcontextprotocol/sdk": {
@@ -35,10 +35,10 @@
     "fastmcp": {
       "registry": "pypi",
       "role": "FastMCP high-level framework (gofastmcp.com)",
-      "sampled_major": 2,
+      "sampled_major": 3,
       "track_major": true,
       "prose_token": "fastmcp",
-      "_comment": "The standalone FastMCP package (FastMCP 2 +). Named in prose as FastMCP and via gofastmcp.com; matched case-insensitively."
+      "_comment": "The standalone FastMCP package, documented at major 3 (constructor identity-only, async ctx state, fastmcp meta namespace). Named in prose as FastMCP and via gofastmcp.com; matched case-insensitively."
     }
   }
 }

+ 6 - 1
skills/mcp-ops/references/server-architecture.md

@@ -87,7 +87,12 @@ Servers declare what they support during initialization:
 
 ## FastMCP Server Setup (Python)
 
-FastMCP is the recommended high-level API for Python MCP servers.
+FastMCP is the recommended high-level API for Python MCP servers. Two variants exist:
+the frozen 1.x-era version bundled in the official SDK (`from mcp.server.fastmcp import
+FastMCP`, used below) and the actively developed standalone `fastmcp` package at major 3
+(`from fastmcp import FastMCP`). The decorator surface shown here works on both; on
+standalone FastMCP 3, transport configuration moves out of the constructor to serve time,
+and `ctx.set_state()`/`ctx.get_state()` are async.
 
 ### Basic Server
 

+ 14 - 14
skills/migrate-ops/SKILL.md

@@ -1,6 +1,6 @@
 ---
 name: migrate-ops
-description: "Framework and language migration patterns - version upgrades, breaking changes, dependency audit, safe rollback. Use for: migrate, migration, upgrade, version bump, breaking changes, deprecation, dependency audit, npm audit, pip-audit, codemod, jscodeshift, rector, rollback, semver, changelog, framework upgrade, language upgrade, React 19, Vue 3, Next.js App Router, Laravel 11, Angular, Python 3.12, Node 22, TypeScript 5, Go 1.22, Rust 2024, PHP 8.4."
+description: "Framework and language migration patterns - version upgrades, breaking changes, dependency audit, safe rollback. Use for: migrate, migration, upgrade, version bump, breaking changes, deprecation, dependency audit, npm audit, pip-audit, codemod, jscodeshift, rector, rollback, semver, changelog, framework upgrade, language upgrade, React 19, Vue 3, Next.js App Router, Laravel 13, Angular, Python 3.14, Node 26, TypeScript 6, Go 1.26, Rust 2024, PHP 8.5."
 license: MIT
 allowed-tools: "Read Edit Write Bash Glob Grep Agent"
 metadata:
@@ -12,7 +12,7 @@ metadata:
 
 Comprehensive migration skill covering framework upgrades, language version bumps, dependency auditing, breaking change detection, codemods, and rollback strategies.
 
-> Ecosystem facts verified as of 2026-07.
+> Ecosystem facts verified as of 2026-07-05.
 
 ## Migration Strategy Decision Tree
 
@@ -23,7 +23,7 @@ What kind of migration are you performing?
 │  └─ In-place upgrade
 │     Update dependency, run tests, deploy
-├─ Major framework version (React 18→19, Vue 2→3, Laravel 10→11)
+├─ Major framework version (React 18→19, Vue 2→3, Laravel 12→13)
 │  │
 │  ├─ Codebase < 50k LOC, good test coverage (>70%)
 │  │  └─ Big Bang Migration
@@ -49,7 +49,7 @@ What kind of migration are you performing?
 │        Pros: highest confidence, catch subtle differences
 │        Cons: double infrastructure cost, comparison logic
-└─ Language version upgrade (Python 3.9→3.12, Node 18→22)
+└─ Language version upgrade (Python 3.12→3.14, Node 22→26)
    └─ In-place upgrade with CI matrix
       Test against both old and new versions in CI
       Drop old version support once all tests pass
@@ -84,13 +84,13 @@ Which framework are you upgrading?
 │  ├─ Tool: Migration build (@vue/compat) for incremental migration
 │  └─ Load: ./references/framework-upgrades.md
-├─ Laravel 10 → 11
-│  ├─ Check: Adopt slim application skeleton
-│  ├─ Check: Update config file structure (consolidated configs)
-│  ├─ Check: Review per-second scheduling changes
-│  ├─ Check: Update Dumpable trait usage
+├─ Laravel 12 → 13
+│  ├─ Check: PHP 8.3 is now the minimum (8.5 supported)
+│  ├─ Check: Cache/Redis key prefixes now use hyphenated suffixes
+│  ├─ Check: Adopt native PHP attributes (models, jobs, controllers) — optional
+│  ├─ Check: Queue routing by class via Queue::route(...) — optional
 │  ├─ Tool: laravel shift (automated upgrade service)
-│  └─ Load: ./references/framework-upgrades.md
+│  └─ Load: ./references/framework-upgrades.md (covers 10→11 in depth; 12→13 is near zero-break)
 ├─ Angular (any major version)
 │  ├─ Check: Run ng update for guided migration
@@ -197,7 +197,7 @@ How do you detect breaking changes?
 | **Next.js** | next-codemod | `npx @next/codemod@latest` | Next.js version upgrades |
 | **Vue** | vue-codemod | `npx @vue/codemod src/` | Vue 2 to 3 transforms |
 | **PHP** | Rector | `vendor/bin/rector process src` | PHP version + framework upgrades |
-| **Python** | pyupgrade | `pyupgrade --py312-plus *.py` | Python version syntax upgrades |
+| **Python** | pyupgrade | `pyupgrade --py314-plus *.py` | Python version syntax upgrades |
 | **Python** | django-upgrade | `django-upgrade --target-version 5.0 *.py` | Django version upgrades |
 | **Go** | gofmt | `gofmt -w .` | Go formatting updates |
 | **Go** | gofix | `go fix ./...` | Go API changes |
@@ -266,13 +266,13 @@ Migration failed or caused issues — how to roll back?
 
 | File | Contents | Lines |
 |------|----------|-------|
-| `references/framework-upgrades.md` | React 18→19, Next.js Pages→App Router, Vue 2→3, Laravel 10→11, Angular, Django upgrade paths | ~700 |
-| `references/language-upgrades.md` | Python 3.9→3.13, Node 18→22, TypeScript 4→5, Go 1.20→1.23, Rust 2021→2024, PHP 8.1→8.4 | ~600 |
+| `references/framework-upgrades.md` | React 18→19, Next.js Pages→App Router, Vue 2→3, Laravel 10→13, Angular, Django upgrade paths | ~700 |
+| `references/language-upgrades.md` | Python 3.9→3.14, Node 18→26, TypeScript 4→6, Go 1.20→1.26, Rust 2021→2024, PHP 8.1→8.5 | ~650 |
 | `references/dependency-management.md` | Audit tools, update strategies, lock files, monorepo deps, supply chain security | ~550 |
 
 ## Staleness verifier
 
-This skill hardcodes specific framework/language target versions (React 19, Laravel 11, Python 3.12, Node 22, TypeScript 5, Go 1.22, Rust 2024, PHP 8.4). [`scripts/check-migrate-facts.py`](scripts/check-migrate-facts.py) guards them against silent drift:
+This skill hardcodes specific framework/language target versions (React 19, Laravel 13, Python 3.14, Node 26, TypeScript 6, Go 1.26, Rust 2024, PHP 8.5). [`scripts/check-migrate-facts.py`](scripts/check-migrate-facts.py) guards them against silent drift:
 
 ```bash
 # Structural (PR CI, no network): every catalogued target version still appears

+ 19 - 19
skills/migrate-ops/assets/migrate-facts.json

@@ -12,38 +12,38 @@
     },
     {
       "label": "Laravel",
-      "version": "11",
+      "version": "13",
       "where": ["description", "body"],
-      "pattern": "laravel[^\\n]*\\b11\\b",
-      "live": {"source": "endoflife", "product": "laravel", "compare": "major", "documented": "11"}
+      "pattern": "laravel[^\\n]*\\b13\\b",
+      "live": {"source": "endoflife", "product": "laravel", "compare": "major", "documented": "13"}
     },
     {
       "label": "Python",
-      "version": "3.12",
+      "version": "3.14",
       "where": ["description", "body"],
-      "pattern": "python[^\\n]*3\\.12\\b",
-      "live": {"source": "endoflife", "product": "python", "compare": "line", "documented": "3.12"}
+      "pattern": "python[^\\n]*3\\.14\\b",
+      "live": {"source": "endoflife", "product": "python", "compare": "line", "documented": "3.14"}
     },
     {
       "label": "Node",
-      "version": "22",
+      "version": "26",
       "where": ["description", "body"],
-      "pattern": "node[^\\n]*\\b22\\b",
-      "live": {"source": "endoflife", "product": "nodejs", "compare": "major", "documented": "22"}
+      "pattern": "node[^\\n]*\\b26\\b",
+      "live": {"source": "endoflife", "product": "nodejs", "compare": "major", "documented": "26"}
     },
     {
       "label": "TypeScript",
-      "version": "5",
+      "version": "6",
       "where": ["description", "body"],
-      "pattern": "typescript[^\\n]*\\b5\\b",
-      "live": {"source": "npm", "product": "typescript", "compare": "major", "documented": "5"}
+      "pattern": "typescript[^\\n]*\\b6\\b",
+      "live": {"source": "npm", "product": "typescript", "compare": "major", "documented": "6"}
     },
     {
       "label": "Go",
-      "version": "1.22",
+      "version": "1.26",
       "where": ["description", "body"],
-      "pattern": "go[^\\n]*1\\.22\\b",
-      "live": {"source": "endoflife", "product": "go", "compare": "line", "documented": "1.22"}
+      "pattern": "go[^\\n]*1\\.26\\b",
+      "live": {"source": "endoflife", "product": "go", "compare": "line", "documented": "1.26"}
     },
     {
       "label": "Rust",
@@ -51,14 +51,14 @@
       "where": ["description", "body"],
       "pattern": "rust[^\\n]*2024\\b",
       "live": null,
-      "_comment": "Rust 2024 is an EDITION (reached stable with Rust 1.85), not a semver major tracked on endoflife.date's product list. Live-checked nowhere; offline-only."
+      "_comment": "Rust 2024 is an EDITION (reached stable with Rust 1.85), not a semver major tracked on endoflife.date's product list. Still the newest edition as of 2026-07 (next expected 2027). Live-checked nowhere; offline-only."
     },
     {
       "label": "PHP",
-      "version": "8.4",
+      "version": "8.5",
       "where": ["description", "body"],
-      "pattern": "php[^\\n]*8\\.4\\b",
-      "live": {"source": "endoflife", "product": "php", "compare": "line", "documented": "8.4"}
+      "pattern": "php[^\\n]*8\\.5\\b",
+      "live": {"source": "endoflife", "product": "php", "compare": "line", "documented": "8.5"}
     }
   ]
 }

+ 25 - 1
skills/migrate-ops/references/framework-upgrades.md

@@ -325,7 +325,7 @@ npm test
 
 ---
 
-## Laravel 10 to 11
+## Laravel 10 to 13
 
 ### Pre-Upgrade Checklist
 
@@ -431,6 +431,30 @@ php artisan config:show
 php artisan route:list
 ```
 
+### Continuing to Laravel 12 and 13
+
+Upgrade one major at a time (11 → 12 → 13); both steps are deliberately light.
+
+**Laravel 12** (Feb 2025) — maintenance-focused: minimal breaking changes, dependency
+bumps, and new starter kits (React/Vue/Livewire). Most apps upgrade by updating
+`composer.json` constraints and re-running the suite.
+
+**Laravel 13** (Mar 2026) — stability-first with an AI/DX push:
+- **Requires PHP 8.3+** (supports through PHP 8.5) — upgrade the runtime first
+- Default cache and Redis key prefixes now use hyphenated suffixes — irrelevant if your
+  config files already pin these values, breaking if you shared a store across versions
+- New (opt-in): first-party Laravel AI SDK, native PHP attributes across models/jobs/
+  controllers/authorization, `Queue::route(...)` class-based queue routing, query-builder
+  vector similarity search (pgvector)
+- Laravel Shift remains the automated PR-based upgrade path
+
+```bash
+# Per major: bump constraint, update, test
+composer require laravel/framework:^13.0 --with-all-dependencies
+php artisan config:show cache   # verify key-prefix expectations
+php artisan test
+```
+
 ---
 
 ## Angular Version Upgrades

+ 159 - 13
skills/migrate-ops/references/language-upgrades.md

@@ -4,7 +4,7 @@ Detailed upgrade paths for major programming language version transitions.
 
 ---
 
-## Python 3.9 to 3.13
+## Python 3.9 to 3.14
 
 ### Python 3.10 (from 3.9)
 
@@ -140,6 +140,34 @@ rg "typing\.re\." .
 python3.13t script.py  # if built with --disable-gil
 ```
 
+### Python 3.14 (from 3.13)
+
+**Key Features Gained:**
+- Deferred evaluation of annotations by default (PEP 649/749) — inspect via `annotationlib`; `from __future__ import annotations` is no longer needed
+- Template strings / t-strings (PEP 750)
+- Free-threaded build officially supported (PEP 779 — still a separate build, no longer "experimental")
+- Multiple interpreters in the stdlib (`concurrent.interpreters`, PEP 734)
+- Zstandard compression (`compression.zstd`, PEP 784)
+- Safe external debugger interface (`sys.remote_exec`)
+
+**Breaking Changes:**
+- `asyncio.get_event_loop()` no longer creates a new event loop — use `asyncio.run()` or `get_running_loop()`
+- Long-deprecated `ast` aliases (`ast.Num`, `ast.Str`, `ast.Bytes`, ...) removed — use `ast.Constant`
+- Annotation-introspection code that reads `__annotations__` eagerly may see deferred semantics — migrate to `annotationlib`
+
+**Migration Commands:**
+```bash
+# Find eager-annotation assumptions
+rg "from __future__ import annotations" .   # now redundant (harmless to keep)
+rg "__annotations__" .                       # candidates for annotationlib
+
+# Find event-loop creation via the removed pattern
+rg "asyncio\.get_event_loop\(\)" .
+
+# Modernize syntax to the new floor
+pyupgrade --py314-plus **/*.py
+```
+
 ### Python Version Upgrade Summary
 
 | From → To | Key Action | Biggest Risk |
@@ -148,10 +176,11 @@ python3.13t script.py  # if built with --disable-gil
 | 3.10 → 3.11 | Replace `toml` with `tomllib`, enjoy speed boost | `smtpd` removal |
 | 3.11 → 3.12 | Remove `distutils`/`imp`, adopt type syntax | `distutils` full removal, sqlite3 adapter changes |
 | 3.12 → 3.13 | Remove deprecated stdlib modules | Large number of removed stdlib modules |
+| 3.13 → 3.14 | Adopt deferred annotations + t-strings | `asyncio.get_event_loop()` no longer creates a loop |
 
 ---
 
-## Node.js 18 to 22
+## Node.js 18 to 26
 
 ### Node.js 20 (from 18)
 
@@ -233,16 +262,48 @@ node --experimental-require-module app.js
 # Replace: chokidar/nodemon → node --watch
 ```
 
+### Node.js 24 (from 22)
+
+**Key Features Gained:**
+- `require()` of ESM modules enabled by default (no flag)
+- V8 13.6 — `Float16Array`, `RegExp.escape`, `Error.isError`, explicit resource management (`await using`)
+- `URLPattern` as a global
+- Permission model stable (flag renamed to `--permission`)
+- npm 11, Undici 7
+
+**Breaking Changes:**
+- Windows native builds drop MSVC (ClangCL toolchain) — affects native-addon build pipelines
+- Assorted deprecated API removals (`url.parse()` further discouraged — use `URL`)
+
+**Migration Commands:**
+```bash
+nvm install 24
+nvm use 24
+# FROM node:24-alpine
+
+npm rebuild                      # native modules against new ABI
+rg "require\(['\"]\./.*\.mjs" .  # spots that relied on require(esm) flags
+```
+
+### Node.js 26 (from 24)
+
+Node 26 is the Current line (April 2026); it enters Active LTS in October 2026. For
+production, upgrade LTS-to-LTS: 22 → 24 now, 24 → 26 once 26 is LTS. The mechanics are
+the same as every even-major bump: rebuild native modules, re-run the test suite on the
+new V8, and update `FROM node:26` images plus CI matrices when you cut over.
+
 ### Node.js Version Upgrade Summary
 
 | From → To | Key Action | Biggest Risk |
 |-----------|-----------|--------------|
 | 18 → 20 | Rebuild native modules, test URL parsing | Stricter URL validation, loader hooks off-thread |
 | 20 → 22 | Rebuild native modules, check glibc version | Native module compatibility, header validation |
+| 22 → 24 | Adopt require(esm), permission model | Native-addon toolchain change on Windows |
+| 24 → 26 | LTS-to-LTS bump when 26 enters LTS (Oct 2026) | Riding the Current line before LTS |
 
 ---
 
-## TypeScript 4.x to 5.x
+## TypeScript 4.x to 6.0
 
 ### TypeScript 5.0 (from 4.9)
 
@@ -295,27 +356,65 @@ rg '"suppressExcessPropertyErrors"' tsconfig.json
 | **5.6** | Iterator helper methods, `--noUncheckedSideEffectImports` |
 | **5.7** | `--rewriteRelativeImportExtensions`, `--target es2024` |
 
+### TypeScript 6.0 (from 5.x)
+
+The last release on the JavaScript-based compiler — 6.0 modernises defaults as the
+bridge to the native (Go) compiler in TypeScript 7.
+
+**Key Features Gained:**
+- `es2025` target/lib with types for Temporal, `Map.getOrInsert`, `RegExp.escape`
+- Better inference for `this`-less functions; `#/` subpath-import support
+- `--stableTypeOrdering` flag to ease 6.0 → 7.0 migration diffing
+- Large-monorepo `tsc --watch` rebuilds significantly faster
+
+**Breaking Changes:**
+- `strict: true` is now the **default** — configs that never set it surface new errors
+- Defaults changed: `module: esnext`, `target: es2025`
+- Removed: `moduleResolution: classic`; `module: amd/umd/system/none`; minimum `target` is ES2015 (`es5` deprecated)
+- `esModuleInterop` / `allowSyntheticDefaultImports` can no longer be set to `false`
+- Namespace-with-class merging requires explicit `export`
+
+**Migration Commands:**
+```bash
+npm install -D typescript@6
+
+# Find options 6.0 removed or hard-wires
+rg '"moduleResolution":\s*"classic"' tsconfig.json
+rg '"module":\s*"(amd|umd|system|none)"' tsconfig.json
+rg '"esModuleInterop":\s*false' tsconfig.json
+rg '"target":\s*"es5"' -i tsconfig.json
+
+# Surface the strict-by-default delta before committing to it
+npx tsc --noEmit
+```
+
 ### Migration Strategy
 
 ```
 TypeScript version upgrade approach:
-├─ Minor version (5.x → 5.y)
+├─ Minor version (x.y → x.z)
 │  └─ Generally safe, just update and fix new errors
-│     npm install -D typescript@5.y
+│     npm install -D typescript@latest
 │     npx tsc --noEmit
-└─ Major version (4.x → 5.x)
-   ├─ 1. Update tsconfig.json (remove deleted options)
-   ├─ 2. Install typescript@5
-   ├─ 3. Run tsc --noEmit, fix errors
-   ├─ 4. Decide on decorator strategy (legacy vs ECMAScript)
-   └─ 5. Consider adopting moduleResolution: "bundler"
+├─ Major version (4.x → 5.x)
+│  ├─ 1. Update tsconfig.json (remove deleted options)
+│  ├─ 2. Install typescript@5
+│  ├─ 3. Run tsc --noEmit, fix errors
+│  ├─ 4. Decide on decorator strategy (legacy vs ECMAScript)
+│  └─ 5. Consider adopting moduleResolution: "bundler"
+│
+└─ Major version (5.x → 6.0)
+   ├─ 1. Set strict: true explicitly and fix errors BEFORE upgrading
+   ├─ 2. Replace removed module/moduleResolution options
+   ├─ 3. Install typescript@6, run tsc --noEmit
+   └─ 4. Pin the target you actually ship (defaults moved to es2025)
 ```
 
 ---
 
-## Go 1.20 to 1.23
+## Go 1.20 to 1.26
 
 ### Go 1.21 (from 1.20)
 
@@ -403,6 +502,27 @@ rg "\.Stop\(\)" . --glob "*.go"  # Review timer stop behavior
 rg "\.Reset\(" . --glob "*.go"   # Review timer reset behavior
 ```
 
+### Go 1.24 – 1.26 (from 1.23)
+
+**Key Features Gained:**
+- **1.24**: generic type aliases; `tool` directives in go.mod (tracked tool deps); `os.Root` (filesystem-scoped file access); Swiss-table map implementation (runtime perf); `weak` package; `runtime.AddCleanup` (successor to `SetFinalizer`)
+- **1.25**: container-aware `GOMAXPROCS` (respects cgroup CPU limits); `testing/synctest` for concurrent-code tests; experimental green-tea GC
+- **1.26**: current stable line — as with every Go release, review the release notes for `go vet`/runtime deltas; language changes remain rare and gated on the `go` directive
+
+**Breaking Changes:**
+- Effectively none at the language level (Go 1 compatibility promise); behavior deltas are gated on the `go` directive version in go.mod, so bumps activate deliberately
+
+**Migration Commands:**
+```bash
+go mod edit -go=1.26
+go mod tidy
+go vet ./...
+govulncheck ./...
+
+# Adopt tool directives (1.24+) — replaces tools.go pattern
+go get -tool golang.org/x/tools/cmd/stringer
+```
+
 ### Go Version Upgrade Summary
 
 | From → To | Key Action | Biggest Risk |
@@ -410,6 +530,7 @@ rg "\.Reset\(" . --glob "*.go"   # Review timer reset behavior
 | 1.20 → 1.21 | Update go.mod toolchain, adopt slog | Toolchain directive in go.mod |
 | 1.21 → 1.22 | Enjoy loop variable fix, adopt enhanced routing | Loop variable semantics (usually fixes bugs) |
 | 1.22 → 1.23 | Replace x/exp packages, adopt iterators | Timer/Ticker behavior change |
+| 1.23 → 1.26 | Bump go directive stepwise, adopt tool directives + os.Root | Runtime perf deltas (Swiss maps, GC) in hot paths |
 
 ---
 
@@ -471,7 +592,7 @@ cargo doc --no-deps  # check documentation builds
 
 ---
 
-## PHP 8.1 to 8.4
+## PHP 8.1 to 8.5
 
 ### PHP 8.2 (from 8.1)
 
@@ -580,6 +701,30 @@ vendor/bin/rector process src --set php84
 rg "function \w+\([^)]*\w+ \$\w+ = null" src/ --glob "*.php"
 ```
 
+### PHP 8.5 (from 8.4)
+
+**Key Features Gained:**
+- Pipe operator `|>` for left-to-right call chaining
+- `array_first()` and `array_last()`
+- `#[\NoDiscard]` attribute (warn when a return value is ignored)
+- Fatal errors now include backtraces
+- `clone with` — update readonly/other properties during clone
+
+**Breaking Changes:**
+- Minor-release discipline: mostly new deprecations rather than removals; run the suite with deprecations surfaced (`error_reporting(E_ALL)`) before and after
+
+**Migration Commands:**
+```bash
+vendor/bin/rector process src --set php85
+
+# Adopt the pipe operator where nested calls hurt readability
+# Old: trim(strtolower($name))
+# New: $name |> strtolower(...) |> trim(...)
+
+# Replace reset()/end() misuse for first/last element
+rg "\breset\(|\bend\(" src/ --glob "*.php"   # candidates for array_first/array_last
+```
+
 ### PHP Version Upgrade Summary
 
 | From → To | Key Action | Biggest Risk |
@@ -587,6 +732,7 @@ rg "function \w+\([^)]*\w+ \$\w+ = null" src/ --glob "*.php"
 | 8.1 → 8.2 | Fix dynamic properties, deprecation warnings | Dynamic properties deprecated |
 | 8.2 → 8.3 | Adopt typed constants, #[Override] | array_sum/array_product behavior |
 | 8.3 → 8.4 | Adopt property hooks, asymmetric visibility | Implicit nullable deprecation |
+| 8.4 → 8.5 | Adopt pipe operator, array_first/array_last | New deprecations surfacing in dependencies |
 
 ---
 

+ 49 - 8
skills/typescript-ops/SKILL.md

@@ -12,7 +12,7 @@ metadata:
 
 Comprehensive TypeScript skill covering the type system, generics, and production patterns.
 
-> Ecosystem facts verified as of 2026-07.
+> Ecosystem facts verified as of 2026-07-05 (TypeScript 6, Zod 4, Valibot 1).
 
 **Staleness check:** `python scripts/check-typescript-facts.py --offline` asserts the
 catalogued version-bearing facts (TypeScript major, zod, valibot) are still named in the
@@ -162,21 +162,57 @@ type StringKeys<T> = {
 
 **Deep dive**: Load `./references/generics-patterns.md` for advanced type-level programming, recursive types, template literal types.
 
+## Modern Language Features (TypeScript 5.x → 6.0)
+
+| Feature | Since | What It Gives You |
+|---------|-------|-------------------|
+| `satisfies` operator | 4.9 | Check a value against a type without widening it |
+| Standard (TC39) decorators | 5.0 | `@decorator` on classes/methods without `experimentalDecorators` |
+| `const` type parameters | 5.0 | `function f<const T>(x: T)` infers literal types without `as const` at call sites |
+| `using` declarations | 5.2 | Explicit resource management (`Symbol.dispose`), auto-cleanup at scope exit |
+| Inferred type predicates | 5.5 | `arr.filter(x => x !== null)` narrows without a hand-written `x is T` guard |
+| `verbatimModuleSyntax` | 5.0 | Enforces `import type` for type-only imports — replaces `importsNotUsedAsValues` |
+
+```typescript
+// const type parameters (5.0) - literal inference without as const
+function routes<const T extends readonly string[]>(paths: T): T { return paths; }
+const r = routes(["/home", "/about"]); // readonly ["/home", "/about"], not string[]
+
+// using declarations (5.2) - deterministic cleanup
+function readConfig() {
+    using file = openFile("config.json"); // file[Symbol.dispose]() runs at scope exit
+    return parse(file.contents);
+}
+
+// Inferred type predicates (5.5) - no manual guard needed
+const names = ["a", null, "b"].filter(x => x !== null); // string[], not (string | null)[]
+```
+
+### TypeScript 6.0 (Current Major)
+
+TS 6.0 is the last release on the JavaScript-based compiler — it exists to bridge to the
+native (Go) compiler in TS 7, so its headline is stricter, modernised defaults:
+
+- **`strict: true` is the default** — a tsconfig that never set it now gets full strict checks
+- **Defaults modernised**: `module: esnext`, `target: es2025`; `es2025` lib ships types for Temporal, `Map.getOrInsert`, `RegExp.escape`
+- **Legacy options removed**: `moduleResolution: classic`; `module: amd/umd/system/none`; minimum `target` is now ES2015 (`es5` deprecated)
+- **Interop always on**: `esModuleInterop` / `allowSyntheticDefaultImports` can no longer be disabled
+- New `--stableTypeOrdering` flag eases 6.0 → 7.0 migration diffing
+
 ## tsconfig Quick Reference
 
 ```jsonc
 {
     "compilerOptions": {
-        // Strict mode (always enable)
+        // Strict mode (default in TS 6; state it explicitly anyway)
         "strict": true,               // Enables all strict checks
         "noUncheckedIndexedAccess": true,  // arr[0] is T | undefined
 
-        // Module system
+        // Module system (TS 6 defaults to module: esnext; interop is always on)
         "module": "esnext",           // or "nodenext" for Node
         "moduleResolution": "bundler", // or "nodenext"
-        "esModuleInterop": true,
 
-        // Output
+        // Output (TS 6 defaults target to es2025; min supported is es2015)
         "target": "es2022",
         "outDir": "dist",
         "declaration": true,          // Generate .d.ts
@@ -230,16 +266,16 @@ getUser(userId);   // OK
 getUser(orderId);  // Error: OrderId not assignable to UserId
 ```
 
-## Runtime Validation (Zod)
+## Runtime Validation (Zod 4)
 
 ```typescript
 import { z } from "zod";
 
-// Define schema
+// Define schema (Zod 4: string formats are top-level - z.email(), not z.string().email())
 const UserSchema = z.object({
     id: z.number(),
     name: z.string().min(1),
-    email: z.string().email(),
+    email: z.email(),
     role: z.enum(["admin", "user"]),
     settings: z.object({
         theme: z.enum(["light", "dark"]).default("light"),
@@ -254,6 +290,11 @@ const user = UserSchema.parse(untrustedData);       // throws on invalid
 const result = UserSchema.safeParse(untrustedData);  // returns { success, data/error }
 ```
 
+**Zod 4 changes to know** (if you learned Zod 3): string formats moved to the top level
+(`z.email()`, `z.uuid()`, `z.url()` — the `z.string().email()` method form is deprecated);
+error customisation unified under a single `error` param (`invalid_type_error` /
+`required_error` dropped); much faster parsing and a tree-shakeable `zod/mini` entry point.
+
 ## Reference Files
 
 Load these for deep-dive topics. Each is self-contained.

+ 7 - 7
skills/typescript-ops/assets/typescript-facts.json

@@ -3,25 +3,25 @@
   "schema": "claude-mods.typescript-ops.facts/v1",
   "as_of": "2026-07-05",
   "registry": "https://registry.npmjs.org",
-  "currency_note": "> Ecosystem facts verified as of 2026-07.",
+  "currency_note": "> Ecosystem facts verified as of 2026-07-05 (TypeScript 6, Zod 4, Valibot 1).",
   "packages": [
     {
       "name": "typescript",
-      "documented_major": 4,
+      "documented_major": 6,
       "role": "language",
-      "where": "references/config-strict.md (useUnknownInCatchVariables, TS 4.4+); references/type-system.md (in/out variance annotations, TypeScript 4.7+). The skill never anchors a TS 5 feature, so its newest documented floor is the 4.x line."
+      "where": "SKILL.md (Modern Language Features 5.x -> 6.0: satisfies, standard decorators, const type params, using, inferred predicates; TS 6.0 defaults section); references/config-strict.md (strict default in 6.0, removed module/moduleResolution options). Historical feature floors (TS 4.4+, 4.7+) remain as floors, not currency anchors."
     },
     {
       "name": "zod",
-      "documented_major": 3,
+      "documented_major": 4,
       "role": "runtime validation (default choice)",
-      "where": "SKILL.md (Runtime Validation); references/ecosystem.md (z.infer, z.discriminatedUnion, z.coerce). z.object/.infer/.discriminatedUnion/.coerce are the Zod 3+ surface."
+      "where": "SKILL.md (Runtime Validation (Zod 4): top-level z.email(), unified error param, zod/mini); references/ecosystem.md (z.uuid/z.email top-level formats, unified error param, discriminatedUnion, coerce)."
     },
     {
       "name": "valibot",
-      "documented_major": 0,
+      "documented_major": 1,
       "role": "runtime validation (tree-shakeable alt)",
-      "where": "references/ecosystem.md (v.pipe, v.InferOutput, v.safeParse, v.picklist). Valibot shipped as 0.x through the period this skill was written."
+      "where": "references/ecosystem.md (stable since 1.0; v.pipe, v.InferOutput, v.safeParse, v.picklist)."
     }
   ]
 }

+ 8 - 9
skills/typescript-ops/references/config-strict.md

@@ -16,7 +16,7 @@
 
 ### Enable the Full Strict Suite
 
-`"strict": true` is shorthand for enabling all individual strict flags at once. Always enable it.
+`"strict": true` is shorthand for enabling all individual strict flags at once. Always enable it. Since TypeScript 6.0 it is the **default** — a tsconfig that never set `strict` now gets the full suite, so a project upgrading to TS 6 may surface long-hidden errors; set `"strict": false` only as a deliberate, temporary migration step.
 
 ```json
 {
@@ -243,16 +243,15 @@ const y: string = legacyApi.getValue();
     "moduleResolution": "Bundler"
   }
 }
-
-// For browser projects with no bundler (rare)
-{
-  "compilerOptions": {
-    "module": "ESNext",
-    "moduleResolution": "Classic"  // avoid - use Bundler or Node16
-  }
-}
 ```
 
+**TypeScript 6.0 removals**: `moduleResolution: "Classic"` and `module: "amd" / "umd" /
+"system" / "none"` were removed outright, and `esModuleInterop` /
+`allowSyntheticDefaultImports` can no longer be set to `false` (the safe interop behaviour
+is always on). Defaults are now `module: "esnext"` and `target: "es2025"`; the minimum
+`target` is ES2015. If a legacy tsconfig names any removed option, migrate to `Bundler`
+or `Node16`/`NodeNext` before upgrading.
+
 ### Understand ESM vs CJS Interop Issues
 
 With `Node16`/`NodeNext`, you must use explicit `.js` extensions in relative imports (even for `.ts` files).

+ 10 - 6
skills/typescript-ops/references/ecosystem.md

@@ -17,16 +17,16 @@
 
 ### Use Zod for Schema Validation with Type Inference
 
-Zod is the most widely adopted runtime validation library. Define a schema once; infer the TypeScript type from it.
+Zod is the most widely adopted runtime validation library. Define a schema once; infer the TypeScript type from it. As of Zod 4, string formats are top-level functions (`z.email()`, `z.uuid()`, `z.url()`) — the Zod 3 method forms (`z.string().email()`) are deprecated.
 
 ```typescript
 import { z } from 'zod';
 
-// Define schema
+// Define schema (Zod 4 syntax)
 const UserSchema = z.object({
-  id: z.string().uuid(),
+  id: z.uuid(),
   name: z.string().min(1).max(100),
-  email: z.string().email(),
+  email: z.email(),
   age: z.number().int().min(0).max(150).optional(),
   role: z.enum(['admin', 'user', 'moderator']),
   createdAt: z.coerce.date(),
@@ -68,6 +68,10 @@ const EvenNumberSchema = z.number().refine(
   { message: 'Number must be even' }
 );
 
+// Custom error messages (Zod 4: one unified `error` param replaces
+// invalid_type_error / required_error / errorMap)
+const AgeSchema = z.number({ error: 'Age must be a number' }).int().min(0);
+
 // Discriminated union (Zod version)
 const ApiResponseSchema = z.discriminatedUnion('status', [
   z.object({ status: z.literal('success'), data: z.unknown() }),
@@ -78,7 +82,7 @@ type ApiResponse = z.infer<typeof ApiResponseSchema>;
 
 ### Use Valibot as a Tree-Shakeable Alternative
 
-Valibot has an almost identical API to Zod but is tree-shakeable by design, resulting in much smaller bundles for edge/browser deployments.
+Valibot (stable since 1.0) covers the same ground as Zod but is tree-shakeable by design, resulting in much smaller bundles for edge/browser deployments. Zod 4's `zod/mini` entry point narrows the gap, but valibot's pipe composition remains the smallest-bundle option.
 
 ```typescript
 import * as v from 'valibot';
@@ -183,7 +187,7 @@ export const appRouter = t.router({
       }),
 
     create: t.procedure
-      .input(z.object({ name: z.string(), email: z.string().email() }))
+      .input(z.object({ name: z.string(), email: z.email() }))
       .mutation(async ({ input }) => {
         return await db.user.create({ data: input });
       }),

+ 11 - 7
skills/vue-ops/SKILL.md

@@ -1,6 +1,6 @@
 ---
 name: vue-ops
-description: "Vue 3 development patterns, Composition API, Pinia state management, Vue Router, and Nuxt 3. Use for: vue, vuejs, composition api, pinia, vue router, nuxt, nuxt3, script setup, composable, reactive, defineProps, defineEmits, defineModel, v-model, provide inject, vue3."
+description: "Vue 3 development patterns, Composition API, Pinia state management, Vue Router, and Nuxt 4. Use for: vue, vuejs, composition api, pinia, vue router, nuxt, nuxt4, nuxt3, script setup, composable, reactive, defineProps, defineEmits, defineModel, v-model, provide inject, vue3."
 license: MIT
 allowed-tools: "Read Write Bash"
 metadata:
@@ -10,9 +10,9 @@ metadata:
 
 # Vue Operations
 
-Comprehensive Vue 3 reference covering Composition API, Pinia, Vue Router, Nuxt 3, and testing — production patterns with TypeScript throughout.
+Comprehensive Vue 3 reference covering Composition API, Pinia, Vue Router, Nuxt 4, and testing — production patterns with TypeScript throughout.
 
-> Vue 3 / Nuxt 3 ecosystem facts verified as of 2026-07.
+> Vue 3 / Nuxt 4 ecosystem facts verified as of 2026-07-05.
 
 ---
 
@@ -367,7 +367,11 @@ declare module 'vue-router' {
 
 ---
 
-## Nuxt 3 Decision Tree
+## Nuxt 4 Decision Tree
+
+Nuxt 4's flagship change over Nuxt 3 is the `app/` source directory (app code separated
+from `server/` and root config — see [./references/nuxt.md](./references/nuxt.md)); the
+rendering strategies below are unchanged.
 
 ```
 What rendering strategy does my app need?
@@ -450,12 +454,12 @@ What rendering strategy does my app need?
 |------|-------------|
 | [./references/composition-api.md](./references/composition-api.md) | Composables, provide/inject, template refs, custom directives, Teleport, Suspense, slots, transitions, v-model deep patterns |
 | [./references/state-routing.md](./references/state-routing.md) | Pinia advanced patterns (plugins, SSR, store composition), Vue Router (guards, meta typing, scroll behavior, transitions) |
-| [./references/nuxt.md](./references/nuxt.md) | Nuxt 3 data fetching, server routes, middleware, plugins, modules, SEO, deployment, Nuxt Content |
+| [./references/nuxt.md](./references/nuxt.md) | Nuxt 4 directory structure, data fetching, server routes, middleware, plugins, modules, SEO, deployment, Nuxt Content |
 | [./references/testing.md](./references/testing.md) | Vitest setup, Vue Test Utils, Pinia/Router testing, composable testing, MSW, Playwright, Nuxt test utils |
 
 ## Staleness Verifier
 
-This skill encodes fast-moving facts (the Vue 3 minor-version gates, the Nuxt 3
+This skill encodes fast-moving facts (the Vue 3 minor-version gates, the Nuxt 4
 meta-framework, the ecosystem package stack). [`scripts/check-vue-facts.py`](scripts/check-vue-facts.py)
 guards them against silent drift — internal consistency in PR CI, live
 major-version drift in the scheduled freshness job:
@@ -466,7 +470,7 @@ major-version drift in the scheduled freshness job:
 python3 skills/vue-ops/scripts/check-vue-facts.py --offline        # exit 0 consistent, 10 drift
 
 # Live (weekly freshness job, never blocks a PR): is any documented major
-# now behind npm's latest dist-tag? (e.g. Nuxt 4 while the prose says Nuxt 3.)
+# now behind npm's latest dist-tag? (e.g. Nuxt 5 while the prose says Nuxt 4.)
 python3 skills/vue-ops/scripts/check-vue-facts.py --live           # exit 10 a major moved ahead, 7 npm unreachable
 ```
 

+ 3 - 3
skills/vue-ops/assets/vue-facts.json

@@ -1,5 +1,5 @@
 {
-  "_comment": "Canonical fast-moving facts the vue-ops skill encodes. scripts/check-vue-facts.py asserts SKILL.md + references name these consistently (--offline) and probes the npm registry for major-version drift (--live). Edit deliberately: a change here is a skill-content decision, not housekeeping. documented_major is the major the skill's prose commits to (vue 3, nuxt 3) or the current tracked major for ecosystem libs the prose names without pinning a version.",
+  "_comment": "Canonical fast-moving facts the vue-ops skill encodes. scripts/check-vue-facts.py asserts SKILL.md + references name these consistently (--offline) and probes the npm registry for major-version drift (--live). Edit deliberately: a change here is a skill-content decision, not housekeeping. documented_major is the major the skill's prose commits to (vue 3, nuxt 4) or the current tracked major for ecosystem libs the prose names without pinning a version.",
   "schema": "claude-mods.vue-ops.facts/v1",
   "as_of": "2026-07-05",
   "vue_major": 3,
@@ -8,11 +8,11 @@
     "define_model": "Vue 3.4",
     "define_options": "Vue 3.3",
     "use_template_ref": "Vue 3.5",
-    "nuxt_major": "Nuxt 3"
+    "nuxt_major": "Nuxt 4"
   },
   "packages": {
     "vue":               { "documented_major": 3, "prose": ["Vue 3"],              "role": "core (Composition API, <script setup>)" },
-    "nuxt":              { "documented_major": 3, "prose": ["Nuxt 3"],             "role": "meta-framework (SSR/SSG/ISR, App Router host)" },
+    "nuxt":              { "documented_major": 4, "prose": ["Nuxt 4"],             "role": "meta-framework (SSR/SSG/ISR, app/ srcDir host)" },
     "pinia":             { "documented_major": 3, "prose": ["pinia"],              "role": "store" },
     "vue-router":        { "documented_major": 5, "prose": ["vue-router"],         "role": "routing" },
     "@vueuse/core":      { "documented_major": 14, "prose": ["VueUse"],            "role": "composable collection" },

+ 53 - 11
skills/vue-ops/references/nuxt.md

@@ -1,17 +1,50 @@
-# Nuxt 3 Reference
+# Nuxt 4 Reference
 
-Production patterns for Nuxt 3: rendering modes, data fetching, server routes, middleware, plugins, modules, SEO, deployment, and Nuxt Content.
+Production patterns for Nuxt 4: directory structure, rendering modes, data fetching, server routes, middleware, plugins, modules, SEO, deployment, and Nuxt Content.
 
 ---
 
 ## Architecture Overview
 
-Nuxt 3 is built on:
+Nuxt 4 is built on:
 - **Nitro** — universal server engine (runs on Node, Cloudflare Workers, Deno, Bun, etc.)
 - **Vite** — fast dev server and build tool
-- **Vue 3** — Composition API throughout
+- **Vue 3** — Composition API throughout (Nuxt 4.4+ ships Vue Router v5)
 - **Auto-imports** — no need to import `ref`, `computed`, `useFetch`, etc. — Nuxt imports them automatically
-- **File-based routing** — `pages/` directory maps to routes
+- **File-based routing** — `app/pages/` directory maps to routes
+
+---
+
+## Directory Structure (the Nuxt 4 change)
+
+Application code lives under `app/` (the srcDir); server code and config stay at the root. This separates app-environment code from server-environment code — faster dev-server boot, better IDE type inference per environment.
+
+```
+my-app/
+├── app/                  # srcDir — everything that runs in the Vue app
+│   ├── assets/
+│   ├── components/
+│   ├── composables/
+│   ├── layouts/
+│   ├── middleware/       # route middleware (client navigation)
+│   ├── pages/
+│   ├── plugins/
+│   ├── utils/
+│   ├── app.vue
+│   └── error.vue
+├── public/
+├── server/               # stays at root — Nitro (api/, routes/, middleware/)
+├── shared/               # code shared by app/ and server/ — import via #shared
+├── nuxt.config.ts
+└── package.json
+```
+
+Key rules:
+- **`~/` and `@/` resolve to `app/`**, not the project root. Helpers that sat at root level and were imported with `~/` need to move into `app/` (or `shared/`) or get an explicit alias.
+- **`shared/`** is the sanctioned home for code both environments import (types, validators, constants) via the `#shared` alias — server code must not import from `app/`.
+- **Auto-imports scan `app/`** — `app/composables/`, `app/components/`, `app/utils/` work exactly as their root-level Nuxt 3 counterparts did.
+
+**Nuxt 3 differences**: in Nuxt 3 all of these directories sat at the project root and `~/` pointed to the root. Nuxt 4 auto-detects the old flat layout and keeps working with it, so migration is mechanical — move the app directories (plus `app.vue` / `error.vue`) into `app/` and fix any `~/` imports that referenced root-level files.
 
 ---
 
@@ -61,12 +94,17 @@ export default defineNuxtConfig({
 
 ### useFetch — SSR-safe primary fetching
 
+Nuxt 4 semantics: `data`/`error` default to `undefined` (Nuxt 3 used `null`), results are
+shallow-reactive by default (`deep: false`), requests sharing a key are deduplicated and
+their state is cleaned up when the last consuming component unmounts. The `pending` ref is
+deprecated — branch on `status` (`'idle' | 'pending' | 'success' | 'error'`) instead.
+
 ```vue
 <script setup lang="ts">
 interface Post { id: number; title: string; body: string }
 
 // Automatically de-duplicates on server/client, serializes for hydration
-const { data: post, pending, error, refresh } = await useFetch<Post>(
+const { data: post, status, error, refresh } = await useFetch<Post>(
   '/api/posts/1',
   {
     key: 'post-1',                      // deduplicate key (auto-generated if omitted)
@@ -146,11 +184,11 @@ try {
 ```vue
 <script setup lang="ts">
 // lazy: true — don't block render, data loads async
-const { data: comments, pending } = useFetch('/api/comments', { lazy: true })
+const { data: comments, status } = useFetch('/api/comments', { lazy: true })
 </script>
 
 <template>
-  <div v-if="pending" class="skeleton">Loading comments...</div>
+  <div v-if="status === 'pending'" class="skeleton">Loading comments...</div>
   <CommentList v-else :comments="comments" />
 </template>
 ```
@@ -204,7 +242,7 @@ import { z } from 'zod'
 
 const CreateUserSchema = z.object({
   name: z.string().min(2).max(100),
-  email: z.string().email(),
+  email: z.email(),
   role: z.enum(['user', 'admin']).default('user'),
 })
 
@@ -588,7 +626,7 @@ useHead({
 ### Error page (error.vue)
 
 ```vue
-<!-- error.vue — root level, replaces app.vue on error -->
+<!-- app/error.vue — replaces app.vue on error -->
 <script setup lang="ts">
 const props = defineProps<{
   error: {
@@ -747,8 +785,12 @@ export default defineNuxtConfig({
 
 ### Querying content
 
+> The examples below use the Nuxt Content v2 `queryContent()` API. Nuxt Content v3
+> replaces it with typed collections (`content.config.ts` + `queryCollection()`); check
+> which major the project uses before reaching for either API.
+
 ```vue
-<!-- pages/blog/[slug].vue -->
+<!-- app/pages/blog/[slug].vue -->
 <script setup lang="ts">
 const route = useRoute()