1use super::{ChoiceGuard, Protocol, Role, ValidationError};
4use proc_macro2::Ident;
5use serde::{Deserialize, Serialize};
6use std::collections::{BTreeSet, HashMap};
7
8const ATTR_THEOREM_PACKS: &str = "dsl.proof_bundles";
9const ATTR_REQUIRED_THEOREM_PACKS: &str = "dsl.required_proof_bundles";
10const ATTR_INFERRED_REQUIRED_THEOREM_PACKS: &str = "dsl.inferred_required_proof_bundles";
11const ATTR_ROLE_SETS: &str = "dsl.role_sets";
12const ATTR_TOPOLOGIES: &str = "dsl.topologies";
13const ATTR_TYPE_DECLARATIONS: &str = "dsl.type_decls";
14const ATTR_EFFECT_INTERFACE_DECLARATIONS: &str = "dsl.effect_decls";
15const ATTR_PROTOCOL_USES: &str = "dsl.protocol_uses";
16const ATTR_REGION_DECLARATIONS: &str = "dsl.fragment_decls";
17const ATTR_OPERATION_DECLARATIONS: &str = "dsl.operation_decls";
18const ATTR_GUEST_RUNTIME_DECLARATIONS: &str = "dsl.guest_runtime_decls";
19const ATTR_EXECUTION_PROFILE_DECLARATIONS: &str = "dsl.execution_profile_decls";
20const ATTR_PROTOCOL_EXECUTION_PROFILES: &str = "dsl.protocol_execution_profiles";
21const ATTR_AGREEMENT_PROFILE_DECLARATIONS: &str = "dsl.agreement_profile_decls";
22
23#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
25pub struct TheoremPackDeclaration {
26 pub name: String,
28 #[serde(default)]
30 pub capabilities: Vec<String>,
31 #[serde(default)]
33 pub version: Option<String>,
34 #[serde(default)]
36 pub issuer: Option<String>,
37 #[serde(default)]
39 pub constraints: Vec<String>,
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
44pub struct RoleSetDeclaration {
45 pub name: String,
47 #[serde(default)]
49 pub members: Vec<String>,
50 #[serde(default)]
52 pub subset_of: Option<String>,
53 #[serde(default)]
55 pub subset_start: Option<u32>,
56 #[serde(default)]
58 pub subset_end: Option<u32>,
59}
60
61#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
63pub struct TopologyDeclaration {
64 pub kind: String,
66 pub name: String,
68 #[serde(default)]
70 pub members: Vec<String>,
71}
72
73#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
75pub struct TypeDeclaration {
76 pub name: String,
78 pub is_alias: bool,
80 #[serde(default)]
82 pub alias_of: Option<String>,
83 #[serde(default)]
85 pub constructors: Vec<TypeConstructorDeclaration>,
86}
87
88#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
90pub struct TypeConstructorDeclaration {
91 pub name: String,
93 #[serde(default)]
95 pub payload_type: Option<String>,
96}
97
98#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
100pub struct EffectInterfaceDeclaration {
101 pub name: String,
103 #[serde(default)]
105 pub operations: Vec<EffectOperationDeclaration>,
106}
107
108#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
110#[serde(rename_all = "snake_case")]
111pub enum EffectAuthorityClass {
112 Authoritative,
114 #[default]
116 Command,
117 Observe,
119}
120
121#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
123pub struct EffectOperationDeclaration {
124 pub name: String,
126 #[serde(default)]
128 pub authority_class: EffectAuthorityClass,
129 pub semantic_class: String,
131 pub progress: String,
133 pub region: String,
135 pub agreement_use: String,
137 pub reentrancy: String,
139 pub input_type: String,
141 pub output_type: String,
143}
144
145#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
147pub struct EffectContractDeclaration {
148 pub interface_name: String,
150 pub operation_name: String,
152 pub authority_class: EffectAuthorityClass,
154 pub semantic_class: String,
156 pub progress: String,
158 pub region: String,
160 pub agreement_use: String,
162 pub admissibility: String,
164 pub totality: String,
166 pub timeout_policy: String,
168 pub reentrancy_policy: String,
170 pub handler_domain: String,
172}
173
174#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
176pub struct RegionDeclaration {
177 pub name: String,
179 #[serde(default)]
181 pub params: Vec<String>,
182}
183
184#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
186pub struct OperationParameterDeclaration {
187 pub name: String,
189 pub type_name: String,
191}
192
193#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
195pub struct ProgressAttachment {
196 pub contract_name: String,
198 #[serde(default)]
200 pub requires_profile: Option<String>,
201 #[serde(default)]
203 pub within_window: Option<String>,
204 #[serde(default)]
206 pub on_timeout: Option<String>,
207 #[serde(default)]
209 pub on_stall: Option<String>,
210}
211
212#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
214pub struct AgreementProfileDeclaration {
215 pub name: String,
217 pub visibility: String,
219 pub rule: String,
221 pub usable_at: String,
223 pub finalized_at: String,
225 pub evidence: String,
227}
228
229#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
231pub struct OperationAgreementAttachment {
232 pub profile_name: String,
234 #[serde(default)]
236 pub prestate: Option<String>,
237}
238
239#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
241pub struct OperationDeclaration {
242 pub name: String,
244 #[serde(default)]
246 pub params: Vec<OperationParameterDeclaration>,
247 pub owner_role: String,
249 #[serde(default)]
251 pub within: Option<String>,
252 #[serde(default)]
254 pub progress_contract: Option<ProgressAttachment>,
255 #[serde(default)]
257 pub agreement: Option<OperationAgreementAttachment>,
258 #[serde(default)]
260 pub child_effect_aggregation: Option<ChildEffectAggregation>,
261 pub body_source: String,
263}
264
265#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
267pub enum ChildEffectAggregationPolicy {
268 All,
270 First,
272 Threshold {
274 required_successes: u64,
276 },
277}
278
279#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
281pub struct ChildEffectAggregation {
282 pub policy: ChildEffectAggregationPolicy,
284}
285
286impl ChildEffectAggregationPolicy {
287 #[must_use]
289 pub fn dsl_name(&self) -> String {
290 match self {
291 Self::All => "all_success".to_string(),
292 Self::First => "first_success".to_string(),
293 Self::Threshold { required_successes } => {
294 format!("threshold_success({required_successes})")
295 }
296 }
297 }
298}
299
300impl ChildEffectAggregation {
301 #[must_use]
303 pub fn dsl_name(&self) -> String {
304 self.policy.dsl_name()
305 }
306}
307
308#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
310pub struct GuestRuntimeDeclaration {
311 pub name: String,
313 #[serde(default)]
315 pub uses: Vec<String>,
316 pub entry: String,
318}
319
320#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
322pub struct ExecutionProfileDeclaration {
323 pub name: String,
325 #[serde(default)]
327 pub fairness: Option<String>,
328 #[serde(default)]
330 pub admissibility: Option<String>,
331 #[serde(default)]
333 pub escalation_window: Option<String>,
334}
335
336#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
338#[serde(rename_all = "snake_case")]
339pub enum LanguageTier {
340 FullSpec,
341 SessionProjectable,
342 ProtocolMachineExecutable,
343 ProofOnly,
344}
345
346#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
348#[serde(rename_all = "snake_case")]
349pub enum AuthorityMetatheoryTier {
350 SessionTypedCoordination,
352 EvidencePublicationSemanticObjects,
355 RuntimeSemanticOnly,
358}
359
360#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
362pub struct AuthorityMetatheoryStatus {
363 pub strongest_tier: AuthorityMetatheoryTier,
364 pub diagnostic: String,
365}
366
367#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
369pub struct LanguageTierStatus {
370 pub strongest_tier: LanguageTier,
371 pub session_projectable: bool,
372 pub protocol_machine_executable: bool,
373 pub theory_convertible: bool,
374 pub proof_only: bool,
375 pub diagnostic: String,
376}
377
378#[derive(Debug)]
380pub struct Choreography {
381 pub name: Ident,
383 pub namespace: Option<String>,
385 pub roles: Vec<Role>,
387 pub protocol: Protocol,
389 pub attrs: HashMap<String, String>,
391}
392
393impl Choreography {
394 #[must_use]
396 pub fn qualified_name(&self) -> String {
397 match &self.namespace {
398 Some(ns) => format!("{}::{}", ns, self.name),
399 None => self.name.to_string(),
400 }
401 }
402
403 pub fn validate(&self) -> Result<(), ValidationError> {
410 for role in &self.roles {
412 if !self.protocol.mentions_role(role) {
413 return Err(ValidationError::UnusedRole(role.name().to_string()));
414 }
415 }
416
417 self.protocol.validate(&self.roles)?;
419 self.validate_proof_bundles()?;
420 self.validate_effect_surface()?;
421 self.validate_execution_profile_surface()?;
422 self.validate_operation_surface()?;
423
424 Ok(())
425 }
426
427 fn validate_proof_bundles(&self) -> Result<(), ValidationError> {
428 let bundles = self.theorem_packs();
429 let mut declared: BTreeSet<String> = BTreeSet::new();
430 for bundle in &bundles {
431 if !declared.insert(bundle.name.clone()) {
432 return Err(ValidationError::DuplicateProofBundle(bundle.name.clone()));
433 }
434 }
435
436 for required in self.required_theorem_packs() {
437 if !declared.contains(&required) {
438 return Err(ValidationError::MissingProofBundle(required));
439 }
440 }
441
442 let required_caps = self.required_theorem_pack_capabilities();
443 for capability in self.required_protocol_machine_core_capabilities() {
444 if !required_caps.contains(&capability) {
445 return Err(ValidationError::MissingCapability(capability));
446 }
447 }
448
449 Ok(())
450 }
451
452 fn validate_effect_surface(&self) -> Result<(), ValidationError> {
453 let mut effect_names = BTreeSet::new();
454 let mut effect_ops: HashMap<String, HashMap<String, EffectOperationDeclaration>> =
455 HashMap::new();
456 for effect in self.effect_interface_declarations() {
457 if !effect_names.insert(effect.name.clone()) {
458 return Err(ValidationError::ExtensionError(format!(
459 "duplicate effect interface declaration `{}`",
460 effect.name
461 )));
462 }
463 let mut ops = HashMap::new();
464 for op in effect.operations {
465 let semantic_class_ok = matches!(
466 op.semantic_class.as_str(),
467 "authoritative" | "observational" | "best_effort"
468 );
469 if !semantic_class_ok {
470 return Err(ValidationError::ExtensionError(format!(
471 "effect operation `{}.{}` has unsupported `class {}`",
472 effect.name, op.name, op.semantic_class
473 )));
474 }
475 let progress_ok = matches!(op.progress.as_str(), "immediate" | "may_block");
476 if !progress_ok {
477 return Err(ValidationError::ExtensionError(format!(
478 "effect operation `{}.{}` has unsupported `progress {}`",
479 effect.name, op.name, op.progress
480 )));
481 }
482 if !matches!(op.region.as_str(), "session" | "fragment" | "global") {
483 return Err(ValidationError::ExtensionError(format!(
484 "effect operation `{}.{}` has unsupported `region {}`",
485 effect.name, op.name, op.region
486 )));
487 }
488 if !matches!(op.agreement_use.as_str(), "required" | "none" | "forbidden") {
489 return Err(ValidationError::ExtensionError(format!(
490 "effect operation `{}.{}` has unsupported `agreement_use {}`",
491 effect.name, op.name, op.agreement_use
492 )));
493 }
494 if !matches!(
495 op.reentrancy.as_str(),
496 "allow" | "reject_same_operation" | "reject_same_fragment"
497 ) {
498 return Err(ValidationError::ExtensionError(format!(
499 "effect operation `{}.{}` has unsupported `reentrancy {}`",
500 effect.name, op.name, op.reentrancy
501 )));
502 }
503 if matches!(op.authority_class, EffectAuthorityClass::Observe)
504 && op.semantic_class != "observational"
505 {
506 return Err(ValidationError::ExtensionError(format!(
507 "effect operation `{}.{}` is observational and must declare `class : observational`",
508 effect.name, op.name
509 )));
510 }
511 if matches!(op.authority_class, EffectAuthorityClass::Authoritative)
512 && op.semantic_class != "authoritative"
513 {
514 return Err(ValidationError::ExtensionError(format!(
515 "effect operation `{}.{}` is authoritative and must declare `class : authoritative`",
516 effect.name, op.name
517 )));
518 }
519 if op.progress == "immediate"
520 && matches!(op.authority_class, EffectAuthorityClass::Authoritative)
521 {
522 return Err(ValidationError::ExtensionError(format!(
523 "effect operation `{}.{}` may not be both `authoritative` and `progress immediate`",
524 effect.name, op.name
525 )));
526 }
527 if op.agreement_use == "required"
528 && matches!(op.authority_class, EffectAuthorityClass::Observe)
529 {
530 return Err(ValidationError::ExtensionError(format!(
531 "effect operation `{}.{}` may not require agreement use on an observational surface",
532 effect.name, op.name
533 )));
534 }
535 if ops.insert(op.name.clone(), op.clone()).is_some() {
536 return Err(ValidationError::ExtensionError(format!(
537 "duplicate effect operation `{}.{}`",
538 effect.name, op.name
539 )));
540 }
541 }
542 effect_ops.insert(effect.name, ops);
543 }
544
545 let declared = effect_names;
546 let used: BTreeSet<String> = self.protocol_uses().into_iter().collect();
547 for effect in &used {
548 if !declared.contains(effect) {
549 return Err(ValidationError::ExtensionError(format!(
550 "protocol uses undeclared effect interface `{effect}`"
551 )));
552 }
553 }
554
555 fn validate_expr(
556 expr: &super::AuthorityExpr,
557 effect_ops: &HashMap<String, HashMap<String, EffectOperationDeclaration>>,
558 used: &BTreeSet<String>,
559 ) -> Result<(), ValidationError> {
560 match expr {
561 super::AuthorityExpr::Check {
562 effect, operation, ..
563 } => {
564 if !used.contains(effect) {
565 return Err(ValidationError::ExtensionError(format!(
566 "effect invocation `{effect}.{operation}` is not allowed without `uses {effect}`"
567 )));
568 }
569 let Some(ops) = effect_ops.get(effect) else {
570 return Err(ValidationError::ExtensionError(format!(
571 "effect invocation references undeclared interface `{effect}`"
572 )));
573 };
574 let Some(op_decl) = ops.get(operation) else {
575 return Err(ValidationError::ExtensionError(format!(
576 "effect invocation references undeclared operation `{effect}.{operation}`"
577 )));
578 };
579 if matches!(op_decl.authority_class, EffectAuthorityClass::Observe) {
580 return Err(ValidationError::ExtensionError(format!(
581 "effect invocation `{effect}.{operation}` is observational and may not be invoked with `check`"
582 )));
583 }
584 Ok(())
585 }
586 super::AuthorityExpr::Observe {
587 effect, operation, ..
588 } => {
589 if !used.contains(effect) {
590 return Err(ValidationError::ExtensionError(format!(
591 "effect invocation `{effect}.{operation}` is not allowed without `uses {effect}`"
592 )));
593 }
594 let Some(ops) = effect_ops.get(effect) else {
595 return Err(ValidationError::ExtensionError(format!(
596 "effect invocation references undeclared interface `{effect}`"
597 )));
598 };
599 let Some(op_decl) = ops.get(operation) else {
600 return Err(ValidationError::ExtensionError(format!(
601 "effect invocation references undeclared operation `{effect}.{operation}`"
602 )));
603 };
604 if !matches!(op_decl.authority_class, EffectAuthorityClass::Observe) {
605 return Err(ValidationError::ExtensionError(format!(
606 "effect invocation `{effect}.{operation}` is not observational and may not be invoked with `observe`"
607 )));
608 }
609 Ok(())
610 }
611 super::AuthorityExpr::Var(_)
612 | super::AuthorityExpr::Transfer { .. }
613 | super::AuthorityExpr::Constructor { .. }
614 | super::AuthorityExpr::Call { .. } => Ok(()),
615 }
616 }
617
618 fn validate_protocol_effects(
619 protocol: &Protocol,
620 effect_ops: &HashMap<String, HashMap<String, EffectOperationDeclaration>>,
621 used: &BTreeSet<String>,
622 ) -> Result<(), ValidationError> {
623 match protocol {
624 Protocol::Begin { continuation, .. }
625 | Protocol::Await { continuation, .. }
626 | Protocol::Resolve { continuation, .. }
627 | Protocol::Invalidate { continuation, .. }
628 | Protocol::Send { continuation, .. }
629 | Protocol::Broadcast { continuation, .. }
630 | Protocol::Extension { continuation, .. }
631 | Protocol::Let { continuation, .. }
632 | Protocol::Publish { continuation, .. }
633 | Protocol::PublishAuthority { continuation, .. }
634 | Protocol::Materialize { continuation, .. }
635 | Protocol::Handoff { continuation, .. }
636 | Protocol::DependentWork { continuation, .. } => {
637 if let Protocol::Let { mode, expr, .. } = protocol {
638 validate_expr(expr, effect_ops, used)?;
639 match (mode, expr) {
640 (
641 super::AuthorityBindingMode::Plain,
642 super::AuthorityExpr::Check {
643 effect, operation, ..
644 },
645 ) => {
646 let op_decl = effect_ops
647 .get(effect)
648 .and_then(|ops| ops.get(operation))
649 .ok_or_else(|| {
650 ValidationError::ExtensionError(format!(
651 "effect invocation `{effect}.{operation}` was not declared"
652 ))
653 })?;
654 if matches!(
655 op_decl.authority_class,
656 EffectAuthorityClass::Authoritative
657 ) {
658 return Err(ValidationError::ExtensionError(format!(
659 "authoritative effect invocation `{effect}.{operation}` must bind through `authoritative let`"
660 )));
661 }
662 }
663 (
664 super::AuthorityBindingMode::Plain,
665 super::AuthorityExpr::Observe { .. },
666 ) => {
667 return Err(ValidationError::ExtensionError(
668 "`observe` expressions must bind through `observe let`"
669 .to_string(),
670 ));
671 }
672 (
673 super::AuthorityBindingMode::Authoritative,
674 super::AuthorityExpr::Check {
675 effect, operation, ..
676 },
677 ) => {
678 let op_decl = effect_ops
679 .get(effect)
680 .and_then(|ops| ops.get(operation))
681 .ok_or_else(|| {
682 ValidationError::ExtensionError(format!(
683 "effect invocation `{effect}.{operation}` was not declared"
684 ))
685 })?;
686 if !matches!(
687 op_decl.authority_class,
688 EffectAuthorityClass::Authoritative
689 ) {
690 return Err(ValidationError::ExtensionError(format!(
691 "`authoritative let` may only bind authoritative effect invocations; `{effect}.{operation}` is {:?}",
692 op_decl.authority_class
693 )));
694 }
695 }
696 (
697 super::AuthorityBindingMode::Observe,
698 super::AuthorityExpr::Observe { .. },
699 ) => {}
700 (super::AuthorityBindingMode::Plain, _) => {}
701 (super::AuthorityBindingMode::Authoritative, _) => {
702 return Err(ValidationError::ExtensionError(
703 "`authoritative let` must bind a `check` expression"
704 .to_string(),
705 ));
706 }
707 (super::AuthorityBindingMode::Observe, _) => {
708 return Err(ValidationError::ExtensionError(
709 "`observe let` must bind an `observe` expression".to_string(),
710 ));
711 }
712 }
713 }
714 validate_protocol_effects(continuation, effect_ops, used)
715 }
716 Protocol::Choice { branches, .. } => {
717 for branch in branches {
718 if let Some(ChoiceGuard::Evidence {
719 effect, operation, ..
720 }) = &branch.guard
721 {
722 if !used.contains(effect) {
723 return Err(ValidationError::ExtensionError(format!(
724 "effect guard `{effect}.{operation}` is not allowed without `uses {effect}`"
725 )));
726 }
727 let Some(ops) = effect_ops.get(effect) else {
728 return Err(ValidationError::ExtensionError(format!(
729 "effect guard references undeclared interface `{effect}`"
730 )));
731 };
732 let Some(op_decl) = ops.get(operation) else {
733 return Err(ValidationError::ExtensionError(format!(
734 "effect guard references undeclared operation `{effect}.{operation}`"
735 )));
736 };
737 if matches!(op_decl.authority_class, EffectAuthorityClass::Observe) {
738 return Err(ValidationError::ExtensionError(format!(
739 "effect guard `{effect}.{operation}` is observational and may not be invoked with `check`"
740 )));
741 }
742 }
743 validate_protocol_effects(&branch.protocol, effect_ops, used)?;
744 }
745 Ok(())
746 }
747 Protocol::Case { expr, branches } => {
748 validate_expr(expr, effect_ops, used)?;
749 for branch in branches {
750 validate_protocol_effects(&branch.protocol, effect_ops, used)?;
751 }
752 Ok(())
753 }
754 Protocol::Timeout {
755 body,
756 on_timeout,
757 on_cancel,
758 ..
759 } => {
760 validate_protocol_effects(body, effect_ops, used)?;
761 validate_protocol_effects(on_timeout, effect_ops, used)?;
762 if let Some(on_cancel) = on_cancel.as_deref() {
763 validate_protocol_effects(on_cancel, effect_ops, used)?;
764 }
765 Ok(())
766 }
767 Protocol::Loop { body, .. } | Protocol::Rec { body, .. } => {
768 validate_protocol_effects(body, effect_ops, used)
769 }
770 Protocol::Parallel { protocols } => {
771 for protocol in protocols {
772 validate_protocol_effects(protocol, effect_ops, used)?;
773 }
774 Ok(())
775 }
776 Protocol::Var(_) | Protocol::End => Ok(()),
777 }
778 }
779
780 validate_protocol_effects(&self.protocol, &effect_ops, &used)
781 }
782
783 fn validate_execution_profile_surface(&self) -> Result<(), ValidationError> {
784 let mut declared = BTreeSet::new();
785 for profile in self.execution_profile_declarations() {
786 if !declared.insert(profile.name.clone()) {
787 return Err(ValidationError::ExtensionError(format!(
788 "duplicate execution profile declaration `{}`",
789 profile.name
790 )));
791 }
792 }
793
794 for profile in self.protocol_execution_profiles() {
795 if !declared.contains(&profile) {
796 return Err(ValidationError::ExtensionError(format!(
797 "protocol references undeclared execution profile `{profile}`"
798 )));
799 }
800 }
801
802 Ok(())
803 }
804
805 fn validate_operation_surface(&self) -> Result<(), ValidationError> {
806 let declared_agreement_profiles = self
807 .agreement_profile_declarations()
808 .into_iter()
809 .map(|profile| profile.name)
810 .collect::<BTreeSet<_>>();
811
812 for operation in self.operation_declarations() {
813 if operation.progress_contract.is_none() {
814 return Err(ValidationError::ExtensionError(format!(
815 "operation `{}` is parity-critical and must declare a progress contract",
816 operation.name
817 )));
818 }
819 if !operation
820 .progress_contract
821 .as_ref()
822 .is_some_and(|progress| progress.is_explicit())
823 {
824 return Err(ValidationError::ExtensionError(format!(
825 "operation `{}` must declare explicit progress metadata using `requires`, `within`, `on timeout`, or `on stall`",
826 operation.name
827 )));
828 }
829 let Some(agreement) = operation.agreement.as_ref() else {
830 return Err(ValidationError::ExtensionError(format!(
831 "operation `{}` must attach a named agreement profile",
832 operation.name
833 )));
834 };
835 if !declared_agreement_profiles.contains(&agreement.profile_name) {
836 return Err(ValidationError::ExtensionError(format!(
837 "operation `{}` references undeclared agreement profile `{}`",
838 operation.name, agreement.profile_name
839 )));
840 }
841 }
842 Ok(())
843 }
844
845 #[must_use]
846 pub fn language_tier_status(&self) -> LanguageTierStatus {
847 let theory_convertible = super::convert::protocol_to_global(&self.protocol).is_ok();
848 let session_blocker = find_session_projection_blocker(&self.protocol);
849 let missing_progress = self
850 .operation_declarations()
851 .iter()
852 .find(|operation| {
853 operation.progress_contract.is_none()
854 || !operation
855 .progress_contract
856 .as_ref()
857 .is_some_and(|progress| progress.is_explicit())
858 })
859 .map(|operation| {
860 format!(
861 "operation `{}` is missing the required progress contract",
862 operation.name
863 )
864 });
865
866 let protocol_machine_executable = missing_progress.is_none();
867 let session_projectable = session_blocker.is_none();
868 let strongest_tier = match (session_projectable, protocol_machine_executable) {
869 (true, true) => LanguageTier::SessionProjectable,
870 (false, true) => LanguageTier::ProtocolMachineExecutable,
871 (_, false) => LanguageTier::ProofOnly,
872 };
873 let diagnostic = if let Some(blocker) = session_blocker {
874 format!(
875 "full spec is valid, protocol-machine execution is available, but session projection is blocked: {blocker}"
876 )
877 } else if let Some(missing_progress) = missing_progress {
878 format!(
879 "full spec is valid for proof analysis only; protocol-machine execution is blocked: {missing_progress}"
880 )
881 } else if !theory_convertible {
882 "full spec is valid, protocol-machine execution is available, and the protocol is session-projectable, but theory conversion remains explicitly unavailable for this surface".to_string()
883 } else {
884 "full spec is valid, protocol-machine execution is available, the protocol is session-projectable, and theory conversion is available".to_string()
885 };
886
887 LanguageTierStatus {
888 strongest_tier,
889 session_projectable,
890 protocol_machine_executable,
891 theory_convertible,
892 proof_only: matches!(strongest_tier, LanguageTier::ProofOnly),
893 diagnostic,
894 }
895 }
896
897 #[must_use]
898 pub fn authority_metatheory_status(&self) -> AuthorityMetatheoryStatus {
899 let strongest_tier = authority_metatheory_tier(&self.protocol);
900 let diagnostic = match authority_metatheory_blocker(&self.protocol) {
901 Some(blocker) => format!(
902 "authority-bearing constructs remain executable, but the supported theorem slice stops before this runtime semantic surface: {blocker}"
903 ),
904 None => match strongest_tier {
905 AuthorityMetatheoryTier::SessionTypedCoordination =>
906 "the protocol stays within ordinary session-typed coordination; no authority-specific semantic proof obligations are introduced".to_string(),
907 AuthorityMetatheoryTier::EvidencePublicationSemanticObjects =>
908 "the protocol stays within the supported authority theorem slice: evidence-bearing reads and canonical publication/materialization are justified at the protocol-machine semantic-object layer, while session typing continues to cover only the coordination skeleton".to_string(),
909 AuthorityMetatheoryTier::RuntimeSemanticOnly =>
910 "authority-bearing runtime semantics are present, but they currently sit outside the supported theorem slice".to_string(),
911 },
912 };
913
914 AuthorityMetatheoryStatus {
915 strongest_tier,
916 diagnostic,
917 }
918 }
919
920 #[must_use]
922 pub fn get_attributes(&self) -> &HashMap<String, String> {
923 &self.attrs
924 }
925
926 pub fn get_attributes_mut(&mut self) -> &mut HashMap<String, String> {
928 &mut self.attrs
929 }
930
931 #[must_use]
933 pub fn get_attribute(&self, key: &str) -> Option<&String> {
934 self.attrs.get(key)
935 }
936
937 pub fn set_attribute(&mut self, key: String, value: String) {
939 self.attrs.insert(key, value);
940 }
941
942 pub fn remove_attribute(&mut self, key: &str) -> Option<String> {
944 self.attrs.remove(key)
945 }
946
947 #[must_use]
949 pub fn has_attribute(&self, key: &str) -> bool {
950 self.attrs.contains_key(key)
951 }
952
953 pub fn get_attribute_as<T>(&self, key: &str) -> Option<T>
955 where
956 T: std::str::FromStr,
957 {
958 self.get_attribute(key)?.parse().ok()
959 }
960
961 pub fn get_attribute_as_bool(&self, key: &str) -> Option<bool> {
963 let value = self.get_attribute(key)?;
964 match value.to_lowercase().as_str() {
965 "true" | "1" | "yes" | "on" => Some(true),
966 "false" | "0" | "no" | "off" => Some(false),
967 _ => None,
968 }
969 }
970
971 pub fn clear_attributes(&mut self) {
973 self.attrs.clear();
974 }
975
976 pub fn attribute_count(&self) -> usize {
978 self.attrs.len()
979 }
980
981 pub fn attribute_keys(&self) -> Vec<&String> {
983 self.attrs.keys().collect()
984 }
985
986 pub fn validate_required_attributes(&self, required_keys: &[&str]) -> Result<(), Vec<String>> {
988 let missing: Vec<String> = required_keys
989 .iter()
990 .filter(|&key| !self.has_attribute(key))
991 .map(|&key| key.to_string())
992 .collect();
993
994 if missing.is_empty() {
995 Ok(())
996 } else {
997 Err(missing)
998 }
999 }
1000
1001 pub fn find_nodes_with_annotation(&self, key: &str) -> Vec<&Protocol> {
1003 let mut nodes = Vec::new();
1004 self.protocol.collect_nodes_with_annotation(key, &mut nodes);
1005 nodes
1006 }
1007
1008 pub fn find_nodes_with_annotation_value(&self, key: &str, value: &str) -> Vec<&Protocol> {
1010 let mut nodes = Vec::new();
1011 self.protocol
1012 .collect_nodes_with_annotation_value(key, value, &mut nodes);
1013 nodes
1014 }
1015
1016 pub fn total_annotation_count(&self) -> usize {
1018 self.attribute_count() + self.protocol.deep_annotation_count()
1019 }
1020
1021 pub fn set_theorem_packs(
1023 &mut self,
1024 theorem_packs: &[TheoremPackDeclaration],
1025 ) -> Result<(), String> {
1026 let encoded = serde_json::to_string(theorem_packs)
1027 .map_err(|e| format!("encode theorem packs: {e}"))?;
1028 self.attrs.insert(ATTR_THEOREM_PACKS.to_string(), encoded);
1029 Ok(())
1030 }
1031
1032 #[must_use]
1034 pub fn theorem_packs(&self) -> Vec<TheoremPackDeclaration> {
1035 self.attrs
1036 .get(ATTR_THEOREM_PACKS)
1037 .and_then(|s| serde_json::from_str::<Vec<TheoremPackDeclaration>>(s).ok())
1038 .unwrap_or_default()
1039 }
1040
1041 pub fn set_required_theorem_packs(&mut self, required: &[String]) -> Result<(), String> {
1043 let encoded = serde_json::to_string(required)
1044 .map_err(|e| format!("encode required theorem packs: {e}"))?;
1045 self.attrs
1046 .insert(ATTR_REQUIRED_THEOREM_PACKS.to_string(), encoded);
1047 Ok(())
1048 }
1049
1050 #[must_use]
1052 pub fn required_theorem_packs(&self) -> Vec<String> {
1053 self.attrs
1054 .get(ATTR_REQUIRED_THEOREM_PACKS)
1055 .and_then(|s| serde_json::from_str::<Vec<String>>(s).ok())
1056 .unwrap_or_default()
1057 }
1058
1059 pub fn set_inferred_required_theorem_packs(
1061 &mut self,
1062 required: &[String],
1063 ) -> Result<(), String> {
1064 let encoded = serde_json::to_string(required)
1065 .map_err(|e| format!("encode inferred theorem packs: {e}"))?;
1066 self.attrs
1067 .insert(ATTR_INFERRED_REQUIRED_THEOREM_PACKS.to_string(), encoded);
1068 Ok(())
1069 }
1070
1071 #[must_use]
1073 pub fn inferred_required_theorem_packs(&self) -> Vec<String> {
1074 self.attrs
1075 .get(ATTR_INFERRED_REQUIRED_THEOREM_PACKS)
1076 .and_then(|s| serde_json::from_str::<Vec<String>>(s).ok())
1077 .unwrap_or_default()
1078 }
1079
1080 pub fn set_role_sets(&mut self, role_sets: &[RoleSetDeclaration]) -> Result<(), String> {
1082 let encoded =
1083 serde_json::to_string(role_sets).map_err(|e| format!("encode role sets: {e}"))?;
1084 self.attrs.insert(ATTR_ROLE_SETS.to_string(), encoded);
1085 Ok(())
1086 }
1087
1088 #[must_use]
1090 pub fn role_sets(&self) -> Vec<RoleSetDeclaration> {
1091 self.attrs
1092 .get(ATTR_ROLE_SETS)
1093 .and_then(|s| serde_json::from_str::<Vec<RoleSetDeclaration>>(s).ok())
1094 .unwrap_or_default()
1095 }
1096
1097 pub fn set_topologies(&mut self, topologies: &[TopologyDeclaration]) -> Result<(), String> {
1099 let encoded =
1100 serde_json::to_string(topologies).map_err(|e| format!("encode topologies: {e}"))?;
1101 self.attrs.insert(ATTR_TOPOLOGIES.to_string(), encoded);
1102 Ok(())
1103 }
1104
1105 #[must_use]
1107 pub fn topologies(&self) -> Vec<TopologyDeclaration> {
1108 self.attrs
1109 .get(ATTR_TOPOLOGIES)
1110 .and_then(|s| serde_json::from_str::<Vec<TopologyDeclaration>>(s).ok())
1111 .unwrap_or_default()
1112 }
1113
1114 pub fn set_type_declarations(&mut self, decls: &[TypeDeclaration]) -> Result<(), String> {
1116 let encoded =
1117 serde_json::to_string(decls).map_err(|e| format!("encode type declarations: {e}"))?;
1118 self.attrs
1119 .insert(ATTR_TYPE_DECLARATIONS.to_string(), encoded);
1120 Ok(())
1121 }
1122
1123 #[must_use]
1125 pub fn type_declarations(&self) -> Vec<TypeDeclaration> {
1126 self.attrs
1127 .get(ATTR_TYPE_DECLARATIONS)
1128 .and_then(|s| serde_json::from_str::<Vec<TypeDeclaration>>(s).ok())
1129 .unwrap_or_default()
1130 }
1131
1132 pub fn set_effect_interface_declarations(
1134 &mut self,
1135 decls: &[EffectInterfaceDeclaration],
1136 ) -> Result<(), String> {
1137 let encoded =
1138 serde_json::to_string(decls).map_err(|e| format!("encode effect declarations: {e}"))?;
1139 self.attrs
1140 .insert(ATTR_EFFECT_INTERFACE_DECLARATIONS.to_string(), encoded);
1141 Ok(())
1142 }
1143
1144 #[must_use]
1146 pub fn effect_interface_declarations(&self) -> Vec<EffectInterfaceDeclaration> {
1147 self.attrs
1148 .get(ATTR_EFFECT_INTERFACE_DECLARATIONS)
1149 .and_then(|s| serde_json::from_str::<Vec<EffectInterfaceDeclaration>>(s).ok())
1150 .unwrap_or_default()
1151 }
1152
1153 #[must_use]
1156 pub fn effect_contract_declarations(&self) -> Vec<EffectContractDeclaration> {
1157 let used: BTreeSet<String> = self.protocol_uses().into_iter().collect();
1158 self.effect_interface_declarations()
1159 .into_iter()
1160 .flat_map(|effect| {
1161 let is_used = used.contains(effect.name.as_str());
1162 effect.operations.into_iter().map(move |op| {
1163 let admissibility = if is_used {
1164 "declared_use_only"
1165 } else {
1166 "internal_only"
1167 };
1168 let (totality, timeout_policy) = match op.progress.as_str() {
1169 "immediate" => ("immediate", "none"),
1170 "may_block" => ("may_block", "required"),
1171 _ => ("may_block", "required"),
1172 };
1173 let handler_domain = if is_used { "external" } else { "internal" };
1174 EffectContractDeclaration {
1175 interface_name: effect.name.clone(),
1176 operation_name: op.name,
1177 authority_class: op.authority_class,
1178 semantic_class: op.semantic_class.clone(),
1179 progress: op.progress.clone(),
1180 region: op.region.clone(),
1181 agreement_use: op.agreement_use.clone(),
1182 admissibility: admissibility.to_string(),
1183 totality: totality.to_string(),
1184 timeout_policy: timeout_policy.to_string(),
1185 reentrancy_policy: op.reentrancy,
1186 handler_domain: handler_domain.to_string(),
1187 }
1188 })
1189 })
1190 .collect()
1191 }
1192
1193 pub fn set_protocol_uses(&mut self, uses: &[String]) -> Result<(), String> {
1195 let encoded =
1196 serde_json::to_string(uses).map_err(|e| format!("encode protocol uses: {e}"))?;
1197 self.attrs.insert(ATTR_PROTOCOL_USES.to_string(), encoded);
1198 Ok(())
1199 }
1200
1201 #[must_use]
1203 pub fn protocol_uses(&self) -> Vec<String> {
1204 self.attrs
1205 .get(ATTR_PROTOCOL_USES)
1206 .and_then(|s| serde_json::from_str::<Vec<String>>(s).ok())
1207 .unwrap_or_default()
1208 }
1209
1210 pub fn set_region_declarations(&mut self, decls: &[RegionDeclaration]) -> Result<(), String> {
1212 let encoded =
1213 serde_json::to_string(decls).map_err(|e| format!("encode region declarations: {e}"))?;
1214 self.attrs
1215 .insert(ATTR_REGION_DECLARATIONS.to_string(), encoded);
1216 Ok(())
1217 }
1218
1219 #[must_use]
1221 pub fn region_declarations(&self) -> Vec<RegionDeclaration> {
1222 self.attrs
1223 .get(ATTR_REGION_DECLARATIONS)
1224 .and_then(|s| serde_json::from_str::<Vec<RegionDeclaration>>(s).ok())
1225 .unwrap_or_default()
1226 }
1227
1228 pub fn set_operation_declarations(
1230 &mut self,
1231 decls: &[OperationDeclaration],
1232 ) -> Result<(), String> {
1233 let encoded = serde_json::to_string(decls)
1234 .map_err(|e| format!("encode operation declarations: {e}"))?;
1235 self.attrs
1236 .insert(ATTR_OPERATION_DECLARATIONS.to_string(), encoded);
1237 Ok(())
1238 }
1239
1240 #[must_use]
1242 pub fn operation_declarations(&self) -> Vec<OperationDeclaration> {
1243 self.attrs
1244 .get(ATTR_OPERATION_DECLARATIONS)
1245 .and_then(|s| serde_json::from_str::<Vec<OperationDeclaration>>(s).ok())
1246 .unwrap_or_default()
1247 }
1248
1249 pub fn set_guest_runtime_declarations(
1251 &mut self,
1252 decls: &[GuestRuntimeDeclaration],
1253 ) -> Result<(), String> {
1254 let encoded = serde_json::to_string(decls)
1255 .map_err(|e| format!("encode guest runtime declarations: {e}"))?;
1256 self.attrs
1257 .insert(ATTR_GUEST_RUNTIME_DECLARATIONS.to_string(), encoded);
1258 Ok(())
1259 }
1260
1261 #[must_use]
1263 pub fn guest_runtime_declarations(&self) -> Vec<GuestRuntimeDeclaration> {
1264 self.attrs
1265 .get(ATTR_GUEST_RUNTIME_DECLARATIONS)
1266 .and_then(|s| serde_json::from_str::<Vec<GuestRuntimeDeclaration>>(s).ok())
1267 .unwrap_or_default()
1268 }
1269
1270 pub fn set_execution_profile_declarations(
1272 &mut self,
1273 decls: &[ExecutionProfileDeclaration],
1274 ) -> Result<(), String> {
1275 let encoded = serde_json::to_string(decls)
1276 .map_err(|e| format!("encode execution profile declarations: {e}"))?;
1277 self.attrs
1278 .insert(ATTR_EXECUTION_PROFILE_DECLARATIONS.to_string(), encoded);
1279 Ok(())
1280 }
1281
1282 #[must_use]
1284 pub fn execution_profile_declarations(&self) -> Vec<ExecutionProfileDeclaration> {
1285 self.attrs
1286 .get(ATTR_EXECUTION_PROFILE_DECLARATIONS)
1287 .and_then(|s| serde_json::from_str::<Vec<ExecutionProfileDeclaration>>(s).ok())
1288 .unwrap_or_default()
1289 }
1290
1291 pub fn set_protocol_execution_profiles(&mut self, profiles: &[String]) -> Result<(), String> {
1293 let encoded = serde_json::to_string(profiles)
1294 .map_err(|e| format!("encode protocol execution profiles: {e}"))?;
1295 self.attrs
1296 .insert(ATTR_PROTOCOL_EXECUTION_PROFILES.to_string(), encoded);
1297 Ok(())
1298 }
1299
1300 #[must_use]
1302 pub fn protocol_execution_profiles(&self) -> Vec<String> {
1303 self.attrs
1304 .get(ATTR_PROTOCOL_EXECUTION_PROFILES)
1305 .and_then(|s| serde_json::from_str::<Vec<String>>(s).ok())
1306 .unwrap_or_default()
1307 }
1308
1309 pub fn set_agreement_profile_declarations(
1311 &mut self,
1312 decls: &[AgreementProfileDeclaration],
1313 ) -> Result<(), String> {
1314 let encoded = serde_json::to_string(decls)
1315 .map_err(|e| format!("encode agreement profile declarations: {e}"))?;
1316 self.attrs
1317 .insert(ATTR_AGREEMENT_PROFILE_DECLARATIONS.to_string(), encoded);
1318 Ok(())
1319 }
1320
1321 #[must_use]
1323 pub fn agreement_profile_declarations(&self) -> Vec<AgreementProfileDeclaration> {
1324 self.attrs
1325 .get(ATTR_AGREEMENT_PROFILE_DECLARATIONS)
1326 .and_then(|s| serde_json::from_str::<Vec<AgreementProfileDeclaration>>(s).ok())
1327 .unwrap_or_default()
1328 }
1329
1330 fn required_theorem_pack_capabilities(&self) -> BTreeSet<String> {
1331 let required = self.required_theorem_packs();
1332 let required_set: BTreeSet<&str> = required.iter().map(String::as_str).collect();
1333 self.theorem_packs()
1334 .into_iter()
1335 .filter(|bundle| required_set.contains(bundle.name.as_str()))
1336 .flat_map(|bundle| bundle.capabilities.into_iter())
1337 .collect()
1338 }
1339
1340 fn required_protocol_machine_core_capabilities(&self) -> BTreeSet<String> {
1341 fn collect(protocol: &Protocol, out: &mut BTreeSet<String>) {
1342 if let Some(cap) = protocol.get_annotations().custom("required_capability") {
1343 out.insert(cap.to_string());
1344 }
1345 match protocol {
1346 Protocol::Begin { continuation, .. }
1347 | Protocol::Await { continuation, .. }
1348 | Protocol::Resolve { continuation, .. }
1349 | Protocol::Invalidate { continuation, .. }
1350 | Protocol::Send { continuation, .. }
1351 | Protocol::Broadcast { continuation, .. }
1352 | Protocol::Extension { continuation, .. }
1353 | Protocol::Publish { continuation, .. }
1354 | Protocol::PublishAuthority { continuation, .. }
1355 | Protocol::Materialize { continuation, .. }
1356 | Protocol::Handoff { continuation, .. }
1357 | Protocol::DependentWork { continuation, .. } => collect(continuation, out),
1358 Protocol::Choice { branches, .. } => {
1359 for branch in branches {
1360 collect(&branch.protocol, out);
1361 }
1362 }
1363 Protocol::Loop { body, .. } | Protocol::Rec { body, .. } => collect(body, out),
1364 Protocol::Let { continuation, .. } => collect(continuation, out),
1365 Protocol::Case { branches, .. } => {
1366 for branch in branches {
1367 collect(&branch.protocol, out);
1368 }
1369 }
1370 Protocol::Timeout {
1371 body,
1372 on_timeout,
1373 on_cancel,
1374 ..
1375 } => {
1376 collect(body, out);
1377 collect(on_timeout, out);
1378 if let Some(on_cancel) = on_cancel.as_deref() {
1379 collect(on_cancel, out);
1380 }
1381 }
1382 Protocol::Parallel { protocols } => {
1383 for p in protocols {
1384 collect(p, out);
1385 }
1386 }
1387 Protocol::Var(_) | Protocol::End => {}
1388 }
1389 }
1390
1391 let mut out = BTreeSet::new();
1392 collect(&self.protocol, &mut out);
1393 out
1394 }
1395}
1396
1397impl ProgressAttachment {
1398 #[must_use]
1400 pub fn is_explicit(&self) -> bool {
1401 self.requires_profile.is_some()
1402 || self.within_window.is_some()
1403 || self.on_timeout.is_some()
1404 || self.on_stall.is_some()
1405 }
1406}
1407
1408fn find_session_projection_blocker(protocol: &Protocol) -> Option<&'static str> {
1409 match protocol {
1410 Protocol::Begin { .. } => Some("begin requires protocol-machine commitment semantics"),
1411 Protocol::Await { .. } => Some("await requires protocol-machine commitment semantics"),
1412 Protocol::Resolve { .. } => Some("resolve requires protocol-machine commitment semantics"),
1413 Protocol::Invalidate { .. } => {
1414 Some("invalidate requires protocol-machine commitment semantics")
1415 }
1416 Protocol::Send { continuation, .. }
1417 | Protocol::Broadcast { continuation, .. }
1418 | Protocol::Let { continuation, .. }
1419 | Protocol::Publish { continuation, .. }
1420 | Protocol::PublishAuthority { continuation, .. }
1421 | Protocol::Materialize { continuation, .. }
1422 | Protocol::Handoff { continuation, .. }
1423 | Protocol::DependentWork { continuation, .. }
1424 | Protocol::Extension { continuation, .. } => find_session_projection_blocker(continuation),
1425 Protocol::Case { branches, .. } => branches
1426 .iter()
1427 .find_map(|branch| find_session_projection_blocker(&branch.protocol)),
1428 Protocol::Timeout {
1429 body,
1430 on_timeout,
1431 on_cancel,
1432 ..
1433 } => find_session_projection_blocker(body)
1434 .or_else(|| find_session_projection_blocker(on_timeout))
1435 .or_else(|| {
1436 on_cancel
1437 .as_deref()
1438 .and_then(find_session_projection_blocker)
1439 }),
1440 Protocol::Choice { branches, .. } => branches
1441 .iter()
1442 .find_map(|branch| find_session_projection_blocker(&branch.protocol)),
1443 Protocol::Loop { body, .. } | Protocol::Rec { body, .. } => {
1444 find_session_projection_blocker(body)
1445 }
1446 Protocol::Parallel { protocols } => {
1447 protocols.iter().find_map(find_session_projection_blocker)
1448 }
1449 Protocol::Var(_) | Protocol::End => None,
1450 }
1451}
1452
1453fn authority_metatheory_tier(protocol: &Protocol) -> AuthorityMetatheoryTier {
1454 match protocol {
1455 Protocol::Begin { .. }
1456 | Protocol::Await { .. }
1457 | Protocol::Resolve { .. }
1458 | Protocol::Invalidate { .. }
1459 | Protocol::Case { .. }
1460 | Protocol::Timeout { .. }
1461 | Protocol::Handoff { .. }
1462 | Protocol::DependentWork { .. }
1463 | Protocol::Parallel { .. }
1464 | Protocol::Extension { .. } => AuthorityMetatheoryTier::RuntimeSemanticOnly,
1465 Protocol::Publish { continuation, .. }
1466 | Protocol::PublishAuthority { continuation, .. }
1467 | Protocol::Materialize { continuation, .. } => authority_metatheory_tier(continuation)
1468 .max(AuthorityMetatheoryTier::EvidencePublicationSemanticObjects),
1469 Protocol::Let {
1470 expr, continuation, ..
1471 } => {
1472 let expr_tier = match expr {
1473 super::AuthorityExpr::Check { .. } | super::AuthorityExpr::Observe { .. } => {
1474 AuthorityMetatheoryTier::EvidencePublicationSemanticObjects
1475 }
1476 super::AuthorityExpr::Transfer { .. } => {
1477 AuthorityMetatheoryTier::RuntimeSemanticOnly
1478 }
1479 super::AuthorityExpr::Var(_)
1480 | super::AuthorityExpr::Constructor { .. }
1481 | super::AuthorityExpr::Call { .. } => {
1482 AuthorityMetatheoryTier::SessionTypedCoordination
1483 }
1484 };
1485 expr_tier.max(authority_metatheory_tier(continuation))
1486 }
1487 Protocol::Choice { branches, .. } => branches
1488 .iter()
1489 .map(|branch| {
1490 let guard_tier = match branch.guard.as_ref() {
1491 Some(ChoiceGuard::Evidence { .. }) => {
1492 AuthorityMetatheoryTier::EvidencePublicationSemanticObjects
1493 }
1494 Some(ChoiceGuard::Predicate(_)) | None => {
1495 AuthorityMetatheoryTier::SessionTypedCoordination
1496 }
1497 };
1498 guard_tier.max(authority_metatheory_tier(&branch.protocol))
1499 })
1500 .max()
1501 .unwrap_or(AuthorityMetatheoryTier::SessionTypedCoordination),
1502 Protocol::Send { continuation, .. } | Protocol::Broadcast { continuation, .. } => {
1503 authority_metatheory_tier(continuation)
1504 }
1505 Protocol::Loop { body, .. } | Protocol::Rec { body, .. } => authority_metatheory_tier(body),
1506 Protocol::Var(_) | Protocol::End => AuthorityMetatheoryTier::SessionTypedCoordination,
1507 }
1508}
1509
1510fn authority_metatheory_blocker(protocol: &Protocol) -> Option<&'static str> {
1511 match protocol {
1512 Protocol::Begin { .. }
1513 | Protocol::Await { .. }
1514 | Protocol::Resolve { .. }
1515 | Protocol::Invalidate { .. } => {
1516 Some("explicit commitment lifecycle still relies on protocol-machine semantic obligations")
1517 }
1518 Protocol::Case { .. } => Some("authority-local case/of still relies on runtime semantic obligations"),
1519 Protocol::Timeout { .. } => Some("timeout/cancellation still relies on runtime progress semantics"),
1520 Protocol::Handoff { .. } => Some("semantic handoff is still justified through protocol-machine conservation rather than the small authority theorem slice"),
1521 Protocol::DependentWork { .. } => Some("dependent work remains a protocol-machine semantic obligation"),
1522 Protocol::Parallel { .. } => Some("parallel authority composition remains outside the supported authority theorem slice"),
1523 Protocol::Extension { .. } => Some("extension dispatch remains outside the authority theorem slice"),
1524 Protocol::Let { expr, continuation, .. } => match expr {
1525 super::AuthorityExpr::Transfer { .. } => {
1526 Some(
1527 "transfer receipts are first-class transition artifacts but still rely on the wider runtime authority lifecycle",
1528 )
1529 }
1530 _ => authority_metatheory_blocker(continuation),
1531 },
1532 Protocol::Choice { branches, .. } => branches
1533 .iter()
1534 .find_map(|branch| authority_metatheory_blocker(&branch.protocol)),
1535 Protocol::Send { continuation, .. }
1536 | Protocol::Broadcast { continuation, .. }
1537 | Protocol::Publish { continuation, .. }
1538 | Protocol::PublishAuthority { continuation, .. }
1539 | Protocol::Materialize { continuation, .. } => authority_metatheory_blocker(continuation),
1540 Protocol::Loop { body, .. } | Protocol::Rec { body, .. } => authority_metatheory_blocker(body),
1541 Protocol::Var(_) | Protocol::End => None,
1542 }
1543}
1544
1545#[cfg(test)]
1546mod tests {
1547 use super::*;
1548 use quote::format_ident;
1549
1550 #[test]
1551 fn proof_bundle_metadata_roundtrip() {
1552 let mut choreo = Choreography {
1553 name: format_ident!("RoundTrip"),
1554 namespace: None,
1555 roles: Vec::new(),
1556 protocol: Protocol::End,
1557 attrs: HashMap::new(),
1558 };
1559 let bundles = vec![
1560 TheoremPackDeclaration {
1561 name: "Base".to_string(),
1562 capabilities: vec!["delegation".to_string()],
1563 version: None,
1564 issuer: None,
1565 constraints: Vec::new(),
1566 },
1567 TheoremPackDeclaration {
1568 name: "Guard".to_string(),
1569 capabilities: vec!["guard_tokens".to_string()],
1570 version: None,
1571 issuer: None,
1572 constraints: Vec::new(),
1573 },
1574 ];
1575 let required = vec!["Base".to_string()];
1576
1577 choreo
1578 .set_theorem_packs(&bundles)
1579 .expect("set proof bundles");
1580 choreo
1581 .set_required_theorem_packs(&required)
1582 .expect("set required bundles");
1583
1584 assert_eq!(choreo.theorem_packs(), bundles);
1585 assert_eq!(choreo.required_theorem_packs(), required);
1586 }
1587}