summaryrefslogtreecommitdiff
path: root/docs/superpowers/specs/2026-09-02-pull-based-punch-list-design.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/superpowers/specs/2026-09-02-pull-based-punch-list-design.md')
-rw-r--r--docs/superpowers/specs/2026-09-02-pull-based-punch-list-design.md107
1 files changed, 107 insertions, 0 deletions
diff --git a/docs/superpowers/specs/2026-09-02-pull-based-punch-list-design.md b/docs/superpowers/specs/2026-09-02-pull-based-punch-list-design.md
new file mode 100644
index 0000000..15f333f
--- /dev/null
+++ b/docs/superpowers/specs/2026-09-02-pull-based-punch-list-design.md
@@ -0,0 +1,107 @@
+# Pull-based punch list — design
+
+**Status:** proposed, 2026-09-02
+**Goal:** the daily list should hit zero most days. Today it structurally cannot.
+
+## Problem
+
+Measured against production on 2026-09-02: 113 open native tasks. 83 are
+correctly-hidden locked chain steps. Of the ~30 visible, 19 are free-standing
+and genuinely today-sized ("Take out recycling", "Charge speaker", "Clean
+bathroom"). The punch list itself is fine. Everything around it is not.
+
+Three defects and one design flaw:
+
+1. **Paused chains leaked.** Task queries checked `chain_unlocked` but never the
+ owning chain's status, so 4 of 8 visible chain tasks came from explicitly
+ paused chains. *Fixed 2026-09-02 (3e26cd5).*
+
+2. **Recurring tasks double-book.** The next instance materializes before the
+ current one is closed, so "Bills" occupies two rows (Sep 1 overdue + Oct 1),
+ as does "Bones's meds". Every monthly recurrence permanently costs two slots.
+ *Not yet fixed.*
+
+3. **Buckets never got a pool.** One bucket exists ("Yard Work", cycle 14d,
+ pick 3) holding exactly 3 items. "Pick 3 of 3" makes the rotation a no-op —
+ everything is always active. A duplicate free-standing "Yard work" task does
+ the same job in parallel.
+
+4. **`due_date` is doing two incompatible jobs** — the design flaw underneath the
+ complaint. It means both *"this is a hard deadline"* (Bills, meds, T-ball
+ signup) and *"this is when I intend to work on it."* Chains lean hard on the
+ second meaning: `CreateChain` stamps `due_date = now` on the head, and
+ `advanceChain` does the same on every unlock. So a 5-minute phone call and a
+ 6-month certification are both born "due today," and the only way to make
+ them stop shouting is to hand-reschedule them — which is exactly what has
+ been happening.
+
+The deeper structural issue: the list is a **push** of everything not-blocked,
+not a **pull** of what was committed to. Zero is unreachable by construction when
+the list contains things that cannot be finished today.
+
+## Design
+
+Split the two meanings of `due_date` apart.
+
+- **`due_date`** keeps only its real meaning: an externally-imposed deadline.
+ Bills, meds, signups, appointments. Rare, and always meaningful.
+- **`committed_date`** (new, nullable) means "I chose to do this today."
+
+### What TODAY shows
+
+- tasks with `committed_date = today`
+- tasks with `due_date <= today` (real deadlines still push — they're deadlines)
+- calendar events (unchanged)
+
+Nothing else. Chain steps, bucket-active items, undated backlog, and
+future-dated work are all absent until pulled.
+
+### What chains do
+
+- Chain steps **no longer get a due date on unlock.** A milestone is not a
+ deadline. (`CreateChain` and `advanceChain` both stop stamping `due_date`.)
+- The unlocked step per chain is visible in a **Projects** surface, not the
+ daily list.
+- Pulling a step sets `committed_date = today` and leaves `due_date` alone.
+
+This also means "pausing" stops being load-bearing for noise control — a chain
+you're not working on simply never pushes anything at you, paused or not.
+
+### What buckets do
+
+Unchanged mechanically, but populated properly so the rotation is real: a pool
+substantially larger than `pick_n`. Active items appear in the review surface as
+pull candidates rather than pushing themselves onto the list. The duplicate
+free-standing "Yard work" task gets deleted.
+
+### Review
+
+One screen, the pull surface. Sections: **Projects** (next step per active
+chain) · **Bucket active** · **Undated backlog** · **Due soon**. Each row has a
+"+ Today" action. Intended to take well under a minute.
+
+Note this formalizes something already happening by hand — "Review week plan"
+and "Sailing Readiness Review" currently exist as literal tasks.
+
+### Carry-over
+
+A committed task not finished by end of day stays visible, flagged as carried,
+rather than silently vanishing or silently accumulating. Un-committing drops it
+back to the backlog. Carry-over count is the honest signal for over-committing.
+
+## Migration
+
+- Add `committed_date` (nullable) to `native_tasks`. New numbered migration —
+ never renumber existing ones.
+- Existing chain steps: clear the auto-stamped `due_date` on unlocked steps that
+ were never hand-set. Ambiguous cases stay as-is; they surface in the first
+ review and get triaged by hand.
+- Existing undated free-standing tasks: unchanged, land in backlog.
+- Existing dated free-standing tasks: unchanged, still push. Most are real
+ deadlines already.
+
+## Open questions
+
+- Should a bucket's active items auto-commit at cycle start, or always be
+ pulled? Leaning pulled, for consistency with the one rule.
+- Does the widget mirror TODAY exactly? Probably yes — the point is one list.