@skyf0xx/hedgehog 0.1.0 → 0.1.5

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Will
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -16,7 +16,7 @@ A complete development methodology combining:
16
16
 
17
17
  **Build faster, Save context**. Stay aligned. Ship software you can still understand six months later.
18
18
 
19
- ![Hedgehog — build software the right way, one step at a time](docs/images/hero.png)
19
+ ![Hedgehog — build software the right way, one step at a time](https://raw.githubusercontent.com/skyf0xx/hedgehog/master/docs/images/hero.png)
20
20
 
21
21
  ## Hedgehog gives AI
22
22
 
@@ -31,7 +31,7 @@ A complete development methodology combining:
31
31
  - 🌳 **Self-documenting architecture:** the codebase carries the context, not the AI
32
32
  - 🔁 **Traceable evolution:** decisions are preserved through conventional commits
33
33
 
34
- ![Just describe what you want](docs/images/curve.png)
34
+ ![Just describe what you want](https://raw.githubusercontent.com/skyf0xx/hedgehog/master/docs/images/curve.png)
35
35
 
36
36
  ## Why Hedgehog Exists
37
37
 
@@ -60,7 +60,7 @@ Backend comes first. Every module gets a working, typed API before any screen is
60
60
 
61
61
  The build order is not something you negotiate with the AI. It is encoded into the process.
62
62
 
63
- ![Small steps, big leverage: small context loops, continuous verification, traceable evolution, sustainable velocity](docs/images/small-steps.png)
63
+ ![Small steps, big leverage: small context loops, continuous verification, traceable evolution, sustainable velocity](https://raw.githubusercontent.com/skyf0xx/hedgehog/master/docs/images/small-steps.png)
64
64
 
65
65
  ## The Hedgehog Loop
66
66
 
@@ -79,43 +79,21 @@ Repeat for the next module or the next step
79
79
  ```
80
80
 
81
81
 
82
- ![Why Hedgehog works: a different way to build with AI, comparing traditional AI workflow to Hedgehog](docs/images/why.png)
82
+ ![Why Hedgehog works: a different way to build with AI, comparing traditional AI workflow to Hedgehog](https://raw.githubusercontent.com/skyf0xx/hedgehog/master/docs/images/why.png)
83
83
 
84
84
  ## Installation
85
85
 
86
- Hedgehog installs **into your repo**, not into your editor. The agents and
87
- skills land in `.claude/` and get committed alongside your code — because
88
- the discipline is only real if it travels with the project, versioned and
89
- visible to your team and CI.
90
-
91
- From the root of the repo you want to build with Hedgehog:
86
+ From an empty project folder:
92
87
 
93
88
  ``` bash
94
89
  npx @skyf0xx/hedgehog init
95
90
  ```
96
91
 
97
- This copies:
98
-
99
- - `src/agents/*` → `.claude/agents/` — the `planner`, `ui-builder`, and
100
- `reviewer` roles
101
- - `src/skills/*` → `.claude/skills/` — `hedgehog-bootstrap`,
102
- `hedgehog-loop`, and `conventional-commits`
103
- - `CLAUDE.md` and `TODO.md` templates into the repo root
104
-
105
- If a target file already exists, `init` warns and stops without touching
106
- it. Re-run with `--force` to overwrite:
107
-
108
- ``` bash
109
- npx @skyf0xx/hedgehog init --force
110
- ```
111
-
112
- Then commit the `.claude/` payload, open Claude Code, and say:
92
+ Then open Claude Code and say:
113
93
 
114
94
  > bootstrap this project
115
95
 
116
- That triggers the `hedgehog-bootstrap` skill, which scaffolds the stack and
117
- wires the enforcement config (Nx boundaries, lefthook, commitlint, phase
118
- gate). From there, `hedgehog-loop` takes over one module at a time.
96
+ Or paste the repo URL to your Agent and have it install for you.
119
97
 
120
98
  ## For Builders
121
99
 
package/bin/cli.mjs CHANGED
@@ -33,6 +33,9 @@ const PLAN = [
33
33
  { type: 'dir', from: 'src/skills', to: '.claude/skills' },
34
34
  { type: 'file', from: 'src/templates/CLAUDE.md', to: 'CLAUDE.md' },
35
35
  { type: 'file', from: 'src/templates/TODO.md', to: 'TODO.md' },
36
+ // A minimal root package.json so bootstrap's `nx init` scaffolds a real
37
+ // pnpm workspace rather than falling into standalone (.nx wrapper) mode.
38
+ { type: 'file', from: 'src/templates/package.json', to: 'package.json' },
36
39
  ];
37
40
 
38
41
  const exists = (p) =>
@@ -127,7 +130,7 @@ async function init({ force }) {
127
130
  )}\n`,
128
131
  );
129
132
  console.log('Next steps:');
130
- console.log(` 1. ${bold('git add .claude CLAUDE.md TODO.md && git commit')}`);
133
+ console.log(` 1. ${bold('git add .claude CLAUDE.md TODO.md package.json && git commit')}`);
131
134
  console.log(` 2. Open Claude Code and say: ${bold('"bootstrap this project"')}\n`);
132
135
  }
133
136
 
package/package.json CHANGED
@@ -1,8 +1,12 @@
1
1
  {
2
2
  "name": "@skyf0xx/hedgehog",
3
- "version": "0.1.0",
3
+ "version": "0.1.5",
4
4
  "description": "Install the Hedgehog build discipline (agents + skills) into a repo.",
5
5
  "type": "module",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/skyf0xx/hedgehog.git"
9
+ },
6
10
  "publishConfig": {
7
11
  "access": "public"
8
12
  },
@@ -56,8 +56,13 @@ Revising a draft is a normal edit — the Correction Protocol
56
56
 
57
57
  1. **Scope boundary** — what's in, what's explicitly out.
58
58
  2. **Domain vocabulary** — the nouns and verbs of the problem.
59
- 3. **Screen/flow notes**, when offered — captured verbatim by module, for
60
- `ux-planner` to act on at that module's Phase B (see below).
59
+ 3. **`docs/context.md`** — the product narrative, scope boundary, and
60
+ domain vocabulary, written as current state (see below). Mandatory,
61
+ every project gets one.
62
+ 4. **Screen/flow notes** — captured by module in
63
+ `docs/design/<module>-notes.md`, for `ux-planner` to act on at that
64
+ module's Phase B (see below). Mandatory per module in scope, even when
65
+ nothing was offered for that module.
61
66
 
62
67
  ### Screens, flows, and other visual input
63
68
 
@@ -68,11 +73,70 @@ here, and doubles as raw material for Phase B later.
68
73
 
69
74
  Layout, styling, and interaction described — "the dashboard should show X
70
75
  and Y together," "this should feel like Stripe's checkout" — are
71
- captured under the relevant module in `docs/design/<module>-notes.md`
72
- (create it if needed). `ux-planner` turns this into a screen rationale
73
- once that module's contract and hook exist, in Phase A build order. Name
74
- this in the moment: "noted for `<module>`'s screen — that gets built
75
- after its API is working."
76
+ captured under the relevant module in `docs/design/<module>-notes.md`.
77
+ `ux-planner` turns this into a screen rationale once that module's
78
+ contract and hook exist, in Phase A build order. Name this in the moment:
79
+ "noted for `<module>`'s screen — that gets built after its API is
80
+ working."
81
+
82
+ Every module in scope gets this file, whether or not anything was
83
+ offered for it — a module with no screen input yet still gets a
84
+ `docs/design/<module>-notes.md` stating that plainly, not a missing
85
+ file.
86
+
87
+ ### Interview formatting
88
+
89
+ The interview is the whole UI — there's no screen to lean on, so
90
+ structure carries the weight a layout normally would. Keep a light,
91
+ consistent set of markers so the person can tell at a glance what kind of
92
+ turn they're reading, without turning the transcript noisy:
93
+
94
+ - A bold section title on entering a new stage (`**Scope**`,
95
+ `**Domain vocabulary**`, `**Confirm & lock**`) — once per stage, not
96
+ once per message.
97
+ - 🧭 for the opening brain-dump prompt, ❓ for a single follow-up
98
+ question, 🔍 for reading supplied material (a screenshot, a
99
+ spreadsheet), 📋 for a synthesis recap, 🔒 for the final confirmation
100
+ gate. One icon per turn, at most — this marks the kind of turn, it
101
+ isn't decoration.
102
+ - Tables for anything tabular (scope boundary, vocabulary) — never
103
+ prose pretending to be a table.
104
+ - Short paragraphs, one question per turn (already required below) —
105
+ never stack multiple asks in one block just because both fit.
106
+
107
+ This applies to every Intake turn from here on, not just the ones
108
+ below.
109
+
110
+ ### Confidence tracking
111
+
112
+ Track a running confidence estimate for "I know what this person
113
+ actually wants" — not "I have enough to start guessing." Confidence
114
+ rises only when an answer resolves genuine ambiguity; a detail that
115
+ just restates something already clear doesn't move it.
116
+
117
+ State it out loud periodically as elicitation progresses — after
118
+ synthesis produces a draft, and any time confidence is low enough that
119
+ continuing to guess would be worse than asking:
120
+
121
+ > Confidence: ~70% — clear on the core flow and the actors, still open
122
+ > on what "cancel" means once payment's been taken, and whether
123
+ > `status` is a single lifecycle or independent tags.
124
+
125
+ Keep asking until confidence reaches **95%**: every module has a
126
+ resolved "owned by," at least one out-of-scope item has surfaced, and
127
+ no term is doing double duty for two different lifecycles (see "When
128
+ to ask instead of guess," below). 95% is a floor on understanding
129
+ what's real, not a ceiling on how much detail to gather — don't pad the
130
+ interview past the point where more questions stop resolving
131
+ ambiguity. If the person answers "I don't know, just pick something,"
132
+ that's a resolved answer (their call, recorded as such) — it moves
133
+ confidence up, not down.
134
+
135
+ Never silently settle for "what I think they should want." If a
136
+ guess would fill a gap, surface it as a candidate to confirm or
137
+ correct instead of writing it into the draft unchallenged — this is
138
+ the same move as "offering a candidate boundary... to confirm or
139
+ correct" below, applied to every gap, not just scope.
76
140
 
77
141
  ### Elicitation — what to ask
78
142
 
@@ -141,10 +205,50 @@ on — read which one this is early and let it set the pace.
141
205
  to.
142
206
  5. **Mark it provisional** — consumed by Bootstrap and revised there or
143
207
  at the schema step as needed.
144
- 6. **File any screen/flow notes** under their module in
145
- `docs/design/<module>-notes.md`, verbatim or lightly organized — raw
146
- material for `ux-planner`, not a rationale, so don't polish or
147
- structure beyond attributing it to the right module.
208
+ 6. **Run Confirm & lock** (below) before writing anything. Only after
209
+ the person confirms does synthesis proceed to steps 7–8.
210
+ 7. **Write `docs/context.md`**: product narrative, scope boundary, and
211
+ the domain vocabulary table, stated as current state only — no record
212
+ of alternatives considered, no "originally X, now Y." A later Intake
213
+ updates this file in place so it keeps reading as current state; it
214
+ never grows into a history.
215
+ 8. **File screen/flow notes** under their module in
216
+ `docs/design/<module>-notes.md`, one file per module in scope, even
217
+ when nothing was offered for that module (say so plainly instead of
218
+ omitting the file) — verbatim or lightly organized, raw material for
219
+ `ux-planner`, not a rationale, so don't polish or structure beyond
220
+ attributing it to the right module.
221
+
222
+ ### Confirm & lock
223
+
224
+ Everything above this point is provisional and cheap to change — nothing
225
+ has been written yet. This stage is the last point before that stops
226
+ being true, so it's a hard stop, not a recap in passing.
227
+
228
+ 🔒 **Confirm & lock**. Show, in full, not condensed:
229
+
230
+ - The scope boundary table (in / out).
231
+ - The domain vocabulary table (entity, definition, attributes, owned
232
+ by).
233
+ - The module list in build order, with any cross-module FK dependencies
234
+ flagged.
235
+ - The confidence estimate, and what it's based on (e.g. "95% — every
236
+ module has a resolved owner, one out-of-scope item confirmed, no
237
+ double-duty terms remaining").
238
+
239
+ Then state plainly what happens on confirmation, before it happens:
240
+
241
+ > This locks in `docs/context.md` and the per-module design notes, and
242
+ > starts Phase A build (schema first) on the first module. Anything
243
+ > wrong or missing — say so now; it's a normal edit before this point,
244
+ > and a Correction Protocol entry after. Confirm to proceed, or tell me
245
+ > what to change.
246
+
247
+ Wait for an explicit go-ahead. A revision here is just another
248
+ elicitation turn — update the draft, re-run this stage, don't write
249
+ anything until the confirmation holds. Don't lower the bar because the
250
+ person seems eager to start; the cost of a wrong schema is exactly why
251
+ this gate exists.
148
252
 
149
253
  ### Worked example
150
254
 
@@ -199,15 +303,20 @@ ask now than fix forward later.
199
303
  schema.
200
304
  - Update `TODO.md` to reflect the checklist for what's in scope, mirroring
201
305
  the phase/step structure from `hedgehog-loop`.
306
+ - Write and maintain `docs/context.md` — the product narrative, scope
307
+ boundary, and domain vocabulary, stated as current state only.
308
+ Mandatory on every project; not conditional on domain complexity.
202
309
  - Screens or flows described during Intake are captured under the
203
- relevant module (`docs/design/<module>-notes.md`); Phase B, after the
204
- backend exists for that module, is when they get acted on.
310
+ relevant module (`docs/design/<module>-notes.md`, one per module in
311
+ scope, always present); Phase B, after the backend exists for that
312
+ module, is when they get acted on.
205
313
 
206
314
  ## Workflow
207
315
 
208
316
  1. **Read the requirement** fully before doing anything.
209
- 2. **Check `TODO.md` and the commit log** for what's already built —
210
- `feat(<module>): api` commits mark modules with a closed Phase A.
317
+ 2. **Check `TODO.md`, `docs/context.md`, and the commit log** for what's
318
+ already built — `feat(<module>): api` commits mark modules with a
319
+ closed Phase A.
211
320
  3. **Run Intake** if this is project start: extract scope boundary and
212
321
  domain vocabulary per the procedure above. If input is insufficient,
213
322
  ask — don't guess at scope.
@@ -217,19 +326,35 @@ ask now than fix forward later.
217
326
  referenced by another's FK doesn't need to exist first — FK-by-ID
218
327
  means no compile-time coupling — but flag it if joined reads are
219
328
  expected from day one, since that shapes contract design).
220
- 6. **Write/update `TODO.md`**: a checklist mirroring the Phase A and
329
+ 6. **Run Confirm & lock** (Intake procedure, above) before writing
330
+ anything below. On a project's first Intake this is mandatory; on a
331
+ later Intake adding new scope, re-run it scoped to what's new.
332
+ 7. **Write/update `TODO.md`**: a checklist mirroring the Phase A and
221
333
  Phase B steps per module in scope. Checked or unchecked is its only
222
334
  state. On a second Intake (new scope entering play), append new
223
335
  module sections only — never touch an existing module's checked
224
336
  boxes or reorder modules already in progress.
225
- 7. **File any screen/flow notes** captured during Intake under
226
- `docs/design/<module>-notes.md`, per module.
227
- 8. **Return a summary**: scope boundary, module list, any open questions.
337
+ 8. **Write/update `docs/context.md`**: product narrative, scope boundary,
338
+ domain vocabulary — current state only. On a second Intake, update it
339
+ in place to reflect the new current state; don't append a log of what
340
+ 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
345
+ questions.
228
346
 
229
347
  ## Constraints
230
348
 
231
349
  - Never write or modify application code. Read-only against the
232
- codebase; you may write `TODO.md` and `docs/design/<module>-notes.md`.
350
+ codebase; you may write `TODO.md`, `docs/context.md`, and
351
+ `docs/design/<module>-notes.md`.
352
+ - `docs/context.md` and `docs/design/<module>-notes.md` are not
353
+ optional — every project gets the former, every module in scope gets
354
+ the latter, regardless of how much material Intake produced.
355
+ - State current state only in `docs/context.md` — no negation of
356
+ alternatives, no changelog-style narration, no "we used to say X." If
357
+ Intake revises something, edit the file to say what's true now.
233
358
  - Never invent scope. Ambiguous scope means stop and ask.
234
359
  - Don't replan a module's internal step sequence — fixed by
235
360
  `hedgehog-loop`, not a per-project decision.
@@ -99,16 +99,53 @@ Confirm this hasn't already run: check for an existing Nx workspace
99
99
  existing workspace is a Correction Protocol case (patch the specific
100
100
  config step at its source, per `hedgehog-loop`), not a re-scaffold.
101
101
 
102
+ If a fresh `pnpm install` in the generated workspace fails on a binary
103
+ package's postinstall script with a version-mismatch error (e.g.
104
+ "Expected X but got Y" for a native binary like `esbuild`), don't assume
105
+ project misconfiguration or a corrupted pnpm store — check **Known issue:
106
+ esbuild postinstall version mismatch** below first; this is a known,
107
+ deterministic collision, not something to misdiagnose from scratch.
108
+
102
109
  ## Steps (run in sequence, one commit per step)
103
110
 
104
111
  ### 1. Nx workspace + `packages/config`
105
112
 
113
+ The installer has already placed files at the repo root (`.claude/`, the
114
+ `CLAUDE.md`/`TODO.md` templates, a minimal root `package.json`, and git).
115
+ `create-nx-workspace` refuses a non-empty directory, so scaffold Nx *in
116
+ place* instead — `nx init` tolerates the existing files and merges into
117
+ them (appends to `.gitignore`, injects an Nx block into `CLAUDE.md` between
118
+ marker comments, adds `nx` to the root `package.json`):
119
+
106
120
  ```bash
107
- npx create-nx-workspace@latest . --preset=ts --pm=pnpm --nxCloud=skip
121
+ npx nx@latest init
122
+ pnpm add -D @nx/js
108
123
  ```
109
124
 
110
- Scaffold `packages/config` as a plain `@nx/js` lib holding the locked,
111
- shared files:
125
+ `nx init` needs the root `package.json` the installer dropped — without
126
+ one it falls into standalone (`.nx` wrapper) mode instead of a proper
127
+ pnpm workspace.
128
+
129
+ `nx init` does not reliably respect an existing `packageManager: pnpm@...`
130
+ field — it can run its own install via npm regardless, leaving a
131
+ `package-lock.json` next to the intended `pnpm-lock.yaml`. Immediately
132
+ after `nx init` completes, check for `package-lock.json` at the repo
133
+ root; if present, delete it and run `pnpm install` to regenerate
134
+ `pnpm-lock.yaml` before continuing to step 2. Don't assume `nx init`
135
+ respects the locked package manager — verify.
136
+
137
+ Then generate the first lib. The **first** `@nx/js:lib` call materializes
138
+ the whole workspace shape (`tsconfig.base.json`, root `eslint.config.mjs`,
139
+ `.prettierrc`, `vitest.workspace.ts`, the `packages/` layout, and the
140
+ tsconfig `paths` mapping) — the same scaffolding the `ts` preset would
141
+ have produced, generated lazily on first use:
142
+
143
+ ```bash
144
+ npx nx g @nx/js:lib packages/config --bundler=none --unitTestRunner=vitest
145
+ ```
146
+
147
+ `packages/config` is a plain `@nx/js` lib holding the locked, shared
148
+ files:
112
149
 
113
150
  - `packages/config/eslint-base.js` — flat config, extended by every
114
151
  app/lib. Include `@nx/enforce-module-boundaries` and `depConstraints`
@@ -121,6 +158,18 @@ shared files:
121
158
  declares the project tags table below (`scope:*`, `type:*`) as comments
122
159
  or a lookup, so every later generator step tags its project correctly.
123
160
 
161
+ Add an `esbuild` override to root `package.json` in this step, before step
162
+ 2 installs `drizzle-kit` — see **Known issue: esbuild postinstall version
163
+ mismatch** below for why.
164
+
165
+ ```json
166
+ {
167
+ "pnpm": {
168
+ "overrides": { "esbuild": "0.25.12" }
169
+ }
170
+ }
171
+ ```
172
+
124
173
  Commit: `feat(config): workspace + shared config`
125
174
 
126
175
  ### 2. `packages/db` — Drizzle client + connection
@@ -279,13 +328,13 @@ pre-commit:
279
328
  commands:
280
329
  typecheck:
281
330
  glob: "*.{ts,tsx}"
282
- run: npx nx affected -t typecheck --files={staged_files}
331
+ run: npx nx affected -t typecheck --base=HEAD
283
332
  lint:
284
333
  glob: "*.{ts,tsx}"
285
- run: npx nx affected -t lint --files={staged_files}
334
+ run: npx nx affected -t lint --base=HEAD
286
335
  test:
287
336
  glob: "*.{ts,tsx}"
288
- run: npx nx affected -t test --files={staged_files}
337
+ run: npx nx affected -t test --base=HEAD
289
338
 
290
339
  commit-msg:
291
340
  commands:
@@ -293,6 +342,14 @@ commit-msg:
293
342
  run: npx commitlint --edit {1}
294
343
  ```
295
344
 
345
+ Don't pass `--files={staged_files}` to `nx affected` — `nx affected`
346
+ forwards unrecognized args straight through to the underlying target
347
+ command (e.g. `eslint .`), so `--files={staged_files}` becomes
348
+ `eslint . <path>` and errors on any path that doesn't match eslint's own
349
+ glob expectations. `--base=HEAD` (comparing against the last commit) is
350
+ what makes `nx affected` scope correctly to a pre-commit hook's staged
351
+ changes.
352
+
296
353
  **`commitlint.config.js`:**
297
354
 
298
355
  ```js
@@ -313,6 +370,14 @@ exists once it compiles and passes.
313
370
  Run `pnpm dlx lefthook install` once `lefthook.yml` exists so the gate is
314
371
  active from step 2 onward.
315
372
 
373
+ A machine with lefthook installed globally (e.g. via Homebrew) on `PATH`
374
+ can have that version picked up by the git hook shim instead of the
375
+ project's pinned local one, silently running different — possibly
376
+ incompatible — behavior. After `lefthook install`, verify the hook is
377
+ invoking the local pinned version (check `lefthook version` output during
378
+ a commit, or that the hook script under `.git/hooks/` resolves to
379
+ `node_modules/.bin/lefthook`) rather than a global shadow.
380
+
316
381
  ### Env validation (fail fast)
317
382
 
318
383
  Types-first extended to config. Boot fails immediately on a missing or
@@ -380,6 +445,34 @@ One shared config, extended everywhere:
380
445
 
381
446
  A per-app override request signals to fix the base config at the source.
382
447
 
448
+ ## Known issue: esbuild postinstall version mismatch
449
+
450
+ `@nx/vite` (step 1) declares `esbuild` as an *optional* peer dependency
451
+ (`^0.27.0 || ^0.28.0`), while `drizzle-kit` (step 2) pins a hard dependency
452
+ on `esbuild@^0.25.4`. pnpm's isolated store correctly keeps both esbuild
453
+ majors side by side — but esbuild's own `install.js` resolves its platform
454
+ binary (`@esbuild/<platform>`) via **ambient** Node module resolution
455
+ rather than a path scoped to its own package instance. With multiple
456
+ esbuild majors in the tree, that ambient resolution can walk up and grab a
457
+ sibling major's platform binary, hardlinking the wrong version's binary
458
+ into a package that still claims a different version number. The result
459
+ is a postinstall failure like:
460
+
461
+ ```
462
+ Error: Expected "0.28.1" but got "0.25.12"
463
+ ```
464
+
465
+ This is deterministic (not registry/store corruption) and reproduces even
466
+ from a fully clean pnpm store — it's a real collision in esbuild's install
467
+ script when it meets pnpm's multi-version isolation. The fix is the
468
+ `pnpm.overrides.esbuild` pin added in step 1 above: collapsing to a single
469
+ esbuild version (drizzle-kit's hard-pinned range, since it's non-optional
470
+ — `@nx/vite`'s peer is optional and simply goes unfilled) removes the
471
+ ambiguity that triggers the bug. If this resurfaces after a stack version
472
+ bump, re-check drizzle-kit's current `esbuild` dependency range
473
+ (`pnpm view drizzle-kit dependencies.esbuild`) and update the override to
474
+ match rather than removing it.
475
+
383
476
  ## After Bootstrap
384
477
 
385
478
  Update `TODO.md`: check off every Bootstrap line now built, leave Phase
@@ -13,8 +13,8 @@
13
13
 
14
14
  {{PROJECT_SUMMARY — 2–4 sentences the `planner` writes at Intake: what
15
15
  this project is, who it's for, and what it does. State current intent, not
16
- history. Keep it tight — deeper domain context lives in the commit log and
17
- docs/design, not here.}}
16
+ history. Keep it tight — the full product narrative, scope boundary, and
17
+ domain vocabulary live in docs/context.md, not here.}}
18
18
 
19
19
  This project is built with **Hedgehog**: a backend-first, one-step-at-a-time
20
20
  build discipline. The rules below aren't project preferences — they're how
@@ -28,6 +28,10 @@ whole plan in context — the plan lives in the structure:
28
28
  - **`TODO.md`** is the live checklist and the source of truth for what's
29
29
  next. Read it at the start of every session. Its only state is
30
30
  checked/unchecked.
31
+ - **`docs/context.md`** is the product's current-state document — product
32
+ narrative, scope boundary, domain vocabulary. Every project has one,
33
+ written by `planner` at Intake and kept current on later Intakes. It
34
+ states what's true now, never a history of what changed.
31
35
  - **The commit log** is the record of what's built and why. Conventional
32
36
  commits (`feat(<module>): schema`, `feat(<module>): api`, …) are how
33
37
  progress is read, not a conversation summary.
@@ -63,8 +67,8 @@ steps from memory:
63
67
  ### The agents — delegate the judgment calls
64
68
 
65
69
  - **`planner`** — Intake (scope boundary + domain vocabulary) at project
66
- start, and module scoping when new scope enters play. Writes `TODO.md`
67
- and `docs/design/<module>-notes.md`.
70
+ start, and module scoping when new scope enters play. Writes `TODO.md`,
71
+ `docs/context.md`, and `docs/design/<module>-notes.md`.
68
72
  - **`ux-planner`** — once per module in Phase B, after the hook exists and
69
73
  before the screen: writes `docs/design/<module>.md`.
70
74
  - **`ui-builder`** — builds screens from the ux-planner rationale.
@@ -106,6 +110,7 @@ packages/
106
110
  libs/
107
111
  <module>/port · <module>/repository · <module>/service (one triplet per table)
108
112
  docs/
113
+ context.md product narrative, scope boundary, domain vocabulary (Intake)
109
114
  design <module>-notes.md (Intake) and <module>.md (ux-planner)
110
115
  ```
111
116
 
@@ -147,7 +152,8 @@ off. Keep it thin.
147
152
  **When the build is done:** once every module in scope has both phases
148
153
  checked, the build session is complete. **Delete `TODO.md`** — a finished
149
154
  checklist is noise, and the commit log is the durable record of what was
150
- built.
155
+ built. **`docs/context.md` stays** — it's the product's current-state
156
+ document, not a checklist.
151
157
 
152
158
  ## Managing context
153
159
 
@@ -159,8 +165,9 @@ context small:
159
165
  `TODO.md` and continue. Nothing is lost, because the checklist, commits,
160
166
  and code hold all the state. Prefer this over letting one session
161
167
  accumulate the entire project.
162
- - **A cleared or new session recovers by reading `TODO.md` + the commit
163
- log**, never by needing the prior conversation.
168
+ - **A cleared or new session recovers by reading `TODO.md`,
169
+ `docs/context.md`, and the commit log**, never by needing the prior
170
+ conversation.
164
171
  - **Delegate heavy work to agents.** Intake elicitation (`planner`),
165
172
  screen builds (`ui-builder`), and reviews (`reviewer`) each run in their
166
173
  own isolated context — so that work doesn't pile up in the main thread.
@@ -1,12 +1,12 @@
1
1
  # TODO
2
2
 
3
- <!-- 2-3 sentences: what is this project. Link out to a deeper context
4
- file (e.g. docs/context.md) only if the domain genuinely needs it — most
5
- projects shouldn't need one. -->
3
+ <!-- 2-3 sentences: what is this project. Full product narrative, scope
4
+ boundary, and domain vocabulary live in docs/context.md — every project
5
+ has one, written by planner at Intake. -->
6
6
 
7
7
  ## Context
8
8
 
9
- (fill in per project)
9
+ (fill in per project — see docs/context.md for the full picture)
10
10
 
11
11
  ## Bootstrap
12
12
 
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "app",
3
+ "private": true,
4
+ "packageManager": "pnpm@9.0.0"
5
+ }