SpindleFlow-RL / audit /delegation_trace.py
garvitsachdeva's picture
SpindleFlow RL — periodic push + log persistence
02ff91f
"""
Delegation trace — audit trail for regulated industries.
Every delegation decision is logged. generate_explanation() produces
human-readable audit text.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from datetime import datetime
from env.delegation_graph import DelegationEdge
@dataclass
class DelegationTrace:
"""Complete audit record for one episode."""
episode_id: str
task_description: str
task_complexity: str
start_time: str = field(default_factory=lambda: datetime.utcnow().isoformat())
delegation_edges: list[DelegationEdge] = field(default_factory=list)
scratchpad_entries: list[dict] = field(default_factory=list)
final_reward: float = 0.0
approved_by_policy: bool = True
def record_edge(self, edge: DelegationEdge) -> None:
self.delegation_edges.append(edge)
def record_scratchpad(self, author_id: str, content: str, step: int) -> None:
self.scratchpad_entries.append({
"author": author_id,
"step": step,
"content_preview": content[:200],
})
def generate_explanation(self) -> str:
"""
Generate a human-readable audit trail.
Suitable for compliance export.
"""
lines = [
"=== DELEGATION AUDIT TRAIL ===",
f"Episode: {self.episode_id}",
f"Time: {self.start_time}",
f"Task: {self.task_description}",
f"Complexity: {self.task_complexity}",
f"Final Reward: {self.final_reward:.3f}",
"",
"Delegation Sequence:",
]
for i, edge in enumerate(self.delegation_edges):
lines.append(
f" Step {i+1}: {edge.caller_id} -> {edge.callee_id} "
f"[mode: {edge.delegation_mode}]"
)
lines.extend([
"",
f"Total specialists called: {len(self.delegation_edges)}",
f"Max delegation depth reached: "
f"{max((e.depth for e in self.delegation_edges), default=0)}",
"=== END AUDIT TRAIL ===",
])
return "\n".join(lines)
def to_dict(self) -> dict:
return {
"episode_id": self.episode_id,
"task": self.task_description,
"complexity": self.task_complexity,
"start_time": self.start_time,
"delegation_steps": [
{
"caller": e.caller_id,
"callee": e.callee_id,
"mode": e.delegation_mode,
"depth": e.depth,
}
for e in self.delegation_edges
],
"reward": self.final_reward,
}