@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,70 @@
1
+ /**
2
+ * BAD: Drilling `user` through layers that never use it
3
+ *
4
+ * Problems:
5
+ * - Layout and Sidebar accept `user` only to forward it — they don't render it
6
+ * - Every intermediate signature grows when shared values are added
7
+ * - Refactoring any layer ripples through the whole tree
8
+ * - TypeScript noise: prop types repeated everywhere
9
+ */
10
+
11
+ interface User {
12
+ id: string;
13
+ name: string;
14
+ }
15
+
16
+ interface PageProps {
17
+ user: User;
18
+ }
19
+
20
+ export function Page({ user }: PageProps) {
21
+ // ✗ Layout doesn't use `user` — only forwards it
22
+ return <Layout user={user} />;
23
+ }
24
+
25
+ interface LayoutProps {
26
+ user: User;
27
+ }
28
+
29
+ function Layout({ user }: LayoutProps) {
30
+ return (
31
+ <div>
32
+ <Header />
33
+ {/* ✗ Sidebar doesn't use `user` either — just forwards */}
34
+ <Sidebar user={user} />
35
+ <Content />
36
+ </div>
37
+ );
38
+ }
39
+
40
+ interface SidebarProps {
41
+ user: User;
42
+ }
43
+
44
+ function Sidebar({ user }: SidebarProps) {
45
+ return (
46
+ <aside>
47
+ <Nav />
48
+ {/* Finally, UserMenu actually uses the prop */}
49
+ <UserMenu user={user} />
50
+ </aside>
51
+ );
52
+ }
53
+
54
+ interface UserMenuProps {
55
+ user: User;
56
+ }
57
+
58
+ function UserMenu({ user }: UserMenuProps) {
59
+ return <button type="button">{user.name}</button>;
60
+ }
61
+
62
+ function Header() {
63
+ return <header />;
64
+ }
65
+ function Content() {
66
+ return <main />;
67
+ }
68
+ function Nav() {
69
+ return <nav />;
70
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * GOOD: Data fetching with TanStack Query
3
+ *
4
+ * - Caches, deduplicates, and refetches automatically
5
+ * - Loading and error states are first-class
6
+ * - No race conditions
7
+ * - No useEffect / useState for fetch state
8
+ */
9
+
10
+ import { useQuery } from "@tanstack/react-query";
11
+
12
+ interface User {
13
+ id: string;
14
+ name: string;
15
+ email: string;
16
+ }
17
+
18
+ async function fetchUser(id: string): Promise<User> {
19
+ const response = await fetch(`/api/users/${id}`);
20
+ if (!response.ok) throw new Error(`Failed to fetch user: ${response.statusText}`);
21
+ return response.json();
22
+ }
23
+
24
+ interface UserProfileProps {
25
+ userId: string;
26
+ }
27
+
28
+ export function UserProfile({ userId }: UserProfileProps) {
29
+ const { data: user, isLoading, error } = useQuery({
30
+ queryKey: ["user", userId],
31
+ queryFn: () => fetchUser(userId),
32
+ staleTime: 60_000,
33
+ });
34
+
35
+ if (error) {
36
+ return (
37
+ <div role="alert">
38
+ We couldn't load this profile. Please refresh and try again.
39
+ </div>
40
+ );
41
+ }
42
+
43
+ if (isLoading || !user) {
44
+ return <div aria-busy="true">Loading…</div>;
45
+ }
46
+
47
+ return (
48
+ <article>
49
+ <h1>{user.name}</h1>
50
+ <p>{user.email}</p>
51
+ </article>
52
+ );
53
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * GOOD: Derived values computed during render
3
+ *
4
+ * - One source of truth (items + search)
5
+ * - No re-renders triggered by syncing state to state
6
+ * - Fewer bugs: filtered/total are always in sync with inputs
7
+ */
8
+
9
+ import { useState } from "react";
10
+
11
+ interface Item {
12
+ id: string;
13
+ name: string;
14
+ price: number;
15
+ }
16
+
17
+ interface ItemListProps {
18
+ items: Item[];
19
+ }
20
+
21
+ export function ItemList({ items }: ItemListProps) {
22
+ const [search, setSearch] = useState("");
23
+
24
+ // Computed during render — always in sync, no useEffect needed
25
+ const filtered = items.filter((item) =>
26
+ item.name.toLowerCase().includes(search.toLowerCase()),
27
+ );
28
+ const total = filtered.reduce((sum, item) => sum + item.price, 0);
29
+
30
+ return (
31
+ <section>
32
+ <label>
33
+ Search
34
+ <input
35
+ type="search"
36
+ value={search}
37
+ onChange={(event) => setSearch(event.target.value)}
38
+ />
39
+ </label>
40
+ <p>
41
+ {filtered.length} items — total ${total.toFixed(2)}
42
+ </p>
43
+ <ul>
44
+ {filtered.map((item) => (
45
+ <li key={item.id}>
46
+ {item.name} — ${item.price.toFixed(2)}
47
+ </li>
48
+ ))}
49
+ </ul>
50
+ </section>
51
+ );
52
+ }
@@ -0,0 +1,141 @@
1
+ /**
2
+ * GOOD EXAMPLE: Feature-driven, colocated, accessible invoice table
3
+ *
4
+ * Demonstrates:
5
+ * - Feature-colocated hook and types
6
+ * - TanStack Query for server state
7
+ * - URL state for filters
8
+ * - Accessible table markup
9
+ * - Explicit props, no spreading unknown props
10
+ * - No useEffect for data fetching or derived state
11
+ * - Controlled loading and error states
12
+ */
13
+
14
+ import { useQuery } from "@tanstack/react-query";
15
+ import { useSearch } from "@tanstack/react-router";
16
+ import { type Invoice, invoiceSchema } from "./invoice.schema";
17
+
18
+ // ─── Types ────────────────────────────────────────────────────────────────────
19
+
20
+ interface InvoiceTableProps {
21
+ onRowClick: (id: string) => void;
22
+ }
23
+
24
+ // ─── Data fetching ─────────────────────────────────────────────────────────────
25
+
26
+ async function fetchInvoices(status: string): Promise<Invoice[]> {
27
+ const response = await fetch(`/api/invoices?status=${status}`);
28
+ if (!response.ok) throw new Error(`Failed to fetch invoices: ${response.statusText}`);
29
+ const data = await response.json();
30
+ return invoiceSchema.array().parse(data);
31
+ }
32
+
33
+ // ─── Feature hook ─────────────────────────────────────────────────────────────
34
+
35
+ function useInvoiceTable() {
36
+ // URL state — shareable, bookmarkable
37
+ const { status = "all" } = useSearch({ from: "/invoices" });
38
+
39
+ const { data: invoices = [], isLoading, error } = useQuery({
40
+ queryKey: ["invoices", status],
41
+ queryFn: () => fetchInvoices(status),
42
+ staleTime: 30_000,
43
+ });
44
+
45
+ // Derived state — computed during render, no useEffect
46
+ const totalAmount = invoices.reduce((sum, inv) => sum + inv.amount, 0);
47
+
48
+ return { invoices, isLoading, error, totalAmount, status };
49
+ }
50
+
51
+ // ─── Component ────────────────────────────────────────────────────────────────
52
+
53
+ export function InvoiceTable({ onRowClick }: InvoiceTableProps) {
54
+ const { invoices, isLoading, error, totalAmount } = useInvoiceTable();
55
+
56
+ if (error) {
57
+ return (
58
+ <div role="alert">
59
+ <p>We couldn't load your invoices. Please refresh the page or contact support.</p>
60
+ <p>
61
+ <small>Error: {error.message}</small>
62
+ </p>
63
+ </div>
64
+ );
65
+ }
66
+
67
+ if (isLoading) {
68
+ return <div aria-busy="true" aria-label="Loading invoices">Loading…</div>;
69
+ }
70
+
71
+ if (invoices.length === 0) {
72
+ return <p>No invoices found.</p>;
73
+ }
74
+
75
+ return (
76
+ <section aria-labelledby="invoice-table-heading">
77
+ <h2 id="invoice-table-heading">Invoices</h2>
78
+ <table>
79
+ <caption>
80
+ {invoices.length} invoices — total:{" "}
81
+ {new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(
82
+ totalAmount,
83
+ )}
84
+ </caption>
85
+ <thead>
86
+ <tr>
87
+ <th scope="col">Title</th>
88
+ <th scope="col">Status</th>
89
+ <th scope="col">Amount</th>
90
+ <th scope="col">Due Date</th>
91
+ <th scope="col"><span className="sr-only">Actions</span></th>
92
+ </tr>
93
+ </thead>
94
+ <tbody>
95
+ {invoices.map((invoice) => (
96
+ <InvoiceRow
97
+ key={invoice.id}
98
+ invoice={invoice}
99
+ onRowClick={onRowClick}
100
+ />
101
+ ))}
102
+ </tbody>
103
+ </table>
104
+ </section>
105
+ );
106
+ }
107
+
108
+ // ─── Sub-component ─────────────────────────────────────────────────────────────
109
+
110
+ interface InvoiceRowProps {
111
+ invoice: Invoice;
112
+ onRowClick: (id: string) => void;
113
+ }
114
+
115
+ function InvoiceRow({ invoice, onRowClick }: InvoiceRowProps) {
116
+ return (
117
+ <tr>
118
+ <td>{invoice.title}</td>
119
+ <td>{invoice.status}</td>
120
+ <td>
121
+ {new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(
122
+ invoice.amount,
123
+ )}
124
+ </td>
125
+ <td>
126
+ <time dateTime={invoice.dueDate.toISOString()}>
127
+ {invoice.dueDate.toLocaleDateString()}
128
+ </time>
129
+ </td>
130
+ <td>
131
+ <button
132
+ type="button"
133
+ onClick={() => onRowClick(invoice.id)}
134
+ aria-label={`View invoice: ${invoice.title}`}
135
+ >
136
+ View
137
+ </button>
138
+ </td>
139
+ </tr>
140
+ );
141
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Schema for the InvoiceTable example.
3
+ *
4
+ * Demonstrates the standards-preferred pattern:
5
+ * - Single Zod schema as source of truth
6
+ * - Inferred TypeScript type from the schema
7
+ * - Shared between FE and BE (in a real project this would live in shared/schemas/)
8
+ */
9
+
10
+ import { z } from "zod";
11
+
12
+ export const invoiceSchema = z.object({
13
+ id: z.string().cuid(),
14
+ title: z.string().min(1).max(200),
15
+ status: z.enum(["draft", "open", "paid", "void"]),
16
+ amount: z.number().nonnegative(),
17
+ dueDate: z.coerce.date(),
18
+ });
19
+
20
+ export type Invoice = z.infer<typeof invoiceSchema>;
@@ -0,0 +1,83 @@
1
+ /**
2
+ * GOOD: Context provides user data where it's actually needed
3
+ *
4
+ * - Intermediate components carry no extra props
5
+ * - Adding new shared data doesn't require updating every layer
6
+ * - Tree is composed declaratively at the top
7
+ */
8
+
9
+ import { createContext, useContext, type ReactNode } from "react";
10
+
11
+ interface User {
12
+ id: string;
13
+ name: string;
14
+ }
15
+
16
+ interface UserContextValue {
17
+ user: User;
18
+ }
19
+
20
+ const UserContext = createContext<UserContextValue | null>(null);
21
+
22
+ interface UserProviderProps {
23
+ user: User;
24
+ children: ReactNode;
25
+ }
26
+
27
+ export function UserProvider({ user, children }: UserProviderProps) {
28
+ return <UserContext.Provider value={{ user }}>{children}</UserContext.Provider>;
29
+ }
30
+
31
+ function useCurrentUser(): User {
32
+ const context = useContext(UserContext);
33
+ if (!context) throw new Error("useCurrentUser must be used within UserProvider");
34
+ return context.user;
35
+ }
36
+
37
+ // ─── Components — no `user` prop forwarded through the tree ───────────────────
38
+
39
+ interface PageProps {
40
+ user: User;
41
+ }
42
+
43
+ export function Page({ user }: PageProps) {
44
+ return (
45
+ <UserProvider user={user}>
46
+ <Layout />
47
+ </UserProvider>
48
+ );
49
+ }
50
+
51
+ function Layout() {
52
+ return (
53
+ <div>
54
+ <Header />
55
+ <Sidebar />
56
+ <Content />
57
+ </div>
58
+ );
59
+ }
60
+
61
+ function Sidebar() {
62
+ return (
63
+ <aside>
64
+ <Nav />
65
+ <UserMenu />
66
+ </aside>
67
+ );
68
+ }
69
+
70
+ function UserMenu() {
71
+ const user = useCurrentUser();
72
+ return <button type="button">{user.name}</button>;
73
+ }
74
+
75
+ function Header() {
76
+ return <header />;
77
+ }
78
+ function Content() {
79
+ return <main />;
80
+ }
81
+ function Nav() {
82
+ return <nav />;
83
+ }
@@ -0,0 +1,93 @@
1
+ # AI Agent Behavior
2
+
3
+ Rules for AI agents (Claude Code, Cursor, Continue, Copilot, etc.) working within a codebase governed by these standards.
4
+
5
+ ## Stack Selection
6
+
7
+ The primary stack is **TypeScript on Node.js with React on the frontend**. Default to this stack for:
8
+
9
+ - New projects
10
+ - New services or modules
11
+ - Greenfield features
12
+ - Any task where the language is not already chosen
13
+
14
+ **PHP standards exist in [`../php/`](../php/) but are secondary.** Load and apply them only when:
15
+
16
+ - The user explicitly asks for PHP
17
+ - You are working inside an existing PHP codebase
18
+ - The task involves modifying a PHP file
19
+
20
+ Do not propose rewriting a Node/TypeScript codebase in PHP. Do not suggest PHP as an alternative to JavaScript/TypeScript "for performance" or "for simplicity." If the user has not specified PHP, they want TypeScript.
21
+
22
+ ## PRIORITIZE
23
+
24
+ - Readability
25
+ - Accessibility
26
+ - Composability
27
+ - Maintainability
28
+ - Type safety
29
+ - Feature isolation
30
+ - Incremental refactoring
31
+ - Explicit APIs
32
+ - Predictable architecture
33
+
34
+ ## AVOID
35
+
36
+ - Giant files (trigger decomposition review)
37
+ - `useEffect` abuse (derived state, state sync, data orchestration)
38
+ - Premature abstractions
39
+ - Hook mini-frameworks
40
+ - Defensive memoization
41
+ - Over-generalized utilities
42
+ - Excessive inheritance
43
+ - Redux
44
+ - Hidden side effects
45
+ - Implementation testing (test behavior, not internals)
46
+ - State synchronization effects
47
+ - Aggressive rewrites without explicit approval
48
+
49
+ ## REFACTORING RULES
50
+
51
+ ### DO
52
+
53
+ - Preserve behavior when refactoring
54
+ - Prefer additive refactors (add new, migrate, delete old)
55
+ - Write tests before refactoring untested code where possible
56
+ - Preserve architecture boundaries
57
+ - Migrate legacy systems gently and incrementally
58
+
59
+ ### DO NOT
60
+
61
+ - Perform massive rewrites unless explicitly requested
62
+ - Introduce major architectural divergence without asking first
63
+ - Break existing behavior in the name of improvement
64
+ - Rename aggressively across unrelated files in a single PR
65
+
66
+ ## ASKING VS. PROCEEDING
67
+
68
+ Ask before:
69
+ - Changing state management strategy
70
+ - Reorganizing folder structure significantly
71
+ - Replacing a library or major dependency
72
+ - Changing the testing approach for a module
73
+ - Any action that affects more than the immediate task
74
+
75
+ Proceed without asking for:
76
+ - Small, contained refactors within a single file
77
+ - Fixing obvious bugs that are clearly unintentional
78
+ - Adding types to untyped code
79
+ - Improving accessibility of existing components
80
+
81
+ ## OUTPUT QUALITY EXPECTATIONS
82
+
83
+ - Every change must leave the codebase at least as readable as it was found
84
+ - Every change must leave the codebase at least as accessible as it was found
85
+ - No regressions in type coverage
86
+ - No new `any` types without explicit justification
87
+ - No new `useEffect` without a clear external synchronization reason
88
+
89
+ ## See Also
90
+
91
+ - [`core-principles.md`](core-principles.md) — the architectural values these rules implement
92
+ - [`../architecture/refactoring.md`](../architecture/refactoring.md) — incremental refactoring rules
93
+ - [`../react/anti-patterns.md`](../react/anti-patterns.md) — patterns to avoid in React code
@@ -0,0 +1,49 @@
1
+ # Core Engineering Principles
2
+
3
+ ## Core Principle
4
+
5
+ Prefer explicit, composable, feature-oriented architecture over generalized abstractions and centralized complexity.
6
+
7
+ ## Architectural Values
8
+
9
+ ### DO
10
+
11
+ - Composition over inheritance
12
+ - Readability over cleverness
13
+ - Explicitness over magic
14
+ - Feature ownership over centralized organization
15
+ - Incremental abstraction over premature DRY
16
+ - Maintainability over premature optimization
17
+ - Colocation over fragmentation
18
+ - Accessibility-first development
19
+ - Strong type safety
20
+ - Predictable data flow
21
+
22
+ ### PRIORITY
23
+
24
+ ```
25
+ Readability > Maintainability > Performance > Cleverness
26
+ ```
27
+
28
+ ## Applying These Values
29
+
30
+ **Colocation** — Keep feature-specific code inside the feature. Move to shared only when genuinely reused by 3+ features.
31
+
32
+ **Incrementalism** — Start simple. Add abstraction when the need is proven, not anticipated.
33
+
34
+ **Explicitness** — Prefer code that is obvious over code that is elegant. A future reader should not need to trace magic to understand behavior.
35
+
36
+ **Feature ownership** — A feature folder owns its components, hooks, types, and tests. It does not scatter logic across the codebase by type.
37
+
38
+ **Accessibility first** — Semantic HTML and keyboard support are not optional. They are part of the definition of done.
39
+
40
+ **Type safety** — Types are documentation and safety nets. Avoid `any`, type assertions, and type gymnastics.
41
+
42
+ **Predictable data flow** — Data flows downward. Side effects are explicit and isolated. State changes are traceable.
43
+
44
+ ## See Also
45
+
46
+ - [`ai-agent-behavior.md`](ai-agent-behavior.md) — concrete rules derived from these principles
47
+ - [`maintainability.md`](maintainability.md) — colocation, deletability, naming
48
+ - [`readability.md`](readability.md) — obvious over clever
49
+ - [`incremental-abstraction.md`](incremental-abstraction.md) — when to abstract vs. inline
@@ -0,0 +1,56 @@
1
+ # Incremental Abstraction
2
+
3
+ ## Core Rule
4
+
5
+ Abstract when the need is proven. Inline when the need is uncertain.
6
+
7
+ ## The Three-Instance Rule
8
+
9
+ Do not extract a shared abstraction until you have at least three real, distinct use cases. Two similar things may be coincidence. Three is a pattern.
10
+
11
+ ## Progression
12
+
13
+ ```
14
+ Inline → Local function → Shared utility → Published package
15
+ ```
16
+
17
+ Move right only when left is insufficient. Do not start at the right.
18
+
19
+ ## DO
20
+
21
+ - Duplicate small pieces of logic until the pattern is clear
22
+ - Extract to a local function first, before considering a shared module
23
+ - Name extracted abstractions after what they do, not what they contain
24
+ - Keep abstractions small and single-purpose
25
+
26
+ ## DO NOT
27
+
28
+ - Create a utility file on the first use
29
+ - Extract "just in case" it is needed elsewhere
30
+ - Build generic frameworks from a single use case
31
+ - Name abstractions by what they wrap (`useUserData` wrapping `useQuery` is not an abstraction — it is indirection)
32
+
33
+ ## Signs You Are Abstracting Too Early
34
+
35
+ - The abstraction has only one caller
36
+ - The abstraction has more configuration options than callers
37
+ - You are passing flags to switch between behaviors (`isEdit`, `mode: 'create' | 'edit'`)
38
+ - The abstraction is harder to understand than the code it replaces
39
+
40
+ ## Signs You Should Abstract Now
41
+
42
+ - Three or more places duplicate the same logic with minor variations
43
+ - A bug was fixed in one place but exists in two others
44
+ - A feature requirement forced you to update the same logic in multiple files
45
+
46
+ ## PRIORITY
47
+
48
+ ```
49
+ Clarity of local code > DRY
50
+ ```
51
+
52
+ ## See Also
53
+
54
+ - [`maintainability.md`](maintainability.md) — deletability and naming
55
+ - [`../architecture/shared-code.md`](../architecture/shared-code.md) — when to promote to `shared/`
56
+ - [`../architecture/refactoring.md`](../architecture/refactoring.md) — additive refactor strategy
@@ -0,0 +1,66 @@
1
+ # Maintainability
2
+
3
+ ## Core Rule
4
+
5
+ Write code that the next engineer (or AI agent) can understand, change, and delete safely.
6
+
7
+ ## DO
8
+
9
+ - Colocate feature-specific logic with the feature
10
+ - Delete dead code rather than commenting it out
11
+ - Name things after their intent, not their implementation
12
+ - Keep functions and components focused on a single responsibility
13
+ - Make dependencies explicit (imports, props, parameters)
14
+ - Prefer flat structures over deep nesting
15
+ - Write the simplest thing that works
16
+
17
+ ## DO NOT
18
+
19
+ - Leave commented-out code without a dated explanation
20
+ - Create files that "might be useful later"
21
+ - Hide behavior inside abstractions named too generically (`utils.ts`, `helpers.ts`, `common.ts`)
22
+ - Use abbreviations that require domain knowledge to decode
23
+ - Rely on implicit global state or module-level side effects
24
+ - Write clever code when obvious code works
25
+
26
+ ## File Size
27
+
28
+ Large files are a signal, not a rule violation. Review for decomposition when a file:
29
+ - Exceeds ~300 lines
30
+ - Has multiple unrelated responsibilities
31
+ - Is difficult to scan or navigate
32
+
33
+ Do not split files purely to hit a line count target.
34
+
35
+ ## Naming
36
+
37
+ - Name functions after what they do: `formatCurrency`, `fetchUserProfile`
38
+ - Name components after what they render: `InvoiceTable`, `UserAvatarGroup`
39
+ - Name hooks after what behavior they encapsulate: `useScrollLock`, `useDebounce`
40
+ - Avoid: `handleStuff`, `doThing`, `myHelper`, `useData`
41
+
42
+ ## Colocation
43
+
44
+ ```
45
+ features/
46
+ invoices/
47
+ InvoiceTable.tsx ← component
48
+ InvoiceTable.test.tsx ← test alongside component
49
+ useInvoiceSort.ts ← hook used only here
50
+ invoice.types.ts ← types scoped to this feature
51
+ invoice.api.ts ← data fetching scoped to this feature
52
+ ```
53
+
54
+ Move to `shared/` only when used across 3+ features.
55
+
56
+ ## PRIORITY
57
+
58
+ ```
59
+ Deletability > Reusability > Cleverness
60
+ ```
61
+
62
+ ## See Also
63
+
64
+ - [`readability.md`](readability.md) — short files, clear names, flat structure
65
+ - [`incremental-abstraction.md`](incremental-abstraction.md) — abstract late, not early
66
+ - [`../architecture/feature-driven.md`](../architecture/feature-driven.md) — colocation as a structural pattern