Skip to main content

beyonder_core/
provenance.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4use crate::{ActorId, BlockId};
5
6/// A causal chain tracking how a block came to exist.
7/// Answers: "Agent X produced this because of Y, authorized by Z."
8#[derive(Debug, Clone, Default, Serialize, Deserialize)]
9pub struct ProvenanceChain {
10    pub nodes: Vec<ProvenanceNode>,
11}
12
13impl ProvenanceChain {
14    pub fn with_cause(mut self, cause: CauseKind, actor: ActorId) -> Self {
15        self.nodes.push(ProvenanceNode {
16            cause,
17            actor,
18            timestamp: Utc::now(),
19        });
20        self
21    }
22
23    /// The immediate cause — the last node in the chain.
24    pub fn immediate_cause(&self) -> Option<&ProvenanceNode> {
25        self.nodes.last()
26    }
27
28    /// The root cause — the first node (usually a human prompt).
29    pub fn root_cause(&self) -> Option<&ProvenanceNode> {
30        self.nodes.first()
31    }
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct ProvenanceNode {
36    pub cause: CauseKind,
37    pub actor: ActorId,
38    pub timestamp: DateTime<Utc>,
39}
40
41/// The kind of causal link in the provenance chain.
42#[derive(Debug, Clone, Serialize, Deserialize)]
43#[serde(tag = "type", rename_all = "snake_case")]
44pub enum CauseKind {
45    /// Triggered by a human user prompt.
46    HumanPrompt { prompt_summary: String },
47    /// Triggered by another block's content.
48    BlockOutput { block_id: BlockId },
49    /// Triggered by a tool call result.
50    ToolResult { tool_name: String },
51    /// Authorized by a human approval action.
52    ApprovalGranted { approval_block_id: BlockId },
53    /// Triggered by agent's own planning.
54    AgentPlan { plan_description: String },
55    /// System-initiated (e.g., startup, auto-resume).
56    System { reason: String },
57}