hunter-party-py/solid-hunter-py/SKILL.md
Audit Python class and interface design for SOLID violations — god classes, rigid extension points, broken substitutability, fat ABCs/Protocols, and concrete dependency chains. Focuses on responsibility assignment and abstraction fitness. Use when: reviewing class hierarchies, preparing for extension with new variants, reducing coupling between services, or improving testability of class-heavy code.
npx skillsauth add skyosev/agent-skills solid-hunter-pyInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Audit Python code for SOLID principle violations at the class and interface level — places where responsibilities are misassigned, extension requires modification, subtypes break contracts, ABCs/Protocols force unused dependencies, or classes are wired to concrete implementations. The goal: classes have clear responsibilities, extend without modification, substitute safely, expose cohesive interfaces, and depend on abstractions.
Single Responsibility (SRP). A class should have one reason to change — one actor, one domain concern. When a class serves multiple actors (e.g., persistence AND presentation), changes for one actor risk breaking the other. This is about responsibility assignment, not method count.
Open/Closed (OCP). A class should be open for extension but closed for modification. Adding a new variant or behavior should not require editing existing code. Extension points — strategy Protocols, plugin hooks, polymorphic dispatch — allow new behavior without touching proven code.
Liskov Substitution (LSP). A subtype must be usable wherever its base type is expected, without the caller
knowing or caring. Overrides must honor the base contract: same preconditions or weaker, same postconditions or
stronger. If a subclass raises NotImplementedError on an inherited method or silently ignores it, the substitution
is broken.
Interface Segregation (ISP). No client should be forced to depend on methods it does not use. Fat ABCs or Protocols that bundle unrelated capabilities force implementors into stub methods and callers into unnecessary coupling. Split into role-specific Protocols that each describe one capability.
Dependency Inversion (DIP). High-level policy should not depend on low-level detail — both should depend on
abstractions. When a service class instantiates its own dependencies or accepts concrete types in its __init__,
it becomes untestable and unchangeable without editing the class itself.
SOLID principles are guidelines for managing change, not rules to apply universally. Do not flag:
Classes that accumulate responsibilities from multiple domain concerns, becoming the "hub" that everything touches.
Signals:
__init__ with 5+ dependencies (high fan-in = multiple reasons to change)Manager, Handler, Service, Utils, Helper with broad scopeAction: Identify distinct responsibilities. Extract each into a focused class. The original class becomes a coordinator that delegates, or is dissolved entirely.
Code that must be modified — not extended — when a new variant, strategy, or behavior is added.
Signals:
if/elif chains on a type discriminant that appear in 3+ locations across the codebaseif type == 'email': send_email(); elif type == 'sms': send_sms())if/elif statements and no registration mechanismAction: Introduce a strategy/plugin pattern: define a Protocol for the variant behavior, implement per variant, dispatch polymorphically via a dict mapping or registry. The core class should not know about individual variants.
Subclasses or Protocol/ABC implementations that violate the contract of their base type.
Signals:
NotImplementedError or similarNone) instead of performing the base behaviorisinstance checks in consumer code to handle a specific subclass differentlyAction: If the subclass genuinely cannot support the base contract, the inheritance is wrong. Extract the shared behavior into a separate Protocol or use composition instead of inheritance.
ABCs or Protocols that bundle unrelated capabilities, forcing implementors to depend on methods they don't use.
Signals:
Action: Split into role-specific Protocols (e.g., Readable, Writable instead of ReadWriteStore). Clients
depend only on the Protocol slice they use.
High-level classes that directly depend on low-level implementations instead of abstractions.
Signals:
self.client = EmailClient() in __init__)__init__ parameter typed as a concrete class instead of a ProtocolAction: Define a Protocol for the dependency. Accept the Protocol in __init__. Wire the concrete implementation
at the composition root (app startup, DI container, factory).
Note: Direct instantiation of dataclasses, value objects, and builders is fine — DIP applies to service dependencies that represent behavior, not data construction.
main/master)BASE=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo main)
SCOPE=$(git diff --name-only $(git merge-base HEAD $BASE)...HEAD)
Constrain all subsequent scans to the resolved surface.EXCLUDE='--glob !**/*_test.py --glob !**/test_*.py --glob !**/tests/** --glob !**/venv/** --glob !**/.venv/** --glob !**/dist/**'
# Classes (starting point for SRP, DIP analysis)
rg 'class\s+\w+' --type py $EXCLUDE
# ABCs and Protocols (starting point for ISP analysis)
rg '(ABC|Protocol)\b' --type py $EXCLUDE
# Inheritance (extends/implements relationships)
rg --pcre2 'class\s+\w+\s*\([^)]+\)' --type py $EXCLUDE
# Direct instantiation in non-factory code (DIP signal)
rg --pcre2 'self\.\w+\s*=\s*\w+\(' --type py $EXCLUDE
# isinstance checks (LSP signal — caller sniffing subtypes)
rg 'isinstance\s*\(' --type py $EXCLUDE
# if/elif chains (OCP signal)
rg '^\s*elif\s' --type py $EXCLUDE
# NotImplementedError throws (LSP signal)
rg 'NotImplementedError|raise\s+NotImplemented' --type py $EXCLUDE
# @abstractmethod (interface method tracking)
rg '@abstractmethod' --type py $EXCLUDE
For each class found in Phase 2:
__init__ dependencies typed as Protocols or concrete classes? Does it instantiate its own service
dependencies?For each ABC/Protocol:
For each inheritance relationship:
isinstance checks that work around broken
substitution?Save as YYYY-MM-DD-solid-hunter-audit-{$LLM-name}.md in the project's docs folder (or project root if no docs folder
exists).
# SOLID Hunter Audit — {date}
## Scope
- Surface: {diff / path / codebase}
- Files: {count or list}
- Exclusions: {list}
- Architecture style: {class-based / mixed / functional (limited findings)}
## SRP Violations — God Classes
| # | Class | Location | Responsibilities | Dependencies | Action |
| - | ----- | -------- | ---------------- | ------------ | ------ |
| 1 | `OrderService` | file:line | order CRUD + email + invoicing | 7 injected | Split into 3 focused services |
## OCP Violations — Rigid Extension Points
| # | Location | Pattern | Occurrences | Action |
| - | -------- | ------- | ----------- | ------ |
| 1 | file:line | `if/elif` chain with 5 variants in 4 files | 4 | Extract strategy Protocol |
## LSP Violations — Broken Substitution
| # | Class | Base | Location | Violation | Action |
| - | ----- | ---- | -------- | --------- | ------ |
| 1 | `ReadOnlyRepo` | `Repository` | file:line | `save()` raises NotImplementedError | Use composition, not inheritance |
## ISP Violations — Fat Interfaces
| # | Interface | Location | Methods | Avg Used by Implementors | Action |
| - | --------- | -------- | ------- | ------------------------ | ------ |
| 1 | `DataStore` | file:line | 12 | 5 | Split into `Reader` + `Writer` + `Admin` |
## DIP Violations — Concrete Dependencies
| # | Class | Location | Concrete Dependency | Action |
| - | ----- | -------- | ------------------- | ------ |
| 1 | `UserService` | file:line | `EmailClient()` in `__init__` | Inject via Protocol |
## Recommendations (Priority Order)
1. **Must-fix**: {god classes with 5+ responsibilities, broken substitution causing runtime errors}
2. **Should-fix**: {rigid extension points touched by every new variant, fat ABCs with stub methods}
3. **Consider**: {concrete dependency chains limiting testability, speculative Protocol splits}
file/path.py:line with the exact code.development
Transforms vague feature ideas into precise, codebase-grounded technical requirements. Use when requirements are ambiguous/incomplete, the user struggles to describe behavior, terminology is unclear, or multiple concepts are mixed. Output is a requirements spec—NOT an implementation plan.
tools
Audit TypeScript type definitions for design debt — duplicated shapes, missing derivations, over-engineered generics, under-constrained type parameters, reinvented utility types, and disorganized type architecture. Type structure and maintainability, not type enforcement. Use when: reviewing type definitions for maintainability, reducing type duplication, simplifying over-engineered type-level logic, or reorganizing type architecture after growth.
development
Audit TypeScript test code for quality gaps — missing coverage on critical paths, brittle tests coupled to implementation, over-mocking, assertion-free tests, missing edge cases, and duplicated test setup. Focuses on test effectiveness, not production code structure. Use when: reviewing TypeScript test suites for reliability, reducing false-positive test failures, improving coverage of critical business logic, or cleaning up test debt.
tools
Audit TypeScript class and interface design for SOLID violations — god classes, rigid extension points, broken substitutability, fat interfaces, and concrete dependency chains. Focuses on responsibility assignment and abstraction fitness. Use when: reviewing class hierarchies, preparing for extension with new variants, reducing coupling between services, or improving testability of class-heavy code.