@widnyana/design-thinking 0.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.
- package/LICENSE +30 -0
- package/README.md +82 -0
- package/extensions/design-thinking.ts +166 -0
- package/package.json +51 -0
- package/prompts/cg-map.md +41 -0
- package/prompts/cg-plan.md +65 -0
- package/prompts/cg-review.md +69 -0
- package/prompts/cg.md +50 -0
- package/references/design-graph.md +88 -0
- package/references/effect-ts.md +156 -0
- package/references/method.md +229 -0
- package/references/protocol.md +228 -0
- package/skills/design-graph/SKILL.md +98 -0
- package/skills/design-method/SKILL.md +239 -0
- package/skills/graph-protocol/SKILL.md +238 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: design-graph
|
|
3
|
+
description: >
|
|
4
|
+
Design Graph — the artifact: annotated call graph of a concrete problem;
|
|
5
|
+
nodes = functions, edges = data flow; every node annotated with A,
|
|
6
|
+
cardinality, E, R. Use when drawing a graph before writing code or
|
|
7
|
+
reconstructing and diffing one against existing code. Includes the
|
|
8
|
+
completeness checklist.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Design Graph — The Artifact
|
|
12
|
+
|
|
13
|
+
A **Design Graph** is the annotated call graph of a concrete problem. It is
|
|
14
|
+
what you produce when you apply [Design Thinking](/skill:design-method) to
|
|
15
|
+
something, and it is written in [Graph Protocol](/skill:graph-protocol) notation.
|
|
16
|
+
|
|
17
|
+
Nodes are functions. Edges are data flow. The annotations answer, for every
|
|
18
|
+
node: what flows through it (A), how many times it runs (cardinality), where
|
|
19
|
+
and how it can fail (E), and what it needs to exist (R) — plus the global
|
|
20
|
+
concerns: trust boundaries, behavior layers, resource scope, and test layers.
|
|
21
|
+
|
|
22
|
+
A Design Graph is not documentation written after the fact. It is drawn
|
|
23
|
+
**before** implementation (planning), **reconstructed** from implementation
|
|
24
|
+
(review), and **compared** against implementation (verdict). The graph is the
|
|
25
|
+
contract; the code is one instantiation of it.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## What a complete Design Graph contains
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
PROBLEM: <one line>
|
|
33
|
+
|
|
34
|
+
X → DesignGraph<A, E, R>
|
|
35
|
+
│ │ │ │ │
|
|
36
|
+
│ │ │ │ └─ R: what each node needs (§5)
|
|
37
|
+
│ │ │ └──── E: where the graph breaks (§4)
|
|
38
|
+
│ │ └─────── A: what flows through nodes (§2)
|
|
39
|
+
│ │
|
|
40
|
+
│ └─ nodes = functions, edges = data flow
|
|
41
|
+
│
|
|
42
|
+
└─ the problem
|
|
43
|
+
|
|
44
|
+
SHAPES: … every noun the graph needs
|
|
45
|
+
GRAPH: … ascii call graph with inline R:/E:/🔒/(n) annotations
|
|
46
|
+
CARDINALITY: … every node marked (1) (N) or (T)
|
|
47
|
+
BOUNDARIES: … every unknown → trusted crossing
|
|
48
|
+
BEHAVIOR: … every orthogonal layer and what it wraps
|
|
49
|
+
SCOPE: … every resource with acquire@x → release@x
|
|
50
|
+
TEST LAYERS: … the R tests provide — same GRAPH, zero code change
|
|
51
|
+
VERDICT: … code matches graph? A and E structurally separated?
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Completeness checklist
|
|
55
|
+
|
|
56
|
+
A Design Graph is complete when all of the following hold:
|
|
57
|
+
|
|
58
|
+
- [ ] Every node has a cardinality marker — `(1)`, `(N)`, or `(T)`.
|
|
59
|
+
- [ ] Every node lists its `R:` — even if `R: none` (that is a feature: the
|
|
60
|
+
node is runnable standalone).
|
|
61
|
+
- [ ] Every edge crossing a trust boundary is marked `🔒 unknown → trusted`.
|
|
62
|
+
- [ ] Every failure mode has a strategy — `⟳retry`, `↯escape(alt)`, or
|
|
63
|
+
`☠die`. No unmarked breaks, no silent swallowing.
|
|
64
|
+
- [ ] Every layer scopes its E before handing off (inner errors translated,
|
|
65
|
+
never leaked).
|
|
66
|
+
- [ ] Every resource has an acquire point and a structural release point.
|
|
67
|
+
- [ ] BEHAVIOR lists only layers that can be removed without touching the
|
|
68
|
+
happy path.
|
|
69
|
+
- [ ] TEST LAYERS shows a full R substitution — if it can't, the graph has
|
|
70
|
+
hidden dependencies.
|
|
71
|
+
- [ ] VERDICT explicitly answers whether code and graph match, and names the
|
|
72
|
+
mismatches.
|
|
73
|
+
|
|
74
|
+
## The three uses
|
|
75
|
+
|
|
76
|
+
### 1. Plan (`/cg-plan`)
|
|
77
|
+
Draw the graph first. Code is written only after the VERDICT question — "does
|
|
78
|
+
my code match this?" — has a target to be checked against. If a code
|
|
79
|
+
suggestion contradicts the graph, fix the graph or fix the code; never let
|
|
80
|
+
them drift silently.
|
|
81
|
+
|
|
82
|
+
### 2. Extract (`/cg`)
|
|
83
|
+
Given existing code, reconstruct the graph the code *actually* implements —
|
|
84
|
+
not the one the author intended. The reconstructed graph is the ground truth
|
|
85
|
+
for review and refactoring.
|
|
86
|
+
|
|
87
|
+
### 3. Review (`/cg-review`)
|
|
88
|
+
Render the implemented graph, then diff it against the intended graph (or
|
|
89
|
+
against the method's completeness checklist when no intended graph exists).
|
|
90
|
+
Every mismatch is a finding, grouped by § number. The review ends with a
|
|
91
|
+
VERDICT line.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Worked example
|
|
96
|
+
|
|
97
|
+
See the end of [Graph Protocol](/skill:graph-protocol) for a complete, non-TS worked
|
|
98
|
+
example (a resumable URL-sync CLI in Python) with all sections filled.
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: design-method
|
|
3
|
+
description: >
|
|
4
|
+
Design Thinking — the §1–§10 problem-first pipeline: shapes → graph(A) →
|
|
5
|
+
cardinality → break points (E: ⟳ ↯ ☠) → requirements (R) → boundaries →
|
|
6
|
+
behavior layers → scope → test layers → verdict. Stack-agnostic. Use when
|
|
7
|
+
planning, designing, or refactoring any feature or system.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Design Thinking — The Method
|
|
11
|
+
|
|
12
|
+
A stack-agnostic design methodology. Read the problem. Draw the data flow as a
|
|
13
|
+
call graph (the **Design Graph**). Write code that IS the graph. If the code
|
|
14
|
+
doesn't match the graph, something is wrong.
|
|
15
|
+
|
|
16
|
+
The method is language-neutral. Where it says "Effect", "gen", or "pipe" in the
|
|
17
|
+
Effect-TS appendix (`effect-ts.md`), substitute your stack's equivalent: Result
|
|
18
|
+
types, exceptions at layer joins, middleware, RAII, constructor injection —
|
|
19
|
+
whatever your language provides. The discipline never changes, only the vocabulary.
|
|
20
|
+
|
|
21
|
+
Render every result in [Graph Protocol](/skill:graph-protocol) format.
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
X → DesignGraph<A, E, R>
|
|
25
|
+
│ │ │ │ │
|
|
26
|
+
│ │ │ │ └─ what each node needs (§5)
|
|
27
|
+
│ │ │ └──── where the graph breaks (§4)
|
|
28
|
+
│ │ └─────── what flows through nodes (§2)
|
|
29
|
+
│ │
|
|
30
|
+
│ └─ nodes = functions, edges = data flow
|
|
31
|
+
│
|
|
32
|
+
└─ the problem: what you're trying to build
|
|
33
|
+
|
|
34
|
+
§1 Shapes the nouns: records, IDs, variants, errors
|
|
35
|
+
§2 A happy path as call graph
|
|
36
|
+
§3 Cardinality one-shot (1), many (N), or time-bounded (T)
|
|
37
|
+
§4 E break points: retry, escape, die
|
|
38
|
+
§5 R dependencies: compile-time or wiring-time proof
|
|
39
|
+
§6 Boundary parse: unknown → trusted at edges
|
|
40
|
+
§7 Behavior orthogonal layers wrap without changing the graph
|
|
41
|
+
§8 Scope acquire/release tied to lifecycle
|
|
42
|
+
§9 Test swap R, same graph shape
|
|
43
|
+
§10 Structure happy path = A, failure handling = E, never tangled
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 1. Name the shapes
|
|
47
|
+
|
|
48
|
+
What are the things? Before drawing a graph, define the domain language.
|
|
49
|
+
|
|
50
|
+
- **Records** — entities that flow through nodes. A User, a Product, an Order.
|
|
51
|
+
- **IDs** — identity of things. Branded, constrained, never a bare string.
|
|
52
|
+
- **Variants** — internal state transitions. A step is Continue or Finished.
|
|
53
|
+
A status is Pending, Active, or Cancelled.
|
|
54
|
+
- **Errors** — named failure modes. Not strings. Structured, carrying context.
|
|
55
|
+
|
|
56
|
+
These are the nouns. The graph is the verbs. You cannot draw the graph until
|
|
57
|
+
you know what flows through it.
|
|
58
|
+
|
|
59
|
+
→ *Protocol section: SHAPES.*
|
|
60
|
+
|
|
61
|
+
## 2. Think A first
|
|
62
|
+
|
|
63
|
+
Map the happy path as a call graph before writing any code.
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
F1(A) -> F2(A) -> F3(A)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
What is the success data flow? What goes in, what comes out, what transforms
|
|
70
|
+
happen in between? This graph IS your program structure. Draw it first. The
|
|
71
|
+
code follows.
|
|
72
|
+
|
|
73
|
+
→ *Protocol section: GRAPH (edges) + header tree (A).*
|
|
74
|
+
|
|
75
|
+
## 3. One or many?
|
|
76
|
+
|
|
77
|
+
Is each node one-shot or a flow?
|
|
78
|
+
|
|
79
|
+
- **One value** — the node runs, produces A, done. `(1)`
|
|
80
|
+
- **Many values over time** — the node emits A repeatedly: events,
|
|
81
|
+
subscriptions, paginated pulls, batches. `(N)`
|
|
82
|
+
- **Time-bounded** — the result is valid for a window. Cache it. Deduplicate
|
|
83
|
+
concurrent lookups. `(T)`
|
|
84
|
+
|
|
85
|
+
Same three channels (A, E, R) in all cases. Different cardinality. Mark it on
|
|
86
|
+
the graph so the code matches.
|
|
87
|
+
|
|
88
|
+
→ *Protocol section: CARDINALITY.*
|
|
89
|
+
|
|
90
|
+
## 4. Think E second
|
|
91
|
+
|
|
92
|
+
Mark where the graph can break. Each break point is one of three things:
|
|
93
|
+
|
|
94
|
+
- **Retry** `⟳` — transient failure, try again. Network timeout, rate limit,
|
|
95
|
+
connection reset.
|
|
96
|
+
- **Escape Hatch** `↯` — recoverable, return an alternative. Fallback value,
|
|
97
|
+
cached result, default, skip-and-continue.
|
|
98
|
+
- **Die** `☠` — defect, invariant violation, programmer bug. NOT a domain
|
|
99
|
+
error. Something is fundamentally wrong.
|
|
100
|
+
|
|
101
|
+
Failures are first-class values in the E channel until you truly cannot handle
|
|
102
|
+
them. They flow through the graph like data — whether your language expresses
|
|
103
|
+
that as Result types, checked exceptions, error returns, or a strict rule that
|
|
104
|
+
exceptions only cross at layer joins. You decide at each node: retry, escape,
|
|
105
|
+
or propagate. Only `die` when the program's assumptions are violated.
|
|
106
|
+
|
|
107
|
+
→ *Protocol section: GRAPH (E annotations) + VERDICT.*
|
|
108
|
+
|
|
109
|
+
## 5. Think R third
|
|
110
|
+
|
|
111
|
+
Mark what each node in the graph needs to exist. "We cannot do X if we don't
|
|
112
|
+
have Y."
|
|
113
|
+
|
|
114
|
+
R is proof that dependencies are satisfied — at compile time (injected via
|
|
115
|
+
constructors, generics, typeclasses, traits) or at wiring time (composition
|
|
116
|
+
root, DI container, context object). Every node declares what it requires: a
|
|
117
|
+
database connection, a config value, an HTTP client. R shrinks as you provide
|
|
118
|
+
layers. When R is empty, the program can run. If R is not empty, the compiler
|
|
119
|
+
or the wiring tells you exactly what's missing.
|
|
120
|
+
|
|
121
|
+
→ *Protocol section: GRAPH (R annotations) + TEST LAYERS.*
|
|
122
|
+
|
|
123
|
+
## 6. Trust at boundary
|
|
124
|
+
|
|
125
|
+
Where does untrusted data enter the graph? HTTP requests, file reads,
|
|
126
|
+
environment variables, user input, third-party API responses.
|
|
127
|
+
|
|
128
|
+
Convert `unknown → trusted` at the edges — a schema, a parsing constructor, a
|
|
129
|
+
validating factory, a DTO boundary. One definition = type + validator +
|
|
130
|
+
transformer. Define it once, use it everywhere.
|
|
131
|
+
|
|
132
|
+
Trust nothing at the boundary. Trust everything inside. The boundary is the
|
|
133
|
+
only place you parse. After that, the types guarantee shape.
|
|
134
|
+
|
|
135
|
+
→ *Protocol section: BOUNDARIES.*
|
|
136
|
+
|
|
137
|
+
## 7. Layer behavior
|
|
138
|
+
|
|
139
|
+
What cross-cutting concerns wrap nodes? Retry policies, timeouts, logging,
|
|
140
|
+
tracing, caching.
|
|
141
|
+
|
|
142
|
+
These wrap WITHOUT changing the core graph — middleware, decorators,
|
|
143
|
+
higher-order functions, aspects. The happy path stays clean: you can read it
|
|
144
|
+
without wading through retry logic and timeout configuration. Behavior is
|
|
145
|
+
composed orthogonally. The graph says WHAT happens. The layers say HOW it
|
|
146
|
+
behaves under pressure.
|
|
147
|
+
|
|
148
|
+
→ *Protocol section: BEHAVIOR.*
|
|
149
|
+
|
|
150
|
+
## 8. Scope resources
|
|
151
|
+
|
|
152
|
+
What nodes acquire resources? Database connections, file handles, WebSocket
|
|
153
|
+
connections, child processes, locks.
|
|
154
|
+
|
|
155
|
+
Acquire/release must be structurally bound: RAII, `defer`, context managers,
|
|
156
|
+
`try/finally` at the owning node — even on error, even on interrupt. Cleanup
|
|
157
|
+
is structural, not a TODO comment you hope someone remembers.
|
|
158
|
+
|
|
159
|
+
→ *Protocol section: SCOPE.*
|
|
160
|
+
|
|
161
|
+
## 9. Swap R to prove it
|
|
162
|
+
|
|
163
|
+
The call graph doesn't change between production and tests. Only R changes.
|
|
164
|
+
|
|
165
|
+
Same graph shape. Same A flowing through. Same E possible. Different layer
|
|
166
|
+
behind R. If the graph can't run with a test R, the design has hidden
|
|
167
|
+
dependencies. If you have to mock the world to test one node, the node is
|
|
168
|
+
doing too much.
|
|
169
|
+
|
|
170
|
+
This is the payoff of separating A, E, and R — you prove the graph correct by
|
|
171
|
+
swapping what's behind R.
|
|
172
|
+
|
|
173
|
+
→ *Protocol section: TEST LAYERS.*
|
|
174
|
+
|
|
175
|
+
## 10. Separate A from E structurally
|
|
176
|
+
|
|
177
|
+
The separation of A (§2) and E (§4) maps directly onto code structure, in any
|
|
178
|
+
language:
|
|
179
|
+
|
|
180
|
+
- **The happy path body** — one readable place. Every call is an A flowing
|
|
181
|
+
through the graph. No error handling inside. (In Effect-TS: the `Effect.gen`
|
|
182
|
+
body. Elsewhere: the main function, the do-notation block, the straight-line
|
|
183
|
+
pipeline of calls.)
|
|
184
|
+
- **The failure join points** — the complete E enumeration. Before writing
|
|
185
|
+
them, read the actual failure modes of every node on the happy path. The
|
|
186
|
+
join points catch, retry, or translate every E the happy path can produce.
|
|
187
|
+
(In Effect-TS: `.pipe()` after gen. Elsewhere: the middleware stack, the
|
|
188
|
+
Result chain, the try/catch at the layer boundary.)
|
|
189
|
+
|
|
190
|
+
This is not a stylistic preference. If error handling lives inside the happy
|
|
191
|
+
path body, the A path and E path are tangled — you cannot read the happy path
|
|
192
|
+
without wading through catches and retries. The happy path body IS the call
|
|
193
|
+
graph from §2. The join points ARE the error annotations from §4.
|
|
194
|
+
|
|
195
|
+
### E scoping at graph layers
|
|
196
|
+
|
|
197
|
+
Each layer scopes its own E before passing to the next layer. A service catches
|
|
198
|
+
storage errors and produces a domain error. Auth catches domain errors and
|
|
199
|
+
produces an access error. The handler catches access errors and produces the
|
|
200
|
+
declared interface error. Each layer's handling is a complete enumeration of
|
|
201
|
+
what IT received — not what originated three layers down. Consumers never see
|
|
202
|
+
implementation errors from deeper layers. *(See protocol.md for the diagram.)*
|
|
203
|
+
|
|
204
|
+
### Divergent strategies
|
|
205
|
+
|
|
206
|
+
When two nodes in the same happy path need different failure handling — one
|
|
207
|
+
should fail hard, the other should fall back gracefully — that is a
|
|
208
|
+
**divergent strategy**. Handle each node's E inline within the happy path at
|
|
209
|
+
the fork, because the outer join point cannot distinguish which node produced
|
|
210
|
+
which error. This is the ONE exception to "no error handling in the happy
|
|
211
|
+
path." It exists because the graph has a fork: two branches with different
|
|
212
|
+
failure semantics sharing a node. Mark it clearly — it should be rare.
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## The Pipeline
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
PROBLEM
|
|
220
|
+
-> "What are the shapes?" -> define the domain language
|
|
221
|
+
-> "What is the happy path?" -> draw the call graph (A)
|
|
222
|
+
-> "Is each node one-shot or a flow?" -> mark cardinality
|
|
223
|
+
-> "Where can it break?" -> annotate errors on the graph (E)
|
|
224
|
+
-> "What does each node need?" -> annotate requirements on the graph (R)
|
|
225
|
+
-> "Where does untrusted data enter?" -> parse at graph boundaries
|
|
226
|
+
-> "What wraps nodes without changing them?" -> layer behavior orthogonally
|
|
227
|
+
-> "What resources need cleanup?" -> scope lifecycle
|
|
228
|
+
-> "Can I swap R and the graph still works?" -> verify with test layers
|
|
229
|
+
-> "Does my code separate A from E structurally?" -> happy path = A, joins = E
|
|
230
|
+
-> CODE -> the code IS the graph
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**If the code doesn't match the call graph, the implementation is wrong.**
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
*The original Effect-TS instantiation of this method ships verbatim in the
|
|
238
|
+
package at `references/effect-ts.md` — for attribution and Effect-TS users
|
|
239
|
+
only. Everything above is stack-agnostic.*
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: graph-protocol
|
|
3
|
+
description: >
|
|
4
|
+
Graph Protocol — notation spec for Design Graphs. Fixed sections (PROBLEM,
|
|
5
|
+
SHAPES, GRAPH, CARDINALITY, BOUNDARIES, BEHAVIOR, SCOPE, TEST LAYERS,
|
|
6
|
+
VERDICT) and fixed markers (⟳retry ↯escape ☠die 🔒boundary (1)(N)(T) ⛈layer
|
|
7
|
+
@scope). Use when writing, reviewing, or checking any Design Graph so
|
|
8
|
+
notation is mechanically checkable and identical across languages.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Graph Protocol
|
|
12
|
+
|
|
13
|
+
The notation spec for **Design Graphs**. A Design Graph is the annotated call graph
|
|
14
|
+
of a concrete problem: nodes are functions, edges are data flow, and every node
|
|
15
|
+
carries annotations for data (A), cardinality, failure (E), and requirements (R).
|
|
16
|
+
|
|
17
|
+
The Protocol is stack-agnostic. A graph written for Go, Rust, Python, TypeScript,
|
|
18
|
+
SQL, or a spreadsheet renders identically. Only the vocabulary inside the
|
|
19
|
+
annotations changes (e.g. what "retry" means in your language).
|
|
20
|
+
|
|
21
|
+
Every tool in this package (`/cg`, `/cg-plan`, `/cg-review`, `/cg-map`, and the
|
|
22
|
+
`/dt` active mode) MUST emit output in the format below.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Canonical output skeleton
|
|
27
|
+
|
|
28
|
+
Sections appear in this fixed order. Omit a section only when it is genuinely
|
|
29
|
+
empty for the problem at hand (say so: `SCOPE: none`), never to skip thinking.
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
PROBLEM: <one-line statement of what you're building or examining>
|
|
33
|
+
|
|
34
|
+
X → DesignGraph<A, E, R>
|
|
35
|
+
│ │ │ │ │
|
|
36
|
+
│ │ │ │ └─ R: what each node needs (§5)
|
|
37
|
+
│ │ │ └──── E: where the graph breaks (§4)
|
|
38
|
+
│ │ └─────── A: what flows through nodes (§2)
|
|
39
|
+
│ │
|
|
40
|
+
│ └─ nodes = functions, edges = data flow
|
|
41
|
+
│
|
|
42
|
+
└─ the problem
|
|
43
|
+
|
|
44
|
+
SHAPES: <the nouns: records, IDs, variants, errors>
|
|
45
|
+
|
|
46
|
+
GRAPH:
|
|
47
|
+
<ascii call graph, annotated inline>
|
|
48
|
+
|
|
49
|
+
CARDINALITY: <node (1|N|T) list>
|
|
50
|
+
BOUNDARIES: <every edge crossing untrusted → trusted>
|
|
51
|
+
BEHAVIOR: <cross-cutting layers that wrap nodes>
|
|
52
|
+
SCOPE: <resources: acquire@… → release@…>
|
|
53
|
+
TEST LAYERS: <the R that tests provide — same GRAPH, zero code change>
|
|
54
|
+
VERDICT: <code matches graph? what is missing/tangled?>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## The header tree
|
|
60
|
+
|
|
61
|
+
The header names the problem and anchors the three channels. `X` is your problem;
|
|
62
|
+
everything after it is the graph you build.
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
X → DesignGraph<A, E, R>
|
|
66
|
+
│ │ │ │ │
|
|
67
|
+
│ │ │ │ └─ R: what each node needs (§5)
|
|
68
|
+
│ │ │ └──── E: where the graph breaks (§4)
|
|
69
|
+
│ │ └─────── A: what flows through nodes (§2)
|
|
70
|
+
│ │
|
|
71
|
+
│ └─ nodes = functions, edges = data flow
|
|
72
|
+
│
|
|
73
|
+
└─ the problem: what you're trying to build
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Section rules
|
|
79
|
+
|
|
80
|
+
### PROBLEM
|
|
81
|
+
|
|
82
|
+
One line. The thing you're building or the question you're answering. If you
|
|
83
|
+
cannot write it in one line, the problem is not understood — stop and refine.
|
|
84
|
+
|
|
85
|
+
### SHAPES
|
|
86
|
+
|
|
87
|
+
The domain nouns. Name them before drawing anything:
|
|
88
|
+
|
|
89
|
+
- **Records** — entities that flow through nodes (User, Order, Config).
|
|
90
|
+
- **IDs** — identity, never a bare primitive (UserId, Email).
|
|
91
|
+
- **Variants** — internal state transitions (pending|active|cancelled).
|
|
92
|
+
- **Errors** — named failure modes, structured, carrying context (not strings).
|
|
93
|
+
|
|
94
|
+
Format: comma-separated list. These are the nouns; the graph is the verbs.
|
|
95
|
+
|
|
96
|
+
### GRAPH
|
|
97
|
+
|
|
98
|
+
ASCII call graph. Nodes are functions/steps; edges are data flow. Annotate
|
|
99
|
+
**inline**, using the branch conventions from the header tree:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
parse → Handler → Service → Repo
|
|
103
|
+
│ │ │
|
|
104
|
+
│ │ └─ R: DbConn · (N)
|
|
105
|
+
│ └─ E: timeout ⟳retry×3 → ↯escape(cached)
|
|
106
|
+
└─ 🔒 HTTP body → trusted
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Inline annotation grammar:
|
|
110
|
+
|
|
111
|
+
| Annotation | Meaning | Example |
|
|
112
|
+
|---|---|---|
|
|
113
|
+
| `R: a, b` | what the node requires (§5) | `R: DbConn, Config` |
|
|
114
|
+
| `E: <err> ⟳retry[×n] → ↯escape(alt)` | failure handling (§4) | `E: NetErr ⟳retry×3 → ↯escape(fallback)` |
|
|
115
|
+
| `E: <err> ☠die` | defect, not domain error (§4) | `E: NullConfig ☠die` |
|
|
116
|
+
| `🔒 x → y` | untrusted → trusted boundary (§6) | `🔒 JSON → Settings` |
|
|
117
|
+
| `(1) (N) (T)` | cardinality (§3) | `(N)` = many over time |
|
|
118
|
+
| `⛈ name` | behavior layer wrapping the node (§7) | `⛈logging wraps Service` |
|
|
119
|
+
| `@scope` | resource bound to a scope (§8) | `DbConn @main` |
|
|
120
|
+
|
|
121
|
+
### CARDINALITY
|
|
122
|
+
|
|
123
|
+
One line listing each node's arity:
|
|
124
|
+
|
|
125
|
+
- `(1)` — one-shot: runs once, produces one value.
|
|
126
|
+
- `(N)` — many: emits values over time (stream, iterator, subscription, pages).
|
|
127
|
+
- `(T)` — time-bounded: result valid for a window; cache it, dedupe concurrent lookups.
|
|
128
|
+
|
|
129
|
+
Same three channels (A, E, R) in all cases. Different cardinality. The code must
|
|
130
|
+
match: don't implement a `(N)` node as a one-shot.
|
|
131
|
+
|
|
132
|
+
### BOUNDARIES
|
|
133
|
+
|
|
134
|
+
Every place untrusted data enters the graph: HTTP requests, file reads, env
|
|
135
|
+
vars, user input, third-party responses. The rule: **parse, don't validate** —
|
|
136
|
+
convert `unknown → trusted` once, at the edge, then trust the types everywhere
|
|
137
|
+
inside. One definition per shape = type + validator + transformer, reused
|
|
138
|
+
everywhere.
|
|
139
|
+
|
|
140
|
+
### BEHAVIOR
|
|
141
|
+
|
|
142
|
+
Cross-cutting concerns that wrap nodes WITHOUT changing the core graph:
|
|
143
|
+
retry policies, timeouts, logging, tracing, caching. Composed orthogonally
|
|
144
|
+
(middleware, decorators, higher-order wrappers, aspects). The graph says WHAT
|
|
145
|
+
happens; the layers say HOW it behaves under pressure. If removing logging
|
|
146
|
+
forces you to edit business logic, behavior is not orthogonal.
|
|
147
|
+
|
|
148
|
+
### SCOPE
|
|
149
|
+
|
|
150
|
+
Resources that must be released: connections, file handles, sockets, child
|
|
151
|
+
processes. Acquire/release must be structurally bound (RAII, `defer`, context
|
|
152
|
+
managers, `try/finally`) — never convention or a TODO. Notation:
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
SCOPE: DbConn acquire@main → release@main · WS acquire@Handler → release@Handler
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### TEST LAYERS
|
|
159
|
+
|
|
160
|
+
The R that tests provide. The call graph does not change between production and
|
|
161
|
+
tests — only R changes. Same graph, same A, same E, different layers behind R.
|
|
162
|
+
If the graph can't run with a test R, the design has hidden dependencies. If
|
|
163
|
+
you must mock the world to test one node, the node does too much.
|
|
164
|
+
|
|
165
|
+
### VERDICT
|
|
166
|
+
|
|
167
|
+
The structural check. Answer explicitly:
|
|
168
|
+
|
|
169
|
+
- Is the happy path (A) readable on its own, free of failure handling?
|
|
170
|
+
- Is every E the graph can produce handled at a defined join point?
|
|
171
|
+
- Do per-layer E scopes translate errors (inner errors never leak through)?
|
|
172
|
+
- Does the code match the graph? **If the code doesn't match the call graph,
|
|
173
|
+
the implementation is wrong.**
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## E scoping at graph layers
|
|
178
|
+
|
|
179
|
+
Each layer scopes its own E before passing to the next:
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
Services → Auth → Handlers
|
|
183
|
+
E=SqlErr E=DbErr E=AuthErr E=RPC errs
|
|
184
|
+
↓ scope ↓ scope ↓ scope
|
|
185
|
+
DbErr AuthErr RPC errs
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
A service catches `SqlErr` and produces `DbErr`. Auth catches `DbErr` and
|
|
189
|
+
produces `AuthErr`. The handler catches `AuthErr` and produces the declared
|
|
190
|
+
error. Each layer's handling enumerates what IT receives — never what originated
|
|
191
|
+
three layers down. Consumers never see implementation errors from deeper layers.
|
|
192
|
+
|
|
193
|
+
## Divergent strategies (the one exception)
|
|
194
|
+
|
|
195
|
+
When two nodes in the same happy path need different failure semantics — one
|
|
196
|
+
must fail hard, another must fall back gracefully — handle that node's E
|
|
197
|
+
inline at the fork, because the outer layer cannot distinguish which node
|
|
198
|
+
produced which error. Mark it clearly; it should be rare.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Worked example (non-TS: Python CLI)
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
PROBLEM: sync a list of URLs to local files, resumable, with a status line
|
|
206
|
+
|
|
207
|
+
X → DesignGraph<A, E, R>
|
|
208
|
+
│ │ │ │ │
|
|
209
|
+
│ │ │ │ └─ R: Http, Clock, Logger (§5)
|
|
210
|
+
│ │ │ └──── E: NetErr, DiskErr (§4)
|
|
211
|
+
│ │ └─────── A: (url, bytes), Status (§2)
|
|
212
|
+
│ │
|
|
213
|
+
│ └─ nodes = functions, edges = data flow
|
|
214
|
+
│
|
|
215
|
+
└─ the problem
|
|
216
|
+
|
|
217
|
+
SHAPES: Url, Target(path), Downloaded(url, bytes), Status(pending|done|failed), NetErr, DiskErr
|
|
218
|
+
|
|
219
|
+
GRAPH:
|
|
220
|
+
load_state → plan → fetch … download → write → report
|
|
221
|
+
│ │ │ │
|
|
222
|
+
│ │ ├─ E: NetErr ⟳retry×3(-backoff) → ↯escape(skip, mark failed)
|
|
223
|
+
│ │ └─ 🔒 raw bytes → Downloaded (size, content-type checked)
|
|
224
|
+
│ └─ R: StateFile · (1)
|
|
225
|
+
└─ 🔒 state file → trusted (schema-validated; corrupt → ↯escape(empty, warn))
|
|
226
|
+
|
|
227
|
+
CARDINALITY: load_state (1) · plan (1) · fetch (N) · download (1) · write (1) · report (T)
|
|
228
|
+
BOUNDARIES: load_state 🔒 file → State · fetch 🔒 wire → Downloaded
|
|
229
|
+
BEHAVIOR: ⛈retry wraps fetch · ⛈rate-limit wraps fetch · ⛈progress wraps write
|
|
230
|
+
SCOPE: Http acquire@main → release@main · StateFile @main (context manager)
|
|
231
|
+
TEST LAYERS: R = {Http: FakeHttp, Clock: FrozenClock, Logger: Silent} — same GRAPH, no stubs in nodes
|
|
232
|
+
VERDICT: happy path reads as the pipeline above; all NetErr handling lives at the
|
|
233
|
+
write/report join, not inside download; state corruption escapes at the edge.
|
|
234
|
+
Matches graph → implementation valid.
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Note the stack vocabulary: `context manager` for scope, exceptions caught at
|
|
238
|
+
layer joins for E, plain functions for nodes. Protocol unchanged.
|