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}