Skip to content

refactor: adopt in-TS startup-migrations as schema convention (PR #88 Phase 3)#89

Merged
petterlindstrom79 merged 1 commit into
mainfrom
refactor/in-ts-startup-migrations-convention
May 11, 2026
Merged

refactor: adopt in-TS startup-migrations as schema convention (PR #88 Phase 3)#89
petterlindstrom79 merged 1 commit into
mainfrom
refactor/in-ts-startup-migrations-convention

Conversation

@petterlindstrom79
Copy link
Copy Markdown
Member

Summary

  • Phase 3 Harden of the PR refactor: scheduled_testing_eligible column + derivation bridge (PR A) #88 bug fix. Codifies the in-TS-block convention that's already running in prod, retires the misleading Drizzle SQL surface, closes the deploy-order regression class.
  • Block 0066 now owns the scheduled_testing_eligible column lifecycle: ALTER TABLE ADD COLUMN IF NOT EXISTS + existing reconciliation UPDATE. Renamed runMigration0066_ensureEligibilityColumnAndReconcile. Idempotent on existing prod; functional on fresh DBs.
  • Deleted: apps/api/drizzle/ (63 SQL files + meta + README), apps/api/drizzle.config.ts, check-migration-prefixes.mjs, verify-migration-rename.ts, the matching CI step, db:* package scripts, drizzle-kit devDependency. Updated schema-validator.ts fix hint and schema.ts external-managed comment.

Why now

PR #88 (commit cb4e8c1) added apps/api/drizzle/0063_decouple_scheduled_testing_eligibility.sql expecting the file to apply at deploy time. The Dockerfile CMD runs node dist/index.jsrunStartupMigrations() (in-TS blocks) → never invokes drizzle-kit migrate. Deploy healthcheck-failed when in-TS block 0066 referenced the missing column. Phase 1 (Contain) applied the column manually via railway ssh; Phase 2 (Journal course-correction) named the failure pattern. This PR closes the class.

Audit-phase correction (vs Phase 2 finding)

Phase 2's stated reason ("__drizzle_migrations doesn't exist in prod") was wrong — the table EXISTS under the drizzle schema (not public) with 60 historical entries; last applied ~2026-04-04. The conclusion (Drizzle SQL files not part of deploy pipeline; in-TS is the working convention) was correct; the reason for it was wrong. Corrected understanding actually strengthens Option B: the SQL files were a manual-run footgun (npm run db:migrate against prod would have hit non-idempotent DDL errors). Detailed in the handoff note.

Notion

Verification

  • Type-check: clean (pre-existing routes/mcp.ts strale-mcp/tools errors unchanged).
  • vitest: 540 passing / 11 skipped / 1 pre-existing failure (app.classify-error.test.ts).
  • BLOCKS-list canonical test passes with renamed block.
  • Block 0066 idempotency: ADD COLUMN IF NOT EXISTS is no-op on existing prod (column exists from Phase 1 manual recovery); reconciliation UPDATE filters IS DISTINCT FROM so re-runs match zero rows.

Closing-steps rule walk

  • Rule 1 (Audit-first): present and corrected mid-stream when README evidence contradicted the prior session's Phase 2 finding.
  • Rule 3 (Local vs structural): this IS the structural fix. No further follow-up named.
  • Rule 7 (Bug fix framework): Phase 3 Harden of PR refactor: scheduled_testing_eligible column + derivation bridge (PR A) #88. Phases 1+2 shipped earlier; structural gate produced here.
  • Rule 8 (Schema-change deployment): this PR amends Rule 8 itself — convention is now in-TS blocks; no ## After deployment migrate step.
  • Rule 11 (DEC supersession): does NOT fire — DEC-20260511-C refines DEC-20260420-A and DEC-20260420-B without superseding.
  • Rule 14 (Deploy mechanism verification): post-deploy verify by reading Railway boot logs and confirming block 0066 fires with its expected outcome.
  • Rule 16 (Self-merge): enabled. Will self-merge on CI green.

Test plan

  • Block 0066 modified, BLOCKS-list test updated
  • Full vitest pass (540 tests; pre-existing failure unchanged)
  • DEC + To-do + Working rules page updated in Notion
  • After Railway deploy: boot logs show block 0066 fires with "column ensured; no rows to reconcile (already aligned)" (column already exists in prod from Phase 1)
  • After Railway deploy: /health/deep returns 200

🤖 Generated with Claude Code

…phase 3 harden)

Codify the in-startup-migrations.ts pattern as the official schema-change
convention for the api service. Retire the dual-track Drizzle SQL surface
that misled PR #88 into a healthcheck failure.

Background. PR #88 (2026-05-11) added apps/api/drizzle/0063_*.sql expecting
the file to apply at deploy time. Nothing in this codebase applies Drizzle
SQL files anywhere in the deploy pipeline — the Dockerfile CMD runs
node dist/index.js, which calls runStartupMigrations() (in-TS blocks),
which never invokes drizzle-kit migrate. The drizzle.__drizzle_migrations
tracking table exists under the `drizzle` schema with 60 historical
entries but its last apply was ~2026-04-04; since then, in-TS blocks have
been doing the schema work. PR #88's healthcheck failed because in-TS
block 0066 referenced the new column before anything created it.

Phase 1 (Contain) and Phase 2 (Understand) shipped 2026-05-11. This is
Phase 3 (Harden) per DEC-20260511-C.

Changes
- apps/api/src/lib/startup-migrations.ts — block 0066 renamed to
  runMigration0066_ensureEligibilityColumnAndReconcile and the SQL now
  starts with ALTER TABLE ADD COLUMN IF NOT EXISTS before the existing
  reconciliation UPDATE. Idempotent on existing prod (column already
  there from Phase 1 manual recovery); functional on fresh DBs.
- apps/api/drizzle/ — directory deleted (63 SQL files + meta/ +
  README.md).
- apps/api/drizzle.config.ts — deleted.
- apps/api/scripts/check-migration-prefixes.mjs — deleted (vestigial
  with the dir gone).
- apps/api/scripts/verify-migration-rename.ts — deleted (forensic script
  for an already-resolved 0046 collision).
- .github/workflows/ci.yml — check-migration-prefixes step removed.
- apps/api/package.json — db:generate/db:migrate/db:push scripts removed,
  drizzle-kit devDependency removed.
- package-lock.json — refreshed.
- apps/api/src/lib/schema-validator.ts — fix hint now points at
  startup-migrations.ts blocks (was: cd apps/api && npx drizzle-kit migrate).
- apps/api/src/db/schema.ts — integrity_hash_status external-managed
  comment reworded (was referencing drizzle-kit generate).
- apps/api/src/lib/startup-migrations.test.ts — BLOCKS-list assertion
  updated to the renamed block.
- handoff/_general/from-code/2026-05-11-in-ts-migrations-convention-pr88-phase3.md
  — session summary + drift inventory + PR B implication note.

Verification
- Type-check: clean (pre-existing routes/mcp.ts strale-mcp/tools errors
  unchanged).
- vitest: 540 passing / 11 skipped / 1 pre-existing failure
  (app.classify-error.test.ts, unrelated).
- BLOCKS-list canonical test passes with renamed block.

DEC-20260420-A (hand-written discipline) and DEC-20260420-B (schema.ts
sync) are preserved — we still hand-write, schema.ts edits still ship
alongside. Neither is superseded by DEC-20260511-C. Rule 11 does not
fire.

Closes Notion P1 To-do
35d67c87082c810bbe04e597c38f6d89 (Harden schema-migration deploy
pipeline). The canonical Rule 8 in cc-prompts skill is amended in
lockstep; the Notion Working rules page header references DEC-20260511-C.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@petterlindstrom79 petterlindstrom79 merged commit 3e60d5d into main May 11, 2026
1 check passed
petterlindstrom79 added a commit that referenced this pull request May 11, 2026
Two reference-content handoffs from earlier 2026-05-11 sessions. Per
Rule G (handoff note hygiene, DEC-20260510-A), notes with substantive
"Landed" / "Outcome" / runbook content are promoted; pure session
narration is deleted.

Promoted:
- 2026-05-11-decouple-scheduled-testing-eligible-pr-a.md (PR #88 PR A
  shipped state — landed-list, decisions locked, follow-ups).
- 2026-05-11-haiku-cost-leak-audit-contain-cleanup.md (4-PR incident
  arc: #84/#85/#86/#87 with outcomes, audits, decisions).

Deleted (not in this commit; rm'd locally) the third 2026-05-11 note:
- 2026-05-11-pr88-deploy-recovery-and-phase3-halt.md
  Pure session-progress narration. Phase 3 halt was resolved by PR #89
  (merged 2026-05-11); content superseded by DEC-20260511-C + PR #89's
  handoff note (2026-05-11-in-ts-migrations-convention-pr88-phase3.md).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
petterlindstrom79 added a commit that referenced this pull request May 13, 2026
Five from-code handoffs authored during the 2026-05-13 sessions are
staged but uncommitted; promoting to tracked per the standing chore
PR pattern (Rule G handoff-promotion).

Contents:
- 2026-05-13-beacon-hosting-verification.md — verifies scan.strale.io is on Vercel, high confidence
- 2026-05-13-drizzle-quirks-verification.md — original Outcome B was wrong; correction header added mid-session (PR #89 had fully shipped DEC-20260511-C; the report was reading stale branch state from the strale trunk)
- 2026-05-13-lovable-cancellation-audit.md — verdict SAFE TO CANCEL; zero runtime dependencies confirmed across DNS, headers, webhooks, CI, commit activity
- 2026-05-13-structural-backstops-and-readiness-audits.md — session-arc summary covering the deploy-health monitor + capabilities list↔detail contract invariant PRs
- 2026-05-13-lovable-cancellation-cleanup-execution.md — session-arc summary of the cleanup PRs (#9 frontend, #105 backend) + the deploy-health monitor catching PR #9's silent CF Pages failure + the bun-lockfile hotfix (#10)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant