Show only the day's docket on the Today board and add ad hoc Now workouts

- Due-filter schedules: daily always, fixed days by weekday, one-offs on
  their date; rest days get an empty state
- Workouts with no due schedule row (ad hoc or off-day starts) now render
  as their own board rows
- The + sheet is now "New Workout" with a "When" picker; "Now" (offered
  when adding on today) skips the schedule and starts the workout directly
- Remove the times-per-week scheduling mode everywhere (enum, document,
  entity, mappers, planner, seeds, tests)
This commit is contained in:
2026-07-11 11:09:11 -04:00
parent 9dc9283b4c
commit 7cb2d6da26
14 changed files with 231 additions and 199 deletions
+11 -23
View File
@@ -52,18 +52,17 @@ A schedule = **one routine + a recurrence + an optional goal tag**, persisted
as `Schedules/<ULID>.json`. Recurrence flavors:
1. **Fixed days** — "every Mon & Thu". Deterministic; the board just shows it.
2. **Frequency**"2×/week, whenever". The planner picks the days.
3. **Daily** — e.g. the morning routine or evening meditation.
2. **Daily**e.g. the morning routine or evening meditation.
(A one-off `.once` recurrence also exists for a single-day plan; a "times per
week, whenever" flexible mode was tried and removed — it added planner
complexity nobody used, and fixed days cover the common case just as well.)
Plus an optional **time window** (e.g. morning, default cutoff ~11 am,
tunable) with a per-schedule setting for whether the window gates the streak:
on = only in-window starts feed the streak, though the row stays startable and
completable all day; off = any-time completion counts.
**Pool rotation is emergent, not a feature**: three 1×/week frequency
schedules (Legs, Upper, Core) under Strength rotate naturally via the
planner's recency pressure. Schedules stay minimal.
**Missed fixed days get a catch-up nudge**: the schedule reappears next day as
due-with-a-reason ("missed Mon · catch up?"), planner-placed on the best
make-up day before the next fixed slot (recovery-aware in Phase 2), with an
@@ -105,19 +104,15 @@ Thursday, July 10
│ ✓ Mobility ·7:04· [Morning] 🔥12│ done — stays in place, streak ticked
│ ● Lower Body [Strength] │ fixed schedule, due today
│ Mon & Thu · ▶ Start │
│ · Zone-2 Ride [Endurance] │ flexible — ambient status row
│ 1 of 2 this week · 3 days left │
└──────────────────────────────────┘
This week ▪▪▪▫▫▫▫ + one encouraging line
```
- **Row states**: *due* (start button, streak at stake — fixed schedules on
their day, flexible ones when the planner says so, catch-ups after a miss) →
*in progress* (resume affordance, always wins attention) → *done* (✓, time,
bumped streak). Done rows stay in place — the agenda's shape is stable and
the day visibly fills up. Flexible schedules not due today render as
**ambient** compact rows (week progress, on-pace, next suggested day),
tappable to start anyway. Never blocked, just not shouting.
- **Row states**: *due* (start button, streak at stake — fixed/daily
schedules on their day, catch-ups after a miss) → *in progress* (resume
affordance, always wins attention) → *done* (✓, time, bumped streak). Done
rows stay in place — the agenda's shape is stable and the day visibly fills
up.
- **Due rows carry reason chips** from the planner ("missed Mon", "5 days
since legs · you're usually here Thursdays") — never a black box. An
override is a non-event and becomes signal.
@@ -135,12 +130,8 @@ This week ▪▪▪▫▫▫▫ + one encouraging line
A **pure, unit-testable scorer** (precedent: `SeedReconcilePlanner`). With
explicit schedules, its scope narrows to where inference is genuinely needed:
- **Placing flexible (frequency) schedules** — which of the remaining days
this week, driven by recency pressure vs. days left.
- **Catch-up placement** — the best make-up day for a missed fixed slot
before its next occurrence.
- **Ordering emergent rotations** — several flexible schedules under one goal
rotate via recency pressure.
- **(Phase 2) Recovery guard** — overlap between a routine's target muscles
and muscles trained in the last ~48 h downweights it, using the bundled
library's `Targets:` metadata (already parsed by `ExerciseInfo`). Because it
@@ -149,10 +140,7 @@ explicit schedules, its scope narrows to where inference is genuinely needed:
- **(Phase 2) Location affinity** — boost routines historically done at the
current place; missing/denied location contributes nothing.
Weekday affinity remains a minor learned signal for flexible schedules.
**Cold start:** with little history, flexible placement falls back to even
spacing and says so. Deterministic, hand-tuned weights, fixture-history unit
tests.
Deterministic, hand-tuned weights, fixture-history unit tests.
### Location capture (Phase 2)