Files
bookhoard/PROJECT_GUIDELINES.md
john-okeefe 5b7540be99 docs: update Alpine.js migration guide and project guidelines
- Fix incorrect function references in ALPINE_COMPLETION_GUIDE.md
  - header.changeThemeTo -> changeTheme
  - header.logout -> logout
  - woodPaneling.change -> changeWoodPaneling
- Add SSR-first principles section to PROJECT_GUIDELINES.md
- Add page type classifications (Type 1, 2, 3)
- Fix extra asterisks on line 43
- Update to reference TypeScript instead of JavaScript
2026-03-13 12:50:47 -04:00

22 KiB

Combined Project Guidelines for Bookhoard

🚨 CRITICAL PROHIBITIONS (Never violate these)

Backend & Database

  • *NEVER modify backend code when working on frontend-only tasks
  • NEVER modify database schema unless explicitly instructed for full-stack changes
  • NEVER use Docker - use Podman only
  • NEVER build server binaries locally - all builds through Dockerfile/docker-compose
  • NEVER create new migration files - merge changes into current one until release
  • NEVER use git checkout on schema files without checking what will be lost
  • NEVER break existing functionality unless explicitly instructed
  • NEVER duplicate business logic - keep logic in services, not handlers
  • NEVER bypass service layer - all database operations must go through services

Testing

  • *ALWAYS use setupTestServer() helper from cmd/server/tests/test_helpers_test.go
  • Share one test setup across all subtests - call setupTestServer() once at test function level, not per subtest
  • Prefer table-driven tests - use t.Run() with test cases instead of duplicate test functions
  • Configure database pools efficiently - use max_conns=1 for test pools (via pgxpool.ParseConfig()) to prevent connection exhaustion
  • NEVER create separate pgxpool per test - each pool creates 4 connections by default; 78 tests = 312 potential connections > PostgreSQL's 100 limit
  • NEVER call setupTestServer() in loops or within subtests - creates unnecessary database pools and exhausts connections
  • DO verify tests pass - run full test suite before completing work
  • Use t.Cleanup() properly - the TestServerSetup pattern automatically handles cleanup via t.Cleanup()

Frontend & Styling

  • *NEVER modify backend/API for frontend features without user confirmation
  • NEVER use custom CSS - TailwindCSS classes only
    • ⚠️ EXCEPTION: templates/error.templ may have inline CSS because error pages must work when main app fails (404, server errors, CSS fails to load)
  • NEVER use JavaScript - convert all to TypeScript
    • ⚠️ EXCEPTION: Inline JS function calls in HTML attributes (e.g., onclick="myFunction()") are acceptable for simple interactions
  • NEVER use Object-Oriented Programming (no classes, inheritance, or this-capture)
  • DO use procedural/imperative style as your default
  • DO borrow functional techniques when they simplify code
  • DO avoid ideological purity - the best paradigm is the one that fits the problem
  • *NEVER add new Dockerfiles without user confirmation
  • NEVER fetch initial data via AJAX on page load - use server-side rendering instead
  • NEVER break progressive enhancement - pages must work without JavaScript

Note: Go methods in the backend are fine and encouraged. This guideline applies to TypeScript/JavaScript frontend code only.

General

  • NEVER skip pre-commit hooks unless explicitly requested
  • NEVER force push to main/master branches
  • NEVER commit files with secrets (.env, credentials.json, etc.)
  • NEVER make assumptions - ask clarifying questions when uncertain
  • NEVER delete code without reading full context first (minimum 20 lines before/after)
  • *NEVER make cascading fix-up edits without git diff review
    • After compilation error: STOP, review git diff, understand full impact
    • Use revert/reapply pattern instead of blind fixes
  • NEVER skip post-edit verification - must compile after each file edit
  • NEVER run git commands concurrently - always run sequentially:
    • Run git add <files> and wait for completion
    • Run git commit -m "<message>" and wait for completion
    • Run git push and wait for completion
    • Never use && to chain git commands together
  • *Make good organized git commits for the entire project (not just what you changed) and push - run git add, commit, push sequentially as separate commands, no need to repeatedly check status
  • *Indentation is always 2 spaces unless the language prohibits it

Cascading Fix-up Pattern (PROHIBITED)

WHAT NOT TO DO - This caused critical bugs:

// ❌ WRONG: Blindly making fixes after compilation error

Edit 1: Delete deprecated code
[Compilation error: undefined Register]

Edit 2: Try to fix error (over-broad deletion)
[More errors: undefined Login, GetProfile, etc.]

Edit 3: Try to fix again (worse damage)
[Even more errors: major functionality broken]

CORRECT APPROACH:

// ✅ CORRECT: Stop, understand, then fix deliberately

Edit 1: Delete deprecated code
[Compilation error: undefined Register]

