fester/backend/pipeline/feedback.py

69 lines
2.1 KiB
Python
Executable File

"""
Pipeline feedback — feeds execution results back into the policy engine
so it can learn which nodes / actions succeed vs fail.
F-04 remediation:
``report_execution()`` now actually calls
``policy.update_reputation(...)`` instead of being a stub. The
singleton policy engine still works with ``db=None`` — reputation
is stored in-process on the engine itself.
"""
from __future__ import annotations
import logging
from typing import Any, Dict, Optional
from backend.policy.engine import PolicyEngine
log = logging.getLogger(__name__)
# Singleton policy engine (db=None — reputation is kept in-process on
# the engine itself; pass a db to also enable the historical layer).
policy = PolicyEngine(db=None)
def report_execution(
node: str,
action: str,
success: bool,
duration: float,
temp_before: float,
temp_after: float,
) -> Dict[str, Any]:
"""Feed execution results back to the policy engine.
Computes the thermal-spike flag and pushes a reputation update to
the singleton :class:`PolicyEngine`. Returns the assessment dict
(kept for backward compatibility — older callers consumed it
directly).
"""
thermal_spike = (temp_after - temp_before) > 0.15
# Actually update the reputation store so the scheduler can learn.
try:
updated = policy.update_reputation(
node=node,
success=bool(success),
duration=float(duration or 0.0),
thermal_spike=bool(thermal_spike),
)
log.debug(
"feedback: node=%s action=%s success=%s dur=%.1fs thermal=%s -> %s",
node, action, success, duration, thermal_spike, updated,
)
except Exception:
# Reputation updates must never break the pipeline.
log.warning("policy.update_reputation failed", exc_info=True)
updated = {}
return {
"node": node,
"action": action,
"success": success,
"duration": duration,
"thermal_spike": thermal_spike,
"temp_delta": temp_after - temp_before,
"reputation": updated,
}