Skip to content

chore(dx): timestamp migrations, journal-order guard, lint OOM root fix, hook + e2e locale fixes - #193

Merged
leon0399 merged 3 commits into
masterfrom
chore/dx-quick-wins
Jul 12, 2026
Merged

chore(dx): timestamp migrations, journal-order guard, lint OOM root fix, hook + e2e locale fixes#193
leon0399 merged 3 commits into
masterfrom
chore/dx-quick-wins

Conversation

@leon0399

@leon0399 leon0399 commented Jul 12, 2026

Copy link
Copy Markdown
Owner

DX quick wins from a friction retrospective over the last few feature cycles. Eight independent fixes, each small; none change product behavior.

Migrations: timestamp prefixes + apply-order guard

  • drizzle.config.ts sets migrations.prefix: 'timestamp' → new migrations are YYYYMMDDHHMMSS_<name>.sql, so parallel branches stop colliding on the next sequential number (this cycle alone: 0021 renumbered twice, and 0023 landed in parallel with 0022). 00000023 stay index-prefixed; apply order comes from meta/_journal.json, not filenames. Smoke-verified with a throwaway drizzle-kit generate --custom (produced 20260712025511_….sql, then reverted).
  • New guard spec apps/api/src/db/migration-journal.spec.ts pins the journal's two apply-order invariants: contiguous idx and strictly increasing when. drizzle-orm's migrator (pg-core/dialect migrate) applies an entry only when its when is newer than the newest already-applied migration — an out-of-order entry is silently skipped on existing databases, which is exactly the shape a careless rebase produces. Runs in the normal unit suite; no CI wiring.
  • The guard immediately caught a live instance: hand-authored 0004's journal when was stamped older than 0003's, so a database parked exactly at 0003 would have silently skipped 0004. Re-stamped to sit between 0003 and 0005. Trade-off (deliberate): a volume parked exactly at 0004 would now try to re-apply it and fail loudly — better than the silent skip, and any volume parked pre-0005 is a year old and needs pnpm db:reset regardless. No real deployment is affected.

Lint/typecheck OOM: root cause instead of folklore

apps/api/tsconfig.json had no exclude, so the implicit include pulled dist/**/*.d.ts into the tsgo program — both typecheck and tsgolint (type-aware lint) doubled their file set after any local build, which is what actually OOM'd memory-constrained machines (the working fix until now was rm -rf dist before linting). With the exclude, lint passes in ~1.8s with dist/ present (~400MB max RSS). nest build is unaffected — tsconfig.build.json defines its own exclude.

Related: the api test script pins jest --maxWorkers=2 — stable on constrained dev machines, negligible on 4-core CI, and removes the pnpm --filter api test -- --maxWorkers=2 trap (jest treats the flag as a test pattern).

