Skip to main content

telltale_language/ast/
protocol.rs

1// Protocol AST definitions
2
3use super::annotation::Annotations;
4use super::{MessageType, NonEmptyVec, ProgressAttachment, Role, ValidationError};
5use proc_macro2::{Ident, TokenStream};
6
7#[path = "protocol_validation.rs"]
8mod validation;
9use validation::{ensure_declared_role, validate_choice_branches};
10
11fn annotation_has_key(annotation: &super::annotation::ProtocolAnnotation, key: &str) -> bool {
12    annotation
13        .dsl_entries()
14        .iter()
15        .any(|(entry_key, _)| entry_key == key)
16}
17
18fn annotation_has_value(
19    annotation: &super::annotation::ProtocolAnnotation,
20    key: &str,
21    value: &str,
22) -> bool {
23    annotation
24        .dsl_entries()
25        .iter()
26        .any(|(entry_key, entry_value)| entry_key == key && entry_value.eq_ignore_ascii_case(value))
27}
28
29/// Protocol specification using choreographic constructs
30#[derive(Debug)]
31pub enum Protocol {
32    /// Begin one explicit semantic operation instance.
33    Begin {
34        operation: String,
35        args: Vec<String>,
36        progress: Option<ProgressAttachment>,
37        continuation: Box<Protocol>,
38    },
39
40    /// Await one previously begun semantic operation instance.
41    Await {
42        operation: String,
43        continuation: Box<Protocol>,
44    },
45
46    /// Resolve one previously begun semantic operation instance.
47    Resolve {
48        operation: String,
49        outcome: CommitmentOutcome,
50        continuation: Box<Protocol>,
51    },
52
53    /// Invalidate one previously begun semantic operation instance.
54    Invalidate {
55        operation: String,
56        continuation: Box<Protocol>,
57    },
58
59    /// Message send: A -> B: Message
60    Send {
61        from: Role,
62        to: Role,
63        message: MessageType,
64        continuation: Box<Protocol>,
65        /// Statement-level annotations
66        annotations: Annotations,
67        /// From role annotations
68        from_annotations: Annotations,
69        /// To role annotations
70        to_annotations: Annotations,
71    },
72
73    /// Broadcast: A -> *: Message
74    Broadcast {
75        from: Role,
76        to_all: NonEmptyVec<Role>,
77        message: MessageType,
78        continuation: Box<Protocol>,
79        /// Statement-level annotations
80        annotations: Annotations,
81        /// From role annotations
82        from_annotations: Annotations,
83    },
84
85    /// Choice made by a role
86    Choice {
87        role: Role,
88        branches: NonEmptyVec<Branch>,
89        /// Statement-level annotations
90        annotations: Annotations,
91    },
92
93    /// Local authority/evidence binding.
94    Let {
95        /// Bound variable name.
96        name: String,
97        /// Whether the binding is authoritative, observational, or plain.
98        mode: AuthorityBindingMode,
99        /// Bound expression.
100        expr: AuthorityExpr,
101        /// Whether the binding is linear/single-use.
102        linear: bool,
103        /// Continuation after the binding.
104        continuation: Box<Protocol>,
105    },
106
107    /// Local authority/result match.
108    Case {
109        /// Scrutinee expression.
110        expr: AuthorityExpr,
111        /// Match branches.
112        branches: NonEmptyVec<CaseBranch>,
113    },
114
115    /// Explicit timeout/cancel surface syntax prior to projection lowering.
116    Timeout {
117        /// Role that owns the timeout decision.
118        role: Role,
119        /// Timeout duration in milliseconds.
120        duration_ms: u64,
121        /// Main body before timeout fires.
122        body: Box<Protocol>,
123        /// Timeout branch.
124        on_timeout: Box<Protocol>,
125        /// Optional explicit cancellation branch.
126        on_cancel: Option<Box<Protocol>>,
127    },
128
129    /// Loop construct
130    Loop {
131        condition: Option<Condition>,
132        body: Box<Protocol>,
133    },
134
135    /// Parallel composition
136    Parallel { protocols: NonEmptyVec<Protocol> },
137
138    /// Recursive protocol with label
139    Rec { label: Ident, body: Box<Protocol> },
140
141    /// Reference to recursive label
142    Var(Ident),
143
144    /// Canonical semantic publication surface.
145    Publish {
146        event: String,
147        arg: Option<String>,
148        continuation: Box<Protocol>,
149    },
150
151    /// Canonical publication that lifts an authoritative witness into a named publication.
152    PublishAuthority {
153        witness: String,
154        publication_name: String,
155        continuation: Box<Protocol>,
156    },
157
158    /// Canonical materialization from one named publication.
159    Materialize {
160        proof: String,
161        publication: String,
162        continuation: Box<Protocol>,
163    },
164
165    /// Explicit semantic owner handoff.
166    Handoff {
167        operation: String,
168        target: Role,
169        receipt: String,
170        continuation: Box<Protocol>,
171    },
172
173    /// Declared semantically required dependent work.
174    DependentWork {
175        name: String,
176        arg: Option<String>,
177        required_for: String,
178        continuation: Box<Protocol>,
179    },
180
181    /// Protocol extension point for custom behaviors
182    Extension {
183        /// The extension implementation
184        extension: Box<dyn crate::extensions::ProtocolExtension>,
185        /// Continuation after this extension
186        continuation: Box<Protocol>,
187        /// Statement-level annotations
188        annotations: Annotations,
189    },
190
191    /// Protocol termination
192    End,
193}
194
195/// A branch in a choice
196#[derive(Debug)]
197pub struct Branch {
198    pub label: Ident,
199    pub guard: Option<ChoiceGuard>,
200    pub protocol: Protocol,
201}
202
203/// Match branch in a `case/of` expression.
204#[derive(Debug)]
205pub struct CaseBranch {
206    pub pattern: CasePattern,
207    pub protocol: Protocol,
208}
209
210/// Pattern accepted by DSL `case/of`.
211#[derive(Debug, Clone, PartialEq, Eq)]
212pub struct CasePattern {
213    pub constructor: String,
214    pub binders: Vec<String>,
215}
216
217/// Explicit authority/observation mode for one local binding.
218#[derive(Debug, Clone, Copy, PartialEq, Eq)]
219pub enum AuthorityBindingMode {
220    Plain,
221    Authoritative,
222    Observe,
223}
224
225/// Authority- or evidence-oriented expression surface syntax.
226#[derive(Debug, Clone, PartialEq, Eq)]
227pub enum AuthorityExpr {
228    Var(String),
229    Check {
230        effect: String,
231        operation: String,
232        args: Vec<String>,
233    },
234    Observe {
235        effect: String,
236        operation: String,
237        args: Vec<String>,
238    },
239    Transfer {
240        subject: String,
241        from: String,
242        to: String,
243    },
244    Constructor {
245        name: String,
246        arg: Option<String>,
247    },
248    Call {
249        name: String,
250        args: Vec<String>,
251    },
252}
253
254/// Explicit outcome used to resolve a begun semantic operation.
255#[derive(Debug, Clone, PartialEq, Eq)]
256pub enum CommitmentOutcome {
257    Success(Option<String>),
258    Failure(Option<String>),
259    Timeout(Option<String>),
260    Cancelled,
261}
262
263/// Guard surface syntax attached to one choice branch.
264#[derive(Debug, Clone)]
265pub enum ChoiceGuard {
266    Predicate(TokenStream),
267    Evidence {
268        effect: String,
269        operation: String,
270        args: Vec<String>,
271        binding: String,
272    },
273}
274
275/// Loop condition
276#[derive(Debug, Clone)]
277pub enum Condition {
278    /// Loop while a role decides
279    RoleDecides(Role),
280    /// Fixed iteration count
281    Count(usize),
282    /// Custom condition
283    Custom(TokenStream),
284    /// Fuel-based bounding: maximum number of loop iterations
285    Fuel(usize),
286    /// Yield control after N communication steps
287    YieldAfter(usize),
288    /// Yield when a specific label/condition is encountered
289    YieldWhen(String),
290}
291
292impl Protocol {
293    #[must_use]
294    pub fn mentions_role(&self, role: &Role) -> bool {
295        match self {
296            Protocol::Begin { continuation, .. }
297            | Protocol::Await { continuation, .. }
298            | Protocol::Resolve { continuation, .. }
299            | Protocol::Invalidate { continuation, .. } => continuation.mentions_role(role),
300            Protocol::Send {
301                from,
302                to,
303                continuation,
304                ..
305            } => {
306                from.matches_family(role)
307                    || to.matches_family(role)
308                    || continuation.mentions_role(role)
309            }
310            Protocol::Broadcast {
311                from,
312                to_all,
313                continuation,
314                ..
315            } => {
316                from.matches_family(role)
317                    || to_all.iter().any(|r| r.matches_family(role))
318                    || continuation.mentions_role(role)
319            }
320            Protocol::Choice {
321                role: r, branches, ..
322            } => r.matches_family(role) || branches.iter().any(|b| b.protocol.mentions_role(role)),
323            Protocol::Let { continuation, .. } => continuation.mentions_role(role),
324            Protocol::Case { branches, .. } => {
325                branches.iter().any(|b| b.protocol.mentions_role(role))
326            }
327            Protocol::Timeout {
328                role: timeout_role,
329                body,
330                on_timeout,
331                on_cancel,
332                ..
333            } => {
334                timeout_role.matches_family(role)
335                    || body.mentions_role(role)
336                    || on_timeout.mentions_role(role)
337                    || on_cancel
338                        .as_deref()
339                        .is_some_and(|branch| branch.mentions_role(role))
340            }
341            Protocol::Loop { body, .. } => body.mentions_role(role),
342            Protocol::Parallel { protocols } => protocols.iter().any(|p| p.mentions_role(role)),
343            Protocol::Rec { body, .. } => body.mentions_role(role),
344            Protocol::Publish { continuation, .. }
345            | Protocol::PublishAuthority { continuation, .. }
346            | Protocol::Materialize { continuation, .. }
347            | Protocol::DependentWork { continuation, .. } => continuation.mentions_role(role),
348            Protocol::Handoff {
349                target,
350                continuation,
351                ..
352            } => target.matches_family(role) || continuation.mentions_role(role),
353            Protocol::Extension {
354                extension,
355                continuation,
356                ..
357            } => extension.mentions_role(role) || continuation.mentions_role(role),
358            Protocol::Var(_) | Protocol::End => false,
359        }
360    }
361
362    pub(crate) fn validate(&self, roles: &[Role]) -> Result<(), ValidationError> {
363        match self {
364            Protocol::Begin { continuation, .. }
365            | Protocol::Await { continuation, .. }
366            | Protocol::Resolve { continuation, .. }
367            | Protocol::Invalidate { continuation, .. } => continuation.validate(roles),
368            Protocol::Send {
369                from,
370                to,
371                continuation,
372                ..
373            } => {
374                ensure_declared_role(roles, from)?;
375                ensure_declared_role(roles, to)?;
376                continuation.validate(roles)
377            }
378            Protocol::Broadcast {
379                from,
380                to_all,
381                continuation,
382                ..
383            } => {
384                ensure_declared_role(roles, from)?;
385                for to in to_all {
386                    ensure_declared_role(roles, to)?;
387                }
388                continuation.validate(roles)
389            }
390            Protocol::Choice { role, branches, .. } => {
391                ensure_declared_role(roles, role)?;
392                validate_choice_branches(role, branches)?;
393                Ok(())
394            }
395            Protocol::Let { continuation, .. } => continuation.validate(roles),
396            Protocol::Case { branches, .. } => {
397                for branch in branches {
398                    branch.protocol.validate(roles)?;
399                }
400                Ok(())
401            }
402            Protocol::Timeout {
403                role,
404                body,
405                on_timeout,
406                on_cancel,
407                ..
408            } => {
409                ensure_declared_role(roles, role)?;
410                body.validate(roles)?;
411                on_timeout.validate(roles)?;
412                if let Some(on_cancel) = on_cancel.as_deref() {
413                    on_cancel.validate(roles)?;
414                }
415                Ok(())
416            }
417            Protocol::Loop { body, .. } => body.validate(roles),
418            Protocol::Parallel { protocols } => {
419                for p in protocols {
420                    p.validate(roles)?;
421                }
422                Ok(())
423            }
424            Protocol::Rec { body, .. } => body.validate(roles),
425            Protocol::Publish { continuation, .. }
426            | Protocol::PublishAuthority { continuation, .. }
427            | Protocol::Materialize { continuation, .. }
428            | Protocol::DependentWork { continuation, .. } => continuation.validate(roles),
429            Protocol::Handoff {
430                target,
431                continuation,
432                ..
433            } => {
434                ensure_declared_role(roles, target)?;
435                continuation.validate(roles)
436            }
437            Protocol::Extension {
438                extension,
439                continuation,
440                ..
441            } => {
442                // Validate the extension with the extension system's validation
443                extension.validate(roles).map_err(|e| {
444                    ValidationError::ExtensionError(format!("Extension validation failed: {}", e))
445                })?;
446                continuation.validate(roles)
447            }
448            Protocol::Var(_) | Protocol::End => Ok(()),
449        }
450    }
451
452    /// Get statement-level annotations for this protocol node
453    pub fn get_annotations(&self) -> &Annotations {
454        match self {
455            Protocol::Send { annotations, .. } => annotations,
456            Protocol::Broadcast { annotations, .. } => annotations,
457            Protocol::Choice { annotations, .. } => annotations,
458            Protocol::Begin { .. }
459            | Protocol::Await { .. }
460            | Protocol::Resolve { .. }
461            | Protocol::Invalidate { .. }
462            | Protocol::Let { .. }
463            | Protocol::Case { .. }
464            | Protocol::Timeout { .. }
465            | Protocol::Publish { .. }
466            | Protocol::PublishAuthority { .. }
467            | Protocol::Materialize { .. }
468            | Protocol::Handoff { .. }
469            | Protocol::DependentWork { .. } => {
470                static EMPTY: std::sync::OnceLock<Annotations> = std::sync::OnceLock::new();
471                EMPTY.get_or_init(Annotations::new)
472            }
473            Protocol::Extension { annotations, .. } => annotations,
474            Protocol::Loop { .. }
475            | Protocol::Parallel { .. }
476            | Protocol::Rec { .. }
477            | Protocol::Var(_)
478            | Protocol::End => {
479                // Return empty annotations for protocol nodes that don't have annotations yet
480                static EMPTY: std::sync::OnceLock<Annotations> = std::sync::OnceLock::new();
481                EMPTY.get_or_init(Annotations::new)
482            }
483        }
484    }
485
486    /// Get from-role annotations for Send/Broadcast statements
487    pub fn get_from_annotations(&self) -> Option<&Annotations> {
488        match self {
489            Protocol::Send {
490                from_annotations, ..
491            } => Some(from_annotations),
492            Protocol::Broadcast {
493                from_annotations, ..
494            } => Some(from_annotations),
495            _ => None,
496        }
497    }
498
499    /// Get to-role annotations for Send statements
500    pub fn get_to_annotations(&self) -> Option<&Annotations> {
501        match self {
502            Protocol::Send { to_annotations, .. } => Some(to_annotations),
503            _ => None,
504        }
505    }
506
507    /// Get mutable reference to annotations for modification
508    pub fn get_annotations_mut(&mut self) -> Option<&mut Annotations> {
509        match self {
510            Protocol::Send { annotations, .. } => Some(annotations),
511            Protocol::Broadcast { annotations, .. } => Some(annotations),
512            Protocol::Choice { annotations, .. } => Some(annotations),
513            Protocol::Begin { .. }
514            | Protocol::Await { .. }
515            | Protocol::Resolve { .. }
516            | Protocol::Invalidate { .. }
517            | Protocol::Let { .. }
518            | Protocol::Case { .. }
519            | Protocol::Timeout { .. }
520            | Protocol::Publish { .. }
521            | Protocol::PublishAuthority { .. }
522            | Protocol::Materialize { .. }
523            | Protocol::Handoff { .. }
524            | Protocol::DependentWork { .. } => None,
525            Protocol::Extension { annotations, .. } => Some(annotations),
526            Protocol::Loop { .. }
527            | Protocol::Parallel { .. }
528            | Protocol::Rec { .. }
529            | Protocol::Var(_)
530            | Protocol::End => None,
531        }
532    }
533
534    /// Get mutable reference to from-role annotations
535    pub fn get_from_annotations_mut(&mut self) -> Option<&mut Annotations> {
536        match self {
537            Protocol::Send {
538                from_annotations, ..
539            } => Some(from_annotations),
540            Protocol::Broadcast {
541                from_annotations, ..
542            } => Some(from_annotations),
543            _ => None,
544        }
545    }
546
547    /// Get mutable reference to to-role annotations
548    pub fn get_to_annotations_mut(&mut self) -> Option<&mut Annotations> {
549        match self {
550            Protocol::Send { to_annotations, .. } => Some(to_annotations),
551            _ => None,
552        }
553    }
554
555    /// Add a typed annotation
556    pub fn add_annotation(&mut self, annotation: super::annotation::ProtocolAnnotation) -> bool {
557        if let Some(annotations) = self.get_annotations_mut() {
558            annotations.push(annotation);
559            true
560        } else {
561            false
562        }
563    }
564
565    /// Clear all annotations on this protocol node
566    pub fn clear_annotations(&mut self) {
567        if let Some(annotations) = self.get_annotations_mut() {
568            annotations.clear();
569        }
570        if let Some(from_annotations) = self.get_from_annotations_mut() {
571            from_annotations.clear();
572        }
573        if let Some(to_annotations) = self.get_to_annotations_mut() {
574            to_annotations.clear();
575        }
576    }
577
578    /// Check if any annotations are present
579    pub fn has_any_annotations(&self) -> bool {
580        !self.get_annotations().is_empty()
581            || self.get_from_annotations().is_some_and(|a| !a.is_empty())
582            || self.get_to_annotations().is_some_and(|a| !a.is_empty())
583    }
584
585    /// Count total number of annotations (statement + role annotations)
586    pub fn annotation_count(&self) -> usize {
587        self.get_annotations().len()
588            + self.get_from_annotations().map_or(0, |a| a.len())
589            + self.get_to_annotations().map_or(0, |a| a.len())
590    }
591
592    /// Merge annotations from another protocol node
593    pub fn merge_annotations_from(&mut self, other: &Protocol) {
594        // Merge statement annotations
595        if let Some(self_annotations) = self.get_annotations_mut() {
596            self_annotations.merge(other.get_annotations());
597        }
598
599        // Merge from-role annotations
600        if let (Some(self_from), Some(other_from)) = (
601            self.get_from_annotations_mut(),
602            other.get_from_annotations(),
603        ) {
604            self_from.merge(other_from);
605        }
606
607        // Merge to-role annotations
608        if let (Some(self_to), Some(other_to)) =
609            (self.get_to_annotations_mut(), other.get_to_annotations())
610        {
611            self_to.merge(other_to);
612        }
613    }
614
615    /// Validate that required annotations are present
616    pub fn validate_required_annotations(&self, required_keys: &[&str]) -> Result<(), Vec<String>> {
617        let missing: Vec<String> = required_keys
618            .iter()
619            .filter(|&key| {
620                !self
621                    .get_annotations()
622                    .iter()
623                    .any(|annotation| annotation_has_key(annotation, key))
624            })
625            .map(|&key| key.to_string())
626            .collect();
627
628        if missing.is_empty() {
629            Ok(())
630        } else {
631            Err(missing)
632        }
633    }
634
635    /// Collect all protocol nodes that have a specific annotation (recursive traversal)
636    pub fn collect_nodes_with_annotation<'a>(&'a self, key: &str, nodes: &mut Vec<&'a Protocol>) {
637        if self
638            .get_annotations()
639            .iter()
640            .any(|annotation| annotation_has_key(annotation, key))
641        {
642            nodes.push(self);
643        }
644
645        // Recursively traverse protocol structure
646        match self {
647            Protocol::Begin { continuation, .. }
648            | Protocol::Await { continuation, .. }
649            | Protocol::Resolve { continuation, .. }
650            | Protocol::Invalidate { continuation, .. } => {
651                continuation.collect_nodes_with_annotation(key, nodes);
652            }
653            Protocol::Send { continuation, .. } => {
654                continuation.collect_nodes_with_annotation(key, nodes);
655            }
656            Protocol::Broadcast { continuation, .. } => {
657                continuation.collect_nodes_with_annotation(key, nodes);
658            }
659            Protocol::Let { continuation, .. } => {
660                continuation.collect_nodes_with_annotation(key, nodes);
661            }
662            Protocol::Choice { branches, .. } => {
663                for branch in branches {
664                    branch.protocol.collect_nodes_with_annotation(key, nodes);
665                }
666            }
667            Protocol::Case { branches, .. } => {
668                for branch in branches {
669                    branch.protocol.collect_nodes_with_annotation(key, nodes);
670                }
671            }
672            Protocol::Timeout {
673                body,
674                on_timeout,
675                on_cancel,
676                ..
677            } => {
678                body.collect_nodes_with_annotation(key, nodes);
679                on_timeout.collect_nodes_with_annotation(key, nodes);
680                if let Some(on_cancel) = on_cancel.as_deref() {
681                    on_cancel.collect_nodes_with_annotation(key, nodes);
682                }
683            }
684            Protocol::Loop { body, .. } => {
685                body.collect_nodes_with_annotation(key, nodes);
686            }
687            Protocol::Parallel { protocols } => {
688                for protocol in protocols {
689                    protocol.collect_nodes_with_annotation(key, nodes);
690                }
691            }
692            Protocol::Rec { body, .. } => {
693                body.collect_nodes_with_annotation(key, nodes);
694            }
695            Protocol::Publish { continuation, .. }
696            | Protocol::PublishAuthority { continuation, .. }
697            | Protocol::Materialize { continuation, .. }
698            | Protocol::Handoff { continuation, .. }
699            | Protocol::DependentWork { continuation, .. } => {
700                continuation.collect_nodes_with_annotation(key, nodes);
701            }
702            Protocol::Extension { continuation, .. } => {
703                continuation.collect_nodes_with_annotation(key, nodes);
704            }
705            Protocol::Var(_) | Protocol::End => {
706                // Terminal nodes - no further traversal needed
707            }
708        }
709    }
710
711    /// Collect all protocol nodes that have a specific annotation with a specific value
712    pub fn collect_nodes_with_annotation_value<'a>(
713        &'a self,
714        key: &str,
715        value: &str,
716        nodes: &mut Vec<&'a Protocol>,
717    ) {
718        if self
719            .get_annotations()
720            .iter()
721            .any(|annotation| annotation_has_value(annotation, key, value))
722        {
723            nodes.push(self);
724        }
725
726        // Recursively traverse protocol structure
727        match self {
728            Protocol::Begin { continuation, .. }
729            | Protocol::Await { continuation, .. }
730            | Protocol::Resolve { continuation, .. }
731            | Protocol::Invalidate { continuation, .. } => {
732                continuation.collect_nodes_with_annotation_value(key, value, nodes);
733            }
734            Protocol::Send { continuation, .. } => {
735                continuation.collect_nodes_with_annotation_value(key, value, nodes);
736            }
737            Protocol::Broadcast { continuation, .. } => {
738                continuation.collect_nodes_with_annotation_value(key, value, nodes);
739            }
740            Protocol::Let { continuation, .. } => {
741                continuation.collect_nodes_with_annotation_value(key, value, nodes);
742            }
743            Protocol::Choice { branches, .. } => {
744                for branch in branches {
745                    branch
746                        .protocol
747                        .collect_nodes_with_annotation_value(key, value, nodes);
748                }
749            }
750            Protocol::Case { branches, .. } => {
751                for branch in branches {
752                    branch
753                        .protocol
754                        .collect_nodes_with_annotation_value(key, value, nodes);
755                }
756            }
757            Protocol::Timeout {
758                body,
759                on_timeout,
760                on_cancel,
761                ..
762            } => {
763                body.collect_nodes_with_annotation_value(key, value, nodes);
764                on_timeout.collect_nodes_with_annotation_value(key, value, nodes);
765                if let Some(on_cancel) = on_cancel.as_deref() {
766                    on_cancel.collect_nodes_with_annotation_value(key, value, nodes);
767                }
768            }
769            Protocol::Loop { body, .. } => {
770                body.collect_nodes_with_annotation_value(key, value, nodes);
771            }
772            Protocol::Parallel { protocols } => {
773                for protocol in protocols {
774                    protocol.collect_nodes_with_annotation_value(key, value, nodes);
775                }
776            }
777            Protocol::Rec { body, .. } => {
778                body.collect_nodes_with_annotation_value(key, value, nodes);
779            }
780            Protocol::Publish { continuation, .. }
781            | Protocol::PublishAuthority { continuation, .. }
782            | Protocol::Materialize { continuation, .. }
783            | Protocol::Handoff { continuation, .. }
784            | Protocol::DependentWork { continuation, .. } => {
785                continuation.collect_nodes_with_annotation_value(key, value, nodes);
786            }
787            Protocol::Extension { continuation, .. } => {
788                continuation.collect_nodes_with_annotation_value(key, value, nodes);
789            }
790            Protocol::Var(_) | Protocol::End => {
791                // Terminal nodes - no further traversal needed
792            }
793        }
794    }
795
796    /// Count total annotations throughout the protocol tree (recursive)
797    pub fn deep_annotation_count(&self) -> usize {
798        let mut count = self.annotation_count();
799
800        match self {
801            Protocol::Begin { continuation, .. }
802            | Protocol::Await { continuation, .. }
803            | Protocol::Resolve { continuation, .. }
804            | Protocol::Invalidate { continuation, .. } => {
805                count += continuation.deep_annotation_count();
806            }
807            Protocol::Send { continuation, .. } => {
808                count += continuation.deep_annotation_count();
809            }
810            Protocol::Broadcast { continuation, .. } => {
811                count += continuation.deep_annotation_count();
812            }
813            Protocol::Let { continuation, .. } => {
814                count += continuation.deep_annotation_count();
815            }
816            Protocol::Choice { branches, .. } => {
817                for branch in branches {
818                    count += branch.protocol.deep_annotation_count();
819                }
820            }
821            Protocol::Case { branches, .. } => {
822                for branch in branches {
823                    count += branch.protocol.deep_annotation_count();
824                }
825            }
826            Protocol::Timeout {
827                body,
828                on_timeout,
829                on_cancel,
830                ..
831            } => {
832                count += body.deep_annotation_count();
833                count += on_timeout.deep_annotation_count();
834                if let Some(on_cancel) = on_cancel.as_deref() {
835                    count += on_cancel.deep_annotation_count();
836                }
837            }
838            Protocol::Loop { body, .. } => {
839                count += body.deep_annotation_count();
840            }
841            Protocol::Parallel { protocols } => {
842                for protocol in protocols {
843                    count += protocol.deep_annotation_count();
844                }
845            }
846            Protocol::Rec { body, .. } => {
847                count += body.deep_annotation_count();
848            }
849            Protocol::Publish { continuation, .. }
850            | Protocol::PublishAuthority { continuation, .. }
851            | Protocol::Materialize { continuation, .. }
852            | Protocol::Handoff { continuation, .. }
853            | Protocol::DependentWork { continuation, .. } => {
854                count += continuation.deep_annotation_count();
855            }
856            Protocol::Extension { continuation, .. } => {
857                count += continuation.deep_annotation_count();
858            }
859            Protocol::Var(_) | Protocol::End => {
860                // Terminal nodes - no additional annotations
861            }
862        }
863
864        count
865    }
866
867    /// Apply a function to all protocol nodes that have annotations (visitor pattern)
868    pub fn visit_annotated_nodes<F>(&self, f: &mut F)
869    where
870        F: FnMut(&Protocol),
871    {
872        if self.has_any_annotations() {
873            f(self);
874        }
875
876        match self {
877            Protocol::Begin { continuation, .. }
878            | Protocol::Await { continuation, .. }
879            | Protocol::Resolve { continuation, .. }
880            | Protocol::Invalidate { continuation, .. } => {
881                continuation.visit_annotated_nodes(f);
882            }
883            Protocol::Send { continuation, .. } => {
884                continuation.visit_annotated_nodes(f);
885            }
886            Protocol::Broadcast { continuation, .. } => {
887                continuation.visit_annotated_nodes(f);
888            }
889            Protocol::Let { continuation, .. } => {
890                continuation.visit_annotated_nodes(f);
891            }
892            Protocol::Choice { branches, .. } => {
893                for branch in branches {
894                    branch.protocol.visit_annotated_nodes(f);
895                }
896            }
897            Protocol::Case { branches, .. } => {
898                for branch in branches {
899                    branch.protocol.visit_annotated_nodes(f);
900                }
901            }
902            Protocol::Timeout {
903                body,
904                on_timeout,
905                on_cancel,
906                ..
907            } => {
908                body.visit_annotated_nodes(f);
909                on_timeout.visit_annotated_nodes(f);
910                if let Some(on_cancel) = on_cancel.as_deref() {
911                    on_cancel.visit_annotated_nodes(f);
912                }
913            }
914            Protocol::Loop { body, .. } => {
915                body.visit_annotated_nodes(f);
916            }
917            Protocol::Parallel { protocols } => {
918                for protocol in protocols {
919                    protocol.visit_annotated_nodes(f);
920                }
921            }
922            Protocol::Rec { body, .. } => {
923                body.visit_annotated_nodes(f);
924            }
925            Protocol::Publish { continuation, .. }
926            | Protocol::PublishAuthority { continuation, .. }
927            | Protocol::Materialize { continuation, .. }
928            | Protocol::Handoff { continuation, .. }
929            | Protocol::DependentWork { continuation, .. } => {
930                continuation.visit_annotated_nodes(f);
931            }
932            Protocol::Extension { continuation, .. } => {
933                continuation.visit_annotated_nodes(f);
934            }
935            Protocol::Var(_) | Protocol::End => {
936                // Terminal nodes
937            }
938        }
939    }
940
941    /// Apply a mutable function to all protocol nodes that have annotations
942    pub fn visit_annotated_nodes_mut<F>(&mut self, f: &mut F)
943    where
944        F: FnMut(&mut Protocol),
945    {
946        if self.has_any_annotations() {
947            f(self);
948        }
949
950        match self {
951            Protocol::Begin { continuation, .. }
952            | Protocol::Await { continuation, .. }
953            | Protocol::Resolve { continuation, .. }
954            | Protocol::Invalidate { continuation, .. } => {
955                continuation.visit_annotated_nodes_mut(f);
956            }
957            Protocol::Send { continuation, .. } => {
958                continuation.visit_annotated_nodes_mut(f);
959            }
960            Protocol::Broadcast { continuation, .. } => {
961                continuation.visit_annotated_nodes_mut(f);
962            }
963            Protocol::Let { continuation, .. } => {
964                continuation.visit_annotated_nodes_mut(f);
965            }
966            Protocol::Choice { branches, .. } => {
967                for branch in branches {
968                    branch.protocol.visit_annotated_nodes_mut(f);
969                }
970            }
971            Protocol::Case { branches, .. } => {
972                for branch in branches {
973                    branch.protocol.visit_annotated_nodes_mut(f);
974                }
975            }
976            Protocol::Timeout {
977                body,
978                on_timeout,
979                on_cancel,
980                ..
981            } => {
982                body.visit_annotated_nodes_mut(f);
983                on_timeout.visit_annotated_nodes_mut(f);
984                if let Some(on_cancel) = on_cancel.as_deref_mut() {
985                    on_cancel.visit_annotated_nodes_mut(f);
986                }
987            }
988            Protocol::Loop { body, .. } => {
989                body.visit_annotated_nodes_mut(f);
990            }
991            Protocol::Parallel { protocols } => {
992                for protocol in protocols {
993                    protocol.visit_annotated_nodes_mut(f);
994                }
995            }
996            Protocol::Rec { body, .. } => {
997                body.visit_annotated_nodes_mut(f);
998            }
999            Protocol::Publish { continuation, .. }
1000            | Protocol::PublishAuthority { continuation, .. }
1001            | Protocol::Materialize { continuation, .. }
1002            | Protocol::Handoff { continuation, .. }
1003            | Protocol::DependentWork { continuation, .. } => {
1004                continuation.visit_annotated_nodes_mut(f);
1005            }
1006            Protocol::Extension { continuation, .. } => {
1007                continuation.visit_annotated_nodes_mut(f);
1008            }
1009            Protocol::Var(_) | Protocol::End => {
1010                // Terminal nodes
1011            }
1012        }
1013    }
1014}