argus-standards 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- argus_standards-0.1.0/.argus.yml +18 -0
- argus_standards-0.1.0/.claude/rules/atomic-commit.md +38 -0
- argus_standards-0.1.0/.claude/rules/code-quality.md +36 -0
- argus_standards-0.1.0/.claude/rules/dependency-injection.md +28 -0
- argus_standards-0.1.0/.claude/rules/design-patterns.md +30 -0
- argus_standards-0.1.0/.claude/rules/documentation-standards.md +27 -0
- argus_standards-0.1.0/.claude/rules/error-handling.md +27 -0
- argus_standards-0.1.0/.claude/rules/pre-commit.md +27 -0
- argus_standards-0.1.0/.claude/rules/refactoring.md +62 -0
- argus_standards-0.1.0/.claude/rules/solid.md +30 -0
- argus_standards-0.1.0/.claude/rules/tdd.md +40 -0
- argus_standards-0.1.0/.claude/rules/testing-strategy.md +82 -0
- argus_standards-0.1.0/.claude/rules/type-safety.md +25 -0
- argus_standards-0.1.0/.claude/skills/atomic-commit/SKILL.md +38 -0
- argus_standards-0.1.0/.claude/skills/code-quality/SKILL.md +17 -0
- argus_standards-0.1.0/.claude/skills/dependency-injection/SKILL.md +79 -0
- argus_standards-0.1.0/.claude/skills/design-patterns/SKILL.md +101 -0
- argus_standards-0.1.0/.claude/skills/documentation-standards/SKILL.md +59 -0
- argus_standards-0.1.0/.claude/skills/error-handling/SKILL.md +49 -0
- argus_standards-0.1.0/.claude/skills/pre-commit/SKILL.md +25 -0
- argus_standards-0.1.0/.claude/skills/refactoring/SKILL.md +122 -0
- argus_standards-0.1.0/.claude/skills/solid/SKILL.md +33 -0
- argus_standards-0.1.0/.claude/skills/tdd/SKILL.md +63 -0
- argus_standards-0.1.0/.claude/skills/testing-strategy/SKILL.md +118 -0
- argus_standards-0.1.0/.claude/skills/type-safety/SKILL.md +41 -0
- argus_standards-0.1.0/.cursor/rules/atomic-commit.md +38 -0
- argus_standards-0.1.0/.cursor/rules/code-quality.md +36 -0
- argus_standards-0.1.0/.cursor/rules/dependency-injection.md +28 -0
- argus_standards-0.1.0/.cursor/rules/design-patterns.md +30 -0
- argus_standards-0.1.0/.cursor/rules/documentation-standards.md +27 -0
- argus_standards-0.1.0/.cursor/rules/error-handling.md +27 -0
- argus_standards-0.1.0/.cursor/rules/pre-commit.md +27 -0
- argus_standards-0.1.0/.cursor/rules/refactoring.md +62 -0
- argus_standards-0.1.0/.cursor/rules/solid.md +30 -0
- argus_standards-0.1.0/.cursor/rules/tdd.md +40 -0
- argus_standards-0.1.0/.cursor/rules/testing-strategy.md +82 -0
- argus_standards-0.1.0/.cursor/rules/type-safety.md +25 -0
- argus_standards-0.1.0/.github/copilot-instructions.md +479 -0
- argus_standards-0.1.0/.github/workflows/ci.yml +41 -0
- argus_standards-0.1.0/.github/workflows/publish.yml +61 -0
- argus_standards-0.1.0/.opencode/commands/atomic-commit.md +10 -0
- argus_standards-0.1.0/.opencode/commands/code-quality.md +10 -0
- argus_standards-0.1.0/.opencode/commands/dependency-injection.md +10 -0
- argus_standards-0.1.0/.opencode/commands/design-patterns.md +10 -0
- argus_standards-0.1.0/.opencode/commands/documentation-standards.md +12 -0
- argus_standards-0.1.0/.opencode/commands/error-handling.md +11 -0
- argus_standards-0.1.0/.opencode/commands/pre-commit.md +18 -0
- argus_standards-0.1.0/.opencode/commands/refactoring.md +11 -0
- argus_standards-0.1.0/.opencode/commands/solid.md +26 -0
- argus_standards-0.1.0/.opencode/commands/tdd.md +13 -0
- argus_standards-0.1.0/.opencode/commands/testing-strategy.md +11 -0
- argus_standards-0.1.0/.opencode/commands/type-safety.md +10 -0
- argus_standards-0.1.0/.opencode/skills/atomic-commit/SKILL.md +38 -0
- argus_standards-0.1.0/.opencode/skills/code-quality/SKILL.md +36 -0
- argus_standards-0.1.0/.opencode/skills/dependency-injection/SKILL.md +28 -0
- argus_standards-0.1.0/.opencode/skills/design-patterns/SKILL.md +30 -0
- argus_standards-0.1.0/.opencode/skills/documentation-standards/SKILL.md +27 -0
- argus_standards-0.1.0/.opencode/skills/error-handling/SKILL.md +27 -0
- argus_standards-0.1.0/.opencode/skills/pre-commit/SKILL.md +27 -0
- argus_standards-0.1.0/.opencode/skills/refactoring/SKILL.md +62 -0
- argus_standards-0.1.0/.opencode/skills/solid/SKILL.md +30 -0
- argus_standards-0.1.0/.opencode/skills/tdd/SKILL.md +40 -0
- argus_standards-0.1.0/.opencode/skills/testing-strategy/SKILL.md +82 -0
- argus_standards-0.1.0/.opencode/skills/type-safety/SKILL.md +25 -0
- argus_standards-0.1.0/AGENTS.md +479 -0
- argus_standards-0.1.0/CLAUDE.md +1140 -0
- argus_standards-0.1.0/LICENSE +21 -0
- argus_standards-0.1.0/PKG-INFO +133 -0
- argus_standards-0.1.0/README.md +104 -0
- argus_standards-0.1.0/argus/__init__.py +2 -0
- argus_standards-0.1.0/argus/adapters/__init__.py +0 -0
- argus_standards-0.1.0/argus/adapters/base.py +40 -0
- argus_standards-0.1.0/argus/adapters/claude.py +56 -0
- argus_standards-0.1.0/argus/adapters/copilot.py +20 -0
- argus_standards-0.1.0/argus/adapters/cursor.py +20 -0
- argus_standards-0.1.0/argus/adapters/opencode.py +40 -0
- argus_standards-0.1.0/argus/cli.py +168 -0
- argus_standards-0.1.0/argus/config.py +23 -0
- argus_standards-0.1.0/argus/generator.py +50 -0
- argus_standards-0.1.0/argus/loader.py +63 -0
- argus_standards-0.1.0/argus/packs/atomic-commit/checklist.md +8 -0
- argus_standards-0.1.0/argus/packs/atomic-commit/examples.md +19 -0
- argus_standards-0.1.0/argus/packs/atomic-commit/instructions.md +36 -0
- argus_standards-0.1.0/argus/packs/atomic-commit/pack.yml +5 -0
- argus_standards-0.1.0/argus/packs/code-quality/checklist.md +8 -0
- argus_standards-0.1.0/argus/packs/code-quality/instructions.md +34 -0
- argus_standards-0.1.0/argus/packs/code-quality/pack.yml +5 -0
- argus_standards-0.1.0/argus/packs/dependency-injection/checklist.md +8 -0
- argus_standards-0.1.0/argus/packs/dependency-injection/examples.md +60 -0
- argus_standards-0.1.0/argus/packs/dependency-injection/instructions.md +26 -0
- argus_standards-0.1.0/argus/packs/dependency-injection/pack.yml +5 -0
- argus_standards-0.1.0/argus/packs/design-patterns/checklist.md +8 -0
- argus_standards-0.1.0/argus/packs/design-patterns/examples.md +82 -0
- argus_standards-0.1.0/argus/packs/design-patterns/instructions.md +28 -0
- argus_standards-0.1.0/argus/packs/design-patterns/pack.yml +5 -0
- argus_standards-0.1.0/argus/packs/documentation-standards/checklist.md +10 -0
- argus_standards-0.1.0/argus/packs/documentation-standards/examples.md +38 -0
- argus_standards-0.1.0/argus/packs/documentation-standards/instructions.md +25 -0
- argus_standards-0.1.0/argus/packs/documentation-standards/pack.yml +5 -0
- argus_standards-0.1.0/argus/packs/error-handling/checklist.md +9 -0
- argus_standards-0.1.0/argus/packs/error-handling/examples.md +29 -0
- argus_standards-0.1.0/argus/packs/error-handling/instructions.md +25 -0
- argus_standards-0.1.0/argus/packs/error-handling/pack.yml +5 -0
- argus_standards-0.1.0/argus/packs/pre-commit/checklist.md +16 -0
- argus_standards-0.1.0/argus/packs/pre-commit/instructions.md +25 -0
- argus_standards-0.1.0/argus/packs/pre-commit/pack.yml +9 -0
- argus_standards-0.1.0/argus/packs/refactoring/checklist.md +9 -0
- argus_standards-0.1.0/argus/packs/refactoring/examples.md +102 -0
- argus_standards-0.1.0/argus/packs/refactoring/instructions.md +60 -0
- argus_standards-0.1.0/argus/packs/refactoring/pack.yml +6 -0
- argus_standards-0.1.0/argus/packs/solid/checklist.md +24 -0
- argus_standards-0.1.0/argus/packs/solid/instructions.md +28 -0
- argus_standards-0.1.0/argus/packs/solid/pack.yml +5 -0
- argus_standards-0.1.0/argus/packs/tdd/checklist.md +11 -0
- argus_standards-0.1.0/argus/packs/tdd/examples.md +41 -0
- argus_standards-0.1.0/argus/packs/tdd/instructions.md +38 -0
- argus_standards-0.1.0/argus/packs/tdd/pack.yml +6 -0
- argus_standards-0.1.0/argus/packs/testing-strategy/checklist.md +9 -0
- argus_standards-0.1.0/argus/packs/testing-strategy/examples.md +98 -0
- argus_standards-0.1.0/argus/packs/testing-strategy/instructions.md +80 -0
- argus_standards-0.1.0/argus/packs/testing-strategy/pack.yml +6 -0
- argus_standards-0.1.0/argus/packs/type-safety/checklist.md +8 -0
- argus_standards-0.1.0/argus/packs/type-safety/examples.md +22 -0
- argus_standards-0.1.0/argus/packs/type-safety/instructions.md +23 -0
- argus_standards-0.1.0/argus/packs/type-safety/pack.yml +5 -0
- argus_standards-0.1.0/docs/superpowers/plans/2026-06-14-argus-v1.md +2335 -0
- argus_standards-0.1.0/docs/superpowers/plans/2026-06-15-documentation-standards-pack.md +302 -0
- argus_standards-0.1.0/docs/superpowers/plans/2026-06-15-error-handling-pack.md +299 -0
- argus_standards-0.1.0/docs/superpowers/plans/2026-06-15-production-ready.md +489 -0
- argus_standards-0.1.0/docs/superpowers/plans/2026-06-15-type-safety-pack.md +261 -0
- argus_standards-0.1.0/docs/superpowers/plans/2026-06-16-refactoring-pack.md +400 -0
- argus_standards-0.1.0/docs/superpowers/plans/2026-06-16-testing-strategy-pack.md +421 -0
- argus_standards-0.1.0/docs/superpowers/plans/2026-06-20-pypi-publish.md +364 -0
- argus_standards-0.1.0/docs/superpowers/specs/2026-06-14-argus-design.md +395 -0
- argus_standards-0.1.0/docs/superpowers/specs/2026-06-15-documentation-standards-design.md +133 -0
- argus_standards-0.1.0/docs/superpowers/specs/2026-06-15-error-handling-design.md +126 -0
- argus_standards-0.1.0/docs/superpowers/specs/2026-06-15-production-ready-design.md +139 -0
- argus_standards-0.1.0/docs/superpowers/specs/2026-06-15-type-safety-design.md +105 -0
- argus_standards-0.1.0/docs/superpowers/specs/2026-06-16-refactoring-design.md +234 -0
- argus_standards-0.1.0/docs/superpowers/specs/2026-06-16-testing-strategy-design.md +250 -0
- argus_standards-0.1.0/docs/superpowers/specs/2026-06-20-pypi-publish-design.md +142 -0
- argus_standards-0.1.0/opencode.json +4 -0
- argus_standards-0.1.0/pyproject.toml +79 -0
- argus_standards-0.1.0/tests/__init__.py +0 -0
- argus_standards-0.1.0/tests/adapters/__init__.py +0 -0
- argus_standards-0.1.0/tests/adapters/conftest.py +16 -0
- argus_standards-0.1.0/tests/adapters/test_base.py +44 -0
- argus_standards-0.1.0/tests/adapters/test_claude.py +79 -0
- argus_standards-0.1.0/tests/adapters/test_copilot.py +26 -0
- argus_standards-0.1.0/tests/adapters/test_cursor.py +22 -0
- argus_standards-0.1.0/tests/adapters/test_opencode.py +46 -0
- argus_standards-0.1.0/tests/integration/__init__.py +0 -0
- argus_standards-0.1.0/tests/integration/test_generate.py +92 -0
- argus_standards-0.1.0/tests/test_cli.py +157 -0
- argus_standards-0.1.0/tests/test_config.py +31 -0
- argus_standards-0.1.0/tests/test_exceptions.py +15 -0
- argus_standards-0.1.0/tests/test_generator.py +54 -0
- argus_standards-0.1.0/tests/test_loader.py +91 -0
- argus_standards-0.1.0/tests/test_packs.py +40 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
packs:
|
|
2
|
+
- atomic-commit
|
|
3
|
+
- tdd
|
|
4
|
+
- solid
|
|
5
|
+
- code-quality
|
|
6
|
+
- pre-commit
|
|
7
|
+
- type-safety
|
|
8
|
+
- error-handling
|
|
9
|
+
- documentation-standards
|
|
10
|
+
- dependency-injection
|
|
11
|
+
- design-patterns
|
|
12
|
+
- refactoring
|
|
13
|
+
- testing-strategy
|
|
14
|
+
platforms:
|
|
15
|
+
- claude
|
|
16
|
+
- opencode
|
|
17
|
+
- copilot
|
|
18
|
+
- cursor
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<!-- Generated by argus. Run `argus generate` to update. Do not edit manually. -->
|
|
2
|
+
|
|
3
|
+
# Atomic Commit Workflow
|
|
4
|
+
|
|
5
|
+
## Core Rule
|
|
6
|
+
Every logical change is one commit. A commit represents a single, discrete, complete unit of work.
|
|
7
|
+
|
|
8
|
+
## Diagnostic Rule
|
|
9
|
+
If your commit message requires the word "also", that is a second commit.
|
|
10
|
+
|
|
11
|
+
## Commit Cycle (within TDD)
|
|
12
|
+
STOP → RED → GREEN → COMMIT → REFACTOR → COMMIT
|
|
13
|
+
|
|
14
|
+
Each phase produces exactly one commit. Never batch phases.
|
|
15
|
+
|
|
16
|
+
## Commit Message Format (Conventional Commits — conventionalcommits.org)
|
|
17
|
+
`type(scope): description`
|
|
18
|
+
|
|
19
|
+
**Types:**
|
|
20
|
+
- `feat` — new functionality
|
|
21
|
+
- `fix` — bug fix
|
|
22
|
+
- `test` — adding or correcting tests
|
|
23
|
+
- `refactor` — code change that neither fixes a bug nor adds a feature
|
|
24
|
+
- `docs` — documentation only changes
|
|
25
|
+
- `chore` — build process, tooling, dependencies
|
|
26
|
+
|
|
27
|
+
**Examples:**
|
|
28
|
+
```
|
|
29
|
+
test: add failing test for discount calculation
|
|
30
|
+
feat: implement discount calculation
|
|
31
|
+
refactor: extract discount rate to named constant
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Red Flags — Do Not Commit If:
|
|
35
|
+
- The word "also" appears in your commit message
|
|
36
|
+
- The commit contains changes to more than one logical concern
|
|
37
|
+
- The commit mixes test and implementation changes (except the GREEN phase commit)
|
|
38
|
+
- Tests are failing
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<!-- Generated by argus. Run `argus generate` to update. Do not edit manually. -->
|
|
2
|
+
|
|
3
|
+
# Code Quality Metrics
|
|
4
|
+
|
|
5
|
+
All thresholds below are grounded in industry standards, not opinion.
|
|
6
|
+
|
|
7
|
+
## Cyclomatic Complexity
|
|
8
|
+
- **Maximum: 10 per method** (NIST standard; NASA mandates < 10 for mission-critical software)
|
|
9
|
+
- Cyclomatic complexity = number of linearly independent paths through a method
|
|
10
|
+
- Count: +1 for each `if`, `elif`, `for`, `while`, `case`, `catch`, `and`, `or`
|
|
11
|
+
- Methods exceeding 10 are the highest reliability risk (NASA Software Assurance Technology Center)
|
|
12
|
+
|
|
13
|
+
## Method Size
|
|
14
|
+
- **Maximum: 20 lines** (excluding blank lines and comments)
|
|
15
|
+
- Size is the single most predictive code quality metric (industry consensus)
|
|
16
|
+
- If a method exceeds 20 lines, extract a private method with a descriptive name
|
|
17
|
+
|
|
18
|
+
## Class Size
|
|
19
|
+
- **Maximum: 300 lines** (class body only — imports, package declarations, comments excluded)
|
|
20
|
+
- A class exceeding 300 lines almost always violates SRP
|
|
21
|
+
- Extract responsibilities into focused collaborator classes
|
|
22
|
+
|
|
23
|
+
## Parameters
|
|
24
|
+
- **Maximum: 5 parameters per method**
|
|
25
|
+
- If a method needs more than 5 parameters, introduce a parameter object (data class / struct)
|
|
26
|
+
|
|
27
|
+
## Duplication
|
|
28
|
+
- **Zero tolerance for duplicated logic**
|
|
29
|
+
- If the same logic appears in two places, extract it once and reference it
|
|
30
|
+
- "Rule of Three": duplicated once is tolerable; duplicated twice, extract it
|
|
31
|
+
|
|
32
|
+
## Red Flags
|
|
33
|
+
- Method with cyclomatic complexity > 10: refactor before committing
|
|
34
|
+
- Method > 20 lines: extract before committing
|
|
35
|
+
- Class > 300 lines: split before committing
|
|
36
|
+
- Method with > 5 parameters: introduce parameter object before committing
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<!-- Generated by argus. Run `argus generate` to update. Do not edit manually. -->
|
|
2
|
+
|
|
3
|
+
# Dependency Injection
|
|
4
|
+
|
|
5
|
+
## Core Rule
|
|
6
|
+
Depend on abstractions, not concretions. Inject all dependencies via constructor — never
|
|
7
|
+
instantiate collaborators inside a class body.
|
|
8
|
+
|
|
9
|
+
## Constructor Injection
|
|
10
|
+
- Accept dependencies as constructor parameters typed to abstract interfaces (Protocol / ABC)
|
|
11
|
+
- Never call `ConcreteClass()` inside a class body; receive it as a parameter instead
|
|
12
|
+
- Store injected dependencies as instance attributes; do not pass them through method chains
|
|
13
|
+
|
|
14
|
+
## Abstract Boundaries
|
|
15
|
+
- Define a Protocol or ABC for every dependency that has more than one conceivable implementation
|
|
16
|
+
- Application-layer classes depend only on those abstractions, never on concrete imports from
|
|
17
|
+
infrastructure layers (databases, HTTP clients, file system adapters)
|
|
18
|
+
|
|
19
|
+
## Composition Root
|
|
20
|
+
- Wire concrete implementations to abstractions in exactly one place: the entry point (CLI, app
|
|
21
|
+
factory, `main()`)
|
|
22
|
+
- Test code is the only other place that substitutes implementations
|
|
23
|
+
|
|
24
|
+
## Red Flags — Stop and Correct
|
|
25
|
+
- `ConcreteClass()` instantiated inside a class body
|
|
26
|
+
- `import` of a concrete infrastructure class inside a domain or application module
|
|
27
|
+
- Method that accepts a flag/enum to select which implementation to use (use polymorphism instead)
|
|
28
|
+
- Test that patches internals of the class under test rather than injecting a substitute
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<!-- Generated by argus. Run `argus generate` to update. Do not edit manually. -->
|
|
2
|
+
|
|
3
|
+
# Design Patterns
|
|
4
|
+
|
|
5
|
+
## Core Rule
|
|
6
|
+
Apply structural patterns to eliminate `isinstance` checks, `if/elif` type-dispatch chains, and
|
|
7
|
+
duplicated logic. A pattern is the fix, not the decoration.
|
|
8
|
+
|
|
9
|
+
## Pattern Quick Reference
|
|
10
|
+
|
|
11
|
+
| Smell | Pattern | Fix |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| `if type == "A": ... elif type == "B":` | Strategy | One class per behaviour; caller holds the variant |
|
|
14
|
+
| Object creation buried in application logic | Factory / Factory Method | Centralise creation, return abstract type |
|
|
15
|
+
| Many callers updated when state changes | Observer | Callers subscribe; emitter calls `notify()` |
|
|
16
|
+
| Incompatible interfaces must work together | Adapter | Wrap the foreign interface; expose the local protocol |
|
|
17
|
+
| Step sequence fixed, some steps vary | Template Method | Base class owns the skeleton; subclasses override steps |
|
|
18
|
+
| Repeated `if feature_flag:` throughout code | Decorator | Compose behaviours at the composition root |
|
|
19
|
+
|
|
20
|
+
## Usage Rules
|
|
21
|
+
- Apply a pattern only when the smell exists — not speculatively
|
|
22
|
+
- Name the participant classes after the pattern role: `*Strategy`, `*Factory`, `*Observer`, `*Adapter`
|
|
23
|
+
- Keep pattern participants in separate modules; avoid god-module pattern files
|
|
24
|
+
- Prefer composition over inheritance for Strategy and Decorator
|
|
25
|
+
|
|
26
|
+
## Red Flags — Stop and Correct
|
|
27
|
+
- `match`/`switch` dispatching on an object's type or an enum variant
|
|
28
|
+
- `isinstance` check before calling a method (fix the type hierarchy instead)
|
|
29
|
+
- New variant requires editing an existing class body (violates Open/Closed)
|
|
30
|
+
- Pattern introduced with only one concrete implementation (premature — wait for the second)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<!-- Generated by argus. Run `argus generate` to update. Do not edit manually. -->
|
|
2
|
+
|
|
3
|
+
# Documentation Standards
|
|
4
|
+
|
|
5
|
+
## Docstring Requirements
|
|
6
|
+
- All public classes must have a one-line docstring describing their purpose
|
|
7
|
+
- All public methods and functions must have a one-line docstring
|
|
8
|
+
- All Click CLI commands must have a one-line docstring (used by `--help`)
|
|
9
|
+
- `__init__` methods are exempt — document the class instead
|
|
10
|
+
- Private methods (prefixed `_`) are exempt
|
|
11
|
+
|
|
12
|
+
## Docstring Style
|
|
13
|
+
- One sentence, imperative mood: "Load packs from the search path." not "Loads packs..."
|
|
14
|
+
- No restating the function name: `def load():` → not "Load the load."
|
|
15
|
+
- Fits on one line — if you need more, the function probably does too much
|
|
16
|
+
|
|
17
|
+
## Comment Discipline
|
|
18
|
+
- Write comments only when the WHY is non-obvious: a hidden constraint, a workaround,
|
|
19
|
+
a subtle invariant, or behaviour that would surprise a reader
|
|
20
|
+
- Never explain WHAT the code does — well-named identifiers already do that
|
|
21
|
+
- Never reference the current task, ticket, or caller in a comment
|
|
22
|
+
|
|
23
|
+
## Red Flags — Stop and Correct
|
|
24
|
+
- Public class, method, or CLI command with no docstring
|
|
25
|
+
- Docstring that restates the function name or explains what the code does
|
|
26
|
+
- Comment that says what the code does rather than why
|
|
27
|
+
- Multi-line docstring on a function under 10 lines
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<!-- Generated by argus. Run `argus generate` to update. Do not edit manually. -->
|
|
2
|
+
|
|
3
|
+
# Error Handling
|
|
4
|
+
|
|
5
|
+
## Exception Design
|
|
6
|
+
- All custom exceptions inherit from a project-level base exception (e.g. `ArgusError`)
|
|
7
|
+
- Exception names end in `Error`
|
|
8
|
+
- Define the project base exception in the project's root package (e.g. `argus/__init__.py`); define subclass exceptions in the module that raises them, importing the base
|
|
9
|
+
- Exceptions carry a human-readable message sufficient to understand the failure
|
|
10
|
+
|
|
11
|
+
## Raise vs Return
|
|
12
|
+
- Raise for conditions the caller cannot reasonably anticipate or recover from inline
|
|
13
|
+
- Return for expected outcomes the caller must handle (results, None, empty collections)
|
|
14
|
+
- Never use exceptions for control flow
|
|
15
|
+
|
|
16
|
+
## Catching Rules
|
|
17
|
+
- Catch only at system boundaries (CLI entry points, public API surfaces); test fixtures and context managers are exempt
|
|
18
|
+
- Catch only the specific exception types you can handle — never bare `except:` or `except Exception:`
|
|
19
|
+
- Never swallow exceptions silently (`except ...: pass` is always wrong)
|
|
20
|
+
- When catching to re-raise with context, use `raise NewError(...) from original`
|
|
21
|
+
|
|
22
|
+
## Red Flags — Stop and Correct
|
|
23
|
+
- Custom exception inherits directly from `Exception` without a project base class
|
|
24
|
+
- `except:` or `except Exception:` anywhere except a top-level CLI handler
|
|
25
|
+
- `except ...: pass` (silent swallow)
|
|
26
|
+
- try/except inside a function that is not a system boundary
|
|
27
|
+
- Exception name does not end in `Error`
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<!-- Generated by argus. Run `argus generate` to update. Do not edit manually. -->
|
|
2
|
+
|
|
3
|
+
# Pre-Commit Gate
|
|
4
|
+
|
|
5
|
+
Before every `git commit`, all of the following must be true. No exceptions.
|
|
6
|
+
|
|
7
|
+
## Non-Negotiable Gates (🔴 RED LIGHT — do not commit if any fail)
|
|
8
|
+
|
|
9
|
+
1. **All unit tests pass** — run your project's test suite to completion
|
|
10
|
+
2. **Build succeeds** — the project compiles / type-checks without errors
|
|
11
|
+
3. **No lint errors** — linter exits with code 0
|
|
12
|
+
4. **TDD cycle followed** — test existed and was RED before implementation was written
|
|
13
|
+
5. **No SOLID violations** — check the SOLID checklist before committing
|
|
14
|
+
6. **No code quality violations** — no method > 20 lines, no class > 300 lines, complexity ≤ 10
|
|
15
|
+
7. **Commit is atomic** — one logical change, message has no "also", follows conventional format
|
|
16
|
+
|
|
17
|
+
## OK to Commit (🟢 GREEN LIGHT)
|
|
18
|
+
|
|
19
|
+
- All unit tests pass
|
|
20
|
+
- Build succeeds
|
|
21
|
+
- Lint clean
|
|
22
|
+
- TDD cycle was followed for this change
|
|
23
|
+
- Commit message is `type(scope): description` format
|
|
24
|
+
- This commit could be reverted cleanly without affecting other concerns
|
|
25
|
+
|
|
26
|
+
## When In Doubt
|
|
27
|
+
If you are unsure whether a commit is ready: it is not ready. Fix the uncertainty first.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<!-- Generated by argus. Run `argus generate` to update. Do not edit manually. -->
|
|
2
|
+
|
|
3
|
+
# Refactoring
|
|
4
|
+
|
|
5
|
+
## Core Rule
|
|
6
|
+
Refactoring means changing code structure without changing external behavior. Never add new
|
|
7
|
+
functionality in the same commit as a refactor — that is always a separate change.
|
|
8
|
+
|
|
9
|
+
## When to Refactor
|
|
10
|
+
|
|
11
|
+
**Rule of Three:** The first time you write something, just do it. The second time you do
|
|
12
|
+
something similar, note the duplication. The third time, refactor.
|
|
13
|
+
|
|
14
|
+
**Preparatory refactoring:** Before adding a feature, refactor the code to make the feature
|
|
15
|
+
easy to add. "Make the change easy, then make the easy change." — Kent Beck. The refactor
|
|
16
|
+
is one commit; the feature is the next.
|
|
17
|
+
|
|
18
|
+
**TDD Refactor phase:** The REFACTOR step only happens after GREEN (tests passing). Never
|
|
19
|
+
refactor during RED — finish making the test pass first.
|
|
20
|
+
|
|
21
|
+
**Never refactor when:**
|
|
22
|
+
- Tests are failing
|
|
23
|
+
- You are in the RED phase of TDD
|
|
24
|
+
- You are mid-feature (finish the feature first, then clean up)
|
|
25
|
+
|
|
26
|
+
## Code Smells — What Signals Refactoring
|
|
27
|
+
|
|
28
|
+
### Bloaters (things that grew too large)
|
|
29
|
+
- **Long Method** — any method over 20 lines → Extract Method
|
|
30
|
+
- **Large Class** — class over 300 lines → Extract Class
|
|
31
|
+
- **Long Parameter List** — more than 5 parameters → Introduce Parameter Object
|
|
32
|
+
- **Data Clumps** — same group of variables together in multiple places → Extract Class
|
|
33
|
+
- **Primitive Obsession** — using a `str` or `int` where a small class would be clearer → Replace Primitive with Object
|
|
34
|
+
|
|
35
|
+
### Dispensables (things that add no value)
|
|
36
|
+
- **Duplicate Code** — same logic in two or more places → Extract Function
|
|
37
|
+
- **Dead Code** — unreachable or unused code → Delete it
|
|
38
|
+
- **Speculative Generality** — abstractions nobody uses yet → Delete it
|
|
39
|
+
- **Excessive Comments** — a comment explaining what the code does signals unclear code → Rename or extract until the comment is unnecessary
|
|
40
|
+
|
|
41
|
+
### Object-Orientation Abusers
|
|
42
|
+
- **Switch/Match on type** — dispatching behavior based on an object's type → Strategy pattern (see design-patterns pack)
|
|
43
|
+
- **Refused Bequest** — subclass ignores most of what the parent provides → Rework the hierarchy
|
|
44
|
+
|
|
45
|
+
### Couplers (inappropriate dependencies)
|
|
46
|
+
- **Feature Envy** — a method uses another class's data more than its own → Move Method
|
|
47
|
+
- **Message Chains** — `a.b().c().d()` → Extract Method or Hide Delegate
|
|
48
|
+
- **Inappropriate Intimacy** — two classes know too much about each other's internals → Extract Class or Move Method
|
|
49
|
+
|
|
50
|
+
## How to Refactor Safely
|
|
51
|
+
|
|
52
|
+
1. **Confirm tests pass** before starting — if they don't, fix that first
|
|
53
|
+
2. **Make one change** — extract one method, rename one variable, move one class
|
|
54
|
+
3. **Run tests** after each change — if they fail, undo and reconsider
|
|
55
|
+
4. **Commit** when tests pass and the change is complete: `refactor: <what you improved>`
|
|
56
|
+
5. **Never** mix refactoring and new behavior in the same commit
|
|
57
|
+
|
|
58
|
+
## Red Flags — Stop and Correct
|
|
59
|
+
- Refactoring with failing tests
|
|
60
|
+
- Adding a new feature during a refactoring commit
|
|
61
|
+
- Making multiple structural changes before running tests
|
|
62
|
+
- Commit message contains both `feat:` and `refactor:` concerns
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<!-- Generated by argus. Run `argus generate` to update. Do not edit manually. -->
|
|
2
|
+
|
|
3
|
+
# SOLID Principles
|
|
4
|
+
|
|
5
|
+
## Violation Quick Reference
|
|
6
|
+
|
|
7
|
+
The following patterns are mechanically detectable. Stop and correct before committing.
|
|
8
|
+
|
|
9
|
+
| Violation | Principle | Fix |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| God class (300+ lines, 10+ methods) | SRP | Extract focused classes |
|
|
12
|
+
| `switch`/`match` on type | OCP | Strategy pattern or polymorphism |
|
|
13
|
+
| Subclass throws `UnsupportedOperationException` or `NotImplementedError` | LSP | Rework the abstraction hierarchy |
|
|
14
|
+
| Interface with stub methods (`pass`, `TODO`) | ISP | Split into focused interfaces |
|
|
15
|
+
| `new ConcreteClass()` constructed inside a class | DIP | Constructor injection with interface |
|
|
16
|
+
| Method name contains "And" | SRP | Split into two methods |
|
|
17
|
+
| Class imports 10+ packages | SRP | Extract responsibilities |
|
|
18
|
+
| `isinstance`/`is` type checks before method calls | LSP | Fix the type hierarchy |
|
|
19
|
+
|
|
20
|
+
## Principle Summaries
|
|
21
|
+
|
|
22
|
+
**S — Single Responsibility:** A class has one reason to change. If you can describe what it does and the description requires "and", it has two responsibilities.
|
|
23
|
+
|
|
24
|
+
**O — Open/Closed:** Open for extension, closed for modification. Add behaviour by adding code, not by editing existing code. `switch` on type is the canonical violation.
|
|
25
|
+
|
|
26
|
+
**L — Liskov Substitution:** Subtypes must be substitutable for their base type. If a subclass throws for a method the base defines, the hierarchy is wrong.
|
|
27
|
+
|
|
28
|
+
**I — Interface Segregation:** No class should implement methods it doesn't use. If a class has `pass` or `raise NotImplementedError` for interface methods, split the interface.
|
|
29
|
+
|
|
30
|
+
**D — Dependency Inversion:** Depend on abstractions, not concretions. High-level modules should not import concrete low-level classes directly.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<!-- Generated by argus. Run `argus generate` to update. Do not edit manually. -->
|
|
2
|
+
|
|
3
|
+
# Test-Driven Development (TDD)
|
|
4
|
+
|
|
5
|
+
## Core Rule
|
|
6
|
+
Never write or modify production code before there exists at least one failing test for the targeted behaviour. (TDD Guard contract — see: news.lavx.hu/article/tdd-guard)
|
|
7
|
+
|
|
8
|
+
## The Cycle
|
|
9
|
+
STOP → RED → GREEN → COMMIT → REFACTOR → COMMIT
|
|
10
|
+
|
|
11
|
+
1. **STOP** — Understand the requirement. Write nothing yet.
|
|
12
|
+
2. **RED** — Write the smallest failing test that captures the requirement. Run it. Confirm it fails with the right error.
|
|
13
|
+
3. **GREEN** — Write the minimum production code to make the test pass. No extra logic.
|
|
14
|
+
4. **COMMIT** — `git commit -m "feat: implement <behaviour>"`
|
|
15
|
+
5. **REFACTOR** — Improve code quality without changing behaviour. Run tests after each change.
|
|
16
|
+
6. **COMMIT** — `git commit -m "refactor: <what you improved>"`
|
|
17
|
+
|
|
18
|
+
## Coverage Threshold
|
|
19
|
+
- Minimum 80% unit test coverage for all changed code (NIST-aligned standard)
|
|
20
|
+
- 100% coverage required for critical paths (payment processing, authentication, data integrity)
|
|
21
|
+
- Integration tests and E2E tests do NOT count toward the 80% unit coverage threshold
|
|
22
|
+
|
|
23
|
+
## Test Structure (Given-When-Then)
|
|
24
|
+
```
|
|
25
|
+
def test_<behaviour>_when_<condition>():
|
|
26
|
+
# Given
|
|
27
|
+
<setup state>
|
|
28
|
+
# When
|
|
29
|
+
<perform action>
|
|
30
|
+
# Then
|
|
31
|
+
assert <expected outcome>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Red Flags — Stop and Correct
|
|
35
|
+
- Implementation file modified before any test file exists for that behaviour
|
|
36
|
+
- Tests written after implementation to confirm existing code (not to specify behaviour)
|
|
37
|
+
- "I'll add tests later"
|
|
38
|
+
- Test file created after the implementation already passes
|
|
39
|
+
- Batching multiple RED phases before going GREEN
|
|
40
|
+
- Tests that don't assert anything meaningful (trivial pass-through tests)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<!-- Generated by argus. Run `argus generate` to update. Do not edit manually. -->
|
|
2
|
+
|
|
3
|
+
# Testing Strategy
|
|
4
|
+
|
|
5
|
+
## Core Rule
|
|
6
|
+
Test at the lowest layer that gives you confidence. More unit tests than integration tests;
|
|
7
|
+
more integration tests than E2E tests. Inverting this pyramid produces a slow, fragile suite.
|
|
8
|
+
|
|
9
|
+
## The Test Pyramid
|
|
10
|
+
|
|
11
|
+
/\
|
|
12
|
+
/E2E\ ← few, slow, fragile — critical journeys only
|
|
13
|
+
/------\
|
|
14
|
+
/Integr. \ ← moderate — only at system boundaries
|
|
15
|
+
/----------\
|
|
16
|
+
/ Unit Tests \ ← many, fast, isolated — all business logic
|
|
17
|
+
/--------------\
|
|
18
|
+
|
|
19
|
+
The pyramid shape is intentional: unit tests are cheap to write and run in milliseconds;
|
|
20
|
+
E2E tests are expensive, slow, and occasionally flaky. Inverting it is a common mistake
|
|
21
|
+
that teams pay for with hours of CI time and fragile suites.
|
|
22
|
+
|
|
23
|
+
## Unit Tests
|
|
24
|
+
|
|
25
|
+
**Use for:** Business logic, algorithms, transformations, domain rules — anything that can
|
|
26
|
+
be tested without external dependencies.
|
|
27
|
+
**Characteristics:** No I/O, no network, no database. Milliseconds per test. Deterministic.
|
|
28
|
+
**Coverage:** 80% minimum for changed code (see `tdd` pack).
|
|
29
|
+
**Rule:** If it can be tested without any external dependency, test it here.
|
|
30
|
+
|
|
31
|
+
## Integration Tests
|
|
32
|
+
|
|
33
|
+
**Use for:** Code that crosses a system boundary — database queries, external API calls,
|
|
34
|
+
file I/O, message queues, adapter implementations.
|
|
35
|
+
**Characteristics:** Slower than unit tests. Uses real infrastructure or a realistic substitute
|
|
36
|
+
(e.g., an in-memory database, a local test container, a fake HTTP server).
|
|
37
|
+
**Scope is narrow:** Test the integration point itself, not every code path that reaches it.
|
|
38
|
+
One integration test per repository method or adapter — not one per caller.
|
|
39
|
+
**Rule:** If the behavior depends on a real external system, test it here, not in unit tests.
|
|
40
|
+
|
|
41
|
+
## End-to-End Tests
|
|
42
|
+
|
|
43
|
+
**Use for:** The 2–3 critical user journeys that must never break regardless of internal changes.
|
|
44
|
+
**Characteristics:** Slow (seconds to minutes), occasionally flaky due to timing and environment,
|
|
45
|
+
expensive to maintain. Tests the whole deployed system.
|
|
46
|
+
**Rule:** If a unit or integration test can answer the question, use that instead. E2E tests
|
|
47
|
+
are a last resort, not a default.
|
|
48
|
+
**Anti-pattern:** Testing every feature E2E. This produces a suite that takes 20+ minutes to
|
|
49
|
+
run and breaks on every UI or API change.
|
|
50
|
+
|
|
51
|
+
## Test Doubles
|
|
52
|
+
|
|
53
|
+
Use the simplest double that makes the test work:
|
|
54
|
+
|
|
55
|
+
| Double | What it is | When to use |
|
|
56
|
+
|---|---|---|
|
|
57
|
+
| **Fake** | A working simplified implementation | Integration tests — in-memory DB, fake HTTP server |
|
|
58
|
+
| **Stub** | Returns canned values, no behavior | Unit tests — when you need a value from a collaborator |
|
|
59
|
+
| **Mock** | Verifies that specific calls were made | Unit tests — when the interaction itself is the assertion |
|
|
60
|
+
|
|
61
|
+
**Prefer fakes and stubs over mocks.** Mocks assert on HOW code works internally — they
|
|
62
|
+
break when you refactor, even when behavior is correct. Fakes and stubs assert on WHAT
|
|
63
|
+
the code produces — they survive refactoring. See the `dependency-injection` pack for how
|
|
64
|
+
constructor injection makes substitution easy without patching internals.
|
|
65
|
+
|
|
66
|
+
**Never patch internals to test.** If you need to monkey-patch a private method or module
|
|
67
|
+
internals to write a test, that is a signal to inject the dependency instead.
|
|
68
|
+
|
|
69
|
+
## Test Execution Tiers
|
|
70
|
+
|
|
71
|
+
| When | What runs | Why |
|
|
72
|
+
|---|---|---|
|
|
73
|
+
| Before every commit | Unit tests only | Fast, isolated — no reason to skip |
|
|
74
|
+
| Before push | Unit tests + integration tests | Catch boundary failures before teammates see them |
|
|
75
|
+
| CI pipeline | Unit + integration + E2E | Hard gate — failures block merge |
|
|
76
|
+
|
|
77
|
+
## Red Flags — Stop and Correct
|
|
78
|
+
- E2E test written for a feature that could be covered by a unit test
|
|
79
|
+
- Mock asserting on an internal private method call
|
|
80
|
+
- Integration test covering business logic (that belongs in unit tests)
|
|
81
|
+
- Unit test hitting a real database or network
|
|
82
|
+
- Test suite takes more than 5 minutes to run locally (pyramid is inverted)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<!-- Generated by argus. Run `argus generate` to update. Do not edit manually. -->
|
|
2
|
+
|
|
3
|
+
# Type Safety
|
|
4
|
+
|
|
5
|
+
## Core Rule
|
|
6
|
+
Every function and method must have fully annotated parameters and return type.
|
|
7
|
+
Run `mypy argus/` before committing — it must exit 0.
|
|
8
|
+
|
|
9
|
+
## Annotation Requirements
|
|
10
|
+
- All parameters annotated — no bare untyped arguments
|
|
11
|
+
- All return types explicit — including `-> None`
|
|
12
|
+
- Use `X | None` syntax (not `Optional[X]`)
|
|
13
|
+
- Use built-in generics: `list[str]`, `dict[str, int]` (not `List`, `Dict` from `typing`)
|
|
14
|
+
|
|
15
|
+
## `Any` Policy
|
|
16
|
+
`Any` is permitted **only at external data boundaries** (YAML, JSON, CLI input).
|
|
17
|
+
Assign the unstructured value to a typed local variable as soon as its shape is known.
|
|
18
|
+
`Any` in function signatures is never permitted.
|
|
19
|
+
|
|
20
|
+
## Red Flags — Stop and Correct
|
|
21
|
+
- Unannotated function parameter or return type
|
|
22
|
+
- `Any` in a function signature
|
|
23
|
+
- `Optional[X]` instead of `X | None`
|
|
24
|
+
- `List`, `Dict`, `Tuple` imported from `typing` (use built-ins)
|
|
25
|
+
- mypy exits non-zero
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: atomic-commit
|
|
3
|
+
description: Atomic commit discipline and conventional commits format
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- Generated by argus. Run `argus generate` to update. Do not edit manually. -->
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Atomic Commit Checklist
|
|
11
|
+
|
|
12
|
+
- [ ] This commit addresses exactly one logical change
|
|
13
|
+
- [ ] The commit message does NOT contain the word "also"
|
|
14
|
+
- [ ] Commit message follows format: `type(scope): description`
|
|
15
|
+
- [ ] Type is one of: feat, fix, test, refactor, docs, chore
|
|
16
|
+
- [ ] All tests pass
|
|
17
|
+
- [ ] This commit could be reverted without affecting any other concern
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## Atomic Commit Examples
|
|
21
|
+
|
|
22
|
+
### Correct — one feature, three commits (TDD cycle)
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
git commit -m "test: add failing test for user email validation"
|
|
26
|
+
git commit -m "feat: implement user email validation"
|
|
27
|
+
git commit -m "refactor: extract email regex to named constant"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Incorrect — never do this
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Bad: the word "and" signals multiple concerns
|
|
34
|
+
git commit -m "add user validation and fix login bug and update docs"
|
|
35
|
+
|
|
36
|
+
# Bad: mixed concerns
|
|
37
|
+
git commit -m "feat: add discount plus fix rounding error"
|
|
38
|
+
```
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-quality
|
|
3
|
+
description: NIST-backed code metrics — cyclomatic complexity, method size, class size
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- Generated by argus. Run `argus generate` to update. Do not edit manually. -->
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Code Quality Checklist
|
|
11
|
+
|
|
12
|
+
- [ ] No method has cyclomatic complexity > 10 (NIST standard)
|
|
13
|
+
- [ ] No method exceeds 20 lines (excluding blank lines and comments)
|
|
14
|
+
- [ ] No class body exceeds 300 lines
|
|
15
|
+
- [ ] No method has more than 5 parameters
|
|
16
|
+
- [ ] No duplicated logic exists in two or more places
|
|
17
|
+
- [ ] Every extracted method has a name that describes what it does (not how)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dependency-injection
|
|
3
|
+
description: Inject dependencies via constructor; depend on abstractions, not concretions
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- Generated by argus. Run `argus generate` to update. Do not edit manually. -->
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Dependency Injection Checklist
|
|
11
|
+
|
|
12
|
+
- [ ] All dependencies received via constructor parameters, not instantiated internally
|
|
13
|
+
- [ ] Constructor parameter types are abstractions (Protocol / ABC), not concrete classes
|
|
14
|
+
- [ ] No `ConcreteClass()` call inside a class body
|
|
15
|
+
- [ ] No concrete infrastructure imports in domain or application modules
|
|
16
|
+
- [ ] Composition root (entry point) is the only place that wires concrete implementations
|
|
17
|
+
- [ ] Tests inject substitutes via constructor — no patching of internals
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## Dependency Injection Examples
|
|
21
|
+
|
|
22
|
+
### Correct
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from typing import Protocol
|
|
26
|
+
|
|
27
|
+
class PackRepository(Protocol):
|
|
28
|
+
def find(self, name: str) -> Pack: ...
|
|
29
|
+
|
|
30
|
+
class PackLoader:
|
|
31
|
+
"""Load packs using an injected repository."""
|
|
32
|
+
|
|
33
|
+
def __init__(self, repository: PackRepository) -> None:
|
|
34
|
+
self._repository = repository
|
|
35
|
+
|
|
36
|
+
def load(self, name: str) -> Pack:
|
|
37
|
+
"""Return a pack by name from the repository."""
|
|
38
|
+
return self._repository.find(name)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
# Composition root — only place that wires concrete types
|
|
43
|
+
def main() -> None:
|
|
44
|
+
repo = FileSystemPackRepository(root=Path("packs"))
|
|
45
|
+
loader = PackLoader(repository=repo)
|
|
46
|
+
cli(loader=loader)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Incorrect
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
class PackLoader:
|
|
53
|
+
def __init__(self) -> None:
|
|
54
|
+
self._repository = FileSystemPackRepository() # concrete, internal
|
|
55
|
+
|
|
56
|
+
def load(self, name: str) -> Pack:
|
|
57
|
+
return self._repository.find(name)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
# Infrastructure import in domain module
|
|
62
|
+
from argus.infrastructure.fs_repo import FileSystemPackRepository # wrong layer
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Testable with injection
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
class FakeRepository:
|
|
69
|
+
def find(self, name: str) -> Pack:
|
|
70
|
+
return Pack(name=name, content="# stub")
|
|
71
|
+
|
|
72
|
+
def test_loader_returns_pack_by_name():
|
|
73
|
+
# Given
|
|
74
|
+
loader = PackLoader(repository=FakeRepository())
|
|
75
|
+
# When
|
|
76
|
+
result = loader.load("tdd")
|
|
77
|
+
# Then
|
|
78
|
+
assert result.name == "tdd"
|
|
79
|
+
```
|