I also looked into this and it seems that this bug is related only to pages 0 and 1.
The selection queries are something like:
SELECT ... LIMIT 30; -- Query A (page 0)
SELECT ... LIMIT 30 OFFSET 29; -- Query B (page 1)
SELECT ... LIMIT 30 OFFSET 59; -- Query C (page 2)
Query A is going to select first 30 rows (1-30); query B is going to select 30 rows, but would skip the first 29 (30-59 – this is why row 30 is duplicated); and query C is going to select 30 rows, but would skip the first 59, so (60-89).
The fix would be to always skip page * 30. Currently the formula is page * 30 - 1.
Unfortunately, I do not know where exactly to look to fix this issue. I have been looking through the ListController, but failed. Any suggestions are appreciated.
Also, I believe that this will not affect the front-end, but I cannot say for sure.