Update README, BACKLOG, and AGENTS to reflect current state
Assisted-by: Claude Opus 4.6 via Claude Code
change xmytsmkwrzrxzswstlkoxnvywtvztwys
commit 5d31fed3a789bbb0e570cd490e2fd9895a8f8e47
author Alpha Chen <alpha@kejadlen.dev>
date
parent nwoqrour
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.