From 603d667608a5b6e25892f9e4980141c9239cfd90 Mon Sep 17 00:00:00 2001 From: xuhd Date: Wed, 3 Jun 2026 18:06:21 +0800 Subject: [PATCH] fix(skill-evolution): strip non-user-configurable fields from per-user overrides MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit load_user_overrides 只保留 interval_hours/stale_after_days/archive_after_days, 过滤掉老文件中的 enabled、model_name 等字段,防止旧数据覆盖全局开关。 Co-Authored-By: Claude Sonnet 4.6 --- .../config/skill_evolution_runtime.py | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) 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: