diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-03-22 23:45:19 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-03-22 23:45:19 +0000 |
| commit | 8abc63efdbc0bb96cd6c9aa99d6e9166e0bcabae (patch) | |
| tree | f4d6a082eed9b10bc67436a3ca5188e0182961eb /DESIGN.md | |
| parent | 11b905fd437d651b2e39745aa82a5dd36f70331e (diff) | |
chore: unify and centralize agent configuration in .agent/
Diffstat (limited to 'DESIGN.md')
| -rw-r--r-- | DESIGN.md | 831 |
1 files changed, 0 insertions, 831 deletions
diff --git a/DESIGN.md b/DESIGN.md deleted file mode 100644 index f7b55c8..0000000 --- a/DESIGN.md +++ /dev/null @@ -1,831 +0,0 @@ -# Task Dashboard - Design Document - -## Overview - -Task Dashboard is a personal productivity web application that aggregates tasks, meals, calendar events, and shopping lists from multiple external services into a unified, mobile-friendly interface. Built with Go backend, HTMX frontend, Tailwind CSS styling, and SQLite storage. - -**Stack:** Go 1.24 | chi router | HTMX 1.x | Tailwind CSS | SQLite (WAL mode) - ---- - -## Project Intent - -The dashboard serves as a **personal consolidation hub** designed to: - -1. **Aggregate multiple data sources** into one view (Trello, Todoist, PlanToEat, Google Calendar, Google Tasks, user-reported bugs) -2. **Prioritize reliability** over speed - graceful degradation when APIs fail -3. **Support mobile-first usage** - optimized for phone/tablet in-store or on-the-go -4. **Minimize context switching** - complete tasks from any source without opening multiple apps -5. **Provide live monitoring** - volcano webcams and weather for Hawaii location - ---- - -## Architecture - -### Directory Structure - -``` -task-dashboard/ -├── cmd/dashboard/ -│ └── main.go # Entry point, route registration, ldflags -├── internal/ -│ ├── api/ # External API clients -│ │ ├── interfaces.go # API client contracts (interfaces) -│ │ ├── http.go # BaseClient HTTP utilities -│ │ ├── trello.go # Trello API client -│ │ ├── todoist.go # Todoist API (REST v2 + Sync v9) -│ │ ├── plantoeat.go # PlanToEat web scraper -│ │ ├── google_calendar.go # Google Calendar integration -│ │ └── google_tasks.go # Google Tasks integration -│ ├── auth/ -│ │ ├── auth.go # Authentication service (bcrypt + WebAuthn) -│ │ ├── handlers.go # Login/logout/passkey handlers -│ │ └── middleware.go # Session + CSRF protection -│ ├── config/ -│ │ ├── config.go # Environment configuration -│ │ ├── constants.go # App constants (timeouts, limits) -│ │ └── timezone.go # Display timezone helpers -│ ├── handlers/ -│ │ ├── handlers.go # Main HTTP handlers + aggregation -│ │ ├── settings.go # Settings page handlers -│ │ ├── shopping.go # Shopping tab + mode handlers -│ │ ├── agent.go # Agent auth + context API -│ │ ├── websocket.go # WebSocket notification hub -│ │ ├── timeline.go # Timeline view handler -│ │ ├── timeline_logic.go # Timeline data processing -│ │ ├── atoms.go # Unified task aggregation -│ │ ├── cache.go # Generic cache fetcher pattern -│ │ ├── helpers.go # Form parsing helpers -│ │ ├── renderer.go # Template renderer interface -│ │ └── response.go # JSON/HTML response utilities -│ ├── middleware/ -│ │ └── security.go # Security headers + rate limiting -│ ├── models/ -│ │ ├── types.go # Data models (Task, Card, Meal, Agent, etc.) -│ │ ├── atom.go # Unified Atom model + converters -│ │ └── timeline.go # TimelineItem + DaySection models -│ └── store/ -│ └── sqlite.go # SQLite database layer -├── migrations/ # SQL migration files (001-014) -├── scripts/ -│ ├── bugs # List bugs from production DB (local) -│ ├── deploy # Build + local deploy to /site -│ ├── logs # Fetch production journalctl (local) -│ └── resolve-bug # Resolve a production bug (local) -├── web/ -``` - -### Request Flow - -``` -Browser (HTMX request) - ↓ -chi Router (middleware stack) - ├── Logger, Recoverer, Timeout - ├── SecurityHeaders - ├── Session (LoadAndSave) - └── CSRFProtect - ↓ -Handler Function - ├── API Client (fetch from external service) - │ └── BaseClient.Get/Post (HTTP with context) - └── Store (SQLite cache) - └── Get*/Save* methods - ↓ -Template Rendering - ├── Full page: ExecuteTemplate(w, "filename.html", data) - └── Partial: ExecuteTemplate(w, "partial-name", data) - ↓ -HTML Response → HTMX swap -``` - -### Data Flow Patterns - -**Cache-First with Fallback:** -```go -// internal/handlers/cache.go pattern -1. Check IsCacheValid(key) - TTL-based -2. If valid, return cached data from SQLite -3. If expired, fetch from API -4. If API fails, return stale cache (graceful degradation) -5. On success, save to cache + update metadata -``` - -**Unified Atom Model:** -```go -// internal/models/atom.go -// Normalizes Todoist tasks, Trello cards, bugs → single Atom struct -TaskToAtom(Task) → Atom{Source: "todoist", SourceIcon: "🔴", ...} -CardToAtom(Card) → Atom{Source: "trello", SourceIcon: "📋", ...} -BugToAtom(Bug) → Atom{Source: "bug", SourceIcon: "🐛", ...} -``` - -**Timeline Aggregation:** -```go -// internal/handlers/timeline_logic.go -BuildTimeline(ctx, store, calendarClient, tasksClient, start, end) -├── Fetch tasks with due dates (Todoist) -├── Fetch cards with due dates (Trello) -├── Fetch meals (PlanToEat) → apply default times -├── Fetch events (Google Calendar) -├── Fetch tasks (Google Tasks) -├── Convert all to TimelineItem -├── Compute DaySection (today/tomorrow/later) -└── Sort by Time -``` - ---- - -## Visual Design - -### Color Palette - -| Usage | Value | Tailwind | -|-------|-------|----------| -| Panel background | `rgba(0,0,0,0.4)` | `bg-black/40` | -| Card background | `rgba(0,0,0,0.5)` | `bg-black/50` | -| Card hover | `rgba(0,0,0,0.6)` | `bg-black/60` | -| Primary text | `#ffffff` | `text-white` | -| Secondary text | `rgba(255,255,255,0.7)` | `text-white/70` | -| Muted text | `rgba(255,255,255,0.5)` | `text-white/50` | -| Border subtle | `rgba(255,255,255,0.1)` | `border-white/10` | -| Border accent | `rgba(255,255,255,0.2)` | `border-white/20` | -| Todoist accent | `#e44332` | `border-red-500` | -| Trello accent | `#0079bf` | `border-blue-500` | -| PlanToEat accent | `#10b981` | `border-green-500` | - -### Component Patterns - -**Card Container:** -```html -<div class="bg-card bg-card-hover transition-colors rounded-lg border border-white/5"> - <div class="flex items-start gap-3 p-3"> - <!-- checkbox → icon → content → link --> - </div> -</div> -``` - -**Tab Button:** -```html -<button class="tab-button {{if .IsActive}}tab-button-active{{end}}"> - <span>🗓️</span> Timeline -</button> -``` - -**Badge:** -```html -<span class="text-xs px-2 py-0.5 rounded bg-blue-900/50 text-blue-300"> - trello -</span> -``` - -**Glass Morphism:** -- `bg-black/40 backdrop-blur-sm` for panels -- `bg-black/60 backdrop-blur-lg` for modals -- `box-shadow: 0 0 12px black` for depth - -**Z-Index Hierarchy:** -| Layer | z-index | Element | -|-------|---------|---------| -| Content | default | Page content | -| Sticky headers | z-10 | Timeline section headers | -| FAB button | z-50 | Floating action button | -| Modals | z-50 | Action modal, edit modal | -| Dropdowns/Popups | z-100 | Navigation dropdown, context menus | - -### Typography - -- **Font:** Inter (300, 400, 500, 600 weights) -- **Headings:** `font-light tracking-wide text-white` -- **Labels:** `text-xs font-medium tracking-wider uppercase` -- **Body:** `text-sm text-white/70` - -### Quick Add UX Pattern - -All quick-add forms (shopping, tasks, etc.) must follow these behaviors for rapid data entry: - -**On Successful Submission:** -1. **Clear input** - Reset the form/input field immediately -2. **Refocus input** - Return focus to the text input for the next entry -3. **Visual feedback** - Brief flash (green background, 300ms) to confirm success -4. **Update list** - New item appears in the list immediately - -**On Error:** -1. **Preserve input** - Do not clear the input so user can retry -2. **Show error** - Display error message near the input -3. **Maintain focus** - Keep focus on the input - -**Checked/Completed Items:** -- Completed items are immediately removed from view (not just crossed out) -- User items: Deleted from database -- External items (Trello, PlanToEat): Marked as checked, filtered from display - ---- - -## API Integrations - -### Summary Table - -| Service | Auth Method | Config Var | Read | Write | Notes | -|---------|-------------|------------|------|-------|-------| -| Todoist | Bearer token | `TODOIST_API_KEY` | ✓ Tasks, Projects | ✓ Create, Complete | Incremental sync available | -| Trello | Key + Token | `TRELLO_API_KEY`, `TRELLO_TOKEN` | ✓ Boards, Cards, Lists | ✓ Create, Archive | Concurrent board fetching | -| PlanToEat | Session cookie | `PLANTOEAT_SESSION` | ✓ Meals, Shopping | ✗ | Web scraping (brittle) | -| Google Calendar | OAuth file | `GOOGLE_CREDENTIALS_FILE` | ✓ Events | ✗ | Multi-calendar support | -| Google Tasks | OAuth file | `GOOGLE_CREDENTIALS_FILE` | ✓ Tasks | ✓ Complete | Shared credentials with Calendar | - -### Interface Pattern - -All API clients implement interfaces (in `internal/api/interfaces.go`): - -```go -type TodoistAPI interface { - GetTasks(ctx context.Context) ([]models.Task, error) - GetProjects(ctx context.Context) ([]models.Project, error) - CreateTask(ctx context.Context, content, projectID string, dueDate *time.Time, priority int) (*models.Task, error) - CompleteTask(ctx context.Context, taskID string) error - ReopenTask(ctx context.Context, taskID string) error - // ... -} -``` - -Compile-time enforcement: -```go -var _ TodoistAPI = (*TodoistClient)(nil) -``` - -### Timezone Handling - -All times normalized to display timezone: -```go -config.SetDisplayTimezone("Pacific/Honolulu") // Set at startup -config.Now() // Current time in display TZ -config.Today() // Current date in display TZ -config.ToDisplayTZ(t) // Convert any time -config.ParseDateInDisplayTZ("2026-01-27") // Parse in display TZ -``` - ---- - -## Features - -### Timeline View (`/tabs/timeline`) - -Chronological view of all upcoming items grouped by day section. - -**Data Sources:** Todoist, Trello, PlanToEat, Google Calendar, Google Tasks - -**Organization:** -- **Today** (expanded, collapsible) - Items due today -- **Tomorrow** (expanded, collapsible) - Items due tomorrow -- **Later** (collapsed) - Items 2+ days out - -**Item Display:** -- Color-coded dot (blue=event, orange=meal, green=task, yellow=gtask) -- Time display (hidden for all-day items at midnight) -- Checkbox for completable items -- Title + description (2-line clamp) -- External link icon - -### Tasks View (`/tabs/tasks`) - -Unified task grid showing Atoms (normalized tasks from all sources). - -**Layout:** 3-column responsive grid - -**Features:** -- Priority sorting (overdue → today → future → no date) -- Source icons (🔴 Todoist, 📋 Trello, 🐛 Bug) -- Expandable descriptions -- Collapsible "Future" section - -### Shopping View (`/tabs/shopping`) - -Aggregated shopping lists from Trello + PlanToEat + user items. - -**Tab Mode:** Store sections with items, quick-add form - -**Shopping Mode (`/shopping/mode/{store}`):** Full-screen mobile-optimized view -- Store switcher (horizontal tabs) -- Large touch targets -- Fixed bottom quick-add - -### Meals View (`/tabs/meals`) - -PlanToEat meal schedule for the next 7 days. - -**Features:** -- Meals grouped by date + meal type (breakfast/lunch/dinner) -- Multiple recipes for same slot combined with " + " separator -- Sorted by date, then meal type order - -### Conditions Page (`/conditions`) - -**Public route** - no authentication required. - -Standalone live feeds page with: -- 3 Kilauea volcano webcams (USGS) -- 2 Hawaii weather maps (Windy.com) -- 1 National weather map - -Auto-refresh webcam images every 60 seconds. - -### Agent Context API (`/agent/*`) - -External agent authentication and data access system. Allows CLI tools, automation scripts, and AI assistants to access dashboard context securely. - -**Authentication Flow:** -1. Agent sends `POST /agent/auth/request` with name + agent_id (UUID) -2. Dashboard checks trust level: - - **TRUSTED**: Auto-approve if agent_id was previously approved - - **UNKNOWN**: Send WebSocket notification to connected browsers - - **BLOCKED**: Reject immediately -3. User approves/denies via modal in browser (pushed via WebSocket) -4. Agent polls `GET /agent/auth/poll?token=REQUEST_TOKEN` for result -5. On approval, agent receives session_token (1-hour TTL) -6. Agent uses session_token in Authorization header for context access - -**Endpoints:** - -| Method | Path | Auth | Purpose | -|--------|------|------|---------| -| POST | `/agent/auth/request` | None | Request access (returns request_token) | -| GET | `/agent/auth/poll` | None | Poll for approval status | -| POST | `/agent/auth/approve` | Browser session | User approves agent request | -| POST | `/agent/auth/deny` | Browser session | User denies agent request | -| GET | `/agent/context` | Agent session | Full timeline context (7 days back, 14 forward) | -| GET | `/agent/web/request` | None | HTML auth flow for browser-only agents | -| GET | `/agent/web/status` | None | HTML status page | -| GET | `/agent/web/context` | None | HTML context view | - -**WebSocket Notifications (`/ws/notifications`):** -- Pushes agent auth requests to connected browsers in real-time -- JSON protocol with auto-reconnect on disconnect -- Frontend handler in `web/static/js/app.js` - -**Security:** -- Rate limit: 10 requests/minute per IP on auth endpoints -- Session TTL: 1 hour, one active session per agent_id -- Impersonation detection: Rejects if name/agent_id mismatch previous binding - -**Implementation:** `internal/handlers/agent.go`, `internal/handlers/websocket.go` - -### Completed Tasks Log - -Tracks task completion history for audit trail and agent context. - -**Purpose:** -- Provides completion history in agent context API -- Enables future analytics/reporting -- Audit trail of completed work - -**Storage:** `completed_tasks` table (source, source_id, title, due_date, completed_at) - -**Feature Toggle:** `completed_log` (enabled by default) - -**Populated by:** `HandleCompleteAtom()` on task/card completion - ---- - -## Database Schema - -**Engine:** SQLite with WAL mode (concurrent reads) - -**Key Tables:** - -| Table | Purpose | Key Columns | -|-------|---------|-------------| -| `tasks` | Todoist cache | id, content, due_date, priority, completed | -| `boards` | Trello boards | id, name | -| `cards` | Trello cards | id, name, board_id, list_id, due_date | -| `meals` | PlanToEat meals | id, recipe_name, date, meal_type | -| `users` | Authentication | id, username, password_hash | -| `sessions` | SCS sessions | token, data, expiry | -| `bugs` | Bug reports | id, description, resolved_at | -| `user_shopping_items` | User-added items | id, name, store, checked | -| `shopping_item_checks` | External item state | source, item_id, checked | -| `cache_metadata` | Cache timestamps | key, last_fetch, ttl_minutes | -| `sync_tokens` | Incremental sync | service, token | -| `agents` | Registered agents | name, agent_id, trust_level, last_seen | -| `agent_sessions` | Agent auth sessions | agent_id, request_token, session_token, status, expires_at | -| `completed_tasks` | Completion log | source, source_id, title, due_date, completed_at | -| `feature_toggles` | Feature flags | name, description, enabled | -| `source_config` | Source filtering | source, item_type, item_id, item_name, enabled | - ---- - -## Development Guide for Claude Code - -### Getting Started - -1. **Read this file first** - Understand architecture and patterns -2. **Check `CLAUDE.md`** - Project-specific instructions -3. **Run tests before changes:** `go test ./...` -4. **Run after changes:** `go test ./...` then `go build` - -### Key Principles - -1. **Cache-First, Fail-Soft:** Never hard-fail if an API is down. Return cached data. -2. **Minimal Changes:** Surgical edits only. Don't refactor unrelated code. -3. **Interface-Driven:** API clients implement interfaces for testability. -4. **HTMX for Updates:** Use partials and `hx-swap` for reactive UI. - -### Common Patterns - -**Adding a new handler:** -```go -// internal/handlers/handlers.go -func (h *Handler) HandleNewFeature(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - - // 1. Parse input - if err := r.ParseForm(); err != nil { - JSONError(w, http.StatusBadRequest, "Parse failed", err) - return - } - - // 2. Validate - value := r.FormValue("key") - if value == "" { - JSONError(w, http.StatusBadRequest, "Missing key", nil) - return - } - - // 3. Business logic - result, err := h.apiClient.DoSomething(ctx, value) - if err != nil { - JSONError(w, http.StatusInternalServerError, "Failed", err) - return - } - - // 4. Save to cache (best-effort) - _ = h.store.SaveResult(result) - - // 5. Respond - HTMLResponse(w, h.renderer, "partial-name", result) -} -``` - -**Adding a new API method:** -```go -// 1. Add to interface (internal/api/interfaces.go) -type MyAPI interface { - NewMethod(ctx context.Context, param string) (Result, error) -} - -// 2. Implement in client (internal/api/myclient.go) -func (c *MyClient) NewMethod(ctx context.Context, param string) (Result, error) { - var result Result - if err := c.Get(ctx, "/endpoint/"+param, c.authHeaders(), &result); err != nil { - return Result{}, fmt.Errorf("failed to fetch: %w", err) - } - return result, nil -} -``` - -**Adding a new template:** -```html -<!-- web/templates/partials/new-feature.html --> -{{define "new-feature"}} -<div class="space-y-4"> - {{range .Items}} - <div class="bg-card rounded-lg p-4"> - <h3 class="text-white font-medium">{{.Title}}</h3> - </div> - {{end}} -</div> -{{end}} -``` - -**Adding a new route:** -```go -// cmd/dashboard/main.go inside protected routes group -r.Get("/tabs/newfeature", h.HandleNewFeature) -r.Post("/newfeature/action", h.HandleNewAction) -``` - -### Template Naming Convention - -| Type | Location | Reference | -|------|----------|-----------| -| Standalone page | `web/templates/name.html` | `"name.html"` | -| Partial | `web/templates/partials/name.html` | `"name"` (define name) | - -**Example:** -```go -// Standalone page - use filename -h.renderer.Render(w, "shopping-mode.html", data) - -// Partial - use define name -HTMLResponse(w, h.renderer, "shopping-tab", data) -``` - -### HTMX Patterns - -**Tab content loading:** -```html -<div id="tab-content" - hx-get="/tabs/{{.ActiveTab}}" - hx-trigger="load" - hx-swap="innerHTML"> -</div> -``` - -**Form submission with swap:** -```html -<form hx-post="/complete-atom" - hx-vals='{"id": "{{.ID}}", "source": "{{.Source}}"}' - hx-target="closest div.rounded-lg" - hx-swap="outerHTML"> -``` - -**Refresh trigger:** -```javascript -// After action completes -htmx.trigger(document.body, 'refresh-tasks') -``` - -### Test Patterns - -**Unit test for store:** -```go -func TestStore_SaveTasks(t *testing.T) { - db := setupTestStore(t) - defer db.Close() - - tasks := []models.Task{{ID: "1", Content: "Test"}} - err := db.SaveTasks(tasks) - require.NoError(t, err) - - result, err := db.GetTasks() - require.NoError(t, err) - assert.Len(t, result, 1) -} -``` - -**Handler test:** -```go -func TestHandler_HandleDashboard(t *testing.T) { - h := setupTestHandler(t) - - req := httptest.NewRequest(http.MethodGet, "/", nil) - w := httptest.NewRecorder() - - h.HandleDashboard(w, req) - - assert.Equal(t, http.StatusOK, w.Code) -} -``` - -### Debugging Tips - -1. **Production logs:** `bash scripts/logs -n 200 2>&1 | grep -i error` -2. **Production bugs:** `bash scripts/bugs` to list, `bash scripts/resolve-bug <id>` to close -3. **Cache issues:** Check `cache_metadata` table for stale timestamps -4. **API failures:** Look for `"Warning:"` or `"ERROR:"` prefixes in logs -5. **Template errors:** Check for `html/template: "name" is undefined` -6. **HTMX issues:** Browser DevTools Network tab shows all requests -7. **WebAuthn issues:** Check for "WebAuthn initialized" in startup logs; requires `WEBAUTHN_RP_ID` + `WEBAUTHN_ORIGIN` env vars - -### Files You'll Modify Most - -| Task | Primary Files | -|------|--------------| -| New feature | `handlers.go` or new handler file, `main.go`, new template | -| Bug fix | Relevant handler file or API client | -| UI change | Template in `partials/`, maybe `input.css` | -| API integration | New file in `internal/api/`, update `interfaces.go` | -| Database change | New migration in `migrations/`, update `sqlite.go` | -| Settings | `settings.go`, `settings.html` | -| Shopping | `shopping.go`, `shopping-tab.html` or `shopping-mode.html` | - -### Common Mistakes to Avoid - -1. **Don't use `"name"` for standalone pages** - Use `"name.html"` -2. **Don't forget context propagation** - All handlers should use `r.Context()` -3. **Don't panic on API errors** - Return cached data or empty slice -4. **Don't modify tests without running them** - `go test ./...` -5. **Don't add unnecessary comments** - Code should be self-explanatory -6. **Don't create new files unless necessary** - Prefer editing existing -7. **Don't refactor working code** - Only touch what's needed - -### Feature Toggles - -Feature toggles allow gradual rollout of new features and easy rollback if issues arise. - -**When to use feature toggles:** -- New features that may need adjustment after deployment -- Experimental features being tested -- Features that depend on external services that may be unreliable -- Any change that could break existing functionality - -**Creating a feature toggle:** - -1. Add a migration or use the Settings UI at `/settings` -2. Check the toggle in your handler: -```go -if h.store.IsFeatureEnabled("my_feature") { - // New behavior -} else { - // Old behavior or disabled state -} -``` - -3. Document the toggle in this file under "Active Feature Toggles" - -**Managing feature toggles:** -- Access the Settings page at `/settings` to view/toggle/create features -- Store methods: `IsFeatureEnabled()`, `SetFeatureEnabled()`, `CreateFeatureToggle()` -- Toggles are stored in the `feature_toggles` table - -**Lifecycle:** -1. **Create** toggle (disabled by default) before deploying feature -2. **Enable** after deployment if tests pass -3. **Monitor** for issues -4. **Remove** toggle and conditional code once feature is stable (usually after 1-2 weeks) - -**Active Feature Toggles:** -| Name | Description | Status | -|------|-------------|--------| -| `source_config` | Configure which boards/lists/calendars to fetch | Disabled | -| `calendar_timeline` | Show timeline as a calendar view with time slots | Disabled | -| `completed_log` | Track and display completed tasks log | Enabled | - -### Bug Management Workflow - -Use the bug tracking system to report and resolve issues: - -**Viewing bugs (production):** -```bash -./scripts/bugs -# Lists all bugs from production database with ID, description, and created_at -``` - -**Resolving a bug:** -```bash -./scripts/resolve-bug <bug_id> -# Shows bug description, then deletes it from the database -``` - -**Workflow:** -1. Bugs are reported via the dashboard UI (🐛 tab in quick-add modal) -2. Run `./scripts/bugs` to see current issues -3. Fix the bug with tests -4. Deploy the fix -5. Run `./scripts/resolve-bug <id>` to close it - -Bugs appear as atoms in the Tasks view (🐛 icon) until resolved. - -### Test Locations & Commands - -**Test locations:** -| Component | Test File | -|-----------|-----------| -| Store methods | `internal/store/sqlite_test.go` | -| Handlers | `internal/handlers/handlers_test.go` | -| Timeline logic | `internal/handlers/timeline_logic_test.go` | -| Agent handlers | `internal/handlers/agent_test.go` | -| API clients | `internal/api/*_test.go` | -| Auth | `internal/auth/*_test.go` | -| Integration | `test/acceptance_test.go` | - -**Running tests:** -```bash -# All tests -go test ./... - -# Specific package -go test ./internal/handlers/... - -# Verbose with coverage -go test -v -cover ./... - -# Single test -go test -run TestBuildTimeline ./internal/handlers/... -``` - -**Test patterns:** -```go -func TestFeature_Behavior(t *testing.T) { - // Arrange - db := setupTestStore(t) - defer db.Close() - - // Act - result, err := db.SomeMethod(input) - - // Assert - require.NoError(t, err) - assert.Equal(t, expected, result) -} -``` - -### Architecture Decision Records - -**Current ADRs:** -| ADR | Decision | -|-----|----------| -| 001 | Session-based authentication with SQLite storage | -| 002 | Polymorphic TimelineItem model for unified aggregation | -| 003 | HTMX partials for all write operations | -| 004 | Concurrent fetching with semaphore + cache fallback | -| 005 | Agent API notification-based authentication | - -**ADR template:** -```markdown -# ADR NNN: Title - -## Status -Proposed | Accepted | Deprecated | Superseded - -## Context -What is the issue or decision we need to make? - -## Decision -What did we decide and why? - -### Technical Details: -- Implementation specifics -- Key code locations - -## Consequences -- **Pros:** Benefits of this approach -- **Cons:** Tradeoffs and limitations - -## Alternatives Considered -- Option A: Why rejected -- Option B: Why rejected -``` - -**Numbering:** Use sequential 3-digit numbers (001, 002, etc.) - ---- - -## Quick Reference - -### Commands - -```bash -# Run application -go run cmd/dashboard/main.go - -# Run tests -go test ./... - -# Build binary -go build -o dashboard cmd/dashboard/main.go - -# Rebuild Tailwind CSS -npx tailwindcss -i web/static/css/input.css -o web/static/css/output.css --watch -``` - -### Key Endpoints - -| Method | Path | Purpose | -|--------|------|---------| -| GET | `/` | Main dashboard | -| GET | `/tabs/timeline` | Timeline partial | -| GET | `/tabs/tasks` | Tasks partial | -| GET | `/tabs/planning` | Planning partial | -| GET | `/tabs/meals` | Meals partial | -| GET | `/tabs/shopping` | Shopping partial | -| GET | `/tabs/conditions` | Conditions partial | -| GET | `/conditions` | Live feeds page (public) | -| POST | `/complete-atom` | Complete task/card/bug | -| POST | `/uncomplete-atom` | Reopen task/card | -| POST | `/unified-add` | Quick add task | -| GET | `/bugs` | List bug reports | -| POST | `/bugs` | Submit bug report | -| POST | `/shopping/add` | Add shopping item | -| GET | `/shopping/mode/{store}` | Shopping mode view | -| POST | `/shopping/mode/{store}/toggle` | Toggle shopping item | -| POST | `/shopping/mode/{store}/complete` | Complete shopping item | -| GET | `/settings` | Settings page | -| POST | `/settings/features` | Create feature toggle | -| POST | `/settings/features/toggle` | Toggle feature on/off | -| GET | `/settings/passkeys` | List passkeys (requires WebAuthn) | -| POST | `/passkeys/register/begin` | Start passkey registration | -| POST | `/passkeys/register/finish` | Complete passkey registration | -| GET | `/ws/notifications` | WebSocket for agent notifications | -| POST | `/agent/auth/request` | Agent requests access | -| GET | `/agent/auth/poll` | Agent polls for approval | -| POST | `/agent/auth/approve` | User approves agent | -| GET | `/agent/context` | Agent gets timeline context | - -### Handler Signature - -```go -func (h *Handler) HandleName(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - // ... implementation -} -``` - -### Response Helpers - -```go -JSONResponse(w, data) // 200 + JSON + no-cache -JSONError(w, status, "message", err) // Error + log -HTMLResponse(w, h.renderer, "name", data) // Partial render + no-cache -HTMLString(w, "<html>...</html>") // Raw HTML -``` |
