Hooks & Lifecycle Events
3.5 Hooks & Lifecycle Events
The SDK provides a hook system that allows external code to observe and react to simulation lifecycle events without modifying model code. Hooks are the mechanism through which output recording, guardrail enforcement, checkpointing, and telemetry are wired into the simulation --- they are not ad-hoc callbacks but a structured extensibility layer.
HookEvent
Eleven lifecycle events are defined:
| Event | Fired When | Typical Use |
|---|---|---|
BEFORE_SETUP | Before setup(config) is called | Validate config, initialise external resources |
AFTER_SETUP | After setup(config) completes | Log initial state, start timers |
BEFORE_STEP | Before each step() call | Inject external data, pre-step guardrail checks |
AFTER_STEP | After each step() completes | Record output, checkpointing, post-step guardrails |
BEFORE_ACTION | Before each Action executes | Fine-grained tracing, action-level timing |
AFTER_ACTION | After each Action completes | Action-level metrics, message counting |
ON_MESSAGE_SEND | When an agent calls send() or broadcast() | Message logging, rate limiting |
ON_MESSAGE_RECEIVE | When messages are delivered to an inbox | Message inspection, anomaly detection |
ON_AGENT_CREATE | When a new agent is created | Agent registration, initial state logging |
ON_AGENT_STOP | When agent.stop() is called | Agent removal tracking, cascade detection |
ON_ERROR | When an unhandled exception occurs | Error logging, graceful degradation |
Hooks are registered on the model's HookRegistry:
from simudyne.engine.hooks import HookEvent
def my_after_step_hook(context):
print(f"Step {context.tick} complete, {context.model.agent_count()} agents")
model.hooks.on(HookEvent.AFTER_STEP, my_after_step_hook)
The HookContext object passed to every callback provides access to the current event, the model, the current agent (for agent-level events), the current action (for action-level events), the tick number, a data dictionary for arbitrary metadata, and the exception object (for ON_ERROR).
The SimulationRecorder (Section 4) attaches itself via AFTER_STEP to capture multi-level output. The CheckpointManager uses AFTER_STEP for auto-checkpointing. The GuardrailRegistry uses AFTER_STEP to enforce runtime invariants. OpenTelemetry uses BEFORE_STEP/AFTER_STEP for span timing. This hook-based architecture means that all cross-cutting concerns are pluggable and composable without modifying model code.