FileUtils.is_cloud_placeholder+FileUtils.free_space— cloud/space-aware primitives for diagnosing unreadable files (2026-06-27). Two zero-dependency, Windows-aware helpers.is_cloud_placeholder(path)returns True for a “dehydrated” cloud placeholder (OneDrive / Dropbox / Google Drive / Nextcloud) — it satisfiesos.path.isfile/os.stat(size/mtime from placeholder metadata) but its bytes live in the cloud; reads the Windowsst_file_attributesbitmask (OFFLINE / RECALL_ON_OPEN / RECALL_ON_DATA_ACCESS), never triggers a recall, False on non-Windows or any stat failure. It is detection, not diagnosis: a recall usually succeeds on demand, so a True result only means “cloud-managed, the sync client is relevant” — it does not imply the file is unreadable.free_space(path)returns the free bytes on the volume holdingpath(resolving up to the nearest existing ancestor, so it works on a file, a dir, or a not-yet-created child) viashutil.disk_usage, or None when no ancestor resolves; reads volume metadata only (safe on a placeholder). Together they turn an opaqueOSErrorinto a checkable cause — a (nearly) full volume, a common real reason anopen()fails afterisfilepassed (a cloud file can’t land bytes with no room) — rather than guessing the placeholder state itself. Motivated by mayatk’s Shot Manifest, which previously mis-reported a stranded Dropbox CSV with confidently-wrong advice (“make it available offline”) when the real cause was a full partition (see mayatk CHANGELOG 2026-06-27). Tests:test_file.py::FileTest(+8: placeholder online-only / legacy-OFFLINE → True, local / POSIX-no-field / missing → False; free_space int for an existing dir, resolves a nonexistent child, None when unresolvable).LoggingMixin—setLevelnow re-enables custom levels after a level change (2026-06-27). Fixed a latent bug where lowering a logger's level would not re-enable the custom levels (SUCCESS/RESULT/NOTICE). These loggers are built via theLogger()constructor (notgetLogger), so they are absent frommanager.loggerDict; the stdlibsetLevel'smanager._clear_cache()therefore never touched their per-loggerisEnabledForcache. OnceisEnabledFor(SUCCESS)was evaluated while the level was aboveSUCCESS(cachingFalse),success()and the other custom levels stayed silently suppressed even after the level was lowered again.LoggerExt._set_levelnow clearsself._cachedirectly. Surfaced by mayatk's scene_exporter promoting a per-task line toSUCCESS. Test:test_logging_mixin.py::LoggerExtTest::test_setlevel_clears_custom_level_cache; full module suite 55 green.