@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,273 @@
|
|
|
1
|
+
# Schema Types
|
|
2
|
+
|
|
3
|
+
> Covers `@directive-run/core` — schema builders (`t.string` / `t.number` / `t.array` / etc.) and inference helpers.
|
|
4
|
+
|
|
5
|
+
The `t.*()` builders define fact types in module schemas. They provide runtime validation in dev mode and full TypeScript inference.
|
|
6
|
+
|
|
7
|
+
## Decision Tree: "Which type builder do I use?"
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
What kind of value?
|
|
11
|
+
├── String → t.string() or t.string<"a" | "b">() for literal unions
|
|
12
|
+
├── Number → t.number() with optional .min() / .max()
|
|
13
|
+
├── Boolean → t.boolean()
|
|
14
|
+
├── Array → t.array<ItemType>() with optional .of() / .nonEmpty()
|
|
15
|
+
├── Object/Record → t.object<Shape>()
|
|
16
|
+
├── Fixed set of string values → t.enum("a", "b", "c")
|
|
17
|
+
├── Exact value → t.literal(42) or t.literal("active")
|
|
18
|
+
├── Nullable → t.nullable(t.string())
|
|
19
|
+
├── Optional → t.optional(t.number())
|
|
20
|
+
├── Union → t.union(t.string(), t.number())
|
|
21
|
+
├── Map/Set → t.object<Map<K,V>>() or t.object<Set<T>>()
|
|
22
|
+
├── Date → t.object<Date>() or t.number() for timestamps
|
|
23
|
+
├── Unknown/any → t.object<unknown>()
|
|
24
|
+
└── Any type + metadata → t.string().meta({ label: "Email", tags: ["pii"] })
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
All builders support `.meta({ label, description, category, color, tags })` for devtools/AI annotations. Chains with other methods: `t.string().meta({...}).nullable()`.
|
|
28
|
+
|
|
29
|
+
## Primitive Types
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { createModule, t } from "@directive-run/core";
|
|
33
|
+
|
|
34
|
+
const myModule = createModule("example", {
|
|
35
|
+
schema: {
|
|
36
|
+
facts: {
|
|
37
|
+
// Basic string
|
|
38
|
+
name: t.string(),
|
|
39
|
+
|
|
40
|
+
// String literal union – full type safety
|
|
41
|
+
phase: t.string<"idle" | "loading" | "done">(),
|
|
42
|
+
|
|
43
|
+
// Number with validation
|
|
44
|
+
count: t.number(),
|
|
45
|
+
age: t.number().min(0).max(150),
|
|
46
|
+
|
|
47
|
+
// Boolean
|
|
48
|
+
isActive: t.boolean(),
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
init: (facts) => {
|
|
52
|
+
facts.name = "";
|
|
53
|
+
facts.phase = "idle";
|
|
54
|
+
facts.count = 0;
|
|
55
|
+
facts.age = 25;
|
|
56
|
+
facts.isActive = false;
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Complex Types
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
schema: {
|
|
65
|
+
facts: {
|
|
66
|
+
// Object with shape
|
|
67
|
+
user: t.object<{ id: string; name: string; email: string }>(),
|
|
68
|
+
|
|
69
|
+
// Nullable object
|
|
70
|
+
profile: t.object<{ bio: string; avatar: string } | null>(),
|
|
71
|
+
|
|
72
|
+
// Array
|
|
73
|
+
tags: t.array<string>(),
|
|
74
|
+
items: t.array<{ id: string; label: string }>().nonEmpty(),
|
|
75
|
+
|
|
76
|
+
// Record (key-value map)
|
|
77
|
+
scores: t.object<Record<string, number>>(),
|
|
78
|
+
|
|
79
|
+
// Nested complex type
|
|
80
|
+
config: t.object<{
|
|
81
|
+
theme: "light" | "dark";
|
|
82
|
+
notifications: { email: boolean; push: boolean };
|
|
83
|
+
}>(),
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Enum and Literal
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
schema: {
|
|
92
|
+
facts: {
|
|
93
|
+
// Enum – string literal union from values
|
|
94
|
+
status: t.enum("pending", "active", "archived"),
|
|
95
|
+
// TypeScript type: "pending" | "active" | "archived"
|
|
96
|
+
|
|
97
|
+
// Literal – exact match
|
|
98
|
+
version: t.literal(2),
|
|
99
|
+
mode: t.literal("strict"),
|
|
100
|
+
enabled: t.literal(true),
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Nullable and Optional
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
schema: {
|
|
109
|
+
facts: {
|
|
110
|
+
// Nullable – T | null
|
|
111
|
+
selectedItem: t.nullable(t.string()),
|
|
112
|
+
|
|
113
|
+
// Optional – T | undefined
|
|
114
|
+
nickname: t.optional(t.string()),
|
|
115
|
+
|
|
116
|
+
// Union – combine types
|
|
117
|
+
result: t.union(t.string(), t.number()),
|
|
118
|
+
|
|
119
|
+
// Nullable via object generic (also valid)
|
|
120
|
+
user: t.object<{ id: string } | null>(),
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Chainable Methods (Available on All Types)
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
schema: {
|
|
129
|
+
facts: {
|
|
130
|
+
// Default value – used if init doesn't set it
|
|
131
|
+
theme: t.string<"light" | "dark">().default("light"),
|
|
132
|
+
|
|
133
|
+
// Custom validation – runs in dev mode
|
|
134
|
+
email: t.string().validate((val) => val.includes("@")),
|
|
135
|
+
|
|
136
|
+
// Transform on set – runs when fact is mutated
|
|
137
|
+
slug: t.string().transform((val) => val.toLowerCase().replace(/\s+/g, "-")),
|
|
138
|
+
|
|
139
|
+
// Branded type – nominal typing
|
|
140
|
+
userId: t.string().brand<"UserId">(),
|
|
141
|
+
|
|
142
|
+
// Description – for docs and devtools
|
|
143
|
+
retryCount: t.number().describe("Number of failed attempts"),
|
|
144
|
+
|
|
145
|
+
// Refinement – predicate with error message
|
|
146
|
+
port: t.number().refine(
|
|
147
|
+
(n) => n >= 1 && n <= 65535,
|
|
148
|
+
"Port must be between 1 and 65535",
|
|
149
|
+
),
|
|
150
|
+
|
|
151
|
+
// Chaining – combine multiple modifiers
|
|
152
|
+
score: t.number()
|
|
153
|
+
.min(0)
|
|
154
|
+
.max(100)
|
|
155
|
+
.default(0)
|
|
156
|
+
.describe("Player score"),
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Array-Specific Methods
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
schema: {
|
|
165
|
+
facts: {
|
|
166
|
+
// Basic array
|
|
167
|
+
items: t.array<string>(),
|
|
168
|
+
|
|
169
|
+
// Non-empty validation
|
|
170
|
+
requiredItems: t.array<string>().nonEmpty(),
|
|
171
|
+
|
|
172
|
+
// Length constraints
|
|
173
|
+
topFive: t.array<string>().maxLength(5),
|
|
174
|
+
atLeastThree: t.array<number>().minLength(3),
|
|
175
|
+
|
|
176
|
+
// Combined
|
|
177
|
+
tags: t.array<string>().nonEmpty().maxLength(10),
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Object-Specific Methods
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
schema: {
|
|
186
|
+
facts: {
|
|
187
|
+
// Non-null assertion
|
|
188
|
+
config: t.object<{ url: string }>().nonNull(),
|
|
189
|
+
|
|
190
|
+
// Required keys validation
|
|
191
|
+
settings: t.object<Record<string, unknown>>().hasKeys("apiUrl", "timeout"),
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Types That DO NOT Exist
|
|
197
|
+
|
|
198
|
+
These are common AI hallucinations. Do not use them.
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
// WRONG – t.map() does not exist
|
|
202
|
+
items: t.map<string, number>(),
|
|
203
|
+
// CORRECT
|
|
204
|
+
items: t.object<Map<string, number>>(),
|
|
205
|
+
|
|
206
|
+
// WRONG – t.set() does not exist
|
|
207
|
+
tags: t.set<string>(),
|
|
208
|
+
// CORRECT
|
|
209
|
+
tags: t.object<Set<string>>(),
|
|
210
|
+
|
|
211
|
+
// WRONG – t.date() does not exist
|
|
212
|
+
createdAt: t.date(),
|
|
213
|
+
// CORRECT
|
|
214
|
+
createdAt: t.object<Date>(),
|
|
215
|
+
// OR use timestamps
|
|
216
|
+
createdAt: t.number(), // Unix ms
|
|
217
|
+
|
|
218
|
+
// WRONG – t.tuple() does not exist
|
|
219
|
+
coords: t.tuple<[number, number]>(),
|
|
220
|
+
// CORRECT
|
|
221
|
+
coords: t.array<[number, number]>(),
|
|
222
|
+
|
|
223
|
+
// WRONG – t.record() does not exist
|
|
224
|
+
scores: t.record<string, number>(),
|
|
225
|
+
// CORRECT
|
|
226
|
+
scores: t.object<Record<string, number>>(),
|
|
227
|
+
|
|
228
|
+
// WRONG – t.promise() does not exist (facts are synchronous)
|
|
229
|
+
result: t.promise<string>(),
|
|
230
|
+
|
|
231
|
+
// WRONG – t.any() does not exist
|
|
232
|
+
data: t.any(),
|
|
233
|
+
// CORRECT
|
|
234
|
+
data: t.object<unknown>(),
|
|
235
|
+
|
|
236
|
+
// WRONG – t.void() does not exist (not a fact type)
|
|
237
|
+
// WRONG – t.function() does not exist (functions are not serializable)
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Type Assertion Alternative
|
|
241
|
+
|
|
242
|
+
For simple modules, you can skip `t.*()` and use TypeScript type assertions:
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
const myModule = createModule("simple", {
|
|
246
|
+
schema: {
|
|
247
|
+
facts: {} as {
|
|
248
|
+
count: number;
|
|
249
|
+
name: string;
|
|
250
|
+
items: string[];
|
|
251
|
+
},
|
|
252
|
+
derivations: {} as {
|
|
253
|
+
doubled: number;
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
init: (facts) => {
|
|
257
|
+
facts.count = 0;
|
|
258
|
+
facts.name = "";
|
|
259
|
+
facts.items = [];
|
|
260
|
+
},
|
|
261
|
+
derive: {
|
|
262
|
+
doubled: (facts) => facts.count * 2,
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
This gives full TypeScript inference but skips runtime validation. Use `t.*()` when you want dev-mode validation, transforms, or self-documenting schemas.
|
|
268
|
+
|
|
269
|
+
## See also
|
|
270
|
+
|
|
271
|
+
- [`core-patterns.md`](./core-patterns.md) — where the schemas these builders define get attached (modules)
|
|
272
|
+
- [`system-api.md`](./system-api.md) — schema flows through `createSystem` and shows up on `system.facts.*`
|
|
273
|
+
- [`anti-patterns.md`](./anti-patterns.md) — hallucinated schema types (`t.map`, `t.set`, `t.date`, `t.promise`, `t.tuple`, `t.record`, `t.any`) that don't exist
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: reviewing-directive-code
|
|
3
|
+
description: "Review Directive code for anti-patterns, naming violations, missing error boundaries, constraint/resolver misuse, and performance issues. Use when asked to review, audit, or improve existing Directive modules, systems, or orchestrators."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Reviewing Directive Code
|
|
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 Triggers
|
|
15
|
+
- User asks to "review my Directive code" or "audit this module"
|
|
16
|
+
- User asks "is this the right pattern" or "am I doing this correctly"
|
|
17
|
+
- User wants a code review of constraint/resolver/module code
|
|
18
|
+
- User asks about Directive best practices for existing code
|
|
19
|
+
- User suspects they have anti-patterns or performance issues
|
|
20
|
+
|
|
21
|
+
### Exclusions – Use a Different Skill
|
|
22
|
+
- User wants to write NEW code from scratch → `writing-directive-modules`
|
|
23
|
+
- User wants to write tests → `testing-directive-code`
|
|
24
|
+
- User wants to migrate FROM another library → `migrating-to-directive`
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Review Checklist
|
|
29
|
+
|
|
30
|
+
## 1. Module Structure
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
✓ Module name is kebab-case
|
|
34
|
+
✓ Schema keys are camelCase
|
|
35
|
+
✓ All facts have explicit type builders (t.string(), t.number(), etc.)
|
|
36
|
+
✓ init() sets ALL schema keys (no undefined facts)
|
|
37
|
+
✓ No business logic in init() – just defaults
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Anti-Patterns
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
// BAD: Missing type builder
|
|
44
|
+
schema: { count: 0 }
|
|
45
|
+
// GOOD: Explicit type builder
|
|
46
|
+
schema: { count: t.number() }
|
|
47
|
+
|
|
48
|
+
// BAD: Business logic in init
|
|
49
|
+
init: (facts) => {
|
|
50
|
+
facts.count = localStorage.getItem("count") ?? 0;
|
|
51
|
+
}
|
|
52
|
+
// GOOD: Use a resolver for async/side-effect init
|
|
53
|
+
init: (facts) => {
|
|
54
|
+
facts.count = 0;
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## 2. Derivations
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
✓ Derivations are pure functions (no side effects)
|
|
62
|
+
✓ No mutations inside derive functions
|
|
63
|
+
✓ Using derive-to-derive composition correctly (via second arg)
|
|
64
|
+
✓ Not duplicating fact values (derive should compute, not mirror)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Anti-Patterns
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
// BAD: Side effect in derivation
|
|
71
|
+
derive: {
|
|
72
|
+
status: (facts) => {
|
|
73
|
+
console.log("computing status"); // side effect!
|
|
74
|
+
return facts.isReady ? "ready" : "loading";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// BAD: Mirroring a fact (useless derivation)
|
|
79
|
+
derive: {
|
|
80
|
+
currentCount: (facts) => facts.count // just use facts.count
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// GOOD: Meaningful computation
|
|
84
|
+
derive: {
|
|
85
|
+
isOverBudget: (facts) => facts.spent > facts.budget,
|
|
86
|
+
budgetStatus: (facts, derived) => derived.isOverBudget ? "over" : "under",
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## 3. Constraints
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
✓ when() is a pure predicate (no side effects)
|
|
94
|
+
✓ require returns a typed requirement with UPPER_SNAKE_CASE type
|
|
95
|
+
✓ Async constraints have explicit deps: []
|
|
96
|
+
✓ Priority is set when multiple constraints may conflict
|
|
97
|
+
✓ No redundant constraints (check if constraint already exists)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Anti-Patterns
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
// BAD: Side effect in when()
|
|
104
|
+
when: (facts) => {
|
|
105
|
+
analytics.track("constraint-checked"); // side effect!
|
|
106
|
+
return facts.count > 10;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// BAD: Requirement type not UPPER_SNAKE_CASE
|
|
110
|
+
require: { type: "fetchData" }
|
|
111
|
+
// GOOD:
|
|
112
|
+
require: { type: "FETCH_DATA" }
|
|
113
|
+
|
|
114
|
+
// BAD: Async constraint without deps
|
|
115
|
+
constraints: {
|
|
116
|
+
check: {
|
|
117
|
+
async: true,
|
|
118
|
+
when: async (facts) => await validate(facts.input),
|
|
119
|
+
require: { type: "HANDLE_INVALID" },
|
|
120
|
+
// Missing: deps: ["input"]
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## 4. Resolvers
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
✓ Resolver params use (req, context) – not (req, ctx)
|
|
129
|
+
✓ Resolver handles errors (try/catch or error boundaries)
|
|
130
|
+
✓ Deduplication key set for idempotent requirements
|
|
131
|
+
✓ Retry policy configured for network/external calls
|
|
132
|
+
✓ Not doing work that belongs in a constraint or effect
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Anti-Patterns
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
// BAD: Abbreviated context parameter
|
|
139
|
+
resolve: async (req, ctx) => { ... }
|
|
140
|
+
// GOOD:
|
|
141
|
+
resolve: async (req, context) => { ... }
|
|
142
|
+
|
|
143
|
+
// BAD: No error handling for external calls
|
|
144
|
+
resolve: async (req, context) => {
|
|
145
|
+
const data = await fetch("/api/data");
|
|
146
|
+
context.facts.data = await data.json();
|
|
147
|
+
}
|
|
148
|
+
// GOOD: With retry
|
|
149
|
+
resolvers: {
|
|
150
|
+
fetchData: {
|
|
151
|
+
requirement: "FETCH_DATA",
|
|
152
|
+
retry: { attempts: 3, backoff: "exponential" },
|
|
153
|
+
resolve: async (req, context) => {
|
|
154
|
+
const data = await fetch("/api/data");
|
|
155
|
+
context.facts.data = await data.json();
|
|
156
|
+
},
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// BAD: Resolver checking conditions (that's a constraint's job)
|
|
161
|
+
resolve: async (req, context) => {
|
|
162
|
+
if (context.facts.isReady) {
|
|
163
|
+
// do something
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## 5. Effects
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
✓ Effects are fire-and-forget (not resolving requirements)
|
|
172
|
+
✓ Effects have cleanup functions for subscriptions
|
|
173
|
+
✓ Not mutating critical state in effects (use resolvers instead)
|
|
174
|
+
✓ Prev parameter used for change detection
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## 6. System Composition
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
✓ Multi-module: crossModuleDeps declared for cross-module constraints
|
|
181
|
+
✓ No circular dependencies between modules
|
|
182
|
+
✓ Plugins added at system level, not module level
|
|
183
|
+
✓ Single module uses { module: x }, multi uses { modules: { a, b } }
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## 7. Performance Review
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
✓ Derivations don't do expensive computation on every change
|
|
190
|
+
✓ Constraints are not overly broad (when() triggers on minimal deps)
|
|
191
|
+
✓ Resolvers use deduplication keys to prevent duplicate work
|
|
192
|
+
✓ Batch resolvers used for N+1 scenarios
|
|
193
|
+
✓ Effects don't trigger cascading mutations
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## 8. Naming Convention Audit
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
Module names: kebab-case ("user-auth", "shopping-cart")
|
|
200
|
+
Fact keys: camelCase (isLoggedIn, userName)
|
|
201
|
+
Derivation keys: camelCase (canEdit, totalPrice)
|
|
202
|
+
Constraint keys: camelCase (enforceAuth, validateInput)
|
|
203
|
+
Resolver keys: camelCase (fetchProfile, submitOrder)
|
|
204
|
+
Requirement type: UPPER_SNAKE_CASE (FETCH_PROFILE, SUBMIT_ORDER)
|
|
205
|
+
Event types: UPPER_SNAKE_CASE (USER_CLICKED, FORM_SUBMITTED)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Review Output Format
|
|
209
|
+
|
|
210
|
+
When reviewing, output findings as:
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
## Review: [module-name]
|
|
214
|
+
|
|
215
|
+
### Issues Found
|
|
216
|
+
| Severity | Issue | Location |
|
|
217
|
+
|----------|-------|----------|
|
|
218
|
+
| Critical | ... | file:line |
|
|
219
|
+
| Major | ... | file:line |
|
|
220
|
+
| Minor | ... | file:line |
|
|
221
|
+
|
|
222
|
+
### Recommendations
|
|
223
|
+
1. ...
|
|
224
|
+
2. ...
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Reference Files
|
|
228
|
+
|
|
229
|
+
Supporting knowledge files loaded with this skill:
|
|
230
|
+
- `anti-patterns.md` – Full anti-pattern catalog with fixes
|
|
231
|
+
- `core-patterns.md` – Correct patterns to recommend
|
|
232
|
+
- `naming.md` – Naming conventions reference
|