Skip to content

Conversation

@marcodejongh
Copy link
Owner

@marcodejongh marcodejongh commented Jan 4, 2026

Summary

Complete database consolidation from 36 board-specific Aurora tables to 18 unified tables with board_type discriminator.

Changes

Phase 1-5: Unified Tables (Previously Completed)

  • Created 22 unified board_* tables with composite primary keys
  • Migrated all kilter/tension data
  • Updated table-select.ts with UNIFIED_TABLES constant
  • Updated sync functions to use unified tables
  • Updated all query files to use unified tables

Phase 6: Ascents/Bids Migration (This Update)

Key Decision: Switched from Aurora user_id (integer) to NextAuth userId (string) for all ascent/bid data. Data not associated with NextAuth users is dropped.

Files Updated:

  • saveAscent.ts: Write to boardsesh_ticks with NextAuth userId
  • saveAscent/route.ts: Require NextAuth session
  • get-logbook.ts: Read from boardsesh_ticks
  • create-climb-filters.ts: Personal progress filters use boardsesh_ticks
  • holds-heatmap.ts: User data queries use boardsesh_ticks
  • heatmap/route.ts: Use getServerSession for NextAuth
  • user-sync.ts: Remove dual-write to legacy tables
  • aurora-credentials/route.ts: Remove legacy migration call
  • saveClimb.ts: Use unified boardClimbs table
  • beta/route.ts: Use unified boardBetaLinks table
  • view/page.tsx: Use unified boardBetaLinks table
  • slug-utils.ts: Use unified tables

Files Deleted:

  • migrate-user-history.ts: No longer needed
  • migrate-users-cron/route.ts: Cron job no longer needed

Migration Added:

  • 0030_drop_legacy_ascents_bids.sql: Drop kilter_ascents, kilter_bids, tension_ascents, tension_bids

Testing

  • npm run build passes
  • npm run db:migrate runs successfully
  • All query files use unified tables with board_type filtering

Remaining Work

  • Task 7: MoonBoard server-side storage (separate PR)
  • Phase 6b: Drop remaining legacy board-specific tables (after validation period)

🤖 Generated with Claude Code

marcodejongh and others added 5 commits January 4, 2026 15:05
Create 22 unified board_* tables with board_type discriminator column
and migrate all data from kilter_* and tension_* tables:

- Phase 1: board_attempts, board_difficulty_grades (+ MoonBoard seed data)
- Phase 2: board_products, board_sets, board_product_sizes, board_layouts,
  board_holes, board_placement_roles, board_leds, board_placements,
  board_product_sizes_layouts_sets
- Phase 3: board_climbs, board_climb_stats, board_climb_holds,
  board_climb_stats_history, board_beta_links
- Phase 4: board_users, board_circuits, board_circuits_climbs,
  board_walls, board_tags
- Phase 5: board_user_syncs, board_shared_syncs

Data migration verified:
- 435,997 climbs migrated (318K kilter + 118K tension)
- 5.5M climb_holds migrated with deduplication
- All foreign key constraints validated

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add UNIFIED_TABLES constant and helper functions to table-select.ts
- Update shared-sync.ts to use unified tables for:
  - board_attempts, board_difficulty_grades (seeded with MoonBoard data)
  - board_climbs, board_climb_stats, board_climb_stats_history
  - board_climb_holds, board_beta_links
  - board_shared_syncs
- Update user-sync.ts to use unified tables for:
  - board_users, board_walls, board_circuits, board_tags
  - board_user_syncs
  - draft_climbs (uses board_climbs)
- Keep legacy getTable for ascents/bids (phased out, already in boardsesh_ticks)
- All inserts now include boardType, conflict targets include boardType
- All queries filter by boardType

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add Implementation Status Summary table showing:
- Phase 0-5: All complete (migrations applied)
- Task 4-5: Complete (table-select.ts and sync functions)
- Task 6: Pending (query files with raw SQL)
- Task 7 & Phase 6: Planned

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update create-climb-filters.ts:
  - Use board_climb_holds with board_type filter for hold state conditions
  - Use board_product_sizes with board_type filter for tall climbs query
  - Keep legacy getTableName for ascents/bids (phased out)

- Update holds-heatmap.ts:
  - Use board_climb_holds with board_type filter in user stats queries
  - Keep legacy getTableName for ascents/bids (phased out)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ed tables

- Update setter-stats.ts to use UNIFIED_TABLES with board_type filter
- Update queries.ts to use unified table names (board_climbs, board_climb_stats, etc.)
- Eliminate product_sizes JOIN by using getSizeEdges() for edge values
- Mark Task 6 as complete in documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Jan 4, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
boardsesh Building Building Preview, Comment Jan 4, 2026 5:53am

@claude
Copy link

claude bot commented Jan 4, 2026

Claude Review

⚠️ Needs attention - Missing board_type filter in holds-heatmap.ts query joins, potential performance concern with large dataset migration

Issues Found

  1. Missing board_type filter in holds-heatmap.ts:66-69, 92-95 - The innerJoin between climbHolds and tables.climbs doesn't include a board_type filter on the climbHolds side. While tables.climbs is still using the legacy board-specific table (via getBoardTables), the climbHolds table is also from tables which should be the legacy one. However, when fully migrated to unified tables, this join will be missing the board_type condition on the climbHolds table.

  2. Inconsistency between raw SQL and Drizzle ORM queries - queries.ts:32-46 uses raw SQL with board_climbs, board_climb_stats, board_difficulty_grades (unified tables), while holds-heatmap.ts uses getBoardTables() which returns legacy board-specific tables. This creates an inconsistent mix during the migration.

  3. Missing index creation in migration - The migration (0025_shocking_clint_barton.sql) creates tables and foreign keys but doesn't explicitly create indexes on the board_type column which will be heavily used for filtering. While composite primary keys include it, queries filtering by board_type + climb_uuid without the full PK may need additional indexes for performance.

  4. No test coverage for the migration or updated queries - The test plan in the PR description shows critical items unchecked:

    • Test climb search functionality
    • Test sync functionality with Aurora API
    • Test heatmap display
  5. docs/database-consolidation.md - Documentation was updated correctly to reflect implementation status.

- Update create-climb-filters.ts to use UNIFIED_TABLES directly
- Remove `tables` parameter, now uses unified tables internally
- Add board_type filter to baseConditions for all climb queries
- Add getClimbHoldsJoinConditions() helper with board_type filter
- Update holds-heatmap.ts to use UNIFIED_TABLES
- Fix potential stale data issue where sync writes only to unified tables

This ensures heatmap and filter queries read from the same tables
that sync writes to, preventing data inconsistency.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@claude
Copy link

claude bot commented Jan 4, 2026

Claude Review

⚠️ Needs attention - Missing board_type filter on board_climb_holds join causing potential cross-board data leakage; SQL injection risk in raw table name interpolation.

Issues

  1. Missing board_type filter in holds-heatmap.ts:108,126 - Raw SQL queries joining board_climb_holds only filter by ch.board_type in the ON clause but the main climbHolds table query (line 63) doesn't include a standalone board_type WHERE condition on the climbHolds table before the join. The join does include it via getClimbHoldsJoinConditions(), but ensure this is correctly applied.

  2. Potential SQL injection via getTableName() in create-climb-filters.ts:153-154,203-204 - While board_name is validated against SUPPORTED_BOARDS on line 27, the getTableName() function returns a string that's used with sql.identifier(). Verify that getTableName() doesn't perform any string manipulation that could bypass the validation.

  3. Missing board_type in legacy table subqueries in create-climb-filters.ts:159-196 - The personal progress filter subqueries and user logbook selects query legacy kilter_ascents/tension_ascents tables which don't need board_type filter (they're board-specific), but the variable ascentsTable/bidsTable naming suggests unified tables. This is confusing - consider adding a comment clarifying these are legacy tables.

  4. Inconsistent board_type handling in queries.ts:35-36 - The getClimb query correctly filters by board_type on both board_climbs and board_climb_stats, but the board_difficulty_grades join (line 38-39) correctly includes it too. However, the stats query in getClimbStatsForAllAngles (lines 75-76) also handles this correctly.

  5. Documentation not fully updated - The docs/database-consolidation.md marks "Aurora sync continues working" as incomplete ([ ] Aurora sync continues working (requires application layer changes - Task 5)), but the sync functions have been updated. Consider marking this complete.

Minor Notes

  • The migration file 0025_shocking_clint_barton.sql uses DISTINCT ON for deduplication (lines 434-437) which is PostgreSQL-specific - acceptable given the project uses PostgreSQL, but worth noting.
  • No tests included for the new table-select.ts utility functions or the migration.

## Changes

### Ascent/Bid Data Migration
- All ascent/bid reads now use boardsesh_ticks table
- All ascent/bid writes now use boardsesh_ticks table
- Switched from Aurora user_id (integer) to NextAuth userId (string)
- Removed dual-write pattern from user-sync.ts

### Files Updated
- saveAscent.ts: Write to boardsesh_ticks with NextAuth userId
- saveAscent/route.ts: Require NextAuth session, pass user.id
- get-logbook.ts: Read from boardsesh_ticks
- create-climb-filters.ts: Personal progress uses boardsesh_ticks
- holds-heatmap.ts: User data queries use boardsesh_ticks
- heatmap/route.ts: Use getServerSession for NextAuth
- user-sync.ts: Remove legacy table writes
- aurora-credentials/route.ts: Remove migration call

### Files Deleted
- migrate-user-history.ts: No longer needed
- migrate-users-cron/route.ts: Cron job no longer needed

### Migration
- 0030_drop_legacy_ascents_bids.sql: Drop kilter/tension ascents/bids tables

### Additional Fixes
- saveClimb.ts: Use unified boardClimbs table
- beta/route.ts: Use unified boardBetaLinks table
- view/page.tsx: Use unified boardBetaLinks table
- slug-utils.ts: Use unified tables

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@claude
Copy link

claude bot commented Jan 4, 2026

Claude Review

Ready to merge - Minor issues noted below, but nothing blocking.

Issues Found

  1. packages/web/app/lib/db/queries/util/table-select.ts:212-234 - Dead code: UNIFIED_TABLES constant includes kilter/tension legacy imports and BOARD_TABLES mapping that are no longer used. The legacy table imports (lines 4-40) and BOARD_TABLES (lines 94-135) should be removed to avoid confusion.

  2. packages/web/app/lib/data-sync/aurora/user-sync.ts:500-504 - The code silently skips syncing ascents/bids when no NextAuth user mapping exists, but logs a warning. Consider whether this should be a more visible error for debugging sync issues.

  3. packages/db/drizzle/0030_drop_legacy_ascents_bids.sql - Irreversible migration drops 4 tables. Ensure data has been validated as successfully migrated to boardsesh_ticks before deploying.

Documentation Check

✅ Documentation updated in docs/database-consolidation.md - status checkboxes updated appropriately.

@marcodejongh marcodejongh merged commit 35efb89 into main Jan 4, 2026
4 of 7 checks passed
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.

2 participants