VFS now loads on first has()/getMtime() call rather than requiring
explicit initialization in sync/index.mjs. This ensures it works
regardless of entry point (sync:download, splatnet:quick, etc.).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
exists() and olderThan() now check the local filesystem first and only
consult the VFS as a fallback. This means freshly written files are
found naturally without callers needing to register them.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Uses S3 ListObjectsV2 to build an in-memory file listing at startup,
allowing exists() and olderThan() to resolve from the listing instead
of requiring files on disk. sync:download now skips assets/splatnet/,
data/xrank/, data/festivals.ranking.*, and status-screenshots/.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Added mkdirp() helper to app/common/fs.mjs that wraps
fs.mkdir(dir, { recursive: true }). Updated all call sites to use it.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>