@directive-run/knowledge 1.13.0 → 1.15.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 (43) hide show
  1. package/README.md +50 -15
  2. package/ai/ai-adapters.md +2 -0
  3. package/ai/ai-agents-streaming.md +182 -149
  4. package/ai/ai-budget-resilience.md +299 -132
  5. package/ai/ai-communication.md +215 -197
  6. package/ai/ai-debug-observability.md +253 -173
  7. package/ai/ai-guardrails-memory.md +185 -153
  8. package/ai/ai-mcp-rag.md +199 -199
  9. package/ai/ai-multi-agent.md +247 -153
  10. package/ai/ai-orchestrator.md +344 -114
  11. package/ai/ai-security.md +282 -180
  12. package/ai/ai-tasks.md +3 -1
  13. package/ai/ai-testing-evals.md +357 -256
  14. package/core/anti-patterns.md +9 -2
  15. package/core/constraints.md +6 -2
  16. package/core/core-patterns.md +2 -0
  17. package/core/error-boundaries.md +2 -0
  18. package/core/history.md +2 -0
  19. package/core/multi-module.md +8 -3
  20. package/core/naming.md +15 -6
  21. package/core/plugins.md +2 -0
  22. package/core/react-adapter.md +250 -174
  23. package/core/resolvers.md +2 -0
  24. package/core/schema-types.md +2 -0
  25. package/core/system-api.md +2 -0
  26. package/core/testing.md +251 -143
  27. package/examples/checkers.ts +15 -16
  28. package/examples/contact-form.ts +2 -2
  29. package/examples/counter-react.ts +1 -1
  30. package/examples/counter-svelte.ts +1 -1
  31. package/examples/counter-vue.ts +1 -1
  32. package/examples/counter.ts +1 -1
  33. package/examples/data-triggers.ts +4 -4
  34. package/examples/feature-flags.ts +2 -2
  35. package/examples/form-wizard.ts +2 -2
  36. package/examples/newsletter.ts +2 -2
  37. package/examples/server.ts +2 -2
  38. package/examples/shopping-cart.ts +1 -1
  39. package/examples/topic-guard.ts +1 -1
  40. package/package.json +3 -3
  41. package/sitemap.md +26 -3
  42. package/examples/debounce-constraints.ts +0 -95
  43. package/examples/multi-module.ts +0 -58
@@ -1,294 +1,370 @@
1
1
  # React Adapter
2
2
 
3
+ > Covers `@directive-run/react` — `useFact`, `useDerived`, `useEvents`, `useSelector`, `useDirective`, `createDirectiveContext`.
4
+
3
5
  The React adapter connects Directive systems to React components. Import from `@directive-run/react`.
4
6
 
5
- ## Decision Tree: "How do I use Directive in React?"
7
+ ## Canonical pattern: `createDirectiveContext`
8
+
9
+ This is the recommended way to use Directive in React. One call returns a typed `Provider` plus bound hooks — no system arg needed at every call site, no `useContext` boilerplate, full type inference from the schema.
10
+
11
+ ```tsx
12
+ // counter-context.ts — create once, import everywhere
13
+ import { createSystem } from "@directive-run/core";
14
+ import { createDirectiveContext } from "@directive-run/react";
15
+ import { counterModule } from "./counter-module";
16
+
17
+ export const counterSystem = createSystem({ module: counterModule });
18
+ export const Counter = createDirectiveContext(counterSystem);
19
+ ```
20
+
21
+ ```tsx
22
+ // App.tsx
23
+ import { Counter } from "./counter-context";
24
+ import { Display } from "./Display";
25
+
26
+ export function App() {
27
+ return (
28
+ <Counter.Provider>
29
+ <Display />
30
+ </Counter.Provider>
31
+ );
32
+ }
33
+ ```
34
+
35
+ ```tsx
36
+ // Display.tsx
37
+ import { Counter } from "./counter-context";
6
38
 
39
+ export function Display() {
40
+ const count = Counter.useFact("count");
41
+ const doubled = Counter.useDerived("doubled");
42
+ const events = Counter.useEvents();
43
+
44
+ return (
45
+ <div>
46
+ <p>Count: {count} (doubled: {doubled})</p>
47
+ <button onClick={() => events.increment()}>+1</button>
48
+ <button onClick={() => events.reset()}>Reset</button>
49
+ </div>
50
+ );
51
+ }
7
52
  ```
