UPDATES:
- Remove smart_section_types table references
- Update for collections table with user_id, query_type, priority, is_system_collection
- Update TypeScript type examples (8 fields instead of 11)
- Update type field values ('system'/'user' instead of 'smart'/'collection')
- Update method names: getContinueReading, getNotStarted, RestoreSystemCollection
- Update field names: hidden_collections, collection_order
- Update template verification for collection terminology
- Add per-collection restore button verification
- Remove getInProgress and getUnread method references
- Update all example code to match unified architecture
VERIFICATION:
- All checklist items now verify unified collections approach
- Type examples show correct 8-field structure
- System collections properly distinguished from user collections
- Per-collection restore functionality included
86 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.
⚠️ 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"
type: string; // matches Go's json:"type" ("system" or "user")
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
❌ UNACCEPTABLE: Partial TypeScript Types
// WRONG: TypeScript interface with only subset of Go fields (breaks type safety)
interface SectionData {
id: string;
type: string;
title: string;
items: BookInfo[];
// Missing: description, icon, view_all_url, priority
// This is a PARTIAL type and violates type safety guidelines
}
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) *DashboardServiceGetSectionItems(ctx, userID, libraryID, limit, sectionOrder, hiddenSections) ([]SectionItems, error)filterHiddenSections(items []SectionItems, hidden []string) []SectionItemsreorderSections(items []SectionItems, order []string) []SectionItemsgetContinueReading(ctx, userID, libraryID, limit) ([]MediaItems, error)getRecentlyAdded(ctx, libraryID, limit) ([]MediaItems, error)getRecentlyRead(ctx, userID, libraryID, limit) ([]MediaItems, error)getNotStarted(ctx, userID, libraryID, limit) ([]MediaItems, error)getCollectionSections(ctx, userID, libraryID, limit) ([]SectionItems, error)GetDashboardPreferences(ctx, userID, libraryID) (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 "GetSectionItems.*\[\]SectionItems" 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 getCollectionSections 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 getCollectionSections implementation
rg "func.*getCollectionSections" 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
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
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 sections)
Response format:
- Returns JSON object with
sectionsarray - Each section has:
id(collection key or name)type("system" or "user")titleiconitems(array of books)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 Helper Functions
Required helpers:
buildJSONSections(items []SectionItems) []map[string]interface{}getSectionType(key string) stringgetSectionTitle(key string) stringgetSectionIcon(key string) stringgetSectionViewAllURL(key string) string
System collections mapping:
continue-reading→ type: "system", title: "Continue Reading", icon: "📖"recently-added→ type: "system", title: "Recently Added", icon: "🆕"recently-read→ type: "system", title: "Recently Read", icon: "✅"not-started→ type: "system", title: "Not Started", icon: "📕"
User collections:
- Non-system collections → type: "user"
- Title uses collection name
- Icon uses collection icon
- view_all_url is empty string
Verification:
# Check helper functions
rg "func (getSectionType|getSectionTitle|getSectionIcon|getSectionViewAllURL)" internal/handlers/dashboard.go -A 10
# Verify mappings
rg "continue-reading|recently-added|recently-read|unread" internal/handlers/dashboard.go
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
For internal/handlers/collections.go:
PreviewAutoAssignRulesmethod exists- POST
/api/collections/previewroute registered - Accepts library_id, rules, limit in request body
- Evaluates rules against library items
- Returns matching books with count
- Uses existing
collectionService.EvaluateRules() - Returns
handlers.BookInfoformat
Request format:
{
"library_id": "uuid",
"rules": [
{
"id": "rule1",
"field": "genre",
"operator": "equals",
"value": "Sci-Fi",
"priority": 5
}
],
"limit": 20
}
Response format:
{
"books": [
{
"id": "uuid",
"title": "Dune",
"author": "Frank Herbert",
"cover_image_path": "/path/to/cover.jpg"
}
],
"count": 2
}
Verification:
# Check handler method exists
rg "func.*PreviewAutoAssignRules" internal/handlers/collections.go -A 30
# Check route registration
rg 'POST.*"/preview"' internal/router/collections.go
# Verify EvaluateRules usage
rg "collectionService.EvaluateRules" internal/handlers/collections.go
# Check BookInfo conversion
rg "handlers.BookInfo" internal/handlers/collections.go
6.4 Verify Config Setup
For internal/router/router.go:
DashboardServiceadded to Config structDashboardHandleradded to Config struct- Service initialized in main.go
- Handler initialized in main.go
- Passed to router via Config
For cmd/server/main.go:
cfg.DashboardService = services.NewDashboardService(cfg.Queries)cfg.DashboardHandler = handlers.NewDashboardHandler(cfg.Queries)- Both initialized before router setup
Verification:
# Check Config struct
rg "type Config struct" internal/router/router.go -A 20
# Check initialization in main.go
rg "DashboardService|DashboardHandler" cmd/server/main.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-collection-typedata-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
For templates/custom_section.templ:
- SSR page for creating filter-based custom sections
- Uses handler types (libraries, user)
- Section details form (name, description, library selector)
- Auto-assign rules section with dynamic rule addition
- Preview section showing matching books
- Form uses
data-action="create-custom-section" - Cancel button uses
data-action="cancel-create-section" - TailwindCSS only
- Event delegation
- Includes required scripts (custom-section-builder.js)
- Links to
/custom-sectionroute
Form structure verification:
- Name input (required)
- Description textarea (optional)
- Library selector (required, populated from SSR)
- Rules container with
id="rules-container" - "Add Rule" button with
data-action="add-rule" - Preview button with
data-action="preview-section" - Submit button with
data-action="create-custom-section" - Cancel button with
data-action="cancel-create-section"
Rule fields (dynamically added via JavaScript):
- Field selector (genre, author, series, language, publisher, copyright_year, tags)
- Operator selector (equals, contains, starts_with, ends_with, greater_than, less_than)
- Value input (text)
- Priority input (number, 1-10)
- Remove button with
data-action="remove-rule"anddata-rule-id
Preview section:
- Preview container with
id="preview-container" - Shows loading state while fetching
- Displays matching books in grid layout
- Shows count of matching books
- Handles empty results gracefully
Dashboard settings modal enhancement:
- "Create Custom Section" button in settings modal
- Links to
/custom-sectionpage - Has description explaining what custom sections are
- Styled with accent color
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 parameter)
rg "CustomSectionBuilder.*libraries" templates/custom_section.templ
# Verify data-action attributes
rg 'data-action="(add-rule|remove-rule|preview-section|create-custom-section|cancel-create-section)"' 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="description"' templates/custom_section.templ
# Verify rules container
rg 'id="rules-container"' templates/custom_section.templ
# Verify preview container
rg 'id="preview-container"' templates/custom_section.templ
# Verify TailwindCSS only (no custom CSS)
rg '<style' templates/custom_section.templ
# Should return nothing
# Verify event delegation (no inline onclick)
rg 'onclick=' templates/custom_section.templ
# Should return nothing
# Verify script includes
rg 'custom-section-builder.js' templates/custom_section.templ
# Check dashboard settings modal has Create Custom Section button
rg 'Create Custom Section' templates/dashboard.templ
rg '/custom-section' templates/dashboard.templ
JavaScript integration verification:
- No duplicate change event listener (library select uses delegation only)
- Event delegation handles all form actions
- Rules dynamically added via
insertAdjacentHTML - Form submission prevented, JSON sent via API
- Preview updates DOM without page reload
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
For web/src/custom-section-builder.ts:
- Procedural style (no classes, no
this) - Functions exported to
windowobject - Event delegation via
data-actionattributes - Dynamic rule addition/removal
- Rule interface matches Go services.Rule
- Uses shared API client and toast
- Preview functionality with loading states
Required functions:
addRule(): void- Adds new rule row to formremoveRule(ruleId: string): void- Removes rule rowcollectRules(): Rule[]- Collects all rules from formpreviewSection(): Promise<void>- Calls preview API, displays resultscreateCustomSection(): Promise<void>- Creates collection with rulescancelCreateSection(): void- Navigates back to dashboardinitializeCustomSectionBuilder(): void- Sets up event delegation
Rule structure verification:
- Rule interface has: id, field, operator, value, priority
- Field options: genre, author, series, language, publisher, copyright_year, tags
- Operator options: equals, contains, starts_with, ends_with, greater_than, less_than
- Priority: 1-10 (default 5)
Preview functionality:
- Validates library_id is selected
- Validates at least one rule exists
- Calls
/api/collections/previewendpoint - Displays matching books in grid
- Shows count of matches
- 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 Rule interface
rg "interface Rule" web/src/custom-section-builder.ts -A 10
# Check required functions exist
rg "^function (addRule|removeRule|collectRules|previewSection|createCustomSection|cancelCreateSection)" 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|info)" web/src/custom-section-builder.ts
# Verify rule HTML generation
rg "ruleHTML|insertAdjacentHTML" web/src/custom-section-builder.ts
# Verify event delegation
rg "addEventListener\('click'" web/src/custom-section-builder.ts -A 50
Form verification:
- No duplicate change event listener (only delegation handles library select)
- Event delegation handles all actions
- Actions: add-rule, remove-rule, preview-section, create-custom-section, cancel-create-section
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/dashboard.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 responseBookCardDatainterface matches ALL fields fromhandlers.BookInfoJSON response- 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→string | undefinedpgtype.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)
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
rg "type BookInfo struct" internal/handlers/*.go -A 30 | grep 'json:"'
# Step 2: Extract TypeScript interface
rg "interface 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/dashboard.go):
# type SectionData struct {
# ID string `json:"id"`
# Type string `json:"type"`
# Title string `json:"title"`
# Description pgtype.Text `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;
# type: string; // "system" or "user"
# title: string;
# description: string;
# icon: string;
# items: BookInfo[];
# view_all_url: string;
# priority: number;
# }
# TypeScript interface (web/src/types/dashboard.d.ts) - MUST HAVE ALL 5 FIELDS:
# interface BookInfo {
# media_item_id: string; // ✅ matches
# title: string | undefined; // ✅ matches
# author: string | undefined; // ✅ matches
# cover_image_path: string | undefined; // ✅ matches
# created_at: string; // ✅ matches
# }
# ❌ WRONG - Missing fields (partial type definition):
# interface BookInfo {
# media_item_id: string;
# title: string;
# author: string;
# // Missing: cover_image_path, created_at
# }
# 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/dashboard.go)
type SectionData struct {
ID string `json:"id"`
Type string `json:"type"`
Title string `json:"title"`
Description pgtype.Text `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;
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;
type: string;
title: string;
items: BookInfo[];
// Missing: description, icon, view_all_url, priority
}
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.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
Verification:
# Check test files
ls -la bruno/dashboard/
# Run tests
# (using Bruno CLI or UI)
# Verify test coverage
rg "test|assert" bruno/dashboard/*.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_FilterHiddenSections- Tests section filteringTestDashboardService_ReorderSections- Tests custom orderingTestDashboardService_GetSectionItems- Validates method signature
- Uses testify/assert and testify/require
- Tests cover edge cases (empty arrays, nil values, duplicates)
Unit tests for dashboard handler:
internal/handlers/dashboard_handler_test.goexists- Tests for:
TestBuildSections- Tests conversion from service to handler typesTestGetSectionHelpers- Tests helper functions- getSectionType, getSectionTitle, getSectionIcon
- Validates pgtype field conversion
Integration tests with test_helpers:
internal/handlers/dashboard_integration_test.goexists- Uses
test_helpers.TestSuitefor database setup/teardown - Tests for:
TestGetSections- Full dashboard sections APITestGetSections_UserPreferences- Preferences (hidden, order, limits)TestGetSections_CustomCollections- Auto-assign rulesTestGetSections_Validation- Error handling (400 errors)
- 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:
internal/handlers/collections_integration_test.gomodified- Adds:
TestPreviewAutoAssignRules- Tests rule preview endpointTestCreateCollectionWithAutoAssign- Tests collection creation
- Validates genre filtering works
- Validates auto-assign rules persist to database
Verification:
# Check test files exist
ls -la internal/services/dashboard_service_test.go
ls -la internal/handlers/dashboard_handler_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)
interface BookInfo {
media_item_id: string; // pgtype.UUID → string
title: string | undefined; // pgtype.Text → string | undefined
cover_image: string | undefined; // pgtype.Text → string | undefined
is_featured: boolean; // pgtype.Bool → boolean
tags: string[]; // pgtype.TextArray → string[]
}
Verification:
# Handler type with JSON tags
rg "type.*struct" internal/handlers/*.go -A 20 | grep "json:"
# TypeScript type matches handler JSON
rg "interface" web/src/types/dashboard.d.ts -A 15
# Compare field names (manual verification)
# Handler: json:"media_item_id"
# TypeScript: media_item_id: string
# Verify pgtype mapping
rg "pgtype\.(UUID|Text|Bool|TextArray)" internal/handlers/*.go
# Corresponding TypeScript should use string, boolean, string[]
# 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;
type: string; // "system" or "user"
title: string;
description: string;
icon: string;
items: BookInfo[];
view_all_url: string;
priority: number;
// 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 (Router): 30 min ✅
- Phase 6 (Frontend Routes): 1-2 hours ✅
- Phase 7 (Template Types): 30 min ✅
- Phase 8 (Settings Template): 2 hours ✅
- Phase 9 (Templates): 4-5 hours ✅
- Phase 10 (TypeScript): 2-3 hours ✅
Total: 18-24 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. Documentation Verification
19.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/dashboard.go
// JSON response from /api/dashboard/sections
// All 8 fields from Go struct included
export interface SectionData {
id: string; // Section identifier
type: string; // "system" or "user"
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
}
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
20. 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