diff --git a/offline-backend-20260512/backend/packages/harness/deerflow/config/skill_evolution_runtime.py b/offline-backend-20260512/backend/packages/harness/deerflow/config/skill_evolution_runtime.py index 093f306f..9d99c0a7 100644 --- a/offline-backend-20260512/backend/packages/harness/deerflow/config/skill_evolution_runtime.py +++ b/offline-backend-20260512/backend/packages/harness/deerflow/config/skill_evolution_runtime.py @@ -135,8 +135,18 @@ def resolve_user_path(user_id: str) -> Path: return get_paths().user_dir(user_id) / _FILENAME +# Only these curator sub-fields may be overridden per user. +# All other fields (enabled, model_name, timeouts, etc.) are global-only. +_USER_ALLOWED_CURATOR_KEYS = {"interval_hours", "stale_after_days", "archive_after_days"} + + def load_user_overrides(user_id: str) -> dict[str, Any] | None: - """Read the per-user overrides file for ``user_id`` (None if missing/invalid).""" + """Read the per-user overrides file for ``user_id`` (None if missing/invalid). + + Only the three curator threshold keys are honoured; any other keys + (e.g. legacy ``enabled`` or ``model_name``) are silently stripped so that + old files written by previous versions cannot override global-only settings. + """ path = resolve_user_path(user_id) if not path.exists(): return None @@ -154,7 +164,14 @@ def load_user_overrides(user_id: str) -> dict[str, Any] | None: if not isinstance(data, dict): logger.warning("User skill_evolution overrides at %s must be a JSON object", path) return None - return data + # Strip top-level keys that are not user-configurable (e.g. legacy "enabled"). + curator_raw = data.get("curator") + if not isinstance(curator_raw, dict): + return None + filtered_curator = {k: v for k, v in curator_raw.items() if k in _USER_ALLOWED_CURATOR_KEYS} + if not filtered_curator: + return None + return {"curator": filtered_curator} def save_user_overrides(user_id: str, payload: dict[str, Any]) -> Path: