1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
use sim_kernel::{Expr, Symbol};
use sim_value::build::entry;
/// One validation defect produced by BRIDGE receive checks.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BridgeObligation {
/// Stable packet path where the defect was found.
pub path: String,
/// Human-facing reason for the obligation.
pub reason: String,
/// Expected value, shape, or condition.
pub expected: String,
/// Actual value or condition observed.
pub actual: String,
/// Repair choices that a caller may offer to a human or model seat.
pub repair_menu: Vec<String>,
}
impl BridgeObligation {
/// Builds an obligation with explicit repair menu entries.
pub fn new(
path: impl Into<String>,
reason: impl Into<String>,
expected: impl Into<String>,
actual: impl Into<String>,
repair_menu: Vec<String>,
) -> Self {
Self {
path: path.into(),
reason: reason.into(),
expected: expected.into(),
actual: actual.into(),
repair_menu,
}
}
/// Builds an obligation with the default repair menu.
pub fn repair_packet(
path: impl Into<String>,
reason: impl Into<String>,
expected: impl Into<String>,
actual: impl Into<String>,
) -> Self {
Self::new(
path,
reason,
expected,
actual,
vec![
"repair packet".to_owned(),
"fetch missing context".to_owned(),
"return receipt".to_owned(),
],
)
}
/// Projects this obligation to a stable expression record.
pub fn to_expr(&self) -> Expr {
Expr::Map(vec![
entry("path", Expr::String(self.path.clone())),
entry("reason", Expr::String(self.reason.clone())),
entry("expected", Expr::String(self.expected.clone())),
entry("actual", Expr::String(self.actual.clone())),
entry(
"repair-menu",
Expr::List(
self.repair_menu
.iter()
.map(|item| Expr::String(item.clone()))
.collect(),
),
),
])
}
}
/// Receive-check report for one BRIDGE packet.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BridgeReport {
/// Packet content id used for reporting.
pub packet_cid: String,
/// Accepted part ids.
pub accepted_parts: Vec<String>,
/// Rejected part ids.
pub rejected_parts: Vec<String>,
/// Obligations that must be repaired before the packet is accepted.
pub obligations: Vec<BridgeObligation>,
}
impl BridgeReport {
/// Builds an empty report for `packet_cid`.
pub fn new(packet_cid: impl Into<String>) -> Self {
Self {
packet_cid: packet_cid.into(),
accepted_parts: Vec::new(),
rejected_parts: Vec::new(),
obligations: Vec::new(),
}
}
/// Records one accepted part id.
pub fn accept(&mut self, id: &Symbol) {
let id = id.as_qualified_str();
if !self.accepted_parts.contains(&id) {
self.accepted_parts.push(id);
}
}
/// Records one rejected part id.
pub fn reject(&mut self, id: &Symbol) {
let id = id.as_qualified_str();
if !self.rejected_parts.contains(&id) {
self.rejected_parts.push(id);
}
}
/// Appends an obligation.
pub fn obligate(&mut self, obligation: BridgeObligation) {
self.obligations.push(obligation);
}
/// Returns true when no obligation remains.
pub fn accepted(&self) -> bool {
self.obligations.is_empty()
}
/// Projects this report to a stable expression record.
pub fn to_expr(&self) -> Expr {
Expr::Map(vec![
entry("bridge-report", Expr::Bool(true)),
entry("packet-cid", Expr::String(self.packet_cid.clone())),
entry(
"accepted-parts",
Expr::List(
self.accepted_parts
.iter()
.map(|id| Expr::String(id.clone()))
.collect(),
),
),
entry(
"rejected-parts",
Expr::List(
self.rejected_parts
.iter()
.map(|id| Expr::String(id.clone()))
.collect(),
),
),
entry(
"obligations",
Expr::List(
self.obligations
.iter()
.map(BridgeObligation::to_expr)
.collect(),
),
),
])
}
}