[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6DB14H8KCQKHFS69GCA8DS4":3,"skill-same:01M6DB14H8KCQKHFS69GCA8DS4":75},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":31,"publishedAt":49,"linkOnly":50,"markdownContent":51,"markdownBody":52,"frontMatter":53,"refName":55,"commitSha":56,"skillDirectory":57,"forksCount":14,"repositoryHtmlUrl":58,"repositoryHomepageUrl":6,"ownerHtmlUrl":59,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":60,"install":61,"resources":74},"01M6DB14H8KCQKHFS69GCA8DS4","ai-regression-testing",null,"Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code.","Prekzursil\u002Fagent-skills-toolchain","Prekzursil","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F54636077?v=4","01M6DACZ1PCQHD99SCR1YXG28B","Andrei Mihai Visalon","01M6DACZ670WZF535V3WYYSS4Z",0,"MIT","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fai-regression-testing\u002FSKILL.md",[18,23,26],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":27,"categoryName":28,"taxonomyType":29,"taxonomyTypeDesc":30},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48],"ai regression testing","testing","regression","model","strategies","dependencies","patterns","spots","bug","api testing","check","api","database","code","development","workflows","sandbox","2026-11-10 10:12:30",false,"---\nname: ai-regression-testing\ndescription: Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code.\norigin: ECC\n---\n\n# AI Regression Testing\n\nTesting patterns specifically designed for AI-assisted development, where the same model writes code and reviews it — creating systematic blind spots that only automated tests can catch.\n\n## When to Activate\n\n- AI agent (Claude Code, Cursor, Codex) has modified API routes or backend logic\n- A bug was found and fixed — need to prevent re-introduction\n- Project has a sandbox\u002Fmock mode that can be leveraged for DB-free testing\n- Running `\u002Fbug-check` or similar review commands after code changes\n- Multiple code paths exist (sandbox vs production, feature flags, etc.)\n\n## The Core Problem\n\nWhen an AI writes code and then reviews its own work, it carries the same assumptions into both steps. This creates a predictable failure pattern:\n\n```\nAI writes fix → AI reviews fix → AI says \"looks correct\" → Bug still exists\n```\n\n**Real-world example** (observed in production):\n\n```\nFix 1: Added notification_settings to API response\n  → Forgot to add it to the SELECT query\n  → AI reviewed and missed it (same blind spot)\n\nFix 2: Added it to SELECT query\n  → TypeScript build error (column not in generated types)\n  → AI reviewed Fix 1 but didn't catch the SELECT issue\n\nFix 3: Changed to SELECT *\n  → Fixed production path, forgot sandbox path\n  → AI reviewed and missed it AGAIN (4th occurrence)\n\nFix 4: Test caught it instantly on first run ✅\n```\n\nThe pattern: **sandbox\u002Fproduction path inconsistency** is the #1 AI-introduced regression.\n\n## Sandbox-Mode API Testing\n\nMost projects with AI-friendly architecture have a sandbox\u002Fmock mode. This is the key to fast, DB-free API testing.\n\n### Setup (Vitest + Next.js App Router)\n\n```typescript\n\u002F\u002F vitest.config.ts\nimport { defineConfig } from \"vitest\u002Fconfig\";\nimport path from \"path\";\n\nexport default defineConfig({\n  test: {\n    environment: \"node\",\n    globals: true,\n    include: [\"__tests__\u002F**\u002F*.test.ts\"],\n    setupFiles: [\"__tests__\u002Fsetup.ts\"],\n  },\n  resolve: {\n    alias: {\n      \"@\": path.resolve(__dirname, \".\"),\n    },\n  },\n});\n```\n\n```typescript\n\u002F\u002F __tests__\u002Fsetup.ts\n\u002F\u002F Force sandbox mode — no database needed\nprocess.env.SANDBOX_MODE = \"true\";\nprocess.env.NEXT_PUBLIC_SUPABASE_URL = \"\";\nprocess.env.NEXT_PUBLIC_SUPABASE_ANON_KEY = \"\";\n```\n\n### Test Helper for Next.js API Routes\n\n```typescript\n\u002F\u002F __tests__\u002Fhelpers.ts\nimport { NextRequest } from \"next\u002Fserver\";\n\nexport function createTestRequest(\n  url: string,\n  options?: {\n    method?: string;\n    body?: Record\u003Cstring, unknown>;\n    headers?: Record\u003Cstring, string>;\n    sandboxUserId?: string;\n  },\n): NextRequest {\n  const { method = \"GET\", body, headers = {}, sandboxUserId } = options || {};\n  const fullUrl = url.startsWith(\"http\") ? url : `http:\u002F\u002Flocalhost:3000${url}`;\n  const reqHeaders: Record\u003Cstring, string> = { ...headers };\n\n  if (sandboxUserId) {\n    reqHeaders[\"x-sandbox-user-id\"] = sandboxUserId;\n  }\n\n  const init: { method: string; headers: Record\u003Cstring, string>; body?: string } = {\n    method,\n    headers: reqHeaders,\n  };\n\n  if (body) {\n    init.body = JSON.stringify(body);\n    reqHeaders[\"content-type\"] = \"application\u002Fjson\";\n  }\n\n  return new NextRequest(fullUrl, init);\n}\n\nexport async function parseResponse(response: Response) {\n  const json = await response.json();\n  return { status: response.status, json };\n}\n```\n\n### Writing Regression Tests\n\nThe key principle: **write tests for bugs that were found, not for code that works**.\n\n```typescript\n\u002F\u002F __tests__\u002Fapi\u002Fuser\u002Fprofile.test.ts\nimport { describe, it, expect } from \"vitest\";\nimport { createTestRequest, parseResponse } from \"..\u002F..\u002Fhelpers\";\nimport { GET, PATCH } from \"@\u002Fapp\u002Fapi\u002Fuser\u002Fprofile\u002Froute\";\n\n\u002F\u002F Define the contract — what fields MUST be in the response\nconst REQUIRED_FIELDS = [\n  \"id\",\n  \"email\",\n  \"full_name\",\n  \"phone\",\n  \"role\",\n  \"created_at\",\n  \"avatar_url\",\n  \"notification_settings\",  \u002F\u002F ← Added after bug found it missing\n];\n\ndescribe(\"GET \u002Fapi\u002Fuser\u002Fprofile\", () => {\n  it(\"returns all required fields\", async () => {\n    const req = createTestRequest(\"\u002Fapi\u002Fuser\u002Fprofile\");\n    const res = await GET(req);\n    const { status, json } = await parseResponse(res);\n\n    expect(status).toBe(200);\n    for (const field of REQUIRED_FIELDS) {\n      expect(json.data).toHaveProperty(field);\n    }\n  });\n\n  \u002F\u002F Regression test — this exact bug was introduced by AI 4 times\n  it(\"notification_settings is not undefined (BUG-R1 regression)\", async () => {\n    const req = createTestRequest(\"\u002Fapi\u002Fuser\u002Fprofile\");\n    const res = await GET(req);\n    const { json } = await parseResponse(res);\n\n    expect(\"notification_settings\" in json.data).toBe(true);\n    const ns = json.data.notification_settings;\n    expect(ns === null || typeof ns === \"object\").toBe(true);\n  });\n});\n```\n\n### Testing Sandbox\u002FProduction Parity\n\nThe most common AI regression: fixing production path but forgetting sandbox path (or vice versa).\n\n```typescript\n\u002F\u002F Test that sandbox responses match the expected contract\ndescribe(\"GET \u002Fapi\u002Fuser\u002Fmessages (conversation list)\", () => {\n  it(\"includes partner_name in sandbox mode\", async () => {\n    const req = createTestRequest(\"\u002Fapi\u002Fuser\u002Fmessages\", {\n      sandboxUserId: \"user-001\",\n    });\n    const res = await GET(req);\n    const { json } = await parseResponse(res);\n\n    \u002F\u002F This caught a bug where partner_name was added\n    \u002F\u002F to production path but not sandbox path\n    if (json.data.length > 0) {\n      for (const conv of json.data) {\n        expect(\"partner_name\" in conv).toBe(true);\n      }\n    }\n  });\n});\n```\n\n## Integrating Tests into Bug-Check Workflow\n\n### Custom Command Definition\n\n```markdown\n\u003C!-- .claude\u002Fcommands\u002Fbug-check.md -->\n# Bug Check\n\n## Step 1: Automated Tests (mandatory, cannot skip)\n\nRun these commands FIRST before any code review:\n\n    npm run test       # Vitest test suite\n    npm run build      # TypeScript type check + build\n\n- If tests fail → report as highest priority bug\n- If build fails → report type errors as highest priority\n- Only proceed to Step 2 if both pass\n\n## Step 2: Code Review (AI review)\n\n1. Sandbox \u002F production path consistency\n2. API response shape matches frontend expectations\n3. SELECT clause completeness\n4. Error handling with rollback\n5. Optimistic update race conditions\n\n## Step 3: For each bug fixed, propose a regression test\n```\n\n### The Workflow\n\n```\nUser: \"バグチェックして\" (or \"\u002Fbug-check\")\n  │\n  ├─ Step 1: npm run test\n  │   ├─ FAIL → Bug found mechanically (no AI judgment needed)\n  │   └─ PASS → Continue\n  │\n  ├─ Step 2: npm run build\n  │   ├─ FAIL → Type error found mechanically\n  │   └─ PASS → Continue\n  │\n  ├─ Step 3: AI code review (with known blind spots in mind)\n  │   └─ Findings reported\n  │\n  └─ Step 4: For each fix, write a regression test\n      └─ Next bug-check catches if fix breaks\n```\n\n## Common AI Regression Patterns\n\n### Pattern 1: Sandbox\u002FProduction Path Mismatch\n\n**Frequency**: Most common (observed in 3 out of 4 regressions)\n\n```typescript\n\u002F\u002F ❌ AI adds field to production path only\nif (isSandboxMode()) {\n  return { data: { id, email, name } };  \u002F\u002F Missing new field\n}\n\u002F\u002F Production path\nreturn { data: { id, email, name, notification_settings } };\n\n\u002F\u002F ✅ Both paths must return the same shape\nif (isSandboxMode()) {\n  return { data: { id, email, name, notification_settings: null } };\n}\nreturn { data: { id, email, name, notification_settings } };\n```\n\n**Test to catch it**:\n\n```typescript\nit(\"sandbox and production return same fields\", async () => {\n  \u002F\u002F In test env, sandbox mode is forced ON\n  const res = await GET(createTestRequest(\"\u002Fapi\u002Fuser\u002Fprofile\"));\n  const { json } = await parseResponse(res);\n\n  for (const field of REQUIRED_FIELDS) {\n    expect(json.data).toHaveProperty(field);\n  }\n});\n```\n\n### Pattern 2: SELECT Clause Omission\n\n**Frequency**: Common with Supabase\u002FPrisma when adding new columns\n\n```typescript\n\u002F\u002F ❌ New column added to response but not to SELECT\nconst { data } = await supabase\n  .from(\"users\")\n  .select(\"id, email, name\")  \u002F\u002F notification_settings not here\n  .single();\n\nreturn { data: { ...data, notification_settings: data.notification_settings } };\n\u002F\u002F → notification_settings is always undefined\n\n\u002F\u002F ✅ Use SELECT * or explicitly include new columns\nconst { data } = await supabase\n  .from(\"users\")\n  .select(\"*\")\n  .single();\n```\n\n### Pattern 3: Error State Leakage\n\n**Frequency**: Moderate — when adding error handling to existing components\n\n```typescript\n\u002F\u002F ❌ Error state set but old data not cleared\ncatch (err) {\n  setError(\"Failed to load\");\n  \u002F\u002F reservations still shows data from previous tab!\n}\n\n\u002F\u002F ✅ Clear related state on error\ncatch (err) {\n  setReservations([]);  \u002F\u002F Clear stale data\n  setError(\"Failed to load\");\n}\n```\n\n### Pattern 4: Optimistic Update Without Proper Rollback\n\n```typescript\n\u002F\u002F ❌ No rollback on failure\nconst handleRemove = async (id: string) => {\n  setItems(prev => prev.filter(i => i.id !== id));\n  await fetch(`\u002Fapi\u002Fitems\u002F${id}`, { method: \"DELETE\" });\n  \u002F\u002F If API fails, item is gone from UI but still in DB\n};\n\n\u002F\u002F ✅ Capture previous state and rollback on failure\nconst handleRemove = async (id: string) => {\n  const prevItems = [...items];\n  setItems(prev => prev.filter(i => i.id !== id));\n  try {\n    const res = await fetch(`\u002Fapi\u002Fitems\u002F${id}`, { method: \"DELETE\" });\n    if (!res.ok) throw new Error(\"API error\");\n  } catch {\n    setItems(prevItems);  \u002F\u002F Rollback\n    alert(\"削除に失敗しました\");\n  }\n};\n```\n\n## Strategy: Test Where Bugs Were Found\n\nDon't aim for 100% coverage. Instead:\n\n```\nBug found in \u002Fapi\u002Fuser\u002Fprofile     → Write test for profile API\nBug found in \u002Fapi\u002Fuser\u002Fmessages    → Write test for messages API\nBug found in \u002Fapi\u002Fuser\u002Ffavorites   → Write test for favorites API\nNo bug in \u002Fapi\u002Fuser\u002Fnotifications  → Don't write test (yet)\n```\n\n**Why this works with AI development:**\n\n1. AI tends to make the **same category of mistake** repeatedly\n2. Bugs cluster in complex areas (auth, multi-path logic, state management)\n3. Once tested, that exact regression **cannot happen again**\n4. Test count grows organically with bug fixes — no wasted effort\n\n## Quick Reference\n\n| AI Regression Pattern | Test Strategy | Priority |\n|---|---|---|\n| Sandbox\u002Fproduction mismatch | Assert same response shape in sandbox mode | ? High |\n| SELECT clause omission | Assert all required fields in response | ? High |\n| Error state leakage | Assert state cleanup on error | ? Medium |\n| Missing rollback | Assert state restored on API failure | ? Medium |\n| Type cast masking null | Assert field is not undefined | ? Medium |\n\n## DO \u002F DON'T\n\n**DO:**\n- Write tests immediately after finding a bug (before fixing it if possible)\n- Test the API response shape, not the implementation\n- Run tests as the first step of every bug-check\n- Keep tests fast (\u003C 1 second total with sandbox mode)\n- Name tests after the bug they prevent (e.g., \"BUG-R1 regression\")\n\n**DON'T:**\n- Write tests for code that has never had a bug\n- Trust AI self-review as a substitute for automated tests\n- Skip sandbox path testing because \"it's just mock data\"\n- Write integration tests when unit tests suffice\n- Aim for coverage percentage — aim for regression prevention\n","# AI Regression Testing\n\nTesting patterns specifically designed for AI-assisted development, where the same model writes code and reviews it — creating systematic blind spots that only automated tests can catch.\n\n## When to Activate\n\n- AI agent (Claude Code, Cursor, Codex) has modified API routes or backend logic\n- A bug was found and fixed — need to prevent re-introduction\n- Project has a sandbox\u002Fmock mode that can be leveraged for DB-free testing\n- Running `\u002Fbug-check` or similar review commands after code changes\n- Multiple code paths exist (sandbox vs production, feature flags, etc.)\n\n## The Core Problem\n\nWhen an AI writes code and then reviews its own work, it carries the same assumptions into both steps. This creates a predictable failure pattern:\n\n```\nAI writes fix → AI reviews fix → AI says \"looks correct\" → Bug still exists\n```\n\n**Real-world example** (observed in production):\n\n```\nFix 1: Added notification_settings to API response\n  → Forgot to add it to the SELECT query\n  → AI reviewed and missed it (same blind spot)\n\nFix 2: Added it to SELECT query\n  → TypeScript build error (column not in generated types)\n  → AI reviewed Fix 1 but didn't catch the SELECT issue\n\nFix 3: Changed to SELECT *\n  → Fixed production path, forgot sandbox path\n  → AI reviewed and missed it AGAIN (4th occurrence)\n\nFix 4: Test caught it instantly on first run ✅\n```\n\nThe pattern: **sandbox\u002Fproduction path inconsistency** is the #1 AI-introduced regression.\n\n## Sandbox-Mode API Testing\n\nMost projects with AI-friendly architecture have a sandbox\u002Fmock mode. This is the key to fast, DB-free API testing.\n\n### Setup (Vitest + Next.js App Router)\n\n```typescript\n\u002F\u002F vitest.config.ts\nimport { defineConfig } from \"vitest\u002Fconfig\";\nimport path from \"path\";\n\nexport default defineConfig({\n  test: {\n    environment: \"node\",\n    globals: true,\n    include: [\"__tests__\u002F**\u002F*.test.ts\"],\n    setupFiles: [\"__tests__\u002Fsetup.ts\"],\n  },\n  resolve: {\n    alias: {\n      \"@\": path.resolve(__dirname, \".\"),\n    },\n  },\n});\n```\n\n```typescript\n\u002F\u002F __tests__\u002Fsetup.ts\n\u002F\u002F Force sandbox mode — no database needed\nprocess.env.SANDBOX_MODE = \"true\";\nprocess.env.NEXT_PUBLIC_SUPABASE_URL = \"\";\nprocess.env.NEXT_PUBLIC_SUPABASE_ANON_KEY = \"\";\n```\n\n### Test Helper for Next.js API Routes\n\n```typescript\n\u002F\u002F __tests__\u002Fhelpers.ts\nimport { NextRequest } from \"next\u002Fserver\";\n\nexport function createTestRequest(\n  url: string,\n  options?: {\n    method?: string;\n    body?: Record\u003Cstring, unknown>;\n    headers?: Record\u003Cstring, string>;\n    sandboxUserId?: string;\n  },\n): NextRequest {\n  const { method = \"GET\", body, headers = {}, sandboxUserId } = options || {};\n  const fullUrl = url.startsWith(\"http\") ? url : `http:\u002F\u002Flocalhost:3000${url}`;\n  const reqHeaders: Record\u003Cstring, string> = { ...headers };\n\n  if (sandboxUserId) {\n    reqHeaders[\"x-sandbox-user-id\"] = sandboxUserId;\n  }\n\n  const init: { method: string; headers: Record\u003Cstring, string>; body?: string } = {\n    method,\n    headers: reqHeaders,\n  };\n\n  if (body) {\n    init.body = JSON.stringify(body);\n    reqHeaders[\"content-type\"] = \"application\u002Fjson\";\n  }\n\n  return new NextRequest(fullUrl, init);\n}\n\nexport async function parseResponse(response: Response) {\n  const json = await response.json();\n  return { status: response.status, json };\n}\n```\n\n### Writing Regression Tests\n\nThe key principle: **write tests for bugs that were found, not for code that works**.\n\n```typescript\n\u002F\u002F __tests__\u002Fapi\u002Fuser\u002Fprofile.test.ts\nimport { describe, it, expect } from \"vitest\";\nimport { createTestRequest, parseResponse } from \"..\u002F..\u002Fhelpers\";\nimport { GET, PATCH } from \"@\u002Fapp\u002Fapi\u002Fuser\u002Fprofile\u002Froute\";\n\n\u002F\u002F Define the contract — what fields MUST be in the response\nconst REQUIRED_FIELDS = [\n  \"id\",\n  \"email\",\n  \"full_name\",\n  \"phone\",\n  \"role\",\n  \"created_at\",\n  \"avatar_url\",\n  \"notification_settings\",  \u002F\u002F ← Added after bug found it missing\n];\n\ndescribe(\"GET \u002Fapi\u002Fuser\u002Fprofile\", () => {\n  it(\"returns all required fields\", async () => {\n    const req = createTestRequest(\"\u002Fapi\u002Fuser\u002Fprofile\");\n    const res = await GET(req);\n    const { status, json } = await parseResponse(res);\n\n    expect(status).toBe(200);\n    for (const field of REQUIRED_FIELDS) {\n      expect(json.data).toHaveProperty(field);\n    }\n  });\n\n  \u002F\u002F Regression test — this exact bug was introduced by AI 4 times\n  it(\"notification_settings is not undefined (BUG-R1 regression)\", async () => {\n    const req = createTestRequest(\"\u002Fapi\u002Fuser\u002Fprofile\");\n    const res = await GET(req);\n    const { json } = await parseResponse(res);\n\n    expect(\"notification_settings\" in json.data).toBe(true);\n    const ns = json.data.notification_settings;\n    expect(ns === null || typeof ns === \"object\").toBe(true);\n  });\n});\n```\n\n### Testing Sandbox\u002FProduction Parity\n\nThe most common AI regression: fixing production path but forgetting sandbox path (or vice versa).\n\n```typescript\n\u002F\u002F Test that sandbox responses match the expected contract\ndescribe(\"GET \u002Fapi\u002Fuser\u002Fmessages (conversation list)\", () => {\n  it(\"includes partner_name in sandbox mode\", async () => {\n    const req = createTestRequest(\"\u002Fapi\u002Fuser\u002Fmessages\", {\n      sandboxUserId: \"user-001\",\n    });\n    const res = await GET(req);\n    const { json } = await parseResponse(res);\n\n    \u002F\u002F This caught a bug where partner_name was added\n    \u002F\u002F to production path but not sandbox path\n    if (json.data.length > 0) {\n      for (const conv of json.data) {\n        expect(\"partner_name\" in conv).toBe(true);\n      }\n    }\n  });\n});\n```\n\n## Integrating Tests into Bug-Check Workflow\n\n### Custom Command Definition\n\n```markdown\n\u003C!-- .claude\u002Fcommands\u002Fbug-check.md -->\n# Bug Check\n\n## Step 1: Automated Tests (mandatory, cannot skip)\n\nRun these commands FIRST before any code review:\n\n    npm run test       # Vitest test suite\n    npm run build      # TypeScript type check + build\n\n- If tests fail → report as highest priority bug\n- If build fails → report type errors as highest priority\n- Only proceed to Step 2 if both pass\n\n## Step 2: Code Review (AI review)\n\n1. Sandbox \u002F production path consistency\n2. API response shape matches frontend expectations\n3. SELECT clause completeness\n4. Error handling with rollback\n5. Optimistic update race conditions\n\n## Step 3: For each bug fixed, propose a regression test\n```\n\n### The Workflow\n\n```\nUser: \"バグチェックして\" (or \"\u002Fbug-check\")\n  │\n  ├─ Step 1: npm run test\n  │   ├─ FAIL → Bug found mechanically (no AI judgment needed)\n  │   └─ PASS → Continue\n  │\n  ├─ Step 2: npm run build\n  │   ├─ FAIL → Type error found mechanically\n  │   └─ PASS → Continue\n  │\n  ├─ Step 3: AI code review (with known blind spots in mind)\n  │   └─ Findings reported\n  │\n  └─ Step 4: For each fix, write a regression test\n      └─ Next bug-check catches if fix breaks\n```\n\n## Common AI Regression Patterns\n\n### Pattern 1: Sandbox\u002FProduction Path Mismatch\n\n**Frequency**: Most common (observed in 3 out of 4 regressions)\n\n```typescript\n\u002F\u002F ❌ AI adds field to production path only\nif (isSandboxMode()) {\n  return { data: { id, email, name } };  \u002F\u002F Missing new field\n}\n\u002F\u002F Production path\nreturn { data: { id, email, name, notification_settings } };\n\n\u002F\u002F ✅ Both paths must return the same shape\nif (isSandboxMode()) {\n  return { data: { id, email, name, notification_settings: null } };\n}\nreturn { data: { id, email, name, notification_settings } };\n```\n\n**Test to catch it**:\n\n```typescript\nit(\"sandbox and production return same fields\", async () => {\n  \u002F\u002F In test env, sandbox mode is forced ON\n  const res = await GET(createTestRequest(\"\u002Fapi\u002Fuser\u002Fprofile\"));\n  const { json } = await parseResponse(res);\n\n  for (const field of REQUIRED_FIELDS) {\n    expect(json.data).toHaveProperty(field);\n  }\n});\n```\n\n### Pattern 2: SELECT Clause Omission\n\n**Frequency**: Common with Supabase\u002FPrisma when adding new columns\n\n```typescript\n\u002F\u002F ❌ New column added to response but not to SELECT\nconst { data } = await supabase\n  .from(\"users\")\n  .select(\"id, email, name\")  \u002F\u002F notification_settings not here\n  .single();\n\nreturn { data: { ...data, notification_settings: data.notification_settings } };\n\u002F\u002F → notification_settings is always undefined\n\n\u002F\u002F ✅ Use SELECT * or explicitly include new columns\nconst { data } = await supabase\n  .from(\"users\")\n  .select(\"*\")\n  .single();\n```\n\n### Pattern 3: Error State Leakage\n\n**Frequency**: Moderate — when adding error handling to existing components\n\n```typescript\n\u002F\u002F ❌ Error state set but old data not cleared\ncatch (err) {\n  setError(\"Failed to load\");\n  \u002F\u002F reservations still shows data from previous tab!\n}\n\n\u002F\u002F ✅ Clear related state on error\ncatch (err) {\n  setReservations([]);  \u002F\u002F Clear stale data\n  setError(\"Failed to load\");\n}\n```\n\n### Pattern 4: Optimistic Update Without Proper Rollback\n\n```typescript\n\u002F\u002F ❌ No rollback on failure\nconst handleRemove = async (id: string) => {\n  setItems(prev => prev.filter(i => i.id !== id));\n  await fetch(`\u002Fapi\u002Fitems\u002F${id}`, { method: \"DELETE\" });\n  \u002F\u002F If API fails, item is gone from UI but still in DB\n};\n\n\u002F\u002F ✅ Capture previous state and rollback on failure\nconst handleRemove = async (id: string) => {\n  const prevItems = [...items];\n  setItems(prev => prev.filter(i => i.id !== id));\n  try {\n    const res = await fetch(`\u002Fapi\u002Fitems\u002F${id}`, { method: \"DELETE\" });\n    if (!res.ok) throw new Error(\"API error\");\n  } catch {\n    setItems(prevItems);  \u002F\u002F Rollback\n    alert(\"削除に失敗しました\");\n  }\n};\n```\n\n## Strategy: Test Where Bugs Were Found\n\nDon't aim for 100% coverage. Instead:\n\n```\nBug found in \u002Fapi\u002Fuser\u002Fprofile     → Write test for profile API\nBug found in \u002Fapi\u002Fuser\u002Fmessages    → Write test for messages API\nBug found in \u002Fapi\u002Fuser\u002Ffavorites   → Write test for favorites API\nNo bug in \u002Fapi\u002Fuser\u002Fnotifications  → Don't write test (yet)\n```\n\n**Why this works with AI development:**\n\n1. AI tends to make the **same category of mistake** repeatedly\n2. Bugs cluster in complex areas (auth, multi-path logic, state management)\n3. Once tested, that exact regression **cannot happen again**\n4. Test count grows organically with bug fixes — no wasted effort\n\n## Quick Reference\n\n| AI Regression Pattern | Test Strategy | Priority |\n|---|---|---|\n| Sandbox\u002Fproduction mismatch | Assert same response shape in sandbox mode | ? High |\n| SELECT clause omission | Assert all required fields in response | ? High |\n| Error state leakage | Assert state cleanup on error | ? Medium |\n| Missing rollback | Assert state restored on API failure | ? Medium |\n| Type cast masking null | Assert field is not undefined | ? Medium |\n\n## DO \u002F DON'T\n\n**DO:**\n- Write tests immediately after finding a bug (before fixing it if possible)\n- Test the API response shape, not the implementation\n- Run tests as the first step of every bug-check\n- Keep tests fast (\u003C 1 second total with sandbox mode)\n- Name tests after the bug they prevent (e.g., \"BUG-R1 regression\")\n\n**DON'T:**\n- Write tests for code that has never had a bug\n- Trust AI self-review as a substitute for automated tests\n- Skip sandbox path testing because \"it's just mock data\"\n- Write integration tests when unit tests suffice\n- Aim for coverage percentage — aim for regression prevention\n",{"name":5,"origin":54,"description":7},"ECC","main","a298c929b6cbd662e1fce54302ce520d5ceea1a5",".agents\u002Fskills\u002Fai-regression-testing","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain","https:\u002F\u002Fgithub.com\u002FPrekzursil","2026-11-02 14:30:57",{"source":8,"skillName":5,"commands":62,"prompt":72,"zipAvailable":73},[63,66,69],{"tool":64,"command":65},"npx","npx skills add Prekzursil\u002Fagent-skills-toolchain --skill ai-regression-testing",{"tool":67,"command":68},"bunx","bunx skills add Prekzursil\u002Fagent-skills-toolchain --skill ai-regression-testing",{"tool":70,"command":71},"pnpm","pnpm dlx skills add Prekzursil\u002Fagent-skills-toolchain --skill ai-regression-testing","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fai-regression-testing\u002FSKILL.md",true,[],[76,98,127,151,165,180],{"skillNo":77,"name":78,"nameZh":6,"description":79,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":80,"categories":81,"keywords":82,"publishedAt":49},"01M6DF7D7478R55P46REYH4QAC","clarify-requirements","Resolve genuine pre-execution ambiguity by scanning repo context, generating 1-6 grounded options, and asking 1-3 focused questions via AskUserQuestion. NOT for mid-task use (injection risk); NOT when the goal is already unambiguous; use to disambiguate vague prompts before any file edits.","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Fworkspace-hooks\u002Fskills\u002Fclarify-requirements\u002FSKILL.md",[],[83,84,85,86,87,88,89,90,91,92,93,94,95,96,97],"clarify requirements","clarify","requirements","risk","options","task","prompts","goal","scanning","use","context","repo","injection","execution","file",{"skillNo":99,"name":100,"nameZh":6,"description":101,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":102,"categories":103,"keywords":107,"publishedAt":49},"01M6DF79CA4BHJS66J8EZDBPWJ","openmm-md","Run FREE, open-source, Windows-native GPU molecular dynamics via the openmm-md MCP (OpenMM + CUDA on the local NVIDIA GPU). Use for protein \u002F protein-ligand MD when you want a free stack or are on Windows without a Schrodinger license. Covers the full workflow (prepare, solvate, minimize, equilibrate, production, analyze, mmgbsa) AND an honest decision guide vs Schrodinger Desmond, including how to run Desmond legitimately on Linux or a cloud GPU with a real license. This is a deliberately LESSER free alternative and does NOT replace the schrodinger MCP tools. Do NOT use it to pick a free tool when a validated FEP+, WaterMap, IFD-MD, or Prime MM-GBSA result is actually required.","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Fmcp\u002Fopenmm-md-mcp\u002Fskill\u002Fopenmm-md\u002FSKILL.md",[104],{"categoryNo":105,"categoryName":106,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[108,109,110,111,112,113,114,115,116,117,118,119,120,121,92,122,123,124,125,126],"openmm md","how","decision","pick","linux","analyze","tool","stack","open","NVIDIA","gpu","license","workflow","windows","free","molecular dynamics","local","do","MCP",{"skillNo":128,"name":129,"nameZh":6,"description":130,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":131,"categories":132,"keywords":137,"publishedAt":49},"01M6DF76C1MG85VEWP8VZCBNMN","visual-review","Take screenshots of web pages and UI using Playwright for visual review and iteration","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Feditor\u002Fmetaswarm\u002Fskills\u002Fvisual-review\u002FSKILL.md",[133,136],{"categoryNo":134,"categoryName":135,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000003","演示文稿",{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[138,139,140,141,142,143,144,145,42,146,109,147,148,149,150],"review","visual","visual review","pages","web","false","iteration","take","slides","ui","screenshot","playwright","screenshots",{"skillNo":152,"name":153,"nameZh":6,"description":154,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":155,"categories":156,"keywords":157,"publishedAt":49},"01M6DF73AQVCBZKEN0YXEX9FXP","status","Diagnostic status report — shows metaswarm installation state, project setup, and potential issues","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Feditor\u002Fmetaswarm\u002Fskills\u002Fstatus\u002FSKILL.md",[],[153,158,159,160,161,162,163,164],"setup","report","project","installation","project setup","issues","state",{"skillNo":166,"name":167,"nameZh":6,"description":168,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":169,"categories":170,"keywords":172,"publishedAt":49},"01M6DF6X6C2XB0P6GX76P7TTYV","start","Use when starting work on any task, when the user mentions metaswarm, or when the user wants to begin tracked development work","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Feditor\u002Fmetaswarm\u002Fskills\u002Fstart\u002FSKILL.md",[171],{"categoryNo":105,"categoryName":106,"taxonomyType":21,"taxonomyTypeDesc":22},[167,173,88,174,175,176,92,177,178,179,46],"start issue","issue","ready","work on issue","user","work","work on",{"skillNo":181,"name":158,"nameZh":6,"description":182,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":183,"categories":184,"keywords":185,"publishedAt":49},"01M6DF6QMJ9AJKFKHHKRT005PQ","Interactive project setup — detects your project, configures metaswarm, writes project-local files","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Feditor\u002Fmetaswarm\u002Fskills\u002Fsetup\u002FSKILL.md",[],[158,160,124,186,162,187],"files","interactive"]