STOP  Review git diff  Understand Register was accidentally deleted
RESTORE  Get exact Register function from git history
VERIFY  Compile successfully

Key Principle: When compilation errors occur after edits:

  1. STOP - Don't make more edits
  2. ANALYZE - Use git diff to understand what was changed
  3. RECOVER - Restore what was accidentally deleted/broken
  4. VERIFY - Compile and test before proceeding

🎯 CONTEXT-SPECIFIC RULES

When Working on Frontend-Only Tasks

  • DO NOT touch backend code - handlers, services, database layer
  • DO NOT modify API routes - use existing endpoints only
  • DO NOT change database schema - work with existing structure
  • If backend change seems necessary:
    1. Identify the required change
    2. Explain why you need it
    3. Provide impact analysis
    4. *ASK FOR USER CONFIRMATION before proceeding

When Working on Full-Stack Tasks

  • Backend changes are allowed when explicitly part of the task
  • Still follow all database protocols (atomic changes, validation, etc.)
  • If modifying database schema: Update local database after schema.sql changes (see Database Operations section)
  • Still use Podman for all builds
  • Still include Bruno requests for API changes

MANDATORY REQUIREMENTS

Database Operations (Full-Stack Tasks Only)

  • Follow pgx v5 standards for all database operations
  • Treat schema changes as ATOMIC - complete success or complete rejection
  • *⚠️ CRITICAL: This is a pre-production application (NO production deployments exist)
    • When database/schema/schema.sql is updated, local databases must be updated
    • Option 1 (Recommended): Recreate database with fresh schema:
      podman compose down -v  # Delete volumes (WARNING: loses all data)
      podman compose up -d    # Start fresh with new schema
      
    • Option 2: Manually apply schema changes to existing database using psql
    • DO NOT create migration files - no legacy schema support needed
  • Use pre-change checklist: read schema → identify columns → plan changes → verify → read back
  • Post-change validation: ensure schema.sql, models.go, and queries.sql are in sync

Build & Deployment

  • Use Podman exclusively (not Docker)
  • All builds through existing Dockerfile and *docker-compose.yml
  • Stop building server binaries - everything goes through containers

API Changes (Full-Stack Tasks Only)

  • Include Bruno OpenCollection YAML requests with all API documentation
  • Integration Tests (cmd/server/tests) must be comprehensive and cover three contexts: no user, user, and admin
  • Maintain backward compatibility for mobile apps and external consumers

Frontend & Styling

  • Always use TailwindCSS classes for all styling
  • Convert all JavaScript to TypeScript
  • Never use Object-Oriented Programming (no classes, inheritance, or this-capture)
  • Use procedural/imperative style as your default
  • Borrow functional techniques when they simplify code
  • Avoid ideological purity - the best paradigm is the one that fits the problem
  • Extract domain concepts/types only when clearly beneficial - apply YAGNI, avoid over-engineering
  • Render initial data server-side in Go templates for fast page loads
  • Use JavaScript/HTMX for CRUD operations (create, update, delete)
  • Ensure progressive enhancement - pages work without JavaScript

Service Layer Architecture

  • All business logic in services - never in handlers
  • Services must be reusable by both SSR handlers and API endpoints
  • Database operations through services only - never direct from handlers
  • When adding features: Add service logic → Create API endpoint → Use SSR for initial render → Use JS for updates

Code Organization

  • Minimize project structure changes
  • Place new files in *contextually appropriate directories
  • Follow KISS, DRY, and YAGNI principles
  • Use multiple, logical git commits with clear messages
  • NEVER duplicate types between handlers and templates - define data types in handlers, reuse directly in templates
  • DO share handler types with templates - templates should use handlers.CollectionData, handlers.BookInfo, etc. directly
  • NEVER create parallel type systems - no templates.XxxData types for data that originates from handlers
  • DO enhance handler types with template-specific fields when needed (e.g., add Icon, FormattedDate fields to handlers structs)
  • DO keep template-only types in templates package - PageData, UnsafeHTML, and template-specific utilities are appropriate
  • NEVER create conversion helper functions to map between handler and template types - use handler types directly

Configuration & Environment

  • If .env is missing, auto-generate secure values
  • Never commit secrets to repository

Code Modification Safety

  • Post-Edit Verification (MANDATORY for ALL file modifications):
    • Run go build for affected packages immediately after each edit
    • Review git diff filename to verify only intended changes
    • Validate functionality still works as expected
    • Never proceed to next file until current edit is verified
  • Backup Before Large Changes:
    • Create a stash: git stash push -m "Pre-cleanup snapshot" before removing >50 lines
    • Or create a backup branch: git branch backup-before-cleanup
    • This allows instant recovery if mistakes occur
  • Large Deletion Safety Pattern:
    • Read at least 20 lines before/after deletion target
    • Include unique identifiers in match (function signatures, specific comments)
    • Use narrow matches - avoid generic patterns
    • Verify line numbers match intended section

