1use serde::{Deserialize, Serialize};
66use serde_json::{json, Map, Value};
67use sha2::{Digest, Sha256};
68use std::path::Path;
69use std::str::FromStr;
70
71use super::drain::{
72 AdmissionCap, BandContext, BandInput, BandStatus, ComparatorStatus, DerivedBand, SampleRow,
73 StreamMode, StreamWitness, SCHEMA_VERSION,
74};
75use super::join::{BandRatios, JoinKey};
76use super::protocol::ProtocolParams;
77use super::samples::SamplesFile;
78use super::witness::BatchInvarianceWitness;
79
80pub const SPEC_ID: &str = "PP-LLAMA-001 v3.0";
82
83pub const CLOCK_SOURCE_SYSTEM_REALTIME: &str = "std::time::SystemTime (CLOCK_REALTIME)";
85
86pub const SERVER_ONLY_FIELDS: &str = "§4.4.9 scheduler block (max_in_flight, admission_rejected, \
89 preempted_recompute, preempted_swap, kv_blocks_total, kv_blocks_peak_used, \
90 kv_bytes_reserved, kv_bytes_used, gpu_layers_requested, gpu_layers_resolved, \
91 gpu_layers_total, backend_loaded[], autofit_applied[]) — every one is reported by the \
92 SERVER. PP-13: max_in_flight is reported by the server, not inferred by the harness; PP-2: \
93 gpu_layers_resolved is read from the loader and never inferred. A client-side estimate \
94 would read exactly like a measurement, so none is emitted.";
95
96#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
105#[serde(try_from = "String", into = "String")]
106pub struct RunId(String);
107
108impl RunId {
109 #[must_use]
111 pub fn derive(started_utc: &str, host: &str, client_sha256: &str, pid: u32) -> Self {
112 let mut hasher = Sha256::new();
113 hasher.update(started_utc.as_bytes());
114 hasher.update(host.as_bytes());
115 hasher.update(client_sha256.as_bytes());
116 hasher.update(pid.to_string().as_bytes());
117 let digest = format!("{:x}", hasher.finalize());
118 Self(digest[..32].to_string())
119 }
120
121 #[must_use]
123 pub fn as_str(&self) -> &str {
124 &self.0
125 }
126}
127
128impl TryFrom<String> for RunId {
129 type Error = String;
130
131 fn try_from(value: String) -> Result<Self, Self::Error> {
132 if value.len() == 32
133 && value
134 .bytes()
135 .all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase())
136 {
137 Ok(Self(value))
138 } else {
139 Err(format!(
140 "run_id {value:?} is not 32 lowercase hex characters — PP-3 keys the baseline on \
141 it, so a malformed one would make every ratio unjoinable"
142 ))
143 }
144 }
145}
146
147impl From<RunId> for String {
148 fn from(id: RunId) -> Self {
149 id.0
150 }
151}
152
153#[cfg(not(target_arch = "wasm32"))]
159#[must_use]
160pub fn now_utc_millis() -> String {
161 chrono::Utc::now()
162 .format("%Y-%m-%dT%H:%M:%S%.3fZ")
163 .to_string()
164}
165
166#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
170#[serde(rename_all = "snake_case")]
171pub enum ComputeClass {
172 Cpu,
174 Cuda,
176 Metal,
178 Wgpu,
180 Unknown,
182}
183
184impl ComputeClass {
185 #[must_use]
187 pub fn wire_token(self) -> &'static str {
188 match self {
189 Self::Cpu => "cpu",
190 Self::Cuda => "cuda",
191 Self::Metal => "metal",
192 Self::Wgpu => "wgpu",
193 Self::Unknown => "unknown",
194 }
195 }
196}
197
198impl FromStr for ComputeClass {
205 type Err = String;
206
207 fn from_str(s: &str) -> Result<Self, Self::Err> {
208 [
209 Self::Cpu,
210 Self::Cuda,
211 Self::Metal,
212 Self::Wgpu,
213 Self::Unknown,
214 ]
215 .into_iter()
216 .find(|c| c.wire_token() == s)
217 .ok_or_else(|| {
218 format!(
219 "compute_class {s:?}: expected one of cpu, cuda, metal, wgpu, unknown (PP-2 \
220 requires the path TAKEN, not the hardware present)"
221 )
222 })
223 }
224}
225
226#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
228#[serde(deny_unknown_fields)]
229pub struct SubjectIdentity {
230 pub path: String,
232 pub sha256: String,
234 pub commit: String,
237 pub feature_set: Vec<String>,
239}
240
241#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
243#[serde(deny_unknown_fields)]
244pub struct ClientIdentity {
245 pub path: String,
247 pub sha256: String,
249 pub commit: String,
251 pub pid: u32,
261}
262
263#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
265#[serde(deny_unknown_fields)]
266pub struct ComparatorIdentity {
267 pub commit: String,
269 pub cmake: String,
271 pub sha256: String,
273 pub pin_expiry: String,
276 pub props: Value,
278}
279
280#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
282#[serde(deny_unknown_fields)]
283pub struct ModelIdentity {
284 pub path: String,
286 pub sha256: String,
288 pub bytes: u64,
290}
291
292#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
299#[serde(deny_unknown_fields)]
300pub struct Provenance {
301 pub binary_path: String,
305 pub binary_sha256: String,
308 pub resolution: String,
310 pub compute_class: ComputeClass,
312 pub host: String,
314 pub accelerator: String,
316 pub model: String,
318 pub quantization: String,
320 pub feature_set: Vec<String>,
322 pub started_utc: String,
324 pub clock_source: String,
326 pub subject: SubjectIdentity,
328 pub client: ClientIdentity,
330 pub comparator: Option<ComparatorIdentity>,
332 pub server_config: Option<Value>,
336 pub model_file: Option<ModelIdentity>,
338}
339
340impl Provenance {
341 pub fn validate(&self) -> Result<(), String> {
348 for (name, value) in self.required_strings() {
349 if value.trim().is_empty() {
350 return Err(format!(
351 "provenance.{name}: empty — this field has no default; a receipt that does \
352 not say {name} is an anonymous number, not evidence"
353 ));
354 }
355 }
356 for (name, digest) in self.digests() {
357 if !is_sha256(digest) {
358 return Err(format!(
359 "provenance.{name}: {digest:?} is not 64 lowercase hex characters"
360 ));
361 }
362 }
363 validate_rfc3339_utc_millis("provenance.started_utc", &self.started_utc)?;
364 if let Some(c) = &self.comparator {
365 validate_rfc3339_utc_millis("provenance.comparator.pin_expiry", &c.pin_expiry)?;
366 }
367 self.validate_feature_set()
368 }
369
370 #[must_use]
376 pub fn comparator_is_stale(&self) -> bool {
377 self.comparator
378 .as_ref()
379 .is_some_and(|c| c.pin_expiry < self.started_utc)
380 }
381
382 fn required_strings(&self) -> Vec<(&'static str, &str)> {
383 let mut out = vec![
384 ("binary_path", self.binary_path.as_str()),
385 ("binary_sha256", self.binary_sha256.as_str()),
386 ("resolution", self.resolution.as_str()),
387 ("host", self.host.as_str()),
388 ("accelerator", self.accelerator.as_str()),
389 ("model", self.model.as_str()),
390 ("quantization", self.quantization.as_str()),
391 ("started_utc", self.started_utc.as_str()),
392 ("clock_source", self.clock_source.as_str()),
393 ("subject.path", self.subject.path.as_str()),
394 ("subject.commit", self.subject.commit.as_str()),
395 ("client.path", self.client.path.as_str()),
396 ("client.commit", self.client.commit.as_str()),
397 ];
398 if let Some(c) = &self.comparator {
399 out.push(("comparator.commit", c.commit.as_str()));
400 out.push(("comparator.cmake", c.cmake.as_str()));
401 out.push(("comparator.pin_expiry", c.pin_expiry.as_str()));
402 }
403 if let Some(m) = &self.model_file {
404 out.push(("model_file.path", m.path.as_str()));
405 }
406 out
407 }
408
409 fn digests(&self) -> Vec<(&'static str, &str)> {
410 let mut out = vec![
411 ("binary_sha256", self.binary_sha256.as_str()),
412 ("subject.sha256", self.subject.sha256.as_str()),
413 ("client.sha256", self.client.sha256.as_str()),
414 ];
415 if let Some(c) = &self.comparator {
416 out.push(("comparator.sha256", c.sha256.as_str()));
417 }
418 if let Some(m) = &self.model_file {
419 out.push(("model_file.sha256", m.sha256.as_str()));
420 }
421 out
422 }
423
424 fn validate_feature_set(&self) -> Result<(), String> {
428 let needs_feature = matches!(self.compute_class, ComputeClass::Cuda | ComputeClass::Wgpu);
429 let token = self.compute_class.wire_token();
430 if needs_feature && !self.subject.feature_set.iter().any(|f| f == token) {
431 return Err(format!(
432 "provenance.compute_class={token} but subject.feature_set={:?} does not contain \
433 it — a build without the feature cannot take that path (PP-2)",
434 self.subject.feature_set
435 ));
436 }
437 Ok(())
438 }
439}
440
441fn validate_rfc3339_utc_millis(field: &str, value: &str) -> Result<(), String> {
449 const SHAPE: &str = "YYYY-MM-DDTHH:MM:SS.mmmZ";
450 let bytes = value.as_bytes();
451 let ok = bytes.len() == 24
452 && bytes.iter().enumerate().all(|(i, b)| match i {
453 4 | 7 => *b == b'-',
454 10 => *b == b'T',
455 13 | 16 => *b == b':',
456 19 => *b == b'.',
457 23 => *b == b'Z',
458 _ => b.is_ascii_digit(),
459 });
460 if !ok {
461 return Err(format!(
462 "{field}: {value:?} is not {SHAPE} — PP-30 needs a canonical UTC instant, because \
463 PP-20 compares a pin expiry against it as a string and any other spelling sorts \
464 wrongly"
465 ));
466 }
467 Ok(())
468}
469
470#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
472#[serde(rename_all = "snake_case")]
473pub enum TokenCountingMethod {
474 ServerUsage,
477 ClientTokenizer,
479}
480
481impl TokenCountingMethod {
482 #[must_use]
484 pub fn wire_token(self) -> &'static str {
485 match self {
486 Self::ServerUsage => "server_usage",
487 Self::ClientTokenizer => "client_tokenizer",
488 }
489 }
490}
491
492#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
504#[serde(tag = "method", rename_all = "snake_case", deny_unknown_fields)]
505pub enum TokenizationBlock {
506 ServerUsage {
508 counts_special_tokens: bool,
510 counts_prompt_echo: bool,
512 },
513 ClientTokenizer {
515 tokenizer_sha256: String,
517 counts_special_tokens: bool,
519 counts_prompt_echo: bool,
521 },
522}
523
524impl TokenizationBlock {
525 #[must_use]
527 pub fn method(&self) -> TokenCountingMethod {
528 match self {
529 Self::ServerUsage { .. } => TokenCountingMethod::ServerUsage,
530 Self::ClientTokenizer { .. } => TokenCountingMethod::ClientTokenizer,
531 }
532 }
533
534 pub fn validate(&self) -> Result<(), String> {
537 match self {
538 Self::ServerUsage { .. } => Ok(()),
539 Self::ClientTokenizer {
540 tokenizer_sha256, ..
541 } if is_sha256(tokenizer_sha256) => Ok(()),
542 Self::ClientTokenizer {
543 tokenizer_sha256, ..
544 } => Err(format!(
545 "tokenization.tokenizer_sha256: {tokenizer_sha256:?} is not 64 lowercase hex \
546 characters — §4.4.6 requires it when method = client_tokenizer"
547 )),
548 }
549 }
550
551 pub fn require_counter(&self, has_client_counter: bool) -> Result<(), String> {
557 match (self.method(), has_client_counter) {
558 (TokenCountingMethod::ClientTokenizer, false) => Err(
559 "tokenization.method = client_tokenizer but no client TokenCounter was supplied"
560 .to_string(),
561 ),
562 (TokenCountingMethod::ServerUsage, true) => Err(
563 "tokenization.method = server_usage but a client TokenCounter was supplied; \
564 declare client_tokenizer or drop the counter"
565 .to_string(),
566 ),
567 _ => Ok(()),
568 }
569 }
570}
571
572#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
587#[serde(deny_unknown_fields)]
588pub struct KvBlock {
589 bytes_used: u64,
590 bytes_reserved: u64,
591 admission_rejected: Option<u64>,
592 preempted_swap: Option<u64>,
593}
594
595impl KvBlock {
596 #[must_use]
599 pub fn from_server_report(
600 bytes_used: u64,
601 bytes_reserved: u64,
602 admission_rejected: Option<u64>,
603 preempted_swap: Option<u64>,
604 ) -> Self {
605 Self {
606 bytes_used,
607 bytes_reserved,
608 admission_rejected,
609 preempted_swap,
610 }
611 }
612
613 #[must_use]
618 pub fn uncounted_fields(&self) -> Vec<&'static str> {
619 let mut out = Vec::new();
620 if self.admission_rejected.is_none() {
621 out.push("kv.admission_rejected");
622 }
623 if self.preempted_swap.is_none() {
624 out.push("kv.preempted_swap");
625 }
626 out
627 }
628}
629
630#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
632#[serde(deny_unknown_fields)]
633pub struct SlotsAdmitted {
634 pub apr: Option<u32>,
636 pub llama: Option<u32>,
638}
639
640#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
646#[serde(deny_unknown_fields)]
647pub struct Ladder {
648 pub declared: Vec<u32>,
650 pub derived: Vec<u32>,
652 pub slots_admitted: SlotsAdmitted,
654}
655
656impl Ladder {
657 #[must_use]
664 pub fn derive(declared: &[u32], slots_admitted: SlotsAdmitted) -> Self {
665 let cap = match (slots_admitted.apr, slots_admitted.llama) {
666 (Some(a), Some(l)) => Some(a.min(l)),
667 (Some(a), None) => Some(a),
668 (None, Some(l)) => Some(l),
669 (None, None) => None,
670 };
671 let derived = declared
672 .iter()
673 .copied()
674 .filter(|c| cap.admits(*c))
675 .collect();
676 Self {
677 declared: declared.to_vec(),
678 derived,
679 slots_admitted,
680 }
681 }
682
683 #[must_use]
686 pub fn is_underived(&self) -> bool {
687 self.slots_admitted.apr.is_none() && self.slots_admitted.llama.is_none()
688 }
689}
690
691trait CapExt {
694 fn admits(self, c: u32) -> bool;
695}
696
697impl CapExt for Option<u32> {
698 fn admits(self, c: u32) -> bool {
699 match self {
700 None => true,
701 Some(cap) => cap >= c,
702 }
703 }
704}
705
706#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
715#[serde(deny_unknown_fields)]
716pub struct Roofline {
717 pub bandwidth_bytes_per_sec: f64,
719 pub model_bytes: u64,
721}
722
723impl Roofline {
724 #[must_use]
726 pub fn tok_per_sec(self) -> Option<f64> {
727 if self.model_bytes == 0 || self.bandwidth_bytes_per_sec <= 0.0 {
728 return None;
729 }
730 Some(self.bandwidth_bytes_per_sec / self.model_bytes as f64)
731 }
732}
733
734#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
736pub enum Workload {
737 W1,
739 W2,
741}
742
743impl Workload {
744 #[must_use]
746 pub fn wire_token(self) -> &'static str {
747 match self {
748 Self::W1 => "W1",
749 Self::W2 => "W2",
750 }
751 }
752}
753
754impl FromStr for Workload {
757 type Err = String;
758
759 fn from_str(s: &str) -> Result<Self, Self::Err> {
760 [Self::W1, Self::W2]
761 .into_iter()
762 .find(|w| w.wire_token() == s)
763 .ok_or_else(|| format!("workload {s:?}: expected W1 or W2 (§5.1)"))
764 }
765}
766
767pub fn sha256_file(path: &Path) -> std::io::Result<String> {
773 let mut file = std::fs::File::open(path)?;
774 let mut hasher = Sha256::new();
775 std::io::copy(&mut file, &mut hasher)?;
776 Ok(format!("{:x}", hasher.finalize()))
777}
778
779#[derive(Debug, Clone, PartialEq)]
781pub struct ReceiptInput {
782 pub schema_version: u32,
784 pub run_id: RunId,
786 pub provenance: Provenance,
788 pub tokenization: TokenizationBlock,
790 pub workload: Workload,
792 pub protocol: ProtocolParams,
794 pub commit: String,
796 pub ladder: Ladder,
798 pub bands: Vec<BandInput>,
800 pub kv: Option<KvBlock>,
802 pub roofline: Option<Roofline>,
804}
805
806impl ReceiptInput {
807 #[must_use]
810 pub fn new(
811 run_id: RunId,
812 provenance: Provenance,
813 tokenization: TokenizationBlock,
814 workload: Workload,
815 protocol: ProtocolParams,
816 commit: impl Into<String>,
817 ladder: Ladder,
818 bands: Vec<BandInput>,
819 ) -> Self {
820 Self {
821 schema_version: SCHEMA_VERSION,
822 run_id,
823 provenance,
824 tokenization,
825 workload,
826 protocol,
827 commit: commit.into(),
828 ladder,
829 bands,
830 kv: None,
831 roofline: None,
832 }
833 }
834
835 #[must_use]
837 pub fn band_context(&self) -> BandContext {
838 BandContext {
839 schema_version: self.schema_version,
840 replicates: self.protocol.replicates,
841 interleaved: self.protocol.interleaved,
842 comparator_stale: self.provenance.comparator_is_stale(),
843 ..BandContext::default()
844 }
845 }
846
847 #[must_use]
849 pub fn join_key(&self, band: &BandInput) -> JoinKey {
850 JoinKey::of(self, band)
851 }
852
853 pub fn render(&self) -> Result<Value, String> {
863 self.provenance.validate()?;
864 self.tokenization.validate()?;
865 self.check_ladder_is_derived()?;
866 if self.bands.is_empty() {
867 return Err(
868 "receipt has no bands — a measurement over zero bands is a vacuous pass"
869 .to_string(),
870 );
871 }
872 let ctx = self.band_context();
873 let stale = ctx.comparator_stale;
874 let mut bands = Vec::with_capacity(self.bands.len());
875 for input in &self.bands {
876 self.check_ladder(input)?;
877 let mut derived = input.derive_in(&ctx)?.with_join_key(self.join_key(input));
878 if stale {
879 let expiry = self
880 .provenance
881 .comparator
882 .as_ref()
883 .map_or("", |c| c.pin_expiry.as_str());
884 derived = derived.marked_comparator_stale(expiry, &self.provenance.started_utc);
885 }
886 bands.push(derived);
887 }
888 self.check_roofline(&bands)?;
889 let samples = samples_ms(&bands);
890 validate_samples(&samples)?;
891 Ok(self.assemble(&bands, samples))
892 }
893
894 pub fn render_string(&self) -> Result<String, String> {
899 let value = self.render()?;
900 serde_json::to_string_pretty(&value).map_err(|e| format!("serialising receipt: {e}"))
901 }
902
903 fn check_ladder_is_derived(&self) -> Result<(), String> {
913 let recomputed = Ladder::derive(&self.ladder.declared, self.ladder.slots_admitted);
914 if recomputed.derived == self.ladder.derived {
915 return Ok(());
916 }
917 Err(format!(
918 "PP-24: ladder.derived is {:?} but declared {:?} with slots_admitted apr={:?} \
919 llama={:?} derives {:?} — `derived` is `{{c ∈ declared : c ≤ min(slots_admitted)}}`, \
920 not a field a producer may state. A supplied ladder that disagrees with its own \
921 inputs excuses exactly the bands PP-24 exists to exclude.",
922 self.ladder.derived,
923 self.ladder.declared,
924 self.ladder.slots_admitted.apr,
925 self.ladder.slots_admitted.llama,
926 recomputed.derived
927 ))
928 }
929
930 fn check_ladder(&self, band: &BandInput) -> Result<(), String> {
933 if self.ladder.derived.contains(&band.concurrency) {
934 return Ok(());
935 }
936 match &band.comparator {
937 ComparatorStatus::NotApplicable { .. } => Ok(()),
938 ComparatorStatus::Unmeasured {
939 admission_capped: Some(_),
940 ..
941 } => Ok(()),
942 _ => Err(format!(
943 "PP-24: band c={} is not in the derived ladder {:?} (slots_admitted apr={:?} \
944 llama={:?}) and carries neither an admission cap nor a decision — a band above \
945 what the servers admitted measured a queue, not a server",
946 band.concurrency,
947 self.ladder.derived,
948 self.ladder.slots_admitted.apr,
949 self.ladder.slots_admitted.llama
950 )),
951 }
952 }
953
954 fn check_roofline(&self, bands: &[DerivedBand]) -> Result<(), String> {
958 let Some(ceiling) = self.roofline.and_then(Roofline::tok_per_sec) else {
959 return Ok(());
960 };
961 for b in bands.iter().filter(|b| b.concurrency == 1) {
962 if let Some(dec) = b.decode_tok_per_sec {
963 if dec > ceiling {
964 return Err(format!(
965 "PP-23: decode_tok_per_sec={dec:.1} at c=1 exceeds the memory-bandwidth \
966 ceiling {ceiling:.1} tok/s — decoding a token reads the whole model \
967 once, so this is not a fast run, it is a wrong measurement"
968 ));
969 }
970 }
971 }
972 Ok(())
973 }
974
975 fn assemble(&self, bands: &[DerivedBand], samples: Vec<f64>) -> Value {
976 let mut map = Map::new();
977 map.insert("spec".into(), json!(SPEC_ID));
978 map.insert("schema_version".into(), json!(self.schema_version));
979 map.insert("run_id".into(), json!(self.run_id.as_str()));
980 map.insert("commit".into(), json!(self.commit));
981 map.insert("workload".into(), json!(self.workload.wire_token()));
982 map.insert("protocol".into(), to_value(&self.protocol));
983 map.insert("client_model".into(), json!("closed_loop"));
984 map.insert("provenance".into(), to_value(&self.provenance));
985 map.insert("tokenization".into(), to_value(&self.tokenization));
986 insert_counts(&mut map, bands);
987 map.insert(
988 "short_of_n_predict".into(),
989 json!(sum(bands, |b| b.short_of_n_predict)),
990 );
991 map.insert("drain_ms".into(), json!(receipt_drain_ms(bands)));
992 map.insert("n".into(), json!(samples.len()));
993 map.insert("samples_ms".into(), json!(samples));
994 map.insert("ladder".into(), to_value(&self.ladder));
995 let roofline = self.roofline.and_then(Roofline::tok_per_sec);
996 let render_ctx = RenderContexts {
997 subject: RenderContext {
998 agg1: band_metric(bands, 1, |b| b.aggregate_tok_per_sec),
999 dec1: band_metric(bands, 1, |b| b.decode_tok_per_sec),
1000 roofline,
1001 },
1002 comparator: RenderContext {
1007 agg1: baseline_metric(bands, 1, |b| b.aggregate_tok_per_sec),
1008 dec1: baseline_metric(bands, 1, |b| b.decode_tok_per_sec),
1009 roofline,
1010 },
1011 };
1012 map.insert(
1013 "bands".into(),
1014 Value::Array(
1015 bands
1016 .iter()
1017 .map(|b| band_json(b, &render_ctx.subject, Some(&render_ctx)))
1018 .collect(),
1019 ),
1020 );
1021 if let Some(kv) = self.kv {
1022 map.insert("kv".into(), to_value(&kv));
1023 }
1024 map.insert("unproduced_fields".into(), json!(self.unproduced(bands)));
1025 Value::Object(map)
1026 }
1027
1028 fn unproduced(&self, bands: &[DerivedBand]) -> Vec<String> {
1029 let mut out = vec![SERVER_ONLY_FIELDS.to_string()];
1030 match &self.kv {
1031 None => out.push(
1032 "Arm D `kv` block (bytes_used, bytes_reserved, admission_rejected, \
1033 preempted_swap) — server-reported. Absent here, so this receipt is legal at \
1034 merge phase and correctly FAILS at release phase rather than carrying invented \
1035 memory figures."
1036 .to_string(),
1037 ),
1038 Some(kv) => {
1039 let uncounted = kv.uncounted_fields();
1040 if !uncounted.is_empty() {
1041 out.push(format!(
1042 "Arm D {uncounted:?} — the server reported the KV byte figures but not \
1043 these counters: the mechanism they would count does not exist on this \
1044 build. They are null rather than 0, because \"not counted\" and \
1045 \"counted none\" are different facts and Arm D reads one of them as \
1046 evidence."
1047 ));
1048 }
1049 }
1050 }
1051 if self.roofline.is_none() {
1052 out.push(
1053 "PP-23 roofline_tok_per_sec — no `[V]` memory bandwidth is committed for this \
1054 host, so the ceiling is null on every band. A vendor GB/s figure is not a \
1055 measurement (PP-12)."
1056 .to_string(),
1057 );
1058 }
1059 if self.ladder.is_underived() {
1060 out.push(format!(
1061 "PP-24 ladder.slots_admitted — neither lane reported a slot count, so \
1062 ladder.derived is the declared set {:?} on no evidence. The band ceiling is \
1063 server-reported (PP-13) and this run did not observe one.",
1064 self.ladder.declared
1065 ));
1066 }
1067 if self.provenance.server_config.is_none() {
1068 out.push(
1069 "PP-2 provenance.server_config — `GET /v1/effective-config` was not stored, so \
1070 resolved max_batch, GpuProfile, scheduler identity and the memory fields are \
1071 absent. Every one of them is server-reported; none is inferred here."
1072 .to_string(),
1073 );
1074 }
1075 out.extend(bands.iter().flat_map(|b| b.unproduced.clone()));
1076 out
1077 }
1078}
1079
1080struct RenderContext {
1089 agg1: Option<f64>,
1090 dec1: Option<f64>,
1091 roofline: Option<f64>,
1092}
1093
1094struct RenderContexts {
1096 subject: RenderContext,
1097 comparator: RenderContext,
1098}
1099
1100fn band_metric(
1101 bands: &[DerivedBand],
1102 concurrency: u32,
1103 f: impl Fn(&DerivedBand) -> Option<f64>,
1104) -> Option<f64> {
1105 bands
1106 .iter()
1107 .find(|b| b.concurrency == concurrency)
1108 .and_then(f)
1109}
1110
1111fn baseline_metric(
1114 bands: &[DerivedBand],
1115 concurrency: u32,
1116 f: impl Fn(&DerivedBand) -> Option<f64>,
1117) -> Option<f64> {
1118 match &bands
1119 .iter()
1120 .find(|b| b.concurrency == concurrency)?
1121 .comparator
1122 {
1123 ComparatorStatus::Measured(join) => f(join.baseline()),
1124 ComparatorStatus::NotApplicable { .. } | ComparatorStatus::Unmeasured { .. } => None,
1125 }
1126}
1127
1128fn receipt_drain_ms(bands: &[DerivedBand]) -> f64 {
1134 bands.iter().map(|b| b.drain_ms).fold(0.0_f64, f64::max)
1135}
1136
1137fn insert_counts(map: &mut Map<String, Value>, bands: &[DerivedBand]) {
1138 map.insert("requested".into(), json!(sum(bands, |b| b.requested)));
1139 map.insert("completed".into(), json!(sum(bands, |b| b.completed)));
1140 map.insert("timeouts".into(), json!(sum(bands, |b| b.timeouts)));
1141 map.insert("truncated".into(), json!(sum(bands, |b| b.truncated)));
1142 map.insert("errors".into(), json!(sum(bands, |b| b.errors)));
1143}
1144
1145fn sum(bands: &[DerivedBand], f: impl Fn(&DerivedBand) -> usize) -> usize {
1146 bands.iter().map(f).sum()
1147}
1148
1149fn samples_ms(bands: &[DerivedBand]) -> Vec<f64> {
1150 bands.iter().flat_map(|b| b.latencies_ms.clone()).collect()
1151}
1152
1153fn validate_samples(samples: &[f64]) -> Result<(), String> {
1155 if samples.is_empty() {
1156 return Err(
1157 "samples_ms would be empty — no band completed a single request, and a \
1158 receipt with no retained samples permanently forecloses the bootstrap (PP-7)"
1159 .to_string(),
1160 );
1161 }
1162 let first = samples[0];
1163 if samples.len() > 1 && samples.iter().all(|s| (s - first).abs() < f64::EPSILON) {
1164 return Err(format!(
1165 "samples_ms: all {} samples identical ({first}) — a real timing distribution is not \
1166 constant; this is the fabricated-measurement shape (F12)",
1167 samples.len()
1168 ));
1169 }
1170 Ok(())
1171}
1172
1173fn to_value<T: Serialize>(value: &T) -> Value {
1176 serde_json::to_value(value).unwrap_or(Value::Null)
1177}
1178
1179fn band_json(b: &DerivedBand, ctx: &RenderContext, comparator: Option<&RenderContexts>) -> Value {
1186 let mut map = Map::new();
1187 map.insert("concurrency".into(), json!(b.concurrency));
1188 map.insert("replicate".into(), json!(b.replicate));
1189 map.insert("status".into(), json!(b.status.wire_token()));
1190 if let Some(agg) = b.aggregate_tok_per_sec {
1191 map.insert("aggregate_tok_per_sec".into(), json!(agg));
1192 }
1193 map.insert("tokens_total".into(), json!(b.tokens_total));
1194 map.insert("span_ms".into(), json!(b.span_ms));
1195 map.insert("window_ms".into(), json!(b.window_ms));
1196 map.insert("drain_ms".into(), json!(b.drain_ms));
1197 map.insert("requested".into(), json!(b.requested));
1198 map.insert("completed".into(), json!(b.completed));
1199 map.insert("timeouts".into(), json!(b.timeouts));
1200 map.insert("truncated".into(), json!(b.truncated));
1201 map.insert("errors".into(), json!(b.errors));
1202 map.insert("short_of_n_predict".into(), json!(b.short_of_n_predict));
1203 map.insert("suspect".into(), json!(b.suspect));
1204 map.insert(
1205 "stream_mode".into(),
1206 b.stream_mode.map_or(Value::Null, |m| to_value(&m)),
1207 );
1208 map.insert(
1209 "stream_witness".into(),
1210 b.stream_witness.map_or(Value::Null, |w| to_value(&w)),
1211 );
1212 map.insert(
1213 "witness".into(),
1214 b.witness.as_ref().map_or(Value::Null, to_value),
1215 );
1216 map.insert("scaling_efficiency".into(), scaling_efficiency(b, ctx));
1217 map.insert("overhead_share".into(), overhead_share(b, ctx));
1218 map.insert(
1219 "roofline_tok_per_sec".into(),
1220 ctx.roofline.map_or(Value::Null, |r| json!(r)),
1221 );
1222 map.insert(
1223 "samples_file".into(),
1224 b.samples_file.as_ref().map_or(Value::Null, to_value),
1225 );
1226 map.insert("samples".into(), to_value(&b.samples));
1227 map.insert(
1228 "join_key".into(),
1229 b.join_key.as_ref().map_or(Value::Null, to_value),
1230 );
1231 if let Some(run_id) = &b.run_id {
1232 map.insert("run_id".into(), json!(run_id.as_str()));
1233 }
1234 insert_optional_latency(&mut map, b);
1235 if let Some(contexts) = comparator {
1236 insert_comparator(&mut map, &b.comparator, contexts);
1237 }
1238 Value::Object(map)
1239}
1240
1241fn insert_optional_latency(map: &mut Map<String, Value>, b: &DerivedBand) {
1243 for (key, value) in [
1244 ("decode_tok_per_sec", b.decode_tok_per_sec),
1245 ("ttft_p50_ms", b.ttft_p50_ms),
1246 ("ttft_p95_ms", b.ttft_p95_ms),
1247 ("itl_p50_ms", b.itl_p50_ms),
1248 ("itl_p95_ms", b.itl_p95_ms),
1249 ] {
1250 if let Some(v) = value {
1251 map.insert(key.into(), json!(v));
1252 }
1253 }
1254 if let Some(prefill) = b.prefill_tok_per_sec {
1255 map.insert("prefill_tok_per_sec".into(), json!(prefill));
1256 map.insert("prefill_source".into(), json!("server"));
1258 }
1259}
1260
1261fn scaling_efficiency(b: &DerivedBand, ctx: &RenderContext) -> Value {
1266 if b.concurrency <= 1 {
1267 return Value::Null;
1268 }
1269 match (b.aggregate_tok_per_sec, ctx.agg1) {
1270 (Some(agg), Some(agg1)) if agg1 > 0.0 => {
1271 json!(agg / (f64::from(b.concurrency) * agg1))
1272 }
1273 _ => Value::Null,
1274 }
1275}
1276
1277fn overhead_share(b: &DerivedBand, ctx: &RenderContext) -> Value {
1281 if b.concurrency != 1 {
1282 return Value::Null;
1283 }
1284 match (ctx.agg1, ctx.dec1) {
1285 (Some(agg1), Some(dec1)) if dec1 > 0.0 => json!(agg1 / dec1),
1286 _ => Value::Null,
1287 }
1288}
1289
1290fn insert_comparator(
1291 map: &mut Map<String, Value>,
1292 status: &ComparatorStatus,
1293 ctx: &RenderContexts,
1294) {
1295 map.insert("comparator_status".into(), json!(status.wire_token()));
1296 match status {
1297 ComparatorStatus::NotApplicable {
1298 decided_by,
1299 reason,
1300 budget,
1301 } => {
1302 map.insert("comparator_decided_by".into(), json!(decided_by));
1303 map.insert("comparator_reason".into(), json!(reason));
1304 if let Some(b) = budget {
1305 map.insert("comparator_budget".into(), json!(b));
1306 }
1307 map.insert("baseline".into(), Value::Null);
1308 map.insert("ratios".into(), Value::Null);
1309 }
1310 ComparatorStatus::Unmeasured {
1311 owner,
1312 reason,
1313 admission_capped,
1314 } => {
1315 map.insert("comparator_owner".into(), json!(owner));
1316 map.insert("comparator_reason".into(), json!(reason));
1317 if let Some(cap) = admission_capped {
1318 map.insert("comparator_admission_capped".into(), to_value(cap));
1319 }
1320 map.insert("baseline".into(), Value::Null);
1321 map.insert("ratios".into(), Value::Null);
1322 }
1323 ComparatorStatus::Measured(join) => {
1324 map.insert(
1329 "baseline".into(),
1330 band_json(join.baseline(), &ctx.comparator, None),
1331 );
1332 map.insert("ratios".into(), to_value(join.ratios()));
1333 }
1334 }
1335}
1336
1337fn is_sha256(value: &str) -> bool {
1338 value.len() == 64
1339 && value
1340 .bytes()
1341 .all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase())
1342}
1343
1344#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1363#[serde(deny_unknown_fields)]
1364pub struct Receipt {
1365 pub spec: String,
1367 pub schema_version: u32,
1369 pub run_id: RunId,
1371 pub commit: String,
1373 pub workload: Workload,
1375 pub protocol: ProtocolParams,
1377 pub client_model: String,
1379 pub provenance: Provenance,
1381 pub tokenization: TokenizationBlock,
1383 pub requested: usize,
1385 pub completed: usize,
1387 pub timeouts: usize,
1389 pub truncated: usize,
1391 pub errors: usize,
1393 pub short_of_n_predict: usize,
1395 pub drain_ms: f64,
1397 pub n: usize,
1399 pub samples_ms: Vec<f64>,
1401 pub ladder: Ladder,
1403 pub bands: Vec<ReceiptBand>,
1405 #[serde(default, skip_serializing_if = "Option::is_none")]
1407 pub kv: Option<KvBlock>,
1408 pub unproduced_fields: Vec<String>,
1410 #[serde(default, skip_serializing_if = "Option::is_none")]
1415 pub signature: Option<serde_json::Value>,
1416}
1417
1418#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1420#[serde(deny_unknown_fields)]
1421#[allow(clippy::struct_excessive_bools)]
1422pub struct ReceiptBand {
1423 pub concurrency: u32,
1425 pub replicate: u32,
1427 pub status: String,
1429 #[serde(default, skip_serializing_if = "Option::is_none")]
1431 pub aggregate_tok_per_sec: Option<f64>,
1432 pub tokens_total: u64,
1434 pub span_ms: f64,
1436 pub window_ms: f64,
1438 pub drain_ms: f64,
1440 pub requested: usize,
1442 pub completed: usize,
1444 pub timeouts: usize,
1446 pub truncated: usize,
1448 pub errors: usize,
1450 pub short_of_n_predict: usize,
1452 pub suspect: Vec<String>,
1454 pub stream_mode: Option<StreamMode>,
1456 pub stream_witness: Option<StreamWitness>,
1458 pub witness: Option<BatchInvarianceWitness>,
1460 pub scaling_efficiency: Option<f64>,
1462 pub overhead_share: Option<f64>,
1464 pub roofline_tok_per_sec: Option<f64>,
1466 pub samples_file: Option<SamplesFile>,
1468 pub samples: Vec<SampleRow>,
1470 pub join_key: Option<JoinKey>,
1472 #[serde(default, skip_serializing_if = "Option::is_none")]
1474 pub run_id: Option<RunId>,
1475 #[serde(default, skip_serializing_if = "Option::is_none")]
1477 pub decode_tok_per_sec: Option<f64>,
1478 #[serde(default, skip_serializing_if = "Option::is_none")]
1480 pub ttft_p50_ms: Option<f64>,
1481 #[serde(default, skip_serializing_if = "Option::is_none")]
1483 pub ttft_p95_ms: Option<f64>,
1484 #[serde(default, skip_serializing_if = "Option::is_none")]
1486 pub itl_p50_ms: Option<f64>,
1487 #[serde(default, skip_serializing_if = "Option::is_none")]
1489 pub itl_p95_ms: Option<f64>,
1490 #[serde(default, skip_serializing_if = "Option::is_none")]
1492 pub prefill_tok_per_sec: Option<f64>,
1493 #[serde(default, skip_serializing_if = "Option::is_none")]
1495 pub prefill_source: Option<String>,
1496 #[serde(default, skip_serializing_if = "Option::is_none")]
1498 pub comparator_status: Option<String>,
1499 #[serde(default, skip_serializing_if = "Option::is_none")]
1501 pub comparator_owner: Option<String>,
1502 #[serde(default, skip_serializing_if = "Option::is_none")]
1504 pub comparator_decided_by: Option<String>,
1505 #[serde(default, skip_serializing_if = "Option::is_none")]
1507 pub comparator_reason: Option<String>,
1508 #[serde(default, skip_serializing_if = "Option::is_none")]
1510 pub comparator_budget: Option<String>,
1511 #[serde(default, skip_serializing_if = "Option::is_none")]
1513 pub comparator_admission_capped: Option<AdmissionCap>,
1514 #[serde(default, skip_serializing_if = "Option::is_none")]
1516 pub baseline: Option<Box<ReceiptBand>>,
1517 #[serde(default, skip_serializing_if = "Option::is_none")]
1519 pub ratios: Option<BandRatios>,
1520}
1521
1522impl Receipt {
1523 pub fn parse(text: &str) -> Result<Self, String> {
1528 serde_json::from_str(text).map_err(|e| format!("parsing receipt: {e}"))
1529 }
1530
1531 pub fn validate(&self) -> Result<(), String> {
1539 if self.spec != SPEC_ID {
1540 return Err(format!(
1541 "receipt.spec is {:?}, expected {SPEC_ID:?}",
1542 self.spec
1543 ));
1544 }
1545 if self.schema_version != SCHEMA_VERSION {
1546 return Err(format!(
1547 "receipt.schema_version is {}, expected {SCHEMA_VERSION} — a receipt at another \
1548 version is historical and is never a baseline (PP-4)",
1549 self.schema_version
1550 ));
1551 }
1552 self.provenance.validate()?;
1553 self.tokenization.validate()?;
1554 if self.bands.is_empty() {
1555 return Err(
1556 "receipt has no bands — a measurement over zero bands is a vacuous \
1557 pass"
1558 .to_string(),
1559 );
1560 }
1561 self.check_run_id()?;
1562 for band in &self.bands {
1563 band.validate()?;
1564 }
1565 Ok(())
1566 }
1567
1568 fn check_run_id(&self) -> Result<(), String> {
1577 let recomputed = RunId::derive(
1578 &self.provenance.started_utc,
1579 &self.provenance.host,
1580 &self.provenance.client.sha256,
1581 self.provenance.client.pid,
1582 );
1583 if recomputed == self.run_id {
1584 return Ok(());
1585 }
1586 Err(format!(
1587 "PP-3: run_id is {} but sha256(started_utc ‖ host ‖ client.sha256 ‖ client.pid)[..32] over this receipt's own provenance is {} — the id is DERIVED, and one that its own contents do not reproduce identifies nothing",
1588 self.run_id.as_str(),
1589 recomputed.as_str()
1590 ))
1591 }
1592}
1593
1594impl ReceiptBand {
1595 pub fn validate(&self) -> Result<(), String> {
1602 let known = BandStatus::vocabulary()
1603 .iter()
1604 .any(|s| s.wire_token() == self.status);
1605 if !known {
1606 return Err(format!(
1607 "band c={}: status {:?} is outside the §7.4 vocabulary {:?}",
1608 self.concurrency,
1609 self.status,
1610 BandStatus::vocabulary()
1611 .iter()
1612 .map(|s| s.wire_token())
1613 .collect::<Vec<_>>()
1614 ));
1615 }
1616 if self.ratios.is_some() && self.baseline.is_none() {
1617 return Err(format!(
1618 "PP-3 band c={}: `ratios` without a `baseline` — a ratio is representable only \
1619 against a baseline object that itself passes every receipt rule and shares the \
1620 run_id",
1621 self.concurrency
1622 ));
1623 }
1624 if let Some(baseline) = &self.baseline {
1625 if baseline.baseline.is_some() || baseline.ratios.is_some() {
1626 return Err(format!(
1627 "PP-3 band c={}: the baseline carries its own baseline/ratios — a baseline is \
1628 one comparator lane, not a chain of them",
1629 self.concurrency
1630 ));
1631 }
1632 baseline.validate()?;
1633 }
1634 Ok(())
1635 }
1636}
1637
1638#[cfg(test)]
1639mod producer_tests {
1640 use super::*;
1644 use std::io::Write;
1645
1646 #[test]
1649 fn compute_class_roundtrip_is_the_only_spelling() {
1650 for c in [
1651 ComputeClass::Cpu,
1652 ComputeClass::Cuda,
1653 ComputeClass::Metal,
1654 ComputeClass::Wgpu,
1655 ComputeClass::Unknown,
1656 ] {
1657 assert_eq!(
1658 ComputeClass::from_str(c.wire_token()).expect("wire token must parse"),
1659 c
1660 );
1661 }
1662 }
1663
1664 #[test]
1667 fn the_wire_tokens_are_bench_receipt_pys_compute_classes() {
1668 let tokens: Vec<&str> = ["cpu", "cuda", "metal", "wgpu", "unknown"].into();
1669 for t in &tokens {
1670 assert!(ComputeClass::from_str(t).is_ok(), "{t} must parse");
1671 }
1672 assert!(ComputeClass::from_str("tpu").is_err());
1673 assert!(ComputeClass::from_str("gpu").is_err());
1674 assert!(
1675 ComputeClass::from_str("CUDA").is_err(),
1676 "case is load-bearing"
1677 );
1678 }
1679
1680 #[test]
1681 fn workload_roundtrips_and_refuses_anything_else() {
1682 for w in [Workload::W1, Workload::W2] {
1683 assert_eq!(Workload::from_str(w.wire_token()).expect("parses"), w);
1684 }
1685 assert!(Workload::from_str("W3").is_err());
1686 assert!(Workload::from_str("w1").is_err());
1687 }
1688
1689 #[test]
1692 fn sha256_file_produces_a_digest_provenance_accepts() {
1693 let dir = tempfile::tempdir().expect("tempdir");
1694 let path = dir.path().join("payload.bin");
1695 let mut f = std::fs::File::create(&path).expect("create");
1696 f.write_all(b"abc").expect("write");
1697 drop(f);
1698
1699 let digest = sha256_file(&path).expect("hashes");
1700 assert_eq!(
1702 digest,
1703 "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
1704 );
1705 assert_eq!(digest.len(), 64);
1706 assert!(is_sha256(&digest), "must satisfy the receipt's own check");
1707 }
1708
1709 #[test]
1710 fn sha256_file_reports_a_missing_file_rather_than_a_digest() {
1711 assert!(sha256_file(Path::new("/nonexistent/perf-025")).is_err());
1712 }
1713
1714 #[test]
1717 fn the_run_id_is_derived_from_the_receipts_own_contents() {
1718 let a = RunId::derive("2026-09-02T10:11:12.345Z", "lambda", &"c".repeat(64), 4242);
1719 let b = RunId::derive("2026-09-02T10:11:12.345Z", "lambda", &"c".repeat(64), 4242);
1720 assert_eq!(a, b, "the same four facts give the same id");
1721 assert_eq!(a.as_str().len(), 32);
1722 assert!(a
1723 .as_str()
1724 .bytes()
1725 .all(|c| c.is_ascii_hexdigit() && !c.is_ascii_uppercase()));
1726
1727 for changed in [
1728 RunId::derive("2026-09-02T10:11:12.346Z", "lambda", &"c".repeat(64), 4242),
1729 RunId::derive("2026-09-02T10:11:12.345Z", "gx10", &"c".repeat(64), 4242),
1730 RunId::derive("2026-09-02T10:11:12.345Z", "lambda", &"d".repeat(64), 4242),
1731 RunId::derive("2026-09-02T10:11:12.345Z", "lambda", &"c".repeat(64), 4243),
1732 ] {
1733 assert_ne!(a, changed, "every input must move the id");
1734 }
1735 }
1736
1737 #[test]
1739 fn a_malformed_run_id_is_refused() {
1740 assert!(RunId::try_from("abc".to_string()).is_err());
1741 assert!(RunId::try_from("A".repeat(32)).is_err(), "case matters");
1742 assert!(RunId::try_from("z".repeat(32)).is_err(), "hex only");
1743 assert!(RunId::try_from("a".repeat(33)).is_err());
1744 assert!(RunId::try_from("a".repeat(32)).is_ok());
1745 }
1746
1747 #[test]
1749 fn started_utc_must_be_rfc3339_utc() {
1750 assert!(validate_rfc3339_utc_millis("t", "2026-09-02T10:11:12.345Z").is_ok());
1751 for bad in [
1752 "",
1753 "2026-09-02",
1754 "2026-09-02T10:11:12Z",
1755 "2026-09-02T10:11:12.345+00:00",
1756 "2026-09-02t10:11:12.345Z",
1757 "2026-09-02T10:11:12.3456Z",
1758 "not-a-time-at-all-....Z",
1759 ] {
1760 assert!(
1761 validate_rfc3339_utc_millis("t", bad).is_err(),
1762 "{bad:?} must be refused"
1763 );
1764 }
1765 }
1766
1767 #[test]
1770 fn canonical_timestamps_sort_chronologically() {
1771 let mut times = vec![
1772 "2026-12-01T00:00:00.000Z".to_string(),
1773 "2026-09-02T10:11:12.345Z".to_string(),
1774 "2026-09-02T10:11:12.344Z".to_string(),
1775 "2025-01-01T00:00:00.000Z".to_string(),
1776 ];
1777 times.sort();
1778 assert_eq!(
1779 times,
1780 vec![
1781 "2025-01-01T00:00:00.000Z",
1782 "2026-09-02T10:11:12.344Z",
1783 "2026-09-02T10:11:12.345Z",
1784 "2026-12-01T00:00:00.000Z",
1785 ]
1786 );
1787 }
1788
1789 #[test]
1791 fn ladder_derives_from_the_minimum_admission() {
1792 let declared = [1_u32, 4, 8, 16];
1793 let l = Ladder::derive(
1794 &declared,
1795 SlotsAdmitted {
1796 apr: Some(11),
1797 llama: Some(16),
1798 },
1799 );
1800 assert_eq!(l.derived, vec![1, 4, 8], "c=16 exceeds the subject's 11");
1801 assert!(!l.is_underived());
1802
1803 let other_way = Ladder::derive(
1804 &declared,
1805 SlotsAdmitted {
1806 apr: Some(16),
1807 llama: Some(4),
1808 },
1809 );
1810 assert_eq!(other_way.derived, vec![1, 4], "the comparator caps too");
1811
1812 let one_lane = Ladder::derive(
1813 &declared,
1814 SlotsAdmitted {
1815 apr: Some(8),
1816 llama: None,
1817 },
1818 );
1819 assert_eq!(one_lane.derived, vec![1, 4, 8]);
1820
1821 let blind = Ladder::derive(
1822 &declared,
1823 SlotsAdmitted {
1824 apr: None,
1825 llama: None,
1826 },
1827 );
1828 assert_eq!(
1829 blind.derived,
1830 vec![1, 4, 8, 16],
1831 "no evidence does not narrow the ladder"
1832 );
1833 assert!(blind.is_underived(), "…but it is named as unevidenced");
1834 }
1835
1836 #[cfg(not(target_arch = "wasm32"))]
1840 #[test]
1841 fn now_utc_millis_is_the_shape_the_validator_accepts() {
1842 let now = now_utc_millis();
1843 validate_rfc3339_utc_millis("now", &now)
1844 .unwrap_or_else(|e| panic!("{now:?} must satisfy the receipt's own check: {e}"));
1845 assert!(now.ends_with('Z'));
1846 assert_eq!(now.len(), 24);
1847 }
1848
1849 #[test]
1852 fn the_roofline_is_bandwidth_over_model_bytes() {
1853 let r = Roofline {
1854 bandwidth_bytes_per_sec: 1_008_000_000_000.0,
1855 model_bytes: 4_683_073_440,
1856 };
1857 let ceiling = r.tok_per_sec().expect("a sized model has a ceiling");
1858 assert!((ceiling - 215.2).abs() < 0.1, "{ceiling}");
1859 assert!(Roofline {
1860 bandwidth_bytes_per_sec: 1.0,
1861 model_bytes: 0
1862 }
1863 .tok_per_sec()
1864 .is_none());
1865 assert!(Roofline {
1866 bandwidth_bytes_per_sec: 0.0,
1867 model_bytes: 10
1868 }
1869 .tok_per_sec()
1870 .is_none());
1871 }
1872}