Updated Carousel Dashboard documentation to reflect finalized architecture decisions and resolve discrepancies between plan and verification checklist. ## CAROUSEL_DASHBOARD_PLAN.md Changes ### Added Phase 4.5: Collections Preview Endpoint - Documented why preview endpoint is required (web UI + mobile apps) - Explained why client-side preview is a bad idea (download entire library, code duplication, maintenance nightmare) - Added full PreviewCollection handler implementation - Added Bruno test specification ### Enhanced Phase 7: Router Registration & Config Setup - Renamed from "Router Registration" to "Router Registration & Config Setup" - Added Step 1: Update router.go Config struct with line numbers - Added Step 2: Update main.go initialization with line numbers - Added Step 3: Update test_helpers.go with line numbers - Added explanation: Why both DashboardService AND DashboardHandler? ### Updated Phase 10.5.4: Collections Preview Endpoint - Referenced Phase 4.5 (endpoint already implemented earlier) - Clarified needed for web UI AND mobile apps - Noted no additional work needed ### Added Phase 10.6: Implementation Checklist - 30+ checklist items with file paths and verification commands - Organized by layer (Database, Service, Handler, Router, Templates, TypeScript, Tests, Docs) - Added Build & Verification section - Added Timeline Estimate (20-26 hours) - Added Post-Implementation Tasks ## CAROUSEL_DASHBOARD_VERIFICATION_CHECKLIST.md Changes ### Added Clarification Section (at top) - Explained all discrepancies between plan and checklist - Preview endpoint IS in plan (Phase 4.5) - Custom Section Builder IS in plan (Phase 10.5.2 and 10.5.3) - Service method names - Plan is correct - Config struct - Documented with exact line numbers - DashboardService vs DashboardHandler - Explained why both needed ### Updated Service Method Names (Section 3.2) Changed to match plan's actual implementation: - GetDashboardSections (not GetSectionItems) - filterHiddenCollections (not filterHiddenSections) - reorderCollections (not reorderSections) - sortByPriority (new method) - getUserCollectionItems (not getCollectionSections) - getCollectionItemsByQueryType (renamed) ### Enhanced Config Verification (Section 6.4) Added exact line numbers for all 3 files: - internal/router/router.go lines 58-59 - cmd/server/main.go lines 123-124, 172-173 - cmd/server/tests/test_helpers.go lines 419-420, 458-459 ### Updated Preview Endpoint Section (Section 6.3) Added clear explanation of why endpoint is REQUIRED and why NOT client-side. ### Clarified Custom Section Builder (Sections 8.5, 9.4) Both now explicitly state "IS in the plan (Phase 10.5)" ## docs/developer/api/dashboard.md Changes Updated API documentation to match new unified collections architecture: - Terminology: "smart sections" → "system collections" - Field: `type: string` → `is_system: boolean` - Field: `id` → `media_item_id` for books - Request: `hidden_sections` → `hidden_collections` - Request: `section_order` → `collection_order` - Removed: "in-progress" and "unread" smart sections - Added: Update Dashboard Preferences endpoint - Added: Restore System Collection endpoint - Updated: Example responses with new field names and types - Updated: Error responses table ## Impact These changes clarify: 1. Preview endpoint is required for both web UI custom section builder and mobile apps 2. Custom Section Builder IS a major feature in the plan (not missing) 3. Service method names use "collections" terminology consistently 4. Config struct updates are clearly documented with exact line numbers (3 files only) 5. Why both DashboardService AND DashboardHandler are needed in Config All documentation now accurately reflects the finalized Carousel Dashboard architecture.
108 KiB
Carousel Dashboard Plan Verification Checklist
Use this checklist to comprehensively audit the Carousel Dashboard Plan in a single pass. Each item includes verification steps to confirm accuracy.
⚠️ CLARIFICATION: Plan vs Checklist Discrepancies Resolved
After thorough analysis, the following discrepancies have been resolved:
1. Preview Endpoint - IS in the plan
- Checklist concern: "Missing Collection Preview Endpoint"
- Reality: Endpoint is specified in Phase 4.5 of the plan
- Why required: Web UI custom section builder + future mobile apps need to preview filter rules before saving
- Location:
internal/handlers/collections.go-PreviewCollectionmethod - Route: POST
/api/collections/preview - Documentation: Explained in Phase 4.5 why client-side preview is a bad idea
2. Custom Section Builder - IS in the plan
- Checklist concern: "Missing Custom Builder sections 10.5.2 and 10.5.3"
- Reality: Both sections exist in the plan:
- 10.5.2: Custom Section Builder Template (
templates/custom_section.templ) - 10.5.3: Custom Section Builder TypeScript (
web/src/custom-section-builder.ts)
- 10.5.2: Custom Section Builder Template (
- This is a major feature with 13+ filter fields
3. Service Method Names - Plan is correct
- Checklist expects:
GetSectionItems,filterHiddenSections,reorderSections - Plan implements:
GetDashboardSections,filterHiddenCollections,reorderCollections - Plan names are better: More descriptive, uses "collections" terminology consistently
- Action taken: Updated checklist to match plan's actual method names
4. Config Struct Updates - Documented with line numbers
- Concern: "Touching Config breaks dozens of functions"
- Reality: Only 3 files need updates, all with exact line numbers specified:
internal/router/router.goline 58-59cmd/server/main.golines 123-124, 172-173cmd/server/tests/test_helpers.golines 419-420, 458-459
- 18 router functions accept
*Configbut don't need changes (just receive pointer)
5. DashboardService in Config - Why both Service and Handler?
- DashboardService: Used by SSR routes (frontend.go) for data fetching
- DashboardHandler: Used by API routes (dashboard.go) for JSON endpoints
- Mobile apps: Will use DashboardHandler
- Web UI: Uses both (SSR via Service, interactions via Handler)
⚠️ CRITICAL DISTINCTION: Type Duplication
Before using this checklist, understand this important guideline:
❌ UNACCEPTABLE: Duplicate Go Types
// WRONG: Creating duplicate types in Go templates package
package templates
type SectionData struct { ... } // DON'T DO THIS - duplicates handlers.SectionData
✅ ACCEPTABLE: TypeScript Type Recreation (MUST BE COMPLETE)
// OKAY: Recreating types in TypeScript .d.ts files
// Go's pgtype fields cannot auto-convert, so manual recreation is necessary
// CRITICAL: Must include ALL fields from Go handler (no partial types)
interface SectionData {
id: string; // matches Go's json:"id"
is_system: boolean; // matches Go's json:"is_system" (was "type" string)
title: string; // matches Go's json:"title"
description: string; // matches Go's json:"description"
icon: string; // matches Go's json:"icon"
items: BookInfo[]; // matches Go's json:"items"
view_all_url: string; // matches Go's json:"view_all_url"
priority: number; // matches Go's json:"priority"
}
// All 8 fields from Go struct included - COMPLETE TYPE MATCHING
interface BookInfo {
media_item_id: string; // matches Go's json:"media_item_id" (NOT "id")
title: string; // matches Go's json:"title"
author: string; // matches Go's json:"author"
cover_image_path: string; // matches Go's json:"cover_image_path"
}
// All 4 fields from Go struct included - COMPLETE TYPE MATCHING
❌ UNACCEPTABLE: Partial TypeScript Types
// WRONG: TypeScript interface with only subset of Go fields (breaks type safety)
interface SectionData {
id: string;
type: string; // WRONG: should be is_system: boolean
title: string;
items: BookInfo[];
// Missing: description, icon, view_all_url, priority
// This is a PARTIAL type and violates type safety guidelines
}
// WRONG: Using wrong field name for BookInfo
interface BookInfo {
id: string; // WRONG: should be media_item_id
title: string;
author: string;
cover_image_path: string;
}
Key Points:
- In Go: Templates MUST use
handlers.*types directly (no duplication) - In TypeScript:
.d.tsfiles recreate complete handler JSON structure (all fields) - Reason: Go's
pgtype.Text,pgtype.UUID, etc. don't map cleanly to TypeScript - Type Safety: Partial TypeScript types break type safety and can cause runtime errors
- Verification: Field counts must match (Go struct has N fields = TypeScript has N fields)
This checklist enforces NO Go duplication while requiring complete TypeScript duplication.
1. Prerequisites Verification
1.1 Verify TypeScript Conversion Plan Completed
Before starting Carousel Dashboard:
- TypeScript Conversion Plan (20-25.5 days) is fully completed
- All infrastructure modules exist in
web/src/:api.ts- Centralized API client with authtoast.ts- Toast notification systemevents.ts- Event delegation utilitiesstorage.ts- localStorage wrapperdom.ts- DOM utilities (escapeHtml, etc.)types/api.d.ts- Type definitions for all API responses
- Event delegation pattern established (data attributes)
- TypeScript compilation pipeline working (
npm run build:ts) - All inline JavaScript removed from templates
- Progressive enhancement maintained across all features
Verification Commands:
# Verify TypeScript modules exist
ls -la web/src/{api,toast,events,storage,dom}.ts
# Verify type definitions exist
ls -la web/src/types/api.d.ts
# Verify build works
npm run build:ts
# Check for remaining inline JavaScript
rg '<script>' templates/*.templ | grep -v 'src="/static/'
Common Pitfalls:
- Starting dashboard implementation before TypeScript conversion completes
- Missing utility modules that dashboard depends on
- TypeScript compilation errors blocking dashboard development
1.2 Verify Database Schema Readiness
Before modifying schema:
- Current schema.sql is backed up
- Database is in pre-production state (can be recreated)
- No critical data in database that needs migration
- Volume deletion is acceptable (
podman compose down -v) - Team understands data will be lost
Verification:
# Check if database is pre-production
# (no production data, can be safely recreated)
# Backup current schema
cp database/schema/schema.sql database/schema/schema.sql.backup
# Verify schema.sql syntax
psql -f database/schema/schema.sql --dry-run
2. Database Schema Verification
2.1 Verify Schema Changes Are Atomic
For each table/alteration in schema.sql:
- All changes are in single transaction
- No partial updates possible (all-or-nothing)
- Foreign key constraints are correct
- Indexes are created after tables
- Default values are appropriate
- NOT NULL constraints are correct
Verification Commands:
# Check schema.sql syntax
psql -f database/schema/schema.sql --dry-run
# Test schema creation in isolated environment
createdb test_bookhoard
psql test_bookhoard -f database/schema/schema.sql
# Verify tables created
psql test_bookhoard -c "\dt"
psql test_bookhoard -c "\d user_dashboard_preferences"
psql test_bookhoard -c "\d smart_section_types"
# Verify indexes created
psql test_bookhoard -c "\di"
# Cleanup
dropdb test_bookhoard
2.2 Verify New Tables Match Plan
For user_dashboard_preferences:
- All required columns exist:
id UUID PRIMARY KEY DEFAULT gen_random_uuid()user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADElibrary_id UUID REFERENCES libraries(id) ON DELETE CASCADEhidden_collections TEXT[] DEFAULT '{}'collection_order TEXT[] DEFAULT '{}'items_per_section INT DEFAULT 20created_at TIMESTAMP DEFAULT NOW()updated_at TIMESTAMP DEFAULT NOW()- Unique constraint on
(user_id, library_id) - Index on
(user_id, library_id)for fast lookups
For collections table modifications:
user_id UUID NULL REFERENCES users(id)added (NULL for system collections)show_on_dashboard BOOLEAN DEFAULT falseaddedquery_type TEXT DEFAULT 'filter'addedpriority INT DEFAULT 100addedis_system_collection BOOLEAN DEFAULT falseadded- Index created on
(user_id, show_on_dashboard, priority)WHERE show_on_dashboard = true - 4 system collections pre-seeded with user_id = NULL:
continue-reading(priority 1, query_type='continue-reading')recently-added(priority 2, query_type='recently-added')recently-read(priority 3, query_type='recently-read')not-started(priority 4, query_type='not-started')
For collections.show_on_dashboard column:
- Column added with
ALTER TABLE collections ADD COLUMN IF NOT EXISTSclause included- Default value is
false - Index created on `(user_id, show_on_dashboard, priority) WHERE show_on_dashboard = true
For collection_items.excluded column:
- Column added with
ALTER TABLE collection_items ADD COLUMN IF NOT EXISTSclause included- Default value is
false - Index created on
(collection_id, excluded) WHERE excluded = true - Allows users to exclude auto-assigned items from filter-based collections
Verification:
# Check table definitions
psql bookhoard -c "\d user_dashboard_preferences"
psql bookhoard -c "\d collections" | grep -E "show_on_dashboard|query_type|priority|is_system_collection"
# Check system collections exist
psql bookhoard -c "SELECT name, query_type, priority, is_system_collection FROM collections WHERE user_id IS NULL"
2.3 Verify No Migration Files
Critical for pre-production app:
- Changes are in
database/schema/schema.sql(NOT migration files) - No migration files in
database/migrations/ - Plan explicitly states to recreate database
- Volume deletion instructions are clear
- Warning about data loss is prominent
Check:
# Should return no files or only old/unused migrations
ls -la database/migrations/
# Verify schema.sql has latest changes
rg "user_dashboard_preferences" database/schema/schema.sql
rg "show_on_dashboard" database/schema/schema.sql
rg "smart_section_types" database/schema/schema.sql
3. Service Layer Verification
3.1 Verify Service Follows Guidelines
For internal/services/dashboard_service.go:
- All business logic in service (not in handlers)
- Procedural/imperative style (no OOP)
- No classes or methods
- Functions accept context as first parameter
- Uses database.Queries interface (not direct db access)
- Returns raw data (not formatted for templates/API)
- Error handling is consistent
- No HTTP concerns in service
Verification:
# Check for OOP patterns (should find nothing)
rg "class " internal/services/dashboard_service.go
rg "this\.|self\." internal/services/dashboard_service.go
# Verify function signatures
rg "^func \(" internal/services/dashboard_service.go
# Check that service uses database.Queries
rg "db\." internal/services/dashboard_service.go | grep -v "pgtype\."
# Verify no HTTP imports
rg "import.*net/http" internal/services/dashboard_service.go
# Should return nothing
3.2 Verify Service Methods Match Plan
Required methods:
NewDashboardService(db *database.Queries) *DashboardServiceGetDashboardSections(ctx, userID, libraryID, limit, collectionOrder, hiddenCollections) ([]DashboardSection, error)filterHiddenCollections(sections []DashboardSection, hidden []string) []DashboardSectionreorderCollections(sections []DashboardSection, order []string) []DashboardSectionsortByPriority(sections []DashboardSection) []DashboardSectiongetCollectionItemsByQueryType(ctx, coll, userID, libraryID, limit) ([]MediaItems, error)getUserCollectionItems(ctx, coll, userID, libraryID, limit) ([]MediaItems, error)GetDashboardPreferences(ctx, userID, libraryID) (UserDashboardPreferences, error)UpsertDashboardPreferences(ctx, params) (UserDashboardPreferences, error)RestoreSystemCollection(ctx, userID, collectionName) error
Verification:
# List all exported functions
rg "^func [A-Z]" internal/services/dashboard_service.go
# Verify return types match plan
rg "GetDashboardSections.*\[\]DashboardSection" internal/services/dashboard_service.go
# Verify method names match plan (not checklist)
rg "filterHiddenCollections|reorderCollections|sortByPriority" internal/services/dashboard_service.go
3.3 Verify Section Logic Correctness
For each smart section:
- Continue Reading: Progress > 0 AND progress < 1
- Recently Added: ORDER BY created_at DESC
- Recently Read: Progress >= 1 (completed)
- Not Started: Progress = 0 OR no reading_progress record
- User Collections: WHERE show_on_dashboard = true AND is_system_collection = false
Check:
# Find SQL queries in service
rg "SELECT.*FROM media_items" internal/services/dashboard_service.go
rg "WHERE.*progress" internal/services/dashboard_service.go
# Verify ordering
rg "ORDER BY" internal/services/dashboard_service.go
3.4 Verify User Preference Logic
Filter hidden collections:
- Empty hidden list returns all collections
- Non-empty hidden list filters matching collections
- Comparison is case-sensitive
- No errors on empty collection list
Reorder collections:
- Empty order returns collections as-is
- Ordered collections come first
- Unordered collections appended at end
- No collections are lost
- No duplicate collections in result
Verification:
# Find preference logic
rg "filterHiddenSections|reorderSections" internal/services/dashboard_service.go -A 20
# Check edge cases
rg "if len.*== 0" internal/services/dashboard_service.go
3.4 Verify Auto-Assign Rule Evaluation
For getUserCollectionItems method:
- Fetches collections with
show_on_dashboard = true - Parses
auto_assign_rulesJSONB from collection - Fetches all library items via
GetLibraryItems - Evaluates rules for each library item using
collectionService.EvaluateRules - Merges manual items (not excluded) + auto-matched items
- Filters out excluded items (where
excluded = true) - Applies limit after merging
- Only adds collection if it has items in current library
Excluded items handling:
- Manual items query returns
excludedcolumn fromcollection_items - Filters out items where
excluded = true - Auto-matched items checked against manual items to avoid duplicates
- Excluded items not added even if they match rules
Rule evaluation logic:
- Calls
collectionService.EvaluateRules(item, rules) - Checks if
eval.Matches && eval.Confidence > 0.7 - Adds matching items to collection
- Respects priority (higher priority rules evaluated first)
Merge logic:
// Pseudo-code for merge logic
manualItems := getManualItems(collectionID) // with excluded=false filter
autoItems := evaluateAutoAssignRules(libraryItems, rules)
finalItems := merge(manualItems, autoItems)
finalItems = applyLimit(finalItems, limit)
Verification:
# Check getUserCollectionItems implementation
rg "func.*getUserCollectionItems" internal/services/dashboard_service.go -A 100
# Verify auto-assign rules parsing
rg "json.Unmarshal.*AutoAssignRules" internal/services/dashboard_service.go
# Verify EvaluateRules usage
rg "collectionService.EvaluateRules" internal/services/dashboard_service.go
# Check GetLibraryItems query
rg "GetLibraryItems" internal/services/dashboard_service.go
# Verify excluded items filtering
rg "Excluded.*Bool" internal/services/dashboard_service.go
rg "!item.Excluded.Valid || !item.Excluded.Bool" internal/services/dashboard_service.go
# Check manual + auto merge logic
rg "manualNonExcluded.*append.*autoItems" internal/services/dashboard_service.go
# Verify limit applied after merge
rg "len.*finalItems.*limit" internal/services/dashboard_service.go
Edge case verification:
- Collection with no auto-assign rules (manual only)
- Collection with auto-assign rules but no matches
- Collection where all manual items are excluded
- Collection with auto-assign rules + manual additions
- Collection with excluded items that match rules
- Large library (performance check)
Integration with CollectionService:
- DashboardService has collectionService dependency
- NewDashboardService creates CollectionService instance
- Reuses existing EvaluateRules from collection_service.go
- No duplicate rule evaluation logic
4. Database Queries Verification
4.1 Verify Queries Match Schema
For each query in internal/database/queries/queries.sql:
- Query name follows
sqlcnaming convention :one,:many, or:execsuffix is correct- Parameters use
$1,$2, etc. (PostgreSQL syntax) - Table names match schema.sql
- Column names match schema.sql
- ON CONFLICT clause is correct for upserts
- RETURNING clause returns all modified columns
Verification:
# Check query syntax
cat internal/database/queries/queries.sql | rg "dashboard"
# Verify queries compile
cd internal/database && sqlc generate
# Check generated code
cat internal/database/dashboard.go # Should exist after sqlc generate)
rg "GetDashboardPreferences|UpsertDashboardPreferences|UpdateDashboardPreferences" internal/database/queries.sql
4.2 Verify Query Correctness
For GetDashboardPreferences:
- Selects all columns from
user_dashboard_preferences - WHERE clause matches on
user_idANDlibrary_id - Returns single row or error
For UpsertDashboardPreferences:
- INSERTs on conflict with
(user_id, library_id) - Updates all preferences columns
- Updates
updated_atto NOW() - Returns inserted/updated row
For GetCollectionsForDashboard:
- Two separate queries for system and user collections
- System collections: WHERE user_id IS NULL AND show_on_dashboard = true
- User collections: WHERE user_id = $1 AND show_on_dashboard = true AND is_system_collection = false
- Both ordered by priority ASC
- Returns multiple rows
For RestoreSystemCollection:
- Deletes user-owned copy of system collection
- WHERE user_id = $1 AND name = $2 AND is_system_collection = true
- System collection (user_id = NULL) automatically appears after deletion
Verification:
# Test queries in isolation
psql bookhoard <<EOF
-- Test GetDashboardPreferences
SELECT * FROM user_dashboard_preferences WHERE user_id = '$1' AND library_id = '$2';
-- Test GetCollectionsForDashboard
SELECT c.* FROM collections c
WHERE c.user_id = '$1'
AND c.show_on_dashboard = true
ORDER BY c.created_at DESC;
EOF
4.3 Verify Queries Generated
After running sqlc generate:
internal/database/models.gohas new structsinternal/database/dashboard.gohas query functions- No compilation errors in generated code
- All query functions are exported
- Parameter types are correct (UUID, TEXT[], INT, etc.)
Check:
# Regenerate queries
cd internal/database && sqlc generate
# Check generated files
ls -la internal/database/*.go | grep -E "(models|dashboard|queries)"
# Look for compilation errors
go build ./internal/database/...
5. API Handler Verification
5.1 Verify Handler Follows Guidelines
For internal/handlers/dashboard.go:
- Uses service layer (no direct database access)
- Returns JSON responses
- Uses proper HTTP status codes
- Error responses are consistent
- Authentication required (JWT middleware)
- Input validation is performed
- No HTML responses (API only)
- Reusable by SSR, API, mobile
- Uses shared types from collections.go (SectionData, BookInfo)
- No duplicate type definitions
Verification:
# Check for direct database access (should find nothing)
rg "Queries\." internal/handlers/dashboard.go
# Only service calls should exist
# Check HTTP status codes
rg "http\.Status" internal/handlers/dashboard.go
# Check error responses
rg "c\.JSON.*error" internal/handlers/dashboard.go
# Verify authentication
rg "c\.Get\(\"user\"\)" internal/handlers/dashboard.go
# Verify no duplicate types in dashboard.go
rg "type (SectionData|BookInfo) struct" internal/handlers/dashboard.go
# Should return nothing - these are in collections.go
# Verify types imported from collections.go
rg "collections\.go" internal/handlers/dashboard.go
5.2 Verify GetSections Endpoint
Request parameters:
library_idquery parameter (required)limitquery parameter (optional, default 20, max 100)- User from JWT context
- User preferences applied (order, hidden collections)
Response format:
- Returns JSON object with
sectionsarray - Each section has:
id(collection key or name)is_system(boolean: true for system collections, false for user collections)titledescriptioniconitems(array of books)- Each book item has:
media_item_id(NOTid)titleauthorcover_image_path
view_all_url(empty for user collections)priorityid(UUID string)titleauthorcover_image_path
Error cases:
- 400 if
library_idmissing - 400 if
library_idinvalid UUID - 401 if not authenticated
- 500 if service fails
Verification:
# Check endpoint implementation
rg "func.*GetSections" internal/handlers/dashboard.go -A 50
# Verify response structure
rg "buildJSONSections" internal/handlers/dashboard.go -A 30
# Test with Bruno
cd bruno/dashboard/
# Run: GET /api/dashboard/sections?library_id=...
5.3 Verify BuildSections Function
Required function:
BuildSections(items []services.SectionItems) []SectionDatain dashboard.go
Key requirements:
- Converts service
SectionItemsto handlerSectionData(from collections.go) - Maps
database.MediaItemstohandlers.BookInfo(from collections.go) - Uses
MediaItemIDfield (notID) when creating BookInfo - Uses
IsSystemboolean directly from database (no string conversion) - No helper functions needed for type/title/icon (use database values directly)
getViewAllURL()helper for system collection URLs only
System collections data (from database):
continue-reading→ is_system: true, title from DB, icon from DBrecently-added→ is_system: true, title from DB, icon from DBrecently-read→ is_system: true, title from DB, icon from DBnot-started→ is_system: true, title from DB, icon from DB
User collections:
- is_system: false (from database
is_system_collectionfield) - Title uses collection name from database
- Icon uses collection icon from database
- view_all_url is empty string
Verification:
# Check BuildSections function
rg "func BuildSections" internal/handlers/dashboard.go -A 50
# Verify MediaItemID field usage
rg "MediaItemID.*String\(\)" internal/handlers/dashboard.go
# Verify IsSystem boolean used directly
rg "IsSystem.*si\.IsSystem" internal/handlers/dashboard.go
# Verify no type conversion helpers
rg "getSectionType|getSectionTitle|getSectionIcon" internal/handlers/dashboard.go
# Should return nothing - no longer needed
6. Routing Verification
6.1 Verify API Routes
For internal/router/dashboard.go:
- Route group registered at
/api/dashboard - JWT middleware applied
- GET
/api/dashboard/sections→GetSections - Handler injected via Config struct
- Route registration called from main router
Verification:
# Check route definition
rg "registerDashboardRoutes" internal/router/dashboard.go -A 20
# Verify handler registered
rg "DashboardHandler" internal/router/router.go
# Check middleware
rg "jwtMiddleware" internal/router/dashboard.go
6.2 Verify Frontend Routes
For internal/router/frontend.go:
- GET
/dashboardroute exists - Uses
DashboardServicefor data - Renders
templates.Dashboard - Passes sections, libraries, currentLibraryID
- Gets user from context
- Applies user preferences
- Library switching supported
For /settings route:
- GET
/settings→ renders settings form - POST
/settings→ updates user and preferences - Handles profile fields (email, username, name)
- Handles theme selection
- Handles dashboard preferences
- Returns JSON on success
Verification:
# Check dashboard route
rg 'GET.*"/dashboard"' internal/router/frontend.go -A 50
# Check settings route
rg 'GET.*"/settings"' internal/router/frontend.go -A 20
rg 'POST.*"/settings"' internal/router/frontend.go -A 40
For /custom-section route:
- GET
/custom-section→ renders custom section builder form - Passes libraries for selector
- Renders
templates.CustomSectionBuilder - Links from dashboard settings modal
- No POST route (form submits via JSON to
/api/collections)
Verification:
# Check custom-section route
rg 'GET.*"/custom-section"' internal/router/frontend.go -A 20
# Verify template rendering
rg "templates.CustomSectionBuilder" internal/router/frontend.go
# Verify libraries passed to template
rg "CustomSectionBuilder.*libraries" internal/router/frontend.go
6.3 Verify Collections Preview Endpoint
IMPORTANT: This endpoint is REQUIRED for both web UI and mobile apps
Why this endpoint is necessary:
- Web UI: Custom section builder needs to test filter rules before saving
- Mobile apps: Will need this endpoint for future custom section creation
- No duplication: Reuses existing
collectionService.EvaluateRules()logic - Single source of truth: Rule evaluation logic stays in Go service layer
Why NOT client-side preview?
- Would require downloading entire library (10,000+ books) to browser
- Would duplicate 500+ lines of rule evaluation logic in TypeScript
- Maintenance nightmare (keeping Go and TypeScript in sync)
- Risk of client/server evaluating rules differently
For internal/handlers/collections.go:
PreviewCollectionmethod exists- POST
/api/collections/previewroute registered in collections.go - Accepts library_id, rules, manual_book_ids, limit in request body
- Evaluates filter rules against library items
- Merges filter matches + manually selected books
- Deduplicates items (no duplicates in final result)
- Applies limit after merging
- Uses existing
collectionService.EvaluateRules() - Returns
handlers.BookInfoformat withmedia_item_idfield
Request format:
{
"library_id": "uuid",
"rules": [
{
"id": "rule1",
"field": "genre",
"operator": "equals",
"value": "Sci-Fi",
"priority": 5
}
],
"manual_book_ids": ["uuid1", "uuid2"],
"limit": 20
}
Response format:
{
"items": [
{
"media_item_id": "uuid",
"title": "Dune",
"author": "Frank Herbert",
"cover_image_path": "/path/to/cover.jpg"
}
]
}
Verification:
# Check handler method exists (Phase 4.5 of plan)
rg "func.*PreviewCollection" internal/handlers/collections.go -A 30
# Check route registration (Phase 4.5 of plan)
rg 'POST.*"/preview"' internal/router/collections.go
# Verify EvaluateRules usage
rg "collectionService.EvaluateRules" internal/handlers/collections.go
# Check BookInfo conversion with MediaItemID
rg "MediaItemID.*String\(\)" internal/handlers/collections.go
# Verify manual book ID handling
rg "manual_book_ids" internal/handlers/collections.go
# Check Bruno test exists
ls -la bruno/dashboard/preview-collection.bru
6.4 Verify Config Setup
CRITICAL: Config struct must be updated in 3 files
For internal/router/router.go:
DashboardService *services.DashboardServiceadded to Config struct (after line 56)DashboardHandler *handlers.DashboardHandleradded to Config struct (after line 57)- Field order matches other service/handler fields
For cmd/server/main.go:
dashboardService := services.NewDashboardService(queries)initialized (after line 123)dashboardHandler := handlers.NewDashboardHandler(queries)initialized (after line 124)DashboardService: dashboardService,added to routerConfig (after line 172)DashboardHandler: dashboardHandler,added to routerConfig (after line 173)- Both initialized before router.RegisterRoutes() call
For cmd/server/tests/test_helpers.go:
dashboardService := services.NewDashboardService(queries)initialized (after line 419)dashboardHandler := handlers.NewDashboardHandler(queries)initialized (after line 420)DashboardService: dashboardService,added to routerConfig (after line 458)DashboardHandler: dashboardHandler,added to routerConfig (after line 459)
Why both DashboardService AND DashboardHandler?
- DashboardService: Used by SSR routes in frontend.go for data fetching
- DashboardHandler: Used by API routes for JSON endpoints
- Mobile apps: Will use DashboardHandler API endpoints
- Web UI: Uses DashboardService for SSR + DashboardHandler for interactions
Verification:
# Check Config struct has both fields
rg "DashboardService|DashboardHandler" internal/router/router.go
# Check main.go initialization (should find 2 initializations + 2 config assignments)
rg "dashboardService|dashboardHandler" cmd/server/main.go
# Check test_helpers.go initialization (should find 2 initializations + 2 config assignments)
rg "dashboardService|dashboardHandler" cmd/server/tests/test_helpers.go
# Verify service is used in frontend.go
rg "DashboardService" internal/router/frontend.go
# Verify handler is used in dashboard.go router
rg "DashboardHandler" internal/router/dashboard.go
7. Template Types Verification
7.1 Verify No Duplicate Types in Go
CRITICAL GUIDELINE COMPLIANCE (Go side):
- NO
templates.SectionDataortemplates.BookCardDatacreated in Go - Dashboard sections use handler types directly (e.g.,
handlers.CollectionData,handlers.BookInfo) - OR use existing database types (e.g.,
database.MediaItems) - Template-only types are ONLY for UI-specific concerns (e.g.,
PageData,UnsafeHTML) - No parallel type systems in Go
✅ CORRECT (Go):
// Handler type defined once in handlers package
// Reused directly in template
templ Dashboard(sections []handlers.SectionData, books []handlers.BookInfo)
❌ WRONG (Go):
// Handler type defined in handlers
// Duplicate type defined in templates - VIOLATES GUIDELINES
type SectionData struct { ... } // DON'T DO THIS in Go
When template-specific fields needed:
// ✅ ENHANCE handler type (don't create new type)
type BookInfo struct {
MediaItemID string
Title string
Author string
// Add template-specific fields to handler struct
Icon string // Template enhancement
}
TypeScript Side (.d.ts files) - ACCEPTABLE DUPLICATION:
- TypeScript type definitions in
.d.tsfiles ARE acceptable - Go's
pgtypefields cannot auto-convert to TypeScript - Manual recreation of types in
web/src/types/*.d.tsis necessary - TypeScript types must match Go handler JSON responses (snake_case)
- This is cross-language duplication, not Go duplication
✅ CORRECT (TypeScript):
// web/src/types/dashboard.d.ts
// Acceptable: Recreate types to match Go JSON responses
// Go's pgtype.Text, pgtype.UUID, etc. need manual mapping
interface SectionData {
id: string; // matches Go's json:"id"
title: string; // matches Go's json:"title"
items: BookCardData[]; // matches Go's json:"items"
}
Verification:
# Check for duplicate Go types (should find none)
rg "type (SectionData|BookCardData) struct" templates/types.go
# Should return nothing - use handler types instead
# Verify handler types exist
rg "type.*Data struct" internal/handlers/*.go
# Check template imports handler package
rg 'import.*handlers' templates/*.templ
# Check template uses handler types
rg 'handlers\.(CollectionData|BookInfo|UserProfile)' templates/*.templ
# TypeScript types (acceptable to exist)
ls -la web/src/types/dashboard.d.ts # OK - .d.ts file
ls -la web/static/dashboard.d.ts # Should NOT exist - no JS output
Common Pitfalls:
- Creating
templates.SectionDatain Go whenhandlers.SectionDatashould be enhanced - Creating
templates.BookCardDatain Go whenhandlers.BookInfoshould be used - Adding conversion helper functions (unnecessary - use handler types directly)
- Parallel type systems in Go (handlers + templates with same data)
- NOT a pitfall: TypeScript
.d.tsfiles recreating types (necessary due to pgtype)
7.2 Verify Handler Type Tags
All handler struct fields:
- Have
json:""tags for API responses - Use snake_case in JSON tags
- Matching Go field names are PascalCase
- No duplicate tags
- No missing tags
- Template-specific fields added to handler structs when needed
Check:
# Verify JSON tags in handler types
rg 'json:"' internal/handlers/*.go
# Check for snake_case
rg 'json:"[A-Z]' internal/handlers/*.go
# Should return nothing (no camelCase in JSON tags)
# Verify handler types used in templates
rg 'handlers\.' templates/*.templ
8. Template Implementation Verification
8.1 Verify Dashboard Template
For templates/dashboard.templ:
- Uses handler types directly (e.g.,
handlers.SectionData,handlers.BookInfo) - OR uses database types (e.g.,
database.MediaItems) - NO
templates.SectionDataortemplates.BookCardData(violates guidelines) - SSR data pre-populated (
sections,libraries) - TailwindCSS classes only (no custom CSS)
- HTMX for library switching
- Event delegation (no inline onclick)
- Data attributes for TypeScript hooks
- Includes all required scripts:
/static/htmx.min.js/static/toast.js/static/api.js/static/events.js/static/dashboard.js
Template structure:
- Sticky header with library selector
- Settings button (data-action="open-dashboard-settings")
- Refresh button (data-action="reload-page")
- Sections container (#sections-container)
- HTMX loading indicator
- Dashboard settings modal
Verification:
# Check template structure
rg "templ Dashboard" templates/dashboard.templ -A 100
# Verify TailwindCSS usage
rg 'class="[^"]*"' templates/dashboard.templ | rg -v 'style='
# Should have many Tailwind classes
# Check for inline styles (should use CSS variables)
rg 'style=' templates/dashboard.templ
# Verify event delegation
rg 'data-action=' templates/dashboard.templ
# Check for inline onclick (should be none)
rg 'onclick=' templates/dashboard.templ
# Should return nothing
# Verify script includes
rg '<script src=' templates/dashboard.templ
8.2 Verify Component Templates
For CollectionCarousel:
- Accepts handler type parameter (e.g.,
handlers.SectionData) - OR accepts database type (e.g.,
[]database.MediaItems) - NOT
templates.SectionData(violates guidelines) - Renders collection header (title, icon, view-all link)
- Renders carousel container
- Navigation buttons (left/right)
- Carousel track (overflow-x-auto)
- Book cards (snap-start)
- Data attributes:
data-collection-iddata-is-system(boolean: "true" or "false")data-action="scroll-carousel"
- Accessibility attributes:
aria-labelon nav buttonstabindex="0"on book cardsrole="button"on book cards
- For system collections: Shows "Restore" button
For BookCard:
- Accepts handler type parameter (e.g.,
handlers.BookInfo) - OR accepts database type (e.g.,
database.MediaItems) - NOT
templates.BookCardData(violates guidelines) - Aspect ratio 2:3 for cover
- Lazy loading on images
- Fallback placeholder image
- Title line-clamp (2 lines)
- Author line-clamp (1 line)
- Hover effects (scale-105)
- Data attributes:
data-action="view-book"data-book-id
For DashboardSettingsModal:
- Fixed overlay with backdrop
- Draggable collection list
- Toggle switches for visibility
- Per-collection "Restore" buttons for system collections
- Items per collection slider
- Save/Cancel buttons
- Data attributes:
data-action="close-dashboard-settings"data-action="toggle-collection-visibility"data-action="restore-system-collection"(for system collections)data-action="update-items-count"data-action="save-dashboard-settings"
Verification:
# Check component structure
rg "templ (SectionCarousel|BookCard|DashboardSettingsModal)" templates/ -A 50
# CRITICAL: Verify templates use handler types
rg 'handlers\.(SectionData|BookInfo|CollectionData)' templates/*.templ
# CRITICAL: Check for template types (should find none)
rg "templates\.(SectionData|BookCardData)" templates/*.templ
# Should return nothing
# Verify accessibility
rg 'aria-|role=|tabindex' templates/
# Check data attributes
rg 'data-(action|section|book)=' templates/
8.3 Verify Library Selector (TypeScript, Not HTMX)
Library selector attributes:
- Select element has
id="library-select"andname="library_id" - NO HTMX attributes (no
hx-get,hx-target, etc.) - Uses
data-action="switch-library"or change event listener - Options rendered from SSR data (
librariesparameter) - Current library selected by default
Loading indicator:
#loading-spinnerelement exists (hidden by default)- Used by TypeScript during library switching
- Shows/hides via CSS class manipulation
Verification:
# Check library selector (should NOT have HTMX)
rg 'id="library-select"' templates/dashboard.templ -A 5
# Verify NO HTMX attributes on library selector
rg 'id="library-select"' templates/dashboard.templ -A 5 | rg 'hx-'
# Should return nothing
# Check loading indicator exists
rg 'loading-spinner' templates/dashboard.templ
# Verify data-action for library switching
rg 'data-action="switch-library"' templates/dashboard.templ
8.4 Verify Settings Template
For templates/settings.templ:
- Uses handler/database types (
database.Users,database.UserDashboardPreferences) - Profile section (email, username, name)
- Theme selector (tokyo-night, light, dark)
- Dashboard preferences (items per section)
- Form uses data-action="save-settings"
- TailwindCSS only
- Event delegation
- Includes required scripts
- NO template types for user data (use
database.Usersdirectly)
Verification:
# Check settings template
rg "templ Settings" templates/settings.templ -A 100
# Verify form handling
rg 'data-action="save-settings"' templates/settings.templ
# CRITICAL: Verify database types used, not template types
rg "database\.(Users|UserDashboardPreferences)" templates/settings.templ
8.5 Verify Custom Section Builder Template
IMPORTANT: Custom Section Builder IS in the plan (Phase 10.5)
This is a major feature allowing users to create filter-based custom dashboard sections with 13+ filter fields.
For templates/custom_section.templ:
- SSR page for creating filter-based custom sections
- Uses handler types (handlers.User, templates.LibraryData)
- Section details form (name, icon, description, library selector)
- Filter rules section with dynamic rule addition
- Manual book selection section with search
- Live preview section showing matching books
- TailwindCSS only
- Includes required script (
custom-section-builder.js) - Links to
/custom-sectionroute (registered in Phase 8)
Form structure verification:
- Name input (required)
- Icon input (optional, emoji)
- Description textarea (optional)
- Library selector (required, populated from SSR)
- Filter rules container with
id="rules-container" - "Add Rule" button with
id="add-rule-btn" - Match type selector (ALL/ANY)
- Manual book selection section
- Book search input with
id="book-search" - Search button with
id="search-books-btn" - Search results container with
id="search-results" - Selected books container with
id="selected-books" - Preview button with
id="preview-btn" - Preview container with
id="preview-container" - Save button (form submit)
- Cancel button with
id="cancel-btn"
Rule fields (13+ filter fields):
- Field selector (title, author, genre, series, progress, rating, date_added, last_read, publisher, language, format, tags, narrators)
- Operator selector (varies by field type)
- Value input (text, number, date, or select)
- Remove button for each rule
Manual book selection:
- Search input with debounced autocomplete
- Search results display with "+" button to add
- Selected books displayed as removable chips
- Search results hidden by default
Preview section:
- Shows loading state while fetching
- Displays matching books in carousel layout
- Shows count of matching books
- Handles empty results gracefully
Verification:
# Check template exists
ls -la templates/custom_section.templ
# Check template structure
rg "templ CustomSectionBuilder" templates/custom_section.templ -A 100
# Verify SSR data (libraries, user parameters)
rg "CustomSectionBuilder.*user.*libraries" templates/custom_section.templ
# Verify form fields
rg 'name="name".*required' templates/custom_section.templ
rg 'name="library_id".*required' templates/custom_section.templ
rg 'name="icon"' templates/custom_section.templ
rg 'name="description"' templates/custom_section.templ
# Verify filter rules container
rg 'id="rules-container"' templates/custom_section.templ
rg 'id="add-rule-btn"' templates/custom_section.templ
# Verify match type selector
rg 'name="match_type"' templates/custom_section.templ
# Verify manual book selection
rg 'id="book-search"' templates/custom_section.templ
rg 'id="search-books-btn"' templates/custom_section.templ
rg 'id="search-results"' templates/custom_section.templ
rg 'id="selected-books"' templates/custom_section.templ
# Verify preview section
rg 'id="preview-container"' templates/custom_section.templ
rg 'id="preview-btn"' templates/custom_section.templ
# Verify TailwindCSS only (no custom CSS)
rg '<style' templates/custom_section.templ
# Should return nothing
# Verify no inline event handlers (flexible - inline onclick acceptable)
# Event delegation preferred but not strictly required
# Verify script includes
rg 'custom-section-builder.js' templates/custom_section.templ
# Check for 13+ filter field options in plan
rg "title|author|genre|series|progress|rating|date_added|last_read|publisher|language|format|tags|narrators" CAROUSEL_DASHBOARD_PLAN.md
JavaScript integration verification:
- Event delegation handles all form actions
- Rules dynamically added to DOM
- Book search with debounce (300ms)
- Selected books stored in Map for deduplication
- Form submission prevented, JSON sent via API
- Preview updates DOM without page reload
- Global functions for inline onclick (addBookToSelection, removeBookFromSelection)
9. TypeScript Implementation Verification
9.1 Verify TypeScript Module Structure
For web/src/dashboard.ts:
- Procedural style (no classes, no
this) - Functions exported to
windowobject - Event delegation via
data-actionattributes - Uses shared modules:
(window as any).apifrom api.ts(window as any).showToastfrom toast.ts(window as any).eventsfrom events.ts
- Type definitions imported from
types/api.d.tsortypes/dashboard.d.ts - No ES module imports/exports (browser globals)
Verification:
# Check for OOP patterns (should find nothing)
rg "class |this\." web/src/dashboard.ts
# Verify window exports
rg "window\." web/src/dashboard.ts
# Check for ES modules (should use 'import type' only)
rg "^import " web/src/dashboard.ts
rg "import type " web/src/dashboard.ts
# Verify shared module usage
rg "api\.|showToast\.|events\." web/src/dashboard.ts
9.2 Verify TypeScript Dashboard Functions
Required functions:
scrollCarousel(collectionId: string, direction: number): voidswitchLibrary(libraryId: string): Promise<void>- Fetches JSON and re-renders collectionsrenderCollections(sections: SectionData[]): void- Renders collections from JSONrenderBookCard(book: BookInfo): string- Renders single book card HTMLopenDashboardSettings(): voidcloseDashboardSettings(): voidsaveDashboardSettings(): Promise<void>toggleCollectionVisibility(collectionId: string): voidrestoreSystemCollection(collectionName: string, collectionTitle: string): Promise<void>viewBook(bookId: string): Promise<void>reloadPage(): voidupdateItemsCount(count: number): void
Behavior verification:
- Scroll amount is 300px (SCROLL_AMOUNT constant)
- Smooth scrolling enabled
- Modal show/hide toggles CSS classes
- Settings saved via API call
- Toast notifications on success/error
- Error handling for network failures
Verification:
# Check function definitions
rg "^function (scroll|open|close|save|toggle|update|view|reload)" web/src/dashboard.ts
# Verify scroll behavior
rg "scrollBy|scrollLeft" web/src/dashboard.ts
# Check API calls
rg "api\.(get|post|put|delete)" web/src/dashboard.ts
# Verify toast usage
rg "showToast\.(error|success|info)" web/src/dashboard.ts
9.3 Verify Event Delegation
Event listeners:
- Single click listener for all dashboard actions
- Library select change event listener (for
switchLibrary) - Uses
data-actionattributes - Handles:
switch-library(library select changes)scroll-carouselopen-dashboard-settingsclose-dashboard-settingssave-dashboard-settingstoggle-section-visibilityupdate-items-countview-bookreload-page
Verification:
# Check event listener setup
rg "addEventListener\('click'" web/src/dashboard.ts -A 50
# Verify data-action checks
rg 'dataset\.action|getAttribute\("data-action"\)' web/src/dashboard.ts
9.4 Verify Settings TypeScript
For web/src/settings.ts:
- Form submission handler
- Profile field updates
- Theme selection
- Dashboard preferences updates
- Client-side validation
- Error handling
- Toast notifications
- Progressive enhancement (works without JS)
Verification:
# Check settings module
rg "^function " web/src/settings.ts
# Verify form handling
rg "addEventListener\('submit'" web/src/settings.ts
# Check validation
rg "validity|checkValidity" web/src/settings.ts
9.4 Verify Custom Section Builder TypeScript
IMPORTANT: Custom Section Builder TypeScript IS in the plan (Phase 10.5.3)
This file implements the custom section builder UI with 13+ filter fields and live preview functionality.
For web/src/custom-section-builder.ts:
- Procedural style (no classes, no
this) - Uses shared API client and toast
- Event delegation or inline onclick (flexible)
- Dynamic rule addition/removal
- Live preview functionality using
/api/collections/previewendpoint - Manual book selection with search
- 13+ filter fields with various operators
Type definitions:
FilterFieldinterface with id, label, operators, valueType, optionsOperatorinterface with id, label, requiresValueFilterRuleinterface with id, field, operator, value, priorityFILTER_FIELDSconst array with 13+ fields
Required functions:
initCustomSectionBuilder(): void- Sets up event listenersaddFilterRule(): void- Adds new rule row to formonFieldChange(ruleElement: HTMLElement): void- Updates operators/value input based on fieldremoveFilterRule(ruleId: string): void- Removes rule rowsearchBooks(): Promise<void>- Searches for books by title/authoronBookSearchInput(): void- Debounced search handlerdisplaySearchResults(books: BookInfo[]): void- Shows search resultsaddBookToSelection(bookId, title, author): void- Global function for onclickremoveBookFromSelection(bookId: string): void- Global function for onclickupdateSelectedBooksDisplay(): void- Updates selected books UIgatherFilterRules(): FilterRule[]- Collects all rules from formloadPreview(): Promise<void>- Calls preview API, displays resultssaveCustomSection(event: Event): Promise<void>- Creates collectionescapeHtml(text: string): string- Utility for HTML escaping
13+ Filter fields verification:
title- text field (contains, equals, starts_with, ends_with, regex)author- text field (contains, equals)genre- select field (equals, not_equals, in, not_in)series- text field (is_set, is_not_set, equals, contains)progress- number field (equals, not_equals, greater_than, less_than, between, is_set, is_not_set)rating- number field (equals, not_equals, greater_than, less_than, is_set, is_not_set)date_added- date field (equals, not_equals, before, after, between, last_x_days)last_read- date field (equals, before, after, between, last_x_days, is_set, is_not_set)publisher- text field (contains, equals)language- select field (equals, not_equals, in)format- select field (equals, in)tags- text field (contains, not_contains, equals)narrators- text field (contains, equals, is_set, is_not_set)
Preview functionality:
- Validates library_id is selected
- Calls
/api/collections/previewendpoint - Sends rules array and manual_book_ids array
- Displays matching books in carousel layout
- Shows count of matching books
- Handles loading state
- Handles errors with toast notification
Verification:
# Check file exists
ls -la web/src/custom-section-builder.ts
# Check for OOP patterns (should find nothing)
rg "class |this\." web/src/custom-section-builder.ts
# Verify type definitions
rg "interface (FilterField|Operator|FilterRule)" web/src/custom-section-builder.ts
# Check FILTER_FIELDS const has 13+ fields
rg "FILTER_FIELDS.*=.*\[" web/src/custom-section-builder.ts -A 100
rg "id:.*title|id:.*author|id:.*genre|id:.*series|id:.*progress|id:.*rating" web/src/custom-section-builder.ts
# Check required functions exist
rg "^function (initCustomSectionBuilder|addFilterRule|onFieldChange|removeFilterRule|searchBooks|loadPreview|saveCustomSection)" web/src/custom-section-builder.ts
# Verify global functions for inline onclick
rg "window\.addBookToSelection|window\.removeBookFromSelection" web/src/custom-section-builder.ts
# Verify API integration
rg "api\.post.*collections/preview" web/src/custom-section-builder.ts
# Verify toast usage
rg "showToast\.(error|success|warning|info)" web/src/custom-section-builder.ts
# Verify rule HTML generation
rg "insertAdjacentHTML|createElement" web/src/custom-section-builder.ts
# Verify search debounce
rg "searchTimeout|setTimeout|clearTimeout" web/src/custom-section-builder.ts
# Verify selected books tracking
rg "selectedBooks.*Map|new Map\(\)" web/src/custom-section-builder.ts
# Verify escapeHtml utility
rg "function escapeHtml" web/src/custom-section-builder.ts
Form verification:
- Event delegation handles actions OR inline onclick used (flexible)
- Rules dynamically added to DOM
- Book search with debounce (300ms timeout)
- Selected books stored in Map for deduplication
- Form submission prevented, JSON sent via API
- Preview updates DOM without page reload
9.5 Verify TypeScript Compilation
Build verification:
npm run build:tssucceedsweb/static/dashboard.jsgeneratedweb/static/settings.jsgenerated- No compilation errors
- No type errors
- Source maps generated (if configured)
Check:
# Compile TypeScript
npm run build:ts
# Verify output
ls -la web/static/dashboard.js
ls -la web/static/settings.js
# Check for errors
echo $? # Should be 0
10. Type Definition Verification
10.1 Verify Dashboard Type Definitions
For web/src/types/api.d.ts (.d.ts = no JS output):
- TypeScript interfaces recreate complete handler JSON structure (acceptable - necessary due to pgtype)
SectionDatainterface matches ALL fields fromhandlers.SectionDataJSON responseBookInfointerface matches ALL fields fromhandlers.BookInfoJSON responseDashboardPreferencesinterface matches request/response structure- NO missing fields - TypeScript must include every field from Go handler
- NO partial type definitions - if Go struct has 10 fields, TypeScript must have 10 fields
- Field names use snake_case (matching JSON responses exactly)
- pgtype fields mapped correctly:
pgtype.Text→stringpgtype.UUID→stringpgtype.Timestamp→string(ISO datetime)pgtype.Int4→numberpgtype.Bool→booleanpgtype.TextArray→string[]
- Field order doesn't matter (TypeScript interfaces are unordered)
TypeScript Duplication is ACCEPTABLE:
.d.tsfiles recreate types from Go handlers (necessary)- Go's pgtype fields cannot auto-convert to TypeScript
- Manual type mapping is required and acceptable
- This is cross-language duplication, not in-Go duplication
- NOT acceptable: Creating
templates.SectionDatain Go (violates guidelines) - CRITICAL: TypeScript types must match in full the Go handler return types
- All fields from Go struct included in TypeScript interface
- No partial type definitions (missing fields)
- Field names match exactly (snake_case JSON tags)
- Types match correctly (pgtype mapped to TypeScript types)
Custom Section Builder type definitions:
FilterFieldinterface incustom-section-builder.ts(not in api.d.ts - UI-specific)FilterRuleinterface matches Goservices.Rulestruct- Rule fields: id (string), field (string), operator (string), value (string | string[]), priority (number)
- 13+ field options defined in FILTER_FIELDS const
- Each field has appropriate operators based on type
Verification:
# Check TypeScript type definitions
cat web/src/types/dashboard.d.ts
# Verify .d.ts extension (no JS output)
ls -la web/src/types/dashboard.d.ts
# Ensure it doesn't compile to JS
npm run build:ts
ls -la web/static/dashboard.d.ts # Should not exist
# CRITICAL: Verify handler types exist in Go
rg "type.*SectionData|type.*BookData|type.*BookInfo" internal/handlers/*.go
# Check plan references handler types, not Go template types
rg "handlers\." CAROUSEL_DASHBOARD_PLAN.md | rg -i "section|book"
# TypeScript types can mirror handler types (acceptable)
rg "interface.*Data" web/src/types/*.d.ts
# CRITICAL: Verify TypeScript types match Go types IN FULL
# Step 1: Extract Go handler struct with JSON tags
# NOTE: SectionData and BookInfo are in internal/handlers/collections.go
rg "type (SectionData|BookInfo) struct" internal/handlers/collections.go -A 30 | grep 'json:"'
# Step 2: Extract TypeScript interface
rg "interface (SectionData|BookInfo)" web/src/types/dashboard.d.ts -A 30
# Step 3: Manually compare - Go should have same fields as TypeScript
# Example verification:
# Go handler struct (internal/handlers/collections.go):
# type SectionData struct {
# ID string `json:"id"`
# IsSystem bool `json:"is_system"` // Changed from Type string
# Title string `json:"title"`
# Description string `json:"description"`
# Icon string `json:"icon"`
# Items []BookInfo `json:"items"`
# ViewAllURL string `json:"view_all_url"`
# Priority int `json:"priority"`
# }
# // 8 fields total
# TypeScript interface (web/src/types/dashboard.d.ts) - MUST HAVE ALL 8 FIELDS:
# interface SectionData {
# id: string;
# is_system: boolean; // Changed from type: string
# title: string;
# description: string;
# icon: string;
# items: BookInfo[];
# view_all_url: string;
# priority: number;
# }
# Go handler struct (internal/handlers/collections.go):
# type BookInfo struct {
# MediaItemID string `json:"media_item_id"` // NOT "id"
# Title string `json:"title"`
# Author string `json:"author"`
# CoverImagePath string `json:"cover_image_path"`
# }
# // 4 fields total
# TypeScript interface (web/src/types/dashboard.d.ts) - MUST HAVE ALL 4 FIELDS:
# interface BookInfo {
# media_item_id: string; // ✅ matches (NOT "id")
# title: string; // ✅ matches
# author: string; // ✅ matches
# cover_image_path: string; // ✅ matches
# }
# ❌ WRONG - Wrong field names:
# interface BookInfo {
# id: string; // WRONG: should be media_item_id
# title: string;
# author: string;
# cover_image_path: string;
# }
# ❌ WRONG - Wrong type field:
# interface SectionData {
# id: string;
# type: string; // WRONG: should be is_system: boolean
# title: string;
# // ... other fields
# }
# Check for missing fields (manual verification required):
# 1. List all Go struct fields with JSON tags
# 2. List all TypeScript interface fields
# 3. Verify 1:1 correspondence (same field names, correct types)
10.2 Verify Type Consistency
Across layers:
- Database schema → Go structs (models.go)
- Go structs → Handler types (with template-specific fields added)
- Handler types → Template usage (direct import, no conversion in Go)
- Handler JSON responses → TypeScript interfaces (manual recreation in .d.ts)
- All use snake_case for JSON
Key Pattern:
Database (models.go) → Handler (enhanced) → Template (uses handlers.Type) → TypeScript (.d.ts recreates handlers.Type JSON IN FULL)
CRITICAL REQUIREMENT: TypeScript Types Must Match Go Types IN FULL
- TypeScript interfaces include ALL fields from Go handler structs
- No partial type definitions (e.g., Go has 10 fields, TypeScript has 10 fields)
- Field names match exactly (snake_case from JSON tags)
- Types map correctly (pgtype → TypeScript types)
- Missing fields break type safety and can cause runtime errors
Example of Full Type Matching:
// Go handler (internal/handlers/collections.go)
type SectionData struct {
ID string `json:"id"`
IsSystem bool `json:"is_system"` // Changed from Type string
Title string `json:"title"`
Description string `json:"description"`
Icon string `json:"icon"`
Items []BookInfo `json:"items"`
ViewAllURL string `json:"view_all_url"`
Priority int `json:"priority"`
}
// 8 fields total
// TypeScript (web/src/types/dashboard.d.ts)
// ✅ CORRECT - All 8 fields present
interface SectionData {
id: string;
is_system: boolean; // Changed from type: string
title: string;
description: string;
icon: string;
items: BookInfo[];
view_all_url: string;
priority: number;
}
// ❌ WRONG - Missing fields (partial type, breaks type safety)
interface SectionData {
id: string;
is_system: boolean;
title: string;
items: BookInfo[];
// Missing: description, icon, view_all_url, priority
}
// ❌ WRONG - Wrong field name and type
interface SectionData {
id: string;
type: string; // WRONG: should be is_system: boolean
title: string;
// ... other fields
}
IMPORTANT DISTINCTIONS:
- ✅ Acceptable: TypeScript
.d.tsrecreateshandlers.SectionData(cross-language) - ❌ Unacceptable: Go
templates.SectionDataduplicateshandlers.SectionData(in-language)
Verification:
# Compare field names across layers
# Database
rg "show_on_dashboard" database/schema/schema.sql
# Go struct (database)
rg "ShowOnDashboard" internal/database/models.go
# Handler type (enhanced)
rg "ShowOnDashboard|Icon|FormattedDate" internal/handlers/*.go
# Template (uses handler types directly - no Go duplication)
rg 'handlers\.' templates/*.templ
# TypeScript (matches handler JSON - acceptable cross-language duplication)
rg "show_on_dashboard" web/src/types/api.d.ts
rg "show_on_dashboard|icon" web/src/types/dashboard.d.ts
# CRITICAL: Check for duplicate Go types (should find none)
rg "type.*Data struct" templates/types.go | rg -v "PageData|UnsafeHTML"
# TypeScript types are OK (different language)
ls -la web/src/types/*.d.ts # Should exist
# CRITICAL: Verify TypeScript has same number of fields as Go handler
# Count Go struct fields (example: SectionData)
rg "type SectionData struct" internal/handlers/*.go -A 30 | grep 'json:' | wc -l
# Output: 11 (for example)
# Count TypeScript interface fields
rg "interface SectionData" web/src/types/dashboard.d.ts -A 30 | rg '^\s+[a-z_]+:' | wc -l
# Output: Should also be 11 (matching Go struct)
# If counts don't match, TypeScript is missing fields (CRITICAL ISSUE)
# List all fields for manual comparison
# Go fields:
rg "type SectionData struct" internal/handlers/*.go -A 30 | grep 'json:' | sed 's/.*`json:"//; s/".*//'
# TypeScript fields:
rg "interface SectionData" web/src/types/dashboard.d.ts -A 30 | rg '^\s+[a-z_]+:' | sed 's/.*://; s/:.*//'
11. Accessibility Verification
11.1 Verify Keyboard Navigation
For dashboard carousel:
- Tab key focuses book cards
- Enter/Space on focused card opens book
- Arrow keys navigate between cards
- Focus visible indicators
- No keyboard traps
- Logical tab order
Verification:
# Check tabindex attributes
rg 'tabindex=' templates/
# Check aria labels
rg 'aria-label=' templates/
# Test in browser (manual)
# 1. Load dashboard
# 2. Press Tab repeatedly
# 3. Verify focus moves through books
# 4. Press Enter on focused book
# 5. Verify book opens
11.2 Verify Screen Reader Support
ARIA attributes:
- Buttons have
aria-label - Sections have semantic headings
- Book cards have
role="button" - Live regions for dynamic updates
- Alt text on cover images
Verification:
# Check ARIA attributes
rg 'aria-' templates/
# Check image alt text
rg '<img' templates/ | rg 'alt='
# Test with screen reader (manual)
# macOS: VoiceOver
# Windows: NVDA
11.3 Verify Touch Gestures
Mobile/carousel:
- Swipe gestures work on carousels
- Tap to open book
- Long press for options
- Scroll with momentum
- No accidental clicks
Verification:
# Check overflow behavior
rg 'overflow-x-auto' templates/
# Test on mobile device (manual)
# 1. Open dashboard on phone
# 2. Swipe carousel left/right
# 3. Tap book card
# 4. Verify smooth scrolling
12. SSR + TypeScript Hybrid Verification
12.1 Verify SSR Initial Load
Critical paths:
- Dashboard loads with pre-populated SSR data (sections rendered by server)
- All sections and books visible in initial HTML (view source to verify)
- No client-side fetching on initial page load
- Library selector options rendered server-side
- Current library pre-selected in HTML
Test procedure:
- Open DevTools → Network tab
- Navigate to
/dashboard?library_id=<id> - Verify in HTML response:
- Sections present in initial HTML (not added via JS)
- Books present in initial HTML
- Library selector has all options
- View page source (Ctrl+U) and verify:
- Section HTML present in source
- Book cards present in source
Verification:
# Check template has sections parameter (SSR)
rg "templ Dashboard.*sections" templates/dashboard.templ -A 5
# Verify frontend route passes data to template
rg "templates.Dashboard.*sections" internal/router/frontend.go -A 5
# Check for initial load fetching (should be NONE)
rg "fetch.*onload|DOMContentLoaded.*fetch" web/src/dashboard.ts
# Should return nothing - no fetch on initial load
12.2 Verify TypeScript Updates
Critical paths:
- Library switching triggers JSON fetch
- Loading indicator shows during fetch
- Sections re-render after library switch
- Settings save via API, then page reload
- Error handling with toast notifications
Test procedure:
- Load dashboard (verify SSR data present)
- Change library selector
- Verify:
- Loading spinner appears
- Network tab shows
/api/dashboard/sections?library_id=<new>request - Sections update without full page reload
- URL updates with
?library_id=<new>
- Open settings, change items per section
- Verify:
- API POST to
/dashboard/settings - Success toast appears
- Page reloads with new settings applied
- API POST to
Verification:
# Check TypeScript has switchLibrary function
rg "function switchLibrary|async switchLibrary" web/src/dashboard.ts -A 10
# Verify it fetches JSON
rg "fetch.*api/dashboard/sections" web/src/dashboard.ts
# Verify renderSections function exists
rg "function renderSections" web/src/dashboard.ts -A 10
# Check settings API call
rg "api.post.*settings|fetch.*settings" web/src/dashboard.ts
13. Performance Verification
13.1 Verify Database Query Performance
Index verification:
idx_dashboard_prefs_user_libraryexistsidx_collections_dashboardexists (partial index)- Queries use indexes (EXPLAIN ANALYZE)
- No full table scans
- Limit parameters respected
Verification:
# Check indexes
psql bookhoard -c "\di" | grep dashboard
# Explain queries
psql bookhoard -c "EXPLAIN ANALYZE SELECT * FROM user_dashboard_preferences WHERE user_id = '...' AND library_id = '...'"
# Check query plans for sections
# (simulate GetSectionItems queries)
psql bookhoard -c "EXPLAIN ANALYZE SELECT * FROM media_items WHERE ... ORDER BY created_at DESC LIMIT 20"
13.2 Verify Frontend Performance
Bundle size:
dashboard.js< 50 KB (unminified)settings.js< 30 KB (unminified)- No duplicate dependencies
- Code splitting by page
- Lazy loading for images
Loading performance:
- Initial page load < 2 seconds (3G)
- Time to Interactive < 3 seconds
- Lighthouse score > 90
- No layout shifts
- Smooth scrolling
Verification:
# Check bundle sizes
ls -lh web/static/dashboard.js
ls -lh web/static/settings.js
# Check for duplicate code
rg "function " web/src/*.ts | wc -l
# Test loading (manual)
# 1. Open Chrome DevTools → Lighthouse
# 2. Run audit
# 3. Check Performance score
# 4. Check Metrics (FCP, LCP, TTI)
13.3 Verify Image Optimization
Cover images:
- Lazy loading enabled (
loading="lazy") - Fallback placeholder
- Error handling (
onerror) - Aspect ratio reserved
- No layout shift
Verification:
# Check lazy loading
rg 'loading="lazy"' templates/
# Check fallback images
rg 'onerror=' templates/ | grep placeholder
# Check aspect ratio
rg 'aspect-\[' templates/
14. Testing Verification
14.1 Verify Bruno Tests Exist
For dashboard endpoints:
bruno/dashboard/get-sections-success.bruexistsbruno/dashboard/get-sections-missing-library-id.bruexistsbruno/dashboard/get-sections-unauthorized.bruexistsbruno/dashboard/put-preferences-success.bruexistsbruno/dashboard/restore-system-collection-success.bruexistsbruno/dashboard/restore-system-collection-invalid-name.bruexists- Tests for:
- No user (401)
- Regular user (200)
- Admin user (200)
- Missing library_id (400)
- Invalid library_id (400)
- Limit parameter (default, min, max)
- User preferences applied
- Environment variables configured
- JSON response validation
For collections preview endpoint:
bruno/dashboard/preview-collection-success.bruexistsbruno/dashboard/preview-collection-manual-selection.bruexistsbruno/dashboard/preview-collection-combined.bruexistsbruno/dashboard/preview-collection-invalid-library.bruexistsbruno/dashboard/preview-collection-unauthorized.bruexists- Tests verify:
- Filter rules evaluate correctly
- Manual book selection works
- Combined filters + manual selection
- Invalid library_id returns 400
- Unauthorized request returns 401
- Response has
itemsarray (notbooks) - Response uses
media_item_idfield
Verification:
# Check dashboard test files
ls -la bruno/dashboard/
# Verify collections preview tests exist
ls -la bruno/dashboard/preview-collection-*.bru
# Run tests
cd bruno/dashboard && bru run --env local
# Verify test coverage
rg "test|assert" bruno/dashboard/*.bru
# Verify response field names
rg "media_item_id" bruno/dashboard/preview-collection-*.bru
14.2 Verify Manual Testing Checklist
Critical user flows:
- View dashboard (SSR data loaded)
- Switch library (HTMX update)
- Scroll carousel (left/right buttons)
- Open book (click card)
- Customize dashboard (settings modal)
- Hide/show sections
- Reorder sections
- Change items per section
- Update profile settings
- Change theme
- Save preferences
Edge cases:
- Empty sections (no books)
- All sections hidden
- Very long titles (truncate)
- Missing cover images (placeholder)
- Network errors (toast shown)
- Invalid library_id (error handled)
- Unauthorized (redirect to login)
Verification:
# Create test plan document
cat > TESTING_CHECKLIST.md <<EOF
# Dashboard Testing Checklist
## Smoke Tests
- [ ] Dashboard loads
- [ ] Sections render
- [ ] Books display
## Functionality Tests
- [ ] Library switching
- [ ] Carousel scrolling
- [ ] Book opening
- [ ] Settings modal
## Edge Cases
- [ ] Empty sections
- [ ] Missing covers
- [ ] Network errors
## Accessibility Tests
- [ ] Keyboard navigation
- [ ] Screen reader
- [ ] Touch gestures
## Performance Tests
- [ ] Page load time
- [ ] Lighthouse score
- [ ] Bundle size
EOF
14.3 Verify Browser Compatibility
Test in browsers:
- Chrome/Edge (Chromium)
- Firefox
- Safari (if available)
- Mobile browsers (iOS Safari, Chrome Android)
Features to test:
- CSS Grid/Flexbox
- CSS variables (theme)
- HTMX attributes
- Event delegation
- Touch events
- Smooth scrolling
Verification:
# Check for browser-specific features
rg "webkit|moz|ms" web/src/dashboard.ts
# Should be minimal (use standard APIs)
# Check CSS compatibility
rg "backdrop-filter|aspect-ratio|snap" templates/
# Verify fallbacks if needed
14.4 Verify Backend Tests Exist
Unit tests for dashboard service:
internal/services/dashboard_service_test.goexists- Tests for:
TestDashboardService_GetSystemCollectionsForDashboard- System collectionsTestDashboardService_GetUserCollectionsForDashboard- User collectionsTestDashboardService_AutoAssignRules- Rule evaluationTestDashboardService_ExcludedItems- Excluded items filtering- Uses testify/assert and testify/require
- Tests cover edge cases (empty arrays, nil values, duplicates)
Unit tests for dashboard handler:
internal/handlers/dashboard_test.goexists- Tests for:
TestBuildSectionsFromDB_ConvertsDatabaseTypes- Type conversionTestBuildSectionsFromDB_FilterHiddenCollections- Hidden filteringTestBuildSectionsFromDB_ReorderCollections- Custom orderingTestBuildSectionsFromDB_SortByPriority- Priority sorting- Validates pgtype field conversion to handler types
Integration tests with test_helpers:
internal/handlers/dashboard_integration_test.goexists- Uses
test_helpers.TestSuitefor database setup/teardown - Tests for:
TestGetSections_EndToEndFlow- Full dashboard sections APITestGetSections_WithUserCollections- User + system collectionsTestRestoreSystemCollection- System collection restoreTestRestoreSystemCollection_InvalidName- Error handling- Creates test fixtures (users, libraries, media items)
- Tests reading progress (in progress, completed, unread)
- Tests manual + auto-matched items merging
- Tests excluded items filtering
Integration tests for collections preview:
internal/handlers/collections_preview_test.goexists- Uses
test_helpers.TestSuitefor database setup/teardown - Tests for:
TestPreviewCollection_FilterRules- Filter rule evaluationTestPreviewCollection_ManualBookSelection- Manual selectionTestPreviewCollection_CombinedFiltersAndManual- Combined approachTestPreviewCollection_LimitRespected- Limit enforcementTestPreviewCollection_InvalidLibraryID- Error handling- Validates 13+ filter fields work correctly
- Validates deduplication of items
Verification:
# Check test files exist
ls -la internal/services/dashboard_service_test.go
ls -la internal/handlers/dashboard_test.go
ls -la internal/handlers/dashboard_integration_test.go
ls -la internal/handlers/collections_preview_test.go
# Run tests
go test ./internal/services/dashboard_service_test.go -v
go test ./internal/handlers/dashboard_test.go -v
go test ./internal/handlers/dashboard_integration_test.go -v
go test ./internal/handlers/collections_preview_test.go -v
# Run with coverage
go test ./internal/services/... ./internal/handlers/... -coverprofile=coverage.out
go tool cover -html=coverage.out
# Verify test helpers usage
rg "test_helpers.TestSuite" internal/handlers/dashboard_integration_test.go
rg "test_helpers.TestSuite" internal/handlers/collections_preview_test.go
# Verify 13+ filter fields tested
rg "genre|author|series|progress|rating|date_added|last_read|publisher|language|format|tags|narrators" internal/handlers/collections_preview_test.go
ls -la internal/handlers/dashboard_integration_test.go
# Run tests
go test ./internal/services/dashboard_service_test.go -v
go test ./internal/handlers/dashboard_handler_test.go -v
go test ./internal/handlers/dashboard_integration_test.go -v
# Run with coverage
go test ./internal/... -cover -coverprofile=coverage.out
go tool cover -html=coverage.out
# Verify test helpers usage
rg "test_helpers.TestSuite" internal/handlers/dashboard_integration_test.go
rg "CreateTestUser|CreateTestLibrary|CreateTestMediaItem" internal/handlers/dashboard_integration_test.go
Test coverage requirements:
- Unit tests for all service methods (filterHiddenSections, reorderSections)
- Unit tests for all helper functions
- Integration tests for all API endpoints
- Integration tests use test_helpers
- Coverage > 80% for new code
15. Cross-Reference Verification
15.1 Verify Database → Go Types
Schema.sql → models.go:
- Column names match
- Types match (UUID, TEXT[], INT, TIMESTAMP, etc.)
- NULL constraints match (pgtype vs required)
- Default values match
- Foreign keys match
Verification:
# Compare schema with generated types
# Schema
rg "user_dashboard_preferences" database/schema/schema.sql -A 15
# Generated type
rg "type UserDashboardPreferences struct" internal/database/models.go -A 15
# Check field matching
rg "pgtype\.(UUID|Text|Int4|TextArray)" internal/database/models.go
15.2 Verify Go → Template Types
Handlers → Templates:
- Handler calls service
- Service returns database types
- Frontend router passes handler types to template
- Template uses handler types directly (no conversion)
- No type mismatches
- NO
templates.SectionDatacreated (usehandlers.SectionDataif needed)
CRITICAL GUIDELINE:
- Templates import handlers package
- Templates use
handlers.CollectionData,handlers.BookInfodirectly - Handler types enhanced with template-specific fields (Icon, etc.)
- No conversion helper functions
- No parallel type systems
Verification:
# Check handler to template flow
# Handler
rg "GetSectionItems" internal/router/frontend.go -B 5 -A 20
# Template uses handler types (NOT template types)
rg 'handlers\.(CollectionData|BookInfo|SectionData)' templates/*.templ
# CRITICAL: Check for duplicate types in templates (should be none)
rg "type.*Data struct" templates/types.go | grep -v "PageData|UnsafeHTML"
# Check for conversion functions (should be none)
rg "func.*ToTemplate|func.*Convert" internal/router/frontend.go
15.3 Verify Handler → TypeScript Types
Handler JSON → TypeScript (.d.ts):
- Handler types have JSON tags (snake_case)
- TypeScript interfaces recreate handler JSON structure
- Field names use snake_case in both
- Types match (pgtype correctly mapped to TypeScript)
- No missing fields
- Template-specific fields in handler types reflected in TypeScript
- ACCEPTABLE: TypeScript duplicates handler types (cross-language, necessary)
pgtype Mapping Verification:
// Go handler
type BookInfo struct {
MediaItemID pgtype.UUID `json:"media_item_id"`
Title pgtype.Text `json:"title"`
CoverImage pgtype.Text `json:"cover_image"`
IsFeatured pgtype.Bool `json:"is_featured"`
Tags pgtype.TextArray `json:"tags"`
}
// TypeScript .d.ts (acceptable recreation)
// Source: handlers.BookInfo in collections.go
interface BookInfo {
media_item_id: string; // string field in Go
title: string; // string field in Go
author: string; // string field in Go
cover_image_path: string; // string field in Go
}
Verification:
# Handler type with JSON tags (in collections.go)
rg "type BookInfo struct" internal/handlers/collections.go -A 10
# TypeScript type matches handler JSON
rg "interface BookInfo" web/src/types/dashboard.d.ts -A 10
# Compare field names (manual verification)
# Handler (collections.go): json:"media_item_id"
# TypeScript: media_item_id: string
# Verify correct field names (NOT "id")
rg "json:\"id\"" internal/handlers/collections.go | grep BookInfo -A 5
# Should return nothing - BookInfo uses MediaItemID
# TypeScript should use media_item_id (not id)
rg "id: string" web/src/types/dashboard.d.ts | grep -i bookinfo
# Should return nothing for BookInfo - uses media_item_id
# CRITICAL: No Go template types in TypeScript (this is wrong)
rg "templates\." web/src/types/dashboard.d.ts
# Should return nothing - TypeScript references handler JSON, not Go template types
# TypeScript types are OK (acceptable cross-language duplication)
ls -la web/src/types/*.d.ts
15.4 Verify TypeScript → API Responses
API → TypeScript:
- Handler returns JSON
- JSON structure matches TypeScript interface IN FULL
- All fields from Go handler present in TypeScript
- Field names match (snake_case)
- Types match (pgtype correctly mapped)
- Null handling consistent (pgtype.Text →
string | undefined) - No missing fields (complete type safety)
Verification:
# API response structure
rg "buildJSONSections" internal/handlers/dashboard.go -A 30
# TypeScript interface
rg "interface (SectionData|BookCardData)" web/src/types/dashboard.d.ts
# Test API response and compare with TypeScript
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8080/api/dashboard/sections?library_id=..." | jq '.sections[0]'
# Compare API response fields with TypeScript interface
# API response example:
# {
# "id": "continue-reading",
# "type": "system",
# "title": "Continue Reading",
# "description": "Books you're currently reading",
# "icon": "📖",
# "items": [...],
# "view_all_url": "/section/continue-reading",
# "priority": 1
# }
# TypeScript interface MUST have all 8 fields above
interface SectionData {
id: string;
is_system: boolean; // true for system collections, false for user collections
title: string;
description: string;
icon: string;
items: BookInfo[];
view_all_url: string;
priority: number;
// Missing any of these = CRITICAL TYPE SAFETY ISSUE
}
# TypeScript interface MUST have all 4 fields for BookInfo
interface BookInfo {
media_item_id: string; // NOT "id"
title: string;
author: string;
cover_image_path: string;
// Missing any of these = CRITICAL TYPE SAFETY ISSUE
}
# CRITICAL: Count fields in API response vs TypeScript
# Count API response fields
curl -s -H "Authorization: Bearer $TOKEN" \
"http://localhost:8080/api/dashboard/sections?library_id=..." | jq '.sections[0] | keys | length'
# Count TypeScript interface fields
rg "interface SectionData" web/src/types/dashboard.d.ts -A 30 | rg '^\s+[a-z_]+:' | wc -l
# Both counts MUST match (example: 9 = 9)
16. Architecture Compliance Verification
16.1 Verify Project Guidelines Adherence
Frontend standards:
- TailwindCSS classes only (no custom CSS)
- TypeScript in web/src/ (no inline JavaScript)
- Procedural/imperative style (no OOP)
- SSR for initial data
- Progressive enhancement
- HTMX for updates
- Event delegation pattern
- API client usage
- Toast notification usage
- NO duplicate type definitions (use handler types directly)
- Handler types enhanced with template fields (not parallel types)
- NO conversion helper functions (use handler types directly)
Backend standards:
- All business logic in services
- Handlers use service layer
- No direct database access from handlers
- Atomic schema changes
- No migration files
- Bruno tests for API endpoints
Code organization:
- Handler types defined in
internal/handlers/*.go - Templates import handlers package
- Templates use handler types directly
- Template-only types for UI concerns only (PageData, UnsafeHTML)
Verification:
# Check for custom CSS (should be none)
rg "<style>" templates/dashboard.templ
# Check for inline JS (should be none)
rg "<script>" templates/dashboard.templ | grep -v "src="
# Check for OOP (should be none)
rg "class |this\." web/src/dashboard.ts
# Verify TailwindCSS usage
rg 'class="[^"]*"' templates/dashboard.templ | head -20
# CRITICAL: Check for duplicate type definitions
rg "type.*Data struct" templates/types.go
# Should have NO dashboard data types - use handler types
# Verify templates use handler types
rg 'handlers\.' templates/*.templ
# Check for conversion helpers (should be none)
rg "func.*ToTemplate|func.*Convert.*To" templates/*.go
# Should return nothing - use handler types directly
16.2 Verify Service Layer Pattern
All business logic in services:
- Handlers don't query database directly
- Service methods are reusable
- Same service used by SSR and API
- Service has no HTTP concerns
- Service returns raw data
- Handler formats for templates/API
Verification:
# Check handler for direct DB access (should be none)
rg "Queries\." internal/handlers/dashboard.go
# Check service usage
rg "dashboardService\." internal/router/frontend.go
rg "dashboardService\." internal/handlers/dashboard.go
16.3 Verify Single Source of Truth
Service layer is source of truth:
- SSR uses DashboardService
- API handler uses DashboardService
- No duplicate logic
- Same data for all clients
Verification:
# Find all service usage
rg "DashboardService" internal/ -r
# Verify no duplicate queries in handlers
rg "SELECT.*FROM" internal/handlers/dashboard.go
# Should return nothing
17. Contradiction Detection
17.1 Check for Internal Contradictions
Search for conflicting statements:
- Timeline estimates consistent (3-4 days, not 2 weeks)
- Architecture description consistent throughout
- Type definitions match in all sections
- Code examples follow guidelines
- Phase dependencies are acyclic
- No "do X" in one section, "don't do X" in another
CRITICAL: Type Definition Contradictions
- NO
templates/types.gotypes that duplicate handler types in Go - Plan does NOT create
templates.SectionDataortemplates.BookCardDatain Go - Plan DOES use handler types directly or enhance them
- No conversion helper functions mentioned for Go
- Handler types are source of truth in Go
- ACCEPTABLE: TypeScript
.d.tsfiles recreate types (cross-language, necessary) - Plan distinguishes between Go duplication (wrong) and TypeScript duplication (acceptable)
- CRITICAL: TypeScript types must include ALL fields from Go handlers (no partial types)
- Plan doesn't mention TypeScript types with "optional" or "partial" fields
- Field counts match between Go structs and TypeScript interfaces
- No "TypeScript has subset of fields for UI only" (violates type safety)
Common contradictions:
- "Procedural style only" but examples show classes
- "No inline JavaScript" but templates have
<script>blocks - "SSR first" but initial load uses AJAX
- "Progressive enhancement" but forms require JavaScript
- "3-4 days" but tasks estimate 10+ days
- "No duplicate types" but plan creates
templates.SectionData - "Use handler types" but plan has conversion functions
Verification:
# Search for class usage
rg "class " CAROUSEL_DASHBOARD_PLAN.md
# Should be in examples of what NOT to do
# Search for timeline
rg "day|days|week|weeks" CAROUSEL_DASHBOARD_PLAN.md
# Verify consistency
# Check for inline JS mentions
rg "<script>" CAROUSEL_DASHBOARD_PLAN.md
# Should only mention external script tags
# CRITICAL: Check for duplicate type creation
rg "templates/types.go|templates\.SectionData|templates\.BookCardData" CAROUSEL_DASHBOARD_PLAN.md
# Should find nothing - use handler types instead
# Check for enhancement pattern (correct)
rg "Enhance.*handler|template-specific fields" CAROUSEL_DASHBOARD_PLAN.md
# Should mention enhancing handler types
# Check for conversion functions (wrong)
rg "convert|ToTemplate|MapTo" CAROUSEL_DASHBOARD_PLAN.md -i
# Should be minimal or none for type conversions
# CRITICAL: Check for partial TypeScript types (violates guidelines)
rg "partial|subset|fields.*needed|UI.*only" CAROUSEL_DASHBOARD_PLAN.md -i
# Should not mention TypeScript having fewer fields than Go
# Check if plan documents full type matching
rg "all fields|complete|full.*match|every field" CAROUSEL_DASHBOARD_PLAN.md -i
# Should mention TypeScript matching Go types completely
17.2 Check for Contradictions with TypeScript Plan
Carousel Dashboard assumes TypeScript Conversion completed:
- No conflicts with TypeScript architecture
- Uses same event delegation pattern
- Uses same shared modules
- Follows same procedural style
- No duplicate type definitions
Verification:
# Compare event delegation patterns
rg "data-action" TYPESCRIPT_CONVERSION_PLAN.md
rg "data-action" CAROUSEL_DASHBOARD_PLAN.md
# Should be consistent
# Compare module structure
rg "web/src/" TYPESCRIPT_CONVERSION_PLAN.md
rg "web/src/" CAROUSEL_DASHBOARD_PLAN.md
# Carousel should use existing modules
# Check for duplicate type definitions
rg "interface.*Data" TYPESCRIPT_CONVERSION_PLAN.md
rg "interface.*Data" CAROUSEL_DASHBOARD_PLAN.md
# Carousel types should be new, not duplicates
17.3 Check for Contradictions with Codebase
Verify plan matches existing patterns:
- Router pattern matches existing routes
- Service pattern matches existing services
- Template pattern matches existing templates
- Handler pattern matches existing handlers
- TypeScript pattern matches existing TS
Verification:
# Compare with existing router
rg "func register" internal/router/*.go
# Carousel should follow same pattern
# Compare with existing service
rg "type.*Service struct" internal/services/*.go
# Carousel should follow same pattern
# Compare with existing handler
rg "type.*Handler struct" internal/handlers/*.go
# Carousel should follow same pattern
# Compare with existing TypeScript
rg "^function " web/src/*.ts
# Carousel should follow same pattern
18. Realism Verification
18.1 Verify Timeline Estimates
Phase duration estimates:
- Phase 1 (Schema): 2-3 hours ✅
- Phase 2 (Service): 3-4 hours ✅
- Phase 3 (Queries): 1-2 hours ✅
- Phase 4 (Handler): 1-2 hours ✅
- Phase 5 (Bruno Tests): 1 hour ✅
- Phase 6 (TypeScript Types): 30 min ✅
- Phase 7 (Router): 30 min ✅
- Phase 8 (SSR Routes): 1-2 hours ✅
- Phase 9 (Template): 2 hours ✅
- Phase 10 (TypeScript): 2-3 hours ✅
- Phase 11 (Unit Tests): 3-4 hours ✅
- Phase 12 (Documentation): 2-3 hours ✅
Total: 20-28 hours = 3-4 days ✅
Verification:
# Sum phase durations
# (Manual calculation from plan)
# Check for missing tasks
rg "TODO|TBD|FIXME" CAROUSEL_DASHBOARD_PLAN.md
# Should return nothing
18.2 Verify Effort Estimates
For each phase:
- Line counts are accurate (if mentioned)
- File counts are accurate
- Complexity matches duration
- No phase exceeds 1 day (except maybe templates)
- Parallel work is possible where stated
Verification:
# Check for unrealistic estimates
# (Manual review of each phase)
18.3 Verify Technical Feasibility
Check for over-ambitious goals:
- No entirely new architecture (uses existing patterns)
- No unproven technologies (uses Go, templ, HTMX, TailwindCSS)
- No breaking changes to existing systems
- Rollback is possible (delete files, revert schema)
- Testing is feasible within timeline
- No blocking dependencies
Red flags:
- "Need to refactor entire X"
- "Rewrite Y from scratch"
- "New framework Z"
- "Breaking change to W"
Verification:
# Search for red flags
rg "refactor|rewrite|breaking" CAROUSEL_DASHBOARD_PLAN.md -i
# Should only mention positive changes
19. Bruno Tests Verification
19.1 Verify Bruno Test Directory Created
Required files in bruno/dashboard/:
get-sections-success.bru- Happy path testget-sections-missing-library-id.bru- Missing required parameterget-sections-invalid-library-id.bru- Invalid UUID formatget-sections-unauthorized.bru- No authenticationput-preferences-success.bru- Update preferencesput-preferences-unauthorized.bru- No authenticationrestore-system-collection-success.bru- Restore collectionrestore-system-collection-invalid-name.bru- Invalid collection namerestore-system-collection-unauthorized.bru- No authentication
Verification:
# Check Bruno test directory exists
ls -la bruno/dashboard/ || echo "Directory does not exist yet"
# Verify test files exist (after Phase 12 of plan)
ls -la bruno/dashboard/*.bru 2>/dev/null | wc -l
# Should return at least 9 after Phase 12
# Run Bruno tests
cd bruno/dashboard && bru run --env local
19.2 Verify Bruno Test Response Structure
For get-sections-success.bru:
- Response includes
is_systemfield (boolean) - Response does NOT include
typefield (old field name) - Books have
media_item_idfield (notid) - Response structure matches Go handler JSON tags
- Assertions verify correct field types
For restore-system-collection-success.bru:
- Request body uses
collection_nameparameter - Valid collection names: continue-reading, recently-added, recently-read, not-started
- Invalid names return 400 status
- Success returns 200 status with message
Verification:
# Check Bruno test files for correct field names
rg "is_system" bruno/dashboard/*.bru
rg "media_item_id" bruno/dashboard/*.bru
rg "hidden_collections|collection_order" bruno/dashboard/*.bru
# Verify no old field names
rg "\"type\":\s*\"smart\"|\"type\":\s*\"collection\"" bruno/dashboard/*.bru
# Should return nothing
# Verify no old id field (except media_item_id which is correct)
rg "\"id\":\s*\"[^\"]*\"" bruno/dashboard/*.bru | grep -v "media_item_id"
# Should return nothing
# Check collection_name in restore tests
rg "collection_name" bruno/dashboard/restore-system-collection-*.bru
20. Documentation Verification
19.1 Verify Developer API Documentation
File: docs/developer/api/dashboard.md:
- Updated to show
is_system: boolean(nottype: string) - Updated to show 4 system collections (removed "In Progress")
- Books use
media_item_idfield (notid) - Field names updated:
hidden_collections,collection_order - Restore System Collection endpoint documented
- Response examples match Bruno tests
- Error cases documented
- Table of system collections shows correct 4 entries
Verification:
# Check API documentation has correct field names
rg "is_system.*boolean" docs/developer/api/dashboard.md
rg "media_item_id" docs/developer/api/dashboard.md
rg "hidden_collections|collection_order" docs/developer/api/dashboard.md
# Verify no old field names
rg "\"type\":\s*\"smart\"" docs/developer/api/dashboard.md
# Should return nothing
# Verify only 4 system collections (no "In Progress")
rg "In Progress" docs/developer/api/dashboard.md
# Should return nothing
# Count system collections mentioned
rg "continue-reading|recently-added|recently-read|not-started" docs/developer/api/dashboard.md | wc -l
# Should return at least 4
19.2 Verify User Documentation
File: docs/user/dashboard.md:
- Lists only 4 smart sections (no "In Progress")
- Explains system collections can be restored to defaults
- Customization instructions mention "System" badge
- Uses "collection" terminology consistently
- No references to "smart sections" concept
- Keyboard navigation documented
- Touch gestures documented
Verification:
# Check user documentation
rg "In Progress" docs/user/dashboard.md
# Should return nothing
# Verify 4 sections listed
rg "Continue Reading|Recently Added|Recently Read|Not Started" docs/user/dashboard.md
# Should return all 4
# Check for collection terminology
rg "collection" docs/user/dashboard.md | wc -l
# Should appear multiple times
# Verify system collection restore mentioned
rg "restore|System.*badge" docs/user/dashboard.md -i
# Should mention restoration feature
# Verify no old smart sections references
rg "smart section" docs/user/dashboard.md -i
# Should return nothing
19.3 Verify Contributing Documentation
File: docs/contributing/development.md:
dashboard.goadded to handlers listdashboard_service.goadded to services list- Handler types mentioned use
handlers.SectionDataandhandlers.BookInfo - Notes about shared types in
collections.go - No duplicate type definitions mentioned
Verification:
# Check development guide
rg "dashboard\.go" docs/contributing/development.md
rg "dashboard_service\.go" docs/contributing/development.md
# Verify handler types mentioned correctly
rg "handlers\.(SectionData|BookInfo)" docs/contributing/development.md
# Verify collections.go mentioned as shared types
rg "collections\.go.*SectionData|collections\.go.*BookInfo" docs/contributing/development.md
19.4 Verify API Reference
File: docs/developer/api/api-reference.md:
- Dashboard section added to index
- Links to
./dashboard.mddocumentation - Endpoints listed in correct section
- All three endpoints mentioned:
- Get Dashboard Sections
- Update Dashboard Preferences
- Restore System Collection
Verification:
# Check API reference index
rg "Dashboard" docs/developer/api/api-reference.md -A 5
rg "\./dashboard\.md" docs/developer/api/api-reference.md
# Verify all three endpoints mentioned
rg "Get Dashboard Sections|Update Dashboard Preferences|Restore System Collection" docs/developer/api/api-reference.md
19.5 Verify Custom Section Builder Documentation
File: docs/developer/api/custom-section-builder.md:
- File exists or is planned
- Preview Collection endpoint documented
- Create Custom Section endpoint documented
- 13+ filter fields documented with operators
- Request/response examples provided
- Filter rule structure explained
- Manual book selection explained
- Field types (text, number, date, select) documented
- Response format matches Bruno tests
Verification:
# Check custom section builder documentation
ls -la docs/developer/api/custom-section-builder.md
# Verify filter fields documented
rg "title|author|genre|series|progress|rating|date_added|last_read|publisher|language|format|tags|narrators" docs/developer/api/custom-section-builder.md | wc -l
# Should be 13 or more
# Verify preview endpoint documented
rg "Preview Collection|POST.*preview" docs/developer/api/custom-section-builder.md
# Verify operators documented
rg "equals|contains|greater_than|less_than|is_set|between" docs/developer/api/custom-section-builder.md
# Verify response format
rg "media_item_id" docs/developer/api/custom-section-builder.md
File: docs/user/dashboard.md:
- Custom Sections section added or planned
- Step-by-step instructions for creating custom sections
- Examples of filter rules provided
- 13+ filter fields listed with descriptions
- Manual book selection explained
- Preview functionality explained
- Example custom sections provided
Verification:
# Check user documentation has custom sections
rg "Custom Section|custom section" docs/user/dashboard.md -i
# Verify filter fields listed for users
rg "genre|author|series|progress|rating" docs/user/dashboard.md | wc -l
# Should mention several fields
# Verify examples provided
rg "example|For example|e\.g\." docs/user/dashboard.md -A 2 | rg "Sci-Fi|Fiction"
19.6 Verify Operations Documentation
File: docs/operations/operations.md (if exists):
- Database recreation process documented
- Schema change warnings included
- System collection restoration process documented
- Troubleshooting guide updated if needed
Verification:
# Check operations docs for dashboard-related content
rg "dashboard|system collection|schema.*recreate" docs/operations/operations.md -i
# Check if documentation exists
ls -la docs/operations/operations.md || echo "File may not exist yet"
21. Type Definition Documentation
20.1 Verify Type Definition Documentation
For each type in web/src/types/dashboard.d.ts:
- Comment explains purpose
- Comments reference source (Go handler file)
- Comments reference Go struct name
- All fields documented (not just some)
- Fields have inline comments if purpose unclear
- Complex types have usage examples
- Document field count (e.g., "// 11 fields from handlers.SectionData")
Example:
// web/src/types/dashboard.d.ts
// Matches handlers.SectionData from internal/handlers/collections.go
// JSON response from /api/dashboard/sections
// All 8 fields from Go struct included
export interface SectionData {
id: string; // Section identifier
is_system: boolean; // true for system collections, false for user collections
title: string; // Section display title
description: string; // Section description
icon: string; // Section icon (emoji)
items: BookInfo[]; // Books in this section
view_all_url: string; // Link to view all items
priority: number; // Display order priority
}
// Matches handlers.BookInfo from internal/handlers/collections.go
// All 4 fields from Go struct included
export interface BookInfo {
media_item_id: string; // Book identifier (NOT "id")
title: string; // Book title
author: string; // Book author
cover_image_path: string; // Path to cover image
}
For Go handler types (internal/handlers/*.go):
- All struct fields have JSON tags
- Comments explain purpose if not obvious
- Exported types have package-level documentation
- pgtype fields documented if behavior is unclear
Verification:
# Check TypeScript type documentation
cat web/src/types/dashboard.d.ts | rg "//"
# Verify field count mentioned in comments
rg "All.*fields included|fields from" web/src/types/dashboard.d.ts
# Check Go handler documentation
rg "type.*struct" internal/handlers/*.go -B 5 | rg "//"
# Verify all Go fields have JSON tags
rg "type.*struct" internal/handlers/*.go -A 30 | grep -v 'json:"'
# Should return nothing (all fields should have json tags)
Verification:
# Check for comments
rg "// " templates/types.go | rg -A 5 "SectionData|BookCardData"
# Check TypeScript comments
rg "// " web/src/types/dashboard.d.ts
19.2 Verify Code Example Accuracy
For each code example in plan:
- Code compiles/runs without errors
- Imports are correct
- Type annotations are accurate
- Variable names match plan's conventions
- No syntax errors
Test by:
- Copying example to actual file
- Running
go buildornpm run build:ts - Checking for compilation errors
Verification:
# Extract Go code examples and test
# (Manual testing)
# Extract TypeScript code examples and test
# (Manual testing)
19.3 Verify Template Examples
For each template example:
- Valid Go template syntax (
templ) - Correct handler type references
- Script tags use correct paths
- No inline JavaScript
- HTMX attributes are correct
- TailwindCSS classes are valid
Verification:
# Check template syntax
rg "templ " templates/
# Should use `templ` keyword
# Check HTMX attributes
rg "hx-" templates/dashboard.templ
# Should be valid HTMX attributes
# Check TailwindCSS classes
rg "class=\"" templates/dashboard.templ | head -10
# Should be valid Tailwind classes
22. Missing Information Detection
20.1 Check for Unaddressed Features
Features mentioned but not implemented:
- All dashboard sections implemented
- All API endpoints implemented
- All template components implemented
- All TypeScript modules implemented
- All settings options implemented
Verification:
# Find features mentioned
rg "Section|Carousel|Settings|Modal" CAROUSEL_DASHBOARD_PLAN.md
# Check if all have implementation
rg "Phase.*:" CAROUSEL_DASHBOARD_PLAN.md
# Verify all features covered in phases
20.2 Check for Missing API Endpoints
For each operation:
- API endpoint exists
- Endpoint is registered
- Handler implements endpoint
- Bruno test exists
- Type definition matches
Find all API calls:
# Find fetch calls in plan
rg "fetch\(|api\.(get|post|put|delete)" CAROUSEL_DASHBOARD_PLAN.md
# Verify endpoints exist
rg "GET|POST|PUT|DELETE" internal/router/dashboard.go
20.3 Check for Missing Build Steps
Verify plan includes:
- Database schema recreation instructions
- sqlc generate command
- Go build command
- TypeScript compilation command
- Template generation command
- Docker integration
Check:
# Find build commands
rg "sqlc generate|go build|templ generate|npm run build:ts" CAROUSEL_DASHBOARD_PLAN.md
Summary Checklist
Before approving the Carousel dashboard plan, verify:
Critical (Must Pass)
- TypeScript Conversion Plan completed first
- Database schema changes are atomic
- No migration files (direct schema.sql modification)
- All business logic in service layer
- Procedural/imperative style (no OOP)
- SSR for initial data
- Progressive enhancement maintained
- Event delegation pattern used
- TailwindCSS only (no custom CSS)
- NO duplicate Go types (use
handlers.*types, nottemplates.*types) - Handler types enhanced with template fields (not parallel Go type systems)
- NO Go conversion helper functions (templates use
handlers.*types directly) - TypeScript
.d.tstypes acceptable (necessary due to pgtype, cross-language) - TypeScript interfaces match Go handler types IN FULL (all fields, no partial types)
- Field count matches (Go struct has N fields = TypeScript has N fields)
- pgtype mappings correct (Text→string|undefined, UUID→string, Timestamp→string, etc.)
- API endpoints match TypeScript interfaces
- Bruno tests exist for all endpoints
- Database indexes created
- No breaking changes to existing systems
Important (Should Pass)
- Timeline estimates are realistic (3-4 days)
- Code examples are accurate
- Template examples compile
- TypeScript modules compile
- Accessibility attributes present
- Keyboard navigation works
- Touch gestures work
- Performance is acceptable (< 2s load time)
- Bundle sizes are reasonable (< 50 KB)
- Browser compatibility verified
- No internal contradictions
- No contradictions with TypeScript plan
- No contradictions with codebase
- Documentation is clear
- Rollback strategy is defined
Nice to Have
- Common pitfalls documented
- Verification commands provided
- Testing checklist comprehensive
- Examples follow guidelines
- Plan is easy to follow
- Success criteria are measurable
Usage Instructions
- Before Starting Review: Read the entire CAROUSEL_DASHBOARD_PLAN.md
- During Review: Go through each section of this checklist systematically
- For Each Item: Run the provided verification commands
- Document Findings: Note any issues found with file/line references
- Categorize Issues: Mark as Critical, Important, or Nice to Have
- Verify Fixes: After fixing issues, re-run relevant checklist sections
Common Issues Found
- Duplicate Go Types: Creating
templates.SectionDatawhenhandlers.SectionDatashould be used (CRITICAL VIOLATION in Go) - Partial TypeScript Types: TypeScript interfaces missing fields from Go handler structs (TYPE SAFETY ISSUE)
- Conversion Helper Functions: Unnecessary mapping between handler and template types in Go
- Parallel Go Type Systems: Handler types + template types for same data (violates guidelines in Go)
- Type Mismatches: Handler types don't match TypeScript interfaces (field names, types, or count)
- pgtype Mapping Errors: Incorrect mapping from Go pgtype to TypeScript types
- Missing Indexes: Database queries slow due to missing indexes
- Breaking Changes: Schema changes break existing functionality
- OOP Patterns: Classes or
thisreferences in TypeScript - Inline JavaScript:
<script>blocks in templates - Missing HTMX Fallbacks: Forms don't work without JavaScript
- Accessibility Issues: Missing ARIA attributes or keyboard support
- Performance Issues: Large bundle sizes or slow queries
- Timeline Errors: Underestimated effort for phases
- Contradictions: Plan conflicts with itself or other guidelines
IMPORTANT DISTINCTIONS:
- ❌ Wrong:
templates/types.gocreating duplicate Go types - ✅ Correct:
web/src/types/*.d.tsrecreating types for TypeScript (necessary due to pgtype) - ❌ Wrong: TypeScript interfaces with partial fields (e.g., only 3 of 10 Go struct fields)
- ✅ Correct: TypeScript interfaces with ALL fields from Go handler struct
IMPORTANT DISTINCTIONS:
- ❌ Wrong:
templates/types.gocreating duplicate Go types - ✅ Correct:
web/src/types/*.d.tsrecreating types for TypeScript (necessary due to pgtype) - ❌ Wrong: TypeScript interfaces with partial fields (e.g., only 3 of 10 Go struct fields)
- ✅ Correct: TypeScript interfaces with ALL fields from Go handler struct
Created: 2025-02-18 Purpose: Comprehensive verification of Carousel Dashboard plan Version: 1.0