[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M7193P7CQW9NM80PXJVFZWZ4":3,"skill-same:01M7193P7CQW9NM80PXJVFZWZ4":129},{"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":26,"publishedAt":47,"linkOnly":48,"markdownContent":49,"markdownBody":50,"frontMatter":51,"refName":83,"commitSha":84,"skillDirectory":85,"forksCount":86,"repositoryHtmlUrl":87,"repositoryHomepageUrl":6,"ownerHtmlUrl":88,"ownerBlogUrl":89,"ownerDescription":6,"updatedAt":90,"install":91,"resources":104},"01M7193P7CQW9NM80PXJVFZWZ4","slopgate-test-extender",null,"Extend Python test suites with parametrization, fixture reuse, and quality enforcement. Use when asked to add tests, extend existing tests, improve test coverage, consolidate duplicate tests, fix test quality issues, or when Slopgate reports untested-production-code, missing test references, or coverage gaps. Triggers on requests like \"add tests for X\", \"extend these tests\", \"parametrize tests\", \"consolidate tests\", \"fix test quality\", \"improve test coverage\", \"write tests for this function\", \"test this module\", \"behavior tests\", \"integration tests\", \"missing tests\", \"coverage holes\", \"untested code\", or when a hook denies with test-related findings.","vasceannie\u002Fslopgate","vasceannie","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F20271133?v=4","01M718RGA8VV2EF22XJEMXVQ0Q","Travis Vasceannie","01M718RGB0GWSXBBZBN67Q6NHQ",1,"MIT","https:\u002F\u002Fgithub.com\u002Fvasceannie\u002Fslopgate\u002Fblob\u002Fmaster\u002F.opencode\u002Fskills\u002Fslopgate-test-extender\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000007","代码审查","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",[27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],"test","slopgate test extender","only","testing","when","candidate","fixture","consolidate","references","reports","opencode","write tests","orchestrator","fix","coverage","loop","improve","issues","code","cleanup","2026-11-10 10:12:30",false,"---\nname: slopgate-test-extender\ndescription: |\n  Extend Python test suites with parametrization, fixture reuse, and quality enforcement. Use when asked to add tests, extend existing tests, improve test coverage, consolidate duplicate tests, fix test quality issues, or when Slopgate reports untested-production-code, missing test references, or coverage gaps. Triggers on requests like \"add tests for X\", \"extend these tests\", \"parametrize tests\", \"consolidate tests\", \"fix test quality\", \"improve test coverage\", \"write tests for this function\", \"test this module\", \"behavior tests\", \"integration tests\", \"missing tests\", \"coverage holes\", \"untested code\", or when a hook denies with test-related findings.\nversion: 1.0.0\nauthor: Slopgate\nlicense: MIT\ncompatibility: claude-code, opencode, codex, hermes, slopgate\nmetadata:\n  hermes:\n    tags: [slopgate,testing,pytest,hypothesis,coverage]\n    related_skills: [slopgate-code-hygiene-refactor,slopgate-hygiene-orchestrator]\n  slopgate:\n    rule_ids: [untested-production-code,hypothesis-candidate,test-integrity,weak-assertion,loop-to-parametrize]\n    activation:\n      primary: [missing tests,coverage gaps,parametrization,property testing,weak assertions]\n      avoid: [production-only refactor,broad lint cleanup,helper ownership discovery]\n---\n\n# Test Extender\n\nExtend pytest test suites by finding existing tests, evaluating parametrization opportunities, reusing fixtures, and enforcing quality standards.\n\n## When to Use\n\nUse when the primary work is test coverage, test quality, parametrization, fixtures, or property-based testing.\n\n- Slopgate reports `untested-production-code`, `hypothesis-candidate`, `test-integrity`, weak assertions, loop-to-parametrize findings, or missing test references.\n- User asks to add tests, extend tests, improve coverage, parametrize examples, or decide between finite examples and Hypothesis.\n- Use alongside production refactor skills only after the production seam is clear.\n\n## When Not to Use\n\nDo not use as the primary skill for production-only structure failures. Use `slopgate-code-hygiene-refactor` first, then return here for coverage.\n\nDo not add Hypothesis just because it sounds stronger: use parametrization for finite named examples and Hypothesis for broad domains\u002Finvariants.\n\nDo not use for repo-wide lint orchestration; use `slopgate-hygiene-orchestrator`.\n\n## Workflow\n\n### 1. Discover Existing Tests\n\nRun analysis script to find tests and identify issues:\n\n```bash\npython scripts\u002Fanalyze_tests.py tests\u002F --json\n```\n\nOr search with ripgrep for specific patterns:\n\n```bash\n# Find tests for a specific function\nrg \"def test_.*\u003Cfunction_name>\" tests\u002F -l\n\n# Find parametrized tests\nrg \"@pytest.mark.parametrize\" tests\u002F -A5\n\n# Find tests with loops (anti-pattern)\nrg \"for .* in .*:\" tests\u002Ftest_*.py -B2 -A2\n```\n\nUse semantic search via Serena tools:\n\n```\nfind_symbol(name_path_pattern=\"test_*\", relative_path=\"tests\u002F\", include_kinds=[12])\n```\n\n### 2. Discover Fixtures\n\nRun fixture discovery:\n\n```bash\npython scripts\u002Ffind_fixtures.py tests\u002F --with-usage --json\n```\n\nCheck conftest hierarchy:\n\n```bash\nrg \"@pytest.fixture\" tests\u002F**\u002Fconftest.py -A3\n```\n\n### 3. Evaluate Extension Opportunities\n\n**Parametrization candidates:**\n- Multiple similar test functions (test_foo_case1, test_foo_case2)\n- Tests with loops or conditionals\n- Tests with 3+ assertions on similar values\n- Finite, explicit edge-case sets (empty, single, max, boundary)\n\n**Property-based \u002F Hypothesis candidates:**\n- Functions with wide input domains (strings, numbers, collections)\n- Invariants that should hold for ALL inputs (round-trip, idempotency, monotonicity)\n- Complex state machines or parsers where enumerating cases is infeasible\n- Code that processes untrusted\u002Fexternal data\n- When the test would need 10+ parametrized cases to feel confident\n\n**Decision rule:**\n- Start with parametrization for known edge cases and behavioral examples.\n- Add Hypothesis when the input space is large, the invariant is strong, or the function processes arbitrary data.\n- Both can coexist: parametrize the known-critical cases, use Hypothesis for the general invariant.\n- Do not add Hypothesis as a new project dependency without checking project policy; if it is unavailable and dependency changes are out of scope, write named parametrized examples and leave the property-test recommendation explicit.\n\n**Fixture reuse opportunities:**\n- Repeated setup code across tests\n- Common object construction patterns\n- Shared test data\n\n**Coverage gap candidates (from Slopgate `untested-production-code`):**\n- Public symbols with `static_test_reference_coverage=0%`\n- Functions\u002Fclasses with no test references\n- New code written without accompanying tests\n\n### 4. Implement Extensions\n\nFollow quality rules strictly. See [references\u002Fquality_rules.md](references\u002Fquality_rules.md).\n\n**Key requirements:**\n- No loops or conditionals in test bodies\n- All assertions include descriptive messages\n- No magic values (use constants, fixtures, parametrize)\n- Use `pytest.param()` with `id=` for named cases\n- Match naming: `test_\u003Cunit>_\u003Cscenario>_\u003Cexpected>`\n\n**Parametrize vs Hypothesis — implementation guidance:**\n\n| Aspect | Parametrize | Hypothesis |\n|--------|-------------|------------|\n| **When** | Known edge cases, explicit examples | Large input space, universal invariants |\n| **Import** | `import pytest` | `from hypothesis import given, strategies as st` |\n| **Decorator** | `@pytest.mark.parametrize(\"x,y\", [...])` | `@given(st.integers())` |\n| **Cases** | Explicit: `[(1, 2), (0, 0)]` | Generated: `st.text(), st.lists(st.integers())` |\n| **Best for** | Behavior examples, regression cases, boundary values | Round-trips, parsers, validators, idempotency |\n| **Avoid** | 10+ hand-written cases for simple invariants | When the \"property\" is just reimplementing the function |\n\n**Parametrize example:**\n```python\n@pytest.mark.parametrize(\n    (\"input_val\", \"expected\"),\n    [\n        pytest.param(1, 2, id=\"simple_case\"),\n        pytest.param(0, 0, id=\"zero_case\"),\n        pytest.param(-1, -2, id=\"negative_case\"),\n    ],\n)\ndef test_double_value(input_val: int, expected: int) -> None:\n    result = double(input_val)\n    assert result == expected, f\"double({input_val}) should be {expected}\"\n```\n\n**Hypothesis example:**\n```python\nfrom hypothesis import given, strategies as st\n\n@given(st.lists(st.integers()))\ndef test_reverse_twice_returns_original(values: list[int]) -> None:\n    result = reverse(reverse(values))\n    assert result == values, \"reversing twice should preserve the original list\"\n\n@given(st.text())\ndef test_serialize_roundtrip(raw: str) -> None:\n    \"\"\"parse(serialize(x)) == x\"\"\"\n    assert parse(serialize(raw)) == raw, \"round-trip should preserve data\"\n```\n\n**Coexistence pattern:**\n```python\n# Known-critical cases as parametrize\n@pytest.mark.parametrize(\"raw\", [\n    pytest.param(\"\", id=\"empty\"),\n    pytest.param(\"\\x00\", id=\"null_byte\"),\n    pytest.param(\"a\" * 10000, id=\"huge_string\"),\n])\ndef test_parse_known_cases(raw: str) -> None:\n    assert parse(raw) is not None\n\n# General invariant as hypothesis\n@given(st.text())\ndef test_parse_never_crashes(raw: str) -> None:\n    result = parse(raw)  # should not raise\n    assert isinstance(result, (ParseResult, type(None)))\n```\n\n**Responding to `untested-production-code` findings:**\n\n1. Read the Slopgate output for the unreferenced symbol(s) and the suggested scaffold.\n2. Identify the public behavior users depend on — not just the function signature.\n3. Add behavior\u002Fintegration tests around the public entrypoint.\n4. Avoid testing every helper directly; prefer observable behavior through the public API.\n5. If the symbol is dead API, consider deleting or private-scoping instead of adding tests.\n6. Verify with `pytest \u003Caffected_tests>` and re-run `slopgate lint check`.\n\nSee [references\u002Fparametrization_patterns.md](references\u002Fparametrization_patterns.md) for patterns.\nSee [references\u002Fhypothesis_patterns.md](references\u002Fhypothesis_patterns.md) for property-based testing patterns.\n\n### 5. Validate Quality\n\n```bash\nmake quality\n```\n\n**Progress gate:** Acknowledge guidelines compliance before proceeding.\n\n## Quick Reference\n\n### Parametrize Pattern\n\n```python\n@pytest.mark.parametrize(\n    (\"input_val\", \"expected\"),\n    [\n        pytest.param(1, 2, id=\"simple_case\"),\n        pytest.param(0, 0, id=\"zero_case\"),\n    ],\n)\ndef test_double_value(input_val: int, expected: int) -> None:\n    result = double(input_val)\n    assert result == expected, f\"double({input_val}) should be {expected}\"\n```\n\n### Hypothesis Pattern\n\n```python\nfrom hypothesis import given, strategies as st\n\n@given(st.lists(st.integers()))\ndef test_reverse_twice_returns_original(values: list[int]) -> None:\n    result = reverse(reverse(values))\n    assert result == values, \"reversing twice should preserve the original list\"\n```\n\n### Coexistence Pattern\n\n```python\n# Explicit edge cases via parametrize\n@pytest.mark.parametrize(\"raw\", [\n    pytest.param(\"\", id=\"empty\"),\n    pytest.param(\"\\x00\", id=\"null_byte\"),\n])\ndef test_parse_edge_cases(raw: str) -> None:\n    assert parse(raw) is not None\n\n# General invariant via hypothesis\n@given(st.text())\ndef test_parse_never_crashes(raw: str) -> None:\n    result = parse(raw)  # should not raise\n    assert isinstance(result, (ParseResult, type(None)))\n```\n\n### Fixture Scopes\n\n| Scope | When |\n|-------|------|\n| `function` | Fresh state per test (default) |\n| `module` | Expensive setup, shared in file |\n| `session` | Very expensive, immutable |\n\n### Test Classification\n\nTests must verify: **function** (correctness), **behavior** (state changes), **robustness** (error handling), or **rigor** (boundaries).\n\nStructure-only tests should be absorbed into meaningful tests or removed.\n\n## Troubleshooting\n\n### Fixture Consolidation with Duplicate-Check Hooks\n\nWhen hooks block duplicate fixture definitions, follow this order:\n\n**Problem:** Cannot add fixture to conftest.py because hook detects duplicate with test file.\n\n**Solution 1: Use intermediate conftest.py**\n```\n# If consolidating from tests\u002Fgrpc\u002Ftest_foo.py:\n# 1. Add fixture to tests\u002Fgrpc\u002Fconftest.py (not tests\u002Fconftest.py)\n# 2. Remove fixture from tests\u002Fgrpc\u002Ftest_foo.py\n# 3. Tests inherit from nearest conftest.py in hierarchy\n```\n\n**Solution 2: Rename-then-migrate**\n```python\n# In test file, temporarily rename:\n@pytest.fixture\ndef _local_sample_config() -> Config:  # Renamed to avoid conflict\n    ...\n\n# Now add to conftest.py with original name\n# Then remove _local_sample_config from test file\n```\n\n**Key insight:** The conftest.py hierarchy allows fixtures in `tests\u002Fgrpc\u002Fconftest.py` to serve all tests under `tests\u002Fgrpc\u002F` without conflicting with `tests\u002Fconftest.py`.\n\n### Loop-to-Parametrize Conversion\n\nWhen converting loops to parametrization, split into separate test functions if the loop body has multiple assertions:\n\n```python\n# Before (anti-pattern):\ndef test_states() -> None:\n    for state in (A, B, C):\n        assert state in valid_states\n        obj = create(state=state)\n        assert obj.should_skip\n\n# After (two parametrized tests):\n@pytest.mark.parametrize(\"state\", [pytest.param(A, id=\"a\"), ...])\ndef test_state_is_valid(state: State) -> None:\n    assert state in valid_states, f\"{state} should be valid\"\n\n@pytest.mark.parametrize(\"state\", [pytest.param(A, id=\"a\"), ...])\ndef test_state_triggers_skip(state: State) -> None:\n    obj = create(state=state)\n    assert obj.should_skip, f\"{state} should trigger skip\"\n```\n\n## Resources\n\n- `scripts\u002Fanalyze_tests.py` - Find tests, detect anti-patterns\n- `scripts\u002Ffind_fixtures.py` - Discover fixtures, analyze scope\u002Fusage\n- `references\u002Fquality_rules.md` - Complete quality rules\n- `references\u002Fparametrization_patterns.md` - Parametrization examples\n- `references\u002Fhypothesis_patterns.md` - Property-based testing examples\n","# Test Extender\n\nExtend pytest test suites by finding existing tests, evaluating parametrization opportunities, reusing fixtures, and enforcing quality standards.\n\n## When to Use\n\nUse when the primary work is test coverage, test quality, parametrization, fixtures, or property-based testing.\n\n- Slopgate reports `untested-production-code`, `hypothesis-candidate`, `test-integrity`, weak assertions, loop-to-parametrize findings, or missing test references.\n- User asks to add tests, extend tests, improve coverage, parametrize examples, or decide between finite examples and Hypothesis.\n- Use alongside production refactor skills only after the production seam is clear.\n\n## When Not to Use\n\nDo not use as the primary skill for production-only structure failures. Use `slopgate-code-hygiene-refactor` first, then return here for coverage.\n\nDo not add Hypothesis just because it sounds stronger: use parametrization for finite named examples and Hypothesis for broad domains\u002Finvariants.\n\nDo not use for repo-wide lint orchestration; use `slopgate-hygiene-orchestrator`.\n\n## Workflow\n\n### 1. Discover Existing Tests\n\nRun analysis script to find tests and identify issues:\n\n```bash\npython scripts\u002Fanalyze_tests.py tests\u002F --json\n```\n\nOr search with ripgrep for specific patterns:\n\n```bash\n# Find tests for a specific function\nrg \"def test_.*\u003Cfunction_name>\" tests\u002F -l\n\n# Find parametrized tests\nrg \"@pytest.mark.parametrize\" tests\u002F -A5\n\n# Find tests with loops (anti-pattern)\nrg \"for .* in .*:\" tests\u002Ftest_*.py -B2 -A2\n```\n\nUse semantic search via Serena tools:\n\n```\nfind_symbol(name_path_pattern=\"test_*\", relative_path=\"tests\u002F\", include_kinds=[12])\n```\n\n### 2. Discover Fixtures\n\nRun fixture discovery:\n\n```bash\npython scripts\u002Ffind_fixtures.py tests\u002F --with-usage --json\n```\n\nCheck conftest hierarchy:\n\n```bash\nrg \"@pytest.fixture\" tests\u002F**\u002Fconftest.py -A3\n```\n\n### 3. Evaluate Extension Opportunities\n\n**Parametrization candidates:**\n- Multiple similar test functions (test_foo_case1, test_foo_case2)\n- Tests with loops or conditionals\n- Tests with 3+ assertions on similar values\n- Finite, explicit edge-case sets (empty, single, max, boundary)\n\n**Property-based \u002F Hypothesis candidates:**\n- Functions with wide input domains (strings, numbers, collections)\n- Invariants that should hold for ALL inputs (round-trip, idempotency, monotonicity)\n- Complex state machines or parsers where enumerating cases is infeasible\n- Code that processes untrusted\u002Fexternal data\n- When the test would need 10+ parametrized cases to feel confident\n\n**Decision rule:**\n- Start with parametrization for known edge cases and behavioral examples.\n- Add Hypothesis when the input space is large, the invariant is strong, or the function processes arbitrary data.\n- Both can coexist: parametrize the known-critical cases, use Hypothesis for the general invariant.\n- Do not add Hypothesis as a new project dependency without checking project policy; if it is unavailable and dependency changes are out of scope, write named parametrized examples and leave the property-test recommendation explicit.\n\n**Fixture reuse opportunities:**\n- Repeated setup code across tests\n- Common object construction patterns\n- Shared test data\n\n**Coverage gap candidates (from Slopgate `untested-production-code`):**\n- Public symbols with `static_test_reference_coverage=0%`\n- Functions\u002Fclasses with no test references\n- New code written without accompanying tests\n\n### 4. Implement Extensions\n\nFollow quality rules strictly. See [references\u002Fquality_rules.md](references\u002Fquality_rules.md).\n\n**Key requirements:**\n- No loops or conditionals in test bodies\n- All assertions include descriptive messages\n- No magic values (use constants, fixtures, parametrize)\n- Use `pytest.param()` with `id=` for named cases\n- Match naming: `test_\u003Cunit>_\u003Cscenario>_\u003Cexpected>`\n\n**Parametrize vs Hypothesis — implementation guidance:**\n\n| Aspect | Parametrize | Hypothesis |\n|--------|-------------|------------|\n| **When** | Known edge cases, explicit examples | Large input space, universal invariants |\n| **Import** | `import pytest` | `from hypothesis import given, strategies as st` |\n| **Decorator** | `@pytest.mark.parametrize(\"x,y\", [...])` | `@given(st.integers())` |\n| **Cases** | Explicit: `[(1, 2), (0, 0)]` | Generated: `st.text(), st.lists(st.integers())` |\n| **Best for** | Behavior examples, regression cases, boundary values | Round-trips, parsers, validators, idempotency |\n| **Avoid** | 10+ hand-written cases for simple invariants | When the \"property\" is just reimplementing the function |\n\n**Parametrize example:**\n```python\n@pytest.mark.parametrize(\n    (\"input_val\", \"expected\"),\n    [\n        pytest.param(1, 2, id=\"simple_case\"),\n        pytest.param(0, 0, id=\"zero_case\"),\n        pytest.param(-1, -2, id=\"negative_case\"),\n    ],\n)\ndef test_double_value(input_val: int, expected: int) -> None:\n    result = double(input_val)\n    assert result == expected, f\"double({input_val}) should be {expected}\"\n```\n\n**Hypothesis example:**\n```python\nfrom hypothesis import given, strategies as st\n\n@given(st.lists(st.integers()))\ndef test_reverse_twice_returns_original(values: list[int]) -> None:\n    result = reverse(reverse(values))\n    assert result == values, \"reversing twice should preserve the original list\"\n\n@given(st.text())\ndef test_serialize_roundtrip(raw: str) -> None:\n    \"\"\"parse(serialize(x)) == x\"\"\"\n    assert parse(serialize(raw)) == raw, \"round-trip should preserve data\"\n```\n\n**Coexistence pattern:**\n```python\n# Known-critical cases as parametrize\n@pytest.mark.parametrize(\"raw\", [\n    pytest.param(\"\", id=\"empty\"),\n    pytest.param(\"\\x00\", id=\"null_byte\"),\n    pytest.param(\"a\" * 10000, id=\"huge_string\"),\n])\ndef test_parse_known_cases(raw: str) -> None:\n    assert parse(raw) is not None\n\n# General invariant as hypothesis\n@given(st.text())\ndef test_parse_never_crashes(raw: str) -> None:\n    result = parse(raw)  # should not raise\n    assert isinstance(result, (ParseResult, type(None)))\n```\n\n**Responding to `untested-production-code` findings:**\n\n1. Read the Slopgate output for the unreferenced symbol(s) and the suggested scaffold.\n2. Identify the public behavior users depend on — not just the function signature.\n3. Add behavior\u002Fintegration tests around the public entrypoint.\n4. Avoid testing every helper directly; prefer observable behavior through the public API.\n5. If the symbol is dead API, consider deleting or private-scoping instead of adding tests.\n6. Verify with `pytest \u003Caffected_tests>` and re-run `slopgate lint check`.\n\nSee [references\u002Fparametrization_patterns.md](references\u002Fparametrization_patterns.md) for patterns.\nSee [references\u002Fhypothesis_patterns.md](references\u002Fhypothesis_patterns.md) for property-based testing patterns.\n\n### 5. Validate Quality\n\n```bash\nmake quality\n```\n\n**Progress gate:** Acknowledge guidelines compliance before proceeding.\n\n## Quick Reference\n\n### Parametrize Pattern\n\n```python\n@pytest.mark.parametrize(\n    (\"input_val\", \"expected\"),\n    [\n        pytest.param(1, 2, id=\"simple_case\"),\n        pytest.param(0, 0, id=\"zero_case\"),\n    ],\n)\ndef test_double_value(input_val: int, expected: int) -> None:\n    result = double(input_val)\n    assert result == expected, f\"double({input_val}) should be {expected}\"\n```\n\n### Hypothesis Pattern\n\n```python\nfrom hypothesis import given, strategies as st\n\n@given(st.lists(st.integers()))\ndef test_reverse_twice_returns_original(values: list[int]) -> None:\n    result = reverse(reverse(values))\n    assert result == values, \"reversing twice should preserve the original list\"\n```\n\n### Coexistence Pattern\n\n```python\n# Explicit edge cases via parametrize\n@pytest.mark.parametrize(\"raw\", [\n    pytest.param(\"\", id=\"empty\"),\n    pytest.param(\"\\x00\", id=\"null_byte\"),\n])\ndef test_parse_edge_cases(raw: str) -> None:\n    assert parse(raw) is not None\n\n# General invariant via hypothesis\n@given(st.text())\ndef test_parse_never_crashes(raw: str) -> None:\n    result = parse(raw)  # should not raise\n    assert isinstance(result, (ParseResult, type(None)))\n```\n\n### Fixture Scopes\n\n| Scope | When |\n|-------|------|\n| `function` | Fresh state per test (default) |\n| `module` | Expensive setup, shared in file |\n| `session` | Very expensive, immutable |\n\n### Test Classification\n\nTests must verify: **function** (correctness), **behavior** (state changes), **robustness** (error handling), or **rigor** (boundaries).\n\nStructure-only tests should be absorbed into meaningful tests or removed.\n\n## Troubleshooting\n\n### Fixture Consolidation with Duplicate-Check Hooks\n\nWhen hooks block duplicate fixture definitions, follow this order:\n\n**Problem:** Cannot add fixture to conftest.py because hook detects duplicate with test file.\n\n**Solution 1: Use intermediate conftest.py**\n```\n# If consolidating from tests\u002Fgrpc\u002Ftest_foo.py:\n# 1. Add fixture to tests\u002Fgrpc\u002Fconftest.py (not tests\u002Fconftest.py)\n# 2. Remove fixture from tests\u002Fgrpc\u002Ftest_foo.py\n# 3. Tests inherit from nearest conftest.py in hierarchy\n```\n\n**Solution 2: Rename-then-migrate**\n```python\n# In test file, temporarily rename:\n@pytest.fixture\ndef _local_sample_config() -> Config:  # Renamed to avoid conflict\n    ...\n\n# Now add to conftest.py with original name\n# Then remove _local_sample_config from test file\n```\n\n**Key insight:** The conftest.py hierarchy allows fixtures in `tests\u002Fgrpc\u002Fconftest.py` to serve all tests under `tests\u002Fgrpc\u002F` without conflicting with `tests\u002Fconftest.py`.\n\n### Loop-to-Parametrize Conversion\n\nWhen converting loops to parametrization, split into separate test functions if the loop body has multiple assertions:\n\n```python\n# Before (anti-pattern):\ndef test_states() -> None:\n    for state in (A, B, C):\n        assert state in valid_states\n        obj = create(state=state)\n        assert obj.should_skip\n\n# After (two parametrized tests):\n@pytest.mark.parametrize(\"state\", [pytest.param(A, id=\"a\"), ...])\ndef test_state_is_valid(state: State) -> None:\n    assert state in valid_states, f\"{state} should be valid\"\n\n@pytest.mark.parametrize(\"state\", [pytest.param(A, id=\"a\"), ...])\ndef test_state_triggers_skip(state: State) -> None:\n    obj = create(state=state)\n    assert obj.should_skip, f\"{state} should trigger skip\"\n```\n\n## Resources\n\n- `scripts\u002Fanalyze_tests.py` - Find tests, detect anti-patterns\n- `scripts\u002Ffind_fixtures.py` - Discover fixtures, analyze scope\u002Fusage\n- `references\u002Fquality_rules.md` - Complete quality rules\n- `references\u002Fparametrization_patterns.md` - Parametrization examples\n- `references\u002Fhypothesis_patterns.md` - Property-based testing examples\n",{"name":5,"author":52,"license":15,"version":53,"metadata":54,"description":81,"compatibility":82},"Slopgate","1.0.0",{"hermes":55,"slopgate":63},{"tags":56,"related_skills":60},[57,30,58,59,41],"slopgate","pytest","hypothesis",[61,62],"slopgate-code-hygiene-refactor","slopgate-hygiene-orchestrator",{"rule_ids":64,"activation":70},[65,66,67,68,69],"untested-production-code","hypothesis-candidate","test-integrity","weak-assertion","loop-to-parametrize",{"avoid":71,"primary":75},[72,73,74],"production-only refactor","broad lint cleanup","helper ownership discovery",[76,77,78,79,80],"missing tests","coverage gaps","parametrization","property testing","weak assertions","Extend Python test suites with parametrization, fixture reuse, and quality enforcement. Use when asked to add tests, extend existing tests, improve test coverage, consolidate duplicate tests, fix test quality issues, or when Slopgate reports untested-production-code, missing test references, or coverage gaps. Triggers on requests like \"add tests for X\", \"extend these tests\", \"parametrize tests\", \"consolidate tests\", \"fix test quality\", \"improve test coverage\", \"write tests for this function\", \"test this module\", \"behavior tests\", \"integration tests\", \"missing tests\", \"coverage holes\", \"untested code\", or when a hook denies with test-related findings.\n","claude-code, opencode, codex, hermes, slopgate","master","5fab8004af1390aa17b3447f2780766c8bf2482c",".opencode\u002Fskills\u002Fslopgate-test-extender",0,"https:\u002F\u002Fgithub.com\u002Fvasceannie\u002Fslopgate","https:\u002F\u002Fgithub.com\u002Fvasceannie","thingswithstuff.io","2026-11-12 20:22:46",{"source":8,"skillName":5,"commands":92,"prompt":102,"zipAvailable":103},[93,96,99],{"tool":94,"command":95},"npx","npx skills add vasceannie\u002Fslopgate --skill slopgate-test-extender",{"tool":97,"command":98},"bunx","bunx skills add vasceannie\u002Fslopgate --skill slopgate-test-extender",{"tool":100,"command":101},"pnpm","pnpm dlx skills add vasceannie\u002Fslopgate --skill slopgate-test-extender","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fvasceannie\u002Fslopgate\u002Fblob\u002Fmaster\u002F.opencode\u002Fskills\u002Fslopgate-test-extender\u002FSKILL.md",true,[105,111,115,119,125],{"path":106,"resourceType":107,"resourceTypeDesc":108,"sizeBytes":109,"sourceHtmlUrl":110},"references\u002Fhypothesis_patterns.md","REFERENCE","参考文档",3838,"https:\u002F\u002Fgithub.com\u002Fvasceannie\u002Fslopgate\u002Fblob\u002Fmaster\u002F.opencode\u002Fskills\u002Fslopgate-test-extender\u002Freferences\u002Fhypothesis_patterns.md",{"path":112,"resourceType":107,"resourceTypeDesc":108,"sizeBytes":113,"sourceHtmlUrl":114},"references\u002Fparametrization_patterns.md",9443,"https:\u002F\u002Fgithub.com\u002Fvasceannie\u002Fslopgate\u002Fblob\u002Fmaster\u002F.opencode\u002Fskills\u002Fslopgate-test-extender\u002Freferences\u002Fparametrization_patterns.md",{"path":116,"resourceType":107,"resourceTypeDesc":108,"sizeBytes":117,"sourceHtmlUrl":118},"references\u002Fquality_rules.md",8114,"https:\u002F\u002Fgithub.com\u002Fvasceannie\u002Fslopgate\u002Fblob\u002Fmaster\u002F.opencode\u002Fskills\u002Fslopgate-test-extender\u002Freferences\u002Fquality_rules.md",{"path":120,"resourceType":121,"resourceTypeDesc":122,"sizeBytes":123,"sourceHtmlUrl":124},"scripts\u002Fanalyze_tests.py","SCRIPT","脚本",9786,"https:\u002F\u002Fgithub.com\u002Fvasceannie\u002Fslopgate\u002Fblob\u002Fmaster\u002F.opencode\u002Fskills\u002Fslopgate-test-extender\u002Fscripts\u002Fanalyze_tests.py",{"path":126,"resourceType":121,"resourceTypeDesc":122,"sizeBytes":127,"sourceHtmlUrl":128},"scripts\u002Ffind_fixtures.py",10880,"https:\u002F\u002Fgithub.com\u002Fvasceannie\u002Fslopgate\u002Fblob\u002Fmaster\u002F.opencode\u002Fskills\u002Fslopgate-test-extender\u002Fscripts\u002Ffind_fixtures.py",[130,154,161,198,222,243],{"skillNo":131,"name":132,"nameZh":6,"description":133,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":134,"categories":135,"keywords":137,"publishedAt":47},"01M71951JDWCYMS8GDC2C7395D","type-strictness","Enforce strict type compliance by eliminating `Any` types and `# type: ignore` comments. Use when:\n(1) Editing code that contains `Any` as a type annotation\n(2) Code has `# type: ignore` or `# pyright: ignore` comments\n(3) User requests type fixes or type strictness enforcement\n(4) Importing from untyped third-party libraries\n(5) User asks to \"fix types\", \"remove Any\", or \"add proper typing\"\n(6) Pyrefly or mypy reports type errors that need resolution\n(7) Code review reveals weak typing patterns\nFollows hierarchical resolution: codebase inference → type stubs → library inspection → type guards → casting (last resort).","https:\u002F\u002Fgithub.com\u002Fvasceannie\u002Fslopgate\u002Fblob\u002Fmaster\u002Fbundle\u002Fshared\u002Fskills\u002Ftype-strictness\u002FSKILL.md",[136],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[138,139,140,141,142,143,40,144,145,31,45,146,36,147,148,149,150,151,152,153],"type","type strictness","typing","types","remove","code review","errors","inspection","review","libraries","casting","pyrefly","mypy","pyright","library","patterns",{"skillNo":155,"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":156,"categories":157,"keywords":160,"publishedAt":47},"01M7194XGBQXSE7Y5BZ4K5CSTK","https:\u002F\u002Fgithub.com\u002Fvasceannie\u002Fslopgate\u002Fblob\u002Fmaster\u002Fbundle\u002Fshared\u002Fskills\u002Fslopgate-test-extender\u002FSKILL.md",[158,159],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[28,27,41,33,30,40,44,38,43,29,46,42,31,45,32,34,36,35,37,39],{"skillNo":162,"name":163,"nameZh":6,"description":164,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":165,"categories":166,"keywords":182,"publishedAt":47},"01M7194RMVQS5YJJ4ZEFXX20VS","slopgate-intelligent-coding-patterns","Pattern-selection playbook for agents editing code with complex branching, stateful parsers\u002Fworkflows, duplicated skeletons, policy checks, or ordered transforms. Load before refactoring if\u002Felif dispatch chains, boolean-flag state machines, repeated validation predicates, parallel provider\u002Flanguage implementations, or regex\u002Fcontent transformation pipelines. Also load when asked to \"apply a design pattern\", \"which pattern should I use here\", \"refactor this switch statement\", \"replace if-elif chain\", \"state machine pattern\", \"strategy pattern\", \"template method\", \"pipeline pattern\", \"specification pattern\", \"dispatch table\", \"guard clause refactor\", \"reduce cyclomatic complexity\", or when Slopgate fires PY-CODE-014, PY-CODE-017, PY-CODE-018, QUALITY-LINT-001, or PY-QUALITY-010. Use with behavior-locking tests and avoid pattern theater for simple one-off code.","https:\u002F\u002Fgithub.com\u002Fvasceannie\u002Fslopgate\u002Fblob\u002Fmaster\u002Fbundle\u002Fshared\u002Fskills\u002Fslopgate-intelligent-coding-patterns\u002FSKILL.md",[167,168,171,174,179],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":169,"categoryName":170,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000015","视觉设计",{"categoryNo":172,"categoryName":173,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":175,"categoryName":176,"taxonomyType":177,"taxonomyTypeDesc":178},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",{"categoryNo":180,"categoryName":181,"taxonomyType":177,"taxonomyTypeDesc":178},"01JBSEEDCAT000000000000032","设计师",[183,184,153,185,186,187,188,189,190,191,192,193,194,29,46,195,196,31,197,45],"coding patterns","slopgate intelligent coding patterns","coding","stateful","complexity","branch","which","chain","parsers","pipeline","selection","design","utility","workflows","reduce",{"skillNo":199,"name":62,"nameZh":6,"description":200,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":201,"categories":202,"keywords":205,"publishedAt":47},"01M7194KTXBM85GB0W7JKFQD55","Orchestrate systematic elimination of linter warnings and errors across Python, TypeScript, and Rust codebases. Use when asked to \"clean up lints\", \"fix all warnings\", \"run hygiene\", \"eliminate type errors\", \"orchestrate code quality fixes\", \"fix lint errors\", \"resolve quality gate failures\", \"pass CI checks\", \"reduce tech debt\", \"refactor to pass hooks\", or when a Slopgate hook denies with QUALITY-LINT-001, PY-CODE-017, PY-CODE-018, or any collector finding. This skill coordinates multiple code-hygiene-enforcer agents working in parallel on non-conflicting file groupings, tracks progress persistently via JSON exports, and loops until all issues are resolved. CRITICAL: Never run linters directly—always use `slopgate lint check` as the primary gate, discover project-specific tools from config files, and export findings to JSON for tracking.","https:\u002F\u002Fgithub.com\u002Fvasceannie\u002Fslopgate\u002Fblob\u002Fmaster\u002Fbundle\u002Fshared\u002Fskills\u002Fslopgate-hygiene-orchestrator\u002FSKILL.md",[203,204],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":172,"categoryName":173,"taxonomyType":21,"taxonomyTypeDesc":22},[206,207,39,208,40,209,210,211,212,213,46,197,214,215,216,217,218,219,220,221],"slopgate hygiene orchestrator","hygiene","project","ci","local","clean","never run","files","run","lint check","systematic","config","typescript","debt","quality-gates","hook",{"skillNo":223,"name":224,"nameZh":6,"description":225,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":226,"categories":227,"keywords":230,"publishedAt":47},"01M7194G50FZYXXT8VNK360RPA","slopgate-code-smell-utility-locator","Autoactivate when code-smell hooks or reviews mention feature envy, thin wrappers, duplicate helpers, duplicate code,\nscattered helpers, builders\u002Ffactories\u002Fconfig\u002Fconstants\u002Fdataclass reuse, PY-CODE-012, PY-CODE-013, QUALITY-LINT-001,\nor repeated slopgate code-quality denials. Also load when asked to \"find existing helpers\", \"consolidate utilities\",\n\"search for duplicate code\", \"where is this already implemented\", \"reuse existing logic\", \"don't duplicate this\",\n\"find similar functions\", \"utility inventory\", \"code smell radar\", or before adding new helpers\u002Fwrappers so agents can\nfind existing shared utilities and consolidate instead of duplicating.","https:\u002F\u002Fgithub.com\u002Fvasceannie\u002Fslopgate\u002Fblob\u002Fmaster\u002Fbundle\u002Fshared\u002Fskills\u002Fslopgate-code-smell-utility-locator\u002FSKILL.md",[228,229],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":175,"categoryName":176,"taxonomyType":177,"taxonomyTypeDesc":178},[195,231,45,232,41,233,234,235,236,29,237,31,238,34,37,239,240,217,241,242],"slopgate code smell utility locator","shared","reviews","inventory","search","hooks","new","coordination","radar","code-quality","Utilities","dataclass",{"skillNo":244,"name":61,"nameZh":6,"description":245,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":246,"categories":247,"keywords":250,"publishedAt":47},"01M7194AWG1Q37F9756H4SFDH9","Recovery playbook for Slopgate and project quality failures. Load after hook\u002Flint denials such as PY-CODE-017 flat sibling modules, PY-CODE-018 oversized modules, QUALITY-LINT-001 post-edit quality backstops, PY-QUALITY-010 magic numbers \u002F repeated literals, PY-CODE-012\u002FPY-CODE-013 smell findings, type-suppression bans, duplicate helpers, or repeated hygiene retry loops. Also load when asked to \"refactor this module\", \"split this file\", \"extract a class\", \"reduce complexity\", \"fix god class\", \"flatten module hierarchy\", \"move code to a package\", \"extract helpers\", \"consolidate duplicates\", \"replace magic numbers with constants\", or when a hook blocks an edit with structural complaints. Focuses on preserving public APIs while refactoring toward packages, cohesive modules, typed seams, and focused tests.","https:\u002F\u002Fgithub.com\u002Fvasceannie\u002Fslopgate\u002Fblob\u002Fmaster\u002Fbundle\u002Fshared\u002Fskills\u002Fslopgate-code-hygiene-refactor\u002FSKILL.md",[248,249],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":175,"categoryName":176,"taxonomyType":177,"taxonomyTypeDesc":178},[251,252,45,253,207,254,187,255,256,208,257,234,258,40,29,195,31,197,238,34],"code hygiene","slopgate code hygiene refactor","refactor","modules","duplicates","retry","move","structural"]