8
- What are you building?
9
- ├── Read system state in a component useSelector(system, selector)
10
- ├── Dispatch events from a component → useEvent(system)
11
- ├── Create a system scoped to a component useSystem(config)
12
- ├── Share a system across components → DirectiveProvider + useDirectiveContext()
13
- └── Global system shared by entire app → Create outside React, use useSelector
53
+
54
+ `createDirectiveContext(system)` returns: `{ Provider, useSystem, useFact, useDerived, useEvents, useDispatch, useSelector, useWatch, useInspect, useExplain, useHistory }`. All bound to the typed system — no system argument needed when calling them.
55
+
56
+ The Provider accepts an optional `system` prop override for testing:
57
+
58
+ ```tsx
59
+ <Counter.Provider system={testSystem}>
60
+ <ComponentUnderTest />
61
+ </Counter.Provider>
14
62
  ```
15
63
 
16
- ## Setup: System Outside React (Recommended)
64
+ ## Standalone hooks (without a context)
17
65
 
18
- Create the system outside of React. Components subscribe to it.
66
+ When you don't want a context (e.g. one-off components, prototypes), import the hooks directly and pass the system to each call:
19
67
 
20
- ```typescript
21
- // system.ts – created once, imported anywhere
68
+ ```tsx
69
+ // system.ts
22
70
  import { createSystem } from "@directive-run/core";
23
71
  import { counterModule } from "./counter-module";
24
72
 
25
73
  export const system = createSystem({ module: counterModule });
26
74
  ```
27
75
 
28
- ```typescript
76
+ ```tsx
29
77
  // Counter.tsx
30
- import { useSelector, useEvent } from "@directive-run/react";
78
+ import { useFact, useDerived, useEvents } from "@directive-run/react";
31
79
  import { system } from "./system";
32
80
 
33
- function Counter() {
34
- // Subscribe to derived state – re-renders only when value changes
35
- const count = useSelector(system, (s) => s.facts.count);
36
- const doubled = useSelector(system, (s) => s.derive.doubled);
37
-
38
- // Get event dispatcher
39
- const events = useEvent(system);
81
+ export function Counter() {
82
+ const count = useFact(system, "count");
83
+ const doubled = useDerived(system, "doubled");
84
+ const events = useEvents(system);
40
85
 
41
86
  return (
42
87
  <div>
43
- <p>Count: {count}</p>
44
- <p>Doubled: {doubled}</p>
88
+ <p>Count: {count} (doubled: {doubled})</p>
45
89
  <button onClick={() => events.increment()}>+1</button>
46
- <button onClick={() => events.reset()}>Reset</button>
47
90
  </div>
48
91
  );
49
92
  }
50
93
  ```
51
94
 
52
- ## useSelector
95
+ ## Hook reference
53
96
 
54
- Subscribes to system state. Re-renders the component only when the selected value changes (shallow comparison).
97
+ ### `useFact(system, key)` or `useFact(system, [keys])`
55
98
 
56
- ```typescript
57
- import { useSelector } from "@directive-run/react";
99
+ Reads one fact (single key) or many facts (array of keys). Re-renders only when the selected fact(s) change. Return type is narrowed by the schema.
58
100
 
59
- function UserProfile() {
60
- // Select a single fact
61
- const name = useSelector(system, (s) => s.facts.userName);
101
+ ```tsx
102
+ import { useFact } from "@directive-run/react";
62
103
 
