#106 — BatchController now paginates by id cursor (WHERE c.id > :lastId)
instead of always querying offset 0. A row that fails to insert falls behind
the cursor and is not re-fetched, so the batch always terminates and reaches
100% even with persistent failures. process() returns examined + last_id; the
editor JS drives the cursor and stops when a chunk examines 0 rows.
#100 — Sitemap:
- Throttle: regenerate at most once per 60s on content save (SITEMAP_MIN_INTERVAL)
so bulk edits/imports don't rebuild the whole file every save
- SEF URLs: route each article via Route::link('site', ...) with a fallback to
the non-SEF index.php URL if routing fails (worst case = prior behavior)
(access-level filtering + atomic write were done earlier in the cycle)