@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,128 @@
1
+ # CI and Pull Requests
2
+
3
+ ## Required CI Gates
4
+
5
+ Every PR must pass these gates before merge:
6
+
7
+ - **Lint** — `pnpm lint` (Biome or ESLint)
8
+ - **Format** — `pnpm format --check` (Biome or Prettier)
9
+ - **Typecheck** — `pnpm typecheck` (`tsc --noEmit`)
10
+ - **Test** — `pnpm test` (Vitest)
11
+
12
+ E2E tests (Playwright) are recommended on critical-path branches but may be slower / less frequent.
13
+
14
+ ## Example GitHub Actions Workflow
15
+
16
+ ```yaml
17
+ # .github/workflows/ci.yml
18
+ name: CI
19
+
20
+ on:
21
+ pull_request:
22
+ push:
23
+ branches: [main]
24
+
25
+ jobs:
26
+ check:
27
+ runs-on: ubuntu-latest
28
+ steps:
29
+ - uses: actions/checkout@v4
30
+ - uses: pnpm/action-setup@v4
31
+ - uses: actions/setup-node@v4
32
+ with:
33
+ node-version: 22
34
+ cache: pnpm
35
+ - run: pnpm install --frozen-lockfile
36
+ - run: pnpm lint
37
+ - run: pnpm format --check
38
+ - run: pnpm typecheck
39
+ - run: pnpm test
40
+ ```
41
+
42
+ For monorepos with Nx, use `nx affected` to skip unchanged projects:
43
+
44
+ ```yaml
45
+ - run: pnpm nx affected --target=lint --base=origin/main
46
+ - run: pnpm nx affected --target=typecheck --base=origin/main
47
+ - run: pnpm nx affected --target=test --base=origin/main
48
+ ```
49
+
50
+ ## Pull Request Sizing
51
+
52
+ ### DO
53
+
54
+ - Keep PRs focused on one concern — easier to review, faster to land
55
+ - Split a refactor and a feature change into separate PRs
56
+ - Open a draft PR early to make work visible
57
+
58
+ ### DO NOT
59
+
60
+ - Bundle unrelated changes into one PR
61
+ - Mix renames and behavior changes in the same commit
62
+ - Wait until everything is "done" to open a PR
63
+
64
+ ### When Large PRs Are Acceptable
65
+
66
+ - Mechanical changes (codemod, formatter run) — easy to review despite size
67
+ - Migrations that cannot be split safely
68
+ - Initial scaffolds
69
+
70
+ ## PR Titles and Descriptions
71
+
72
+ ### Title
73
+
74
+ - Imperative mood: "Add invoice export", not "Added" or "Adds"
75
+ - Under ~70 characters
76
+ - Scoped if the project uses scopes: `invoices: add CSV export`
77
+
78
+ ### Description
79
+
80
+ Always include:
81
+ - **Summary** — what changed and why, 1–3 bullets
82
+ - **Test plan** — how it was verified
83
+ - **Screenshots** — for UI changes
84
+
85
+ Use [`templates/pull-request-template.md`](../templates/pull-request-template.md) as a starting point.
86
+
87
+ ## Merge Strategy
88
+
89
+ **Prefer squash merges.** This keeps `main`'s history linear and readable. Each PR becomes one commit on `main`.
90
+
91
+ - The PR title becomes the commit message — write it well
92
+ - The PR description becomes the commit body
93
+ - Branch commits are preserved in the PR itself for archeology
94
+
95
+ Avoid:
96
+ - Merge commits on `main` (history becomes a tangle)
97
+ - Rebase-and-merge without squashing (every WIP commit ends up on `main`)
98
+
99
+ ## Branch Hygiene
100
+
101
+ - Branch from `main` (or `develop` if your project uses it)
102
+ - Use descriptive branch names: `feature/invoice-csv-export`, `fix/login-redirect-loop`
103
+ - Delete branches after merge
104
+ - Avoid long-lived feature branches — merge early behind a flag if needed
105
+
106
+ ## Worktree-Based Workflows
107
+
108
+ For AI-assisted PR workflows, use [`@burglekitt/worktree`](https://github.com/burglekitt/worktree) — a CLI utility for managing git worktree branches. Multiple agents (or a human plus an agent) can work on parallel branches in isolated worktrees without stomping on each other.
109
+
110
+ ## CI for Dependency Changes
111
+
112
+ When dependencies change, CI should:
113
+ - Run a full install (no partial cache)
114
+ - Run security audit (`pnpm audit --audit-level=high`)
115
+ - Re-run all gates
116
+
117
+ ## PRIORITY
118
+
119
+ ```
120
+ Reviewability > Speed > Volume
121
+ Small PRs > Large PRs (unless mechanical)
122
+ ```
123
+
124
+ ## See Also
125
+
126
+ - [`templates/pull-request-template.md`](../templates/pull-request-template.md)
127
+ - [`architecture/refactoring.md`](../architecture/refactoring.md) — incremental refactor strategies
128
+ - [`tooling/biome.md`](biome.md) — lint and format gate
@@ -0,0 +1,194 @@
1
+ # Dates, Times, and Timezones
2
+
3
+ ## Core Rules
4
+
5
+ - Use `Temporal` types for all date/time work — never the legacy `Date` object
6
+ - Store timestamps as UTC (`Temporal.Instant` serialized to ISO 8601)
7
+ - Convert to the user's timezone only at display time (`Temporal.ZonedDateTime`)
8
+ - Use `Temporal.PlainDate` for calendar dates (birthdays, holidays) — not timestamps
9
+ - Never assume the server and client share a timezone
10
+
11
+ ## Preferred Library
12
+
13
+ Use [`@burglekitt/gmt`](https://github.com/burglekitt/gmt) — a thin layer over the modern [Temporal API](https://tc39.es/proposal-temporal/docs/) for handling date/time, timezones, and arithmetic. Includes a polyfill for environments without native Temporal yet.
14
+
15
+ Temporal replaces the legacy `Date` object and provides:
16
+ - Immutable types with explicit timezone handling
17
+ - Calendar-aware arithmetic that doesn't break on DST or leap seconds
18
+ - Clear separation between instants, zoned datetimes, plain dates, and durations
19
+
20
+ ## Choosing the Right Temporal Type
21
+
22
+ | Concept | Type | Use for |
23
+ |---|---|---|
24
+ | A specific moment globally | `Temporal.Instant` | Server timestamps, event times, audit logs |
25
+ | A moment in a specific timezone | `Temporal.ZonedDateTime` | Scheduled local meetings, recurring events |
26
+ | A calendar date with no time | `Temporal.PlainDate` | Birthdays, holidays, due dates |
27
+ | Wall-clock time with no date | `Temporal.PlainTime` | Daily schedules, opening hours |
28
+ | Date + time without timezone | `Temporal.PlainDateTime` | Floating events ("3pm on the 19th, wherever") |
29
+ | A time span | `Temporal.Duration` | TTLs, intervals, elapsed time |
30
+
31
+ Choosing the right type prevents whole categories of bugs:
32
+
33
+ ```ts
34
+ import { Temporal } from "@burglekitt/gmt";
35
+
36
+ // Birthday — calendar date, no timezone (avoids "birthday shows wrong day in Pacific time")
37
+ const birthday = Temporal.PlainDate.from("1990-04-12");
38
+
39
+ // Event — specific instant in time
40
+ const eventAt = Temporal.Instant.from("2026-05-19T15:00:00Z");
41
+
42
+ // Recurring meeting — wall-clock time bound to a zone
43
+ const meeting = Temporal.ZonedDateTime.from("2026-05-19T10:00-04:00[America/New_York]");
44
+
45
+ // Duration — for arithmetic
46
+ const ttl = Temporal.Duration.from({ hours: 24 });
47
+ ```
48
+
49
+ ## Storage
50
+
51
+ Store in UTC. The database column should be:
52
+ - PostgreSQL: `TIMESTAMP WITH TIME ZONE` (`timestamptz`)
53
+ - MySQL: `DATETIME` stored in UTC by convention
54
+ - Prisma: `DateTime` (maps to `timestamptz` on Postgres)
55
+
56
+ Convert between Prisma's `Date` and `Temporal.Instant` at the ORM boundary. Treat Temporal types as canonical inside your application.
57
+
58
+ ```ts
59
+ // Reading from Prisma
60
+ const invoice = await db.invoice.findUnique({ where: { id } });
61
+ const dueAt = Temporal.Instant.fromEpochMilliseconds(invoice.dueDate.getTime());
62
+
63
+ // Writing to Prisma
64
+ await db.invoice.update({
65
+ where: { id },
66
+ data: { dueDate: new Date(dueAt.epochMilliseconds) },
67
+ });
68
+ ```
69
+
70
+ For calendar dates with no time component, use a `DATE` column and `Temporal.PlainDate`:
71
+
72
+ ```prisma
73
+ model User {
74
+ birthday DateTime @db.Date // stored as a date, not a timestamp
75
+ }
76
+ ```
77
+
78
+ ## API Contracts
79
+
80
+ Serialize as ISO 8601 strings. Parse and validate with Zod, then convert to Temporal types:
81
+
82
+ ```ts
83
+ import { Temporal } from "@burglekitt/gmt";
84
+ import { z } from "zod";
85
+
86
+ const invoiceSchema = z.object({
87
+ id: z.string(),
88
+ dueDate: z.string().transform((s) => Temporal.Instant.from(s)),
89
+ createdAt: z.string().transform((s) => Temporal.Instant.from(s)),
90
+ });
91
+
92
+ type Invoice = z.infer<typeof invoiceSchema>;
93
+ // Invoice["dueDate"] is now Temporal.Instant
94
+ ```
95
+
96
+ For calendar dates, use `PlainDate`:
97
+
98
+ ```ts
99
+ const userSchema = z.object({
100
+ birthday: z.string().transform((s) => Temporal.PlainDate.from(s)),
101
+ });
102
+ ```
103
+
104
+ ## Detecting User Timezone
105
+
106
+ ```ts
107
+ const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
108
+ // "America/New_York", "Europe/London", etc.
109
+ ```
110
+
111
+ Store the user's preferred timezone on their profile if they may travel; fall back to browser-detected timezone otherwise.
112
+
113
+ ## Display
114
+
115
+ Convert to the user's timezone at the edge — in the component, not in storage or transport:
116
+
117
+ ```tsx
118
+ import { Temporal } from "@burglekitt/gmt";
119
+
120
+ interface InvoiceDateProps {
121
+ instant: Temporal.Instant;
122
+ timezone: string;
123
+ }
124
+
125
+ export function InvoiceDate({ instant, timezone }: InvoiceDateProps) {
126
+ const zoned = instant.toZonedDateTimeISO(timezone);
127
+ const formatted = zoned.toLocaleString("en-US", {
128
+ dateStyle: "medium",
129
+ timeStyle: "short",
130
+ });
131
+ return (
132
+ <time dateTime={instant.toString()}>
133
+ {formatted}
134
+ </time>
135
+ );
136
+ }
137
+ ```
138
+
139
+ ## Arithmetic
140
+
141
+ Temporal types are immutable — every operation returns a new value:
142
+
143
+ ```ts
144
+ import { Temporal } from "@burglekitt/gmt";
145
+
146
+ const created = Temporal.Instant.from("2026-05-19T15:00:00Z");
147
+ const dueIn30 = created.add({ days: 30 });
148
+
149
+ const today = Temporal.Now.plainDateISO();
150
+ const due = Temporal.PlainDate.from("2026-06-18");
151
+ const daysUntilDue = today.until(due, { largestUnit: "days" }).days;
152
+ const isOverdue = Temporal.PlainDate.compare(today, due) > 0;
153
+ ```
154
+
155
+ DST and timezone offsets are handled correctly because Temporal knows about calendars and timezones — manual millisecond math does not.
156
+
157
+ ## Relative Time
158
+
159
+ For "2 hours ago" style display:
160
+
161
+ ```ts
162
+ import { Temporal } from "@burglekitt/gmt";
163
+
164
+ const elapsed = Temporal.Now.instant().since(invoice.createdAt);
165
+ // elapsed is a Temporal.Duration — format with Intl.RelativeTimeFormat
166
+ const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
167
+ rtf.format(-elapsed.total({ unit: "hour" }), "hour");
168
+ // "2 hours ago"
169
+ ```
170
+
171
+ ## DO NOT
172
+
173
+ - Use the legacy `Date` object for new code — Temporal is strictly better
174
+ - Use `Date.parse` or `new Date("string")` — parse with `Temporal.Instant.from` etc.
175
+ - Compare dates with `<` / `>` operators — use `Temporal.Instant.compare` / `Temporal.PlainDate.compare`
176
+ - Do millisecond arithmetic — use `.add()` / `.subtract()` / `.until()` / `.since()`
177
+ - Mix `Date` and `Temporal` types — convert at the boundary, stay Temporal internally
178
+ - Use a timestamp where a calendar date belongs (causes timezone display bugs)
179
+ - Trust client-supplied timestamps for security-critical logic — derive on the server
180
+
181
+ ## PRIORITY
182
+
183
+ ```
184
+ Temporal types > Date object
185
+ UTC storage > Local storage
186
+ Display at the edge > Display everywhere
187
+ Right type for the concept > Generic timestamp
188
+ ```
189
+
190
+ ## See Also
191
+
192
+ - [`../typescript/validation.md`](../typescript/validation.md) — Zod schemas with Temporal parsing
193
+ - [`prisma.md`](prisma.md) — Prisma date columns
194
+ - [`dependencies.md`](dependencies.md) — when to prefer native APIs
@@ -0,0 +1,90 @@
1
+ # Dependencies
2
+
3
+ ## Core Rule
4
+
5
+ Every dependency is a long-term liability. Add one only when the value clearly exceeds the cost.
6
+
7
+ ## DO
8
+
9
+ - Prefer browser-native or runtime-native APIs when they exist
10
+ - Prefer mature, well-maintained libraries with active commits and a healthy issue tracker
11
+ - Check bundle impact before adding any client-side dependency (`bundlephobia.com`)
12
+ - Commit a lockfile (`pnpm-lock.yaml`, `package-lock.json`, `yarn.lock`)
13
+ - Pin exact versions for tooling that breaks easily across minor updates (linters, formatters, build tools)
14
+ - Audit dependencies periodically (`pnpm audit`, Dependabot, Renovate)
15
+
16
+ ## DO NOT
17
+
18
+ - Add a dependency to avoid writing 5–20 lines of straightforward code (`left-pad`, `is-array`, `is-odd`)
19
+ - Add a dependency for a feature already provided by the platform (`lodash.clone` → `structuredClone`, `axios` → `fetch`)
20
+ - Add unmaintained packages (no commits in 2+ years, open issues piling up, last release stale)
21
+ - Add packages with license incompatibilities (GPL into proprietary code, etc.)
22
+ - Add packages that pull in massive transitive dependency trees for trivial features
23
+
24
+ ## Native Over Library
25
+
26
+ Prefer the platform when it does the job:
27
+
28
+ | Instead of… | Use… |
29
+ |---|---|
30
+ | `lodash.clone`, `lodash.cloneDeep` | `structuredClone` |
31
+ | `axios` | `fetch` |
32
+ | `moment`, `date-fns` (for simple cases) | `Intl.DateTimeFormat`, `Intl.RelativeTimeFormat` |
33
+ | `uuid` (for non-cryptographic IDs) | `crypto.randomUUID()` |
34
+ | `query-string` | `URLSearchParams` |
35
+ | `classnames` | `clsx` (smaller) or template strings |
36
+ | `lodash.debounce`, `lodash.throttle` | tiny custom util or `@uidotdev/usehooks` |
37
+
38
+ ## Evaluating a Library
39
+
40
+ Before adding, check:
41
+
42
+ 1. **Maintenance** — last release date, commit frequency, issue response time
43
+ 2. **Adoption** — weekly downloads on npm, GitHub stars relative to age
44
+ 3. **Bundle size** — `bundlephobia.com/package/<name>` for minified + gzipped size
45
+ 4. **Transitive deps** — what does it pull in?
46
+ 5. **Alternatives** — is there a smaller / more focused option?
47
+ 6. **License** — compatible with your project
48
+
49
+ ## Bundle Impact Tools
50
+
51
+ ```bash
52
+ # Inspect the size of a published package
53
+ npx bundlephobia <package-name>
54
+
55
+ # Visualize what's in your bundle
56
+ pnpm dlx source-map-explorer dist/assets/*.js
57
+
58
+ # Vite plugin for bundle analysis
59
+ pnpm add -D rollup-plugin-visualizer
60
+ ```
61
+
62
+ ## When to Vendor or Fork
63
+
64
+ Vendor (copy into your repo) when:
65
+ - The library is unmaintained but you need a small piece of it
66
+ - You need a small modification the maintainer won't accept
67
+ - The package is so small (< 50 lines) that the dependency overhead exceeds the code
68
+
69
+ Fork when:
70
+ - You need a sustained, significant modification
71
+ - You can commit to maintaining the fork
72
+
73
+ ## Lockfile Discipline
74
+
75
+ - Always commit the lockfile
76
+ - Use the same package manager across the team (pnpm preferred — strict, fast, disk-efficient)
77
+ - Use `pnpm install --frozen-lockfile` in CI
78
+
79
+ ## PRIORITY
80
+
81
+ ```
82
+ Native API > Tiny focused library > Mature comprehensive library > Custom code
83
+ ```
84
+
85
+ ## See Also
86
+
87
+ - [`biome.md`](biome.md) — replacing ESLint + Prettier with one tool
88
+ - [`vite.md`](vite.md) — bundle analysis
89
+ - [`ci.md`](ci.md) — security audits in CI
90
+ - [`dates.md`](dates.md) — date library recommendation
@@ -0,0 +1,113 @@
1
+ # Nx
2
+
3
+ ## Purpose
4
+
5
+ Nx is the build system and task orchestrator for monorepos. It provides:
6
+
7
+ - Affected command computation (only build/test what changed)
8
+ - Local and remote caching
9
+ - Generators for scaffolding
10
+ - Dependency graph visualization
11
+
12
+ ## Key Commands
13
+
14
+ ```bash
15
+ # Run a target for a specific project
16
+ nx build web
17
+ nx test api
18
+ nx lint ui
19
+
20
+ # Run a target for all projects
21
+ nx run-many --target=build
22
+ nx run-many --target=test --parallel=3
23
+
24
+ # Only run for affected projects (CI-optimized)
25
+ nx affected --target=build --base=main
26
+ nx affected --target=test --base=main
27
+
28
+ # Visualize the dependency graph
29
+ nx graph
30
+
31
+ # Generate code
32
+ nx generate @nx/react:component InvoiceTable --project=web
33
+ nx generate @nx/node:library schemas --directory=packages/schemas
34
+ ```
35
+
36
+ ## Project Configuration
37
+
38
+ Each project has a `project.json`:
39
+
40
+ ```json
41
+ {
42
+ "name": "web",
43
+ "projectType": "application",
44
+ "targets": {
45
+ "build": {
46
+ "executor": "@nx/vite:build",
47
+ "options": { "outputPath": "dist/apps/web" }
48
+ },
49
+ "dev": {
50
+ "executor": "@nx/vite:dev-server",
51
+ "options": { "buildTarget": "web:build" }
52
+ },
53
+ "test": {
54
+ "executor": "@nx/vite:test"
55
+ },
56
+ "lint": {
57
+ "executor": "@nx/eslint:lint"
58
+ },
59
+ "typecheck": {
60
+ "executor": "nx:run-commands",
61
+ "options": { "command": "tsc --noEmit" }
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ ## Caching
68
+
69
+ Nx caches task outputs by default. CI should use Nx Cloud or remote cache:
70
+
71
+ ```bash
72
+ # nx.json
73
+ {
74
+ "tasksRunnerOptions": {
75
+ "default": {
76
+ "runner": "nx-cloud",
77
+ "options": {
78
+ "cacheableOperations": ["build", "test", "lint", "typecheck"]
79
+ }
80
+ }
81
+ }
82
+ }
83
+ ```
84
+
85
+ ## Enforcing Boundaries
86
+
87
+ ```json
88
+ // nx.json
89
+ {
90
+ "targetDefaults": {},
91
+ "namedInputs": {},
92
+ "generators": {
93
+ "@nx/react": {
94
+ "library": { "bundler": "vite" }
95
+ }
96
+ }
97
+ }
98
+ ```
99
+
100
+ Use `@nx/eslint-plugin` with `enforce-module-boundaries` to prevent cross-boundary imports.
101
+
102
+ ## DO NOT
103
+
104
+ - Skip `nx affected` in CI — it is the primary value proposition of Nx
105
+ - Manually run all tests when only one project changed
106
+ - Create circular dependencies between projects
107
+ - Publish packages unless explicitly required for external consumption
108
+
109
+ ## See Also
110
+
111
+ - [`../architecture/monorepos.md`](../architecture/monorepos.md) — when and how to use a monorepo
112
+ - [`../architecture/dependency-boundaries.md`](../architecture/dependency-boundaries.md) — enforcing imports
113
+ - [`ci.md`](ci.md) — `nx affected` in CI
@@ -0,0 +1,183 @@
1
+ # Observability — Logging, Metrics, Errors
2
+
3
+ ## Core Rules
4
+
5
+ - Log with structured context — never raw strings without metadata
6
+ - Never log PII, secrets, or sensitive payloads
7
+ - Send errors to an error monitoring service (Sentry, Datadog, etc.)
8
+ - Use levels intentionally — `error` is not the same as `warn`
9
+ - Correlate logs with a request / trace ID
10
+
11
+ ## Logging Levels
12
+
13
+ | Level | Use for |
14
+ |---|---|
15
+ | `error` | An operation failed and the system could not complete the user's intent |
16
+ | `warn` | Something unexpected, but the operation continued (degraded mode, fallback) |
17
+ | `info` | Normal operational events — startup, shutdown, important state changes |
18
+ | `debug` | Detail useful during investigation; off in production |
19
+
20
+ `error` should page someone. `warn` should not. Use them accordingly.
21
+
22
+ ## Structured Logging
23
+
24
+ Log objects, not strings. Structured logs are searchable and indexable in any modern log aggregator:
25
+
26
+ ```ts
27
+ // BAD — string concatenation, ungrep-able
28
+ logger.info(`User ${userId} created invoice ${invoiceId} for $${amount}`);
29
+
30
+ // GOOD — structured fields
31
+ logger.info("invoice.created", {
32
+ userId,
33
+ invoiceId,
34
+ amount,
35
+ currency: "USD",
36
+ });
37
+ ```
38
+
39
+ Prefer libraries that emit JSON in production:
40
+
41
+ - **pino** — fast, JSON output, Node.js
42
+ - **winston** — flexible, multi-transport
43
+ - **slog** — built into Go (mentioned for backend parity)
44
+
45
+ ## Context Propagation
46
+
47
+ Attach request / trace / user context once at the boundary, then log everywhere:
48
+
49
+ ```ts
50
+ // Express / Hono middleware — set request context once
51
+ import { AsyncLocalStorage } from "node:async_hooks";
52
+
53
+ const logContext = new AsyncLocalStorage<{ requestId: string; userId?: string }>();
54
+
55
+ app.use((req, res, next) => {
56
+ const requestId = req.headers["x-request-id"] ?? crypto.randomUUID();
57
+ logContext.run({ requestId, userId: req.user?.id }, () => next());
58
+ });
59
+
60
+ // Anywhere in the request — context is automatically included
61
+ function log(message: string, fields: Record<string, unknown> = {}) {
62
+ const ctx = logContext.getStore();
63
+ logger.info(message, { ...ctx, ...fields });
64
+ }
65
+ ```
66
+
67
+ ## What Not to Log
68
+
69
+ Never log:
70
+
71
+ - Passwords (even hashed) — exposure risk if logs leak
72
+ - Tokens, API keys, session IDs
73
+ - Personal data — email, phone, SSN, addresses — unless legally required and access-controlled
74
+ - Full request/response bodies that might contain PII
75
+ - Stripe / payment data — PCI compliance forbids this
76
+
77
+ Redact at the source:
78
+
79
+ ```ts
80
+ function redactUser(user: User) {
81
+ return { id: user.id, role: user.role }; // omit email, name, etc.
82
+ }
83
+
84
+ logger.info("user.action", { user: redactUser(user), action: "login" });
85
+ ```
86
+
87
+ ## Error Monitoring
88
+
89
+ Use a hosted service (Sentry, Datadog, Honeybadger, Bugsnag). Setup once at app entry:
90
+
91
+ ```ts
92
+ // app/providers.tsx (client) or server entry
93
+ import * as Sentry from "@sentry/react";
94
+
95
+ Sentry.init({
96
+ dsn: env.SENTRY_DSN,
97
+ environment: env.NODE_ENV,
98
+ tracesSampleRate: 0.1,
99
+ replaysSessionSampleRate: 0.01,
100
+ beforeSend(event) {
101
+ // strip PII before sending
102
+ if (event.user) delete event.user.email;
103
+ return event;
104
+ },
105
+ });
106
+ ```
107
+
108
+ Send errors from:
109
+
110
+ - React error boundaries (`onError` callback) — see [`react/error-boundaries.md`](../react/error-boundaries.md)
111
+ - Server-side error middleware
112
+ - TanStack Query `onError` callbacks
113
+ - Unhandled promise rejections / uncaught exceptions
114
+
115
+ ## Correlating Frontend and Backend
116
+
117
+ Attach a request ID header on every API call. The backend echoes it in responses and logs it. Sentry / Datadog can then stitch frontend + backend traces:
118
+
119
+ ```ts
120
+ // Frontend
121
+ const requestId = crypto.randomUUID();
122
+ const response = await fetch("/api/invoices", {
123
+ headers: { "X-Request-ID": requestId },
124
+ });
125
+
126
+ // On error — include requestId in the error report
127
+ Sentry.captureException(error, { tags: { requestId } });
128
+ ```
129
+
130
+ ## Metrics
131
+
132
+ For latency, throughput, and business metrics, prefer:
133
+
134
+ - **Vercel Analytics / Cloudflare Analytics** — out-of-the-box for frontend
135
+ - **OpenTelemetry** — vendor-neutral, integrates with most backends
136
+ - **Prometheus / Grafana** — self-hosted
137
+
138
+ Track:
139
+ - Request latency (p50, p95, p99)
140
+ - Error rate by route
141
+ - Critical business events (signups, conversions, purchases)
142
+
143
+ ## Web Vitals
144
+
145
+ For frontend perceived performance, monitor Core Web Vitals:
146
+
147
+ ```ts
148
+ import { onCLS, onFID, onLCP, onINP, onTTFB } from "web-vitals";
149
+
150
+ function send(metric: { name: string; value: number; id: string }) {
151
+ fetch("/api/metrics", {
152
+ method: "POST",
153
+ body: JSON.stringify(metric),
154
+ headers: { "Content-Type": "application/json" },
155
+ });
156
+ }
157
+
158
+ onCLS(send);
159
+ onLCP(send);
160
+ onINP(send);
161
+ onTTFB(send);
162
+ ```
163
+
164
+ ## DO NOT
165
+
166
+ - Log to `console.log` in production code — use a logger
167
+ - Catch errors and log without re-throwing (silently swallows failures)
168
+ - Log inside hot loops without rate limiting
169
+ - Send 100% of traffic to a paid APM — sample appropriately
170
+
171
+ ## PRIORITY
172
+
173
+ ```
174
+ Structured logs > String logs
175
+ Errors reported > Errors only logged
176
+ Correlated traces > Isolated logs
177
+ ```
178
+
179
+ ## See Also
180
+
181
+ - [`typescript/error-handling.md`](../typescript/error-handling.md) — error patterns
182
+ - [`react/error-boundaries.md`](../react/error-boundaries.md) — React error reporting
183
+ - [`security/secrets.md`](../security/secrets.md) — what not to log