@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,327 @@
1
+ # Multi-Module Systems
2
+
3
+ > Covers `@directive-run/core` — namespaced multi-module systems, cross-module deps, `facts.self.*`.
4
+
5
+ How to compose multiple modules into a namespaced system with cross-module type safety.
6
+
7
+ ## Decision Tree: "Single or Multi-Module?"
8
+
9
+ ```
10
+ How many state domains?
11
+ ├── One → createSystem({ module: myModule })
12
+ │ Direct access: system.facts.count
13
+
14
+ └── Two or more → createSystem({ modules: { auth, cart, ui } })
15
+ Namespaced: system.facts.auth.token
16
+
17
+ Does module A need to read module B's state?
18
+ ├── No → No crossModuleDeps needed
19
+
20
+ └── Yes → Declare crossModuleDeps on the consuming module
21
+ Own facts at facts.self.*, other at facts.otherModule.*
22
+ ```
23
+
24
+ ## Creating a Multi-Module System
25
+
26
+ ```typescript
27
+ import { createModule, createSystem, t } from "@directive-run/core";
28
+
29
+ const authModule = createModule("auth", {
30
+ schema: {
31
+ facts: {
32
+ token: t.string<string | null>(),
33
+ isAuthenticated: t.boolean(),
34
+ },
35
+ events: {
36
+ login: { token: t.string() },
37
+ logout: {},
38
+ },
39
+ },
40
+ init: (facts) => {
41
+ facts.token = null;
42
+ facts.isAuthenticated = false;
43
+ },
44
+ events: {
45
+ login: (facts, payload) => {
46
+ facts.token = payload.token;
47
+ facts.isAuthenticated = true;
48
+ },
49
+ logout: (facts) => {
50
+ facts.token = null;
51
+ facts.isAuthenticated = false;
52
+ },
53
+ },
54
+ });
55
+
56
+ const cartModule = createModule("cart", {
57
+ schema: {
58
+ facts: {
59
+ items: t.array(t.object<{ id: string; qty: number }>()),
60
+ },
61
+ derivations: {
62
+ itemCount: t.number(),
63
+ },
64
+ },
65
+ init: (facts) => {
66
+ facts.items = [];
67
+ },
68
+ derive: {
69
+ itemCount: (facts) => facts.items.length,
70
+ },
71
+ });
72
+
73
+ // Multi-module system – namespaced access
74
+ const system = createSystem({
75
+ modules: { auth: authModule, cart: cartModule },
76
+ });
77
+
78
+ system.start();
79
+ ```
80
+
81
+ ## Accessing Namespaced State
82
+
83
+ ```typescript
84
+ // Facts – namespaced under module name
85
+ system.facts.auth.token;
86
+ system.facts.auth.isAuthenticated;
87
+ system.facts.cart.items;
88
+
89
+ // Derivations – namespaced under module name
90
+ system.derive.cart.itemCount;
91
+
92
+ // Events – namespaced under module name
93
+ system.events.auth.login({ token: "abc123" });
94
+ system.events.auth.logout();
95
+
96
+ // Subscribe – use "namespace.key" format
97
+ system.subscribe(["auth.token", "cart.items"], () => {
98
+ console.log("auth or cart changed");
99
+ });
100
+
101
+ // Watch – use "namespace.key" format
102
+ system.watch("auth.isAuthenticated", (newVal, oldVal) => {
103
+ console.log(`Auth: ${oldVal} -> ${newVal}`);
104
+ });
105
+
106
+ // Subscribe to all keys in a module
107
+ system.subscribeModule("cart", () => {
108
+ console.log("anything in cart changed");
109
+ });
110
+
111
+ // Wait for condition – facts are namespaced
112
+ await system.when((facts) => facts.auth.isAuthenticated);
113
+ ```
114
+
115
+ ## Cross-Module Dependencies
116
+
117
+ When a module needs to read another module's facts in its constraints, effects, or derivations, declare `crossModuleDeps`.
118
+
119
+ ### Extracting the Schema
120
+
121
+ Export the schema separately from the module so other modules can reference it:
122
+
123
+ ```typescript
124
+ // modules/auth.ts
125
+ export const authSchema = {
126
+ facts: {
127
+ token: t.string<string | null>(),
128
+ isAuthenticated: t.boolean(),
129
+ },
130
+ events: {
131
+ login: { token: t.string() },
132
+ logout: {},
133
+ },
134
+ } as const;
135
+
136
+ export const authModule = createModule("auth", {
137
+ schema: authSchema,
138
+ init: (facts) => {
139
+ facts.token = null;
140
+ facts.isAuthenticated = false;
141
+ },
142
+ // ...
143
+ });
144
+ ```
145
+
146
+ ### Declaring crossModuleDeps
147
+
148
+ ```typescript
149
+ // modules/data.ts
150
+ import { authSchema } from "./auth";
151
+
152
+ const dataModule = createModule("data", {
153
+ schema: {
154
+ facts: {
155
+ items: t.array(t.string()),
156
+ loaded: t.boolean(),
157
+ },
158
+ requirements: {
159
+ FETCH_ITEMS: {},
160
+ },
161
+ },
162
+
163
+ // Declare cross-module dependency
164
+ crossModuleDeps: { auth: authSchema },
165
+
166
+ init: (facts) => {
167
+ facts.items = [];
168
+ facts.loaded = false;
169
+ },
170
+
171
+ // CORRECT – facts.self for own module, facts.auth for cross-module
172
+ constraints: {
173
+ fetchWhenAuth: {
174
+ when: (facts) => facts.auth.isAuthenticated && !facts.self.loaded,
175
+ require: { type: "FETCH_ITEMS" },
176
+ },
177
+ },
178
+
179
+ resolvers: {
180
+ fetchItems: {
181
+ requirement: "FETCH_ITEMS",
182
+ resolve: async (req, context) => {
183
+ const res = await fetch("/api/items");
184
+ context.facts.items = await res.json();
185
+ context.facts.loaded = true;
186
+ },
187
+ },
188
+ },
189
+
190
+ // Effects also get cross-module facts
191
+ effects: {
192
+ onAuthChange: {
193
+ run: (facts, prev) => {
194
+ if (prev && prev.auth.isAuthenticated && !facts.auth.isAuthenticated) {
195
+ console.log("User logged out, clearing data");
196
+ }
197
+ },
198
+ },
199
+ },
200
+ });
201
+ ```
202
+
203
+ ## Common Mistakes
204
+
205
+ ### Using bare `facts.*` instead of `facts.self.*`
206
+
207
+ ```typescript
208
+ // WRONG – in cross-module context, bare facts has no self-module properties
209
+ constraints: {
210
+ check: {
211
+ when: (facts) => facts.loaded, // TypeScript error
212
+ require: { type: "FETCH" },
213
+ },
214
+ },
215
+
216
+ // CORRECT – use facts.self for own module
217
+ constraints: {
218
+ check: {
219
+ when: (facts) => facts.self.loaded,
220
+ require: { type: "FETCH" },
221
+ },
222
+ },
223
+ ```
224
+
225
+ ### Bracket notation for internal keys
226
+
227
+ ```typescript
228
+ // WRONG – the :: separator is internal, never use it directly
229
+ system.facts["auth::token"];
230
+ system.read("auth::status");
231
+
232
+ // CORRECT – dot notation through namespace proxy
233
+ system.facts.auth.token;
234
+ system.read("auth.status");
235
+ ```
236
+
237
+ ### Forgetting crossModuleDeps
238
+
239
+ ```typescript
240
+ // WRONG – facts.auth is untyped without crossModuleDeps
241
+ const dataModule = createModule("data", {
242
+ schema: { facts: { items: t.array(t.string()) } },
243
+ constraints: {
244
+ check: {
245
+ when: (facts) => facts.auth.isAuthenticated, // No type info
246
+ require: { type: "FETCH" },
247
+ },
248
+ },
249
+ });
250
+
251
+ // CORRECT – declare the dependency
252
+ const dataModule = createModule("data", {
253
+ schema: { facts: { items: t.array(t.string()) } },
254
+ crossModuleDeps: { auth: authSchema },
255
+ constraints: {
256
+ check: {
257
+ when: (facts) => facts.auth.isAuthenticated, // Fully typed
258
+ require: { type: "FETCH" },
259
+ },
260
+ },
261
+ });
262
+ ```
263
+
264
+ ## System Configuration Options
265
+
266
+ ```typescript
267
+ const system = createSystem({
268
+ modules: { auth: authModule, cart: cartModule, data: dataModule },
269
+
270
+ // Initial facts – applied after init(), before first reconciliation
271
+ initialFacts: {
272
+ auth: { token: "restored-token" },
273
+ cart: { items: cachedItems },
274
+ },
275
+
276
+ // Init order – control module initialization sequence
277
+ initOrder: "auto", // Sort by crossModuleDeps topology (default)
278
+ // initOrder: "declaration", // Use object key order
279
+ // initOrder: ["auth", "data", "cart"], // Explicit order
280
+
281
+ plugins: [loggingPlugin()],
282
+ history: true,
283
+ });
284
+
285
+ // Hydrate from async source (call before start)
286
+ await system.hydrate(async () => {
287
+ const stored = localStorage.getItem("app-state");
288
+
289
+ return stored ? JSON.parse(stored) : {};
290
+ });
291
+
292
+ system.start();
293
+ await system.settle();
294
+ ```
295
+
296
+ ## Dynamic Module Registration
297
+
298
+ ```typescript
299
+ // Lazy-load and register a module at runtime
300
+ const chatModule = await import("./modules/chat");
301
+ system.registerModule("chat", chatModule.default);
302
+
303
+ // Now accessible at system.facts.chat.*, system.events.chat.*, etc.
304
+ ```
305
+
306
+ ## Cross-Module Events
307
+
308
+ Events are namespaced at the system level. **Prefer the typed events accessor** — it carries autocomplete and per-event payload typing.
309
+
310
+ ```typescript
311
+ // Multi-module events — canonical form
312
+ system.events.auth.login({ token: "abc" });
313
+ system.events.cart.addItem({ id: "item-1", qty: 1 });
314
+
315
+ // dispatch() is supported when you need to forward a programmatically-built
316
+ // event (e.g., replaying a serialized event). The string form
317
+ // (system.dispatch("login", payload)) does NOT exist — only the single-arg
318
+ // object form is valid.
319
+ system.dispatch({ type: "login", token: "abc" });
320
+ ```
321
+
322
+ ## See also
323
+
324
+ - [`core-patterns.md`](./core-patterns.md) — start here for the single-module shape before going namespaced
325
+ - [`system-api.md`](./system-api.md) — `createSystem({ modules: { ... } })` and the namespaced facts proxy
326
+ - [`constraints.md`](./constraints.md) — `crossModuleDeps` for constraints that fire on facts owned by another module
327
+ - [`naming.md`](./naming.md) — `facts.self.*` convention; dot-notation vs bracket-notation rules
@@ -0,0 +1,361 @@
1
+ # Plugins
2
+
3
+ > Covers `@directive-run/core/plugins` — plugin authoring + built-ins (logging, devtools, persistence, audit-ledger, observability, OTLP).
4
+
5
+ Plugins extend Directive systems with cross-cutting functionality like logging, persistence, devtools, and resilience patterns.
6
+
7
+ ## Decision Tree: "Which plugin do I need?"
8
+
9
+ ```
10
+ What do you want?
11
+ ├── See state changes in console → loggingPlugin()
12
+ ├── Connect to browser DevTools → devtoolsPlugin()
13
+ ├── Persist state across reloads → persistencePlugin(config)
14
+ ├── Protect resolvers from cascading failures → createCircuitBreaker(config)
15
+ ├── Track metrics, traces, alerts → createObservability(config)
16
+ └── Custom cross-cutting behavior → Write a custom plugin
17
+ ```
18
+
19
+ ## Using Built-In Plugins
20
+
21
+ ```typescript
22
+ import { createSystem } from "@directive-run/core";
23
+ import {
24
+ devtoolsPlugin,
25
+ loggingPlugin,
26
+ persistencePlugin,
27
+ } from "@directive-run/core/plugins";
28
+
29
+ const system = createSystem({
30
+ module: myModule,
31
+ plugins: [
32
+ devtoolsPlugin(),
33
+ loggingPlugin({ level: "debug" }),
34
+ persistencePlugin({
35
+ key: "my-app-state",
36
+ storage: localStorage,
37
+ }),
38
+ ],
39
+ });
40
+ ```
41
+
42
+ Plugins are applied in order. Place logging first to capture all events including those from other plugins.
43
+
44
+ ## loggingPlugin
45
+
46
+ Logs state changes, requirements, and resolutions to the console.
47
+
48
+ ```typescript
49
+ import { loggingPlugin } from "@directive-run/core/plugins";
50
+
51
+ // Default – logs facts changes and resolver start/complete at "info" level
52
+ loggingPlugin()
53
+
54
+ // Debug level – logs everything including derivation recomputation and constraint evaluation
55
+ loggingPlugin({ level: "debug" })
56
+
57
+ // Custom filter – only log specific events
58
+ loggingPlugin({
59
+ filter: (event) => event.startsWith("resolver."),
60
+ })
61
+
62
+ // Custom logger and prefix
63
+ loggingPlugin({
64
+ level: "warn",
65
+ prefix: "[MyApp]",
66
+ logger: customLogger,
67
+ })
68
+ ```
69
+
70
+ Options: `level` ("debug" | "info" | "warn" | "error"), `filter` (predicate on event name string), `logger` (Console-compatible object), `prefix` (string, default "[Directive]").
71
+
72
+ ## devtoolsPlugin
73
+
74
+ Connects to the Directive DevTools browser extension for visual state inspection.
75
+
76
+ ```typescript
77
+ import { devtoolsPlugin } from "@directive-run/core/plugins";
78
+
79
+ // Default
80
+ devtoolsPlugin()
81
+
82
+ // With options
83
+ devtoolsPlugin({
84
+ name: "My App", // Name shown in DevTools
85
+ maxEvents: 1000, // Max trace events to retain (default: 1000)
86
+ trace: true, // Enable trace logging
87
+ panel: true, // Show floating debug panel (dev mode only)
88
+ })
89
+ ```
90
+
91
+ Only enable in development. The plugin is a no-op if the DevTools extension is not installed.
92
+
93
+ ## persistencePlugin
94
+
95
+ Persists fact state to storage. Restores on system creation.
96
+
97
+ ```typescript
98
+ import { persistencePlugin } from "@directive-run/core/plugins";
99
+
100
+ // localStorage (default)
101
+ persistencePlugin({
102
+ key: "my-app-state",
103
+ storage: localStorage,
104
+ })
105
+
106
+ // sessionStorage – cleared when tab closes
107
+ persistencePlugin({
108
+ key: "session-state",
109
+ storage: sessionStorage,
110
+ })
111
+
112
+ // Custom storage adapter
113
+ persistencePlugin({
114
+ key: "my-app",
115
+ storage: {
116
+ getItem: (key) => myCustomStore.get(key),
117
+ setItem: (key, value) => myCustomStore.set(key, value),
118
+ removeItem: (key) => myCustomStore.delete(key),
119
+ },
120
+ })
121
+
122
+ // Selective persistence – only persist certain facts
123
+ persistencePlugin({
124
+ key: "my-app",
125
+ storage: localStorage,
126
+ include: ["user", "preferences"], // Only these facts
127
+ // OR
128
+ exclude: ["tempData", "sessionId"], // Everything except these
129
+ })
130
+
131
+ // With callbacks
132
+ persistencePlugin({
133
+ key: "my-app",
134
+ storage: localStorage,
135
+ debounce: 200, // Debounce saves (default: 100ms)
136
+ onRestore: (data) => console.log("Restored:", data),
137
+ onSave: (data) => console.log("Saved:", data),
138
+ onError: (err) => console.error("Persistence error:", err),
139
+ })
140
+ ```
141
+
142
+ ## createCircuitBreaker
143
+
144
+ Standalone utility (not a Plugin) that implements the circuit breaker pattern for resilience. Use it inside resolvers to protect against cascading failures from external services.
145
+
146
+ ```typescript
147
+ import { createCircuitBreaker } from "@directive-run/core/plugins";
148
+
149
+ const breaker = createCircuitBreaker({
150
+ failureThreshold: 5, // Failures before opening (default: 5)
151
+ recoveryTimeMs: 30000, // Time before HALF_OPEN (default: 30000)
152
+ halfOpenMaxRequests: 3, // Requests allowed in HALF_OPEN (default: 3)
153
+ failureWindowMs: 60000, // Window for counting failures (default: 60000)
154
+ name: "api-breaker", // Name for metrics/errors
155
+ onStateChange: (from, to) => console.log(`Circuit: ${from} -> ${to}`),
156
+ });
157
+
158
+ // Use inside a resolver
159
+ resolvers: {
160
+ fetchData: {
161
+ requirement: "FETCH_DATA",
162
+ resolve: async (req, context) => {
163
+ const result = await breaker.execute(async () => {
164
+ return await callExternalAPI();
165
+ });
166
+ context.facts.data = result;
167
+ },
168
+ },
169
+ },
170
+
171
+ // Wire circuit state into constraints
172
+ constraints: {
173
+ apiDown: {
174
+ when: () => breaker.getState() === "OPEN",
175
+ require: { type: "FALLBACK_RESPONSE" },
176
+ },
177
+ },
178
+ ```
179
+
180
+ Circuit breaker states: **Closed** (normal) -> **Open** (failing, rejects immediately) -> **Half-Open** (testing limited requests).
181
+
182
+ ## createObservability
183
+
184
+ Metrics, tracing, and alerting for production systems.
185
+
186
+ ```typescript
187
+ import { createObservability } from "@directive-run/core/plugins";
188
+
189
+ const system = createSystem({
190
+ module: myModule,
191
+ plugins: [
192
+ createObservability({
193
+ metrics: {
194
+ enabled: true,
195
+ // Track resolver duration, constraint evaluation count, etc.
196
+ },
197
+ tracing: {
198
+ enabled: true,
199
+ // Trace resolver execution with spans
200
+ },
201
+ alerts: {
202
+ enabled: true,
203
+ onAlert: (alert) => {
204
+ // Send to monitoring service
205
+ sendToDatadog(alert);
206
+ },
207
+ },
208
+ }),
209
+ ],
210
+ });
211
+ ```
212
+
213
+ ## Plugin Lifecycle Hooks
214
+
215
+ Plugins hook into the system lifecycle. Use these to build custom plugins.
216
+
217
+ ```typescript
218
+ import type { Plugin, ModuleSchema } from "@directive-run/core";
219
+
220
+ const myPlugin: Plugin<ModuleSchema> = {
221
+ name: "my-custom-plugin",
222
+
223
+ // System lifecycle
224
+ onInit: (system) => {
225
+ // Called when system is created (only async hook)
226
+ },
227
+ onStart: (system) => {
228
+ // Called when system.start() is invoked
229
+ },
230
+ onStop: (system) => {
231
+ // Called when system.stop() is invoked
232
+ },
233
+ onDestroy: (system) => {
234
+ // Called when system.destroy() is invoked
235
+ },
236
+
237
+ // Fact tracking
238
+ onFactSet: (key, value, prev) => {
239
+ // Called when a single fact is set
240
+ },
241
+ onFactsBatch: (changes) => {
242
+ // Called after a batch of fact changes completes
243
+ },
244
+
245
+ // Requirement pipeline
246
+ onRequirementCreated: (requirement) => {
247
+ // Called when a constraint emits a requirement
248
+ // requirement.type, requirement.id
249
+ },
250
+ onRequirementMet: (requirement, byResolver) => {
251
+ // Called when a requirement is fulfilled
252
+ },
253
+
254
+ // Resolver pipeline
255
+ onResolverStart: (resolverId, requirement) => {
256
+ // Called when a resolver begins executing
257
+ },
258
+ onResolverComplete: (resolverId, requirement, duration) => {
259
+ // Called when a resolver finishes successfully
260
+ },
261
+ onResolverError: (resolverId, requirement, error) => {
262
+ // Called when a resolver fails (after all retries)
263
+ },
264
+
265
+ // Error handling
266
+ onError: (error) => {
267
+ // Called on any DirectiveError in the system
268
+ // error.source, error.message, error.context
269
+ },
270
+ };
271
+
272
+ const system = createSystem({
273
+ module: myModule,
274
+ plugins: [myPlugin],
275
+ });
276
+ ```
277
+
278
+ ## Common Mistakes
279
+
280
+ ### Enabling devtools in production
281
+
282
+ ```typescript
283
+ // WRONG – devtools overhead in production
284
+ const system = createSystem({
285
+ module: myModule,
286
+ plugins: [devtoolsPlugin()],
287
+ });
288
+
289
+ // CORRECT – conditional on environment
290
+ const plugins = [];
291
+ if (process.env.NODE_ENV === "development") {
292
+ plugins.push(devtoolsPlugin());
293
+ plugins.push(loggingPlugin({ level: "debug" }));
294
+ }
295
+
296
+ const system = createSystem({
297
+ module: myModule,
298
+ plugins,
299
+ });
300
+ ```
301
+
302
+ ### Persistence without filtering
303
+
304
+ ```typescript
305
+ // WRONG – persists everything including transient state
306
+ persistencePlugin({
307
+ key: "app-state",
308
+ storage: localStorage,
309
+ })
310
+
311
+ // CORRECT – use include/exclude to control what's persisted
312
+ persistencePlugin({
313
+ key: "app-state",
314
+ storage: localStorage,
315
+ include: ["user", "preferences", "settings"],
316
+ })
317
+ ```
318
+
319
+ ### Plugin order matters
320
+
321
+ ```typescript
322
+ // WRONG – logging misses events from persistence restore
323
+ const system = createSystem({
324
+ module: myModule,
325
+ plugins: [
326
+ persistencePlugin({ key: "app", storage: localStorage }),
327
+ loggingPlugin(), // Misses restore events
328
+ ],
329
+ });
330
+
331
+ // CORRECT – logging first to capture everything
332
+ const system = createSystem({
333
+ module: myModule,
334
+ plugins: [
335
+ loggingPlugin(),
336
+ persistencePlugin({ key: "app", storage: localStorage }),
337
+ ],
338
+ });
339
+ ```
340
+
341
+ ### Persisting sensitive or transient data
342
+
343
+ ```typescript
344
+ // WRONG – persists auth tokens and loading state
345
+ persistencePlugin({
346
+ key: "app",
347
+ storage: localStorage,
348
+ })
349
+
350
+ // CORRECT – exclude sensitive and transient facts
351
+ persistencePlugin({
352
+ key: "app",
353
+ storage: localStorage,
354
+ exclude: ["authToken", "isLoading", "error"],
355
+ })
356
+ ```
357
+
358
+ ## See also
359
+
360
+ - [`system-api.md`](./system-api.md) — `plugins:` option on `createSystem` and the lifecycle hooks plugins observe
361
+ - [`error-boundaries.md`](./error-boundaries.md) — `createCircuitBreaker` and the resilience plugins that pair with error boundaries