Skip to main content

tear_types/
block.rs

1//! Wire-shape mirror of `tear_core::blocks::Block`.
2//!
3//! Lives in `tear-types` because the daemon's wire layer needs
4//! the type but can't depend on `tear-core` (no upward dep —
5//! tear-core depends on tear-types). The two crate's `Block`
6//! structs share the same serde representation byte-for-byte;
7//! we use `From` conversions on the tear-core side to bridge.
8
9use serde::{Deserialize, Serialize};
10
11use crate::yurai::Yurai;
12
13#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
14pub struct Block {
15    pub index: u64,
16    pub prompt: String,
17    pub command: String,
18    pub output: String,
19    pub exit_code: Option<i32>,
20    pub started_at_unix_ms: u64,
21    pub ended_at_unix_ms: Option<u64>,
22    /// Working directory at prompt start, captured from the
23    /// shell's OSC 7 `file://<host><path>` notification.
24    /// `None` when the shell hasn't emitted OSC 7 (older
25    /// configurations or pure /bin/sh).
26    #[serde(default)]
27    pub cwd: Option<String>,
28    /// WHO ran this block — the provenance of the pane that
29    /// produced it, stamped at prompt start.
30    ///
31    /// Not `Option`: every block answers the question, and
32    /// [`Yurai::Unknown`] IS an answer — the honest one for a
33    /// pane no attested connection minted, or for a record
34    /// written before this field existed. Making it optional
35    /// would let a consumer skip the question entirely, which
36    /// is the state this field exists to remove.
37    ///
38    /// **This is the field a block history is worth having.**
39    /// Without it an agent-run `rm -rf` and an operator-run one
40    /// are the same row, so "who ran this" is answerable only
41    /// by correlating logs — a reconstruction, not a record.
42    /// With it, attribution is carried by the artifact itself.
43    ///
44    /// Tier-honest: this records a CLAIM at the tier it was
45    /// made. `Yurai::Automation` means a connection declared
46    /// itself automation; it is not proof, and
47    /// [`Yurai`]'s own docs are the authority on that boundary.
48    #[serde(default)]
49    pub yurai: Yurai,
50}
51
52impl Block {
53    /// Wall-clock duration of the block (output end - start).
54    /// `None` while still in progress.
55    #[must_use]
56    pub fn duration_ms(&self) -> Option<u64> {
57        self.ended_at_unix_ms
58            .map(|end| end.saturating_sub(self.started_at_unix_ms))
59    }
60}