1 Development Guide
GSD Agent edited this page 2026-04-03 21:08:31 +00:00

Development Guide

Getting Started

Prerequisites

  • Docker + Docker Compose
  • Node.js 18+ (for frontend dev)
  • Python 3.11+ (for backend dev)
  • SSH access to ub01

Local Development

The simplest approach is working directly on ub01:

ssh ub01
cd /vmPool/r/repos/xpltdco/chrysopedia

For frontend-only work, you can run Vite locally and proxy to the remote API:

cd frontend
npm install
npm run dev  # Vite dev server with /api proxy to localhost:8001

Project Structure

chrysopedia/
├── backend/
│   ├── config.py           # Settings (env vars, LRU cached)
│   ├── database.py         # Async SQLAlchemy engine + session
│   ├── main.py             # FastAPI app, router registration
│   ├── models.py           # All 13 SQLAlchemy models
│   ├── schemas.py          # Pydantic request/response schemas
│   ├── search_service.py   # Async search (Qdrant + keyword fallback)
│   ├── redis_client.py     # Async Redis client
│   ├── watcher.py          # Transcript folder watcher
│   ├── routers/            # FastAPI route handlers
│   ├── pipeline/           # Celery pipeline stages
│   │   ├── stages.py       # Stage implementations
│   │   └── quality/        # Prompt quality toolkit
│   ├── services/           # Business logic services
│   └── tests/              # pytest test suite
├── frontend/
│   └── src/
│       ├── App.tsx          # Routes, layout
│       ├── App.css          # All styles (5,820 lines)
│       ├── main.tsx         # React entry point
│       ├── api/             # API client (public-client.ts)
│       ├── pages/           # Page components (11)
│       ├── components/      # Shared components (11+)
│       ├── hooks/           # Custom hooks (3)
│       └── utils/           # Utilities (citations, slugs)
├── prompts/                 # LLM prompt templates
├── alembic/                 # DB migrations (if configured)
├── docker-compose.yml
├── Dockerfile.api
├── Dockerfile.web
└── CLAUDE.md               # AI agent development reference

Common Gotchas

asyncpg Timestamp Errors

Use datetime.now(timezone.utc).replace(tzinfo=None) for all timestamp defaults. asyncpg rejects timezone-aware datetimes for TIMESTAMP WITHOUT TIME ZONE columns.

SQLAlchemy Column Name Conflicts

Never name a column relationship, query, or metadata — these shadow ORM functions. Use from sqlalchemy.orm import relationship as sa_relationship if the schema requires it.

Vite Build Constants

Always wrap with JSON.stringify(): define: { __APP_VERSION__: JSON.stringify(version) }. Without it, the built code gets unquoted values (syntax error).

Docker ARG/ENV Ordering

ARG VITE_FOO=defaultENV VITE_FOO=$VITE_FOORUN npm run build. The ENV line must appear before the build step.

Slim Docker Images

python:3.x-slim doesn't include procps (no pgrep, ps). Use python -c "import os; os.kill(1, 0)" for healthchecks.

Host Port 8000 Conflict

Port 8000 on ub01 may be used by kerf-engine. Use 8001 for local testing, or ensure kerf-engine is stopped.

Nginx Stale DNS

After rebuilding API container, restart the web container: docker compose restart chrysopedia-web-8096.

ZFS Filesystem Watchers

Use watchdog.observers.polling.PollingObserver instead of the default inotify observer — inotify doesn't reliably detect changes on ZFS/NFS.

File Stability for SCP Uploads

Wait for file size stability (check twice with 2-second gap) before processing files received via SCP/rsync.

Testing

cd backend
python -m pytest tests/ -v

Tests use:

  • NullPool for async engine (prevents connection pool contention)
  • Module-level patching for Celery stage globals (_engine, _SessionLocal)
  • patch('pipeline.stages.run_pipeline') for lazy import mocking (not at the router level)

Adding New Features

New API Endpoint

  1. Create router in backend/routers/foo.py with APIRouter(prefix="/foo", tags=["foo"])
  2. Register in backend/main.py: app.include_router(foo.router, prefix="/api/v1")
  3. Define schemas in backend/schemas.py
  4. Use paginated response: {items, total, offset, limit}

New Frontend Route

  1. Add <Route> to App.tsx
  2. Create page component in frontend/src/pages/
  3. Call useDocumentTitle() in the component
  4. Add API functions to public-client.ts

New Database Model

  1. Add to backend/models.py
  2. Add schemas to backend/schemas.py
  3. Apply DDL manually or via Alembic migration
  4. Use _now() helper for timestamp defaults

New CSS

  1. Append to App.css using BEM naming
  2. Use CSS custom properties for all colors
  3. Prefer 768px breakpoint for mobile/desktop split
  4. Namespace Phase 2 selectors: .p2-feature__element

See also: Architecture, Frontend, Deployment