Pre-commit prettier hook (#173)

{staged_files} includes deletions, and prettier --check <missing-path> exits 2 — blocking any commit that deletes an api .ts file and masking real drift in surviving files (both observed). The job now sources files from git diff --cached --diff-filter=ACMR with the .ts scoping moved into the git pathspec. That move also closes a latent gap the fix surfaced: lefthook's glob needs a subdirectory to match **/, so direct children of src/ (e.g. src/main.ts) never hit the local format gate at all — CI's format:check caught them, the hook didn't.

Verified against the full matrix: deletion-only commit → job skips, hook passes; deletion + unformatted nested survivor → fails on the survivor; unformatted src/main.ts (direct child) → now fails locally too.

Local e2e locale crash

playwright.config.ts now forces LANG/LC_ALL=en_US.UTF-8 into every spawned e2e server. Node ≥21 derives navigator.language from the process locale; under WSL's default C.UTF-8 it reports an invalid BCP-47 tag and new Intl.Locale(...) throws during SSR (TanStack Query devtools under next dev). CI already forces this at the job level — local runs now match CI instead of inheriting the shell locale.

Ride-alongs (#164 + the Fork-menu timeout from #179's side note)

  • rls-test.sh derives its container name from the invocation (llame-rls-test-$$) and probes 55440–55490 for a free host port (an explicit RLS_TEST_PORT still wins), so two harness runs on one machine no longer share llame-rls-test/55432 and kill each other's container mid-lifecycle. host_reachable() is deduplicated onto the same TCP probe. Proven end-to-end: with a decoy listener occupying 55440, the script chose 55441, ran fully green (126 integration + 3 active-runs + 63 e2e tests), and removed its own container.
  • The Fork-menu vitest tests get an explicit 15s timeout — userEvent's pointer sequences against the Radix menu repeatedly blew the 5s default under contended local runs (never in CI; noted inside flaky/red CI: tool-loop refresh browser e2e fails on master itself #179 as separate).

Verification

  • migration-journal.spec.ts 3/3 green; full api unit suite via the changed script: 333 passed (DB-backed suites self-skip as usual)
  • drizzle-kit check green; timestamp-prefix generation smoke-tested and reverted
  • api lint + typecheck + build green with dist/ present; root format:check green
  • lefthook matrix above reproduced live with lefthook run pre-commit
  • playwright test --list compiles the config (18 tests in 5 files)

Closes #173
Closes #164


Summary by cubic

DX quick wins to reduce friction: timestamped migrations with a journal-order guard, a real fix for lint/typecheck OOMs, a reliable pre-commit formatter, stable local e2e, a collision-free RLS harness, and less flaky web tests. No product behavior changes.

  • DX Improvements

    • drizzle-kit migrations now use timestamp prefixes via apps/api/drizzle.config.ts; 00000023 stay index-prefixed. Added apps/api/src/db/migration-journal.spec.ts to enforce contiguous idx and strictly increasing when, and re-stamped 0004 in the journal to satisfy it.
    • apps/api/tsconfig.json excludes dist/ to prevent type-aware lint/typecheck OOMs; jest --maxWorkers=2 is set in apps/api/package.json for stability.
    • apps/api/scripts/rls-test.sh picks a free port (55440–55490 or RLS_TEST_PORT) and a per-run container name to avoid collisions; integration spec comments now reference the dynamic port. Closes rls-test.sh: parameterize container name/port for concurrent runs #164.
  • Bug Fixes

    • Pre-commit prettier hook checks only existing staged files via git diff --cached --diff-filter=ACMR and a git pathspec that also covers src/*.ts; fixes false failures on deletions and closes the src direct-child gap. Closes bug(hooks): pre-commit prettier hook exits 2 on any commit that deletes an apps/api ts file #173.
    • playwright.config.ts forces LANG/LC_ALL=en_US.UTF-8 for spawned servers to avoid Intl.Locale crashes under C.UTF-8.
    • Web Fork-menu vitest tests use a 15s timeout to reduce local flakiness.

Written for commit e4492e6. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Quality Improvements

    • Migration filenames now use timestamps to help prevent naming collisions.
    • Added validation to ensure migrations are applied in the correct order.
    • Improved reliability of API tests, formatting checks, and end-to-end tests.
  • Bug Fixes

    • Prevented generated files from causing type-checking and linting resource issues.
    • Resolved locale-related failures in Playwright tests.
    • Avoided formatting errors when staged files have been deleted.

…ix, hook + e2e locale fixes

DX quick wins from a friction retrospective over recent feature cycles:

- drizzle.config.ts sets migrations.prefix: 'timestamp' — parallel branches
  stop colliding on the next sequential migration number; the remaining merge
  surface is meta/_journal.json's append-only entries. 0000–0023 stay
  index-prefixed (apply order comes from the journal, not filenames).
- New guard spec src/db/migration-journal.spec.ts pins contiguous idx and
  strictly increasing `when`: drizzle's migrator silently skips any entry
  whose `when` is older than the newest already-applied migration — the exact
  shape a careless rebase produces. The guard immediately caught a live
  instance: hand-authored 0004's `when` was stamped older than 0003's (a
  database parked at 0003 would have silently skipped it) — re-stamped.
- apps/api/tsconfig.json excludes dist/: its .d.ts files were pulled into the
  tsgo/tsgolint program by the implicit include — the actual cause of local
  lint/typecheck OOMs after a build (previous folklore fix: rm -rf dist).
- api `test` script pins jest --maxWorkers=2 (stable on memory-constrained
  dev machines, negligible on 4-core CI).
- Pre-commit prettier hook filters staged files to existing ones
  (--diff-filter=ACMR): a commit deleting an api .ts file no longer aborts on
  a missing path while masking real drift in survivors. The .ts scoping moves
  into the git pathspec, which also closes a latent gap where direct children
  of src/ (e.g. src/main.ts) escaped the hook — lefthook's glob needs a
  subdirectory to match `**/`.
- playwright.config.ts forces LANG/LC_ALL=en_US.UTF-8 into every spawned e2e
  server: local runs match CI instead of crashing SSR with Intl.Locale
  RangeErrors under WSL's default C.UTF-8 locale (Node >=21 derives
  navigator.language from the process locale).

Closes #173

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtvG7ZN3kWgAMt7ni11CdX
@mesa-dot-dev

mesa-dot-dev Bot commented Jul 12, 2026

Copy link
Copy Markdown

You do not have enough credits to review this pull request. Please purchase more credits to continue.

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Drizzle migrations now use timestamp-prefixed filenames with journal-order validation. API typechecking and tests receive narrower resource scopes, the pre-commit formatter ignores deleted staged files, and Playwright servers use explicit UTF-8 locale settings.

Changes

Developer safeguards

Layer / File(s) Summary
Migration ordering and journal validation
apps/api/drizzle.config.ts, apps/api/src/db/migrations/meta/_journal.json, apps/api/src/db/migration-journal.spec.ts, apps/api/AGENTS.md, CHANGELOG.md
Drizzle migration filenames use timestamp prefixes, journal timestamps are corrected, and tests enforce non-empty contiguous journal indexes with strictly increasing when values.
API typecheck and test resource limits
apps/api/tsconfig.json, apps/api/package.json
TypeScript excludes node_modules and dist, while Jest runs with a maximum of two workers.
Formatting hook and Playwright environment
lefthook.yml, playwright.config.ts
Pre-commit Prettier checks only existing staged API TypeScript files, and Playwright web servers receive LANG and LC_ALL set to en_US.UTF-8.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related issues

  • leon0399/llame issue 173: The pre-commit formatter now filters deleted staged TypeScript files before invoking Prettier.

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main DX-focused changes: migration timestamping, journal-order checks, lint memory fix, hook updates, and locale fixes.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/dx-quick-wins

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces several developer experience (DX) improvements and guardrails. Key changes include configuring Drizzle migrations to use timestamp-prefixed filenames to prevent collisions across parallel branches, and adding a new test suite (migration-journal.spec.ts) to enforce journal invariants (contiguous indices and strictly increasing timestamps). Additionally, the API's tsconfig.json now excludes the dist/ directory to prevent local linting and type-checking out-of-memory (OOM) errors, Jest tests are limited to two workers for stability on memory-constrained machines, the pre-commit Prettier hook is optimized to filter out deleted files, and Playwright is configured to force a UTF-8 locale to prevent SSR crashes. The changelog and documentation have been updated accordingly. There are no review comments, so we have no feedback to provide.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR bundles several developer-experience fixes across the monorepo: migration naming/order safety, reduced local lint/typecheck memory usage, more reliable pre-commit formatting, and more stable local Playwright e2e runs by pinning process locale.

Changes:

  • Make new Drizzle migrations timestamp-prefixed and add a unit test guard to prevent out-of-order migration journal entries.
  • Exclude apps/api/dist from the API tsgo program and cap Jest workers to improve reliability on constrained machines.
  • Fix pre-commit prettier hooking to avoid deleted-file failures and align local Playwright server locale with CI.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
playwright.config.ts Forces LANG/LC_ALL in spawned servers to avoid locale-derived SSR crashes locally.
lefthook.yml Refines the API prettier pre-commit job to only check existing staged files.
CHANGELOG.md Records the DX changes shipped on 2026-07-12.
apps/api/tsconfig.json Excludes dist/ to avoid tsgo/tsgolint program bloat after builds.
apps/api/src/db/migrations/meta/_journal.json Restamps migration 0004 to restore increasing apply-order timestamps.
apps/api/src/db/migration-journal.spec.ts Adds a Jest guard test for journal idx contiguity and strictly increasing when.
apps/api/package.json Pins Jest --maxWorkers=2 for more stable API unit test runs.
apps/api/drizzle.config.ts Sets Drizzle migrations filename prefixing to timestamp.
apps/api/AGENTS.md Documents the new migration naming scheme and the journal invariants/guard.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lefthook.yml
Comment thread CHANGELOG.md Outdated
leon0399 and others added 2 commits July 12, 2026 05:16
…imeouts

Ride-alongs on the DX quick-wins branch, same retrospective:

- rls-test.sh derives its container name from the invocation
  (llame-rls-test-$$) and probes 55440-55490 for a free host port (an
  explicit RLS_TEST_PORT still wins), so two harness runs on one machine no
  longer share llame-rls-test/55432 and kill each other's container
  mid-lifecycle. Cleanup already targeted only $CONTAINER, which is now
  unique per run. host_reachable() is deduplicated onto the same TCP probe.
  Verified end-to-end: with a decoy listener occupying 55440, the script
  chose 55441, ran the full suite green (126 integration + 3 active-runs +
  63 e2e), and removed its own container.
- The Fork-menu vitest tests (chat-item.test.tsx) get an explicit 15s
  timeout: userEvent's pointer sequences against the Radix menu repeatedly
  blew the 5s default under contended local runs (noted in #179; never
  failed in CI).

Closes #164

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtvG7ZN3kWgAMt7ni11CdX
@leon0399
leon0399 merged commit 023bfb7 into master Jul 12, 2026
8 of 9 checks passed
@leon0399
leon0399 deleted the chore/dx-quick-wins branch July 12, 2026 03:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants