[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6E0JG9F44E7AVWKXFV2JCSS":3,"skill-same:01M6E0JG9F44E7AVWKXFV2JCSS":78},{"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":23,"publishedAt":44,"linkOnly":45,"markdownContent":46,"markdownBody":47,"frontMatter":48,"refName":50,"commitSha":51,"skillDirectory":52,"forksCount":14,"repositoryHtmlUrl":53,"repositoryHomepageUrl":6,"ownerHtmlUrl":54,"ownerBlogUrl":55,"ownerDescription":56,"updatedAt":57,"install":58,"resources":71},"01M6E0JG9F44E7AVWKXFV2JCSS","test-cleanup",null,"Audit and prune a bloated test suite down to the minimum tests that hit the coverage threshold and guard real behavior. Replaces many shallow unit tests with fewer well-scoped integration tests. Use when test count is disproportionate to app size.","LucasSantana-Dev\u002Fforgekit","LucasSantana-Dev","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F98131142?v=4","01M6E038X8H7VE9GCEJP9PT5CA","Lucas Santana","01M6E0391WB0JYZTWXEBR9WSXA",1,"MIT","https:\u002F\u002Fgithub.com\u002FLucasSantana-Dev\u002Fforgekit\u002Fblob\u002Fmain\u002Fpackages\u002Fcatalog\u002Fcatalog\u002Fskills\u002Fadt-test-cleanup\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43],"test cleanup","test","cleanup","with","scoped","down","count","suite","when","unit","guard","unit tests","use","tests","test suite","integration tests","prune","audit","app","coverage","2026-11-10 10:12:30",false,"---\nname: test-cleanup\ndescription: Audit and prune a bloated test suite down to the minimum tests that hit the coverage threshold and guard real behavior. Replaces many shallow unit tests with fewer well-scoped integration tests. Use when test count is disproportionate to app size.\ntriggers:\n  - test-cleanup\n---\n\n# Test Cleanup\n\n**Goal: hit the coverage threshold with the fewest, fastest tests possible.**\n\nNot zero tests. Not maximum deletion. The minimum set of well-written tests that:\n1. Reach the project's coverage target\n2. Guard real behavior and regressions\n3. Run fast enough that nobody skips them\n\nA bloated suite of 1.4k shallow tests gives worse protection than 150 integration tests\nthat actually exercise real code paths. Many shallow tests count coverage lines the same\nway one integration test does — but the integration test finds real bugs.\n\n---\n\n## Step 1 — Gather baseline numbers\n\n```bash\n# Coverage threshold (check jest.config \u002F vitest.config \u002F package.json)\ngrep -r \"coverageThreshold\\|threshold\\|coverage\" jest.config* vitest.config* package.json \\\n  | grep -v node_modules | head -10\n\n# Run with coverage AND timing\nnpm test -- --coverage --verbose 2>&1 | tee \u002Ftmp\u002Ftest-baseline.txt\ntail -30 \u002Ftmp\u002Ftest-baseline.txt\n\n# Pull the key numbers from the run\ngrep -E \"Tests:|Test Suites:|Time:\" \u002Ftmp\u002Ftest-baseline.txt\ngrep -E \"Statements|Branches|Functions|Lines\" \u002Ftmp\u002Ftest-baseline.txt\n\n# Current test count\ngrep -r \"^\\s*it\\(\\|^\\s*test\\(\" --include=\"*.spec.*\" --include=\"*.test.*\" . \\\n  | grep -v node_modules | wc -l\n\n# Source LOC (not tests)\nfind src -name '*.ts' -not -path '*\u002Fspec*' -not -path '*\u002Ftest*' \\\n  | xargs wc -l 2>\u002Fdev\u002Fnull | tail -1\n```\n\nRecord all four baseline metrics before touching anything:\n- **Test count**\n- **Suite runtime** (seconds)\n- **Coverage %** (lines or statements — whichever the threshold uses)\n- **Source LOC**\n\nMap source LOC to the proportionality table to set your count target:\n\n| App type | Source LOC | Efficient test count |\n|---|---|---|\n| Discord bot (≤30 commands) | ~5k | 50–200 |\n| Browser extension | ~3k | 40–150 |\n| REST API (≤20 routes) | ~4k | 80–250 |\n| CLI tool | ~2k | 30–120 |\n| Full-stack app | ~15k | 200–600 |\n| Polyglot bot \u002F multi-feature app | 15k–50k | 500–1500 |\n\nThe \"polyglot bot\" row covers full-stack-tier bots\u002Fservices that are well past the\n≤30-commands bracket — Discord bots with 30+ commands across distinct subsystems\n(music + scrobble + automod + giveaways + moderation + levels + …), platform plugins,\nor feature-rich CLIs that ship multiple domain modules. Treat the count as a ceiling,\nnot a floor: aggressive deletion below 500 should only happen if the suite shrinks\norganically and integration coverage stays. See\n`docs\u002Fdecisions\u002F2026-05-09-bot-test-suite-cleanup-strategy.md` in the Lucky repo for\na worked example of why 50–200 was unreachable for a 39k LOC bot.\n\n**Plan: reach the count target while maintaining coverage and cutting suite runtime.**\nAt 1.4k tests targeting 50–200, plan to delete ~85% of the suite. You will likely write a\nsmall number of replacement integration tests to recover coverage lost from deleting shallow\nunits. Net count and runtime still drop dramatically.\n\n---\n\n## Step 1.5 — Reality check: is the target reachable under the current gate?\n\nBefore touching anything, verify the proportionality target is achievable given the\nproject's coverage threshold. This is the most common reason cleanup stalls at \"still\n1.4k tests after a pass\".\n\n```bash\n# Count testable functions in the covered source\nfind src extension lib -name '*.ts' -o -name '*.js' 2>\u002Fdev\u002Fnull \\\n  | grep -vE \"spec|test|__tests__|node_modules\" \\\n  | xargs grep -cE \"^\\s*(function|const \\w+ = \\(|export (default )?(function|const)|class \\w+)\" 2>\u002Fdev\u002Fnull \\\n  | awk -F: '{s+=$2} END {print s}'\n```\n\n**Compatibility check:**\n\nIf the gate is `functions: ≥99%` and source has 1500 functions, the suite **cannot**\nhave fewer than ~1485 tests unless you write integration tests that each cover ~10\nfunctions. The proportionality target of 40–150 tests is then mathematically unreachable\nwithout one of:\n\n- **Lowering the gate** to a number compatible with the target (e.g., functions ≥85%)\n- **Excluding more files** from coverage measurement (test fixtures, generated code,\n  trivial wrappers)\n- **Writing integration tests aggressively** so each test covers ~10x the functions a\n  unit test would (this is mandatory if neither of the above is acceptable)\n\n**If incompatible, STOP and surface the conflict explicitly to the user before pruning:**\n\n```\nCOVERAGE\u002FTARGET CONFLICT\n\nProject gate:        functions ≥99% over \u003Cpath>\nFunctions in scope:  1480\nImplied min tests:   ~1480 (1 per function) or ~150 with 10x integration coverage\nProportionality:     40–150 (browser extension, ~3k LOC)\n\nThe current gate makes the proportionality target unreachable through deletion alone.\nChoose one before continuing:\n\n  A) Lower the gate to \u003CX>% (writes a recommended jest.config patch)\n  B) Exclude \u003Cfiles> from coverage scope (writes the patch)\n  C) Commit to writing integration tests during this pass (skill continues into\n     the integration-test phase, not just deletion)\n  D) Accept higher count; cap deletion at \"obvious waste\" (mocked-SUT, filler,\n     skipped) and leave structural tests in place\n\nDefault if no answer: A with gate at 90% functions \u002F 90% lines (typical for browser\nextensions), and continue cleanup against the new gate.\n```\n\nDo not start deletion until this is resolved. Pruning under a structurally incompatible\ngate will always stall at \"still way over target\" and waste the cleanup effort.\n\n---\n\n## Step 2 — Delete skipped and pending tests immediately\n\nBefore any analysis, sweep for dead tests. These have zero coverage contribution and zero\nbehavioral value. Delete without checking anything else.\n\n```bash\n# Find all skipped\u002Fpending tests\ngrep -rn \"it\\.skip\\|xit\\|xdescribe\\|describe\\.skip\\|test\\.skip\\|it\\.todo\\|test\\.todo\" \\\n  --include=\"*.spec.*\" --include=\"*.test.*\" . | grep -v node_modules\n```\n\nDelete the entire `it.skip` \u002F `xit` \u002F `xdescribe` block for each hit. If an `xdescribe`\nwraps the whole file, delete the file.\n\nThese are tests someone broke and never fixed, or wrote and never enabled. They're not\ncoming back. Delete them now, before the baseline run pollutes the count.\n\n---\n\n## Step 3 — Use test names as a triage signal\n\nBad names reliably indicate bad tests. Scan for them to decide which files to audit first.\n\n```bash\ngrep -rn \"should work\\|test 1\\|test 2\\|handles the case\\|it works\\|works correctly\\\n\\|does the thing\\|basic test\\|simple test\\|dummy\\|placeholder\\|TODO\\|FIXME\" \\\n  --include=\"*.spec.*\" --include=\"*.test.*\" . | grep -v node_modules\n```\n\nFiles with many hits in this list go to the top of the audit queue. A test named\n\"should work\" almost never tests anything meaningful — it was written to close a coverage\ngap, not to catch a bug. Read those tests first; they are usually the first deletions.\n\nDo not skip files with good names — good naming doesn't guarantee good tests. But bad\nnaming is a reliable fast-path to the worst offenders.\n\n---\n\n## Step 4 — Delete whole files\n\nAssess entire files before reading individual tests.\n\n**Delete the whole file if:**\n- The source file it tests no longer exists\n- Every test mocks the module under test (nothing real is being tested)\n- It only tests TypeScript types, interfaces, or enums (the compiler handles this)\n- It is a structural duplicate of another spec file with >70% overlap\n- All tests are `toBeDefined()`, `toBeInstanceOf()`, or similar filler\n\n```bash\n# Find orphaned spec files (source file gone)\nfor f in $(find . -name '*.spec.ts' | grep -v node_modules); do\n  src=\"${f\u002F.spec.ts\u002F.ts}\"\n  src=\"${src\u002F\\\u002F__tests__\\\u002F\u002F\\\u002F}\"\n  [ ! -f \"$src\" ] && echo \"ORPHAN: $f\"\ndone\n```\n\n---\n\n## Step 5 — Delete waste patterns within remaining files\n\nWithin each file, delete `it()` blocks matching these patterns.\n\n### Mocked-everything units (largest source of bloat)\n\nThe file under test is mocked inside its own spec — nothing real runs:\n```ts\njest.mock('..\u002FmyService')  \u002F\u002F myService is what this file is supposed to test\n```\nDelete every test in that describe block. If the whole file is this pattern, delete the file.\n\nOnly assertion is that a mock was called with its own mock input:\n```ts\nexpect(mockSendEmail).toHaveBeenCalledWith(mockPayload)\n\u002F\u002F where mockPayload is defined in the same test\n```\nThis is circular. Delete.\n\n### Filler assertions\n\n```ts\nexpect(result).toBeDefined()\nexpect(result).not.toBeNull()\nexpect(true).toBe(true)\nexpect(service).toBeInstanceOf(MyService)\nexpect(typeof handler).toBe('function')\n```\n\n### Trivial structure tests\n\n```ts\nit('should be defined', () => { expect(service).toBeDefined() })\nit('should create an instance', () => { expect(new MyClass()).toBeInstanceOf(MyClass) })\nit('should return the id', () => { expect(obj.getId()).toBe(obj.id) })\n```\n\n### Redundant same-path tests\n\n3+ tests exercise the identical code path with cosmetically different inputs that don't\nrepresent meaningfully distinct edge cases. Keep 1, delete the rest:\n\n```ts\nit('formats Alice', () => expect(format('Alice')).toBe('Alice'))\nit('formats Bob',   () => expect(format('Bob')).toBe('Bob'))\nit('formats Carol', () => expect(format('Carol')).toBe('Carol'))\n```\n→ Keep 1.\n\n### Snapshot bloat\n\n`toMatchSnapshot()` on pure data transformations, serialized plain objects, or anything\nwhere the snapshot is a stringified version of the input. Delete unless the rendered output\nis itself the contract (CLI help text, email templates, PDF output).\n\n### Slow test smell\n\n```bash\n# Find tests with artificial delays\ngrep -rn \"sleep\\|setTimeout\\|setInterval\\|waitFor.*\\d{4,}\" \\\n  --include=\"*.spec.*\" --include=\"*.test.*\" . | grep -v node_modules\n```\n\nTests with multi-second waits are usually testing timing rather than behavior. If the\nsource code has a hardcoded delay, that's a source bug — not something to test in the\nsuite. Delete the timing-dependent tests; fix the source if needed.\n\n---\n\n## Step 6 — Check coverage after each batch of deletions\n\nAfter deleting a batch (every 5–10 files):\n\n```bash\nnpm test -- --coverage 2>&1 | tail -10\n```\n\nIf coverage drops **below the threshold**, that batch deleted sole coverage contributors.\nTwo options:\n\n**Option A — Write one replacement integration test** covering those paths as a natural\nside effect of exercising a real flow. One integration test covering 80 lines is better\nthan 12 shallow mocks covering the same 80 lines one at a time.\n\n**Option B — Restore the single best test from that batch** (not all of them) — the one\nthat covers the most uncovered lines with the least mocking.\n\nNever restore a whole batch. Find the gap, fix it surgically.\n\n---\n\n## Step 7 — Write replacement integration tests (mandatory when stuck)\n\nThis step is **not optional** when the count target hasn't been reached and you're up\nagainst the coverage gate. Bailing out with \"further deletion needs replacement tests\nand that's a separate scope\" is the failure mode this skill exists to prevent.\n\nWhen pure deletion stalls because the gate is binding:\n\n1. **Identify the structural cluster** — group remaining tests by the source file or\n   feature they cover. Look for groups of 5+ tests covering one module via different\n   inputs\u002Fbranches.\n\n2. **Write the integration test that subsumes the cluster.** It should:\n   - Enter through the public API of the module (exported function, command handler,\n     route, message handler)\n   - Drive the input that exercises every branch the cluster was covering, in one or\n     a few realistic scenarios (table-driven `it.each` is fine — that's still one test)\n   - Use real implementations of internal collaborators; mock only external boundaries\n     (network, file system, time)\n   - Assert on the observable output \u002F side effect, not internal call sequences\n\n3. **Delete the cluster.** All of it. Re-run coverage to confirm the gate still holds.\n   If coverage drops, the integration test is missing a branch — extend it (don't\n   restore the deleted unit tests).\n\n4. **Repeat for every cluster** until either the count target is hit or every remaining\n   test independently earns its keep by the keep criteria in step 3 of the skill.\n\n**Target: each integration test covers at least 3× the lines of the tests it replaces,\nat 1\u002F5th the test count.** A 30-test cluster collapses into ~6 `it.each` rows in one\nintegration test, with same or better coverage.\n\n### When to consider an `it.each` table\n\nIf the cluster is \"same code path, different inputs\" (e.g., 30 tests of \"this concept\nmaps to that label\"), `it.each([...]).test('%s → %s', ...)` is a single integration test\nthat covers all 30 cases without paying the per-test setup cost. Coverage tools count\neach row as one test execution; the suite counts it as ONE test. This alone can collapse\nthe COMPOSED_EN\u002FPT-style branch tests at the heart of most browser extension bloat.\n\n```ts\nconst cases = [\n  ['en', 'concept-a', 'Label A'],\n  ['en', 'concept-b', 'Label B'],\n  \u002F\u002F ...28 more rows\n] as const\n\nit.each(cases)('renders %s\u002F%s as \"%s\"', (lang, concept, expected) => {\n  expect(render(lang, concept)).toBe(expected)\n})\n```\n\nReplaces 30 individual `it()` blocks with 1, no coverage loss, branch coverage stays\nidentical.\n\n### Failure modes when the agent stops here\n\nIf you find yourself writing \"further deletion needs replacement integration tests and\nthat's separate scope\" — that means you stopped exactly where the value was. The\nreplacement-test phase IS the cleanup. Going through this step is what converts\n\"deleted 16 tests\" into \"deleted 1200 tests\".\n\n---\n\n## Step 8 — Consolidate fragmented spec files\n\nAfter deletion, many files will have 2–3 surviving tests that all cover the same module.\nA module with 5 spec files each containing 2 tests is harder to navigate than one spec\nfile with 10 tests.\n\n```bash\n# Find modules with multiple spec files\nfind . -name '*.spec.ts' | grep -v node_modules \\\n  | sed 's\u002F\\.spec\\.ts\u002F\u002F' | sed 's\u002F\\\u002F__tests__\\\u002F\u002F\\\u002F\u002F' \\\n  | sort | uniq -d\n```\n\nFor each module with multiple sparse spec files:\n1. Merge all surviving tests into the primary spec file\n2. Deduplicate `beforeEach` setup\n3. Delete the now-empty secondary spec files\n\nDo not consolidate files that are intentionally separated (e.g., unit vs. integration\nspec for the same module where the separation is meaningful). Merge only when the split\nis arbitrary or historical.\n\n---\n\n## Step 9 — Final count, timing, and coverage\n\n```bash\nnpm test -- --coverage --verbose 2>&1 | tee \u002Ftmp\u002Ftest-after.txt\ngrep -E \"Tests:|Test Suites:|Time:\" \u002Ftmp\u002Ftest-after.txt\ngrep -E \"Statements|Branches|Functions|Lines\" \u002Ftmp\u002Ftest-after.txt\n\ngrep -r \"^\\s*it\\(\\|^\\s*test\\(\" --include=\"*.spec.*\" --include=\"*.test.*\" . \\\n  | grep -v node_modules | wc -l\n```\n\nReport:\n- Test count: before → after (% reduction)\n- Suite runtime: before → after (% faster)\n- Coverage: before → after (must be ≥ threshold)\n- Replacement integration tests written (count + what they cover)\n- Files deleted entirely (count + dominant reason)\n- Top 3 waste patterns by volume\n\n**Success = count target hit AND coverage threshold maintained AND runtime reduced.**\n\nIf coverage is below threshold, write more integration tests before declaring done.\nIf count is above the proportionality target, continue deleting.\n\n---\n\n## Step 10 — Optional: mutation testing to validate the survivors\n\nRun mutation testing against the cleaned suite to verify that the tests that survived\nactually catch failures when the source is broken. This is the definitive answer to\n\"is what's left actually protective?\"\n\n```bash\n# JavaScript\u002FTypeScript — Stryker\nnpx stryker run\n\n# Python — mutmut\nmutmut run\nmutmut results\n\n# Go — go-mutesting\ngo-mutesting .\u002F...\n```\n\nStryker config if not present:\n\n```json\n{\n  \"mutate\": [\"src\u002F**\u002F*.ts\", \"!src\u002F**\u002F*.spec.ts\"],\n  \"testRunner\": \"jest\",\n  \"reporters\": [\"clear-text\", \"progress\"],\n  \"coverageAnalysis\": \"perTest\"\n}\n```\n\n**Mutation score interpretation:**\n- >80% — the suite is genuinely protective\n- 60–80% — acceptable, some gaps remain\n- \u003C60% — significant portions of the surviving suite are not catching failures;\n  consider another deletion pass to remove tests with 0 mutation kills and replace\n  them with tests that actually verify outcomes\n\nThis step is optional but highly recommended after a major cleanup. It often reveals\nthat some \"legitimate-looking\" tests that survived the earlier passes are still not\ncatching anything meaningful.\n\n---\n\n## Failure \u002F Stop Conditions\n\n- If a deletion causes cascading failures in unrelated tests: shared state or bad\n  isolation — fix the isolation, then continue deleting\n- Writing integration tests is **never** out of scope for this skill. If you find\n  yourself wanting to declare it so, that's the signal to do it now — see step 7. The\n  only valid stop conditions related to coverage are: (a) the user explicitly accepted\n  a higher count in the step-1.5 conflict resolution, (b) coverage gate has already\n  been lowered to a number compatible with the proportionality target, or (c) the\n  remaining tests all pass the keep criteria in step 3 individually\n- If coverage tooling cannot run at all (broken config, missing dependencies): stop and\n  fix the tooling first; pruning blind to coverage is too risky\n- If `--dry-run`: output the full deletion list and any replacement tests needed,\n  without touching files\n\n## Memory Hooks\n\n- Write memory with: final test count, suite runtime, coverage %, and proportionality\n  target so future sessions don't re-pad the suite\n","# Test Cleanup\n\n**Goal: hit the coverage threshold with the fewest, fastest tests possible.**\n\nNot zero tests. Not maximum deletion. The minimum set of well-written tests that:\n1. Reach the project's coverage target\n2. Guard real behavior and regressions\n3. Run fast enough that nobody skips them\n\nA bloated suite of 1.4k shallow tests gives worse protection than 150 integration tests\nthat actually exercise real code paths. Many shallow tests count coverage lines the same\nway one integration test does — but the integration test finds real bugs.\n\n---\n\n## Step 1 — Gather baseline numbers\n\n```bash\n# Coverage threshold (check jest.config \u002F vitest.config \u002F package.json)\ngrep -r \"coverageThreshold\\|threshold\\|coverage\" jest.config* vitest.config* package.json \\\n  | grep -v node_modules | head -10\n\n# Run with coverage AND timing\nnpm test -- --coverage --verbose 2>&1 | tee \u002Ftmp\u002Ftest-baseline.txt\ntail -30 \u002Ftmp\u002Ftest-baseline.txt\n\n# Pull the key numbers from the run\ngrep -E \"Tests:|Test Suites:|Time:\" \u002Ftmp\u002Ftest-baseline.txt\ngrep -E \"Statements|Branches|Functions|Lines\" \u002Ftmp\u002Ftest-baseline.txt\n\n# Current test count\ngrep -r \"^\\s*it\\(\\|^\\s*test\\(\" --include=\"*.spec.*\" --include=\"*.test.*\" . \\\n  | grep -v node_modules | wc -l\n\n# Source LOC (not tests)\nfind src -name '*.ts' -not -path '*\u002Fspec*' -not -path '*\u002Ftest*' \\\n  | xargs wc -l 2>\u002Fdev\u002Fnull | tail -1\n```\n\nRecord all four baseline metrics before touching anything:\n- **Test count**\n- **Suite runtime** (seconds)\n- **Coverage %** (lines or statements — whichever the threshold uses)\n- **Source LOC**\n\nMap source LOC to the proportionality table to set your count target:\n\n| App type | Source LOC | Efficient test count |\n|---|---|---|\n| Discord bot (≤30 commands) | ~5k | 50–200 |\n| Browser extension | ~3k | 40–150 |\n| REST API (≤20 routes) | ~4k | 80–250 |\n| CLI tool | ~2k | 30–120 |\n| Full-stack app | ~15k | 200–600 |\n| Polyglot bot \u002F multi-feature app | 15k–50k | 500–1500 |\n\nThe \"polyglot bot\" row covers full-stack-tier bots\u002Fservices that are well past the\n≤30-commands bracket — Discord bots with 30+ commands across distinct subsystems\n(music + scrobble + automod + giveaways + moderation + levels + …), platform plugins,\nor feature-rich CLIs that ship multiple domain modules. Treat the count as a ceiling,\nnot a floor: aggressive deletion below 500 should only happen if the suite shrinks\norganically and integration coverage stays. See\n`docs\u002Fdecisions\u002F2026-05-09-bot-test-suite-cleanup-strategy.md` in the Lucky repo for\na worked example of why 50–200 was unreachable for a 39k LOC bot.\n\n**Plan: reach the count target while maintaining coverage and cutting suite runtime.**\nAt 1.4k tests targeting 50–200, plan to delete ~85% of the suite. You will likely write a\nsmall number of replacement integration tests to recover coverage lost from deleting shallow\nunits. Net count and runtime still drop dramatically.\n\n---\n\n## Step 1.5 — Reality check: is the target reachable under the current gate?\n\nBefore touching anything, verify the proportionality target is achievable given the\nproject's coverage threshold. This is the most common reason cleanup stalls at \"still\n1.4k tests after a pass\".\n\n```bash\n# Count testable functions in the covered source\nfind src extension lib -name '*.ts' -o -name '*.js' 2>\u002Fdev\u002Fnull \\\n  | grep -vE \"spec|test|__tests__|node_modules\" \\\n  | xargs grep -cE \"^\\s*(function|const \\w+ = \\(|export (default )?(function|const)|class \\w+)\" 2>\u002Fdev\u002Fnull \\\n  | awk -F: '{s+=$2} END {print s}'\n```\n\n**Compatibility check:**\n\nIf the gate is `functions: ≥99%` and source has 1500 functions, the suite **cannot**\nhave fewer than ~1485 tests unless you write integration tests that each cover ~10\nfunctions. The proportionality target of 40–150 tests is then mathematically unreachable\nwithout one of:\n\n- **Lowering the gate** to a number compatible with the target (e.g., functions ≥85%)\n- **Excluding more files** from coverage measurement (test fixtures, generated code,\n  trivial wrappers)\n- **Writing integration tests aggressively** so each test covers ~10x the functions a\n  unit test would (this is mandatory if neither of the above is acceptable)\n\n**If incompatible, STOP and surface the conflict explicitly to the user before pruning:**\n\n```\nCOVERAGE\u002FTARGET CONFLICT\n\nProject gate:        functions ≥99% over \u003Cpath>\nFunctions in scope:  1480\nImplied min tests:   ~1480 (1 per function) or ~150 with 10x integration coverage\nProportionality:     40–150 (browser extension, ~3k LOC)\n\nThe current gate makes the proportionality target unreachable through deletion alone.\nChoose one before continuing:\n\n  A) Lower the gate to \u003CX>% (writes a recommended jest.config patch)\n  B) Exclude \u003Cfiles> from coverage scope (writes the patch)\n  C) Commit to writing integration tests during this pass (skill continues into\n     the integration-test phase, not just deletion)\n  D) Accept higher count; cap deletion at \"obvious waste\" (mocked-SUT, filler,\n     skipped) and leave structural tests in place\n\nDefault if no answer: A with gate at 90% functions \u002F 90% lines (typical for browser\nextensions), and continue cleanup against the new gate.\n```\n\nDo not start deletion until this is resolved. Pruning under a structurally incompatible\ngate will always stall at \"still way over target\" and waste the cleanup effort.\n\n---\n\n## Step 2 — Delete skipped and pending tests immediately\n\nBefore any analysis, sweep for dead tests. These have zero coverage contribution and zero\nbehavioral value. Delete without checking anything else.\n\n```bash\n# Find all skipped\u002Fpending tests\ngrep -rn \"it\\.skip\\|xit\\|xdescribe\\|describe\\.skip\\|test\\.skip\\|it\\.todo\\|test\\.todo\" \\\n  --include=\"*.spec.*\" --include=\"*.test.*\" . | grep -v node_modules\n```\n\nDelete the entire `it.skip` \u002F `xit` \u002F `xdescribe` block for each hit. If an `xdescribe`\nwraps the whole file, delete the file.\n\nThese are tests someone broke and never fixed, or wrote and never enabled. They're not\ncoming back. Delete them now, before the baseline run pollutes the count.\n\n---\n\n## Step 3 — Use test names as a triage signal\n\nBad names reliably indicate bad tests. Scan for them to decide which files to audit first.\n\n```bash\ngrep -rn \"should work\\|test 1\\|test 2\\|handles the case\\|it works\\|works correctly\\\n\\|does the thing\\|basic test\\|simple test\\|dummy\\|placeholder\\|TODO\\|FIXME\" \\\n  --include=\"*.spec.*\" --include=\"*.test.*\" . | grep -v node_modules\n```\n\nFiles with many hits in this list go to the top of the audit queue. A test named\n\"should work\" almost never tests anything meaningful — it was written to close a coverage\ngap, not to catch a bug. Read those tests first; they are usually the first deletions.\n\nDo not skip files with good names — good naming doesn't guarantee good tests. But bad\nnaming is a reliable fast-path to the worst offenders.\n\n---\n\n## Step 4 — Delete whole files\n\nAssess entire files before reading individual tests.\n\n**Delete the whole file if:**\n- The source file it tests no longer exists\n- Every test mocks the module under test (nothing real is being tested)\n- It only tests TypeScript types, interfaces, or enums (the compiler handles this)\n- It is a structural duplicate of another spec file with >70% overlap\n- All tests are `toBeDefined()`, `toBeInstanceOf()`, or similar filler\n\n```bash\n# Find orphaned spec files (source file gone)\nfor f in $(find . -name '*.spec.ts' | grep -v node_modules); do\n  src=\"${f\u002F.spec.ts\u002F.ts}\"\n  src=\"${src\u002F\\\u002F__tests__\\\u002F\u002F\\\u002F}\"\n  [ ! -f \"$src\" ] && echo \"ORPHAN: $f\"\ndone\n```\n\n---\n\n## Step 5 — Delete waste patterns within remaining files\n\nWithin each file, delete `it()` blocks matching these patterns.\n\n### Mocked-everything units (largest source of bloat)\n\nThe file under test is mocked inside its own spec — nothing real runs:\n```ts\njest.mock('..\u002FmyService')  \u002F\u002F myService is what this file is supposed to test\n```\nDelete every test in that describe block. If the whole file is this pattern, delete the file.\n\nOnly assertion is that a mock was called with its own mock input:\n```ts\nexpect(mockSendEmail).toHaveBeenCalledWith(mockPayload)\n\u002F\u002F where mockPayload is defined in the same test\n```\nThis is circular. Delete.\n\n### Filler assertions\n\n```ts\nexpect(result).toBeDefined()\nexpect(result).not.toBeNull()\nexpect(true).toBe(true)\nexpect(service).toBeInstanceOf(MyService)\nexpect(typeof handler).toBe('function')\n```\n\n### Trivial structure tests\n\n```ts\nit('should be defined', () => { expect(service).toBeDefined() })\nit('should create an instance', () => { expect(new MyClass()).toBeInstanceOf(MyClass) })\nit('should return the id', () => { expect(obj.getId()).toBe(obj.id) })\n```\n\n### Redundant same-path tests\n\n3+ tests exercise the identical code path with cosmetically different inputs that don't\nrepresent meaningfully distinct edge cases. Keep 1, delete the rest:\n\n```ts\nit('formats Alice', () => expect(format('Alice')).toBe('Alice'))\nit('formats Bob',   () => expect(format('Bob')).toBe('Bob'))\nit('formats Carol', () => expect(format('Carol')).toBe('Carol'))\n```\n→ Keep 1.\n\n### Snapshot bloat\n\n`toMatchSnapshot()` on pure data transformations, serialized plain objects, or anything\nwhere the snapshot is a stringified version of the input. Delete unless the rendered output\nis itself the contract (CLI help text, email templates, PDF output).\n\n### Slow test smell\n\n```bash\n# Find tests with artificial delays\ngrep -rn \"sleep\\|setTimeout\\|setInterval\\|waitFor.*\\d{4,}\" \\\n  --include=\"*.spec.*\" --include=\"*.test.*\" . | grep -v node_modules\n```\n\nTests with multi-second waits are usually testing timing rather than behavior. If the\nsource code has a hardcoded delay, that's a source bug — not something to test in the\nsuite. Delete the timing-dependent tests; fix the source if needed.\n\n---\n\n## Step 6 — Check coverage after each batch of deletions\n\nAfter deleting a batch (every 5–10 files):\n\n```bash\nnpm test -- --coverage 2>&1 | tail -10\n```\n\nIf coverage drops **below the threshold**, that batch deleted sole coverage contributors.\nTwo options:\n\n**Option A — Write one replacement integration test** covering those paths as a natural\nside effect of exercising a real flow. One integration test covering 80 lines is better\nthan 12 shallow mocks covering the same 80 lines one at a time.\n\n**Option B — Restore the single best test from that batch** (not all of them) — the one\nthat covers the most uncovered lines with the least mocking.\n\nNever restore a whole batch. Find the gap, fix it surgically.\n\n---\n\n## Step 7 — Write replacement integration tests (mandatory when stuck)\n\nThis step is **not optional** when the count target hasn't been reached and you're up\nagainst the coverage gate. Bailing out with \"further deletion needs replacement tests\nand that's a separate scope\" is the failure mode this skill exists to prevent.\n\nWhen pure deletion stalls because the gate is binding:\n\n1. **Identify the structural cluster** — group remaining tests by the source file or\n   feature they cover. Look for groups of 5+ tests covering one module via different\n   inputs\u002Fbranches.\n\n2. **Write the integration test that subsumes the cluster.** It should:\n   - Enter through the public API of the module (exported function, command handler,\n     route, message handler)\n   - Drive the input that exercises every branch the cluster was covering, in one or\n     a few realistic scenarios (table-driven `it.each` is fine — that's still one test)\n   - Use real implementations of internal collaborators; mock only external boundaries\n     (network, file system, time)\n   - Assert on the observable output \u002F side effect, not internal call sequences\n\n3. **Delete the cluster.** All of it. Re-run coverage to confirm the gate still holds.\n   If coverage drops, the integration test is missing a branch — extend it (don't\n   restore the deleted unit tests).\n\n4. **Repeat for every cluster** until either the count target is hit or every remaining\n   test independently earns its keep by the keep criteria in step 3 of the skill.\n\n**Target: each integration test covers at least 3× the lines of the tests it replaces,\nat 1\u002F5th the test count.** A 30-test cluster collapses into ~6 `it.each` rows in one\nintegration test, with same or better coverage.\n\n### When to consider an `it.each` table\n\nIf the cluster is \"same code path, different inputs\" (e.g., 30 tests of \"this concept\nmaps to that label\"), `it.each([...]).test('%s → %s', ...)` is a single integration test\nthat covers all 30 cases without paying the per-test setup cost. Coverage tools count\neach row as one test execution; the suite counts it as ONE test. This alone can collapse\nthe COMPOSED_EN\u002FPT-style branch tests at the heart of most browser extension bloat.\n\n```ts\nconst cases = [\n  ['en', 'concept-a', 'Label A'],\n  ['en', 'concept-b', 'Label B'],\n  \u002F\u002F ...28 more rows\n] as const\n\nit.each(cases)('renders %s\u002F%s as \"%s\"', (lang, concept, expected) => {\n  expect(render(lang, concept)).toBe(expected)\n})\n```\n\nReplaces 30 individual `it()` blocks with 1, no coverage loss, branch coverage stays\nidentical.\n\n### Failure modes when the agent stops here\n\nIf you find yourself writing \"further deletion needs replacement integration tests and\nthat's separate scope\" — that means you stopped exactly where the value was. The\nreplacement-test phase IS the cleanup. Going through this step is what converts\n\"deleted 16 tests\" into \"deleted 1200 tests\".\n\n---\n\n## Step 8 — Consolidate fragmented spec files\n\nAfter deletion, many files will have 2–3 surviving tests that all cover the same module.\nA module with 5 spec files each containing 2 tests is harder to navigate than one spec\nfile with 10 tests.\n\n```bash\n# Find modules with multiple spec files\nfind . -name '*.spec.ts' | grep -v node_modules \\\n  | sed 's\u002F\\.spec\\.ts\u002F\u002F' | sed 's\u002F\\\u002F__tests__\\\u002F\u002F\\\u002F\u002F' \\\n  | sort | uniq -d\n```\n\nFor each module with multiple sparse spec files:\n1. Merge all surviving tests into the primary spec file\n2. Deduplicate `beforeEach` setup\n3. Delete the now-empty secondary spec files\n\nDo not consolidate files that are intentionally separated (e.g., unit vs. integration\nspec for the same module where the separation is meaningful). Merge only when the split\nis arbitrary or historical.\n\n---\n\n## Step 9 — Final count, timing, and coverage\n\n```bash\nnpm test -- --coverage --verbose 2>&1 | tee \u002Ftmp\u002Ftest-after.txt\ngrep -E \"Tests:|Test Suites:|Time:\" \u002Ftmp\u002Ftest-after.txt\ngrep -E \"Statements|Branches|Functions|Lines\" \u002Ftmp\u002Ftest-after.txt\n\ngrep -r \"^\\s*it\\(\\|^\\s*test\\(\" --include=\"*.spec.*\" --include=\"*.test.*\" . \\\n  | grep -v node_modules | wc -l\n```\n\nReport:\n- Test count: before → after (% reduction)\n- Suite runtime: before → after (% faster)\n- Coverage: before → after (must be ≥ threshold)\n- Replacement integration tests written (count + what they cover)\n- Files deleted entirely (count + dominant reason)\n- Top 3 waste patterns by volume\n\n**Success = count target hit AND coverage threshold maintained AND runtime reduced.**\n\nIf coverage is below threshold, write more integration tests before declaring done.\nIf count is above the proportionality target, continue deleting.\n\n---\n\n## Step 10 — Optional: mutation testing to validate the survivors\n\nRun mutation testing against the cleaned suite to verify that the tests that survived\nactually catch failures when the source is broken. This is the definitive answer to\n\"is what's left actually protective?\"\n\n```bash\n# JavaScript\u002FTypeScript — Stryker\nnpx stryker run\n\n# Python — mutmut\nmutmut run\nmutmut results\n\n# Go — go-mutesting\ngo-mutesting .\u002F...\n```\n\nStryker config if not present:\n\n```json\n{\n  \"mutate\": [\"src\u002F**\u002F*.ts\", \"!src\u002F**\u002F*.spec.ts\"],\n  \"testRunner\": \"jest\",\n  \"reporters\": [\"clear-text\", \"progress\"],\n  \"coverageAnalysis\": \"perTest\"\n}\n```\n\n**Mutation score interpretation:**\n- >80% — the suite is genuinely protective\n- 60–80% — acceptable, some gaps remain\n- \u003C60% — significant portions of the surviving suite are not catching failures;\n  consider another deletion pass to remove tests with 0 mutation kills and replace\n  them with tests that actually verify outcomes\n\nThis step is optional but highly recommended after a major cleanup. It often reveals\nthat some \"legitimate-looking\" tests that survived the earlier passes are still not\ncatching anything meaningful.\n\n---\n\n## Failure \u002F Stop Conditions\n\n- If a deletion causes cascading failures in unrelated tests: shared state or bad\n  isolation — fix the isolation, then continue deleting\n- Writing integration tests is **never** out of scope for this skill. If you find\n  yourself wanting to declare it so, that's the signal to do it now — see step 7. The\n  only valid stop conditions related to coverage are: (a) the user explicitly accepted\n  a higher count in the step-1.5 conflict resolution, (b) coverage gate has already\n  been lowered to a number compatible with the proportionality target, or (c) the\n  remaining tests all pass the keep criteria in step 3 individually\n- If coverage tooling cannot run at all (broken config, missing dependencies): stop and\n  fix the tooling first; pruning blind to coverage is too risky\n- If `--dry-run`: output the full deletion list and any replacement tests needed,\n  without touching files\n\n## Memory Hooks\n\n- Write memory with: final test count, suite runtime, coverage %, and proportionality\n  target so future sessions don't re-pad the suite\n",{"name":5,"triggers":49,"description":7},[5],"main","f2e470f7ead3f9982aee7aadc1aa5bef5d4db833","packages\u002Fcatalog\u002Fcatalog\u002Fskills\u002Fadt-test-cleanup","https:\u002F\u002Fgithub.com\u002FLucasSantana-Dev\u002Fforgekit","https:\u002F\u002Fgithub.com\u002FLucasSantana-Dev","https:\u002F\u002Flucassantana.tech","Senior engineer. AI agent tooling and evals on Node\u002FTS backends. Brazil, UTC-3.","2026-11-11 07:05:28",{"source":8,"skillName":5,"commands":59,"prompt":69,"zipAvailable":70},[60,63,66],{"tool":61,"command":62},"npx","npx skills add LucasSantana-Dev\u002Fforgekit --skill test-cleanup",{"tool":64,"command":65},"bunx","bunx skills add LucasSantana-Dev\u002Fforgekit --skill test-cleanup",{"tool":67,"command":68},"pnpm","pnpm dlx skills add LucasSantana-Dev\u002Fforgekit --skill test-cleanup","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FLucasSantana-Dev\u002Fforgekit\u002Fblob\u002Fmain\u002Fpackages\u002Fcatalog\u002Fcatalog\u002Fskills\u002Fadt-test-cleanup\u002FSKILL.md",true,[72],{"path":73,"resourceType":74,"resourceTypeDesc":75,"sizeBytes":76,"sourceHtmlUrl":77},"manifest.json","REFERENCE","参考文档",1159,"https:\u002F\u002Fgithub.com\u002FLucasSantana-Dev\u002Fforgekit\u002Fblob\u002Fmain\u002Fpackages\u002Fcatalog\u002Fcatalog\u002Fskills\u002Fadt-test-cleanup\u002Fmanifest.json",[79,110,140,161,170,188],{"skillNo":80,"name":81,"nameZh":6,"description":82,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":83,"categories":84,"keywords":90,"publishedAt":44},"01M6WR9SQGQ5WDEJY9HH3T93E8","token-audit","Audit historical Claude Code token usage from JSONL session files — shows total spend, cache hit rates, top costly sessions, weekly trends, and where tokens are going. Run after heavy sessions or weekly to measure optimization impact. Answers how many tokens a session used, how expensive was that session, session cost, and token usage report.","https:\u002F\u002Fgithub.com\u002FLucasSantana-Dev\u002Fforgekit\u002Fblob\u002Fmain\u002Fpackages\u002Fcatalog\u002Fcatalog\u002Fskills\u002Fadt-token-audit\u002FSKILL.md",[85],{"categoryNo":86,"categoryName":87,"taxonomyType":88,"taxonomyTypeDesc":89},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[41,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109],"token","token audit","session","rate","weekly","top","usage","jsonl","usage report","impact","tokens","files","cost","how","Optimization","cache","code","run","sessions",{"skillNo":111,"name":112,"nameZh":6,"description":113,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":114,"categories":115,"keywords":119,"publishedAt":44},"01M6WR919T3NGWMZ5R5XKWX9KN","smart-model-route","Auto-classify prompt complexity via Haiku (with heuristic fallback) and inject model routing hints for Agent tool calls. Wired as a UserPromptSubmit hook — fires on every prompt, routes low tasks to Haiku, standard to Sonnet, complex to Sonnet with thorough reasoning, and critical (security\u002Farch\u002Fmigrations) to Opus. Handles model selection, for example choose model by task complexity.","https:\u002F\u002Fgithub.com\u002FLucasSantana-Dev\u002Fforgekit\u002Fblob\u002Fmain\u002Fpackages\u002Fcatalog\u002Fcatalog\u002Fskills\u002Fadt-smart-model-route\u002FSKILL.md",[116],{"categoryNo":117,"categoryName":118,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139],"smart model route","route","model route","model","security","model selection","tasks","critical","arch","classify","selection","fallback","complexity","auto","opus","haiku","choose","hook","agent tool","sonnet",{"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":147,"publishedAt":44},"01M6WR8TMGS9G79DFTX0K1ZA8T","smart-commands","Decision guide for when to proactively use Claude Code slash commands — \u002Fthink, \u002Fmodel, \u002Fcompact, \u002Fclear. Use when choosing whether to invoke a command before or during a task, or when a complexity-classifier hook has flagged a task as high or critical. Pairs with the complexity-classifier UserPromptSubmit hook. Answers when to compact, should I switch models, and whether to use extended thinking. Use to pick the right \u002Fmodel command and switch model.","https:\u002F\u002Fgithub.com\u002FLucasSantana-Dev\u002Fforgekit\u002Fblob\u002Fmain\u002Fpackages\u002Fcatalog\u002Fcatalog\u002Fskills\u002Fadt-smart-commands\u002FSKILL.md",[146],{"categoryNo":86,"categoryName":87,"taxonomyType":88,"taxonomyTypeDesc":89},[148,149,32,150,36,151,137,127,152,153,154,155,132,156,157,158,159,160,123,107],"smart commands","commands","as","for","compact","thinking","switch","classifier","invoke","guide","command","decision","pick",{"skillNo":162,"name":163,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":164,"categories":165,"keywords":166,"publishedAt":44},"01M6E0ZGQ78S4SE6X6RCN1PR9A","spec-ship","https:\u002F\u002Fgithub.com\u002FLucasSantana-Dev\u002Fforgekit\u002Fblob\u002Fmain\u002Fpackages\u002Fcore\u002Fkit\u002Fspecs\u002Fskills\u002Fspec-ship\u002FSKILL.md",[],[167,168,169],"spec","ship","spec ship",{"skillNo":171,"name":172,"nameZh":6,"description":173,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":174,"categories":175,"keywords":176,"publishedAt":44},"01M6E0ZD27S87Z954JTHECWG34","spec-new","Create a committed per-feature spec under `docs\u002Fspecs\u002FYYYY-MM-DD-\u003Cslug>\u002F{spec,tasks}.md` with frontmatter (status, created, owner, pr, tags). Promotes an ephemeral `.claude\u002Fplans\u002F\u003Cname>.md` into a persistent, searchable artifact. Use when starting a feature that's larger than a one-shot PR.","https:\u002F\u002Fgithub.com\u002FLucasSantana-Dev\u002Fforgekit\u002Fblob\u002Fmain\u002Fpackages\u002Fcore\u002Fkit\u002Fspecs\u002Fskills\u002Fspec-new\u002FSKILL.md",[],[177,167,178,36,179,180,126,181,182,183,184,185,186,187],"spec new","artifact","dd","create","pr","status","docs","feature","tags","name","plans",{"skillNo":189,"name":190,"nameZh":6,"description":191,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":192,"categories":193,"keywords":197,"publishedAt":44},"01M6E0Z9B5YE1G4NM05JPNVWMK","roadmap-refresh","Regenerate `docs\u002Froadmap.md` for a repo from all specs under `docs\u002Fspecs\u002F` + their frontmatter (status, tags, pr). Produces Now\u002FNext\u002FRecently-shipped sections. Run after creating \u002F shipping a spec, or at the start of a new session for orientation.","https:\u002F\u002Fgithub.com\u002FLucasSantana-Dev\u002Fforgekit\u002Fblob\u002Fmain\u002Fpackages\u002Fcore\u002Fkit\u002Fspecs\u002Fskills\u002Froadmap-refresh\u002FSKILL.md",[194],{"categoryNo":195,"categoryName":196,"taxonomyType":88,"taxonomyTypeDesc":89},"01JBSEEDCAT000000000000031","产品经理",[198,199,200,201,185,202,93,203,204,205,206,108,181,182,183,167],"roadmap refresh","roadmap","refresh","next","now","repo","new session","start","shipping"]