lulz 1.0.3 → 2.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/AGENTS.md +609 -0
- package/README.md +242 -140
- package/TODO.md +132 -0
- package/examples.js +169 -197
- package/index.js +164 -14
- package/package.json +16 -17
- package/src/flow.js +362 -215
- package/src/red-lib.js +595 -0
- package/src/rx-lib.js +679 -0
- package/src/utils.js +270 -0
- package/src/workers.js +367 -0
- package/test.js +505 -279
- package/src/nodes.js +0 -520
package/AGENTS.md
ADDED
|
@@ -0,0 +1,609 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
## Flow-First Programming: A Manifesto
|
|
4
|
+
|
|
5
|
+
> Software has always been about moving information.
|
|
6
|
+
> We simply forgot to draw the pipes.
|
|
7
|
+
|
|
8
|
+
This document describes **flow-first programming**: a discipline where programs are expressed as readable graphs of **pipes** (facts) and **motions** (transformations).
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 0. The Problem We Forgot We Had
|
|
13
|
+
|
|
14
|
+
Most software is a **lie detector's nightmare**.
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
// Traditional code: What is happening?
|
|
18
|
+
if (user && user.isValid && !user.banned) {
|
|
19
|
+
const order = processOrder(cart);
|
|
20
|
+
if (order.success) {
|
|
21
|
+
if (payment.charge(order.total)) {
|
|
22
|
+
// 47 more nested conditions...
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
This code **hides its shape**. You cannot see:
|
|
29
|
+
- What paths exist
|
|
30
|
+
- What outcomes are possible
|
|
31
|
+
- Where errors go to die
|
|
32
|
+
|
|
33
|
+
Flow-first programming has one rule:
|
|
34
|
+
|
|
35
|
+
> **If you can draw it, you can debug it.**
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
request ──→ validate ──→ price ──→ charge ──→ 'paid'
|
|
39
|
+
│ │
|
|
40
|
+
└──→ 'invalid' └──→ 'declined'
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The program **is** the diagram. The diagram **is** the program.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 1. What This Is (and Is Not)
|
|
48
|
+
|
|
49
|
+
This is **not**:
|
|
50
|
+
- an AI abstraction
|
|
51
|
+
- a fancy DSL
|
|
52
|
+
- a new syntax to learn
|
|
53
|
+
|
|
54
|
+
This **is**:
|
|
55
|
+
- a way to see your program's skeleton
|
|
56
|
+
- a shared language for humans and LLMs
|
|
57
|
+
- a method that scales from scripts to systems
|
|
58
|
+
- the spiritual successor to Unix pipes and FFmpeg filtergraphs
|
|
59
|
+
|
|
60
|
+
**The test**: Can a stranger read your program aloud and understand it?
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 2. The Two Symbols That Matter
|
|
65
|
+
|
|
66
|
+
Every flow-first program uses exactly two concepts:
|
|
67
|
+
|
|
68
|
+
### 2.1 Pipes — Named Facts (Nouns)
|
|
69
|
+
|
|
70
|
+
A **pipe** is a named checkpoint. A promise kept.
|
|
71
|
+
|
|
72
|
+
Written as a **quoted string**:
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
'request' // An HTTP request arrived
|
|
76
|
+
'validated' // Data passed all checks
|
|
77
|
+
'paid' // Money changed hands
|
|
78
|
+
'published' // The world can see it
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Pipes are contracts.** When you see one, you can say:
|
|
82
|
+
|
|
83
|
+
> "This happened. I can depend on it."
|
|
84
|
+
|
|
85
|
+
Think of pipes as **milestones on a highway**. They tell you where you are.
|
|
86
|
+
|
|
87
|
+
### 2.2 Motions — Transformations (Verbs)
|
|
88
|
+
|
|
89
|
+
Anything **unquoted** is motion—work being done:
|
|
90
|
+
|
|
91
|
+
```js
|
|
92
|
+
validate // check the data
|
|
93
|
+
calculate // compute something
|
|
94
|
+
save // persist to storage
|
|
95
|
+
notify // tell someone
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Motions are **honest about uncertainty**. They might:
|
|
99
|
+
- Transform the packet and pass it on
|
|
100
|
+
- Drop the packet silently (this is how branching works!)
|
|
101
|
+
- Create errors
|
|
102
|
+
|
|
103
|
+
**Motions do not promise. Pipes do.**
|
|
104
|
+
|
|
105
|
+
### The Shape of a Line
|
|
106
|
+
|
|
107
|
+
Every line follows this pattern:
|
|
108
|
+
|
|
109
|
+
```js
|
|
110
|
+
['source', motion, motion, motion, 'destination']
|
|
111
|
+
// noun verb verb verb noun
|
|
112
|
+
|
|
113
|
+
// Read it like English:
|
|
114
|
+
// "From source, do this, then this, then this, arriving at destination."
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 3. The Golden Rules
|
|
120
|
+
|
|
121
|
+
### Rule 1: Producers Begin, Pipes End
|
|
122
|
+
|
|
123
|
+
**Producers** create packets from the outside world:
|
|
124
|
+
- `http` — incoming web requests
|
|
125
|
+
- `stdin` — terminal input
|
|
126
|
+
- `watch` — file changes
|
|
127
|
+
- `socket` — real-time messages
|
|
128
|
+
- `timer` — scheduled events
|
|
129
|
+
|
|
130
|
+
Producers appear **only at the start** of a feature:
|
|
131
|
+
|
|
132
|
+
```js
|
|
133
|
+
[http, route, 'request'] // ✓ Producer starts the flow
|
|
134
|
+
['request', process, 'done'] // ✓ Pipe starts subsequent flows
|
|
135
|
+
[process, 'done'] // ✗ Motion cannot start a flow
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Rule 2: Every Feature Must Name a Pipe
|
|
139
|
+
|
|
140
|
+
A feature isn't done when it runs. It's done when it **commits to a name**.
|
|
141
|
+
|
|
142
|
+
```js
|
|
143
|
+
// Bad: Where does this go? What can depend on it?
|
|
144
|
+
['request', handle]
|
|
145
|
+
|
|
146
|
+
// Good: Clear commitment
|
|
147
|
+
['request', handle, 'response']
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Rule 3: Motions Drop, Never Branch
|
|
151
|
+
|
|
152
|
+
There is no `if`. There is no `switch`. There is only:
|
|
153
|
+
|
|
154
|
+
> **Forward what you understand. Drop what you don't.**
|
|
155
|
+
|
|
156
|
+
```js
|
|
157
|
+
['request', isGetRequest, loadPage, 'page']
|
|
158
|
+
['request', isPostRequest, saveData, 'saved']
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Both lines listen to `'request'`. Each motion decides:
|
|
162
|
+
- "Is this for me? → Forward it."
|
|
163
|
+
- "Not for me? → Drop it silently."
|
|
164
|
+
|
|
165
|
+
**This is how branching works.** The packet finds its own path.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## 4. Reading Flows Aloud
|
|
170
|
+
|
|
171
|
+
If you can read it aloud, you can understand it.
|
|
172
|
+
|
|
173
|
+
```js
|
|
174
|
+
const wiki = flow([
|
|
175
|
+
[http, route, 'request'],
|
|
176
|
+
['request', loadPage, render, 'viewed'], // loadPage is both a test isLoadPage and page loader, it simply does nothing if not load page.
|
|
177
|
+
['request', savePage, confirm, 'saved'],
|
|
178
|
+
]);
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Read it:
|
|
182
|
+
|
|
183
|
+
> "HTTP requests become requests.
|
|
184
|
+
> Requests that load pages get rendered and become viewed.
|
|
185
|
+
> Requests that save pages get confirmed and become saved."
|
|
186
|
+
|
|
187
|
+
No translation needed. No mental compiler required.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## 5. The Packet's Journey (A Mental Model)
|
|
192
|
+
|
|
193
|
+
Imagine a **letter** traveling through a postal system.
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
197
|
+
│ │
|
|
198
|
+
│ 📧 Packet │
|
|
199
|
+
│ ┌──────────────────────┐ │
|
|
200
|
+
│ │ payload: "Hello" │ │
|
|
201
|
+
│ │ topic: "greeting" │ │
|
|
202
|
+
│ │ _timestamp: 17283... │ │
|
|
203
|
+
│ └──────────────────────┘ │
|
|
204
|
+
│ │
|
|
205
|
+
│ The packet travels through motions: │
|
|
206
|
+
│ │
|
|
207
|
+
│ validate ──→ transform ──→ save │
|
|
208
|
+
│ │ │ │ │
|
|
209
|
+
│ │ │ └─→ Adds { saved: true } │
|
|
210
|
+
│ │ └─→ Uppercases payload │
|
|
211
|
+
│ └─→ Checks format, drops if invalid │
|
|
212
|
+
│ │
|
|
213
|
+
└─────────────────────────────────────────────────────────────┘
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Each motion can:
|
|
217
|
+
1. **Pass it through** unchanged
|
|
218
|
+
2. **Enrich it** with new data
|
|
219
|
+
3. **Transform it** entirely
|
|
220
|
+
4. **Drop it** (the packet simply stops)
|
|
221
|
+
|
|
222
|
+
When the packet reaches a pipe, **that pipe emits**. Listeners wake up.
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## 6. Patterns That Emerge
|
|
227
|
+
|
|
228
|
+
### 6.1 The Funnel (Validation)
|
|
229
|
+
|
|
230
|
+
```js
|
|
231
|
+
['input',
|
|
232
|
+
notEmpty, // drops empty
|
|
233
|
+
isValidEmail, // drops invalid format
|
|
234
|
+
notDisposable, // drops throwaway domains
|
|
235
|
+
'validEmail'
|
|
236
|
+
]
|
|
237
|
+
// Only survivors reach 'validEmail'
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### 6.2 The Fork (Routing)
|
|
241
|
+
|
|
242
|
+
```js
|
|
243
|
+
['request', isApi, handleApi, 'apiResponse']
|
|
244
|
+
['request', isPage, renderPage, 'pageResponse']
|
|
245
|
+
['request', isAsset, serveFile, 'assetResponse']
|
|
246
|
+
// Each packet takes exactly one path
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### 6.3 The Merge (Convergence)
|
|
250
|
+
|
|
251
|
+
```js
|
|
252
|
+
['apiResponse', format, 'response']
|
|
253
|
+
['pageResponse', format, 'response']
|
|
254
|
+
['assetResponse', format, 'response']
|
|
255
|
+
// All roads lead to 'response'
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### 6.4 The Fan (Parallel Effects)
|
|
259
|
+
|
|
260
|
+
```js
|
|
261
|
+
['order', [
|
|
262
|
+
sendEmail, // notify customer
|
|
263
|
+
updateStock, // adjust inventory
|
|
264
|
+
logAnalytics, // record metrics
|
|
265
|
+
], 'processed']
|
|
266
|
+
// All three happen simultaneously
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### 6.5 The Saga (Multi-Step Process)
|
|
270
|
+
|
|
271
|
+
```js
|
|
272
|
+
['checkout',
|
|
273
|
+
validateCart,
|
|
274
|
+
reserveStock,
|
|
275
|
+
chargePayment,
|
|
276
|
+
confirmOrder,
|
|
277
|
+
'completed'
|
|
278
|
+
]
|
|
279
|
+
// Each step must succeed for the next to run
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## 7. TODOs Instead of Errors
|
|
285
|
+
|
|
286
|
+
Missing code should not block understanding.
|
|
287
|
+
|
|
288
|
+
The `todo` prefix turns unimplemented features into **work queues**:
|
|
289
|
+
|
|
290
|
+
```js
|
|
291
|
+
const wiki = flow([
|
|
292
|
+
[todo.http, todo.route, 'request'],
|
|
293
|
+
['request', todo.loadPage, todo.render, 'viewed'],
|
|
294
|
+
['request', todo.savePage, todo.confirm, 'saved'],
|
|
295
|
+
]);
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
This flow **runs**. It **reads correctly**. It just doesn't do anything yet.
|
|
299
|
+
|
|
300
|
+
You can hand this to an LLM and say: "Implement `todo.loadPage`."
|
|
301
|
+
|
|
302
|
+
The shape is preserved. The poem remains intact.
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## 8. Examples (Annotated for Beginners)
|
|
307
|
+
|
|
308
|
+
### 8.1 Static Web Server
|
|
309
|
+
|
|
310
|
+
```js
|
|
311
|
+
const server = flow([
|
|
312
|
+
|
|
313
|
+
// ─── Entry Point ─────────────────────────────
|
|
314
|
+
[http, route, 'request'],
|
|
315
|
+
// http: produces packets from incoming connections
|
|
316
|
+
// route: parses URL, method, headers
|
|
317
|
+
// 'request': guaranteed to be a valid, parsed request
|
|
318
|
+
|
|
319
|
+
// ─── Static Files ────────────────────────────
|
|
320
|
+
['request',
|
|
321
|
+
isStaticFile, // drops non-file requests, nothing is sent to readFile
|
|
322
|
+
readFile, // loads from disk (drops if missing)
|
|
323
|
+
detectMime, // adds content-type
|
|
324
|
+
respond, // sends to client
|
|
325
|
+
'served'
|
|
326
|
+
],
|
|
327
|
+
// 'served': a file was successfully delivered
|
|
328
|
+
|
|
329
|
+
]);
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### 8.2 Pastebin
|
|
333
|
+
|
|
334
|
+
```js
|
|
335
|
+
const pastebin = flow([
|
|
336
|
+
|
|
337
|
+
[http, route, 'request'],
|
|
338
|
+
|
|
339
|
+
// ─── Create Paste ────────────────────────────
|
|
340
|
+
['request',
|
|
341
|
+
isPost, // drops GET requests
|
|
342
|
+
parseBody, // extracts paste content
|
|
343
|
+
generateId, // creates unique identifier
|
|
344
|
+
store, // saves to database
|
|
345
|
+
'stored'
|
|
346
|
+
],
|
|
347
|
+
|
|
348
|
+
// ─── View Paste ──────────────────────────────
|
|
349
|
+
['request',
|
|
350
|
+
isGet, // drops POST requests
|
|
351
|
+
extractId, // gets paste ID from URL
|
|
352
|
+
retrieve, // loads from database (drops if missing)
|
|
353
|
+
'found'
|
|
354
|
+
],
|
|
355
|
+
|
|
356
|
+
// ─── Responses ───────────────────────────────
|
|
357
|
+
['stored', respondWithLink, 'done'],
|
|
358
|
+
['found', respondWithContent, 'done'],
|
|
359
|
+
|
|
360
|
+
]);
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### 8.3 Wiki
|
|
364
|
+
|
|
365
|
+
```js
|
|
366
|
+
const wiki = flow([
|
|
367
|
+
|
|
368
|
+
[http, route, 'request'],
|
|
369
|
+
|
|
370
|
+
// ─── View Page ───────────────────────────────
|
|
371
|
+
['request',
|
|
372
|
+
isViewRequest, // drops edits and saves
|
|
373
|
+
loadPage, // fetches from storage
|
|
374
|
+
renderMarkdown, // converts to HTML
|
|
375
|
+
wrapInLayout, // adds header/footer
|
|
376
|
+
'viewResponse'
|
|
377
|
+
],
|
|
378
|
+
|
|
379
|
+
// ─── Edit Page ───────────────────────────────
|
|
380
|
+
['request',
|
|
381
|
+
isEditRequest, // drops views and saves
|
|
382
|
+
loadPage, // fetches current content
|
|
383
|
+
renderEditor, // shows edit form
|
|
384
|
+
'editResponse'
|
|
385
|
+
],
|
|
386
|
+
|
|
387
|
+
// ─── Save Page ───────────────────────────────
|
|
388
|
+
['request',
|
|
389
|
+
isSaveRequest, // drops views and edits
|
|
390
|
+
parseChanges, // extracts new content
|
|
391
|
+
validateContent, // checks for spam, etc.
|
|
392
|
+
saveToHistory, // preserves old version
|
|
393
|
+
saveToStorage, // writes new version
|
|
394
|
+
'saveResponse'
|
|
395
|
+
],
|
|
396
|
+
|
|
397
|
+
// ─── Send Response ───────────────────────────
|
|
398
|
+
['viewResponse', respond, 'done'],
|
|
399
|
+
['editResponse', respond, 'done'],
|
|
400
|
+
['saveResponse', respondWithRedirect, 'done'],
|
|
401
|
+
|
|
402
|
+
]);
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### 8.4 Coding Agent
|
|
406
|
+
|
|
407
|
+
```js
|
|
408
|
+
const agent = flow([
|
|
409
|
+
|
|
410
|
+
// ─── Intake ──────────────────────────────────
|
|
411
|
+
[stdin, clean, parse, 'order'],
|
|
412
|
+
// User's raw input becomes a structured order
|
|
413
|
+
|
|
414
|
+
// ─── Negotiation ─────────────────────────────
|
|
415
|
+
['order',
|
|
416
|
+
understand, // LLM comprehends intent
|
|
417
|
+
plan, // LLM creates action plan
|
|
418
|
+
confirm('Proceed?'), // Human approves or rejects
|
|
419
|
+
'decision'
|
|
420
|
+
],
|
|
421
|
+
|
|
422
|
+
['decision', approved, 'greenlight'],
|
|
423
|
+
['decision', refused, 'rejected'],
|
|
424
|
+
|
|
425
|
+
// ─── Execution ───────────────────────────────
|
|
426
|
+
['greenlight',
|
|
427
|
+
prepare, // set up environment
|
|
428
|
+
execute, // run the plan
|
|
429
|
+
observe, // check results
|
|
430
|
+
'result'
|
|
431
|
+
],
|
|
432
|
+
|
|
433
|
+
// ─── Recovery ────────────────────────────────
|
|
434
|
+
['rejected',
|
|
435
|
+
explain, // clarify what was misunderstood
|
|
436
|
+
rephrase, // offer alternative
|
|
437
|
+
'proposal'
|
|
438
|
+
],
|
|
439
|
+
|
|
440
|
+
// ─── Output ──────────────────────────────────
|
|
441
|
+
['result', summarize, format, 'output'],
|
|
442
|
+
['proposal', format, 'output'],
|
|
443
|
+
|
|
444
|
+
['output', print, 'spoken'],
|
|
445
|
+
|
|
446
|
+
]);
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
Delete every function body. **You still understand the system.**
|
|
450
|
+
|
|
451
|
+
That is the point.
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
## 9. Common Mistakes (And How to Fix Them)
|
|
456
|
+
|
|
457
|
+
### Mistake 1: Returning Instead of Sending
|
|
458
|
+
|
|
459
|
+
```js
|
|
460
|
+
// Wrong: Functions don't return
|
|
461
|
+
function double(packet) {
|
|
462
|
+
return { ...packet, payload: packet.payload * 2 };
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Right: Functions send forward
|
|
466
|
+
function double(options) {
|
|
467
|
+
return (send, packet) => {
|
|
468
|
+
send({ ...packet, payload: packet.payload * 2 });
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
### Mistake 2: Explicit Branching
|
|
474
|
+
|
|
475
|
+
```js
|
|
476
|
+
// Wrong: Traditional if/else thinking
|
|
477
|
+
function router(send, packet) {
|
|
478
|
+
if (packet.method === 'GET') {
|
|
479
|
+
send({ ...packet, route: 'get' });
|
|
480
|
+
} else if (packet.method === 'POST') {
|
|
481
|
+
send({ ...packet, route: 'post' });
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Right: Separate motions that drop
|
|
486
|
+
function isGet(options) {
|
|
487
|
+
return (send, packet) => {
|
|
488
|
+
if (packet.method === 'GET') send(packet);
|
|
489
|
+
// Otherwise: silence (packet dropped)
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function isPost(options) {
|
|
494
|
+
return (send, packet) => {
|
|
495
|
+
if (packet.method === 'POST') send(packet);
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Usage: each path is its own line
|
|
500
|
+
['request', isGet, handleGet, 'response']
|
|
501
|
+
['request', isPost, handlePost, 'response']
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### Mistake 3: Pipes in the Middle
|
|
505
|
+
|
|
506
|
+
```js
|
|
507
|
+
// Wrong: Pipe mid-sentence
|
|
508
|
+
['request', validate, 'validated', transform, 'result']
|
|
509
|
+
|
|
510
|
+
// Right: One pipe per line
|
|
511
|
+
['request', validate, 'validated']
|
|
512
|
+
['validated', transform, 'result']
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
### Mistake 4: Unnamed Endings
|
|
516
|
+
|
|
517
|
+
```js
|
|
518
|
+
// Wrong: Where does this go?
|
|
519
|
+
['request', process, notify]
|
|
520
|
+
|
|
521
|
+
// Right: Commit to a name
|
|
522
|
+
['request', process, notify, 'notified']
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
---
|
|
526
|
+
|
|
527
|
+
## 10. Why This Works
|
|
528
|
+
|
|
529
|
+
### For Humans
|
|
530
|
+
|
|
531
|
+
- **Readability**: Programs read like sentences
|
|
532
|
+
- **Debuggability**: Add a `tap` anywhere to see packets
|
|
533
|
+
- **Refactorability**: Move lines around without breaking logic
|
|
534
|
+
- **Onboarding**: New developers understand the system in minutes
|
|
535
|
+
|
|
536
|
+
### For LLMs
|
|
537
|
+
|
|
538
|
+
- **Structured**: Clear grammar, predictable patterns
|
|
539
|
+
- **Bounded**: Each motion is a small, focused task
|
|
540
|
+
- **Testable**: Easy to verify individual transformations
|
|
541
|
+
- **Extensible**: "Add a motion after X" is unambiguous
|
|
542
|
+
|
|
543
|
+
### For Systems
|
|
544
|
+
|
|
545
|
+
- **Composable**: Flows embed inside flows
|
|
546
|
+
- **Observable**: Every pipe is an event you can monitor
|
|
547
|
+
- **Resilient**: Dropped packets don't crash the system
|
|
548
|
+
- **Parallelizable**: Independent paths run concurrently
|
|
549
|
+
|
|
550
|
+
---
|
|
551
|
+
|
|
552
|
+
## 11. The Philosophy
|
|
553
|
+
|
|
554
|
+
Flow-first programming rests on three beliefs:
|
|
555
|
+
|
|
556
|
+
### Belief 1: Shape Over Syntax
|
|
557
|
+
|
|
558
|
+
The **topology** of a program matters more than its tokens.
|
|
559
|
+
If you can see the shape, you can reason about behavior.
|
|
560
|
+
|
|
561
|
+
### Belief 2: Silence Over Exceptions
|
|
562
|
+
|
|
563
|
+
When a motion doesn't apply, it drops the packet.
|
|
564
|
+
No errors. No catches. No noise.
|
|
565
|
+
The packet simply takes another path—or no path.
|
|
566
|
+
|
|
567
|
+
### Belief 3: Names Over Comments
|
|
568
|
+
|
|
569
|
+
A well-named pipe is worth a thousand comments.
|
|
570
|
+
When `'validated'` appears, no explanation is needed.
|
|
571
|
+
The name carries the meaning.
|
|
572
|
+
|
|
573
|
+
---
|
|
574
|
+
|
|
575
|
+
## 12. Getting Started
|
|
576
|
+
|
|
577
|
+
1. **Draw first**. Sketch the flow on paper before coding.
|
|
578
|
+
2. **Name your pipes**. What are the important states?
|
|
579
|
+
3. **List your motions**. What transformations connect them?
|
|
580
|
+
4. **Wire it up**. Write the flow array.
|
|
581
|
+
5. **Implement motions**. One small function at a time.
|
|
582
|
+
|
|
583
|
+
Start with `todo.` prefixes. Let the shape emerge.
|
|
584
|
+
Fill in the implementations later.
|
|
585
|
+
|
|
586
|
+
The program will tell you what it needs.
|
|
587
|
+
|
|
588
|
+
---
|
|
589
|
+
|
|
590
|
+
## 13. A Final Word
|
|
591
|
+
|
|
592
|
+
> Programs should be written for people to read,
|
|
593
|
+
> and only incidentally for machines to execute.
|
|
594
|
+
> — Abelson & Sussman
|
|
595
|
+
|
|
596
|
+
Flow-first programming takes this seriously.
|
|
597
|
+
|
|
598
|
+
The graph is the spec.
|
|
599
|
+
The graph is the documentation.
|
|
600
|
+
The graph is the test plan.
|
|
601
|
+
The graph is the program.
|
|
602
|
+
|
|
603
|
+
Draw your pipes.
|
|
604
|
+
Name your truths.
|
|
605
|
+
Let the packets flow.
|
|
606
|
+
|
|
607
|
+
---
|
|
608
|
+
|
|
609
|
+
*End of AGENTS.md*
|