def test_agent_action_registries_keep_l2_and_l3_boundaries(): import services.agent_actions as actions assert set(actions.SAFE_ACTIONS) == { "retry_task", "query_km", "silence_alert", "flag_for_human_review", "route_to_km", "mark_for_relearn", } assert set(actions.OPS_ACTIONS) == { "pause_task", "resume_task", "force_retry_now", } assert set(actions.SAFE_ACTIONS).isdisjoint(actions.OPS_ACTIONS) for action_name, func in actions.SAFE_ACTIONS.items(): assert getattr(actions, action_name) is func for action_name, func in actions.OPS_ACTIONS.items(): assert getattr(actions, action_name) is func def test_flag_for_human_review_writes_pending_memory(monkeypatch): import services.agent_actions as actions import services.openclaw_learning_service as learning calls = [] monkeypatch.setattr(actions, "_audit", lambda *args, **kwargs: 999) monkeypatch.setattr( learning, "store_insight", lambda **kwargs: calls.append(kwargs) or 123, ) result = actions.flag_for_human_review("SKU-1", "銷量斷崖,請人工確認") assert result["status"] == "pending_review" assert result["insight_id"] == 123 assert calls[0]["insight_type"] == "human_review" assert calls[0]["status"] == "pending" assert calls[0]["product_sku"] == "SKU-1" def test_route_to_km_writes_archived_memory(monkeypatch): import services.agent_actions as actions import services.openclaw_learning_service as learning calls = [] monkeypatch.setattr(actions, "_audit", lambda *args, **kwargs: 999) monkeypatch.setattr(learning, "store_insight", lambda **kwargs: calls.append(kwargs) or 456) result = actions.route_to_km("SKU-2", "pricing", "競品價差擴大") assert result == {"status": "archived", "sku": "SKU-2", "domain": "pricing", "insight_id": 456} assert calls[0]["insight_type"] == "km_entry" assert calls[0]["metadata"]["domain"] == "pricing" def test_mark_for_relearn_writes_pending_marker(monkeypatch): import services.agent_actions as actions import services.openclaw_learning_service as learning calls = [] monkeypatch.setattr(actions, "_audit", lambda *args, **kwargs: 999) monkeypatch.setattr(learning, "store_insight", lambda **kwargs: calls.append(kwargs) or 789) result = actions.mark_for_relearn("SKU-3", "NemoTron 信心不足") assert result["status"] == "marked" assert result["insight_id"] == 789 assert calls[0]["insight_type"] == "relearn_marker" assert calls[0]["status"] == "pending"