Problem
The dotfiles Stop-hook drain (drain-directed-events.sh) uses a peek-then-bounded-consume pattern: peek N events non-consumingly, surface them, then consume with --limit N to advance the cursor past exactly what was surfaced. This only works because its event-type filtering (--exclude) is client-side — the server's --limit and the peeked count refer to the same raw event window.
Server-side min_level (#129) breaks that contract: a min_level read returns the filtered view of a raw batch, but the cursor advances over the raw batch. A consume bounded to the filtered count would advance past a different raw window than the peek saw — risking events that are consumed but never surfaced. This is why the drain path could not migrate off its client-side denylist in evansenter/dotfiles#328, defeating part of #129's purpose (one canonical noise policy).
Proposed fix
An explicit ack: let a session advance its cursor to a specific event id it already holds.
ack_events(session_id, cursor) # sets last_cursor = cursor
- The peek response already returns
next_cursor = the raw-batch high-water mark, so the drain flow becomes: peek (min_level, non-consuming) → surface → ack_events(session_id, peek.next_cursor). Peek and ack then refer to the same raw window by construction, closing the race the bounded consume was engineered around.
- Implementation is small:
storage.update_session_cursor already exists; this exposes it as a tool + CLI command (ack --cursor N), with validation that the session exists. Guard against moving the cursor backwards (or document that it's allowed for deliberate replay).
- With this in place,
eventbus-collect.sh can drop EB_EXCLUDE entirely and use --min-level everywhere.
Context
Follow-up from #129 / #132; the dotfiles-side workaround and a fuller explanation live in evansenter/dotfiles#328 (see the comment above EB_EXCLUDE in eventbus-collect.sh).
Problem
The dotfiles Stop-hook drain (
drain-directed-events.sh) uses a peek-then-bounded-consume pattern: peek N events non-consumingly, surface them, then consume with--limit Nto advance the cursor past exactly what was surfaced. This only works because its event-type filtering (--exclude) is client-side — the server's--limitand the peeked count refer to the same raw event window.Server-side
min_level(#129) breaks that contract: amin_levelread returns the filtered view of a raw batch, but the cursor advances over the raw batch. A consume bounded to the filtered count would advance past a different raw window than the peek saw — risking events that are consumed but never surfaced. This is why the drain path could not migrate off its client-side denylist in evansenter/dotfiles#328, defeating part of #129's purpose (one canonical noise policy).Proposed fix
An explicit ack: let a session advance its cursor to a specific event id it already holds.
next_cursor= the raw-batch high-water mark, so the drain flow becomes: peek (min_level, non-consuming) → surface →ack_events(session_id, peek.next_cursor). Peek and ack then refer to the same raw window by construction, closing the race the bounded consume was engineered around.storage.update_session_cursoralready exists; this exposes it as a tool + CLI command (ack --cursor N), with validation that the session exists. Guard against moving the cursor backwards (or document that it's allowed for deliberate replay).eventbus-collect.shcan dropEB_EXCLUDEentirely and use--min-leveleverywhere.Context
Follow-up from #129 / #132; the dotfiles-side workaround and a fuller explanation live in evansenter/dotfiles#328 (see the comment above
EB_EXCLUDEineventbus-collect.sh).