summaryrefslogtreecommitdiff
path: root/DESIGN.md
diff options
context:
space:
mode:
Diffstat (limited to 'DESIGN.md')
-rw-r--r--DESIGN.md111
1 files changed, 73 insertions, 38 deletions
diff --git a/DESIGN.md b/DESIGN.md
index e568217..a62ec47 100644
--- a/DESIGN.md
+++ b/DESIGN.md
@@ -4,7 +4,7 @@
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.21+ | chi router | HTMX 1.x | Tailwind CSS | SQLite (WAL mode)
+**Stack:** Go 1.24 | chi router | HTMX 1.x | Tailwind CSS | SQLite (WAL mode)
---
@@ -12,7 +12,7 @@ Task Dashboard is a personal productivity web application that aggregates tasks,
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)
+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
@@ -22,15 +22,15 @@ 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.
+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 | 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 | 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
@@ -89,7 +89,7 @@ Tasks flow through these states:
```
task-dashboard/
├── cmd/dashboard/
-│ └── main.go # Application entry point, route registration
+│ └── main.go # Entry point, route registration, ldflags
├── internal/
│ ├── api/ # External API clients
│ │ ├── interfaces.go # API client contracts (interfaces)
@@ -100,40 +100,54 @@ task-dashboard/
│ │ ├── google_calendar.go # Google Calendar integration
│ │ └── google_tasks.go # Google Tasks integration
│ ├── auth/
-│ │ ├── auth.go # Authentication service (bcrypt)
-│ │ ├── handlers.go # Login/logout handlers
+│ │ ├── 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 (~2000 LOC)
-│ │ ├── response.go # JSON/HTML response utilities
-│ │ ├── cache.go # Generic cache fetcher pattern
-│ │ ├── atoms.go # Unified task aggregation
+│ │ ├── 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
+│ │ ├── 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, etc.)
+│ │ ├── 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 (~700 LOC)
-├── migrations/ # SQL migration files (001-009)
+│ └── 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
-│ │ └── partials/ # HTMX partial templates
+│ │ ├── 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
+│ └── js/app.js # Client-side logic + WebSocket
+├── deploy.sh # Build + deploy script (ldflags injection)
└── tailwind.config.js
```
@@ -524,7 +538,7 @@ func (h *Handler) HandleNewFeature(w http.ResponseWriter, r *http.Request) {
_ = h.store.SaveResult(result)
// 5. Respond
- HTMLResponse(w, h.templates, "partial-name", result)
+ HTMLResponse(w, h.renderer, "partial-name", result)
}
```
@@ -576,10 +590,10 @@ r.Post("/newfeature/action", h.HandleNewAction)
**Example:**
```go
// Standalone page - use filename
-h.templates.ExecuteTemplate(w, "shopping-mode.html", data)
+h.renderer.Render(w, "shopping-mode.html", data)
// Partial - use define name
-HTMLResponse(w, h.templates, "shopping-tab", data)
+HTMLResponse(w, h.renderer, "shopping-tab", data)
```
### HTMX Patterns
@@ -641,21 +655,25 @@ func TestHandler_HandleDashboard(t *testing.T) {
### Debugging Tips
-1. **Check logs:** All errors logged with `log.Printf`
-2. **Cache issues:** Check `cache_metadata` table for stale timestamps
-3. **API failures:** Look for `"Warning:"` or `"ERROR:"` prefixes
-4. **Template errors:** Check for `html/template: "name" is undefined`
-5. **HTMX issues:** Browser DevTools Network tab shows all requests
+1. **Production logs:** `bash scripts/logs -n 200 2>&1 | grep -i error`
+2. **Production bugs:** `bash scripts/bugs` to list, `bash scripts/resolve-bug <id>` to close
+3. **Cache issues:** Check `cache_metadata` table for stale timestamps
+4. **API failures:** Look for `"Warning:"` or `"ERROR:"` prefixes in logs
+5. **Template errors:** Check for `html/template: "name" is undefined`
+6. **HTMX issues:** Browser DevTools Network tab shows all requests
+7. **WebAuthn issues:** Check for "WebAuthn initialized" in startup logs; requires `WEBAUTHN_RP_ID` + `WEBAUTHN_ORIGIN` env vars
### Files You'll Modify Most
| Task | Primary Files |
|------|--------------|
-| New feature | `handlers.go`, `main.go`, new template |
-| Bug fix | Usually `handlers.go` or specific API client |
+| 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, update `sqlite.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
@@ -668,13 +686,19 @@ func TestHandler_HandleDashboard(t *testing.T) {
**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")
+- `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
@@ -745,7 +769,7 @@ 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.5 <noreply@anthropic.com>
+Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
```
**If history diverges:** Use `git pull --rebase=false` to merge, never force push.
@@ -787,8 +811,11 @@ Bugs appear as atoms in the Tasks view (🐛 icon) until resolved.
| Component | Test File |
|-----------|-----------|
| Store methods | `internal/store/sqlite_test.go` |
-| Handlers | `internal/handlers/*_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:**
@@ -915,11 +942,16 @@ npx tailwindcss -i web/static/css/input.css -o web/static/css/output.css --watch
| 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 |
+| 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 |
@@ -927,6 +959,9 @@ npx tailwindcss -i web/static/css/input.css -o web/static/css/output.css --watch
| 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 |
@@ -945,8 +980,8 @@ func (h *Handler) HandleName(w http.ResponseWriter, r *http.Request) {
### Response Helpers
```go
-JSONResponse(w, data) // 200 + JSON
+JSONResponse(w, data) // 200 + JSON + no-cache
JSONError(w, status, "message", err) // Error + log
-HTMLResponse(w, h.templates, "name", data) // Partial render
+HTMLResponse(w, h.renderer, "name", data) // Partial render + no-cache
HTMLString(w, "<html>...</html>") // Raw HTML
```