63
- // Select a derivation
64
- const isAdmin = useSelector(system, (s) => s.derive.isAdmin);
104
+ function Profile() {
105
+ // Single key typed: name is string | undefined
106
+ const name = useFact(system, "name");
65
107
 
66
- // Select a computed value from multiple facts
67
- const summary = useSelector(system, (s) => ({
68
- name: s.facts.userName,
69
- role: s.facts.role,
70
- isAdmin: s.derive.isAdmin,
71
- }));
108
+ // Multi-key typed: { name: string; age: number }
109
+ const { name, age } = useFact(system, ["name", "age"]);
72
110
 
73
- return <div>{summary.name} ({summary.role})</div>;
111
+ return <div>{name} ({age})</div>;
74
112
  }
75
113
  ```
76
114
 
77
- ### Multi-Module System
115
+ ### `useDerived(system, id)` or `useDerived(system, [ids])`
78
116
 
79
- ```typescript
80
- const system = createSystem({
81
- modules: { auth: authModule, cart: cartModule },
82
- });
117
+ Same shape as `useFact` but for derivations. Re-renders only when the derivation's value changes.
83
118
 
84
- function Header() {
85
- const token = useSelector(system, (s) => s.facts.auth.token);
86
- const itemCount = useSelector(system, (s) => s.derive.cart.itemCount);
119
+ ```tsx
120
+ import { useDerived } from "@directive-run/react";
121
+
122
+ function Status() {
123
+ const isReady = useDerived(system, "isReady");
124
+ const { isReady, isLoading } = useDerived(system, ["isReady", "isLoading"]);
87
125
 
88
- return <header>Items: {itemCount}</header>;
126
+ return <div>{isReady ? "ready" : "loading"}</div>;
89
127
  }
90
128
  ```
91
129
 
92
- ## useEvent
130
+ ### `useEvents(system)`
93
131
 
94
- Returns the system's event dispatcher. Stable reference (does not cause re-renders).
132
+ Returns the system's typed events accessor. Stable identity does not cause re-renders. **This is the primary way to dispatch events from a component** — it carries autocomplete for every event name and payload shape.
95
133
 
96
- ```typescript
97
- import { useEvent } from "@directive-run/react";
134
+ ```tsx
135
+ import { useEvents } from "@directive-run/react";
136
+
137
+ function CartActions() {
138
+ const events = useEvents(system);
98
139
 
99
- function LoginForm() {
100
- const events = useEvent(system);
140
+ return (
141
+ <>
142
+ <button onClick={() => events.addItem({ productId: "p1" })}>Add</button>
143
+ <button onClick={() => events.checkout()}>Checkout</button>
144
+ </>
145
+ );
146
+ }
101
147
 
102
- const handleSubmit = (email: string, password: string) => {
103
- events.login({ email, password });
104
- };
148
+ // Namespaced (multi-module) system events are nested by module name
149
+ function Header() {
150
+ const events = useEvents(namespacedSystem);
105
151
 
106
- return <form onSubmit={() => handleSubmit("a@b.com", "pass")}>...</form>;
152
+ return <button onClick={() => events.cart.addItem({ productId: "p1" })}>Add</button>;
107
153
  }
154
+ ```
108
155
 
109
- // Multi-module
110
- function CartActions() {
111
- const events = useEvent(system);
156
+ ### `useSelector(system, selector, equalityFn?)`
112
157
 
113
- return (
114
- <button onClick={() => events.cart.addItem({ productId: "p1" })}>
115
- Add to Cart
116
- </button>
117
- );
158
+ For computed values across multiple facts and derivations. Auto-tracks accessed keys; re-renders only when the selected result changes (by `Object.is` equality, override with `equalityFn`).
159
+
160
+ ```tsx
161
+ import { useSelector, shallowEqual } from "@directive-run/react";
162
+
163
+ function CheckoutSummary() {
164
+ const summary = useSelector(system, (state) => ({
165
+ itemCount: state.items.length,
166
+ total: state.cartTotal,
167
+ isReady: state.canCheckout,
168
+ }), shallowEqual);
169
+
170
+ return <div>{summary.itemCount} items · {summary.total}</div>;
118
171
  }
119
172
  ```
120
173
 
121
- ## useSystem
174
+ `state` carries both facts and derivations in a flat namespace — that's why it's typed `InferSelectorState<S>`, not `InferFacts<S>`.
122
175
 
123
- Creates and manages a system's lifecycle within a React component. The system is created on mount and destroyed on unmount.
176
+ ### `useDispatch(system)`
124
177
 
125
- ```typescript
126
- import { useSystem, useSelector, useEvent } from "@directive-run/react";
178
+ Returns a raw `dispatch(event)` function. Useful when you need to forward events programmatically or when the event name is computed at runtime. Prefer `useEvents` for normal dispatch — it carries typing.
179
+
180
+ ```tsx
181
+ import { useDispatch } from "@directive-run/react";
182
+
183
+ function ToolBar({ actions }: { actions: SystemEvent[] }) {
184
+ const dispatch = useDispatch(system);
185
+
186
+ return actions.map((a) => (
187
+ <button key={a.type} onClick={() => dispatch(a)}>{a.type}</button>
188
+ ));
189
+ }
190
+ ```
191
+
192
+ ### `useDirective(moduleOrOptions, selections?)`
193
+
194
+ Convenience hook that **creates a scoped system** AND selects facts/derivations in one call. The system lives for the lifetime of the component (created on mount, destroyed on unmount). Use this when the system's lifecycle should match a component's lifecycle — game boards, wizard flows, modal forms.
195
+
196
+ ```tsx
197
+ import { useDirective } from "@directive-run/react";
198
+ import { gameModule } from "./game-module";
127
199
 
128
200
  function GameBoard() {
129
- // System created on mount, destroyed on unmount
130
- const gameSystem = useSystem({
131
- module: gameModule,
132
- history: true,
201
+ // Selective subscription only re-renders when score or isOver change
202
+ const { facts: { score }, derived: { isOver }, events } = useDirective(gameModule, {
203
+ facts: ["score"],
204
+ derived: ["isOver"],
133
205
  });
134
206
 
135
- const score = useSelector(gameSystem, (s) => s.facts.score);
136
- const events = useEvent(gameSystem);
137
-
138
207
  return (
139
208
  <div>
140
- <p>Score: {score}</p>
209
+ <p>Score: {score}{isOver && " — game over!"}</p>
141
210
  <button onClick={() => events.move({ direction: "up" })}>Up</button>
142
211
  </div>
143
212
  );
144
213
  }
214
+
215
+ // Subscribe to everything (omit keys)
216
+ function Debug() {
217
+ const { facts, derived, events, dispatch } = useDirective(gameModule);
218
+
219
+ return <pre>{JSON.stringify({ facts, derived }, null, 2)}</pre>;
220
+ }
145
221
  ```
146
222
 
147
- Use `useSystem` when the system's lifecycle matches a component's lifecycle (e.g., a game board, a wizard, a modal form). For app-wide state, create the system outside React.
223
+ For app-wide state, prefer `createDirectiveContext` over `useDirective` the context pattern creates the system once instead of per-component.
148
224
 
149
- ## DirectiveProvider and useDirectiveContext
225
+ ### `useDirectiveRef(moduleOrOptions, config?)`
150
226
 
151
- Share a system through React context.
227
+ Just the system-lifecycle half of `useDirective`. Returns the `SingleModuleSystem<S>` instance. Use this when you need a scoped system without consuming its state in this component (e.g., you're going to thread it through props or a context).
152
228
 
153
- ```typescript
154
- import { DirectiveProvider, useDirectiveContext, useSelector, useEvent } from "@directive-run/react";
229
+ ### `useWatch(system, factOrDerivationId, callback)`
155
230
 
156
- // Provide the system at the top of your tree
157
- function App() {
158
- return (
159
- <DirectiveProvider system={system}>
160
- <Dashboard />
161
- </DirectiveProvider>
162
- );
163
- }
231
+ Imperative side-effect when a fact or derivation changes. Does NOT cause re-renders — use this for analytics, logging, focus management. For state you want to render, use `useFact` / `useDerived` / `useSelector`.
164
232
 
165
- // Consume the system anywhere below
166
- function Dashboard() {
167
- const system = useDirectiveContext();
168
- const stats = useSelector(system, (s) => s.derive.dashboardStats);
233
+ ### Status + introspection hooks
169
234
 
170
- return <div>{stats.totalUsers} users</div>;
171
- }
172
- ```
235
+ | Hook | Purpose |
236
+ |---|---|
237
+ | `useRequirementStatus(system, type)` | Reactive `idle / running / success / error` status for a requirement type |
238
+ | `useConstraintStatus(system, id)` | Reactive constraint enabled/disabled state |
239
+ | `useSuspenseRequirement(system, type)` | Same as `useRequirementStatus` but suspends until resolved (Suspense boundary required) |
240
+ | `useExplain(system, requirementId)` | Reactive English explanation of a requirement's `when` predicate |
241
+ | `useInspect(system, options?)` | Full system inspection state — constraints, requirements, resolvers, derivations |
242
+ | `useHistory(system)` | Time-travel UI helpers: `goBack`, `goForward`, `canUndo`, `canRedo`, snapshot list |
243
+ | `useAuditLedger(system)` | Subscribe to a configured `createAuditLedger` plugin's entries |
173
244
 
174
- ## CRITICAL: Hooks That DO NOT Exist
245
+ ### SSR + hydration
175
246
 
176
- ```typescript
177
- // WRONG – useDirective() does not exist. This is a common hallucination.
178
- const { facts, derive, events } = useDirective(system);
247
+ `DirectiveHydrator` and `useHydratedSystem` close the SSR loop. See `system-api.md` → "Hydration" for the full pattern.
179
248
 
180
- // CORRECT – use useSelector for state, useEvent for actions
181
- const count = useSelector(system, (s) => s.facts.count);
182
- const events = useEvent(system);
183
- ```
249
+ ### Suspense for data fetching
184
250
 
185
- ## createDirectiveContext
251
+ `useQuerySystem` + `useSuspenseQuery` integrate `@directive-run/query`'s subscription/mutation primitives with React's Suspense and Error Boundaries. See the `@directive-run/query` docs.
186
252
 
187
- Eliminates prop-drilling by providing the system via React context:
253
+ ## CRITICAL: hooks that DO NOT exist
188
254
 
189
- ```typescript
190
- import { createDirectiveContext } from "@directive-run/react";
255
+ LLMs frequently hallucinate these names because they sound plausible. None of them are exported from `@directive-run/react`.
191
256
 
192
- const Counter = createDirectiveContext(counterSystem);
257
+ | Hallucination | Use instead |
258
+ |---|---|
259
+ | `useEvent(system)` (singular) | `useEvents(system)` (plural) |
260
+ | `useSystem(config)` as a top-level import | `useDirective(module)` or `createDirectiveContext().useSystem` |
261
+ | `DirectiveProvider` as a top-level import | `createDirectiveContext(system).Provider` |
262
+ | `useDirectiveContext()` | `createDirectiveContext(system).useSystem()` |
263
+ | `useState`-style writes like `setCount(5)` | Dispatch events: `events.setCount(5)` (or whatever your module defines) |
193
264
 
194
- function App() {
195
- return (
196
- <Counter.Provider>
197
- <Display />
198
- </Counter.Provider>
199
- );
200
- }
265
+ ```tsx
266
+ // WRONG — useEvent does not exist
267
+ import { useEvent } from "@directive-run/react";
268
+ const events = useEvent(system);
201
269
 
202
- function Display() {
203
- const count = Counter.useFact("count"); // No system arg needed
204
- const doubled = Counter.useDerived("doubled");
205
- const events = Counter.useEvents();
206
- return <button onClick={() => events.increment()}>{count}</button>;
207
- }
270
+ // CORRECT — useEvents (plural)
271
+ import { useEvents } from "@directive-run/react";
272
+ const events = useEvents(system);
208
273
  ```
209
274
 
210
- Returns: `{ Provider, useSystem, useFact, useDerived, useEvents, useDispatch, useSelector, useWatch, useInspect, useExplain, useHistory }`.
211
-
212
- Provider accepts `system` prop override for testing:
213
275
  ```tsx
214
- <Counter.Provider system={testSystem}><ComponentUnderTest /></Counter.Provider>
276
+ // WRONG — DirectiveProvider is not a top-level export
277
+ import { DirectiveProvider, useDirectiveContext } from "@directive-run/react";
278
+ <DirectiveProvider system={system}>...</DirectiveProvider>
279
+
280
+ // CORRECT — Provider comes from createDirectiveContext()
281
+ import { createDirectiveContext } from "@directive-run/react";
282
+ export const Counter = createDirectiveContext(counterSystem);
283
+ <Counter.Provider>...</Counter.Provider>
215
284
  ```
216
285
 
217
- ## Common Mistakes
286
+ ## Common mistakes
218
287
 
219
- ### Creating the system inside a component without useSystem
288
+ ### Creating the system inside a component body
220
289
 
221
- ```typescript
222
- // WRONG creates a new system on every render
290
+ ```tsx
291
+ // WRONG creates a new system on every render
223
292
  function Counter() {
224
- const system = createSystem({ module: counterModule }); // New system each render!
225
- const count = useSelector(system, (s) => s.facts.count);
226
-
293
+ const system = createSystem({ module: counterModule });
294
+ const count = useFact(system, "count");
227
295
  return <div>{count}</div>;
228
296
  }
229
297
 
230
- // CORRECT create outside the component
298
+ // CORRECT create outside the component
231
299
  const system = createSystem({ module: counterModule });
232
300
 
233
301
  function Counter() {
234
- const count = useSelector(system, (s) => s.facts.count);
235
-
302
+ const count = useFact(system, "count");
236
303
  return <div>{count}</div>;
237
304
  }
238
305
 
239
- // ALSO CORRECT useSystem manages lifecycle
306
+ // ALSO CORRECT useDirective manages lifecycle
240
307
  function Counter() {
241
- const system = useSystem({ module: counterModule });
242
- const count = useSelector(system, (s) => s.facts.count);
243
-
308
+ const { facts: { count } } = useDirective(counterModule, { facts: ["count"] });
244
309
  return <div>{count}</div>;
245
310
  }
246
311
  ```
247
312
 
248
- ### Selecting too much state (causes unnecessary re-renders)
313
+ ### Selecting too much state (excess re-renders)
314
+
315
+ ```tsx
316
+ // WRONG — re-renders on ANY fact change
317
+ const allFacts = useSelector(system, (s) => s);
318
+
319
+ // WRONG — re-renders even when other facts change (new object identity every time)
320
+ const profile = useSelector(system, (s) => ({ name: s.name, age: s.age }));
249
321
 
250
- ```typescript
251
- // WRONG re-renders on ANY fact change
252
- const allFacts = useSelector(system, (s) => s.facts);
322
+ // CORRECT — use the targeted hook
323
+ const name = useFact(system, "name");
253
324
 
254
- // CORRECT select only what you need
255
- const name = useSelector(system, (s) => s.facts.userName);
256
- const count = useSelector(system, (s) => s.facts.count);
325
+ // CORRECT multi-key reads a stable subset
326
+ const { name, age } = useFact(system, ["name", "age"]);
327
+
328
+ // CORRECT — useSelector with shallowEqual for shaped output
329
+ import { shallowEqual } from "@directive-run/react";
330
+ const profile = useSelector(system, (s) => ({ name: s.name, age: s.age }), shallowEqual);
257
331
  ```
258
332
 
259
- ### Mutating facts directly in event handlers
333
+ ### Mutating facts directly from an event handler
260
334
 
261
- ```typescript
262
- // WRONG bypass the event system
335
+ ```tsx
336
+ // WRONG bypasses the event system, no audit trail, no constraint reaction
263
337
  function Counter() {
264
- const count = useSelector(system, (s) => s.facts.count);
265
-
266
- return (
267
- <button onClick={() => { system.facts.count += 1; }}>
268
- {count}
269
- </button>
270
- );
338
+ const count = useFact(system, "count");
339
+ return <button onClick={() => { system.facts.count += 1; }}>{count}</button>;
271
340
  }
272
341
 
273
- // CORRECT use events for intent-driven mutations
342
+ // CORRECT dispatch an event
274
343
  function Counter() {
275
- const count = useSelector(system, (s) => s.facts.count);
276
- const events = useEvent(system);
277
-
278
- return (
279
- <button onClick={() => events.increment()}>
280
- {count}
281
- </button>
282
- );
344
+ const count = useFact(system, "count");
345
+ const events = useEvents(system);
346
+ return <button onClick={() => events.increment()}>{count}</button>;
283
347
  }
284
348
  ```
285
349
 
286
- ### Casting values from useSelector
350
+ ### Casting values from hooks
351
+
352
+ ```tsx
353
+ // WRONG — types come from the schema
354
+ const profile = useFact(system, "profile") as UserProfile;
287
355
 
288
- ```typescript
289
- // WRONG unnecessary type casting
290
- const profile = useSelector(system, (s) => s.facts.profile as UserProfile);
356
+ // CORRECT — the schema types it for you
357
+ const profile = useFact(system, "profile");
358
+ ```
359
+
360
+ ### Forgetting `useEvents` is stable
361
+
362
+ ```tsx
363
+ // UNNECESSARY — useEvents returns a stable reference, no useCallback needed
364
+ const events = useEvents(system);
365
+ const handleClick = useCallback(() => events.increment(), [events]);
291
366
 
292
- // CORRECT types are inferred from the module schema
293
- const profile = useSelector(system, (s) => s.facts.profile);
367
+ // FINE events.increment is stable across renders too
368
+ const events = useEvents(system);
369
+ <button onClick={() => events.increment()}>+</button>
294
370
  ```
package/core/resolvers.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Resolvers
2
2
 
3
+ > Covers `@directive-run/core` — resolver definition: retry policies, batching, cancellation, custom dedup keys, `owns` binding.
4
+
3
5
  Resolvers fulfill requirements emitted by constraints. They are the supply side of the constraint-resolver pattern. Resolvers handle async work and mutate state through `context.facts`.
4
6
 
5
7
  ## Decision Tree: "How should this resolver work?"
@@ -1,5 +1,7 @@
1
1
  # Schema Types
2
2
 
3
+ > Covers `@directive-run/core` — schema builders (`t.string` / `t.number` / `t.array` / etc.) and inference helpers.
4
+
3
5
  The `t.*()` builders define fact types in module schemas. They provide runtime validation in dev mode and full TypeScript inference.
4
6
 
5
7
  ## Decision Tree: "Which type builder do I use?"
@@ -1,5 +1,7 @@
1
1
  # System API
2
2
 
3
+ > Covers `@directive-run/core` — `createSystem`, the system instance surface, hydration, inspection, distributable snapshots.
4
+
3
5
  The system is created with `createSystem()` and is the runtime that orchestrates modules, constraints, resolvers, and plugins.
4
6
 
5
7
  ## Decision Tree: "How do I interact with the system?"