Document multi-user planning intent, reminder alerts pattern, useConfirmAction hook, and naive datetime contract. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
8.8 KiB
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-composecommands — 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
AlertsProvidercontext. Dashboard showsAlertBanner, other pages get Sonner toasts (max 3 + summary). Snooze/dismiss withclient_nowfor Docker UTC offset. - Two-click delete:
useConfirmActionhook inhooks/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()fromlib/date-utils.tswhen the backend needs "now". Docker container runs UTC;client_nowbridges the gap.
Known Issues
- Git push auth flake. The first
git pushto 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 (noZsuffix, no.toISOString()). Use local datetime formatting helpers instead. - Eager load relationships in async SQLAlchemy. Any
response_modelthat includes a relationship (e.g.ProjectResponse.tasks) must useselectinload()when querying. Lazy loading raisesMissingGreenletin async context. - Never shadow SQLAlchemy names. Model columns must not be named
relationship,column,metadata, or other SQLAlchemy reserved names. Theperson.pymodel aliases the import assa_relationshipfor this reason. - Frontend date inputs require exact formats.
<input type="date">needsYYYY-MM-DD,<input type="datetime-local">needsYYYY-MM-DDThh:mm. Backend may return2026-02-15T00:00:00which 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
urllibfor 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
Authority Links
- 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()- usedatetime.now(timezone.utc)instead (deprecated in 3.12) - Do not return relationships from async endpoints without
selectinload() - Do not use
curlin backend Docker healthchecks (not available in python:slim) - Do not use
git push --forceor destructive git operations without explicit approval