Documentation

Documentation Structure (updated with full docs system):

  • README.md - Project overview, quick start, and setup instructions only
  • docs/ - Comprehensive documentation system with search
  • docs/developer/api/ - API reference documentation (split by endpoint/category)
  • docs/user/ - User-facing features, guides, and workflows
  • docs/user/devices/ - Device setup guides (KOBO, KOReader, etc.)
  • docs/contributing/ - Development and contribution guides

Where to document changes:

Change Type Location Examples
User-facing features docs/user/ New features, UI changes, workflows
API endpoints docs/developer/api/<category>/<endpoint>.md New endpoints, modified responses, authentication changes
API behavior Update existing docs/developer/api/ files Parameter changes, error codes, rate limits
Device setup docs/user/devices/ New device support, setup instructions
Development docs/contributing/ Build changes, architecture decisions
Quick start/setup README.md Installation, environment setup, first-run
Breaking changes Both README.md and relevant docs/ Migration guides, deprecation notices
Bug fixes Update relevant docs/ only if user-visible Clarifications, troubleshooting additions
Bruno OpenCollection YAML requests .yml files in bruno folder in appropriate folder/sub-folder API contract testing, examples

Documentation Update Workflow:

  1. Identify the audience (end users, developers, API consumers)
  2. Choose appropriate location based on table above
  3. Update documentation before or with code changes
  4. Verify documentation renders at /docs endpoint
  5. Test search finds new/updated content
  6. For API changes: Update both docs/developer/api/ files, integration test files (cmd/server/tests) AND Bruno OpenCollection YAML .yml files
  7. Commit separately with clear message: docs: <description>

When in doubt:

  • End-user visible → docs/user/
  • API reference → docs/developer/api/
  • Setup/onboarding → README.md
  • Development related → docs/contributing/

Process & Continuity

  • If mid-task and receive "no response", *continue the task
  • Verify no regressions before modifying/removing code

🔧 TECHNICAL STANDARDS

Backend Stack

  • Language: Go 1.25+
  • Database: PostgreSQL 15+ with pgx v5 driver only
  • Authentication: JWT tokens with bcrypt password hashing
  • Architecture: Service layer pattern (handlers → services → database)

Frontend Stack

  • Rendering: SSR-first - Go templates render initial page with all data
  • Interactivity: Alpine.js for UI state (modals, dropdowns, transitions)
  • Dynamic Updates: HTMX for CRUD operations (no full page reloads)
  • Styling: TailwindCSS (no custom CSS)
  • Language: TypeScript (no JavaScript)
  • Patterns: Procedural/imperative with functional techniques where helpful (no OOP)

SSR-First Principles

  • NEVER fetch initial data via AJAX - server renders complete page
  • NEVER fetch data in Alpine x-init - data already SSR'd
  • Use Alpine.js only for UI state - modal visibility, dropdown toggles
  • Use HTMX for dynamic operations - form submissions, partial page updates
  • x-init is for setup only - event listeners, not data fetching
  • Data fetching happens after user actions - not on page load

Page Type Classifications

  1. Type 1: 80% SSR (most pages)

    • Backend provides all initial data
    • Alpine handles modals/dropdowns only
    • x-init NEVER fetches data
  2. Type 2: SSR + Interactive (dashboard, bookshelf)

    • Backend provides initial data
    • Alpine handles interactivity (drag-drop, filtering)
    • x-init ONLY sets up event listeners
  3. Type 3: 80% TypeScript (analytics, complex dashboards)

    • Some client-side data fetching acceptable
    • Still prefer SSR when possible

Containerization

  • Runtime: Podman (not Docker)
  • Build: Existing Dockerfile and docker-compose.yml only
  • No local builds allowed

🔄 ERROR RECOVERY PROTOCOL

When code modification mistakes occur (deleted wrong code, broke compilation, etc.):

Immediate Actions

  1. STOP - Don't make more edits
  2. ASSESS - What was deleted? Is it critical?
  3. REVIEW - Run git diff to see exact changes
  4. RESTORE - Choose appropriate recovery method:
    • Recent mistake: git checkout -- filename
    • Complex restoration: Use git show HEAD:filename to recover deleted code
    • Multiple files: git reset HEAD~1 (if safe)
  5. VERIFY - Compile and test restored code
  6. DOCUMENT - Note what went wrong for future reference

Recovery Examples

# Recover a deleted function from original file
git show HEAD:internal/handlers/auth.go | sed -n '70,275p' > recovery.txt

# Revert entire file to original state
git checkout HEAD -- internal/handlers/auth.go

