morning: recite the day the store actually holds

Four defects in the plan, all of them in what it reads or how it prints
it. The checklist line was keyed on Status.Active, which Evaluate reports
only inside the window, so a morning routine skipped and asked about at
14:00 said nothing. Outstanding answers the question the plan asks, "what
did today still not get done", and the line stays placed at the nudge
time so it sorts to the top of the day. Nothing before the window opens
counts, so 06:00 is not a complaint.

The event text kept the "@ 14:00-14:30" tail FactValue writes, next to a
line that prints the hour itself, so every event said its time twice.
Reminders came off ListReminders, which orders by creation, so the 500
row cap dropped a reminder stated long ago for today and kept one stated
this morning for next year. PendingReminders bounds by fire time instead.
The pending filter used a string literal, one typo from matching nothing.

After now marks the plan it trimmed. "что дальше?" past the last item
answered "на 03.08.2026 ничего не запланировано", which denies a day he
just lived through.

The surface the plan belongs on is still open, tracked as Vikunja #431;
the comment in actions_query.go points at it.
Found in review of #58.
This commit is contained in:
kami
2026-08-01 14:07:43 +04:00
parent 38b09ded95
commit b3c2fad4ec
7 changed files with 234 additions and 27 deletions
+8 -9
View File
@@ -19,6 +19,7 @@ import (
"sync"
"time"
"github.com/kami/maven/internal/calendar"
"github.com/kami/maven/internal/config"
"github.com/kami/maven/internal/delivery"
"github.com/kami/maven/internal/ipc"
@@ -808,8 +809,10 @@ func (t *tickLoop) dayPlan(ctx context.Context, now time.Time) ipc.DayPlan {
}
for _, f := range facts {
events = append(events, morning.PlanEntry{
At: f.Ts,
Text: f.Value,
At: f.Ts,
// The plan prints the hour itself, so the "@ 14:00-14:30" tail the
// fact value carries would say it twice.
Text: calendar.FactSummary(f.Value),
Kind: morning.PlanEvent,
// Provenance below a calendar read (an ambient relay, #126) is
// hedged rather than recited as fact.
@@ -818,12 +821,12 @@ func (t *tickLoop) dayPlan(ctx context.Context, now time.Time) ipc.DayPlan {
}
var reminders []morning.PlanEntry
rems, err := t.store.ListReminders(ctx, dayPlanMaxReminders)
rems, err := t.store.PendingReminders(ctx, dayStart, dayEnd)
if err != nil {
log.Printf("tick: day plan: list reminders: %v", err)
log.Printf("tick: day plan: pending reminders: %v", err)
}
for _, r := range rems {
if r.Status != "pending" {
if r.Status != store.ReminderPending {
continue
}
fire := r.NextFireTs
@@ -856,10 +859,6 @@ func (t *tickLoop) dayPlan(ctx context.Context, now time.Time) ipc.DayPlan {
return out
}
// dayPlanMaxReminders bounds the reminder scan. The plan covers one day; a
// pending queue longer than this is a bug elsewhere, not a plan to recite.
const dayPlanMaxReminders = 500
// tune — the feedback auto-tuner's impure step. runs on a slow cadence
// (autotuneInterval, see run) so it doesn't write a fact every tick. for each
// rule: