@skyf0xx/hedgehog 0.1.2 → 0.1.7
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 +1 -0
- package/package.json +4 -1
- package/src/agents/planner.md +145 -20
- package/src/skills/hedgehog-bootstrap/SKILL.md +298 -12
- package/src/templates/CLAUDE.md +25 -12
- package/src/templates/TODO.md +5 -5
package/README.md
CHANGED
|
@@ -124,6 +124,7 @@ Hedgehog is a package of agents and skills. An opinionated stack is used so the
|
|
|
124
124
|
| Backend | NestJS | Modules naturally mirror Hedgehog's build progression. |
|
|
125
125
|
| ORM | Drizzle + drizzle-zod | Database schema is the single source of truth. |
|
|
126
126
|
| Database | PostgreSQL | Simple, relational, predictable. |
|
|
127
|
+
| Local infra | Docker Compose | Postgres/Redis run identically on every machine. |
|
|
127
128
|
| Platform | Railway | Infrastructure is available from the first commit. |
|
|
128
129
|
| API contract | ts-rest | Contracts are code, not documentation. |
|
|
129
130
|
| Validation | Zod | One schema for runtime and compile time. |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skyf0xx/hedgehog",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
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",
|
package/src/agents/planner.md
CHANGED
|
@@ -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.
|
|
60
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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. **
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
|
204
|
-
|
|
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
|
|
210
|
-
`feat(<module>): api` commits mark modules with 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. **
|
|
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
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
|
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.
|
|
@@ -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 |
|
|
@@ -99,6 +100,20 @@ Confirm this hasn't already run: check for an existing Nx workspace
|
|
|
99
100
|
existing workspace is a Correction Protocol case (patch the specific
|
|
100
101
|
config step at its source, per `hedgehog-loop`), not a re-scaffold.
|
|
101
102
|
|
|
103
|
+
If a fresh `pnpm install` in the generated workspace fails on a binary
|
|
104
|
+
package's postinstall script with a version-mismatch error (e.g.
|
|
105
|
+
"Expected X but got Y" for a native binary like `esbuild`), don't assume
|
|
106
|
+
project misconfiguration or a corrupted pnpm store — check **Known issue:
|
|
107
|
+
esbuild postinstall version mismatch** below first; this is a known,
|
|
108
|
+
deterministic collision, not something to misdiagnose from scratch.
|
|
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
|
+
|
|
102
117
|
## Steps (run in sequence, one commit per step)
|
|
103
118
|
|
|
104
119
|
### 1. Nx workspace + `packages/config`
|
|
@@ -119,6 +134,26 @@ pnpm add -D @nx/js
|
|
|
119
134
|
one it falls into standalone (`.nx` wrapper) mode instead of a proper
|
|
120
135
|
pnpm workspace.
|
|
121
136
|
|
|
137
|
+
`nx init` does not reliably respect an existing `packageManager: pnpm@...`
|
|
138
|
+
field — it can run its own install via npm regardless, leaving a
|
|
139
|
+
`package-lock.json` next to the intended `pnpm-lock.yaml`. Immediately
|
|
140
|
+
after `nx init` completes, check for `package-lock.json` at the repo
|
|
141
|
+
root; if present, delete it and run `pnpm install` to regenerate
|
|
142
|
+
`pnpm-lock.yaml` before continuing to step 2. Don't assume `nx init`
|
|
143
|
+
respects the locked package manager — verify.
|
|
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
|
+
|
|
122
157
|
Then generate the first lib. The **first** `@nx/js:lib` call materializes
|
|
123
158
|
the whole workspace shape (`tsconfig.base.json`, root `eslint.config.mjs`,
|
|
124
159
|
`.prettierrc`, `vitest.workspace.ts`, the `packages/` layout, and the
|
|
@@ -135,7 +170,16 @@ files:
|
|
|
135
170
|
- `packages/config/eslint-base.js` — flat config, extended by every
|
|
136
171
|
app/lib. Include `@nx/enforce-module-boundaries` and `depConstraints`
|
|
137
172
|
from Enforcement wiring below, verbatim.
|
|
138
|
-
- `packages/config/prettier.js` —
|
|
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.
|
|
139
183
|
- `packages/config/env.schema.ts` — the Zod env schema from Enforcement
|
|
140
184
|
wiring below (`DATABASE_URL`, `BETTER_AUTH_SECRET`, `REDIS_URL`,
|
|
141
185
|
`NODE_ENV`; extend per project as new infra is added later).
|
|
@@ -143,6 +187,65 @@ files:
|
|
|
143
187
|
declares the project tags table below (`scope:*`, `type:*`) as comments
|
|
144
188
|
or a lookup, so every later generator step tags its project correctly.
|
|
145
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
|
+
|
|
237
|
+
Add an `esbuild` override to root `package.json` in this step, before step
|
|
238
|
+
2 installs `drizzle-kit` — see **Known issue: esbuild postinstall version
|
|
239
|
+
mismatch** below for why.
|
|
240
|
+
|
|
241
|
+
```json
|
|
242
|
+
{
|
|
243
|
+
"pnpm": {
|
|
244
|
+
"overrides": { "esbuild": "0.25.12" }
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
146
249
|
Commit: `feat(config): workspace + shared config`
|
|
147
250
|
|
|
148
251
|
### 2. `packages/db` — Drizzle client + connection
|
|
@@ -182,6 +285,20 @@ structured logging. Call `loadEnv()` at the top of `apps/api/src/main.ts`.
|
|
|
182
285
|
Tag: `scope:api`. No controllers beyond a health check — domain
|
|
183
286
|
controllers arrive per module in Phase A.
|
|
184
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
|
+
|
|
185
302
|
Commit: `feat(api): nest shell + global guard + pino`
|
|
186
303
|
|
|
187
304
|
### 5. `apps/worker` — BullMQ seam (Redis, no consumers yet)
|
|
@@ -203,17 +320,73 @@ Commit: `feat(worker): bullmq seam, no consumers`
|
|
|
203
320
|
```bash
|
|
204
321
|
npx nx g @nx/next:app apps/web
|
|
205
322
|
pnpm add @tanstack/react-query
|
|
206
|
-
pnpm dlx shadcn@latest init
|
|
207
323
|
```
|
|
208
324
|
|
|
209
|
-
|
|
210
|
-
`apps/web`
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
|
215
355
|
start until Phase A closes for at least one module. Tag: `scope:web`.
|
|
216
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
|
+
|
|
217
390
|
Commit: `feat(web): next shell + query provider + base theme`
|
|
218
391
|
|
|
219
392
|
### 7. `apps/mobile` — Expo shell (only if mobile is in scope)
|
|
@@ -288,6 +461,51 @@ e.g. `libs/orders/port`, `libs/orders/repository`, `libs/orders/service`.
|
|
|
288
461
|
Building out of order (a controller before a service exists, a hook
|
|
289
462
|
reaching into `apps/api` directly) fails `nx lint`.
|
|
290
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
|
+
|
|
291
509
|
### Commit gate (lefthook + commitlint)
|
|
292
510
|
|
|
293
511
|
Enforces one coherent change, tested, conventionally committed. Runs on
|
|
@@ -301,13 +519,13 @@ pre-commit:
|
|
|
301
519
|
commands:
|
|
302
520
|
typecheck:
|
|
303
521
|
glob: "*.{ts,tsx}"
|
|
304
|
-
run: npx nx affected -t typecheck --
|
|
522
|
+
run: npx nx affected -t typecheck --base=HEAD
|
|
305
523
|
lint:
|
|
306
524
|
glob: "*.{ts,tsx}"
|
|
307
|
-
run: npx nx affected -t lint --
|
|
525
|
+
run: npx nx affected -t lint --base=HEAD
|
|
308
526
|
test:
|
|
309
527
|
glob: "*.{ts,tsx}"
|
|
310
|
-
run: npx nx affected -t test --
|
|
528
|
+
run: npx nx affected -t test --base=HEAD
|
|
311
529
|
|
|
312
530
|
commit-msg:
|
|
313
531
|
commands:
|
|
@@ -315,6 +533,14 @@ commit-msg:
|
|
|
315
533
|
run: npx commitlint --edit {1}
|
|
316
534
|
```
|
|
317
535
|
|
|
536
|
+
Don't pass `--files={staged_files}` to `nx affected` — `nx affected`
|
|
537
|
+
forwards unrecognized args straight through to the underlying target
|
|
538
|
+
command (e.g. `eslint .`), so `--files={staged_files}` becomes
|
|
539
|
+
`eslint . <path>` and errors on any path that doesn't match eslint's own
|
|
540
|
+
glob expectations. `--base=HEAD` (comparing against the last commit) is
|
|
541
|
+
what makes `nx affected` scope correctly to a pre-commit hook's staged
|
|
542
|
+
changes.
|
|
543
|
+
|
|
318
544
|
**`commitlint.config.js`:**
|
|
319
545
|
|
|
320
546
|
```js
|
|
@@ -335,6 +561,14 @@ exists once it compiles and passes.
|
|
|
335
561
|
Run `pnpm dlx lefthook install` once `lefthook.yml` exists so the gate is
|
|
336
562
|
active from step 2 onward.
|
|
337
563
|
|
|
564
|
+
A machine with lefthook installed globally (e.g. via Homebrew) on `PATH`
|
|
565
|
+
can have that version picked up by the git hook shim instead of the
|
|
566
|
+
project's pinned local one, silently running different — possibly
|
|
567
|
+
incompatible — behavior. After `lefthook install`, verify the hook is
|
|
568
|
+
invoking the local pinned version (check `lefthook version` output during
|
|
569
|
+
a commit, or that the hook script under `.git/hooks/` resolves to
|
|
570
|
+
`node_modules/.bin/lefthook`) rather than a global shadow.
|
|
571
|
+
|
|
338
572
|
### Env validation (fail fast)
|
|
339
573
|
|
|
340
574
|
Types-first extended to config. Boot fails immediately on a missing or
|
|
@@ -398,10 +632,58 @@ One shared config, extended everywhere:
|
|
|
398
632
|
|
|
399
633
|
- `packages/config/eslint-base.js` — flat config, extended by every
|
|
400
634
|
app/lib.
|
|
401
|
-
- `packages/config/prettier.js` —
|
|
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).
|
|
402
638
|
|
|
403
639
|
A per-app override request signals to fix the base config at the source.
|
|
404
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
|
+
|
|
659
|
+
## Known issue: esbuild postinstall version mismatch
|
|
660
|
+
|
|
661
|
+
`@nx/vite` (step 1) declares `esbuild` as an *optional* peer dependency
|
|
662
|
+
(`^0.27.0 || ^0.28.0`), while `drizzle-kit` (step 2) pins a hard dependency
|
|
663
|
+
on `esbuild@^0.25.4`. pnpm's isolated store correctly keeps both esbuild
|
|
664
|
+
majors side by side — but esbuild's own `install.js` resolves its platform
|
|
665
|
+
binary (`@esbuild/<platform>`) via **ambient** Node module resolution
|
|
666
|
+
rather than a path scoped to its own package instance. With multiple
|
|
667
|
+
esbuild majors in the tree, that ambient resolution can walk up and grab a
|
|
668
|
+
sibling major's platform binary, hardlinking the wrong version's binary
|
|
669
|
+
into a package that still claims a different version number. The result
|
|
670
|
+
is a postinstall failure like:
|
|
671
|
+
|
|
672
|
+
```
|
|
673
|
+
Error: Expected "0.28.1" but got "0.25.12"
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
This is deterministic (not registry/store corruption) and reproduces even
|
|
677
|
+
from a fully clean pnpm store — it's a real collision in esbuild's install
|
|
678
|
+
script when it meets pnpm's multi-version isolation. The fix is the
|
|
679
|
+
`pnpm.overrides.esbuild` pin added in step 1 above: collapsing to a single
|
|
680
|
+
esbuild version (drizzle-kit's hard-pinned range, since it's non-optional
|
|
681
|
+
— `@nx/vite`'s peer is optional and simply goes unfilled) removes the
|
|
682
|
+
ambiguity that triggers the bug. If this resurfaces after a stack version
|
|
683
|
+
bump, re-check drizzle-kit's current `esbuild` dependency range
|
|
684
|
+
(`pnpm view drizzle-kit dependencies.esbuild`) and update the override to
|
|
685
|
+
match rather than removing it.
|
|
686
|
+
|
|
405
687
|
## After Bootstrap
|
|
406
688
|
|
|
407
689
|
Update `TODO.md`: check off every Bootstrap line now built, leave Phase
|
|
@@ -423,3 +705,7 @@ gated by lefthook, each its own commit.
|
|
|
423
705
|
- Each of the 7 steps is its own commit, in order — same unit-of-work
|
|
424
706
|
discipline as every other step in the discipline, even though this is
|
|
425
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**.
|
package/src/templates/CLAUDE.md
CHANGED
|
@@ -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 —
|
|
17
|
-
docs/
|
|
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.
|
|
@@ -77,11 +81,13 @@ steps from memory:
|
|
|
77
81
|
### Stack (locked)
|
|
78
82
|
|
|
79
83
|
Nx monorepo · pnpm · **NestJS** (all domain logic + DB access) · **Drizzle**
|
|
80
|
-
(+ `drizzle-zod`) · **PostgreSQL** ·
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
(
|
|
84
|
-
|
|
84
|
+
(+ `drizzle-zod`) · **PostgreSQL** · **Docker Compose** (local Postgres +
|
|
85
|
+
Redis, every host OS) · Railway · **ts-rest** contracts · **Zod**
|
|
86
|
+
validation · **Better Auth** · **TanStack Query** hooks · **Next.js** +
|
|
87
|
+
ShadCN + Tailwind (web, UI only) · Expo + React Native Reusables +
|
|
88
|
+
NativeWind (mobile, optional) · **BullMQ + Redis** (queues) · Pino logging ·
|
|
89
|
+
Vitest + Playwright (tests) · Conventional Commits + commitlint + lefthook ·
|
|
90
|
+
Sentry.
|
|
85
91
|
|
|
86
92
|
Don't substitute libraries. If a package or generator name changed
|
|
87
93
|
upstream, verify against current docs before running — don't swap in a
|
|
@@ -90,6 +96,7 @@ different library.
|
|
|
90
96
|
### Layout
|
|
91
97
|
|
|
92
98
|
```
|
|
99
|
+
docker-compose.yml local Postgres + Redis — every host OS, no native install
|
|
93
100
|
apps/
|
|
94
101
|
web Next.js — UI only
|
|
95
102
|
mobile Expo — optional
|
|
@@ -106,6 +113,7 @@ packages/
|
|
|
106
113
|
libs/
|
|
107
114
|
<module>/port · <module>/repository · <module>/service (one triplet per table)
|
|
108
115
|
docs/
|
|
116
|
+
context.md product narrative, scope boundary, domain vocabulary (Intake)
|
|
109
117
|
design <module>-notes.md (Intake) and <module>.md (ux-planner)
|
|
110
118
|
```
|
|
111
119
|
|
|
@@ -125,6 +133,9 @@ docs/
|
|
|
125
133
|
(lefthook gate).
|
|
126
134
|
- **Fix wrong steps at the source** via the Correction Protocol — never a
|
|
127
135
|
downstream workaround.
|
|
136
|
+
- **Local Postgres/Redis always run through `docker-compose.yml`**, on
|
|
137
|
+
every host OS. Never a natively-installed Postgres/Redis, even to match
|
|
138
|
+
a contributor's existing local setup.
|
|
128
139
|
- **`packages/config` is the single source** for shared config. A per-app
|
|
129
140
|
override request means fix the base config, not add an override.
|
|
130
141
|
|
|
@@ -147,7 +158,8 @@ off. Keep it thin.
|
|
|
147
158
|
**When the build is done:** once every module in scope has both phases
|
|
148
159
|
checked, the build session is complete. **Delete `TODO.md`** — a finished
|
|
149
160
|
checklist is noise, and the commit log is the durable record of what was
|
|
150
|
-
built.
|
|
161
|
+
built. **`docs/context.md` stays** — it's the product's current-state
|
|
162
|
+
document, not a checklist.
|
|
151
163
|
|
|
152
164
|
## Managing context
|
|
153
165
|
|
|
@@ -159,8 +171,9 @@ context small:
|
|
|
159
171
|
`TODO.md` and continue. Nothing is lost, because the checklist, commits,
|
|
160
172
|
and code hold all the state. Prefer this over letting one session
|
|
161
173
|
accumulate the entire project.
|
|
162
|
-
- **A cleared or new session recovers by reading `TODO.md
|
|
163
|
-
log**, never by needing the prior
|
|
174
|
+
- **A cleared or new session recovers by reading `TODO.md`,
|
|
175
|
+
`docs/context.md`, and the commit log**, never by needing the prior
|
|
176
|
+
conversation.
|
|
164
177
|
- **Delegate heavy work to agents.** Intake elicitation (`planner`),
|
|
165
178
|
screen builds (`ui-builder`), and reviews (`reviewer`) each run in their
|
|
166
179
|
own isolated context — so that work doesn't pile up in the main thread.
|
package/src/templates/TODO.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# TODO
|
|
2
2
|
|
|
3
|
-
<!-- 2-3 sentences: what is this project.
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
|
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
|