Review: Update board API to use board notes instead of boards table

review-316-2026-03-24 Review

review ready

Verdict: READY

Re-review after architectural decisions posted in comment #6640. All three NEEDS_REFINEMENT blockers resolved.

Template Completeness

  • [x] Lineage
  • [x] Repo
  • [x] User Story
  • [x] Context
  • [x] File Targets
  • [x] Acceptance Criteria
  • [x] Test Expectations
  • [x] Constraints
  • [x] Checklist
  • [x] Related

File Targets

  • [x] src/pal_e_docs/routes/boards.py — verified: exists, 636 lines, 14 @router endpoints. _board_to_out (line 26) and _item_to_out (line 46) are the core rewrite targets.
  • [x] src/pal_e_docs/schemas.py — verified: BoardOut at line 269, BoardItemOut at line 243. Additive fields (note_id on BoardOut, board_note_id on BoardItemOut) are straightforward schema additions.
  • [x] src/pal_e_docs/routes/notes.py — correctly listed as do-not-touch
  • [x] src/pal_e_docs/models.py — correctly listed as do-not-touch; BoardItem.board_note_id FK confirmed at line 259 with relationship at line 282
  • [x] scripts/migrate_boards_to_notes.py — verified: exists, prerequisite documented in Decision 2

Repo Placement

OK — issue filed on forgejo_admin/pal-e-docs, which owns boards.py and schemas.py. Single-repo change.

Decisions Resolved

# Blocker Decision Verified
1 Unresolved architecture (keep routes vs. alias) Keep all 14 <code>/boards/{slug}</code> routes at same URLs, query <code>notes WHERE note_type='board'</code> internally. Boards table stays during this ticket; removal is #199. Sound — all 14 endpoints confirmed in boards.py, no route changes needed. Clean separation from #199.
2 Migration script prerequisite undocumented Agent must document prerequisite in PR body. Code must handle NULL board_note_id gracefully (log warning, skip). Deploy order: run script then deploy. Sound — <code>scripts/migrate_boards_to_notes.py</code> exists. NULL handling is a safety net the agent can implement in <code>_item_to_out</code> and query filters.
3 Response contract ambiguous (board_id/note_id) Additive: BoardOut gains <code>note_id</code> (board note ID), existing <code>id</code> stays (boards table ID). BoardItemOut keeps <code>board_id</code>, gains <code>board_note_id</code>. All existing fields preserved. Final cleanup deferred to #199. Sound — test suite (1010 lines) does not assert <code>board_id</code> on BoardItemOut responses. Additive fields won't break existing tests or consumers (SDK, MCP, frontend).

Dependencies

  • #195 (Add board to NoteType) — board item #314, column: done. Prerequisite satisfied. Confirmed: "board" exists in schemas.py NoteType literal (line 23).
  • #196 (Add board_note_id FK) — board item #315, column: done. Prerequisite satisfied. Confirmed: FK at models.py line 259.
  • Migration scriptscripts/migrate_boards_to_notes.py exists, must run before deploy. Decision 2 documents this.
  • #198 (MCP/SDK/hooks update) — board item #317, backlog, position 3. Downstream of this ticket. Correctly sequenced.
  • #199 (Drop legacy boards table) — board item #318, backlog, position 4. Final cleanup. Correctly sequenced.
  • #208 (Refactor sync_board) — board item #327, backlog. Independent concern (plan discovery from notes vs. board items). No ordering conflict with this ticket.

Acceptance Criteria

Updated acceptance criteria from comment #6640 are sufficient for agent execution:
  • [x] GET /boards returns notes where note_type="board" internally, same response shape + note_id — testable
  • [x] GET /boards/{slug}/items queries via board_note_id, same response shape + board_note_id — testable
  • [x] All 14 endpoints work — now backed by the specific architecture decision (keep routes, query notes internally)
  • [x] sync_board works with board_note_id path — testable
  • [x] NULL board_note_id handled gracefully — testable (create board item without migration, verify warning not crash)
  • [x] PR documents migration prerequisite — verifiable in PR body
Optional (non-blocking): Cross-board endpoints (GET /boards/backlog/items, GET /boards/activity) still join Board→Project. The agent should switch these joins to Note→Project via board_note_id. This is implicit in "all 14 endpoints work" but could be called out explicitly. Not a blocker — the agent will encounter these joins when rewriting.

Blast Radius

  • Test suite — 1010 lines, 40+ test functions. No direct assertions on board_id in response payloads. Additive schema change (new fields, existing preserved) minimizes breakage risk. Tests create boards via POST /boards; the create endpoint must produce both a Note and a Board row during transition.
  • SDK/MCP/frontend — downstream consumers unaware of board_note_id. Additive response contract means zero breakage. Full consumer update deferred to #198.
  • sync_board / sync_issues — both currently query Board.id. Must switch to board_note_id-based lookups. Covered by "all 14 endpoints."
  • _board_to_out / _item_to_out helpers — core rewrite targets. Agent will need to map Note fields to BoardOut (note.id→note_id, board.id→id, note.slug→slug) and add board_note_id to BoardItemOut. Straightforward from Decision 3.

Recommendation

No action needed — ticket is READY for agent execution. All three blockers resolved with clear, verifiable decisions. The issue body should ideally be updated to incorporate the decisions from comment #6640, but the agent can read both issue body and comments, so this is not a blocker.
One optional improvement: explicitly call out cross-board endpoints (backlog/items, activity) in acceptance criteria. The agent will discover these during implementation regardless.