fix(ongo-poll): honor Slack has_more=true as truncation#31
Conversation
The truncation detector only fired when `len(msgs) >= 200`, but Slack's `conversations.history(oldest=T, limit=200)` can return far fewer than 200 messages with `has_more=true` (server-side page slicing / tier-3 throttling). When that happens, ongo-poll returned `status="ok", user_count=0`, and the loop marked the channel drained while a queued user message sat one page forward. Also corrects the bug-4 commentary: empirically with `oldest=T, latest=unset, inclusive=true`, Slack returns the OLDEST page first (ascending from T) and sets `has_more=true` if newer messages remain. The original commentary had this backwards (claimed newest-first, oldest dropped). Failure mode was therefore reversed: the NEWEST unprocessed user messages vanished, not the oldest. Patch: - Plumb `has_more` out of `_read_once`. - Trip the truncated branch on `len >= LIM` OR `has_more==true`. - Safe re-poll anchor is now `newest_returned_ts` (advance forward) so the next poll's `--since` picks up the missing newer slice. The `> last_user_ts` strict filter prevents duplicate user-message emission across pages. Verified end-to-end against a real channel where a missed user msg sat 10ks past the last cursor: page 1 -> status=truncated, anchor advances; page 2 -> status=ok, user_count=1, message surfaced. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…p-check) The has_more truncation fix in ongo-poll is a non-docs change; the version-bump-check workflow requires the SKILL.md frontmatter version to advance above main. Merged current main (0.3.14) into the branch and bumped to 0.3.15. No behavior change in this commit; the poll fix (gate truncation on has_more OR len>=LIM, advance cursor forward) is unchanged and verified. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ContextWhat was broken. Empirical Slack behavior ( The fix. Trip the This push. Merged current
— cryptid (Ergodic Labs), via the moorkh agent fork |
Summary
len(msgs) >= 200; Slack can short-page (e.g. 15 returned withhas_more=true), so the gate stayed closed and the loop reportedstatus=ok, user_count=0with a real user msg sitting one page forward.conversations.history(oldest=T, latest=unset)returns the oldest page first (ascending fromT), andhas_more=truemeans newer pages remain — original bug-4 commentary had this reversed.len >= LIMorhas_more==trueand advances the safe-anchor forward tonewest_returned_ts; the> last_user_tsstrict filter dedupes user messages across pages.Reproduction (this session)
With
last_user_ts=1779206832.557039and one queued user message at1779225932.196819:{status:"ok", total_seen:15, user_count:0}(15/200, has_more=true) → loop deaf.{status:"truncated", total_seen:15, newest_user_ts:"1779215569.382309"}.{status:"ok", total_seen:11, user_count:1}— message surfaces.Test plan
last_user_tslagging a multi-page window; confirm two-poll drain (truncated → ok) and that the queued user msg is emitted exactly once.status=="error"(ratelimited / SlackApiError) paths still fire; the_read_oncereturn arity changed (3-tuple), nothing else touches it.has_more==false.🤖 Generated with Claude Code