The last-status, GitHub cache, notification dismissal and model catalog stores each grew their
own legacy-file import, read-only startup read, changed-row writer and retry backoff. They now
share src/main/sqlite/row-cache/ (KeyedJsonRowCache with load/write, the legacy JSON source,
one failure classifier and backoff); each store keeps only its cadence and fencing policy.
Edge cases now behave the same in every store:
- a corrupt or rejected older-build file keeps the stored rows (it no longer wipes them);
- an older-build file that exists but cannot be read is never treated as absent;
- the import receipt is the file's content hash, without its mtime, so a copied leftover is
still recognised as imported;
- while the rows or that file cannot be read, the store saves nothing that session and never
deletes a file it did not load (dismissals keep retrying, as before);
- files are user-only and hardened everywhere.
The last-status and GitHub caches move to the shared row layout, so each rebuilds empty once.