@directive-run/claude-plugin 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/.claude-plugin/plugin.json +6 -0
  2. package/LICENSE +26 -0
  3. package/README.md +113 -0
  4. package/dist/index.cjs +2 -0
  5. package/dist/index.cjs.map +1 -0
  6. package/dist/index.d.cts +66 -0
  7. package/dist/index.d.ts +66 -0
  8. package/dist/index.js +2 -0
  9. package/dist/index.js.map +1 -0
  10. package/package.json +64 -0
  11. package/skills/README.md +9 -0
  12. package/skills/building-ai-agents/SKILL.md +414 -0
  13. package/skills/building-ai-agents/ai-adapters.md +257 -0
  14. package/skills/building-ai-agents/ai-agents-streaming.md +307 -0
  15. package/skills/building-ai-agents/ai-communication.md +304 -0
  16. package/skills/building-ai-agents/api-skeleton.md +5 -0
  17. package/skills/building-ai-agents/examples.md +849 -0
  18. package/skills/building-ai-orchestrators/SKILL.md +401 -0
  19. package/skills/building-ai-orchestrators/ai-multi-agent.md +375 -0
  20. package/skills/building-ai-orchestrators/ai-orchestrator.md +466 -0
  21. package/skills/building-ai-orchestrators/ai-tasks.md +268 -0
  22. package/skills/building-ai-orchestrators/api-skeleton.md +5 -0
  23. package/skills/building-ai-orchestrators/examples.md +1609 -0
  24. package/skills/building-directive-systems/SKILL.md +499 -0
  25. package/skills/building-directive-systems/api-skeleton.md +5 -0
  26. package/skills/building-directive-systems/examples.md +4149 -0
  27. package/skills/building-directive-systems/multi-module.md +327 -0
  28. package/skills/building-directive-systems/plugins.md +361 -0
  29. package/skills/building-directive-systems/react-adapter.md +376 -0
  30. package/skills/building-directive-systems/system-api.md +463 -0
  31. package/skills/getting-started-with-directive/SKILL.md +198 -0
  32. package/skills/getting-started-with-directive/api-skeleton.md +5 -0
  33. package/skills/getting-started-with-directive/core-patterns.md +240 -0
  34. package/skills/getting-started-with-directive/examples.md +87 -0
  35. package/skills/getting-started-with-directive/sitemap.md +202 -0
  36. package/skills/hardening-ai-systems/SKILL.md +377 -0
  37. package/skills/hardening-ai-systems/ai-budget-resilience.md +408 -0
  38. package/skills/hardening-ai-systems/ai-guardrails-memory.md +370 -0
  39. package/skills/hardening-ai-systems/ai-security.md +400 -0
  40. package/skills/hardening-ai-systems/api-skeleton.md +5 -0
  41. package/skills/hardening-ai-systems/examples.md +628 -0
  42. package/skills/migrating-to-directive/SKILL.md +249 -0
  43. package/skills/migrating-to-directive/anti-patterns.md +382 -0
  44. package/skills/migrating-to-directive/api-skeleton.md +5 -0
  45. package/skills/migrating-to-directive/core-patterns.md +240 -0
  46. package/skills/migrating-to-directive/examples.md +511 -0
  47. package/skills/migrating-to-directive/schema-types.md +273 -0
  48. package/skills/reviewing-directive-code/SKILL.md +232 -0
  49. package/skills/reviewing-directive-code/anti-patterns.md +382 -0
  50. package/skills/reviewing-directive-code/api-skeleton.md +5 -0
  51. package/skills/reviewing-directive-code/core-patterns.md +240 -0
  52. package/skills/reviewing-directive-code/examples.md +463 -0
  53. package/skills/reviewing-directive-code/naming.md +321 -0
  54. package/skills/scaffolding-directive-modules/SKILL.md +218 -0
  55. package/skills/scaffolding-directive-modules/api-skeleton.md +5 -0
  56. package/skills/scaffolding-directive-modules/core-patterns.md +240 -0
  57. package/skills/scaffolding-directive-modules/examples.md +1386 -0
  58. package/skills/scaffolding-directive-modules/naming.md +321 -0
  59. package/skills/scaffolding-directive-modules/schema-types.md +273 -0
  60. package/skills/testing-ai-systems/SKILL.md +398 -0
  61. package/skills/testing-ai-systems/ai-debug-observability.md +329 -0
  62. package/skills/testing-ai-systems/ai-mcp-rag.md +293 -0
  63. package/skills/testing-ai-systems/ai-testing-evals.md +485 -0
  64. package/skills/testing-ai-systems/api-skeleton.md +5 -0
  65. package/skills/testing-ai-systems/examples.md +1261 -0
  66. package/skills/testing-directive-code/SKILL.md +479 -0
  67. package/skills/testing-directive-code/api-skeleton.md +5 -0
  68. package/skills/testing-directive-code/examples.md +156 -0
  69. package/skills/testing-directive-code/history.md +351 -0
  70. package/skills/testing-directive-code/testing.md +412 -0
  71. package/skills/writing-directive-constraints/SKILL.md +486 -0
  72. package/skills/writing-directive-constraints/api-skeleton.md +5 -0
  73. package/skills/writing-directive-constraints/constraints.md +274 -0
  74. package/skills/writing-directive-constraints/error-boundaries.md +330 -0
  75. package/skills/writing-directive-constraints/examples.md +1361 -0
  76. package/skills/writing-directive-constraints/resolvers.md +367 -0
  77. package/skills/writing-directive-modules/SKILL.md +342 -0
  78. package/skills/writing-directive-modules/anti-patterns.md +382 -0
  79. package/skills/writing-directive-modules/api-skeleton.md +5 -0
  80. package/skills/writing-directive-modules/core-patterns.md +240 -0
  81. package/skills/writing-directive-modules/examples.md +1639 -0
  82. package/skills/writing-directive-modules/naming.md +321 -0
  83. package/skills/writing-directive-modules/schema-types.md +273 -0
