@manifesto-ai/skills 0.2.0 → 0.2.2
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/README.md +43 -0
- package/SKILL.md +19 -6
- package/knowledge/architecture.md +74 -110
- package/knowledge/effect-patterns.md +72 -103
- package/knowledge/llm/LLM-INDEX.md +74 -65
- package/knowledge/llm/LLM-NORMS.md +40 -35
- package/knowledge/packages/codegen.md +7 -47
- package/knowledge/packages/compiler.md +22 -87
- package/knowledge/packages/core.md +47 -56
- package/knowledge/packages/host.md +21 -92
- package/knowledge/packages/sdk.md +87 -43
- package/knowledge/packages/world.md +69 -105
- package/knowledge/patch-rules.md +120 -39
- package/knowledge/spec-index.md +81 -68
- package/package.json +8 -6
- package/scripts/manifesto-skills.mjs +146 -0
- package/tracking/source-map.json +52 -46
- package/knowledge/packages/app.md +0 -24
- package/scripts/postinstall.mjs +0 -25
package/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# @manifesto-ai/skills
|
|
2
|
+
|
|
3
|
+
LLM knowledge pack for Manifesto.
|
|
4
|
+
|
|
5
|
+
## Codex setup
|
|
6
|
+
|
|
7
|
+
With npm:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm i -D @manifesto-ai/skills
|
|
11
|
+
npm exec manifesto-skills install-codex
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
With pnpm:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add -D @manifesto-ai/skills
|
|
18
|
+
pnpm exec manifesto-skills install-codex
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
This installs the managed `manifesto` skill into `$CODEX_HOME/skills/manifesto` or `~/.codex/skills/manifesto`.
|
|
22
|
+
The setup is explicit so package-manager `postinstall` approval policies do not block installation.
|
|
23
|
+
|
|
24
|
+
If package-manager exec is unavailable, run:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
node ./node_modules/@manifesto-ai/skills/scripts/manifesto-skills.mjs install-codex
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
After installation, restart Codex.
|
|
31
|
+
|
|
32
|
+
## Claude Code setup
|
|
33
|
+
|
|
34
|
+
Add this to your `CLAUDE.md`:
|
|
35
|
+
|
|
36
|
+
```md
|
|
37
|
+
See @node_modules/@manifesto-ai/skills/SKILL.md for Manifesto development rules.
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Notes
|
|
41
|
+
|
|
42
|
+
- This package does not auto-install Codex files from `postinstall`.
|
|
43
|
+
- The installer refuses to overwrite an existing non-managed `manifesto` skill directory.
|
package/SKILL.md
CHANGED
|
@@ -1,14 +1,27 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
name: manifesto
|
|
3
|
+
description: Use when working on Manifesto repositories, MEL flows, or Core/Host/SDK/World boundaries. Loads implementation-aligned architecture, patch, effect, and package guidance.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Manifesto Skills v0.2.0
|
|
2
7
|
|
|
3
8
|
You are working on a Manifesto-based project. These rules are non-negotiable.
|
|
4
9
|
|
|
10
|
+
## Scope Note
|
|
11
|
+
|
|
12
|
+
This skills pack follows the current implementation in this repo.
|
|
13
|
+
|
|
14
|
+
- `@manifesto-ai/world` is the canonical governed composition package and exact consumer-facing facade.
|
|
15
|
+
- `@manifesto-ai/governance` and `@manifesto-ai/lineage` are implemented code packages and own their protocol-layer behavior.
|
|
16
|
+
- For governed work, prefer top-level `@manifesto-ai/world` for composition and import split-native packages directly only when the task is intentionally limited to governance or lineage.
|
|
17
|
+
|
|
5
18
|
## Absolute Rules
|
|
6
19
|
|
|
7
20
|
1. **Core is pure.** No IO, no `Date.now()`, no side effects, no randomness. `compute()` is a pure function.
|
|
8
21
|
2. **Snapshot is the only medium.** All communication between computations happens through Snapshot. No hidden channels, no return values from effects.
|
|
9
22
|
3. **Three patch ops only.** `set`, `unset`, `merge` (shallow). No other mutation operations exist.
|
|
10
23
|
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
|
|
24
|
+
5. **Errors are values.** Errors live in Snapshot state, never thrown. Core must not throw for business logic.
|
|
12
25
|
6. **Flows terminate.** No unbounded loops in Flow. Host controls iteration. All guards required for re-entry safety.
|
|
13
26
|
7. **`$` is reserved.** `$host`, `$mel`, `$system` are platform namespaces. Never use `$` in domain identifiers.
|
|
14
27
|
|
|
@@ -18,17 +31,17 @@ SPEC > FDR > ADR > Code > README. Never invent semantics not in SPEC.
|
|
|
18
31
|
|
|
19
32
|
## Task-Specific Knowledge
|
|
20
33
|
|
|
21
|
-
Load these
|
|
34
|
+
Load these before writing code in each area:
|
|
22
35
|
|
|
23
36
|
| Task | Knowledge File |
|
|
24
37
|
|------|---------------|
|
|
25
|
-
| Understanding Core/Host/World boundaries | `@knowledge/architecture.md` |
|
|
38
|
+
| Understanding Core/Host/SDK/World boundaries | `@knowledge/architecture.md` |
|
|
26
39
|
| Writing MEL domain code | `@knowledge/mel-patterns.md` |
|
|
27
40
|
| MEL complete function reference | `@knowledge/mel-reference.md` |
|
|
28
41
|
| Implementing effect handlers | `@knowledge/effect-patterns.md` |
|
|
29
42
|
| Working with state/patches | `@knowledge/patch-rules.md` |
|
|
30
43
|
| Reviewing or debugging | `@knowledge/antipatterns.md` |
|
|
31
|
-
| Looking up
|
|
44
|
+
| Looking up current specs and design docs | `@knowledge/spec-index.md` |
|
|
32
45
|
|
|
33
46
|
## Package API Reference
|
|
34
47
|
|
|
@@ -47,7 +60,7 @@ Load when working with a specific package API:
|
|
|
47
60
|
|
|
48
61
|
Before submitting any code change, verify:
|
|
49
62
|
|
|
50
|
-
- [ ] Determinism preserved? (same input
|
|
63
|
+
- [ ] Determinism preserved? (same input -> same output)
|
|
51
64
|
- [ ] Snapshot is sole communication medium?
|
|
52
65
|
- [ ] All mutations via patches?
|
|
53
66
|
- [ ] No forbidden imports across package boundaries?
|
|
@@ -1,94 +1,86 @@
|
|
|
1
1
|
# Manifesto Architecture
|
|
2
2
|
|
|
3
|
-
> Source:
|
|
4
|
-
> Last synced: 2026-
|
|
3
|
+
> Source: `packages/core/docs/core-SPEC.md`, `packages/host/docs/host-SPEC.md`, `packages/sdk/docs/sdk-SPEC-v2.0.0.md`, current `packages/world/src/*`
|
|
4
|
+
> Last synced: 2026-03-31
|
|
5
5
|
|
|
6
6
|
## Rules
|
|
7
7
|
|
|
8
|
-
> **R1**: Core computes, Host executes. These concerns never mix.
|
|
9
|
-
> **R2**: Snapshot is the only medium of communication. If it's not in Snapshot, it doesn't exist.
|
|
10
|
-
> **R3**: There is no suspended execution context. All continuity is expressed through Snapshot.
|
|
11
|
-
> **R4**: Effects are declarations, not executions. Core declares; Host fulfills.
|
|
12
|
-
> **R5**: If you need a value, read it from Snapshot. There is no other place.
|
|
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
|
-
```
|
|
8
|
+
> **R1**: Core computes, Host executes. These concerns never mix.
|
|
9
|
+
> **R2**: Snapshot is the only medium of communication. If it's not in Snapshot, it doesn't exist.
|
|
10
|
+
> **R3**: There is no suspended execution context. All continuity is expressed through Snapshot.
|
|
11
|
+
> **R4**: Effects are declarations, not executions. Core declares; Host fulfills.
|
|
12
|
+
> **R5**: If you need a value, read it from Snapshot. There is no other place.
|
|
25
13
|
|
|
26
14
|
## The Fundamental Equation
|
|
27
15
|
|
|
28
|
-
```
|
|
29
|
-
compute(schema, snapshot, intent, context)
|
|
16
|
+
```typescript
|
|
17
|
+
compute(schema, snapshot, intent, context) -> {
|
|
18
|
+
patches,
|
|
19
|
+
systemDelta,
|
|
20
|
+
trace,
|
|
21
|
+
status,
|
|
22
|
+
}
|
|
30
23
|
```
|
|
31
24
|
|
|
32
|
-
- **Pure**: Same input
|
|
33
|
-
- **Total**:
|
|
34
|
-
- **Traceable**:
|
|
35
|
-
-
|
|
36
|
-
- If `requirements` is non-empty → Host fulfills them, then calls `compute()` again
|
|
25
|
+
- **Pure**: Same input must produce the same result.
|
|
26
|
+
- **Total**: Business logic failures are reported as values, not thrown.
|
|
27
|
+
- **Traceable**: Compute returns a trace graph for explainability.
|
|
28
|
+
- **Resumable via Snapshot**: Host applies `patches` + `systemDelta`, then re-enters `compute()` with the new snapshot.
|
|
37
29
|
|
|
38
|
-
##
|
|
30
|
+
## Runtime Paths
|
|
39
31
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
Host
|
|
46
|
-
|
|
47
|
-
Core (pure computation)
|
|
48
|
-
↓
|
|
49
|
-
New Snapshot (via patches)
|
|
50
|
-
↓
|
|
51
|
-
New World (immutable)
|
|
32
|
+
Two practical paths exist in the current repo:
|
|
33
|
+
|
|
34
|
+
```text
|
|
35
|
+
SDK-style app
|
|
36
|
+
createManifesto()
|
|
37
|
+
-> Host
|
|
38
|
+
-> Core
|
|
52
39
|
```
|
|
53
40
|
|
|
54
|
-
|
|
41
|
+
```text
|
|
42
|
+
Governed path
|
|
43
|
+
Actor submits IntentInstance
|
|
44
|
+
-> World
|
|
45
|
+
-> WorldExecutor
|
|
46
|
+
-> Host
|
|
47
|
+
-> Core
|
|
48
|
+
-> new World state + lineage records
|
|
49
|
+
```
|
|
55
50
|
|
|
56
51
|
## Package Sovereignty
|
|
57
52
|
|
|
58
53
|
| Package | Responsibility | MUST NOT |
|
|
59
54
|
|---------|---------------|----------|
|
|
60
|
-
| **Core** | Pure computation, expression evaluation, flow interpretation, patch generation,
|
|
61
|
-
| **Host** | Effect execution, patch application, compute loop, requirement fulfillment |
|
|
62
|
-
| **World** | Proposal
|
|
63
|
-
| **
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
| runtime | SDK public surface |
|
|
74
|
-
| sdk | core internals, host internals, world internals |
|
|
55
|
+
| **Core** | Pure computation, expression evaluation, flow interpretation, patch generation, validation, explanation | IO, wall-clock access, effect execution, Host/World policy |
|
|
56
|
+
| **Host** | Effect execution, patch application, compute loop orchestration, requirement fulfillment | Compute semantic meaning, suppress declared effects, make governance decisions |
|
|
57
|
+
| **World** | Proposal lifecycle, authority evaluation, lineage DAG, persistence, governance event emission | Import `@manifesto-ai/host` directly, compute semantic meaning, apply Core patches itself |
|
|
58
|
+
| **SDK** | Public app entrypoint (`createManifesto`), typed effect registration, `dispatchAsync`, typed patch helpers, selected re-exports | Invent semantics outside Core/Host/World public contracts |
|
|
59
|
+
|
|
60
|
+
## Current Governed Structure
|
|
61
|
+
|
|
62
|
+
In this repo's implementation:
|
|
63
|
+
|
|
64
|
+
- `@manifesto-ai/world` is the exact consumer-facing governed facade
|
|
65
|
+
- `@manifesto-ai/governance` is the current governance protocol package
|
|
66
|
+
- `@manifesto-ai/lineage` is the current continuity protocol package
|
|
67
|
+
- top-level `@manifesto-ai/world` re-exports the split-native surfaces needed for explicit composition
|
|
75
68
|
|
|
76
69
|
## Snapshot Structure
|
|
77
70
|
|
|
78
71
|
```typescript
|
|
79
72
|
type Snapshot = {
|
|
80
|
-
data:
|
|
81
|
-
computed: Record<string, unknown>;
|
|
73
|
+
data: unknown;
|
|
74
|
+
computed: Record<string, unknown>;
|
|
82
75
|
system: {
|
|
83
|
-
status:
|
|
76
|
+
status: "idle" | "computing" | "pending" | "error";
|
|
84
77
|
lastError: ErrorValue | null;
|
|
85
|
-
|
|
86
|
-
pendingRequirements: readonly Requirement[];
|
|
78
|
+
pendingRequirements: Requirement[];
|
|
87
79
|
currentAction: string | null;
|
|
88
80
|
};
|
|
89
|
-
input: unknown;
|
|
81
|
+
input: unknown;
|
|
90
82
|
meta: {
|
|
91
|
-
version: number;
|
|
83
|
+
version: number;
|
|
92
84
|
timestamp: number;
|
|
93
85
|
randomSeed: string;
|
|
94
86
|
schemaHash: string;
|
|
@@ -96,63 +88,35 @@ type Snapshot = {
|
|
|
96
88
|
};
|
|
97
89
|
```
|
|
98
90
|
|
|
99
|
-
##
|
|
100
|
-
|
|
101
|
-
- `$host` — Host-owned internal state (intent slots, execution context). Excluded from hash.
|
|
102
|
-
- `$mel` — Compiler-owned guard state (`$mel.guards.*`). Excluded from hash.
|
|
103
|
-
- `$system.*` — System values (uuid, time.now). Lowered to effects by compiler.
|
|
104
|
-
- Domain schemas MUST NOT define `$`-prefixed fields.
|
|
91
|
+
## Compute / Apply Cycle
|
|
105
92
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
```
|
|
93
|
+
```text
|
|
109
94
|
Host calls compute(schema, snapshot, intent, context)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
Each `compute()` is complete and independent. There is no "resume".
|
|
117
|
-
|
|
118
|
-
## Antipatterns
|
|
119
|
-
|
|
120
|
-
### Intelligent Host
|
|
121
|
-
```typescript
|
|
122
|
-
// FORBIDDEN — Host making decisions
|
|
123
|
-
if (shouldSkipEffect(req)) { return []; }
|
|
124
|
-
|
|
125
|
-
// Host MUST execute or report failure, never decide
|
|
95
|
+
-> Core returns patches + systemDelta + trace + status
|
|
96
|
+
-> Host applies patches
|
|
97
|
+
-> Host applies systemDelta
|
|
98
|
+
-> If status is pending, Host fulfills requirements and calls compute() again
|
|
126
99
|
```
|
|
127
100
|
|
|
128
|
-
|
|
129
|
-
```typescript
|
|
130
|
-
// FORBIDDEN
|
|
131
|
-
const result = await executeEffect();
|
|
132
|
-
core.compute(schema, snapshot, { ...intent, result });
|
|
133
|
-
|
|
134
|
-
// CORRECT — Effect returns patches → Host applies → Core reads from Snapshot
|
|
135
|
-
```
|
|
101
|
+
Each `compute()` call is complete and independent. Continuity lives in the snapshot, not in hidden runtime state.
|
|
136
102
|
|
|
137
|
-
|
|
138
|
-
```typescript
|
|
139
|
-
// FORBIDDEN — Core cannot know about execution
|
|
140
|
-
if (effectExecutionSucceeded) { ... }
|
|
103
|
+
## Platform Namespaces
|
|
141
104
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
105
|
+
- `$host` is Host-owned internal state.
|
|
106
|
+
- `$mel` is compiler-owned guard state.
|
|
107
|
+
- `$system.*` values are surfaced in MEL and lowered through platform mechanics.
|
|
108
|
+
- Domain schemas must not define `$`-prefixed fields.
|
|
145
109
|
|
|
146
110
|
## Why
|
|
147
111
|
|
|
148
|
-
|
|
149
|
-
- **
|
|
150
|
-
- **
|
|
151
|
-
- **
|
|
152
|
-
- **Reproducibility**: Snapshot serialization enables time-travel debugging
|
|
112
|
+
- **Determinism**: Core can be tested without mocks.
|
|
113
|
+
- **Auditability**: World records proposal and decision lineage.
|
|
114
|
+
- **Portability**: Host remains the execution seam.
|
|
115
|
+
- **Clarity**: SDK is the public app-facing direct-dispatch layer, while World is the explicit governed composition layer around Host.
|
|
153
116
|
|
|
154
117
|
## Cross-References
|
|
155
118
|
|
|
156
|
-
- MEL syntax:
|
|
157
|
-
- Effect handlers:
|
|
158
|
-
- Patch operations:
|
|
119
|
+
- MEL syntax: `@knowledge/mel-patterns.md`
|
|
120
|
+
- Effect handlers: `@knowledge/effect-patterns.md`
|
|
121
|
+
- Patch operations: `@knowledge/patch-rules.md`
|
|
122
|
+
- World package API: `@knowledge/packages/world.md`
|
|
@@ -1,52 +1,66 @@
|
|
|
1
1
|
# Effect Patterns
|
|
2
2
|
|
|
3
|
-
> Source:
|
|
4
|
-
> Last synced: 2026-
|
|
3
|
+
> Source: `packages/host/docs/host-SPEC.md`, `packages/sdk/src/types.ts`, `packages/sdk/src/create-manifesto.ts`
|
|
4
|
+
> Last synced: 2026-03-28
|
|
5
5
|
|
|
6
6
|
## Rules
|
|
7
7
|
|
|
8
|
-
> **R1**: Effect handlers
|
|
9
|
-
> **R2**: Failures
|
|
10
|
-
> **R3**: Effect handlers
|
|
11
|
-
> **R4**: Effects are declared by Core
|
|
12
|
-
> **R5**:
|
|
8
|
+
> **R1**: Effect handlers return `Patch[]` and should not use exceptions as the business-logic result channel.
|
|
9
|
+
> **R2**: Failures should be expressed as state patches or terminal execution results.
|
|
10
|
+
> **R3**: Effect handlers are IO adapters, not policy engines.
|
|
11
|
+
> **R4**: Effects are declared by Core and executed by Host.
|
|
12
|
+
> **R5**: Handler patches should target domain-owned or platform-approved paths, not ad hoc hidden state.
|
|
13
13
|
|
|
14
14
|
## Handler Contract
|
|
15
15
|
|
|
16
|
-
Developers register effect handlers through the
|
|
16
|
+
Developers register effect handlers through the SDK:
|
|
17
17
|
|
|
18
18
|
```typescript
|
|
19
|
-
|
|
19
|
+
type EffectContext<T = unknown> = {
|
|
20
|
+
readonly snapshot: Readonly<Snapshot<T>>;
|
|
21
|
+
};
|
|
22
|
+
|
|
20
23
|
type EffectHandler = (
|
|
21
24
|
params: unknown,
|
|
22
|
-
ctx:
|
|
25
|
+
ctx: EffectContext,
|
|
23
26
|
) => Promise<readonly Patch[]>;
|
|
27
|
+
```
|
|
24
28
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
Host uses a different internal signature, but SDK adapts your handler automatically.
|
|
30
|
+
|
|
31
|
+
## Registration Pattern
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { createManifesto } from "@manifesto-ai/sdk";
|
|
35
|
+
|
|
36
|
+
const app = createManifesto({
|
|
37
|
+
schema: domainSchema,
|
|
38
|
+
effects: {
|
|
39
|
+
"api.fetchUser": fetchUser,
|
|
40
|
+
"api.createTodo": createTodo,
|
|
41
|
+
"payment.process": processPayment,
|
|
42
|
+
},
|
|
43
|
+
});
|
|
30
44
|
```
|
|
31
45
|
|
|
32
|
-
|
|
46
|
+
Effect names must match effect types declared in schema or MEL.
|
|
33
47
|
|
|
34
48
|
## Patterns
|
|
35
49
|
|
|
36
|
-
### Successful
|
|
50
|
+
### Successful effect
|
|
37
51
|
|
|
38
52
|
```typescript
|
|
39
53
|
async function fetchUser(params: { id: string }): Promise<Patch[]> {
|
|
40
54
|
const response = await fetch(`/users/${params.id}`);
|
|
41
55
|
const data = await response.json();
|
|
42
56
|
return [
|
|
43
|
-
{ op:
|
|
44
|
-
{ op:
|
|
57
|
+
{ op: "set", path: "data.user.data", value: data },
|
|
58
|
+
{ op: "set", path: "data.user.error", value: null },
|
|
45
59
|
];
|
|
46
60
|
}
|
|
47
61
|
```
|
|
48
62
|
|
|
49
|
-
### Failed
|
|
63
|
+
### Failed effect
|
|
50
64
|
|
|
51
65
|
```typescript
|
|
52
66
|
async function fetchUser(params: { id: string }): Promise<Patch[]> {
|
|
@@ -54,134 +68,89 @@ async function fetchUser(params: { id: string }): Promise<Patch[]> {
|
|
|
54
68
|
const response = await fetch(`/users/${params.id}`);
|
|
55
69
|
if (!response.ok) {
|
|
56
70
|
return [
|
|
57
|
-
{ op:
|
|
58
|
-
{ op:
|
|
71
|
+
{ op: "set", path: "data.user.error", value: { code: response.status } },
|
|
72
|
+
{ op: "set", path: "data.user.data", value: null },
|
|
59
73
|
];
|
|
60
74
|
}
|
|
75
|
+
|
|
61
76
|
const data = await response.json();
|
|
62
77
|
return [
|
|
63
|
-
{ op:
|
|
64
|
-
{ op:
|
|
78
|
+
{ op: "set", path: "data.user.data", value: data },
|
|
79
|
+
{ op: "set", path: "data.user.error", value: null },
|
|
65
80
|
];
|
|
66
81
|
} catch (error) {
|
|
67
82
|
return [
|
|
68
|
-
{
|
|
69
|
-
|
|
83
|
+
{
|
|
84
|
+
op: "set",
|
|
85
|
+
path: "data.user.error",
|
|
86
|
+
value: { message: error instanceof Error ? error.message : String(error) },
|
|
87
|
+
},
|
|
88
|
+
{ op: "set", path: "data.user.data", value: null },
|
|
70
89
|
];
|
|
71
90
|
}
|
|
72
91
|
}
|
|
73
92
|
```
|
|
74
93
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
```typescript
|
|
78
|
-
import { createApp } from '@manifesto-ai/sdk';
|
|
79
|
-
|
|
80
|
-
const app = createApp({
|
|
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.
|
|
94
|
+
## Built-in collection effects
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
Collection-oriented effects such as `array.filter`, `array.map`, and `record.keys` are platform/compiler features. This document is about custom domain handlers such as API, storage, or integration effects.
|
|
97
97
|
|
|
98
98
|
## Antipatterns
|
|
99
99
|
|
|
100
|
-
### Throwing
|
|
100
|
+
### Throwing as the primary failure channel
|
|
101
101
|
|
|
102
102
|
```typescript
|
|
103
|
-
//
|
|
103
|
+
// Avoid this for business logic failures
|
|
104
104
|
async function bad(params) {
|
|
105
|
-
if (!params.id) throw new Error(
|
|
106
|
-
|
|
107
|
-
return [{ op: 'set', path: 'data.result', value: result }];
|
|
105
|
+
if (!params.id) throw new Error("Missing id");
|
|
106
|
+
return [{ op: "set", path: "data.result", value: await api.call(params.id) }];
|
|
108
107
|
}
|
|
109
108
|
|
|
110
|
-
//
|
|
109
|
+
// Prefer patches
|
|
111
110
|
async function good(params) {
|
|
112
111
|
if (!params.id) {
|
|
113
|
-
return [{ op:
|
|
112
|
+
return [{ op: "set", path: "data.error", value: "Missing id" }];
|
|
114
113
|
}
|
|
115
|
-
|
|
116
|
-
return [{ op: 'set', path: 'data.result', value: result }];
|
|
114
|
+
return [{ op: "set", path: "data.result", value: await api.call(params.id) }];
|
|
117
115
|
}
|
|
118
116
|
```
|
|
119
117
|
|
|
120
|
-
### Domain
|
|
118
|
+
### Domain logic in handler
|
|
121
119
|
|
|
122
120
|
```typescript
|
|
123
|
-
//
|
|
121
|
+
// Avoid policy decisions here
|
|
124
122
|
async function purchaseHandler(params) {
|
|
125
|
-
if (params.amount > 1000) {
|
|
126
|
-
return [{ op:
|
|
123
|
+
if (params.amount > 1000) {
|
|
124
|
+
return [{ op: "set", path: "data.approval.required", value: true }];
|
|
127
125
|
}
|
|
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 }];
|
|
126
|
+
return [];
|
|
135
127
|
}
|
|
136
128
|
```
|
|
137
129
|
|
|
138
|
-
|
|
130
|
+
Keep business rules in Core / Flow / MEL where they remain traceable.
|
|
131
|
+
|
|
132
|
+
### Returning raw values
|
|
139
133
|
|
|
140
134
|
```typescript
|
|
141
|
-
//
|
|
135
|
+
// Wrong: not a Patch[]
|
|
142
136
|
async function bad(params) {
|
|
143
|
-
return
|
|
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 }];
|
|
137
|
+
return api.fetchData(params.id);
|
|
150
138
|
}
|
|
151
139
|
```
|
|
152
140
|
|
|
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
141
|
## Requirement Lifecycle
|
|
164
142
|
|
|
165
143
|
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
144
|
|
|
179
|
-
|
|
145
|
+
1. Core emits a pending result with requirements in snapshot/system state.
|
|
146
|
+
2. Host executes the matching handler.
|
|
147
|
+
3. Host applies returned patches.
|
|
148
|
+
4. Host updates system state and re-enters compute.
|
|
180
149
|
|
|
181
|
-
|
|
150
|
+
The details differ between Core and Host internals, but the contract is the same: effect execution feeds back through snapshot changes, never hidden channels.
|
|
182
151
|
|
|
183
152
|
## Cross-References
|
|
184
153
|
|
|
185
|
-
- Patch operations:
|
|
186
|
-
- State structure:
|
|
187
|
-
-
|
|
154
|
+
- Patch operations: `@knowledge/patch-rules.md`
|
|
155
|
+
- State structure: `@knowledge/architecture.md`
|
|
156
|
+
- SDK package API: `@knowledge/packages/sdk.md`
|