diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-02-05 10:37:09 -1000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-02-05 10:37:09 -1000 |
| commit | 223c94f52ebaa878f6951ebf7d08754e413bdca7 (patch) | |
| tree | fc02a201fb83ec49fd5978d2ec9bf79f83e5d57c | |
| parent | 4ac78382343e14c00ba21ee11ee4642255509eef (diff) | |
Document multi-agent workflow, ADRs, and Agent API
- Add Development Workflow section to DESIGN.md documenting three-role
system (Architect, Implementor, Reviewer) with handoff documents
- Update CLAUDE.md with Key Documents section pointing to DESIGN.md,
role definitions, and ADRs
- Add ADR-first documentation policy across all role definitions
- Update REVIEWER_ROLE.md with comprehensive test quality checklist
- Document Agent API and completed tasks log in DESIGN.md
- Update database schema table with 5 missing tables
- Update endpoint reference with 10 missing routes
- Create ADRs 002-005 capturing key architectural decisions:
- 002: Timeline aggregation architecture
- 003: HTMX write operations pattern
- 004: Concurrent data fetching with graceful degradation
- 005: Agent API notification-based authentication
- Add migrations/README.md documenting schema history and 007 gap
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
| -rw-r--r-- | ARCHITECT_ROLE.md | 8 | ||||
| -rw-r--r-- | CLAUDE.md | 35 | ||||
| -rw-r--r-- | DESIGN.md | 177 | ||||
| -rw-r--r-- | IMPLEMENTOR_ROLE.md | 3 | ||||
| -rw-r--r-- | REVIEWER_ROLE.md | 26 | ||||
| -rw-r--r-- | docs/adr/002-timeline-aggregation.md | 76 | ||||
| -rw-r--r-- | docs/adr/003-htmx-write-operations.md | 85 | ||||
| -rw-r--r-- | docs/adr/004-concurrent-data-fetching.md | 104 | ||||
| -rw-r--r-- | docs/adr/005-agent-api-authentication.md | 95 | ||||
| -rw-r--r-- | migrations/README.md | 36 |
10 files changed, 618 insertions, 27 deletions
diff --git a/ARCHITECT_ROLE.md b/ARCHITECT_ROLE.md index 233f157..409102d 100644 --- a/ARCHITECT_ROLE.md +++ b/ARCHITECT_ROLE.md @@ -12,9 +12,15 @@ **Gemini Architect Persona:** * You are the **Lead Architect**. * **Constraint:** You **DO NOT** write or edit Project Source Code (e.g., `.go`, `.html`, `.js`). -* **Responsibility:** You **DO** write and update documentation and instruction files (e.g., `SESSION_STATE.md`, `instructions.md`, `issues/*.md`). Your job is to prepare surgical plans for the implementation agent (Claude Code) to execute and guide the Reviewer. +* **Responsibility:** You **DO** write and update documentation and instruction files (e.g., `SESSION_STATE.md`, `instructions.md`, `issues/*.md`, `docs/adr/*.md`). Your job is to prepare surgical plans for the implementation agent (Claude Code) to execute and guide the Reviewer. * **Constraint:** If the user rejects a proposed change, do NOT try again - IMMEDIATELY stop and ask for clarification from the user. +**ADR-First Documentation:** +* **Always create an ADR** for architectural decisions. Do NOT create one-off design documents. +* ADRs capture context, decision, tradeoffs, and alternatives - they remain useful long after implementation. +* Use `instructions.md` for ephemeral implementation details only. +* See `DESIGN.md` → "Architecture Decision Records" for the template and current ADRs. + **Workflow Instructions:** 1. **Analyze:** @@ -1,9 +1,29 @@ # Claude Code Project Guidelines ## Project Overview -A unified web dashboard aggregating Trello (PRIMARY), Todoist, Obsidian, and PlanToEat. +A unified web dashboard aggregating Trello (PRIMARY), Todoist, Obsidian, and PlanToEat. **Stack:** Go backend + HTMX + Tailwind CSS + SQLite. +## Key Documents (Read These First) + +| Document | Purpose | When to Read | +|----------|---------|--------------| +| `DESIGN.md` | Authoritative design doc: architecture, patterns, visual design, dev guide | Before any significant work | +| `SESSION_STATE.md` | Current task state, next steps | Start of every session | +| `docs/adr/*.md` | Architecture Decision Records | Before implementing features in that area | + +### Multi-Agent Workflow + +This project uses a three-role development workflow. **Read your role definition before starting work:** + +| Role | Definition File | Responsibilities | +|------|-----------------|------------------| +| Architect | `ARCHITECT_ROLE.md` | Plans, documents, creates ADRs. Does NOT edit code. | +| Implementor | `IMPLEMENTOR_ROLE.md` | Executes plans, writes code, runs tests. | +| Reviewer | `REVIEWER_ROLE.md` | Reviews code quality and tests. Does NOT edit code. | + +**Handoff docs:** `instructions.md` (Architect → Implementor), `review_feedback.md` (Reviewer → Implementor) + ## Efficiency & Token Management - **Context Minimization:** Do not read entire files if `grep`, `sed`, or `ls` can answer a question. - **Surgical Edits:** Perform small, targeted file edits. Avoid rewriting entire files for single changes. @@ -22,19 +42,26 @@ A unified web dashboard aggregating Trello (PRIMARY), Todoist, Obsidian, and Pla - **Build:** `go build -o dashboard cmd/dashboard/main.go` ## State Management -- **SESSION_STATE.md:** The source of truth for resuming work. It must include: +- **SESSION_STATE.md:** The source of truth for resuming work. Must include: - Current Task Goal - Completed Items - - Architecture Decisions - **Next 3 Specific Steps** +- **Status tags:** `[TODO]` → `[IN_PROGRESS]` → `[REVIEW_READY]` → `[APPROVED]` (or `[NEEDS_FIX]`) ## Technical Context - **Trello is PRIMARY:** Key + Token required in query params. -- **Architecture:** chi router -> Handlers (`internal/handlers/`) -> Store (`internal/store/sqlite.go`). +- **Architecture:** chi router → Handlers (`internal/handlers/`) → Store (`internal/store/sqlite.go`). - **Errors:** Partial data/cache fallback preferred over hard failure. +- **Full details:** See `DESIGN.md` → Architecture section. ## Coding Style - Use concise, idiomatic Go. - Avoid verbose explanations or comments for self-evident logic. - Prioritize terminal-based verification over manual code review. +- **Patterns:** See `DESIGN.md` → Development Guide for handler/template patterns. + +## Documentation +- **ADR-first:** Capture architectural decisions in `docs/adr/*.md`, not one-off design docs. +- **Update DESIGN.md** for new features, endpoints, or schema changes. +- **Do NOT create** standalone design documents—use ADRs instead. @@ -20,6 +20,68 @@ The dashboard serves as a **personal consolidation hub** designed to: --- +## Development Workflow + +This project uses a **multi-agent development workflow** with three specialized roles that collaborate through shared documents. + +### Roles + +| Role | Agent | Responsibilities | +|------|-------|------------------| +| **Architect** | Gemini | Plans features, creates issues, writes surgical instructions. Does NOT edit source code. | +| **Implementor** | Claude Code | Executes plans, writes code, runs tests. Updates state after completing work. | +| **Reviewer** | QA Agent | 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 + +### 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 @@ -330,6 +392,61 @@ Standalone live feeds page with: 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 @@ -351,6 +468,11 @@ Auto-refresh webcam images every 60 seconds. | `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 | --- @@ -705,22 +827,33 @@ func TestFeature_Behavior(t *testing.T) { - Add tests for new functionality - Update tests for changed behavior -### Design Documents and ADRs +### Architecture Decision Records (ADRs) -**When to update DESIGN.md:** -- Adding new features or views -- Changing data flow patterns -- Modifying API integrations -- Updating database schema +**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. -**When to create an ADR (Architecture Decision Record):** +**Always create an ADR when:** - Choosing between multiple implementation approaches -- Adding new external dependencies -- Changing authentication/security model -- Significant refactoring decisions +- 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 @@ -749,11 +882,11 @@ What did we decide and why? **Numbering:** Use sequential 3-digit numbers (001, 002, etc.) -**Example ADRs to consider:** -- API client retry strategy -- Caching invalidation approach -- New data source integration -- UI framework changes +**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 --- @@ -783,12 +916,22 @@ npx tailwindcss -i web/static/css/input.css -o web/static/css/output.css --watch | GET | `/tabs/timeline` | Timeline partial | | GET | `/tabs/tasks` | Tasks partial | | GET | `/tabs/shopping` | Shopping partial | -| GET | `/conditions` | Live feeds page | +| GET | `/conditions` | Live feeds page (public) | | POST | `/complete-atom` | Complete task/card | | POST | `/uncomplete-atom` | Reopen task/card | | POST | `/unified-add` | Quick add task | | POST | `/shopping/add` | Add shopping item | -| GET | `/shopping/mode/{store}` | Shopping mode | +| 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 | `/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 diff --git a/IMPLEMENTOR_ROLE.md b/IMPLEMENTOR_ROLE.md index 62bfd7e..31c3f53 100644 --- a/IMPLEMENTOR_ROLE.md +++ b/IMPLEMENTOR_ROLE.md @@ -23,7 +23,8 @@ 2. **Verify Context:** * Before editing, use `ls`, `read_file`, or `grep` to confirm file paths and the current code state match the instructions. - * If the instructions seem outdated or conflict with the current codebase, stop and ask for clarification. + * **Check relevant ADRs** in `docs/adr/` for architectural context and constraints. + * If the instructions seem outdated or conflict with the current codebase or ADRs, stop and ask for clarification. 3. **Test-Driven Execution (TDD):** * **Pre-Check:** Run existing tests (`go test ./...`) or the specific reproduction test case provided to confirm the baseline (fail state for bugs, pass state for refactors). diff --git a/REVIEWER_ROLE.md b/REVIEWER_ROLE.md index 7c43dca..9bcf101 100644 --- a/REVIEWER_ROLE.md +++ b/REVIEWER_ROLE.md @@ -16,8 +16,9 @@ * **Focus:** * **Correctness:** Does the code do what it is supposed to do? * **Clean Code:** Is the code readable? Are functions small and focused? Are names descriptive? - * **TDD:** Are there tests? Do they pass? Do they cover edge cases? Was the test written *before* the code (inferable from structure/commit history)? + * **Test Quality:** Are tests effective, clear, and complete? (See Test Review Checklist below) * **Simplicity:** Is this the simplest thing that could possibly work? (YAGNI). + * **Documentation:** For significant changes, verify an ADR exists in `docs/adr/`. Flag missing ADRs for architectural decisions. **Workflow Instructions:** @@ -28,9 +29,25 @@ 2. **Verify (Dynamic Analysis):** * **Run Tests:** Execute `go test ./...` or specific package tests to ensure the build is green. - * **Coverage:** Check if new code is covered by tests. + * **Coverage:** Check if new code is covered by tests. Use `go test -cover ./...` for coverage report. -3. **Critique (Static Analysis):** +3. **Review Tests (Test Quality Analysis):** + * **Effective:** Do tests actually verify the behavior they claim to test? + * Check assertions match test names - a test named `TestCreateTask_InvalidInput` should assert error handling, not success. + * Verify tests would fail if the code was broken - watch for tests that always pass. + * Ensure tests exercise the code path in question, not just adjacent code. + * **Clear:** Can you understand what the test verifies at a glance? + * Test names should describe the scenario and expected outcome: `TestHandler_MissingField_Returns400` + * Arrange-Act-Assert structure should be obvious. + * No magic numbers - use named constants or clear literals. + * Minimal setup - only what's needed for this specific test. + * **Complete:** Do tests cover the important cases? + * Happy path (normal operation) + * Error cases (invalid input, missing data, API failures) + * Edge cases (empty lists, nil values, boundary conditions) + * For bug fixes: regression test that would have caught the bug. + +4. **Critique (Static Analysis):** * **Read Code:** Analyze the changes. Look for: * **Complexity:** Nested loops, deep conditionals, long functions. * **Naming:** Vague variable names, misleading function names. @@ -38,7 +55,7 @@ * **Architecture:** Leaky abstractions (e.g., SQL in handlers). * **Security:** Basic checks (input validation, error handling). -4. **Report & State Update:** +5. **Report & State Update:** * **Write Feedback:** Create or update `review_feedback.md`. * **Decision:** * **PASS:** If code meets standards, update `SESSION_STATE.md` item to `[APPROVED]`. @@ -47,6 +64,7 @@ * `# Review Cycle [Date/Time]` * `## Status: [NEEDS_FIX / APPROVED]` * `## Critical Issues (Blocking)`: Must be fixed before approval. + * `## Test Quality Issues`: Missing tests, unclear tests, ineffective assertions. * `## Clean Code Suggestions (Non-Blocking)`: Improvements for readability. * `## Praise`: What was done well. diff --git a/docs/adr/002-timeline-aggregation.md b/docs/adr/002-timeline-aggregation.md new file mode 100644 index 0000000..2700776 --- /dev/null +++ b/docs/adr/002-timeline-aggregation.md @@ -0,0 +1,76 @@ +# ADR 002: Timeline Aggregation Architecture + +## Status +Accepted + +## Context +The dashboard aggregates data from multiple sources (Todoist, Trello, PlanToEat, Google Calendar, Google Tasks). Users need a unified chronological view of upcoming items across all sources without opening multiple apps. + +Key challenges: +- Different data sources have different models (Task, Card, Meal, Event) +- Some sources have time-of-day, others only have dates +- Meals have no explicit time but should appear at logical meal times +- Need to support grouping by day section (Today, Tomorrow, Later) + +## Decision +Implement a **polymorphic TimelineItem model** that normalizes all data sources into a single sortable structure. + +### Technical Details: + +**Unified Model (`internal/models/timeline.go`):** +```go +type TimelineItem struct { + ID string + Type TimelineItemType // task, meal, card, event, gtask + Title string + Description string + Time time.Time + AllDay bool + URL string + DaySection string // overdue, today, tomorrow, later + // ... additional fields +} +``` + +**Meal Time Defaults:** +- Breakfast → 08:00 +- Lunch → 12:00 +- Dinner → 19:00 +- Other → 12:00 + +**Implementation:** +- `internal/handlers/timeline_logic.go` - `BuildTimeline()` aggregation function +- `internal/handlers/timeline.go` - HTTP handler +- `internal/models/timeline.go` - Data models +- Store methods: `GetTasksByDateRange()`, `GetMealsByDateRange()`, `GetCardsByDateRange()` + +**Grouping Strategy:** +Items are grouped into collapsible day sections: +- **Overdue** - Past due items (expanded) +- **Today** - Due today (expanded) +- **Tomorrow** - Due tomorrow (expanded) +- **Later** - 2+ days out (collapsed by default) + +## Consequences + +**Pros:** +- Single unified view reduces context switching +- Consistent sorting regardless of data source +- Extensible - new sources just need a converter to TimelineItem +- Collapsible sections reduce cognitive load + +**Cons:** +- Meal times are assumed, not actual (user might eat breakfast at 10am) +- All-day items appear at midnight, requiring special handling +- Multiple API calls per request (mitigated by caching) + +## Alternatives Considered + +**Option A: Source-specific views only** +- Rejected: Defeats the purpose of a unified dashboard + +**Option B: Store normalized items in database** +- Rejected: Adds complexity, staleness issues, harder to keep in sync + +**Option C: Client-side aggregation (JavaScript)** +- Rejected: More complex, slower on mobile, harder to test diff --git a/docs/adr/003-htmx-write-operations.md b/docs/adr/003-htmx-write-operations.md new file mode 100644 index 0000000..2471a90 --- /dev/null +++ b/docs/adr/003-htmx-write-operations.md @@ -0,0 +1,85 @@ +# ADR 003: HTMX-Based Write Operations + +## Status +Accepted + +## Context +Phase 1-2 of the dashboard were read-only views. Phase 3 required transforming it into an active command center where users can: +- Complete tasks/cards +- Create new tasks +- Add shopping items +- Toggle item states + +Traditional approaches would require either: +- Full page reloads (poor UX) +- Complex JavaScript SPA framework (React, Vue) +- Custom AJAX handlers + +## Decision +Use **HTMX attributes** for all write operations, returning HTML partials that swap into the DOM. + +### Technical Details: + +**Pattern: Form submission with partial swap** +```html +<form hx-post="/complete-atom" + hx-vals='{"id": "{{.ID}}", "source": "{{.Source}}"}' + hx-target="closest .task-card" + hx-swap="outerHTML"> + <button type="submit">✓</button> +</form> +``` + +**Handler returns HTML partial:** +```go +func (h *Handler) HandleCompleteAtom(w http.ResponseWriter, r *http.Request) { + // ... complete the task via API + // Return completed state HTML or empty response + HTMLResponse(w, h.renderer, "completed-atom", data) +} +``` + +**Quick Add Pattern:** +```html +<input hx-post="/unified-add" + hx-trigger="keyup[key=='Enter']" + hx-target="#task-list" + hx-swap="afterbegin" + name="content"> +``` + +**Optimistic UI:** +- Use CSS transitions for immediate visual feedback +- On error, swap in error banner partial +- HTMX `hx-on` events for state management + +**Unified Quick Add:** +- Single input parses text for routing hints +- `#groceries` tag → routes to shopping +- `#work` tag → routes to Trello +- Default → routes to Todoist + +## Consequences + +**Pros:** +- No JavaScript framework needed +- Server renders all HTML (single source of truth) +- Progressive enhancement (works without JS, enhanced with) +- Trivial to test (just HTTP handlers) +- Fast development cycle + +**Cons:** +- More HTTP requests than SPA approach +- Partial HTML responses require careful template organization +- Limited offline capability + +## Alternatives Considered + +**Option A: React/Vue SPA** +- Rejected: Overkill for this use case, adds build complexity, harder to maintain + +**Option B: Full page reloads** +- Rejected: Poor UX, especially on mobile + +**Option C: Custom JavaScript + JSON APIs** +- Rejected: Reinventing what HTMX provides, more code to maintain diff --git a/docs/adr/004-concurrent-data-fetching.md b/docs/adr/004-concurrent-data-fetching.md new file mode 100644 index 0000000..c7238f8 --- /dev/null +++ b/docs/adr/004-concurrent-data-fetching.md @@ -0,0 +1,104 @@ +# ADR 004: Concurrent Data Fetching with Graceful Degradation + +## Status +Accepted + +## Context +The dashboard fetches data from 5+ external APIs on each page load: +- Todoist (tasks) +- Trello (boards, cards) +- PlanToEat (meals, shopping) +- Google Calendar (events) +- Google Tasks (tasks) + +Sequential fetching would be unacceptably slow (5+ seconds). However, concurrent fetching introduces: +- Rate limiting concerns (especially Trello) +- Partial failure scenarios +- Data race risks + +## Decision +Implement **parallel fetching with semaphore-limited concurrency** and **cache-first fallback** for graceful degradation. + +### Technical Details: + +**Parallel Aggregation (`internal/handlers/handlers.go`):** +```go +func (h *Handler) aggregateData(ctx context.Context) (*DashboardData, error) { + var wg sync.WaitGroup + var mu sync.Mutex + data := &DashboardData{} + + // Launch goroutines for each data source + wg.Add(4) + go func() { defer wg.Done(); fetchTasks(ctx, &mu, data) }() + go func() { defer wg.Done(); fetchBoards(ctx, &mu, data) }() + go func() { defer wg.Done(); fetchMeals(ctx, &mu, data) }() + go func() { defer wg.Done(); fetchCalendar(ctx, &mu, data) }() + wg.Wait() + + return data, nil +} +``` + +**Semaphore for Trello (`internal/api/trello.go`):** +```go +const maxConcurrentRequests = 5 + +func (c *TrelloClient) GetBoardsWithCards(ctx context.Context) ([]Board, error) { + sem := make(chan struct{}, maxConcurrentRequests) + // ... limit concurrent board fetches +} +``` + +**Cache-First Fallback:** +```go +func (h *Handler) fetchWithFallback(ctx context.Context, fetch func() error, getCache func() (T, error)) T { + if err := fetch(); err != nil { + log.Printf("Warning: API fetch failed, using cache: %v", err) + cached, _ := getCache() + return cached + } + return freshData +} +``` + +**Error Collection:** +- Errors are collected in `data.Errors` slice +- UI displays warning banner if any source failed +- Page still renders with available data + +## Consequences + +**Pros:** +- Fast page loads (parallel fetching) +- Resilient to individual API failures +- Respects rate limits (semaphore pattern) +- Users see cached data rather than error page + +**Cons:** +- Complexity in error handling +- Cache fallback may show stale data without clear indication +- Debugging requires checking multiple error sources +- Mutex needed for shared data structure + +## Alternatives Considered + +**Option A: Sequential fetching** +- Rejected: 5+ second page loads unacceptable + +**Option B: Fail-fast on any error** +- Rejected: One flaky API shouldn't break entire dashboard + +**Option C: Background sync with eventual consistency** +- Rejected: Adds complexity, users expect fresh data on load + +## Implementation Notes + +**Race Condition Prevention:** +- All goroutines write to different fields OR use mutex +- Trello client uses mutex for `boards[i]` slice element writes +- Run `go test -race ./...` to verify + +**Monitoring:** +- Log all API failures with `Warning:` prefix +- Consider adding metrics for cache hit/miss rates diff --git a/docs/adr/005-agent-api-authentication.md b/docs/adr/005-agent-api-authentication.md new file mode 100644 index 0000000..e64a5a8 --- /dev/null +++ b/docs/adr/005-agent-api-authentication.md @@ -0,0 +1,95 @@ +# ADR 005: Agent API Authentication Architecture + +## Status +Accepted + +## Context +External agents (CLI tools, automation scripts, AI assistants) need secure access to dashboard context without exposing user credentials. Traditional approaches have weaknesses: + +- **API Keys**: Long-lived, often stored insecurely, difficult to revoke +- **OAuth2**: Complex setup, overkill for personal dashboard +- **SSH Keys**: Key management burden on user + +Requirements: +- User awareness of agent access (no hidden API keys) +- Easy revocation (deny re-auth requests) +- Time-limited sessions to reduce exposure +- Support for both CLI and browser-based agents + +## Decision +Implement **notification-based approval flow** with ephemeral sessions. + +### Technical Details: + +**Identity Model:** +- Agents identified by `name` + `agent_id` (UUID) pair +- Binding persisted on first approval +- Impersonation detected if name/agent_id mismatch + +**Trust Levels:** +- `TRUSTED`: Previously approved, auto-approve on re-auth +- `UNKNOWN`: New agent, requires user approval +- `BLOCKED`: Denied agent, reject immediately + +**Session Model:** +- 1-hour TTL tokens +- One active session per agent_id +- Session token in Authorization header + +**Approval Flow:** +``` +Agent Dashboard Browser + | | | + |-- POST /auth/request --> | | + | |-- WebSocket push ----> | + | | | (user sees modal) + | | | + |<-- request_token --------| | + | | | + |-- GET /auth/poll ------> | | + | (polling) | | + | | <-- POST /approve ---- | + | | | + |<-- session_token --------| | + | | | + |-- GET /context --------> | | + | (with session_token) | | +``` + +**Implementation:** +- `internal/handlers/agent.go` - Auth flow handlers (615 LOC) +- `internal/handlers/websocket.go` - Real-time notifications (216 LOC) +- `migrations/010_agent_tables.sql` - Storage schema +- `web/static/js/app.js` - Browser UI for approval modal + +**Rate Limiting:** +- 10 requests/minute per IP on auth endpoints +- Prevents brute-force token guessing + +## Consequences + +**Pros:** +- User-visible agent access (approval modal shows agent name) +- Easy revocation by denying re-auth or blocking agent +- Trust level caching reduces approval fatigue for known agents +- Time-limited sessions reduce credential exposure +- Works for both CLI agents (polling) and browser agents (web endpoints) + +**Cons:** +- Requires browser window open for first-time approval +- Poll loop adds latency to auth flow (~1-5 seconds) +- Session expiry interrupts long-running agent tasks (must re-auth) + +## Alternatives Considered + +**Option A: Traditional API Keys** +- Rejected: Long-lived credentials, no user awareness of access + +**Option B: OAuth2 Device Flow** +- Rejected: Complex implementation, requires external auth server setup + +**Option C: SSH-style Key Pairs** +- Rejected: Key management burden, harder to revoke + +**Option D: Magic Links (email-based)** +- Rejected: Adds email dependency, slower approval flow diff --git a/migrations/README.md b/migrations/README.md new file mode 100644 index 0000000..e849b9c --- /dev/null +++ b/migrations/README.md @@ -0,0 +1,36 @@ +# Database Migrations + +Sequential SQL migrations for task-dashboard schema evolution. + +## Migration History + +| # | File | Purpose | +|---|------|---------| +| 001 | `001_initial_schema.sql` | Initial schema (tasks, boards, cards, meals) | +| 002 | `002_add_cache_metadata.sql` | Cache metadata table for TTL tracking | +| 003 | `003_add_sync_tokens.sql` | Sync tokens for incremental sync | +| 004 | `004_add_auth.sql` | Authentication (users, sessions) | +| 005 | `005_add_bugs.sql` | Bug tracking table | +| 006 | `006_remove_resolved_bugs.sql` | Add resolved_at timestamp to bugs | +| 007 | — | **SKIPPED** (reserved, never used) | +| 008 | `008_user_shopping_items.sql` | User-added shopping items | +| 009 | `009_shopping_item_checks.sql` | External item check state | +| 010 | `010_agent_tables.sql` | Agent authentication (agents, agent_sessions) | +| 011 | `011_completed_tasks.sql` | Completed tasks log | +| 012 | `012_feature_toggles.sql` | Feature toggle system | +| 013 | `013_source_config.sql` | Source filtering configuration | + +## Migration 007 Gap + +Migration 007 was reserved during development but never implemented. The gap is intentional and preserved to maintain clean git history. Future migrations continue from 014. + +## Running Migrations + +Migrations are applied automatically on application startup via `store.RunMigrations()`. The `schema_migrations` table tracks which migrations have been applied. + +## Creating New Migrations + +1. Create file: `migrations/NNN_description.sql` (next number is 014) +2. Write idempotent SQL (use `IF NOT EXISTS` where possible) +3. Test locally: `go run cmd/dashboard/main.go` +4. Verify: Check `schema_migrations` table for new entry |
