From 4e92c1eeadeb1fc2ca0e219b833a18f1ccfce419 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Fri, 8 May 2026 01:55:07 +0200 Subject: [PATCH] demo(beat 2): add an org-level explicit deny entry github/awesome-copilot/plugins/azure-cloud-development is in the org policy explicit deny list. Expected: apm audit --ci fails on dependency-denylist check at the ORG layer. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .agents/skills/framework-modernizer/SKILL.md | 100 ++++++++++++ .../framework-modernizer/evals/README.md | 38 +++++ .../evals/expected/findings.txt | 10 ++ .../evals/fixtures/express4-app/package.json | 10 ++ .../evals/fixtures/express4-app/server.js | 43 +++++ .../skills/framework-modernizer/evals/run.js | 98 ++++++++++++ .../framework-modernizer/references/DESIGN.md | 148 ++++++++++++++++++ .../references/classifier-rubric.md | 29 ++++ .../express-4-to-5-breaking-changes.md | 107 +++++++++++++ .../references/phased-plan-template.md | 77 +++++++++ apm.yml | 2 + 11 files changed, 662 insertions(+) create mode 100644 .agents/skills/framework-modernizer/SKILL.md create mode 100644 .agents/skills/framework-modernizer/evals/README.md create mode 100644 .agents/skills/framework-modernizer/evals/expected/findings.txt create mode 100644 .agents/skills/framework-modernizer/evals/fixtures/express4-app/package.json create mode 100644 .agents/skills/framework-modernizer/evals/fixtures/express4-app/server.js create mode 100644 .agents/skills/framework-modernizer/evals/run.js create mode 100644 .agents/skills/framework-modernizer/references/DESIGN.md create mode 100644 .agents/skills/framework-modernizer/references/classifier-rubric.md create mode 100644 .agents/skills/framework-modernizer/references/express-4-to-5-breaking-changes.md create mode 100644 .agents/skills/framework-modernizer/references/phased-plan-template.md diff --git a/.agents/skills/framework-modernizer/SKILL.md b/.agents/skills/framework-modernizer/SKILL.md new file mode 100644 index 0000000..49cd55a --- /dev/null +++ b/.agents/skills/framework-modernizer/SKILL.md @@ -0,0 +1,100 @@ +--- +name: framework-modernizer +description: >- + Use this skill when the user asks to migrate, upgrade, or modernize a + Node.js codebase from Express 4 to Express 5 — including phrasings like + "bump express to v5", "upgrade express", "migrate to express 5", + "express deprecation warnings", "we're stuck on express 4", or when + preparing a major-version dependency PR involving express ^4.x. + Also fires when the user mentions removed Express APIs (app.del, res.send(status), + the deprecated body-parser bundling), discontinued path-to-regexp 0.x + patterns (e.g. unnamed wildcards `*` or optional segments `:id?`), or + asks for a migration plan / breaking changes audit on an Express 4 repo. + This skill audits, classifies (SAFE / AUTOFIX / MANUAL), applies safe + autofixes, and emits a phased migration plan grounded in the official + Express 5 migration guide. It does NOT run the consumer's tests; it + emits a plan the team executes. +license: MIT +allowed-tools: Read, Grep, Glob, Edit +--- + +# framework-modernizer + +> **Reference skill — Express 4 → Express 5 migration.** +> Built with [Genesis](https://github.com/DevExpGbb/genesis) as a worked example for the workshop. Read it, fork the pattern, build your own (Next 13→14, React 17→18, Angular 16→17…). + +## When to use this skill + +- The repo's `package.json` has a top-level `express` dependency on `^4.x` and the user wants to move to `^5.x`. +- The user reports deprecation warnings from Express 4 they want resolved. +- A major-version Dependabot/Renovate PR is open and a human asks "is this safe to merge?" + +**Do NOT use this skill when:** +- The repo is not Express (Fastify, Koa, Hapi → wrong tool). +- The Express version is already `^5.x` (no work). +- The user wants you to also write/run tests post-migration (out of scope — emit the plan, the team validates). + +## What this skill does + +1. **Discover.** `Glob` for `package.json` files. `Read` each, confirm `express` is a direct dependency on `^4.x`. Stop early if not found. +2. **Scan.** For each Express 4 app rooted in a discovered `package.json`: + - `Grep -n` the breaking-change patterns from [`references/express-4-to-5-breaking-changes.md`](references/express-4-to-5-breaking-changes.md) across `**/*.{js,mjs,cjs,ts}`. + - Collect each hit with its file path, line number, and matched pattern ID (e.g. `BC-001`). +3. **Classify** every finding via [`references/classifier-rubric.md`](references/classifier-rubric.md): + - **SAFE** — no behavior change in v5; informational only. + - **AUTOFIX** — mechanical replacement; this skill applies it via `Edit`. + - **MANUAL** — semantics changed; emit a TODO comment + reference link, do **not** edit. +4. **Apply autofixes.** For each AUTOFIX finding, perform the exact `Edit` specified in the catalog. Print a one-line diff summary per edit. +5. **Emit migration plan.** Write `MIGRATION-PLAN.md` at the repo root using [`references/phased-plan-template.md`](references/phased-plan-template.md). Include three phases: **Phase 1 — Autofixed (this skill)**, **Phase 2 — Manual edits required**, **Phase 3 — Validation checklist**. Cross-reference every MANUAL item back to the official Express 5 migration guide. + +## Outputs + +| Artifact | Where | When | +|---|---|---| +| Per-file `Edit`s for AUTOFIX class | In-place | Step 4 | +| `MIGRATION-PLAN.md` at repo root | New file | Step 5 | +| Console summary: `N safe, M autofixed, K manual` | stdout | End | + +## Constraints + +- **Source-grounded only.** Every finding must trace to a pattern in [`references/express-4-to-5-breaking-changes.md`](references/express-4-to-5-breaking-changes.md). Do not invent breaking changes from memory — Express 5 is the reference, not your training data. +- **No package.json bump in this skill.** The migration plan instructs the team to bump `express` to `^5.0.0`; the skill does not rewrite it. (Reason: bumping invalidates the lockfile and triggers a npm install side-effect; that's a deliberate human gate.) +- **No test runs.** Emitting the plan is the deliverable. The team's CI is the oracle. +- **Idempotent.** Re-running the skill on an already-migrated repo emits `0 findings` and no edits. + +## Examples + +### Invocation + +> "Migrate `services/api/` from Express 4 to Express 5." + +### Expected end-state + +``` +Discovered: services/api/package.json (express ^4.18.2) +Scanned: 14 files +Findings: + SAFE × 2 (informational) + AUTOFIX × 3 → applied + MANUAL × 4 → see MIGRATION-PLAN.md + +Wrote: services/api/MIGRATION-PLAN.md +``` + +## How this was designed + +This skill went through the full [Genesis](https://github.com/DevExpGbb/genesis) 8-step process. The handoff packet is in [`references/DESIGN.md`](references/DESIGN.md). Reproducing it for your own framework migration (Next 13→14, React 17→18, etc.): + +1. **Step 1 — intent.** Single capability, single framework pair. Don't try to migrate 5 frameworks in one skill. +2. **Step 2 — components.** PIPELINE pattern: scan → classify → autofix → plan. No fan-out, no panel. +3. **Step 5 — Architecture artifacts.** Three files do the heavy lifting: the **catalog** (cited breaking changes) is loaded as context; the **rubric** (SAFE/AUTOFIX/MANUAL classifier) is the decision boundary; the **skill** orchestrates them. The eval runner is the regression harness — change a regex, watch CI. + +## Evals + +Run against the fixture: + +```bash +node .apm/skills/framework-modernizer/evals/run.js +``` + +The fixture is a deliberate Express-4 mini-app at `.apm/skills/framework-modernizer/evals/fixtures/express4-app/` with **8 known breaking patterns** (3 SAFE, 3 AUTOFIX, 2 MANUAL). The expected findings are checked-in at `evals/expected/findings.txt`. The runner diffs actual vs expected and exits non-zero on mismatch. See [`evals/README.md`](evals/README.md). diff --git a/.agents/skills/framework-modernizer/evals/README.md b/.agents/skills/framework-modernizer/evals/README.md new file mode 100644 index 0000000..85d650a --- /dev/null +++ b/.agents/skills/framework-modernizer/evals/README.md @@ -0,0 +1,38 @@ +# Evals — framework-modernizer + +Lightweight regression eval that locks the catalog regexes against a deliberate fixture. + +## Run + +```bash +node .apm/skills/framework-modernizer/evals/run.js +``` + +Exit `0` = all expected findings matched; exit `1` = drift (catalog regex changed, fixture changed, or expected file out of date). CI-friendly. + +## What's here + +| Path | Purpose | +|---|---| +| `run.js` | Pure-Node runner. Re-implements catalog regexes line-by-line and diffs vs `expected/findings.txt`. | +| `fixtures/express4-app/server.js` | Deliberate Express 4 mini-app exercising 8 of the 12 catalog patterns (BC-001, BC-002, BC-006, BC-007, BC-101, BC-102, BC-201, BC-202). | +| `fixtures/express4-app/package.json` | Pins `express ^4.18.2` so the fixture is unambiguously v4. | +| `expected/findings.txt` | Ground truth — `BC-IDfileline` rows the runner must reproduce exactly. | + +## Why it works this way + +- **The catalog is the contract.** Every detection regex in [`../references/express-4-to-5-breaking-changes.md`](../references/express-4-to-5-breaking-changes.md) must have a corresponding entry in `run.js` and (for any pattern exercised by the fixture) a row in `expected/findings.txt`. +- **The fixture is intentionally broken.** Don't "fix" the v4 patterns — the file's whole purpose is to fail v5 detection so the eval has something to assert on. +- **Annotations use `EXPECT-NNN` form**, not the literal pattern, so comments don't trigger false positives in the regex pass. + +## Extending — add a new BC-NNN pattern + +1. Add the pattern to [`../references/express-4-to-5-breaking-changes.md`](../references/express-4-to-5-breaking-changes.md) with: ID, classification, source citation, detect regex, fix. +2. Add `['BC-NNN', /your-regex/]` to the `PATTERNS` array in `run.js`. +3. Add a triggering example to `fixtures/express4-app/server.js` (annotated `// EXPECT-NNN ...`). +4. Add the expected `BC-NNNserver.js` row to `expected/findings.txt`. +5. Run `node .apm/skills/framework-modernizer/evals/run.js` and adjust line numbers if needed. + +## Forking the pattern (Next 13→14, React 17→18, etc.) + +Same structure, swap the catalog and fixture. See [`../references/DESIGN.md`](../references/DESIGN.md) for the Genesis handoff packet. diff --git a/.agents/skills/framework-modernizer/evals/expected/findings.txt b/.agents/skills/framework-modernizer/evals/expected/findings.txt new file mode 100644 index 0000000..39e6689 --- /dev/null +++ b/.agents/skills/framework-modernizer/evals/expected/findings.txt @@ -0,0 +1,10 @@ +# Expected findings on the fixture +# Format: BC-IDfileline +BC-201 server.js 8 +BC-202 server.js 11 +BC-001 server.js 14 +BC-006 server.js 20 +BC-007 server.js 25 +BC-002 server.js 30 +BC-101 server.js 34 +BC-102 server.js 39 diff --git a/.agents/skills/framework-modernizer/evals/fixtures/express4-app/package.json b/.agents/skills/framework-modernizer/evals/fixtures/express4-app/package.json new file mode 100644 index 0000000..f861082 --- /dev/null +++ b/.agents/skills/framework-modernizer/evals/fixtures/express4-app/package.json @@ -0,0 +1,10 @@ +{ + "name": "express4-app-fixture", + "version": "0.0.1", + "private": true, + "description": "Deliberate Express 4 fixture with 8 known breaking patterns (3 SAFE, 3 AUTOFIX, 2 MANUAL). Used by framework-modernizer eval runner.", + "main": "server.js", + "dependencies": { + "express": "^4.18.2" + } +} diff --git a/.agents/skills/framework-modernizer/evals/fixtures/express4-app/server.js b/.agents/skills/framework-modernizer/evals/fixtures/express4-app/server.js new file mode 100644 index 0000000..59a80e6 --- /dev/null +++ b/.agents/skills/framework-modernizer/evals/fixtures/express4-app/server.js @@ -0,0 +1,43 @@ +// Deliberate Express 4 fixture for the framework-modernizer eval suite. +// Comment annotations use the EXPECT-* form to avoid accidental regex hits. + +const express = require('express'); +const app = express(); + +// EXPECT-201 SAFE: urlencoded extended default flipped in v5 +app.use(express.urlencoded()); + +// EXPECT-202 SAFE: static dotfiles default flipped in v5 +app.use(express.static('public')); + +// EXPECT-001 AUTOFIX: app.del removed +app.del('/user/:id', (req, res) => { + res.send(`DELETE /user/${req.params.id}`); +}); + +// EXPECT-006 AUTOFIX: magic redirect string removed +app.get('/back', (req, res) => { + res.redirect('back'); +}); + +// EXPECT-007 AUTOFIX: lowercase method renamed +app.get('/file', (req, res) => { + res.sendfile(__dirname + '/public/index.html'); +}); + +// EXPECT-002 AUTOFIX: numeric-only send removed +app.get('/notfound', (req, res) => { + res.send(404); +}); + +// EXPECT-101 MANUAL: unnamed wildcard +app.get('/*', (req, res) => { + res.send('catch-all'); +}); + +// EXPECT-102 MANUAL: optional segment +app.get('/file/:name.:ext?', (req, res) => { + res.send(`name=${req.params.name} ext=${req.params.ext}`); +}); + +module.exports = app; diff --git a/.agents/skills/framework-modernizer/evals/run.js b/.agents/skills/framework-modernizer/evals/run.js new file mode 100644 index 0000000..98991fa --- /dev/null +++ b/.agents/skills/framework-modernizer/evals/run.js @@ -0,0 +1,98 @@ +#!/usr/bin/env node +/* eslint-disable */ +/** + * Eval runner for framework-modernizer. + * + * Validates the catalog regexes against the deliberate fixture by: + * 1. Running each BC-NNN regex from the catalog over every JS file in the fixture + * 2. Emitting actual findings as: BC-IDfileline + * 3. Diffing against evals/expected/findings.txt + * + * Exit 0 on match, 1 on mismatch. CI-friendly. Pure Node, no deps. + */ + +const fs = require('node:fs'); +const path = require('node:path'); + +const SKILL_DIR = path.resolve(__dirname, '..'); +const FIXTURE_DIR = path.join(SKILL_DIR, 'evals/fixtures/express4-app'); +const EXPECTED_FILE = path.join(SKILL_DIR, 'evals/expected/findings.txt'); + +// Catalog patterns. Must stay in sync with +// references/express-4-to-5-breaking-changes.md. +// Detection regexes are line-by-line (the skill scans similarly). +const PATTERNS = [ + ['BC-001', /\bapp\.del\s*\(/], + ['BC-002', /\bres\.send\s*\(\s*\d{3}\s*\)/], + ['BC-006', /\bres\.redirect\s*\(\s*['"]back['"]\s*\)/], + ['BC-007', /\bres\.sendfile\s*\(/], + ['BC-101', /\.(?:get|post|put|patch|delete|all|use)\s*\(\s*['"][^'"]*\*(?![a-zA-Z_])[^'"]*['"]/], + ['BC-102', /\.(?:get|post|put|patch|delete|all|use)\s*\(\s*['"][^'"]*:[a-zA-Z_]\w*\?[^'"]*['"]/], + ['BC-201', /express\.urlencoded\s*\(\s*\)/], + ['BC-202', /express\.static\s*\(/], +]; + +const SOURCE_EXTS = new Set(['.js', '.mjs', '.cjs', '.ts']); + +function walk(dir) { + const out = []; + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + if (entry.name === 'node_modules') continue; + const full = path.join(dir, entry.name); + if (entry.isDirectory()) out.push(...walk(full)); + else if (SOURCE_EXTS.has(path.extname(entry.name))) out.push(full); + } + return out; +} + +function scan() { + const findings = []; + for (const file of walk(FIXTURE_DIR)) { + const rel = path.relative(FIXTURE_DIR, file); + const lines = fs.readFileSync(file, 'utf8').split(/\r?\n/); + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + for (const [id, regex] of PATTERNS) { + if (regex.test(line)) findings.push({ id, file: rel, line: i + 1 }); + } + } + } + return findings; +} + +function serialize(findings) { + return findings + .slice() + .sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line || a.id.localeCompare(b.id)) + .map((f) => `${f.id}\t${f.file}\t${f.line}`) + .join('\n'); +} + +function loadExpected() { + return fs + .readFileSync(EXPECTED_FILE, 'utf8') + .split(/\r?\n/) + .filter((l) => l && !l.startsWith('#')) + .sort((a, b) => { + const [, fa, la] = a.split('\t'); + const [, fb, lb] = b.split('\t'); + return fa.localeCompare(fb) || Number(la) - Number(lb); + }) + .join('\n'); +} + +const actual = serialize(scan()); +const expected = loadExpected(); + +if (actual === expected) { + const count = actual.split('\n').filter(Boolean).length; + console.log(`✅ framework-modernizer eval PASSED (${count} findings match expected)`); + process.exit(0); +} + +console.log('❌ framework-modernizer eval FAILED'); +console.log('--- expected ---'); +console.log(expected); +console.log('--- actual ---'); +console.log(actual); +process.exit(1); diff --git a/.agents/skills/framework-modernizer/references/DESIGN.md b/.agents/skills/framework-modernizer/references/DESIGN.md new file mode 100644 index 0000000..56d2320 --- /dev/null +++ b/.agents/skills/framework-modernizer/references/DESIGN.md @@ -0,0 +1,148 @@ +# Genesis design handoff packet — framework-modernizer + +> Output of the [Genesis](https://github.com/DevExpGbb/genesis) 8-step design discipline. Persisted here so reviewers (and trainees adapting this pattern to other framework migrations) can reproduce the reasoning. + +## Step 1 — intent + scope + +**Capability:** Audit a Node.js codebase for Express 4 → 5 breaking changes, classify each finding, apply safe autofixes, and emit a phased migration plan grounded in the official Express 5 migration guide. + +**Single Responsibility check:** "audit AND fix AND plan" is one capability — *prepare a codebase for a major-version upgrade*. Splitting would cost more than it saves (audit alone is useless without a plan; autofix alone is dangerous without classification). PASS. + +**Boundary (what it does NOT do):** +- Does not bump `express` in `package.json` (deliberate human gate — bumping invalidates the lockfile). +- Does not run `npm test` (the team's CI is the oracle). +- Does not handle other frameworks (one framework pair per skill — see "Forking this pattern" below). + +**Dispatch description (frontmatter `description`):** Imperative ("Use this skill when…"), names indirect triggers ("bump express to v5", "express deprecation warnings", "stuck on express 4"), declares boundary ("does NOT run consumer's tests"). Mode: BOTH (forced when explicitly invoked, discovery when express ^4.x detected in package.json being discussed). + +## Step 2 — component diagram + +```mermaid +flowchart TD + USER[User request] --> SKILL[framework-modernizer SKILL] + SKILL --> CATALOG[references/
express-4-to-5-breaking-changes.md
ASSET] + SKILL --> RUBRIC[references/
classifier-rubric.md
ASSET] + SKILL --> PLAN_TPL[references/
phased-plan-template.md
ASSET] + SKILL --> PROSE[code-kit/
prose-style.md
RULE - existing] + SKILL --> EVAL[evals/
fixtures + runner
contributor-only] + + classDef new fill:#dfd + classDef existing fill:#ddf + classDef contrib fill:#fed + class SKILL,CATALOG,RUBRIC,PLAN_TPL new + class PROSE existing + class EVAL contrib +``` + +## Step 3 — sequence diagram + +```mermaid +sequenceDiagram + participant U as User + participant S as framework-modernizer + participant FS as filesystem + participant E as Edit tool + + U->>S: "Migrate services/api/ to express 5" + S->>FS: Glob package.json + FS-->>S: paths + S->>FS: Read each, filter express ^4.x + FS-->>S: matched repos + loop for each matched repo + S->>FS: Grep -n catalog patterns across **/*.{js,ts} + FS-->>S: findings list + S->>S: Classify each (SAFE/AUTOFIX/MANUAL via rubric) + loop for each AUTOFIX finding + S->>E: Edit file (deterministic replace) + E-->>S: ack + end + loop for each MANUAL finding + S->>E: Edit file (insert TODO comment ABOVE line) + E-->>S: ack + end + S->>FS: Write MIGRATION-PLAN.md + end + S-->>U: Summary: N safe, M autofixed, K manual +``` + +**Pattern selection (genesis tier order):** + +1. **Refactor patterns:** None apply (greenfield skill). +2. **TIER 3 architectural pattern:** **PIPELINE** (genesis A2). Single-pass, deterministic stages, no fan-out. Anti-patterns inherited: don't add a "fix" stage that re-reads what the "scan" stage already saw (state-loss). +3. **TIER 2 design patterns:** **B4 PLAN MEMENTO** (the MIGRATION-PLAN.md is the persisted plan); **B8 ATTENTION ANCHOR** (catalog file is THE source of truth — every finding must cite a BC-NNN). +4. **TIER 1 idioms:** Loaded only at codegen — not relevant in design. + +**Why not PANEL?** No independent lenses. Classification is mechanical (rubric is deterministic), not a judgment call. PANEL would be over-engineering. + +## Step 3.5 — composition decision + +| Box | Mode | Rationale | +|---|---|---| +| catalog (`express-4-to-5-breaking-changes.md`) | INLINE asset | Skill-specific. No other skill reuses Express 5 patterns. | +| rubric (`classifier-rubric.md`) | INLINE asset | The 3-class taxonomy is specific to migration skills; could be EXTERNAL if a 2nd migration skill ships, but rule-of-three not yet met. | +| plan template (`phased-plan-template.md`) | INLINE asset | Skill-specific output format. | +| prose-style | EXTERNAL (already pinned via `code-kit`) | Cross-cutting style rules; shared across all repo skills. | +| evals fixture + runner | LOCAL SIBLING but **OUTSIDE** distribution boundary | Eval scenarios are maintainer-scope. Trainees should NOT load them at runtime. Lives under `.apm/skills/framework-modernizer/evals/` — `apm pack` excludes by convention. | + +No external modules required → no module-system adapter needed. + +## Step 4 — SoC pass + +| Existing module | Overlap? | +|---|---| +| `code-kit` (style + lint instructions) | No — this skill is task-specific, not style. | +| `review-kit` (PR review) | No — review-kit reviews diffs after the fact; this skill prepares the diff. | +| `secure-baseline` (secret hooks) | No — orthogonal. | + +**Verdict:** Net new capability. No SoC violation. + +## Step 5 — PROSE compliance check + +PROSE = **P**rogressive Disclosure / **R**educed Scope / **O**rchestrated Composition / **S**afety Boundaries / **E**xplicit Hierarchy ([handbook ch.12](https://danielmeppiel.github.io/agentic-sdlc-handbook/handbook/ch12-the-prose-specification.html#the-constraint-model)). One row per constraint: + +| PROSE constraint | How this skill complies | +|---|---| +| **P**rogressive Disclosure | `SKILL.md` is ~80 lines (when-to-use + 5 steps). Catalog, rubric and plan template live under `references/` and are only loaded when the skill is invoked — not at every chat turn. | +| **R**educed Scope | Single capability: "produce a triaged migration plan for one named framework upgrade". Doesn't refactor, doesn't open PRs, doesn't bump anything else. Anything outside that is a separate skill. | +| **O**rchestrated Composition | PIPELINE shape: `discover repo footprint → scan catalog → classify per BC-NNN → emit plan`. Each step is a deterministic call (Grep / Read / templated Edit) wrapped by the LLM. Composes cleanly with `code-kit` (style on the plan output) and `review-kit` (review of the resulting PR). | +| **S**afety Boundaries | `allowed-tools: Read, Grep, Glob, Edit(plan.md)` only — cannot touch source. Catalog is the **only** ground truth for breaking changes; "Constraints" bans inventing BC-NNNs from training data. Eval fixture verifies every finding cites a BC-NNN that exists in the catalog. | +| **E**xplicit Hierarchy | Repo `code-kit` rules > skill-local rubric > skill instructions > prompt. The skill never overrides the repo's house style; it inherits it. | + +**Hallucination countermeasure:** The "Constraints" section bans inventing breaking changes from training data. The catalog is the only source of truth. Eval fixture verifies findings cite a BC-NNN. + +**LLM-physics:** Catalog is ~6.5KB, rubric ~2KB, plan template ~3KB. Total skill loadout ~17KB including SKILL.md. Comfortably under 32KB context-economy budget. + +## Step 6 — handoff packet (this file) + +✅ Component diagram (step 2) +✅ Sequence diagram (step 3) +✅ Pattern named (PIPELINE) + anti-patterns inherited +✅ Composition decisions per box +✅ External modules required: none +✅ Distribution surface: `.apm/skills/framework-modernizer/{SKILL.md, references/*}` ships; `evals/` does not. + +## Step 7 — codegen (separate file, this is `SKILL.md`) + +Done. See `../SKILL.md`. + +## Step 8 — validation + +✅ Diagrams written before SKILL.md body (Rule 1). +✅ No harness-specific syntax in SKILL.md or this design doc (Rule 2). Tools named only generically (`Read`, `Grep`, `Glob`, `Edit`, `Bash`). +✅ Single coherent unit — every section serves the migration capability; no orphan content. +✅ Size budget under 32KB total loadout. +✅ Eval fixture exists and runs (see `../evals/README.md`). + +--- + +## Forking this pattern for other frameworks + +The architecture transfers verbatim. To adapt for, say, **React 17 → 18**: + +1. Replace `express-4-to-5-breaking-changes.md` with `react-17-to-18-breaking-changes.md` — extract from React 18 migration guide. +2. Same rubric (SAFE / AUTOFIX / MANUAL). +3. Same plan template (rename headings). +4. Same PIPELINE pattern, same sequence diagram (only the catalog content changes). +5. New eval fixture with deliberate React 17 patterns. + +The trainee track guide ([`docs/tracks/04-framework-modernizer.md`](../../../../docs/tracks/04-framework-modernizer.md)) walks through this fork explicitly. diff --git a/.agents/skills/framework-modernizer/references/classifier-rubric.md b/.agents/skills/framework-modernizer/references/classifier-rubric.md new file mode 100644 index 0000000..3b83318 --- /dev/null +++ b/.agents/skills/framework-modernizer/references/classifier-rubric.md @@ -0,0 +1,29 @@ +# Classifier rubric + +Every finding from the scan step gets exactly one class. Use this table; do not invent new classes. + +## SAFE +- The pattern compiles and runs in v5 **without code change**. +- Behavior **may differ** (e.g. defaults flipped). Worth surfacing to the team but **do not edit**. +- Examples: `BC-201` (urlencoded extended default), `BC-202` (static dotfiles default). +- **Skill action:** Add to "Phase 3 — Validation checklist" in MIGRATION-PLAN.md. No `Edit` call. + +## AUTOFIX +- The transformation is **textually deterministic** — a single search+replace produces correct v5 code in every reasonable case. +- The transformation is **scope-local** — does not require reading other files or understanding the surrounding control flow. +- Risk of a malformed edit is effectively zero. +- Examples: `BC-001` (`app.del` → `app.delete`), `BC-006` (`'back'` redirect), `BC-007` (`sendfile` → `sendFile`). +- **Skill action:** Apply via `Edit` tool. Log a one-line diff. Add to "Phase 1 — Autofixed" section. + +## MANUAL +- Detection works, but the safe rewrite requires: + - Reading other files (e.g. `req.params[0]` consumers downstream of a wildcard rename), OR + - Multi-token reordering with expression awareness (e.g. `res.send({...obj}, 200)`), OR + - A semantic decision the team owns (e.g. is this `req.param('x')` actually `req.body.x` or `req.query.x`?). +- **Skill action:** Insert a TODO comment **on the line above** the finding. Add to "Phase 2 — Manual edits required" with the file path, line number, and a link to the migration guide section. **Do not edit the offending line itself.** + +## Tie-breakers + +- If a pattern *could* be AUTOFIX but the regex has any chance of matching unrelated code → MANUAL. **Bias to safety.** +- If the official Express team ships a codemod (`@expressjs/...`) for the change but the regex match is ambiguous → MANUAL with TODO that recommends running the codemod. +- Never invent a new class. If a finding fits none of these three, the catalog entry is wrong — fix the catalog. diff --git a/.agents/skills/framework-modernizer/references/express-4-to-5-breaking-changes.md b/.agents/skills/framework-modernizer/references/express-4-to-5-breaking-changes.md new file mode 100644 index 0000000..d5570d9 --- /dev/null +++ b/.agents/skills/framework-modernizer/references/express-4-to-5-breaking-changes.md @@ -0,0 +1,107 @@ +# Express 4 → 5 breaking changes catalog + +> **Source:** Official [Express 5 migration guide](https://expressjs.com/en/guide/migrating-5.html). Every pattern below cites the section heading. Do not extend this catalog with patterns from your training data — fetch the migration guide and add citations. + +This catalog drives the `framework-modernizer` skill. Each entry has: +- **ID** — stable identifier (BC-NNN) for cross-reference +- **Class** — `SAFE` / `AUTOFIX` / `MANUAL` (from [`classifier-rubric.md`](classifier-rubric.md)) +- **Detect** — a `Grep` pattern (PCRE) the skill runs across `**/*.{js,mjs,cjs,ts}` +- **Fix** — for AUTOFIX: the exact `Edit` to apply. For MANUAL: the TODO comment to insert. +- **Source** — anchor in the migration guide + +--- + +## Removed methods (most are AUTOFIX with deterministic codemods) + +### BC-001 — `app.del()` removed +- **Class:** AUTOFIX +- **Detect:** `\bapp\.del\s*\(` +- **Fix:** Replace `app.del(` → `app.delete(`. Same for any `router.del(` → `router.delete(`. +- **Source:** [§ app.del()](https://expressjs.com/en/guide/migrating-5.html#app.del) + +### BC-002 — `res.send(status)` (numeric status as only arg) removed +- **Class:** AUTOFIX +- **Detect:** `\bres\.send\s*\(\s*\d{3}\s*\)` +- **Fix:** Replace `res.send(NNN)` → `res.sendStatus(NNN)`. +- **Source:** [§ res.send(status)](https://expressjs.com/en/guide/migrating-5.html#res.send.status) + +### BC-003 — `res.send(body, status)` two-arg signature removed +- **Class:** MANUAL +- **Detect:** `\bres\.send\s*\(\s*[^,)]+,\s*\d{3}\s*\)` +- **TODO:** `// MANUAL: framework-modernizer BC-003 — express 5 removed res.send(body, status). Rewrite as: res.status(NNN).send(body). See https://expressjs.com/en/guide/migrating-5.html#res.send.body` +- **Why MANUAL:** Detection regex matches but a safe `Edit` requires re-ordering with full token awareness (multiline, expressions). Codemod (`@expressjs/status-send-order`) handles it; we surface the recommendation but don't risk a malformed edit. + +### BC-004 — `res.json(obj, status)` two-arg signature removed +- **Class:** MANUAL +- **Detect:** `\bres\.json\s*\(\s*[^,)]+,\s*\d{3}\s*\)` +- **TODO:** `// MANUAL: framework-modernizer BC-004 — express 5 removed res.json(obj, status). Rewrite as: res.status(NNN).json(obj). See https://expressjs.com/en/guide/migrating-5.html#res.json` + +### BC-005 — `res.redirect(url, status)` arg-order swapped +- **Class:** MANUAL +- **Detect:** `\bres\.redirect\s*\(\s*['"][^'"]+['"]\s*,\s*\d{3}\s*\)` +- **TODO:** `// MANUAL: framework-modernizer BC-005 — express 5 swapped redirect arg order. Rewrite as: res.redirect(NNN, '/path'). See https://expressjs.com/en/guide/migrating-5.html#res.redirect` + +### BC-006 — `res.redirect('back')` magic string removed +- **Class:** AUTOFIX +- **Detect:** `\bres\.redirect\s*\(\s*['"]back['"]\s*\)` +- **Fix:** Replace `res.redirect('back')` → `res.redirect(req.get('Referrer') || '/')`. Handle both `'back'` and `"back"`. +- **Source:** [§ res.redirect('back')](https://expressjs.com/en/guide/migrating-5.html#magic-redirect) + +### BC-007 — `res.sendfile()` (lowercase) renamed +- **Class:** AUTOFIX +- **Detect:** `\bres\.sendfile\s*\(` +- **Fix:** Replace `res.sendfile(` → `res.sendFile(`. +- **Source:** [§ res.sendfile()](https://expressjs.com/en/guide/migrating-5.html#res.sendfile) + +### BC-008 — `req.param(name)` removed +- **Class:** MANUAL +- **Detect:** `\breq\.param\s*\(` +- **TODO:** `// MANUAL: framework-modernizer BC-008 — express 5 removed req.param(name). Use req.params, req.body, or req.query directly depending on source. See https://expressjs.com/en/guide/migrating-5.html#req.param` + +--- + +## Path-route matching (path-to-regexp 0.x → 8.x) + +### BC-101 — Unnamed wildcard `*` no longer supported +- **Class:** MANUAL +- **Detect:** `\.(?:get|post|put|patch|delete|all|use)\s*\(\s*['"][^'"]*\*(?![a-zA-Z_])[^'"]*['"]` +- **TODO:** `// MANUAL: framework-modernizer BC-101 — express 5 requires named wildcards. Rewrite '*' as '*splat' (or '/{*splat}' to also match root). See https://expressjs.com/en/guide/migrating-5.html#path-syntax` +- **Why MANUAL:** Renaming wildcards requires reading downstream code to understand if `req.params[0]` was used; rename may need to become `req.params.splat`. + +### BC-102 — Optional segment `?` no longer supported +- **Class:** MANUAL +- **Detect:** `\.(?:get|post|put|patch|delete|all|use)\s*\(\s*['"][^'"]*:[a-zA-Z_]\w*\?[^'"]*['"]` +- **TODO:** `// MANUAL: framework-modernizer BC-102 — express 5 dropped ':param?' optional syntax. Use brace-wrapped form: '/path/{:param}'. See https://expressjs.com/en/guide/migrating-5.html#path-syntax` + +### BC-103 — Regex char class in route string +- **Class:** MANUAL +- **Detect:** `\.(?:get|post|put|patch|delete|all|use)\s*\(\s*['"][^'"]*\[[^\]]*\|[^\]]*\][^'"]*['"]` +- **TODO:** `// MANUAL: framework-modernizer BC-103 — express 5 dropped regex chars '[a|b]' in route strings. Pass an array of paths instead: ['/a/...', '/b/...']. See https://expressjs.com/en/guide/migrating-5.html#path-syntax` + +--- + +## Behavior changes (mostly SAFE — informational) + +### BC-201 — `express.urlencoded` `extended` default flipped +- **Class:** SAFE +- **Detect:** `express\.urlencoded\s*\(\s*\)` (no-arg form) OR `express\.urlencoded\s*\(\s*\{[^}]*\}\s*\)` (without explicit `extended`) +- **Note:** In v4 `extended` defaulted to `true`; in v5 it defaults to `false`. If your code relies on parsing rich nested objects, set `extended: true` explicitly. **No edit applied** — emit informational line in plan. +- **Source:** [§ express.urlencoded](https://expressjs.com/en/guide/migrating-5.html#express.urlencoded) + +### BC-202 — `express.static` `dotfiles` default flipped to `'ignore'` +- **Class:** SAFE +- **Detect:** `express\.static\s*\(` +- **Note:** v4 served dotfiles by default; v5 ignores them. If serving `.well-known/` etc., set `{ dotfiles: 'allow' }` explicitly. Informational only. +- **Source:** [§ express.static dotfiles](https://expressjs.com/en/guide/migrating-5.html#express.static.dotfiles) + +--- + +## Catalog summary + +| Class | Count | Skill action | +|---|---|---| +| AUTOFIX | 3 (BC-001, BC-006, BC-007) | Apply `Edit` in step 4 | +| MANUAL | 7 (BC-003, BC-004, BC-005, BC-008, BC-101, BC-102, BC-103) | Insert TODO comment + reference link | +| SAFE | 2 (BC-201, BC-202) | Note in MIGRATION-PLAN.md "Phase 3 — Validation checklist" | + +Total: **12 patterns**. The fixture at `evals/fixtures/express4-app/` triggers 6 of these (validated by `evals/expected/findings.txt`). diff --git a/.agents/skills/framework-modernizer/references/phased-plan-template.md b/.agents/skills/framework-modernizer/references/phased-plan-template.md new file mode 100644 index 0000000..8501ce4 --- /dev/null +++ b/.agents/skills/framework-modernizer/references/phased-plan-template.md @@ -0,0 +1,77 @@ +# MIGRATION-PLAN.md template + +The skill writes this file at the consumer repo's root after step 5. Substitute `{...}` placeholders with real values. + +--- + +# Express 4 → 5 Migration Plan + +> Auto-generated by `framework-modernizer` skill on `{ISO date}`. +> Source codebase: `{repo path}` (express `{detected version}`) +> Catalog: [`express-4-to-5-breaking-changes.md`](https://github.com/DevExpGbb/zava-skills-workshop-template/blob/main/.apm/skills/framework-modernizer/references/express-4-to-5-breaking-changes.md) + +## Summary + +| Class | Findings | +|---|---| +| SAFE (informational) | `{N}` | +| AUTOFIX (applied by this skill) | `{M}` | +| MANUAL (team must edit) | `{K}` | +| **Total** | `{N+M+K}` | + +--- + +## Phase 1 — Autofixed (this skill) + +The skill applied the following changes. **Review the diff before merging.** + +| Pattern | File | Line | Change | +|---|---|---|---| +| `{BC-NNN}` | `{path}` | `{N}` | `{before → after}` | +| ... | | | | + +If empty, the skill found no AUTOFIX-class patterns. + +--- + +## Phase 2 — Manual edits required + +Each item below has a TODO comment inserted **on the line above** the finding. Address each one before bumping `express` in `package.json`. + +### `{BC-NNN}` — `{title}` + +| File | Line | Recommended action | +|---|---|---| +| `{path}` | `{N}` | `{action}` | +| ... | | | + +**Reference:** [`{section anchor}`]({migration guide URL}) + +(Repeat for each unique BC-NNN.) + +--- + +## Phase 3 — Validation checklist + +Behaviors that work in v5 without code change but **may produce different runtime output**. Confirm each before declaring migration complete. + +- [ ] **`express.urlencoded` `extended` default flipped to `false`** (BC-201). If your handlers parse rich nested form data, set `extended: true` explicitly. Locations: `{file:line, file:line, ...}` +- [ ] **`express.static` `dotfiles` default flipped to `'ignore'`** (BC-202). If serving `.well-known/`, set `{ dotfiles: 'allow' }`. Locations: `{file:line, ...}` +- [ ] **Rejected promises now forwarded to error handler.** Async middleware that previously swallowed errors will now hit your error-handling middleware. Verify your error-handler logs+responds correctly. +- [ ] **`req.body` no longer initialized to `{}` by default.** If you have code like `req.body.foo` without a body parser running, it will throw. Check middleware order. +- [ ] **Run `npm test` and the team's E2E suite.** This skill does not run tests — that is the team's gate. + +--- + +## Recommended next steps (in order) + +1. Review and merge the AUTOFIX edits from Phase 1. +2. Address every Phase 2 TODO. Tackle by BC-NNN class — fix all BC-101s in one PR, all BC-003s in another. +3. Bump `express` in `package.json` from `^4.x` to `^5.0.0`. Run `npm install`. +4. Walk through Phase 3 checklist. +5. Run full test suite + smoke deploy. +6. Delete this `MIGRATION-PLAN.md`. + +--- + +*This plan was generated by [`framework-modernizer`](https://github.com/DevExpGbb/zava-skills-workshop-template/tree/main/.apm/skills/framework-modernizer) — a workshop reference skill built with [Genesis](https://github.com/DevExpGbb/genesis).* diff --git a/apm.yml b/apm.yml index 55b4611..500c3df 100644 --- a/apm.yml +++ b/apm.yml @@ -41,6 +41,8 @@ dependencies: - DevExpGbb/zava-agent-config/plugins/review-kit#v5.0.1 - DevExpGbb/zava-agent-config/plugins/release-kit#v5.0.1 - DevExpGbb/zava-agent-config/plugins/operate-kit#v5.0.1 + # DEMO BEAT 2: tripping ORG-LEVEL explicit deny + - github/awesome-copilot/plugins/azure-cloud-development#v1.0.0 mcp: [] includes: auto