fix(skill-evolution): strip non-user-configurable fields from per-user overrides

load_user_overrides 只保留 interval_hours/stale_after_days/archive_after_days,
过滤掉老文件中的 enabled、model_name 等字段,防止旧数据覆盖全局开关。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
xuhdandClaude Sonnet 4.6 committed 2026-06-05 13:32:19 +08:00
1 parent e9125b0900
commit 603d667608
1 file changed
+19 -2
@@ -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: