feat: add board_note_id FK to board_items + data migration #207

Merged
forgejo_admin merged 1 commit from 196-board-note-id-fk into main 2026-03-24 17:55:31 +00:00

Summary

  • Adds nullable board_note_id FK column on board_items pointing to notes.id, enabling transition from board-centric to note-centric kanban
  • Includes idempotent data migration script that creates note_type="board" notes for all 13 existing boards and backfills the new FK
  • Dual FK period: both board_id and board_note_id remain valid during transition

Changes

  • alembic/versions/q7l8m9n0o1p2_add_board_note_id_to_board_items.py — Alembic migration adding board_note_id column, index, and FK constraint to board_items
  • src/pal_e_docs/models.py — Added board_note_id mapped column and board_note relationship to BoardItem
  • scripts/migrate_boards_to_notes.py — Idempotent data migration script that creates board notes, maps board.id to note.id, and backfills board_note_id on all board_items

Test Plan

  • Tests pass locally — all 639 tests pass, no regressions
  • After merge, run Alembic migration: alembic upgrade head
  • Run data migration script: python /tmp/migrate_boards_to_notes.py
  • Verify 13 notes with note_type='board' exist
  • Verify all board_items have board_note_id populated

Review Checklist

  • Passed automated review-fix loop
  • No secrets committed
  • No unnecessary file changes
  • Commit messages are descriptive
  • Closes #196
  • Depends on: #201 (board note_type — merged)
  • Next: #197 (API routes use board_note_id), #199 (drop board_id)
## Summary - Adds nullable `board_note_id` FK column on `board_items` pointing to `notes.id`, enabling transition from board-centric to note-centric kanban - Includes idempotent data migration script that creates `note_type="board"` notes for all 13 existing boards and backfills the new FK - Dual FK period: both `board_id` and `board_note_id` remain valid during transition ## Changes - `alembic/versions/q7l8m9n0o1p2_add_board_note_id_to_board_items.py` — Alembic migration adding `board_note_id` column, index, and FK constraint to `board_items` - `src/pal_e_docs/models.py` — Added `board_note_id` mapped column and `board_note` relationship to `BoardItem` - `scripts/migrate_boards_to_notes.py` — Idempotent data migration script that creates board notes, maps board.id to note.id, and backfills `board_note_id` on all board_items ## Test Plan - [x] Tests pass locally — all 639 tests pass, no regressions - [ ] After merge, run Alembic migration: `alembic upgrade head` - [ ] Run data migration script: `python /tmp/migrate_boards_to_notes.py` - [ ] Verify 13 notes with note_type='board' exist - [ ] Verify all board_items have board_note_id populated ## Review Checklist - [x] Passed automated review-fix loop - [x] No secrets committed - [x] No unnecessary file changes - [x] Commit messages are descriptive ## Related - Closes #196 - Depends on: #201 (board note_type — merged) - Next: #197 (API routes use board_note_id), #199 (drop board_id)
feat: add board_note_id FK to board_items + data migration
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
652e997750
Adds board_note_id (nullable FK to notes.id) on board_items table.
Includes Alembic migration for the new column and index.
Creates scripts/migrate_boards_to_notes.py to:
  - Create board notes (note_type=board) for all existing boards
  - Backfill board_note_id from board->note mapping
Dual FK period: both board_id and board_note_id valid.
Script is idempotent — safe to re-run.

Closes forgejo_admin/pal-e-docs#196

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Author
Owner

QA Review -- PR #207

Alembic Migration (q7l8m9n0o1p2)

  • Correctly chains from p6k7l8m9n0o1 (latest head)
  • Adds nullable Integer column, index, and named FK constraint -- clean
  • Downgrade reverses in correct order (constraint, index, column)
  • Named constraint fk_board_items_board_note_id is explicit, which is good for Postgres (production target)

Model Changes (models.py)

  • board_note_id column: nullable FK to notes.id, indexed -- matches migration
  • board_note relationship: uses explicit foreign_keys=[board_note_id] to avoid ambiguity with existing Note FKs -- correct
  • No back_populates on Note side -- appropriate since reverse navigation is not needed yet
  • Existing board_id FK untouched -- dual FK period as specified
  • Schemas (BoardItemOut, BoardItemCreate) not modified -- correct per scope (#197 owns API changes)

Data Migration Script (scripts/migrate_boards_to_notes.py)

  • Idempotent: checks for existing board notes by slug+note_type before creating, skips items with board_note_id already set
  • Handles slug collisions with non-board notes (warns and skips)
  • Uses session.flush() to get note IDs before backfilling items -- correct
  • Transaction safety: single commit at end, rollback on exception
  • sys.path.insert pattern matches existing backfill_blocks.py convention
  • Env var resolution: PALDOCS_DATABASE_URL first, DATABASE_URL fallback -- matches app config pattern
  • Run instructions in docstring are clear (kubectl cp + exec)

Test Results

  • All 639 tests pass with zero failures
  • No schema changes means no test updates needed (board_note_id is nullable, defaults to None)

Scope Compliance

  • Does NOT modify routes/boards.py (deferred to #197)
  • Does NOT drop board_id (deferred to #199)
  • Does NOT run the migration on production -- script only

No Issues Found

VERDICT: APPROVE

## QA Review -- PR #207 ### Alembic Migration (`q7l8m9n0o1p2`) - Correctly chains from `p6k7l8m9n0o1` (latest head) - Adds nullable Integer column, index, and named FK constraint -- clean - Downgrade reverses in correct order (constraint, index, column) - Named constraint `fk_board_items_board_note_id` is explicit, which is good for Postgres (production target) ### Model Changes (`models.py`) - `board_note_id` column: nullable FK to `notes.id`, indexed -- matches migration - `board_note` relationship: uses explicit `foreign_keys=[board_note_id]` to avoid ambiguity with existing Note FKs -- correct - No `back_populates` on Note side -- appropriate since reverse navigation is not needed yet - Existing `board_id` FK untouched -- dual FK period as specified - Schemas (`BoardItemOut`, `BoardItemCreate`) not modified -- correct per scope (#197 owns API changes) ### Data Migration Script (`scripts/migrate_boards_to_notes.py`) - Idempotent: checks for existing board notes by slug+note_type before creating, skips items with board_note_id already set - Handles slug collisions with non-board notes (warns and skips) - Uses `session.flush()` to get note IDs before backfilling items -- correct - Transaction safety: single commit at end, rollback on exception - `sys.path.insert` pattern matches existing `backfill_blocks.py` convention - Env var resolution: PALDOCS_DATABASE_URL first, DATABASE_URL fallback -- matches app config pattern - Run instructions in docstring are clear (kubectl cp + exec) ### Test Results - All 639 tests pass with zero failures - No schema changes means no test updates needed (board_note_id is nullable, defaults to None) ### Scope Compliance - Does NOT modify `routes/boards.py` (deferred to #197) - Does NOT drop `board_id` (deferred to #199) - Does NOT run the migration on production -- script only ### No Issues Found VERDICT: **APPROVE**
forgejo_admin deleted branch 196-board-note-id-fk 2026-03-24 17:55:31 +00:00
Sign in to join this conversation.
No description provided.