mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-11-02 09:23:59 +00:00
b721dfb74d
* feat(store): date-window item filter and one shared visibility clause (TASK-4104 D1) ItemListParams.FieldDateWindow keeps items where any named date field falls on a day in a window, on both stored date shapes and both dialects. The pending-reminder visibility switch becomes scopeClause so the agenda reads the same predicate. Claude-Session: https://claude.ai/code/session_01Pa22hHpfXoZbQkEFvPtNeS * feat(schedule): workspace agenda endpoint (TASK-4104 D1) GET /workspaces/{ws}/agenda?from&to&tz lists the schedules' occurrences with their state and the items dated in the window, in the caller's zone, under the dashboard's visibility applied in SQL. A per-schedule cap degrades that schedule to truncated or unavailable instead of failing the read. History rows merge by wall for floating and all-day schedules, by instant for pinned timed ones. Claude-Session: https://claude.ai/code/session_01Pa22hHpfXoZbQkEFvPtNeS * test(schedule): classify the agenda in the compress census and the lifecycle guard (TASK-4104 D1) Claude-Session: https://claude.ai/code/session_01Pa22hHpfXoZbQkEFvPtNeS * fix(schedule): agenda scan skips ended schedules, one bad schedule is unavailable, empty tz is refused (TASK-4104 D1 r1-1, r1-2, r1-5) - r1-1: the 1000-schedule scan bound no longer counts schedules that ended before from (ended_at IS NULL OR ended_at >= from). - r1-2: a non-cap error from one schedule's expansion is logged by schedule id and error class only and listed under unavailable with code internal; the agenda stays 200. - r1-5: an explicit empty or blank tz is 400 invalid_tz. - Tests: scan starvation, corrupt schedule, empty tz, the 100 and 500 cuts, an app-token ceiling vs the item list, a pinned HOURLY and a floating missed count across New York's 2026-11-01 fall-back, a stronger runaway row. - Docs: date-entry selection and at, the cut before date ordering, accepted deviations. Claude-Session: https://claude.ai/code/session_01Pa22hHpfXoZbQkEFvPtNeS * fix(schedule): agenda lists an early-resolved schedule, store classifies expansion errors (TASK-4104 D1 r2-1, r2-2) r2-1: an ended schedule stays in the agenda scan while a history row can reach [from-36h, to+36h), so a deadline marked done early still lists as done; one whose history is wholly outside the window still spends no bound slot. ORDER BY is qualified: the unnamed reminder subselects also output an "id" column, which Postgres refuses as ambiguous. r2-2: ExpandAgenda reports unparseable stored state as unavailable "internal" with a nil error and returns an error only for a database failure; the handler answers that with a 500 again. The 1.5 s budget is checked between schedules and never reaches that branch. r2-3: the API docs state the three behaviours under a plain label. r2-4: the runaway test proves the runaway's own ref and the healthy refs. Claude-Session: https://claude.ai/code/session_01Pa22hHpfXoZbQkEFvPtNeS * test(schedule): the agenda's DB-failure row breaks only the history read; the runaway row pins its one outcome (TASK-4104 D1 r4-2, r4-4) TestAgendaDatabaseFailureDuringExpansionIs500 dropped schedule_occurrences, which since r2-1 also fails the list query's EXISTS, so it 500ed before ExpandAgenda and could not catch a handler that swallowed the error. It now renames a column only agendaOccurrences selects. A mutant that swallows the error as unavailable=internal turns it red. TestAgendaOneRunawayScheduleDoesNotTakeItDown accepted either outcome. A MINUTELY rule over the window hits the instance cap with partial occurrences, cut to the per-schedule cap: exactly 100 entries, truncated. It pins that; AgendaMaxPerSchedule 100 -> 99 turns it red. Claude-Session: https://claude.ai/code/session_01Pa22hHpfXoZbQkEFvPtNeS