@martinffx/atelier 1.2.2 → 1.4.0
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.
- package/.codex-plugin/plugin.json +27 -0
- package/CHANGELOG.md +33 -0
- package/README.md +51 -59
- package/agents/architect.md +1 -1
- package/agents/oracle.md +1 -1
- package/dist/atelier.js +14171 -12949
- package/package.json +6 -1
- package/skills/code-commit/SKILL.md +77 -77
- package/skills/code-handoff/SKILL.md +1 -1
- package/skills/code-pull-request/SKILL.md +36 -0
- package/skills/code-pull-request/references/common.md +76 -0
- package/skills/code-pull-request/references/create-pr.md +122 -0
- package/skills/code-pull-request/references/default-template.md +25 -0
- package/skills/code-pull-request/references/leave-comment.md +53 -0
- package/skills/code-pull-request/references/merge-pr.md +94 -0
- package/skills/code-pull-request/references/read-comments.md +61 -0
- package/skills/code-pull-request/references/respond-to-comment.md +66 -0
- package/skills/code-review/SKILL.md +21 -12
- package/skills/code-review/references/reviewers.md +142 -41
- package/skills/code-review/references/rq.md +48 -32
- package/skills/oracle-debug/SKILL.md +218 -0
- package/skills/{code-debug → oracle-debug}/references/bug-patterns.md +3 -3
- package/skills/oracle-domain-modelling/SKILL.md +84 -0
- package/skills/oracle-grill-me/SKILL.md +20 -0
- package/skills/spec-brainstorm/SKILL.md +91 -71
- package/skills/spec-finish/SKILL.md +20 -14
- package/skills/spec-implement/SKILL.md +3 -2
- package/skills/spec-orchestrator/SKILL.md +4 -15
- package/skills/spec-plan/SKILL.md +1 -1
- package/agents/scout.md +0 -40
- package/skills/code-commit/references/pr-workflow.md +0 -145
- package/skills/code-debug/SKILL.md +0 -199
- package/skills/code-docs/SKILL.md +0 -171
- package/skills/code-docs/references/api-docs.md +0 -116
- package/skills/code-docs/references/changelog.md +0 -64
- package/skills/code-docs/references/readme-structure.md +0 -94
- package/skills/oracle-architect/SKILL.md +0 -327
- package/skills/oracle-architect/references/api-design.md +0 -345
- package/skills/oracle-architect/references/data-modeling.md +0 -318
- package/skills/oracle-architect/references/ddd-patterns.md +0 -348
- package/skills/oracle-architect/references/interface-design.md +0 -203
- package/skills/oracle-challenge/SKILL.md +0 -126
- package/skills/oracle-doubt/SKILL.md +0 -252
- package/skills/oracle-doubt/references/adversarial-prompt.md +0 -47
- package/skills/oracle-grillme/SKILL.md +0 -160
- package/skills/oracle-security/SKILL.md +0 -225
- package/skills/oracle-security/references/security-patterns.md +0 -236
- package/skills/oracle-security/references/threat-modeling-guide.md +0 -137
- package/skills/oracle-testing/SKILL.md +0 -237
- package/skills/oracle-testing/references/anti-patterns.md +0 -618
- package/skills/oracle-testing/references/boundaries.md +0 -579
- package/skills/oracle-testing/references/core-testing.md +0 -691
- package/skills/oracle-testing/references/edge-testing.md +0 -794
- package/skills/oracle-testing/references/stub-driven-tdd.md +0 -322
- package/skills/oracle-thinkdeep/SKILL.md +0 -164
- /package/skills/{code-debug → oracle-debug}/references/language-tools.md +0 -0
- /package/skills/{code-debug → oracle-debug}/references/techniques.md +0 -0
- /package/skills/{oracle-grillme → oracle-domain-modelling}/references/ADR-FORMAT.md +0 -0
- /package/skills/{oracle-grillme → oracle-domain-modelling}/references/CONTEXT-FORMAT.md +0 -0
|
@@ -1,348 +0,0 @@
|
|
|
1
|
-
# DDD Patterns Reference
|
|
2
|
-
|
|
3
|
-
Domain-Driven Design patterns for modeling business domains and enforcing consistency.
|
|
4
|
-
|
|
5
|
-
## Aggregates
|
|
6
|
-
|
|
7
|
-
**Definition:** Cluster of domain objects treated as a single unit for data changes
|
|
8
|
-
|
|
9
|
-
**Rules:**
|
|
10
|
-
- One entity is the aggregate root (e.g., Order)
|
|
11
|
-
- External references only to the root
|
|
12
|
-
- Changes go through the root
|
|
13
|
-
- Root enforces invariants
|
|
14
|
-
|
|
15
|
-
**Example:**
|
|
16
|
-
```typescript
|
|
17
|
-
class Order { // Aggregate root
|
|
18
|
-
private items: OrderItem[]; // Part of aggregate
|
|
19
|
-
|
|
20
|
-
addItem(item: OrderItem): Result<Order> {
|
|
21
|
-
// Root enforces invariants
|
|
22
|
-
if (this.items.length >= 100) {
|
|
23
|
-
return Err('Order cannot have more than 100 items');
|
|
24
|
-
}
|
|
25
|
-
this.items.push(item);
|
|
26
|
-
return Ok(this);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
**Business Rules Enforcement:**
|
|
32
|
-
```typescript
|
|
33
|
-
class Order {
|
|
34
|
-
private status: OrderStatus;
|
|
35
|
-
private appliedDiscounts: Discount[] = [];
|
|
36
|
-
|
|
37
|
-
applyDiscount(discount: Discount): Result<void> {
|
|
38
|
-
// Business rule: can't apply discount to shipped orders
|
|
39
|
-
if (this.status === 'shipped') {
|
|
40
|
-
return Err('Cannot discount shipped orders');
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Business rule: no duplicate discounts
|
|
44
|
-
if (this.appliedDiscounts.some(d => d.id === discount.id)) {
|
|
45
|
-
return Err('Discount already applied');
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
this.appliedDiscounts.push(discount);
|
|
49
|
-
return Ok(undefined);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Value Objects
|
|
55
|
-
|
|
56
|
-
**Definition:** Immutable objects defined by their attributes, not identity
|
|
57
|
-
|
|
58
|
-
**Characteristics:**
|
|
59
|
-
- No identity (equality by value)
|
|
60
|
-
- Immutable
|
|
61
|
-
- Self-validating
|
|
62
|
-
- Encapsulate validation logic
|
|
63
|
-
|
|
64
|
-
**Example:**
|
|
65
|
-
```typescript
|
|
66
|
-
class Money {
|
|
67
|
-
constructor(
|
|
68
|
-
public readonly amount: number,
|
|
69
|
-
public readonly currency: string
|
|
70
|
-
) {
|
|
71
|
-
if (amount < 0) throw new Error('Amount cannot be negative');
|
|
72
|
-
if (!['USD', 'EUR', 'GBP'].includes(currency)) {
|
|
73
|
-
throw new Error('Unsupported currency');
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
add(other: Money): Money {
|
|
78
|
-
if (this.currency !== other.currency) {
|
|
79
|
-
throw new Error('Cannot add different currencies');
|
|
80
|
-
}
|
|
81
|
-
return new Money(this.amount + other.amount, this.currency);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
equals(other: Money): boolean {
|
|
85
|
-
return this.amount === other.amount && this.currency === other.currency;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
static fromCents(cents: number, currency: string): Money {
|
|
89
|
-
return new Money(cents / 100, currency);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
**More Examples:**
|
|
95
|
-
```typescript
|
|
96
|
-
// Email VO with validation
|
|
97
|
-
class Email {
|
|
98
|
-
readonly value: string;
|
|
99
|
-
|
|
100
|
-
constructor(value: string) {
|
|
101
|
-
if (!this.isValid(value)) throw new Error('Invalid email');
|
|
102
|
-
this.value = value.toLowerCase();
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
private isValid(email: string): boolean {
|
|
106
|
-
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
equals(other: Email): boolean {
|
|
110
|
-
return this.value === other.value;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Address VO with composition
|
|
115
|
-
class Address {
|
|
116
|
-
constructor(
|
|
117
|
-
public readonly street: string,
|
|
118
|
-
public readonly city: string,
|
|
119
|
-
public readonly zipCode: string,
|
|
120
|
-
public readonly country: string
|
|
121
|
-
) {}
|
|
122
|
-
|
|
123
|
-
equals(other: Address): boolean {
|
|
124
|
-
return this.street === other.street &&
|
|
125
|
-
this.city === other.city &&
|
|
126
|
-
this.zipCode === other.zipCode &&
|
|
127
|
-
this.country === other.country;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
## Domain Events
|
|
133
|
-
|
|
134
|
-
**Definition:** Something that happened in the domain that domain experts care about
|
|
135
|
-
|
|
136
|
-
**Characteristics:**
|
|
137
|
-
- Immutable facts about what happened
|
|
138
|
-
- Named in past tense
|
|
139
|
-
- Include context about what changed
|
|
140
|
-
- Timestamped
|
|
141
|
-
|
|
142
|
-
**Example:**
|
|
143
|
-
```typescript
|
|
144
|
-
class OrderPlaced {
|
|
145
|
-
constructor(
|
|
146
|
-
public readonly orderId: string,
|
|
147
|
-
public readonly customerId: string,
|
|
148
|
-
public readonly total: number,
|
|
149
|
-
public readonly occurredAt: Date
|
|
150
|
-
) {}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Service publishes events
|
|
154
|
-
const order = await orderService.createOrder(request);
|
|
155
|
-
await eventBus.publish(
|
|
156
|
-
new OrderPlaced(order.id, order.customerId, order.total, new Date())
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
// Events cross bounded contexts
|
|
160
|
-
class OrderShipped {
|
|
161
|
-
constructor(
|
|
162
|
-
public readonly orderId: string,
|
|
163
|
-
public readonly shippingAddress: Address,
|
|
164
|
-
public readonly trackingNumber: string,
|
|
165
|
-
public readonly occurredAt: Date
|
|
166
|
-
) {}
|
|
167
|
-
}
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
**Event-Driven Workflows:**
|
|
171
|
-
```typescript
|
|
172
|
-
// Order context publishes OrderPlaced
|
|
173
|
-
// Inventory context listens and consumes
|
|
174
|
-
class InventoryService {
|
|
175
|
-
async handleOrderPlaced(event: OrderPlaced): Promise<Result<void>> {
|
|
176
|
-
// Reserve inventory in response to order
|
|
177
|
-
const result = await this.inventoryRepo.reserve(event.orderId);
|
|
178
|
-
if (!result.ok) {
|
|
179
|
-
// Handle reservation failure
|
|
180
|
-
await this.eventBus.publish(new InventoryReservationFailed(event.orderId));
|
|
181
|
-
}
|
|
182
|
-
return result;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
## Bounded Contexts
|
|
188
|
-
|
|
189
|
-
**Definition:** Explicit boundaries where a domain model applies
|
|
190
|
-
|
|
191
|
-
**Guidelines:**
|
|
192
|
-
- Each context has its own ubiquitous language
|
|
193
|
-
- Contexts communicate via well-defined contracts
|
|
194
|
-
- Same concept may have different models in different contexts
|
|
195
|
-
- Example: "Customer" in Sales context vs "User" in Auth context
|
|
196
|
-
|
|
197
|
-
**Context Composition:**
|
|
198
|
-
```
|
|
199
|
-
Bounded Context: "Orders"
|
|
200
|
-
┌────────────────────────────────────────────────────────────────┐
|
|
201
|
-
│ │
|
|
202
|
-
│ Effectful Edge Functional Core │
|
|
203
|
-
│ ┌──────────────────┐ ┌──────────────────┐ │
|
|
204
|
-
│ │ Router │────────────▶│ Service │ │
|
|
205
|
-
│ │ POST /orders │ │ OrderService │ │
|
|
206
|
-
│ └──────────────────┘ └────────┬─────────┘ │
|
|
207
|
-
│ │ │
|
|
208
|
-
│ ┌──────────────────┐ ┌────────▼─────────┐ │
|
|
209
|
-
│ │ Repository │◀────────────│ Aggregate │ │
|
|
210
|
-
│ │ OrderRepository │ │ Order (root) │ │
|
|
211
|
-
│ └──────────────────┘ │ └─ OrderItem[] │ │
|
|
212
|
-
│ │ └─ Money (VO) │ │
|
|
213
|
-
│ ┌──────────────────┐ └──────────────────┘ │
|
|
214
|
-
│ │ Producer │◀── Domain Event: OrderPlaced │
|
|
215
|
-
│ └────────┬─────────┘ │
|
|
216
|
-
└────────────│───────────────────────────────────────────────────┘
|
|
217
|
-
│
|
|
218
|
-
▼ Events cross context boundaries
|
|
219
|
-
┌────────────────────────────────────────────────────────────────┐
|
|
220
|
-
│ Bounded Context: "Inventory" │
|
|
221
|
-
│ Consumer ──▶ InventoryService ──▶ StockLevel (Aggregate) │
|
|
222
|
-
└────────────────────────────────────────────────────────────────┘
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
**Context Anti-Patterns:**
|
|
226
|
-
- Shared database (breaks boundary enforcement)
|
|
227
|
-
- Generic "core" or "common" domain (pollutes context boundaries)
|
|
228
|
-
- Cross-context validation (move validation to appropriate context)
|
|
229
|
-
- Tight coupling via shared types (use context-specific DTOs)
|
|
230
|
-
|
|
231
|
-
## Composition Rules
|
|
232
|
-
|
|
233
|
-
| DDD Pattern | Maps To | Location |
|
|
234
|
-
|-------------|---------|----------|
|
|
235
|
-
| Bounded Context | Module/Package boundary | Contains all layers |
|
|
236
|
-
| Aggregate | Entity cluster | Functional Core |
|
|
237
|
-
| Aggregate Root | Primary Entity | Entity layer |
|
|
238
|
-
| Value Object | Immutable type | Entity layer |
|
|
239
|
-
| Domain Event | Event class | Published from Service, consumed by edge |
|
|
240
|
-
| Repository | Data access | Effectful Edge (interface in Core) |
|
|
241
|
-
|
|
242
|
-
**Key Interactions:**
|
|
243
|
-
1. **Router → Service**: HTTP request parsed, passed to service
|
|
244
|
-
2. **Service → Aggregate**: Service orchestrates aggregate operations
|
|
245
|
-
3. **Aggregate → Repository**: Service uses repository to persist aggregate
|
|
246
|
-
4. **Service → Producer**: Service publishes domain events after state change
|
|
247
|
-
5. **Consumer → Service**: Events from other contexts trigger service operations
|
|
248
|
-
|
|
249
|
-
## Seam Placement
|
|
250
|
-
|
|
251
|
-
Where you put the seam matters as much as what goes behind it.
|
|
252
|
-
|
|
253
|
-
### Core/Edge Seam
|
|
254
|
-
|
|
255
|
-
The primary seam runs between the functional core and the effectful edge:
|
|
256
|
-
|
|
257
|
-
```
|
|
258
|
-
┌─────────────────────────────────┐
|
|
259
|
-
│ Functional Core (pure) │
|
|
260
|
-
│ ┌──────────────────────────┐ │
|
|
261
|
-
│ │ OrderService │ │
|
|
262
|
-
│ │ createOrder() ────────┼───┼──▶ (seam)
|
|
263
|
-
│ │ processPayment() │ │
|
|
264
|
-
│ └──────────────────────────┘ │
|
|
265
|
-
└─────────────────────────────────┘
|
|
266
|
-
│
|
|
267
|
-
▼ seam
|
|
268
|
-
┌─────────────────────────────────┐
|
|
269
|
-
│ Effectful Edge (IO) │
|
|
270
|
-
│ ┌──────────────────────────┐ │
|
|
271
|
-
│ │ PostgresOrderRepository │◀──┼── adapter
|
|
272
|
-
│ │ HttpPaymentClient │ │ adapter
|
|
273
|
-
│ │ InMemoryOrderRepository │ │ adapter (test)
|
|
274
|
-
│ └──────────────────────────┘ │
|
|
275
|
-
└─────────────────────────────────┘
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
**Rules:**
|
|
279
|
-
- Core defines the interface; edge implements it (dependency inversion)
|
|
280
|
-
- The seam must have **at least two adapters** to be real (production + test)
|
|
281
|
-
- Core never imports edge code; edge imports core interfaces
|
|
282
|
-
|
|
283
|
-
### Internal Seams
|
|
284
|
-
|
|
285
|
-
A deep module can have **internal seams** — private to its implementation, used by its own tests:
|
|
286
|
-
|
|
287
|
-
```typescript
|
|
288
|
-
class Order {
|
|
289
|
-
// External seam (public interface)
|
|
290
|
-
validate(): Result<Order> { ... }
|
|
291
|
-
|
|
292
|
-
// Internal seam (private, tested directly)
|
|
293
|
-
private calculateTax(subtotal: number): Money { ... }
|
|
294
|
-
}
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
Internal seams let you test complex logic without exposing it in the public interface.
|
|
298
|
-
|
|
299
|
-
### When to Create a Seam
|
|
300
|
-
|
|
301
|
-
Use the **adapter rule**: one adapter = hypothetical; two = real.
|
|
302
|
-
|
|
303
|
-
| Scenario | Action |
|
|
304
|
-
|----------|--------|
|
|
305
|
-
| One database implementation, no test fake | No seam needed. Direct dependency is simpler. |
|
|
306
|
-
| One database implementation, test fake exists | Extract interface. The fake is the second adapter. |
|
|
307
|
-
| Multiple database implementations (Postgres + DynamoDB) | Extract interface. The implementations are the adapters. |
|
|
308
|
-
| External API with one client | Direct dependency. Introduce seam only when you need a mock/stub for testing. |
|
|
309
|
-
|
|
310
|
-
### Anti-Patterns
|
|
311
|
-
|
|
312
|
-
**Seam at every dependency:**
|
|
313
|
-
```typescript
|
|
314
|
-
// ❌ Too many seams — every dependency gets an interface
|
|
315
|
-
interface IValidator { ... }
|
|
316
|
-
interface IFormatter { ... }
|
|
317
|
-
interface IConverter { ... }
|
|
318
|
-
// Each has one adapter. Indirection without value.
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
**No seam at the core/edge boundary:**
|
|
322
|
-
```typescript
|
|
323
|
-
// ❌ Core imports edge directly
|
|
324
|
-
import { db } from '../edge/postgres';
|
|
325
|
-
class OrderService {
|
|
326
|
-
async createOrder(req) {
|
|
327
|
-
await db.orders.create(...); // Core knows about Postgres
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
**Leaky seam:**
|
|
333
|
-
```typescript
|
|
334
|
-
// ❌ Interface exposes implementation details
|
|
335
|
-
interface OrderRepository {
|
|
336
|
-
queryRaw(sql: string): Promise<Row[]>; // Caller knows SQL
|
|
337
|
-
beginTransaction(): Promise<Transaction>; // Caller manages transactions
|
|
338
|
-
}
|
|
339
|
-
// The seam leaks. Callers become coupled to the database.
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
### Seam Placement Checklist
|
|
343
|
-
|
|
344
|
-
Before introducing a new seam, verify:
|
|
345
|
-
- [ ] At least two adapters will exist (production + test, or multiple production)
|
|
346
|
-
- [ ] The interface hides implementation details (no SQL, no HTTP, no framework types)
|
|
347
|
-
- [ ] The core doesn't import edge code
|
|
348
|
-
- [ ] Deleting the module would re-appear complexity across N callers
|
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
# Interface Design Reference
|
|
2
|
-
|
|
3
|
-
Patterns for designing module interfaces that maximize depth, leverage, and locality.
|
|
4
|
-
|
|
5
|
-
Based on "Design It Twice" (Ousterhout) — your first idea is unlikely to be the best.
|
|
6
|
-
|
|
7
|
-
## Design It Twice
|
|
8
|
-
|
|
9
|
-
Before committing to a module's interface, sketch at least two alternatives with different trade-offs.
|
|
10
|
-
|
|
11
|
-
### Process
|
|
12
|
-
|
|
13
|
-
1. **Frame the problem space**
|
|
14
|
-
- List constraints any interface must satisfy
|
|
15
|
-
- Identify dependencies and which category they fall into
|
|
16
|
-
- Write a rough illustrative code sketch to ground the constraints
|
|
17
|
-
|
|
18
|
-
2. **Sketch alternatives**
|
|
19
|
-
- Alternative A: Minimize the interface (1–3 entry points max)
|
|
20
|
-
- Alternative B: Maximize flexibility (support many use cases)
|
|
21
|
-
- Alternative C: Optimize for the most common caller (default case trivial)
|
|
22
|
-
|
|
23
|
-
3. **Compare by depth**
|
|
24
|
-
- Which alternative has the smallest interface for the most behaviour?
|
|
25
|
-
- Which concentrates the most knowledge in one place?
|
|
26
|
-
- Which has the best test surface?
|
|
27
|
-
|
|
28
|
-
### Example: Order Creation Interface
|
|
29
|
-
|
|
30
|
-
**Problem:** Design the interface for creating an order.
|
|
31
|
-
|
|
32
|
-
**Alternative A — Minimal interface:**
|
|
33
|
-
```typescript
|
|
34
|
-
interface OrderService {
|
|
35
|
-
createOrder(request: CreateOrderRequest): Promise<Result<Order>>;
|
|
36
|
-
}
|
|
37
|
-
```
|
|
38
|
-
- **Pros:** Tiny surface. One seam. Easy to mock.
|
|
39
|
-
- **Cons:** All complexity hidden inside. Hard to test partial flows (e.g. validation without persistence).
|
|
40
|
-
- **Depth:** Very deep. High leverage, high locality.
|
|
41
|
-
|
|
42
|
-
**Alternative B — Flexible interface:**
|
|
43
|
-
```typescript
|
|
44
|
-
interface OrderService {
|
|
45
|
-
validate(request: CreateOrderRequest): Promise<Result<Order>>;
|
|
46
|
-
reserveInventory(order: Order): Promise<Result<Order>>;
|
|
47
|
-
save(order: Order): Promise<Result<Order>>;
|
|
48
|
-
publishEvents(order: Order): Promise<void>;
|
|
49
|
-
}
|
|
50
|
-
```
|
|
51
|
-
- **Pros:** Callers compose their own flows. Easy to test each step.
|
|
52
|
-
- **Cons:** Interface is as complex as implementation. Callers must know ordering constraints (reserve before save).
|
|
53
|
-
- **Depth:** Shallow. Low leverage — callers replicate orchestration.
|
|
54
|
-
|
|
55
|
-
**Alternative C — Optimized for common case:**
|
|
56
|
-
```typescript
|
|
57
|
-
interface OrderService {
|
|
58
|
-
createOrder(request: CreateOrderRequest): Promise<Result<Order>>;
|
|
59
|
-
createOrderDraft(request: CreateOrderRequest): Promise<Result<Order>>; // no inventory check
|
|
60
|
-
confirmOrder(orderId: string): Promise<Result<Order>>; // draft → confirmed
|
|
61
|
-
}
|
|
62
|
-
```
|
|
63
|
-
- **Pros:** Common case (createOrder) is trivial. Draft pattern supports async workflows.
|
|
64
|
-
- **Cons:** More surface area than Alternative A. Callers must understand draft vs confirmed.
|
|
65
|
-
- **Depth:** Moderate. Good leverage for the common case.
|
|
66
|
-
|
|
67
|
-
**Recommendation:** Alternative C. It balances depth with real workflow needs. The draft/confirm split is a real seam (two callers: interactive vs batch).
|
|
68
|
-
|
|
69
|
-
## Interface Size Heuristics
|
|
70
|
-
|
|
71
|
-
### The Rule of Three
|
|
72
|
-
|
|
73
|
-
A module's public interface should have no more than 3 primary entry points for each responsibility:
|
|
74
|
-
|
|
75
|
-
```typescript
|
|
76
|
-
// ❌ Too wide
|
|
77
|
-
interface OrderService {
|
|
78
|
-
createOrder(...): Promise<Order>;
|
|
79
|
-
updateOrder(...): Promise<Order>;
|
|
80
|
-
deleteOrder(...): Promise<void>;
|
|
81
|
-
getOrder(...): Promise<Order>;
|
|
82
|
-
listOrders(...): Promise<Order[]>;
|
|
83
|
-
searchOrders(...): Promise<Order[]>;
|
|
84
|
-
cancelOrder(...): Promise<Order>;
|
|
85
|
-
shipOrder(...): Promise<Order>;
|
|
86
|
-
refundOrder(...): Promise<Order>;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// ✅ Split by responsibility (2 modules, each with ~3 methods)
|
|
90
|
-
interface OrderLifecycleService {
|
|
91
|
-
create(...): Promise<Order>;
|
|
92
|
-
cancel(...): Promise<Order>;
|
|
93
|
-
ship(...): Promise<Order>;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
interface OrderQueryService {
|
|
97
|
-
get(id: string): Promise<Order>;
|
|
98
|
-
list(filters: OrderFilters): Promise<Order[]>;
|
|
99
|
-
search(query: string): Promise<Order[]>;
|
|
100
|
-
}
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### Invariants in the Interface
|
|
104
|
-
|
|
105
|
-
The interface includes not just types, but everything a caller must know:
|
|
106
|
-
|
|
107
|
-
```typescript
|
|
108
|
-
interface PaymentService {
|
|
109
|
-
// ❌ Interface hides invariants
|
|
110
|
-
processPayment(orderId: string, amount: number): Promise<Receipt>;
|
|
111
|
-
// Caller must discover: amount must match order total, order must be confirmed,
|
|
112
|
-
// can't process twice, throws if payment gateway is down.
|
|
113
|
-
|
|
114
|
-
// ✅ Interface encodes invariants in types
|
|
115
|
-
processPayment(
|
|
116
|
-
order: ConfirmedOrder, // type encodes "must be confirmed"
|
|
117
|
-
payment: PaymentMethod // type encodes valid methods
|
|
118
|
-
): Promise<Result<Receipt, PaymentError>>;
|
|
119
|
-
// Error mode explicit in return type. Ordering implicit in input types.
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
## Dependency Categories
|
|
123
|
-
|
|
124
|
-
The shape of an interface depends on what it depends on.
|
|
125
|
-
|
|
126
|
-
| Category | Description | Interface Impact |
|
|
127
|
-
|----------|-------------|------------------|
|
|
128
|
-
| **In-process** | Same memory space, no serialization | Can pass rich types, callbacks, streams |
|
|
129
|
-
| **Local-substitutable** | Same interface, different implementations (real vs test) | Seam is real. Interface must hide implementation |
|
|
130
|
-
| **Ports & adapters** | Crosses a real boundary (DB, HTTP, message queue) | Interface must be narrow. Errors are part of the contract |
|
|
131
|
-
| **Mock** | Test-only dependency, no production equivalent | Don't create a seam. Test the real module |
|
|
132
|
-
|
|
133
|
-
## Common Interface Anti-Patterns
|
|
134
|
-
|
|
135
|
-
### Leaky Abstraction
|
|
136
|
-
|
|
137
|
-
```typescript
|
|
138
|
-
// ❌ Interface exposes implementation
|
|
139
|
-
interface UserRepository {
|
|
140
|
-
query(sql: string, params: any[]): Promise<Row[]>;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// ✅ Interface hides implementation
|
|
144
|
-
interface UserRepository {
|
|
145
|
-
findByEmail(email: Email): Promise<User | null>;
|
|
146
|
-
save(user: User): Promise<User>;
|
|
147
|
-
}
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
### Pass-Through Interface
|
|
151
|
-
|
|
152
|
-
```typescript
|
|
153
|
-
// ❌ Every method delegates 1:1 to another module
|
|
154
|
-
class OrderService {
|
|
155
|
-
async findById(id: string) { return this.repo.findById(id); }
|
|
156
|
-
async save(order: Order) { return this.repo.save(order); }
|
|
157
|
-
async delete(id: string) { return this.repo.delete(id); }
|
|
158
|
-
}
|
|
159
|
-
// Deletion test: delete OrderService → complexity doesn't reappear.
|
|
160
|
-
// It's a pass-through. Remove it and call repo directly.
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
### God Interface
|
|
164
|
-
|
|
165
|
-
```typescript
|
|
166
|
-
// ❌ One module does everything
|
|
167
|
-
interface PlatformService {
|
|
168
|
-
createUser(...): Promise<User>;
|
|
169
|
-
sendEmail(...): Promise<void>;
|
|
170
|
-
processPayment(...): Promise<Receipt>;
|
|
171
|
-
generateReport(...): Promise<PDF>;
|
|
172
|
-
}
|
|
173
|
-
// No cohesion. Deleting it would reappear complexity, but in unrelated places.
|
|
174
|
-
// Split by domain responsibility instead.
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
### Chatty Interface
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
// ❌ Many small methods that must be called in sequence
|
|
181
|
-
interface OrderBuilder {
|
|
182
|
-
setCustomer(customerId: string): this;
|
|
183
|
-
addItem(item: OrderItem): this;
|
|
184
|
-
setShipping(address: Address): this;
|
|
185
|
-
setPayment(method: PaymentMethod): this;
|
|
186
|
-
validate(): Result<void>;
|
|
187
|
-
build(): Order;
|
|
188
|
-
}
|
|
189
|
-
// Callers must know ordering. State machine hidden in method calls.
|
|
190
|
-
// Better: single method that takes a complete request.
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
## Decision Checklist
|
|
194
|
-
|
|
195
|
-
Before finalizing an interface:
|
|
196
|
-
|
|
197
|
-
- [ ] Run the deletion test — does complexity reappear across N callers?
|
|
198
|
-
- [ ] Check depth — is the interface at least 3x smaller than the implementation?
|
|
199
|
-
- [ ] Verify seam — are there at least two adapters?
|
|
200
|
-
- [ ] Encode invariants — are error modes, ordering, and constraints visible in types?
|
|
201
|
-
- [ ] Test surface — can you test all behaviour through the public interface?
|
|
202
|
-
- [ ] Hide details — does the interface leak SQL, HTTP, frameworks, or implementation?
|
|
203
|
-
- [ ] Common case — is the most frequent caller's experience trivial?
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: oracle-challenge
|
|
3
|
-
description: Challenge an approach with critical thinking. Use when questioning assumptions, validating decisions, testing approach validity, or preventing automatic agreement.
|
|
4
|
-
user-invocable: true
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Challenge: Critical Thinking Prompt
|
|
8
|
-
|
|
9
|
-
## Step 1: Parse Challenge Request
|
|
10
|
-
|
|
11
|
-
<strategist>
|
|
12
|
-
@agent-oracle
|
|
13
|
-
|
|
14
|
-
Analyze the challenge request: $ARGUMENTS
|
|
15
|
-
|
|
16
|
-
**Challenge Extraction:**
|
|
17
|
-
- **Core concern**: Extract the main doubt or question
|
|
18
|
-
- **Target approach**: Identify what is being challenged
|
|
19
|
-
- **Context**: Relevant background from current session
|
|
20
|
-
- **Specific aspects**: Particular elements to question
|
|
21
|
-
|
|
22
|
-
**Challenge Summary:**
|
|
23
|
-
You're challenging: [identified approach]
|
|
24
|
-
Because: [extracted concern]
|
|
25
|
-
In context of: [session context]
|
|
26
|
-
</strategist>
|
|
27
|
-
|
|
28
|
-
## Step 2: Set Up Critical Thinking Framework
|
|
29
|
-
|
|
30
|
-
<framework>
|
|
31
|
-
**What to Question:**
|
|
32
|
-
- **Underlying assumptions**: What beliefs support this approach?
|
|
33
|
-
- **Evidence base**: What data or experience validates it?
|
|
34
|
-
- **Context fit**: How well does it work in your specific situation?
|
|
35
|
-
- **Alternatives considered**: What other options were explored?
|
|
36
|
-
- **Risk factors**: What could go wrong with this approach?
|
|
37
|
-
|
|
38
|
-
**Critical Thinking Prompts:**
|
|
39
|
-
- Is this approach solving the right problem?
|
|
40
|
-
- Are the underlying assumptions still valid?
|
|
41
|
-
- What evidence contradicts this direction?
|
|
42
|
-
- How does this fit with your constraints and goals?
|
|
43
|
-
- What are the opportunity costs?
|
|
44
|
-
</framework>
|
|
45
|
-
|
|
46
|
-
## Step 3: Sequential Thinking Analysis
|
|
47
|
-
|
|
48
|
-
<strategist>
|
|
49
|
-
@agent-oracle
|
|
50
|
-
|
|
51
|
-
Use sequential thinking (mcp__sequential-thinking__sequentialthinking) to analyze this challenge:
|
|
52
|
-
|
|
53
|
-
**Thought 1**: Question the fundamental assumptions
|
|
54
|
-
**Thought 2**: Examine contradictory evidence
|
|
55
|
-
**Thought 3**: Explore alternative approaches
|
|
56
|
-
**Thought 4**: Assess context-specific fit
|
|
57
|
-
**Thought 5**: Evaluate risks and trade-offs
|
|
58
|
-
**Thought 6**: Synthesize findings into recommendation
|
|
59
|
-
|
|
60
|
-
Build systematically through evidence, alternatives, and risks.
|
|
61
|
-
Continue until you reach a clear conclusion.
|
|
62
|
-
</strategist>
|
|
63
|
-
|
|
64
|
-
## Step 4: Critical Evaluation Output
|
|
65
|
-
|
|
66
|
-
**Self-Critique Questions:**
|
|
67
|
-
- Does the analysis address the user's specific context?
|
|
68
|
-
- Are the recommendations practical and implementable?
|
|
69
|
-
- Have we considered the most important constraints?
|
|
70
|
-
- Are there any blind spots or missing perspectives?
|
|
71
|
-
|
|
72
|
-
**Final Synthesis:**
|
|
73
|
-
- **Assumption validity**: Are the underlying assumptions sound?
|
|
74
|
-
- **Evidence assessment**: Does evidence support or contradict?
|
|
75
|
-
- **Alternative recommendation**: If current approach is problematic, what instead?
|
|
76
|
-
- **Risk mitigation**: How to address identified concerns?
|
|
77
|
-
|
|
78
|
-
---
|
|
79
|
-
|
|
80
|
-
## Usage Examples
|
|
81
|
-
|
|
82
|
-
**Challenge Technical Decisions:**
|
|
83
|
-
```
|
|
84
|
-
/atelier-challenge "Do we really need a microservices architecture for this simple app?"
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
**Challenge Implementation Approach:**
|
|
88
|
-
```
|
|
89
|
-
/atelier-challenge "I think this caching strategy will actually slow things down"
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
**Challenge Requirements:**
|
|
93
|
-
```
|
|
94
|
-
/atelier-challenge "Are we solving the right problem with this feature?"
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
**Challenge Architectural Patterns:**
|
|
98
|
-
```
|
|
99
|
-
/atelier-challenge "Should we really use event sourcing for this use case?"
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
## When to Use Challenge
|
|
103
|
-
|
|
104
|
-
**Before Major Decisions:**
|
|
105
|
-
- Architecture choices
|
|
106
|
-
- Technology stack decisions
|
|
107
|
-
- Design pattern selection
|
|
108
|
-
- Implementation approach
|
|
109
|
-
|
|
110
|
-
**When Something Feels Off:**
|
|
111
|
-
- "This seems overly complex"
|
|
112
|
-
- "I'm not sure this solves the real problem"
|
|
113
|
-
- "This approach feels wrong"
|
|
114
|
-
- "Are we over-engineering this?"
|
|
115
|
-
|
|
116
|
-
**To Prevent Automatic Agreement:**
|
|
117
|
-
- When you want genuine critical evaluation
|
|
118
|
-
- When you need to challenge conventional wisdom
|
|
119
|
-
- When you want to test your own assumptions
|
|
120
|
-
|
|
121
|
-
## Challenge vs ThinkDeep
|
|
122
|
-
|
|
123
|
-
**Use /atelier-challenge**: Question assumptions, test validity, assess risks, prevent automatic agreement
|
|
124
|
-
**Use /atelier-thinkdeep**: Deep exploration, comprehensive analysis, alternative discovery, complex decisions
|
|
125
|
-
|
|
126
|
-
**Key distinction**: Challenge = critical evaluation, ThinkDeep = deep exploration
|