@baldurpan/create-ai-workflow 0.2.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 (116) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +165 -0
  3. package/dist/agents-block.js +70 -0
  4. package/dist/bin.js +14 -0
  5. package/dist/check/markdown.js +47 -0
  6. package/dist/check/parse.js +113 -0
  7. package/dist/check/rules.js +245 -0
  8. package/dist/cli.js +109 -0
  9. package/dist/commands/check.js +41 -0
  10. package/dist/commands/install.js +76 -0
  11. package/dist/commands/standards-add.js +145 -0
  12. package/dist/commands/update.js +214 -0
  13. package/dist/layout.js +115 -0
  14. package/dist/log.js +20 -0
  15. package/dist/manifest.js +53 -0
  16. package/dist/paths.js +41 -0
  17. package/package.json +37 -0
  18. package/templates/blocks/agents-block.md +30 -0
  19. package/templates/claude/agents/planner.agent.md +60 -0
  20. package/templates/claude/agents/reviewer.agent.md +77 -0
  21. package/templates/context/README.md +81 -0
  22. package/templates/context/plan-template.md +69 -0
  23. package/templates/context/plan-template.notes.md +55 -0
  24. package/templates/context/roles/coder.md +76 -0
  25. package/templates/context/workflow.md +143 -0
  26. package/templates/skills/feature-close/SKILL.md +102 -0
  27. package/templates/skills/feature-implement/SKILL.md +142 -0
  28. package/templates/skills/feature-plan/SKILL.md +138 -0
  29. package/templates/skills/feature-status/SKILL.md +86 -0
  30. package/templates/skills/onboard/SKILL.md +122 -0
  31. package/templates/skills/orchestrate/SKILL.md +92 -0
  32. package/templates/skills/roadmap/SKILL.md +97 -0
  33. package/templates/standards/.source +8 -0
  34. package/templates/standards/README.md +104 -0
  35. package/templates/standards/architecture/api-design.md +212 -0
  36. package/templates/standards/architecture/dependency-boundaries.md +104 -0
  37. package/templates/standards/architecture/feature-driven.md +102 -0
  38. package/templates/standards/architecture/folder-structure.md +108 -0
  39. package/templates/standards/architecture/monorepos.md +107 -0
  40. package/templates/standards/architecture/refactoring.md +97 -0
  41. package/templates/standards/architecture/shared-code.md +81 -0
  42. package/templates/standards/docs/PHP-SPEC.md +1195 -0
  43. package/templates/standards/docs/SPEC.md +540 -0
  44. package/templates/standards/examples/bad/data-fetching.tsx +52 -0
  45. package/templates/standards/examples/bad/derived-state.tsx +60 -0
  46. package/templates/standards/examples/bad/feature-component.tsx +80 -0
  47. package/templates/standards/examples/bad/prop-drilling.tsx +70 -0
  48. package/templates/standards/examples/good/data-fetching.tsx +53 -0
  49. package/templates/standards/examples/good/derived-state.tsx +52 -0
  50. package/templates/standards/examples/good/feature-component.tsx +141 -0
  51. package/templates/standards/examples/good/invoice.schema.ts +20 -0
  52. package/templates/standards/examples/good/prop-drilling.tsx +83 -0
  53. package/templates/standards/philosophy/ai-agent-behavior.md +93 -0
  54. package/templates/standards/philosophy/core-principles.md +49 -0
  55. package/templates/standards/philosophy/incremental-abstraction.md +56 -0
  56. package/templates/standards/philosophy/maintainability.md +66 -0
  57. package/templates/standards/philosophy/readability.md +87 -0
  58. package/templates/standards/php/README.md +62 -0
  59. package/templates/standards/php/anti-patterns.md +259 -0
  60. package/templates/standards/php/architecture.md +241 -0
  61. package/templates/standards/php/frameworks.md +197 -0
  62. package/templates/standards/php/rules.md +205 -0
  63. package/templates/standards/php/security.md +190 -0
  64. package/templates/standards/php/testing.md +139 -0
  65. package/templates/standards/php/tooling.md +245 -0
  66. package/templates/standards/react/accessibility.md +130 -0
  67. package/templates/standards/react/anti-patterns.md +187 -0
  68. package/templates/standards/react/component-design.md +139 -0
  69. package/templates/standards/react/error-boundaries.md +195 -0
  70. package/templates/standards/react/forms.md +147 -0
  71. package/templates/standards/react/hooks.md +88 -0
  72. package/templates/standards/react/memoization.md +87 -0
  73. package/templates/standards/react/state-management.md +123 -0
  74. package/templates/standards/react/testing.md +118 -0
  75. package/templates/standards/react/use-effect.md +110 -0
  76. package/templates/standards/security/api-security.md +136 -0
  77. package/templates/standards/security/auth.md +112 -0
  78. package/templates/standards/security/secrets.md +105 -0
  79. package/templates/standards/security/validation.md +89 -0
  80. package/templates/standards/templates/.editorconfig +16 -0
  81. package/templates/standards/templates/.nvmrc +1 -0
  82. package/templates/standards/templates/AGENTS.md +46 -0
  83. package/templates/standards/templates/CLAUDE.md +3 -0
  84. package/templates/standards/templates/_dot_gitignore +50 -0
  85. package/templates/standards/templates/biome.json +53 -0
  86. package/templates/standards/templates/eslint.config.js +61 -0
  87. package/templates/standards/templates/project-readme.md +76 -0
  88. package/templates/standards/templates/pull-request-template.md +46 -0
  89. package/templates/standards/templates/tsconfig.json +35 -0
  90. package/templates/standards/tooling/biome.md +92 -0
  91. package/templates/standards/tooling/ci.md +128 -0
  92. package/templates/standards/tooling/dates.md +194 -0
  93. package/templates/standards/tooling/dependencies.md +90 -0
  94. package/templates/standards/tooling/nx.md +113 -0
  95. package/templates/standards/tooling/observability.md +183 -0
  96. package/templates/standards/tooling/prisma.md +152 -0
  97. package/templates/standards/tooling/shadcn.md +115 -0
  98. package/templates/standards/tooling/tailwind.md +135 -0
  99. package/templates/standards/tooling/tanstack-query.md +214 -0
  100. package/templates/standards/tooling/tanstack-router.md +365 -0
  101. package/templates/standards/tooling/tanstack.md +31 -0
  102. package/templates/standards/tooling/vite.md +135 -0
  103. package/templates/standards/typescript/anti-patterns.md +107 -0
  104. package/templates/standards/typescript/error-handling.md +216 -0
  105. package/templates/standards/typescript/naming.md +78 -0
  106. package/templates/standards/typescript/rules.md +159 -0
  107. package/templates/standards/typescript/tsconfig/base.json +32 -0
  108. package/templates/standards/typescript/tsconfig/nextjs.json +12 -0
  109. package/templates/standards/typescript/tsconfig/react.json +9 -0
  110. package/templates/standards/typescript/validation.md +104 -0
  111. package/templates/stubs/executors.md +49 -0
  112. package/templates/stubs/findings.md +40 -0
  113. package/templates/stubs/history.md +13 -0
  114. package/templates/stubs/roadmap.md +29 -0
  115. package/templates/stubs/stack.md +35 -0
  116. package/templates/stubs/verify.md +48 -0
