specky-sdd 2.0.0 → 2.2.2

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 (92) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/README.md +803 -88
  3. package/SECURITY.md +110 -0
  4. package/dist/config.d.ts +12 -0
  5. package/dist/config.d.ts.map +1 -0
  6. package/dist/config.js +66 -0
  7. package/dist/config.js.map +1 -0
  8. package/dist/constants.d.ts +100 -1
  9. package/dist/constants.d.ts.map +1 -1
  10. package/dist/constants.js +112 -1
  11. package/dist/constants.js.map +1 -1
  12. package/dist/index.js +17 -2
  13. package/dist/index.js.map +1 -1
  14. package/dist/schemas/environment.d.ts +12 -37
  15. package/dist/schemas/environment.d.ts.map +1 -1
  16. package/dist/schemas/infrastructure.d.ts +22 -42
  17. package/dist/schemas/infrastructure.d.ts.map +1 -1
  18. package/dist/schemas/input.d.ts +13 -34
  19. package/dist/schemas/input.d.ts.map +1 -1
  20. package/dist/schemas/integration.d.ts +12 -80
  21. package/dist/schemas/integration.d.ts.map +1 -1
  22. package/dist/schemas/pipeline.d.ts +24 -230
  23. package/dist/schemas/pipeline.d.ts.map +1 -1
  24. package/dist/schemas/quality.d.ts +27 -39
  25. package/dist/schemas/quality.d.ts.map +1 -1
  26. package/dist/schemas/quality.js +13 -0
  27. package/dist/schemas/quality.js.map +1 -1
  28. package/dist/schemas/testing.d.ts +23 -0
  29. package/dist/schemas/testing.d.ts.map +1 -0
  30. package/dist/schemas/testing.js +26 -0
  31. package/dist/schemas/testing.js.map +1 -0
  32. package/dist/schemas/transcript.d.ts +18 -40
  33. package/dist/schemas/transcript.d.ts.map +1 -1
  34. package/dist/schemas/utility.d.ts +33 -65
  35. package/dist/schemas/utility.d.ts.map +1 -1
  36. package/dist/schemas/visualization.d.ts +28 -39
  37. package/dist/schemas/visualization.d.ts.map +1 -1
  38. package/dist/services/test-generator.d.ts +61 -0
  39. package/dist/services/test-generator.d.ts.map +1 -0
  40. package/dist/services/test-generator.js +217 -0
  41. package/dist/services/test-generator.js.map +1 -0
  42. package/dist/tools/input.d.ts.map +1 -1
  43. package/dist/tools/input.js +12 -0
  44. package/dist/tools/input.js.map +1 -1
  45. package/dist/tools/integration.d.ts.map +1 -1
  46. package/dist/tools/integration.js +24 -0
  47. package/dist/tools/integration.js.map +1 -1
  48. package/dist/tools/quality.d.ts +3 -2
  49. package/dist/tools/quality.d.ts.map +1 -1
  50. package/dist/tools/quality.js +84 -3
  51. package/dist/tools/quality.js.map +1 -1
  52. package/dist/tools/testing.d.ts +9 -0
  53. package/dist/tools/testing.d.ts.map +1 -0
  54. package/dist/tools/testing.js +130 -0
  55. package/dist/tools/testing.js.map +1 -0
  56. package/dist/tools/utility.d.ts.map +1 -1
  57. package/dist/tools/utility.js +36 -1
  58. package/dist/tools/utility.js.map +1 -1
  59. package/dist/types.d.ts +20 -0
  60. package/dist/types.d.ts.map +1 -1
  61. package/hooks/auto-docs.md +53 -0
  62. package/hooks/auto-test.md +61 -0
  63. package/hooks/changelog.md +74 -0
  64. package/hooks/security-scan.md +72 -0
  65. package/hooks/spec-sync.md +80 -0
  66. package/hooks/srp-validator.md +86 -0
  67. package/package.json +14 -5
  68. package/references/design-patterns.md +434 -0
  69. package/references/ears-notation.md +605 -0
  70. package/references/spec-templates.md +936 -0
  71. package/templates/analysis.md +1 -0
  72. package/templates/api-docs.md +1 -0
  73. package/templates/bugfix.md +1 -0
  74. package/templates/checklist.md +1 -0
  75. package/templates/compliance.md +1 -0
  76. package/templates/constitution.md +1 -0
  77. package/templates/cross-analysis.md +1 -0
  78. package/templates/data-model.md +1 -0
  79. package/templates/design.md +1 -0
  80. package/templates/devcontainer.md +1 -0
  81. package/templates/dockerfile.md +1 -0
  82. package/templates/onboarding.md +1 -0
  83. package/templates/research.md +1 -0
  84. package/templates/runbook.md +1 -0
  85. package/templates/specification.md +1 -0
  86. package/templates/sync-report.md +1 -0
  87. package/templates/tasks.md +3 -2
  88. package/templates/terraform.md +1 -0
  89. package/templates/test-stub.md +34 -0
  90. package/templates/user-stories.md +1 -0
  91. package/templates/verification.md +1 -0
  92. package/templates/work-items.md +1 -0
