Previously, handleJetstreamEvent saved the time-based cursor after
every event regardless of whether applyCommit succeeded. That is fine
for permanently bad records (malformed JSON, schema violations) where
replaying achieves nothing, but wrong for transient infra failures
(SQLite busy, store closed during shutdown, disk full): the cursor
would advance past a perfectly good event and silently drop the
membership or repo row that backs it, with no way to recover short of
a manual replay.
applyCommit now distinguishes the two classes via a new
badRecordError wrapper. JSON decode failures in applySpindleMember,
applyRepo, and applyRepoCollaborator are wrapped with
badRecord(...) so they remain cursor-advancing. Everything else
returned from applyCommit is treated as transient:
handleJetstreamEvent logs it, returns the error to the scheduler, and
skips SaveCursor so the next reconnect (which already rewinds by
jetstreamRewind) will redeliver and retry.