summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-02-27 22:51:51 +0200
committerPaul Buetow <paul@buetow.org>2026-02-27 22:51:51 +0200
commitd501cff2c6c521d4d8ff0d535164ed70ac790c84 (patch)
tree962103bbdd03304cf23e54cc19288576014fb250
parent86c0aa6c11eabe10ac7feedacce2e8f879920a9d (diff)
add solid principles
-rw-r--r--prompts/skills/solid-principles/SKILL.md123
-rw-r--r--prompts/skills/solid-principles/references/dip.md127
-rw-r--r--prompts/skills/solid-principles/references/isp.md110
-rw-r--r--prompts/skills/solid-principles/references/lsp.md114
-rw-r--r--prompts/skills/solid-principles/references/ocp.md88
-rw-r--r--prompts/skills/solid-principles/references/srp.md92
6 files changed, 654 insertions, 0 deletions
diff --git a/prompts/skills/solid-principles/SKILL.md b/prompts/skills/solid-principles/SKILL.md
new file mode 100644
index 0000000..8293150
--- /dev/null
+++ b/prompts/skills/solid-principles/SKILL.md
@@ -0,0 +1,123 @@
+---
+name: solid-principles
+version: 1.0.0
+description: >
+ This skill should be used when the user asks to "check SOLID violations",
+ "audit class design", "review code quality", "find design smells", or
+ "improve object-oriented architecture". Also triggers when the user
+ mentions a principle by name (e.g., "check SRP", "is this violating LSP?",
+ "dependency inversion"), or asks about coupling, cohesion, or class
+ responsibilities. Supports checking all five principles at once or
+ focusing on a single principle.
+---
+
+# SOLID Principles
+
+Analyze source code for violations of Robert C. Martin's SOLID principles of
+object-oriented design. Produce actionable findings with severity ratings,
+code locations, and concrete refactoring suggestions.
+
+## Subcommands
+
+Request a full audit or focus on a single principle:
+
+| Command Pattern | Principle | Reference |
+|----------------|-----------|-----------|
+| `solid all` / `solid check` / `solid` | All five principles | All references |
+| `solid srp` | Single Responsibility | `references/srp.md` |
+| `solid ocp` | Open/Closed | `references/ocp.md` |
+| `solid lsp` | Liskov Substitution | `references/lsp.md` |
+| `solid isp` | Interface Segregation | `references/isp.md` |
+| `solid dip` | Dependency Inversion | `references/dip.md` |
+
+When no subcommand is specified, default to checking all five principles.
+When a principle is mentioned by name (even without saying "solid"), match it to
+the appropriate subcommand.
+
+## Workflow
+
+### 1. Identify Target Code
+
+Determine what code to analyze:
+- When files or a directory are provided, use those.
+- When a class/module is referenced by name, locate it.
+- When ambiguous, ask which files or directories to scan.
+
+Supported languages: any OO language (Python, Java, TypeScript, C#, C++, Kotlin,
+Go with struct methods, Rust with impl blocks, etc.). Adapt the principle checks
+to the idioms of the target language — not every principle manifests identically
+across languages.
+
+### 2. Load Principle References
+
+Before analyzing, read the reference file(s) for the requested principle(s):
+
+- [`references/srp.md`](references/srp.md) for SRP checks
+- [`references/ocp.md`](references/ocp.md) for OCP checks
+- [`references/lsp.md`](references/lsp.md) for LSP checks
+- [`references/isp.md`](references/isp.md) for ISP checks
+- [`references/dip.md`](references/dip.md) for DIP checks
+
+For a full audit (`solid all`), read all five.
+
+### 3. Analyze
+
+For each target file/class, apply the violation patterns from the loaded references.
+Think carefully about each pattern — not every heuristic match is a true violation.
+Consider context, project size, and pragmatism. A 50-line script doesn't need the
+same architectural rigor as a large production system.
+
+### 4. Report Findings
+
+Present findings using this structure:
+
+#### Per Violation
+
+```
+**[PRINCIPLE] Violation — Severity: HIGH | MEDIUM | LOW**
+Location: `filename.py`, class `ClassName`, lines ~XX-YY
+Issue: Clear description of what violates the principle and why it matters.
+Suggestion: Concrete refactoring approach with brief code sketch if helpful.
+```
+
+Severity guidelines:
+- **HIGH**: Active maintenance pain, bug risk, or blocks extensibility.
+- **MEDIUM**: Code smell that will cause problems as the codebase grows.
+- **LOW**: Minor design impurity, worth noting but fine to defer.
+
+#### Summary
+
+After all findings, provide:
+- A count table: `| Principle | HIGH | MEDIUM | LOW |`
+- Top 3 priorities: which violations to fix first and why.
+- Overall assessment: one paragraph on the code's structural health.
+
+### 5. Refactor Mode (Optional)
+
+When a fix or refactoring is requested (e.g., "fix this", "refactor it",
+"show me the clean version"), produce refactored code that resolves the identified
+violations. Explain each change briefly.
+
+## Pragmatism Guidelines
+
+These are guidelines, not laws. Apply judgment:
+
+- Small utility scripts and prototypes get a lighter touch. Don't flag a 30-line
+ script for lacking dependency injection.
+- Some "violations" are conscious trade-offs. When a rationale is provided for
+ a trade-off, acknowledge it rather than insisting on purity.
+- Language idioms matter. A Python module with top-level functions isn't violating
+ SRP just because it's not a class. Go interfaces are implicitly satisfied, so
+ ISP looks different there.
+- Prefer actionable findings over exhaustive catalogs. Five important findings
+ beat twenty trivial ones.
+
+## Example Interaction
+
+**User**: `solid srp` (with a file attached)
+
+**Claude**:
+1. Reads `references/srp.md`
+2. Analyzes the attached file for SRP violations
+3. Reports findings with locations, severity, and suggestions
+4. Provides a summary with priorities
diff --git a/prompts/skills/solid-principles/references/dip.md b/prompts/skills/solid-principles/references/dip.md
new file mode 100644
index 0000000..8597f1c
--- /dev/null
+++ b/prompts/skills/solid-principles/references/dip.md
@@ -0,0 +1,127 @@
+# Dependency Inversion Principle (DIP)
+
+> "High-level modules should not depend on low-level modules. Both should depend
+> on abstractions. Abstractions should not depend on details. Details should
+> depend on abstractions."
+> — Robert C. Martin
+
+## Core Idea
+
+The flow of control and the flow of source code dependency should point in
+opposite directions at architectural boundaries. Business logic (high-level
+policy) should define the interfaces it needs, and infrastructure (low-level
+detail) should implement those interfaces. This makes the core of your
+application independent of databases, frameworks, and external services.
+
+DIP is NOT simply "use interfaces everywhere." It specifically addresses the
+direction of dependency at boundaries between layers.
+
+## Violation Patterns
+
+### 1. Direct Infrastructure Coupling
+**Heuristic**: Business logic classes directly import and instantiate
+infrastructure classes (database clients, HTTP libraries, file system APIs).
+
+**Look for**:
+- Business logic that imports `psycopg2`, `requests`, `boto3`, `smtplib`, etc.
+- Classes that create their own database connections or HTTP clients.
+- Domain logic files with imports from infrastructure/framework packages.
+
+**Refactoring**: Define an interface/protocol in the domain layer for the
+capability needed (e.g., `UserRepository`, `EmailSender`). Implement it in
+the infrastructure layer. Inject the implementation at composition time.
+
+### 2. Concrete Class Injection
+**Heuristic**: Dependency injection is used, but concrete classes are injected
+instead of interfaces/protocols.
+
+**Look for**:
+- Constructor type hints referencing concrete classes rather than abstractions:
+ `def __init__(self, db: PostgresDatabase)` instead of `def __init__(self, db: Database)`.
+- Factory methods that return concrete types.
+- DI container bindings that map concrete to concrete.
+
+**Refactoring**: Introduce an abstraction and depend on it. The concrete class
+implements the abstraction and is wired in at the composition root.
+
+### 3. Framework Entanglement
+**Heuristic**: Domain/business classes inherit from or directly use framework
+base classes, coupling the domain to the framework.
+
+**Look for**:
+- Domain entities extending ORM base classes (e.g., `class User(db.Model)`).
+- Business logic decorated with framework decorators that can't run without the framework.
+- Domain objects that require framework initialization to instantiate.
+
+**Refactoring**: Keep domain objects plain (POJOs/dataclasses/structs). Create
+adapter/mapper layers between the domain and the framework. The domain defines
+what it needs; the framework adapts to it, not the other way around.
+
+### 4. Upward Dependencies
+**Heuristic**: A low-level module imports from a high-level module, creating
+a circular or inverted dependency.
+
+**Look for**:
+- A utility/infrastructure module importing from a service/domain module.
+- Circular import errors or lazy imports used to work around circular dependencies.
+- A "shared" module that depends on specific feature modules.
+
+**Refactoring**: Extract the shared contract into an interface module that both
+layers can depend on. The dependency arrows should point inward (toward
+abstractions), not upward or circularly.
+
+### 5. Service Locator Anti-Pattern
+**Heuristic**: Instead of injecting dependencies, code reaches into a global
+registry or container to pull them out at runtime.
+
+**Look for**:
+- Calls like `Container.get(UserService)` or `ServiceLocator.resolve("db")`
+ scattered through business logic.
+- Global singleton access patterns in domain code.
+- `import settings` / `from config import db` in domain logic.
+
+**Refactoring**: Use constructor injection. Dependencies are declared explicitly
+in the constructor signature and wired at the composition root (main/entry point).
+
+### 6. Missing Abstraction Boundary
+**Heuristic**: There's no interface at all between layers — high-level code
+directly calls low-level code with no seam for substitution.
+
+**Look for**:
+- No interfaces/protocols/ABCs in the codebase at architectural boundaries.
+- Inability to run business logic tests without a real database/API.
+- Integration tests where unit tests should suffice.
+
+**Refactoring**: Introduce interface boundaries at the points where
+high-level policy meets low-level detail. You don't need interfaces everywhere —
+only at architectural boundaries.
+
+## Language-Specific Notes
+
+- **Python**: Use `Protocol` (structural subtyping) or `ABC` for abstractions.
+ Python's duck typing can mask DIP violations — things work until you try to
+ test or swap implementations. Type hints with concrete classes are a smell.
+- **Java/C#**: Natural territory for DIP with strong interface support and
+ mature DI frameworks (Spring, .NET DI). Watch for `new` inside business
+ logic as the primary violation signal.
+- **TypeScript**: Use interfaces or abstract classes. Watch for direct imports
+ of concrete implementations across module boundaries.
+- **Go**: Interfaces are implicitly satisfied, which makes DIP natural. Define
+ interfaces in the consumer package (where they're needed), not the provider
+ package. This is idiomatic Go and inherently supports DIP.
+- **Rust**: Traits serve as the abstraction boundary. Watch for concrete type
+ dependencies where `dyn Trait` or generic `T: Trait` would be more appropriate
+ at module boundaries.
+
+## False Positives to Avoid
+
+- **Leaf nodes**: Code at the edges of the system (entry points, scripts, CLI
+ tools) naturally depends on concretes — that's the composition root. Don't
+ flag `main()` for instantiating concrete classes.
+- **Value objects and DTOs**: These are details, but they're typically stable
+ and shared across layers. Depending on a `User` dataclass is fine.
+- **Standard library**: Depending on `datetime`, `pathlib`, `collections`, etc.
+ is not a DIP violation. These are stable abstractions.
+- **Small projects**: A 500-line application probably doesn't need interface
+ boundaries. DIP shines in systems with multiple developers, long lifespans,
+ and complex infrastructure dependencies.
diff --git a/prompts/skills/solid-principles/references/isp.md b/prompts/skills/solid-principles/references/isp.md
new file mode 100644
index 0000000..150084b
--- /dev/null
+++ b/prompts/skills/solid-principles/references/isp.md
@@ -0,0 +1,110 @@
+# Interface Segregation Principle (ISP)
+
+> "Clients should not be forced to depend on interfaces they do not use."
+> — Robert C. Martin
+
+## Core Idea
+
+Keep interfaces small and focused. When a class is forced to implement methods
+it doesn't need, those empty/stub methods are dead weight that couples the class
+to concerns it shouldn't know about. ISP says: split large interfaces into
+smaller, role-specific ones so that implementing classes only depend on the
+methods they actually use.
+
+ISP works hand-in-hand with SRP at the interface level — where SRP is about
+implementation cohesion, ISP is about contract cohesion.
+
+## Violation Patterns
+
+### 1. Fat Interface
+**Heuristic**: An interface/abstract class with 7+ methods where most
+implementations only meaningfully use a subset.
+
+**Look for**:
+- Implementations with multiple `pass` / `return None` / `throw NotImplementedError`
+ stub methods.
+- An interface that mixes read and write operations when some consumers are read-only.
+- A "God interface" that defines operations for multiple unrelated capabilities.
+
+**Refactoring**: Split into role-based interfaces. For example,
+`IRepository` → `IReader` + `IWriter`. Implementing classes can implement
+one or both.
+
+### 2. Marker Methods / Stub Implementations
+**Heuristic**: A class implements an interface but leaves some methods as
+no-ops or throws exceptions.
+
+**Look for**:
+- Methods with body `pass`, `return null`, `throw new UnsupportedOperationException()`.
+- Comments like "// not needed for this implementation".
+- Methods that always return a default/empty value because the class doesn't
+ actually support that operation.
+
+**Refactoring**: The class shouldn't be forced to implement those methods.
+Extract the unsupported methods into a separate interface.
+
+### 3. Parameter Interfaces / Bag-of-Methods
+**Heuristic**: A function or constructor accepts a large interface but only
+calls 1-2 methods on it.
+
+**Look for**:
+- A function that takes `IUserService` but only calls `getUserById()`.
+- Constructor injection of a broad service where only a narrow slice is used.
+- Test mocks that must implement 10 methods but the test only exercises 2.
+
+**Refactoring**: Depend on a smaller interface that exposes only what's needed.
+This also makes testing dramatically easier.
+
+### 4. Leaking Infrastructure into Domain Interfaces
+**Heuristic**: A domain-level interface includes infrastructure concerns like
+serialization, caching, or transport.
+
+**Look for**:
+- Domain interfaces with methods like `toJson()`, `serialize()`, `cache()`.
+- An interface mixing business operations (`calculateTotal()`) with
+ infrastructure (`save()`, `publish()`).
+
+**Refactoring**: Separate domain interfaces from infrastructure interfaces.
+A class can implement both, but consumers depend only on the interface relevant
+to their layer.
+
+### 5. Callback / Event Listener Bloat
+**Heuristic**: An event listener or callback interface requires implementing
+many event handlers when most consumers care about only one or two.
+
+**Look for**:
+- Adapter base classes that exist solely to provide empty default implementations
+ of a fat listener interface (e.g., Java's `MouseAdapter`).
+- Event handlers with mostly empty method bodies.
+
+**Refactoring**: Use single-method interfaces (functional interfaces) per
+event type, or an event bus pattern where consumers subscribe to specific events.
+
+## Language-Specific Notes
+
+- **Python**: Python uses Protocols and ABCs. ISP violations show up as Protocol
+ classes with too many required methods. Also applies to function signatures —
+ if a function accepts a complex object but only reads `.name`, consider accepting
+ just the string.
+- **Java/C#**: Classic ISP territory. Watch for interfaces with `default` methods
+ (Java 8+) used to paper over the fact that the interface is too broad.
+- **TypeScript**: `Pick<T, K>` and mapped types allow narrowing interfaces at the
+ call site, but the underlying interface might still be too broad. Also check for
+ `Partial<T>` used to avoid implementing all properties.
+- **Go**: Interfaces are implicitly satisfied and typically small (often 1-2 methods),
+ which naturally encourages ISP. Violations happen when someone defines a large
+ explicit interface mimicking Java-style patterns.
+- **Rust**: Traits can become fat. Look for traits with many methods where
+ implementors use `todo!()` or `unimplemented!()` for some.
+
+## False Positives to Avoid
+
+- **Genuinely cohesive interfaces**: A `Stream` interface with `read()`, `write()`,
+ `seek()`, `close()` is cohesive if all stream types support all operations.
+ Only flag it if some streams meaningfully can't support some operations.
+- **Standard library interfaces**: Don't flag language-standard interfaces
+ (e.g., Java's `Iterable`, Python's `Sequence`) as too broad — they're
+ established contracts.
+- **Small total interface count**: If an interface has 3-4 methods and all
+ implementors use all of them, it's fine. ISP isn't about making every
+ interface have exactly one method.
diff --git a/prompts/skills/solid-principles/references/lsp.md b/prompts/skills/solid-principles/references/lsp.md
new file mode 100644
index 0000000..bfe74b7
--- /dev/null
+++ b/prompts/skills/solid-principles/references/lsp.md
@@ -0,0 +1,114 @@
+# Liskov Substitution Principle (LSP)
+
+> "Objects of a supertype shall be replaceable with objects of a subtype without
+> altering the correctness of the program."
+> — Barbara Liskov
+
+## Core Idea
+
+If class `B` extends class `A`, you should be able to use `B` anywhere `A` is
+expected without surprises. Subtypes must honor the behavioral contract of their
+parent — not just the type signature, but the semantic expectations: preconditions,
+postconditions, invariants, and side effects.
+
+LSP is the formal foundation of polymorphism. When it's violated, inheritance
+becomes a liability rather than a tool.
+
+## Violation Patterns
+
+### 1. Refusing Inherited Behavior
+**Heuristic**: A subclass overrides a method to throw `NotImplementedError`,
+return `None`, or do nothing — effectively saying "I don't support this."
+
+**Look for**:
+- `raise NotImplementedError` in an override.
+- Override that returns a hardcoded empty/null value.
+- Override with `pass` as the body.
+- Comments like "not applicable for this subtype."
+
+**Refactoring**: The inheritance hierarchy is wrong. Either extract the
+unsupported method into a separate interface/mixin, or restructure the hierarchy
+so the subclass isn't forced to inherit behavior it can't fulfill.
+
+### 2. Strengthened Preconditions
+**Heuristic**: A subclass method requires MORE from its inputs than the parent's
+contract promises.
+
+**Look for**:
+- Added `if` guards at the start of an override that reject inputs the parent
+ would accept (e.g., parent accepts any int, subclass rejects negatives).
+- Type narrowing in overrides (e.g., parent accepts `Animal`, subclass demands `Dog`).
+- Additional validation not present in the parent.
+
+**Refactoring**: Subtypes should accept at least everything the parent accepts.
+Widen preconditions or rethink the hierarchy.
+
+### 3. Weakened Postconditions
+**Heuristic**: A subclass method provides LESS in its return value than the
+parent guarantees.
+
+**Look for**:
+- Parent returns a fully populated object; subclass returns a partial/null result.
+- Parent guarantees sorted output; subclass doesn't maintain the order.
+- Parent guarantees non-null; subclass may return null.
+
+**Refactoring**: Subtypes must deliver at least what the parent promises.
+Strengthen the subclass implementation or adjust the contract.
+
+### 4. Violated Invariants
+**Heuristic**: A subclass breaks a property that the parent always maintains.
+
+**Look for**:
+- Parent maintains `balance >= 0`; subclass allows negative balance.
+- Parent ensures a collection is always sorted; subclass inserts without sorting.
+- State machine invariants broken (e.g., skipping required transitions).
+
+**Refactoring**: Either enforce the invariant in the subclass or don't inherit.
+
+### 5. The Classic Rectangle-Square Problem
+**Heuristic**: A subclass constrains independent properties of the parent, creating
+contradictory behavior under mutation.
+
+**Look for**:
+- Subclass overrides setters to enforce constraints that break parent behavior
+ (e.g., `Square.set_width()` also sets height).
+- Tests that pass for the parent but fail for the subclass.
+
+**Refactoring**: Use composition or a common interface instead of inheritance.
+Make `Square` and `Rectangle` siblings, not parent-child.
+
+### 6. Exception Surprises
+**Heuristic**: A subclass throws exceptions that callers of the parent type
+wouldn't expect.
+
+**Look for**:
+- Override that throws new checked/unchecked exception types not in the parent's
+ contract.
+- Override that can fail in scenarios where the parent is documented as safe.
+
+**Refactoring**: Subtypes should only throw exceptions that are subtypes of
+the parent's declared exceptions, or handle errors internally.
+
+## Language-Specific Notes
+
+- **Python**: Duck typing makes LSP violations subtle — they manifest at runtime
+ as `AttributeError` or unexpected `None`. Watch for Protocol/ABC mismatches.
+- **Java/C#**: The compiler enforces type signatures but NOT behavioral contracts.
+ Look for `@Override` methods that change semantics.
+- **TypeScript**: Structural typing means you can create implicit subtypes that
+ violate LSP. Watch for interface implementations that throw on "unsupported"
+ methods.
+- **Go**: No inheritance, but interface satisfaction can be violated if a concrete
+ type's method has different semantics than the interface implies.
+
+## False Positives to Avoid
+
+- **Template Method pattern**: Abstract methods that are *designed* to be overridden
+ with different behavior are not LSP violations — the parent's contract explicitly
+ delegates to subclasses.
+- **Intentional restriction**: If a subclass is documented as a restricted variant
+ and callers are aware, this may be acceptable (though it suggests composition
+ over inheritance).
+- **Different behavior, same contract**: A `LinkedList` and `ArrayList` behave
+ differently in performance characteristics but both honor the `List` contract.
+ That's fine.
diff --git a/prompts/skills/solid-principles/references/ocp.md b/prompts/skills/solid-principles/references/ocp.md
new file mode 100644
index 0000000..476b7d2
--- /dev/null
+++ b/prompts/skills/solid-principles/references/ocp.md
@@ -0,0 +1,88 @@
+# Open/Closed Principle (OCP)
+
+> "Software entities should be open for extension, but closed for modification."
+> — Bertrand Meyer (popularized by Robert C. Martin)
+
+## Core Idea
+
+You should be able to add new behavior to a system without modifying existing,
+working code. This is achieved through abstractions: interfaces, abstract classes,
+strategy patterns, plugins, or higher-order functions. When new requirements arrive,
+you extend (add new implementations) rather than edit existing classes.
+
+OCP doesn't mean "never touch existing code" — it means design so that the *common*
+axis of change can be handled by extension rather than modification.
+
+## Violation Patterns
+
+### 1. Type-Switching / Conditional Dispatch
+**Heuristic**: `if/elif/else` or `switch/case` chains that branch on a type,
+status, enum, or string tag to decide behavior.
+
+**Look for**:
+- `if isinstance(x, Foo)` / `if type == "bar"` chains.
+- The same switch structure repeated across multiple methods/locations.
+- Adding a new type requires editing every switch statement.
+
+**Refactoring**: Replace conditionals with polymorphism. Define an interface,
+implement per type, and dispatch via method calls instead of branches.
+
+### 2. Hardcoded Strategies
+**Heuristic**: An algorithm or behavior is baked directly into a class with no
+way to swap it out without modifying the class itself.
+
+**Look for**:
+- A class that internally instantiates its collaborators (e.g., `self.sorter = QuickSort()`).
+- Formatting/encoding logic written inline rather than delegated.
+- Configuration that requires code changes (magic strings, hardcoded URLs).
+
+**Refactoring**: Extract the strategy into an interface/protocol and inject it.
+The class becomes open to new strategies without modification.
+
+### 3. Modification Magnets
+**Heuristic**: A single file or class that must be edited for every new feature,
+even when the features are independent.
+
+**Look for**:
+- A router/registry where every new handler requires adding a line.
+- A factory with a growing `if/elif` chain.
+- A config file that's really just a list of hardcoded mappings.
+
+**Refactoring**: Use registration patterns (decorators, plugin discovery,
+convention-based loading) so new features register themselves.
+
+### 4. Rigid Data Pipelines
+**Heuristic**: A processing pipeline where adding a new step requires modifying
+the pipeline class rather than plugging in a new processor.
+
+**Look for**:
+- Sequential method calls in a `process()` method with no way to add/remove steps.
+- ETL code where each new transformation is added by editing the main function.
+
+**Refactoring**: Use a pipeline/chain pattern where processors implement a common
+interface and can be composed dynamically.
+
+## Language-Specific Notes
+
+- **Python**: Protocols and ABCs enable OCP. Decorators and first-class functions
+ are natural extension points. `functools.singledispatch` is an idiomatic
+ alternative to type-switching.
+- **Java/C#**: Interfaces and abstract classes are the primary mechanism. Look
+ for `switch` on enums as a common violation.
+- **TypeScript**: Union types with exhaustive switches are sometimes intentional
+ (discriminated unions). This is a valid pattern when the set of types is truly
+ closed. Flag it only when the set is expected to grow.
+- **Go**: Interface satisfaction is implicit, which makes OCP natural. Watch for
+ type-switch statements (`switch v := x.(type)`) as potential violations.
+
+## False Positives to Avoid
+
+- **Discriminated unions in functional-style code**: TypeScript `type Shape = Circle | Square`
+ with exhaustive pattern matching is a deliberate design choice, not a violation —
+ the compiler enforces handling all cases.
+- **Simple mappings**: A dictionary/map from string to handler is often fine and
+ doesn't need a full plugin system.
+- **Early-stage code**: Adding abstractions before you know the axis of change
+ is premature. OCP is most valuable when applied to known extension points.
+- **Configuration**: Not every hardcoded value is an OCP violation. Constants
+ that genuinely don't change are fine.
diff --git a/prompts/skills/solid-principles/references/srp.md b/prompts/skills/solid-principles/references/srp.md
new file mode 100644
index 0000000..a6fd5d5
--- /dev/null
+++ b/prompts/skills/solid-principles/references/srp.md
@@ -0,0 +1,92 @@
+# Single Responsibility Principle (SRP)
+
+> "A class should have one, and only one, reason to change."
+> — Robert C. Martin
+
+## Core Idea
+
+SRP is about **cohesion**: every module, class, or function should be responsible
+to exactly one actor (stakeholder). If two different actors depend on the same class
+for different reasons, changes for one actor risk breaking the other.
+
+This is NOT simply "a class should do one thing." A class can have multiple methods
+and still satisfy SRP if they all serve the same actor and change for the same reason.
+
+## Violation Patterns
+
+### 1. God Class / Blob Class
+**Heuristic**: Class exceeds ~200 lines, has 10+ public methods, or touches 3+
+unrelated domains (e.g., database access + email sending + PDF generation).
+
+**Look for**:
+- Methods that could be grouped into separate, named clusters.
+- Instance variables that are only used by a subset of methods.
+- Imports at the top that span unrelated domains (e.g., `import smtplib` alongside
+ `import sqlalchemy` alongside `import matplotlib`).
+
+**Refactoring**: Extract cohesive method groups into separate classes. The original
+class can delegate or coordinate.
+
+### 2. Mixed Abstraction Levels
+**Heuristic**: A single class handles both high-level policy/orchestration AND
+low-level detail (e.g., a `ReportService` that both decides *what* to report and
+*how* to format HTML).
+
+**Look for**:
+- Methods mixing business logic with I/O, serialization, or formatting.
+- A class that would need to change if either the business rules OR the
+ infrastructure changes.
+
+**Refactoring**: Separate policy from mechanism. Create a high-level class for
+business rules and a low-level class for the technical details.
+
+### 3. Feature Envy
+**Heuristic**: A method in class A spends most of its body accessing data from
+class B.
+
+**Look for**:
+- Long chains like `self.order.customer.address.city`.
+- Methods that take another object as a parameter and then call 5+ methods on it.
+
+**Refactoring**: Move the method to the class whose data it primarily uses.
+
+### 4. Data Class Serving Multiple Masters
+**Heuristic**: A data/model class has methods added for different consumers
+(e.g., `to_api_response()`, `to_csv_row()`, `to_admin_view()`).
+
+**Look for**:
+- Serialization methods for different formats or consumers on the same class.
+- Presentation logic mixed with domain logic.
+
+**Refactoring**: Use separate serializer/presenter classes per consumer.
+
+### 5. Utility Dumping Ground
+**Heuristic**: A `Utils`, `Helpers`, or `Common` class/module that accumulates
+unrelated functions over time.
+
+**Look for**:
+- Functions in the same module with zero shared state or concept.
+- The file grows with every feature because "it didn't fit anywhere else."
+
+**Refactoring**: Group related utilities into purpose-named modules
+(e.g., `string_utils`, `date_utils`, `validation`).
+
+## Language-Specific Notes
+
+- **Python**: Modules themselves can be a unit of responsibility. A module with
+ related top-level functions is fine — SRP violations happen when unrelated
+ concerns are lumped together.
+- **Java/C#**: Classes are the natural unit. Watch for `Service` classes that
+ accumulate unrelated methods.
+- **TypeScript**: Both modules and classes apply. Watch for barrel files that
+ re-export unrelated functionality.
+- **Go**: Packages are the unit of responsibility. A package with a clear name
+ and focused purpose satisfies SRP.
+
+## False Positives to Avoid
+
+- A class with many methods that all operate on the same cohesive data structure
+ (e.g., a `Matrix` class with `add`, `multiply`, `transpose`, `determinant`).
+- A facade or coordinator class that delegates to focused collaborators — the
+ coordination itself is the single responsibility.
+- DTOs/value objects with multiple fields — having many fields isn't a violation.