Skip to main content

canic_core/dto/
component_registry.rs

1//! Module: dto::component_registry
2//!
3//! Responsibility: carry root-local Component Registry preparation and allocation evidence.
4//! Does not own: admission policy, stable mutation, artifact resolution, or lifecycle effects.
5//! Boundary: callers name intent and Spec while the root allocates identity under verified authority.
6
7use crate::{
8    cdk::types::Cycles,
9    config::schema::ComponentChildKind,
10    dto::{
11        fleet_registry::{FleetDirectorySnapshot, FleetRegistryVersion},
12        root_store::RootStoreBootstrapRequest,
13    },
14    ids::{
15        CanisterRole, ComponentBinding, ComponentChildBinding, ComponentInstanceId,
16        ComponentSpecId, ComponentTopologyDigest, FleetSubnetRootReleaseSet,
17        ManagedCanisterBinding,
18    },
19};
20use candid::{CandidType, Principal};
21use serde::{Deserialize, Serialize};
22
23///
24/// RootComponentRegistryPreparationRequest
25///
26/// Exact authority required before an empty root-local Component Registry may be prepared.
27///
28
29#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
30pub struct RootComponentRegistryPreparationRequest {
31    pub store_bootstrap: RootStoreBootstrapRequest,
32    pub expected_fleet_registry: FleetRegistryVersion,
33}
34
35///
36/// RootComponentInitialInventoryStatus
37///
38/// Durable initial Component inventory sealed for one Fleet Subnet Root activation.
39///
40
41#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
42pub struct RootComponentInitialInventoryStatus {
43    pub fleet_activation_operation_id: [u8; 32],
44    pub component_count: u32,
45    pub inventory_hash: [u8; 32],
46    pub sealed_at_ns: u64,
47    pub directories_converged: bool,
48    pub root_runtime_activated: bool,
49}
50
51///
52/// RootComponentRegistryStatusResponse
53///
54/// Compact durable Component Registry authority and current allocation counters.
55///
56
57#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
58pub struct RootComponentRegistryStatusResponse {
59    pub fleet_subnet_root: Principal,
60    pub prepared_against_registry: FleetRegistryVersion,
61    pub release_set: FleetSubnetRootReleaseSet,
62    pub component_topology_digest: ComponentTopologyDigest,
63    pub next_allocation_sequence: u64,
64    pub reserved_component_instances: u32,
65    pub committed_component_instances: u32,
66    pub managed_descendants: u32,
67    pub known_created_component_canisters: u32,
68    pub encoded_bytes: u64,
69    pub initial_inventory: Option<RootComponentInitialInventoryStatus>,
70}
71
72///
73/// RootComponentAllocationRequest
74///
75/// Controller command naming one idempotent top-level Component reservation intent.
76///
77
78#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
79pub struct RootComponentAllocationRequest {
80    pub operation_id: [u8; 32],
81    pub component_spec: ComponentSpecId,
82}
83
84///
85/// RootComponentAllocationStatusRequest
86///
87/// Read-only lookup key for one durable top-level Component allocation operation.
88///
89
90#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
91pub struct RootComponentAllocationStatusRequest {
92    pub operation_id: [u8; 32],
93}
94
95///
96/// RootComponentChildAllocationRequest
97///
98/// Parent command naming one idempotent direct-child reservation intent.
99///
100
101#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
102pub struct RootComponentChildAllocationRequest {
103    pub operation_id: [u8; 32],
104    pub component: ComponentInstanceId,
105    pub expected_registry: ComponentRegistryHead,
106    pub child_role: CanisterRole,
107}
108
109///
110/// RootComponentChildAllocationStatusRequest
111///
112/// Parent lookup key for one durable direct-child reservation.
113///
114
115#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
116pub struct RootComponentChildAllocationStatusRequest {
117    pub operation_id: [u8; 32],
118    pub component: ComponentInstanceId,
119}
120
121///
122/// RootComponentSubtreeRemovalRequest
123///
124/// Controller command durably fencing one registered child subtree.
125///
126
127#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
128pub struct RootComponentSubtreeRemovalRequest {
129    pub operation_id: [u8; 32],
130    pub component: ComponentInstanceId,
131    pub target_canister_id: Principal,
132    pub expected_registry: ComponentRegistryHead,
133}
134
135///
136/// RootComponentSubtreeRemovalAdvanceRequest
137///
138/// Controller command advancing bounded traversal from one observed durable step.
139///
140
141#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
142pub struct RootComponentSubtreeRemovalAdvanceRequest {
143    pub operation_id: [u8; 32],
144    pub component: ComponentInstanceId,
145    pub expected_traversal_steps: u32,
146}
147
148///
149/// RootComponentSubtreeRemovalStopPreparationRequest
150///
151/// Controller command freezing the exact selected leaf and root stop authority.
152///
153
154#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
155pub struct RootComponentSubtreeRemovalStopPreparationRequest {
156    pub operation_id: [u8; 32],
157    pub component: ComponentInstanceId,
158    pub expected_traversal_steps: u32,
159    pub expected_leaf_canister_id: Principal,
160    pub expected_leaf_parent_canister_id: Principal,
161}
162
163///
164/// RootComponentSubtreeRemovalStopRequest
165///
166/// Controller command reconciling and stopping one exactly prepared leaf.
167///
168
169#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
170pub struct RootComponentSubtreeRemovalStopRequest {
171    pub operation_id: [u8; 32],
172    pub component: ComponentInstanceId,
173    pub expected_traversal_steps: u32,
174    pub expected_leaf_canister_id: Principal,
175    pub expected_leaf_parent_canister_id: Principal,
176}
177
178///
179/// RootComponentSubtreeRemovalDeletePreparationRequest
180///
181/// Controller command freezing exact deletion authority from one stopped receipt.
182///
183
184#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
185pub struct RootComponentSubtreeRemovalDeletePreparationRequest {
186    pub operation_id: [u8; 32],
187    pub component: ComponentInstanceId,
188    pub expected_traversal_steps: u32,
189    pub expected_leaf_canister_id: Principal,
190    pub expected_leaf_parent_canister_id: Principal,
191}
192
193///
194/// RootComponentSubtreeRemovalDeleteRequest
195///
196/// Controller command reconciling and deleting one exactly prepared leaf.
197///
198
199#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
200pub struct RootComponentSubtreeRemovalDeleteRequest {
201    pub operation_id: [u8; 32],
202    pub component: ComponentInstanceId,
203    pub expected_traversal_steps: u32,
204    pub expected_leaf_canister_id: Principal,
205    pub expected_leaf_parent_canister_id: Principal,
206}
207
208///
209/// RootComponentSubtreeRemovalMembershipRemovalRequest
210///
211/// Controller command removing one independently deleted leaf from Registry membership.
212///
213
214#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
215pub struct RootComponentSubtreeRemovalMembershipRemovalRequest {
216    pub operation_id: [u8; 32],
217    pub component: ComponentInstanceId,
218    pub expected_traversal_steps: u32,
219    pub expected_leaf_canister_id: Principal,
220    pub expected_leaf_parent_canister_id: Principal,
221}
222
223///
224/// RootComponentSubtreeRemovalDirectorySynchronizationRequest
225///
226/// Controller command converging the post-removal Directory on surviving members.
227///
228
229#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
230pub struct RootComponentSubtreeRemovalDirectorySynchronizationRequest {
231    pub operation_id: [u8; 32],
232    pub component: ComponentInstanceId,
233    pub expected_traversal_steps: u32,
234    pub expected_leaf_canister_id: Principal,
235    pub expected_leaf_parent_canister_id: Principal,
236}
237
238///
239/// RootComponentSubtreeRemovalLeafFinalizationRequest
240///
241/// Controller command archiving one completed leaf and resuming its retained parent.
242///
243
244#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
245pub struct RootComponentSubtreeRemovalLeafFinalizationRequest {
246    pub operation_id: [u8; 32],
247    pub component: ComponentInstanceId,
248    pub expected_traversal_steps: u32,
249    pub expected_leaf_canister_id: Principal,
250    pub expected_leaf_parent_canister_id: Principal,
251}
252
253///
254/// RootComponentSubtreeRemovalStatusRequest
255///
256/// Controller lookup key for one durable child-subtree removal operation.
257///
258
259#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
260pub struct RootComponentSubtreeRemovalStatusRequest {
261    pub operation_id: [u8; 32],
262    pub component: ComponentInstanceId,
263}
264
265///
266/// RootComponentChildCreationRequest
267///
268/// Parent command continuing one already reserved direct-child operation.
269///
270
271#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
272pub struct RootComponentChildCreationRequest {
273    pub operation_id: [u8; 32],
274    pub component: ComponentInstanceId,
275}
276
277///
278/// RootComponentChildInstallRequest
279///
280/// Parent command installing and verifying one already created direct-child operation.
281///
282
283#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
284pub struct RootComponentChildInstallRequest {
285    pub operation_id: [u8; 32],
286    pub component: ComponentInstanceId,
287}
288
289///
290/// RootComponentChildCommitRequest
291///
292/// Parent command committing one already verified direct-child operation.
293///
294
295#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
296pub struct RootComponentChildCommitRequest {
297    pub operation_id: [u8; 32],
298    pub component: ComponentInstanceId,
299}
300
301///
302/// RootComponentChildDirectoryPreparationRequest
303///
304/// Parent command distributing one committed child's Directory and converging its affected members.
305///
306
307#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
308pub struct RootComponentChildDirectoryPreparationRequest {
309    pub operation_id: [u8; 32],
310    pub component: ComponentInstanceId,
311}
312
313///
314/// RootComponentChildRuntimeActivationRequest
315///
316/// Parent command activating one Directory-prepared direct-child runtime.
317///
318
319#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
320pub struct RootComponentChildRuntimeActivationRequest {
321    pub operation_id: [u8; 32],
322    pub component: ComponentInstanceId,
323}
324
325///
326/// RootComponentChildMembershipActivationRequest
327///
328/// Parent command activating one runtime-active direct child's Registry membership.
329///
330
331#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
332pub struct RootComponentChildMembershipActivationRequest {
333    pub operation_id: [u8; 32],
334    pub component: ComponentInstanceId,
335}
336
337///
338/// RootComponentCreationRequest
339///
340/// Controller command continuing one already reserved top-level Component operation.
341///
342
343#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
344pub struct RootComponentCreationRequest {
345    pub operation_id: [u8; 32],
346}
347
348///
349/// RootComponentInstallRequest
350///
351/// Controller command continuing one already created top-level Component operation.
352///
353
354#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
355pub struct RootComponentInstallRequest {
356    pub operation_id: [u8; 32],
357}
358
359///
360/// RootComponentCommitRequest
361///
362/// Controller command committing one already verified top-level Component operation.
363///
364
365#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
366pub struct RootComponentCommitRequest {
367    pub operation_id: [u8; 32],
368}
369
370///
371/// RootComponentDirectoryPreparationRequest
372///
373/// Controller command distributing exact Directories to one committed top-level Component.
374///
375
376#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
377pub struct RootComponentDirectoryPreparationRequest {
378    pub operation_id: [u8; 32],
379}
380
381///
382/// RootComponentRuntimeActivationRequest
383///
384/// Controller command activating one Directory-prepared top-level Component runtime.
385///
386
387#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
388pub struct RootComponentRuntimeActivationRequest {
389    pub operation_id: [u8; 32],
390}
391
392///
393/// RootComponentMembershipActivationRequest
394///
395/// Controller command activating one runtime-active Component's Registry membership.
396///
397
398#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
399pub struct RootComponentMembershipActivationRequest {
400    pub operation_id: [u8; 32],
401}
402
403///
404/// ComponentProvisioningOrigin
405///
406/// Authenticated causal authority retained with one top-level Component allocation.
407///
408
409#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
410pub enum ComponentProvisioningOrigin {
411    FleetAdministrator { caller: Principal },
412}
413
414///
415/// RootComponentAllocationPhase
416///
417/// Durable root-local progress of one top-level Component allocation operation.
418///
419
420#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
421pub enum RootComponentAllocationPhase {
422    Reserved,
423    CreationIntent,
424    Created,
425    InstallIntent,
426    Installed,
427    Verified,
428    Committed,
429}
430
431///
432/// RootComponentSubtreeRemovalPhase
433///
434/// Durable root-local progress of one child-subtree removal operation.
435///
436
437#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
438#[expect(
439    clippy::large_enum_variant,
440    reason = "wire phases retain complete inline receipts for deterministic Candid responses"
441)]
442pub enum RootComponentSubtreeRemovalPhase {
443    Fenced,
444    Traversing(RootComponentSubtreeRemovalNode),
445    LeafSelected(RootComponentSubtreeRemovalNode),
446    StopIntent(RootComponentSubtreeRemovalStopIntent),
447    Stopped(RootComponentSubtreeRemovalStoppedReceipt),
448    DeleteIntent(RootComponentSubtreeRemovalDeleteIntent),
449    Deleted(RootComponentSubtreeRemovalDeletedReceipt),
450    MembershipRemoved(RootComponentSubtreeRemovalMembershipRemovedReceipt),
451    DirectorySynchronized(RootComponentSubtreeRemovalDirectorySynchronizedReceipt),
452    Completed(RootComponentSubtreeRemovalCompletedReceipt),
453}
454
455///
456/// RootComponentSubtreeRemovalNode
457///
458/// Exact registered child selected as a traversal cursor or removable leaf.
459///
460
461#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
462pub struct RootComponentSubtreeRemovalNode {
463    pub canister_id: Principal,
464    pub parent_canister_id: Principal,
465    pub role: CanisterRole,
466    pub kind: ComponentChildKind,
467    pub installed_artifact_hash: [u8; 32],
468    pub status: ComponentLifecycleStatus,
469}
470
471///
472/// RootComponentSubtreeRemovalStopIntent
473///
474/// Exact registered leaf and sole root controller frozen before a stop call.
475///
476
477#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
478pub struct RootComponentSubtreeRemovalStopIntent {
479    pub leaf: RootComponentSubtreeRemovalNode,
480    pub controller: Principal,
481}
482
483///
484/// RootComponentSubtreeRemovalStoppedReceipt
485///
486/// Frozen stop authority plus the independently observed installed module.
487///
488
489#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
490pub struct RootComponentSubtreeRemovalStoppedReceipt {
491    pub stop: RootComponentSubtreeRemovalStopIntent,
492    pub observed_module_hash: [u8; 32],
493}
494
495///
496/// RootComponentSubtreeRemovalDeleteIntent
497///
498/// Exact stopped receipt frozen before the destructive management call.
499///
500
501#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
502pub struct RootComponentSubtreeRemovalDeleteIntent {
503    pub stopped: RootComponentSubtreeRemovalStoppedReceipt,
504}
505
506///
507/// RootComponentSubtreeRemovalDeletedReceipt
508///
509/// Frozen deletion authority committed only after independently observed absence.
510///
511
512#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
513pub struct RootComponentSubtreeRemovalDeletedReceipt {
514    pub deletion: RootComponentSubtreeRemovalDeleteIntent,
515}
516
517///
518/// RootComponentSubtreeRemovalMembershipRemovedReceipt
519///
520/// Exact Registry transition retained after the independently deleted leaf is unregistered.
521///
522
523#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
524pub struct RootComponentSubtreeRemovalMembershipRemovedReceipt {
525    pub deleted: RootComponentSubtreeRemovalDeletedReceipt,
526    pub removed_from_registry: ComponentRegistryHead,
527    pub previous_descendant_content_hash: [u8; 32],
528    pub previous_committed_descendants: u32,
529    pub registry: ComponentRegistryHead,
530    pub descendant_content_hash: [u8; 32],
531    pub registry_encoded_bytes: u64,
532    pub reserved_descendants: u32,
533    pub committed_descendants: u32,
534    pub directory_synchronized_at_ns: u64,
535    pub directory_authority_hash: [u8; 32],
536    pub parent_role_instances: u32,
537    pub root_managed_descendants: u32,
538    pub root_known_created_component_canisters: u32,
539}
540
541///
542/// RootComponentSubtreeRemovalDirectoryConvergenceEvidence
543///
544/// Compact durable proof that one surviving member covered the required Directory.
545///
546
547#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
548pub struct RootComponentSubtreeRemovalDirectoryConvergenceEvidence {
549    pub operation_id: [u8; 32],
550    pub canister_id: Principal,
551    pub activation: ComponentRuntimeActivationEvidence,
552}
553
554///
555/// RootComponentSubtreeRemovalDirectorySynchronizedReceipt
556///
557/// Membership removal plus independently verified surviving-member convergence.
558///
559
560#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
561pub struct RootComponentSubtreeRemovalDirectorySynchronizedReceipt {
562    pub membership_removed: RootComponentSubtreeRemovalMembershipRemovedReceipt,
563    pub covered_fleet_registry_revision: u64,
564    pub covered_fleet_registry_content_hash: [u8; 32],
565    pub covered_component_registry: ComponentRegistryHead,
566    pub covered_authority_hash: [u8; 32],
567    pub owning_component: RootComponentSubtreeRemovalDirectoryConvergenceEvidence,
568    pub parent: Option<RootComponentSubtreeRemovalDirectoryConvergenceEvidence>,
569}
570
571///
572/// RootComponentSubtreeRemovalCompletedReceipt
573///
574/// Terminal Registry and Directory authority after the fenced target is finalized.
575///
576
577#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
578pub struct RootComponentSubtreeRemovalCompletedReceipt {
579    pub registry: ComponentRegistryHead,
580    pub directory_authority_hash: [u8; 32],
581}
582
583///
584/// ComponentLifecycleStatus
585///
586/// Root-owned runtime lifecycle state of one committed Component Registry member.
587///
588
589#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
590pub enum ComponentLifecycleStatus {
591    Prepared,
592    Active,
593    Draining,
594    Removed,
595}
596
597///
598/// ComponentRegistryHead
599///
600/// Exact independently versioned authority of one Component Registry partition.
601///
602
603#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
604pub struct ComponentRegistryHead {
605    pub component: ComponentInstanceId,
606    pub revision: u64,
607    pub content_hash: [u8; 32],
608}
609
610///
611/// ComponentRegistryPartitionRequest
612///
613/// Read-only lookup key for one committed Component Registry partition.
614///
615
616#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
617pub struct ComponentRegistryPartitionRequest {
618    pub component: ComponentInstanceId,
619}
620
621///
622/// ComponentRegistryPartitionResponse
623///
624/// Protected top-level row and independent head of one Component Registry partition.
625///
626
627#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
628pub struct ComponentRegistryPartitionResponse {
629    pub head: ComponentRegistryHead,
630    pub binding: ComponentBinding,
631    pub provisioning_origin: ComponentProvisioningOrigin,
632    pub release_set: FleetSubnetRootReleaseSet,
633    pub status: ComponentLifecycleStatus,
634    pub reserved_descendants: u32,
635    pub committed_descendants: u32,
636    pub encoded_bytes: u64,
637}
638
639///
640/// ComponentDirectoryProvenance
641///
642/// Exact Component Registry authority from which one Component Directory is derived.
643///
644
645#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
646pub struct ComponentDirectoryProvenance {
647    pub component: ComponentBinding,
648    pub source_fleet_subnet_root: Principal,
649    pub component_registry_revision: u64,
650    pub component_registry_content_hash: [u8; 32],
651    pub synchronized_at_ns: u64,
652}
653
654///
655/// ComponentDirectoryHead
656///
657/// Compact independently versioned discovery projection for one Component tree.
658///
659
660#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
661pub struct ComponentDirectoryHead {
662    pub provenance: ComponentDirectoryProvenance,
663    pub descendant_count: u32,
664}
665
666///
667/// ComponentDirectoryHeadRequest
668///
669/// Read-only lookup key for one committed Component Directory head.
670///
671
672#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
673pub struct ComponentDirectoryHeadRequest {
674    pub component: ComponentInstanceId,
675}
676
677///
678/// ComponentDirectoryPageCursor
679///
680/// Opaque revision- and filter-bound continuation for one bounded Directory page.
681///
682
683#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
684pub struct ComponentDirectoryPageCursor(pub Vec<u8>);
685
686///
687/// ComponentDirectoryPageRequest
688///
689/// Bounded member query against one exact current Component Directory authority.
690///
691
692#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
693pub struct ComponentDirectoryPageRequest {
694    pub directory: ComponentDirectoryHead,
695    pub parent_canister_id: Option<Principal>,
696    pub role: Option<CanisterRole>,
697    pub status: Option<ComponentLifecycleStatus>,
698    pub cursor: Option<ComponentDirectoryPageCursor>,
699    pub limit: u16,
700}
701
702///
703/// ComponentDirectoryChildEntry
704///
705/// One authoritative normalized child projected with its complete protected binding.
706///
707
708#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
709pub struct ComponentDirectoryChildEntry {
710    pub binding: ComponentChildBinding,
711    pub kind: ComponentChildKind,
712    pub installed_artifact_hash: [u8; 32],
713    pub status: ComponentLifecycleStatus,
714}
715
716///
717/// ComponentDirectoryPageResponse
718///
719/// One bounded caller-scoped page under the exact requested Directory head.
720///
721
722#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
723pub struct ComponentDirectoryPageResponse {
724    pub directory: ComponentDirectoryHead,
725    pub entries: Vec<ComponentDirectoryChildEntry>,
726    pub next_cursor: Option<ComponentDirectoryPageCursor>,
727}
728
729///
730/// ComponentRuntimeDirectoryAuthority
731///
732/// Exact Fleet and Component discovery authority retained by one managed Component-tree node.
733///
734
735#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
736pub struct ComponentRuntimeDirectoryAuthority {
737    pub fleet: FleetDirectorySnapshot,
738    pub component: ComponentDirectoryHead,
739}
740
741///
742/// ComponentRuntimeDirectoryPreparationRequest
743///
744/// Root-issued exact Directory preparation command for one managed Component-tree node.
745///
746
747#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
748pub struct ComponentRuntimeDirectoryPreparationRequest {
749    pub operation_id: [u8; 32],
750    pub authority: ComponentRuntimeDirectoryAuthority,
751}
752
753///
754/// ComponentRuntimeDirectorySynchronizationRequest
755///
756/// Root-issued replacement of one active managed Component node's current Directory authority.
757///
758
759#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
760pub struct ComponentRuntimeDirectorySynchronizationRequest {
761    pub operation_id: [u8; 32],
762    pub authority: ComponentRuntimeDirectoryAuthority,
763}
764
765///
766/// ComponentRuntimePhase
767///
768/// Target-local progress from installation through Component runtime activation.
769///
770
771#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
772pub enum ComponentRuntimePhase {
773    AwaitingDirectory,
774    DirectoryPrepared,
775    Active,
776}
777
778///
779/// ComponentRuntimeActivationEvidence
780///
781/// Exact retained Directory authority under which one Component runtime became Active.
782///
783
784#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
785pub struct ComponentRuntimeActivationEvidence {
786    pub directory_authority_hash: [u8; 32],
787    pub activated_at_ns: u64,
788}
789
790///
791/// ComponentRuntimeActivationRequest
792///
793/// Root-issued exact activation command for one Directory-prepared managed Component node.
794///
795
796#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
797pub struct ComponentRuntimeActivationRequest {
798    pub operation_id: [u8; 32],
799    pub directory_authority_hash: [u8; 32],
800}
801
802///
803/// ComponentRuntimeStatusResponse
804///
805/// Independently observable target-local binding and exact retained Directory authority.
806///
807
808#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
809pub struct ComponentRuntimeStatusResponse {
810    pub operation_id: [u8; 32],
811    pub binding: ManagedCanisterBinding,
812    pub phase: ComponentRuntimePhase,
813    pub authority: Option<ComponentRuntimeDirectoryAuthority>,
814    pub authority_hash: Option<[u8; 32]>,
815    pub activation: Option<ComponentRuntimeActivationEvidence>,
816}
817
818///
819/// ComponentRuntimeDirectoryConvergenceEvidence
820///
821/// Stable root evidence that one active member covered at least the required Directory authority.
822///
823
824#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
825pub struct ComponentRuntimeDirectoryConvergenceEvidence {
826    pub operation_id: [u8; 32],
827    pub binding: ManagedCanisterBinding,
828    pub covered_authority: ComponentRuntimeDirectoryAuthority,
829    pub covered_authority_hash: [u8; 32],
830    pub activation: ComponentRuntimeActivationEvidence,
831}
832
833///
834/// RootComponentCreationEvidence
835///
836/// Exact Store artifact and root-owned creation settings frozen before the paid effect.
837///
838
839#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
840pub struct RootComponentCreationEvidence {
841    pub wasm_store: Principal,
842    pub payload_hash: [u8; 32],
843    pub payload_size_bytes: u64,
844    pub initial_cycles: Cycles,
845    pub controller: Principal,
846    pub canister: Option<Principal>,
847}
848
849///
850/// RootComponentInstallEvidence
851///
852/// Exact raw artifact, chunk source and immutable target binding frozen before installation.
853///
854
855#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
856pub struct RootComponentInstallEvidence {
857    pub raw_module_hash: [u8; 32],
858    pub chunk_hashes: Vec<Vec<u8>>,
859    pub binding: ComponentBinding,
860}
861
862///
863/// RootComponentChildInstallEvidence
864///
865/// Exact child module and immutable retained binding frozen before installation.
866///
867
868#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
869pub struct RootComponentChildInstallEvidence {
870    pub raw_module_hash: [u8; 32],
871    pub chunk_hashes: Vec<Vec<u8>>,
872    pub binding: ComponentChildBinding,
873}
874
875///
876/// RootComponentAllocationResponse
877///
878/// Durable identity reservation returned identically for exact operation retry.
879///
880
881#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
882pub struct RootComponentAllocationResponse {
883    pub operation_id: [u8; 32],
884    pub allocation_sequence: u64,
885    pub component: ComponentInstanceId,
886    pub component_spec: ComponentSpecId,
887    pub spec_hash: [u8; 32],
888    pub role: CanisterRole,
889    pub provisioning_origin: ComponentProvisioningOrigin,
890    pub release_set: FleetSubnetRootReleaseSet,
891    pub phase: RootComponentAllocationPhase,
892    pub creation: Option<RootComponentCreationEvidence>,
893    pub installation: Option<RootComponentInstallEvidence>,
894}
895
896///
897/// RootComponentChildAllocationResponse
898///
899/// Durable direct-child lifecycle progress returned identically for exact parent retry.
900///
901
902#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
903pub struct RootComponentChildAllocationResponse {
904    pub operation_id: [u8; 32],
905    pub component: ComponentInstanceId,
906    pub parent_canister_id: Principal,
907    pub parent_role: CanisterRole,
908    pub child_role: CanisterRole,
909    pub child_kind: ComponentChildKind,
910    pub maximum_instances_per_parent: u32,
911    pub maximum_descendants: u32,
912    pub maximum_registry_bytes: u64,
913    pub reserved_against_registry: ComponentRegistryHead,
914    pub release_set: FleetSubnetRootReleaseSet,
915    pub phase: RootComponentAllocationPhase,
916    pub creation: Option<RootComponentCreationEvidence>,
917    pub installation: Option<RootComponentChildInstallEvidence>,
918}
919
920///
921/// RootComponentSubtreeRemovalResponse
922///
923/// Current durable snapshot of one monotonic subtree-removal operation.
924///
925
926#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
927pub struct RootComponentSubtreeRemovalResponse {
928    pub operation_id: [u8; 32],
929    pub component: ComponentInstanceId,
930    pub target_canister_id: Principal,
931    pub target_parent_canister_id: Principal,
932    pub target_role: CanisterRole,
933    pub target_status: ComponentLifecycleStatus,
934    pub reserved_against_registry: ComponentRegistryHead,
935    pub maximum_completed_leaves: u32,
936    pub completed_leaves: u32,
937    pub traversal_steps: u32,
938    pub phase: RootComponentSubtreeRemovalPhase,
939}
940
941///
942/// RootComponentChildCommitResponse
943///
944/// Exact committed child operation, authoritative Component Registry and next Directory head.
945///
946
947#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
948pub struct RootComponentChildCommitResponse {
949    pub allocation: RootComponentChildAllocationResponse,
950    pub registry: ComponentRegistryPartitionResponse,
951    pub directory: ComponentDirectoryHead,
952}
953
954///
955/// RootComponentChildDirectoryPreparationResponse
956///
957/// Exact child preparation plus stable bounded active-member Directory coverage.
958///
959
960#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
961pub struct RootComponentChildDirectoryPreparationResponse {
962    pub committed: RootComponentChildCommitResponse,
963    pub child: ComponentRuntimeStatusResponse,
964    pub owning_component: ComponentRuntimeDirectoryConvergenceEvidence,
965    pub parent: Option<ComponentRuntimeDirectoryConvergenceEvidence>,
966}
967
968///
969/// RootComponentChildRuntimeActivationResponse
970///
971/// Exact child commitment plus independently observed Directory-bound runtime activation.
972///
973
974#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
975pub struct RootComponentChildRuntimeActivationResponse {
976    pub committed: RootComponentChildCommitResponse,
977    pub child: ComponentRuntimeStatusResponse,
978}
979
980///
981/// RootComponentChildMembershipActivationResponse
982///
983/// Original child commitment plus active Registry, Directory and target convergence evidence.
984///
985
986#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
987pub struct RootComponentChildMembershipActivationResponse {
988    pub committed: RootComponentChildCommitResponse,
989    pub registry: ComponentRegistryPartitionResponse,
990    pub directory: ComponentDirectoryHead,
991    pub child: ComponentRuntimeStatusResponse,
992}
993
994///
995/// RootComponentCommitResponse
996///
997/// Exact committed allocation, authoritative Registry row and derived Directory head.
998///
999
1000#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1001pub struct RootComponentCommitResponse {
1002    pub allocation: RootComponentAllocationResponse,
1003    pub registry: ComponentRegistryPartitionResponse,
1004    pub directory: ComponentDirectoryHead,
1005}
1006
1007///
1008/// RootComponentDirectoryPreparationResponse
1009///
1010/// Exact root authority plus independently observed target-local Directory preparation.
1011///
1012
1013#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1014pub struct RootComponentDirectoryPreparationResponse {
1015    pub committed: RootComponentCommitResponse,
1016    pub target: ComponentRuntimeStatusResponse,
1017}
1018
1019///
1020/// RootComponentRuntimeActivationResponse
1021///
1022/// Exact root authority plus independently observed target-local runtime activation.
1023///
1024
1025#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1026pub struct RootComponentRuntimeActivationResponse {
1027    pub committed: RootComponentCommitResponse,
1028    pub target: ComponentRuntimeStatusResponse,
1029}
1030
1031///
1032/// RootComponentMembershipActivationResponse
1033///
1034/// Exact active Registry authority plus independently observed current target Directory.
1035///
1036
1037#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1038pub struct RootComponentMembershipActivationResponse {
1039    pub allocation: RootComponentAllocationResponse,
1040    pub registry: ComponentRegistryPartitionResponse,
1041    pub directory: ComponentDirectoryHead,
1042    pub target: ComponentRuntimeStatusResponse,
1043}
1044
1045#[cfg(test)]
1046mod tests {
1047    use super::*;
1048    use crate::{
1049        dto::root_store::RootStoreBootstrapRequest,
1050        ids::{
1051            AppId, CanonicalNetworkId, FleetCoordinatorBinding, FleetId, FleetKey,
1052            FleetRegistryAuthority, ReleaseBuildId, ReleaseBuildNonce, ReleaseSetDigest, SubnetId,
1053        },
1054    };
1055
1056    #[test]
1057    fn component_registry_contracts_round_trip_through_candid() {
1058        let request = RootComponentRegistryPreparationRequest {
1059            store_bootstrap: RootStoreBootstrapRequest {
1060                manifest_payload_size_bytes: 128,
1061            },
1062            expected_fleet_registry: FleetRegistryVersion {
1063                authority: fleet_registry_authority(),
1064                revision: 4,
1065                content_hash: [5; 32],
1066            },
1067        };
1068        let response = RootComponentRegistryStatusResponse {
1069            fleet_subnet_root: Principal::from_slice(&[6; 29]),
1070            prepared_against_registry: request.expected_fleet_registry.clone(),
1071            release_set: FleetSubnetRootReleaseSet {
1072                release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
1073                    [7; 32],
1074                )),
1075                manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
1076            },
1077            component_topology_digest: ComponentTopologyDigest::from_bytes([9; 32]),
1078            next_allocation_sequence: 1,
1079            reserved_component_instances: 0,
1080            committed_component_instances: 0,
1081            managed_descendants: 0,
1082            known_created_component_canisters: 0,
1083            encoded_bytes: 0,
1084            initial_inventory: Some(RootComponentInitialInventoryStatus {
1085                fleet_activation_operation_id: [10; 32],
1086                component_count: 0,
1087                inventory_hash: [11; 32],
1088                sealed_at_ns: 12,
1089                directories_converged: true,
1090                root_runtime_activated: true,
1091            }),
1092        };
1093        let allocation = RootComponentAllocationResponse {
1094            operation_id: [10; 32],
1095            allocation_sequence: 1,
1096            component: ComponentInstanceId::from_generated_bytes([11; 32]),
1097            component_spec: "projects".parse().expect("Component Spec ID"),
1098            spec_hash: [12; 32],
1099            role: CanisterRole::new("project_hub"),
1100            provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
1101                caller: Principal::from_slice(&[13; 29]),
1102            },
1103            release_set: response.release_set,
1104            phase: RootComponentAllocationPhase::Reserved,
1105            creation: None,
1106            installation: None,
1107        };
1108        let created = RootComponentAllocationResponse {
1109            phase: RootComponentAllocationPhase::Created,
1110            creation: Some(RootComponentCreationEvidence {
1111                wasm_store: Principal::from_slice(&[14; 29]),
1112                payload_hash: [15; 32],
1113                payload_size_bytes: 4_096,
1114                initial_cycles: Cycles::new(5_000_000_000_000),
1115                controller: Principal::from_slice(&[6; 29]),
1116                canister: Some(Principal::from_slice(&[16; 29])),
1117            }),
1118            installation: None,
1119            ..allocation.clone()
1120        };
1121        let request_bytes = candid::encode_one(&request).expect("encode request");
1122        let response_bytes = candid::encode_one(&response).expect("encode response");
1123        let allocation_bytes = candid::encode_one(&allocation).expect("encode allocation");
1124        let created_bytes = candid::encode_one(&created).expect("encode created allocation");
1125
1126        assert_eq!(
1127            candid::decode_one::<RootComponentRegistryPreparationRequest>(&request_bytes)
1128                .expect("decode request"),
1129            request
1130        );
1131        assert_eq!(
1132            candid::decode_one::<RootComponentRegistryStatusResponse>(&response_bytes)
1133                .expect("decode response"),
1134            response
1135        );
1136        assert_eq!(
1137            candid::decode_one::<RootComponentAllocationResponse>(&allocation_bytes)
1138                .expect("decode allocation"),
1139            allocation
1140        );
1141        assert_eq!(
1142            candid::decode_one::<RootComponentAllocationResponse>(&created_bytes)
1143                .expect("decode created allocation"),
1144            created
1145        );
1146    }
1147
1148    #[test]
1149    fn component_commit_response_round_trips_through_candid() {
1150        let root = Principal::from_slice(&[6; 29]);
1151        let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1152        let component_spec: ComponentSpecId = "projects".parse().expect("Component Spec ID");
1153        let release_set = FleetSubnetRootReleaseSet {
1154            release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
1155                [7; 32],
1156            )),
1157            manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
1158        };
1159        let provisioning_origin = ComponentProvisioningOrigin::FleetAdministrator {
1160            caller: Principal::from_slice(&[13; 29]),
1161        };
1162        let binding = ComponentBinding {
1163            authority: fleet_registry_authority(),
1164            component,
1165            component_spec: component_spec.clone(),
1166            spec_hash: [12; 32],
1167            role: CanisterRole::new("project_hub"),
1168            placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
1169            fleet_subnet_root: root,
1170            canister_id: Principal::from_slice(&[16; 29]),
1171        };
1172        let head = ComponentRegistryHead {
1173            component,
1174            revision: 1,
1175            content_hash: [18; 32],
1176        };
1177        let committed = RootComponentCommitResponse {
1178            allocation: RootComponentAllocationResponse {
1179                operation_id: [10; 32],
1180                allocation_sequence: 1,
1181                component,
1182                component_spec,
1183                spec_hash: binding.spec_hash,
1184                role: binding.role.clone(),
1185                provisioning_origin: provisioning_origin.clone(),
1186                release_set,
1187                phase: RootComponentAllocationPhase::Committed,
1188                creation: Some(RootComponentCreationEvidence {
1189                    wasm_store: Principal::from_slice(&[14; 29]),
1190                    payload_hash: [15; 32],
1191                    payload_size_bytes: 4_096,
1192                    initial_cycles: Cycles::new(5_000_000_000_000),
1193                    controller: root,
1194                    canister: Some(binding.canister_id),
1195                }),
1196                installation: Some(RootComponentInstallEvidence {
1197                    raw_module_hash: [20; 32],
1198                    chunk_hashes: vec![vec![21; 32]],
1199                    binding: binding.clone(),
1200                }),
1201            },
1202            registry: ComponentRegistryPartitionResponse {
1203                head: head.clone(),
1204                binding: binding.clone(),
1205                provisioning_origin,
1206                release_set,
1207                status: ComponentLifecycleStatus::Prepared,
1208                reserved_descendants: 0,
1209                committed_descendants: 0,
1210                encoded_bytes: 2_048,
1211            },
1212            directory: ComponentDirectoryHead {
1213                provenance: ComponentDirectoryProvenance {
1214                    component: binding,
1215                    source_fleet_subnet_root: root,
1216                    component_registry_revision: head.revision,
1217                    component_registry_content_hash: head.content_hash,
1218                    synchronized_at_ns: 19,
1219                },
1220                descendant_count: 0,
1221            },
1222        };
1223        let committed_bytes = candid::encode_one(&committed).expect("encode committed allocation");
1224
1225        assert_eq!(
1226            candid::decode_one::<RootComponentCommitResponse>(&committed_bytes)
1227                .expect("decode committed allocation"),
1228            committed
1229        );
1230    }
1231
1232    #[test]
1233    fn component_directory_page_contracts_round_trip_through_candid() {
1234        let root = Principal::from_slice(&[6; 29]);
1235        let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1236        let binding = ComponentBinding {
1237            authority: fleet_registry_authority(),
1238            component,
1239            component_spec: "projects".parse().expect("Component Spec ID"),
1240            spec_hash: [12; 32],
1241            role: CanisterRole::new("project_hub"),
1242            placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
1243            fleet_subnet_root: root,
1244            canister_id: Principal::from_slice(&[16; 29]),
1245        };
1246        let directory = ComponentDirectoryHead {
1247            provenance: ComponentDirectoryProvenance {
1248                component: binding.clone(),
1249                source_fleet_subnet_root: root,
1250                component_registry_revision: 3,
1251                component_registry_content_hash: [18; 32],
1252                synchronized_at_ns: 19,
1253            },
1254            descendant_count: 1,
1255        };
1256        let request = ComponentDirectoryPageRequest {
1257            directory: directory.clone(),
1258            parent_canister_id: Some(binding.canister_id),
1259            role: Some(CanisterRole::new("project_instance")),
1260            status: Some(ComponentLifecycleStatus::Active),
1261            cursor: Some(ComponentDirectoryPageCursor(vec![20; 64])),
1262            limit: 50,
1263        };
1264        let response = ComponentDirectoryPageResponse {
1265            directory,
1266            entries: vec![ComponentDirectoryChildEntry {
1267                binding: ComponentChildBinding {
1268                    component: binding.clone(),
1269                    parent_canister_id: binding.canister_id,
1270                    role: CanisterRole::new("project_instance"),
1271                    canister_id: Principal::from_slice(&[21; 29]),
1272                },
1273                kind: ComponentChildKind::Instance,
1274                installed_artifact_hash: [22; 32],
1275                status: ComponentLifecycleStatus::Active,
1276            }],
1277            next_cursor: Some(ComponentDirectoryPageCursor(vec![23; 64])),
1278        };
1279        let request_bytes = candid::encode_one(&request).expect("encode Directory page request");
1280        let response_bytes = candid::encode_one(&response).expect("encode Directory page response");
1281
1282        assert_eq!(
1283            candid::decode_one::<ComponentDirectoryPageRequest>(&request_bytes)
1284                .expect("decode Directory page request"),
1285            request
1286        );
1287        assert_eq!(
1288            candid::decode_one::<ComponentDirectoryPageResponse>(&response_bytes)
1289                .expect("decode Directory page response"),
1290            response
1291        );
1292    }
1293
1294    fn fleet_registry_authority() -> FleetRegistryAuthority {
1295        FleetRegistryAuthority {
1296            binding: FleetCoordinatorBinding {
1297                fleet: crate::ids::FleetBinding {
1298                    fleet: FleetKey {
1299                        canonical_network_id: CanonicalNetworkId::ic_mainnet(),
1300                        fleet_id: FleetId::from_generated_bytes([1; 32]),
1301                    },
1302                    app: AppId::from("toko"),
1303                },
1304                coordinator_subnet: SubnetId::from_principal(Principal::from_slice(&[2; 29])),
1305                coordinator: Principal::from_slice(&[3; 29]),
1306            },
1307            epoch: 1,
1308        }
1309    }
1310
1311    #[test]
1312    fn component_creation_request_round_trips_through_candid() {
1313        let request = RootComponentCreationRequest {
1314            operation_id: [10; 32],
1315        };
1316        let bytes = candid::encode_one(request).expect("encode creation request");
1317
1318        assert_eq!(
1319            candid::decode_one::<RootComponentCreationRequest>(&bytes)
1320                .expect("decode creation request"),
1321            request
1322        );
1323    }
1324
1325    #[test]
1326    #[expect(
1327        clippy::too_many_lines,
1328        reason = "one Candid contract test covers every subtree-removal phase receipt"
1329    )]
1330    fn component_subtree_removal_contracts_round_trip_through_candid() {
1331        let component = ComponentInstanceId::from_generated_bytes([41; 32]);
1332        let registry = ComponentRegistryHead {
1333            component,
1334            revision: 7,
1335            content_hash: [42; 32],
1336        };
1337        let request = RootComponentSubtreeRemovalRequest {
1338            operation_id: [43; 32],
1339            component,
1340            target_canister_id: Principal::from_slice(&[44; 29]),
1341            expected_registry: registry.clone(),
1342        };
1343        let status_request = RootComponentSubtreeRemovalStatusRequest {
1344            operation_id: request.operation_id,
1345            component,
1346        };
1347        let advance_request = RootComponentSubtreeRemovalAdvanceRequest {
1348            operation_id: request.operation_id,
1349            component,
1350            expected_traversal_steps: 1,
1351        };
1352        let stop_request = RootComponentSubtreeRemovalStopPreparationRequest {
1353            operation_id: request.operation_id,
1354            component,
1355            expected_traversal_steps: 2,
1356            expected_leaf_canister_id: Principal::from_slice(&[46; 29]),
1357            expected_leaf_parent_canister_id: request.target_canister_id,
1358        };
1359        let stopped = RootComponentSubtreeRemovalStoppedReceipt {
1360            observed_module_hash: [49; 32],
1361            stop: RootComponentSubtreeRemovalStopIntent {
1362                controller: Principal::from_slice(&[48; 29]),
1363                leaf: RootComponentSubtreeRemovalNode {
1364                    canister_id: Principal::from_slice(&[46; 29]),
1365                    parent_canister_id: request.target_canister_id,
1366                    role: CanisterRole::new("project_ledger"),
1367                    kind: ComponentChildKind::Singleton,
1368                    installed_artifact_hash: [47; 32],
1369                    status: ComponentLifecycleStatus::Active,
1370                },
1371            },
1372        };
1373        let response = RootComponentSubtreeRemovalResponse {
1374            operation_id: request.operation_id,
1375            component,
1376            target_canister_id: request.target_canister_id,
1377            target_parent_canister_id: Principal::from_slice(&[45; 29]),
1378            target_role: CanisterRole::new("project_instance"),
1379            target_status: ComponentLifecycleStatus::Active,
1380            reserved_against_registry: registry,
1381            maximum_completed_leaves: 4,
1382            completed_leaves: 1,
1383            traversal_steps: 2,
1384            phase: RootComponentSubtreeRemovalPhase::DirectorySynchronized(
1385                RootComponentSubtreeRemovalDirectorySynchronizedReceipt {
1386                    membership_removed: RootComponentSubtreeRemovalMembershipRemovedReceipt {
1387                        deleted: RootComponentSubtreeRemovalDeletedReceipt {
1388                            deletion: RootComponentSubtreeRemovalDeleteIntent { stopped },
1389                        },
1390                        removed_from_registry: ComponentRegistryHead {
1391                            component,
1392                            revision: 8,
1393                            content_hash: [50; 32],
1394                        },
1395                        previous_descendant_content_hash: [51; 32],
1396                        previous_committed_descendants: 4,
1397                        registry: ComponentRegistryHead {
1398                            component,
1399                            revision: 9,
1400                            content_hash: [52; 32],
1401                        },
1402                        descendant_content_hash: [53; 32],
1403                        registry_encoded_bytes: 4_096,
1404                        reserved_descendants: 1,
1405                        committed_descendants: 3,
1406                        directory_synchronized_at_ns: 54,
1407                        directory_authority_hash: [55; 32],
1408                        parent_role_instances: 0,
1409                        root_managed_descendants: 4,
1410                        root_known_created_component_canisters: 4,
1411                    },
1412                    covered_fleet_registry_revision: 6,
1413                    covered_fleet_registry_content_hash: [56; 32],
1414                    covered_component_registry: ComponentRegistryHead {
1415                        component,
1416                        revision: 9,
1417                        content_hash: [52; 32],
1418                    },
1419                    covered_authority_hash: [55; 32],
1420                    owning_component: RootComponentSubtreeRemovalDirectoryConvergenceEvidence {
1421                        operation_id: [57; 32],
1422                        canister_id: Principal::from_slice(&[58; 29]),
1423                        activation: ComponentRuntimeActivationEvidence {
1424                            directory_authority_hash: [59; 32],
1425                            activated_at_ns: 60,
1426                        },
1427                    },
1428                    parent: Some(RootComponentSubtreeRemovalDirectoryConvergenceEvidence {
1429                        operation_id: [61; 32],
1430                        canister_id: request.target_canister_id,
1431                        activation: ComponentRuntimeActivationEvidence {
1432                            directory_authority_hash: [62; 32],
1433                            activated_at_ns: 63,
1434                        },
1435                    }),
1436                },
1437            ),
1438        };
1439
1440        let request_bytes = candid::encode_one(&request).expect("encode subtree removal request");
1441        let advance_bytes =
1442            candid::encode_one(advance_request).expect("encode subtree removal advance request");
1443        let stop_bytes =
1444            candid::encode_one(stop_request).expect("encode subtree removal stop request");
1445        let status_bytes =
1446            candid::encode_one(status_request).expect("encode subtree removal status request");
1447        let response_bytes =
1448            candid::encode_one(&response).expect("encode subtree removal response");
1449
1450        assert_eq!(
1451            candid::decode_one::<RootComponentSubtreeRemovalRequest>(&request_bytes)
1452                .expect("decode subtree removal request"),
1453            request
1454        );
1455        assert_eq!(
1456            candid::decode_one::<RootComponentSubtreeRemovalAdvanceRequest>(&advance_bytes)
1457                .expect("decode subtree removal advance request"),
1458            advance_request
1459        );
1460        assert_eq!(
1461            candid::decode_one::<RootComponentSubtreeRemovalStopPreparationRequest>(&stop_bytes)
1462                .expect("decode subtree removal stop request"),
1463            stop_request
1464        );
1465        assert_eq!(
1466            candid::decode_one::<RootComponentSubtreeRemovalStatusRequest>(&status_bytes)
1467                .expect("decode subtree removal status request"),
1468            status_request
1469        );
1470        assert_eq!(
1471            candid::decode_one::<RootComponentSubtreeRemovalResponse>(&response_bytes)
1472                .expect("decode subtree removal response"),
1473            response
1474        );
1475    }
1476
1477    #[test]
1478    fn component_subtree_removal_deletion_requests_round_trip_through_candid() {
1479        let prepare = RootComponentSubtreeRemovalDeletePreparationRequest {
1480            operation_id: [50; 32],
1481            component: ComponentInstanceId::from_generated_bytes([51; 32]),
1482            expected_traversal_steps: 3,
1483            expected_leaf_canister_id: Principal::from_slice(&[52; 29]),
1484            expected_leaf_parent_canister_id: Principal::from_slice(&[53; 29]),
1485        };
1486        let request = RootComponentSubtreeRemovalDeleteRequest {
1487            operation_id: prepare.operation_id,
1488            component: prepare.component,
1489            expected_traversal_steps: prepare.expected_traversal_steps,
1490            expected_leaf_canister_id: prepare.expected_leaf_canister_id,
1491            expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
1492        };
1493        let membership_request = RootComponentSubtreeRemovalMembershipRemovalRequest {
1494            operation_id: prepare.operation_id,
1495            component: prepare.component,
1496            expected_traversal_steps: prepare.expected_traversal_steps,
1497            expected_leaf_canister_id: prepare.expected_leaf_canister_id,
1498            expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
1499        };
1500        let directory_request = RootComponentSubtreeRemovalDirectorySynchronizationRequest {
1501            operation_id: prepare.operation_id,
1502            component: prepare.component,
1503            expected_traversal_steps: prepare.expected_traversal_steps,
1504            expected_leaf_canister_id: prepare.expected_leaf_canister_id,
1505            expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
1506        };
1507        let finalization_request = RootComponentSubtreeRemovalLeafFinalizationRequest {
1508            operation_id: prepare.operation_id,
1509            component: prepare.component,
1510            expected_traversal_steps: prepare.expected_traversal_steps,
1511            expected_leaf_canister_id: prepare.expected_leaf_canister_id,
1512            expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
1513        };
1514        let prepare_bytes = candid::encode_one(prepare)
1515            .expect("encode subtree removal deletion preparation request");
1516        let request_bytes =
1517            candid::encode_one(request).expect("encode subtree removal deletion request");
1518        let membership_request_bytes = candid::encode_one(membership_request)
1519            .expect("encode subtree removal membership-removal request");
1520        let directory_request_bytes = candid::encode_one(directory_request)
1521            .expect("encode subtree removal Directory synchronization request");
1522        let finalization_request_bytes = candid::encode_one(finalization_request)
1523            .expect("encode subtree removal leaf-finalization request");
1524
1525        assert_eq!(
1526            candid::decode_one::<RootComponentSubtreeRemovalDeletePreparationRequest>(
1527                &prepare_bytes
1528            )
1529            .expect("decode subtree removal deletion preparation request"),
1530            prepare
1531        );
1532        assert_eq!(
1533            candid::decode_one::<RootComponentSubtreeRemovalDeleteRequest>(&request_bytes)
1534                .expect("decode subtree removal deletion request"),
1535            request
1536        );
1537        assert_eq!(
1538            candid::decode_one::<RootComponentSubtreeRemovalMembershipRemovalRequest>(
1539                &membership_request_bytes
1540            )
1541            .expect("decode subtree removal membership-removal request"),
1542            membership_request
1543        );
1544        assert_eq!(
1545            candid::decode_one::<RootComponentSubtreeRemovalDirectorySynchronizationRequest>(
1546                &directory_request_bytes
1547            )
1548            .expect("decode subtree removal Directory synchronization request"),
1549            directory_request
1550        );
1551        assert_eq!(
1552            candid::decode_one::<RootComponentSubtreeRemovalLeafFinalizationRequest>(
1553                &finalization_request_bytes
1554            )
1555            .expect("decode subtree removal leaf-finalization request"),
1556            finalization_request
1557        );
1558    }
1559
1560    #[test]
1561    fn component_subtree_removal_stop_request_round_trips_through_candid() {
1562        let request = RootComponentSubtreeRemovalStopRequest {
1563            operation_id: [50; 32],
1564            component: ComponentInstanceId::from_generated_bytes([51; 32]),
1565            expected_traversal_steps: 3,
1566            expected_leaf_canister_id: Principal::from_slice(&[52; 29]),
1567            expected_leaf_parent_canister_id: Principal::from_slice(&[53; 29]),
1568        };
1569        let bytes =
1570            candid::encode_one(request).expect("encode subtree removal stop execution request");
1571
1572        assert_eq!(
1573            candid::decode_one::<RootComponentSubtreeRemovalStopRequest>(&bytes)
1574                .expect("decode subtree removal stop execution request"),
1575            request
1576        );
1577    }
1578
1579    #[test]
1580    #[expect(
1581        clippy::too_many_lines,
1582        reason = "one round-trip test keeps the complete child lifecycle boundary coherent"
1583    )]
1584    fn component_child_lifecycle_contracts_round_trip_through_candid() {
1585        let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1586        let registry = ComponentRegistryHead {
1587            component,
1588            revision: 2,
1589            content_hash: [12; 32],
1590        };
1591        let request = RootComponentChildAllocationRequest {
1592            operation_id: [13; 32],
1593            component,
1594            expected_registry: registry.clone(),
1595            child_role: CanisterRole::new("project_instance"),
1596        };
1597        let status_request = RootComponentChildAllocationStatusRequest {
1598            operation_id: request.operation_id,
1599            component,
1600        };
1601        let creation_request = RootComponentChildCreationRequest {
1602            operation_id: request.operation_id,
1603            component,
1604        };
1605        let install_request = RootComponentChildInstallRequest {
1606            operation_id: request.operation_id,
1607            component,
1608        };
1609        let commit_request = RootComponentChildCommitRequest {
1610            operation_id: request.operation_id,
1611            component,
1612        };
1613        let directory_request = RootComponentChildDirectoryPreparationRequest {
1614            operation_id: request.operation_id,
1615            component,
1616        };
1617        let activation_request = RootComponentChildRuntimeActivationRequest {
1618            operation_id: request.operation_id,
1619            component,
1620        };
1621        let membership_request = RootComponentChildMembershipActivationRequest {
1622            operation_id: request.operation_id,
1623            component,
1624        };
1625        let root = Principal::from_slice(&[17; 29]);
1626        let parent = Principal::from_slice(&[14; 29]);
1627        let child = Principal::from_slice(&[18; 29]);
1628        let child_binding = ComponentChildBinding {
1629            component: ComponentBinding {
1630                authority: fleet_registry_authority(),
1631                component,
1632                component_spec: "projects".parse().expect("Component Spec"),
1633                spec_hash: [19; 32],
1634                role: CanisterRole::new("project_hub"),
1635                placement_subnet: SubnetId::from_principal(Principal::from_slice(&[20; 29])),
1636                fleet_subnet_root: root,
1637                canister_id: parent,
1638            },
1639            parent_canister_id: parent,
1640            role: request.child_role.clone(),
1641            canister_id: child,
1642        };
1643        let response = RootComponentChildAllocationResponse {
1644            operation_id: request.operation_id,
1645            component,
1646            parent_canister_id: parent,
1647            parent_role: CanisterRole::new("project_hub"),
1648            child_role: request.child_role.clone(),
1649            child_kind: ComponentChildKind::Instance,
1650            maximum_instances_per_parent: 10_000,
1651            maximum_descendants: 20_000,
1652            maximum_registry_bytes: 16_777_216,
1653            reserved_against_registry: registry,
1654            release_set: FleetSubnetRootReleaseSet {
1655                release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
1656                    [15; 32],
1657                )),
1658                manifest_digest: ReleaseSetDigest::from_bytes([16; 32]),
1659            },
1660            phase: RootComponentAllocationPhase::Verified,
1661            creation: Some(RootComponentCreationEvidence {
1662                wasm_store: Principal::from_slice(&[21; 29]),
1663                payload_hash: [22; 32],
1664                payload_size_bytes: 4_096,
1665                initial_cycles: Cycles::new(5_000_000_000_000),
1666                controller: root,
1667                canister: Some(child),
1668            }),
1669            installation: Some(RootComponentChildInstallEvidence {
1670                raw_module_hash: [23; 32],
1671                chunk_hashes: vec![vec![24; 32]],
1672                binding: child_binding.clone(),
1673            }),
1674        };
1675        let commit_response = RootComponentChildCommitResponse {
1676            allocation: response.clone(),
1677            registry: ComponentRegistryPartitionResponse {
1678                head: ComponentRegistryHead {
1679                    component,
1680                    revision: 3,
1681                    content_hash: [25; 32],
1682                },
1683                binding: child_binding.component.clone(),
1684                provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
1685                    caller: Principal::from_slice(&[26; 29]),
1686                },
1687                release_set: response.release_set,
1688                status: ComponentLifecycleStatus::Active,
1689                reserved_descendants: 0,
1690                committed_descendants: 1,
1691                encoded_bytes: 8_192,
1692            },
1693            directory: ComponentDirectoryHead {
1694                provenance: ComponentDirectoryProvenance {
1695                    component: child_binding.component.clone(),
1696                    source_fleet_subnet_root: root,
1697                    component_registry_revision: 3,
1698                    component_registry_content_hash: [25; 32],
1699                    synchronized_at_ns: 27,
1700                },
1701                descendant_count: 1,
1702            },
1703        };
1704        let runtime_authority = ComponentRuntimeDirectoryAuthority {
1705            fleet: FleetDirectorySnapshot {
1706                provenance: crate::dto::fleet_registry::FleetDirectoryProvenance {
1707                    registry: FleetRegistryVersion {
1708                        authority: fleet_registry_authority(),
1709                        revision: 4,
1710                        content_hash: [28; 32],
1711                    },
1712                    source_fleet_subnet_root: root,
1713                },
1714                fleet_subnet_roots: vec![
1715                    crate::dto::fleet_registry::FleetSubnetRootDirectoryEntry {
1716                        placement_subnet: commit_response.registry.binding.placement_subnet,
1717                        fleet_subnet_root: root,
1718                        status: crate::dto::fleet_registry::FleetSubnetRootStatus::Active,
1719                    },
1720                ],
1721            },
1722            component: commit_response.directory.clone(),
1723        };
1724        let activation = ComponentRuntimeActivationEvidence {
1725            directory_authority_hash: [29; 32],
1726            activated_at_ns: 30,
1727        };
1728        let directory_response = RootComponentChildDirectoryPreparationResponse {
1729            committed: commit_response.clone(),
1730            child: ComponentRuntimeStatusResponse {
1731                operation_id: request.operation_id,
1732                binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
1733                phase: ComponentRuntimePhase::DirectoryPrepared,
1734                authority: Some(runtime_authority.clone()),
1735                authority_hash: Some([31; 32]),
1736                activation: None,
1737            },
1738            owning_component: ComponentRuntimeDirectoryConvergenceEvidence {
1739                operation_id: [32; 32],
1740                binding: ManagedCanisterBinding::Component(child_binding.component.clone()),
1741                covered_authority: runtime_authority.clone(),
1742                covered_authority_hash: [31; 32],
1743                activation,
1744            },
1745            parent: None,
1746        };
1747        let activation_response = RootComponentChildRuntimeActivationResponse {
1748            committed: commit_response.clone(),
1749            child: ComponentRuntimeStatusResponse {
1750                operation_id: request.operation_id,
1751                binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
1752                phase: ComponentRuntimePhase::Active,
1753                authority: Some(runtime_authority.clone()),
1754                authority_hash: Some([31; 32]),
1755                activation: Some(ComponentRuntimeActivationEvidence {
1756                    directory_authority_hash: [31; 32],
1757                    activated_at_ns: 33,
1758                }),
1759            },
1760        };
1761        let active_directory = ComponentDirectoryHead {
1762            provenance: ComponentDirectoryProvenance {
1763                component: child_binding.component.clone(),
1764                source_fleet_subnet_root: root,
1765                component_registry_revision: 4,
1766                component_registry_content_hash: [34; 32],
1767                synchronized_at_ns: 35,
1768            },
1769            descendant_count: 1,
1770        };
1771        let active_authority = ComponentRuntimeDirectoryAuthority {
1772            fleet: runtime_authority.fleet,
1773            component: active_directory.clone(),
1774        };
1775        let membership_response = RootComponentChildMembershipActivationResponse {
1776            committed: commit_response.clone(),
1777            registry: ComponentRegistryPartitionResponse {
1778                head: ComponentRegistryHead {
1779                    component,
1780                    revision: 4,
1781                    content_hash: [34; 32],
1782                },
1783                binding: child_binding.component.clone(),
1784                provisioning_origin: commit_response.registry.provisioning_origin.clone(),
1785                release_set: commit_response.registry.release_set,
1786                status: ComponentLifecycleStatus::Active,
1787                reserved_descendants: 0,
1788                committed_descendants: 1,
1789                encoded_bytes: 8_256,
1790            },
1791            directory: active_directory,
1792            child: ComponentRuntimeStatusResponse {
1793                operation_id: request.operation_id,
1794                binding: ManagedCanisterBinding::ComponentChild(child_binding),
1795                phase: ComponentRuntimePhase::Active,
1796                authority: Some(active_authority),
1797                authority_hash: Some([36; 32]),
1798                activation: Some(ComponentRuntimeActivationEvidence {
1799                    directory_authority_hash: [31; 32],
1800                    activated_at_ns: 33,
1801                }),
1802            },
1803        };
1804
1805        let request_bytes = candid::encode_one(&request).expect("encode child reservation");
1806        let status_bytes =
1807            candid::encode_one(status_request).expect("encode child reservation status");
1808        let creation_bytes =
1809            candid::encode_one(creation_request).expect("encode child creation request");
1810        let install_bytes =
1811            candid::encode_one(install_request).expect("encode child install request");
1812        let response_bytes = candid::encode_one(&response).expect("encode child response");
1813        let commit_request_bytes =
1814            candid::encode_one(commit_request).expect("encode child commit request");
1815        let directory_request_bytes =
1816            candid::encode_one(directory_request).expect("encode child Directory request");
1817        let activation_request_bytes =
1818            candid::encode_one(activation_request).expect("encode child activation request");
1819        let membership_request_bytes =
1820            candid::encode_one(membership_request).expect("encode child membership request");
1821        let commit_response_bytes =
1822            candid::encode_one(&commit_response).expect("encode child commit response");
1823        let directory_response_bytes =
1824            candid::encode_one(&directory_response).expect("encode child Directory response");
1825        let activation_response_bytes =
1826            candid::encode_one(&activation_response).expect("encode child activation response");
1827        let membership_response_bytes =
1828            candid::encode_one(&membership_response).expect("encode child membership response");
1829
1830        assert_eq!(
1831            candid::decode_one::<RootComponentChildAllocationRequest>(&request_bytes)
1832                .expect("decode child reservation"),
1833            request
1834        );
1835        assert_eq!(
1836            candid::decode_one::<RootComponentChildAllocationStatusRequest>(&status_bytes)
1837                .expect("decode child reservation status"),
1838            status_request
1839        );
1840        assert_eq!(
1841            candid::decode_one::<RootComponentChildCreationRequest>(&creation_bytes)
1842                .expect("decode child creation request"),
1843            creation_request
1844        );
1845        assert_eq!(
1846            candid::decode_one::<RootComponentChildInstallRequest>(&install_bytes)
1847                .expect("decode child install request"),
1848            install_request
1849        );
1850        assert_eq!(
1851            candid::decode_one::<RootComponentChildAllocationResponse>(&response_bytes)
1852                .expect("decode child response"),
1853            response
1854        );
1855        assert_eq!(
1856            candid::decode_one::<RootComponentChildCommitRequest>(&commit_request_bytes)
1857                .expect("decode child commit request"),
1858            commit_request
1859        );
1860        assert_eq!(
1861            candid::decode_one::<RootComponentChildDirectoryPreparationRequest>(
1862                &directory_request_bytes
1863            )
1864            .expect("decode child Directory request"),
1865            directory_request
1866        );
1867        assert_eq!(
1868            candid::decode_one::<RootComponentChildCommitResponse>(&commit_response_bytes)
1869                .expect("decode child commit response"),
1870            commit_response
1871        );
1872        assert_eq!(
1873            candid::decode_one::<RootComponentChildDirectoryPreparationResponse>(
1874                &directory_response_bytes
1875            )
1876            .expect("decode child Directory response"),
1877            directory_response
1878        );
1879        assert_eq!(
1880            candid::decode_one::<RootComponentChildRuntimeActivationRequest>(
1881                &activation_request_bytes
1882            )
1883            .expect("decode child activation request"),
1884            activation_request
1885        );
1886        assert_eq!(
1887            candid::decode_one::<RootComponentChildRuntimeActivationResponse>(
1888                &activation_response_bytes
1889            )
1890            .expect("decode child activation response"),
1891            activation_response
1892        );
1893        assert_eq!(
1894            candid::decode_one::<RootComponentChildMembershipActivationRequest>(
1895                &membership_request_bytes
1896            )
1897            .expect("decode child membership request"),
1898            membership_request
1899        );
1900        assert_eq!(
1901            candid::decode_one::<RootComponentChildMembershipActivationResponse>(
1902                &membership_response_bytes
1903            )
1904            .expect("decode child membership response"),
1905            membership_response
1906        );
1907    }
1908
1909    #[test]
1910    fn component_install_request_round_trips_through_candid() {
1911        let request = RootComponentInstallRequest {
1912            operation_id: [10; 32],
1913        };
1914        let bytes = candid::encode_one(request).expect("encode install request");
1915
1916        assert_eq!(
1917            candid::decode_one::<RootComponentInstallRequest>(&bytes)
1918                .expect("decode install request"),
1919            request
1920        );
1921    }
1922
1923    #[test]
1924    fn component_commit_request_round_trips_through_candid() {
1925        let request = RootComponentCommitRequest {
1926            operation_id: [10; 32],
1927        };
1928        let bytes = candid::encode_one(request).expect("encode commit request");
1929
1930        assert_eq!(
1931            candid::decode_one::<RootComponentCommitRequest>(&bytes)
1932                .expect("decode commit request"),
1933            request
1934        );
1935    }
1936
1937    #[test]
1938    fn component_runtime_activation_requests_round_trip_through_candid() {
1939        let root_request = RootComponentRuntimeActivationRequest {
1940            operation_id: [22; 32],
1941        };
1942        let target_request = ComponentRuntimeActivationRequest {
1943            operation_id: root_request.operation_id,
1944            directory_authority_hash: [23; 32],
1945        };
1946        let membership_request = RootComponentMembershipActivationRequest {
1947            operation_id: root_request.operation_id,
1948        };
1949        let root_bytes = candid::encode_one(root_request).expect("encode root activation request");
1950        let target_bytes =
1951            candid::encode_one(target_request).expect("encode target activation request");
1952        let membership_bytes =
1953            candid::encode_one(membership_request).expect("encode membership activation request");
1954
1955        assert_eq!(
1956            candid::decode_one::<RootComponentRuntimeActivationRequest>(&root_bytes)
1957                .expect("decode root activation request"),
1958            root_request
1959        );
1960        assert_eq!(
1961            candid::decode_one::<ComponentRuntimeActivationRequest>(&target_bytes)
1962                .expect("decode target activation request"),
1963            target_request
1964        );
1965        assert_eq!(
1966            candid::decode_one::<RootComponentMembershipActivationRequest>(&membership_bytes)
1967                .expect("decode membership activation request"),
1968            membership_request
1969        );
1970    }
1971}