@@ -0,0 +1,434 @@
1
+ ---
2
+ title: Software Design Patterns Reference for Architecture and Design
3
+ version: 1.0.0
4
+ date: 2026-03-21
5
+ author: Specky
6
+ description: Curated reference of software design patterns used in SDD architecture and design phases
7
+ ---
8
+
9
+ # Software Design Patterns Reference
10
+
11
+ ## Introduction
12
+
13
+ This reference covers the design patterns most relevant to software systems designed with the **Spec-Driven Development (SDD)** methodology. Each pattern includes intent, structure, participants, when to use, when to avoid, and a pseudocode example to anchor the DESIGN.md author.
14
+
15
+ Use this guide during the **Design phase** (`sdd_write_design`) to select appropriate patterns and justify architecture decisions in DESIGN.md.
16
+
17
+ ---
18
+
19
+ ## Creational Patterns
20
+
21
+ ### Factory Method
22
+
23
+ **Intent:** Define an interface for creating an object, but let subclasses decide which class to instantiate.
24
+
25
+ **When to use:**
26
+ - The exact type of object to create is determined at runtime
27
+ - You want to decouple client code from concrete implementations
28
+ - You anticipate adding new product types in the future
29
+
30
+ **When to avoid:**
31
+ - There are only one or two concrete implementations that will never change
32
+ - The overhead of the abstraction outweighs the flexibility benefit
33
+
34
+ **Example:**
35
+ ```typescript
36
+ interface DocumentParser {
37
+ parse(content: string): ParsedDocument;
38
+ }
39
+
40
+ class PdfParser implements DocumentParser { ... }
41
+ class DocxParser implements DocumentParser { ... }
42
+
43
+ function createParser(mimeType: string): DocumentParser {
44
+ if (mimeType === "application/pdf") return new PdfParser();
45
+ if (mimeType === "application/vnd.openxmlformats-officedocument.wordprocessingml.document") return new DocxParser();
46
+ throw new Error(`Unsupported MIME type: ${mimeType}`);
47
+ }
48
+ ```
49
+
50
+ **SDD usage:** `DocumentConverter` service uses this pattern to dispatch to format-specific parsers based on file extension.
51
+
52
+ ---
53
+
54
+ ### Builder
55
+
56
+ **Intent:** Separate the construction of a complex object from its representation so the same construction process can create different representations.
57
+
58
+ **When to use:**
59
+ - Object construction requires many parameters or multi-step initialization
60
+ - You want immutable objects with many optional fields
61
+ - The same construction process should produce different output types
62
+
63
+ **When to avoid:**
64
+ - The object is simple with few fields
65
+ - The object is mutable and doesn't require a staged construction
66
+
67
+ **Example:**
68
+ ```typescript
69
+ class ReportBuilder {
70
+ private title = "";
71
+ private sections: Section[] = [];
72
+
73
+ withTitle(title: string): this { this.title = title; return this; }
74
+ addSection(section: Section): this { this.sections.push(section); return this; }
75
+ build(): Report { return new Report(this.title, this.sections); }
76
+ }
77
+ ```
78
+
79
+ **SDD usage:** `TemplateEngine.renderWithFrontmatter()` acts as a builder — assembles YAML frontmatter + Markdown body into a final document.
80
+
81
+ ---
82
+
83
+ ### Singleton
84
+
85
+ **Intent:** Ensure a class has only one instance and provide a global access point to it.
86
+
87
+ **When to use:**
88
+ - Shared resource management (connection pool, configuration, logger)
89
+ - When instantiating multiple instances would be incorrect or expensive
90
+
91
+ **When to avoid:**
92
+ - Unit testing scenarios where test isolation is critical
93
+ - When state leakage between tests is a concern (prefer dependency injection)
94
+
95
+ **Example:**
96
+ ```typescript
97
+ class ConfigManager {
98
+ private static instance: ConfigManager;
99
+ private constructor() { /* load config */ }
100
+ static getInstance(): ConfigManager {
101
+ if (!ConfigManager.instance) ConfigManager.instance = new ConfigManager();
102
+ return ConfigManager.instance;
103
+ }
104
+ }
105
+ ```
106
+
107
+ ---
108
+
109
+ ## Structural Patterns
110
+
111
+ ### Adapter
112
+
113
+ **Intent:** Convert the interface of a class into another interface clients expect. Lets classes work together that couldn't otherwise because of incompatible interfaces.
114
+
115
+ **When to use:**
116
+ - Integrating a third-party library with an incompatible interface
117
+ - Wrapping legacy code with a modern interface
118
+ - Building a unified interface over multiple external APIs
119
+
120
+ **When to avoid:**
121
+ - The external interface is stable and simple — thin wrappers add unnecessary complexity
122
+
123
+ **Example:**
124
+ ```typescript
125
+ // External SDK with shape we don't control
126
+ class ExternalMcpClient {
127
+ sendRequest(payload: unknown): Promise<unknown> { ... }
128
+ }
129
+
130
+ // Our interface
131
+ interface McpGateway {
132
+ call(tool: string, args: Record<string, unknown>): Promise<McpResult>;
133
+ }
134
+
135
+ class McpClientAdapter implements McpGateway {
136
+ constructor(private client: ExternalMcpClient) {}
137
+ async call(tool: string, args: Record<string, unknown>): Promise<McpResult> {
138
+ const raw = await this.client.sendRequest({ tool, arguments: args });
139
+ return parseMcpResult(raw);
140
+ }
141
+ }
142
+ ```
143
+
144
+ **SDD usage:** MCP-to-MCP routing in `sdd_create_pr` / `sdd_export_work_items` wraps GitHub/Azure DevOps MCP servers through adapter-style wrappers.
145
+
146
+ ---
147
+
148
+ ### Facade
149
+
150
+ **Intent:** Provide a simplified interface to a complex subsystem.
151
+
152
+ **When to use:**
153
+ - You want to provide a simple interface to a complex body of code
154
+ - You want to layer your subsystem and use a facade to define entry points at each level
155
+ - Clients should be shielded from the complexity of subsystems
156
+
157
+ **When to avoid:**
158
+ - The simplified interface still exposes too much complexity
159
+ - You need fine-grained control over the subsystem that the facade would hide
160
+
161
+ **Example:**
162
+ ```typescript
163
+ class SpecPipelineFacade {
164
+ constructor(
165
+ private fileManager: FileManager,
166
+ private stateMachine: StateMachine,
167
+ private templateEngine: TemplateEngine,
168
+ ) {}
169
+
170
+ async runFullPipeline(featureDir: string): Promise<void> {
171
+ await this.stateMachine.advance(featureDir, "init");
172
+ const spec = await this.fileManager.readSpecFile(featureDir, "SPECIFICATION.md");
173
+ const analyzed = await this.analyzeSpec(spec);
174
+ await this.fileManager.writeSpecFile(featureDir, "ANALYSIS.md", analyzed);
175
+ await this.stateMachine.advance(featureDir, "analyze");
176
+ }
177
+ }
178
+ ```
179
+
180
+ **SDD usage:** Each MCP tool is a facade over the service layer. `sdd_auto_pipeline` is the highest-level facade — it wraps the full 10-phase pipeline.
181
+
182
+ ---
183
+
184
+ ### Composite
185
+
186
+ **Intent:** Compose objects into tree structures to represent part-whole hierarchies. Lets clients treat individual objects and compositions uniformly.
187
+
188
+ **When to use:**
189
+ - You need to represent hierarchies (file trees, requirement hierarchies, task trees)
190
+ - You want clients to ignore the difference between leaf and composite objects
191
+
192
+ **Example:**
193
+ ```typescript
194
+ interface Requirement {
195
+ id: string;
196
+ validate(): ValidationResult;
197
+ }
198
+
199
+ class LeafRequirement implements Requirement {
200
+ validate(): ValidationResult { return earsValidator.validate(this.text); }
201
+ }
202
+
203
+ class RequirementGroup implements Requirement {
204
+ private children: Requirement[] = [];
205
+ add(r: Requirement): void { this.children.push(r); }
206
+ validate(): ValidationResult {
207
+ const results = this.children.map((c) => c.validate());
208
+ return aggregateResults(results);
209
+ }
210
+ }
211
+ ```
212
+
213
+ ---
214
+
215
+ ## Behavioral Patterns
216
+
217
+ ### Strategy
218
+
219
+ **Intent:** Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
220
+
221
+ **When to use:**
222
+ - You need different variants of an algorithm (e.g., different export formats, different diagram types)
223
+ - You want to eliminate conditional logic that selects behavior at runtime
224
+
225
+ **When to avoid:**
226
+ - There are only 2 strategies and they rarely change
227
+ - Clients must be aware of different strategies, which complicates client code
228
+
229
+ **Example:**
230
+ ```typescript
231
+ interface ExportStrategy {
232
+ export(tasks: Task[]): string;
233
+ }
234
+
235
+ class GitHubIssuesExporter implements ExportStrategy { ... }
236
+ class AzureBoardsExporter implements ExportStrategy { ... }
237
+ class JiraExporter implements ExportStrategy { ... }
238
+
239
+ class WorkItemExporter {
240
+ constructor(private strategy: ExportStrategy) {}
241
+ export(tasks: Task[]): string { return this.strategy.export(tasks); }
242
+ }
243
+ ```
244
+
245
+ **SDD usage:** `WorkItemExporter` uses Strategy — the `platform` parameter (`github`, `azure_boards`, `jira`) selects the export strategy at runtime.
246
+
247
+ ---
248
+
249
+ ### State Machine
250
+
251
+ **Intent:** Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.
252
+
253
+ **When to use:**
254
+ - An object's behavior depends on its state and must change at runtime
255
+ - Operations have large, multipart conditional statements that depend on the object's state
256
+
257
+ **When to avoid:**
258
+ - The state transitions are trivial and only 2-3 states exist
259
+ - Simpler boolean flags convey the same intent more clearly
260
+
261
+ **Example:**
262
+ ```typescript
263
+ type Phase = "init" | "discover" | "specify" | "design" | "tasks" | "analyze" | "implement" | "verify" | "release";
264
+
265
+ class PipelineStateMachine {
266
+ private state: Phase = "init";
267
+ private readonly transitions: Record<Phase, Phase[]> = {
268
+ init: ["discover"],
269
+ discover: ["specify"],
270
+ specify: ["clarify", "design"],
271
+ clarify: ["design"],
272
+ design: ["tasks"],
273
+ tasks: ["analyze"],
274
+ analyze: ["implement"],
275
+ implement: ["verify"],
276
+ verify: ["release"],
277
+ release: [],
278
+ };
279
+
280
+ advance(to: Phase): void {
281
+ if (!this.transitions[this.state].includes(to)) {
282
+ throw new Error(`Invalid transition: ${this.state} → ${to}`);
283
+ }
284
+ this.state = to;
285
+ }
286
+ }
287
+ ```
288
+
289
+ **SDD usage:** `StateMachine` service implements this pattern to enforce the 10-phase SDD pipeline. Each `sdd_advance_phase` call transitions the state machine.
290
+
291
+ ---
292
+
293
+ ### Observer
294
+
295
+ **Intent:** Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
296
+
297
+ **When to use:**
298
+ - When a change to one object requires changing others, and you don't know how many objects need to change
299
+ - An object should notify other objects without assuming who those objects are
300
+
301
+ **When to avoid:**
302
+ - The number of observers is small and fixed
303
+ - The notification order matters and must be deterministic
304
+
305
+ **Example:**
306
+ ```typescript
307
+ interface PipelineObserver {
308
+ onPhaseChange(from: Phase, to: Phase, featureDir: string): void;
309
+ }
310
+
311
+ class StateMachine {
312
+ private observers: PipelineObserver[] = [];
313
+ subscribe(observer: PipelineObserver): void { this.observers.push(observer); }
314
+ private notify(from: Phase, to: Phase, featureDir: string): void {
315
+ for (const obs of this.observers) obs.onPhaseChange(from, to, featureDir);
316
+ }
317
+ }
318
+ ```
319
+
320
+ ---
321
+
322
+ ### Command
323
+
324
+ **Intent:** Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
325
+
326
+ **When to use:**
327
+ - You want to parameterize objects with an action to perform
328
+ - You need to support undo/redo
329
+ - You need to queue operations or log them for audit
330
+
331
+ **Example:**
332
+ ```typescript
333
+ interface SpecCommand {
334
+ execute(): Promise<void>;
335
+ undo(): Promise<void>;
336
+ }
337
+
338
+ class WriteSpecCommand implements SpecCommand {
339
+ async execute(): Promise<void> { await fileManager.writeSpecFile(...); }
340
+ async undo(): Promise<void> { await fileManager.deleteSpecFile(...); }
341
+ }
342
+ ```
343
+
344
+ ---
345
+
346
+ ## Architectural Patterns
347
+
348
+ ### Layered Architecture (N-Tier)
349
+
350
+ **Intent:** Organize code into horizontal layers where each layer has a specific role and depends only on the layer directly below it.
351
+
352
+ **Layers in Specky:**
353
+ ```
354
+ ┌─────────────────────────────┐
355
+ │ MCP Tools (tools/) │ ← Input validation, output formatting
356
+ ├─────────────────────────────┤
357
+ │ Services (services/) │ ← Business logic, domain rules
358
+ ├─────────────────────────────┤
359
+ │ File Manager │ ← I/O abstraction (single point of disk access)
360
+ └─────────────────────────────┘
361
+ ```
362
+
363
+ **Rules:**
364
+ - Tools MUST NOT access the filesystem directly — always via `FileManager`
365
+ - Services MUST NOT import from tools
366
+ - `FileManager` MUST NOT contain business logic
367
+
368
+ ---
369
+
370
+ ### Repository Pattern
371
+
372
+ **Intent:** Mediate between the domain and data mapping layers using a collection-like interface for accessing domain objects.
373
+
374
+ **When to use:**
375
+ - To abstract data access and make business logic testable without I/O
376
+ - To centralize query logic for a data source
377
+
378
+ **SDD usage:** `FileManager` is a repository for spec artifacts. All reads/writes to `.specs/` go through `FileManager`, enabling easy mocking in tests.
379
+
380
+ ---
381
+
382
+ ### Pipeline / Chain of Responsibility
383
+
384
+ **Intent:** Pass a request along a chain of handlers. Each handler decides either to process the request or pass it to the next handler in the chain.
385
+
386
+ **When to use:**
387
+ - Multiple handlers may process a request
388
+ - You want to decouple sender from receivers
389
+ - The set of handlers can vary dynamically
390
+
391
+ **SDD usage:** `sdd_auto_pipeline` and `sdd_batch_transcripts` implement a pipeline — each phase feeds into the next, and any phase can short-circuit on error.
392
+
393
+ ---
394
+
395
+ ## Anti-Patterns to Avoid
396
+
397
+ | Anti-Pattern | Symptom | Fix |
398
+ |---|---|---|
399
+ | **God Object** | One class/service handles everything | Split into single-responsibility services |
400
+ | **Magic Numbers** | `if (status === 4)` | Use named constants or enums |
401
+ | **Primitive Obsession** | Passing raw strings for typed values (phase names, feature numbers) | Define types or value objects |
402
+ | **Shotgun Surgery** | One change requires modifying many files | Group related behavior using cohesion |
403
+ | **Feature Envy** | Method uses data from another class more than its own | Move method to the class whose data it uses |
404
+ | **Leaky Abstractions** | Implementation details bleed through the API | Strengthen interface boundaries |
405
+ | **Tight Coupling via New** | `new ConcreteService()` inside another service | Inject dependencies via constructor |
406
+
407
+ ---
408
+
409
+ ## Pattern Selection Guide
410
+
411
+ Use this table during the Design phase to select patterns for your DESIGN.md:
412
+
413
+ | Scenario | Recommended Pattern |
414
+ |---|---|
415
+ | Multiple output formats for the same data | Strategy |
416
+ | Complex multi-step object creation | Builder |
417
+ | Wrapping an incompatible external API | Adapter |
418
+ | Simplifying a complex subsystem | Facade |
419
+ | State-dependent behavior with transitions | State Machine |
420
+ | Hierarchical data structures | Composite |
421
+ | Pluggable algorithms | Strategy |
422
+ | Cross-cutting notifications | Observer |
423
+ | Undo/redo, queued operations | Command |
424
+ | Data access abstraction | Repository |
425
+ | Sequential processing with early exit | Pipeline |
426
+
427
+ ---
428
+
429
+ ## References
430
+
431
+ - Gamma, E., Helm, R., Johnson, R., Vlissides, J. (1994). *Design Patterns: Elements of Reusable Object-Oriented Software*. Addison-Wesley.
432
+ - Fowler, M. (2002). *Patterns of Enterprise Application Architecture*. Addison-Wesley.
433
+ - [Refactoring Guru — Design Patterns](https://refactoring.guru/design-patterns) (comprehensive examples in TypeScript)
434
+ - [Microsoft — Cloud Design Patterns](https://learn.microsoft.com/en-us/azure/architecture/patterns/)