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