Skip to content

Fix back-to-back meetings merging into one note + defer abandoned-meeting generation#16

Open
nickybmon wants to merge 2 commits into
mainfrom
fix/back-to-back-boundary-expiry
Open

Fix back-to-back meetings merging into one note + defer abandoned-meeting generation#16
nickybmon wants to merge 2 commits into
mainfrom
fix/back-to-back-boundary-expiry

Conversation

@nickybmon

Copy link
Copy Markdown
Owner

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 zero upgrade_from_empty events ever — confirmed the "upgrade from empty id" path was never firing.

Root cause (commit 1)

After each meeting the engine records _last_completed_boundary so 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_id filters the new meeting out and returns None every 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

  • Stamp the boundary with a wall-clock set time; 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.
  • Emit an active_empty_no_detection diagnostic so this stuck state is visible in logs going forward.
  • TestBoundaryExpiry regression 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.

nickybmon added 2 commits July 2, 2026 13:56
…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.
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.

1 participant