diff --git a/api/models.py b/api/models.py index 702fc6993..5e53e796f 100644 --- a/api/models.py +++ b/api/models.py @@ -3133,25 +3133,33 @@ def _cached_session_lags_disk(cached) -> bool: disk_scenes = disk_meta_quick.get('anchor_activity_scenes') or {} if not isinstance(disk_scenes, dict): disk_scenes = {} - if not disk_scenes or set(disk_scenes.keys()) != set(cached_scenes.keys()): - # Scene key set differs — disk has been updated with new - # keys the cache hasn't seen. - return True - # Same key set: check the latest updated_at timestamp. - def _max_updated(records): - latest = 0.0 - for record in records.values(): - if not isinstance(record, dict): - continue - try: - ua = float(record.get('updated_at') or 0) - except (TypeError, ValueError): - ua = 0.0 - if ua > latest: - latest = ua - return latest - if _max_updated(disk_scenes) > _max_updated(cached_scenes): - return True + if disk_scenes: + # Directional: only reload when disk is strictly ahead + # of cache. Mirror master's subset comparison — cache + # that is ahead of disk must NOT force a reload, or + # un-persisted scene data is silently dropped. + disk_keys = {str(key) for key, value in disk_scenes.items() + if key and isinstance(value, dict)} + cached_keys = _anchor_scene_record_keys(cached) + if disk_keys and not disk_keys.issubset(cached_keys): + return True + # Same key set (or disk is subset): check the latest + # updated_at timestamp. + def _max_updated(records): + latest = 0.0 + for record in records.values(): + if not isinstance(record, dict): + continue + try: + ua = float(record.get('updated_at') or 0) + except (TypeError, ValueError): + ua = 0.0 + if ua > latest: + latest = ua + return latest + if _max_updated(disk_scenes) > _anchor_scene_records_updated_at(cached): + return True + # disk has no scenes, cache does -> cache is ahead; keep it. else: # Cached session has no scene records. Check if disk has gained # the first scene record — without this the fast-path would miss diff --git a/api/routes.py b/api/routes.py index 53f2dbafc..4bab6cf68 100644 --- a/api/routes.py +++ b/api/routes.py @@ -6333,6 +6333,13 @@ def _read_profile_config_cached(profile_name: str, cfg_path: str) -> dict | None in-place rewrites where inode+mtime+size are identical on coarse filesystems — most editors use atomic-rename (new inode), but tools like sed -i or file.write() in-place can keep the same inode. + + NOTE: This is a best-effort backstop, not a full integrity check. + Edits that change bytes only after the first 128 characters while + keeping inode+mtime+size identical are not detected until the TTL + expires. This trade-off is acceptable because the TTL is only 60s + and the common case (atomic-rename editors) is fully handled by + the inode key component. """ try: st = os.stat(cfg_path)