Phase 1 of #1045 (optional centralized sidecar storage): introduce
py/utils/sidecar_paths.py as the single place that resolves .metadata.json
and preview locations, and replace all inline splitext-based derivations
across scanners, services, download manager, and route handlers.
No behavior change: the default 'alongside' storage mode resolves every
path exactly as before. .civitai.info (third-party sidecar) derivation is
intentionally left co-located.
- Three-state autov3 field (not-checked / checked-unavailable / 12-hex value)
in .metadata.json sidecars, in-memory ModelHashIndex, and SQLite
(models.autov3 column + autov3_index table) with column-presence migration
- Background self-terminating backfill for legacy rows: per-model-type
concurrency guard, executor-offloaded I/O, Civitai-first resolution
(SHA256-matched version file) falling back to the embedded safetensors
header hash
- Civitai-first propagation on metadata refresh, scan, and download paths;
reject the empty-string SHA256 placeholder and strip OneTrainer 0x prefix
- List API hash filters and hash index lookups accept 12-char AutoV3
- Cap safetensors header reads at 64 MiB to prevent crafted-file allocation
- Prevent stale AutoV3 mappings on file replacement while preserving them on
same-file re-registration (lazy-hash completion)