Add background timeline rebuild task (mirroring stats cache pattern)
that refreshes denormalized timeline event snapshots when entities are
updated. Includes cascade logic so editing a roaster refreshes timeline
events for its roasts, bags, brews, and cups.
- Add update_by_entity/delete_by_entity/delete_all to TimelineEventRepository
- Add TimelineInvalidator with debounced background rebuild task
- Add invalidate() calls to all 7 entity update handlers
- Add delete_by_entity cleanup to define_delete_handler! macro
- Add gear_id filter to BrewFilter for cascade traversal
- Add `brewlog timeline rebuild` CLI command for full rebuild
- Add 5 integration tests for timeline sync behavior
Cover text fields, numeric fields, +/- adjuster buttons, searchable
selects, image upload/replace/remove, deferred image save, and the
cancel button across roaster, gear, cafe, brew, and cup entities.
Add wait_for_url_not_contains helper to correctly detect post-submit
redirects when the edit page URL shares a prefix with the detail page.
Cover tab switching, search, pagination, and sorting on the data list
page. Verify detail page rendering for roasters, roasts, bags, gear,
and cafes. Test the close-bag flow from the detail page. Check that
home, stats, and timeline pages load correctly with data.
BrowserSession wraps headless Chrome via thirtyfour. Auth helper injects
session cookies to bypass WebAuthn. Wait helpers handle Datastar's async
DOM updates (visibility, text, URL). Form helpers find visible elements
to avoid hidden duplicates on tabbed pages. Chromedriver is auto-spawned
on first use via a dedicated parked thread with PR_SET_PDEATHSIG so the
kernel kills it when the test binary exits.