@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.
- package/.claude-plugin/plugin.json +6 -0
- package/LICENSE +26 -0
- package/README.md +113 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +66 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +64 -0
- package/skills/README.md +9 -0
- package/skills/building-ai-agents/SKILL.md +414 -0
- package/skills/building-ai-agents/ai-adapters.md +257 -0
- package/skills/building-ai-agents/ai-agents-streaming.md +307 -0
- package/skills/building-ai-agents/ai-communication.md +304 -0
- package/skills/building-ai-agents/api-skeleton.md +5 -0
- package/skills/building-ai-agents/examples.md +849 -0
- package/skills/building-ai-orchestrators/SKILL.md +401 -0
- package/skills/building-ai-orchestrators/ai-multi-agent.md +375 -0
- package/skills/building-ai-orchestrators/ai-orchestrator.md +466 -0
- package/skills/building-ai-orchestrators/ai-tasks.md +268 -0
- package/skills/building-ai-orchestrators/api-skeleton.md +5 -0
- package/skills/building-ai-orchestrators/examples.md +1609 -0
- package/skills/building-directive-systems/SKILL.md +499 -0
- package/skills/building-directive-systems/api-skeleton.md +5 -0
- package/skills/building-directive-systems/examples.md +4149 -0
- package/skills/building-directive-systems/multi-module.md +327 -0
- package/skills/building-directive-systems/plugins.md +361 -0
- package/skills/building-directive-systems/react-adapter.md +376 -0
- package/skills/building-directive-systems/system-api.md +463 -0
- package/skills/getting-started-with-directive/SKILL.md +198 -0
- package/skills/getting-started-with-directive/api-skeleton.md +5 -0
- package/skills/getting-started-with-directive/core-patterns.md +240 -0
- package/skills/getting-started-with-directive/examples.md +87 -0
- package/skills/getting-started-with-directive/sitemap.md +202 -0
- package/skills/hardening-ai-systems/SKILL.md +377 -0
- package/skills/hardening-ai-systems/ai-budget-resilience.md +408 -0
- package/skills/hardening-ai-systems/ai-guardrails-memory.md +370 -0
- package/skills/hardening-ai-systems/ai-security.md +400 -0
- package/skills/hardening-ai-systems/api-skeleton.md +5 -0
- package/skills/hardening-ai-systems/examples.md +628 -0
- package/skills/migrating-to-directive/SKILL.md +249 -0
- package/skills/migrating-to-directive/anti-patterns.md +382 -0
- package/skills/migrating-to-directive/api-skeleton.md +5 -0
- package/skills/migrating-to-directive/core-patterns.md +240 -0
- package/skills/migrating-to-directive/examples.md +511 -0
- package/skills/migrating-to-directive/schema-types.md +273 -0
- package/skills/reviewing-directive-code/SKILL.md +232 -0
- package/skills/reviewing-directive-code/anti-patterns.md +382 -0
- package/skills/reviewing-directive-code/api-skeleton.md +5 -0
- package/skills/reviewing-directive-code/core-patterns.md +240 -0
- package/skills/reviewing-directive-code/examples.md +463 -0
- package/skills/reviewing-directive-code/naming.md +321 -0
- package/skills/scaffolding-directive-modules/SKILL.md +218 -0
- package/skills/scaffolding-directive-modules/api-skeleton.md +5 -0
- package/skills/scaffolding-directive-modules/core-patterns.md +240 -0
- package/skills/scaffolding-directive-modules/examples.md +1386 -0
- package/skills/scaffolding-directive-modules/naming.md +321 -0
- package/skills/scaffolding-directive-modules/schema-types.md +273 -0
- package/skills/testing-ai-systems/SKILL.md +398 -0
- package/skills/testing-ai-systems/ai-debug-observability.md +329 -0
- package/skills/testing-ai-systems/ai-mcp-rag.md +293 -0
- package/skills/testing-ai-systems/ai-testing-evals.md +485 -0
- package/skills/testing-ai-systems/api-skeleton.md +5 -0
- package/skills/testing-ai-systems/examples.md +1261 -0
- package/skills/testing-directive-code/SKILL.md +479 -0
- package/skills/testing-directive-code/api-skeleton.md +5 -0
- package/skills/testing-directive-code/examples.md +156 -0
- package/skills/testing-directive-code/history.md +351 -0
- package/skills/testing-directive-code/testing.md +412 -0
- package/skills/writing-directive-constraints/SKILL.md +486 -0
- package/skills/writing-directive-constraints/api-skeleton.md +5 -0
- package/skills/writing-directive-constraints/constraints.md +274 -0
- package/skills/writing-directive-constraints/error-boundaries.md +330 -0
- package/skills/writing-directive-constraints/examples.md +1361 -0
- package/skills/writing-directive-constraints/resolvers.md +367 -0
- package/skills/writing-directive-modules/SKILL.md +342 -0
- package/skills/writing-directive-modules/anti-patterns.md +382 -0
- package/skills/writing-directive-modules/api-skeleton.md +5 -0
- package/skills/writing-directive-modules/core-patterns.md +240 -0
- package/skills/writing-directive-modules/examples.md +1639 -0
- package/skills/writing-directive-modules/naming.md +321 -0
- package/skills/writing-directive-modules/schema-types.md +273 -0
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: building-directive-systems
|
|
3
|
+
description: "Compose Directive modules into systems: single-module and multi-module createSystem() calls, namespaced fact/derive/event access, cross-module dependencies, initialFacts and hydration, plugins (logging, devtools, persistence, circuit breaker), React adapter hooks (useSelector, useEvent, useSystem, DirectiveProvider), and system lifecycle. Use when creating a system, connecting modules together, adding plugins, or integrating Directive with React."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Building Directive Systems
|
|
7
|
+
|
|
8
|
+
## Prerequisites
|
|
9
|
+
|
|
10
|
+
This skill applies when the project uses `@directive-run/core`. If not found in `package.json`, suggest installing it: `npm install @directive-run/core`.
|
|
11
|
+
|
|
12
|
+
## When Claude Should Use This Skill
|
|
13
|
+
|
|
14
|
+
**Auto-invoke when the user:**
|
|
15
|
+
- Says "create a system", "compose modules", "add plugins", or "connect to React"
|
|
16
|
+
- Shows `createSystem()` and asks to extend it with more modules or plugins
|
|
17
|
+
- Asks about `useSelector`, `useEvent`, `useSystem`, or `DirectiveProvider`
|
|
18
|
+
- Asks how modules share state or read each other's facts
|
|
19
|
+
- Asks about system lifecycle, hydration, or dynamic module registration
|
|
20
|
+
|
|
21
|
+
**Do NOT invoke when:**
|
|
22
|
+
- Writing a module for the first time (start with `writing-directive-modules.md`)
|
|
23
|
+
- Writing constraints/resolvers only (see `writing-directive-constraints.md`)
|
|
24
|
+
- Writing tests (see `testing-directive-code.md`)
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Decision Tree: Single or Multi-Module?
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
How many state domains?
|
|
32
|
+
├── One → createSystem({ module: myModule })
|
|
33
|
+
│ Direct access: system.facts.count
|
|
34
|
+
│
|
|
35
|
+
└── Two or more → createSystem({ modules: { auth, cart, ui } })
|
|
36
|
+
Namespaced: system.facts.auth.token
|
|
37
|
+
|
|
38
|
+
Does module A need to read module B's state in constraints/effects/derivations?
|
|
39
|
+
├── No → No crossModuleDeps needed
|
|
40
|
+
└── Yes → Declare crossModuleDeps on the consuming module
|
|
41
|
+
Own facts at facts.self.*, other module at facts.otherModule.*
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## System Creation
|
|
47
|
+
|
|
48
|
+
### Single module
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { createSystem } from "@directive-run/core";
|
|
52
|
+
import { loggingPlugin, devtoolsPlugin } from "@directive-run/core/plugins";
|
|
53
|
+
|
|
54
|
+
const system = createSystem({
|
|
55
|
+
module: myModule,
|
|
56
|
+
plugins: [loggingPlugin(), devtoolsPlugin()],
|
|
57
|
+
history: { maxSnapshots: 100 },
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
system.start();
|
|
61
|
+
await system.settle(); // Wait for all resolvers to complete
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Multi-module
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
const system = createSystem({
|
|
68
|
+
modules: { auth: authModule, cart: cartModule, data: dataModule },
|
|
69
|
+
plugins: [loggingPlugin(), devtoolsPlugin()],
|
|
70
|
+
initialFacts: {
|
|
71
|
+
auth: { token: "restored-token" },
|
|
72
|
+
cart: { items: [] },
|
|
73
|
+
},
|
|
74
|
+
initOrder: "auto", // "auto" | "declaration" | ["auth", "data", "cart"]
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
system.start();
|
|
78
|
+
await system.settle();
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Hydration from async source (before start)
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
await system.hydrate(async () => {
|
|
85
|
+
const stored = localStorage.getItem("app-state");
|
|
86
|
+
|
|
87
|
+
return stored ? JSON.parse(stored) : {};
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
system.start();
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Dynamic module registration at runtime
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
const chatModule = await import("./modules/chat");
|
|
97
|
+
system.registerModule("chat", chatModule.default);
|
|
98
|
+
// Now: system.facts.chat.*, system.events.chat.*, etc.
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Accessing System State
|
|
104
|
+
|
|
105
|
+
### Single module – direct access
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
system.facts.count = 5;
|
|
109
|
+
const val = system.facts.count;
|
|
110
|
+
const loading = system.derive.isLoading;
|
|
111
|
+
system.events.increment();
|
|
112
|
+
system.events.setUser({ user: { id: "1", name: "Alice" } });
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Multi-module – namespaced access (dot notation always)
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
system.facts.auth.token;
|
|
119
|
+
system.facts.cart.items;
|
|
120
|
+
system.derive.auth.isAdmin;
|
|
121
|
+
system.derive.cart.itemCount;
|
|
122
|
+
system.events.auth.login({ token: "abc" });
|
|
123
|
+
system.events.cart.addItem({ productId: "p1", qty: 1 });
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
// WRONG – internal separator never used in public API
|
|
128
|
+
system.facts["auth::token"];
|
|
129
|
+
system.facts["auth_token"];
|
|
130
|
+
|
|
131
|
+
// CORRECT – dot notation through namespace proxy
|
|
132
|
+
system.facts.auth.token;
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Subscribing and watching
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
// Subscribe to multiple keys
|
|
139
|
+
const unsub = system.subscribe(["count", "isLoading"], () => {
|
|
140
|
+
console.log(system.facts.count, system.derive.isLoading);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Multi-module keys use "module.key" format
|
|
144
|
+
system.subscribe(["auth.token", "cart.items"], () => { ... });
|
|
145
|
+
|
|
146
|
+
// Watch a single value with old/new
|
|
147
|
+
system.watch("count", (newVal, oldVal) => {
|
|
148
|
+
console.log(`Count: ${oldVal} -> ${newVal}`);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// Subscribe to all keys in a module
|
|
152
|
+
system.subscribeModule("cart", () => { ... });
|
|
153
|
+
|
|
154
|
+
// Wait for condition
|
|
155
|
+
await system.when((facts) => facts.phase === "done");
|
|
156
|
+
await system.when((facts) => facts.auth.isAuthenticated, { timeout: 5000 });
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### System lifecycle
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
system.start(); // Begins constraint evaluation and reconciliation
|
|
163
|
+
system.stop(); // Pauses evaluation, cancels inflight resolvers
|
|
164
|
+
system.destroy(); // Full cleanup – subscriptions, plugins, resources
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Always call `destroy()` when a system is no longer needed (teardown, React unmount, test cleanup).
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Cross-Module Dependencies
|
|
172
|
+
|
|
173
|
+
Export the schema separately so other modules can reference it for type safety.
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
// modules/auth.ts
|
|
177
|
+
export const authSchema = {
|
|
178
|
+
facts: {
|
|
179
|
+
token: t.string<string | null>(),
|
|
180
|
+
isAuthenticated: t.boolean(),
|
|
181
|
+
},
|
|
182
|
+
} as const;
|
|
183
|
+
|
|
184
|
+
export const authModule = createModule("auth", {
|
|
185
|
+
schema: authSchema,
|
|
186
|
+
init: (facts) => {
|
|
187
|
+
facts.token = null;
|
|
188
|
+
facts.isAuthenticated = false;
|
|
189
|
+
},
|
|
190
|
+
events: {
|
|
191
|
+
login: (facts, payload) => { facts.token = payload.token; facts.isAuthenticated = true; },
|
|
192
|
+
logout: (facts) => { facts.token = null; facts.isAuthenticated = false; },
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
// modules/data.ts
|
|
199
|
+
import { authSchema } from "./auth";
|
|
200
|
+
|
|
201
|
+
const dataModule = createModule("data", {
|
|
202
|
+
schema: { facts: { items: t.array(t.string()), loaded: t.boolean() } },
|
|
203
|
+
crossModuleDeps: { auth: authSchema },
|
|
204
|
+
init: (facts) => { facts.items = []; facts.loaded = false; },
|
|
205
|
+
|
|
206
|
+
constraints: {
|
|
207
|
+
fetchWhenAuth: {
|
|
208
|
+
when: (facts) => facts.auth.isAuthenticated && !facts.self.loaded,
|
|
209
|
+
require: { type: "FETCH_ITEMS" },
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
effects: {
|
|
214
|
+
onAuthChange: {
|
|
215
|
+
run: (facts, prev) => {
|
|
216
|
+
if (prev && prev.auth.isAuthenticated && !facts.auth.isAuthenticated) {
|
|
217
|
+
console.log("User logged out");
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Plugins
|
|
228
|
+
|
|
229
|
+
### Built-in plugins
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
import {
|
|
233
|
+
loggingPlugin,
|
|
234
|
+
devtoolsPlugin,
|
|
235
|
+
persistencePlugin,
|
|
236
|
+
} from "@directive-run/core/plugins";
|
|
237
|
+
|
|
238
|
+
const plugins = [];
|
|
239
|
+
|
|
240
|
+
if (process.env.NODE_ENV === "development") {
|
|
241
|
+
plugins.push(loggingPlugin({ verbose: true })); // Logging first
|
|
242
|
+
plugins.push(devtoolsPlugin({ name: "My App" }));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
plugins.push(persistencePlugin({
|
|
246
|
+
key: "app-state",
|
|
247
|
+
storage: localStorage,
|
|
248
|
+
exclude: ["authToken", "isLoading"], // Skip sensitive/transient
|
|
249
|
+
version: 1,
|
|
250
|
+
migrate: (old, version) => old, // Always version when persisting
|
|
251
|
+
}));
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Plugin order matters – logging first captures all events including those from other plugins.
|
|
255
|
+
|
|
256
|
+
### Custom plugin
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
import type { DirectivePlugin } from "@directive-run/core";
|
|
260
|
+
|
|
261
|
+
const myPlugin: DirectivePlugin = {
|
|
262
|
+
name: "my-plugin",
|
|
263
|
+
onInit: (system) => { ... },
|
|
264
|
+
onStart: (system) => { ... },
|
|
265
|
+
onStop: (system) => { ... },
|
|
266
|
+
onSnapshot: (snapshot) => {
|
|
267
|
+
// snapshot.facts, snapshot.changedKeys
|
|
268
|
+
},
|
|
269
|
+
onRequirementEmitted: (requirement) => { ... },
|
|
270
|
+
onResolutionStart: (resolution) => { ... },
|
|
271
|
+
onResolutionComplete: (resolution) => { ... },
|
|
272
|
+
onError: (error, context) => { ... },
|
|
273
|
+
};
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## React Adapter
|
|
279
|
+
|
|
280
|
+
Import from `@directive-run/react`.
|
|
281
|
+
|
|
282
|
+
### Setup: create system outside React
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
// system.ts – created once, imported anywhere
|
|
286
|
+
export const system = createSystem({ module: counterModule });
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### useSelector – subscribe to state
|
|
290
|
+
|
|
291
|
+
Re-renders only when the selected value changes (shallow comparison).
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
import { useSelector, useEvent } from "@directive-run/react";
|
|
295
|
+
|
|
296
|
+
function Counter() {
|
|
297
|
+
const count = useSelector(system, (s) => s.facts.count);
|
|
298
|
+
const doubled = useSelector(system, (s) => s.derive.doubled);
|
|
299
|
+
|
|
300
|
+
// Multi-module
|
|
301
|
+
const token = useSelector(system, (s) => s.facts.auth.token);
|
|
302
|
+
const itemCount = useSelector(system, (s) => s.derive.cart.itemCount);
|
|
303
|
+
|
|
304
|
+
const events = useEvent(system);
|
|
305
|
+
|
|
306
|
+
return (
|
|
307
|
+
<div>
|
|
308
|
+
<p>Count: {count}</p>
|
|
309
|
+
<button onClick={() => events.increment()}>+1</button>
|
|
310
|
+
</div>
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### useEvent – dispatch events (stable reference)
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
const events = useEvent(system);
|
|
319
|
+
events.increment();
|
|
320
|
+
events.auth.login({ token: "abc" }); // Multi-module
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### useSystem – lifecycle scoped to component
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
import { useSystem } from "@directive-run/react";
|
|
327
|
+
|
|
328
|
+
function GameBoard() {
|
|
329
|
+
const gameSystem = useSystem({ // Created on mount, destroyed on unmount
|
|
330
|
+
module: gameModule,
|
|
331
|
+
history: true,
|
|
332
|
+
});
|
|
333
|
+
const score = useSelector(gameSystem, (s) => s.facts.score);
|
|
334
|
+
|
|
335
|
+
return <div>Score: {score}</div>;
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
Use `useSystem` when the system's lifecycle matches a component's lifecycle (wizard, game board, modal form). For app-wide state, create outside React.
|
|
340
|
+
|
|
341
|
+
### DirectiveProvider – share system via context
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
import { DirectiveProvider, useDirectiveContext } from "@directive-run/react";
|
|
345
|
+
|
|
346
|
+
function App() {
|
|
347
|
+
return (
|
|
348
|
+
<DirectiveProvider system={system}>
|
|
349
|
+
<Dashboard />
|
|
350
|
+
</DirectiveProvider>
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function Dashboard() {
|
|
355
|
+
const system = useDirectiveContext();
|
|
356
|
+
const stats = useSelector(system, (s) => s.derive.dashboardStats);
|
|
357
|
+
|
|
358
|
+
return <div>{stats.totalUsers} users</div>;
|
|
359
|
+
}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## Critical Anti-Patterns
|
|
365
|
+
|
|
366
|
+
### 1. Reading facts before settling
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
// WRONG – resolver hasn't completed
|
|
370
|
+
system.start();
|
|
371
|
+
console.log(system.facts.user); // null
|
|
372
|
+
|
|
373
|
+
// CORRECT
|
|
374
|
+
system.start();
|
|
375
|
+
await system.settle();
|
|
376
|
+
console.log(system.facts.user); // resolved
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### 2. Forgetting to start the system
|
|
380
|
+
|
|
381
|
+
```typescript
|
|
382
|
+
// WRONG – constraints never evaluate
|
|
383
|
+
const system = createSystem({ module: myModule });
|
|
384
|
+
console.log(system.facts.phase); // "idle" – resolvers never ran
|
|
385
|
+
|
|
386
|
+
// CORRECT
|
|
387
|
+
system.start();
|
|
388
|
+
await system.settle();
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### 3. Missing crossModuleDeps
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
// WRONG – facts.auth untyped, no reactive tracking
|
|
395
|
+
when: (facts) => facts.auth.isAuthenticated, // TypeScript error
|
|
396
|
+
|
|
397
|
+
// CORRECT
|
|
398
|
+
crossModuleDeps: { auth: authSchema },
|
|
399
|
+
when: (facts) => facts.auth.isAuthenticated, // Fully typed + tracked
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### 4. Bare facts.* in cross-module context
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
// WRONG – in cross-module constraints, bare facts has no self properties
|
|
406
|
+
when: (facts) => facts.loaded,
|
|
407
|
+
|
|
408
|
+
// CORRECT
|
|
409
|
+
when: (facts) => facts.self.loaded,
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### 5. Bracket notation for namespaced access
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
// WRONG
|
|
416
|
+
system.facts["auth::token"];
|
|
417
|
+
|
|
418
|
+
// CORRECT
|
|
419
|
+
system.facts.auth.token;
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### 6. Creating system inside a React component without useSystem
|
|
423
|
+
|
|
424
|
+
```typescript
|
|
425
|
+
// WRONG – new system on every render
|
|
426
|
+
function Counter() {
|
|
427
|
+
const system = createSystem({ module: counterModule }); // Bug!
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// CORRECT – module-level or useSystem()
|
|
431
|
+
const system = createSystem({ module: counterModule });
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### 7. The nonexistent useDirective hook
|
|
435
|
+
|
|
436
|
+
```typescript
|
|
437
|
+
// WRONG – this does not exist
|
|
438
|
+
const { facts, derive, events } = useDirective(system);
|
|
439
|
+
|
|
440
|
+
// CORRECT
|
|
441
|
+
const count = useSelector(system, (s) => s.facts.count);
|
|
442
|
+
const events = useEvent(system);
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### 8. Selecting too much state in useSelector
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
// WRONG – re-renders on any fact change
|
|
449
|
+
const allFacts = useSelector(system, (s) => s.facts);
|
|
450
|
+
|
|
451
|
+
// CORRECT – select only what you need
|
|
452
|
+
const name = useSelector(system, (s) => s.facts.userName);
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### 9. devtoolsPlugin in production
|
|
456
|
+
|
|
457
|
+
```typescript
|
|
458
|
+
// CORRECT – gate on environment
|
|
459
|
+
const plugins = process.env.NODE_ENV === "development"
|
|
460
|
+
? [loggingPlugin(), devtoolsPlugin()]
|
|
461
|
+
: [];
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### 10. persistencePlugin without version
|
|
465
|
+
|
|
466
|
+
```typescript
|
|
467
|
+
// WRONG – schema changes break existing users
|
|
468
|
+
persistencePlugin({ key: "app", storage: localStorage })
|
|
469
|
+
|
|
470
|
+
// CORRECT
|
|
471
|
+
persistencePlugin({ key: "app", storage: localStorage, version: 1, migrate: (old) => old })
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
## Inspecting System State
|
|
477
|
+
|
|
478
|
+
```typescript
|
|
479
|
+
const inspection = system.inspect();
|
|
480
|
+
inspection.facts; // Current fact snapshot
|
|
481
|
+
inspection.derivations; // Derivation values
|
|
482
|
+
inspection.requirements; // Active requirements
|
|
483
|
+
inspection.constraintDefs; // Constraint definitions and state
|
|
484
|
+
inspection.resolvers; // Resolver statuses
|
|
485
|
+
inspection.inflight; // Currently running resolvers
|
|
486
|
+
inspection.unmet; // Requirements with no matching resolver
|
|
487
|
+
|
|
488
|
+
system.explain("req-123"); // Why this requirement exists
|
|
489
|
+
system.isSettled; // boolean
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
---
|
|
493
|
+
|
|
494
|
+
## Reference Files
|
|
495
|
+
|
|
496
|
+
- `multi-module.md` – crossModuleDeps, module schema export pattern, subscribe/watch in multi-module
|
|
497
|
+
- `system-api.md` – full system API reference, read(), watch(), when(), lifecycle order
|
|
498
|
+
- `plugins.md` – all built-in plugins with full config, custom plugin lifecycle hooks
|
|
499
|
+
- `react-adapter.md` – all React hooks with examples, common mistakes, multi-module patterns
|