Commit 92c243a
fix(imessage): clamp catchup watermark to last dispatched row when truncating
clawsweeper review on PR #79387 caught a regression I introduced when
adding the parse-rejected high-watermark fix in 8c6421b: when the
cross-chat fetch returns more valid rows than `perRunLimit`, the
fetcher caps `capped = collected.slice(0, limit)` but `rawWatermarkRowid`
still reflects the highest rowid across ALL raw rows — including the
cap-truncated tail. The catchup loop applies that watermark as a
cursor floor, so the persisted cursor leapfrogs past valid-but-
undispatched rows. Same silent message-loss class as the original
codex finding, just on a different code path.
Concrete repro in `catchup-bridge.test.ts > caps cross-chat results at
perRunLimit, oldest first`: 8 valid rows (chat 1: rowids 100-103,
chat 2: rowids 200-203), perRunLimit=5. Dispatched: 100, 101, 102,
103, 200. Before this fix: cursor persisted at 203 (raw watermark of
chat 2's last row), so rowids 201, 202, 203 — explicitly promised by
the WARN log to be picked up on the next startup — were silently
dropped. After this fix: cursor stops at 200 (last dispatched rowid).
Fix: in `catchup-bridge.ts`, derive `effectiveWatermarkRowid` /
`effectiveWatermarkMs` from `rawWatermarkRowid` / `rawWatermarkMs`
clamped to `capped[capped.length-1]` when truncation hits. When no
truncation, the watermark still covers parse-rejected rows
interspersed with the dispatched batch (the original forward-progress
guarantee from 8c6421b). When `capped.length === 0` (pathological
limit=0 or all rows parse-rejected past the cap boundary), suppress
the watermark entirely so the cursor stays put.
Regression test: extended the existing "caps cross-chat results at
perRunLimit, oldest first" test to assert
`summary.cursorAfter.lastSeenRowid === 200` (the last dispatched
rowid), not 203 (the raw watermark).
Acceptance criteria from clawsweeper:
- pnpm vitest run extensions/imessage/src/monitor/catchup.test.ts
extensions/imessage/src/monitor/catchup-bridge.test.ts → 29/29 pass
- pnpm exec oxfmt --check --threads=1 ... → All matched files use
the correct format
- pnpm lint:extensions:bundled → 0 warnings, 0 errors
Also drops `[...collected].sort()` for `collected.toSorted()` in the
same block (oxlint no-array-sort, latent in the 8c6421b commit
because oxlint missed it on one path).
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>1 parent 4dbf956 commit 92c243a
3 files changed
Lines changed: 54 additions & 21 deletions
File tree
- extensions/imessage/src/monitor
- src/config
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
270 | 270 | | |
271 | 271 | | |
272 | 272 | | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
273 | 282 | | |
274 | 283 | | |
275 | 284 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
193 | 193 | | |
194 | 194 | | |
195 | 195 | | |
196 | | - | |
197 | | - | |
198 | | - | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
199 | 200 | | |
200 | | - | |
| 201 | + | |
201 | 202 | | |
202 | 203 | | |
203 | | - | |
204 | | - | |
205 | | - | |
206 | | - | |
| 204 | + | |
| 205 | + | |
207 | 206 | | |
208 | 207 | | |
209 | 208 | | |
| |||
212 | 211 | | |
213 | 212 | | |
214 | 213 | | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
215 | 237 | | |
216 | 238 | | |
217 | 239 | | |
218 | | - | |
219 | | - | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
220 | 244 | | |
221 | 245 | | |
222 | 246 | | |
| |||
0 commit comments