1use 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#[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(°raded).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}