# 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 --- ## Development Workflow This project supports a **multi-agent development workflow** with three specialized roles. The primary workflow is single-agent (Claude Code reading CLAUDE.md + DESIGN.md directly). The role files exist for optional multi-agent use. ### Roles | Role | Responsibilities | |------|------------------| | **Architect** | Plans features, creates issues, writes surgical instructions. Does NOT edit source code. | | **Implementor** | Executes plans, writes code, runs tests. Updates state after completing work. | | **Reviewer** | Reviews code quality, runs tests, provides feedback. Does NOT edit source code. | ### Role Definitions Each role has a detailed persona document: - `ARCHITECT_ROLE.md` - Planning, documentation, issue tracking - `IMPLEMENTOR_ROLE.md` - Code execution, TDD, verification - `REVIEWER_ROLE.md` - Quality gates, clean code review ### Handoff Documents | Document | Purpose | Writer | Reader | |----------|---------|--------|--------| | `instructions.md` | Surgical implementation plan (ephemeral) | Architect | Implementor | | `review_feedback.md` | Code review results (ephemeral) | Reviewer | Implementor | | `SESSION_STATE.md` | Current state + next steps | All | All | | `issues/*.md` | Bug reports + feature specs | Architect | All | | `docs/adr/*.md` | Architectural decisions (permanent) | Architect | All | **Note:** `instructions.md` and `review_feedback.md` are ephemeral - they contain current task details. Architectural decisions belong in ADRs, not in ephemeral docs. ### Status Tags Tasks flow through these states: ``` [TODO] → [IN_PROGRESS] → [REVIEW_READY] → [APPROVED] ↑ ↓ └── [NEEDS_FIX] ←┘ ``` ### Workflow Example 1. **Architect** identifies bug, creates `issues/bug_042_description.md` 2. **Architect** writes fix plan to `instructions.md`, updates `SESSION_STATE.md` to `[TODO]` 3. **Implementor** reads instructions, writes failing test (TDD), implements fix 4. **Implementor** runs `go test ./...`, updates state to `[REVIEW_READY]` 5. **Reviewer** reads code, runs tests, writes `review_feedback.md` 6. **Reviewer** marks `[APPROVED]` or `[NEEDS_FIX]` with specific feedback 7. If `[NEEDS_FIX]`, **Implementor** addresses feedback, returns to step 4 8. **All roles** run self-improvement cycle: reflect, perform 1-3 improvements, record in `SESSION_STATE.md` ### Self-Improvement Cycle After completing each task, every role performs a structured improvement cycle: 1. **Reflect** — Answer role-specific questions about what went well and what caused friction 2. **Improve (1-3 actions)** — Make concrete changes: extract test helpers, add scripts, update checklists, fix role definitions, document bug patterns, create ADRs for implicit decisions 3. **Record** — Log improvements in `SESSION_STATE.md` → "Process Improvements" for cross-session visibility See each role file (`*_ROLE.md`) for the full role-specific checklist of reflection questions and improvement actions. ### Principles - **Surgical edits** - Prefer targeted changes over full-file rewrites - **TDD** - Write failing test before implementation - **State tracking** - Always update `SESSION_STATE.md` after completing work - **Tool verification** - Use `go test`, `go build`, `grep` to verify state before planning - **ADR-first** - Document architectural decisions in ADRs, not one-off design docs --- ## 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 │ ├── logs # Fetch production journalctl │ └── resolve-bug # Resolve a production bug ├── web/ │ ├── templates/ │ │ ├── index.html # Main dashboard shell │ │ ├── login.html # Authentication page │ │ ├── settings.html # Settings + passkeys page │ │ ├── conditions.html # Standalone live feeds page │ │ ├── shopping-mode.html # Full-screen shopping mode │ │ ├── passkeys_list.html # Passkeys list partial │ │ ├── agent-*.html # Agent auth flow pages (4 files) │ │ └── partials/ # HTMX partial templates (12 files) │ └── static/ │ ├── css/input.css # Tailwind source │ ├── css/output.css # Compiled CSS │ └── js/app.js # Client-side logic + WebSocket ├── deploy.sh # Build + deploy script (ldflags injection) └── tailwind.config.js ``` ### 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
``` **Tab Button:** ```html ``` **Badge:** ```html trello ``` **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 {{define "new-feature"}}
{{range .Items}}

{{.Title}}

{{end}}
{{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
``` **Form submission with swap:** ```html
``` **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 ` 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` | ### Configuration Reference **Required:** - `TODOIST_API_KEY` - Todoist API key - `TRELLO_API_KEY` - Trello API key - `TRELLO_TOKEN` - Trello token - `DEFAULT_PASS` - Admin password (must be set) **Optional:** - `DEFAULT_USER` (default: "admin") - `PLANTOEAT_SESSION` - PlanToEat session cookie - `PLANTOEAT_API_KEY` - PlanToEat API key - `GOOGLE_CREDENTIALS_FILE` - OAuth credentials JSON path - `GOOGLE_CALENDAR_ID` (default: "primary") — comma-separated for multiple - `GOOGLE_TASKS_LIST_ID` (default: "@default") - `WEBAUTHN_RP_ID` - Passkey Relying Party ID (e.g., "doot.terst.org") - `WEBAUTHN_ORIGIN` - Passkey expected origin (e.g., "https://doot.terst.org") - `DATABASE_PATH` (default: "./dashboard.db") - `PORT` (default: "8080") - `CACHE_TTL_MINUTES` (default: 5) - `TIMEZONE` (default: "Pacific/Honolulu") - `TEMPLATE_DIR` (default: "web/templates") - `STATIC_DIR` (default: "web/static") - `MIGRATION_DIR` (default: "migrations") - `DEBUG` (default: false) ### 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 8. **Don't alter git history** - Never amend, rebase, or force push. Keep a clean, linear history with new commits only. ### 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 | ### Git Practices **Clean history principles:** - **Never amend commits** - Create new commits for fixes instead - **Never rebase** - Use merge to integrate changes - **Never force push** - All pushes should be fast-forward only - **One logical change per commit** - Keep commits focused and atomic **Commit message format:** ``` Short summary (50 chars or less) Optional longer description explaining the why, not the what. Reference bug numbers with #N format. Co-Authored-By: Claude Opus 4.6 ``` **If history diverges:** Use `git pull --rebase=false` to merge, never force push. ### 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 # 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 ` to close it Bugs appear as atoms in the Tasks view (🐛 icon) until resolved. ### Test-Driven Development (TDD) **Required approach for all changes:** 1. **Write failing test first** - Define expected behavior before implementation 2. **Make it pass** - Write minimal code to pass the test 3. **Refactor** - Clean up while tests stay green **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) } ``` **Before submitting any change:** - `go test ./...` must pass - Add tests for new functionality - Update tests for changed behavior ### Architecture Decision Records (ADRs) **ADRs are the primary way to document architectural decisions.** They capture the context, decision, and tradeoffs for significant choices. Unlike one-off design documents that become stale, ADRs remain as a permanent record of why things are the way they are. **Always create an ADR when:** - Choosing between multiple implementation approaches - Adding new external dependencies or integrations - Changing authentication, security, or data flow patterns - Making significant refactoring decisions - Introducing new patterns or abstractions **Do NOT create one-off design docs.** Instead: - For feature planning → create an ADR with the architectural approach - For implementation details → put them in `instructions.md` (ephemeral) - For bug analysis → create an issue in `issues/` (ephemeral) **ADR location:** `docs/adr/NNN-title.md` **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.) **When to update DESIGN.md:** - Adding new features or views (update Features section) - New database tables (update Schema section) - New API endpoints (update Quick Reference) - Changes to the development workflow --- ## 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, "...") // Raw HTML ```