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
use crate::identity::AccuracyClass;
use core::fmt;
use pleiades_types::CelestialBody;
/// The release-claim status of a single body for a single backend.
///
/// This is orthogonal to [`AccuracyClass`]: accuracy describes the numeric
/// band, the tier describes what the project promises at release.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum BodyClaimTier {
/// Production release claim: validated against a reference corpus
/// (packaged-artifact validation to its published ceiling, or a named
/// SPK/reference corpus). The specific numeric band and evidence are carried
/// separately by [`AccuracyClass`] and [`ClaimEvidence`].
ReleaseGrade,
/// Source-backed but below the release ceiling, or corpus/kernel dependent.
Constrained,
/// Algorithmic approximation; no release claim.
Approximate,
/// Explicitly not supported; preflight rejects requests for it.
Unsupported,
}
impl BodyClaimTier {
/// Returns a stable human-readable label.
pub const fn label(self) -> &'static str {
match self {
Self::ReleaseGrade => "ReleaseGrade",
Self::Constrained => "Constrained",
Self::Approximate => "Approximate",
Self::Unsupported => "Unsupported",
}
}
/// Returns the merge rank: stronger tiers win on backend body collisions.
pub const fn rank(self) -> u8 {
match self {
Self::ReleaseGrade => 3,
Self::Constrained => 2,
Self::Approximate => 1,
Self::Unsupported => 0,
}
}
}
impl fmt::Display for BodyClaimTier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.label())
}
}
/// The evidence backing a body claim.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ClaimEvidence {
/// Validated inside the packaged artifact build against the reference corpus.
ArtifactValidated,
/// Validated against a named reference corpus (e.g. `de440`, `sb441-n373s`).
CorpusValidated {
/// The reference source identifier.
source: String,
},
/// Backed only by an algorithmic model (VSOP87, compact ELP).
AlgorithmicModel,
/// No release-relevant evidence.
None,
}
impl ClaimEvidence {
/// Returns a compact human-readable label.
pub fn label(&self) -> String {
match self {
Self::ArtifactValidated => "artifact-validated".to_string(),
Self::CorpusValidated { source } => format!("corpus-validated:{source}"),
Self::AlgorithmicModel => "algorithmic-model".to_string(),
Self::None => "none".to_string(),
}
}
}
impl fmt::Display for ClaimEvidence {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.label())
}
}
/// A single backend's claim about a single body.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct BodyClaim {
/// The body this claim describes.
pub body: CelestialBody,
/// The release-claim tier.
pub tier: BodyClaimTier,
/// The per-body accuracy class.
pub accuracy: AccuracyClass,
/// The evidence backing the claim.
pub evidence: ClaimEvidence,
}
impl BodyClaim {
/// Creates a claim from explicit parts.
pub fn new(
body: CelestialBody,
tier: BodyClaimTier,
accuracy: AccuracyClass,
evidence: ClaimEvidence,
) -> Self {
Self {
body,
tier,
accuracy,
evidence,
}
}
/// Creates a `ReleaseGrade` claim.
pub fn release_grade(
body: CelestialBody,
accuracy: AccuracyClass,
evidence: ClaimEvidence,
) -> Self {
Self::new(body, BodyClaimTier::ReleaseGrade, accuracy, evidence)
}
/// Creates a `Constrained` claim.
pub fn constrained(
body: CelestialBody,
accuracy: AccuracyClass,
evidence: ClaimEvidence,
) -> Self {
Self::new(body, BodyClaimTier::Constrained, accuracy, evidence)
}
/// Creates an `Approximate` claim.
pub fn approximate(body: CelestialBody) -> Self {
Self::new(
body,
BodyClaimTier::Approximate,
AccuracyClass::Approximate,
ClaimEvidence::AlgorithmicModel,
)
}
/// Creates an `Unsupported` claim (listed for explicitness; preflight rejects it).
pub fn unsupported(body: CelestialBody) -> Self {
Self::new(
body,
BodyClaimTier::Unsupported,
AccuracyClass::Unknown,
ClaimEvidence::None,
)
}
/// Returns a compact one-line rendering.
pub fn summary_line(&self) -> String {
format!(
"{} [{}; accuracy={}; evidence={}]",
self.body, self.tier, self.accuracy, self.evidence
)
}
}
impl From<CelestialBody> for BodyClaim {
fn from(body: CelestialBody) -> Self {
Self::constrained(body, AccuracyClass::Unknown, ClaimEvidence::None)
}
}
#[cfg(test)]
#[path = "claims_tests.rs"]
mod tests;