[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71MP0QP4SQDHVYJ2QQFK72T":3,"skill-same:01M71MP0QP4SQDHVYJ2QQFK72T":96},{"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":52,"linkOnly":53,"markdownContent":54,"markdownBody":55,"frontMatter":56,"refName":61,"commitSha":62,"skillDirectory":63,"forksCount":14,"repositoryHtmlUrl":64,"repositoryHomepageUrl":65,"ownerHtmlUrl":66,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":67,"install":68,"resources":81},"01M71MP0QP4SQDHVYJ2QQFK72T","coverage-analysis",null,"Measure and improve test coverage meaningfully. Covers Istanbul\u002FV8\u002Fcoverage.py configuration, coverage gap analysis by risk, coverage-as-ratchet in CI (never let it decrease), PR coverage diff checks, mutation testing for assertion quality, and distinguishing meaningful from vanity coverage. Use when: \"code coverage,\" \"coverage gap,\" \"Istanbul,\" \"coverage threshold,\" \"coverage report,\" \"branch coverage.\" Not for: writing the tests that raise coverage — use unit-testing; coverage as a tracked KPI trend over time — use qa-metrics. Related: unit-testing, ci-cd-integration, qa-metrics, ai-qa-review.","pattanation\u002FNorvik","pattanation","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F235419268?v=4","01M71M5THHYYB15ME8R2E4FCZG","Patta","01M71M5TJM1NDM992M8PDKYR2K",0,"MIT","https:\u002F\u002Fgithub.com\u002Fpattanation\u002FNorvik\u002Fblob\u002Fclaude\u002Finstall-gsd-build-foQdV\u002F.agents\u002Fskills\u002Fcoverage-analysis\u002FSKILL.md",[18,23,26],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000018","写作与文案",{"categoryNo":27,"categoryName":28,"taxonomyType":29,"taxonomyTypeDesc":30},"01JBSEEDCAT000000000000036","内容创作者","PROFESSION","适用职业",[32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51],"analysis","coverage analysis","coverage","test coverage","trend","mutation testing","ratchet","review","code","when","mutation","writing","qa","improve","ci","testing","time","never","branch","unit","2026-11-10 10:56:23",false,"---\nname: coverage-analysis\ndescription: >-\n  Measure and improve test coverage meaningfully. Covers Istanbul\u002FV8\u002Fcoverage.py\n  configuration, coverage gap analysis by risk, coverage-as-ratchet in CI (never let\n  it decrease), PR coverage diff checks, mutation testing for assertion quality, and\n  distinguishing meaningful from vanity coverage. Use when: \"code coverage,\" \"coverage\n  gap,\" \"Istanbul,\" \"coverage threshold,\" \"coverage report,\" \"branch coverage.\"\n  Not for: writing the tests that raise coverage — use unit-testing; coverage as a\n  tracked KPI trend over time — use qa-metrics.\n  Related: unit-testing, ci-cd-integration, qa-metrics, ai-qa-review.\nlicense: MIT\nmetadata:\n  author: kindlmann\n  version: \"2.0\"\n  category: metrics\n---\n\n\u003Cobjective>\nA suite at 90% line coverage with assertion-free tests catches zero bugs — line coverage\nproves code ran, not that a regression would be caught. This skill measures the right\nthings (branch coverage, mutation score, critical-path coverage), gates them in CI with a\nratchet so coverage can only go up, and surfaces gaps by risk instead of chasing a vanity\nnumber. It prevents the classic failure: a green coverage badge over a test suite that\nnever asserts anything meaningful, while the payment module sits at 30%.\n\u003C\u002Fobjective>\n\n## Quick Route\n\n| Situation | Go to |\n|-----------|-------|\n| Pick and configure a coverage provider | Coverage Tools → `references\u002Ftool-config.md` |\n| Decide where to write tests next | Gap Analysis |\n| Stop coverage from regressing in CI | Coverage as CI Gate → Ratchet Pattern |\n| Show per-PR coverage to reviewers | Coverage as CI Gate → PR Diff |\n| Tests run code but don't assert | Mutation Testing |\n| Decide what to exclude \u002F what target to set | Meaningful vs Vanity Coverage |\n\n---\n\n## Discovery Questions\n\nCheck `.agents\u002Fqa-project-context.md` first — if it exists, use it and skip anything already answered there. Then:\n\n1. **What test runner and coverage tooling is configured?** Check for `vitest.config.*` (coverage block), `jest.config.*` (coverageProvider), `.nycrc`, `c8` in scripts, or `[tool.coverage]` in `pyproject.toml`. The runner decides the install — Vitest pulls `@vitest\u002Fcoverage-v8`, not c8 (see Coverage Tools).\n2. **What is the current coverage level?** Run the existing coverage command and note line, branch, and function percentages. This is the baseline for the ratchet.\n3. **Is coverage gated in CI?** Check GitHub Actions \u002F GitLab CI for `--coverage`, `coverageThreshold`, `fail_under`, or `--cov-fail-under`. No gate means coverage is decorative.\n4. **What is the target, and who set it?** A target without rationale (\"the VP said 80%\") leads to gaming. Targets should reflect risk tolerance and codebase maturity, not a round number.\n\n---\n\n## Core Principles\n\n**1. Coverage measures breadth, not depth.** A line being executed does not mean it is tested correctly. `expect(true).toBe(true)` executes the function but asserts nothing. Coverage tells you what code ran, not whether the tests would catch a bug — that is what mutation testing measures.\n\n**2. Branch coverage matters more than line coverage.** A ternary `condition ? a : b` on one line counts as fully covered in line coverage even if only one branch ran. Line coverage does not guarantee branch coverage. Gate on branches, not just lines:\n\n```typescript\nfunction discount(price: number, isPremium: boolean): number {\n  return isPremium ? price * 0.8 : price;\n}\n\n\u002F\u002F Line coverage: 100% (the line executed). Branch coverage: 50% (only the true branch ran).\nexpect(discount(100, true)).toBe(80);\n\n\u002F\u002F Fix — assert BOTH paths so branch coverage reaches 100%:\nexpect(discount(100, true)).toBe(80);\nexpect(discount(100, false)).toBe(100);\n```\n\n**3. Ratchet pattern: never decrease, only increase.** Record current coverage as the minimum threshold. Every PR must meet or exceed it. Coverage climbs over time without forcing an artificial target up front.\n\n**4. Focus on gaps by risk, not on the number.** A project at 85% is not automatically \"better\" than one at 75%. What matters is whether the untested slice contains payment, auth, or data-integrity logic. Analyze gaps by risk.\n\n**5. New code has a higher bar than legacy code.** Require 90%+ on new code in PRs even if the project sits at 65%. This stops coverage decay without demanding a rewrite of legacy code.\n\n---\n\n## Coverage Tools\n\nPick the provider by **test runner first**, then by how cleanly it maps to your build output.\n\n| Runner \u002F context | Install | Provider |\n|---|---|---|\n| **Vitest** | `@vitest\u002Fcoverage-v8` (default) or `@vitest\u002Fcoverage-istanbul` | `coverage.provider: 'v8'` \u002F `'istanbul'` |\n| **Jest** | bundled (`coverageProvider: 'v8'` or `'babel'`) | V8 or Istanbul\u002Fbabel |\n| **Non-Vitest Node** (`node:test`, plain mocha) | `c8` CLI | V8 via `c8 \u003Ccommand>` |\n| **Legacy Istanbul CLI** | `nyc` | Istanbul instrumentation |\n| **Python** | `pytest-cov` (wraps coverage.py) | coverage.py |\n\nTwo engines underneath:\n\n- **V8 coverage** — built into the V8 engine, so it does not instrument source: faster, no Babel transform. For Vitest, install `@vitest\u002Fcoverage-v8` (NOT `c8` — that is the standalone CLI for non-Vitest runners). For a plain `node:test` or mocha project, `c8` is the CLI wrapper around the same V8 data. Default for new Node\u002FVitest projects.\n- **Istanbul** — instruments source code; slower but maps more reliably through transpilers and bundlers. Switch to it (`@vitest\u002Fcoverage-istanbul`, or `nyc`) when V8 maps poorly. **Symptom that V8 maps poorly:** reported uncovered lines land on blank lines, closing braces, or decorators, or whole covered functions show as red — that means the source map is misattributing lines (common with certain TS bundlers \u002F SWC configs). When you see that, flip to the Istanbul provider.\n\n> **Node baseline:** `c8` 11.x and `nyc` 18.x are current. c8 11 still supports Node >=12; nyc 18 requires **Node 20 || >= 22**. If you must stay on Node 18, pin `nyc@^17` (c8 11 runs fine on Node 18). New projects should standardize on Node 20+.\n\nSee `references\u002Ftool-config.md` for the full provider configs (Vitest `coverage` block, `.nycrc.json`, Jest `coverageThreshold`, `pyproject.toml` \u002F `.coveragerc.toml`), install commands, and run invocations.\n\n### Merging coverage across test types\n\nUnit, integration, and E2E runs each produce partial coverage. Combine them so a line covered only by an integration test isn't reported as a gap:\n\n- **Vitest** — run as multiple projects\u002Fconfigs and let Vitest merge, or merge `coverage-final.json` outputs.\n- **nyc** — `nyc merge .nyc_output merged.json && nyc report -t merged` combines `.json` files from separate runs.\n- **coverage.py** — `coverage combine` after running each suite with `coverage run -p`.\n\nMerge first, gate on the merged total. Don't gate each suite's coverage in isolation.\n\n### Coverage Report Types\n\n| Reporter | Output | Use Case |\n|----------|--------|----------|\n| `text` | Terminal table | Quick local check |\n| `html` | Interactive HTML | Detailed local analysis, clicking through files |\n| `lcov` | `lcov.info` file | SonarQube, Codecov, Coveralls integration |\n| `json-summary` | `coverage-summary.json` | CI scripts, PR comments, dashboard metrics |\n| `cobertura` | `cobertura-coverage.xml` | GitLab CI coverage visualization |\n\n---\n\n## Gap Analysis\n\nCoverage reports show which lines and branches did not run. Not all gaps are equal — prioritize by risk.\n\n**Step 1: Generate the report.**\n\n```bash\nnpm run test:coverage\n# Open coverage\u002Findex.html in a browser\n```\n\n**Step 2: Sort files by uncovered lines.** Parse `coverage-summary.json`, sort files by `(total - covered)` descending, focus on the top 20. A small script that reads the JSON summary and outputs file \u002F line% \u002F branch% \u002F uncovered-count makes this repeatable.\n\n**Step 3: Map gaps to risk.**\n\n| Gap Location | Risk Level | Action |\n|-------------|-----------|--------|\n| Payment processing | Critical | Write tests immediately |\n| Auth\u002Fpermissions | Critical | Write tests immediately |\n| Data validation | High | Add to next sprint |\n| Error handling paths | High | Add to next sprint |\n| Utility functions | Medium | Cover when modifying |\n| UI formatting | Low | Skip unless regression-prone |\n| Generated code | None | Exclude from coverage |\n\nInclude branch coverage in the sort, not just lines — a file at 100% line \u002F 50% branch hides untested paths a line-only sort would rank as \"done.\"\n\n---\n\n## Coverage as CI Gate\n\n### Threshold Configuration\n\nSet a **global threshold** as the project minimum, then layer **per-directory thresholds** stricter for critical code (payments, auth) than for utilities. Vitest uses glob keys under `thresholds` (e.g. `\"src\u002Fpayments\u002F**\": { lines: 95, branches: 90 }`); Jest uses path keys under `coverageThreshold`. Both support per-path overrides.\n\nSee `references\u002Fci-gating.md` for the global, per-directory, and Jest per-file threshold config.\n\n### Ratchet Pattern\n\nNever let coverage decrease. Record the current level as the minimum and floor it upward when coverage improves. A ratchet script reads `coverage-summary.json`, compares each metric against a committed `.coverage-ratchet.json`, fails the build on any regression, and updates the baseline when coverage improves.\n\nCommit `.coverage-ratchet.json` (e.g. `{ \"lines\": 82, \"branches\": 78, ... }`). In CI, run the ratchet script after tests. On main-branch merges, auto-commit the updated ratchet file if coverage improved.\n\nSee `references\u002Fci-gating.md` for the full `coverage-ratchet.ts` script.\n\n### PR Diff Coverage Gate\n\nRequire new code in a PR to meet a higher threshold (e.g. 90%) than the project baseline. In CI, use `git diff --name-only origin\u002Fmain...HEAD` to identify changed files, then check their coverage from `coverage-summary.json`. Fail the pipeline if changed-file coverage falls below the threshold. This stops decay without rewriting legacy code.\n\nSurface the diff to reviewers with `davelosert\u002Fvitest-coverage-report-action@v2` (reads the JSON summary) or `marocchino\u002Fsticky-pull-request-comment@v2` with a script that filters to changed files. See `references\u002Fci-gating.md` for the full PR workflow.\n\n**Hosted alternatives:** **Codecov**, **Coveralls**, and **Trunk Coverage** ship first-class differential PR coverage with merge-blocking gates and inline annotations. Most teams prefer these over hand-rolled diff scripts — pick one if you don't already have a coverage host. Codecov + GitHub: `codecov\u002Fcodecov-action@v5` reads `lcov.info` and posts a PR diff comment automatically.\n\n---\n\n## Mutation Testing\n\nMutation testing measures *assertion quality*, not just code execution. It mutates your source (flips a `>` to `>=`, deletes a line) and checks whether a test fails. A surviving mutant means a real bug your tests would miss. With **Stryker JS v9.6+** and **Vitest 4.1+** the cost is low enough to run on PR-changed files; **mutmut 3.x** covers Python.\n\n### Targeting\n\nMutation testing is expensive on whole codebases — run it **incrementally**. Stryker's `incremental: true` (JSON cache) plus `--mutate` scoped to the git diff re-mutates only touched files; mutmut similarly mutates per path. Restrict to:\n\n- Pure business logic (validators, calculators, transformers)\n- Critical paths (payment, auth, data integrity)\n- Code with high line coverage but suspect assertions (branch coverage > 90% but few assertion variants)\n\nSkip UI rendering, glue code, and generated code.\n\nSee `references\u002Fmutation-testing.md` for the Stryker config (`stryker.config.json`, incremental, run on changed files only) and the mutmut invocation.\n\n### Reading the score\n\nA mutation score of 80% means 80% of injected bugs were caught. Lower than your coverage % is normal — many mutants land in untested branches the coverage report already flagged. The interesting signal is **high coverage + low mutation score**: code executes but assertions don't constrain it.\n\n---\n\n## Meaningful vs Vanity Coverage\n\n### Why 100% Coverage Is Usually Wrong\n\n100% requires testing every branch of every line, including:\n- Error handling for impossible states\n- Default cases in exhaustive switches\n- Framework lifecycle methods never called directly\n- Defensive checks against corrupted data\n\nTests written to hit 100% are often trivial, brittle, and catch no real bugs.\n\n### Diminishing Returns\n\n| Coverage Range | Value | Effort |\n|---------------|-------|--------|\n| 0% to 60% | High — main paths, obvious regressions | Low |\n| 60% to 80% | Medium — error paths, edge cases | Medium |\n| 80% to 90% | Lower — unusual combinations, defensive code | High |\n| 90% to 100% | Minimal — unreachable code, framework internals | Very high |\n\nThe sweet spot is **75–85%** for most projects. Critical paths (payments, auth) aim higher (**90%+**). Set the global threshold in the sweet spot and per-directory thresholds at 90%+ for payment\u002Fauth.\n\n### What NOT to Cover\n\nExclude these — they inflate the denominator without adding value. Document each exclusion's justification in a CONTRIBUTING\u002Fcoverage note so the exclude list can't quietly hide real gaps.\n\n```typescript\n\u002F\u002F vitest.config.ts \u002F jest.config.js — exclude patterns\nexclude: [\n  \"**\u002F*.d.ts\",              \u002F\u002F Type definitions\n  \"**\u002Findex.ts\",            \u002F\u002F Barrel exports (re-exports only)\n  \"**\u002F*.stories.{ts,tsx}\",  \u002F\u002F Storybook stories\n  \"**\u002Fgenerated\u002F**\",        \u002F\u002F Auto-generated code (GraphQL, Prisma)\n  \"**\u002Fmigrations\u002F**\",       \u002F\u002F Database migrations\n  \"**\u002F__mocks__\u002F**\",        \u002F\u002F Test mocks\n  \"**\u002Ftypes\u002F**\",            \u002F\u002F Type-only modules\n]\n```\n\n### Quality Indicators Beyond Percentage\n\n| Indicator | What It Measures | How to Get It |\n|-----------|-----------------|---------------|\n| **Mutation score** | Would tests catch a real bug? | Stryker \u002F mutmut |\n| **Branch coverage** | Are all conditional paths tested? | V8\u002FIstanbul with branch reporting |\n| **Critical path coverage** | Are payment\u002Fauth\u002Fdata flows fully covered? | Per-directory thresholds |\n| **Defect escape rate** | Do production bugs occur in tested code? | Post-incident analysis |\n| **Coverage delta** | Is coverage improving or declining? | Ratchet pattern tracking |\n\n---\n\n## Anti-Patterns\n\n### 1. Treating coverage as proof of quality\n\"We have 90% coverage so we're well-tested\" is dangerous. Coverage says code executed, not that behavior was verified. **Fix:** pair the percentage with a mutation score on critical modules; a test with no assertions should drop the mutation score even at 100% line coverage.\n\n### 2. Excluding files to inflate numbers\nAdding hard-to-test files (error handlers, integration modules) to the exclude list hides the most important gaps. **Fix:** only exclude genuinely untestable code — generated files, type definitions, barrel exports — and justify each exclusion in writing.\n\n### 3. Writing trivial tests to hit targets\n`it(\"should exist\", () => expect(MyClass).toBeDefined())` adds coverage without value. **Fix:** every test verifies a behavior that, if broken, affects users; mutation testing flags these no-op tests.\n\n### 4. Global threshold without per-module analysis\nAn 80% global threshold passes even if payments sits at 30%, as long as utilities inflate the average. **Fix:** per-directory thresholds at 90%+ for payment\u002Fauth.\n\n### 5. Coverage threshold set once, never adjusted\nA team stuck at 78% for six months isn't improving. **Fix:** the ratchet floors upward automatically; review it quarterly and investigate stagnation.\n\n### 6. Ignoring branch coverage\nLine coverage reports 100% on `const r = cond ? a : b` even if one branch never runs. **Fix:** always report and gate on `branches` alongside `lines`.\n\n### 7. Coverage from E2E tests only\nA single E2E test touches 60% of the codebase without testing any edge case — broad and shallow. **Fix:** measure unit\u002Fintegration coverage separately from E2E and gate on the unit\u002Fintegration total; E2E coverage is a bonus signal, not the gate.\n\n---\n\n## Verification\n\nProve the gate actually fails on regression — a green build with no enforcement is worthless.\n\n1. **Threshold fires:** temporarily lower one committed metric below current (or delete one passing test), run `npm run test:coverage` (or `pytest --cov=src --cov-fail-under=80`), and confirm a **non-zero exit code**. Restore afterward.\n2. **Ratchet fires:** with `.coverage-ratchet.json` committed, drop a test so coverage regresses, run the ratchet script, and confirm it prints `FAIL: ... coverage dropped` and exits 1.\n3. **Branch gate is on:** confirm the report shows a `branches` column and that `branches` appears in the threshold config — not just `lines`.\n4. **PR diff renders:** open a draft PR touching one source file and confirm the coverage-diff comment posts with the changed file's numbers.\n\nIf step 1 exits 0 after you lowered coverage, the gate is not wired — fix that before claiming Done.\n\n---\n\n## Done When\n\n- Coverage runs automatically in CI on every push (no manual step to generate the report).\n- Coverage threshold is enforced in CI: the build exits non-zero when line **or branch** coverage drops below the defined minimum (verified per Verification step 1).\n- Coverage report is published as a CI artifact (HTML + `json-summary`) and a per-PR coverage delta posts to PR comments.\n- The coverage config contains an exclude list and per-directory thresholds (90%+ for payments\u002Fauth), and a CONTRIBUTING\u002Fcoverage doc lists each exclusion's justification.\n- Ratchet is wired: `.coverage-ratchet.json` is committed, the ratchet script runs in CI, and the build fails on regression from the recorded baseline (verified per Verification step 2).\n\n## Reference Files (in `references\u002F`)\n\n- **tool-config.md** — Full provider configs for Vitest (`@vitest\u002Fcoverage-v8` \u002F `-istanbul`), the c8 CLI for non-Vitest runners, nyc, Jest, and coverage.py, with install and run commands.\n- **ci-gating.md** — PR coverage-diff workflow, global\u002Fper-directory\u002Fper-file thresholds, and the `coverage-ratchet.ts` script.\n- **mutation-testing.md** — Stryker (`stryker.config.json`, incremental) and mutmut configuration for measuring assertion quality.\n\n## Related Skills\n\n- **unit-testing** — Writing the tests that raise coverage: mocking strategies, framework-specific config, and Vitest `coverage.changed` for changed-files-only coverage in CI. Go there to author tests; this skill measures and gates them.\n- **ci-cd-integration** — Pipeline wiring for coverage gates, artifact storage, and PR comments.\n- **qa-metrics** — Coverage as a tracked KPI trend over time alongside mutation score and defect escape rate. Go there for dashboards and trends; this skill is per-repo configuration and gating.\n- **ai-qa-review** — AI-assisted identification of undertested paths and Vitest browser mode for component coverage parity.\n","\u003Cobjective>\nA suite at 90% line coverage with assertion-free tests catches zero bugs — line coverage\nproves code ran, not that a regression would be caught. This skill measures the right\nthings (branch coverage, mutation score, critical-path coverage), gates them in CI with a\nratchet so coverage can only go up, and surfaces gaps by risk instead of chasing a vanity\nnumber. It prevents the classic failure: a green coverage badge over a test suite that\nnever asserts anything meaningful, while the payment module sits at 30%.\n\u003C\u002Fobjective>\n\n## Quick Route\n\n| Situation | Go to |\n|-----------|-------|\n| Pick and configure a coverage provider | Coverage Tools → `references\u002Ftool-config.md` |\n| Decide where to write tests next | Gap Analysis |\n| Stop coverage from regressing in CI | Coverage as CI Gate → Ratchet Pattern |\n| Show per-PR coverage to reviewers | Coverage as CI Gate → PR Diff |\n| Tests run code but don't assert | Mutation Testing |\n| Decide what to exclude \u002F what target to set | Meaningful vs Vanity Coverage |\n\n---\n\n## Discovery Questions\n\nCheck `.agents\u002Fqa-project-context.md` first — if it exists, use it and skip anything already answered there. Then:\n\n1. **What test runner and coverage tooling is configured?** Check for `vitest.config.*` (coverage block), `jest.config.*` (coverageProvider), `.nycrc`, `c8` in scripts, or `[tool.coverage]` in `pyproject.toml`. The runner decides the install — Vitest pulls `@vitest\u002Fcoverage-v8`, not c8 (see Coverage Tools).\n2. **What is the current coverage level?** Run the existing coverage command and note line, branch, and function percentages. This is the baseline for the ratchet.\n3. **Is coverage gated in CI?** Check GitHub Actions \u002F GitLab CI for `--coverage`, `coverageThreshold`, `fail_under`, or `--cov-fail-under`. No gate means coverage is decorative.\n4. **What is the target, and who set it?** A target without rationale (\"the VP said 80%\") leads to gaming. Targets should reflect risk tolerance and codebase maturity, not a round number.\n\n---\n\n## Core Principles\n\n**1. Coverage measures breadth, not depth.** A line being executed does not mean it is tested correctly. `expect(true).toBe(true)` executes the function but asserts nothing. Coverage tells you what code ran, not whether the tests would catch a bug — that is what mutation testing measures.\n\n**2. Branch coverage matters more than line coverage.** A ternary `condition ? a : b` on one line counts as fully covered in line coverage even if only one branch ran. Line coverage does not guarantee branch coverage. Gate on branches, not just lines:\n\n```typescript\nfunction discount(price: number, isPremium: boolean): number {\n  return isPremium ? price * 0.8 : price;\n}\n\n\u002F\u002F Line coverage: 100% (the line executed). Branch coverage: 50% (only the true branch ran).\nexpect(discount(100, true)).toBe(80);\n\n\u002F\u002F Fix — assert BOTH paths so branch coverage reaches 100%:\nexpect(discount(100, true)).toBe(80);\nexpect(discount(100, false)).toBe(100);\n```\n\n**3. Ratchet pattern: never decrease, only increase.** Record current coverage as the minimum threshold. Every PR must meet or exceed it. Coverage climbs over time without forcing an artificial target up front.\n\n**4. Focus on gaps by risk, not on the number.** A project at 85% is not automatically \"better\" than one at 75%. What matters is whether the untested slice contains payment, auth, or data-integrity logic. Analyze gaps by risk.\n\n**5. New code has a higher bar than legacy code.** Require 90%+ on new code in PRs even if the project sits at 65%. This stops coverage decay without demanding a rewrite of legacy code.\n\n---\n\n## Coverage Tools\n\nPick the provider by **test runner first**, then by how cleanly it maps to your build output.\n\n| Runner \u002F context | Install | Provider |\n|---|---|---|\n| **Vitest** | `@vitest\u002Fcoverage-v8` (default) or `@vitest\u002Fcoverage-istanbul` | `coverage.provider: 'v8'` \u002F `'istanbul'` |\n| **Jest** | bundled (`coverageProvider: 'v8'` or `'babel'`) | V8 or Istanbul\u002Fbabel |\n| **Non-Vitest Node** (`node:test`, plain mocha) | `c8` CLI | V8 via `c8 \u003Ccommand>` |\n| **Legacy Istanbul CLI** | `nyc` | Istanbul instrumentation |\n| **Python** | `pytest-cov` (wraps coverage.py) | coverage.py |\n\nTwo engines underneath:\n\n- **V8 coverage** — built into the V8 engine, so it does not instrument source: faster, no Babel transform. For Vitest, install `@vitest\u002Fcoverage-v8` (NOT `c8` — that is the standalone CLI for non-Vitest runners). For a plain `node:test` or mocha project, `c8` is the CLI wrapper around the same V8 data. Default for new Node\u002FVitest projects.\n- **Istanbul** — instruments source code; slower but maps more reliably through transpilers and bundlers. Switch to it (`@vitest\u002Fcoverage-istanbul`, or `nyc`) when V8 maps poorly. **Symptom that V8 maps poorly:** reported uncovered lines land on blank lines, closing braces, or decorators, or whole covered functions show as red — that means the source map is misattributing lines (common with certain TS bundlers \u002F SWC configs). When you see that, flip to the Istanbul provider.\n\n> **Node baseline:** `c8` 11.x and `nyc` 18.x are current. c8 11 still supports Node >=12; nyc 18 requires **Node 20 || >= 22**. If you must stay on Node 18, pin `nyc@^17` (c8 11 runs fine on Node 18). New projects should standardize on Node 20+.\n\nSee `references\u002Ftool-config.md` for the full provider configs (Vitest `coverage` block, `.nycrc.json`, Jest `coverageThreshold`, `pyproject.toml` \u002F `.coveragerc.toml`), install commands, and run invocations.\n\n### Merging coverage across test types\n\nUnit, integration, and E2E runs each produce partial coverage. Combine them so a line covered only by an integration test isn't reported as a gap:\n\n- **Vitest** — run as multiple projects\u002Fconfigs and let Vitest merge, or merge `coverage-final.json` outputs.\n- **nyc** — `nyc merge .nyc_output merged.json && nyc report -t merged` combines `.json` files from separate runs.\n- **coverage.py** — `coverage combine` after running each suite with `coverage run -p`.\n\nMerge first, gate on the merged total. Don't gate each suite's coverage in isolation.\n\n### Coverage Report Types\n\n| Reporter | Output | Use Case |\n|----------|--------|----------|\n| `text` | Terminal table | Quick local check |\n| `html` | Interactive HTML | Detailed local analysis, clicking through files |\n| `lcov` | `lcov.info` file | SonarQube, Codecov, Coveralls integration |\n| `json-summary` | `coverage-summary.json` | CI scripts, PR comments, dashboard metrics |\n| `cobertura` | `cobertura-coverage.xml` | GitLab CI coverage visualization |\n\n---\n\n## Gap Analysis\n\nCoverage reports show which lines and branches did not run. Not all gaps are equal — prioritize by risk.\n\n**Step 1: Generate the report.**\n\n```bash\nnpm run test:coverage\n# Open coverage\u002Findex.html in a browser\n```\n\n**Step 2: Sort files by uncovered lines.** Parse `coverage-summary.json`, sort files by `(total - covered)` descending, focus on the top 20. A small script that reads the JSON summary and outputs file \u002F line% \u002F branch% \u002F uncovered-count makes this repeatable.\n\n**Step 3: Map gaps to risk.**\n\n| Gap Location | Risk Level | Action |\n|-------------|-----------|--------|\n| Payment processing | Critical | Write tests immediately |\n| Auth\u002Fpermissions | Critical | Write tests immediately |\n| Data validation | High | Add to next sprint |\n| Error handling paths | High | Add to next sprint |\n| Utility functions | Medium | Cover when modifying |\n| UI formatting | Low | Skip unless regression-prone |\n| Generated code | None | Exclude from coverage |\n\nInclude branch coverage in the sort, not just lines — a file at 100% line \u002F 50% branch hides untested paths a line-only sort would rank as \"done.\"\n\n---\n\n## Coverage as CI Gate\n\n### Threshold Configuration\n\nSet a **global threshold** as the project minimum, then layer **per-directory thresholds** stricter for critical code (payments, auth) than for utilities. Vitest uses glob keys under `thresholds` (e.g. `\"src\u002Fpayments\u002F**\": { lines: 95, branches: 90 }`); Jest uses path keys under `coverageThreshold`. Both support per-path overrides.\n\nSee `references\u002Fci-gating.md` for the global, per-directory, and Jest per-file threshold config.\n\n### Ratchet Pattern\n\nNever let coverage decrease. Record the current level as the minimum and floor it upward when coverage improves. A ratchet script reads `coverage-summary.json`, compares each metric against a committed `.coverage-ratchet.json`, fails the build on any regression, and updates the baseline when coverage improves.\n\nCommit `.coverage-ratchet.json` (e.g. `{ \"lines\": 82, \"branches\": 78, ... }`). In CI, run the ratchet script after tests. On main-branch merges, auto-commit the updated ratchet file if coverage improved.\n\nSee `references\u002Fci-gating.md` for the full `coverage-ratchet.ts` script.\n\n### PR Diff Coverage Gate\n\nRequire new code in a PR to meet a higher threshold (e.g. 90%) than the project baseline. In CI, use `git diff --name-only origin\u002Fmain...HEAD` to identify changed files, then check their coverage from `coverage-summary.json`. Fail the pipeline if changed-file coverage falls below the threshold. This stops decay without rewriting legacy code.\n\nSurface the diff to reviewers with `davelosert\u002Fvitest-coverage-report-action@v2` (reads the JSON summary) or `marocchino\u002Fsticky-pull-request-comment@v2` with a script that filters to changed files. See `references\u002Fci-gating.md` for the full PR workflow.\n\n**Hosted alternatives:** **Codecov**, **Coveralls**, and **Trunk Coverage** ship first-class differential PR coverage with merge-blocking gates and inline annotations. Most teams prefer these over hand-rolled diff scripts — pick one if you don't already have a coverage host. Codecov + GitHub: `codecov\u002Fcodecov-action@v5` reads `lcov.info` and posts a PR diff comment automatically.\n\n---\n\n## Mutation Testing\n\nMutation testing measures *assertion quality*, not just code execution. It mutates your source (flips a `>` to `>=`, deletes a line) and checks whether a test fails. A surviving mutant means a real bug your tests would miss. With **Stryker JS v9.6+** and **Vitest 4.1+** the cost is low enough to run on PR-changed files; **mutmut 3.x** covers Python.\n\n### Targeting\n\nMutation testing is expensive on whole codebases — run it **incrementally**. Stryker's `incremental: true` (JSON cache) plus `--mutate` scoped to the git diff re-mutates only touched files; mutmut similarly mutates per path. Restrict to:\n\n- Pure business logic (validators, calculators, transformers)\n- Critical paths (payment, auth, data integrity)\n- Code with high line coverage but suspect assertions (branch coverage > 90% but few assertion variants)\n\nSkip UI rendering, glue code, and generated code.\n\nSee `references\u002Fmutation-testing.md` for the Stryker config (`stryker.config.json`, incremental, run on changed files only) and the mutmut invocation.\n\n### Reading the score\n\nA mutation score of 80% means 80% of injected bugs were caught. Lower than your coverage % is normal — many mutants land in untested branches the coverage report already flagged. The interesting signal is **high coverage + low mutation score**: code executes but assertions don't constrain it.\n\n---\n\n## Meaningful vs Vanity Coverage\n\n### Why 100% Coverage Is Usually Wrong\n\n100% requires testing every branch of every line, including:\n- Error handling for impossible states\n- Default cases in exhaustive switches\n- Framework lifecycle methods never called directly\n- Defensive checks against corrupted data\n\nTests written to hit 100% are often trivial, brittle, and catch no real bugs.\n\n### Diminishing Returns\n\n| Coverage Range | Value | Effort |\n|---------------|-------|--------|\n| 0% to 60% | High — main paths, obvious regressions | Low |\n| 60% to 80% | Medium — error paths, edge cases | Medium |\n| 80% to 90% | Lower — unusual combinations, defensive code | High |\n| 90% to 100% | Minimal — unreachable code, framework internals | Very high |\n\nThe sweet spot is **75–85%** for most projects. Critical paths (payments, auth) aim higher (**90%+**). Set the global threshold in the sweet spot and per-directory thresholds at 90%+ for payment\u002Fauth.\n\n### What NOT to Cover\n\nExclude these — they inflate the denominator without adding value. Document each exclusion's justification in a CONTRIBUTING\u002Fcoverage note so the exclude list can't quietly hide real gaps.\n\n```typescript\n\u002F\u002F vitest.config.ts \u002F jest.config.js — exclude patterns\nexclude: [\n  \"**\u002F*.d.ts\",              \u002F\u002F Type definitions\n  \"**\u002Findex.ts\",            \u002F\u002F Barrel exports (re-exports only)\n  \"**\u002F*.stories.{ts,tsx}\",  \u002F\u002F Storybook stories\n  \"**\u002Fgenerated\u002F**\",        \u002F\u002F Auto-generated code (GraphQL, Prisma)\n  \"**\u002Fmigrations\u002F**\",       \u002F\u002F Database migrations\n  \"**\u002F__mocks__\u002F**\",        \u002F\u002F Test mocks\n  \"**\u002Ftypes\u002F**\",            \u002F\u002F Type-only modules\n]\n```\n\n### Quality Indicators Beyond Percentage\n\n| Indicator | What It Measures | How to Get It |\n|-----------|-----------------|---------------|\n| **Mutation score** | Would tests catch a real bug? | Stryker \u002F mutmut |\n| **Branch coverage** | Are all conditional paths tested? | V8\u002FIstanbul with branch reporting |\n| **Critical path coverage** | Are payment\u002Fauth\u002Fdata flows fully covered? | Per-directory thresholds |\n| **Defect escape rate** | Do production bugs occur in tested code? | Post-incident analysis |\n| **Coverage delta** | Is coverage improving or declining? | Ratchet pattern tracking |\n\n---\n\n## Anti-Patterns\n\n### 1. Treating coverage as proof of quality\n\"We have 90% coverage so we're well-tested\" is dangerous. Coverage says code executed, not that behavior was verified. **Fix:** pair the percentage with a mutation score on critical modules; a test with no assertions should drop the mutation score even at 100% line coverage.\n\n### 2. Excluding files to inflate numbers\nAdding hard-to-test files (error handlers, integration modules) to the exclude list hides the most important gaps. **Fix:** only exclude genuinely untestable code — generated files, type definitions, barrel exports — and justify each exclusion in writing.\n\n### 3. Writing trivial tests to hit targets\n`it(\"should exist\", () => expect(MyClass).toBeDefined())` adds coverage without value. **Fix:** every test verifies a behavior that, if broken, affects users; mutation testing flags these no-op tests.\n\n### 4. Global threshold without per-module analysis\nAn 80% global threshold passes even if payments sits at 30%, as long as utilities inflate the average. **Fix:** per-directory thresholds at 90%+ for payment\u002Fauth.\n\n### 5. Coverage threshold set once, never adjusted\nA team stuck at 78% for six months isn't improving. **Fix:** the ratchet floors upward automatically; review it quarterly and investigate stagnation.\n\n### 6. Ignoring branch coverage\nLine coverage reports 100% on `const r = cond ? a : b` even if one branch never runs. **Fix:** always report and gate on `branches` alongside `lines`.\n\n### 7. Coverage from E2E tests only\nA single E2E test touches 60% of the codebase without testing any edge case — broad and shallow. **Fix:** measure unit\u002Fintegration coverage separately from E2E and gate on the unit\u002Fintegration total; E2E coverage is a bonus signal, not the gate.\n\n---\n\n## Verification\n\nProve the gate actually fails on regression — a green build with no enforcement is worthless.\n\n1. **Threshold fires:** temporarily lower one committed metric below current (or delete one passing test), run `npm run test:coverage` (or `pytest --cov=src --cov-fail-under=80`), and confirm a **non-zero exit code**. Restore afterward.\n2. **Ratchet fires:** with `.coverage-ratchet.json` committed, drop a test so coverage regresses, run the ratchet script, and confirm it prints `FAIL: ... coverage dropped` and exits 1.\n3. **Branch gate is on:** confirm the report shows a `branches` column and that `branches` appears in the threshold config — not just `lines`.\n4. **PR diff renders:** open a draft PR touching one source file and confirm the coverage-diff comment posts with the changed file's numbers.\n\nIf step 1 exits 0 after you lowered coverage, the gate is not wired — fix that before claiming Done.\n\n---\n\n## Done When\n\n- Coverage runs automatically in CI on every push (no manual step to generate the report).\n- Coverage threshold is enforced in CI: the build exits non-zero when line **or branch** coverage drops below the defined minimum (verified per Verification step 1).\n- Coverage report is published as a CI artifact (HTML + `json-summary`) and a per-PR coverage delta posts to PR comments.\n- The coverage config contains an exclude list and per-directory thresholds (90%+ for payments\u002Fauth), and a CONTRIBUTING\u002Fcoverage doc lists each exclusion's justification.\n- Ratchet is wired: `.coverage-ratchet.json` is committed, the ratchet script runs in CI, and the build fails on regression from the recorded baseline (verified per Verification step 2).\n\n## Reference Files (in `references\u002F`)\n\n- **tool-config.md** — Full provider configs for Vitest (`@vitest\u002Fcoverage-v8` \u002F `-istanbul`), the c8 CLI for non-Vitest runners, nyc, Jest, and coverage.py, with install and run commands.\n- **ci-gating.md** — PR coverage-diff workflow, global\u002Fper-directory\u002Fper-file thresholds, and the `coverage-ratchet.ts` script.\n- **mutation-testing.md** — Stryker (`stryker.config.json`, incremental) and mutmut configuration for measuring assertion quality.\n\n## Related Skills\n\n- **unit-testing** — Writing the tests that raise coverage: mocking strategies, framework-specific config, and Vitest `coverage.changed` for changed-files-only coverage in CI. Go there to author tests; this skill measures and gates them.\n- **ci-cd-integration** — Pipeline wiring for coverage gates, artifact storage, and PR comments.\n- **qa-metrics** — Coverage as a tracked KPI trend over time alongside mutation score and defect escape rate. Go there for dashboards and trends; this skill is per-repo configuration and gating.\n- **ai-qa-review** — AI-assisted identification of undertested paths and Vitest browser mode for component coverage parity.\n",{"name":5,"license":15,"metadata":57,"description":7},{"author":58,"version":59,"category":60},"kindlmann","2.0","metrics","claude\u002Finstall-gsd-build-foQdV","d86cd127f527124b5fd0fd0bc679a4d79b7d01d7",".agents\u002Fskills\u002Fcoverage-analysis","https:\u002F\u002Fgithub.com\u002Fpattanation\u002FNorvik","https:\u002F\u002Fnorvik-orpin.vercel.app","https:\u002F\u002Fgithub.com\u002Fpattanation","2026-07-17 11:17:04",{"source":8,"skillName":5,"commands":69,"prompt":79,"zipAvailable":80},[70,73,76],{"tool":71,"command":72},"npx","npx skills add pattanation\u002FNorvik --skill coverage-analysis",{"tool":74,"command":75},"bunx","bunx skills add pattanation\u002FNorvik --skill coverage-analysis",{"tool":77,"command":78},"pnpm","pnpm dlx skills add pattanation\u002FNorvik --skill coverage-analysis","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fpattanation\u002FNorvik\u002Fblob\u002Fclaude\u002Finstall-gsd-build-foQdV\u002F.agents\u002Fskills\u002Fcoverage-analysis\u002FSKILL.md",true,[82,88,92],{"path":83,"resourceType":84,"resourceTypeDesc":85,"sizeBytes":86,"sourceHtmlUrl":87},"references\u002Fci-gating.md","REFERENCE","参考文档",3943,"https:\u002F\u002Fgithub.com\u002Fpattanation\u002FNorvik\u002Fblob\u002Fclaude\u002Finstall-gsd-build-foQdV\u002F.agents\u002Fskills\u002Fcoverage-analysis\u002Freferences\u002Fci-gating.md",{"path":89,"resourceType":84,"resourceTypeDesc":85,"sizeBytes":90,"sourceHtmlUrl":91},"references\u002Fmutation-testing.md",883,"https:\u002F\u002Fgithub.com\u002Fpattanation\u002FNorvik\u002Fblob\u002Fclaude\u002Finstall-gsd-build-foQdV\u002F.agents\u002Fskills\u002Fcoverage-analysis\u002Freferences\u002Fmutation-testing.md",{"path":93,"resourceType":84,"resourceTypeDesc":85,"sizeBytes":94,"sourceHtmlUrl":95},"references\u002Ftool-config.md",4931,"https:\u002F\u002Fgithub.com\u002Fpattanation\u002FNorvik\u002Fblob\u002Fclaude\u002Finstall-gsd-build-foQdV\u002F.agents\u002Fskills\u002Fcoverage-analysis\u002Freferences\u002Ftool-config.md",[97,127,153,176,198,213],{"skillNo":98,"name":99,"nameZh":6,"description":100,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":101,"licenseFileUrl":102,"sourceHtmlUrl":103,"categories":104,"keywords":109,"publishedAt":126},"01M71QDV4DM8T19HEHWATEF1K4","webapp-testing","Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.","Apache-2.0","https:\u002F\u002Fgithub.com\u002Fpattanation\u002FNorvik\u002Fblob\u002Fclaude\u002Finstall-gsd-build-foQdV\u002F.agents\u002Fskills\u002Fwebapp-testing\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fpattanation\u002FNorvik\u002Fblob\u002Fclaude\u002Finstall-gsd-build-foQdV\u002F.agents\u002Fskills\u002Fwebapp-testing\u002FSKILL.md",[105,106],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":107,"categoryName":108,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发",[110,47,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125],"webapp","webapp testing","for","applications","local","frontend","debugging","toolkit","logs","browser","with","playwright","web","behavior","ui","screenshots","2026-11-10 13:02:02",{"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":101,"licenseFileUrl":131,"sourceHtmlUrl":132,"categories":133,"keywords":135,"publishedAt":126},"01M71QDH2GGMXGA9105C9W1N7B","web-artifacts-builder","Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn\u002Fui). Use for complex artifacts requiring state management, routing, or shadcn\u002Fui components - not for simple single-file HTML\u002FJSX artifacts.","https:\u002F\u002Fgithub.com\u002Fpattanation\u002FNorvik\u002Fblob\u002Fclaude\u002Finstall-gsd-build-foQdV\u002F.agents\u002Fskills\u002Fweb-artifacts-builder\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fpattanation\u002FNorvik\u002Fblob\u002Fclaude\u002Finstall-gsd-build-foQdV\u002F.agents\u002Fskills\u002Fweb-artifacts-builder\u002FSKILL.md",[134],{"categoryNo":107,"categoryName":108,"taxonomyType":21,"taxonomyTypeDesc":22},[136,137,138,122,139,140,141,112,142,143,115,144,145,146,147,148,149,150,151,152],"builder","artifacts builder","artifacts","web artifacts builder","single-file","css","Tailwind","ui components","single","use","file","components","react","multi","state","routing","state management",{"skillNo":154,"name":155,"nameZh":6,"description":156,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":157,"sourceHtmlUrl":158,"categories":159,"keywords":161,"publishedAt":126},"01M71QD9T2XC3EWMQNN9ACNWQ1","vueuse-functions","Apply VueUse composables where appropriate to build concise, maintainable Vue.js \u002F Nuxt features.","https:\u002F\u002Fgithub.com\u002Fpattanation\u002FNorvik\u002Fblob\u002Fclaude\u002Finstall-gsd-build-foQdV\u002F.agents\u002Fskills\u002Fvueuse-functions\u002FLICENSE.md","https:\u002F\u002Fgithub.com\u002Fpattanation\u002FNorvik\u002Fblob\u002Fclaude\u002Finstall-gsd-build-foQdV\u002F.agents\u002Fskills\u002Fvueuse-functions\u002FSKILL.md",[160],{"categoryNo":107,"categoryName":108,"taxonomyType":21,"taxonomyTypeDesc":22},[162,163,164,165,166,167,168,169,170,171,172,173,174,175],"functions","vueuse","vueuse functions","project","nuxt","apply","github","concise","build","https","features","vue","js","composables",{"skillNo":177,"name":178,"nameZh":6,"description":179,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":180,"sourceHtmlUrl":181,"categories":182,"keywords":185,"publishedAt":126},"01M71QD51HWSGYMCMB7RW7PGK8","vue-testing-best-practices","Use for Vue.js testing. Covers Vitest, Vue Test Utils, component testing, mocking, testing patterns, and Playwright for E2E testing.","https:\u002F\u002Fgithub.com\u002Fpattanation\u002FNorvik\u002Fblob\u002Fclaude\u002Finstall-gsd-build-foQdV\u002F.agents\u002Fskills\u002Fvue-testing-best-practices\u002FLICENSE.md","https:\u002F\u002Fgithub.com\u002Fpattanation\u002FNorvik\u002Fblob\u002Fclaude\u002Finstall-gsd-build-foQdV\u002F.agents\u002Fskills\u002Fvue-testing-best-practices\u002FSKILL.md",[183,184],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":107,"categoryName":108,"taxonomyType":21,"taxonomyTypeDesc":22},[186,47,187,173,188,121,189,190,191,168,192,112,193,194,195,145,196,174,197],"vue testing best practices","testing best practices","best practices","test","testing patterns","component","vitest","mocking","e2e","utils","patterns","e2e testing",{"skillNo":199,"name":200,"nameZh":6,"description":201,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":202,"sourceHtmlUrl":203,"categories":204,"keywords":206,"publishedAt":126},"01M71QD358H2DJ9THJRBP77T3T","vue-router-best-practices","Vue Router 4 patterns, navigation guards, route params, and route-component lifecycle interactions.","https:\u002F\u002Fgithub.com\u002Fpattanation\u002FNorvik\u002Fblob\u002Fclaude\u002Finstall-gsd-build-foQdV\u002F.agents\u002Fskills\u002Fvue-router-best-practices\u002FLICENSE.md","https:\u002F\u002Fgithub.com\u002Fpattanation\u002FNorvik\u002Fblob\u002Fclaude\u002Finstall-gsd-build-foQdV\u002F.agents\u002Fskills\u002Fvue-router-best-practices\u002FSKILL.md",[205],{"categoryNo":107,"categoryName":108,"taxonomyType":21,"taxonomyTypeDesc":22},[207,173,208,188,209,191,210,168,196,211,212],"router","vue router best practices","route","guards","navigation","lifecycle",{"skillNo":214,"name":215,"nameZh":6,"description":216,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":217,"sourceHtmlUrl":218,"categories":219,"keywords":221,"publishedAt":126},"01M71QCYPNWDE2MQZ1TF3ED5S4","vue-best-practices","MUST be used for Vue.js tasks. Strongly recommends Composition API with `\u003Cscript setup>` and TypeScript as the standard approach. Covers Vue 3, SSR, Volar, vue-tsc. Load for any Vue, .vue files, Vue Router, Pinia, or Vite with Vue work. ALWAYS use Composition API unless the project explicitly requires Options API.","https:\u002F\u002Fgithub.com\u002Fpattanation\u002FNorvik\u002Fblob\u002Fclaude\u002Finstall-gsd-build-foQdV\u002F.agents\u002Fskills\u002Fvue-best-practices\u002FLICENSE.md","https:\u002F\u002Fgithub.com\u002Fpattanation\u002FNorvik\u002Fblob\u002Fclaude\u002Finstall-gsd-build-foQdV\u002F.agents\u002Fskills\u002Fvue-best-practices\u002FSKILL.md",[220],{"categoryNo":107,"categoryName":108,"taxonomyType":21,"taxonomyTypeDesc":22},[222,173,188,223,165,224,225,226,207,227,228,229,230,231,232,112,233,145,234,235],"vue best practices","Composition","files","tasks","api","pinia","work","options","ssr","typescript","as","vite","load","standard"]