Skip to main content

harn_kernel/benchmark/
schema.rs

1//! JSON Schema projection of the benchmark receipt contract.
2//!
3//! The Rust receipt types and validation policy are the semantic owner. The
4//! CLI writes this projection to `spec/schemas/`; consumers never maintain a
5//! second hand-authored schema.
6
7use serde_json::{json, Value};
8
9use super::{
10    PORTABLE_BENCHMARK_SCHEMA_VERSION, PORTABLE_MAX_COMPILE_ITERATIONS,
11    PORTABLE_MAX_DISPATCH_ITERATIONS, PORTABLE_MAX_WORKERS,
12};
13
14/// Generate the public JSON Schema for [`super::PortableBenchmarkReceipt`].
15pub fn portable_benchmark_json_schema() -> Value {
16    json!({
17      "$schema": "https://json-schema.org/draft/2020-12/schema",
18      "$id": "https://harnlang.com/schemas/portable-kernel-benchmark.v1.schema.json",
19      "$comment": "Generated by `make gen-portable-benchmark-schema`; do not edit by hand.",
20      "title": "Harn Portable Kernel benchmark receipt v1",
21      "type": "object",
22      "additionalProperties": false,
23      "required": [
24        "schemaVersion", "target", "source", "entry", "entryKind",
25        "artifactBytes", "artifactDigest", "iterations", "workers",
26        "provenance", "initializationMs", "compile", "decode", "dispatch",
27        "terminalDigest"
28      ],
29      "properties": {
30        "schemaVersion": { "const": PORTABLE_BENCHMARK_SCHEMA_VERSION },
31        "target": { "enum": ["native", "browser"] },
32        "source": { "type": "string", "minLength": 1 },
33        "entry": { "type": "string", "minLength": 1 },
34        "entryKind": { "enum": ["function", "pipeline"] },
35        "artifactBytes": { "type": "integer", "minimum": 1 },
36        "artifactDigest": { "$ref": "#/$defs/digest" },
37        "iterations": {
38          "type": "integer", "minimum": 1,
39          "maximum": PORTABLE_MAX_DISPATCH_ITERATIONS
40        },
41        "workers": {
42          "type": "integer", "minimum": 1, "maximum": PORTABLE_MAX_WORKERS
43        },
44        "provenance": { "$ref": "#/$defs/provenance" },
45        "initializationMs": { "type": ["number", "null"], "minimum": 0 },
46        "compile": { "$ref": "#/$defs/compileMeasurements" },
47        "decode": {
48          "oneOf": [
49            { "$ref": "#/$defs/compileStatistics" },
50            { "type": "null" }
51          ]
52        },
53        "dispatch": { "$ref": "#/$defs/dispatchMeasurements" },
54        "terminalDigest": { "$ref": "#/$defs/digest" }
55      },
56      "$defs": {
57        "digest": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
58        "provenance": {
59          "type": "object",
60          "additionalProperties": false,
61          "required": [
62            "harnVersion", "kernelVersion", "artifactFormatVersion",
63            "semanticAbiFingerprint", "opcodeAbiFingerprint", "buildProfile",
64            "os", "arch"
65          ],
66          "properties": {
67            "harnVersion": { "type": "string", "minLength": 1 },
68            "kernelVersion": { "type": "string", "minLength": 1 },
69            "artifactFormatVersion": { "type": "integer", "minimum": 1 },
70            "semanticAbiFingerprint": { "$ref": "#/$defs/digest" },
71            "opcodeAbiFingerprint": { "$ref": "#/$defs/digest" },
72            "buildProfile": { "enum": ["debug", "release"] },
73            "os": { "type": "string", "minLength": 1 },
74            "arch": { "type": "string", "minLength": 1 }
75          }
76        },
77        "statistics": {
78          "type": "object",
79          "additionalProperties": false,
80          "required": [
81            "iterations", "min_ms", "mean_ms", "p50_ms", "p95_ms",
82            "max_ms", "stddev_ms", "total_ms"
83          ],
84          "properties": {
85            "iterations": { "type": "integer", "minimum": 1 },
86            "min_ms": { "type": "number", "minimum": 0 },
87            "mean_ms": { "type": "number", "minimum": 0 },
88            "p50_ms": { "type": "number", "minimum": 0 },
89            "p95_ms": { "type": "number", "minimum": 0 },
90            "max_ms": { "type": "number", "minimum": 0 },
91            "stddev_ms": { "type": "number", "minimum": 0 },
92            "total_ms": { "type": "number", "minimum": 0 }
93          }
94        },
95        "compileStatistics": {
96          "allOf": [
97            { "$ref": "#/$defs/statistics" },
98            { "properties": { "iterations": {
99              "maximum": PORTABLE_MAX_COMPILE_ITERATIONS
100            } } }
101          ]
102        },
103        "dispatchStatistics": {
104          "allOf": [
105            { "$ref": "#/$defs/statistics" },
106            { "properties": { "iterations": {
107              "maximum": PORTABLE_MAX_DISPATCH_ITERATIONS
108            } } }
109          ]
110        },
111        "compileMeasurements": {
112          "type": "object",
113          "additionalProperties": false,
114          "required": ["firstMs", "repeated"],
115          "properties": {
116            "firstMs": { "type": "number", "minimum": 0 },
117            "repeated": { "$ref": "#/$defs/compileStatistics" }
118          }
119        },
120        "dispatchMeasurements": {
121          "type": "object",
122          "additionalProperties": false,
123          "required": [
124            "firstMs", "repeated", "batchWallMs", "throughputPerSecond"
125          ],
126          "properties": {
127            "firstMs": { "type": "number", "minimum": 0 },
128            "repeated": { "$ref": "#/$defs/dispatchStatistics" },
129            "batchWallMs": { "type": "number", "exclusiveMinimum": 0 },
130            "throughputPerSecond": { "type": "number", "exclusiveMinimum": 0 }
131          }
132        }
133      }
134    })
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140
141    #[test]
142    fn schema_projects_owned_limits_and_receipt_fields() {
143        let schema = portable_benchmark_json_schema();
144        assert_eq!(
145            schema["properties"]["schemaVersion"]["const"],
146            PORTABLE_BENCHMARK_SCHEMA_VERSION
147        );
148        assert_eq!(
149            schema["properties"]["iterations"]["maximum"],
150            PORTABLE_MAX_DISPATCH_ITERATIONS
151        );
152        assert_eq!(
153            schema["properties"]["workers"]["maximum"],
154            PORTABLE_MAX_WORKERS
155        );
156        let required = schema["required"].as_array().expect("required fields");
157        let properties = schema["properties"].as_object().expect("properties");
158        assert_eq!(required.len(), properties.len());
159        assert!(properties.keys().all(|key| required.contains(&json!(key))));
160    }
161}