@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,249 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: migrating-to-directive
|
|
3
|
+
description: "Migrate state management code from Redux, Zustand, XState, MobX, Recoil, or Jotai to Directive. Provides concept mapping, step-by-step migration patterns, and before/after code examples. Use when asked to migrate, convert, or port existing state management to Directive."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Migrating to Directive
|
|
7
|
+
|
|
8
|
+
## Prerequisites
|
|
9
|
+
|
|
10
|
+
This skill applies when migrating TO Directive. The user should have or plan to install `@directive-run/core`: `npm install @directive-run/core`.
|
|
11
|
+
|
|
12
|
+
## When Claude Should Use This Skill
|
|
13
|
+
|
|
14
|
+
### Auto-Invoke Triggers
|
|
15
|
+
- User mentions migrating FROM Redux, Zustand, XState, MobX, Recoil, or Jotai
|
|
16
|
+
- User asks "how do I convert this Redux code to Directive"
|
|
17
|
+
- User wants to replace their state management with Directive
|
|
18
|
+
- User asks about Directive equivalents of concepts from other libraries
|
|
19
|
+
- User has existing code in another library and wants Directive version
|
|
20
|
+
|
|
21
|
+
### Exclusions – Use a Different Skill
|
|
22
|
+
- User is writing Directive from scratch (no migration) → `writing-directive-modules`
|
|
23
|
+
- User wants to understand Directive basics → `getting-started-with-directive`
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Concept Mapping
|
|
28
|
+
|
|
29
|
+
## Redux → Directive
|
|
30
|
+
|
|
31
|
+
| Redux | Directive | Notes |
|
|
32
|
+
|-------|-----------|-------|
|
|
33
|
+
| Store | System | `createSystem()` wraps modules |
|
|
34
|
+
| Slice / Reducer | Module | `createModule()` with schema + init |
|
|
35
|
+
| State fields | Facts | Schema keys with `t.*()` type builders |
|
|
36
|
+
| Selectors | Derivations | Auto-tracked, no `createSelector` needed |
|
|
37
|
+
| Actions | Requirements | Typed with `UPPER_SNAKE_CASE` |
|
|
38
|
+
| Thunks / Sagas | Resolvers | Async handlers with retry, dedup |
|
|
39
|
+
| Middleware | Plugins | Lifecycle hooks |
|
|
40
|
+
| `useSelector` | `useSelector` | From `@directive-run/react` |
|
|
41
|
+
| `dispatch(action)` | `system.dispatch(event)` | Or constraint auto-triggers |
|
|
42
|
+
| `configureStore` | `createSystem` | With plugins instead of middleware |
|
|
43
|
+
|
|
44
|
+
### Migration Pattern
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
// BEFORE: Redux Toolkit
|
|
48
|
+
const counterSlice = createSlice({
|
|
49
|
+
name: "counter",
|
|
50
|
+
initialState: { value: 0, status: "idle" },
|
|
51
|
+
reducers: {
|
|
52
|
+
increment: (state) => { state.value += 1; },
|
|
53
|
+
decrement: (state) => { state.value -= 1; },
|
|
54
|
+
},
|
|
55
|
+
extraReducers: (builder) => {
|
|
56
|
+
builder.addCase(fetchCount.fulfilled, (state, action) => {
|
|
57
|
+
state.value = action.payload;
|
|
58
|
+
state.status = "loaded";
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// AFTER: Directive
|
|
64
|
+
const counter = createModule("counter", {
|
|
65
|
+
schema: {
|
|
66
|
+
value: t.number(),
|
|
67
|
+
status: t.string<"idle" | "loading" | "loaded">(),
|
|
68
|
+
},
|
|
69
|
+
init: (facts) => {
|
|
70
|
+
facts.value = 0;
|
|
71
|
+
facts.status = "idle";
|
|
72
|
+
},
|
|
73
|
+
// Reducers become direct mutations:
|
|
74
|
+
// system.facts.value += 1 (no action dispatch needed)
|
|
75
|
+
//
|
|
76
|
+
// Async thunks become constraint + resolver:
|
|
77
|
+
constraints: {
|
|
78
|
+
needsData: {
|
|
79
|
+
when: (facts) => facts.status === "idle",
|
|
80
|
+
require: { type: "FETCH_COUNT" },
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
resolvers: {
|
|
84
|
+
fetchCount: {
|
|
85
|
+
requirement: "FETCH_COUNT",
|
|
86
|
+
retry: { attempts: 3, backoff: "exponential" },
|
|
87
|
+
resolve: async (req, context) => {
|
|
88
|
+
context.facts.status = "loading";
|
|
89
|
+
const response = await fetch("/api/count");
|
|
90
|
+
const data = await response.json();
|
|
91
|
+
context.facts.value = data.count;
|
|
92
|
+
context.facts.status = "loaded";
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Zustand → Directive
|
|
100
|
+
|
|
101
|
+
| Zustand | Directive | Notes |
|
|
102
|
+
|---------|-----------|-------|
|
|
103
|
+
| `create()` | `createModule()` | Module is more structured |
|
|
104
|
+
| State object | Schema + init | Explicit types with `t.*()` |
|
|
105
|
+
| Actions (set) | Direct mutation | `system.facts.x = y` |
|
|
106
|
+
| Selectors | Derivations | Auto-tracked |
|
|
107
|
+
| Middleware | Plugins | Built-in logging, devtools |
|
|
108
|
+
| `useStore` | `useSelector` | From React adapter |
|
|
109
|
+
|
|
110
|
+
### Migration Pattern
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
// BEFORE: Zustand
|
|
114
|
+
const useStore = create((set) => ({
|
|
115
|
+
bears: 0,
|
|
116
|
+
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
|
|
117
|
+
removeAllBears: () => set({ bears: 0 }),
|
|
118
|
+
}));
|
|
119
|
+
|
|
120
|
+
// AFTER: Directive
|
|
121
|
+
const bearStore = createModule("bear-store", {
|
|
122
|
+
schema: { bears: t.number() },
|
|
123
|
+
init: (facts) => { facts.bears = 0; },
|
|
124
|
+
});
|
|
125
|
+
const system = createSystem({ module: bearStore });
|
|
126
|
+
|
|
127
|
+
// Actions become direct mutations:
|
|
128
|
+
system.facts.bears += 1; // increasePopulation
|
|
129
|
+
system.facts.bears = 0; // removeAllBears
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## XState → Directive
|
|
133
|
+
|
|
134
|
+
| XState | Directive | Notes |
|
|
135
|
+
|--------|-----------|-------|
|
|
136
|
+
| Machine | Module | States → facts, transitions → constraints |
|
|
137
|
+
| States | Fact values | `status: t.string<"idle" \| "loading">()` |
|
|
138
|
+
| Context | Facts | All state in schema |
|
|
139
|
+
| Events | Events / Requirements | `dispatch()` or auto-triggered |
|
|
140
|
+
| Guards | Constraint `when()` | Predicate functions |
|
|
141
|
+
| Actions | Resolver `resolve()` | Async handlers |
|
|
142
|
+
| Services | Resolvers | With retry policies |
|
|
143
|
+
| `useMachine` | `useSystem` | React adapter |
|
|
144
|
+
|
|
145
|
+
### Migration Pattern
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
// BEFORE: XState
|
|
149
|
+
const toggleMachine = createMachine({
|
|
150
|
+
id: "toggle",
|
|
151
|
+
initial: "inactive",
|
|
152
|
+
states: {
|
|
153
|
+
inactive: { on: { TOGGLE: "active" } },
|
|
154
|
+
active: { on: { TOGGLE: "inactive" } },
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// AFTER: Directive
|
|
159
|
+
const toggle = createModule("toggle", {
|
|
160
|
+
schema: {
|
|
161
|
+
status: t.string<"active" | "inactive">(),
|
|
162
|
+
},
|
|
163
|
+
init: (facts) => { facts.status = "inactive"; },
|
|
164
|
+
events: {
|
|
165
|
+
TOGGLE: (facts) => {
|
|
166
|
+
facts.status = facts.status === "active" ? "inactive" : "active";
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
const system = createSystem({ module: toggle });
|
|
171
|
+
|
|
172
|
+
// Trigger: system.dispatch({ type: "TOGGLE" });
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## MobX → Directive
|
|
176
|
+
|
|
177
|
+
| MobX | Directive | Notes |
|
|
178
|
+
|------|-----------|-------|
|
|
179
|
+
| `observable` | Facts (schema) | Proxy-based, similar feel |
|
|
180
|
+
| `computed` | Derivations | Auto-tracked in both |
|
|
181
|
+
| `action` | Events / direct mutation | Both work |
|
|
182
|
+
| `reaction` | Constraints | When/require pattern |
|
|
183
|
+
| `autorun` | Effects | Fire-and-forget |
|
|
184
|
+
|
|
185
|
+
## Recoil / Jotai → Directive
|
|
186
|
+
|
|
187
|
+
| Recoil/Jotai | Directive | Notes |
|
|
188
|
+
|--------------|-----------|-------|
|
|
189
|
+
| Atoms | Facts | Individual state units |
|
|
190
|
+
| Selectors/Derived atoms | Derivations | Auto-tracked |
|
|
191
|
+
| `useRecoilValue` | `useSelector` | React adapter |
|
|
192
|
+
| Async selectors | Constraint + Resolver | Explicit async pattern |
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Migration Steps
|
|
197
|
+
|
|
198
|
+
## Step-by-Step Process
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
1. Identify state shape
|
|
202
|
+
→ Map to schema with t.*() type builders
|
|
203
|
+
→ Every state field → fact
|
|
204
|
+
|
|
205
|
+
2. Identify computed values
|
|
206
|
+
→ Map to derive {} block
|
|
207
|
+
→ Auto-tracked, no dependency arrays needed
|
|
208
|
+
|
|
209
|
+
3. Identify synchronous actions
|
|
210
|
+
→ Direct mutations: system.facts.x = y
|
|
211
|
+
→ Or events: events: { ACTION_NAME: (facts, payload) => { ... } }
|
|
212
|
+
|
|
213
|
+
4. Identify async operations
|
|
214
|
+
→ Constraint: when should this async work trigger?
|
|
215
|
+
→ Resolver: how to fulfill the requirement?
|
|
216
|
+
→ Add retry policies for network calls
|
|
217
|
+
|
|
218
|
+
5. Identify side effects
|
|
219
|
+
→ Map to effects {} block
|
|
220
|
+
→ Logging, analytics, DOM sync
|
|
221
|
+
|
|
222
|
+
6. Identify middleware
|
|
223
|
+
→ Map to plugins [] array
|
|
224
|
+
→ Use built-in: loggingPlugin, devtoolsPlugin, persistencePlugin
|
|
225
|
+
|
|
226
|
+
7. Update React components
|
|
227
|
+
→ Replace useSelector/useStore with Directive's useSelector
|
|
228
|
+
→ Replace dispatch with direct mutation or system.dispatch
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Key Differences to Explain
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
Redux: Action → Reducer → New State (immutable)
|
|
235
|
+
Directive: Mutation → Auto-constraint-check → Auto-resolve (mutable proxy)
|
|
236
|
+
|
|
237
|
+
Zustand: set() callback → New state
|
|
238
|
+
Directive: Direct mutation → Reactive updates
|
|
239
|
+
|
|
240
|
+
XState: Event → Guard → Transition → Action
|
|
241
|
+
Directive: Fact change → Constraint when() → Requirement → Resolver
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Reference Files
|
|
245
|
+
|
|
246
|
+
Supporting knowledge files loaded with this skill:
|
|
247
|
+
- `core-patterns.md` – Directive patterns reference
|
|
248
|
+
- `schema-types.md` – Type builder reference for migration
|
|
249
|
+
- `anti-patterns.md` – Common mistakes when migrating
|
|
@@ -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
|