UMBRA/.claude/CLAUDE.md
Kyle Pope e1e546c50c Add future roadmap and key features to CLAUDE.md
Document multi-user planning intent, reminder alerts pattern,
useConfirmAction hook, and naive datetime contract.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 04:01:59 +08:00

8.8 KiB

CLAUDE.md - UMBRA

CRUCIAL CONTEXT EFFICIENCY

Subagent Discipline

  • Prefer inline work for tasks under ~5 tool calls. Subagents have overhead — don't delegate trivially.
  • When using subagents, include output rules: "Final response under 2000 characters. List outcomes, not process."
  • Never call TaskOutput twice for the same subagent. If it times out, increase the timeout — don't re-read.

File Reading

  • Read files with purpose. Before reading a file, know what you're looking for.
  • Use Grep to locate relevant sections before reading entire large files.
  • Never re-read a file you've already read in this session.
  • For files over 500 lines, use offset/limit to read only the relevant section.

Responses

  • Don't echo back file contents you just read — the user can see them.
  • Don't narrate tool calls ("Let me read the file..." / "Now I'll edit..."). Just do it.
  • Keep explanations proportional to complexity. Simple changes need one sentence, not three paragraphs.

Deployment & Access

  • UMBRA is hosted on a remote machine at http://10.0.69.35 (not localhost).
  • Login PIN: 1993
  • Rebuilds are manual. Do not run docker-compose commands — the user will rebuild on the remote machine when needed.
  • Browser automation can access the live app at http://10.0.69.35.

Operational Procedures

  • Before making any changes or edits to code, create a branch. Once work is completed, commit and push to the branch automatically, then merge into main and push.
  • When required: For backend work invoke the 'backend-engineer' subagent, for work on the front end, invoke the 'frontend-engineer' subagent. To review work use the 'senior-code-reviewer' subagent and for any research use the 'research-analyst' subagent.
  • For any frontend UI related work you MUST use the frontend design skill AND reference the stylesheet to ensure visual consistency. The stylesheet defines all colors, typography, spacing, component patterns, and design principles for UMBRA. Do not invent new patterns.

Future Roadmap

After the UI refresh is complete, the next major phases are:

  • Multi-user authentication — replace single PIN auth with per-user accounts
  • Backend restructure — add user_id foreign keys to all models, scope all queries per-user
  • Always build for scale. Even though UMBRA is currently single-user, design features, indexes, validations, and state machines with multi-user in mind. Cutting corners now means rework later.

Key Features & Patterns

  • Reminder alerts: Real-time polling (30s) via AlertsProvider context. Dashboard shows AlertBanner, other pages get Sonner toasts (max 3 + summary). Snooze/dismiss with client_now for Docker UTC offset.
  • Two-click delete: useConfirmAction hook in hooks/useConfirmAction.ts — first click shows "Sure?", auto-resets after 4s, second click executes. Used by TodoItem and ReminderItem.
  • Naive datetime contract: All datetimes are naive (no timezone). Frontend sends toLocalDatetime() from lib/date-utils.ts when the backend needs "now". Docker container runs UTC; client_now bridges the gap.

Known Issues

  • Git push auth flake. The first git push to the Gitea remote will fail with an authentication error. Simply retry the same push command — the second attempt succeeds.

Hard Rules

  • Naive datetimes only. The DB uses TIMESTAMP WITHOUT TIME ZONE. Never send timezone-aware strings (no Z suffix, no .toISOString()). Use local datetime formatting helpers instead.
  • Eager load relationships in async SQLAlchemy. Any response_model that includes a relationship (e.g. ProjectResponse.tasks) must use selectinload() when querying. Lazy loading raises MissingGreenlet in async context.
  • Never shadow SQLAlchemy names. Model columns must not be named relationship, column, metadata, or other SQLAlchemy reserved names. The person.py model aliases the import as sa_relationship for this reason.
  • Frontend date inputs require exact formats. <input type="date"> needs YYYY-MM-DD, <input type="datetime-local"> needs YYYY-MM-DDThh:mm. Backend may return 2026-02-15T00:00:00 which must be sliced/converted before binding.
  • All API routes are prefixed with /api. Frontend axios base URL is /api. Nginx proxies /api/ to backend:8000. Never duplicate the prefix.

Tech Stack

