Fix back-to-back meetings merging into one note + defer abandoned-meeting generation#16
Open
nickybmon wants to merge 2 commits into
Open
Fix back-to-back meetings merging into one note + defer abandoned-meeting generation#16nickybmon wants to merge 2 commits into
nickybmon wants to merge 2 commits into
Conversation
…ection The engine records a _last_completed_boundary after each meeting so the just-ended meeting's lingering WAL data can't be re-detected as the next active meeting. That marker carried a date-less seconds-since-midnight freshness floor and was only cleared once a concrete meeting id was tracked. Because the app runs 24/7 as a menu-bar process, a boundary set by an earlier/previous-day meeting survives into a later session. When its clock time is numerically above a new meeting's timestamps (e.g. yesterday 5pm vs this morning 9am), detect_active_meeting_id filters the new meeting out and returns None on every tick. Tracking never upgrades from empty, the boundary never clears, and back-to-back meetings pile into one accumulator until the user manually generates. Confirmed against the 2026-07-02 Brand Studio Crit logs (70 min of ACTIVE with empty tracking; active_id_self_healed from ""). Fix: stamp the boundary with a wall-clock set time and expire it after _BOUNDARY_EXPIRY_SECS (1h) via a new _active_boundary() accessor used at both detection read sites. 1h is far longer than the marker's real few-minute job yet guarantees no cross-session/overnight bleed. Also emit an active_empty_no_detection diag so the stuck state is visible in logs. Adds TestBoundaryExpiry regression tests (verified failing pre-fix).
…roves real
Reworks the Case B abandoned-meeting path so it no longer fires a note the
instant the tracked meeting_id changes. A single meeting whose id oscillates
between two values would otherwise generate a premature note on every flip.
Instead the abandoned snapshot is parked in _pending_abandoned and resolved
on later ticks:
- _maybe_fire_pending_abandoned: once the meeting we switched TO accumulates
its own real content, the switch is a genuine back-to-back boundary and
the abandoned note fires.
- flap-back guard: if the score swings back to the parked id first, it was a
single flapping meeting — restore it and generate nothing.
- _flush_pending_abandoned: on idle / acc-stale generation, finalize any
still-parked note so it isn't stranded.
Extends tests/test_abandoned_meeting_generation.py to cover the deferral,
flap-back restore, and flush paths.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Back-to-back meetings have repeatedly merged into a single note. Traced end to end against the 2026-07-02 Brand Studio Crit logs: the engine ran ACTIVE for 70 minutes with an empty tracked meeting_id (both the 9–10am meeting and the 10am call landed in one accumulator) until the user manually generated.
active_id_self_healed from ""in the logs — and zeroupgrade_from_emptyevents ever — confirmed the "upgrade from empty id" path was never firing.Root cause (commit 1)
After each meeting the engine records
_last_completed_boundaryso the just-ended meeting's lingering WAL data isn't re-detected as the next active meeting. That marker carried a date-less seconds-since-midnight freshness floor and was only cleared once a concrete id was tracked.Because the app runs 24/7 as a menu-bar process, a boundary set by an earlier / previous-day meeting survives into a later session. When its clock time is numerically above a new meeting's timestamps (yesterday 5pm vs this morning 9am),
detect_active_meeting_idfilters the new meeting out and returnsNoneevery tick. Tracking never upgrades from empty → the boundary never clears → deadlock, and every back-to-back meeting piles into one bucket.Proven directly: feeding the real detection code a stale "later in the day" floor makes it return
None; removing it restores correct detection.Fix
_BOUNDARY_EXPIRY_SECS(1h) via a new_active_boundary()accessor used at both detection read sites. 1h is far longer than the marker's real few-minute job yet guarantees no cross-session/overnight bleed.active_empty_no_detectiondiagnostic so this stuck state is visible in logs going forward.TestBoundaryExpiryregression tests — verified failing pre-fix, passing post-fix.Also included (commit 2)
Pre-existing WIP to defer Case B abandoned-meeting note generation until the meeting switched-to proves real (with a flap-back guard for a single meeting whose id oscillates). Targets a different failure mode than commit 1; bundled here at the author's request. Tests extended accordingly.
Testing
Full suite: 139 passed, 23 skipped. The two commits are independent and coexist cleanly (all boundary access goes through the new 3-tuple accessor).
Caveat
Commit 1 fixes the specific failure in today's logs. It may not be the only path to a merged-notes bug; the new diagnostic will surface any recurrence quickly.