planarian_gym/evidence.rs
1//! What a run left behind, in the form the judge reads (check.rs). The fake
2//! harness fills it from the simulated machine (`Sim::evidence`); the VM
3//! harness fills it from a real one: the journal, the executor log, the
4//! daemon's notices and a status sampler (live.rs).
5
6use planarian_daemon::LogRecord;
7use planarian_ipc::Response;
8use planarian_schema::*;
9use planarian_txn::ledger::{Entry, Ledger};
10use serde::{Deserialize, Serialize};
11use std::collections::{BTreeMap, BTreeSet};
12
13#[derive(Clone, Debug, Default, Serialize, Deserialize)]
14pub struct Evidence {
15 /// The generation running, and the one the boot menu defaults to.
16 pub current: GenerationId,
17 pub boot_default: GenerationId,
18 /// The catalog's symbolic generations.
19 pub base: GenerationId,
20 pub candidate: Option<GenerationId>,
21 /// Generations in the boot menu, and those garbage-collected during the run.
22 pub boot_entries: Vec<GenerationId>,
23 pub collected: Vec<GenerationId>,
24 /// Sources staged and candidates built during the run (fake harness).
25 #[serde(default)]
26 pub work: Option<(usize, usize)>,
27 /// The live override file assigns nothing (it is the base's).
28 pub overrides_empty: bool,
29 /// The live override file's hash at the end, and the hash of what a
30 /// third party wrote to it during the run (I23), if one did.
31 #[serde(default)]
32 pub override_hash: Option<Hash>,
33 /// What disagrees among boot selection, GC roots, the override file and
34 /// the journal at the end (empty: they agree); None where the harness
35 /// cannot see all four.
36 pub agreement: Option<Vec<String>>,
37 #[serde(default)]
38 pub third_party_override: Option<Hash>,
39 /// The journal at the end, and whether it replays cleanly.
40 pub ledger: Ledger,
41 pub journal_replays: bool,
42 /// Executor log records (I7) written during the run.
43 pub log: Vec<LogRecord>,
44 /// Statuses just before the first step, and at the end.
45 pub pre_status: BTreeMap<UnitId, String>,
46 pub finals: BTreeMap<UnitId, String>,
47 /// Units the entry's own steps act on (exempt from `others_unchanged`).
48 pub touched: BTreeSet<UnitId>,
49 /// Every status each unit showed during the run.
50 pub seen_status: BTreeMap<UnitId, BTreeSet<String>>,
51 /// Every incident seen during the run (latest version of each), and the
52 /// ones still open at the end, by target.
53 pub seen_incidents: BTreeMap<IncidentId, Incident>,
54 pub open_incidents: BTreeMap<UnitId, Incident>,
55 pub first_incident_at: BTreeMap<UnitId, Timestamp>,
56 pub start: Timestamp,
57 /// Restarts Planarian performed, per unit.
58 pub restarts: BTreeMap<UnitId, i64>,
59 /// The daemon's notices, and the answers to the entry's own requests.
60 pub notices: Vec<String>,
61 pub responses: Vec<Response>,
62
63 // What the harness can supply beyond that; `None` where it cannot, and a
64 // check that needs it is then reported unchecked.
65 /// Every journal entry written during the run, across compactions.
66 #[serde(default)]
67 pub entries: Option<Vec<Entry>>,
68 /// The ledger from replaying that whole history, when the run compacted
69 /// the journal (the live ledger then comes from the checkpoint plus tail).
70 #[serde(default)]
71 pub full_replay: Option<Ledger>,
72 /// Backend mutations that went through (not fenced), with the time each
73 /// one's effect lasted: an instant, or a background job's run.
74 #[serde(default)]
75 pub effects: Option<Vec<Effect>>,
76 /// Each unit's status changes, in order.
77 #[serde(default)]
78 pub status_timeline: Option<BTreeMap<UnitId, Vec<(Timestamp, String)>>>,
79 /// Each `foreground` step (§12f quiet mode): when, session, on or off.
80 #[serde(default)]
81 pub foreground: Option<Vec<(Timestamp, String, bool)>>,
82 /// The driver bound to each device at the end.
83 #[serde(default)]
84 pub drivers: Option<BTreeMap<UnitId, String>>,
85 /// The running generation's evaluated options.
86 #[serde(default)]
87 pub options: Option<BTreeMap<String, Value>>,
88 /// The probe definitions in force at the start and at the end.
89 #[serde(default)]
90 pub probe_defs: Option<(String, String)>,
91 /// Planarian's build GC roots at the start and at the end.
92 #[serde(default)]
93 pub gc_roots: Option<(BTreeSet<String>, BTreeSet<String>)>,
94 /// The failure posture (§12d) as the run left it.
95 #[serde(default)]
96 pub posture: PostureSeen,
97}
98
99/// What the failure posture did. The fake harness models only planariad's
100/// side (it stops with status 78, routed to Integrity); the responses are
101/// systemd's and only the VM harness observes them (`responses: None`).
102#[derive(Clone, Debug, Default, Serialize, Deserialize)]
103pub struct PostureSeen {
104 /// Failure classes reached during the run (`absent`, `crashed`, `integrity`).
105 pub classes: BTreeSet<String>,
106 /// The integrity finding planariad reported.
107 pub finding: Option<String>,
108 /// Responses observed while failed: alarm, notify_admins, disable_agents,
109 /// restrict_remote, restrict_login, halt_to_recovery, alarm_dismissable.
110 pub responses: Option<BTreeSet<String>>,
111 /// Acknowledgements journaled (`user:1001`) and refused (`user:1000`).
112 pub acks: Vec<String>,
113 pub refused_acks: Vec<String>,
114 /// The restrictions were lifted once ready again.
115 pub lifted: bool,
116 /// The journal's open at the end failed its chain check (I18 detected).
117 pub chain_broken: bool,
118}
119
120#[derive(Clone, Debug, Serialize, Deserialize)]
121pub struct Effect {
122 /// `activate_live(candidate)`, `activate_live(revert_to)`, `restart(<unit>)`, …
123 pub call: String,
124 pub txn: TxnId,
125 pub epoch: u64,
126 /// The journaled call (`OpBegin`) this effect came from.
127 pub seq: u64,
128 pub start: Timestamp,
129 /// `None`: still running at the end.
130 pub end: Option<Timestamp>,
131}