Backend

  • Python 3.12 (slim Docker image - no curl, use urllib for healthchecks)
  • FastAPI with async lifespan, Pydantic v2 (model_dump(), ConfigDict(from_attributes=True))
  • SQLAlchemy 2.0 async with Mapped[] type hints, mapped_column(), async_sessionmaker
  • PostgreSQL 16 (Alpine) via asyncpg
  • Alembic for migrations
  • Auth: PIN-based with bcrypt + itsdangerous signed cookies (not JWT)

Frontend

  • React 18 + TypeScript + Vite 6
  • TanStack Query v5 for server state (useQuery, useMutation, invalidateQueries)
  • FullCalendar 6 (dayGrid, timeGrid, interaction plugins)
  • Tailwind CSS 3 with custom dark theme + accent color CSS variables
  • Sonner for toast notifications
  • Lucide React for icons
  • Custom shadcn/ui-style components in frontend/src/components/ui/

Infrastructure

  • Docker Compose - 3 services: db, backend, frontend
  • Nginx (Alpine) serves frontend SPA, proxies /api/ to backend
  • Frontend served on port 80, backend on port 8000
  • ui_refresh.md - UI overhaul project plan, audit findings, implementation stages
  • stylesheet.md - Design system & visual reference for all frontend work

Project Structure

backend/app/
  main.py          # FastAPI app, router registration, health endpoint
  config.py        # Pydantic BaseSettings (DATABASE_URL, SECRET_KEY)
  database.py      # Async engine, session factory, get_db dependency
  models/          # SQLAlchemy 2.0 models (Mapped[] style)
  schemas/         # Pydantic v2 request/response schemas
  routers/         # One router per feature (auth, todos, events, etc.)

frontend/src/
  App.tsx           # Routes + ProtectedRoute wrapper
  lib/api.ts        # Axios instance + getErrorMessage helper
  hooks/            # useAuth, useSettings, useTheme, useCalendars
  types/index.ts    # TypeScript interfaces matching backend schemas
  components/
    ui/             # 16 base components (Button, Dialog, Sheet, Card, Input, Select, etc.)
    layout/         # AppLayout + Sidebar
    auth/           # LockScreen (PIN setup/login)
    dashboard/      # DashboardPage + widgets (Stats, Calendar, Todo, Upcoming, Countdown, etc.)
    calendar/       # CalendarPage, CalendarSidebar, CalendarForm, EventForm, TemplateForm
    todos/          # TodosPage + TodoList + TodoItem + TodoForm
    reminders/      # RemindersPage + ReminderList + ReminderForm
    projects/       # ProjectsPage, ProjectCard, ProjectDetail, ProjectForm, KanbanBoard, TaskRow, TaskForm, TaskDetailPanel
    people/         # PeoplePage + PersonCard + PersonForm
    locations/      # LocationsPage + LocationCard + LocationForm
    settings/       # SettingsPage (accent color, PIN change, first day of week)

Essential Commands

# Build and run all services
docker-compose up --build

# Rebuild single service after changes
docker-compose up --build backend
docker-compose up --build frontend

# View logs
docker-compose logs -f
docker-compose logs -f backend

# Reset database (destroys all data)
docker-compose down -v && docker-compose up --build

# Stop everything
docker-compose down

API Routes

All routes require auth (signed cookie) except /api/auth/* and /health.

Prefix Resource
/api/auth PIN setup/login/logout/status
/api/todos Todos CRUD + toggle
/api/events Calendar events CRUD (incl. recurring)
/api/event-templates Event templates CRUD
/api/calendars User calendars CRUD + visibility
/api/reminders Reminders CRUD + dismiss
/api/projects Projects + nested tasks CRUD
/api/people People CRUD
/api/locations Locations CRUD + search
/api/settings Settings + PIN change
/api/dashboard Dashboard aggregation
/api/upcoming Unified upcoming items
/api/weather Weather data proxy

Stop Conditions

  • Do not add timezone info to datetime strings sent to the backend
  • Do not use datetime.utcnow() - use datetime.now(timezone.utc) instead (deprecated in 3.12)
  • Do not return relationships from async endpoints without selectinload()
  • Do not use curl in backend Docker healthchecks (not available in python:slim)
  • Do not use git push --force or destructive git operations without explicit approval