You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
`npm run dev` does not rebuild the `@wxyc/database` workspace before starting the backend, so any schema-changing PR that lands on `main` between developer rebuilds (or a fresh `git pull`) silently serves a backend that imports a schema older than what the running PostgreSQL database actually has.
When that happens the failure mode is not the kind that points at the cause: the runtime error is a generic `TypeError: Cannot convert undefined or null to object` deep inside `drizzle-orm/utils.js`'s `orderSelectedFields`, with no log line naming the bad column. The flowsheet's `/flowsheet/?page=...`, `/flowsheet/latest`, and the entry-page hydration all 500 with that error until `shared/database/dist/` is regenerated.
Reproduced this directly during the #940 / WXYC/dj-site#560 preview session on 2026-05-18:
Stack trace surfaced through Express's error handler; logged at `apps/backend/services/flowsheet.service.ts:252` (`db.select(FSEntryFieldsRaw).from(flowsheet).leftJoin(rotation, ...)`). Spent ~20 minutes ruling out drizzle config, migration state, schema export, and table presence before checking `shared/database/dist/index.js` against `shared/database/src/schema.ts`.
`ls -la shared/database/dist/index.js shared/database/src/schema.ts` showed `dist/index.js` was from 2026-05-15 09:47 — before commit 3256093 (2026-05-15 17:06). `grep "track_position" shared/database/dist/index.js` returned only the `compilation_track_artist` match; `flowsheet.track_position` was absent.
`npm run build --workspace=@wxyc/database` regenerated `dist/`; restarted backend; the route immediately returned 200.
The dev script doesn't help anyone catch this. `tsup --watch` in `apps/backend` watches its own sources but not the dist files of workspace deps; the only way `@wxyc/database/dist` updates is if someone manually runs `npm run build` on that workspace. CI doesn't have the problem because the deploy build chains all workspaces.
End state
`npm run dev` produces a backend whose `@wxyc/database` import matches the current `shared/database/src/schema.ts`. Two practical paths:
Pre-dev script chain: Add a `predev` script in the root `package.json` that runs `npm run build --workspace=@wxyc/database`. tsup builds are fast (~30ms per the existing build log), so the cost on every `npm run dev` is trivial. Cleanest and works for anyone.
Source export: Switch `shared/database/package.json`'s exports to point at `./src/index.ts` (with TypeScript path resolution) instead of `./dist/index.js`. Eliminates the build step in dev entirely. More invasive: needs verification that the existing `source` condition in the `exports` map is what dev tooling reaches for, and that production builds still pick up `./dist/index.js` correctly. Lower-effort if the `source` condition already does the right thing; could be a one-line change.
Concurrently watch: Add a `build:watch` script in `shared/database` (`tsup --watch`) and add it to the root `concurrently` invocation in `npm run dev`. Catches the case where someone edits `schema.ts` mid-dev session. More moving parts than (1) or (2).
Approach (1) is the smallest defensible change; (2) is the right long-term shape if it works out cleanly.
`shared/database/package.json` (the `exports` map if pursuing option 2).
`CLAUDE.md` under "Running locally" — note that `npm run dev` now includes (or doesn't need) a workspace build step.
Acceptance criteria
After `git pull` brings in a `shared/database/src/schema.ts` change, the next `npm run dev` produces a backend that sees the new schema columns without a manual `npm run build --workspace=@wxyc/database` step.
The cost added to `npm run dev` startup is acceptable (~< 1s for option 1, zero for option 2).
No regression in production builds: `npm run build` from the root still produces the existing `dist/` artifacts that ECR images consume.
CLAUDE.md updated so a future developer doesn't have to re-derive the answer.
Constraints
Not a CI problem: `npm run typecheck` and `npm run build` run from CI cleanly because CI starts from a fresh checkout and builds every workspace. This is purely a dev-loop hazard.
The error class (`Cannot convert undefined or null to object` inside drizzle utils with no column name) is the highest-friction part. Even if the dev script is left as-is, at minimum an error log line that says "schema.ts column X is undefined at runtime — rebuild @wxyc/database" when this specific failure mode trips would save the next debugger from the same 20-minute trace. Open to filing that as a separate, lower-priority issue if reviewer prefers.
Problem
`npm run dev` does not rebuild the `@wxyc/database` workspace before starting the backend, so any schema-changing PR that lands on `main` between developer rebuilds (or a fresh `git pull`) silently serves a backend that imports a schema older than what the running PostgreSQL database actually has.
When that happens the failure mode is not the kind that points at the cause: the runtime error is a generic `TypeError: Cannot convert undefined or null to object` deep inside `drizzle-orm/utils.js`'s `orderSelectedFields`, with no log line naming the bad column. The flowsheet's `/flowsheet/?page=...`, `/flowsheet/latest`, and the entry-page hydration all 500 with that error until `shared/database/dist/` is regenerated.
Reproduced this directly during the #940 / WXYC/dj-site#560 preview session on 2026-05-18:
The dev script doesn't help anyone catch this. `tsup --watch` in `apps/backend` watches its own sources but not the dist files of workspace deps; the only way `@wxyc/database/dist` updates is if someone manually runs `npm run build` on that workspace. CI doesn't have the problem because the deploy build chains all workspaces.
End state
`npm run dev` produces a backend whose `@wxyc/database` import matches the current `shared/database/src/schema.ts`. Two practical paths:
Pre-dev script chain: Add a `predev` script in the root `package.json` that runs `npm run build --workspace=@wxyc/database`. tsup builds are fast (~30ms per the existing build log), so the cost on every `npm run dev` is trivial. Cleanest and works for anyone.
Source export: Switch `shared/database/package.json`'s exports to point at `./src/index.ts` (with TypeScript path resolution) instead of `./dist/index.js`. Eliminates the build step in dev entirely. More invasive: needs verification that the existing `source` condition in the `exports` map is what dev tooling reaches for, and that production builds still pick up `./dist/index.js` correctly. Lower-effort if the `source` condition already does the right thing; could be a one-line change.
Concurrently watch: Add a `build:watch` script in `shared/database` (`tsup --watch`) and add it to the root `concurrently` invocation in `npm run dev`. Catches the case where someone edits `schema.ts` mid-dev session. More moving parts than (1) or (2).
Approach (1) is the smallest defensible change; (2) is the right long-term shape if it works out cleanly.
Where
Acceptance criteria
Constraints
Related