📦 deps(skills): sync thirdparty skills

This commit is contained in:
ci[bot]
2026-05-29 08:33:55 +00:00
parent b7eb57f5da
commit b4c88b32be
25 changed files with 3033 additions and 0 deletions
+240
View File
@@ -0,0 +1,240 @@
# Brooks-Lint — Shared Framework
Code and test quality diagnosis using principles from twelve classic software engineering books.
Use `source-coverage.md` to keep those sources grounded in real evidence, exceptions, and tradeoffs.
## The Iron Law
```
NEVER suggest fixes before completing risk diagnosis.
EVERY finding must follow: Symptom → Source → Consequence → Remedy.
```
Violating this law produces reviews that list rule violations without explaining why they
matter. A finding without a consequence and a remedy is not a finding — it is noise.
> **On-demand sections (skip unless the condition applies):**
> - "Remedy Mode" — only when user passes `--fix` or asks to fix findings
> - "Post-Report Triage" — only in interactive sessions after the report is output
> - "History Tracking" — only after the Health Score is computed
## Project Config
Before executing the review, attempt to read `.brooks-lint.yaml` from the project root.
If the file exists, parse and apply its settings before proceeding.
If the file does not exist, continue with defaults (all risks enabled, no ignores).
In a multi-mode session, re-read only if the user says the config has changed.
### Supported settings
**`disable`** — list of risk codes to skip entirely. Findings for disabled risks are
silently omitted from the report and do not affect the Health Score.
Valid codes: `R1` `R2` `R3` `R4` `R5` `R6` `T1` `T2` `T3` `T4` `T5` `T6`
**`severity`** — override the severity of a specific risk for this project.
Valid values: `critical` `warning` `suggestion`
Example: `R1: suggestion` means every R1 finding is downgraded to Suggestion regardless
of what the guide says.
**`ignore`** — list of glob patterns. Files matching any pattern are excluded from
analysis. Findings that arise solely from ignored files are omitted.
Common entries: `**/*.generated.*`, `**/vendor/**`, `**/migrations/**`
**`focus`** — non-empty list of risk codes to evaluate; all others are skipped.
Omit this key (or leave it empty) to evaluate all non-disabled risks.
Cannot be combined with a non-empty `disable` list.
**Minimal example:**
```yaml
version: 1
disable:
- T5
severity:
R1: suggestion
ignore:
- "**/*.generated.*"
```
If `.brooks-lint.yaml` contains a `custom_risks` map, read `custom-risks-guide.md`
from the `_shared/` directory for loading and scanning instructions.
### Config Validation
Before applying, check for errors and mention each in the report:
- Invalid risk code (not R1R6, T1T6, or a defined `Cx` code): skip it, note `"Config warning: X is not a valid risk code"`
- Invalid severity value (not `critical`/`warning`/`suggestion`): skip it, note the error
- Both `disable` and `focus` are non-empty: treat as a config error, ignore both, note it
If the YAML fails to parse entirely, skip config loading and proceed with defaults.
### Config Reporting
If a config file was found and applied, add this line immediately after the **Scope** line
in the report:
`Config: .brooks-lint.yaml applied (N risks disabled, M paths ignored)`
Include N and M even if zero. Omit this line if no config file was found.
---
## Auto Scope Detection
When no files or code are specified, detect scope automatically:
**PR Review:** `git diff --cached``git diff``git diff main...HEAD` → ask user.
**Architecture Audit / Tech Debt:** Entire project by default. `--since=<ref>`: run `git diff <ref>...HEAD --name-only`, analyze only modules containing changed files; note "Incremental audit — modules touched since <ref>".
**Test Quality:** All test files by default. If a diff exists, prioritize test files co-located with changed production files (`src/foo.ts``src/foo.test.ts`).
**Health Dashboard:** Entire project by default. If user provides a path, scope all dimension sub-scans to that path.
**Scope line:** Always state what was detected — e.g., `Scope: staged changes (3 files)` or `Scope: branch changes vs main (12 files)`.
---
## The Six Decay Risks
Navigation index only — canonical definitions (symptoms, severity guides, sources, "What Not
to Flag" guards) live in `decay-risks.md`. Do not duplicate or edit diagnostic questions here;
update `decay-risks.md` directly. Book-level coverage, exceptions, and tradeoffs are in
`source-coverage.md`.
| Risk | Diagnostic Question |
|------|---------------------|
| Cognitive Overload | How much mental effort to understand this? |
| Change Propagation | How many unrelated things break on one change? |
| Knowledge Duplication | Is the same decision expressed in multiple places? |
| Accidental Complexity | Is the code more complex than the problem? |
| Dependency Disorder | Do dependencies flow in a consistent direction? |
| Domain Model Distortion | Does the code faithfully represent the domain? |
---
## Report Template
**Language rule:** Output the report in the same language the user is using. Translate the
per-finding content and the one-sentence verdict to match the user's language. Keep the
following in English: Iron Law field labels (Symptom / Source / Consequence / Remedy),
book titles, principle and smell names (e.g. "Shotgun Surgery", "Divergent Change"),
and fixed structural headers from the template below (`Findings`, `Summary`,
`Module Dependency Graph`, `Critical`, `Warning`, `Suggestion`).
````
# Brooks-Lint Review
**Mode:** [PR Review / Architecture Audit / Tech Debt Assessment / Test Quality Review]
**Scope:** [file(s), directory, or description of what was reviewed]
**Health Score:** XX/100
[One sentence overall verdict]
---
## Module Dependency Graph
<!-- Mode 2 (Architecture Audit) ONLY — omit this section for other modes -->
<!-- classDef colors: see architecture-guide.md Step 1 Rule 6 -->
```mermaid
graph TD
...
```
---
## Findings
<!-- Sort all findings by severity: Critical first, then Warning, then Suggestion -->
<!-- If no findings in a severity tier, omit that tier's heading -->
### 🔴 Critical
**[Risk Name] — [Short descriptive title]**
Symptom: [exactly what was observed in the code]
Source: [Book title — Principle or Smell name]
Consequence: [what breaks or gets worse if this is not fixed]
Remedy: [concrete, specific action]
### 🟡 Warning
**[Risk Name] — [Short descriptive title]**
Symptom: ...
Source: ...
Consequence: ...
Remedy: ...
### 🟢 Suggestion
**[Risk Name] — [Short descriptive title]**
Symptom: ...
Source: ...
Consequence: ...
Remedy: ...
---
## Summary
[23 sentences: what is the most important action, and what is the overall trend]
````
## Remedy Mode
When the user passes `--fix` or asks to "fix the findings", read
`remedy-guide.md` from the `_shared/` directory before writing the report.
## Health Score Calculation
Base score: 100
Deductions:
- Each 🔴 Critical finding: 15
- Each 🟡 Warning finding: 5
- Each 🟢 Suggestion finding: 1
Floor: 0 (score cannot go below 0)
## History Tracking
After generating the Health Score, attempt to append a record to `.brooks-lint-history.json`
in the project root.
**Append logic:**
1. Read the file (or start with empty array if it doesn't exist)
2. Append: `{ date, mode, score, findings: { critical, warning, suggestion }, scope }`
3. Write the file back
**Trend display:** If the history file exists and contains at least one prior record for
the same mode, add a Trend line after the Health Score in the report:
**Trend:** 85 → 82 (3) over last 3 runs
Show the most recent prior score and the delta. If delta is 0: "Stable at 82".
If this is the first run for this mode: "First run — no trend data".
## Post-Report Triage (Optional)
**Guard:** Interactive sessions only — skip in CI/headless mode.
After reporting Warning or Suggestion findings, offer:
> Would you like to triage these findings? (accept / dismiss / defer / skip)
For each finding one at a time (lowest severity first): show title, ask `[a]ccept / [d]ismiss / [f]defer / [s]kip`; wait for reply before moving to the next.
**Dismiss:** ask one-line reason → append to `.brooks-lint.yaml` under `suppress:` → downgraded to info in future runs.
**Defer:** same as dismiss, add `expires: YYYY-MM-DD` (default 90 days) → resurfaces at original severity after expiry.
**Suppress matching at scan time:** for each `suppress:` entry, match `risk` code and file `pattern` against findings.
- Both match → downgrade to info (not counted in Health Score, shown under collapsed "Suppressed" section).
- `expires` is past → ignore entry, finding resurfaces. Note in Summary: "N suppressed findings have expired and are now active again."
## Reference Files
Read on demand:
| File | When to Read |
|------|-------------|
| `source-coverage.md` | At the start of every review, before writing findings |
| `decay-risks.md` | Before any production-code review or architecture/debt assessment |
| `test-decay-risks.md` | Before any test review and before the PR Review "Quick Test Check" step |
+48
View File
@@ -0,0 +1,48 @@
# Custom Risk Loading Guide
When `.brooks-lint.yaml` contains a `custom_risks` map, this guide governs how those
risks are loaded and scanned. Custom risks use `Cx` codes (C1, C2, …) — no conflict with
the standard R1R6 and T1T6 namespaces.
---
## Loading
1. For each entry in `custom_risks`, validate that it has:
- `name` — non-empty string
- `question` — the diagnostic question to ask
- `symptoms` — non-empty list of symptom patterns
- `severity` — map with at least one of: `critical`, `warning`, `suggestion`
2. Register each valid entry as a `Cx` code alongside R1R6 / T1T6. Once loaded,
`Cx` codes become valid targets for `disable`, `focus`, and `severity` fields in
the same config file.
3. Report any validation errors as config warnings (do not abort the review):
- Missing required field: `"Config warning: C1 missing 'symptoms'"`
- Invalid code format (must be `C` followed by digits): skip, note error
- Code conflicts with R/T namespace: skip, note error
---
## Scanning
During the analysis, treat each custom risk as an additional step after the standard
process:
- Use `question` as the diagnostic question
- Use `symptoms` as the symptom lookup list
- Use the `severity` map for tier classification
- Apply the Iron Law: `Source` field should be `"[Project-defined risk] — <risk name>"`
- Include custom risk findings in the Health Score (same deduction rules as R/T codes)
- In the report, custom findings appear after standard findings under a
**### Project-Specific Risks** sub-heading
---
## Config Validation additions
The following codes are valid in `disable`, `focus`, and `severity`:
- Standard: `R1``R6`, `T1``T6`
- Custom: any `Cx` code defined in `custom_risks`
- Any other code: skip it and emit `"Config warning: X is not a valid risk code"`
+294
View File
@@ -0,0 +1,294 @@
# Decay Risk Reference
Six patterns that cause software to degrade. Apply the Iron Law to each finding.
---
## Risk 1: Cognitive Overload
**Diagnostic question:** How much mental effort does a human need to understand this?
Cognitive load beyond working memory causes mistakes, avoidance, and blocks the refactoring that would fix it.
### Symptoms
- Function longer than 20 lines where multiple levels of abstraction are mixed together
- Nesting depth greater than 3 levels
- Parameter list with more than 4 parameters
- Magic numbers or unexplained constants
- Variable names that require reading the implementation to understand (e.g., `d`, `tmp2`, `flag`)
- Boolean expressions with 3 or more conditions combined
- Train-wreck chains: `a.getB().getC().doD()`
- Code names that do not match what the business calls the same concept
- Flag Arguments: a boolean parameter that makes the function do two fundamentally different
things depending on its value — a sign the function has two responsibilities
- Primitive Obsession: domain concepts represented as primitive types (`String email`,
`int orderId`, `double money`) rather than purpose-built value types — forces callers to know
which string is an email and which is a name
- Shallow module: the interface or documentation of a component is more complex relative to
the functionality it provides
### Sources
| Symptom | Book | Principle / Smell |
|---------|------|-------------------|
| Long Method | Fowler — Refactoring | Long Method |
| Long Parameter List | Fowler — Refactoring | Long Parameter List |
| Message Chains | Fowler — Refactoring | Message Chains |
| Flag Arguments | Fowler — Refactoring | Flag Arguments |
| Primitive Obsession | Fowler — Refactoring | Primitive Obsession |
| Function length and nesting | McConnell — Code Complete | Ch. 7: High-Quality Routines |
| Variable naming | McConnell — Code Complete | Ch. 11: The Power of Variable Names |
| Magic numbers | McConnell — Code Complete | Ch. 12: Fundamental Data Types |
| Domain name mismatch | Evans — Domain-Driven Design | Ubiquitous Language |
| Shallow Module | Ousterhout — A Philosophy of Software Design | Ch. 4: Modules Should Be Deep |
### Severity Guide
- 🔴 Critical: function > 50 lines, nesting > 5, or virtually no meaningful names
- 🟡 Warning: function 2050 lines, nesting 45, some unclear names
- 🟢 Suggestion: minor naming issues, 12 magic numbers, isolated train-wreck chains
### What Not to Flag
- Linear code with clear names and guard clauses is not automatically high cognitive load
- Internal implementation detail hidden behind a deep, simple module boundary is not a shallow-module problem
- Domain-specific terminology should not be flagged if it matches how experts actually speak
---
## Risk 2: Change Propagation
**Diagnostic question:** How many unrelated things break when you change one thing?
Each change ripples to unrelated modules, slowing velocity and multiplying regression risk.
### Symptoms
- Modifying one feature requires touching more than 3 files in unrelated modules
- One class changes for multiple different business reasons (e.g., `UserService` changes for
billing logic AND notification logic AND profile logic)
- A method uses more data from another class than from its own class
- Two classes know each other's internal state directly
- Changing one module requires recompiling or retesting many unrelated modules
- **Hyrum's Law**: with sufficient callers, every observable behavior — including
implementation details, error message text, coincidental call ordering, and undocumented
side effects — becomes an implicit contract that callers depend on, even though it was
never guaranteed by the declared API
- **Orthogonality violation**: changing one dimension of a feature forces edits in
unrelated dimensions — adding a new payment type should not require touching logging,
caching, or notification code, but in a non-orthogonal design it does
- Information Leakage: a design decision (e.g., a file format, protocol detail, or data
shape) is encoded in more than one module, so changing it requires coordinated edits
in multiple places even though only one module "owns" the concept
### Sources
| Symptom | Book | Principle / Smell |
|---------|------|-------------------|
| Shotgun Surgery | Fowler — Refactoring | Shotgun Surgery |
| Divergent Change | Fowler — Refactoring | Divergent Change |
| Feature Envy | Fowler — Refactoring | Feature Envy |
| Inappropriate Intimacy | Fowler — Refactoring | Inappropriate Intimacy |
| Orthogonality violation | Hunt & Thomas — The Pragmatic Programmer | Ch. 2: Orthogonality |
| DIP violation | Martin — Clean Architecture | Dependency Inversion Principle |
| High change propagation radius | Brooks — The Mythical Man-Month | Ch. 2: Brooks's Law (communication overhead) |
| Hyrum's Law | Winters et al. — Software Engineering at Google | Ch. 1: Hyrum's Law |
| Information Leakage | Ousterhout — A Philosophy of Software Design | Ch. 5: Information Hiding and Leakage |
### Severity Guide
- 🔴 Critical: one change touches > 5 files, or there is a structural dependency inversion (domain depends on infrastructure)
- 🟡 Warning: one change touches 35 files, mild coupling between modules
- 🟢 Suggestion: minor coupling, easily isolatable
### What Not to Flag
- A composition root wiring concrete dependencies is not a DIP violation by itself
- A stable public API with intentionally supported behavior is not automatically Hyrum's Law debt
- Similar edits inside one bounded context may be normal coordinated change, not shotgun surgery
---
## Risk 3: Knowledge Duplication
**Diagnostic question:** Is the same decision expressed in more than one place?
Multiple copies drift apart silently. DRY is about decisions, not code lines.
### Symptoms
- Same logic copy-pasted across multiple files or functions
- Same concept named differently in different parts of the codebase
(e.g., `user`, `account`, `member`, `customer` all referring to the same domain entity)
- Parallel class hierarchies that must change in sync
(e.g., adding a new payment type requires adding a class in 3 different hierarchies)
- Configuration values repeated as literals in multiple places
- Two modules that implement the same algorithm independently
### Sources
| Symptom | Book | Principle / Smell |
|---------|------|-------------------|
| Code duplication | Fowler — Refactoring | Duplicate Code |
| Parallel Inheritance | Fowler — Refactoring | Parallel Inheritance Hierarchies |
| DRY violation | Hunt & Thomas — The Pragmatic Programmer | DRY: Don't Repeat Yourself |
| Inconsistent naming | Evans — Domain-Driven Design | Ubiquitous Language |
| Alternative Classes | Fowler — Refactoring | Alternative Classes with Different Interfaces |
### Severity Guide
- 🔴 Critical: core business logic duplicated across modules, or same domain concept named 3+ different ways
- 🟡 Warning: utility code duplicated, naming inconsistent within a subsystem
- 🟢 Suggestion: minor literal duplication, single naming inconsistency
### What Not to Flag
- Repetition across separate bounded contexts is not automatically duplicate knowledge
- Temporary duplication during an active extraction or migration is not necessarily debt
- Shared protocol constants repeated at explicit boundaries may be acceptable when local ownership is clearer
---
## Risk 4: Accidental Complexity
**Diagnostic question:** Is the code more complex than the problem it solves?
Accidental complexity accumulates addition by addition until developers fight scaffolding more than solving the problem.
### Symptoms
- Abstractions built "for future use" with no current consumer
(e.g., a plugin system for a use case that has only one known implementation)
- Classes that barely justify their existence (wrap a single method call)
- Classes that only delegate to another class without adding behavior (pure middle-men)
- Second attempt at a system that is significantly more elaborate than the first,
adding generality for requirements that do not yet exist
- Switch statements that signal missing polymorphism
- Configuration options that have never been changed from their defaults
- Framework code larger than the application it powers
- Code grown under sustained tactical shortcuts: each workaround seemed small, but
accumulated shortcuts mean every new feature requires fighting the existing structure
### Sources
| Symptom | Book | Principle / Smell |
|---------|------|-------------------|
| Speculative Generality | Fowler — Refactoring | Speculative Generality |
| Lazy Class | Fowler — Refactoring | Lazy Class |
| Middle Man | Fowler — Refactoring | Middle Man |
| Switch Statements | Fowler — Refactoring | Switch Statements |
| Second System Effect | Brooks — The Mythical Man-Month | Ch. 5: The Second-System Effect |
| YAGNI violations | McConnell — Code Complete | Ch. 5: Design in Construction |
| Over-engineering | Hunt & Thomas — The Pragmatic Programmer | Topic 4: Good-Enough Software |
| Tactical programming debt | Ousterhout — A Philosophy of Software Design | Ch. 3: Strategic vs. Tactical Programming |
### Severity Guide
- 🔴 Critical: an entire subsystem built around a speculative requirement, or framework overhead dominates domain logic
- 🟡 Warning: several unnecessary abstractions or wrapper classes, unused configuration systems
- 🟢 Suggestion: one or two lazy classes or middle-man patterns in non-critical paths
### What Not to Flag
- A switch over an external protocol, wire format, or closed enum is not automatically missing polymorphism
- Thin wrappers that absorb vendor churn or hide instability may be justified
- A larger second version is not second-system effect unless the added generality exceeds present needs
---
## Risk 5: Dependency Disorder
**Diagnostic question:** Do dependencies flow in a consistent, predictable direction?
When business logic depends on infrastructure, infrastructure changes cascade into domain changes. Cycles prevent isolation.
### Symptoms
- Circular dependencies between modules or packages
- High-level business logic directly imports from low-level infrastructure
(e.g., a domain service imports from a specific database driver)
- Stable, widely-used components depend on unstable, frequently-changing ones
- Abstract components depending on concrete implementations
- Law of Demeter violations: `order.getCustomer().getAddress().getCity()`
- Module fan-out greater than 5 (imports from more than 5 other modules)
- A module implements an interface but only uses a subset of its methods, or must
provide stub implementations for methods it does not need (ISP violation: fat interface
forces unwanted dependencies on callers)
- The system feels like "one mind did not design this" — different modules use
incompatible architectural patterns with no clear rule for which to use where
- Direct version-pinned dependencies on transitive packages (diamond dependency risk);
upgrading one library requires coordinating multiple unrelated teams or repositories
### Sources
| Symptom | Book | Principle / Smell |
|---------|------|-------------------|
| Dependency cycles | Martin — Clean Architecture | Acyclic Dependencies Principle (ADP) |
| DIP violation | Martin — Clean Architecture | Dependency Inversion Principle (DIP) |
| Instability direction | Martin — Clean Architecture | Stable Dependencies Principle (SDP) |
| Abstraction mismatch | Martin — Clean Architecture | Stable Abstractions Principle (SAP) |
| ISP violation | Martin — Clean Architecture | Interface Segregation Principle (ISP) |
| Conceptual integrity | Brooks — The Mythical Man-Month | Ch. 4: Conceptual Integrity |
| Law of Demeter | Hunt & Thomas — The Pragmatic Programmer | Ch. 5: Decoupling and the Law of Demeter |
| SOLID violations | Martin — Clean Architecture | Single Responsibility, Open/Closed Principles |
| Diamond dependency / upgrade blockage | Winters et al. — Software Engineering at Google | Ch. 21: Dependency Management |
### Severity Guide
- 🔴 Critical: dependency cycles present, or domain layer directly depends on infrastructure layer
- 🟡 Warning: several SDP or DIP violations but no cycles; conceptual inconsistency across modules
- 🟢 Suggestion: minor Demeter violations, slightly elevated fan-out in isolated modules
### What Not to Flag
- High fan-out in an orchestration layer or composition root is not automatically disorder
- Adapter modules may depend on both domain and infrastructure when they explicitly translate across the boundary
- A stable facade over many leaf dependencies can be healthy if dependency policy is clear
---
## Risk 6: Domain Model Distortion
**Diagnostic question:** Does the code faithfully represent the problem it is solving?
Code that mismatches business language forces mental translation. Over time it models schemas instead of the domain, with logic bleeding into service layers.
### Symptoms
- Business logic scattered across service layers while domain objects have only getters and setters
(anemic domain model)
- Code variable, class, or method names that do not match what business stakeholders call the concept
- A class whose only purpose is to hold data with no behavior (pure data bag)
- A subclass that ignores or overrides most of its parent's behavior (refuses the inheritance)
- Bounded context boundaries crossed without any translation or anti-corruption layer
- Methods that are more interested in the data of another class than their own
(domain logic in the wrong place)
- A subclass overrides most parent methods with incompatible behavior or throws exceptions
where the parent contract guarantees success (LSP violation: substitution breaks callers)
- Value Objects treated as Entities: a concept defined entirely by its attributes (e.g., Money,
Email, Address) is given a mutable ID and lifecycle instead of being replaced when changed
### Sources
| Symptom | Book | Principle / Smell |
|---------|------|-------------------|
| Anemic Domain Model | Evans — Domain-Driven Design | Domain Model pattern |
| Ubiquitous Language drift | Evans — Domain-Driven Design | Ubiquitous Language |
| Bounded context violation | Evans — Domain-Driven Design | Bounded Context |
| Data Class | Fowler — Refactoring | Data Class |
| Refused Bequest | Fowler — Refactoring | Refused Bequest |
| Feature Envy | Fowler — Refactoring | Feature Envy |
| LSP violation | Martin — Clean Architecture | Liskov Substitution Principle (LSP) |
### Severity Guide
- 🔴 Critical: domain logic entirely in service layer, domain objects are pure data bags with no behavior
- 🟡 Warning: partial anemia, some naming inconsistency between code and domain language
- 🟢 Suggestion: minor naming drift in non-core areas, isolated cases of Feature Envy
### What Not to Flag
- CRUD-heavy workflows may legitimately use transaction scripts instead of rich domain objects
- DTOs, persistence records, and API payload models are allowed to be data-only
- Shared infrastructure language should not be mistaken for domain drift if the business model itself is simple
+37
View File
@@ -0,0 +1,37 @@
# Remedy Guide — Actionable Fix Mode
When `--fix` is active, enhance every finding's Remedy field to be directly actionable:
## Remedy Enhancement Rules
For each finding, the Remedy must include:
1. **Target**: exact file path and function/class name
2. **Action**: specific refactoring operation (e.g., "Extract lines 45-67 into a new
function `calculateShippingCost(items, config)`")
3. **Rationale**: one sentence explaining why this specific fix (not just "refactor")
## Fixability Classification
Classify each finding after writing the enhanced Remedy:
| Tier | Criteria | Report label |
|------|---------|-------------|
| Quick fix | Single-file, mechanical: rename, extract constant, reorder imports | `[quick-fix]` |
| Guided fix | Requires a design choice: where to split, what interface shape | `[guided]` |
| Manual | Cross-module, needs domain knowledge or team discussion | `[manual]` |
Append the label to the finding title: `**R1 — Long function in OrderService [quick-fix]**`
## Output Addition
After the standard report, add a **Fix Summary** section:
| Finding | Tier | Target File | Action |
|---------|------|------------|--------|
| R1 — Long function | quick-fix | src/order.ts:45 | Extract `calculateTotal()` |
| R5 — Circular dep | manual | src/models/ ↔ src/services/ | Introduce interface boundary |
## What NOT to do
- Do NOT modify any files. Phase 1 is diagnosis + actionable plan only.
- Do NOT generate diffs or code blocks. The Remedy text IS the deliverable.
- Do NOT re-score. The Health Score reflects current state, not projected state.
+248
View File
@@ -0,0 +1,248 @@
---
books:
- The Mythical Man-Month
- Code Complete
- Refactoring
- Clean Architecture
- The Pragmatic Programmer
- Domain-Driven Design
- A Philosophy of Software Design
- Software Engineering at Google
- xUnit Test Patterns
- The Art of Unit Testing
- Working Effectively with Legacy Code
- How Google Tests Software
---
# Source Coverage Matrix
Use this file after selecting a mode and before writing findings.
It exists to prevent shallow "book-name citation" reviews.
## Review Discipline
- Cite a book only when the observed symptom actually matches that book's principle.
- A threshold crossing is a hint, not a verdict. Check context, intent, and blast radius.
- Look for justified tradeoffs before flagging a smell as debt.
- Prefer concrete architectural or domain consequences over abstract style complaints.
- If two books pull in different directions, state the tradeoff instead of pretending there is no tension.
---
## Frederick Brooks — *The Mythical Man-Month*
**Encoded today**
- Change propagation as communication overhead
- Second-System Effect
- Conceptual Integrity
**Do not ignore**
- Whether the design shows a single coherent idea or competing local optimizations
- Whether cross-team coordination cost is becoming part of feature cost
**Do not over-flag**
- Large systems are not automatically second systems
- Multi-module designs are acceptable when they preserve conceptual integrity
---
## Steve McConnell — *Code Complete*
**Encoded today**
- Routine length, nesting, naming, and magic numbers
- Construction-phase YAGNI checks
- Defensive programming and error-handling discipline (guard clauses, input validation,
explicit error paths, assertions for invariants)
**Do not ignore**
- Whether low-level readability choices compound into operational risk
- Whether missing error handling makes failure modes invisible to maintainers
**Do not over-flag**
- Small, explicit guard clauses are not cognitive overload
- A long routine may be acceptable when it is linear, well-named, and single-purpose
---
## Martin Fowler — *Refactoring*
**Encoded today**
- Long Method, Long Parameter List, Message Chains
- Shotgun Surgery, Divergent Change, Feature Envy, Inappropriate Intimacy
- Duplicate Code, Speculative Generality, Lazy Class, Middle Man, Data Class
- Flag Arguments: boolean parameters that split a function into two behaviors
- Primitive Obsession: domain concepts expressed as raw primitive types instead of value types
**Do not ignore**
- Whether the code smell is local or systemic
- Whether a refactoring target has a natural home in the model
**Do not over-flag**
- Temporary duplication during an active extraction is not always debt
- A data-focused structure is acceptable when it is intentionally a DTO or boundary record
---
## Robert C. Martin — *Clean Architecture*
**Encoded today**
- DIP, ADP, SDP, SAP, and layering direction
- ISP: fat interfaces that force callers to depend on methods they do not use
- LSP: subclasses that break the behavioral contract of their parent type
- SRP and OCP: classes with multiple reasons to change; modules closed to modification
but open to extension via abstraction
**Do not ignore**
- Policy vs detail boundaries
- Whether dependency arrows preserve replaceability and testability
**Do not over-flag**
- Composition roots may depend on concrete infrastructure by design
- Thin adapter layers can import both directions when they are explicitly boundary glue
---
## Andrew Hunt & David Thomas — *The Pragmatic Programmer*
**Encoded today**
- Orthogonality
- DRY
- Law of Demeter
**Do not ignore**
- Whether knowledge duplication is really duplicated decision-making
- Whether coupling is accidental or a deliberate local simplification
**Do not over-flag**
- Similar code in different bounded contexts is not automatically a DRY violation
- Direct object access inside a cohesive aggregate is not always a Demeter problem
---
## Eric Evans — *Domain-Driven Design*
**Encoded today**
- Ubiquitous Language
- Bounded Context
- Anemic Domain Model
- Entity vs Value Object: objects with identity and lifecycle vs. objects defined solely by
their attributes (Money, Email, Address should be immutable value types, not mutable entities)
- Aggregate Roots: who owns the invariant boundary; cross-aggregate access only through the root
**Do not ignore**
- Aggregate boundaries, invariant ownership, and anti-corruption layers
- Whether names match the business language used by experts
**Do not over-flag**
- CRUD-heavy workflows may legitimately use transaction scripts
- Thin entities are acceptable when the domain itself is simple
---
## John Ousterhout — *A Philosophy of Software Design*
**Encoded today**
- Deep vs shallow modules
- Strategic vs tactical programming
- Information Leakage: a design decision encoded in more than one module, creating
change coupling even when no explicit import exists between the modules
**Do not ignore**
- Interface complexity relative to hidden complexity
- Whether repeated tactical patches are raising long-term cognitive load
- Whether a "helper" exposes internal design decisions that callers should not know
**Do not over-flag**
- Internal implementation complexity is fine when the interface stays simple
- A small wrapper is acceptable when it meaningfully absorbs volatility
---
## Titus Winters, Tom Manshreck, Hyrum Wright — *Software Engineering at Google*
**Encoded today**
- Hyrum's Law
- Dependency management and upgrade blockage
- Code sustainability: whether code as written can be maintained, migrated, and upgraded
over a multi-year horizon without heroic effort
- Backward compatibility: whether API changes preserve existing callers or force
coordinated upgrades across the organization
**Do not ignore**
- De facto APIs created by observable behavior
- The maintenance cost of exposing too much surface area
- Whether the dependency graph will allow independent upgrades over time
**Do not over-flag**
- A stable public API is not a liability if it is intentionally supported
- Fan-out alone is not disorder when dependency policy is explicit and governed
---
## Gerard Meszaros — *xUnit Test Patterns*
**Encoded today**
- Assertion Roulette, Mystery Guest, General Fixture
- Eager Test, Lazy Test, Test Code Duplication, Behavior Verification
- Erratic Test: tests that produce non-deterministic results due to shared state,
time dependence, or ordering assumptions between tests
**Do not ignore**
- Whether test failures are diagnosable
- Whether the suite shape amplifies maintenance cost
**Do not over-flag**
- Multiple assertions are acceptable when they express one behavior with one failure story
- Shared fixtures are acceptable when every field is relevant to the scenario
---
## Roy Osherove — *The Art of Unit Testing*
**Encoded today**
- Test naming discipline
- Test isolation
- Mock usage guidelines
- Completeness of edge-path tests
**Do not ignore**
- Whether tests verify behavior rather than wiring
- Whether seams are used to simplify tests, or production code is being contorted for testability
**Do not over-flag**
- A mock is acceptable when the dependency is nondeterministic and the assertion still verifies behavior
- Naming conventions are guidance; clarity is the goal
---
## Michael Feathers — *Working Effectively with Legacy Code*
**Encoded today**
- Legacy code as code without tests
- Sensing and Separation
- Seams
- Characterization Tests
**Do not ignore**
- Whether the team can change a risky area safely today
- Whether the code offers any seam for isolating behavior under change
**Do not over-flag**
- Untested code is not automatically legacy if it is stable and not under active change
- Characterization tests are most important before modifying unclear existing behavior
---
## Google Engineering — *How Google Tests Software*
**Encoded today**
- Change coverage vs line coverage
- Pyramid shape and suite portfolio economics
**Do not ignore**
- Whether the suite reflects business risk, not just percentages
- Whether expensive tests dominate feedback loops
**Do not over-flag**
- A non-70:20:10 ratio can be healthy when justified by platform constraints or product risk
- High coverage is useful when paired with meaningful branch and change protection
+246
View File
@@ -0,0 +1,246 @@
# Test Decay Risk Reference
Six patterns that cause test suites to degrade. Apply the Iron Law to each finding.
---
## Risk T1: Test Obscurity
**Diagnostic question:** How much effort does it take to understand what this test verifies?
Unclear test intent breeds distrust, missed failures, and duplicates — one step from an abandoned suite.
### Symptoms
- Assertion Roulette: multiple assertions with no message string — when one fails, it is
impossible to determine which behavior broke without reading every assertion
- Mystery Guest: test depends on external state (files, database rows, shared fixtures)
that is not visible in the test body
- Test names that do not express the scenario and expected outcome
(e.g., `test1`, `shouldWork`, `testLogin`, `testUserService`)
- General Fixture: an oversized setUp or beforeEach shared by unrelated tests, making
each test's preconditions invisible
- Test body requires reading production code to understand what is being verified
### Sources
| Symptom | Book | Principle / Smell |
|---------|------|-------------------|
| Assertion Roulette | Meszaros — xUnit Test Patterns | Assertion Roulette (p.224) |
| Mystery Guest | Meszaros — xUnit Test Patterns | Mystery Guest (p.411) |
| General Fixture | Meszaros — xUnit Test Patterns | General Fixture (p.316) |
| Test naming | Osherove — The Art of Unit Testing | method_scenario_expected naming convention |
### Severity Guide
- 🔴 Critical: no test name in the file describes the behavior being tested; all assertions lack messages
- 🟡 Warning: multiple Mystery Guests; several ambiguous test names
- 🟢 Suggestion: minor naming issues; isolated General Fixture
### What Not to Flag
- Multiple assertions are acceptable when they describe one coherent behavior and fail with a clear story
- Shared setup is fine when every initialized value is relevant to nearly every test
- Concise test names are acceptable if scenario and expected outcome are still obvious
---
## Risk T2: Test Brittleness
**Diagnostic question:** Do tests break when you refactor without changing behavior?
Brittle tests punish refactoring — eventually developers stop refactoring and the codebase stagnates to protect the suite.
### Symptoms
- Tests assert on private method results, internal state, or implementation details
rather than observable behavior
- Eager Test: one test method verifies multiple unrelated behaviors; any single change
causes it to fail regardless of which behavior was touched
- Over-specified: assertions enforce mock call order or exact parameter values that are
irrelevant to the behavior being tested
- Renaming or extracting a method causes 5 or more tests to fail even though no behavior changed
- Erratic Test: a test produces different results across runs without any change to
production code — caused by race conditions, time-dependent logic, random data, or
shared mutable state between tests
### Sources
| Symptom | Book | Principle / Smell |
|---------|------|-------------------|
| Eager Test | Meszaros — xUnit Test Patterns | Eager Test (p.228) |
| Erratic Test | Meszaros — xUnit Test Patterns | Erratic Test |
| Implementation coupling | Osherove — The Art of Unit Testing | Test isolation principle |
| Orthogonality violation | Hunt & Thomas — The Pragmatic Programmer | Ch. 2: Orthogonality |
### Severity Guide
- 🔴 Critical: refactoring with no behavior change causes test failures; > 5 tests coupled to a single implementation detail
- 🟡 Warning: Eager Tests common across the suite; moderate implementation-detail assertions
- 🟢 Suggestion: isolated over-specification in non-critical tests
### What Not to Flag
- Verifying an externally observable event or emitted command is not implementation coupling
- One test with several assertions is acceptable when all assertions support one behavior claim
- A fake or in-memory adapter is not brittleness if the test still asserts behavior, not wiring
---
## Risk T3: Test Duplication
**Diagnostic question:** Is the same test scenario expressed in more than one place?
Duplicated tests must change in multiple places and create false confidence without testing distinct behavior.
### Symptoms
- Test Code Duplication: same setup or assertion logic copy-pasted across multiple tests
without extraction into a shared helper
- Lazy Test: multiple tests verifying identical behavior with no differentiation in input,
state, or expected output
- Same boundary condition tested identically at unit, integration, and E2E level —
three copies with no layer differentiation
- Test helper functions or fixtures duplicated across test files instead of shared
### Sources
| Symptom | Book | Principle / Smell |
|---------|------|-------------------|
| Test Code Duplication | Meszaros — xUnit Test Patterns | Test Code Duplication (p.213) |
| Lazy Test | Meszaros — xUnit Test Patterns | Lazy Test (p.232) |
| DRY violation in tests | Hunt & Thomas — The Pragmatic Programmer | DRY: Don't Repeat Yourself |
### Severity Guide
- 🔴 Critical: core business scenario fully duplicated across all three test layers with no differentiation
- 🟡 Warning: common scenario setup repeated in 5 or more tests without extraction
- 🟢 Suggestion: minor helper duplication; isolated Lazy Tests
### What Not to Flag
- The same scenario may appear at unit and integration level when each layer verifies a distinct risk
- Small local setup duplication can be clearer than an over-abstracted fixture maze
- Similar assertions against different domain rules are not Lazy Tests if the business intent differs
---
## Risk T4: Mock Abuse
**Diagnostic question:** Is the test more complex than the behavior it tests?
Mock abuse produces tests that pass while verifying nothing — production code can be fully broken as long as the mocks are wired up.
### Symptoms
- Mock setup code is longer than the test logic itself
- Primary assertion is `expect(mock).toHaveBeenCalledWith(...)` — the test verifies
that a mock was called, not that any real behavior occurred
- Test-only methods added to production classes for lifecycle management in tests
- Single unit test uses more than 3 mocks
- Incomplete Mock: mock object missing fields that downstream code will access,
causing silent failures only visible in integration
- Hard-Coded Test Data: test data has no resemblance to real data shapes or constraints
### Sources
| Symptom | Book | Principle / Smell |
|---------|------|-------------------|
| Mock count > 3 | Osherove — The Art of Unit Testing | Mock usage guidelines |
| Testing mock behavior | Meszaros — xUnit Test Patterns | Behavior Verification (p.544) |
| Test-only production methods | Feathers — Working Effectively with Legacy Code | Ch. 3: Sensing and Separation |
| Hard-Coded Test Data | Meszaros — xUnit Test Patterns | Hard-Coded Test Data (p.534) |
| Incomplete Mock | Osherove — The Art of Unit Testing | Mock completeness requirement |
### Severity Guide
- 🔴 Critical: mock setup > 50% of test code; production class has methods only called from tests
- 🟡 Warning: mocks consistently > 3 per test; primary assertions are mock call verifications
- 🟢 Suggestion: isolated Incomplete Mocks; minor Hard-Coded Test Data
### What Not to Flag
- A small number of mocks around nondeterministic dependencies is acceptable when assertions still verify behavior
- Fakes and spies used to observe state transitions are not mock abuse by default
- One interaction assertion may be appropriate when the interaction itself is the behavior under test
---
## Risk T5: Coverage Illusion
**Diagnostic question:** Does the test suite actually protect against the failures that matter?
Coverage measures execution, not verification. 90% line coverage can still miss every critical failure mode — teams stop looking because the number says "covered."
### Symptoms
- High line coverage but error-handling branches, boundary conditions, and exception paths
have no corresponding tests
- Happy-path only: no sad paths, no null/empty/zero inputs, no concurrency edge cases
- Legacy code areas are being actively modified with no tests present
(Feathers: "legacy code is code without tests")
- Coverage percentage treated as a sign-off criterion; critical change paths remain untested
- Tests assert on return values but not on important side effects such as database writes,
event publications, or state transitions
### Sources
| Symptom | Book | Principle / Smell |
|---------|------|-------------------|
| Legacy code = no tests | Feathers — Working Effectively with Legacy Code | Ch. 1: "Legacy code is code without tests" |
| Change coverage vs line coverage | Google — How Google Tests Software | Ch. 11: Testing at Google Scale |
| Happy-path only | Osherove — The Art of Unit Testing | Test completeness principle |
### Severity Guide
- 🔴 Critical: legacy code area actively being modified with no tests; error-handling paths entirely absent
- 🟡 Warning: coverage > 80% but edge and exception paths are systematically absent
- 🟢 Suggestion: a few non-critical paths missing sad-path tests
### What Not to Flag
- High line coverage is useful when paired with branch, boundary, and change-path coverage
- A new module may have limited coverage early if it is still private and low-risk
- Side-effect assertions may live in integration tests rather than unit tests without implying a gap
---
## Risk T6: Architecture Mismatch
**Diagnostic question:** Does the test suite structure reflect the system's actual risk profile?
Wrong suite shape is slow and expensive — not from bad tests, but from using the wrong type at the wrong layer.
### Symptoms
- Inverted test pyramid: E2E or integration test count exceeds unit test count,
causing a slow and fragile suite
- Legacy code with no seam points: no interfaces, dependency injection, or seams exist,
making it impossible to test in isolation without modifying production code
- Legacy areas being modified have no Characterization Tests to capture current behavior
before changes are made
- Full suite execution time exceeds 10 minutes (indicates architectural problem,
not a performance problem — too many slow tests)
- High-risk and low-risk paths are tested at identical density;
no risk-based prioritization in test distribution
### Sources
| Symptom | Book | Principle / Smell |
|---------|------|-------------------|
| Inverted pyramid | Google — How Google Tests Software | 70:20:10 unit:integration:E2E ratio |
| No seam points | Feathers — Working Effectively with Legacy Code | Ch. 4: Seam Model |
| Missing Characterization Tests | Feathers — Working Effectively with Legacy Code | Ch. 13: Characterization Tests |
| Suite execution time | Meszaros — xUnit Test Patterns | Slow Tests (p. 253) |
### Severity Guide
- 🔴 Critical: legacy code being modified has no seams and no characterization tests; pyramid fully inverted
- 🟡 Warning: suite execution > 10 minutes; integration/E2E count exceeds unit tests
- 🟢 Suggestion: localized pyramid ratio deviation; a few legacy areas missing characterization tests
### What Not to Flag
- Deviating from 70:20:10 can be justified by platform constraints or product risk
- A suite heavy on integration tests can still be healthy if feedback is fast and purposefully layered
- A small number of critical-path E2E tests is desirable, not a smell