Update README, BACKLOG, and AGENTS to reflect current state
Assisted-by: Claude Opus 4.6 via Claude Code
diff --git a/AGENTS.md b/AGENTS.md
index 3293cc6..a5d9e08 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -6,19 +6,27 @@ Read the README for project context, domain concepts, and design intent.
```
lib/
- config.rb # Config Data object, reads DATABASE_URL from env
- db.rb # Sequel connection + auto-migration
- web.rb # Roda app (routes, current_user from Tailscale headers)
- views/
- layout.rb # Phlex base layout
- series/
- new.rb # New series form
+ ketchup/
+ config.rb # Config data object, reads DATABASE_URL from env
+ db.rb # Sequel connection + auto-migration
+ models.rb # User, Series, Task models and associations
+ web.rb # Roda app (routes, current_user from Tailscale headers)
+ views/
+ layout.rb # Phlex base layout (head, nav, body wrapper)
+ dashboard.rb # Main view: overdue, upcoming, series detail/new sidebar
+ sequel/
+ plugins/
+ sole.rb # Custom Sequel plugin: Dataset#sole
db/
- migrate/ # Sequel migrations (numbered)
+ migrate/ # Sequel migrations (numbered)
test/
- test_web.rb # Minitest integration tests
+ test_db.rb # Schema constraint tests
+ test_web.rb # Minitest + Rack::Test integration tests
+ test_sole.rb # Sole plugin tests
public/
- css/ # Static assets
+ js/app.js # Alpine components, OverType editor setup
+ css/ # Static stylesheets
+config.ru # Rack entrypoint (Sentry + Web.app)
```
## Running
@@ -33,7 +41,9 @@ Tests set `DATABASE_URL=:memory:` so they never touch the real database.
## Conventions
-- **Views:** Phlex component classes under `lib/views/`, not ERB templates.
+- **Views:** Phlex component classes under `lib/ketchup/views/`, not ERB templates.
- **Migrations:** Sequel migrations in `db/migrate/`, numbered sequentially (`001_`, `002_`, …). Migrations auto-run on boot.
-- **User identification:** Current user comes from `HTTP_TAILSCALE_USER_LOGIN` / `HTTP_TAILSCALE_USER_NAME` request headers.
+- **User identification:** Current user from `HTTP_TAILSCALE_USER_LOGIN` / `HTTP_TAILSCALE_USER_NAME` request headers.
- **Testing:** Minitest with `Rack::Test`. Fake Tailscale headers via helper.
+- **Client-side:** Alpine.js for reactivity, Alpine Persist for state persistence, OverType for markdown editing. No build step — all loaded via CDN.
+- **Ownership scoping:** User has `many_through_many :tasks` through `:series`. Routes use `@user.tasks_dataset` and `@user.series_dataset` to scope lookups.
diff --git a/BACKLOG.md b/BACKLOG.md
index c7820fe..ffd2578 100644
--- a/BACKLOG.md
+++ b/BACKLOG.md
@@ -2,23 +2,6 @@
## Backlog
-### Complete tasks
-
-**What:** Mark a task as done — completes the current task, creates the next one with a due date advanced by the series interval.
-**Why now:** Core loop — without this, tasks just accumulate and the app isn't usable.
-**Done when:**
-- A task can be marked done from the task list
-- Completing a task sets `completed_at` and creates a new active task with the next due date
-- Both happen atomically in a transaction
-
-### Delay task due date
-
-**What:** Push a task's due date forward without completing it.
-**Why now:** Sometimes you know you can't get to something yet and want to stop it showing as overdue.
-**Done when:**
-- A task's due date can be changed from the task list
-- The task remains active (not completed)
-
### Colorscheme
**What:** A cohesive color palette instead of ad-hoc hex values.
@@ -27,28 +10,9 @@
- Colors are defined as CSS custom properties
- Applied consistently across the app
-### Markdown rendering
-
-**What:** Render task notes as Markdown instead of plain text.
-**Why now:** Notes often contain links, lists, or formatting that would benefit from rendering.
-**Done when:**
-- Task notes render Markdown in the task list
-- The new series form still accepts plain text (rendered on display)
-
-### Calendar view
-
-**What:** A calendar visualization showing when tasks are due, giving a sense of upcoming load.
-**Why now:** The sorted list shows priority but not temporal distribution — hard to see if next week is packed.
-**Done when:**
-- A calendar view shows tasks plotted on their due dates
-- Overdue tasks are visually distinct
-
## Icebox
-- Edit a task/series
-- Delete a task/series
-- Per-completion notes
-- Fixed-schedule recurrence
-- Personal/shared toggle
+- Delete a series
+- Fixed-schedule recurrence (e.g., "every Monday" vs "7 days after completion")
+- Shared tasks — visible to all users, anyone can mark done
- CLI interface
-- Sentry
diff --git a/README.md b/README.md
index f3b64d7..caf3132 100644
--- a/README.md
+++ b/README.md
@@ -1,44 +1,33 @@
-# ketchup
+# Ketchup
-A personal/family tool for tracking recurring tasks and catching up on what's overdue.
+A personal tool for tracking recurring tasks and catching up on what's overdue.
## Overview
- **Users:** Me and my family
-- **Auth:** Handled via Tailscale — rely on remote user header (no in-app auth)
-- **Interface:** Web app (Roda), architected so a CLI can be added later
-- **Stack:** Ruby, Roda, Sequel, SQLite3
+- **Auth:** Tailscale headers (no in-app auth)
+- **Stack:** Ruby, Roda, Sequel, SQLite, Phlex, Alpine.js, OverType
-## Key concepts
+## Domain model
-- **Tasks** are recurring, with a configurable interval (day/week/month/quarter/year) and interval count
- - Recurrence is based on interval+count from last completion (not fixed schedule — but may add fixed schedule later)
-- Each user has their own tasks
-- Tasks can also be **shared** — visible to all users, anyone can mark them done, overdue shows for everyone
-- One-off tasks are out of scope for now
-- No task assignment — shared tasks are not assigned to a specific person
-- Each task has a **note** (free text) — the first line is the task name
- - Per-completion notes may be added later
+A **Series** defines a recurring obligation — "Call Mom every 2 weeks." It holds the note, interval unit (day/week/month/quarter/year), and interval count. The note's first line serves as the display name.
-## Task fields
+Each Series has one active **Task** at a time. Completing a task creates the next one, with a due date advanced by the interval from today. Tasks also hold optional per-completion notes.
-- **Note** — free text; first line serves as the task name/title
-- **Interval unit** — day, week, month, quarter, or year
-- **Interval count** — e.g., 2 (combined with unit: "every 2 weeks")
-- **First due date** — user picks this on creation
-- **Personal or shared**
+A **User** owns Series (and, transitively, Tasks). The `many_through_many` association on User provides direct task access for ownership scoping.
-## Primary use case
+## Main view
-Catching up with friends and family — some weekly, some quarterly, some yearly, etc.
+The dashboard has three columns:
-## Main view
+1. **Overdue** — tasks past due, sorted by urgency (how late relative to interval)
+2. **Upcoming** — a calendar showing tasks by due date, with empty days as context
+3. **Sidebar** — either a new series form or the selected series detail (note, interval, due date, completion history with per-completion notes)
-A single list of tasks in priority order, automatically sorted:
+## Primary use case
-1. **Overdue tasks first** — ordered by how late they are *relative to their interval* (most proportionally overdue at top)
-2. **Upcoming tasks after** — ordered by due date (soonest first)
+Catching up with friends and family — some weekly, some quarterly, some yearly.
-## Architecture
+## Development
-Domain logic should be separated from the web layer so a CLI can be added later.
+Largely vibe-coded with Claude Code. I steer direction and make design calls; Claude writes most of the code.