@@ -0,0 +1,216 @@
1
+ # Error Handling
2
+
3
+ ## Core Rules
4
+
5
+ - Never swallow errors silently
6
+ - Never hide the original error context
7
+ - Never replace a specific error with a generic one
8
+ - Preserve HTTP status codes and original payloads
9
+ - Validate inputs before state transitions
10
+
11
+ ## DO
12
+
13
+ ```ts
14
+ // Preserve original error context
15
+ try {
16
+ await db.user.create({ data });
17
+ } catch (error) {
18
+ throw new DatabaseError("Failed to create user", { cause: error });
19
+ }
20
+
21
+ // Use custom error classes for categorized errors
22
+ class NotFoundError extends Error {
23
+ readonly statusCode = 404;
24
+ constructor(resource: string, id: string) {
25
+ super(`${resource} with id '${id}' not found`);
26
+ this.name = "NotFoundError";
27
+ }
28
+ }
29
+
30
+ // Log with context, not just a message
31
+ logger.error("Invoice creation failed", {
32
+ userId,
33
+ invoiceData,
34
+ error: err.message,
35
+ });
36
+ ```
37
+
38
+ ## DO NOT
39
+
40
+ ```ts
41
+ // BAD — swallows the error
42
+ try {
43
+ await riskyOperation();
44
+ } catch {
45
+ // silent
46
+ }
47
+
48
+ // BAD — loses original context
49
+ try {
50
+ await riskyOperation();
51
+ } catch {
52
+ throw new Error("Something went wrong");
53
+ }
54
+
55
+ // BAD — catches too broadly
56
+ try {
57
+ const user = await fetchUser(id);
58
+ const profile = await fetchProfile(user.profileId);
59
+ const invoices = await fetchInvoices(user.id);
60
+ } catch (error) {
61
+ // which operation failed? unknown
62
+ console.error(error);
63
+ }
64
+ ```
65
+
66
+ ## Async Rules
67
+
68
+ ### Prefer async/await Over .then() Chains
69
+
70
+ For sequential async flow, `async/await` is far more readable than chained `.then().catch()`. Reserve `.then()` / `.catch()` for fire-and-forget side effects.
71
+
72
+ ```ts
73
+ // GOOD — linear, easy to follow, errors propagate naturally
74
+ async function loadDashboard(userId: string) {
75
+ const user = await fetchUser(userId);
76
+ const profile = await fetchProfile(user.profileId);
77
+ const invoices = await fetchInvoices(user.id);
78
+ return { user, profile, invoices };
79
+ }
80
+
81
+ // BAD — nested .then chains, harder to read and reason about
82
+ function loadDashboard(userId: string) {
83
+ return fetchUser(userId)
84
+ .then((user) =>
85
+ fetchProfile(user.profileId).then((profile) =>
86
+ fetchInvoices(user.id).then((invoices) => ({ user, profile, invoices })),
87
+ ),
88
+ )
89
+ .catch((error) => {
90
+ // which step failed? unclear
91
+ });
92
+ }
93
+ ```
94
+
95
+ ### Always Await or Return
96
+
97
+ ```ts
98
+ // GOOD
99
+ async function loadUser(id: string) {
100
+ const user = await fetchUser(id);
101
+ return user;
102
+ }
103
+
104
+ // BAD — floating promise, errors lost
105
+ async function loadUser(id: string) {
106
+ fetchUser(id); // unawaited
107
+ }
108
+ ```
109
+
110
+ ### Fire-and-Forget Operations
111
+
112
+ `.catch()` is appropriate when you intentionally don't await:
113
+
114
+ ```ts
115
+ sendAnalyticsEvent(payload).catch((error) => {
116
+ logger.warn("Analytics event failed", { error });
117
+ });
118
+
119
+ // If you genuinely don't care about errors, mark intent with `void`
120
+ void sendAnalyticsEvent(payload);
121
+ ```
122
+
123
+ ### Parallel Operations
124
+
125
+ Run independent async operations in parallel with `Promise.all`:
126
+
127
+ ```ts
128
+ // GOOD — parallel, faster
129
+ const [user, settings, invoices] = await Promise.all([
130
+ fetchUser(userId),
131
+ fetchSettings(userId),
132
+ fetchInvoices(userId),
133
+ ]);
134
+
135
+ // BAD — sequential when there's no dependency between calls
136
+ const user = await fetchUser(userId);
137
+ const settings = await fetchSettings(userId);
138
+ const invoices = await fetchInvoices(userId);
139
+ ```
140
+
141
+ Use `Promise.allSettled` when some failures are acceptable and you want all results regardless.
142
+
143
+ ## Custom Error Classes
144
+
145
+ ```ts
146
+ class AppError extends Error {
147
+ constructor(
148
+ message: string,
149
+ readonly statusCode: number,
150
+ options?: ErrorOptions,
151
+ ) {
152
+ super(message, options);
153
+ this.name = this.constructor.name;
154
+ }
155
+ }
156
+
157
+ class ValidationError extends AppError {
158
+ constructor(message: string, readonly fields: Record<string, string[]>) {
159
+ super(message, 422);
160
+ }
161
+ }
162
+
163
+ class NotFoundError extends AppError {
164
+ constructor(resource: string, id: string) {
165
+ super(`${resource} '${id}' not found`, 404);
166
+ }
167
+ }
168
+ ```
169
+
170
+ ## User-Facing Errors
171
+
172
+ Every user-facing error must:
173
+
174
+ 1. **Explain the failure** — what went wrong, specifically
175
+ 2. **Explain the next step** — what the user should do
176
+ 3. **Be actionable** — give the user something they can do
177
+
178
+ ```ts
179
+ // BAD
180
+ "Something went wrong. Please try again."
181
+
182
+ // GOOD
183
+ "We couldn't save your invoice. Check your internet connection and try again.
184
+ If the problem continues, contact support with reference #INV-1234."
185
+ ```
186
+
187
+ ## Result Pattern (Optional)
188
+
189
+ For operations where errors are expected and part of normal flow:
190
+
191
+ ```ts
192
+ type Result<T, E = Error> =
193
+ | { ok: true; value: T }
194
+ | { ok: false; error: E };
195
+
196
+ async function parseUser(raw: unknown): Promise<Result<User>> {
197
+ const result = userSchema.safeParse(raw);
198
+ if (!result.success) {
199
+ return { ok: false, error: new ValidationError(result.error.message) };
200
+ }
201
+ return { ok: true, value: result.data };
202
+ }
203
+ ```
204
+
205
+ ## PRIORITY
206
+
207
+ ```
208
+ Preserve context > Specific messages > Actionable user guidance
209
+ ```
210
+
211
+ ## See Also
212
+
213
+ - [`../react/error-boundaries.md`](../react/error-boundaries.md) — catching render-time errors
214
+ - [`../tooling/observability.md`](../tooling/observability.md) — structured logging, error monitoring
215
+ - [`../security/api-security.md`](../security/api-security.md) — safe error responses
216
+ - [`validation.md`](validation.md) — preventing errors via input validation
@@ -0,0 +1,78 @@
1
+ # Naming Conventions
2
+
3
+ ## Core Rule
4
+
5
+ Names should express intent clearly without requiring the reader to trace the code.
6
+
7
+ ## General Rules
8
+
9
+ | Pattern | Convention | Example |
10
+ |---|---|---|
11
+ | Variables | `camelCase` | `userProfile`, `isLoading` |
12
+ | Functions | `camelCase`, verb-first | `fetchUser`, `formatCurrency` |
13
+ | Types / Interfaces | `PascalCase` | `UserProfile`, `InvoiceLineItem` |
14
+ | Components | `PascalCase` | `InvoiceTable`, `UserAvatarGroup` |
15
+ | Hooks | `camelCase`, `use` prefix | `useScrollLock`, `useInvoiceSort` |
16
+ | Constants | `SCREAMING_SNAKE_CASE` for module-level literals | `MAX_RETRIES`, `API_BASE_URL` |
17
+ | Files | `kebab-case` for utilities, `PascalCase` for components | `format-currency.ts`, `InvoiceTable.tsx` |
18
+ | Enum-like objects | `SCREAMING_SNAKE_CASE` keys | `const STATUS = { Active: "ACTIVE" }` |
19
+
20
+ ## DO
21
+
22
+ - Use full words — no abbreviations unless universally understood (`id`, `url`, `api`)
23
+ - Include the entity in the name: `userId` not `id`, `selectedInvoice` not `selected`
24
+ - Prefix booleans: `isLoading`, `hasError`, `canSubmit`, `shouldFetch`
25
+ - Name async functions clearly: `fetchUser`, `loadDashboard`, not `getData`
26
+ - Name event handlers with `handle` prefix: `handleSubmit`, `handleRowClick`
27
+
28
+ ## DO NOT
29
+
30
+ - Abbreviate: `usr`, `mgr`, `btn`, `cfg`, `val`, `res`, `req`
31
+ - Use single letters except for loop indices (`i`, `j`) and type parameters (`T`, `K`)
32
+ - Name things by type: `userArray`, `stringValue`, `dataObject`
33
+ - Name things by position: `item1`, `item2`
34
+ - Use generic names: `handleStuff`, `doThing`, `myHelper`, `useData`, `processItem`
35
+
36
+ ## Generics
37
+
38
+ ```ts
39
+ // BAD — single letter gives no context
40
+ function first<T>(arr: T[]): T | undefined
41
+
42
+ // GOOD — descriptive when it matters
43
+ function mapById<TItem extends { id: string }>(items: TItem[]): Map<string, TItem>
44
+ ```
45
+
46
+ Use single-letter generics (`T`, `K`, `V`) only when the generic is truly unconstrained and the function is simple enough that context is obvious.
47
+
48
+ ## Component Props
49
+
50
+ Suffix prop type names with `Props`:
51
+
52
+ ```ts
53
+ // components/InvoiceTable.tsx
54
+ interface InvoiceTableProps {
55
+ invoices: Invoice[];
56
+ onRowClick: (id: string) => void;
57
+ isLoading?: boolean;
58
+ }
59
+ ```
60
+
61
+ ## Event Handlers
62
+
63
+ ```ts
64
+ // Component props — callback names
65
+ interface ButtonProps {
66
+ onClick: (event: MouseEvent) => void; // external API: "on" prefix
67
+ }
68
+
69
+ // Local handlers — "handle" prefix
70
+ function handleSubmit(event: FormEvent) { ... }
71
+ function handleRowClick(id: string) { ... }
72
+ ```
73
+
74
+ ## See Also
75
+
76
+ - [`../philosophy/readability.md`](../philosophy/readability.md) — names express intent
77
+ - [`rules.md`](rules.md) — explicit, descriptive types
78
+ - [`../react/component-design.md`](../react/component-design.md) — `<ComponentName>Props` interfaces
@@ -0,0 +1,159 @@
1
+ # TypeScript Rules
2
+
3
+ ## DO
4
+
5
+ - Enable `"strict": true` in all projects — no exceptions
6
+ - Prefer union types over enums: `type Status = "active" | "inactive"` not `enum Status`
7
+ - Prefer generated API types (from OpenAPI, tRPC, Prisma) over hand-written DTOs
8
+ - Use schema-driven validation (Zod) for all external data
9
+ - Use explicit, descriptive type names
10
+ - Prefer `interface` over `type` for object shapes. Use `type` only when interfaces cannot express what you need (unions, intersections, mapped/conditional types, primitive aliases)
11
+ - Prefer `unknown` over `any` for untyped external input
12
+ - Prefer `async/await` over `.then().catch()` chains for sequential async flow
13
+ - Use `satisfies` operator to validate against a type without widening
14
+ - Use `const` assertions for literal types where appropriate
15
+
16
+ ## DO NOT
17
+
18
+ - Use `any` — use `unknown` and narrow
19
+ - Use type assertions (`as SomeType`) unless interfacing with third-party types you cannot change
20
+ - Write duplicate DTOs for the same data shape (frontend + backend)
21
+ - Perform type gymnastics — if a type is hard to write, the underlying design may be wrong
22
+ - Use `// @ts-ignore` or `// @ts-expect-error` without a comment explaining why
23
+ - Use `namespace` or `module` declarations in new code
24
+
25
+ ## Function Declarations Over Arrow Expressions
26
+
27
+ Use `function` declarations for named, module-level, and exported functions (including React components). Use arrow functions for anonymous callbacks and inline handlers.
28
+
29
+ ```ts
30
+ // GOOD — named functions use declarations
31
+ function formatCurrency(value: number): string {
32
+ return new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(value);
33
+ }
34
+
35
+ export function InvoiceTable({ invoices }: InvoiceTableProps) {
36
+ return <table>...</table>;
37
+ }
38
+
39
+ // GOOD — anonymous arrows for callbacks and inline handlers
40
+ invoices.map((invoice) => invoice.amount);
41
+ invoices.filter((invoice) => invoice.status === "open");
42
+ <button onClick={() => setOpen(true)}>Open</button>
43
+
44
+ // BAD — arrow expression assigned to const for a named function
45
+ const formatCurrency = (value: number): string => { ... };
46
+ const InvoiceTable = ({ invoices }: InvoiceTableProps) => { ... };
47
+ ```
48
+
49
+ Reasoning:
50
+ - Function declarations are hoisted — usable before their definition in the file
51
+ - Named functions produce clearer stack traces
52
+ - `export function foo()` is easier to scan than `export const foo = () => ...`
53
+ - `this` binding is irrelevant for module-level functions
54
+
55
+ ## Interfaces Over Type Aliases
56
+
57
+ Prefer `interface` for object shapes. Use `type` only when `interface` cannot express what you need.
58
+
59
+ ```ts
60
+ // GOOD — interface for object shapes
61
+ interface User {
62
+ id: string;
63
+ name: string;
64
+ email: string;
65
+ }
66
+
67
+ interface InvoiceTableProps {
68
+ invoices: Invoice[];
69
+ onRowClick: (id: string) => void;
70
+ }
71
+
72
+ // GOOD — type for unions, intersections, mapped/conditional types, primitive aliases
73
+ type Status = "active" | "inactive" | "archived";
74
+ type UserId = string;
75
+ type WithTimestamps<T> = T & { createdAt: Date; updatedAt: Date };
76
+ type Nullable<T> = T | null;
77
+
78
+ // BAD — type for an object shape that interface would handle
79
+ type User = {
80
+ id: string;
81
+ name: string;
82
+ };
83
+ ```
84
+
85
+ Reasoning:
86
+ - Interfaces produce clearer error messages
87
+ - Interfaces are open for declaration merging (useful for extending third-party types)
88
+ - `extends` is more readable than intersection types
89
+ - `interface` signals "this is an object shape" — `type` signals "this could be anything"
90
+
91
+ ## Unions Over Enums
92
+
93
+ ```ts
94
+ // BAD
95
+ enum Direction {
96
+ North = "NORTH",
97
+ South = "SOUTH",
98
+ }
99
+
100
+ // GOOD
101
+ type Direction = "NORTH" | "SOUTH";
102
+
103
+ // GOOD — const object if you need runtime values
104
+ const DIRECTION = {
105
+ North: "NORTH",
106
+ South: "SOUTH",
107
+ } as const;
108
+ type Direction = (typeof DIRECTION)[keyof typeof DIRECTION];
109
+ ```
110
+
111
+ ## Narrowing Over Assertions
112
+
113
+ ```ts
114
+ // BAD
115
+ const value = data as User;
116
+
117
+ // GOOD
118
+ if (!isUser(data)) throw new Error("Expected User");
119
+ const value = data; // now narrowed to User
120
+ ```
121
+
122
+ ## Avoiding DTO Duplication
123
+
124
+ Prefer generating types from the API layer:
125
+
126
+ ```ts
127
+ // tRPC — types come from the router
128
+ // Prisma — types come from the schema
129
+ // OpenAPI — types generated from the spec
130
+ // Zod — infer types from schemas
131
+
132
+ const userSchema = z.object({ id: z.string(), name: z.string() });
133
+ type User = z.infer<typeof userSchema>; // single source of truth
134
+ ```
135
+
136
+ ## satisfies
137
+
138
+ ```ts
139
+ const theme = {
140
+ primary: "#0070f3",
141
+ secondary: "#1a1a1a",
142
+ } satisfies Record<string, string>;
143
+
144
+ theme.primary; // string — not widened to Record<string, string>
145
+ ```
146
+
147
+ ## PRIORITY
148
+
149
+ ```
150
+ Type safety > Brevity > Cleverness
151
+ ```
152
+
153
+ ## See Also
154
+
155
+ - [anti-patterns.md](anti-patterns.md)
156
+ - [validation.md](validation.md)
157
+ - [naming.md](naming.md)
158
+ - [error-handling.md](error-handling.md)
159
+ - [tsconfig/base.json](tsconfig/base.json)
@@ -0,0 +1,32 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "compilerOptions": {
4
+ "strict": true,
5
+ "noUnusedLocals": true,
6
+ "noUnusedParameters": true,
7
+ "noUncheckedIndexedAccess": true,
8
+ "noPropertyAccessFromIndexSignature": true,
9
+ "noFallthroughCasesInSwitch": true,
10
+ "allowUnreachableCode": false,
11
+ "allowUnusedLabels": false,
12
+ "noUncheckedSideEffectImports": true,
13
+ "erasableSyntaxOnly": true,
14
+ "exactOptionalPropertyTypes": true,
15
+ "forceConsistentCasingInFileNames": true,
16
+ "isolatedModules": true,
17
+ "skipLibCheck": true,
18
+ "moduleDetection": "force",
19
+ "target": "ES2022",
20
+ "lib": ["ES2022"],
21
+ "module": "ESNext",
22
+ "moduleResolution": "bundler",
23
+ "resolveJsonModule": true,
24
+ "declaration": true,
25
+ "declarationMap": true,
26
+ "sourceMap": true,
27
+ "paths": {
28
+ "~/*": ["./src/*"]
29
+ }
30
+ },
31
+ "exclude": ["node_modules", "dist", "build"]
32
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "extends": "./react.json",
4
+ "compilerOptions": {
5
+ "plugins": [{ "name": "next" }],
6
+ "moduleResolution": "bundler",
7
+ "allowJs": true,
8
+ "incremental": true
9
+ },
10
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
11
+ "exclude": ["node_modules"]
12
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "extends": "./base.json",
4
+ "compilerOptions": {
5
+ "jsx": "react-jsx",
6
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
7
+ "target": "ES2022"
8
+ }
9
+ }
@@ -0,0 +1,104 @@
1
+ # Validation
2
+
3
+ ## Core Rule
4
+
5
+ Validate all external data at system boundaries. Never trust raw input from users, APIs, or environment variables.
6
+
7
+ ## Preferred Library
8
+
9
+ **Zod** is the default choice.
10
+
11
+ **Valibot** is worth evaluating for bundle-size-sensitive contexts (significantly smaller than Zod).
12
+
13
+ ## Schema-First Approach
14
+
15
+ Define the schema once. Derive types from it. Share the schema across frontend and backend.
16
+
17
+ ```ts
18
+ // shared/schemas/user.schema.ts
19
+ import { z } from "zod";
20
+
21
+ export const userSchema = z.object({
22
+ id: z.string().uuid(),
23
+ name: z.string().min(1).max(100),
24
+ email: z.string().email(),
25
+ role: z.enum(["admin", "member", "viewer"]),
26
+ createdAt: z.coerce.date(),
27
+ });
28
+
29
+ export type User = z.infer<typeof userSchema>;
30
+ ```
31
+
32
+ ## Validate at Boundaries
33
+
34
+ ```ts
35
+ // API response — validate before using
36
+ const response = await fetch("/api/users/1");
37
+ const raw = await response.json();
38
+ const user = userSchema.parse(raw); // throws if invalid
39
+
40
+ // Form submission — validate before submitting
41
+ const formSchema = z.object({
42
+ name: z.string().min(1, "Name is required"),
43
+ email: z.string().email("Must be a valid email"),
44
+ });
45
+
46
+ // Environment variables
47
+ const envSchema = z.object({
48
+ DATABASE_URL: z.string().url(),
49
+ PORT: z.coerce.number().default(3000),
50
+ });
51
+
52
+ export const env = envSchema.parse(process.env);
53
+ ```
54
+
55
+ ## Safe Parse for Non-Throwing Validation
56
+
57
+ ```ts
58
+ const result = userSchema.safeParse(raw);
59
+
60
+ if (!result.success) {
61
+ // result.error.issues contains field-level errors
62
+ return { error: result.error.flatten() };
63
+ }
64
+
65
+ const user = result.data;
66
+ ```
67
+
68
+ ## Shared FE/BE Schemas
69
+
70
+ Place shared schemas in a location accessible to both frontend and backend:
71
+
72
+ ```
73
+ shared/
74
+ schemas/
75
+ user.schema.ts
76
+ invoice.schema.ts
77
+ auth.schema.ts
78
+ ```
79
+
80
+ In a monorepo, export from a shared package:
81
+
82
+ ```ts
83
+ import { userSchema } from "@acme/schemas";
84
+ ```
85
+
86
+ ## DO NOT
87
+
88
+ - Accept `any` from `JSON.parse` without parsing through a schema
89
+ - Duplicate validation logic in both frontend and backend
90
+ - Use manual `if` chains to validate complex shapes — use a schema library
91
+ - Rely on TypeScript types alone for runtime safety — types are erased at runtime
92
+
93
+ ## PRIORITY
94
+
95
+ ```
96
+ Schema-first > Manual validation > No validation
97
+ ```
98
+
99
+ ## See Also
100
+
101
+ - [`rules.md`](rules.md) — general TS rules including no `any`
102
+ - [`../security/validation.md`](../security/validation.md) — validating at API boundaries
103
+ - [`../architecture/api-design.md`](../architecture/api-design.md) — shared schemas across FE/BE
104
+ - [`../react/forms.md`](../react/forms.md) — Zod schemas with TanStack/RHF forms
@@ -0,0 +1,49 @@
1
+ # Executors
2
+
3
+ How this project dispatches a **coder** and a **reviewer**. Hand-written prose, read fresh at dispatch
4
+ time — the exact parallel to [`verify.md`](verify.md), and for the same reason: a skill that hardcodes an
5
+ invocation bakes one machine's setup into a tool that ships everywhere.
6
+
7
+ Run `/onboard` to fill this in.
8
+
9
+ ## Coder
10
+
11
+ <!-- Empty means: implement in-host. That is a valid configuration, not a gap.
12
+
13
+ If an external coder CLI is configured, write its exact invocation here, including any directory or
14
+ permission scoping it needs on this machine. The system prompt is context/roles/coder.md.
15
+
16
+ Record alongside it whether that executor was actually observed reading this repository unaided, and
17
+ when. /onboard tests it; the standing rule below says why the answer changes how briefs are written. -->
18
+
19
+ **Not configured — implement in-host.**
20
+
21
+ ## Reviewer
22
+
23
+ <!-- Empty means: the host reviews the diff itself against the plan's review checklist. Weaker than an
24
+ independent reviewer, but still a gate, and it must say which one it ran.
25
+
26
+ A host that offers review often offers more than one shape of it — a review subcommand, a review skill
27
+ it can be asked to run, or both — and they do not review alike. Whichever was chosen, write the exact
28
+ invocation. Nothing shipped with this tool names one, because the winner differs per host. -->
29
+
30
+ **Not configured — the host reviews the diff against the plan's review checklist, and says so.**
31
+
32
+ ## The contract, whatever is configured
33
+
34
+ A review happens, it returns a verdict with a `P0`–`P3` severity on every blocking finding, and a `FAIL`
35
+ writes a finding to [`findings.md`](findings.md) **before** the loopback.
36
+
37
+ ## Standing rules for any external executor
38
+
39
+ - **Exit code alone proves nothing.** A CLI can exit 0 after hitting a usage limit mid-run, having
40
+ completed most but not provably all of a brief. Grep the captured output for exhaustion and error
41
+ markers before trusting a summary, and on a hit check `git status` and each acceptance criterion
42
+ individually.
43
+ - **Take the model from the CLI's own config**, not from a flag written here. A hardcoded model flag is one
44
+ more place to update when models turn over, and a rejected model can still exit 0 having written nothing.
45
+ - **No blanket permission-bypass flag.** Scope permissions in the CLI's own config instead. A standing
46
+ bypass-everything instruction in a committed file is persistent privilege escalation.
47
+ - **Assume the executor can read this repository** unless you have tested otherwise. Briefs cite paths;
48
+ they do not paste file contents. If an executor genuinely has no filesystem access, say so here — that is
49
+ the one case where a brief has to carry content inline.