Skip to content

fix: ritual horizon reaches the end of its last day, not now's time-of-day - #415

Merged
davekilleen merged 3 commits into
davekilleen:mainfrom
davidorban:fix/ritual-horizon-end-of-day
Aug 12, 2026
Merged

fix: ritual horizon reaches the end of its last day, not now's time-of-day#415
davekilleen merged 3 commits into
davekilleen:mainfrom
davidorban:fix/ritual-horizon-end-of-day

Conversation

@davidorban

Copy link
Copy Markdown
Contributor

The bug

_upcoming_confirmed_occurrence_ids in core/ritual_intelligence/ritual_match.py builds its horizon as current + timedelta(days=days_until_end_of_week). That arithmetic preserves now's time-of-day on the target date rather than reaching the end of it.

On a Sunday days_until_end_of_week is 0, so the horizon collapses onto the current timestamp and every occurrence later that same day falls outside the window. Confirming a ritual on Sunday morning silently drops the Sunday evening session from prep generation. The same off-by-a-few-hours truncation clips the final day of every other weekday's window too; Sunday is just where it degenerates completely.

The lower bound is already normalised with datetime.combine(today, datetime.min.time(), ...). The upper bound was not.

The fix

Anchor the horizon to the end of the last day in the window:

horizon_end = datetime.combine(
    today + timedelta(days=days_until_end_of_week), datetime.max.time(), tzinfo=current.tzinfo
)

The Friday special case now extends days_until_end_of_week before that combine rather than building a second timestamp, so both branches go through one code path.

Verification

test_confirmed_ritual_on_sunday_includes_that_same_evening fails on current main with the exact symptom:

AssertionError: Sunday-evening occurrence was excluded by a horizon that stopped at Sunday morning

and passes with the fix. It uses relative dates so the events always fall inside the matcher's 28-day window. core/tests/test_ritual_match.py is 6 passed; the ritual-selected suite across core/tests/ is 19 passed.

Found and fixed downstream, where it shipped as davidorban/Dex#84.

🤖 Generated with Claude Code

@davidorban
davidorban requested a review from davekilleen as a code owner August 5, 2026 17:59
…f-day

_upcoming_confirmed_occurrence_ids built its horizon as `current + N days`,
which keeps now's time-of-day on that date. On a Sunday, N is zero, so the
horizon collapses onto the current timestamp and every later-that-day
occurrence falls outside it: confirming a ritual on Sunday morning drops the
Sunday evening session from prep generation.

The horizon is now anchored to the end of the last day in the window.

The added test fails on current main with the exact symptom and passes with
the fix.
@davekilleen
davekilleen force-pushed the fix/ritual-horizon-end-of-day branch from 286d4e3 to f26e88d Compare August 12, 2026 00:27
@davekilleen
davekilleen merged commit 513132f into davekilleen:main Aug 12, 2026
9 checks passed
davekilleen added a commit that referenced this pull request Aug 12, 2026
…elease (#490)

* changelog: one 1.95.1 section, so the release notes carry the whole release

main has TWO "## [1.95.1]" headers. The Release workflow extracts the body
between the first header and the next "## [", so I ran its exact sed against
main: it would publish 1384 bytes and three bullets under "Big vaults get ready
to update in seconds", and silently drop everything in the second section —
the end-of-session save that had been keeping nothing, first-party backups,
Pipedrive, roomier daily rituals, and draft-first publishing.

The data-loss fix that is the whole reason to take this update would have gone
unmentioned in its own release notes. This is the same duplicate-header trap
that would have published EMPTY notes for v1.95.0, this time landing where it
drops content rather than all of it.

Collapsed to one section. Every existing subsection is preserved verbatim,
including #482's measured prose and all three attributions to Chris; 30 bullets
before, 30 after. Only the headline changed, to lead with the data-loss fix
rather than a performance improvement.

Also written up: four pieces of user-visible work that were shipping in 1.95.1
with no notes at all —

- a missing component reported as a damaged settings file (#450, Amit Godbole,
  credited as a first-time reporter)
- Doctor's all-clear at the moment an update would refuse (#451, Amit Godbole)
- a ritual confirmed on Sunday morning losing that Sunday evening (#415)
- hook paths that left the dangerous-command guard unable to start (#369)
- and the documented first stop when something seems wrong (#432)

Swept against the CFO test: no "library", no "Python", no bare "auto-commit"
heading; the setting is named once, in context, for the people who enabled it.

* changelog: restore the approved note on why there is no 1.95.0

The sentence carried over from the previous section said this release 'was
renumbered from 1.95.0 to carry the fix', which is weaker and subtly wrong: the
renumber happened because 1.95.0's files were built from the wrong snapshot and
were withdrawn, not to carry any fix. Replaced with the wording approved for
this purpose, placed as the section's closing note — immediately above the 1.94.0
heading, which is where a reader notices the gap and asks.

* changelog: the version note says 'in this section', not 'below'

My own choice to place the note at the 1.95.1 -> 1.94.0 seam broke the word:
from there, 'below' points at 1.94.0 rather than at the fixes it refers to.
'In this section' is position-proof, so it survives the note being moved again.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants