@complexthings/superpowers-agent 9.2.1 → 10.0.1

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.
Files changed (193) hide show
  1. package/.agents/skills/claude-handoff/SKILL.md +18 -0
  2. package/.agents/skills/code-review/SKILL.md +89 -0
  3. package/.agents/skills/{improve-codebase-architecture → codebase-design}/DEEPENING.md +1 -1
  4. package/.agents/skills/{improve-codebase-architecture/INTERFACE-DESIGN.md → codebase-design/DESIGN-IT-TWICE.md} +3 -3
  5. package/.agents/skills/codebase-design/SKILL.md +114 -0
  6. package/.agents/skills/design-an-interface/SKILL.md +94 -0
  7. package/.agents/skills/{diagnose → diagnosing-bugs}/SKILL.md +29 -12
  8. package/.agents/skills/{grill-with-docs → domain-modeling}/CONTEXT-FORMAT.md +1 -4
  9. package/.agents/skills/domain-modeling/SKILL.md +74 -0
  10. package/.agents/skills/fable-mode/SKILL.md +95 -0
  11. package/.agents/skills/git-guardrails-claude-code/SKILL.md +95 -0
  12. package/.agents/skills/git-guardrails-claude-code/scripts/block-dangerous-git.sh +25 -0
  13. package/.agents/skills/grill-me/SKILL.md +7 -0
  14. package/.agents/skills/grill-with-docs/SKILL.md +3 -86
  15. package/.agents/skills/grilling/SKILL.md +14 -0
  16. package/.agents/skills/handoff/SKILL.md +2 -1
  17. package/.agents/skills/i-have-adhd/SKILL.md +120 -0
  18. package/.agents/skills/implement/SKILL.md +11 -0
  19. package/.agents/skills/improve-codebase-architecture/HTML-REPORT.md +3 -3
  20. package/.agents/skills/improve-codebase-architecture/SKILL.md +13 -28
  21. package/.agents/skills/loop-me/SKILL.md +32 -0
  22. package/.agents/skills/prototype/SKILL.md +1 -1
  23. package/.agents/skills/qa/SKILL.md +130 -0
  24. package/.agents/skills/request-refactor-plan/SKILL.md +68 -0
  25. package/.agents/skills/research/SKILL.md +12 -0
  26. package/.agents/skills/resolving-merge-conflicts/SKILL.md +14 -0
  27. package/.agents/skills/scaffold-exercises/SKILL.md +106 -0
  28. package/.agents/skills/setup-matt-pocock-skills/SKILL.md +11 -9
  29. package/.agents/skills/setup-matt-pocock-skills/domain.md +2 -2
  30. package/.agents/skills/setup-matt-pocock-skills/issue-tracker-github.md +23 -0
  31. package/.agents/skills/setup-matt-pocock-skills/issue-tracker-gitlab.md +23 -0
  32. package/.agents/skills/setup-matt-pocock-skills/issue-tracker-local.md +11 -0
  33. package/.agents/skills/skill-creator/LICENSE.txt +202 -0
  34. package/.agents/skills/skill-creator/SKILL.md +485 -0
  35. package/.agents/skills/skill-creator/agents/analyzer.md +274 -0
  36. package/.agents/skills/skill-creator/agents/comparator.md +202 -0
  37. package/.agents/skills/skill-creator/agents/grader.md +223 -0
  38. package/.agents/skills/skill-creator/assets/eval_review.html +146 -0
  39. package/.agents/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  40. package/.agents/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  41. package/.agents/skills/skill-creator/references/schemas.md +430 -0
  42. package/.agents/skills/skill-creator/scripts/__init__.py +0 -0
  43. package/.agents/skills/skill-creator/scripts/__pycache__/__init__.cpython-314.pyc +0 -0
  44. package/.agents/skills/skill-creator/scripts/__pycache__/run_eval.cpython-314.pyc +0 -0
  45. package/.agents/skills/skill-creator/scripts/__pycache__/utils.cpython-314.pyc +0 -0
  46. package/.agents/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  47. package/.agents/skills/skill-creator/scripts/generate_report.py +326 -0
  48. package/.agents/skills/skill-creator/scripts/improve_description.py +247 -0
  49. package/.agents/skills/skill-creator/scripts/package_skill.py +136 -0
  50. package/.agents/skills/skill-creator/scripts/quick_validate.py +103 -0
  51. package/.agents/skills/skill-creator/scripts/run_eval.py +310 -0
  52. package/.agents/skills/skill-creator/scripts/run_loop.py +328 -0
  53. package/.agents/skills/skill-creator/scripts/utils.py +47 -0
  54. package/.agents/skills/tdd/SKILL.md +17 -90
  55. package/.agents/skills/tdd/tests.md +16 -0
  56. package/.agents/skills/teach/GLOSSARY-FORMAT.md +35 -0
  57. package/.agents/skills/teach/LEARNING-RECORD-FORMAT.md +46 -0
  58. package/.agents/skills/teach/MISSION-FORMAT.md +31 -0
  59. package/.agents/skills/teach/RESOURCES-FORMAT.md +32 -0
  60. package/.agents/skills/teach/SKILL.md +140 -0
  61. package/.agents/skills/{to-prd → to-spec}/SKILL.md +11 -12
  62. package/.agents/skills/to-tickets/SKILL.md +114 -0
  63. package/.agents/skills/triage/AGENT-BRIEF.md +40 -1
  64. package/.agents/skills/triage/OUT-OF-SCOPE.md +5 -1
  65. package/.agents/skills/triage/SKILL.md +20 -11
  66. package/.agents/skills/wayfinder/SKILL.md +127 -0
  67. package/.agents/skills/writing-great-skills/GLOSSARY.md +201 -0
  68. package/.agents/skills/writing-great-skills/SKILL.md +83 -0
  69. package/.agents/superpowers-agent +103 -222
  70. package/.agents/superpowers-bootstrap.md +3 -3
  71. package/.agents/templates/AGENTS.md.template +11 -34
  72. package/.agents/templates/SUPERPOWERS.md.template +4 -4
  73. package/.github/copilot-instructions.md +23 -99
  74. package/.github/hooks/rtk-rewrite.json +22 -0
  75. package/AGENTS.md +7 -6
  76. package/README.md +53 -174
  77. package/package.json +2 -2
  78. package/skills/collaboration/brainstorming/SKILL.md +39 -139
  79. package/skills/collaboration/brainstorming/skill.json +2 -2
  80. package/skills/collaboration/leveraging-cli-tools/SKILL.md +70 -71
  81. package/skills/collaboration/leveraging-cli-tools/references/copilot-instructions.md +30 -0
  82. package/skills/collaboration/leveraging-cli-tools/scripts/setup-ponytail.sh +185 -0
  83. package/skills/collaboration/leveraging-cli-tools/scripts/setup-rtk.sh +217 -0
  84. package/skills/collaboration/leveraging-cli-tools/skill.json +1 -1
  85. package/skills/meta/create-skill-json/SKILL.md +4 -4
  86. package/skills/meta/create-skill-json/skill.json +1 -1
  87. package/skills/meta/create-skill-json/test-scenarios.md +1 -1
  88. package/skills/setup-skills/SKILL.md +18 -11
  89. package/skills/setup-skills/skill.json +8 -0
  90. package/.agents/skills/caveman/SKILL.md +0 -49
  91. package/.agents/skills/improve-codebase-architecture/LANGUAGE.md +0 -53
  92. package/.agents/skills/karpathy-guidelines/SKILL.md +0 -75
  93. package/.agents/skills/review/SKILL.md +0 -78
  94. package/.agents/skills/tdd/deep-modules.md +0 -33
  95. package/.agents/skills/tdd/interface-design.md +0 -31
  96. package/.agents/skills/tdd/refactoring.md +0 -10
  97. package/.agents/skills/to-issues/SKILL.md +0 -83
  98. package/.agents/skills/zoom-out/SKILL.md +0 -7
  99. package/skills/architecture/ABOUT.md +0 -20
  100. package/skills/architecture/preserving-productive-tensions/SKILL.md +0 -146
  101. package/skills/architecture/preserving-productive-tensions/skill.json +0 -9
  102. package/skills/collaboration/brainstorming/spec-document-reviewer-prompt.md +0 -50
  103. package/skills/collaboration/brainstorming/visual-companion.md +0 -277
  104. package/skills/collaboration/dispatching-parallel-agents/SKILL.md +0 -174
  105. package/skills/collaboration/dispatching-parallel-agents/skill.json +0 -9
  106. package/skills/collaboration/executing-plans/SKILL.md +0 -130
  107. package/skills/collaboration/executing-plans/skill.json +0 -9
  108. package/skills/collaboration/finishing-a-development-branch/SKILL.md +0 -261
  109. package/skills/collaboration/finishing-a-development-branch/skill.json +0 -9
  110. package/skills/collaboration/leveraging-cli-tools/scripts/slim.py +0 -167
  111. package/skills/collaboration/receiving-code-review/SKILL.md +0 -233
  112. package/skills/collaboration/receiving-code-review/skill.json +0 -9
  113. package/skills/collaboration/requesting-code-review/SKILL.md +0 -110
  114. package/skills/collaboration/requesting-code-review/code-reviewer.md +0 -146
  115. package/skills/collaboration/requesting-code-review/skill.json +0 -12
  116. package/skills/collaboration/subagent-driven-development/SKILL.md +0 -255
  117. package/skills/collaboration/subagent-driven-development/code-quality-reviewer-prompt.md +0 -26
  118. package/skills/collaboration/subagent-driven-development/implementer-prompt.md +0 -113
  119. package/skills/collaboration/subagent-driven-development/skill.json +0 -15
  120. package/skills/collaboration/subagent-driven-development/spec-reviewer-prompt.md +0 -61
  121. package/skills/collaboration/using-git-worktrees/SKILL.md +0 -366
  122. package/skills/collaboration/using-git-worktrees/skill.json +0 -9
  123. package/skills/collaboration/writing-plans/SKILL.md +0 -121
  124. package/skills/collaboration/writing-plans/plan-document-reviewer-prompt.md +0 -52
  125. package/skills/collaboration/writing-plans/skill.json +0 -9
  126. package/skills/debugging/defense-in-depth/SKILL.md +0 -380
  127. package/skills/debugging/defense-in-depth/skill.json +0 -9
  128. package/skills/debugging/root-cause-tracing/SKILL.md +0 -361
  129. package/skills/debugging/root-cause-tracing/find-polluter.sh +0 -63
  130. package/skills/debugging/root-cause-tracing/skill.json +0 -12
  131. package/skills/debugging/systematic-debugging/SKILL.md +0 -299
  132. package/skills/debugging/systematic-debugging/condition-based-waiting-example.ts +0 -158
  133. package/skills/debugging/systematic-debugging/condition-based-waiting.md +0 -115
  134. package/skills/debugging/systematic-debugging/defense-in-depth.md +0 -122
  135. package/skills/debugging/systematic-debugging/find-polluter.sh +0 -63
  136. package/skills/debugging/systematic-debugging/root-cause-tracing.md +0 -169
  137. package/skills/debugging/systematic-debugging/skill.json +0 -9
  138. package/skills/debugging/systematic-debugging/test-academic.md +0 -14
  139. package/skills/debugging/systematic-debugging/test-pressure-1.md +0 -58
  140. package/skills/debugging/systematic-debugging/test-pressure-2.md +0 -68
  141. package/skills/debugging/systematic-debugging/test-pressure-3.md +0 -69
  142. package/skills/debugging/verification-before-completion/SKILL.md +0 -143
  143. package/skills/debugging/verification-before-completion/skill.json +0 -9
  144. package/skills/finding-skills/SKILL.md +0 -101
  145. package/skills/finding-skills/skill.json +0 -8
  146. package/skills/meta/create-agents-md/SKILL.md +0 -182
  147. package/skills/meta/create-agents-md/skill.json +0 -9
  148. package/skills/meta/creating-prompts/SKILL.md +0 -349
  149. package/skills/meta/creating-prompts/examples/do-example.md +0 -65
  150. package/skills/meta/creating-prompts/examples/plan-example.md +0 -75
  151. package/skills/meta/creating-prompts/examples/refine-example.md +0 -65
  152. package/skills/meta/creating-prompts/examples/research-example.md +0 -63
  153. package/skills/meta/creating-prompts/scripts/get-next-number.sh +0 -27
  154. package/skills/meta/creating-prompts/skill.json +0 -20
  155. package/skills/meta/creating-prompts/templates/do-template.md +0 -59
  156. package/skills/meta/creating-prompts/templates/plan-template.md +0 -58
  157. package/skills/meta/creating-prompts/templates/refine-template.md +0 -54
  158. package/skills/meta/creating-prompts/templates/research-template.md +0 -56
  159. package/skills/meta/using-superpowers/SKILL.md +0 -108
  160. package/skills/meta/using-superpowers/skill.json +0 -5
  161. package/skills/meta/writing-prompts/SKILL.md +0 -122
  162. package/skills/meta/writing-prompts/references/platforms.md +0 -114
  163. package/skills/meta/writing-prompts/skill.json +0 -9
  164. package/skills/problem-solving/ABOUT.md +0 -40
  165. package/skills/problem-solving/collision-zone-thinking/SKILL.md +0 -188
  166. package/skills/problem-solving/collision-zone-thinking/references/historical-examples.md +0 -393
  167. package/skills/problem-solving/collision-zone-thinking/skill.json +0 -9
  168. package/skills/problem-solving/inversion-exercise/SKILL.md +0 -174
  169. package/skills/problem-solving/inversion-exercise/skill.json +0 -9
  170. package/skills/problem-solving/meta-pattern-recognition/SKILL.md +0 -116
  171. package/skills/problem-solving/meta-pattern-recognition/skill.json +0 -9
  172. package/skills/problem-solving/scale-game/SKILL.md +0 -222
  173. package/skills/problem-solving/scale-game/skill.json +0 -9
  174. package/skills/problem-solving/simplification-cascades/SKILL.md +0 -113
  175. package/skills/problem-solving/simplification-cascades/skill.json +0 -9
  176. package/skills/problem-solving/when-stuck/SKILL.md +0 -69
  177. package/skills/problem-solving/when-stuck/skill.json +0 -9
  178. package/skills/research/ABOUT.md +0 -20
  179. package/skills/research/tracing-knowledge-lineages/SKILL.md +0 -241
  180. package/skills/research/tracing-knowledge-lineages/skill.json +0 -9
  181. package/skills/testing/condition-based-waiting/SKILL.md +0 -359
  182. package/skills/testing/condition-based-waiting/example.ts +0 -158
  183. package/skills/testing/condition-based-waiting/skill.json +0 -12
  184. package/skills/testing/test-driven-development/SKILL.md +0 -434
  185. package/skills/testing/test-driven-development/skill.json +0 -9
  186. package/skills/testing/testing-anti-patterns/SKILL.md +0 -298
  187. package/skills/testing/testing-anti-patterns/skill.json +0 -9
  188. package/skills/testing/verification-before-completion/SKILL.md +0 -246
  189. package/skills/testing/verification-before-completion/skill.json +0 -10
  190. package/skills/using-a-skill/SKILL.md +0 -101
  191. package/skills/using-a-skill/skill.json +0 -8
  192. /package/.agents/skills/{diagnose → diagnosing-bugs}/scripts/hitl-loop.template.sh +0 -0
  193. /package/.agents/skills/{grill-with-docs → domain-modeling}/ADR-FORMAT.md +0 -0
@@ -1,380 +0,0 @@
1
- ---
2
- name: defense-in-depth
3
- description: Validate at every layer data passes through to make bugs impossible, not just fixed; apply when invalid data causes failures deep in execution or a dangerous operation must never run on bad input
4
- metadata:
5
- version: 4.0.0
6
- languages: all
7
- when_to_use: when invalid data causes failures deep in execution, or a dangerous operation must never execute on bad data
8
- ---
9
-
10
- # Defense-in-Depth Validation
11
-
12
- ## Overview
13
-
14
- When you fix a bug caused by invalid data, adding validation at one place feels sufficient. But that single check can be bypassed by different code paths, refactoring, or mocks.
15
-
16
- **Core principle:** Validate at EVERY layer data passes through. Make the bug structurally impossible.
17
-
18
- Single validation: "We fixed the bug"
19
- Multiple layers: "We made the bug impossible"
20
-
21
- ## When to Apply This Skill
22
-
23
- Apply defense-in-depth validation when:
24
-
25
- - A bug was caused by invalid data reaching deep into the execution path
26
- - A value flows through multiple functions, services, or modules before use
27
- - Mocks or test fixtures bypass application-layer validation
28
- - Refactoring risk: a single validation point may not survive future code changes
29
- - A dangerous operation (file deletion, git operations, external API calls) must never execute on bad data
30
-
31
- Do NOT add defensive checks inside internal, same-trust-level function calls between modules you own. Validation at every internal call is over-engineering — it creates noise, maintenance burden, and obscures where actual trust boundaries are.
32
-
33
- ## Two Types of Invalidity
34
-
35
- Before choosing where to validate, classify the failure:
36
-
37
- **Syntactic invalidity** — the structure or format is wrong:
38
- - Empty string when a value is required
39
- - A number outside an allowed range
40
- - A path that does not exist on disk
41
-
42
- **Semantic invalidity** — the structure is valid but the meaning is wrong for this operation:
43
- - A directory that exists but belongs to a different user
44
- - An order being shipped to a restricted country
45
- - A project being initialized inside the source repository
46
-
47
- Syntactic checks happen at the boundary (Layer 1). Semantic checks require domain context and belong deeper in the call stack (Layer 2).
48
-
49
- ## The Six Layers
50
-
51
- ### Layer 1: Entry Point Validation
52
-
53
- Reject syntactically invalid input at the API boundary before it travels anywhere.
54
-
55
- ```typescript
56
- function createProject(name: string, workingDirectory: string) {
57
- if (!workingDirectory || workingDirectory.trim() === '') {
58
- throw new Error('workingDirectory cannot be empty');
59
- }
60
- if (!existsSync(workingDirectory)) {
61
- throw new Error(`workingDirectory does not exist: ${workingDirectory}`);
62
- }
63
- if (!statSync(workingDirectory).isDirectory()) {
64
- throw new Error(`workingDirectory is not a directory: ${workingDirectory}`);
65
- }
66
- // ... proceed
67
- }
68
- ```
69
-
70
- **Allowlist, not denylist.** Define what IS valid and reject everything else. Denylists are trivially bypassed via encoding tricks, alternate representations, and case variations. They also produce false positives — blocking `'` breaks `O'Brian`.
71
-
72
- **Canonicalize before validating.** Especially for paths:
73
-
74
- ```typescript
75
- // WRONG — bypassed by /etc/./passwd or /etc/../etc/passwd
76
- if (input === '/etc/passwd') throw ...
77
-
78
- // RIGHT — normalize first, then check
79
- const normalized = path.resolve(input);
80
- if (normalized.startsWith('/etc')) throw ...
81
- ```
82
-
83
- **Important:** Valid input is not necessarily safe input. A valid email address can carry an XSS payload. A valid URL can carry a SQL injection. Validation confirms shape and structure; it does not prevent exploitation. Use parameterized queries and output encoding for that — those are orthogonal concerns.
84
-
85
- **Regex pitfalls:** Always anchor patterns (`^` and `$`) — `/\d{4}/` matches `abc1234def`. Avoid catastrophic backtracking in complex patterns; test with adversarial inputs.
86
-
87
- ### Layer 2: Business Logic Validation
88
-
89
- Validate semantic correctness inside the domain. Does this data make sense for THIS operation in THIS context?
90
-
91
- ```typescript
92
- function initializeWorkspace(projectDir: string, sessionId: string) {
93
- if (!projectDir) {
94
- throw new Error('projectDir required for workspace initialization');
95
- }
96
- // Semantic check: project dir must not be inside the agent's own source tree
97
- if (projectDir.startsWith(SOURCE_ROOT)) {
98
- throw new Error(`projectDir cannot be inside source: ${projectDir}`);
99
- }
100
- // ... proceed
101
- }
102
- ```
103
-
104
- Domain validation belongs inside the domain object, not only at the application service layer. Putting validation only in services leaks domain knowledge upward and allows domain objects to be constructed in invalid states.
105
-
106
- **Use the Execute/CanExecute pattern for domain operations:**
107
-
108
- ```typescript
109
- class Delivery {
110
- canDeliver(): { ok: true } | { ok: false; errors: string[] } {
111
- const errors: string[] = [];
112
- if (!this.destination) errors.push('destination required');
113
- if (this.weight > MAX_WEIGHT) errors.push('weight exceeds limit');
114
- return errors.length ? { ok: false, errors } : { ok: true };
115
- }
116
-
117
- deliver(): void {
118
- const check = this.canDeliver();
119
- if (!check.ok) throw new Error(check.errors.join('; '));
120
- // ... perform delivery
121
- }
122
- }
123
- ```
124
-
125
- `canDeliver` is a query (safe to call, collects all errors). `deliver` enforces the contract internally. This follows Command-Query Separation — callers can check before acting without putting the object into an invalid state.
126
-
127
- ### Layer 3: Persistence Constraints
128
-
129
- Database schema is the last line of defense when application layers fail. It is not a substitute for earlier layers — it is the backstop.
130
-
131
- ```sql
132
- CREATE TABLE projects (
133
- id UUID PRIMARY KEY,
134
- name TEXT NOT NULL CHECK (length(name) > 0),
135
- directory TEXT NOT NULL,
136
- owner_id UUID NOT NULL REFERENCES users(id),
137
- created_at TIMESTAMPTZ NOT NULL DEFAULT now()
138
- );
139
-
140
- CREATE UNIQUE INDEX projects_directory_idx ON projects (directory);
141
- ```
142
-
143
- If application code sends a NULL that should never be null, the database will reject it. This surfaces bugs that would otherwise produce silent data corruption.
144
-
145
- For ORMs, define constraints at both the schema and model level:
146
-
147
- ```python
148
- class Project(Base):
149
- __tablename__ = "projects"
150
- id = Column(UUID, primary_key=True, default=uuid4)
151
- name = Column(Text, nullable=False)
152
- directory = Column(Text, nullable=False, unique=True)
153
- owner_id = Column(UUID, ForeignKey("users.id"), nullable=False)
154
-
155
- @validates("name")
156
- def validate_name(self, key, value):
157
- if not value or not value.strip():
158
- raise ValueError("Project name cannot be empty")
159
- return value
160
- ```
161
-
162
- ### Layer 4: Environment Guards
163
-
164
- Prevent dangerous operations in specific runtime contexts — most critically, tests. Tests can bypass ALL upstream validation via mocks, fixtures, and direct instantiation. A guard inside the dangerous operation itself cannot be bypassed regardless of how the operation was invoked.
165
-
166
- ```typescript
167
- async function gitInit(directory: string) {
168
- if (process.env.NODE_ENV === 'test') {
169
- const normalized = normalize(resolve(directory));
170
- const tmpDir = normalize(resolve(tmpdir()));
171
-
172
- if (!normalized.startsWith(tmpDir)) {
173
- throw new Error(
174
- `Refusing git init outside temp dir during tests: ${directory}`
175
- );
176
- }
177
- }
178
- // ... proceed
179
- }
180
- ```
181
-
182
- ### Layer 5: Test Boundary Guards
183
-
184
- Test fixtures must assert their own preconditions before the test body runs. This makes the fixture responsible for its own correctness rather than silently propagating bad state.
185
-
186
- ```typescript
187
- function createTestProject(dir?: string): Project {
188
- const projectDir = dir ?? mkdtempSync(join(tmpdir(), 'test-project-'));
189
-
190
- // Assert preconditions before proceeding
191
- if (!projectDir || projectDir.trim() === '') {
192
- throw new Error('Test fixture: projectDir cannot be empty');
193
- }
194
- if (!projectDir.startsWith(tmpdir())) {
195
- throw new Error(`Test fixture: projectDir must be in tmpdir, got: ${projectDir}`);
196
- }
197
-
198
- return Project.create('test-project', projectDir);
199
- }
200
- ```
201
-
202
- ### Layer 6: Debug Instrumentation
203
-
204
- Capture context for forensics when other layers fail. Structured logging with the call stack allows you to trace exactly which code path produced a bad value.
205
-
206
- ```typescript
207
- async function gitInit(directory: string) {
208
- logger.debug('About to git init', {
209
- directory,
210
- cwd: process.cwd(),
211
- stack: new Error().stack,
212
- });
213
- // ... proceed
214
- }
215
- ```
216
-
217
- This layer does not prevent bugs — it makes them diagnosable in production and staging where you cannot attach a debugger.
218
-
219
- ## Parse, Don't Validate
220
-
221
- The most robust form of defense-in-depth uses the type system to make invalid states unrepresentable. Parse at the boundary; pass a typed object everywhere else.
222
-
223
- ```typescript
224
- // Instead of passing `string` everywhere and re-validating:
225
- class WorkingDirectory {
226
- private constructor(public readonly value: string) {}
227
-
228
- static parse(input: string): WorkingDirectory {
229
- if (!input || input.trim() === '') {
230
- throw new Error('WorkingDirectory cannot be empty');
231
- }
232
- const resolved = resolve(input);
233
- if (!existsSync(resolved)) {
234
- throw new Error(`WorkingDirectory does not exist: ${resolved}`);
235
- }
236
- if (!statSync(resolved).isDirectory()) {
237
- throw new Error(`WorkingDirectory is not a directory: ${resolved}`);
238
- }
239
- return new WorkingDirectory(resolved);
240
- }
241
- }
242
-
243
- // Downstream functions get Layer 1 for free — no re-checking needed
244
- function initializeWorkspace(dir: WorkingDirectory, sessionId: string) {
245
- // dir.value is already known valid and canonicalized
246
- }
247
- ```
248
-
249
- In Python, use dataclasses with `__post_init__` or Pydantic validators:
250
-
251
- ```python
252
- from pydantic import BaseModel, field_validator
253
- from pathlib import Path
254
-
255
- class WorkingDirectory(BaseModel):
256
- value: Path
257
-
258
- @field_validator("value")
259
- @classmethod
260
- def must_be_existing_directory(cls, v: Path) -> Path:
261
- if not v.exists():
262
- raise ValueError(f"Directory does not exist: {v}")
263
- if not v.is_dir():
264
- raise ValueError(f"Path is not a directory: {v}")
265
- return v.resolve()
266
-
267
- # Now downstream code receives a WorkingDirectory with guaranteed invariants
268
- ```
269
-
270
- This pattern **complements** (not replaces) the other layers — business logic, environment guards, and DB constraints still apply.
271
-
272
- ## Assertions vs Runtime Validation
273
-
274
- Two complementary checks serve different purposes:
275
-
276
- **Assertions** — for programmer errors (contract violations between modules you control):
277
- ```python
278
- def calculate_discount(price: float, pct: float) -> float:
279
- assert price > 0, f"price must be positive: {price}"
280
- assert 0 <= pct <= 1, f"pct must be in [0, 1]: {pct}"
281
- return price * (1 - pct)
282
- ```
283
- Assertions crash loudly in development so bugs never silently propagate. They may be disabled in production (`python -O`) — use them for invariants you own, not external inputs.
284
-
285
- **Runtime validation** — for user/environment errors (external data you don't control):
286
- ```python
287
- def handle_discount_request(request_body: dict) -> float:
288
- price = request_body.get("price")
289
- pct = request_body.get("discount_pct")
290
- if price is None or not isinstance(price, (int, float)):
291
- raise ValidationError("price is required and must be a number")
292
- if price <= 0:
293
- raise ValidationError(f"price must be positive, got {price}")
294
- # ...
295
- ```
296
- Runtime validation surfaces actionable error messages to users. It must stay active in production and must never crash the process unexpectedly.
297
-
298
- ## Collecting vs Failing Fast
299
-
300
- **Fail fast** — for single operations: reject and return immediately when validation fails. Do not partially process corrupt data.
301
-
302
- ```python
303
- # Guard clause pattern: fail fast, clear intent
304
- def process_order(order):
305
- if order is None:
306
- raise ValueError("order cannot be None")
307
- if order.amount <= 0:
308
- raise ValueError(f"amount must be positive, got {order.amount}")
309
- # only reach here with valid data
310
- _do_processing(order)
311
- ```
312
-
313
- **Collect errors** — for user-facing forms: gather all validation failures before returning so users fix everything at once, not one error at a time.
314
-
315
- ```typescript
316
- type ValidationResult<T> =
317
- | { ok: true; value: T }
318
- | { ok: false; errors: string[] };
319
-
320
- function validateOrder(input: unknown): ValidationResult<Order> {
321
- const errors: string[] = [];
322
- if (!input.customerId) errors.push('customerId required');
323
- if (!input.items?.length) errors.push('order must have at least one item');
324
- if (input.total <= 0) errors.push('total must be positive');
325
- if (errors.length) return { ok: false, errors };
326
- return { ok: true, value: input as Order };
327
- }
328
- ```
329
-
330
- Use **exceptions** for unexpected failures (programmer errors, infrastructure down).
331
- Use **result objects** for expected validation failures (user input, external data).
332
-
333
- ## Applying the Pattern
334
-
335
- When you find a bug caused by invalid data:
336
-
337
- 1. **Trace the data flow** — Where does the bad value originate? What is its full path through the system?
338
- 2. **Map all checkpoints** — List every function the value passes through
339
- 3. **Classify the invalidity** — Syntactic (structure) or semantic (meaning)?
340
- 4. **Add validation at each applicable layer** — Entry, business, persistence, environment, test fixture, debug logging
341
- 5. **Test each layer independently** — Intentionally bypass Layer 1 and verify Layer 2 still catches it. Verify DB constraints reject invalid rows even when application code sends them.
342
-
343
- ## Anti-Patterns
344
-
345
- | Anti-pattern | Problem | Fix |
346
- |---|---|---|
347
- | Single-point validation | One bypass reaches all downstream code | Add checks at each applicable layer |
348
- | IsValid method without enforcement | Requires object to enter invalid state; caller can ignore the check | Use Execute/CanExecute: command enforces its own preconditions |
349
- | Exceptions for expected failures | User sees a generic error, not specific messages | Return result objects, collect all errors |
350
- | Denylist validation | Trivially bypassed via encoding or alternate representations | Allowlist known-valid patterns instead |
351
- | Client-only validation | JavaScript can be disabled or bypassed | Always re-validate on the server |
352
- | No persistence constraints | Silent data corruption when app layers fail | Add NOT NULL, CHECK, UNIQUE constraints |
353
- | Missing environment guards in tests | Dangerous operations run against real state | Guard in the operation itself, not just callers |
354
- | Trusting internal sources | Internal microservices, message queues, and partner APIs also produce bad data | Treat all external data as untrusted regardless of source |
355
- | Validating format as a security control | A valid email can carry XSS; a valid URL can carry SQLi | Use output encoding and parameterized queries for security; validation is a separate concern |
356
- | Over-validating same-trust internal calls | Noise, maintenance burden, obscured trust boundaries | Validate at trust boundaries only — public APIs, service interfaces, and entry points |
357
-
358
- ## Real-World Example
359
-
360
- **Bug:** Empty `projectDir` caused `git init` to run in the source code directory.
361
-
362
- **Data flow:**
363
- 1. Test setup → empty string (missing fixture initialization)
364
- 2. `Project.create(name, '')`
365
- 3. `WorkspaceManager.createWorkspace('')`
366
- 4. `git init` runs in `process.cwd()` — the source repository
367
-
368
- **Six layers added:**
369
- - Layer 1: `Project.create()` validates not empty, exists, is a directory, is writable
370
- - Layer 2: `WorkspaceManager` validates `projectDir` is not empty and not inside source root
371
- - Layer 3: DB schema `NOT NULL` on directory column
372
- - Layer 4: `WorktreeManager` refuses `git init` outside `tmpdir()` when `NODE_ENV=test`
373
- - Layer 5: Test fixture asserts `projectDir` is non-empty and inside `tmpdir()` before returning
374
- - Layer 6: Stack trace logging immediately before `git init`
375
-
376
- **Result:** All 1847 tests passed. The bug was structurally impossible to reproduce — each layer independently prevented it.
377
-
378
- All six layers were necessary. Different code paths bypassed entry validation. Mocks bypassed business logic checks. Edge cases on different platforms needed environment guards. Without the test fixture guard, missing initialization silently propagated.
379
-
380
- **Don't stop at one validation point.** Add checks at every layer.
@@ -1,9 +0,0 @@
1
- {
2
- "version": "1.1.0",
3
- "name": "superpowers:debugging/defense-in-depth",
4
- "title": "Defense-in-Depth Validation",
5
- "aliases": [
6
- "defense-in-depth",
7
- "debugging/defense-in-depth"
8
- ]
9
- }