deckhearth/.convoys/fix-reset-db-script.md
Randall Stillwell 5f1fa60cba docs: post-convoy cleanup for fix-reset-db-script
Fill in fix-reset-db-script.md § As-shipped with the merged-state record
(PR #25, squash 3ab9bf8, +161/-18) — three bugs atomically resolved by
mirroring setup-neon-db.js post-drop-public-setup shape; CI green
including 5th-consecutive smoke-spec cross-validation; lint-against-
cjs-in-esm-scripts surfaced as new P3 queue entry.

Update ship-readiness.md Queued convoys:
- convert-reset-db-to-esm → RESOLVED (audit-trail kept, not removed)
- purge-weak-creds-from-helpers → scope reduced (reset-db.js half done;
  create-test-users.js + TESTING_GUIDE.md remain)
- lint-against-cjs-in-esm-scripts → NEW (P3 polish; would have caught
  both DPS-B2 and PR #25 at lint time)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-26 22:10:13 -05:00

10 KiB

fix-reset-db-script (P2 hygiene — fold of two queued follow-ups)

Status: RESOLVED 2026-05-26 (PR #25, squash commit 3ab9bf8) Priority: P2 hygiene (not a security blocker; npm run reset-db is dev-only and currently broken on Node 22, so blast radius is low — but the bug pattern is the same as the P0-grade weak-creds shape that drop-public-setup already fixed once) Convoy owner: parent (no architect — fold of two well-scoped follow-ups; single-file fix following an established proven pattern) Opened: 2026-05-25 Merged: 2026-05-26

Problem (3 bugs in 1 file)

scripts/reset-db.js carries three known bugs surfaced by the pick-a-name architect audit (2026-05-24) and ratified for fix in this session:

  1. CJS-in-ESM environment (lines 10, 12, 142): require() calls in an ESM file (package.json "type": "module" since bump-next-js). npm run reset-db throws ReferenceError: require is not defined on Node 22.x. Same bug pattern that hit setup-neon-db.js pre-drop-public-setup Brief 2. Fix is the same fix.
  2. Hardcoded weak admin password (line 143: bcrypt.hash('admin123', 12)): identical anti-pattern to the one drop-public-setup Brief 1 removed from setup-neon-db.js. Should require ADMIN_INITIAL_PASSWORD env var, fail loud if unset.
  3. Password echoed to stdout (line 156: console.log(' Admin Password: admin123')): explicit security anti-pattern. setup-neon-db.js post-drop-public-setup does NOT echo the password — same convention applies here.

Fix (mirror setup-neon-db.js exactly)

The fix shape is fully derived from the post-drop-public-setup scripts/setup-neon-db.js file shipped at b63b509. Verbatim mirror:

  • Replace CJS require('dotenv').config() with ESM import dotenv from 'dotenv'; dotenv.config({ path: '.env.local' })
  • Replace CJS const { neon } = require('@neondatabase/serverless') with ESM import { neon } from '@neondatabase/serverless'
  • Replace inline CJS const bcrypt = require('bcryptjs') (line 142) with top-level ESM import bcrypt from 'bcryptjs'
  • Add the same fail-loud ADMIN_INITIAL_PASSWORD env-var check at the top of the async function resetDatabase() body, with the same helpful error message that points to README.md
  • Replace bcrypt.hash('admin123', 12) with bcrypt.hash(adminPassword, 12)
  • Update the admin email to the post-pick-a-name canonical (admin@deckhearth.com — already correct in the file at line 147, by the B2 sweep)
  • Replace the Admin Password: admin123 log line with Admin user ready (email: admin@deckhearth.com) (matching setup-neon-db.js line 157)
  • Add ON CONFLICT (email) DO NOTHING to the INSERT (matching setup-neon-db.js line 149 — defensive against double-run)

Scope

  • In scope: scripts/reset-db.js only.
  • Out of scope: any other scripts/*.js files (none have the same bugs — setup-neon-db.js already fixed, migration script already ESM, the rest don't ship admin creds).

Why no architect

This is a proven-pattern fold — both convert-reset-db-to-esm and purge-weak-creds-from-helpers were architect-recommended in pick-a-name for "may fold if more such bugs accumulate in helper scripts." All 3 bugs are in 1 file; the fix shape is verbatim-mirror of the post-drop-public-setup setup-neon-db.js. No new decisions; no new precedents; no surface for an architect to add value. Parent applies the fix, runs the bounded checks, opens the PR. If anything surprising surfaces (a 4th bug, a different bcrypt API, etc.), the parent stops and dispatches an architect mid-execution.

Acceptance criteria

  • node --check scripts/reset-db.js exit 0
  • npm run lint exit 1 with 128 problems (baseline preserved; no regression)
  • npm run test:run 21/21 pass (no test surface touched; verification only)
  • Grep: 0 occurrences of require( in scripts/reset-db.js
  • Grep: 0 occurrences of admin123 in scripts/reset-db.js
  • Grep: 0 occurrences of Admin Password in scripts/reset-db.js
  • Grep: ADMIN_INITIAL_PASSWORD referenced (1 hit)

Operator action required pre-merge

  • None pre-merge (no schema change, no env-var addition).
  • Optional post-merge: if the operator wants to verify the fix works end-to-end, they can run npm run reset-db against a non-prod Neon branch (the script drops all tables — DO NOT run against prod). The script will refuse to run if ADMIN_INITIAL_PASSWORD is not set in .env.local, with the same helpful error message setup-neon-db.js already uses.

Known constraints

  • The script is destructive (drops all tables). We do NOT live-test it in this convoy. Boot-the-brief is syntax + lint + vitest only. Live verification is the operator's optional post-merge action.
  • This convoy does NOT add ESLint no-restricted-syntax rule against CJS require() in scripts/**. That would be a separate lint-against-cjs-in-esm-scripts convoy. Surfaced here so future helper scripts don't re-introduce the bug.

Out of scope (queued follow-ups)

  • lint-against-cjs-in-esm-scripts (NEW, P3 polish): add ESLint rule to prevent any future require() in scripts/** once package.json has "type": "module".
  • add-neon-return-shape-rule (P3 polish, surfaced 2026-05-25 by PR #24): codify the neon() vs @vercel/postgres return-shape difference as a rule. May fold into single-sql-client which would eliminate the dual-client problem entirely.

Owns

Parent (single-file proven-pattern fix; no architect or implementer subagent required).

As-shipped

Single squash commit 3ab9bf8 (PR #25, merged 2026-05-26). Parent-owned end-to-end per the convoy spec — no architect, no implementer subagent dispatched. Mirror-the-pattern fix exactly as planned; no mid-execution surprises that would have forced an architect bounce.

Diff: 2 files, +161 / -18. scripts/reset-db.js +55 / -18 (the actual fix); .convoys/fix-reset-db-script.md +124 (the planning document, committed atomically with the fix).

The three bugs, atomically resolved:

  1. CJS-in-ESM (was lines 10, 12, 142): three require() calls replaced by ESM top-level imports (import dotenv from 'dotenv', import { neon } from '@neondatabase/serverless', import bcrypt from 'bcryptjs'). npm run reset-db now executes on Node 22.x instead of throwing ReferenceError: require is not defined.
  2. Hardcoded weak admin password (was line 143): replaced bcrypt.hash('admin123', 12) with bcrypt.hash(adminPassword, 12), where adminPassword is read from process.env.ADMIN_INITIAL_PASSWORD with a fail-loud check at the top of resetDatabase(). The check exits with code 1 BEFORE opening any DB connection, matching setup-neon-db.js's post-drop-public-setup shape verbatim.
  3. Password echoed to stdout (was line 156): replaced the console.log(' Admin Password: admin123') line with console.log(' Admin user ready (email: admin@deckhearth.com)') — exactly the line setup-neon-db.js line 157 uses post-DPS.

Bonus defensive shape: the seed INSERT now uses ON CONFLICT (email) DO NOTHING (matching setup-neon-db.js line 149) so a double-run doesn't error on the existing admin row. This is defensive only; the convoy is destructive (drops all tables first), so the only realistic "existing admin row" scenario is operator confusion between setup-db and reset-db.

Verification (all gates green at merge):

  • node --check scripts/reset-db.js → exit 0
  • npm run lint → 128 problems (baseline preserved, no regression)
  • npm run test:run → 21/21 pass
  • Grep scripts/reset-db.js: 0 require( | 0 admin123 | 0 'Admin Password' | 3 ADMIN_INITIAL_PASSWORD references (docstring + const
    • error msg)
  • CI on PR #25: Lint ✓ (39s) | Vitest 21/21 ✓ (28s) | Playwright smoke 3/3 ✓ (1m5s) | forbidden-endpoints ✓ (5s) | forbidden-cors-headers ✓ (4s) | Vercel preview deploy ✓ | Aggregate gate ✓
  • Screenshot diff: not triggered (script-only PR — paths: filter excludes scripts/**, so the queued tighten-visual-diff-path-filter follow-up correctly did NOT fire here)

Live verification deferred per convoy spec. The script is destructive (drops all tables); we did not exercise it against a Neon branch in this convoy. If the operator wants end-to-end proof, the optional post-merge action is npm run reset-db against a throwaway Neon branch with ADMIN_INITIAL_PASSWORD set (and verify that unsetting it triggers the fail-loud exit with the helpful error message).

Cross-validation finding (organic, not a planned AC). The Playwright smoke 3/3 PASS on a script-only PR confirms that adopt-playwright-smoke's smoke spec is correctly insensitive to scripts/** edits — the deployed preview is unaffected by changes to dev-only utility scripts, and the smoke spec correctly green-lights the deployment. This is the fifth consecutive convoy where the same 3-test smoke spec has defended the auth surface (PR #15 Layout default-user → PR #19 CORS-tighten → PR #20 rate-limiting → PR #21 pick-a-name → PR #25 reset-db-fix) without anyone writing a dedicated test.

Operator action required going forward: none. No new env vars (ADMIN_INITIAL_PASSWORD was already required by setup-neon-db.js post-drop-public-setup; this convoy adds nothing new to the env contract). No schema change. No infra change.

Surfaced follow-up (newly queued): lint-against-cjs-in-esm-scripts (P3 polish) — add an ESLint no-restricted-syntax rule against require( calls in scripts/** once package.json has "type": "module". Would have caught both this convoy AND the drop-public-setup Brief 2 bug at lint time. Filed in .convoys/ship-readiness.md § Queued convoys.

purge-weak-creds-from-helpers scope reduction. This convoy satisfies the scripts/reset-db.js portion of the queued purge-weak-creds-from-helpers follow-up. Remaining scope of that queued convoy: scripts/create-test-users.js (alice/bob test fixtures) and TESTING_GUIDE.md (documents the weak creds). Both are out of scope here per the convoy spec's single-file boundary.