@skyf0xx/hedgehog 0.1.5 → 0.1.8

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/README.md CHANGED
@@ -1,20 +1,12 @@
1
- # The Antidote to AI Spaghetti Code
1
+ # The Antidote to AI Spaghetti Code ⭐
2
2
 
3
- AI can write code faster than humans ever could.
3
+ [![GitHub stars](https://img.shields.io/github/stars/skyf0xx/hedgehog?style=social)](https://github.com/skyf0xx/hedgehog/stargazers)
4
4
 
5
- But **speed without discipline creates chaos**.
5
+ AI writes code faster than humans ever could, but speed without discipline creates chaos.
6
6
 
7
- Hedgehog gives AI the guard-rails it needs to **build software that stays clean.**
7
+ Hedgehog gives AI the guard-rails to build software that stays clean: structured workflows, opinionated architecture, composable skills, incremental build loops, and enforced quality gates.
8
8
 
9
- A complete development methodology combining:
10
-
11
- - structured workflows
12
- - opinionated architecture
13
- - composable skills
14
- - incremental build loops
15
- - enforced quality gates
16
-
17
- **Build faster, Save context**. Stay aligned. Ship software you can still understand six months later.
9
+ Build faster, save context, stay aligned, and ship software you can still understand six months later.
18
10
 
19
11
  ![Hedgehog — build software the right way, one step at a time](https://raw.githubusercontent.com/skyf0xx/hedgehog/master/docs/images/hero.png)
20
12
 
@@ -22,27 +14,22 @@ A complete development methodology combining:
22
14
 
23
15
  1. An opinionated stack
24
16
  2. An enforced build order
25
- 3. Agents and skills that make good engineering the default.
17
+ 3. Agents and skills that make good engineering the default
26
18
 
27
- ## Hedghog's secret to great outcomes
19
+ ## Hedgehog's secret to great outcomes
28
20
 
29
- - 🧩 **Progressive layering:** types → schema → backend → UI, each layer built on a stable one beneath it
30
- - 🎯 **Small context loops:** decompose work into atomic, verifiable changes
31
- - 🌳 **Self-documenting architecture:** the codebase carries the context, not the AI
32
- - 🔁 **Traceable evolution:** decisions are preserved through conventional commits
21
+ - **Progressive layering:** types → schema → backend → UI, each layer built on a stable one beneath it
22
+ - **Small context loops:** decompose work into atomic, verifiable changes
23
+ - **Self-documenting architecture:** the codebase carries the context, not the AI
24
+ - **Traceable evolution:** decisions are preserved through conventional commits
33
25
 
34
26
  ![Just describe what you want](https://raw.githubusercontent.com/skyf0xx/hedgehog/master/docs/images/curve.png)
35
27
 
36
28
  ## Why Hedgehog Exists
37
29
 
38
- AI coding starts fast then breaks down.
39
-
40
- Context accumulates, prompts get longer, architecture drifts.
41
-
42
- Eventually, adding one more feature feels
43
- dangerous.
30
+ AI coding starts fast, then breaks down. Context accumulates, prompts get longer, architecture drifts. Eventually, adding one more feature feels dangerous.
44
31
 
45
- **The enemy isn't AI. It's the absence of guardrails.**
32
+ Hedgehog's answer: guardrails, not more discipline from the AI.
46
33
 
47
34
  ## Plans Expire. Structure Doesn't
48
35
 
@@ -115,7 +102,7 @@ AI becomes the builder operating inside those constraints — turning ideas into
115
102
 
116
103
  ## Architecture
117
104
 
118
- Hedgehog is a package of agents and skills. An opinionated stack is used so the build order above is mechanical and enforced by the tooling itself:
105
+ Hedgehog is a package of agents and skills, built on an opinionated stack so the build order above is mechanical and enforced by the tooling itself:
119
106
 
120
107
  | Layer | Choice | Why |
121
108
  | --- | --- | --- |
@@ -124,6 +111,7 @@ Hedgehog is a package of agents and skills. An opinionated stack is used so the
124
111
  | Backend | NestJS | Modules naturally mirror Hedgehog's build progression. |
125
112
  | ORM | Drizzle + drizzle-zod | Database schema is the single source of truth. |
126
113
  | Database | PostgreSQL | Simple, relational, predictable. |
114
+ | Local infra | Docker Compose | Postgres/Redis run identically on every machine. |
127
115
  | Platform | Railway | Infrastructure is available from the first commit. |
128
116
  | API contract | ts-rest | Contracts are code, not documentation. |
129
117
  | Validation | Zod | One schema for runtime and compile time. |
@@ -140,11 +128,9 @@ Hedgehog is a package of agents and skills. An opinionated stack is used so the
140
128
 
141
129
  ## How Hedgehog Compares
142
130
 
143
- Superpowers and BMAD both improve on raw prompting: one gives the AI good habits, the other gives it a planning process.
131
+ Superpowers and BMAD both improve on raw prompting: one gives the AI good habits, the other gives it a planning process. In both, the order of work is a convention the AI can still break.
144
132
 
145
- But in both, the order of work is a **convention, not a constraint** it's unable to break.
146
-
147
- Hedgehog **enforces its build order with tooling** instead: Nx module boundaries, commit hooks, phase gates. The order holds because the tooling holds it, not because the discipline was followed.
133
+ Hedgehog enforces its build order with tooling instead: Nx module boundaries, commit hooks, phase gates. The order holds because the tooling holds it, not because the AI followed the discipline.
148
134
 
149
135
  | | Superpowers | BMAD | Hedgehog |
150
136
  | --- | --- | --- | --- |
@@ -156,3 +142,9 @@ Hedgehog **enforces its build order with tooling** instead: Nx module boundaries
156
142
  | **Context per step** | As much as the task pulls in | A full brief, PRD, and architecture doc per story | One module layer at a time (e.g. just the repository, just the controller) |
157
143
  | **Finding a bug** | Search wherever the task touched | Search wherever the story touched | Search one layer, in one module, in a fixed order |
158
144
  | **Real cost** | No safety net if the model shortcuts its own process | Documentation overhead most solo projects don't need | Less flexibility: the stack and order aren't negotiable |
145
+
146
+ ## Support Hedgehog
147
+
148
+ If Hedgehog helps you build better AI software, consider giving it a ⭐ on GitHub.
149
+
150
+ [![GitHub stars](https://img.shields.io/github/stars/skyf0xx/hedgehog?style=social)](https://github.com/skyf0xx/hedgehog/stargazers)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyf0xx/hedgehog",
3
- "version": "0.1.5",
3
+ "version": "0.1.8",
4
4
  "description": "Install the Hedgehog build discipline (agents + skills) into a repo.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -13,6 +13,9 @@
13
13
  "bin": {
14
14
  "hedgehog": "bin/cli.mjs"
15
15
  },
16
+ "scripts": {
17
+ "release": "npm version patch -m \"chore: bump version to %s\""
18
+ },
16
19
  "files": [
17
20
  "bin",
18
21
  "src/agents",
@@ -0,0 +1,110 @@
1
+ ---
2
+ name: bootstrap
3
+ description: Use once per invocation, at the start of a new Hedgehog project, to run exactly ONE step of the hedgehog-bootstrap skill's 7-step scaffold, then hand off to a fresh instance of itself for the next step. Not for per-module work — that's hedgehog-loop and its agents (planner, ui-builder, reviewer). Skip entirely if nx.json already exists.
4
+ model: sonnet
5
+ color: green
6
+ tools: Read, Glob, Grep, Edit, Write, Bash
7
+ ---
8
+
9
+ You are the bootstrap role in the Hedgehog discipline. `hedgehog-bootstrap`
10
+ scaffolds the stack and the enforcement config (Nx module boundaries,
11
+ typecheck target, lefthook, commitlint, env validation, phase gate, Docker
12
+ Compose) that makes the stack and build order mechanically true — 7 steps,
13
+ each its own commit. **You run exactly one step per invocation, then
14
+ stop.** Steps 1 and 6 carry the most detail (step 1 bundles the entire
15
+ enforcement config; step 6 has several known-issue workarounds) — reading
16
+ only the one step you're running, not the whole skill file's worth of
17
+ detail for steps you aren't touching yet, is what keeps you inside
18
+ context. A fresh instance of you picks up the next step; nothing about
19
+ this needs to survive in your own memory past your one commit.
20
+
21
+ You touch no domain modules — no schema, no contract, nothing under
22
+ `libs/<module>/`. That's Phase A, started after all 7 steps close, run by
23
+ `hedgehog-loop` and its own agents.
24
+
25
+ ## Which step is yours
26
+
27
+ `TODO.md`'s `## Bootstrap` section has one checkbox per step, in order.
28
+ Before doing anything else:
29
+
30
+ 1. Read `TODO.md`. Find the **first unchecked** Bootstrap box — that line
31
+ is your step, and the only step you touch this run.
32
+ 2. Cross-check against the commit log (`git log --oneline --grep="^feat("`)
33
+ that no commit for this step already exists. TODO.md is the fast path;
34
+ the commit log is the ground truth if the two ever disagree (e.g. a
35
+ commit landed but the box wasn't checked). If they disagree, trust the
36
+ commit log and fix the checkbox before proceeding.
37
+ 3. If every Bootstrap box is already checked, there's no step for you to
38
+ run — stop and say so; `hedgehog-loop` owns everything from here.
39
+ 4. If `nx.json` already exists but boxes are unchecked, or a Bootstrap
40
+ commit exists for a step whose box is unchecked, that's drift between
41
+ TODO.md and reality, not a fresh start — reconcile the checklist to
42
+ match the commits actually on disk before running anything, don't
43
+ re-run a step that already landed.
44
+
45
+ ## Running your one step
46
+
47
+ Open `hedgehog-bootstrap` and read **only the section for your step**
48
+ (plus "Before running" and "Enforcement wiring" if your step is step 1 —
49
+ that's where the enforcement config lives, bundled into step 1's commit).
50
+ Don't read ahead into later steps' detail; you won't need it and it's
51
+ exactly the context cost this design avoids. Every command, package
52
+ choice, and known-issue workaround for your step lives in that skill
53
+ file — follow it exactly, don't work from memory of a prior project's
54
+ bootstrap (package/generator flags drift upstream).
55
+
56
+ If your step is step 1: confirm Docker is available (`docker --version`)
57
+ first — stop and point to installing Docker Desktop/Engine if not, rather
58
+ than falling back to a native Postgres/Redis install. Step 1 also wires
59
+ the full enforcement config (Nx tags/`depConstraints`, the
60
+ `@nx/js/typescript` plugin with `composite`/`declaration`, `lefthook.yml`
61
+ + `commitlint.config.js`, `packages/config/env.schema.ts`, the CI phase
62
+ gate script) — this needs to be live before step 2's commit, not added
63
+ later. After `nx init`, verify it didn't leave a stray
64
+ `package-lock.json` next to `pnpm-lock.yaml` — delete and regenerate via
65
+ `pnpm install` if it did, before committing.
66
+
67
+ If your step is step 7 (`apps/mobile`): check the scope boundary
68
+ `planner` produced at Intake first. Mobile out of scope means this step
69
+ doesn't apply — check its box anyway (skipped-and-confirmed, not
70
+ left dangling for a future run to wonder about) and stop; you're the last
71
+ step either way.
72
+
73
+ ## Closing your step
74
+
75
+ 1. Commit — exactly the message `hedgehog-bootstrap` specifies for your
76
+ step, once it compiles, lints, and passes tests. A step that doesn't
77
+ pass the gate isn't done; don't check its box or hand off.
78
+ 2. Check that one box in `TODO.md`'s `## Bootstrap` section. Leave every
79
+ other box and every other section untouched.
80
+ 3. If every Bootstrap box is now checked (your step was step 7, or step 6
81
+ with mobile out of scope): Bootstrap is closed. State that plainly —
82
+ `hedgehog-loop` owns everything from here, one module at a time. Don't
83
+ hand off again.
84
+ 4. Otherwise: hand off to a fresh instance of yourself for the next step.
85
+ State plainly which step just closed and which step is next, so
86
+ whoever re-invokes you (the user or the orchestrating session) knows
87
+ to just say "continue bootstrap" rather than re-deriving it.
88
+
89
+ ## Constraints
90
+
91
+ - One step per invocation. Never run two steps in the same context just
92
+ because you have room left — the discipline is per-commit, not
93
+ per-context-budget.
94
+ - Never re-run a step whose commit already exists — see "Which step is
95
+ yours." A felt need to redo a landed step is a Correction Protocol case
96
+ (patch it at its source, per `hedgehog-loop`), not a re-run.
97
+ - Don't scaffold `apps/mobile` (step 7) unless mobile is explicitly in
98
+ scope from Intake.
99
+ - Don't add domain schema, contracts, or any `libs/<module>/*` content —
100
+ that's Phase A, started only after every Bootstrap box is checked.
101
+ - Don't deviate from the locked stack or package choices in
102
+ `hedgehog-bootstrap`. If a generator or package name changed upstream
103
+ since that file was written, verify against current docs before
104
+ running — don't substitute a different library.
105
+ - Local Postgres/Redis always run through the `docker-compose.yml` from
106
+ step 1, on every host OS. Never a natively-installed Postgres/Redis,
107
+ even to match a contributor's existing local setup.
108
+ - Don't read ahead into other steps' detail in `hedgehog-bootstrap` beyond
109
+ what "Running your one step" calls for — that's the context budget this
110
+ design protects.
@@ -3,7 +3,7 @@ name: planner
3
3
  description: Use for Intake (scope boundary + domain vocabulary) at the start of a project, and for determining module scope/order when a new set of domain modules enters play. Not a per-step planner — the step sequence within a module and TODO.md already handle that.
4
4
  model: sonnet
5
5
  color: yellow
6
- tools: Read, Glob, Grep, Write
6
+ tools: Read, Glob, Grep, Edit, Write
7
7
  ---
8
8
 
9
9
  You are the planner role in the Hedgehog discipline. The build sequence
@@ -59,7 +59,11 @@ Revising a draft is a normal edit — the Correction Protocol
59
59
  3. **`docs/context.md`** — the product narrative, scope boundary, and
60
60
  domain vocabulary, written as current state (see below). Mandatory,
61
61
  every project gets one.
62
- 4. **Screen/flow notes** — captured by module in
62
+ 4. **Root `CLAUDE.md`'s `{{PROJECT_NAME}}` and `{{PROJECT_SUMMARY}}`
63
+ placeholders** — filled in on the project's first Intake only (see
64
+ below). A later Intake doesn't touch these unless the project's
65
+ identity itself changed, not just its scope.
66
+ 5. **Screen/flow notes** — captured by module in
63
67
  `docs/design/<module>-notes.md`, for `ux-planner` to act on at that
64
68
  module's Phase B (see below). Mandatory per module in scope, even when
65
69
  nothing was offered for that module.
@@ -206,13 +210,19 @@ on — read which one this is early and let it set the pace.
206
210
  5. **Mark it provisional** — consumed by Bootstrap and revised there or
207
211
  at the schema step as needed.
208
212
  6. **Run Confirm & lock** (below) before writing anything. Only after
209
- the person confirms does synthesis proceed to steps 7–8.
213
+ the person confirms does synthesis proceed to steps 7–9.
210
214
  7. **Write `docs/context.md`**: product narrative, scope boundary, and
211
215
  the domain vocabulary table, stated as current state only — no record
212
216
  of alternatives considered, no "originally X, now Y." A later Intake
213
217
  updates this file in place so it keeps reading as current state; it
214
218
  never grows into a history.
215
- 8. **File screen/flow notes** under their module in
219
+ 8. **Fill root `CLAUDE.md`'s `{{PROJECT_NAME}}` and `{{PROJECT_SUMMARY}}`
220
+ placeholders**, first Intake only, then delete the installer's HTML
221
+ comment block at the top of that file (its job — marking what's
222
+ placeholder vs. constant — is done once both are filled). Leave every
223
+ other line untouched; the rest of the file is a Hedgehog constant, not
224
+ project-specific content.
225
+ 9. **File screen/flow notes** under their module in
216
226
  `docs/design/<module>-notes.md`, one file per module in scope, even
217
227
  when nothing was offered for that module (say so plainly instead of
218
228
  omitting the file) — verbatim or lightly organized, raw material for
@@ -338,17 +348,27 @@ ask now than fix forward later.
338
348
  domain vocabulary — current state only. On a second Intake, update it
339
349
  in place to reflect the new current state; don't append a log of what
340
350
  changed or why.
341
- 9. **File screen/flow notes** captured during Intake under
342
- `docs/design/<module>-notes.md`, one file per module in scope — create
343
- it even for a module with no screen input yet, stating that plainly.
344
- 10. **Return a summary**: scope boundary, module list, any open
351
+ 9. **Fill root `CLAUDE.md`'s placeholders**, first Intake only —
352
+ `{{PROJECT_NAME}}`, `{{PROJECT_SUMMARY}}`, then delete the installer's
353
+ comment block. Skip this step entirely on a second or later Intake;
354
+ the file has no other project-specific content to update.
355
+ 10. **File screen/flow notes** captured during Intake under
356
+ `docs/design/<module>-notes.md`, one file per module in scope — create
357
+ it even for a module with no screen input yet, stating that plainly.
358
+ 11. **Return a summary**: scope boundary, module list, any open
345
359
  questions.
346
360
 
347
361
  ## Constraints
348
362
 
349
363
  - Never write or modify application code. Read-only against the
350
- codebase; you may write `TODO.md`, `docs/context.md`, and
351
- `docs/design/<module>-notes.md`.
364
+ codebase; you may write `TODO.md`, `docs/context.md`,
365
+ `docs/design/<module>-notes.md`, and — first Intake only — root
366
+ `CLAUDE.md`'s `{{PROJECT_NAME}}`/`{{PROJECT_SUMMARY}}` placeholders and
367
+ its installer comment block.
368
+ - Never touch root `CLAUDE.md` outside those placeholders. Every other
369
+ line is a Hedgehog constant (stack, layout, rules, agent/skill
370
+ pointers) shared verbatim across every Hedgehog project — not
371
+ project-specific content to edit, extend, or "improve."
352
372
  - `docs/context.md` and `docs/design/<module>-notes.md` are not
353
373
  optional — every project gets the former, every module in scope gets
354
374
  the latter, regardless of how much material Intake produced.
@@ -29,6 +29,7 @@ One opinionated stack, applied the same way on every project:
29
29
  | Backend framework | NestJS |
30
30
  | ORM | Drizzle (+ `drizzle-zod`) |
31
31
  | Database | PostgreSQL |
32
+ | Local infra | Docker Compose (Postgres + Redis) |
32
33
  | Platform | Railway |
33
34
  | API contract | ts-rest |
34
35
  | Validation | Zod |
@@ -106,6 +107,13 @@ project misconfiguration or a corrupted pnpm store — check **Known issue:
106
107
  esbuild postinstall version mismatch** below first; this is a known,
107
108
  deterministic collision, not something to misdiagnose from scratch.
108
109
 
110
+ Confirm Docker is available (`docker --version`) before step 1. Local
111
+ Postgres/Redis run through Docker Compose on every host OS — see **Local
112
+ infra: Docker, always** below. No Docker installed: stop and point the
113
+ user to installing Docker Desktop (macOS/Windows) or Docker
114
+ Engine (Linux) rather than falling back to a natively-installed
115
+ Postgres/Redis.
116
+
109
117
  ## Steps (run in sequence, one commit per step)
110
118
 
111
119
  ### 1. Nx workspace + `packages/config`
@@ -134,6 +142,18 @@ root; if present, delete it and run `pnpm install` to regenerate
134
142
  `pnpm-lock.yaml` before continuing to step 2. Don't assume `nx init`
135
143
  respects the locked package manager — verify.
136
144
 
145
+ `nx init` also does not create `pnpm-workspace.yaml`. Without it, pnpm
146
+ doesn't recognize `packages/*` or `apps/*` as workspace members —
147
+ `pnpm add -w` fails with `ERR_PNPM_ADDING_TO_ROOT`-adjacent errors, and
148
+ cross-package `pnpm add` for a lib silently installs to the wrong place.
149
+ Create it manually right after `nx init`, before the first `pnpm add`:
150
+
151
+ ```yaml
152
+ packages:
153
+ - 'packages/*'
154
+ - 'apps/*'
155
+ ```
156
+
137
157
  Then generate the first lib. The **first** `@nx/js:lib` call materializes
138
158
  the whole workspace shape (`tsconfig.base.json`, root `eslint.config.mjs`,
139
159
  `.prettierrc`, `vitest.workspace.ts`, the `packages/` layout, and the
@@ -150,7 +170,16 @@ files:
150
170
  - `packages/config/eslint-base.js` — flat config, extended by every
151
171
  app/lib. Include `@nx/enforce-module-boundaries` and `depConstraints`
152
172
  from Enforcement wiring below, verbatim.
153
- - `packages/config/prettier.js` — includes `prettier-plugin-tailwindcss`.
173
+ - `packages/config/prettier.js` — **don't** add `prettier-plugin-tailwindcss`
174
+ here. The plugin parses every file prettier touches (not just files with
175
+ Tailwind classes) and throws (`TypeError: e.charAt is not a function` /
176
+ `a.startsWith is not a function`) on any file when no Tailwind config is
177
+ resolvable yet — which is every project from step 1 through step 5, since
178
+ Tailwind doesn't arrive until `apps/web` in step 6. Loading it globally
179
+ here breaks `nx format:write` and any bare `prettier --check` for the
180
+ first five steps of every project. Add the plugin to `apps/web`'s own
181
+ prettier setup (extending this base config) once Tailwind exists, not to
182
+ the shared base.
154
183
  - `packages/config/env.schema.ts` — the Zod env schema from Enforcement
155
184
  wiring below (`DATABASE_URL`, `BETTER_AUTH_SECRET`, `REDIS_URL`,
156
185
  `NODE_ENV`; extend per project as new infra is added later).
@@ -158,6 +187,53 @@ files:
158
187
  declares the project tags table below (`scope:*`, `type:*`) as comments
159
188
  or a lookup, so every later generator step tags its project correctly.
160
189
 
190
+ Root `eslint.config.mjs` is itself ESM (`.mjs`), which surfaces a Node
191
+ warning ("Module type of file... is not specified") unless the root
192
+ `package.json` declares `"type": "module"`. Setting that is correct and
193
+ worth doing — but it changes how Node resolves *every* plain `.js` file
194
+ in the repo from CommonJS to ESM by default. Several files later steps
195
+ generate are CommonJS (`require`/`module.exports`) and will break with
196
+ `ReferenceError: require is not defined` or `module is not defined` the
197
+ moment `"type": "module"` is set: `apps/*/webpack.config.js`,
198
+ `commitlint.config.js`, and any hand-written CommonJS tool script (e.g.
199
+ `tools/phase-gate.js`). Rename each to `.cjs` as it's created (or convert
200
+ its content to ESM, as `next.config.js` supports natively via
201
+ `export default`) rather than discovering the break later when `nx
202
+ graph` or a generator that depends on the project graph fails
203
+ opaquely — Nx surfaces this as "Failed to process project graph" pointing
204
+ at the offending file, not as a module-system explanation.
205
+
206
+ Add a root `docker-compose.yml` provisioning Postgres and Redis for local
207
+ dev, regardless of host OS (macOS, Windows, Linux) — see **Local infra:
208
+ Docker, always** below for why this isn't optional. `DATABASE_URL` and
209
+ `REDIS_URL` in `.env` point at the compose services from the first
210
+ commit, matching the env schema below.
211
+
212
+ ```yaml
213
+ services:
214
+ postgres:
215
+ image: postgres:16-alpine
216
+ environment:
217
+ POSTGRES_USER: postgres
218
+ POSTGRES_PASSWORD: postgres
219
+ POSTGRES_DB: app
220
+ ports:
221
+ - '5432:5432'
222
+ volumes:
223
+ - postgres-data:/var/lib/postgresql/data
224
+
225
+ redis:
226
+ image: redis:7-alpine
227
+ ports:
228
+ - '6379:6379'
229
+ volumes:
230
+ - redis-data:/data
231
+
232
+ volumes:
233
+ postgres-data:
234
+ redis-data:
235
+ ```
236
+
161
237
  Add an `esbuild` override to root `package.json` in this step, before step
162
238
  2 installs `drizzle-kit` — see **Known issue: esbuild postinstall version
163
239
  mismatch** below for why.
@@ -209,6 +285,20 @@ structured logging. Call `loadEnv()` at the top of `apps/api/src/main.ts`.
209
285
  Tag: `scope:api`. No controllers beyond a health check — domain
210
286
  controllers arrive per module in Phase A.
211
287
 
288
+ `@nx/nest:app` also scaffolds a companion `apps/api-e2e` project wired to
289
+ Jest (`jest.config.cts`), not Vitest — inconsistent with the locked stack.
290
+ Convert it: delete the Jest config, add a `vitest.config.mts` (mirroring
291
+ `packages/db`'s), and give `apps/api-e2e`'s `tsconfig.spec.json` the same
292
+ `composite`/`declaration` treatment as above. Rename its target from the
293
+ plugin-inferred `test` to an explicit `e2e` — an HTTP e2e suite needs a
294
+ live server (`dependsOn: ["api:build", "api:serve"]`), and if it keeps the
295
+ default `test` name, `nx affected -t test` (what lefthook's pre-commit
296
+ hook runs on every commit, per the Commit gate below) will try to boot a
297
+ server on every commit. Exclude `apps/api-e2e` from the `@nx/vitest`
298
+ plugin's auto-inference in `nx.json` (`"exclude": ["apps/api-e2e/**"]` on
299
+ that plugin entry) so it stops registering a `test` target for this
300
+ project at all, keeping only the manually-defined `e2e` target.
301
+
212
302
  Commit: `feat(api): nest shell + global guard + pino`
213
303
 
214
304
  ### 5. `apps/worker` — BullMQ seam (Redis, no consumers yet)
@@ -230,17 +320,73 @@ Commit: `feat(worker): bullmq seam, no consumers`
230
320
  ```bash
231
321
  npx nx g @nx/next:app apps/web
232
322
  pnpm add @tanstack/react-query
233
- pnpm dlx shadcn@latest init
234
323
  ```
235
324
 
236
- Wire the TanStack Query provider at the root layout. `shadcn init` writes
237
- `apps/web`'s base theme (CSS variables for color, radius, light/dark
238
- mode) — set the actual palette here, once, rather than leaving ShadCN's
239
- placeholder values for `ui-builder` to inherit unnoticed on the first
240
- screen. Light/dark mode toggle wiring belongs here too, not as a
241
- per-screen decision later. No screens or hooks yet — Phase B doesn't
325
+ **Do not run `pnpm dlx shadcn@latest init` (or `shadcn add`) against
326
+ `apps/web` directly.** The shadcn CLI hard-requires a `package.json` in
327
+ its target directory to detect the project; an Nx-generated app under
328
+ this stack has no per-app `package.json` (dependencies live at the
329
+ workspace root). Finding none, the CLI's default behavior is to offer —
330
+ and on `--yes`, silently proceed — to scaffold a *brand-new*
331
+ `create-next-app` project **inside** `apps/web`, complete with its own
332
+ nested `.git`, its own lockfile, and its own `package.json`. This doesn't
333
+ error; it succeeds and leaves a corrupted nested project (e.g.
334
+ `apps/web/web/`) that has to be manually detected and deleted. There is
335
+ currently no supported non-interactive flag that makes shadcn's CLI
336
+ target an existing package-json-less directory in place.
337
+
338
+ Build the base theme by hand instead: write `apps/web/components.json`
339
+ directly (style, aliases, `tailwind.css` path — the `css` field), add
340
+ `class-variance-authority`, `clsx`, `tailwind-merge`, `lucide-react`, and
341
+ `@radix-ui/react-slot` as dependencies, write `apps/web/src/lib/utils.ts`
342
+ (the standard `cn()` helper), and hand-write the CSS variable theme block
343
+ (light/dark, using shadcn's published default token values) into
344
+ `apps/web`'s global stylesheet. Individual components (e.g. `button.tsx`)
345
+ can then be hand-written from shadcn's published source for that
346
+ component — small, stable, well-known files — rather than fetched via the
347
+ CLI. Set the actual palette here, once, rather than leaving placeholder
348
+ values for `ui-builder` to inherit unnoticed on the first screen. Light/
349
+ dark mode toggle wiring belongs here too, not as a per-screen decision
350
+ later.
351
+
352
+ Wire the TanStack Query provider at the root layout (App Router: a
353
+ `'use client'` `Providers` wrapper component, since the root layout
354
+ itself is a server component). No screens or hooks yet — Phase B doesn't
242
355
  start until Phase A closes for at least one module. Tag: `scope:web`.
243
356
 
357
+ **Set `NODE_ENV` explicitly on the `build` target, or the production
358
+ build silently runs in dev mode under Nx.** Nx's task runner sets
359
+ `NODE_ENV=development` by default for every task unless a target
360
+ overrides it — including a plain `nx:run-commands` target running `next
361
+ build`. Next.js's production build pipeline assumes `NODE_ENV=production`;
362
+ running it under `development` produces a build that compiles and
363
+ appears to succeed on individual pages but crashes prerendering the
364
+ auto-generated `/_global-error` route with `TypeError: Cannot read
365
+ properties of null (reading 'useContext')` — a React-internals mismatch
366
+ from the dev/prod build split, not an app bug. This only reproduces
367
+ through `nx run web:build`; the identical `next build --webpack` run
368
+ directly from `apps/web` succeeds, because a bare shell has no `NODE_ENV`
369
+ set and Next defaults it correctly itself — which makes the symptom look
370
+ Nx-specific and environment-related rather than what it is (a task-runner
371
+ default silently overriding a value the build assumes). Set it on the
372
+ `build` target only, in `apps/web/project.json`:
373
+
374
+ ```json
375
+ {
376
+ "targets": {
377
+ "build": {
378
+ "options": {
379
+ "command": "next build --webpack",
380
+ "env": { "NODE_ENV": "production" }
381
+ }
382
+ }
383
+ }
384
+ }
385
+ ```
386
+
387
+ Leave `dev` alone — it correctly wants `NODE_ENV=development`, which is
388
+ also what Nx already defaults it to.
389
+
244
390
  Commit: `feat(web): next shell + query provider + base theme`
245
391
 
246
392
  ### 7. `apps/mobile` — Expo shell (only if mobile is in scope)
@@ -315,6 +461,51 @@ e.g. `libs/orders/port`, `libs/orders/repository`, `libs/orders/service`.
315
461
  Building out of order (a controller before a service exists, a hook
316
462
  reaching into `apps/api` directly) fails `nx lint`.
317
463
 
464
+ The rule list above is illustrative, not exhaustive — `@nx/enforce-
465
+ module-boundaries` denies by default for any project whose tag isn't
466
+ named as a `sourceTag` in some `depConstraints` entry ("A project without
467
+ tags matching at least one constraint cannot depend on any libraries").
468
+ `packages/db` (`scope:db`, `type:adapter`) and `apps/api` (`scope:api`)
469
+ both hit this the moment they're generated and tagged in steps 2 and 4,
470
+ because neither `type:adapter` nor `scope:api` has an entry above. Add
471
+ constraints for every tag combination as it's introduced, not only the
472
+ ones in the illustrative list — at minimum `type:adapter` (needs
473
+ `type:util`, for `packages/config`) and `scope:api` (needs `type:port`,
474
+ `type:service`, `type:util` — never `scope:db` directly; `apps/api`
475
+ reaches storage through a module's repository/service, not by importing
476
+ `packages/db` itself).
477
+
478
+ ### Typecheck target (required before the commit gate can work)
479
+
480
+ `lefthook.yml`'s pre-commit below runs `nx affected -t typecheck`, but
481
+ `@nx/js:lib` and `@nx/nest:app` don't register a `typecheck` target on
482
+ their own — it only exists once the `@nx/js/typescript` plugin is
483
+ registered in `nx.json`, added in step 1 alongside the eslint and
484
+ vitest plugins:
485
+
486
+ ```json
487
+ {
488
+ "plugin": "@nx/js/typescript",
489
+ "options": { "typecheck": { "targetName": "typecheck" } }
490
+ }
491
+ ```
492
+
493
+ Registering the plugin isn't sufficient on its own, either: `tsc
494
+ --build`'s composite-project mode (what the inferred `typecheck` target
495
+ actually runs) requires every `tsconfig.lib.json` and `tsconfig.spec.json`
496
+ it touches to set `"composite": true` and `"declaration": true`. The
497
+ generators don't set these — every `@nx/js:lib` and `@nx/nest:app`
498
+ project needs both added by hand to `tsconfig.lib.json` /
499
+ `tsconfig.app.json` and `tsconfig.spec.json` right after generation, or
500
+ `nx run <proj>:typecheck` fails immediately with `TS5069: Option
501
+ 'emitDeclarationOnly' cannot be specified without specifying option
502
+ 'declaration' or option 'composite'`. If a project's spec file imports
503
+ from its own lib source (the common case), `tsconfig.spec.json` also
504
+ needs an explicit `references` entry pointing at `tsconfig.lib.json`, or
505
+ `tsc` reports `TS6307: File '...' is not listed within the file list of
506
+ project`. Do this for `packages/config` in step 1 so the pattern is
507
+ established before every later step repeats it.
508
+
318
509
  ### Commit gate (lefthook + commitlint)
319
510
 
320
511
  Enforces one coherent change, tested, conventionally committed. Runs on
@@ -441,10 +632,30 @@ One shared config, extended everywhere:
441
632
 
442
633
  - `packages/config/eslint-base.js` — flat config, extended by every
443
634
  app/lib.
444
- - `packages/config/prettier.js` — includes `prettier-plugin-tailwindcss`.
635
+ - `packages/config/prettier.js` — the shared base, *without*
636
+ `prettier-plugin-tailwindcss` (see step 1 — it belongs in `apps/web`'s
637
+ own config once Tailwind exists, not the shared base).
445
638
 
446
639
  A per-app override request signals to fix the base config at the source.
447
640
 
641
+ ## Local infra: Docker, always
642
+
643
+ Postgres and Redis run through the `docker-compose.yml` from step 1 on
644
+ every project, on every host OS — macOS, Windows, Linux alike. This isn't
645
+ a convenience default; it's what makes "clone the repo, run the stack"
646
+ mechanically true regardless of who's building. A natively-installed
647
+ Postgres or Redis (Homebrew, an existing Windows service, a system
648
+ package) is a per-machine setup step that isn't in the commit history and
649
+ isn't reproducible on the next machine — exactly the kind of
650
+ tribal-knowledge dependency Hedgehog's enforcement exists to remove.
651
+
652
+ Don't offer a "native install" path as an alternative, even if a
653
+ contributor already has Postgres running locally for another project. One
654
+ mechanism, every machine: `docker compose up -d` before `pnpm install`,
655
+ every time. If Docker genuinely can't run on a target machine, that's a
656
+ platform-support gap to raise, not a reason to quietly fall back to a
657
+ native install for that one contributor.
658
+
448
659
  ## Known issue: esbuild postinstall version mismatch
449
660
 
450
661
  `@nx/vite` (step 1) declares `esbuild` as an *optional* peer dependency
@@ -494,3 +705,7 @@ gated by lefthook, each its own commit.
494
705
  - Each of the 7 steps is its own commit, in order — same unit-of-work
495
706
  discipline as every other step in the discipline, even though this is
496
707
  infra rather than a domain module.
708
+ - Local Postgres/Redis always run through the `docker-compose.yml` from
709
+ step 1, on every host OS. Never substitute a natively-installed
710
+ Postgres/Redis, even to match a contributor's existing local setup —
711
+ see **Local infra: Docker, always**.
@@ -66,6 +66,8 @@ steps from memory:
66
66
 
67
67
  ### The agents — delegate the judgment calls
68
68
 
69
+ - **`bootstrap`** — runs `hedgehog-bootstrap`'s 7 steps once, at project
70
+ start, after Intake. Skip if `nx.json` already exists.
69
71
  - **`planner`** — Intake (scope boundary + domain vocabulary) at project
70
72
  start, and module scoping when new scope enters play. Writes `TODO.md`,
71
73
  `docs/context.md`, and `docs/design/<module>-notes.md`.
@@ -81,11 +83,13 @@ steps from memory:
81
83
  ### Stack (locked)
82
84
 
83
85
  Nx monorepo · pnpm · **NestJS** (all domain logic + DB access) · **Drizzle**
84
- (+ `drizzle-zod`) · **PostgreSQL** · Railway · **ts-rest** contracts · **Zod**
85
- validation · **Better Auth** · **TanStack Query** hooks · **Next.js** + ShadCN
86
- + Tailwind (web, UI only) · Expo + React Native Reusables + NativeWind
87
- (mobile, optional) · **BullMQ + Redis** (queues) · Pino logging · Vitest +
88
- Playwright (tests) · Conventional Commits + commitlint + lefthook · Sentry.
86
+ (+ `drizzle-zod`) · **PostgreSQL** · **Docker Compose** (local Postgres +
87
+ Redis, every host OS) · Railway · **ts-rest** contracts · **Zod**
88
+ validation · **Better Auth** · **TanStack Query** hooks · **Next.js** +
89
+ ShadCN + Tailwind (web, UI only) · Expo + React Native Reusables +
90
+ NativeWind (mobile, optional) · **BullMQ + Redis** (queues) · Pino logging ·
91
+ Vitest + Playwright (tests) · Conventional Commits + commitlint + lefthook ·
92
+ Sentry.
89
93
 
90
94
  Don't substitute libraries. If a package or generator name changed
91
95
  upstream, verify against current docs before running — don't swap in a
@@ -94,6 +98,7 @@ different library.
94
98
  ### Layout
95
99
 
96
100
  ```
101
+ docker-compose.yml local Postgres + Redis — every host OS, no native install
97
102
  apps/
98
103
  web Next.js — UI only
99
104
  mobile Expo — optional
@@ -130,6 +135,9 @@ docs/
130
135
  (lefthook gate).
131
136
  - **Fix wrong steps at the source** via the Correction Protocol — never a
132
137
  downstream workaround.
138
+ - **Local Postgres/Redis always run through `docker-compose.yml`**, on
139
+ every host OS. Never a natively-installed Postgres/Redis, even to match
140
+ a contributor's existing local setup.
133
141
  - **`packages/config` is the single source** for shared config. A per-app
134
142
  override request means fix the base config, not add an override.
135
143
 
@@ -168,9 +176,10 @@ context small:
168
176
  - **A cleared or new session recovers by reading `TODO.md`,
169
177
  `docs/context.md`, and the commit log**, never by needing the prior
170
178
  conversation.
171
- - **Delegate heavy work to agents.** Intake elicitation (`planner`),
172
- screen builds (`ui-builder`), and reviews (`reviewer`) each run in their
173
- own isolated context — so that work doesn't pile up in the main thread.
179
+ - **Delegate heavy work to agents.** The project scaffold (`bootstrap`),
180
+ Intake elicitation (`planner`), screen builds (`ui-builder`), and
181
+ reviews (`reviewer`) each run in their own isolated context — so that
182
+ work doesn't pile up in the main thread.
174
183
  - **Don't paste large context back in.** If you find yourself
175
184
  re-explaining the architecture, stop — it's fixed and stated in this
176
185
  file, not something to reconstruct. If you need a project specific, read
@@ -10,7 +10,7 @@ has one, written by planner at Intake. -->
10
10
 
11
11
  ## Bootstrap
12
12
 
13
- - [ ] Nx workspace + `packages/config`
13
+ - [ ] Nx workspace + `packages/config` (incl. `docker-compose.yml` for local Postgres/Redis)
14
14
  - [ ] `packages/db` — Drizzle client
15
15
  - [ ] `packages/auth` — Better Auth config
16
16
  - [ ] `apps/api` — Nest shell, global guard, Pino