From 8abc63efdbc0bb96cd6c9aa99d6e9166e0bcabae Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Sun, 22 Mar 2026 23:45:19 +0000 Subject: chore: unify and centralize agent configuration in .agent/ --- .agent/coding_standards.md | 70 ++++ .agent/config.md | 62 ++++ .agent/design.md | 831 +++++++++++++++++++++++++++++++++++++++++++++ .agent/mission.md | 16 + .agent/narrative.md | 32 ++ .agent/preferences.md | 15 + .agent/timeline_design.md | 82 +++++ .agent/ux_philosophy.md | 82 +++++ .agent/worklog.md | 81 +++++ 9 files changed, 1271 insertions(+) create mode 100644 .agent/coding_standards.md create mode 100644 .agent/config.md create mode 100644 .agent/design.md create mode 100644 .agent/mission.md create mode 100644 .agent/narrative.md create mode 100644 .agent/preferences.md create mode 100644 .agent/timeline_design.md create mode 100644 .agent/ux_philosophy.md create mode 100644 .agent/worklog.md (limited to '.agent') diff --git a/.agent/coding_standards.md b/.agent/coding_standards.md new file mode 100644 index 0000000..50281d0 --- /dev/null +++ b/.agent/coding_standards.md @@ -0,0 +1,70 @@ +# Coding Standards + +This document defines the technical standards for the **Doot** project. Adherence to these standards ensures consistency, security, and maintainability. + +## 1. Go (Backend) + +### Idiomatic Go +- Follow standard Go idioms (Effective Go). +- Keep functions small and focused on a single responsibility. +- Use meaningful, descriptive names for variables, functions, and types. + +### Concurrency +- Use `sync.WaitGroup` and `sync.Mutex` for managing concurrent operations. +- For parallel API fetching, use a semaphore pattern (e.g., buffered channel) to limit concurrency and avoid rate limits. +- **Example:** See `internal/api/trello.go` for the 5-request concurrency limit. + +### Error Handling +- Use the "Partial Data / Cache Fallback" pattern: If an API call fails, return cached data (if available) or an empty set with an error logged, rather than failing the entire request. +- Use structured logging or the `data.Errors` slice in handlers to report non-fatal errors to the UI. + +### Database (SQLite) +- **SQL Injection Prevention:** ALWAYS use parameterized queries (`?` placeholders). +- **Concurrency:** Enable WAL mode and set `MaxOpenConns(1)` to avoid "database is locked" errors. +- **Migrations:** All schema changes must be added as a new file in the `migrations/` directory. + +### Project Structure +- `cmd/dashboard/`: Application entry point. +- `internal/api/`: External service clients (Todoist, Trello, etc.). +- `internal/auth/`: Authentication logic and middleware. +- `internal/config/`: Configuration loading and validation. +- `internal/handlers/`: HTTP request handlers and UI logic. +- `internal/models/`: Shared data structures. +- `internal/store/`: Database operations. + +## 2. Frontend (HTMX + Tailwind CSS) + +### HTMX +- Use HTMX for all state-changing operations (Create, Update, Delete) and tab switching. +- **Response Headers:** Use `HX-Trigger` and `HX-Reswap` to coordinate UI updates. +- **Partial Rendering:** Handlers should return HTML partials (from `web/templates/partials/`) for HTMX requests. + +### CSS & Styling +- Use **Tailwind CSS** for all styling. +- Maintain a consistent mobile-first responsive design. +- Follow the Z-index hierarchy defined in `design.md`. + +### User Feedback +- Provide immediate visual feedback for user actions (loading indicators, success/error banners). +- Ensure all forms clear their inputs upon successful submission. + +## 3. Testing Philosophy + +### Reproduction First +- Before fixing a bug, implement a failing test case that reproduces the issue. +- Verify the fix by ensuring the new test (and all existing tests) passes. + +### Coverage Areas +- **Store:** Unit tests for all SQL operations (`sqlite_test.go`). +- **Handlers:** Integration tests for critical paths (`handlers_test.go`, `agent_test.go`). +- **API Clients:** Use mocks or test servers for external API testing (`todoist_test.go`, `trello_test.go`). + +## 4. Documentation & Git + +### Commit Messages +- Propose clear, concise messages that explain the "why" behind the change. +- Match existing style (usually imperative mood). + +### File Header +- Maintain the standardized `SESSION_STATE.md` (now `worklog.md`) format. +- Document significant architectural changes in `docs/adr/`. diff --git a/.agent/config.md b/.agent/config.md new file mode 100644 index 0000000..45ead3b --- /dev/null +++ b/.agent/config.md @@ -0,0 +1,62 @@ +# Agent Configuration & Master Rulebook (.agent/config.md) + +This is the primary source of truth for all AI agents working on **Doot**. These instructions take absolute precedence over general defaults. + +## 1. Project Directory Structure (.agent/) + +| File | Purpose | +|------|---------| +| `config.md` | **Main Entry Point** — Rules, workflows, and core mandates. | +| `worklog.md` | **Session State** — Current focus, recently completed, and next steps. | +| `design.md` | **Architecture** — High-level design, database schema, and components. | +| `coding_standards.md` | **Technical Standards** — Go/HTMX idioms, testing, and security. | +| `ux_philosophy.md` | **UX Principles** — Interaction models, palette, and mobile-first design. | +| `narrative.md` | **Background** — Historical context and evolution of the project. | + +## 2. Core Mandates + +### ULTRA-STRICT ROOT SAFETY PROTOCOL +1. **Inquiry-Only Default:** Treat every message as research/analysis unless it is an explicit, imperative command (Directive). +2. **Zero Unsolicited Implementation:** Never modify files, directories, or processes based on assumptions. +3. **Interactive Strategy Checkpoint:** Research first, present a strategy, and **WAIT** for an explicit "GO" before any system-changing tool call. +4. **No Destructive Assumptions:** Always verify state (`ps`, `ls`, `git status`) before proposing actions. +5. **Root-Awareness:** Prioritize system integrity and user confirmation over proactiveness. + +### Task Management +- **Worklog:** Update `.agent/worklog.md` at the start and end of every session. +- **Claudomator:** Use `claudomator create "Name" --instructions "Goals"` for new tasks. Use `claudomator finish ` when done. + +### Living Documentation Mandate +1. **Continuous Capture:** Agents MUST proactively update the files in `.agent/` (Design, UX, Standards, Mission, Preferences) as new decisions, patterns, or user preferences are revealed. +2. **No Stale Instructions:** If a workflow or technical standard evolves, the agent is responsible for reflecting that change in the Master Rulebook immediately. +3. **Worklog Integrity:** The `.agent/worklog.md` must be updated at the start and end of EVERY session to ensure continuity. + +## 3. Workflows + +### Research -> Strategy -> Execution +1. **Research:** Map codebase, validate assumptions, reproduce bugs. +2. **Strategy:** Share a summary. Wait for approval if significant. +3. **Execution (Plan-Act-Validate):** + - **Plan:** Define implementation and testing. + - **Act:** Apply surgical, idiomatic changes. + - **Validate:** Run `go test ./...` and `npm run build`. + +## 4. Agent Context API (Snapshots) + +The project includes an internal API for agents to understand the current user's task and meal context. + +- **Request Access:** `POST /agent/auth/request` (returns `request_token`). +- **Poll Status:** `GET /agent/auth/poll?token=` (awaits browser approval). +- **Get Context:** `GET /agent/context` (requires Bearer token). +- **Snapshot Endpoint:** `GET /api/claude/snapshot` (legacy, see `internal/handlers/agent.go` for new unified flow). + +## 5. Essential Commands + +| Command | Action | +|---------|--------| +| `go run cmd/dashboard/main.go` | Start application | +| `go test ./...` | Run all tests | +| `./scripts/deploy` | Deploy to production | +| `./scripts/logs` | View production logs | +| `./scripts/bugs` | List open production bugs | +| `./scripts/resolve-bug ` | Resolve a production bug | diff --git a/.agent/design.md b/.agent/design.md new file mode 100644 index 0000000..f7b55c8 --- /dev/null +++ b/.agent/design.md @@ -0,0 +1,831 @@ +# 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 +
+
+ +
+
+``` + +**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` | + +### 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 +# 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 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, "...") // Raw HTML +``` diff --git a/.agent/mission.md b/.agent/mission.md new file mode 100644 index 0000000..64575e5 --- /dev/null +++ b/.agent/mission.md @@ -0,0 +1,16 @@ +# Project Mission & Strategic Values + +## 1. Core Mission +**Unified Situational Awareness.** Doot is the single "lens" through which the user views their entire digital life. It is not a replacement for the source tools (Trello, Todoist), but a high-speed aggregation layer for clarity. + +## 2. Strategic Values +- **Simplicity over Complexity:** If a feature makes the dashboard harder to read at a glance, it doesn't belong. +- **Glanceable Information:** The UI is designed for "5-second updates," not "5-minute deep dives." +- **Resilience is Mandatory:** Never show an error spinner where stale/cached data could be shown instead. The dashboard must be "always on." +- **Mobile-First Utility:** The primary use case is "checking the plan" while on the move or in the kitchen (meals/shopping). + +## 3. Agent Personality & Role +- **The Proactive Chief of Staff:** Agents should anticipate gaps and propose solutions, but always remain within safety guardrails. +- **Continuous Clarification:** A "GO" (Directive) is a permission to implement, but it is NOT a mandate to stop asking questions. If a sub-task is ambiguous or an unexpected edge case is found during execution, the agent MUST stop and clarify with the user before proceeding. +- **High-Signal, Low-Noise:** Communication should be professional, technical, and concise. +- **Surgical Execution:** Changes should be minimal and idiomatic. diff --git a/.agent/narrative.md b/.agent/narrative.md new file mode 100644 index 0000000..778df85 --- /dev/null +++ b/.agent/narrative.md @@ -0,0 +1,32 @@ +# Engineering Narrative & Project Evolution + +This document tracks the chronological history and major milestones of the **Doot** ecosystem (including its relationship with Claudomator). + +## 1. Ecosystem Origins (Claudomator) +**Claudomator** was developed as the core "task-execution engine." It introduced: +- **Human-in-the-Loop Workflow:** A `READY` state that requires operator sign-off (`Accept/Reject`) before a task is considered `COMPLETED`. +- **Question/Answer Flow:** Agents can transition a task to `BLOCKED` by writing a `question.json` file, allowing users to provide context asynchronously. +- **Subtask Graphing:** The `claudomator` CLI allows agents to break complex goals into manageable subtasks. + +## 2. The Birth of Doot (Consolidation) +**Doot** (this project) was created to solve "Context Blindness." Instead of managing tasks across Todoist, Trello, and Google Calendar separately, Doot aggregates them into a single, high-speed interface. + +### Key Milestones: +- **Phase 1: Read-Only Aggregation:** Initial dashboard with Trello, Todoist, and PlanToEat. +- **Phase 2: Authentication:** Wired up `internal/auth` with SQLite sessions and default admin user. +- **Phase 3: Timeline & Google Integration:** Added the "Timeline" view, aggregating Google Calendar and Google Tasks via a cached store layer. +- **Phase 4: Agent Context API:** Built the `/agent/context` endpoint and WebSocket-based approval flow to let AI agents "see" what the user is working on safely. + +## 3. Current Direction: Strategic Execution +The project is currently evolving into a **Strategic Agent Studio**, where: +- Doot serves as the "Lens" (View/Approve). +- Claudomator serves as the "Muscle" (Execute/Deliver). +- The `.agent/` directory serves as the "Brain" (Config/Context). + +## 4. Historical Activity Log (Raw) + +--- 2026-03-16T01:52:44Z --- +Do a thorough review for refactoring opportunities, logic bugs and code, redundancy and shaky abstractions. Create claudomator tasks for each + +--- 2026-03-17T09:02:26Z --- +Completing Trello tasks doesn't work: failed to load content (Fix: Verified and corrected Trello API param mapping) diff --git a/.agent/preferences.md b/.agent/preferences.md new file mode 100644 index 0000000..4d79ba2 --- /dev/null +++ b/.agent/preferences.md @@ -0,0 +1,15 @@ +# User Preferences & Workflow Quirks + +This file is a "living record" of the user's personal preferences, discovered patterns, and workflow expectations. Agents must proactively update this file as new preferences are revealed during interaction. + +## 1. Interaction & Workflow +- **Safety First:** The user prefers caution and deliberate action. +- **The "Checkpoint" Model:** Research first, strategy second, and wait for a "GO" (Directive). +- **Continuous Clarification:** A "GO" is not a mandate to stop asking questions. If execution hits an ambiguity, stop and ask. +- **Living Docs:** Agents are empowered and expected to update any file in the `.agent/` directory when a new fact, decision, or preference is established. + +## 2. Technical Preferences +- (To be populated as preferences are revealed during the project...) + +## 3. UI/UX Style +- (To be populated as preferences are revealed during the project...) diff --git a/.agent/timeline_design.md b/.agent/timeline_design.md new file mode 100644 index 0000000..61c71b5 --- /dev/null +++ b/.agent/timeline_design.md @@ -0,0 +1,82 @@ +# Timeline Feature Design + +## Objective +Create a unified timeline view showing Tasks, Meals, Trello Cards, and Google Calendar Events, sorted chronologically. + +## Data Sources +1. **Tasks (Todoist):** From DB `tasks` table. Filter: `due_date` is not null. +2. **Meals (PlanToEat):** From DB `meals` table. Filter: `date` is not null. +3. **Cards (Trello):** From DB `cards` table. Filter: `due_date` is not null. +4. **Google Calendar Events:** From `GoogleCalendarClient` (Live API). + +## Backend Logic + +### 1. Data Models (`internal/models/timeline.go`) +We need a polymorphic structure to hold different item types. + +```go +type TimelineItemType string + +const ( + ItemTypeTask TimelineItemType = "task" + ItemTypeMeal TimelineItemType = "meal" + ItemTypeCard TimelineItemType = "card" + ItemTypeEvent TimelineItemType = "event" +) + +type TimelineItem struct { + ID string `json:"id"` + Type TimelineItemType `json:"type"` + Title string `json:"title"` + Description string `json:"description"` + Time time.Time `json:"time"` + AllDay bool `json:"all_day"` + URL string `json:"url"` + Meta map[string]any `json:"meta"` // Extra data like ProjectName, RecipeURL, etc. +} +``` + +### 2. Store Extensions (`internal/store/`) +Add methods to fetch items by date range. +* `GetTasksByDateRange(start, end time.Time) ([]models.Task, error)` +* `GetMealsByDateRange(start, end time.Time) ([]models.Meal, error)` +* `GetCardsByDateRange(start, end time.Time) ([]models.Card, error)` + +### 3. Logic Layer (`internal/handlers/timeline_logic.go`) +* **Function:** `BuildTimeline(store Store, calendarClient *api.GoogleCalendarClient, start, end time.Time) ([]TimelineItem, error)` +* **Aggregation:** + 1. Fetch from Store (Tasks, Meals, Cards). + 2. Fetch from Calendar API. + 3. Convert all to `TimelineItem`. + 4. **Meal Time Defaults:** + * Breakfast: 08:00 + * Lunch: 12:00 + * Dinner: 19:00 + * Other: 12:00 + 5. Sort by `Time`. + +### 4. HTTP Handler (`internal/handlers/timeline.go`) +* **Route:** `GET /timeline` +* **Params:** `start` (default: today), `days` (default: 7) +* **Response:** Render `timeline-tab.html` partial. + +## UI Design (`web/templates/partials/timeline-tab.html`) +* **Structure:** + * Container + * Loop through Days + * Date Header (e.g., "Mon, Jan 2") + * List of Items + * Time Column (e.g., "14:00") + * Content Column (Icon + Title + Subtitle) +* **Styling:** + * Use existing CSS variables. + * Distinct icons/colors for Tasks vs Meals vs Events. + +## Files to Modify +1. `internal/models/timeline.go` (New) +2. `internal/store/store.go` (Interface update) +3. `internal/store/sqlite.go` (Implementation update) +4. `internal/handlers/timeline_logic.go` (New) +5. `internal/handlers/timeline.go` (New) +6. `web/templates/partials/timeline-tab.html` (New) +7. `cmd/dashboard/main.go` (Route registration) diff --git a/.agent/ux_philosophy.md b/.agent/ux_philosophy.md new file mode 100644 index 0000000..e9b6a87 --- /dev/null +++ b/.agent/ux_philosophy.md @@ -0,0 +1,82 @@ +# UI/UX Review & Redesign Proposal + +## Current State Analysis: "The Unusable Form" + +The current "Quick Add" modal (in `index.html`) and the "Planning" tab form suffer from several friction points: + +1. **Context Blindness:** When adding a Trello card, you must first pick a source, then a board, then a list. This is 3+ clicks before you even type the task name. +2. **Repetitive Data Entry:** Creating multiple related stories (e.g., across 3 repos) requires filling the entire form 3 times. +3. **No "Smart" Defaults:** The form doesn't remember your last used project, repo, or base image. +4. **Mobile Friction:** Select dropdowns are native and small, making them hard to use for long lists of repos or boards. +5. **Information Overload:** The "Task" and "Shopping" tabs in the modal use the same `HandleUnifiedAdd` endpoint but different fields, leading to a "leaky abstraction" in the UI. + +--- + +## 1. Redesign: The "Command Palette" Model + +Instead of a traditional form, we should move towards a **Command Palette** style interaction (Ctrl+K). + +### Proposed Streamlined Flow: +1. **Trigger:** `Ctrl+K` opens a search-first modal. +2. **Type:** User types "doot: fix css in dashboard" or "claudomator: update go version". +3. **Prefix Detection:** + * `d:` or `t:` -> Todoist task. + * `tr:` -> Trello card. + * `c:` or `s:` -> Claudomator Story (New). +4. **Contextual Suggestions:** As the user types, the bottom of the modal shows: + * "Repo: doot" (detected from prefix or frequency) + * "Project: Phase 6" + * "Base Image: Go 1.24" + +--- + +## 2. Resource Management Model (Repos, Images, Projects) + +The user requested a "better model for management." We should move away from flat strings and toward a **Resource Graph**. + +### New SQLite Schema (Internal) +* **`claudomator_repos`:** `id, name, remote_url, default_branch, local_path` +* **`claudomator_images`:** `id, name, tag, description (e.g., "Go 1.24 + Node 22")` +* **`claudomator_projects`:** `id, name, description` +* **`project_repos`:** Junction table linking projects to multiple repositories. + +### Management UI +Instead of hiding these in settings, we should have a **"Library" view** (perhaps a new tab or a sub-section of Planning) where users can: +* Quick-add a new repo. +* Toggle which "Base Images" are active for selection. +* Group repos into "Projects" for batch operations. + +--- + +## 3. UI/UX Recommendations & Plugins + +### Recommended Plugins/Libraries +1. **[Tom Select](https://tom-select.js.org/):** To replace native `