# Use git stash to save current state before rollback
git stash push -m "Broken state before fix"
git checkout -- internal/handlers/auth.go

Prevention (Learn From Mistakes)

  • Why did the mistake happen?
  • Was it too-broad matching?
  • Was it insufficient context reading?
  • Was it cascading fix-up attempts?
  • Update guidelines to prevent recurrence

📋 WORKFLOW CHECKLISTS

Before Making Frontend-Only Changes

  • Identify if backend modification could make implementation simpler
  • Plan to use existing API endpoints only
  • If backend change seems necessary, prepare confirmation request:
    • Required change description
    • Why it would help
    • Impact analysis
    • Alternative approaches considered
  • Plan git commit structure (multiple logical commits)
  • Identify documentation location (see Documentation section):
    • User-facing feature → docs/user/
    • UI/workflow changes → docs/user/
    • Setup instructions → README.md

Before Making Full-Stack Changes

  • Read current schema completely (if database changes)
  • Identify all columns that must be preserved
  • Plan exact changes needed
  • Verify Podman will be used for builds
  • Plan git commit structure (multiple logical commits)
  • Identify documentation location:
    • API changes → docs/developer/api/<category>/
    • New endpoints → Create new .md file in docs/developer/api/
    • API behavior → Update existing docs/developer/api/ files
    • Breaking changes → Both README.md + relevant docs/
    • Bruno OpenCollection YAML .yml files → Update/create alongside API changes
    • Integration tests (cmd/server/tests) → Update/create alongside API changes

During Schema Changes (Full-Stack Only)

  • Read current schema completely
  • Identify all columns that must be preserved
  • Plan exact changes needed
  • Set up verification step
  • Make intended changes
  • Immediately verify by reading back modified sections
  • Confirm ALL expected columns are present
  • Verify schema.sql, models.go, and queries.sql are in sync
  • Update local database (choose ONE):
    • Option 1 - Recreate database (recommended, loses data):
      podman compose down -v  # Delete all volumes
      podman compose up -d    # Start with fresh schema
      
    • Option 2 - Manual SQL migration (preserves data):
      podman exec bookhoard_db psql -U postgres -d bookhoard -c "YOUR SQL HERE"
      
  • Verify database has new schema (check column types, indexes, etc.)

After API Changes

  • Create/update Bruno OpenCollection YAML requests
  • Create/update Integration tests (cmd/server/tests)
  • Test with no user context
  • Test with regular user context
  • Test with admin context
  • Verify backward compatibility

Before Committing

  • Run verification script: bash scripts/verify-guidelines.sh
  • Fix any errors - verification must pass (0 errors) to commit
  • Note warnings - informational only, do not auto-fix
  • Run tests: go test ./... -v
  • Run lint/typecheck if available
  • Ensure no secrets in changes
  • Verify logical commit structure
  • Update documentation (see Documentation section):
    • User-facing changes → docs/user/
    • API changes → docs/developer/api/ + Integration tests + Bruno OpenCollection YAML .yml files
    • Setup/onboarding → README.md
    • Development changes → docs/contributing/
  • Verify docs render at /docs endpoint
  • Test docs search finds new content

Error Recovery Protocol (If Code Mistakes Occur)

  • Stop immediately - don't make more edits
  • Assess impact: What was deleted? Is it critical?
  • Review git diff: See exact changes made
  • Restore strategy:
    • If recent mistake: git checkout -- filename
    • If complex: Reconstruct from git diff using git show HEAD:filename
  • Verify recovery: Compile and test restored code
  • Document mistake: Note what went wrong for future reference

Phase Completion Verification (Before Declaring "Complete")

  • All target code is removed/intact as intended
  • No unintended code was deleted
  • All affected files compile successfully
  • Run go build ./... for entire project
  • Run verification script: bash scripts/verify-guidelines.sh - must pass (0 errors)
  • No critical functionality was broken
  • Git diff shows only intended changes
  • Review all modified files with git diff --stat

🏗 ARCHITECTURAL PATTERNS

Current: SSR-First with Alpine.js + HTMX

Browser → Go template (SSR with all data) → Display instantly
                      ↓
        Alpine.js for UI state (modals, dropdowns)
                      ↓
                 HTMX for CRUD (forms, updates)
                      ↓
           Shared service layer (Go)

Layer Responsibilities:

Layer Responsibility
Go Template SSR initial page with real data
Alpine.js UI state only (x-data, x-show, transitions)
HTMX Dynamic updates without page reload
TypeScript Pure business logic (API calls, data processing)

See also: SSR_FIRST_ALPINE_GUIDE.md and ALPINE_COMPLETION_GUIDE.md

Ultimately, whenever you are unsure just ask for confirmation.