[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6DC9MA7JR3AKFKKM2P8FFRF":3,"skill-same:01M6DC9MA7JR3AKFKKM2P8FFRF":92},{"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":48,"linkOnly":49,"markdownContent":50,"markdownBody":51,"frontMatter":52,"refName":54,"commitSha":55,"skillDirectory":56,"forksCount":14,"repositoryHtmlUrl":57,"repositoryHomepageUrl":6,"ownerHtmlUrl":58,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":59,"install":60,"resources":73},"01M6DC9MA7JR3AKFKKM2P8FFRF","tdd-workflow",null,"Use this skill when writing new features, fixing bugs, or refactoring code. Enforces test-driven development with high test coverage including unit, integration, and E2E tests.","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\u002Fci\u002Fskills-allowlist\u002Ftdd-workflow\u002FSKILL.md",[18,23,26],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"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],"tdd","workflow","tdd workflow","writing","test","e2e tests","development","code","integration","e2e","refactoring","coverage","use","test coverage","tests","driven","2026-11-10 10:12:30",false,"---\nname: tdd-workflow\ndescription: Use this skill when writing new features, fixing bugs, or refactoring code. Enforces test-driven development with high test coverage including unit, integration, and E2E tests.\norigin: ECC\n---\n\n# Test-Driven Development Workflow\n\nThis skill ensures all code development follows TDD principles with comprehensive test coverage.\n\n## When to Activate\n\n- Writing new features or functionality\n- Fixing bugs or issues\n- Refactoring existing code\n- Adding API endpoints\n- Creating new components\n\n## Core Principles\n\n### 1. Tests BEFORE Code\nALWAYS write tests first, then implement code to make tests pass.\n\n### 2. Coverage Requirements\n- Coverage meets .coverage-thresholds.json (SSOT; 100% on metaswarm, else 80%+) — unit + integration + E2E\n- All edge cases covered\n- Error scenarios tested\n- Boundary conditions verified\n\n### 3. Test Types\n\n#### Unit Tests\n- Individual functions and utilities\n- Component logic\n- Pure functions\n- Helpers and utilities\n\n#### Integration Tests\n- API endpoints\n- Database operations\n- Service interactions\n- External API calls\n\n#### E2E Tests (Playwright)\n- Critical user flows\n- Complete workflows\n- Browser automation\n- UI interactions\n\n## TDD Workflow Steps\n\n### Step 1: Write User Journeys\n```\nAs a [role], I want to [action], so that [benefit]\n\nExample:\nAs a user, I want to search for markets semantically,\nso that I can find relevant markets even without exact keywords.\n```\n\n### Step 2: Generate Test Cases\nFor each user journey, create comprehensive test cases:\n\n```typescript\ndescribe('Semantic Search', () => {\n  it('returns relevant markets for query', async () => {\n    \u002F\u002F Test implementation\n  })\n\n  it('handles empty query gracefully', async () => {\n    \u002F\u002F Test edge case\n  })\n\n  it('falls back to substring search when Redis unavailable', async () => {\n    \u002F\u002F Test fallback behavior\n  })\n\n  it('sorts results by similarity score', async () => {\n    \u002F\u002F Test sorting logic\n  })\n})\n```\n\n### Step 3: Run Tests (They Should Fail)\n```bash\nnpm test\n# Tests should fail - we haven't implemented yet\n```\n\n### Step 4: Implement Code\nWrite minimal code to make tests pass:\n\n```typescript\n\u002F\u002F Implementation guided by tests\nexport async function searchMarkets(query: string) {\n  \u002F\u002F Implementation here\n}\n```\n\n### Step 5: Run Tests Again\n```bash\nnpm test\n# Tests should now pass\n```\n\n### Step 6: Refactor\nImprove code quality while keeping tests green:\n- Remove duplication\n- Improve naming\n- Optimize performance\n- Enhance readability\n\n### Step 7: Verify Coverage\n```bash\nnpm run test:coverage\n# Verify coverage meets .coverage-thresholds.json (SSOT)\n```\n\n## Testing Patterns\n\n### Unit Test Pattern (Jest\u002FVitest)\n```typescript\nimport { render, screen, fireEvent } from '@testing-library\u002Freact'\nimport { Button } from '.\u002FButton'\n\ndescribe('Button Component', () => {\n  it('renders with correct text', () => {\n    render(\u003CButton>Click me\u003C\u002FButton>)\n    expect(screen.getByText('Click me')).toBeInTheDocument()\n  })\n\n  it('calls onClick when clicked', () => {\n    const handleClick = jest.fn()\n    render(\u003CButton onClick={handleClick}>Click\u003C\u002FButton>)\n\n    fireEvent.click(screen.getByRole('button'))\n\n    expect(handleClick).toHaveBeenCalledTimes(1)\n  })\n\n  it('is disabled when disabled prop is true', () => {\n    render(\u003CButton disabled>Click\u003C\u002FButton>)\n    expect(screen.getByRole('button')).toBeDisabled()\n  })\n})\n```\n\n### API Integration Test Pattern\n```typescript\nimport { NextRequest } from 'next\u002Fserver'\nimport { GET } from '.\u002Froute'\n\ndescribe('GET \u002Fapi\u002Fmarkets', () => {\n  it('returns markets successfully', async () => {\n    const request = new NextRequest('http:\u002F\u002Flocalhost\u002Fapi\u002Fmarkets')\n    const response = await GET(request)\n    const data = await response.json()\n\n    expect(response.status).toBe(200)\n    expect(data.success).toBe(true)\n    expect(Array.isArray(data.data)).toBe(true)\n  })\n\n  it('validates query parameters', async () => {\n    const request = new NextRequest('http:\u002F\u002Flocalhost\u002Fapi\u002Fmarkets?limit=invalid')\n    const response = await GET(request)\n\n    expect(response.status).toBe(400)\n  })\n\n  it('handles database errors gracefully', async () => {\n    \u002F\u002F Mock database failure\n    const request = new NextRequest('http:\u002F\u002Flocalhost\u002Fapi\u002Fmarkets')\n    \u002F\u002F Test error handling\n  })\n})\n```\n\n### E2E Test Pattern (Playwright)\n```typescript\nimport { test, expect } from '@playwright\u002Ftest'\n\ntest('user can search and filter markets', async ({ page }) => {\n  \u002F\u002F Navigate to markets page\n  await page.goto('\u002F')\n  await page.click('a[href=\"\u002Fmarkets\"]')\n\n  \u002F\u002F Verify page loaded\n  await expect(page.locator('h1')).toContainText('Markets')\n\n  \u002F\u002F Search for markets\n  await page.fill('input[placeholder=\"Search markets\"]', 'election')\n\n  \u002F\u002F Wait for debounce and results\n  await page.waitForTimeout(600)\n\n  \u002F\u002F Verify search results displayed\n  const results = page.locator('[data-testid=\"market-card\"]')\n  await expect(results).toHaveCount(5, { timeout: 5000 })\n\n  \u002F\u002F Verify results contain search term\n  const firstResult = results.first()\n  await expect(firstResult).toContainText('election', { ignoreCase: true })\n\n  \u002F\u002F Filter by status\n  await page.click('button:has-text(\"Active\")')\n\n  \u002F\u002F Verify filtered results\n  await expect(results).toHaveCount(3)\n})\n\ntest('user can create a new market', async ({ page }) => {\n  \u002F\u002F Login first\n  await page.goto('\u002Fcreator-dashboard')\n\n  \u002F\u002F Fill market creation form\n  await page.fill('input[name=\"name\"]', 'Test Market')\n  await page.fill('textarea[name=\"description\"]', 'Test description')\n  await page.fill('input[name=\"endDate\"]', '2025-12-31')\n\n  \u002F\u002F Submit form\n  await page.click('button[type=\"submit\"]')\n\n  \u002F\u002F Verify success message\n  await expect(page.locator('text=Market created successfully')).toBeVisible()\n\n  \u002F\u002F Verify redirect to market page\n  await expect(page).toHaveURL(\u002F\\\u002Fmarkets\\\u002Ftest-market\u002F)\n})\n```\n\n## Test File Organization\n\n```\nsrc\u002F\n├── components\u002F\n│   ├── Button\u002F\n│   │   ├── Button.tsx\n│   │   ├── Button.test.tsx          # Unit tests\n│   │   └── Button.stories.tsx       # Storybook\n│   └── MarketCard\u002F\n│       ├── MarketCard.tsx\n│       └── MarketCard.test.tsx\n├── app\u002F\n│   └── api\u002F\n│       └── markets\u002F\n│           ├── route.ts\n│           └── route.test.ts         # Integration tests\n└── e2e\u002F\n    ├── markets.spec.ts               # E2E tests\n    ├── trading.spec.ts\n    └── auth.spec.ts\n```\n\n## Mocking External Services\n\n### Supabase Mock\n```typescript\njest.mock('@\u002Flib\u002Fsupabase', () => ({\n  supabase: {\n    from: jest.fn(() => ({\n      select: jest.fn(() => ({\n        eq: jest.fn(() => Promise.resolve({\n          data: [{ id: 1, name: 'Test Market' }],\n          error: null\n        }))\n      }))\n    }))\n  }\n}))\n```\n\n### Redis Mock\n```typescript\njest.mock('@\u002Flib\u002Fredis', () => ({\n  searchMarketsByVector: jest.fn(() => Promise.resolve([\n    { slug: 'test-market', similarity_score: 0.95 }\n  ])),\n  checkRedisHealth: jest.fn(() => Promise.resolve({ connected: true }))\n}))\n```\n\n### OpenAI Mock\n```typescript\njest.mock('@\u002Flib\u002Fopenai', () => ({\n  generateEmbedding: jest.fn(() => Promise.resolve(\n    new Array(1536).fill(0.1) \u002F\u002F Mock 1536-dim embedding\n  ))\n}))\n```\n\n## Test Coverage Verification\n\n### Run Coverage Report\n```bash\nnpm run test:coverage\n```\n\n### Coverage Thresholds\n```json\n{\n  \"jest\": {\n    \"coverageThresholds\": {\n      \"global\": {\n        \"branches\": 80,\n        \"functions\": 80,\n        \"lines\": 80,\n        \"statements\": 80\n      }\n    }\n  }\n}\n```\n\n## Common Testing Mistakes to Avoid\n\n### ❌ WRONG: Testing Implementation Details\n```typescript\n\u002F\u002F Don't test internal state\nexpect(component.state.count).toBe(5)\n```\n\n### ✅ CORRECT: Test User-Visible Behavior\n```typescript\n\u002F\u002F Test what users see\nexpect(screen.getByText('Count: 5')).toBeInTheDocument()\n```\n\n### ❌ WRONG: Brittle Selectors\n```typescript\n\u002F\u002F Breaks easily\nawait page.click('.css-class-xyz')\n```\n\n### ✅ CORRECT: Semantic Selectors\n```typescript\n\u002F\u002F Resilient to changes\nawait page.click('button:has-text(\"Submit\")')\nawait page.click('[data-testid=\"submit-button\"]')\n```\n\n### ❌ WRONG: No Test Isolation\n```typescript\n\u002F\u002F Tests depend on each other\ntest('creates user', () => { \u002F* ... *\u002F })\ntest('updates same user', () => { \u002F* depends on previous test *\u002F })\n```\n\n### ✅ CORRECT: Independent Tests\n```typescript\n\u002F\u002F Each test sets up its own data\ntest('creates user', () => {\n  const user = createTestUser()\n  \u002F\u002F Test logic\n})\n\ntest('updates user', () => {\n  const user = createTestUser()\n  \u002F\u002F Update logic\n})\n```\n\n## Continuous Testing\n\n### Watch Mode During Development\n```bash\nnpm test -- --watch\n# Tests run automatically on file changes\n```\n\n### Pre-Commit Hook\n```bash\n# Runs before every commit\nnpm test && npm run lint\n```\n\n### CI\u002FCD Integration\n```yaml\n# GitHub Actions\n- name: Run Tests\n  run: npm test -- --coverage\n- name: Upload Coverage\n  uses: codecov\u002Fcodecov-action@v3\n```\n\n## Best Practices\n\n1. **Write Tests First** - Always TDD\n2. **One Assert Per Test** - Focus on single behavior\n3. **Descriptive Test Names** - Explain what's tested\n4. **Arrange-Act-Assert** - Clear test structure\n5. **Mock External Dependencies** - Isolate unit tests\n6. **Test Edge Cases** - Null, undefined, empty, large\n7. **Test Error Paths** - Not just happy paths\n8. **Keep Tests Fast** - Unit tests \u003C 50ms each\n9. **Clean Up After Tests** - No side effects\n10. **Review Coverage Reports** - Identify gaps\n\n## Success Metrics\n\n- Code coverage meets .coverage-thresholds.json (SSOT)\n- All tests passing (green)\n- No skipped or disabled tests\n- Fast test execution (\u003C 30s for unit tests)\n- E2E tests cover critical user flows\n- Tests catch bugs before production\n\n---\n\n**Remember**: Tests are not optional. They are the safety net that enables confident refactoring, rapid development, and production reliability.\n","# Test-Driven Development Workflow\n\nThis skill ensures all code development follows TDD principles with comprehensive test coverage.\n\n## When to Activate\n\n- Writing new features or functionality\n- Fixing bugs or issues\n- Refactoring existing code\n- Adding API endpoints\n- Creating new components\n\n## Core Principles\n\n### 1. Tests BEFORE Code\nALWAYS write tests first, then implement code to make tests pass.\n\n### 2. Coverage Requirements\n- Coverage meets .coverage-thresholds.json (SSOT; 100% on metaswarm, else 80%+) — unit + integration + E2E\n- All edge cases covered\n- Error scenarios tested\n- Boundary conditions verified\n\n### 3. Test Types\n\n#### Unit Tests\n- Individual functions and utilities\n- Component logic\n- Pure functions\n- Helpers and utilities\n\n#### Integration Tests\n- API endpoints\n- Database operations\n- Service interactions\n- External API calls\n\n#### E2E Tests (Playwright)\n- Critical user flows\n- Complete workflows\n- Browser automation\n- UI interactions\n\n## TDD Workflow Steps\n\n### Step 1: Write User Journeys\n```\nAs a [role], I want to [action], so that [benefit]\n\nExample:\nAs a user, I want to search for markets semantically,\nso that I can find relevant markets even without exact keywords.\n```\n\n### Step 2: Generate Test Cases\nFor each user journey, create comprehensive test cases:\n\n```typescript\ndescribe('Semantic Search', () => {\n  it('returns relevant markets for query', async () => {\n    \u002F\u002F Test implementation\n  })\n\n  it('handles empty query gracefully', async () => {\n    \u002F\u002F Test edge case\n  })\n\n  it('falls back to substring search when Redis unavailable', async () => {\n    \u002F\u002F Test fallback behavior\n  })\n\n  it('sorts results by similarity score', async () => {\n    \u002F\u002F Test sorting logic\n  })\n})\n```\n\n### Step 3: Run Tests (They Should Fail)\n```bash\nnpm test\n# Tests should fail - we haven't implemented yet\n```\n\n### Step 4: Implement Code\nWrite minimal code to make tests pass:\n\n```typescript\n\u002F\u002F Implementation guided by tests\nexport async function searchMarkets(query: string) {\n  \u002F\u002F Implementation here\n}\n```\n\n### Step 5: Run Tests Again\n```bash\nnpm test\n# Tests should now pass\n```\n\n### Step 6: Refactor\nImprove code quality while keeping tests green:\n- Remove duplication\n- Improve naming\n- Optimize performance\n- Enhance readability\n\n### Step 7: Verify Coverage\n```bash\nnpm run test:coverage\n# Verify coverage meets .coverage-thresholds.json (SSOT)\n```\n\n## Testing Patterns\n\n### Unit Test Pattern (Jest\u002FVitest)\n```typescript\nimport { render, screen, fireEvent } from '@testing-library\u002Freact'\nimport { Button } from '.\u002FButton'\n\ndescribe('Button Component', () => {\n  it('renders with correct text', () => {\n    render(\u003CButton>Click me\u003C\u002FButton>)\n    expect(screen.getByText('Click me')).toBeInTheDocument()\n  })\n\n  it('calls onClick when clicked', () => {\n    const handleClick = jest.fn()\n    render(\u003CButton onClick={handleClick}>Click\u003C\u002FButton>)\n\n    fireEvent.click(screen.getByRole('button'))\n\n    expect(handleClick).toHaveBeenCalledTimes(1)\n  })\n\n  it('is disabled when disabled prop is true', () => {\n    render(\u003CButton disabled>Click\u003C\u002FButton>)\n    expect(screen.getByRole('button')).toBeDisabled()\n  })\n})\n```\n\n### API Integration Test Pattern\n```typescript\nimport { NextRequest } from 'next\u002Fserver'\nimport { GET } from '.\u002Froute'\n\ndescribe('GET \u002Fapi\u002Fmarkets', () => {\n  it('returns markets successfully', async () => {\n    const request = new NextRequest('http:\u002F\u002Flocalhost\u002Fapi\u002Fmarkets')\n    const response = await GET(request)\n    const data = await response.json()\n\n    expect(response.status).toBe(200)\n    expect(data.success).toBe(true)\n    expect(Array.isArray(data.data)).toBe(true)\n  })\n\n  it('validates query parameters', async () => {\n    const request = new NextRequest('http:\u002F\u002Flocalhost\u002Fapi\u002Fmarkets?limit=invalid')\n    const response = await GET(request)\n\n    expect(response.status).toBe(400)\n  })\n\n  it('handles database errors gracefully', async () => {\n    \u002F\u002F Mock database failure\n    const request = new NextRequest('http:\u002F\u002Flocalhost\u002Fapi\u002Fmarkets')\n    \u002F\u002F Test error handling\n  })\n})\n```\n\n### E2E Test Pattern (Playwright)\n```typescript\nimport { test, expect } from '@playwright\u002Ftest'\n\ntest('user can search and filter markets', async ({ page }) => {\n  \u002F\u002F Navigate to markets page\n  await page.goto('\u002F')\n  await page.click('a[href=\"\u002Fmarkets\"]')\n\n  \u002F\u002F Verify page loaded\n  await expect(page.locator('h1')).toContainText('Markets')\n\n  \u002F\u002F Search for markets\n  await page.fill('input[placeholder=\"Search markets\"]', 'election')\n\n  \u002F\u002F Wait for debounce and results\n  await page.waitForTimeout(600)\n\n  \u002F\u002F Verify search results displayed\n  const results = page.locator('[data-testid=\"market-card\"]')\n  await expect(results).toHaveCount(5, { timeout: 5000 })\n\n  \u002F\u002F Verify results contain search term\n  const firstResult = results.first()\n  await expect(firstResult).toContainText('election', { ignoreCase: true })\n\n  \u002F\u002F Filter by status\n  await page.click('button:has-text(\"Active\")')\n\n  \u002F\u002F Verify filtered results\n  await expect(results).toHaveCount(3)\n})\n\ntest('user can create a new market', async ({ page }) => {\n  \u002F\u002F Login first\n  await page.goto('\u002Fcreator-dashboard')\n\n  \u002F\u002F Fill market creation form\n  await page.fill('input[name=\"name\"]', 'Test Market')\n  await page.fill('textarea[name=\"description\"]', 'Test description')\n  await page.fill('input[name=\"endDate\"]', '2025-12-31')\n\n  \u002F\u002F Submit form\n  await page.click('button[type=\"submit\"]')\n\n  \u002F\u002F Verify success message\n  await expect(page.locator('text=Market created successfully')).toBeVisible()\n\n  \u002F\u002F Verify redirect to market page\n  await expect(page).toHaveURL(\u002F\\\u002Fmarkets\\\u002Ftest-market\u002F)\n})\n```\n\n## Test File Organization\n\n```\nsrc\u002F\n├── components\u002F\n│   ├── Button\u002F\n│   │   ├── Button.tsx\n│   │   ├── Button.test.tsx          # Unit tests\n│   │   └── Button.stories.tsx       # Storybook\n│   └── MarketCard\u002F\n│       ├── MarketCard.tsx\n│       └── MarketCard.test.tsx\n├── app\u002F\n│   └── api\u002F\n│       └── markets\u002F\n│           ├── route.ts\n│           └── route.test.ts         # Integration tests\n└── e2e\u002F\n    ├── markets.spec.ts               # E2E tests\n    ├── trading.spec.ts\n    └── auth.spec.ts\n```\n\n## Mocking External Services\n\n### Supabase Mock\n```typescript\njest.mock('@\u002Flib\u002Fsupabase', () => ({\n  supabase: {\n    from: jest.fn(() => ({\n      select: jest.fn(() => ({\n        eq: jest.fn(() => Promise.resolve({\n          data: [{ id: 1, name: 'Test Market' }],\n          error: null\n        }))\n      }))\n    }))\n  }\n}))\n```\n\n### Redis Mock\n```typescript\njest.mock('@\u002Flib\u002Fredis', () => ({\n  searchMarketsByVector: jest.fn(() => Promise.resolve([\n    { slug: 'test-market', similarity_score: 0.95 }\n  ])),\n  checkRedisHealth: jest.fn(() => Promise.resolve({ connected: true }))\n}))\n```\n\n### OpenAI Mock\n```typescript\njest.mock('@\u002Flib\u002Fopenai', () => ({\n  generateEmbedding: jest.fn(() => Promise.resolve(\n    new Array(1536).fill(0.1) \u002F\u002F Mock 1536-dim embedding\n  ))\n}))\n```\n\n## Test Coverage Verification\n\n### Run Coverage Report\n```bash\nnpm run test:coverage\n```\n\n### Coverage Thresholds\n```json\n{\n  \"jest\": {\n    \"coverageThresholds\": {\n      \"global\": {\n        \"branches\": 80,\n        \"functions\": 80,\n        \"lines\": 80,\n        \"statements\": 80\n      }\n    }\n  }\n}\n```\n\n## Common Testing Mistakes to Avoid\n\n### ❌ WRONG: Testing Implementation Details\n```typescript\n\u002F\u002F Don't test internal state\nexpect(component.state.count).toBe(5)\n```\n\n### ✅ CORRECT: Test User-Visible Behavior\n```typescript\n\u002F\u002F Test what users see\nexpect(screen.getByText('Count: 5')).toBeInTheDocument()\n```\n\n### ❌ WRONG: Brittle Selectors\n```typescript\n\u002F\u002F Breaks easily\nawait page.click('.css-class-xyz')\n```\n\n### ✅ CORRECT: Semantic Selectors\n```typescript\n\u002F\u002F Resilient to changes\nawait page.click('button:has-text(\"Submit\")')\nawait page.click('[data-testid=\"submit-button\"]')\n```\n\n### ❌ WRONG: No Test Isolation\n```typescript\n\u002F\u002F Tests depend on each other\ntest('creates user', () => { \u002F* ... *\u002F })\ntest('updates same user', () => { \u002F* depends on previous test *\u002F })\n```\n\n### ✅ CORRECT: Independent Tests\n```typescript\n\u002F\u002F Each test sets up its own data\ntest('creates user', () => {\n  const user = createTestUser()\n  \u002F\u002F Test logic\n})\n\ntest('updates user', () => {\n  const user = createTestUser()\n  \u002F\u002F Update logic\n})\n```\n\n## Continuous Testing\n\n### Watch Mode During Development\n```bash\nnpm test -- --watch\n# Tests run automatically on file changes\n```\n\n### Pre-Commit Hook\n```bash\n# Runs before every commit\nnpm test && npm run lint\n```\n\n### CI\u002FCD Integration\n```yaml\n# GitHub Actions\n- name: Run Tests\n  run: npm test -- --coverage\n- name: Upload Coverage\n  uses: codecov\u002Fcodecov-action@v3\n```\n\n## Best Practices\n\n1. **Write Tests First** - Always TDD\n2. **One Assert Per Test** - Focus on single behavior\n3. **Descriptive Test Names** - Explain what's tested\n4. **Arrange-Act-Assert** - Clear test structure\n5. **Mock External Dependencies** - Isolate unit tests\n6. **Test Edge Cases** - Null, undefined, empty, large\n7. **Test Error Paths** - Not just happy paths\n8. **Keep Tests Fast** - Unit tests \u003C 50ms each\n9. **Clean Up After Tests** - No side effects\n10. **Review Coverage Reports** - Identify gaps\n\n## Success Metrics\n\n- Code coverage meets .coverage-thresholds.json (SSOT)\n- All tests passing (green)\n- No skipped or disabled tests\n- Fast test execution (\u003C 30s for unit tests)\n- E2E tests cover critical user flows\n- Tests catch bugs before production\n\n---\n\n**Remember**: Tests are not optional. They are the safety net that enables confident refactoring, rapid development, and production reliability.\n",{"name":5,"origin":53,"description":7},"ECC","main","a298c929b6cbd662e1fce54302ce520d5ceea1a5","ci\u002Fskills-allowlist\u002Ftdd-workflow","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain","https:\u002F\u002Fgithub.com\u002FPrekzursil","2026-11-02 14:30:57",{"source":8,"skillName":5,"commands":61,"prompt":71,"zipAvailable":72},[62,65,68],{"tool":63,"command":64},"npx","npx skills add Prekzursil\u002Fagent-skills-toolchain --skill tdd-workflow",{"tool":66,"command":67},"bunx","bunx skills add Prekzursil\u002Fagent-skills-toolchain --skill tdd-workflow",{"tool":69,"command":70},"pnpm","pnpm dlx skills add Prekzursil\u002Fagent-skills-toolchain --skill tdd-workflow","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Fci\u002Fskills-allowlist\u002Ftdd-workflow\u002FSKILL.md",true,[74,80,86],{"path":75,"resourceType":76,"resourceTypeDesc":77,"sizeBytes":78,"sourceHtmlUrl":79},"CKanbanBoard.tsx","OTHER","其他",19590,"https:\u002F\u002Fgithub.com\u002FSHENRUIYANG\u002Forbmobile-ui\u002Fblob\u002Fmain\u002Fsrc\u002Fcomponents\u002FKanban\u002FCKanbanBoard.tsx",{"path":81,"resourceType":82,"resourceTypeDesc":83,"sizeBytes":84,"sourceHtmlUrl":85},"index.ts","SCRIPT","脚本",179,"https:\u002F\u002Fgithub.com\u002FSHENRUIYANG\u002Forbmobile-ui\u002Fblob\u002Fmain\u002Fsrc\u002Fcomponents\u002FKanban\u002Findex.ts",{"path":87,"resourceType":88,"resourceTypeDesc":89,"sizeBytes":90,"sourceHtmlUrl":91},"README.md","REFERENCE","参考文档",2799,"https:\u002F\u002Fgithub.com\u002FSHENRUIYANG\u002Forbmobile-ui\u002Fblob\u002Fmain\u002Fsrc\u002Fcomponents\u002FKanban\u002FREADME.md",[93,114,140,165,179,194],{"skillNo":94,"name":95,"nameZh":6,"description":96,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":97,"categories":98,"keywords":99,"publishedAt":48},"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",[],[100,101,102,103,104,105,106,107,108,44,109,110,111,112,113],"clarify requirements","clarify","requirements","risk","options","task","prompts","goal","scanning","context","repo","injection","execution","file",{"skillNo":115,"name":116,"nameZh":6,"description":117,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":118,"categories":119,"keywords":121,"publishedAt":48},"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",[120],{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[122,123,124,125,126,127,128,129,130,131,132,133,33,134,44,135,136,137,138,139],"openmm md","how","decision","pick","linux","analyze","tool","stack","open","NVIDIA","gpu","license","windows","free","molecular dynamics","local","do","MCP",{"skillNo":141,"name":142,"nameZh":6,"description":143,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":144,"categories":145,"keywords":150,"publishedAt":48},"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",[146,149],{"categoryNo":147,"categoryName":148,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000003","演示文稿",{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[151,152,153,154,155,156,157,158,159,160,123,161,162,163,164],"review","visual","visual review","pages","web","false","iteration","take","check","slides","ui","screenshot","playwright","screenshots",{"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":171,"publishedAt":48},"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",[],[167,172,173,174,175,176,177,178],"setup","report","project","installation","project setup","issues","state",{"skillNo":180,"name":181,"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":186,"publishedAt":48},"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",[185],{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[181,187,105,188,189,190,44,191,192,193,38],"start issue","issue","ready","work on issue","user","work","work on",{"skillNo":195,"name":172,"nameZh":6,"description":196,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":197,"categories":198,"keywords":199,"publishedAt":48},"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",[],[172,174,137,200,176,201],"files","interactive"]