The demo seeder created bills + categories only, so nearly every page
rendered empty; clear-demo had a data-loss bug and error-protection gaps.
Seed now populates every user-facing surface: bills with autopay states,
3-6 months of payment history (with drift + debt paydown), monthly
skips/overrides/snoozes/notes, a demo bank source + accounts + matched/
unmatched/subscription transactions, categorised spending + budgets +
rules, planning income/starting-amounts, category groups, an active
snowball plan (+ extra-payment), merchant rules, and a calendar feed —
so a new user can explore Tracker (incl. overdue/drift), Analytics,
Summary, Spending, Snowball, Payoff, Transactions, Matches, Subscriptions,
Banking and Calendar with realistic data.
Correct removal (the emphasis):
- Migration v1.07 adds an is_seeded marker to the 12 user-scoped,
non-cascading demo tables (bill-children cascade with their seeded bill).
- clearSeededDemoData() (new, in userDataService alongside eraseUserData)
removes ONLY seeded rows in one transaction, child->parent, and keeps a
seeded category if a user's own bill still references it.
- Fixes the category collision: seed marks ONLY categories it creates, so
clear no longer deletes the user's default categories / uncategorises
their real bills.
Error protection: idempotency guard on seeded (not all) bills -> 409
instead of a fake "created 0"; whole seed wrapped in one transaction
(atomic); rate-limit + audit + standardizeError on all three routes;
seeded-status reports payments/transactions counts; dead --force removed.
Client copy/counts corrected.
Verified: typecheck/check clean; server suite 232/232 (+6 new); client
typecheck/lint/build clean; e2e probe 17/17; on a COPY of the real prod
DB the v1.07 migration applies (integrity ok) and a seed->clear round-trip
on the data-rich user restores their real data byte-for-byte with no
orphans.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Completes the services/ TypeScript migration (0 .js remain). These four are the
largest, most dynamic modules (subscription-catalog matcher, xlsx/spreadsheet
parser, OIDC protocol client, SQLite user-DB importer); converted to .cts with a
top-of-file @ts-nocheck so they run via Node type-stripping now, with rich typing
deferred as an incremental follow-up. The other ~46 services are fully typed.
Behavior-preserving. Verified: typecheck:server 0, check:server 0, suite 226/226.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Continues the incremental server TS migration (after utils/*.mts and
paymentValidation.cts): converts 10 services to .cts (CommonJS TypeScript,
run natively via Node type-stripping — no build step), type-checked by
tsconfig.server.json.
Migrated: totpService, amountSuggestionService, encryptionService,
paymentAccountingService, statusService, aprService, driftService,
analyticsService, spendingService, billMerchantRuleService.
- types/db.d.ts: shared minimal better-sqlite3 Db/Statement types (the lib
ships none), reused across the migrated modules.
- Every require of a migrated service updated to the explicit .cts extension
(extensionless require does not resolve .cts) across routes / services /
db migrations / server.js / workers / tests — require-only changes.
- package.json: check:server find pattern now includes *.cts (it silently
skipped .cts before, so paymentValidation.cts had no node --check gate).
Behavior-preserving (types only). Verified: typecheck:server 0,
check:server 0, full server suite 226/226, real boot → /api/health 200.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Second half of the migrations split: moved the ~830-line reconcileLegacyMigrations
array into db/migrations/legacyReconcileMigrations.js via the same
buildLegacyReconcileMigrations(deps) factory (same injected db + helpers; no
inline requires in this one). database.js is now 1,297 lines — down from 4,174
at the start of IMP-CODE-02 (~69%).
Added tests/migrationModules.test.js locking the invariants database.js depends
on: both modules build, versioned versions are unique, and every legacy-reconcile
version has a versioned counterpart (the drift the in-app assertion warns about).
Verified: full suite 125 pass; fresh DB applies all 79 migrations (reconcile
path included — a fresh schema.sql DB triggers it) and is idempotent; real prod
DB copy (v1.06) migrates as a no-op with data intact and no version-sync drift.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
db/database.js carried a ~1,740-line inline `const migrations = [...]` inside
runMigrations(). Moved it to db/migrations/versionedMigrations.js as a factory,
buildVersionedMigrations(deps), injected with the live `db` connection and the
few schema helpers the migration bodies close over (isValidColumnName,
isValidSqlDefinition, ensureTransactionFoundationSchema, and the four
run*Migration helpers). Behavior is identical — the run/check closures resolve
the same bindings, just passed in rather than captured.
Fixed the two path references that broke by moving one directory deeper: the
inline require('../services/...') calls and the __dirname docs JSON require now
use ../../.
database.js: 3,859 → 2,119 lines. Verified: full server suite 122 pass; a fresh
DB applies all 79 migrations and is idempotent on a second boot; the real prod
DB copy (v1.06) migrates as a clean no-op with data intact and no version-sync
drift between the runMigrations and reconcileLegacyMigrations version lists.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>