From e77705d33baaf33de676d67de0c5ed36d2a55499 Mon Sep 17 00:00:00 2001 From: Michele Date: Mon, 6 Apr 2026 02:13:23 +0200 Subject: [PATCH] fix: SQLAlchemy JSON mutation detection for saved ideas and approved examples SQLAlchemy doesn't detect in-place mutations on JSON columns (same object reference). Fixed all JSON write operations to create new list/dict objects: - save_idea: [new_idea] + old_ideas instead of ideas.insert() - delete_idea: new filtered list - mark_idea_used: new list with copied dicts - approve_post: dict(examples) for content_rules Co-Authored-By: Claude Opus 4.6 (1M context) --- backend/app/routers/content.py | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/backend/app/routers/content.py b/backend/app/routers/content.py index 673756a..5bc2cb6 100644 --- a/backend/app/routers/content.py +++ b/backend/app/routers/content.py @@ -334,7 +334,7 @@ def approve_post( }) # Keep only last 5 examples["approved_examples"] = approved_examples[-5:] - character.content_rules = examples + character.content_rules = dict(examples) # new dict for SQLAlchemy change detection character.updated_at = datetime.utcnow() db.commit() @@ -487,7 +487,7 @@ def save_idea( .filter(SystemSetting.key == "saved_ideas", SystemSetting.user_id == current_user.id) .first() ) - ideas = setting.value if setting and isinstance(setting.value, list) else [] + old_ideas = list(setting.value) if setting and isinstance(setting.value, list) else [] new_idea = { "id": str(uuid.uuid4())[:8], @@ -496,13 +496,13 @@ def save_idea( "saved_at": datetime.utcnow().isoformat() + "Z", "used": False, } - ideas.insert(0, new_idea) + new_ideas = [new_idea] + old_ideas # new list — forces SQLAlchemy to detect change if setting: - setting.value = ideas + setting.value = new_ideas setting.updated_at = datetime.utcnow() else: - db.add(SystemSetting(key="saved_ideas", value=ideas, user_id=current_user.id)) + db.add(SystemSetting(key="saved_ideas", value=new_ideas, user_id=current_user.id)) db.commit() return new_idea @@ -522,11 +522,11 @@ def delete_idea( if not setting or not isinstance(setting.value, list): raise HTTPException(status_code=404, detail="Idea not found") - ideas = [i for i in setting.value if i.get("id") != idea_id] - if len(ideas) == len(setting.value): + new_ideas = [i for i in setting.value if i.get("id") != idea_id] + if len(new_ideas) == len(setting.value): raise HTTPException(status_code=404, detail="Idea not found") - setting.value = ideas + setting.value = new_ideas # new list object for SQLAlchemy change detection setting.updated_at = datetime.utcnow() db.commit() return {"ok": True} @@ -547,13 +547,19 @@ def mark_idea_used( if not setting or not isinstance(setting.value, list): raise HTTPException(status_code=404, detail="Idea not found") + updated = [] + found = False for idea in setting.value: - if idea.get("id") == idea_id: - idea["used"] = True - break - else: + copy = dict(idea) + if copy.get("id") == idea_id: + copy["used"] = True + found = True + updated.append(copy) + + if not found: raise HTTPException(status_code=404, detail="Idea not found") + setting.value = updated # new list for SQLAlchemy change detection setting.updated_at = datetime.utcnow() db.commit() return {"ok": True}