@manifesto-ai/skills 0.1.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 +21 -0
- package/SKILL.md +40 -0
- package/claude-code/CLAUDE.md +28 -0
- package/knowledge/antipatterns.md +256 -0
- package/knowledge/architecture.md +160 -0
- package/knowledge/effect-patterns.md +187 -0
- package/knowledge/mel-patterns.md +234 -0
- package/knowledge/patch-rules.md +141 -0
- package/knowledge/spec-index.md +105 -0
- package/package.json +30 -0
- package/scripts/postinstall.mjs +25 -0
- package/tracking/source-map.json +67 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Manifesto AI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Manifesto Skills v0.1
|
|
2
|
+
|
|
3
|
+
You are working on a Manifesto-based project. These rules are non-negotiable.
|
|
4
|
+
|
|
5
|
+
## Absolute Rules
|
|
6
|
+
|
|
7
|
+
1. **Core is pure.** No IO, no `Date.now()`, no side effects, no randomness. `compute()` is a pure function.
|
|
8
|
+
2. **Snapshot is the only medium.** All communication between computations happens through Snapshot. No hidden channels, no return values from effects.
|
|
9
|
+
3. **Three patch ops only.** `set`, `unset`, `merge` (shallow). No other mutation operations exist.
|
|
10
|
+
4. **Effects are declarations.** Core declares requirements; Host executes them. Core never performs IO.
|
|
11
|
+
5. **Errors are values.** Errors live in Snapshot state, never thrown. Core MUST NOT throw for business logic.
|
|
12
|
+
6. **Flows terminate.** No unbounded loops in Flow. Host controls iteration. All guards required for re-entry safety.
|
|
13
|
+
7. **`$` is reserved.** `$host`, `$mel`, `$system` are platform namespaces. Never use `$` in domain identifiers.
|
|
14
|
+
|
|
15
|
+
## Normative Hierarchy
|
|
16
|
+
|
|
17
|
+
SPEC > FDR > ADR > Code > README. Never invent semantics not in SPEC.
|
|
18
|
+
|
|
19
|
+
## Task-Specific Knowledge
|
|
20
|
+
|
|
21
|
+
Load these BEFORE writing code in each area:
|
|
22
|
+
|
|
23
|
+
| Task | Knowledge File |
|
|
24
|
+
|------|---------------|
|
|
25
|
+
| Understanding Core/Host/World boundaries | `@knowledge/architecture.md` |
|
|
26
|
+
| Writing MEL domain code | `@knowledge/mel-patterns.md` |
|
|
27
|
+
| Implementing effect handlers | `@knowledge/effect-patterns.md` |
|
|
28
|
+
| Working with state/patches | `@knowledge/patch-rules.md` |
|
|
29
|
+
| Reviewing or debugging | `@knowledge/antipatterns.md` |
|
|
30
|
+
| Looking up SPEC references | `@knowledge/spec-index.md` |
|
|
31
|
+
|
|
32
|
+
## Self-Check
|
|
33
|
+
|
|
34
|
+
Before submitting any code change, verify:
|
|
35
|
+
|
|
36
|
+
- [ ] Determinism preserved? (same input → same output)
|
|
37
|
+
- [ ] Snapshot is sole communication medium?
|
|
38
|
+
- [ ] All mutations via patches?
|
|
39
|
+
- [ ] No forbidden imports across package boundaries?
|
|
40
|
+
- [ ] Flow guards present for re-entry safety?
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Manifesto Development Rules
|
|
2
|
+
|
|
3
|
+
This project uses the Manifesto framework. Read the rules below before writing any code.
|
|
4
|
+
|
|
5
|
+
## Entry Point
|
|
6
|
+
|
|
7
|
+
See @../SKILL.md for absolute rules and task-specific knowledge routing.
|
|
8
|
+
|
|
9
|
+
## Knowledge Files
|
|
10
|
+
|
|
11
|
+
Load the relevant file BEFORE writing code:
|
|
12
|
+
|
|
13
|
+
- Architecture & boundaries: @../knowledge/architecture.md
|
|
14
|
+
- Writing MEL code: @../knowledge/mel-patterns.md
|
|
15
|
+
- Effect handler implementation: @../knowledge/effect-patterns.md
|
|
16
|
+
- State mutations & patches: @../knowledge/patch-rules.md
|
|
17
|
+
- Common mistakes to avoid: @../knowledge/antipatterns.md
|
|
18
|
+
- SPEC document references: @../knowledge/spec-index.md
|
|
19
|
+
|
|
20
|
+
## Quick Rules
|
|
21
|
+
|
|
22
|
+
1. Core is pure — no IO, no side effects
|
|
23
|
+
2. Snapshot is the only communication medium
|
|
24
|
+
3. Only `set`, `unset`, `merge` patches exist
|
|
25
|
+
4. Effects are declarations, not executions
|
|
26
|
+
5. Errors are values in state, never thrown
|
|
27
|
+
6. All Flow patches/effects must be guarded (when/once/onceIntent)
|
|
28
|
+
7. `$` prefix is reserved for platform namespaces
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# Antipatterns
|
|
2
|
+
|
|
3
|
+
> Source: CLAUDE.md §10, Core FDR v2.0.0, Compiler SPEC v0.5.0 §8
|
|
4
|
+
> Last synced: 2026-02-09
|
|
5
|
+
|
|
6
|
+
If you see these patterns in code, REJECT the change.
|
|
7
|
+
|
|
8
|
+
## Constitutional Violations
|
|
9
|
+
|
|
10
|
+
### AP-001: Intelligent Host
|
|
11
|
+
|
|
12
|
+
Host making policy decisions about effect execution.
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// FORBIDDEN
|
|
16
|
+
async function executeEffect(req) {
|
|
17
|
+
if (shouldSkipEffect(req)) { // Host deciding!
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Host MUST execute or report failure, never decide.
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Why: Host executes, Core computes. Decision-making is Core's job. [FDR-001]
|
|
26
|
+
|
|
27
|
+
### AP-002: Value Passing Outside Snapshot
|
|
28
|
+
|
|
29
|
+
Returning values from effects for direct use, bypassing Snapshot.
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
// FORBIDDEN
|
|
33
|
+
const result = await executeEffect();
|
|
34
|
+
core.compute(schema, snapshot, { ...intent, result });
|
|
35
|
+
|
|
36
|
+
// CORRECT
|
|
37
|
+
// Effect returns patches → Host applies → Next compute() reads from Snapshot
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Why: Snapshot is the sole communication medium. [FDR-002]
|
|
41
|
+
|
|
42
|
+
### AP-003: Execution-Aware Core
|
|
43
|
+
|
|
44
|
+
Core branching on execution state it shouldn't know about.
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
// FORBIDDEN
|
|
48
|
+
if (effectExecutionSucceeded) { ... }
|
|
49
|
+
|
|
50
|
+
// CORRECT
|
|
51
|
+
if (snapshot.data.syncStatus === 'success') { ... }
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Why: Core is pure. It reads Snapshot, nothing else. [FDR-001]
|
|
55
|
+
|
|
56
|
+
### AP-004: Hidden Continuation State
|
|
57
|
+
|
|
58
|
+
Storing execution context outside Snapshot.
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
// FORBIDDEN
|
|
62
|
+
const pendingCallbacks = new Map(); // Hidden state!
|
|
63
|
+
|
|
64
|
+
// CORRECT — All execution state in snapshot.system.pendingRequirements
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Why: There is no suspended execution context. [FDR-003]
|
|
68
|
+
|
|
69
|
+
## Flow Violations
|
|
70
|
+
|
|
71
|
+
### AP-005: Re-Entry Unsafe Flow
|
|
72
|
+
|
|
73
|
+
Flow that runs every compute cycle without state guard.
|
|
74
|
+
|
|
75
|
+
```mel
|
|
76
|
+
// FORBIDDEN — runs every cycle!
|
|
77
|
+
action increment() {
|
|
78
|
+
patch count = add(count, 1) // Increments forever!
|
|
79
|
+
effect api.submit({ }) // Called every cycle!
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// CORRECT
|
|
83
|
+
action increment() {
|
|
84
|
+
onceIntent {
|
|
85
|
+
patch count = add(count, 1)
|
|
86
|
+
effect api.submit({ })
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Why: Flows are re-evaluated on every `compute()` call. Guards prevent re-execution. [FDR-006]
|
|
92
|
+
|
|
93
|
+
### AP-006: Unbounded Loop in Flow
|
|
94
|
+
|
|
95
|
+
Attempting Turing-complete loops in Flow.
|
|
96
|
+
|
|
97
|
+
```mel
|
|
98
|
+
// DOES NOT EXIST — no while/for in MEL
|
|
99
|
+
while eq(pending, true) { ... }
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Why: Flow must terminate in finite steps. Host controls iteration. [FDR-006]
|
|
103
|
+
|
|
104
|
+
### AP-007: Nested Effects
|
|
105
|
+
|
|
106
|
+
Effects inside other effects.
|
|
107
|
+
|
|
108
|
+
```mel
|
|
109
|
+
// FORBIDDEN
|
|
110
|
+
effect array.map({
|
|
111
|
+
source: teams,
|
|
112
|
+
select: {
|
|
113
|
+
members: effect array.filter({ // Nested!
|
|
114
|
+
source: $item.members,
|
|
115
|
+
where: eq($item.active, true)
|
|
116
|
+
})
|
|
117
|
+
},
|
|
118
|
+
into: result
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
// CORRECT — Sequential composition
|
|
122
|
+
effect array.flatMap({ source: teams, select: $item.members, into: allMembers })
|
|
123
|
+
effect array.filter({ source: allMembers, where: eq($item.active, true), into: activeMembers })
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Why: Effects are sequential declarations, not expressions. [Compiler SPEC §8.4]
|
|
127
|
+
|
|
128
|
+
## State Violations
|
|
129
|
+
|
|
130
|
+
### AP-008: Direct Snapshot Mutation
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
// FORBIDDEN
|
|
134
|
+
snapshot.data.count = 5;
|
|
135
|
+
|
|
136
|
+
// CORRECT
|
|
137
|
+
core.apply(schema, snapshot, [{ op: 'set', path: 'data.count', value: 5 }]);
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Why: Snapshots are immutable. All mutations via patches. [FDR-002]
|
|
141
|
+
|
|
142
|
+
### AP-009: Throwing in Effect Handler
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
// FORBIDDEN
|
|
146
|
+
async function handler(params) {
|
|
147
|
+
throw new Error('Something failed');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// CORRECT
|
|
151
|
+
async function handler(params) {
|
|
152
|
+
return [{ op: 'set', path: 'data.error', value: 'Something failed' }];
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Why: Errors are values in Snapshot, never thrown. [FDR-005]
|
|
157
|
+
|
|
158
|
+
## Type Violations
|
|
159
|
+
|
|
160
|
+
### AP-010: String Paths in User API
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
// FORBIDDEN
|
|
164
|
+
{ path: '/data/todos/0/completed' }
|
|
165
|
+
|
|
166
|
+
// CORRECT
|
|
167
|
+
state.todos[0].completed // TypeScript-checked FieldRef
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Why: User-facing APIs must be type-safe with zero string paths.
|
|
171
|
+
|
|
172
|
+
## Governance Violations
|
|
173
|
+
|
|
174
|
+
### AP-011: Authority Bypass
|
|
175
|
+
|
|
176
|
+
Executing intents without World Protocol.
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
// FORBIDDEN
|
|
180
|
+
host.execute(snapshot, intent); // Skips governance!
|
|
181
|
+
|
|
182
|
+
// CORRECT
|
|
183
|
+
world.submitProposal(actor, intentInstance);
|
|
184
|
+
// Authority evaluates → approved intents go to Host
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Why: All intents must pass through World Protocol for governance and auditability.
|
|
188
|
+
|
|
189
|
+
## MEL-Specific Mistakes
|
|
190
|
+
|
|
191
|
+
### AP-012: Truthy/Falsy in Guards
|
|
192
|
+
|
|
193
|
+
```mel
|
|
194
|
+
// FORBIDDEN — items is not boolean
|
|
195
|
+
when items { ... }
|
|
196
|
+
|
|
197
|
+
// CORRECT
|
|
198
|
+
when gt(len(items), 0) { ... }
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### AP-013: Template Literals
|
|
202
|
+
|
|
203
|
+
```mel
|
|
204
|
+
// FORBIDDEN
|
|
205
|
+
patch greeting = `Hello ${name}`
|
|
206
|
+
|
|
207
|
+
// CORRECT
|
|
208
|
+
patch greeting = concat("Hello ", name)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### AP-014: Using `$` in Domain Identifiers
|
|
212
|
+
|
|
213
|
+
```mel
|
|
214
|
+
// FORBIDDEN
|
|
215
|
+
state { $myField: string = "" }
|
|
216
|
+
|
|
217
|
+
// CORRECT
|
|
218
|
+
state { myField: string = "" }
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
`$host`, `$mel`, `$system` are platform-reserved. Domain code must not use `$`.
|
|
222
|
+
|
|
223
|
+
## Computed Violations
|
|
224
|
+
|
|
225
|
+
### AP-015: Circular Computed Dependencies
|
|
226
|
+
|
|
227
|
+
```mel
|
|
228
|
+
// FORBIDDEN — cycle: a depends on b, b depends on a
|
|
229
|
+
computed a = b
|
|
230
|
+
computed b = a
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Computed dependencies MUST form a DAG. Cycles are rejected at validation time. [Core SPEC §14 V-002]
|
|
234
|
+
|
|
235
|
+
## Quick Checklist
|
|
236
|
+
|
|
237
|
+
Before submitting code, verify NONE of these exist:
|
|
238
|
+
|
|
239
|
+
- [ ] Host making decisions (not just executing)
|
|
240
|
+
- [ ] Values passed outside Snapshot
|
|
241
|
+
- [ ] Core knowing about execution state
|
|
242
|
+
- [ ] Flow without state guard (patch/effect outside when/once/onceIntent)
|
|
243
|
+
- [ ] Direct snapshot mutation
|
|
244
|
+
- [ ] Throwing in effect handlers
|
|
245
|
+
- [ ] String paths in user-facing API
|
|
246
|
+
- [ ] Intent executed without governance
|
|
247
|
+
- [ ] Nested effects
|
|
248
|
+
- [ ] Truthy/falsy conditions in guards
|
|
249
|
+
- [ ] Circular computed dependencies
|
|
250
|
+
|
|
251
|
+
## Cross-References
|
|
252
|
+
|
|
253
|
+
- Architecture rules: @knowledge/architecture.md
|
|
254
|
+
- Correct MEL patterns: @knowledge/mel-patterns.md
|
|
255
|
+
- Effect handler contract: @knowledge/effect-patterns.md
|
|
256
|
+
- Patch rules: @knowledge/patch-rules.md
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Manifesto Architecture
|
|
2
|
+
|
|
3
|
+
> Source: Core SPEC v2.0.1, Core FDR v2.0.0, Host SPEC v2.0.2, World SPEC v2.0.3
|
|
4
|
+
> Last synced: 2026-02-09
|
|
5
|
+
|
|
6
|
+
## Rules
|
|
7
|
+
|
|
8
|
+
> **R1**: Core computes, Host executes. These concerns never mix. [FDR-001]
|
|
9
|
+
> **R2**: Snapshot is the only medium of communication. If it's not in Snapshot, it doesn't exist. [FDR-002]
|
|
10
|
+
> **R3**: There is no suspended execution context. All continuity is expressed through Snapshot. [FDR-003]
|
|
11
|
+
> **R4**: Effects are declarations, not executions. Core declares; Host fulfills. [FDR-004]
|
|
12
|
+
> **R5**: If you need a value, read it from Snapshot. There is no other place. [FDR-007]
|
|
13
|
+
|
|
14
|
+
## The Constitution (7 Principles)
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
1. Core is a calculator, not an executor.
|
|
18
|
+
2. Schema is the single source of truth.
|
|
19
|
+
3. Snapshot is the only medium of communication.
|
|
20
|
+
4. Effects are declarations, not executions.
|
|
21
|
+
5. Errors are values, not exceptions.
|
|
22
|
+
6. Everything is explainable.
|
|
23
|
+
7. There is no suspended execution context.
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## The Fundamental Equation
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
compute(schema, snapshot, intent, context) → (snapshot', requirements[], trace)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- **Pure**: Same input MUST always produce same output
|
|
33
|
+
- **Total**: MUST always return a result (never throws)
|
|
34
|
+
- **Traceable**: Every step MUST be recorded
|
|
35
|
+
- If `requirements` is empty → computation complete
|
|
36
|
+
- If `requirements` is non-empty → Host fulfills them, then calls `compute()` again
|
|
37
|
+
|
|
38
|
+
## Data Flow
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
Actor submits Intent
|
|
42
|
+
↓
|
|
43
|
+
Bridge (Projection + Issuer → adds intentId, intentKey)
|
|
44
|
+
↓
|
|
45
|
+
World Protocol (Proposal + Authority)
|
|
46
|
+
↓
|
|
47
|
+
Host (compute loop + effect execution)
|
|
48
|
+
↓
|
|
49
|
+
Core (pure computation)
|
|
50
|
+
↓
|
|
51
|
+
New Snapshot (via patches)
|
|
52
|
+
↓
|
|
53
|
+
New World (immutable)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Information flows ONLY through Snapshot. No other channels exist.
|
|
57
|
+
|
|
58
|
+
## Package Sovereignty
|
|
59
|
+
|
|
60
|
+
| Package | Responsibility | MUST NOT |
|
|
61
|
+
|---------|---------------|----------|
|
|
62
|
+
| **Core** | Pure computation, expression evaluation, flow interpretation, patch generation, trace | IO, time, execution, know about Host/World |
|
|
63
|
+
| **Host** | Effect execution, patch application, compute loop, requirement fulfillment | Make decisions, interpret semantics, suppress effects |
|
|
64
|
+
| **World** | Proposal management, authority evaluation, decision recording, lineage | Execute effects, apply patches, compute transitions |
|
|
65
|
+
| **App** | Composition root — wires Core + Host + World together | Contain domain logic |
|
|
66
|
+
| **Bridge** | Two-way binding (events↔intents, snapshot↔subscribers) | Mutate, apply, execute, govern |
|
|
67
|
+
|
|
68
|
+
## Forbidden Import Matrix
|
|
69
|
+
|
|
70
|
+
| Package | MUST NOT Import |
|
|
71
|
+
|---------|----------------|
|
|
72
|
+
| core | host, world, bridge, react |
|
|
73
|
+
| host | world governance, react |
|
|
74
|
+
| world | host internals, core compute |
|
|
75
|
+
| bridge | core internals, host handlers, world internals |
|
|
76
|
+
| react | core internals, host, world |
|
|
77
|
+
|
|
78
|
+
## Snapshot Structure
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
type Snapshot = {
|
|
82
|
+
data: TData; // Domain state (+ platform namespaces $host, $mel)
|
|
83
|
+
computed: Record<string, unknown>; // Derived values (always recalculated)
|
|
84
|
+
system: {
|
|
85
|
+
status: 'idle' | 'computing' | 'pending' | 'error';
|
|
86
|
+
lastError: ErrorValue | null;
|
|
87
|
+
errors: readonly ErrorValue[];
|
|
88
|
+
pendingRequirements: readonly Requirement[];
|
|
89
|
+
currentAction: string | null;
|
|
90
|
+
};
|
|
91
|
+
input: unknown; // Transient action input
|
|
92
|
+
meta: {
|
|
93
|
+
version: number; // Monotonically increasing
|
|
94
|
+
timestamp: number;
|
|
95
|
+
randomSeed: string;
|
|
96
|
+
schemaHash: string;
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Platform Namespaces
|
|
102
|
+
|
|
103
|
+
- `$host` — Host-owned internal state (intent slots, execution context). Excluded from hash.
|
|
104
|
+
- `$mel` — Compiler-owned guard state (`$mel.guards.*`). Excluded from hash.
|
|
105
|
+
- `$system.*` — System values (uuid, time.now). Lowered to effects by compiler.
|
|
106
|
+
- Domain schemas MUST NOT define `$`-prefixed fields.
|
|
107
|
+
|
|
108
|
+
## Computation Cycle
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
Host calls compute(schema, snapshot, intent, context)
|
|
112
|
+
→ Core evaluates Flow until:
|
|
113
|
+
- Flow completes (requirements=[]) → DONE
|
|
114
|
+
- Effect encountered (requirements=[...]) → Host executes effects, applies patches, calls compute() AGAIN
|
|
115
|
+
- Error occurs → error recorded in Snapshot
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Each `compute()` is complete and independent. There is no "resume".
|
|
119
|
+
|
|
120
|
+
## Antipatterns
|
|
121
|
+
|
|
122
|
+
### Intelligent Host
|
|
123
|
+
```typescript
|
|
124
|
+
// FORBIDDEN — Host making decisions
|
|
125
|
+
if (shouldSkipEffect(req)) { return []; }
|
|
126
|
+
|
|
127
|
+
// Host MUST execute or report failure, never decide
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Value Passing Outside Snapshot
|
|
131
|
+
```typescript
|
|
132
|
+
// FORBIDDEN
|
|
133
|
+
const result = await executeEffect();
|
|
134
|
+
core.compute(schema, snapshot, { ...intent, result });
|
|
135
|
+
|
|
136
|
+
// CORRECT — Effect returns patches → Host applies → Core reads from Snapshot
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Execution-Aware Core
|
|
140
|
+
```typescript
|
|
141
|
+
// FORBIDDEN — Core cannot know about execution
|
|
142
|
+
if (effectExecutionSucceeded) { ... }
|
|
143
|
+
|
|
144
|
+
// CORRECT — Core reads state
|
|
145
|
+
if (snapshot.data.syncStatus === 'success') { ... }
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Why
|
|
149
|
+
|
|
150
|
+
Separation of concerns enables:
|
|
151
|
+
- **Determinism**: Core testable without mocks (same input → same output)
|
|
152
|
+
- **Auditability**: World tracks all governance decisions with lineage
|
|
153
|
+
- **Portability**: Host swappable per environment (browser/server/edge/WASM)
|
|
154
|
+
- **Reproducibility**: Snapshot serialization enables time-travel debugging
|
|
155
|
+
|
|
156
|
+
## Cross-References
|
|
157
|
+
|
|
158
|
+
- MEL syntax: @knowledge/mel-patterns.md
|
|
159
|
+
- Effect handlers: @knowledge/effect-patterns.md
|
|
160
|
+
- Patch operations: @knowledge/patch-rules.md
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# Effect Patterns
|
|
2
|
+
|
|
3
|
+
> Source: Host SPEC v2.0.2 §7, App SPEC v2.3.0 §8, Core FDR v2.0.0
|
|
4
|
+
> Last synced: 2026-02-09
|
|
5
|
+
|
|
6
|
+
## Rules
|
|
7
|
+
|
|
8
|
+
> **R1**: Effect handlers MUST return `Patch[]` and MUST NOT throw exceptions. [HANDLER-1, HANDLER-2]
|
|
9
|
+
> **R2**: Failures MUST be expressed as patches to state (error values in Snapshot). [HANDLER-3]
|
|
10
|
+
> **R3**: Effect handlers MUST NOT contain domain logic. They are pure IO adapters. [HANDLER-4, HANDLER-5]
|
|
11
|
+
> **R4**: Effects are declared by Core, executed by Host. Core never performs IO. [FDR-004]
|
|
12
|
+
> **R5**: Host-generated error patches MUST target `$host` or domain-owned paths, NOT `system.*`. [INV-SNAP-4]
|
|
13
|
+
|
|
14
|
+
## Handler Contract
|
|
15
|
+
|
|
16
|
+
Developers register effect handlers through the **App layer** API:
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
// App-layer handler signature (what you write)
|
|
20
|
+
type EffectHandler = (
|
|
21
|
+
params: unknown,
|
|
22
|
+
ctx: AppEffectContext
|
|
23
|
+
) => Promise<readonly Patch[]>;
|
|
24
|
+
|
|
25
|
+
type Patch = {
|
|
26
|
+
op: 'set' | 'unset' | 'merge';
|
|
27
|
+
path: string;
|
|
28
|
+
value?: unknown;
|
|
29
|
+
};
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Note: The Host layer internally uses a different signature `(type, params, context)` but App wraps this. Handlers receive effect params, perform IO, and return patches.
|
|
33
|
+
|
|
34
|
+
## Patterns
|
|
35
|
+
|
|
36
|
+
### Successful Effect
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
async function fetchUser(params: { id: string }): Promise<Patch[]> {
|
|
40
|
+
const response = await fetch(`/users/${params.id}`);
|
|
41
|
+
const data = await response.json();
|
|
42
|
+
return [
|
|
43
|
+
{ op: 'set', path: 'data.user', value: data },
|
|
44
|
+
{ op: 'set', path: 'data.user.error', value: null }
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Failed Effect (Errors as Patches)
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
async function fetchUser(params: { id: string }): Promise<Patch[]> {
|
|
53
|
+
try {
|
|
54
|
+
const response = await fetch(`/users/${params.id}`);
|
|
55
|
+
if (!response.ok) {
|
|
56
|
+
return [
|
|
57
|
+
{ op: 'set', path: 'data.user.error', value: { code: response.status } },
|
|
58
|
+
{ op: 'set', path: 'data.user.data', value: null }
|
|
59
|
+
];
|
|
60
|
+
}
|
|
61
|
+
const data = await response.json();
|
|
62
|
+
return [
|
|
63
|
+
{ op: 'set', path: 'data.user.data', value: data },
|
|
64
|
+
{ op: 'set', path: 'data.user.error', value: null }
|
|
65
|
+
];
|
|
66
|
+
} catch (error) {
|
|
67
|
+
return [
|
|
68
|
+
{ op: 'set', path: 'data.user.error', value: { message: error.message } },
|
|
69
|
+
{ op: 'set', path: 'data.user.data', value: null }
|
|
70
|
+
];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Effect Registration (App Layer)
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { createManifestoApp } from '@manifesto-ai/app';
|
|
79
|
+
|
|
80
|
+
const app = createManifestoApp({
|
|
81
|
+
schema: domainSchema,
|
|
82
|
+
effects: {
|
|
83
|
+
'api.fetchUser': fetchUser,
|
|
84
|
+
'api.createTodo': createTodo,
|
|
85
|
+
'payment.process': processPayment,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Effect names in handlers must match effect type names declared in MEL.
|
|
91
|
+
|
|
92
|
+
### Collection Effect Handlers
|
|
93
|
+
|
|
94
|
+
Collection effects (`array.filter`, `array.map`, `record.keys`, etc.) are built-in — you do NOT write handlers for these. They are handled by Core/Host internally.
|
|
95
|
+
|
|
96
|
+
You only write handlers for custom domain effects (API calls, storage, etc.).
|
|
97
|
+
|
|
98
|
+
## Antipatterns
|
|
99
|
+
|
|
100
|
+
### Throwing Handler
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
// FORBIDDEN
|
|
104
|
+
async function bad(params) {
|
|
105
|
+
if (!params.id) throw new Error('Missing id'); // Don't throw!
|
|
106
|
+
const result = await api.call(params.id);
|
|
107
|
+
return [{ op: 'set', path: 'data.result', value: result }];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// CORRECT
|
|
111
|
+
async function good(params) {
|
|
112
|
+
if (!params.id) {
|
|
113
|
+
return [{ op: 'set', path: 'data.error', value: 'Missing id' }];
|
|
114
|
+
}
|
|
115
|
+
const result = await api.call(params.id);
|
|
116
|
+
return [{ op: 'set', path: 'data.result', value: result }];
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Domain Logic in Handler
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
// FORBIDDEN — Business rule in handler
|
|
124
|
+
async function purchaseHandler(params) {
|
|
125
|
+
if (params.amount > 1000) { // Domain decision!
|
|
126
|
+
return [{ op: 'set', path: 'data.approval.required', value: true }];
|
|
127
|
+
}
|
|
128
|
+
// ...
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// CORRECT — Handler does IO only, domain logic stays in Flow/MEL
|
|
132
|
+
async function paymentHandler(params) {
|
|
133
|
+
const result = await paymentGateway.charge(params.amount);
|
|
134
|
+
return [{ op: 'set', path: 'data.payment.status', value: result.status }];
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Returning Raw Values
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
// FORBIDDEN — Returns value, not patches
|
|
142
|
+
async function bad(params) {
|
|
143
|
+
return await api.fetchData(params.id); // Returns data directly!
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// CORRECT — Returns patches
|
|
147
|
+
async function good(params) {
|
|
148
|
+
const data = await api.fetchData(params.id);
|
|
149
|
+
return [{ op: 'set', path: 'data.result', value: data }];
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Writing to system.*
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
// FORBIDDEN — Handler must not write system namespace
|
|
157
|
+
return [{ op: 'set', path: 'system.lastError', value: { ... } }];
|
|
158
|
+
|
|
159
|
+
// CORRECT — Write to $host or domain paths
|
|
160
|
+
return [{ op: 'set', path: 'data.fetchError', value: { ... } }];
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Requirement Lifecycle
|
|
164
|
+
|
|
165
|
+
When Core encounters an effect declaration:
|
|
166
|
+
1. Core records a Requirement in `system.pendingRequirements`
|
|
167
|
+
2. Core terminates and returns to Host
|
|
168
|
+
3. Host executes the effect handler
|
|
169
|
+
4. Host applies result patches to Snapshot
|
|
170
|
+
5. Host MUST clear the requirement from `pendingRequirements`
|
|
171
|
+
6. Host calls `compute()` again
|
|
172
|
+
|
|
173
|
+
**Critical**: Requirement MUST be cleared even if handler fails. Leaving it pending causes infinite loops.
|
|
174
|
+
|
|
175
|
+
## Why
|
|
176
|
+
|
|
177
|
+
**Patches as protocol**: Decouples effect execution from Core. Host can serialize, batch, retry, or substitute effects without Core knowing.
|
|
178
|
+
|
|
179
|
+
**Errors as values**: Same inputs (including failures) always produce same snapshot state. Enables deterministic replay.
|
|
180
|
+
|
|
181
|
+
**No domain logic**: Keeps all business decisions in Flow/MEL where they are traceable and deterministic.
|
|
182
|
+
|
|
183
|
+
## Cross-References
|
|
184
|
+
|
|
185
|
+
- Patch operations: @knowledge/patch-rules.md
|
|
186
|
+
- State structure: @knowledge/architecture.md
|
|
187
|
+
- MEL effect declarations: @knowledge/mel-patterns.md
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# MEL Patterns
|
|
2
|
+
|
|
3
|
+
> Source: Compiler SPEC v0.5.0, Compiler FDR v0.5.0
|
|
4
|
+
> Last synced: 2026-02-09
|
|
5
|
+
|
|
6
|
+
## Rules
|
|
7
|
+
|
|
8
|
+
> **R1**: MEL is pure and total. All expressions terminate, are deterministic, and never throw. [A1-A3]
|
|
9
|
+
> **R2**: One pattern per concept. Function calls only, no method chaining. [A6]
|
|
10
|
+
> **R3**: Every mutation (patch/effect) MUST be inside a guard (when/once/onceIntent). [A7]
|
|
11
|
+
> **R4**: Effects are declarations, not executions. Effects are sequential, never nested. [A20]
|
|
12
|
+
> **R5**: `$` is completely prohibited in user identifiers — not just at start, anywhere. [A28]
|
|
13
|
+
|
|
14
|
+
## Domain Structure
|
|
15
|
+
|
|
16
|
+
```mel
|
|
17
|
+
domain MyApp {
|
|
18
|
+
state {
|
|
19
|
+
count: number = 0
|
|
20
|
+
name: string | null = null
|
|
21
|
+
items: Record<string, Item> = {}
|
|
22
|
+
marker: string | null = null // for once() guard
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
computed total = mul(price, quantity)
|
|
26
|
+
computed hasItems = gt(len(items), 0)
|
|
27
|
+
|
|
28
|
+
action increment() { ... }
|
|
29
|
+
action addItem(title: string) { ... }
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Guard Patterns (Re-Entry Safety)
|
|
34
|
+
|
|
35
|
+
Every compute cycle re-evaluates the entire flow. Without guards, patches and effects run every cycle.
|
|
36
|
+
|
|
37
|
+
### `onceIntent` — Per-intent idempotency (preferred)
|
|
38
|
+
|
|
39
|
+
```mel
|
|
40
|
+
action submit() {
|
|
41
|
+
onceIntent {
|
|
42
|
+
patch status = "submitting"
|
|
43
|
+
effect api.submit({ data: formData, into: result })
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Compiler desugars to: stores guard in `$mel.guards.intent`, checks `$meta.intentId`.
|
|
49
|
+
|
|
50
|
+
### `once(marker)` — Explicit marker guard
|
|
51
|
+
|
|
52
|
+
```mel
|
|
53
|
+
action addTask(title: string) {
|
|
54
|
+
once(addingTask) when neq(trim(title), "") {
|
|
55
|
+
patch addingTask = $meta.intentId // MUST be first statement
|
|
56
|
+
patch tasks[$system.uuid] = {
|
|
57
|
+
id: $system.uuid,
|
|
58
|
+
title: trim(title),
|
|
59
|
+
completed: false
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Rule: `patch marker = $meta.intentId` MUST be the first statement inside `once()`.
|
|
66
|
+
|
|
67
|
+
### `when` — Conditional guard
|
|
68
|
+
|
|
69
|
+
```mel
|
|
70
|
+
action setFilter(newFilter: string) {
|
|
71
|
+
when neq(filter, newFilter) {
|
|
72
|
+
patch filter = newFilter
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Conditions MUST be boolean expressions — no truthy/falsy coercion. Use `gt(len(items), 0)` not `when items`.
|
|
78
|
+
|
|
79
|
+
## Expression Syntax
|
|
80
|
+
|
|
81
|
+
```mel
|
|
82
|
+
// Arithmetic
|
|
83
|
+
add(a, b) sub(a, b) mul(a, b) div(a, b) mod(a, b) neg(a)
|
|
84
|
+
|
|
85
|
+
// Math
|
|
86
|
+
abs(n) min(a, b) max(a, b) floor(n) ceil(n) round(n) sqrt(n) pow(base, exp)
|
|
87
|
+
|
|
88
|
+
// Comparison (primitive-only — cannot compare Array/Object/Record)
|
|
89
|
+
eq(a, b) neq(a, b) gt(a, b) gte(a, b) lt(a, b) lte(a, b)
|
|
90
|
+
|
|
91
|
+
// Logical
|
|
92
|
+
and(a, b) or(a, b) not(a)
|
|
93
|
+
|
|
94
|
+
// Null handling
|
|
95
|
+
isNull(x) isNotNull(x) coalesce(a, b, c)
|
|
96
|
+
|
|
97
|
+
// Conditional
|
|
98
|
+
cond(condition, thenValue, elseValue) // NOT if()
|
|
99
|
+
|
|
100
|
+
// String
|
|
101
|
+
concat("Hello ", name) trim(s) lower(s) upper(s) substr(s, 0, 10) strlen(s)
|
|
102
|
+
|
|
103
|
+
// Conversion
|
|
104
|
+
toString(x)
|
|
105
|
+
|
|
106
|
+
// Array/Collection
|
|
107
|
+
len(arr) first(arr) last(arr) at(arr, index)
|
|
108
|
+
|
|
109
|
+
// Aggregation (primitive arrays only)
|
|
110
|
+
sum(arr) min(arr) max(arr)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
No method calls: `str.trim()` is a SyntaxError. Use `trim(str)`.
|
|
114
|
+
Index access `x[y]` desugars to `at(x, y)` — works for both Array and Record.
|
|
115
|
+
|
|
116
|
+
## Effect Syntax
|
|
117
|
+
|
|
118
|
+
```mel
|
|
119
|
+
// Basic effect with into path
|
|
120
|
+
effect api.fetchUser({ id: userId, into: userData })
|
|
121
|
+
|
|
122
|
+
// Collection effects use $item for iteration
|
|
123
|
+
effect array.filter({
|
|
124
|
+
source: tasks,
|
|
125
|
+
where: eq($item.completed, false),
|
|
126
|
+
into: activeTasks
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
effect array.map({
|
|
130
|
+
source: users,
|
|
131
|
+
select: { name: $item.name, active: $item.isActive },
|
|
132
|
+
into: userSummaries
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
// Record operations
|
|
136
|
+
effect record.filter({ source: items, where: ..., into: items })
|
|
137
|
+
effect record.keys({ source: items, into: itemIds })
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Multi-Step Actions
|
|
141
|
+
|
|
142
|
+
```mel
|
|
143
|
+
action checkout() {
|
|
144
|
+
// Step 1: Validate
|
|
145
|
+
once(cart.validatedAt) {
|
|
146
|
+
patch cart.validatedAt = $meta.intentId
|
|
147
|
+
effect validate.cart({ items: cart.items, into: cart.validation })
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Step 2: Pay (only after validation)
|
|
151
|
+
once(payment.processedAt) when cart.validation.success {
|
|
152
|
+
patch payment.processedAt = $meta.intentId
|
|
153
|
+
effect payment.process({ amount: cart.total, into: payment.result })
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Step 3: Create order (only after payment)
|
|
157
|
+
once(order.createdAt) when payment.result.success {
|
|
158
|
+
patch order.createdAt = $meta.intentId
|
|
159
|
+
effect order.create({ items: cart.items, into: order.result })
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Each `once()` guard ensures its block runs exactly once. Effects write results to Snapshot; subsequent guards read from Snapshot.
|
|
165
|
+
|
|
166
|
+
## Forbidden Constructs
|
|
167
|
+
|
|
168
|
+
### Syntactically Forbidden
|
|
169
|
+
```mel
|
|
170
|
+
let x = 5 // No variables
|
|
171
|
+
const y = 10 // No constants
|
|
172
|
+
function foo() {} // No function definitions
|
|
173
|
+
for (...) {} // No loops
|
|
174
|
+
while (...) {} // No loops
|
|
175
|
+
str.trim() // No method calls (use trim(str))
|
|
176
|
+
if (cond) {} // No if/else (use when guard or cond() expression)
|
|
177
|
+
throw new Error() // No exceptions
|
|
178
|
+
async/await // No async
|
|
179
|
+
class Foo {} // No classes
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Semantically Forbidden
|
|
183
|
+
```mel
|
|
184
|
+
filter(items, ...) // Not a builtin — use effect array.filter()
|
|
185
|
+
Date.now() // Not defined — use $system.time.now
|
|
186
|
+
Math.random() // Not defined — determinism required
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## LLM Generation Checklist
|
|
190
|
+
|
|
191
|
+
1. All operations use `functionName(arg1, arg2)` syntax
|
|
192
|
+
2. Property access uses `object.property` (no method calls)
|
|
193
|
+
3. Index access uses `array[index]` or `record[key]` (desugars to `at()`)
|
|
194
|
+
4. Effects use `effect type.name({ param: value, into: path })`
|
|
195
|
+
5. Guards use `when condition { body }`, `once(marker) { body }`, or `onceIntent { body }`
|
|
196
|
+
6. Iteration variable is always `$item` (current element)
|
|
197
|
+
7. Effects are never nested — use sequential effects with intermediate `into:` paths
|
|
198
|
+
8. For nested data, use `flatMap` to flatten, then `filter`/`map`, then `groupBy` to restructure
|
|
199
|
+
9. All patch/effect must be inside guards
|
|
200
|
+
10. Conditions must be boolean expressions — no truthy/falsy (`when items` is wrong)
|
|
201
|
+
11. Markers use intentId — `once(m) { patch m = $meta.intentId; ... }`
|
|
202
|
+
12. Use correct effect family — `array.*` for Array, `record.*` for Record
|
|
203
|
+
13. Use `concat()` for strings — no template literals
|
|
204
|
+
14. Use `cond()` not `if()` — `cond(condition, thenValue, elseValue)`
|
|
205
|
+
15. Computed can reference computed — scope: Params > Computed > State > System
|
|
206
|
+
16. `$system.*` is deduplicated per action — same key = same value
|
|
207
|
+
17. `neq(null, string)` = true — different types are never equal
|
|
208
|
+
18. `eq`/`neq` are primitive-only — cannot compare Array/Object/Record
|
|
209
|
+
19. `$` is completely prohibited in identifiers — not just at start, anywhere
|
|
210
|
+
20. `once()` marker must be first — `patch marker = $meta.intentId` as first statement
|
|
211
|
+
21. Index access `x[y]` is always `call(at)` in IR — not `get` with index
|
|
212
|
+
22. Evaluation is left-to-right — object fields are key-sorted first, then left-to-right
|
|
213
|
+
23. `partition` uses top-level `pass:` and `fail:` — not `into: { pass, fail }`
|
|
214
|
+
24. `$system.*` only in actions — forbidden in computed and state initializers
|
|
215
|
+
25. System values are IO — compiler handles lowering, developer writes surface syntax
|
|
216
|
+
26. `__sys__` prefix reserved — user identifiers cannot start with `__sys__` (compile error E004)
|
|
217
|
+
27. Readiness uses `eq(intent, intentId)` — NOT `isNotNull(value)`, prevents stale value bugs
|
|
218
|
+
28. `available when <Expr>` for action preconditions — must be pure (no effects, no `$system.*`)
|
|
219
|
+
29. `fail` and `stop` must be guarded — unconditional is compile error
|
|
220
|
+
30. `stop` is early-exit, not "waiting" — `"Waiting for..."` messages are lint errors
|
|
221
|
+
31. Named types required — anonymous object types in state are forbidden
|
|
222
|
+
32. Type declarations are metadata — AI-readable domain concepts
|
|
223
|
+
|
|
224
|
+
## Why
|
|
225
|
+
|
|
226
|
+
**Purity by design**: Impossible to express side effects in grammar = impossible to violate purity.
|
|
227
|
+
|
|
228
|
+
**AI-Native**: Grammar optimized for LLM generation. One pattern per concept reduces hallucination. Forbidden constructs don't parse, preventing common mistakes.
|
|
229
|
+
|
|
230
|
+
## Cross-References
|
|
231
|
+
|
|
232
|
+
- Architecture context: @knowledge/architecture.md
|
|
233
|
+
- Effect handler implementation: @knowledge/effect-patterns.md
|
|
234
|
+
- Common mistakes: @knowledge/antipatterns.md
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Patch Rules
|
|
2
|
+
|
|
3
|
+
> Source: Core SPEC v2.0.1 §13.3-14, Core FDR v2.0.0 FDR-012
|
|
4
|
+
> Last synced: 2026-02-09
|
|
5
|
+
|
|
6
|
+
## Rules
|
|
7
|
+
|
|
8
|
+
> **R1**: Only three patch operations exist: `set`, `unset`, `merge`. [Core SPEC §8.4.3]
|
|
9
|
+
> **R2**: All state changes MUST go through `apply(schema, snapshot, patches)`. [Core SPEC §13.3]
|
|
10
|
+
> **R3**: Patches create a new Snapshot. The old Snapshot is unchanged (immutable). [Core SPEC §13.3]
|
|
11
|
+
> **R4**: `version` MUST be incremented on every change. [Core SPEC §13.3]
|
|
12
|
+
> **R5**: Patch paths MUST be statically resolvable. Dynamic paths require two-step pattern. [Core SPEC §8.4.3]
|
|
13
|
+
|
|
14
|
+
## The Three Operations
|
|
15
|
+
|
|
16
|
+
### `set` — Replace value at path (create if missing)
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
{ op: 'set', path: 'data.count', value: 5 }
|
|
20
|
+
{ op: 'set', path: 'data.todos.abc123.completed', value: true }
|
|
21
|
+
{ op: 'set', path: 'data.items', value: [1, 2, 3] }
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### `unset` — Remove property at path
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
{ op: 'unset', path: 'data.tempFlag' }
|
|
28
|
+
{ op: 'unset', path: 'data.todos.abc123' }
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### `merge` — Shallow merge object at path
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
{ op: 'merge', path: 'data.user', value: { lastSeen: '2026-02-09' } }
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Warning: Shallow only.** Nested objects are replaced, not recursively merged. For nested updates, use multiple `set` patches.
|
|
38
|
+
|
|
39
|
+
If merge target is absent, treated as `{}`. If merge target is non-object, runtime validation failure.
|
|
40
|
+
|
|
41
|
+
## MEL Patch Syntax
|
|
42
|
+
|
|
43
|
+
```mel
|
|
44
|
+
// set
|
|
45
|
+
patch count = add(count, 1)
|
|
46
|
+
patch user.name = trim(newName)
|
|
47
|
+
patch items[$system.uuid] = { id: $system.uuid, title: title }
|
|
48
|
+
|
|
49
|
+
// unset
|
|
50
|
+
patch tasks[id] unset
|
|
51
|
+
|
|
52
|
+
// merge (only via effect results or explicit merge op)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Dynamic Path Pattern
|
|
56
|
+
|
|
57
|
+
Patch paths must be static at apply-time. For dynamic keys:
|
|
58
|
+
|
|
59
|
+
```mel
|
|
60
|
+
// Step 1: Fix the dynamic value to Snapshot
|
|
61
|
+
once(creating) {
|
|
62
|
+
patch creating = $meta.intentId
|
|
63
|
+
patch newItemId = $system.uuid // UUID now in Snapshot
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Step 2: Use the fixed value
|
|
67
|
+
when isNotNull(newItemId) {
|
|
68
|
+
patch items[newItemId] = { id: newItemId, title: title }
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Compiler handles the lowering. `$system.uuid` becomes an effect that writes the value to a state slot.
|
|
73
|
+
|
|
74
|
+
## Antipatterns
|
|
75
|
+
|
|
76
|
+
### Direct Mutation
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
// FORBIDDEN
|
|
80
|
+
snapshot.data.count = 5;
|
|
81
|
+
snapshot.meta.version++;
|
|
82
|
+
|
|
83
|
+
// CORRECT
|
|
84
|
+
const newSnapshot = core.apply(schema, snapshot, [
|
|
85
|
+
{ op: 'set', path: 'data.count', value: 5 }
|
|
86
|
+
]);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Deep Merge Assumption
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
// WRONG — merge is shallow, nested objects replaced entirely
|
|
93
|
+
{ op: 'merge', path: 'data', value: { user: { name: 'X', settings: { theme: 'dark' } } } }
|
|
94
|
+
|
|
95
|
+
// CORRECT — multiple set patches for nested paths
|
|
96
|
+
[
|
|
97
|
+
{ op: 'set', path: 'data.user.name', value: 'X' },
|
|
98
|
+
{ op: 'set', path: 'data.user.settings.theme', value: 'dark' }
|
|
99
|
+
]
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Array Push/Pop/Splice
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
// FORBIDDEN — mutates in place
|
|
106
|
+
snapshot.data.todos.push(newTodo);
|
|
107
|
+
|
|
108
|
+
// CORRECT — set entire new array
|
|
109
|
+
const newTodos = [...snapshot.data.todos, newTodo];
|
|
110
|
+
[{ op: 'set', path: 'data.todos', value: newTodos }]
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Unguarded Patch in MEL
|
|
114
|
+
|
|
115
|
+
```mel
|
|
116
|
+
// FORBIDDEN — runs every compute cycle
|
|
117
|
+
action broken() {
|
|
118
|
+
patch count = add(count, 1) // Increments forever!
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// CORRECT — guarded
|
|
122
|
+
action increment() {
|
|
123
|
+
onceIntent {
|
|
124
|
+
patch count = add(count, 1)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Why
|
|
130
|
+
|
|
131
|
+
**Three operations are enough.** Complexity is composed, not built-in. [FDR-012]
|
|
132
|
+
|
|
133
|
+
**Immutability.** Snapshots are time-travel points. Mutation breaks determinism and reproducibility.
|
|
134
|
+
|
|
135
|
+
**Version tracking.** Monotonic version enables conflict detection and audit trails.
|
|
136
|
+
|
|
137
|
+
## Cross-References
|
|
138
|
+
|
|
139
|
+
- Snapshot structure: @knowledge/architecture.md
|
|
140
|
+
- Effect handlers return patches: @knowledge/effect-patterns.md
|
|
141
|
+
- MEL patch syntax: @knowledge/mel-patterns.md
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# SPEC Index
|
|
2
|
+
|
|
3
|
+
> Source: docs/llm/LLM-INDEX.md
|
|
4
|
+
> Last synced: 2026-02-09
|
|
5
|
+
|
|
6
|
+
## Normative Hierarchy
|
|
7
|
+
|
|
8
|
+
1. **SPEC** — highest authority
|
|
9
|
+
2. **FDR** — design rationale; must not contradict SPEC
|
|
10
|
+
3. **ADR** — architectural decisions; must not contradict SPEC/FDR
|
|
11
|
+
4. **Code** — implementation
|
|
12
|
+
5. **README** — lowest authority
|
|
13
|
+
|
|
14
|
+
When documents conflict, prefer higher-ranked sources.
|
|
15
|
+
|
|
16
|
+
## How to Read Patch Documents
|
|
17
|
+
|
|
18
|
+
Some SPECs are published as base + patch. Read the base first, then apply the patch. The composed document is authoritative.
|
|
19
|
+
|
|
20
|
+
Example: Core v2.0.1 = `SPEC-v2.0.0.md` + `SPEC-v2.0.1-patch.md`
|
|
21
|
+
|
|
22
|
+
## Core Packages
|
|
23
|
+
|
|
24
|
+
### Core v2.0.1
|
|
25
|
+
|
|
26
|
+
- Base: `packages/core/docs/SPEC-v2.0.0.md`
|
|
27
|
+
- Patch: `packages/core/docs/SPEC-v2.0.1-patch.md`
|
|
28
|
+
- FDR: `packages/core/docs/FDR-v2.0.0.md`
|
|
29
|
+
|
|
30
|
+
Key sections: §3 Constitution, §8 FlowSpec, §13 Snapshot, §14 Validation, §16 Host Interface
|
|
31
|
+
|
|
32
|
+
### Host v2.0.2
|
|
33
|
+
|
|
34
|
+
- `packages/host/docs/host-SPEC-v2.0.2.md`
|
|
35
|
+
- FDR: `packages/host/docs/host-FDR-v2.0.2.md`
|
|
36
|
+
|
|
37
|
+
Key sections: §7 Effect Handler Contract, §8 Requirement Lifecycle, §10 Execution Model, §13 Error Handling
|
|
38
|
+
|
|
39
|
+
### World v2.0.3
|
|
40
|
+
|
|
41
|
+
- `packages/world/docs/world-SPEC-v2.0.3.md`
|
|
42
|
+
- Patches: `world-SPEC-v2.0.5-patch.md`
|
|
43
|
+
- FDR: `packages/world/docs/world-FDR-v2.0.2.md`
|
|
44
|
+
|
|
45
|
+
Key sections: Governance, Proposals, Authority, Head semantics, Branch persistence
|
|
46
|
+
|
|
47
|
+
## Application Layer
|
|
48
|
+
|
|
49
|
+
### App v2.3.1 (base + patches)
|
|
50
|
+
|
|
51
|
+
- Base: `packages/app/docs/APP-SPEC-v2.0.0.md`
|
|
52
|
+
- Patches: `APP-SPEC-v2.1.0-patch.md`, `APP-SPEC-v2.3.0.md`, `APP-SPEC-v2.3.1-patch.md`
|
|
53
|
+
- FDRs: Multiple (integration, policy, runtime, ext, pub)
|
|
54
|
+
|
|
55
|
+
Key sections: §8 Host Integration (effect registration)
|
|
56
|
+
|
|
57
|
+
### Compiler (MEL) v0.5.0
|
|
58
|
+
|
|
59
|
+
- `packages/compiler/docs/SPEC-v0.5.0.md`
|
|
60
|
+
- FDR: `packages/compiler/docs/FDR-v0.5.0.md`
|
|
61
|
+
|
|
62
|
+
Key sections: §2 Design Principles, §4 Syntax, §6 Semantic Rules, §8 Forbidden Constructs, §14 Examples, §21 $mel Namespace, Appendix (LLM 34-rule checklist)
|
|
63
|
+
|
|
64
|
+
## Intent + Translation
|
|
65
|
+
|
|
66
|
+
### Intent IR v0.2.0
|
|
67
|
+
|
|
68
|
+
- `packages/intent-ir/docs/SPEC-v0.2.0.md`
|
|
69
|
+
- FDR: `packages/intent-ir/docs/FDR-v0.1.0.md`
|
|
70
|
+
|
|
71
|
+
### Translator v1.0.3
|
|
72
|
+
|
|
73
|
+
- `packages/translator/core/docs/translator-SPEC-v1.0.3.md`
|
|
74
|
+
- FDR: `packages/translator/core/docs/translator-FDR-v0.11.md`
|
|
75
|
+
|
|
76
|
+
## Global ADRs
|
|
77
|
+
|
|
78
|
+
- ADR-002: onceIntent + $mel namespace → `docs/adr/adr-002-onceIntent-mel-namespace.md`
|
|
79
|
+
|
|
80
|
+
## Quick Lookup
|
|
81
|
+
|
|
82
|
+
| Need to understand... | Go to |
|
|
83
|
+
|----------------------|-------|
|
|
84
|
+
| Snapshot structure | Core SPEC §13 |
|
|
85
|
+
| Patch operations | Core SPEC §8.4.3, §14 |
|
|
86
|
+
| Effect handler contract | Host SPEC §7, §13 |
|
|
87
|
+
| MEL syntax | Compiler SPEC §4 |
|
|
88
|
+
| Flow guards (when/once/onceIntent) | Compiler SPEC §21 |
|
|
89
|
+
| Forbidden constructs | Compiler SPEC §8 |
|
|
90
|
+
| World governance | World SPEC §5-8 |
|
|
91
|
+
| App composition | App SPEC §8 |
|
|
92
|
+
| LLM code generation rules | Compiler SPEC Appendix |
|
|
93
|
+
|
|
94
|
+
## FDR Canonical Statements
|
|
95
|
+
|
|
96
|
+
| Statement | Source |
|
|
97
|
+
|-----------|--------|
|
|
98
|
+
| "Core computes. Host executes. These concerns never mix." | FDR-001 |
|
|
99
|
+
| "If it's not in Snapshot, it doesn't exist." | FDR-002 |
|
|
100
|
+
| "There is no suspended execution context." | FDR-003 |
|
|
101
|
+
| "Core declares requirements. Host fulfills them." | FDR-004 |
|
|
102
|
+
| "Errors are values. They live in Snapshot. They never throw." | FDR-005 |
|
|
103
|
+
| "Flows always terminate. Unbounded iteration is Host's responsibility." | FDR-006 |
|
|
104
|
+
| "If you need a value, read it from Snapshot. There is no other place." | FDR-007 |
|
|
105
|
+
| "Three operations are enough. Complexity is composed, not built-in." | FDR-012 |
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@manifesto-ai/skills",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "LLM knowledge pack for Manifesto — patterns, rules, and antipatterns for AI-assisted development",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"manifesto",
|
|
9
|
+
"llm",
|
|
10
|
+
"claude-code",
|
|
11
|
+
"cursor",
|
|
12
|
+
"ai-coding",
|
|
13
|
+
"skills"
|
|
14
|
+
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/manifesto-ai/core.git",
|
|
18
|
+
"directory": "skills"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"SKILL.md",
|
|
22
|
+
"knowledge/**",
|
|
23
|
+
"claude-code/**",
|
|
24
|
+
"scripts/**",
|
|
25
|
+
"tracking/**"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"postinstall": "node scripts/postinstall.mjs"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @manifesto-ai/skills postinstall
|
|
5
|
+
*
|
|
6
|
+
* Provides integration instructions after installation.
|
|
7
|
+
* v0.1: Simple message output only.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { dirname, resolve } from 'node:path';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
|
|
13
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const skillsRoot = resolve(__dirname, '..');
|
|
15
|
+
|
|
16
|
+
console.log(`
|
|
17
|
+
@manifesto-ai/skills v0.1.0 installed
|
|
18
|
+
|
|
19
|
+
Knowledge files available at:
|
|
20
|
+
${skillsRoot}/SKILL.md
|
|
21
|
+
|
|
22
|
+
Claude Code integration:
|
|
23
|
+
Add to your CLAUDE.md:
|
|
24
|
+
See @node_modules/@manifesto-ai/skills/SKILL.md for Manifesto development rules.
|
|
25
|
+
`);
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.1.0",
|
|
3
|
+
"lastChecked": "2026-02-09",
|
|
4
|
+
"mappings": [
|
|
5
|
+
{
|
|
6
|
+
"skill": "SKILL.md",
|
|
7
|
+
"sources": [
|
|
8
|
+
{ "path": "CLAUDE.md", "version": "1.0", "sections": ["§1", "§2", "§3", "§11"] },
|
|
9
|
+
{ "path": "packages/core/docs/SPEC-v2.0.0.md", "version": "2.0.0", "sections": ["§3"] }
|
|
10
|
+
],
|
|
11
|
+
"lastSynced": "2026-02-09"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"skill": "knowledge/architecture.md",
|
|
15
|
+
"sources": [
|
|
16
|
+
{ "path": "packages/core/docs/SPEC-v2.0.0.md", "version": "2.0.0", "sections": ["§3", "§13", "§16"] },
|
|
17
|
+
{ "path": "packages/core/docs/SPEC-v2.0.1-patch.md", "version": "2.0.1" },
|
|
18
|
+
{ "path": "packages/core/docs/FDR-v2.0.0.md", "version": "2.0.0", "sections": ["FDR-001", "FDR-002", "FDR-003", "FDR-004", "FDR-007"] },
|
|
19
|
+
{ "path": "packages/host/docs/host-SPEC-v2.0.2.md", "version": "2.0.2", "sections": ["§7", "§8", "§10"] },
|
|
20
|
+
{ "path": "packages/world/docs/world-SPEC-v2.0.3.md", "version": "2.0.3" }
|
|
21
|
+
],
|
|
22
|
+
"lastSynced": "2026-02-09"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"skill": "knowledge/mel-patterns.md",
|
|
26
|
+
"sources": [
|
|
27
|
+
{ "path": "packages/compiler/docs/SPEC-v0.5.0.md", "version": "0.5.0", "sections": ["§2", "§4", "§6", "§8", "§14", "§21", "LLM Checklist"] },
|
|
28
|
+
{ "path": "packages/compiler/docs/FDR-v0.5.0.md", "version": "0.5.0" }
|
|
29
|
+
],
|
|
30
|
+
"lastSynced": "2026-02-09"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"skill": "knowledge/effect-patterns.md",
|
|
34
|
+
"sources": [
|
|
35
|
+
{ "path": "packages/host/docs/host-SPEC-v2.0.2.md", "version": "2.0.2", "sections": ["§7", "§13"] },
|
|
36
|
+
{ "path": "packages/host/docs/host-FDR-v2.0.2.md", "version": "2.0.2" },
|
|
37
|
+
{ "path": "packages/app/docs/APP-SPEC-v2.3.0.md", "version": "2.3.0", "sections": ["§8"] }
|
|
38
|
+
],
|
|
39
|
+
"lastSynced": "2026-02-09"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"skill": "knowledge/patch-rules.md",
|
|
43
|
+
"sources": [
|
|
44
|
+
{ "path": "packages/core/docs/SPEC-v2.0.0.md", "version": "2.0.0", "sections": ["§13.3", "§14"] },
|
|
45
|
+
{ "path": "packages/core/docs/SPEC-v2.0.1-patch.md", "version": "2.0.1" },
|
|
46
|
+
{ "path": "packages/core/docs/FDR-v2.0.0.md", "version": "2.0.0", "sections": ["FDR-012"] }
|
|
47
|
+
],
|
|
48
|
+
"lastSynced": "2026-02-09"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"skill": "knowledge/antipatterns.md",
|
|
52
|
+
"sources": [
|
|
53
|
+
{ "path": "CLAUDE.md", "version": "1.0", "sections": ["§10"] },
|
|
54
|
+
{ "path": "packages/core/docs/FDR-v2.0.0.md", "version": "2.0.0" }
|
|
55
|
+
],
|
|
56
|
+
"note": "Accumulated from real usage feedback and constitutional violations.",
|
|
57
|
+
"lastSynced": "2026-02-09"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"skill": "knowledge/spec-index.md",
|
|
61
|
+
"sources": [
|
|
62
|
+
{ "path": "docs/llm/LLM-INDEX.md", "version": "latest" }
|
|
63
|
+
],
|
|
64
|
+
"lastSynced": "2026-02-09"
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
}
|