@martinffx/atelier 3.1.2 → 3.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/CHANGELOG.md +33 -12
- package/README.md +39 -5
- package/agents/architect.md +5 -4
- package/agents/keymaker.md +36 -0
- package/agents/oracle.md +3 -2
- package/agents/sentinel.md +9 -13
- package/dist/atelier.js +2833 -2768
- package/package.json +5 -2
- package/skills/atelier-orchestrator/SKILL.md +10 -1
- package/skills/code-review/SKILL.md +12 -13
- package/skills/code-review/references/reviewers.md +29 -16
- package/skills/code-review/references/rq.md +42 -30
- package/skills/code-subagents/SKILL.md +22 -7
- package/skills/code-subagents/references/batch-reviewer-prompt.md +2 -1
- package/skills/code-subagents/references/implementor-prompt.md +5 -1
- package/skills/oracle-codebase-design/SKILL.md +129 -0
- package/skills/oracle-codebase-design/references/deepening.md +37 -0
- package/skills/oracle-codebase-design/references/design-it-twice.md +44 -0
- package/skills/oracle-codebase-design/references/principles.md +205 -0
- package/skills/oracle-codebase-design/references/red-flags.md +32 -0
- package/skills/oracle-debug/SKILL.md +5 -3
- package/skills/oracle-grill-me/SKILL.md +2 -1
- package/skills/spec-brainstorm/SKILL.md +11 -4
- package/skills/spec-implement/SKILL.md +18 -5
- package/skills/spec-plan/SKILL.md +23 -3
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Deepening
|
|
2
|
+
|
|
3
|
+
How to deepen a cluster of shallow modules safely, given its dependencies. Assumes the vocabulary in [SKILL.md](../SKILL.md): **module**, **interface**, **seam**, **adapter**.
|
|
4
|
+
|
|
5
|
+
## Dependency categories
|
|
6
|
+
|
|
7
|
+
When assessing a candidate for deepening, classify its dependencies. The category determines how the deepened module is tested across its seam.
|
|
8
|
+
|
|
9
|
+
### 1. In-process
|
|
10
|
+
|
|
11
|
+
Pure computation, in-memory state, no I/O. Always deepenable: merge the modules and test through the new interface directly. No adapter needed.
|
|
12
|
+
|
|
13
|
+
### 2. Local-substitutable
|
|
14
|
+
|
|
15
|
+
Dependencies that have local test stand-ins (PGLite for Postgres, in-memory filesystem). Deepenable if the stand-in exists. The deepened module is tested with the stand-in running in the test suite. The seam is internal; no port at the module's external interface.
|
|
16
|
+
|
|
17
|
+
### 3. Remote but owned (Ports & Adapters)
|
|
18
|
+
|
|
19
|
+
Your own services across a network boundary (microservices, internal APIs). Define a **port** (interface) at the seam. The deep module owns the logic; the transport is injected as an **adapter**. Tests use an in-memory adapter. Production uses an HTTP/gRPC/queue adapter.
|
|
20
|
+
|
|
21
|
+
Recommendation shape: *"Define a port at the seam, implement an HTTP adapter for production and an in-memory adapter for testing, so the logic sits in one deep module even though it's deployed across a network."*
|
|
22
|
+
|
|
23
|
+
### 4. True external (Mock)
|
|
24
|
+
|
|
25
|
+
Third-party services (Stripe, Twilio, etc.) you don't control. The deepened module takes the external dependency as an injected port; tests provide a mock adapter.
|
|
26
|
+
|
|
27
|
+
## Seam discipline
|
|
28
|
+
|
|
29
|
+
- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a port unless at least two adapters are justified (typically production + test). A single-adapter seam is just indirection.
|
|
30
|
+
- **Internal seams vs external seams.** A deep module can have internal seams (private to its implementation, used by its own tests) as well as the external seam at its interface. Don't expose internal seams through the interface just because tests use them.
|
|
31
|
+
|
|
32
|
+
## Testing strategy: replace, don't layer
|
|
33
|
+
|
|
34
|
+
- Old unit tests on shallow modules become waste once tests at the deepened module's interface exist; delete them.
|
|
35
|
+
- Write new tests at the deepened module's interface. The **interface is the test surface**.
|
|
36
|
+
- Tests assert on observable outcomes through the interface, not internal state.
|
|
37
|
+
- Tests should survive internal refactors, since they describe behaviour, not implementation. If a test has to change when the implementation changes, it's testing past the interface.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Design It Twice
|
|
2
|
+
|
|
3
|
+
When the user wants to explore alternative interfaces for a module, use this parallel sub-agent pattern. Based on "Design It Twice" (John Ousterhout, *A Philosophy of Software Design*): your first idea is unlikely to be the best.
|
|
4
|
+
|
|
5
|
+
Uses the vocabulary in [SKILL.md](../SKILL.md): **module**, **interface**, **seam**, **adapter**, **leverage**.
|
|
6
|
+
|
|
7
|
+
## Process
|
|
8
|
+
|
|
9
|
+
### 1. Frame the problem space
|
|
10
|
+
|
|
11
|
+
Before spawning sub-agents, write a user-facing explanation of the problem space:
|
|
12
|
+
|
|
13
|
+
- The constraints any new interface would need to satisfy
|
|
14
|
+
- The dependencies it would rely on, and which category they fall into (see [deepening.md](deepening.md))
|
|
15
|
+
- A rough illustrative code sketch to ground the constraints, not a proposal, just a way to make the constraints concrete
|
|
16
|
+
|
|
17
|
+
Show this to the user, then immediately proceed to Step 2. The user reads and thinks while the sub-agents work in parallel.
|
|
18
|
+
|
|
19
|
+
### 2. Spawn sub-agents
|
|
20
|
+
|
|
21
|
+
Spawn 3+ sub-agents in parallel. Each must produce a **radically different** interface for the module.
|
|
22
|
+
|
|
23
|
+
Prompt each sub-agent with a separate technical brief (file paths, coupling details, dependency category from [deepening.md](deepening.md), what sits behind the seam). The brief is independent of the user-facing problem-space explanation in Step 1. Give each agent a different design constraint:
|
|
24
|
+
|
|
25
|
+
- Agent 1: "Minimize the interface: aim for 1–3 entry points max. Maximise leverage per entry point."
|
|
26
|
+
- Agent 2: "Maximise flexibility: support many use cases and extension."
|
|
27
|
+
- Agent 3: "Optimise for the most common caller: make the default case trivial."
|
|
28
|
+
- Agent 4 (if applicable): "Design around ports & adapters for cross-seam dependencies."
|
|
29
|
+
|
|
30
|
+
Include both [SKILL.md](../SKILL.md) vocabulary and the project's `CONTEXT.md` vocabulary in the brief so each sub-agent names things consistently with the design language and the project's domain language.
|
|
31
|
+
|
|
32
|
+
Each sub-agent outputs:
|
|
33
|
+
|
|
34
|
+
1. Interface (types, methods, params, plus invariants, ordering, error modes)
|
|
35
|
+
2. Usage example showing how callers use it
|
|
36
|
+
3. What the implementation hides behind the seam
|
|
37
|
+
4. Dependency strategy and adapters (see [deepening.md](deepening.md))
|
|
38
|
+
5. Trade-offs: where leverage is high, where it's thin
|
|
39
|
+
|
|
40
|
+
### 3. Present and compare
|
|
41
|
+
|
|
42
|
+
Present designs sequentially so the user can absorb each one, then compare them in prose. Contrast by **depth** (leverage at the interface), **locality** (where change concentrates), and **seam placement**. Check each against [red-flags.md](red-flags.md).
|
|
43
|
+
|
|
44
|
+
After comparing, give your own recommendation: which design you think is strongest and why. If elements from different designs would combine well, propose a hybrid. Be opinionated: the user wants a strong read, not a menu.
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# Principles
|
|
2
|
+
|
|
3
|
+
Each principle: the rule, why it matters in glossary terms (see [SKILL.md](../SKILL.md)), and a short example. Examples are TypeScript.
|
|
4
|
+
|
|
5
|
+
## 1. Deep modules and the deletion test
|
|
6
|
+
|
|
7
|
+
**Rule.** Put a lot of behaviour behind a small interface. Imagine deleting the module: if complexity vanishes, it was a pass-through; if it reappears across N callers, it was earning its keep.
|
|
8
|
+
|
|
9
|
+
**Why.** Depth gives callers leverage and maintainers locality.
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
// Shallow: the interface is the implementation
|
|
13
|
+
class UserRepo {
|
|
14
|
+
getTable() { return this.db.table("users"); }
|
|
15
|
+
runQuery(q: Query) { return this.db.run(q); }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Deep: callers say what they want
|
|
19
|
+
class UserRepo {
|
|
20
|
+
findByEmail(email: Email): Promise<User | undefined> { /* query, mapping, caching */ }
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 2. Test at the correct level
|
|
25
|
+
|
|
26
|
+
**Rule.** The interface is the test surface. Test through the module's interface at its seam, not each internal function. When you deepen a module, replace the old tests; don't layer new ones on top.
|
|
27
|
+
|
|
28
|
+
**Why.** Tests that reach past the interface break on every refactor and prove nothing the interface doesn't. If you want to test past the interface, the module is probably the wrong shape.
|
|
29
|
+
|
|
30
|
+
- Unit-test the functional core directly; it has no I/O.
|
|
31
|
+
- Integration-test the shell with a local stand-in (see [deepening.md](deepening.md)).
|
|
32
|
+
- Don't mock your own modules. Mock only true external dependencies at a port.
|
|
33
|
+
- Assert on observable outcomes, not internal state.
|
|
34
|
+
|
|
35
|
+
**Smell.** A test that changes whenever the implementation changes; many tiny tests of pure helpers while the real bugs hide in how they're called.
|
|
36
|
+
|
|
37
|
+
## 3. Parse, don't validate
|
|
38
|
+
|
|
39
|
+
**Rule.** At the external seam, turn untrusted input into a domain type that can only exist if valid. The core accepts the parsed type and never re-checks.
|
|
40
|
+
|
|
41
|
+
**Why.** Validation returns a boolean and discards what it learned, so every downstream caller must re-validate or hope. Parsing keeps the knowledge in the type. Locality: the check lives in one place.
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
// Validate: knowledge is lost, callers re-check
|
|
45
|
+
function isEmail(s: string): boolean { /* ... */ }
|
|
46
|
+
function sendWelcome(to: string) { if (!isEmail(to)) throw new Error("bad email"); /* ... */ }
|
|
47
|
+
|
|
48
|
+
// Parse: knowledge is kept in the type
|
|
49
|
+
type Email = string & { readonly __brand: "Email" };
|
|
50
|
+
function parseEmail(s: string): Result<Email, InvalidEmail> { /* ... */ }
|
|
51
|
+
function sendWelcome(to: Email) { /* no check needed */ }
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
In a layered codebase this is `Entity.fromRequest`: the router hands raw input to the Entity, which parses it into domain values, and everything below works with parsed types.
|
|
55
|
+
|
|
56
|
+
**Smell.** The same field checked in router, service, and repository; `string` parameters that must be "a valid X".
|
|
57
|
+
|
|
58
|
+
## 4. Make illegal states unrepresentable
|
|
59
|
+
|
|
60
|
+
**Rule.** Shape the types so invalid combinations cannot be constructed. Prefer discriminated unions to a bag of flags and optionals.
|
|
61
|
+
|
|
62
|
+
**Why.** Every representable-but-invalid state is a case every caller must handle, which is interface complexity. Removing it shrinks the interface.
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
// Illegal states possible: loading with an error, data with no result
|
|
66
|
+
type Fetch = { loading: boolean; error?: Error; data?: Data };
|
|
67
|
+
|
|
68
|
+
// Only legal states exist
|
|
69
|
+
type Fetch =
|
|
70
|
+
| { status: "loading" }
|
|
71
|
+
| { status: "failed"; error: Error }
|
|
72
|
+
| { status: "loaded"; data: Data };
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Smell.** Comments like "only set when `kind` is X"; several booleans that can't all be true; optional fields that are required in some states.
|
|
76
|
+
|
|
77
|
+
## 5. Functional core, imperative shell
|
|
78
|
+
|
|
79
|
+
**Rule.** Keep decisions in pure functions that take values and return values. Push I/O (database, network, clock, randomness) to a thin shell that gathers inputs, calls the core, and applies the result.
|
|
80
|
+
|
|
81
|
+
**Why.** The core is testable without mocks and holds the logic, so bugs concentrate there (locality). The shell is simple enough to cover with a few integration tests.
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
// Core: pure decision
|
|
85
|
+
function decideRenewal(sub: Subscription, now: Date): RenewalDecision { /* ... */ }
|
|
86
|
+
|
|
87
|
+
// Shell: I/O around it
|
|
88
|
+
async function renew(id: SubscriptionId) {
|
|
89
|
+
const sub = await repo.get(id);
|
|
90
|
+
const decision = decideRenewal(sub, clock.now());
|
|
91
|
+
await apply(decision);
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Smell.** Business rules interleaved with queries; tests that need a mocked database to check a calculation. Counter-smell: a pure helper extracted only for testability while the real bugs sit in how it's called. Keep decisions and their orchestration close.
|
|
96
|
+
|
|
97
|
+
## 6. Errors as values
|
|
98
|
+
|
|
99
|
+
**Rule.** Expected failures (invalid input, not found, conflict) are part of the interface and are returned as typed values, such as `Result<T, E>`. Exceptions are for bugs and unrecoverable states.
|
|
100
|
+
|
|
101
|
+
**Why.** Error modes are part of the interface. If they're hidden in `throw`, the caller can't see what to handle and the compiler can't help.
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
function placeOrder(cmd: PlaceOrder): Result<Order, OutOfStock | PaymentDeclined> { /* ... */ }
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
But first, try principle 7: if the error can be defined away, it should not be in the interface at all.
|
|
108
|
+
|
|
109
|
+
**Smell.** `catch` blocks that parse error messages; functions documented with "may throw X, Y, Z".
|
|
110
|
+
|
|
111
|
+
## 7. Define errors out of existence
|
|
112
|
+
|
|
113
|
+
**Rule.** Change the semantics so the error condition is not an error. Do this before reaching for `Result`.
|
|
114
|
+
|
|
115
|
+
**Why.** Every error is interface surface callers must handle. The best error handling is none.
|
|
116
|
+
|
|
117
|
+
- `delete(key)` on a missing key succeeds; the postcondition (key absent) holds.
|
|
118
|
+
- `substring(start, end)` clamps out-of-range indices instead of throwing.
|
|
119
|
+
- Return an empty collection, not `null`.
|
|
120
|
+
|
|
121
|
+
**Caution.** Don't swallow errors that matter. Define away only when the caller's intent is satisfied either way.
|
|
122
|
+
|
|
123
|
+
## 8. Information hiding
|
|
124
|
+
|
|
125
|
+
**Rule.** Each design decision (a file format, a protocol, an algorithm, a table layout) lives in exactly one module.
|
|
126
|
+
|
|
127
|
+
**Why.** Knowledge that appears in two modules is a leak: change one and you must change both (change amplification). Locality means a decision changes in one place.
|
|
128
|
+
|
|
129
|
+
**Smell.** Two modules that both know the wire format; callers that must call methods in a particular order (temporal decomposition: structure follows the order of operations rather than the knowledge involved).
|
|
130
|
+
|
|
131
|
+
## 9. Pull complexity downward
|
|
132
|
+
|
|
133
|
+
**Rule.** When complexity must live somewhere, put it in the module, not its callers. A simple interface matters more than a simple implementation.
|
|
134
|
+
|
|
135
|
+
**Why.** There are many callers and one module. Complexity pushed up is paid N times.
|
|
136
|
+
|
|
137
|
+
**Smell.** Config parameters that make callers decide what the module could decide; exceptions the caller can only rethrow.
|
|
138
|
+
|
|
139
|
+
## 10. Somewhat general-purpose modules are deeper
|
|
140
|
+
|
|
141
|
+
**Rule.** Design the interface for the underlying capability, not for today's single use. Keep it general enough to cover related uses, but implement only what's needed now. Keep general-purpose code apart from special-purpose code.
|
|
142
|
+
|
|
143
|
+
**Why.** A general interface is usually smaller and simpler than a stack of special cases, and it survives the next feature.
|
|
144
|
+
|
|
145
|
+
**Smell.** Methods named for one screen or one caller (`deleteSelectedTextFromEditor`); a general mechanism with feature-specific branches inside it.
|
|
146
|
+
|
|
147
|
+
## 11. Different layers, different abstractions
|
|
148
|
+
|
|
149
|
+
**Rule.** Each layer should offer an abstraction distinct from the ones above and below. A method that only forwards to another method with the same signature is a pass-through; so is a variable threaded unchanged through a chain of calls.
|
|
150
|
+
|
|
151
|
+
**Why.** A pass-through adds interface without depth. The deletion test flags it.
|
|
152
|
+
|
|
153
|
+
If a layer exists because the project's architecture demands it (Router → Service → Repository), make it earn its place: give it behaviour the layer below doesn't have, or fold the behaviour down. For a pass-through variable, put it in a context object or shared module.
|
|
154
|
+
|
|
155
|
+
## 12. Together or apart
|
|
156
|
+
|
|
157
|
+
**Rule.** Combine modules that share information, are always used together, or overlap conceptually. Separate general mechanism from specific use. Don't split just to make things short.
|
|
158
|
+
|
|
159
|
+
**Why.** Splitting adds interface and separates things that need each other. Combining adds locality. Judge by interface complexity and dependencies, not by line count.
|
|
160
|
+
|
|
161
|
+
**Smell.** Conjoined methods: you can't understand one without reading the other.
|
|
162
|
+
|
|
163
|
+
## 13. Make the common case simple
|
|
164
|
+
|
|
165
|
+
**Rule.** Give parameters sensible defaults and design the default path to need no configuration. Rare needs may cost more.
|
|
166
|
+
|
|
167
|
+
**Why.** Interface complexity is paid by every caller; the common caller shouldn't pay for the rare one.
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
// Common case: one argument
|
|
171
|
+
createClient(url);
|
|
172
|
+
// Rare case: opt in
|
|
173
|
+
createClient(url, { retries: 5, timeoutMs: 2_000 });
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## 14. Comments and names carry the design
|
|
177
|
+
|
|
178
|
+
**Rule.**
|
|
179
|
+
- Comments describe what isn't obvious from the code. Interface comments state the abstraction (what it does, invariants, error modes); implementation comments explain *why*.
|
|
180
|
+
- Write the interface comment first. If it is hard to write, the design is unclear: fix the design.
|
|
181
|
+
- Names are precise and used consistently for the same thing.
|
|
182
|
+
- Design for ease of reading, not ease of writing.
|
|
183
|
+
|
|
184
|
+
**Why.** A comment that repeats the code adds nothing; a comment that states the abstraction is the interface's documentation. Names are the cheapest interface.
|
|
185
|
+
|
|
186
|
+
**Link.** A name you can't pick, or a concept you can't describe simply, usually means the domain language isn't settled. Resolve it with `oracle-domain-modelling` and record it in `CONTEXT.md`. If the decision is contested, run it through `oracle-grill-me`.
|
|
187
|
+
|
|
188
|
+
## 15. Seam discipline
|
|
189
|
+
|
|
190
|
+
**Rule.**
|
|
191
|
+
- Accept dependencies; don't create them.
|
|
192
|
+
- Return results; don't produce side effects.
|
|
193
|
+
- One adapter means a hypothetical seam. Two means a real one. Don't introduce a port unless something actually varies across it.
|
|
194
|
+
|
|
195
|
+
**Why.** Seams cost interface. Pay only where behaviour varies (typically production and test).
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
// Testable
|
|
199
|
+
function processOrder(order: Order, paymentGateway: PaymentGateway) {}
|
|
200
|
+
|
|
201
|
+
// Hard to test
|
|
202
|
+
function processOrder(order: Order) {
|
|
203
|
+
const gateway = new StripeGateway();
|
|
204
|
+
}
|
|
205
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Red Flags
|
|
2
|
+
|
|
3
|
+
A checklist to run before finalising a design, and when reviewing one. Each flag points to the principle in [principles.md](principles.md) that fixes it. Terms follow [SKILL.md](../SKILL.md).
|
|
4
|
+
|
|
5
|
+
| Red flag | What it looks like | Fix |
|
|
6
|
+
|----------|--------------------|-----|
|
|
7
|
+
| **Shallow module** | The interface is nearly as complex as the implementation. Passes the deletion test the wrong way: deleting it removes nothing. | Deep modules (1). Merge, or move behaviour in. |
|
|
8
|
+
| **Information leakage** | The same design decision (format, protocol, table layout) appears in two modules. | Information hiding (8). |
|
|
9
|
+
| **Temporal decomposition** | Structure follows the order operations run in (read, process, write) instead of the knowledge involved. Shared knowledge ends up in several steps. | Information hiding (8); together or apart (12). |
|
|
10
|
+
| **Overexposure** | The interface forces callers to learn rarely used features to use the common ones. | Make the common case simple (13). |
|
|
11
|
+
| **Pass-through method** | A method that only forwards to another with a similar signature. | Different layers, different abstractions (11). |
|
|
12
|
+
| **Pass-through variable** | An argument threaded unchanged through a chain of calls. | Different layers, different abstractions (11): context object or shared module. |
|
|
13
|
+
| **Repetition** | The same code or check appears in many places. | Parse, don't validate (3); information hiding (8); pull complexity downward (9). |
|
|
14
|
+
| **Special-general mixture** | A general mechanism contains feature-specific branches. | Somewhat general-purpose modules (10). |
|
|
15
|
+
| **Conjoined methods** | You can't understand one method without reading another. | Together or apart (12). |
|
|
16
|
+
| **Comment repeats code** | The comment says what the next line plainly says. | Comments and names (14). |
|
|
17
|
+
| **Implementation contaminates interface** | The interface comment must describe how it works inside. | Information hiding (8); rewrite the interface comment first (14). |
|
|
18
|
+
| **Vague name** | `data`, `info`, `manager`, `handle`, `process`. | Comments and names (14); resolve with `oracle-domain-modelling`. |
|
|
19
|
+
| **Hard to pick a name** | You can't find a precise name for the thing. | The concept is unclear; settle it in `CONTEXT.md`. |
|
|
20
|
+
| **Hard to describe** | The interface comment is long or awkward. | The design is unclear. Redesign before documenting (14). |
|
|
21
|
+
| **Nonobvious code** | A reader can't tell what it does or why without effort. | Names and structure for reading (14). |
|
|
22
|
+
|
|
23
|
+
## Added for this toolkit
|
|
24
|
+
|
|
25
|
+
| Red flag | What it looks like | Fix |
|
|
26
|
+
|----------|--------------------|-----|
|
|
27
|
+
| **Stringly-typed core** | Core logic takes raw `string` or `any` and re-checks it. | Parse, don't validate (3). |
|
|
28
|
+
| **Flag-and-optional bag** | Several booleans and optionals where only some combinations are legal. | Make illegal states unrepresentable (4). |
|
|
29
|
+
| **I/O in the decision** | Business rules interleaved with queries or network calls. | Functional core, imperative shell (5). |
|
|
30
|
+
| **Throwing for expected failure** | `catch` blocks that parse messages; "may throw X, Y, Z". | Define errors out of existence (7), else errors as values (6). |
|
|
31
|
+
| **Testing past the interface** | Tests break on every refactor; many tests of internal helpers. | Test at the correct level (2). |
|
|
32
|
+
| **Single-adapter port** | An interface with one implementation and no test double. | Seam discipline (15). |
|
|
@@ -108,7 +108,9 @@ artifact-based investigation is valid when a loop is unavailable.
|
|
|
108
108
|
|
|
109
109
|
## Phase 2 — Pattern Analysis
|
|
110
110
|
|
|
111
|
-
Find the pattern before fixing.
|
|
111
|
+
Find the pattern before fixing. Finding working examples across a large codebase can be
|
|
112
|
+
delegated to a read-only subagent (see Delegation in atelier-orchestrator). Phases 1, 3 and 4
|
|
113
|
+
stay in the main thread.
|
|
112
114
|
|
|
113
115
|
1. **Find working examples.** Locate similar working code in the same codebase.
|
|
114
116
|
2. **Compare against references.** If implementing a known pattern, read the reference implementation completely.
|
|
@@ -143,7 +145,7 @@ Fix the root cause, not the symptom.
|
|
|
143
145
|
|
|
144
146
|
1. **Create a failing test case.** The simplest possible reproduction. MUST exist before the fix.
|
|
145
147
|
|
|
146
|
-
A **correct seam** is one where the test exercises the real bug pattern as it occurs at the call site. If the only available seam is too shallow, note that the codebase architecture is preventing the bug from being locked down.
|
|
148
|
+
A **correct seam** is one where the test exercises the real bug pattern as it occurs at the call site. If the only available seam is too shallow, note that the codebase architecture is preventing the bug from being locked down. Use the seam and depth vocabulary from `oracle-codebase-design` when you raise it.
|
|
147
149
|
|
|
148
150
|
2. **Implement a single fix.** Address the root cause. One change at a time. No "while I'm here" improvements. No bundled refactoring.
|
|
149
151
|
3. **Verify the fix.** Does the test pass? Do other tests still pass? Does the original repro no longer reproduce?
|
|
@@ -151,7 +153,7 @@ Fix the root cause, not the symptom.
|
|
|
151
153
|
- STOP
|
|
152
154
|
- Count the failed fix attempts
|
|
153
155
|
- If < 3: return to Phase 1 with the new information
|
|
154
|
-
- If ≥ 3: **question the architecture**. Pattern problems, hidden coupling, and shared state that each fix reveals are signs of a wrong pattern. Discuss with the user before attempting Fix #4.
|
|
156
|
+
- If ≥ 3: **question the architecture**. Pattern problems, hidden coupling, and shared state that each fix reveals are signs of a wrong pattern (see the red flags in `oracle-codebase-design`). Discuss with the user before attempting Fix #4.
|
|
155
157
|
|
|
156
158
|
### Cleanup + post-mortem
|
|
157
159
|
|
|
@@ -20,4 +20,5 @@ understanding. Update domain documentation as terminology and decisions are reso
|
|
|
20
20
|
session is nested in spec-brainstorm, return changed assumptions to that workflow for any required
|
|
21
21
|
design update.
|
|
22
22
|
|
|
23
|
-
Run this session using the `oracle-domain-modelling` skill.
|
|
23
|
+
Run this session using the `oracle-domain-modelling` skill. When the decision is about a module's
|
|
24
|
+
interface, seams, or test level, also use the `oracle-codebase-design` skill.
|
|
@@ -55,7 +55,8 @@ it touches multiple files or takes time. That work belongs in an Inline Plan.
|
|
|
55
55
|
|
|
56
56
|
Break the system into units with one clear purpose each. Well-defined interfaces between
|
|
57
57
|
them. Each unit independently understandable and independently testable. If you can't
|
|
58
|
-
explain a unit's job in one sentence, it's doing too much.
|
|
58
|
+
explain a unit's job in one sentence, it's doing too much. Apply **oracle-codebase-design**:
|
|
59
|
+
deep modules, seams placed where behaviour varies, and the interface as the test surface.
|
|
59
60
|
|
|
60
61
|
### Working in existing codebases
|
|
61
62
|
|
|
@@ -169,6 +170,9 @@ Read the relevant codebase deeply. Not signatures — implementations, edge case
|
|
|
169
170
|
handling, data flows. Trace callers and callees. Read tests to understand expected
|
|
170
171
|
behaviour.
|
|
171
172
|
|
|
173
|
+
When research spans more than a few files, delegate it to a read-only subagent per area (see
|
|
174
|
+
Delegation in atelier-orchestrator) and assemble the research table from their cited findings.
|
|
175
|
+
|
|
172
176
|
Collect research findings for the spec as the foundation. Do not write `design.md` until the
|
|
173
177
|
approved design sections are assembled in Step 4c.
|
|
174
178
|
|
|
@@ -200,6 +204,8 @@ Before settling on a design, present **2-3 approaches** with trade-offs.
|
|
|
200
204
|
|
|
201
205
|
The first approach must keep the existing architecture and make the smallest correct change.
|
|
202
206
|
Present broader approaches only when a current requirement makes their extra cost relevant.
|
|
207
|
+
When approaches differ mainly in interface shape, use the design-it-twice reference in
|
|
208
|
+
**oracle-codebase-design**.
|
|
203
209
|
|
|
204
210
|
For each approach, address:
|
|
205
211
|
|
|
@@ -247,9 +253,10 @@ with acceptance criteria and priorities. If rejected: revise. If the rejection
|
|
|
247
253
|
reveals a scope misunderstanding, loop back to Discovery (Step 2).
|
|
248
254
|
|
|
249
255
|
**Batch B: Architecture** — component design, domain modeling, and layer
|
|
250
|
-
boundaries. Use installed language-specific architecture or
|
|
251
|
-
your stack. Then present: component structure, domain model, where
|
|
252
|
-
logic lives, where IO lives
|
|
256
|
+
boundaries. Use **oracle-codebase-design** and any installed language-specific architecture or
|
|
257
|
+
API-design skills relevant to your stack. Then present: component structure, domain model, where
|
|
258
|
+
business logic lives, where IO lives, where untrusted input is parsed (the external seam), and
|
|
259
|
+
the seam each module is tested through. If rejected: revise. If the rejection undermines
|
|
253
260
|
the chosen approach, offer to return to approach exploration (4a). If it
|
|
254
261
|
reveals a fundamental gap, loop back to Research (Step 3). If the detail reveals
|
|
255
262
|
the work is far more complex than estimated, say so and offer to revisit the
|
|
@@ -86,16 +86,27 @@ If the human has not specified an execution style, ask.
|
|
|
86
86
|
- Stop and report: what was done, test output, anything unexpected
|
|
87
87
|
- Wait for human feedback before continuing
|
|
88
88
|
|
|
89
|
+
### Delegation (all modes)
|
|
90
|
+
|
|
91
|
+
In any mode, delegate a task to a fresh subagent when **the design is already settled and the
|
|
92
|
+
file just needs to be made**, as defined by the **code-subagents** "When to Use" criteria.
|
|
93
|
+
|
|
94
|
+
- Invoke **code-subagents** for eligibility, dispatch patterns, and the implementer template
|
|
95
|
+
- Paste the relevant design excerpt into the dispatch so the subagent makes no design decisions
|
|
96
|
+
- Never name an agent; the harness chooses from agent descriptions
|
|
97
|
+
- Keep coupled, exploratory, or design-sensitive tasks in the main thread
|
|
98
|
+
- The main agent runs the combined plan and code-quality review per completed batch, and the
|
|
99
|
+
validation commands itself
|
|
100
|
+
|
|
89
101
|
### Subagent Mode
|
|
90
102
|
|
|
91
103
|
> "Use subagents."
|
|
92
104
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
- One combined plan and code-quality review per completed batch
|
|
96
|
-
- Independent tasks dispatch in parallel, dependent tasks run sequentially
|
|
105
|
+
Delegate every eligible task. Independent tasks dispatch in parallel, dependent tasks run
|
|
106
|
+
sequentially.
|
|
97
107
|
|
|
98
|
-
Default to batched if the human hasn't expressed a preference
|
|
108
|
+
Default to batched if the human hasn't expressed a preference; delegation still applies per
|
|
109
|
+
task.
|
|
99
110
|
|
|
100
111
|
---
|
|
101
112
|
|
|
@@ -117,6 +128,8 @@ Read the task's **inputs** and **description** first.
|
|
|
117
128
|
For behavior-changing code tasks, write tests that cover the validation criteria before writing
|
|
118
129
|
implementation. For documentation, configuration, migration, or verification-only tasks, use the
|
|
119
130
|
task's stated validation instead. Invoke an installed language-specific testing skill when needed.
|
|
131
|
+
Write tests at the module's interface (see **oracle-codebase-design**), and use its red flags
|
|
132
|
+
when refactoring.
|
|
120
133
|
|
|
121
134
|
```
|
|
122
135
|
1. Read task inputs and description
|
|
@@ -43,6 +43,7 @@ request. Implementation requires a later, explicit request after this skill has
|
|
|
43
43
|
same need.
|
|
44
44
|
- Group tests by changed contract and active boundary. Do not repeat the CRUD matrix across
|
|
45
45
|
layers.
|
|
46
|
+
- Plan tests at each module's interface, not past it (see **oracle-codebase-design**).
|
|
46
47
|
|
|
47
48
|
### Proportionality Gate
|
|
48
49
|
|
|
@@ -50,6 +51,25 @@ Before presenting any plan, compare its size and concepts with the requested beh
|
|
|
50
51
|
bounded migration or refactor introduces shared infrastructure, unrelated behavior, or a plan
|
|
51
52
|
substantially larger than the behavior being changed, stop and simplify it.
|
|
52
53
|
|
|
54
|
+
### Challenge Gate
|
|
55
|
+
|
|
56
|
+
Before presenting a plan, dispatch two read-only subagents in parallel with the draft plan and
|
|
57
|
+
its design context. Do not name agents; describe the work so the harness matches it to agent
|
|
58
|
+
descriptions (see Delegation in `atelier-orchestrator`):
|
|
59
|
+
|
|
60
|
+
1. **Design review:** check architectural fit, layer and data-model boundaries, API contracts,
|
|
61
|
+
and whether task ordering respects the dependencies.
|
|
62
|
+
2. **Critical challenge:** challenge assumptions, look for missing cases, scope creep, and
|
|
63
|
+
unproven requirements, and propose simpler alternatives.
|
|
64
|
+
|
|
65
|
+
Each returns at most five evidence-backed findings. Fold in the valid ones, drop the rest with a
|
|
66
|
+
reason, and present the plan with a short **Challenged** note listing what changed.
|
|
67
|
+
|
|
68
|
+
Run the gate on every Spec-backed Plan. Run it on an Inline Plan only when the change touches a
|
|
69
|
+
data model, an API contract, or more than one module or layer; skip it for small bounded edits.
|
|
70
|
+
Run it once per draft; do not re-run it for the human's annotation revisions unless the plan's
|
|
71
|
+
structure changes.
|
|
72
|
+
|
|
53
73
|
## Outputs
|
|
54
74
|
|
|
55
75
|
### Inline Plan (when explicitly selected)
|
|
@@ -63,7 +83,7 @@ structure. Omit optional subsections rather than rendering empty headings.
|
|
|
63
83
|
Read enough of the codebase to identify the current behavior, boundaries, affected files,
|
|
64
84
|
and concrete validation. Keep the plan proportional to implementation risk. Fill the
|
|
65
85
|
template with confirmed, file-and-symbol-level details, including cross-file wiring or
|
|
66
|
-
ordering constraints where they matter. Apply the Planning Rules and
|
|
86
|
+
ordering constraints where they matter. Apply the Planning Rules, Proportionality Gate, and Challenge Gate
|
|
67
87
|
before presenting it.
|
|
68
88
|
|
|
69
89
|
Do not manufacture phases, task IDs, dependency graphs, acceptance matrices, `design.md`,
|
|
@@ -192,8 +212,8 @@ structured `plan.json` when approved.
|
|
|
192
212
|
|
|
193
213
|
## Spec-backed Step 1: Write the Plan Draft
|
|
194
214
|
|
|
195
|
-
Read the approved `design.md`,
|
|
196
|
-
draft into `design.md` or create a separate draft file.
|
|
215
|
+
Read the approved `design.md`, apply the Challenge Gate to the draft, then present the plan
|
|
216
|
+
draft in conversation. Do not write the draft into `design.md` or create a separate draft file.
|
|
197
217
|
|
|
198
218
|
### Plan quality
|
|
199
219
|
|