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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
// @generated by idiolect-codegen. do not edit.
// source: dev.idiolect.observation
//! A signed aggregate over a set of encounter-family records. Observations decouple ranking from the orchestrator (P6): many observers publish competing aggregates over the same traces, and users choose whom to trust.
#![allow(
missing_docs,
clippy::doc_markdown,
clippy::struct_excessive_bools,
clippy::derive_partial_eq_without_eq,
clippy::large_enum_variant
)]
use serde::{Deserialize, Serialize};
/// An observer publishes a structured summary of encounters, corrections, and retrospections matching a declared scope, using a declared method.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Observation {
/// Grounding for the observation. Omit when the repo owner is the observer; set explicitly when a third party is attributing the observation (e.g. caching or relaying another observer's aggregate).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub basis: Option<crate::generated::dev::idiolect::defs::Basis>,
/// Structured method descriptor (see dev.idiolect.observer for the conformance floor). Enables reproducibility and comparison.
pub method: ObservationMethod,
/// DID of the observer publishing this aggregate.
pub observer: idiolect_records::Did,
pub occurred_at: idiolect_records::Datetime,
/// The observation's payload. Shape is method-defined. Typical shapes include correction-rate rankings, quality scores, or structured diagnostic summaries.
pub output: serde_json::Value,
/// The set of records this observation aggregates over.
pub scope: ObservationScope,
/// Version of this observation method; different versions may produce non-comparable outputs.
pub version: String,
pub visibility: crate::generated::dev::idiolect::defs::Visibility,
}
impl crate::Record for Observation {
const NSID: &'static str = "dev.idiolect.observation";
}
/// Structured method descriptor (see dev.idiolect.observer for the conformance floor). Enables reproducibility and comparison.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ObservationMethod {
/// Optional reference to the method's source code or specification.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub code_ref: Option<idiolect_records::AtUri>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
/// Short method identifier.
pub name: String,
/// Method parameters as a free-form JSON object; interpretation is observer-defined.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub parameters: Option<serde_json::Value>,
}
/// The set of records this observation aggregates over.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ObservationScope {
/// Communities whose records are in scope.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub communities: Option<Vec<idiolect_records::AtUri>>,
/// Which encounter kinds this observation weights and includes. Observer must disclose this or the claim is uninterpretable. Slugs are open-enum and resolved against `encounterKindsVocab`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub encounter_kinds: Option<Vec<ObservationScopeEncounterKinds>>,
/// Vocabulary the `encounterKinds` slugs resolve against.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub encounter_kinds_vocab: Option<crate::generated::dev::idiolect::defs::VocabRef>,
/// Lenses included in scope. Empty array or omitted means 'all'.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub lenses: Option<Vec<crate::generated::dev::idiolect::defs::LensRef>>,
/// Time window covered by the aggregation.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub window: Option<ObservationScopeWindow>,
}
/// ObservationScopeEncounterKinds. Open-enum slug; known values are kebab-cased; community-extended values pass through as `Other(String)`.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ObservationScopeEncounterKinds {
InvocationLog,
Curated,
RoundtripVerified,
Production,
Adversarial,
/// Community-extended slug not present in the lexicon's
/// `knownValues`. Resolves through the sibling
/// `*Vocab` field on the containing record.
Other(String),
}
impl ObservationScopeEncounterKinds {
/// Wire-form slug for this value. Known variants render
/// kebab-case; the fallback variant passes through verbatim.
#[must_use]
pub fn as_str(&self) -> &str {
match self {
Self::InvocationLog => "invocation-log",
Self::Curated => "curated",
Self::RoundtripVerified => "roundtrip-verified",
Self::Production => "production",
Self::Adversarial => "adversarial",
Self::Other(s) => s.as_str(),
}
}
/// Whether this slug is subsumed by `ancestor` under the
/// `subsumed_by` relation in the supplied vocab. Reflexive:
/// every slug is subsumed by itself.
#[must_use]
pub fn is_subsumed_by(
&self,
vocab: &idiolect_records::vocab::VocabGraph,
ancestor: &str,
) -> bool {
vocab.is_subsumed_by(self.as_str(), ancestor)
}
/// Whether this slug satisfies a requirement of `target`
/// under the named `relation` in the supplied vocab.
/// Generalises `is_subsumed_by` to any directed relation
/// (e.g. `stronger_than`, `provides_at_least`,
/// `equivalent_to`). Reflexive: a slug satisfies itself.
#[must_use]
pub fn satisfies(
&self,
vocab: &idiolect_records::vocab::VocabGraph,
relation: &str,
target: &str,
) -> bool {
if self.as_str() == target {
return true;
}
vocab
.walk_relation(self.as_str(), relation, false)
.iter()
.any(|n| n == target)
}
/// Translate this slug across vocabularies via
/// `equivalent_to` edges. Returns the translated slug as
/// a target enum value when a translation exists, `None`
/// when no path is found (callers fall back to passing
/// the slug through verbatim, which is wire-compatible).
#[must_use]
pub fn translate_to<T: From<String>>(
&self,
src_vocab_uri: &str,
tgt_vocab_uri: &str,
registry: &idiolect_records::vocab::VocabRegistry,
) -> Option<T> {
registry
.translate(src_vocab_uri, tgt_vocab_uri, self.as_str())
.map(T::from)
}
}
impl From<String> for ObservationScopeEncounterKinds {
fn from(s: String) -> Self {
match s.as_str() {
"invocation-log" => Self::InvocationLog,
"curated" => Self::Curated,
"roundtrip-verified" => Self::RoundtripVerified,
"production" => Self::Production,
"adversarial" => Self::Adversarial,
_ => Self::Other(s),
}
}
}
impl From<&str> for ObservationScopeEncounterKinds {
fn from(s: &str) -> Self {
match s {
"invocation-log" => Self::InvocationLog,
"curated" => Self::Curated,
"roundtrip-verified" => Self::RoundtripVerified,
"production" => Self::Production,
"adversarial" => Self::Adversarial,
_ => Self::Other(s.to_owned()),
}
}
}
impl serde::Serialize for ObservationScopeEncounterKinds {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.as_str())
}
}
impl<'de> serde::Deserialize<'de> for ObservationScopeEncounterKinds {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(Self::from(s))
}
}
/// Time window covered by the aggregation.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ObservationScopeWindow {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub from: Option<idiolect_records::Datetime>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub until: Option<idiolect_records::Datetime>,
}