Skip to main content

harness_core/
sensor.rs

1use crate::{Action, Execution, Signal, World, error::SensorError};
2use async_trait::async_trait;
3use serde::{Deserialize, Serialize};
4use std::sync::Arc;
5
6/// When in the change lifecycle a sensor runs (DESIGN.md §3, lifecycle distribution).
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8#[serde(rename_all = "kebab-case")]
9#[non_exhaustive]
10pub enum Stage {
11    /// Pre-action — runs before a tool invocation (rare; mostly used by hooks).
12    PreAction,
13    /// Inside the agent loop, after each action. Cheap & fast only.
14    SelfCorrect,
15    /// Right before commit / handoff. Heavier checks ok.
16    PreCommit,
17    /// In CI, after integration. Expensive sensors allowed.
18    PostIntegrate,
19    /// Long-running runtime monitoring (SLOs, log anomalies, drift).
20    Continuous,
21}
22
23pub type SensorId = String;
24
25#[async_trait]
26pub trait Sensor: Send + Sync + 'static {
27    fn id(&self) -> &SensorId;
28    fn kind(&self) -> Execution;
29    fn stage(&self) -> Stage;
30    async fn observe(&self, action: &Action, world: &World) -> Result<Vec<Signal>, SensorError>;
31}
32
33pub struct SensorEntry {
34    pub factory: fn() -> Arc<dyn Sensor>,
35}
36
37inventory::collect!(SensorEntry);
38
39pub fn iter_macro_sensors() -> impl Iterator<Item = Arc<dyn Sensor>> {
40    inventory::iter::<SensorEntry>().map(|e| (e.factory)())
41}