@@ -0,0 +1,382 @@
1
+ # Anti-Patterns
2
+
3
+ > Covers `@directive-run/core` and `@directive-run/react` — hallucination-prone API patterns to avoid.
4
+
5
+ 19 most common mistakes when generating Directive code, ranked by AI hallucination frequency. Every code generation MUST be checked against this list.
6
+
7
+ ## 1. Unnecessary Type Casting on Facts/Derivations
8
+
9
+ ```typescript
10
+ // WRONG – schema already provides the type
11
+ const profile = system.facts.profile as ResourceState<Profile>;
12
+
13
+ // CORRECT – trust the schema
14
+ const profile = system.facts.profile;
15
+ ```
16
+
17
+ ## 2. Flat Schema (Missing facts Wrapper)
18
+
19
+ ```typescript
20
+ // WRONG – facts must be nested under schema.facts
21
+ createModule("counter", {
22
+ schema: {
23
+ phase: t.string(),
24
+ count: t.number(),
25
+ },
26
+ });
27
+
28
+ // CORRECT
29
+ createModule("counter", {
30
+ schema: {
31
+ facts: {
32
+ phase: t.string(),
33
+ count: t.number(),
34
+ },
35
+ },
36
+ });
37
+ ```
38
+
39
+ ## 3. Bare `facts.*` in Multi-Module Constraints
40
+
41
+ ```typescript
42
+ // WRONG – multi-module constraints use facts.self for own module
43
+ constraints: {
44
+ checkItems: {
45
+ when: (facts) => facts.items.length > 0,
46
+ require: { type: "PROCESS" },
47
+ },
48
+ },
49
+
50
+ // CORRECT – use facts.self.* for own module facts
51
+ constraints: {
52
+ checkItems: {
53
+ when: (facts) => facts.self.items.length > 0,
54
+ require: { type: "PROCESS" },
55
+ },
56
+ },
57
+ ```
58
+
59
+ ## 4. Nonexistent Schema Builders
60
+
61
+ ```typescript
62
+ // WRONG – t.map(), t.set(), t.promise() do not exist
63
+ schema: {
64
+ facts: {
65
+ cache: t.map<string, User>(),
66
+ tags: t.set<string>(),
67
+ pending: t.promise<Data>(),
68
+ },
69
+ },
70
+
71
+ // CORRECT – use t.object() with type parameter
72
+ schema: {
73
+ facts: {
74
+ cache: t.object<Map<string, User>>(),
75
+ tags: t.object<Set<string>>(),
76
+ pending: t.object<Promise<Data>>(),
77
+ },
78
+ },
79
+ ```
80
+
81
+ ## 5. Abbreviating `context` to `ctx`
82
+
83
+ ```typescript
84
+ // WRONG – never abbreviate context
85
+ resolve: async (req, ctx) => {
86
+ ctx.facts.status = "done";
87
+ },
88
+
89
+ // CORRECT – always spell out context
90
+ resolve: async (req, context) => {
91
+ context.facts.status = "done";
92
+ },
93
+ ```
94
+
95
+ ## 6. Flat Module Config (No schema Wrapper)
96
+
97
+ ```typescript
98
+ // WRONG – properties must be inside schema.facts
99
+ createModule("timer", {
100
+ phase: t.string(),
101
+ elapsed: t.number(),
102
+ });
103
+
104
+ // CORRECT – wrap in schema: { facts: {} }
105
+ createModule("timer", {
106
+ schema: {
107
+ facts: {
108
+ phase: t.string(),
109
+ elapsed: t.number(),
110
+ },
111
+ },
112
+ });
113
+ ```
114
+
115
+ ## 7. String-Based Event Dispatch
116
+
117
+ ```typescript
118
+ // WRONG – there is no two-argument string-keyed dispatch signature
119
+ system.dispatch("login", { token: "abc" });
120
+
121
+ // CORRECT – use the typed events accessor (preferred — autocomplete + payload typing)
122
+ system.events.login({ token: "abc" });
123
+
124
+ // ALSO VALID – the single-arg object form of dispatch() is supported when you
125
+ // need to forward a programmatically-built event. Prefer the events accessor
126
+ // for normal code.
127
+ system.dispatch({ type: "login", token: "abc" });
128
+ ```
129
+
130
+ ## 8. Direct Array/Object Mutation
131
+
132
+ ```typescript
133
+ // WRONG – proxy cannot detect in-place mutations
134
+ facts.items.push(item);
135
+ facts.config.theme = "dark";
136
+
137
+ // CORRECT – replace the entire value
138
+ facts.items = [...facts.items, item];
139
+ facts.config = { ...facts.config, theme: "dark" };
140
+ ```
141
+
142
+ ## 9. Nonexistent `useDirective` Hook
143
+
144
+ ```typescript
145
+ // WRONG – there is no useDirective hook
146
+ const state = useDirective(system);
147
+
148
+ // CORRECT – use useSelector with a selector function
149
+ const count = useSelector(system, (s) => s.facts.count);
150
+ const isLoading = useSelector(system, (s) => s.derive.isLoading);
151
+ ```
152
+
153
+ ## 10. Bracket Notation for Namespaced Facts
154
+
155
+ ```typescript
156
+ // WRONG – internal separator is not part of the public API
157
+ const status = facts["auth::status"];
158
+ const token = facts["auth_token"];
159
+
160
+ // CORRECT – use dot notation through the namespace proxy
161
+ const status = facts.auth.status;
162
+ const token = facts.auth.token;
163
+ ```
164
+
165
+ ## 11. Returning Data from Resolvers
166
+
167
+ ```typescript
168
+ // WRONG – resolvers return void, not data
169
+ resolve: async (req, context) => {
170
+ const user = await fetchUser(req.userId);
171
+
172
+ return user; // Return value is ignored
173
+ },
174
+
175
+ // CORRECT – mutate context.facts to store results
176
+ resolve: async (req, context) => {
177
+ const user = await fetchUser(req.userId);
178
+ context.facts.user = user;
179
+ },
180
+ ```
181
+
182
+ ## 12. Async Logic in `init`
183
+
184
+ ```typescript
185
+ // WRONG – init is synchronous, facts assignment only
186
+ init: async (facts) => {
187
+ const data = await fetch("/api/config");
188
+ facts.config = await data.json();
189
+ },
190
+
191
+ // CORRECT – init sets defaults; use constraints/resolvers for async work
192
+ init: (facts) => {
193
+ facts.config = null;
194
+ facts.phase = "loading";
195
+ },
196
+
197
+ constraints: {
198
+ loadConfig: {
199
+ when: (facts) => facts.config === null,
200
+ require: { type: "LOAD_CONFIG" },
201
+ },
202
+ },
203
+ ```
204
+
205
+ ## 13. Missing `settle()` After `start()`
206
+
207
+ ```typescript
208
+ // WRONG – constraints fire on start, resolvers are async
209
+ system.start();
210
+ console.log(system.facts.data); // May still be null
211
+
212
+ // CORRECT – wait for resolvers to complete
213
+ system.start();
214
+ await system.settle();
215
+ console.log(system.facts.data); // Resolved
216
+ ```
217
+
218
+ ## 14. Missing `crossModuleDeps` Declaration
219
+
220
+ ```typescript
221
+ // WRONG – accessing auth facts without declaring dependency
222
+ const dataModule = createModule("data", {
223
+ schema: { facts: { items: t.array(t.string()) } },
224
+ constraints: {
225
+ fetchWhenAuth: {
226
+ when: (facts) => facts.auth.isAuthenticated, // Type error
227
+ require: { type: "FETCH" },
228
+ },
229
+ },
230
+ });
231
+
232
+ // CORRECT – declare crossModuleDeps for type-safe cross-module access
233
+ const dataModule = createModule("data", {
234
+ schema: { facts: { items: t.array(t.string()) } },
235
+ crossModuleDeps: { auth: authSchema },
236
+ constraints: {
237
+ fetchWhenAuth: {
238
+ when: (facts) => facts.auth.isAuthenticated,
239
+ require: { type: "FETCH" },
240
+ },
241
+ },
242
+ });
243
+ ```
244
+
245
+ ## 15. String Literal for `require`
246
+
247
+ ```typescript
248
+ // WRONG – require must be an object with type property
249
+ constraints: {
250
+ check: {
251
+ when: (facts) => facts.ready,
252
+ require: "FETCH_DATA",
253
+ },
254
+ },
255
+
256
+ // CORRECT – use object form with type
257
+ constraints: {
258
+ check: {
259
+ when: (facts) => facts.ready,
260
+ require: { type: "FETCH_DATA" },
261
+ },
262
+ },
263
+ ```
264
+
265
+ ## 16. Passthrough Derivations
266
+
267
+ ```typescript
268
+ // WRONG – derivation just returns a fact value unchanged
269
+ derive: {
270
+ count: (facts) => facts.count,
271
+ },
272
+
273
+ // CORRECT – remove it, read the fact directly instead
274
+ // system.facts.count instead of system.derive.count
275
+ ```
276
+
277
+ ## 17. Deep Import Paths
278
+
279
+ ```typescript
280
+ // WRONG – internal module paths are not public API
281
+ import { createModule } from "@directive-run/core/module";
282
+ import { createSystem } from "@directive-run/core/system";
283
+
284
+ // CORRECT – import from package root
285
+ import { createModule, createSystem } from "@directive-run/core";
286
+
287
+ // Exception: plugins have their own entry point
288
+ import { loggingPlugin } from "@directive-run/core/plugins";
289
+ ```
290
+
291
+ ## 18. Async `when()` Without `deps`
292
+
293
+ ```typescript
294
+ // WRONG – async constraints need explicit deps for tracking
295
+ constraints: {
296
+ validate: {
297
+ async: true,
298
+ when: async (facts) => {
299
+ const valid = await checkRemote(facts.token);
300
+
301
+ return valid;
302
+ },
303
+ require: { type: "REFRESH_TOKEN" },
304
+ },
305
+ },
306
+
307
+ // CORRECT – add deps array for async constraints
308
+ constraints: {
309
+ validate: {
310
+ async: true,
311
+ deps: ["token"],
312
+ when: async (facts) => {
313
+ const valid = await checkRemote(facts.token);
314
+
315
+ return valid;
316
+ },
317
+ require: { type: "REFRESH_TOKEN" },
318
+ },
319
+ },
320
+ ```
321
+
322
+ ## 19. No Error Handling on Failing Resolvers
323
+
324
+ ```typescript
325
+ // WRONG – unhandled errors crash the system
326
+ resolvers: {
327
+ fetchData: {
328
+ requirement: "FETCH",
329
+ resolve: async (req, context) => {
330
+ const res = await fetch("/api/data");
331
+ context.facts.data = await res.json();
332
+ },
333
+ },
334
+ },
335
+
336
+ // CORRECT – use retry policy and/or module error boundary
337
+ resolvers: {
338
+ fetchData: {
339
+ requirement: "FETCH",
340
+ retry: { attempts: 3, backoff: "exponential" },
341
+ resolve: async (req, context) => {
342
+ const res = await fetch("/api/data");
343
+ if (!res.ok) {
344
+ throw new Error(`HTTP ${res.status}`);
345
+ }
346
+ context.facts.data = await res.json();
347
+ },
348
+ },
349
+ },
350
+
351
+ // Also set error boundary at system level
352
+ const system = createSystem({
353
+ module: myModule,
354
+ errorBoundary: {
355
+ onResolverError: "retry-later",
356
+ },
357
+ });
358
+ ```
359
+
360
+ ## Quick Reference Checklist
361
+
362
+ Before generating any Directive code, verify:
363
+
364
+ 1. Schema is nested: `schema: { facts: { ... } }` (not flat)
365
+ 2. No `as` casts when reading facts or derivations
366
+ 3. Resolver params are `(req, context)` not `(req, ctx)`
367
+ 4. `require` is an object `{ type: "..." }` not a string
368
+ 5. `init()` is synchronous
369
+ 6. Resolvers return void and mutate `context.facts`
370
+ 7. Arrays/objects replaced, not mutated in place
371
+ 8. Multi-module uses `facts.self.*` for own facts
372
+ 9. Imports from `@directive-run/core`, not deep paths
373
+ 10. `await system.settle()` after `system.start()`
374
+
375
+ ## See also
376
+
377
+ - [`naming.md`](./naming.md) — the strict canonical-term rules AND the alias map for cross-paradigm searches
378
+ - [`constraints.md`](./constraints.md) — the constraint shape these anti-patterns reference (`facts` not in scope inside static `require:`, etc.)
379
+ - [`resolvers.md`](./resolvers.md) — the resolver shape these anti-patterns reference (return `void`, mutate `context.facts`, `(req, context)` not `(req, ctx)`)
380
+ - [`schema-types.md`](./schema-types.md) — the `t.*()` builders that exist and the hallucinated ones (`t.map`, `t.set`, `t.promise`, `t.date`) that don't
381
+ - [`react-adapter.md`](./react-adapter.md) — the React hooks that exist and the hallucinated ones (`useEvent`, `useSystem`, `DirectiveProvider`) that don't
382
+ - [`error-boundaries.md`](./error-boundaries.md) — error-handling shapes AI assistants sometimes invent
@@ -0,0 +1,5 @@
1
+ # API Skeleton
2
+
3
+ > Auto-generated. Do not edit.
4
+
5
+ > Source JSON not found. Build website docs first.
@@ -0,0 +1,240 @@
1
+ # Core Patterns
2
+
3
+ > Covers `@directive-run/core` — modules, facts, derivations, effects, events, and the constraint-resolver loop.
4
+
5
+ How to think about building with Directive: modules, systems, and the constraint-resolver pattern.
6
+
7
+ ## Decision Tree: "Where does this logic go?"
8
+
9
+ ```
10
+ User wants to...
11
+ ├── Store state → schema.facts + init()
12
+ ├── Compute derived values → schema.derivations + derive
13
+ ├── React to state changes → effects
14
+ ├── Trigger side effects when conditions are met → constraints + resolvers
15
+ ├── Handle user actions → schema.events + events handlers
16
+ └── Coordinate multiple modules → createSystem({ modules: {} })
17
+ ```
18
+
19
+ ## Module Shape (Canonical Object Syntax)
20
+
21
+ ```typescript
22
+ // CORRECT – full module definition
23
+ import { createModule, t } from "@directive-run/core";
24
+
25
+ const myModule = createModule("name", {
26
+ schema: {
27
+ facts: {
28
+ count: t.number(),
29
+ phase: t.string<"idle" | "loading" | "done">(),
30
+ user: t.object<{ id: string; name: string } | null>(),
31
+ },
32
+ derivations: {
33
+ isLoading: t.boolean(),
34
+ displayName: t.string(),
35
+ },
36
+ events: {
37
+ increment: {},
38
+ setUser: { user: t.object<{ id: string; name: string }>() },
39
+ },
40
+ requirements: {
41
+ FETCH_USER: { userId: t.string() },
42
+ },
43
+ },
44
+
45
+ init: (facts) => {
46
+ facts.count = 0;
47
+ facts.phase = "idle";
48
+ facts.user = null;
49
+ },
50
+
51
+ derive: {
52
+ isLoading: (facts) => facts.phase === "loading",
53
+ displayName: (facts) => {
54
+ if (!facts.user) {
55
+ return "Guest";
56
+ }
57
+
58
+ return facts.user.name;
59
+ },
60
+ },
61
+
62
+ effects: {
63
+ logPhase: {
64
+ run: (facts, prev) => {
65
+ if (prev?.phase !== facts.phase) {
66
+ console.log(`Phase: ${facts.phase}`);
67
+ }
68
+ },
69
+ },
70
+ },
71
+
72
+ constraints: {
73
+ fetchWhenReady: {
74
+ when: (facts) => facts.phase === "idle" && facts.count > 0,
75
+ require: (facts) => ({ type: "FETCH_USER", userId: "user-1" }),
76
+ },
77
+ },
78
+
79
+ resolvers: {
80
+ fetchUser: {
81
+ requirement: "FETCH_USER",
82
+ resolve: async (req, context) => {
83
+ context.facts.phase = "loading";
84
+ const res = await fetch(`/api/users/${req.userId}`);
85
+ const data = await res.json();
86
+ context.facts.user = data;
87
+ context.facts.phase = "done";
88
+ },
89
+ },
90
+ },
91
+
92
+ events: {
93
+ increment: (facts) => {
94
+ facts.count += 1;
95
+ },
96
+ setUser: (facts, payload) => {
97
+ facts.user = payload.user;
98
+ },
99
+ },
100
+ });
101
+ ```
102
+
103
+ ## System Creation
104
+
105
+ ```typescript
106
+ import { createSystem } from "@directive-run/core";
107
+ import { loggingPlugin, devtoolsPlugin } from "@directive-run/core/plugins";
108
+
109
+ // Single module – direct access: system.facts.count
110
+ const system = createSystem({
111
+ module: myModule,
112
+ plugins: [loggingPlugin(), devtoolsPlugin()],
113
+ history: { maxSnapshots: 100 },
114
+ });
115
+
116
+ // Multi-module – namespaced access: system.facts.auth.token
117
+ const system = createSystem({
118
+ modules: { auth: authModule, cart: cartModule },
119
+ plugins: [devtoolsPlugin()],
120
+ });
121
+
122
+ // Lifecycle
123
+ system.start();
124
+ await system.settle(); // Wait for all resolvers to complete
125
+ // ... use the system ...
126
+ system.stop();
127
+ system.destroy();
128
+ ```
129
+
130
+ ## Decision Tree: "User says 'fetch data when authenticated'"
131
+
132
+ WRONG thinking: "I'll put the fetch call in a resolver that checks auth."
133
+
134
+ ```typescript
135
+ // WRONG – resolver doing condition checking + data fetching
136
+ resolvers: {
137
+ fetchData: {
138
+ requirement: "FETCH_DATA",
139
+ resolve: async (req, context) => {
140
+ if (!context.facts.isAuthenticated) {
141
+ return; // Resolver should not check conditions
142
+ }
143
+ const data = await fetch("/api/data");
144
+ context.facts.data = await data.json();
145
+ },
146
+ },
147
+ },
148
+ ```
149
+
150
+ CORRECT thinking: "Constraint declares WHEN, resolver declares HOW."
151
+
152
+ ```typescript
153
+ // CORRECT – constraint declares the need, resolver fulfills it
154
+ constraints: {
155
+ fetchWhenAuthenticated: {
156
+ when: (facts) => facts.isAuthenticated && !facts.data,
157
+ require: { type: "FETCH_DATA" },
158
+ },
159
+ },
160
+
161
+ resolvers: {
162
+ fetchData: {
163
+ requirement: "FETCH_DATA",
164
+ resolve: async (req, context) => {
165
+ const data = await fetch("/api/data");
166
+ context.facts.data = await data.json();
167
+ },
168
+ },
169
+ },
170
+ ```
171
+
172
+ ## Reading System State
173
+
174
+ ```typescript
175
+ // Facts – mutable state
176
+ system.facts.count = 5;
177
+ const val = system.facts.count;
178
+
179
+ // Derivations – read-only computed values
180
+ const loading = system.derive.isLoading;
181
+
182
+ // Events – dispatch user actions
183
+ system.events.increment();
184
+ system.events.setUser({ user: { id: "1", name: "Alice" } });
185
+
186
+ // Subscribe to changes
187
+ const unsub = system.subscribe(["count", "isLoading"], () => {
188
+ console.log(system.facts.count, system.derive.isLoading);
189
+ });
190
+
191
+ // Watch individual values
192
+ system.watch("count", (newVal, oldVal) => {
193
+ console.log(`Count: ${oldVal} -> ${newVal}`);
194
+ });
195
+
196
+ // Wait for a condition
197
+ await system.when((facts) => facts.phase === "done");
198
+ await system.when((facts) => facts.phase === "done", { timeout: 5000 });
199
+ ```
200
+
201
+ ## Schema Patterns
202
+
203
+ Only `facts` is required in the schema. Other sections are optional:
204
+
205
+ ```typescript
206
+ // Minimal module – facts only
207
+ const minimal = createModule("minimal", {
208
+ schema: {
209
+ facts: { count: t.number() },
210
+ },
211
+ init: (facts) => {
212
+ facts.count = 0;
213
+ },
214
+ });
215
+
216
+ // Type assertion pattern (alternative to t.* builders)
217
+ const typed = createModule("typed", {
218
+ schema: {
219
+ facts: {} as { count: number; name: string },
220
+ derivations: {} as { doubled: number },
221
+ },
222
+ init: (facts) => {
223
+ facts.count = 0;
224
+ facts.name = "";
225
+ },
226
+ derive: {
227
+ doubled: (facts) => facts.count * 2,
228
+ },
229
+ });
230
+ ```
231
+
232
+ ## See also
233
+
234
+ - [`constraints.md`](./constraints.md) — the demand side of the constraint-resolver loop
235
+ - [`resolvers.md`](./resolvers.md) — the supply side that fulfills requirements
236
+ - [`schema-types.md`](./schema-types.md) — the `t.*()` builders that define fact and derivation types
237
+ - [`multi-module.md`](./multi-module.md) — when a single module isn't enough
238
+ - [`system-api.md`](./system-api.md) — `createSystem` and the runtime instance once your module is defined
239
+ - [`naming.md`](./naming.md) — terminology conventions every example here follows
240
+ - [`testing.md`](./testing.md) — `createTestSystem` for the modules you're learning to write