Skip to main content

supercov_contracts/
lib.rs

1//! Frozen, implementation-neutral Supercov engine contracts.
2//!
3//! This crate does not contain coverage behavior. It makes contract drift a
4//! compile/test failure while the shipped implementation and Rust candidate
5//! coexist. Independent specifications and conformance oracles—not either
6//! implementation—decide whether a contract is correct.
7
8use std::collections::BTreeSet;
9
10use serde::{Deserialize, Serialize};
11
12pub const CONTRACT_VERSION: u32 = 1;
13pub const EVIDENCE_ARCHIVE_SCHEMA_VERSION: u32 = 3;
14pub const EVIDENCE_ARCHIVE_MAGIC: &str = "SUPERCOV-EVIDENCE-3\n";
15pub const COVERAGE_MODEL_SCHEMA_VERSION: u32 = 1;
16pub const COVERAGE_MODEL_MAX_IDENTIFIER_BYTES: usize = 64;
17pub const COVERAGE_MODEL_MAX_DESCRIPTION_BYTES: usize = 4_096;
18pub const COVERAGE_MODEL_MAX_SURFACES_PER_LIST: usize = 256;
19pub const AGENT_JSON_SCHEMA_VERSION: u32 = 1;
20pub const AGENT_JSON_MAX_BYTES: usize = 65_536;
21pub const DEFAULT_PAGE_SIZE: usize = 20;
22pub const PROCESS_SUPERVISION_SCHEMA_VERSION: u32 = 1;
23pub const DEFAULT_DIAGNOSTIC_INTERVAL_MS: u64 = 60_000;
24pub const COMMAND_TIMEOUT_EXIT_CODE: i32 = 124;
25pub const COMMAND_TERMINATION_GRACE_MS: u64 = 5_000;
26pub const PROBE_V2_VERSION: u32 = 2;
27pub const PROBE_V2_RADIX: u32 = 3;
28pub const PROBE_V2_JS_MAX_CONDITIONS: usize = 32;
29pub const LANGUAGE_FRONTEND_PROTOCOL_VERSION: u32 = 2;
30pub const RUST_PROBE_TRANSPORT_PROTOCOL_VERSION: u32 = 1;
31pub const RUST_PROBE_TRANSPORT_MAGIC: &str = "SCVRUST1";
32pub const RUST_PROBE_TRANSPORT_V3_PROTOCOL_VERSION: u32 = 3;
33pub const RUST_PROBE_TRANSPORT_V3_MAGIC: &str = "SCVRUST3";
34pub const RUST_PROBE_TRANSPORT_HEADER_SIZE: usize = 128;
35pub const RUST_PROBE_TRANSPORT_DESCRIPTOR_SIZE: usize = 40;
36pub const RUST_PROBE_TRANSPORT_TOKEN_SIZE: usize = 16;
37pub const RUST_LIBTEST_EVENT_PROTOCOL_VERSION: u32 = 1;
38pub const RUST_LIBTEST_EVENT_MAGIC: &str = "SCVLTST1";
39pub const RUST_LIBTEST_EVENT_HEADER_SIZE: usize = 64;
40pub const RUST_LIBTEST_EVENT_RECORD_HEADER_SIZE: usize = 48;
41pub const RUST_LIBTEST_EVENT_TOKEN_SIZE: usize = 16;
42pub const RUST_LIBTEST_EVENT_MAX_NAME_BYTES: usize = 1_048_576;
43pub const RUST_LIBTEST_COMPANION_BUNDLE_SCHEMA_VERSION: u32 = 3;
44
45pub const ERROR_CODES: &[&str] = &[
46    "AMBIGUOUS_SELECTOR",
47    "DECISION_NOT_FOUND",
48    "FILTER_UNAVAILABLE",
49    "INTERNAL_ERROR",
50    "INVALID_ARGUMENT",
51    "MINIMIZATION_COMPLEXITY_LIMIT",
52    "NO_RUNS",
53    "RESPONSE_TOO_LARGE",
54    "RUN_NOT_FOUND",
55    "SCOPE_UNAVAILABLE",
56    "SOURCE_NOT_FOUND",
57    "TARGET_UNREACHABLE",
58    "TEST_FILTER_EMPTY",
59    "TEST_NOT_FOUND",
60    "UNATTRIBUTED_EVIDENCE",
61    "UNKNOWN_COMMAND",
62];
63
64#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
65#[serde(rename_all = "camelCase")]
66pub struct ContractRegistry {
67    pub contract_version: u32,
68    pub status: String,
69    pub resident_process: bool,
70    pub evidence_archive: EvidenceArchiveContract,
71    pub run_store: RunStoreContract,
72    pub agent_json: AgentJsonContract,
73    pub process_supervision: ProcessSupervisionContract,
74}
75
76#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
77#[serde(rename_all = "camelCase")]
78pub struct EvidenceArchiveContract {
79    pub schema_version: u32,
80    pub file: String,
81    pub format: String,
82    pub magic: String,
83}
84
85#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
86#[serde(rename_all = "camelCase")]
87pub struct RunStoreContract {
88    pub schema_version: u32,
89    pub store: String,
90    pub workspace_store: String,
91    pub published_run_files: Vec<String>,
92}
93
94#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
95#[serde(rename_all = "camelCase")]
96pub struct AgentJsonContract {
97    pub schema_version: u32,
98    pub max_bytes: usize,
99    pub default_page_size: usize,
100    pub error_codes: Vec<String>,
101}
102
103#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
104#[serde(rename_all = "camelCase")]
105pub struct ProcessSupervisionContract {
106    pub schema_version: u32,
107    pub diagnostic_interval_ms: u64,
108    pub timeout_exit_code: i32,
109    pub termination_grace_ms: u64,
110}
111
112#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
113#[serde(rename_all = "camelCase")]
114pub struct AgentPagination {
115    pub offset: usize,
116    pub limit: usize,
117    pub returned: usize,
118    pub total: usize,
119    pub has_more: bool,
120    pub next_offset: Option<usize>,
121}
122
123pub fn registry() -> Result<ContractRegistry, serde_json::Error> {
124    serde_json::from_str(include_str!("../assets/v1/contract.json"))
125}
126
127#[derive(Debug, Clone, PartialEq, Deserialize)]
128#[serde(rename_all = "camelCase")]
129pub struct ProbeV2Contract {
130    pub probe_version: u32,
131    pub semantics: String,
132    pub implementation: String,
133    pub decision_encoding: ProbeV2DecisionEncoding,
134    pub published_evidence: String,
135    pub attribution_epoch: Vec<String>,
136    pub fallback: String,
137    pub promotion: ProbeV2Promotion,
138}
139
140#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
141#[serde(rename_all = "camelCase")]
142pub struct ProbeV2DecisionEncoding {
143    pub radix: u32,
144    pub digits: ProbeV2Digits,
145    pub outcome_stored_separately: bool,
146    pub javascript_maximum_encoded_conditions: usize,
147    pub wider_decision_behavior: String,
148}
149
150#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
151pub struct ProbeV2Digits {
152    pub unreached: u32,
153    pub r#false: u32,
154    pub r#true: u32,
155}
156
157#[derive(Debug, Clone, PartialEq, Deserialize)]
158#[serde(rename_all = "camelCase")]
159pub struct ProbeV2Promotion {
160    pub realistic_median_runtime_ratio_max: f64,
161    pub semantic_equivalence_required: bool,
162    pub manifest_parity_required: bool,
163    pub evidence_parity_required: bool,
164}
165
166pub fn probe_v2_contract() -> Result<ProbeV2Contract, serde_json::Error> {
167    serde_json::from_str(include_str!("../assets/probe-v2/contract.json"))
168}
169
170#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
171#[serde(rename_all = "camelCase", deny_unknown_fields)]
172pub struct LanguageFrontendProtocolContract {
173    pub frontend_protocol_version: u32,
174    pub status: String,
175    pub manifest_model: String,
176    pub observation_model: String,
177    pub probe_model: String,
178    pub identity_axes: Vec<String>,
179    pub transition_kinds: Vec<String>,
180    pub structural_sources: Vec<String>,
181    pub execution_models: Vec<String>,
182    pub attribution_precisions: Vec<String>,
183    pub limitation_scopes: Vec<String>,
184    pub requirements: LanguageFrontendRequirements,
185}
186
187#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
188#[serde(rename_all = "camelCase", deny_unknown_fields)]
189pub struct LanguageFrontendRequirements {
190    pub complete_manifest_before_execution: bool,
191    pub unknown_obligation_fatal: bool,
192    pub identity_downgrade_requires_limitation: bool,
193    pub unknown_phase_reference_fatal: bool,
194    pub phase_causality_acyclic: bool,
195    pub selected_unstarted_has_test_identity_only: bool,
196    pub multiple_runners_per_frontend: bool,
197    pub structural_limitations_reference_manifest_ids: bool,
198    pub attribution_limitations_runner_scoped: bool,
199    pub timestamp_attribution_may_claim_causality: bool,
200    pub frontend_may_compute_coverage_verdicts: bool,
201    pub engine_owns_manifest_merge: bool,
202    pub engine_owns_evidence_validation: bool,
203    pub engine_owns_attribution_merge: bool,
204    pub engine_owns_coverage_analysis: bool,
205    pub engine_owns_persistence_and_queries: bool,
206}
207
208pub fn language_frontend_protocol_contract()
209-> Result<LanguageFrontendProtocolContract, serde_json::Error> {
210    serde_json::from_str(include_str!("../assets/frontend-v2/contract.json"))
211}
212
213#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
214#[serde(rename_all = "camelCase", deny_unknown_fields)]
215pub struct PythonCoverageImportContract {
216    pub schema_version: u32,
217    pub status: String,
218    pub producer: String,
219    pub supported_collector_cores_for_exact_contexts: Vec<String>,
220    pub requires_branch_measurement: bool,
221    pub database_access: String,
222    pub frontend_computes_verdicts: bool,
223    pub unknown_fields_fatal: bool,
224    pub preserve_unrecognized_contexts_as_background: bool,
225    pub mcdc_availability: String,
226    pub column_locations: String,
227}
228
229pub fn python_coverage_import_contract() -> Result<PythonCoverageImportContract, serde_json::Error>
230{
231    serde_json::from_str(include_str!("../assets/python-coverage-v1/contract.json"))
232}
233
234#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
235#[serde(rename_all = "camelCase", deny_unknown_fields)]
236pub struct EvidenceV3Contract {
237    pub schema_version: u32,
238    pub status: String,
239    pub magic: String,
240    pub framing: String,
241    pub required_entries: Vec<String>,
242    pub frontend_protocol_version: u32,
243    pub coverage_model_schema_version: u32,
244    pub unknown_frontend_fields_fatal: bool,
245    pub unknown_coverage_model_fields_fatal: bool,
246    pub frontend_language_must_match_coverage_model: bool,
247    pub malformed_recognized_jsonl_fatal: bool,
248    pub recognized_jsonl_requires_final_newline: bool,
249}
250
251pub fn evidence_v3_contract() -> Result<EvidenceV3Contract, serde_json::Error> {
252    serde_json::from_str(include_str!("../assets/evidence-v3/contract.json"))
253}
254
255#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
256#[serde(rename_all = "camelCase", deny_unknown_fields)]
257pub struct CoverageModelV1Contract {
258    pub schema_version: u32,
259    pub status: String,
260    pub persisted_entry: String,
261    pub required_fields: Vec<String>,
262    pub unknown_fields_fatal: bool,
263    pub frontend_language_must_match: bool,
264    pub measured_must_be_nonempty: bool,
265    pub surface_lists_must_be_unique: bool,
266    pub surface_lists_must_be_disjoint: bool,
267    pub strings_must_be_trimmed_single_line: bool,
268    pub max_identifier_bytes: usize,
269    pub max_description_bytes: usize,
270    pub max_surfaces_per_list: usize,
271}
272
273pub fn coverage_model_v1_contract() -> Result<CoverageModelV1Contract, serde_json::Error> {
274    serde_json::from_str(include_str!("../assets/coverage-model-v1/contract.json"))
275}
276
277#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
278#[serde(rename_all = "camelCase", deny_unknown_fields)]
279pub struct RustCoverageV1Contract {
280    pub model_version: u32,
281    pub status: String,
282    pub language: String,
283    pub variant: String,
284    pub decision_semantics: String,
285    pub condition_order: String,
286    pub probe_model: String,
287    pub generic_aggregation: String,
288    pub source_identity: RustSourceIdentityContract,
289    pub test_context_identity: RustTestContextIdentityContract,
290    pub runner_attempt_identity: RustRunnerAttemptIdentityContract,
291    pub libtest_event_transport: RustLibtestEventTransportContract,
292    pub point_kinds: Vec<String>,
293    pub control_decision_kinds: Vec<String>,
294    pub branch_kinds: Vec<String>,
295    pub required_owned_surfaces: Vec<String>,
296    pub required_identity_axes: Vec<String>,
297    pub completeness_requires: Vec<String>,
298    pub external_coverage_in_product: bool,
299}
300
301#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
302#[serde(rename_all = "camelCase", deny_unknown_fields)]
303pub struct RustLibtestEventTransportContract {
304    pub protocol_version: u32,
305    pub status: String,
306    pub magic: String,
307    pub byte_order: String,
308    pub header_size: usize,
309    pub record_header_size: usize,
310    pub token_size: usize,
311    pub maximum_name_bytes: usize,
312    pub writer: String,
313    pub event_kinds: Vec<String>,
314    pub terminal_results: Vec<String>,
315    pub publication: String,
316    pub sequence: String,
317    pub integrity: String,
318    pub unknown_event: String,
319    pub truncated_record: String,
320    pub invalid_semantics: String,
321    pub missing_transport: String,
322    pub process_model: String,
323    pub output_authority: String,
324    pub artifact_binding: RustLibtestArtifactBindingContract,
325}
326
327#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
328#[serde(rename_all = "camelCase", deny_unknown_fields)]
329pub struct RustLibtestArtifactBindingContract {
330    pub schema_version: u32,
331    pub manifest_suffix: String,
332    pub digest: String,
333    pub required_bindings: Vec<String>,
334    pub artifact_location: String,
335    pub unknown_fields_fatal: bool,
336    pub mismatch: String,
337}
338
339#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
340#[serde(rename_all = "camelCase", deny_unknown_fields)]
341pub struct RustRunnerAttemptIdentityContract {
342    pub version: u32,
343    pub cargo_test: RustSingleAttemptRunnerContract,
344    pub rustdoc: RustSingleAttemptRunnerContract,
345    pub nextest: RustNextestAttemptContract,
346}
347
348#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
349#[serde(rename_all = "camelCase", deny_unknown_fields)]
350pub struct RustSingleAttemptRunnerContract {
351    pub retry: usize,
352    pub total_attempts: usize,
353    pub identity_authority: String,
354}
355
356#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
357#[serde(rename_all = "camelCase", deny_unknown_fields)]
358pub struct RustNextestAttemptContract {
359    pub minimum_version: String,
360    pub maximum_version: String,
361    pub verified_released_versions: Vec<String>,
362    pub execution_mode: String,
363    pub identity_environment: Vec<String>,
364    pub attempt_numbering: String,
365    pub retry_derivation: String,
366    pub attempt_id_semantics: String,
367    pub list_phase: String,
368    pub selection_projection: String,
369    pub selected_but_unstarted: String,
370    pub concurrent_attempts: String,
371    pub target_runner_death: String,
372    pub partial_identity: String,
373    pub stress_iteration: String,
374}
375
376#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
377#[serde(rename_all = "camelCase", deny_unknown_fields)]
378pub struct RustTestContextIdentityContract {
379    pub version: u32,
380    pub algorithm: String,
381    pub domain: String,
382    pub input: String,
383    pub reserved_values: Vec<String>,
384    pub reserved_remap_xor: String,
385    pub collision_policy: String,
386}
387
388#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
389#[serde(rename_all = "camelCase", deny_unknown_fields)]
390pub struct RustSourceIdentityContract {
391    pub version: u32,
392    pub digest: String,
393    pub id_digest_bytes: usize,
394    pub separator: String,
395    pub authored_canonical_fields: Vec<String>,
396    pub synthetic_expansion_canonical_fields: Vec<String>,
397    pub generated_source_key_fields: Vec<String>,
398    pub repeated_authored_expansions_aggregate: bool,
399    pub distinct_synthetic_invocations_remain_distinct: bool,
400    pub ephemeral_paths_forbidden: bool,
401    pub collision_policy: String,
402}
403
404pub fn rust_coverage_v1_contract() -> Result<RustCoverageV1Contract, serde_json::Error> {
405    serde_json::from_str(include_str!("../assets/rust-coverage-v1/contract.json"))
406}
407
408#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
409#[serde(rename_all = "camelCase", deny_unknown_fields)]
410pub struct RustProbeTransportContract {
411    pub protocol_version: u32,
412    pub status: String,
413    pub magic: String,
414    pub byte_order: String,
415    pub header_size: usize,
416    pub descriptor_size: usize,
417    pub token_size: usize,
418    pub endian_marker: u32,
419    pub header_offsets: RustProbeTransportHeaderOffsets,
420    pub descriptor_offsets: RustProbeTransportDescriptorOffsets,
421    pub record_kinds: RustProbeTransportRecordKinds,
422    #[serde(default)]
423    pub thread_scope: Option<RustProbeTransportThreadScope>,
424    pub context: RustProbeTransportContext,
425    pub publication: RustProbeTransportPublication,
426    pub integrity: RustProbeTransportIntegrity,
427    pub completeness: RustProbeTransportCompleteness,
428    pub supported_targets: Vec<String>,
429    pub unsupported_target: String,
430}
431
432/// The frozen join-bounded thread acceptance rule: a record whose phase chain
433/// includes thread phases is attributed to its root test only when every such
434/// thread phase committed its end before the root test's boundary.
435#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
436#[serde(rename_all = "camelCase", deny_unknown_fields)]
437pub struct RustProbeTransportThreadScope {
438    pub child_derivation: String,
439    pub domain: String,
440    pub thread_end_committed_when_start_routine_returns: bool,
441    pub test_boundary_committed_when_test_context_exits: bool,
442    pub acceptance: String,
443    pub escaped_thread_records_become_background: bool,
444    pub escaped_thread_limitation: String,
445    pub duplicate_thread_end_fatal: bool,
446    pub duplicate_test_boundary_fatal: bool,
447}
448
449#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
450#[serde(rename_all = "camelCase", deny_unknown_fields)]
451pub struct RustProbeTransportHeaderOffsets {
452    pub version: usize,
453    pub header_size: usize,
454    pub descriptor_size: usize,
455    pub descriptor_capacity: usize,
456    pub payload_capacity: usize,
457    pub endian_marker: usize,
458    pub next_descriptor: usize,
459    pub next_payload: usize,
460    pub dropped: usize,
461    pub token: usize,
462    pub attachments: usize,
463    #[serde(default)]
464    pub next_phase: Option<usize>,
465}
466
467#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
468#[serde(rename_all = "camelCase", deny_unknown_fields)]
469pub struct RustProbeTransportDescriptorOffsets {
470    pub commit: usize,
471    pub kind: usize,
472    pub outcome: usize,
473    pub flags: usize,
474    pub process_id: usize,
475    pub context_id: usize,
476    pub payload_offset: usize,
477    pub payload_length: usize,
478    pub id_length: usize,
479    pub value_length: usize,
480    pub checksum: usize,
481}
482
483#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
484#[serde(rename_all = "camelCase", deny_unknown_fields)]
485pub struct RustProbeTransportRecordKinds {
486    pub hit: u8,
487    pub decision: u8,
488    pub ordinal_hit: u8,
489    #[serde(default)]
490    pub phase: Option<u8>,
491    #[serde(default)]
492    pub thread_phase: Option<u8>,
493    #[serde(default)]
494    pub thread_end: Option<u8>,
495    #[serde(default)]
496    pub test_boundary: Option<u8>,
497}
498
499#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
500#[serde(rename_all = "camelCase", deny_unknown_fields)]
501pub struct RustProbeTransportContext {
502    pub zero: String,
503    pub max: String,
504    pub nonzero: String,
505    pub published_identity: Vec<String>,
506}
507
508#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
509#[serde(rename_all = "camelCase", deny_unknown_fields)]
510pub struct RustProbeTransportPublication {
511    pub reservation_order: Vec<String>,
512    pub commit_value: u8,
513    pub writer_ordering: String,
514    pub reader_ordering: String,
515    pub complete_descriptors_independently_recoverable: bool,
516}
517
518#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
519#[serde(rename_all = "camelCase", deny_unknown_fields)]
520pub struct RustProbeTransportIntegrity {
521    pub authentication: String,
522    pub checksum: String,
523    pub unknown_record_kind_fatal: bool,
524    pub nonzero_reserved_byte_fatal: bool,
525    pub symlink_transport_fatal: bool,
526}
527
528#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
529#[serde(rename_all = "camelCase", deny_unknown_fields)]
530pub struct RustProbeTransportCompleteness {
531    pub zero_attachments_blocks_terminal_passing_attempt: bool,
532    pub dropped_records_block_terminal_passing_attempt: bool,
533    pub incomplete_records_block_terminal_passing_attempt: bool,
534    pub context_zero_excluded_from_passed_per_test_coverage: bool,
535    pub malformed_record_fatal: bool,
536}
537
538pub fn rust_probe_transport_contract() -> Result<RustProbeTransportContract, serde_json::Error> {
539    serde_json::from_str(include_str!(
540        "../assets/rust-probe-transport-v1/contract.json"
541    ))
542}
543
544pub fn rust_probe_transport_v3_contract() -> Result<RustProbeTransportContract, serde_json::Error> {
545    serde_json::from_str(include_str!(
546        "../assets/rust-probe-transport-v3/contract.json"
547    ))
548}
549
550#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
551#[serde(rename_all = "kebab-case")]
552pub enum StructuralSource {
553    OwnedProbes,
554    NativeImport,
555    Mixed,
556}
557
558#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
559#[serde(rename_all = "kebab-case")]
560pub enum ExecutionModel {
561    ProcessPerTest,
562    SerialInProcess,
563    ParallelContextPropagated,
564    ParallelUnattributed,
565}
566
567#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
568#[serde(rename_all = "kebab-case")]
569pub enum AttributionPrecision {
570    Exact,
571    Aggregate,
572    Unavailable,
573}
574
575#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
576#[serde(rename_all = "kebab-case")]
577pub enum FrontendTransitionKind {
578    Setup,
579    Test,
580    Action,
581    Assertion,
582    Teardown,
583    Background,
584}
585
586#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
587#[serde(rename_all = "kebab-case")]
588pub enum FrontendLimitationScope {
589    Worker,
590    Test,
591    Retry,
592    Phase,
593    Action,
594    Assertion,
595}
596
597#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
598#[serde(rename_all = "camelCase", deny_unknown_fields)]
599pub struct FrontendAttribution {
600    pub run: AttributionPrecision,
601    pub worker: AttributionPrecision,
602    pub test: AttributionPrecision,
603    pub retry: AttributionPrecision,
604    pub phase: AttributionPrecision,
605    pub action: AttributionPrecision,
606    pub assertion: AttributionPrecision,
607}
608
609#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
610#[serde(rename_all = "camelCase", deny_unknown_fields)]
611pub struct FrontendLimitation {
612    pub id: String,
613    pub scopes: Vec<FrontendLimitationScope>,
614    pub reason: String,
615}
616
617#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
618#[serde(rename_all = "camelCase", deny_unknown_fields)]
619pub struct FrontendRunDeclaration {
620    pub protocol_version: u32,
621    pub frontend_id: String,
622    pub frontend_version: String,
623    pub language: String,
624    pub structural_source: StructuralSource,
625    pub runners: Vec<FrontendRunnerDeclaration>,
626    pub structural_limitations: Vec<String>,
627}
628
629#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
630#[serde(rename_all = "camelCase", deny_unknown_fields)]
631pub struct FrontendRunnerDeclaration {
632    pub runner: String,
633    pub execution_model: ExecutionModel,
634    pub attribution: FrontendAttribution,
635    pub limitations: Vec<FrontendLimitation>,
636}
637
638#[derive(Debug, Clone, PartialEq, Eq)]
639pub enum FrontendDeclarationError {
640    UnsupportedProtocolVersion(u32),
641    InvalidToken(&'static str),
642    NoRunners,
643    DuplicateRunner(String),
644    RunIdentityNotExact,
645    DuplicateLimitation(String),
646    DuplicateLimitationScope(String),
647    EmptyLimitationScopes(String),
648    InvalidLimitationReason(String),
649    DuplicateStructuralLimitation(String),
650    MissingDowngradeLimitation(FrontendLimitationScope),
651    ExactRetryRequiresExactTest,
652    ExactPhaseRequiresExactTest,
653    ExactAssertionRequiresExactTestAndPhase,
654    ExactActionRequiresExactTestAndPhase,
655    ParallelUnattributedCannotClaimExactCausality,
656}
657
658impl std::fmt::Display for FrontendDeclarationError {
659    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
660        match self {
661            Self::UnsupportedProtocolVersion(version) => {
662                write!(
663                    formatter,
664                    "unsupported language-frontend protocol version: {version}"
665                )
666            }
667            Self::InvalidToken(field) => write!(formatter, "invalid frontend {field}"),
668            Self::NoRunners => write!(formatter, "frontend declaration has no runners"),
669            Self::DuplicateRunner(runner) => {
670                write!(formatter, "duplicate frontend runner: {runner}")
671            }
672            Self::RunIdentityNotExact => write!(formatter, "frontend run identity must be exact"),
673            Self::DuplicateLimitation(id) => {
674                write!(formatter, "duplicate frontend limitation: {id}")
675            }
676            Self::EmptyLimitationScopes(id) => {
677                write!(formatter, "frontend limitation has no scopes: {id}")
678            }
679            Self::DuplicateLimitationScope(id) => {
680                write!(formatter, "frontend limitation has duplicate scopes: {id}")
681            }
682            Self::InvalidLimitationReason(id) => {
683                write!(formatter, "frontend limitation has an invalid reason: {id}")
684            }
685            Self::DuplicateStructuralLimitation(id) => {
686                write!(formatter, "duplicate structural limitation reference: {id}")
687            }
688            Self::MissingDowngradeLimitation(scope) => write!(
689                formatter,
690                "non-exact {scope:?} attribution has no matching limitation"
691            ),
692            Self::ExactRetryRequiresExactTest => {
693                write!(
694                    formatter,
695                    "exact retry attribution requires exact test identity"
696                )
697            }
698            Self::ExactPhaseRequiresExactTest => {
699                write!(
700                    formatter,
701                    "exact phase attribution requires exact test identity"
702                )
703            }
704            Self::ExactAssertionRequiresExactTestAndPhase => write!(
705                formatter,
706                "exact assertion attribution requires exact test and phase identity"
707            ),
708            Self::ExactActionRequiresExactTestAndPhase => write!(
709                formatter,
710                "exact action attribution requires exact test and phase identity"
711            ),
712            Self::ParallelUnattributedCannotClaimExactCausality => write!(
713                formatter,
714                "parallel-unattributed execution cannot claim exact test, retry, phase, action, or assertion causality"
715            ),
716        }
717    }
718}
719
720fn validate_frontend_limitations(
721    limitations: &[FrontendLimitation],
722    limitation_ids: &mut BTreeSet<String>,
723) -> Result<BTreeSet<FrontendLimitationScope>, FrontendDeclarationError> {
724    let mut limited_scopes = BTreeSet::new();
725    for limitation in limitations {
726        if !valid_frontend_token(&limitation.id) {
727            return Err(FrontendDeclarationError::InvalidToken("limitation ID"));
728        }
729        if !limitation_ids.insert(limitation.id.clone()) {
730            return Err(FrontendDeclarationError::DuplicateLimitation(
731                limitation.id.clone(),
732            ));
733        }
734        if limitation.scopes.is_empty() {
735            return Err(FrontendDeclarationError::EmptyLimitationScopes(
736                limitation.id.clone(),
737            ));
738        }
739        let unique_scopes = limitation.scopes.iter().copied().collect::<BTreeSet<_>>();
740        if unique_scopes.len() != limitation.scopes.len() {
741            return Err(FrontendDeclarationError::DuplicateLimitationScope(
742                limitation.id.clone(),
743            ));
744        }
745        if limitation.reason.trim().is_empty()
746            || limitation.reason.trim().len() != limitation.reason.len()
747            || limitation.reason.contains(['\n', '\r', '\0'])
748        {
749            return Err(FrontendDeclarationError::InvalidLimitationReason(
750                limitation.id.clone(),
751            ));
752        }
753        limited_scopes.extend(unique_scopes);
754    }
755    Ok(limited_scopes)
756}
757
758fn validate_frontend_runner(
759    runner: &FrontendRunnerDeclaration,
760    limitation_ids: &mut BTreeSet<String>,
761) -> Result<(), FrontendDeclarationError> {
762    if !valid_frontend_runner_token(&runner.runner) {
763        return Err(FrontendDeclarationError::InvalidToken("runner"));
764    }
765    if runner.attribution.run != AttributionPrecision::Exact {
766        return Err(FrontendDeclarationError::RunIdentityNotExact);
767    }
768    let limited_scopes = validate_frontend_limitations(&runner.limitations, limitation_ids)?;
769    if runner.attribution.retry == AttributionPrecision::Exact
770        && runner.attribution.test != AttributionPrecision::Exact
771    {
772        return Err(FrontendDeclarationError::ExactRetryRequiresExactTest);
773    }
774    if runner.attribution.phase == AttributionPrecision::Exact
775        && runner.attribution.test != AttributionPrecision::Exact
776    {
777        return Err(FrontendDeclarationError::ExactPhaseRequiresExactTest);
778    }
779    for (precision, scope) in [
780        (runner.attribution.worker, FrontendLimitationScope::Worker),
781        (runner.attribution.test, FrontendLimitationScope::Test),
782        (runner.attribution.retry, FrontendLimitationScope::Retry),
783        (runner.attribution.phase, FrontendLimitationScope::Phase),
784        (runner.attribution.action, FrontendLimitationScope::Action),
785        (
786            runner.attribution.assertion,
787            FrontendLimitationScope::Assertion,
788        ),
789    ] {
790        if precision != AttributionPrecision::Exact && !limited_scopes.contains(&scope) {
791            return Err(FrontendDeclarationError::MissingDowngradeLimitation(scope));
792        }
793    }
794    if runner.attribution.assertion == AttributionPrecision::Exact
795        && (runner.attribution.test != AttributionPrecision::Exact
796            || runner.attribution.phase != AttributionPrecision::Exact)
797    {
798        return Err(FrontendDeclarationError::ExactAssertionRequiresExactTestAndPhase);
799    }
800    if runner.attribution.action == AttributionPrecision::Exact
801        && (runner.attribution.test != AttributionPrecision::Exact
802            || runner.attribution.phase != AttributionPrecision::Exact)
803    {
804        return Err(FrontendDeclarationError::ExactActionRequiresExactTestAndPhase);
805    }
806    if runner.execution_model == ExecutionModel::ParallelUnattributed
807        && [
808            runner.attribution.test,
809            runner.attribution.retry,
810            runner.attribution.phase,
811            runner.attribution.action,
812            runner.attribution.assertion,
813        ]
814        .contains(&AttributionPrecision::Exact)
815    {
816        return Err(FrontendDeclarationError::ParallelUnattributedCannotClaimExactCausality);
817    }
818    Ok(())
819}
820
821impl std::error::Error for FrontendDeclarationError {}
822
823fn valid_frontend_token(value: &str) -> bool {
824    (1..=64).contains(&value.len())
825        && value.as_bytes()[0].is_ascii_lowercase()
826        && value.bytes().all(|byte| {
827            byte.is_ascii_lowercase() || byte.is_ascii_digit() || matches!(byte, b'.' | b'+' | b'-')
828        })
829}
830
831fn valid_frontend_runner_token(value: &str) -> bool {
832    (1..=64).contains(&value.len())
833        && value.as_bytes()[0].is_ascii_lowercase()
834        && value.bytes().all(|byte| {
835            byte.is_ascii_lowercase()
836                || byte.is_ascii_digit()
837                || matches!(byte, b'.' | b'+' | b'-' | b':')
838        })
839}
840
841fn valid_structural_limitation_reference(value: &str) -> bool {
842    (1..=512).contains(&value.len())
843        && value.trim().len() == value.len()
844        && !value.chars().any(char::is_control)
845}
846
847pub fn validate_frontend_run_declaration(
848    declaration: &FrontendRunDeclaration,
849) -> Result<(), FrontendDeclarationError> {
850    if declaration.protocol_version != LANGUAGE_FRONTEND_PROTOCOL_VERSION {
851        return Err(FrontendDeclarationError::UnsupportedProtocolVersion(
852            declaration.protocol_version,
853        ));
854    }
855    for (field, value) in [
856        ("ID", declaration.frontend_id.as_str()),
857        ("version", declaration.frontend_version.as_str()),
858        ("language", declaration.language.as_str()),
859    ] {
860        if !valid_frontend_token(value) {
861            return Err(FrontendDeclarationError::InvalidToken(field));
862        }
863    }
864    if declaration.runners.is_empty() {
865        return Err(FrontendDeclarationError::NoRunners);
866    }
867    let mut limitation_ids = BTreeSet::new();
868    for limitation in &declaration.structural_limitations {
869        if !valid_structural_limitation_reference(limitation) {
870            return Err(FrontendDeclarationError::InvalidToken(
871                "structural limitation ID",
872            ));
873        }
874        if !limitation_ids.insert(limitation.clone()) {
875            return Err(FrontendDeclarationError::DuplicateStructuralLimitation(
876                limitation.clone(),
877            ));
878        }
879    }
880    let mut runners = BTreeSet::new();
881    for runner in &declaration.runners {
882        if !runners.insert(runner.runner.clone()) {
883            return Err(FrontendDeclarationError::DuplicateRunner(
884                runner.runner.clone(),
885            ));
886        }
887        validate_frontend_runner(runner, &mut limitation_ids)?;
888    }
889    Ok(())
890}
891
892#[cfg(test)]
893mod tests {
894    use super::*;
895
896    #[test]
897    fn checked_in_registry_matches_rust_constants() {
898        let contract = registry().expect("contract registry must be valid JSON");
899        assert_eq!(contract.contract_version, CONTRACT_VERSION);
900        assert_eq!(contract.status, "frozen");
901        assert!(!contract.resident_process);
902        assert_eq!(
903            contract.evidence_archive.schema_version,
904            EVIDENCE_ARCHIVE_SCHEMA_VERSION
905        );
906        assert_eq!(contract.evidence_archive.magic, EVIDENCE_ARCHIVE_MAGIC);
907        assert_eq!(
908            contract.agent_json.schema_version,
909            AGENT_JSON_SCHEMA_VERSION
910        );
911        assert_eq!(contract.agent_json.max_bytes, AGENT_JSON_MAX_BYTES);
912        assert_eq!(contract.agent_json.default_page_size, DEFAULT_PAGE_SIZE);
913        assert_eq!(contract.agent_json.error_codes, ERROR_CODES);
914        assert_eq!(
915            contract.process_supervision.schema_version,
916            PROCESS_SUPERVISION_SCHEMA_VERSION
917        );
918        assert_eq!(
919            contract.process_supervision.diagnostic_interval_ms,
920            DEFAULT_DIAGNOSTIC_INTERVAL_MS
921        );
922        assert_eq!(
923            contract.process_supervision.timeout_exit_code,
924            COMMAND_TIMEOUT_EXIT_CODE
925        );
926        assert_eq!(
927            contract.process_supervision.termination_grace_ms,
928            COMMAND_TERMINATION_GRACE_MS
929        );
930    }
931
932    #[test]
933    fn checked_in_probe_v2_contract_matches_rust_constants() {
934        let contract = probe_v2_contract().expect("probe v2 contract must be valid JSON");
935        assert_eq!(contract.probe_version, PROBE_V2_VERSION);
936        assert_eq!(contract.semantics, "frozen");
937        assert_eq!(contract.implementation, "experimental");
938        assert_eq!(contract.decision_encoding.radix, PROBE_V2_RADIX);
939        assert_eq!(contract.decision_encoding.digits.unreached, 0);
940        assert_eq!(contract.decision_encoding.digits.r#false, 1);
941        assert_eq!(contract.decision_encoding.digits.r#true, 2);
942        assert!(contract.decision_encoding.outcome_stored_separately);
943        assert_eq!(
944            contract
945                .decision_encoding
946                .javascript_maximum_encoded_conditions,
947            PROBE_V2_JS_MAX_CONDITIONS
948        );
949        assert_eq!(contract.promotion.realistic_median_runtime_ratio_max, 1.10);
950    }
951
952    fn exact_frontend_declaration() -> FrontendRunDeclaration {
953        FrontendRunDeclaration {
954            protocol_version: LANGUAGE_FRONTEND_PROTOCOL_VERSION,
955            frontend_id: "javascript".into(),
956            frontend_version: "javascript-v1".into(),
957            language: "javascript".into(),
958            structural_source: StructuralSource::OwnedProbes,
959            runners: vec![FrontendRunnerDeclaration {
960                runner: "playwright".into(),
961                execution_model: ExecutionModel::ParallelContextPropagated,
962                attribution: FrontendAttribution {
963                    run: AttributionPrecision::Exact,
964                    worker: AttributionPrecision::Exact,
965                    test: AttributionPrecision::Exact,
966                    retry: AttributionPrecision::Exact,
967                    phase: AttributionPrecision::Exact,
968                    action: AttributionPrecision::Exact,
969                    assertion: AttributionPrecision::Exact,
970                },
971                limitations: Vec::new(),
972            }],
973            structural_limitations: Vec::new(),
974        }
975    }
976
977    #[test]
978    fn checked_in_language_frontend_contract_matches_rust_types() {
979        let contract = language_frontend_protocol_contract()
980            .expect("language frontend protocol must be valid JSON");
981        assert_eq!(
982            contract.frontend_protocol_version,
983            LANGUAGE_FRONTEND_PROTOCOL_VERSION
984        );
985        assert_eq!(contract.status, "frozen");
986        assert_eq!(contract.manifest_model, "coverage-manifest-v1");
987        assert_eq!(contract.observation_model, "evidence-archive-v3");
988        assert_eq!(contract.probe_model, "ternary-decision-v2");
989        assert!(
990            contract
991                .requirements
992                .selected_unstarted_has_test_identity_only
993        );
994        assert_eq!(
995            contract.identity_axes,
996            ["run", "worker", "test", "retry", "phase"]
997        );
998        assert_eq!(
999            contract.structural_sources,
1000            [
1001                StructuralSource::OwnedProbes,
1002                StructuralSource::NativeImport,
1003                StructuralSource::Mixed,
1004            ]
1005            .map(|value| serde_json::to_value(value)
1006                .unwrap()
1007                .as_str()
1008                .unwrap()
1009                .to_owned())
1010        );
1011        assert_eq!(
1012            contract.execution_models,
1013            [
1014                ExecutionModel::ProcessPerTest,
1015                ExecutionModel::SerialInProcess,
1016                ExecutionModel::ParallelContextPropagated,
1017                ExecutionModel::ParallelUnattributed,
1018            ]
1019            .map(|value| serde_json::to_value(value)
1020                .unwrap()
1021                .as_str()
1022                .unwrap()
1023                .to_owned())
1024        );
1025        assert_eq!(
1026            contract.attribution_precisions,
1027            [
1028                AttributionPrecision::Exact,
1029                AttributionPrecision::Aggregate,
1030                AttributionPrecision::Unavailable,
1031            ]
1032            .map(|value| serde_json::to_value(value)
1033                .unwrap()
1034                .as_str()
1035                .unwrap()
1036                .to_owned())
1037        );
1038        assert_eq!(
1039            contract.transition_kinds,
1040            [
1041                FrontendTransitionKind::Setup,
1042                FrontendTransitionKind::Test,
1043                FrontendTransitionKind::Action,
1044                FrontendTransitionKind::Assertion,
1045                FrontendTransitionKind::Teardown,
1046                FrontendTransitionKind::Background,
1047            ]
1048            .map(|value| serde_json::to_value(value)
1049                .unwrap()
1050                .as_str()
1051                .unwrap()
1052                .to_owned())
1053        );
1054        assert_eq!(
1055            contract.limitation_scopes,
1056            [
1057                FrontendLimitationScope::Worker,
1058                FrontendLimitationScope::Test,
1059                FrontendLimitationScope::Retry,
1060                FrontendLimitationScope::Phase,
1061                FrontendLimitationScope::Action,
1062                FrontendLimitationScope::Assertion,
1063            ]
1064            .map(|value| serde_json::to_value(value)
1065                .unwrap()
1066                .as_str()
1067                .unwrap()
1068                .to_owned())
1069        );
1070        assert!(
1071            !contract
1072                .requirements
1073                .timestamp_attribution_may_claim_causality
1074        );
1075        assert!(!contract.requirements.frontend_may_compute_coverage_verdicts);
1076        assert!(contract.requirements.multiple_runners_per_frontend);
1077        assert!(
1078            contract
1079                .requirements
1080                .structural_limitations_reference_manifest_ids
1081        );
1082        assert!(contract.requirements.attribution_limitations_runner_scoped);
1083        assert!(contract.requirements.complete_manifest_before_execution);
1084        assert!(contract.requirements.unknown_obligation_fatal);
1085        assert!(contract.requirements.identity_downgrade_requires_limitation);
1086        assert!(contract.requirements.unknown_phase_reference_fatal);
1087        assert!(contract.requirements.phase_causality_acyclic);
1088        assert!(contract.requirements.engine_owns_manifest_merge);
1089        assert!(contract.requirements.engine_owns_evidence_validation);
1090        assert!(contract.requirements.engine_owns_attribution_merge);
1091        assert!(contract.requirements.engine_owns_coverage_analysis);
1092        assert!(contract.requirements.engine_owns_persistence_and_queries);
1093    }
1094
1095    #[test]
1096    fn validates_exact_and_explicitly_degraded_frontends() {
1097        validate_frontend_run_declaration(&exact_frontend_declaration()).unwrap();
1098        let mut degraded = exact_frontend_declaration();
1099        degraded.runners[0].runner = "opaque-runner".into();
1100        degraded.runners[0].attribution.action = AttributionPrecision::Unavailable;
1101        degraded.runners[0].limitations.push(FrontendLimitation {
1102            id: "no-action-lifecycle".into(),
1103            scopes: vec![FrontendLimitationScope::Action],
1104            reason: "The runner exposes assertions but no action lifecycle".into(),
1105        });
1106        validate_frontend_run_declaration(&degraded).unwrap();
1107    }
1108
1109    #[test]
1110    fn accepts_the_canonical_node_test_runner_name() {
1111        let mut declaration = exact_frontend_declaration();
1112        declaration.runners[0].runner = "node:test".into();
1113        validate_frontend_run_declaration(&declaration).unwrap();
1114    }
1115
1116    #[test]
1117    fn rejects_unexplained_or_internally_impossible_attribution_claims() {
1118        let mut unexplained = exact_frontend_declaration();
1119        unexplained.runners[0].attribution.assertion = AttributionPrecision::Unavailable;
1120        assert_eq!(
1121            validate_frontend_run_declaration(&unexplained),
1122            Err(FrontendDeclarationError::MissingDowngradeLimitation(
1123                FrontendLimitationScope::Assertion
1124            ))
1125        );
1126
1127        let mut impossible = exact_frontend_declaration();
1128        impossible.runners[0].execution_model = ExecutionModel::ParallelUnattributed;
1129        assert_eq!(
1130            validate_frontend_run_declaration(&impossible),
1131            Err(FrontendDeclarationError::ParallelUnattributedCannotClaimExactCausality)
1132        );
1133
1134        let mut assertion_without_test = exact_frontend_declaration();
1135        assertion_without_test.runners[0].attribution.test = AttributionPrecision::Aggregate;
1136        assertion_without_test.runners[0].attribution.retry = AttributionPrecision::Aggregate;
1137        assertion_without_test.runners[0].attribution.phase = AttributionPrecision::Aggregate;
1138        assertion_without_test.runners[0]
1139            .limitations
1140            .push(FrontendLimitation {
1141                id: "aggregate-tests".into(),
1142                scopes: vec![
1143                    FrontendLimitationScope::Test,
1144                    FrontendLimitationScope::Retry,
1145                    FrontendLimitationScope::Phase,
1146                ],
1147                reason: "The runner pools concurrent test observations".into(),
1148            });
1149        assert_eq!(
1150            validate_frontend_run_declaration(&assertion_without_test),
1151            Err(FrontendDeclarationError::ExactAssertionRequiresExactTestAndPhase)
1152        );
1153    }
1154
1155    #[test]
1156    fn supports_multiple_runners_but_rejects_duplicate_runner_claims() {
1157        let mut declaration = exact_frontend_declaration();
1158        let mut vitest = declaration.runners[0].clone();
1159        vitest.runner = "vitest".into();
1160        declaration.runners.push(vitest.clone());
1161        validate_frontend_run_declaration(&declaration).unwrap();
1162        declaration.runners.push(vitest);
1163        assert_eq!(
1164            validate_frontend_run_declaration(&declaration),
1165            Err(FrontendDeclarationError::DuplicateRunner("vitest".into()))
1166        );
1167    }
1168
1169    #[test]
1170    fn keeps_structural_limitation_references_unique() {
1171        let mut declaration = exact_frontend_declaration();
1172        declaration
1173            .structural_limitations
1174            .push("dynamic-python".into());
1175        validate_frontend_run_declaration(&declaration).unwrap();
1176        declaration
1177            .structural_limitations
1178            .push("dynamic-python".into());
1179        assert_eq!(
1180            validate_frontend_run_declaration(&declaration),
1181            Err(FrontendDeclarationError::DuplicateStructuralLimitation(
1182                "dynamic-python".into()
1183            ))
1184        );
1185    }
1186
1187    #[test]
1188    fn declaration_json_rejects_unknown_fields() {
1189        let mut value = serde_json::to_value(exact_frontend_declaration()).unwrap();
1190        value["verdict"] = serde_json::json!({ "mcdc": 100 });
1191        assert!(serde_json::from_value::<FrontendRunDeclaration>(value).is_err());
1192    }
1193
1194    #[test]
1195    fn checked_in_frontend_examples_are_strict_and_valid() {
1196        for source in [
1197            include_str!("../assets/frontend-v2/examples/javascript-mixed-runners.json"),
1198            include_str!("../assets/frontend-v2/examples/python-pytest-xdist.json"),
1199        ] {
1200            let declaration: FrontendRunDeclaration = serde_json::from_str(source).unwrap();
1201            validate_frontend_run_declaration(&declaration).unwrap();
1202            assert_eq!(
1203                serde_json::from_str::<serde_json::Value>(source).unwrap(),
1204                serde_json::to_value(declaration).unwrap()
1205            );
1206        }
1207    }
1208
1209    #[test]
1210    fn python_coverage_import_contract_keeps_the_oracle_at_the_fact_boundary() {
1211        let contract = python_coverage_import_contract().unwrap();
1212        assert_eq!(contract.schema_version, 1);
1213        assert_eq!(contract.status, "private-spike");
1214        assert_eq!(contract.producer, "coverage.py");
1215        assert_eq!(
1216            contract.supported_collector_cores_for_exact_contexts,
1217            ["ctrace", "pytrace"]
1218        );
1219        assert!(contract.requires_branch_measurement);
1220        assert_eq!(contract.database_access, "forbidden");
1221        assert!(!contract.frontend_computes_verdicts);
1222        assert!(contract.unknown_fields_fatal);
1223        assert!(contract.preserve_unrecognized_contexts_as_background);
1224        assert_eq!(
1225            contract.mcdc_availability,
1226            "unavailable-with-blocking-limitation"
1227        );
1228        assert_eq!(
1229            contract.column_locations,
1230            "unavailable-with-blocking-limitation"
1231        );
1232    }
1233
1234    #[test]
1235    fn evidence_v3_is_the_frozen_language_bound_archive() {
1236        let contract = evidence_v3_contract().unwrap();
1237        assert_eq!(contract.schema_version, EVIDENCE_ARCHIVE_SCHEMA_VERSION);
1238        assert_eq!(contract.status, "frozen");
1239        assert_eq!(contract.magic, EVIDENCE_ARCHIVE_MAGIC);
1240        assert_eq!(contract.framing, "canonical-sorted-length-framed-gzip");
1241        assert_eq!(
1242            contract.required_entries,
1243            ["coverage-model.json", "frontend.json", "manifest.json"]
1244        );
1245        assert_eq!(
1246            contract.frontend_protocol_version,
1247            LANGUAGE_FRONTEND_PROTOCOL_VERSION
1248        );
1249        assert_eq!(
1250            contract.coverage_model_schema_version,
1251            COVERAGE_MODEL_SCHEMA_VERSION
1252        );
1253        assert!(contract.unknown_frontend_fields_fatal);
1254        assert!(contract.unknown_coverage_model_fields_fatal);
1255        assert!(contract.frontend_language_must_match_coverage_model);
1256        assert!(contract.malformed_recognized_jsonl_fatal);
1257        assert!(contract.recognized_jsonl_requires_final_newline);
1258        assert_eq!(EVIDENCE_ARCHIVE_SCHEMA_VERSION, 3);
1259        assert_eq!(EVIDENCE_ARCHIVE_MAGIC, "SUPERCOV-EVIDENCE-3\n");
1260    }
1261
1262    #[test]
1263    fn coverage_model_v1_contract_is_frozen_and_bounded() {
1264        let contract = coverage_model_v1_contract().unwrap();
1265        assert_eq!(contract.schema_version, COVERAGE_MODEL_SCHEMA_VERSION);
1266        assert_eq!(contract.status, "frozen");
1267        assert_eq!(contract.persisted_entry, "coverage-model.json");
1268        assert_eq!(
1269            contract.required_fields,
1270            [
1271                "schemaVersion",
1272                "language",
1273                "variant",
1274                "name",
1275                "completenessMeaning",
1276                "measured",
1277                "notMeasured",
1278            ]
1279        );
1280        assert!(contract.unknown_fields_fatal);
1281        assert!(contract.frontend_language_must_match);
1282        assert!(contract.measured_must_be_nonempty);
1283        assert!(contract.surface_lists_must_be_unique);
1284        assert!(contract.surface_lists_must_be_disjoint);
1285        assert!(contract.strings_must_be_trimmed_single_line);
1286        assert_eq!(contract.max_identifier_bytes, 64);
1287        assert_eq!(contract.max_description_bytes, 4096);
1288        assert_eq!(contract.max_surfaces_per_list, 256);
1289    }
1290
1291    #[test]
1292    fn rust_coverage_v1_contract_fixes_the_complete_target_model() {
1293        let contract = rust_coverage_v1_contract().unwrap();
1294        assert_eq!(contract.model_version, 1);
1295        assert_eq!(contract.status, "frozen-private-frontend");
1296        assert_eq!(contract.language, "rust");
1297        assert_eq!(contract.variant, "rust-source-v1");
1298        assert_eq!(contract.decision_semantics, "masking-mcdc");
1299        assert_eq!(contract.condition_order, "source-evaluation-order");
1300        assert_eq!(contract.probe_model, "ternary-decision-v2");
1301        assert_eq!(contract.source_identity.version, 1);
1302        assert_eq!(contract.source_identity.digest, "sha256");
1303        assert_eq!(contract.source_identity.id_digest_bytes, 12);
1304        assert_eq!(contract.source_identity.separator, "nul");
1305        assert!(
1306            contract
1307                .source_identity
1308                .repeated_authored_expansions_aggregate
1309        );
1310        assert!(
1311            contract
1312                .source_identity
1313                .distinct_synthetic_invocations_remain_distinct
1314        );
1315        assert!(contract.source_identity.ephemeral_paths_forbidden);
1316        assert_eq!(contract.source_identity.collision_policy, "fatal");
1317        assert_eq!(contract.test_context_identity.version, 1);
1318        assert_eq!(contract.test_context_identity.algorithm, "fnv1a-64");
1319        assert_eq!(
1320            contract.test_context_identity.domain,
1321            "supercov-rust-test-v1\0"
1322        );
1323        assert_eq!(
1324            contract.test_context_identity.collision_policy,
1325            "fatal-before-launch"
1326        );
1327        assert_eq!(contract.runner_attempt_identity.version, 1);
1328        assert_eq!(contract.runner_attempt_identity.cargo_test.retry, 0);
1329        assert_eq!(
1330            contract.runner_attempt_identity.cargo_test.total_attempts,
1331            1
1332        );
1333        assert_eq!(contract.runner_attempt_identity.rustdoc.retry, 0);
1334        assert_eq!(contract.runner_attempt_identity.rustdoc.total_attempts, 1);
1335        assert_eq!(
1336            contract.libtest_event_transport.protocol_version,
1337            RUST_LIBTEST_EVENT_PROTOCOL_VERSION
1338        );
1339        assert_eq!(
1340            contract.libtest_event_transport.magic,
1341            RUST_LIBTEST_EVENT_MAGIC
1342        );
1343        assert_eq!(
1344            contract.libtest_event_transport.header_size,
1345            RUST_LIBTEST_EVENT_HEADER_SIZE
1346        );
1347        assert_eq!(
1348            contract.libtest_event_transport.record_header_size,
1349            RUST_LIBTEST_EVENT_RECORD_HEADER_SIZE
1350        );
1351        assert_eq!(
1352            contract.libtest_event_transport.token_size,
1353            RUST_LIBTEST_EVENT_TOKEN_SIZE
1354        );
1355        assert_eq!(
1356            contract.libtest_event_transport.maximum_name_bytes,
1357            RUST_LIBTEST_EVENT_MAX_NAME_BYTES
1358        );
1359        assert_eq!(
1360            contract.libtest_event_transport.event_kinds,
1361            ["filtered-out", "filtered", "started", "timeout", "finished"]
1362        );
1363        assert_eq!(
1364            contract.libtest_event_transport.terminal_results,
1365            ["passed", "failed", "ignored", "benchmarked"]
1366        );
1367        assert_eq!(
1368            contract.libtest_event_transport.process_model,
1369            "one-stock-libtest-process-per-artifact"
1370        );
1371        assert_eq!(
1372            contract.libtest_event_transport.output_authority,
1373            "unmodified-selected-toolchain-libtest"
1374        );
1375        assert_eq!(
1376            contract
1377                .libtest_event_transport
1378                .artifact_binding
1379                .schema_version,
1380            RUST_LIBTEST_COMPANION_BUNDLE_SCHEMA_VERSION
1381        );
1382        assert_eq!(
1383            contract
1384                .libtest_event_transport
1385                .artifact_binding
1386                .required_bindings,
1387            [
1388                "compilerCompanionBuildId",
1389                "rustcCommitHash",
1390                "hostTriple",
1391                "eventProtocolVersion",
1392                "originalSourceSha256",
1393                "eventRuntimeSha256",
1394                "patchedSourceSha256",
1395                "artifactSha256",
1396            ]
1397        );
1398        assert!(
1399            contract
1400                .libtest_event_transport
1401                .artifact_binding
1402                .unknown_fields_fatal
1403        );
1404        assert_eq!(
1405            contract.runner_attempt_identity.nextest.minimum_version,
1406            "0.9.138"
1407        );
1408        assert_eq!(
1409            contract.runner_attempt_identity.nextest.maximum_version,
1410            "0.9.140"
1411        );
1412        assert_eq!(
1413            contract
1414                .runner_attempt_identity
1415                .nextest
1416                .verified_released_versions,
1417            ["0.9.138", "0.9.140"]
1418        );
1419        assert_eq!(
1420            contract.runner_attempt_identity.nextest.execution_mode,
1421            "process-per-test"
1422        );
1423        assert_eq!(
1424            contract
1425                .runner_attempt_identity
1426                .nextest
1427                .identity_environment,
1428            [
1429                "NEXTEST",
1430                "NEXTEST_RUN_ID",
1431                "NEXTEST_VERSION",
1432                "NEXTEST_EXECUTION_MODE",
1433                "NEXTEST_BINARY_ID",
1434                "NEXTEST_TEST_NAME",
1435                "NEXTEST_ATTEMPT",
1436                "NEXTEST_TOTAL_ATTEMPTS",
1437                "NEXTEST_ATTEMPT_ID",
1438                "NEXTEST_STRESS_CURRENT",
1439                "NEXTEST_STRESS_TOTAL",
1440            ]
1441        );
1442        assert_eq!(
1443            contract.runner_attempt_identity.nextest.stress_iteration,
1444            "distinct-axis-unsupported-fail-closed"
1445        );
1446        assert_eq!(
1447            contract
1448                .runner_attempt_identity
1449                .nextest
1450                .selected_but_unstarted,
1451            "logical-test-identity-only"
1452        );
1453        assert_eq!(
1454            contract
1455                .runner_attempt_identity
1456                .nextest
1457                .selection_projection,
1458            "exact-pre-and-post-separator"
1459        );
1460        assert_eq!(
1461            contract.runner_attempt_identity.nextest.concurrent_attempts,
1462            "distinct-durable-ordinal-and-attempt-id"
1463        );
1464        assert_eq!(
1465            contract.runner_attempt_identity.nextest.target_runner_death,
1466            "unmatched-durable-reservation-fatal"
1467        );
1468        assert!(
1469            contract
1470                .source_identity
1471                .authored_canonical_fields
1472                .iter()
1473                .any(|field| field == "semantic-discriminator")
1474        );
1475        assert!(
1476            contract
1477                .source_identity
1478                .synthetic_expansion_canonical_fields
1479                .iter()
1480                .any(|field| field == "owner-local-ordinal")
1481        );
1482        assert_eq!(
1483            contract.required_identity_axes,
1484            ["run", "worker", "test", "retry", "phase"]
1485        );
1486        for surface in [
1487            "authored-source",
1488            "declarative-macro-expansion",
1489            "procedural-macro-expansion",
1490            "derive-expansion",
1491            "build-script-generated-source",
1492            "included-source",
1493            "const-evaluation",
1494            "doctest-source",
1495        ] {
1496            assert!(
1497                contract
1498                    .required_owned_surfaces
1499                    .iter()
1500                    .any(|item| item == surface)
1501            );
1502        }
1503        assert!(!contract.external_coverage_in_product);
1504    }
1505
1506    #[test]
1507    fn rust_probe_transport_contract_fixes_layout_and_fail_closed_health() {
1508        let contract = rust_probe_transport_contract().unwrap();
1509        assert_eq!(
1510            contract.protocol_version,
1511            RUST_PROBE_TRANSPORT_PROTOCOL_VERSION
1512        );
1513        assert_eq!(contract.status, "frozen-private-frontend");
1514        assert_eq!(contract.magic, RUST_PROBE_TRANSPORT_MAGIC);
1515        assert_eq!(contract.byte_order, "little-endian");
1516        assert_eq!(contract.header_size, RUST_PROBE_TRANSPORT_HEADER_SIZE);
1517        assert_eq!(
1518            contract.descriptor_size,
1519            RUST_PROBE_TRANSPORT_DESCRIPTOR_SIZE
1520        );
1521        assert_eq!(contract.token_size, RUST_PROBE_TRANSPORT_TOKEN_SIZE);
1522        assert_eq!(contract.endian_marker, 0x0102_0304);
1523        assert_eq!(contract.header_offsets.next_descriptor, 32);
1524        assert_eq!(contract.header_offsets.next_payload, 40);
1525        assert_eq!(contract.header_offsets.dropped, 48);
1526        assert_eq!(contract.header_offsets.token, 56);
1527        assert_eq!(contract.header_offsets.attachments, 72);
1528        assert_eq!(contract.header_offsets.next_phase, None);
1529        assert_eq!(contract.descriptor_offsets.commit, 0);
1530        assert_eq!(contract.descriptor_offsets.process_id, 4);
1531        assert_eq!(contract.descriptor_offsets.context_id, 8);
1532        assert_eq!(contract.descriptor_offsets.payload_offset, 16);
1533        assert_eq!(contract.descriptor_offsets.checksum, 32);
1534        assert_eq!(contract.record_kinds.hit, 1);
1535        assert_eq!(contract.record_kinds.decision, 2);
1536        assert_eq!(contract.record_kinds.ordinal_hit, 3);
1537        assert_eq!(contract.record_kinds.phase, None);
1538        assert_eq!(contract.publication.commit_value, 1);
1539        assert_eq!(contract.publication.writer_ordering, "release");
1540        assert_eq!(contract.publication.reader_ordering, "acquire");
1541        assert!(
1542            contract
1543                .publication
1544                .complete_descriptors_independently_recoverable
1545        );
1546        assert_eq!(
1547            contract.context.published_identity,
1548            ["run", "worker", "test", "retry", "phase"]
1549        );
1550        assert_eq!(contract.context.zero, "background-or-unattributed");
1551        assert_eq!(contract.context.max, "reserved-runtime-sentinel");
1552        assert!(
1553            contract
1554                .completeness
1555                .zero_attachments_blocks_terminal_passing_attempt
1556        );
1557        assert!(
1558            contract
1559                .completeness
1560                .dropped_records_block_terminal_passing_attempt
1561        );
1562        assert!(
1563            contract
1564                .completeness
1565                .incomplete_records_block_terminal_passing_attempt
1566        );
1567        assert!(
1568            contract
1569                .completeness
1570                .context_zero_excluded_from_passed_per_test_coverage
1571        );
1572        assert!(contract.integrity.symlink_transport_fatal);
1573        assert_eq!(
1574            contract.supported_targets,
1575            [
1576                "aarch64-apple-darwin",
1577                "x86_64-apple-darwin",
1578                "aarch64-unknown-linux-gnu",
1579                "aarch64-unknown-linux-musl",
1580                "x86_64-unknown-linux-gnu",
1581                "x86_64-unknown-linux-musl",
1582            ]
1583        );
1584        assert_eq!(contract.unsupported_target, "fail-closed");
1585    }
1586
1587    #[test]
1588    fn rust_probe_transport_v3_adds_join_bounded_thread_phases_only() {
1589        let v1 = rust_probe_transport_contract().unwrap();
1590        let v3 = rust_probe_transport_v3_contract().unwrap();
1591        assert_eq!(v1.protocol_version, RUST_PROBE_TRANSPORT_PROTOCOL_VERSION);
1592        assert_eq!(v1.magic, RUST_PROBE_TRANSPORT_MAGIC);
1593        assert_eq!(v1.record_kinds.phase, None);
1594        assert_eq!(v1.thread_scope, None);
1595        assert_eq!(
1596            v3.protocol_version,
1597            RUST_PROBE_TRANSPORT_V3_PROTOCOL_VERSION
1598        );
1599        assert_eq!(v3.status, "candidate-private-frontend");
1600        assert_eq!(v3.magic, RUST_PROBE_TRANSPORT_V3_MAGIC);
1601        assert_eq!(v3.record_kinds.phase, Some(4));
1602        assert_eq!(v3.record_kinds.thread_phase, Some(5));
1603        assert_eq!(v3.record_kinds.thread_end, Some(6));
1604        assert_eq!(v3.record_kinds.test_boundary, Some(7));
1605        assert_eq!(v3.header_offsets.next_phase, Some(80));
1606        let thread_scope = v3.thread_scope.as_ref().expect("v3 freezes threadScope");
1607        assert_eq!(thread_scope.domain, "supercov-rust-thread-phase-v1\0");
1608        assert_eq!(
1609            thread_scope.escaped_thread_limitation,
1610            "RUST_THREAD_OUTLIVED_TEST"
1611        );
1612        assert!(thread_scope.escaped_thread_records_become_background);
1613        assert!(thread_scope.duplicate_thread_end_fatal);
1614        assert!(thread_scope.duplicate_test_boundary_fatal);
1615        assert!(thread_scope.thread_end_committed_when_start_routine_returns);
1616        assert!(thread_scope.test_boundary_committed_when_test_context_exits);
1617        assert_eq!(v3.header_size, v1.header_size);
1618        assert_eq!(v3.descriptor_size, v1.descriptor_size);
1619        assert_eq!(v3.token_size, v1.token_size);
1620        let mut v3_header = v3.header_offsets.clone();
1621        v3_header.next_phase = None;
1622        assert_eq!(v3_header, v1.header_offsets);
1623        assert_eq!(v3.descriptor_offsets, v1.descriptor_offsets);
1624        assert_eq!(v3.publication, v1.publication);
1625        assert_eq!(v3.integrity, v1.integrity);
1626        assert_eq!(v3.completeness, v1.completeness);
1627        assert_eq!(v3.supported_targets, v1.supported_targets);
1628        assert_eq!(v3.unsupported_target, v1.unsupported_target);
1629    }
1630}