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::{FleetServiceMemberPurpose, schema::ComponentChildKind},
10    dto::{
11        component_deployment::ProtectedComponentDeployment,
12        fleet_registry::{FleetDirectorySnapshot, FleetRegistryVersion},
13        root_store::RootStoreBootstrapRequest,
14    },
15    ids::{
16        CanisterRole, ComponentBinding, ComponentChildBinding, ComponentGroupMemberPath,
17        ComponentGroupPlacementId, ComponentInstanceId, ComponentSpecId, ComponentTopologyDigest,
18        FleetServiceId, FleetSubnetRootReleaseSet, ManagedCanisterBinding,
19    },
20    role_contract::ProtocolProfileDigest,
21};
22use candid::{CandidType, Principal};
23use serde::{Deserialize, Serialize};
24
25///
26/// RootComponentRegistryPreparationRequest
27///
28/// Exact authority required before an empty root-local Component Registry may be prepared.
29///
30
31#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
32pub struct RootComponentRegistryPreparationRequest {
33    pub store_bootstrap: RootStoreBootstrapRequest,
34    pub expected_fleet_registry: FleetRegistryVersion,
35}
36
37///
38/// RootComponentInitialInventoryStatus
39///
40/// Durable initial Component inventory sealed for one Fleet Subnet Root activation.
41///
42
43#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
44pub struct RootComponentInitialInventoryStatus {
45    pub fleet_activation_operation_id: [u8; 32],
46    pub component_count: u32,
47    pub inventory_hash: [u8; 32],
48    pub sealed_at_ns: u64,
49    pub directories_converged: bool,
50    pub root_runtime_activated: bool,
51}
52
53///
54/// RootComponentRegistryStatusResponse
55///
56/// Compact durable Component Registry authority and current allocation counters.
57///
58
59#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
60pub struct RootComponentRegistryStatusResponse {
61    pub fleet_subnet_root: Principal,
62    pub prepared_against_registry: FleetRegistryVersion,
63    pub release_set: FleetSubnetRootReleaseSet,
64    pub component_topology_digest: ComponentTopologyDigest,
65    pub next_allocation_sequence: u64,
66    pub reserved_component_instances: u32,
67    pub committed_component_instances: u32,
68    pub managed_descendants: u32,
69    pub known_created_component_canisters: u32,
70    pub encoded_bytes: u64,
71    pub initial_inventory: Option<RootComponentInitialInventoryStatus>,
72}
73
74///
75/// RootComponentAllocationRequest
76///
77/// Controller command naming one idempotent top-level Component reservation intent.
78///
79
80#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
81pub struct RootComponentAllocationRequest {
82    pub operation_id: [u8; 32],
83    pub component_spec: ComponentSpecId,
84}
85
86///
87/// PeerComponentRequester
88///
89/// Caller-selected peer proof mode; authoritative requester facts are derived by the target root.
90///
91
92#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
93pub enum PeerComponentRequester {
94    SameRoot,
95    FleetService {
96        service: FleetServiceId,
97        expected_registry: Box<FleetRegistryVersion>,
98    },
99}
100
101///
102/// RootPeerComponentAllocationRequest
103///
104/// Component command naming one local or cross-root idempotent peer reservation intent.
105///
106
107#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
108pub struct RootPeerComponentAllocationRequest {
109    pub operation_id: [u8; 32],
110    pub component_spec: ComponentSpecId,
111    pub requester: PeerComponentRequester,
112}
113
114///
115/// RootComponentAllocationStatusRequest
116///
117/// Read-only lookup key for one durable top-level Component allocation operation.
118///
119
120#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
121pub struct RootComponentAllocationStatusRequest {
122    pub operation_id: [u8; 32],
123}
124
125///
126/// RootComponentChildAllocationRequest
127///
128/// Parent command naming one idempotent direct-child reservation intent.
129///
130
131#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
132pub struct RootComponentChildAllocationRequest {
133    pub operation_id: [u8; 32],
134    pub component: ComponentInstanceId,
135    pub expected_registry: ComponentRegistryHead,
136    pub child_role: CanisterRole,
137    pub application_init_args: Option<Vec<u8>>,
138}
139
140///
141/// RootComponentChildAllocationStatusRequest
142///
143/// Parent lookup key for one durable direct-child reservation.
144///
145
146#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
147pub struct RootComponentChildAllocationStatusRequest {
148    pub operation_id: [u8; 32],
149    pub component: ComponentInstanceId,
150}
151
152///
153/// RootComponentSubtreeRemovalRequest
154///
155/// Controller command durably fencing one registered child subtree.
156///
157
158#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
159pub struct RootComponentSubtreeRemovalRequest {
160    pub operation_id: [u8; 32],
161    pub component: ComponentInstanceId,
162    pub target_canister_id: Principal,
163    pub expected_registry: ComponentRegistryHead,
164}
165
166///
167/// RootComponentSubtreeRemovalAdvanceRequest
168///
169/// Controller command advancing bounded traversal from one observed durable step.
170///
171
172#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
173pub struct RootComponentSubtreeRemovalAdvanceRequest {
174    pub operation_id: [u8; 32],
175    pub component: ComponentInstanceId,
176    pub expected_traversal_steps: u32,
177}
178
179///
180/// RootComponentSubtreeRemovalStopPreparationRequest
181///
182/// Controller command freezing the exact selected leaf and root stop authority.
183///
184
185#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
186pub struct RootComponentSubtreeRemovalStopPreparationRequest {
187    pub operation_id: [u8; 32],
188    pub component: ComponentInstanceId,
189    pub expected_traversal_steps: u32,
190    pub expected_leaf_canister_id: Principal,
191    pub expected_leaf_parent_canister_id: Principal,
192}
193
194///
195/// RootComponentSubtreeRemovalStopRequest
196///
197/// Controller command reconciling and stopping one exactly prepared leaf.
198///
199
200#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
201pub struct RootComponentSubtreeRemovalStopRequest {
202    pub operation_id: [u8; 32],
203    pub component: ComponentInstanceId,
204    pub expected_traversal_steps: u32,
205    pub expected_leaf_canister_id: Principal,
206    pub expected_leaf_parent_canister_id: Principal,
207}
208
209///
210/// RootComponentSubtreeRemovalDeletePreparationRequest
211///
212/// Controller command freezing exact deletion authority from one stopped receipt.
213///
214
215#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
216pub struct RootComponentSubtreeRemovalDeletePreparationRequest {
217    pub operation_id: [u8; 32],
218    pub component: ComponentInstanceId,
219    pub expected_traversal_steps: u32,
220    pub expected_leaf_canister_id: Principal,
221    pub expected_leaf_parent_canister_id: Principal,
222}
223
224///
225/// RootComponentSubtreeRemovalDeleteRequest
226///
227/// Controller command reconciling and deleting one exactly prepared leaf.
228///
229
230#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
231pub struct RootComponentSubtreeRemovalDeleteRequest {
232    pub operation_id: [u8; 32],
233    pub component: ComponentInstanceId,
234    pub expected_traversal_steps: u32,
235    pub expected_leaf_canister_id: Principal,
236    pub expected_leaf_parent_canister_id: Principal,
237}
238
239///
240/// RootComponentSubtreeRemovalMembershipRemovalRequest
241///
242/// Controller command removing one independently deleted leaf from Registry membership.
243///
244
245#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
246pub struct RootComponentSubtreeRemovalMembershipRemovalRequest {
247    pub operation_id: [u8; 32],
248    pub component: ComponentInstanceId,
249    pub expected_traversal_steps: u32,
250    pub expected_leaf_canister_id: Principal,
251    pub expected_leaf_parent_canister_id: Principal,
252}
253
254///
255/// RootComponentSubtreeRemovalDirectorySynchronizationRequest
256///
257/// Controller command converging the post-removal Directory on surviving members.
258///
259
260#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
261pub struct RootComponentSubtreeRemovalDirectorySynchronizationRequest {
262    pub operation_id: [u8; 32],
263    pub component: ComponentInstanceId,
264    pub expected_traversal_steps: u32,
265    pub expected_leaf_canister_id: Principal,
266    pub expected_leaf_parent_canister_id: Principal,
267}
268
269///
270/// RootComponentSubtreeRemovalLeafFinalizationRequest
271///
272/// Controller command archiving one completed leaf and resuming its retained parent.
273///
274
275#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
276pub struct RootComponentSubtreeRemovalLeafFinalizationRequest {
277    pub operation_id: [u8; 32],
278    pub component: ComponentInstanceId,
279    pub expected_traversal_steps: u32,
280    pub expected_leaf_canister_id: Principal,
281    pub expected_leaf_parent_canister_id: Principal,
282}
283
284///
285/// RootComponentSubtreeRemovalStatusRequest
286///
287/// Controller lookup key for one durable child-subtree removal operation.
288///
289
290#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
291pub struct RootComponentSubtreeRemovalStatusRequest {
292    pub operation_id: [u8; 32],
293    pub component: ComponentInstanceId,
294}
295
296///
297/// RootComponentDrainingRequest
298///
299/// Controller command fencing one exact active Component against new mutation.
300///
301
302#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
303pub struct RootComponentDrainingRequest {
304    pub operation_id: [u8; 32],
305    pub component: ComponentInstanceId,
306    pub expected_registry: ComponentRegistryHead,
307}
308
309///
310/// RootComponentDrainingStatusRequest
311///
312/// Read-only lookup key for one durable Component-draining operation.
313///
314
315#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
316pub struct RootComponentDrainingStatusRequest {
317    pub operation_id: [u8; 32],
318    pub component: ComponentInstanceId,
319}
320
321///
322/// RootComponentQuiescenceRequest
323///
324/// Controller command converging and stopping one exact draining Component.
325///
326
327#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
328pub struct RootComponentQuiescenceRequest {
329    pub operation_id: [u8; 32],
330    pub component: ComponentInstanceId,
331    pub expected_registry: ComponentRegistryHead,
332}
333
334///
335/// RootComponentQuiescenceStatusRequest
336///
337/// Read-only lookup key for one draining Component's quiescence progress.
338///
339
340#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
341pub struct RootComponentQuiescenceStatusRequest {
342    pub operation_id: [u8; 32],
343    pub component: ComponentInstanceId,
344}
345
346///
347/// RootComponentDrainingAdvanceRequest
348///
349/// Controller command advancing at most one deterministic draining-removal phase.
350///
351
352#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
353pub struct RootComponentDrainingAdvanceRequest {
354    pub operation_id: [u8; 32],
355    pub component: ComponentInstanceId,
356}
357
358///
359/// RootComponentFinalInventoryRequest
360///
361/// Controller command freezing one exact empty draining Component inventory.
362///
363
364#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
365pub struct RootComponentFinalInventoryRequest {
366    pub operation_id: [u8; 32],
367    pub component: ComponentInstanceId,
368    pub expected_registry: ComponentRegistryHead,
369}
370
371///
372/// RootComponentDeletionRequest
373///
374/// Controller command reconciling one top-level deletion from frozen final inventory.
375///
376
377#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
378pub struct RootComponentDeletionRequest {
379    pub operation_id: [u8; 32],
380    pub component: ComponentInstanceId,
381    pub expected_inventory_hash: [u8; 32],
382}
383
384///
385/// RootComponentDeletionStatusRequest
386///
387/// Read-only lookup key for one durable top-level Component deletion.
388///
389
390#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
391pub struct RootComponentDeletionStatusRequest {
392    pub operation_id: [u8; 32],
393    pub component: ComponentInstanceId,
394}
395
396///
397/// RootComponentChildCreationRequest
398///
399/// Parent command continuing one already reserved direct-child operation.
400///
401
402#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
403pub struct RootComponentChildCreationRequest {
404    pub operation_id: [u8; 32],
405    pub component: ComponentInstanceId,
406}
407
408///
409/// RootComponentChildInstallRequest
410///
411/// Parent command installing and verifying one already created direct-child operation.
412///
413
414#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
415pub struct RootComponentChildInstallRequest {
416    pub operation_id: [u8; 32],
417    pub component: ComponentInstanceId,
418}
419
420///
421/// RootComponentChildCommitRequest
422///
423/// Parent command committing one already verified direct-child operation.
424///
425
426#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
427pub struct RootComponentChildCommitRequest {
428    pub operation_id: [u8; 32],
429    pub component: ComponentInstanceId,
430}
431
432///
433/// RootComponentChildDirectoryPreparationRequest
434///
435/// Parent command distributing one committed child's Directory and converging its affected members.
436///
437
438#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
439pub struct RootComponentChildDirectoryPreparationRequest {
440    pub operation_id: [u8; 32],
441    pub component: ComponentInstanceId,
442}
443
444///
445/// RootComponentChildRuntimeActivationRequest
446///
447/// Parent command activating one Directory-prepared direct-child runtime.
448///
449
450#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
451pub struct RootComponentChildRuntimeActivationRequest {
452    pub operation_id: [u8; 32],
453    pub component: ComponentInstanceId,
454}
455
456///
457/// RootComponentChildMembershipActivationRequest
458///
459/// Parent command activating one runtime-active direct child's Registry membership.
460///
461
462#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
463pub struct RootComponentChildMembershipActivationRequest {
464    pub operation_id: [u8; 32],
465    pub component: ComponentInstanceId,
466}
467
468///
469/// RootComponentCreationRequest
470///
471/// Controller command continuing one already reserved top-level Component operation.
472///
473
474#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
475pub struct RootComponentCreationRequest {
476    pub operation_id: [u8; 32],
477}
478
479///
480/// RootComponentInstallRequest
481///
482/// Controller command continuing one already created top-level Component operation.
483///
484
485#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
486pub struct RootComponentInstallRequest {
487    pub operation_id: [u8; 32],
488}
489
490///
491/// RootComponentCommitRequest
492///
493/// Controller command committing one already verified top-level Component operation.
494///
495
496#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
497pub struct RootComponentCommitRequest {
498    pub operation_id: [u8; 32],
499}
500
501///
502/// RootComponentDirectoryPreparationRequest
503///
504/// Controller command distributing exact Directories to one committed top-level Component.
505///
506
507#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
508pub struct RootComponentDirectoryPreparationRequest {
509    pub operation_id: [u8; 32],
510}
511
512///
513/// RootComponentRuntimeActivationRequest
514///
515/// Controller command activating one Directory-prepared top-level Component runtime.
516///
517
518#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
519pub struct RootComponentRuntimeActivationRequest {
520    pub operation_id: [u8; 32],
521}
522
523///
524/// RootComponentMembershipActivationRequest
525///
526/// Controller command activating one runtime-active Component's Registry membership.
527///
528
529#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
530pub struct RootComponentMembershipActivationRequest {
531    pub operation_id: [u8; 32],
532}
533
534///
535/// ComponentProvisioningOrigin
536///
537/// Authenticated causal authority retained with one top-level Component allocation.
538///
539
540#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
541pub enum ComponentProvisioningOrigin {
542    FleetAdministrator {
543        caller: Principal,
544    },
545    Component {
546        requester: Box<ComponentBinding>,
547        grant: Box<crate::config::ComponentProvisioningGrant>,
548    },
549    FleetServiceComponent {
550        requester: Box<FleetServiceComponentRequester>,
551        registry: Box<FleetRegistryVersion>,
552        grant: Box<crate::config::ComponentProvisioningGrant>,
553    },
554    ComponentGroup {
555        operation_id: [u8; 32],
556        plan_hash: [u8; 32],
557        group_placement: ComponentGroupPlacementId,
558        member_path: ComponentGroupMemberPath,
559    },
560}
561
562///
563/// FleetServiceComponentRequester
564///
565/// Registry-derived remote Fleet-service member identity retained by one target-root operation.
566///
567
568#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
569pub struct FleetServiceComponentRequester {
570    pub service: FleetServiceId,
571    pub member_purpose: FleetServiceMemberPurpose,
572    pub group_placement: ComponentGroupPlacementId,
573    pub member_path: ComponentGroupMemberPath,
574    pub component: ComponentBinding,
575}
576
577///
578/// RootComponentAllocationPhase
579///
580/// Durable root-local progress of one top-level Component allocation operation.
581///
582
583#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
584pub enum RootComponentAllocationPhase {
585    Reserved,
586    CreationIntent,
587    Created,
588    InstallIntent,
589    Installed,
590    Verified,
591    Committed,
592    Removed,
593}
594
595///
596/// RootComponentSubtreeRemovalPhase
597///
598/// Durable root-local progress of one child-subtree removal operation.
599///
600
601#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
602#[expect(
603    clippy::large_enum_variant,
604    reason = "wire phases retain complete inline receipts for deterministic Candid responses"
605)]
606pub enum RootComponentSubtreeRemovalPhase {
607    Fenced,
608    Traversing(RootComponentSubtreeRemovalNode),
609    LeafSelected(RootComponentSubtreeRemovalNode),
610    StopIntent(RootComponentSubtreeRemovalStopIntent),
611    Stopped(RootComponentSubtreeRemovalStoppedReceipt),
612    DeleteIntent(RootComponentSubtreeRemovalDeleteIntent),
613    Deleted(RootComponentSubtreeRemovalDeletedReceipt),
614    MembershipRemoved(RootComponentSubtreeRemovalMembershipRemovedReceipt),
615    DirectorySynchronized(RootComponentSubtreeRemovalDirectorySynchronizedReceipt),
616    Completed(RootComponentSubtreeRemovalCompletedReceipt),
617}
618
619///
620/// RootComponentSubtreeRemovalNode
621///
622/// Exact registered child selected as a traversal cursor or removable leaf.
623///
624
625#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
626pub struct RootComponentSubtreeRemovalNode {
627    pub canister_id: Principal,
628    pub parent_canister_id: Principal,
629    pub role: CanisterRole,
630    pub kind: ComponentChildKind,
631    pub installed_artifact_hash: [u8; 32],
632    pub status: ComponentLifecycleStatus,
633}
634
635///
636/// RootComponentSubtreeRemovalStopIntent
637///
638/// Exact registered leaf and sole root controller frozen before a stop call.
639///
640
641#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
642pub struct RootComponentSubtreeRemovalStopIntent {
643    pub leaf: RootComponentSubtreeRemovalNode,
644    pub controller: Principal,
645}
646
647///
648/// RootComponentSubtreeRemovalStoppedReceipt
649///
650/// Frozen stop authority plus the independently observed installed module.
651///
652
653#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
654pub struct RootComponentSubtreeRemovalStoppedReceipt {
655    pub stop: RootComponentSubtreeRemovalStopIntent,
656    pub observed_module_hash: [u8; 32],
657}
658
659///
660/// RootComponentSubtreeRemovalDeleteIntent
661///
662/// Exact stopped receipt frozen before the destructive management call.
663///
664
665#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
666pub struct RootComponentSubtreeRemovalDeleteIntent {
667    pub stopped: RootComponentSubtreeRemovalStoppedReceipt,
668}
669
670///
671/// RootComponentSubtreeRemovalDeletedReceipt
672///
673/// Frozen workload-deletion authority committed after the Canister is recycled.
674///
675
676#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
677pub struct RootComponentSubtreeRemovalDeletedReceipt {
678    pub deletion: RootComponentSubtreeRemovalDeleteIntent,
679}
680
681///
682/// RootComponentSubtreeRemovalMembershipRemovedReceipt
683///
684/// Exact Registry transition retained after the independently deleted leaf is unregistered.
685///
686
687#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
688pub struct RootComponentSubtreeRemovalMembershipRemovedReceipt {
689    pub deleted: RootComponentSubtreeRemovalDeletedReceipt,
690    pub removed_from_registry: ComponentRegistryHead,
691    pub previous_descendant_content_hash: [u8; 32],
692    pub previous_committed_descendants: u32,
693    pub registry: ComponentRegistryHead,
694    pub descendant_content_hash: [u8; 32],
695    pub registry_encoded_bytes: u64,
696    pub reserved_descendants: u32,
697    pub committed_descendants: u32,
698    pub directory_synchronized_at_ns: u64,
699    pub directory_authority_hash: [u8; 32],
700    pub parent_role_instances: u32,
701    pub root_managed_descendants: u32,
702    pub root_known_created_component_canisters: u32,
703}
704
705///
706/// RootComponentSubtreeRemovalDirectoryConvergenceEvidence
707///
708/// Compact durable proof that one surviving member covered the required Directory.
709///
710
711#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
712pub struct RootComponentSubtreeRemovalDirectoryConvergenceEvidence {
713    pub operation_id: [u8; 32],
714    pub canister_id: Principal,
715    pub activation: ComponentRuntimeActivationEvidence,
716}
717
718///
719/// RootComponentSubtreeRemovalDirectorySynchronizedReceipt
720///
721/// Membership removal plus independently verified surviving-member convergence.
722///
723/// The owner is absent only when its top-level Component is durably quiescent.
724///
725
726#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
727pub struct RootComponentSubtreeRemovalDirectorySynchronizedReceipt {
728    pub membership_removed: RootComponentSubtreeRemovalMembershipRemovedReceipt,
729    pub covered_fleet_registry_revision: u64,
730    pub covered_fleet_registry_content_hash: [u8; 32],
731    pub covered_component_registry: ComponentRegistryHead,
732    pub covered_authority_hash: [u8; 32],
733    pub owning_component: Option<RootComponentSubtreeRemovalDirectoryConvergenceEvidence>,
734    pub parent: Option<RootComponentSubtreeRemovalDirectoryConvergenceEvidence>,
735}
736
737///
738/// RootComponentSubtreeRemovalCompletedReceipt
739///
740/// Terminal Registry and Directory authority after the fenced target is finalized.
741///
742
743#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
744pub struct RootComponentSubtreeRemovalCompletedReceipt {
745    pub registry: ComponentRegistryHead,
746    pub directory_authority_hash: [u8; 32],
747}
748
749///
750/// ComponentLifecycleStatus
751///
752/// Root-owned runtime lifecycle state of one committed Component Registry member.
753///
754
755#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
756pub enum ComponentLifecycleStatus {
757    Prepared,
758    Active,
759    Draining,
760    Removed,
761}
762
763///
764/// ComponentRegistryHead
765///
766/// Exact independently versioned authority of one Component Registry partition.
767///
768
769#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
770pub struct ComponentRegistryHead {
771    pub component: ComponentInstanceId,
772    pub revision: u64,
773    pub content_hash: [u8; 32],
774}
775
776///
777/// ComponentRegistryPartitionRequest
778///
779/// Read-only lookup key for one committed Component Registry partition.
780///
781
782#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
783pub struct ComponentRegistryPartitionRequest {
784    pub component: ComponentInstanceId,
785}
786
787///
788/// ComponentRegistryPartitionResponse
789///
790/// Protected top-level row and independent head of one Component Registry partition.
791///
792
793#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
794pub struct ComponentRegistryPartitionResponse {
795    pub head: ComponentRegistryHead,
796    pub binding: ComponentBinding,
797    pub protocol_profile_digest: ProtocolProfileDigest,
798    pub provisioning_origin: ComponentProvisioningOrigin,
799    pub release_set: FleetSubnetRootReleaseSet,
800    pub status: ComponentLifecycleStatus,
801    pub reserved_descendants: u32,
802    pub committed_descendants: u32,
803    pub encoded_bytes: u64,
804}
805
806///
807/// ComponentDirectoryProvenance
808///
809/// Exact Component Registry authority from which one Component Directory is derived.
810///
811
812#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
813pub struct ComponentDirectoryProvenance {
814    pub component: ComponentBinding,
815    pub source_fleet_subnet_root: Principal,
816    pub component_registry_revision: u64,
817    pub component_registry_content_hash: [u8; 32],
818    pub synchronized_at_ns: u64,
819}
820
821///
822/// ComponentDirectoryHead
823///
824/// Compact independently versioned discovery projection for one Component tree.
825///
826
827#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
828pub struct ComponentDirectoryHead {
829    pub provenance: ComponentDirectoryProvenance,
830    pub descendant_count: u32,
831}
832
833///
834/// ComponentDirectoryHeadRequest
835///
836/// Read-only lookup key for one committed Component Directory head.
837///
838
839#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
840pub struct ComponentDirectoryHeadRequest {
841    pub component: ComponentInstanceId,
842}
843
844///
845/// ComponentDirectoryPageCursor
846///
847/// Opaque revision- and filter-bound continuation for one bounded Directory page.
848///
849
850#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
851pub struct ComponentDirectoryPageCursor(pub Vec<u8>);
852
853///
854/// ComponentDirectoryPageRequest
855///
856/// Bounded member query against one exact current Component Directory authority.
857///
858
859#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
860pub struct ComponentDirectoryPageRequest {
861    pub directory: ComponentDirectoryHead,
862    pub parent_canister_id: Option<Principal>,
863    pub role: Option<CanisterRole>,
864    pub status: Option<ComponentLifecycleStatus>,
865    pub cursor: Option<ComponentDirectoryPageCursor>,
866    pub limit: u16,
867}
868
869///
870/// ComponentDirectoryChildEntry
871///
872/// One authoritative normalized child projected with its complete protected binding.
873///
874
875#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
876pub struct ComponentDirectoryChildEntry {
877    pub binding: ComponentChildBinding,
878    pub kind: ComponentChildKind,
879    pub installed_artifact_hash: [u8; 32],
880    pub protocol_profile_digest: ProtocolProfileDigest,
881    pub status: ComponentLifecycleStatus,
882}
883
884///
885/// ComponentDirectoryPageResponse
886///
887/// One bounded caller-scoped page under the exact requested Directory head.
888///
889
890#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
891pub struct ComponentDirectoryPageResponse {
892    pub directory: ComponentDirectoryHead,
893    pub entries: Vec<ComponentDirectoryChildEntry>,
894    pub next_cursor: Option<ComponentDirectoryPageCursor>,
895}
896
897///
898/// ComponentRuntimeDirectoryAuthority
899///
900/// Exact Fleet and Component discovery authority retained by one managed Component-tree node.
901///
902
903#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
904pub struct ComponentRuntimeDirectoryAuthority {
905    pub fleet: FleetDirectorySnapshot,
906    pub component: ComponentDirectoryHead,
907    pub component_group: Option<crate::dto::component_provisioning::ComponentGroupDirectory>,
908}
909
910///
911/// ComponentRuntimeDirectChild
912///
913/// Exact active direct-child projection delivered with one Component Directory authority.
914///
915
916#[derive(CandidType, Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
917pub struct ComponentRuntimeDirectChild {
918    pub canister_id: Principal,
919    pub role: CanisterRole,
920    pub protocol_profile_digest: ProtocolProfileDigest,
921}
922
923///
924/// ComponentRuntimeDirectoryPreparationRequest
925///
926/// Root-issued exact Directory preparation command for one managed Component-tree node.
927///
928
929#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
930pub struct ComponentRuntimeDirectoryPreparationRequest {
931    pub operation_id: [u8; 32],
932    pub authority: ComponentRuntimeDirectoryAuthority,
933    pub direct_children: Vec<ComponentRuntimeDirectChild>,
934}
935
936///
937/// ComponentRuntimeDirectorySynchronizationRequest
938///
939/// Root-issued replacement of one active managed Component node's current Directory authority.
940///
941
942#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
943pub struct ComponentRuntimeDirectorySynchronizationRequest {
944    pub operation_id: [u8; 32],
945    pub authority: ComponentRuntimeDirectoryAuthority,
946    pub direct_children: Vec<ComponentRuntimeDirectChild>,
947}
948
949///
950/// ComponentRuntimePhase
951///
952/// Target-local progress from installation through Component runtime activation.
953///
954
955#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
956pub enum ComponentRuntimePhase {
957    AwaitingDirectory,
958    DirectoryPrepared,
959    Active,
960}
961
962///
963/// ComponentRuntimeActivationEvidence
964///
965/// Exact retained Directory authority under which one Component runtime became Active.
966///
967
968#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
969pub struct ComponentRuntimeActivationEvidence {
970    pub directory_authority_hash: [u8; 32],
971    pub activated_at_ns: u64,
972}
973
974///
975/// ComponentRuntimeActivationRequest
976///
977/// Root-issued exact activation command for one Directory-prepared managed Component node.
978///
979
980#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
981pub struct ComponentRuntimeActivationRequest {
982    pub operation_id: [u8; 32],
983    pub directory_authority_hash: [u8; 32],
984}
985
986///
987/// ComponentRuntimeStatusResponse
988///
989/// Independently observable target-local binding and exact retained Directory authority.
990///
991
992#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
993pub struct ComponentRuntimeStatusResponse {
994    pub operation_id: [u8; 32],
995    pub binding: ManagedCanisterBinding,
996    pub deployment: Box<ProtectedComponentDeployment>,
997    pub phase: ComponentRuntimePhase,
998    pub authority: Option<ComponentRuntimeDirectoryAuthority>,
999    pub authority_hash: Option<[u8; 32]>,
1000    pub direct_children_hash: Option<[u8; 32]>,
1001    pub activation: Option<ComponentRuntimeActivationEvidence>,
1002}
1003
1004///
1005/// ComponentRuntimeDirectoryConvergenceEvidence
1006///
1007/// Stable root evidence that one active member covered at least the required Directory authority.
1008///
1009
1010#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1011pub struct ComponentRuntimeDirectoryConvergenceEvidence {
1012    pub operation_id: [u8; 32],
1013    pub binding: ManagedCanisterBinding,
1014    pub covered_authority: ComponentRuntimeDirectoryAuthority,
1015    pub covered_authority_hash: [u8; 32],
1016    pub activation: ComponentRuntimeActivationEvidence,
1017}
1018
1019///
1020/// RootComponentCreationEvidence
1021///
1022/// Exact Store artifact and root-owned creation settings frozen before the paid effect.
1023///
1024
1025#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1026pub struct RootComponentCreationEvidence {
1027    pub wasm_store: Principal,
1028    pub payload_hash: [u8; 32],
1029    pub payload_size_bytes: u64,
1030    pub initial_cycles: Cycles,
1031    pub controller: Principal,
1032    pub canister: Option<Principal>,
1033}
1034
1035///
1036/// RootComponentInstallEvidence
1037///
1038/// Exact raw artifact, chunk source and immutable target binding frozen before installation.
1039///
1040
1041#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1042pub struct RootComponentInstallEvidence {
1043    pub raw_module_hash: [u8; 32],
1044    pub chunk_hashes: Vec<Vec<u8>>,
1045    pub binding: ComponentBinding,
1046}
1047
1048///
1049/// RootComponentChildInstallEvidence
1050///
1051/// Exact child module and immutable retained binding frozen before installation.
1052///
1053
1054#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1055pub struct RootComponentChildInstallEvidence {
1056    pub raw_module_hash: [u8; 32],
1057    pub chunk_hashes: Vec<Vec<u8>>,
1058    pub binding: ComponentChildBinding,
1059}
1060
1061///
1062/// RootComponentAllocationResponse
1063///
1064/// Durable identity reservation returned identically for exact operation retry.
1065///
1066
1067#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1068pub struct RootComponentAllocationResponse {
1069    pub operation_id: [u8; 32],
1070    pub allocation_sequence: u64,
1071    pub component: ComponentInstanceId,
1072    pub component_spec: ComponentSpecId,
1073    pub spec_hash: [u8; 32],
1074    pub role: CanisterRole,
1075    pub provisioning_origin: ComponentProvisioningOrigin,
1076    pub release_set: FleetSubnetRootReleaseSet,
1077    pub phase: RootComponentAllocationPhase,
1078    pub creation: Option<RootComponentCreationEvidence>,
1079    pub installation: Option<RootComponentInstallEvidence>,
1080}
1081
1082///
1083/// RootComponentChildAllocationResponse
1084///
1085/// Durable direct-child lifecycle progress returned identically for exact parent retry.
1086///
1087
1088#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1089pub struct RootComponentChildAllocationResponse {
1090    pub operation_id: [u8; 32],
1091    pub component: ComponentInstanceId,
1092    pub parent_canister_id: Principal,
1093    pub parent_role: CanisterRole,
1094    pub child_role: CanisterRole,
1095    pub child_kind: ComponentChildKind,
1096    pub maximum_instances_per_parent: u32,
1097    pub maximum_descendants: u32,
1098    pub maximum_registry_bytes: u64,
1099    pub reserved_against_registry: ComponentRegistryHead,
1100    pub release_set: FleetSubnetRootReleaseSet,
1101    pub phase: RootComponentAllocationPhase,
1102    pub creation: Option<RootComponentCreationEvidence>,
1103    pub installation: Option<RootComponentChildInstallEvidence>,
1104}
1105
1106///
1107/// RootComponentSubtreeRemovalResponse
1108///
1109/// Current durable snapshot of one monotonic subtree-removal operation.
1110///
1111
1112#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1113pub struct RootComponentSubtreeRemovalResponse {
1114    pub operation_id: [u8; 32],
1115    pub component: ComponentInstanceId,
1116    pub target_canister_id: Principal,
1117    pub target_parent_canister_id: Principal,
1118    pub target_role: CanisterRole,
1119    pub target_status: ComponentLifecycleStatus,
1120    pub reserved_against_registry: ComponentRegistryHead,
1121    pub maximum_completed_leaves: u32,
1122    pub completed_leaves: u32,
1123    pub traversal_steps: u32,
1124    pub phase: RootComponentSubtreeRemovalPhase,
1125}
1126
1127///
1128/// RootComponentDrainingResponse
1129///
1130/// Exact Registry and Directory authority produced by the durable draining fence.
1131///
1132
1133#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1134pub struct RootComponentDrainingResponse {
1135    pub operation_id: [u8; 32],
1136    pub component: ComponentInstanceId,
1137    pub previous_registry: ComponentRegistryHead,
1138    pub registry: ComponentRegistryHead,
1139    pub descendant_count: u32,
1140    pub descendant_content_hash: [u8; 32],
1141    pub directory_authority_hash: [u8; 32],
1142    pub started_at_ns: u64,
1143}
1144
1145///
1146/// RootComponentQuiescenceStopIntent
1147///
1148/// Exact runtime, Directory, module and controller authority frozen before stopping.
1149///
1150
1151#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1152pub struct RootComponentQuiescenceStopIntent {
1153    pub registry: ComponentRegistryHead,
1154    pub descendant_count: u32,
1155    pub descendant_content_hash: [u8; 32],
1156    pub canister_id: Principal,
1157    pub controller: Principal,
1158    pub expected_module_hash: [u8; 32],
1159    pub covered_fleet_registry_revision: u64,
1160    pub covered_fleet_registry_content_hash: [u8; 32],
1161    pub covered_authority_hash: [u8; 32],
1162    pub runtime_operation_id: [u8; 32],
1163    pub activation: ComponentRuntimeActivationEvidence,
1164    pub prepared_at_ns: u64,
1165}
1166
1167///
1168/// RootComponentQuiescentReceipt
1169///
1170/// Durable evidence that the exact prepared Component was independently observed stopped.
1171///
1172
1173#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1174pub struct RootComponentQuiescentReceipt {
1175    pub stop: RootComponentQuiescenceStopIntent,
1176    pub observed_module_hash: [u8; 32],
1177    pub quiesced_at_ns: u64,
1178}
1179
1180///
1181/// RootComponentQuiescencePhase
1182///
1183/// Monotonic progress from pre-effect stop authority to observed quiescence.
1184///
1185
1186#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1187pub enum RootComponentQuiescencePhase {
1188    StopIntent(RootComponentQuiescenceStopIntent),
1189    Quiescent(RootComponentQuiescentReceipt),
1190}
1191
1192///
1193/// RootComponentQuiescenceResponse
1194///
1195/// Current durable quiescence progress for one draining Component.
1196///
1197
1198#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1199pub struct RootComponentQuiescenceResponse {
1200    pub operation_id: [u8; 32],
1201    pub component: ComponentInstanceId,
1202    pub phase: RootComponentQuiescencePhase,
1203}
1204
1205///
1206/// RootComponentDrainingDescendantsEmpty
1207///
1208/// Exact current Registry proof that one draining Component has no descendants.
1209///
1210
1211#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1212pub struct RootComponentDrainingDescendantsEmpty {
1213    pub registry: ComponentRegistryHead,
1214    pub descendant_content_hash: [u8; 32],
1215}
1216
1217///
1218/// RootComponentDrainingAdvancePhase
1219///
1220/// One bounded driver result: current subtree progress or exact empty inventory.
1221///
1222
1223#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1224#[expect(
1225    clippy::large_enum_variant,
1226    reason = "wire result embeds the current durable subtree snapshot without a Rust-only indirection"
1227)]
1228pub enum RootComponentDrainingAdvancePhase {
1229    DescendantRemoval(RootComponentSubtreeRemovalResponse),
1230    DescendantsEmpty(RootComponentDrainingDescendantsEmpty),
1231}
1232
1233///
1234/// RootComponentDrainingAdvanceResponse
1235///
1236/// Current bounded progress of one terminally quiescent Component drain.
1237///
1238
1239#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1240pub struct RootComponentDrainingAdvanceResponse {
1241    pub operation_id: [u8; 32],
1242    pub component: ComponentInstanceId,
1243    pub phase: RootComponentDrainingAdvancePhase,
1244}
1245
1246///
1247/// RootComponentFinalInventory
1248///
1249/// Exact empty Component Registry and current Fleet Directory authority frozen before deletion.
1250///
1251
1252#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1253pub struct RootComponentFinalInventory {
1254    pub registry: ComponentRegistryHead,
1255    pub descendant_content_hash: [u8; 32],
1256    pub registry_encoded_bytes: u64,
1257    pub directory_synchronized_at_ns: u64,
1258    pub covered_fleet_registry_revision: u64,
1259    pub covered_fleet_registry_content_hash: [u8; 32],
1260    pub directory_authority_hash: [u8; 32],
1261    pub inventory_hash: [u8; 32],
1262    pub finalized_at_ns: u64,
1263}
1264
1265///
1266/// RootComponentFinalInventoryResponse
1267///
1268/// Response-idempotent receipt for one finalized empty Component inventory.
1269///
1270
1271#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1272pub struct RootComponentFinalInventoryResponse {
1273    pub operation_id: [u8; 32],
1274    pub component: ComponentInstanceId,
1275    pub inventory: RootComponentFinalInventory,
1276}
1277
1278///
1279/// RootComponentDeletionIntent
1280///
1281/// Complete final-inventory and quiescence authority frozen before top-level deletion.
1282///
1283
1284#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1285pub struct RootComponentDeletionIntent {
1286    pub final_inventory: RootComponentFinalInventory,
1287    pub quiescence: RootComponentQuiescentReceipt,
1288    pub prepared_at_ns: u64,
1289}
1290
1291///
1292/// RootComponentDeletedReceipt
1293///
1294/// Terminal authority retained after the top-level workload Canister is recycled.
1295///
1296
1297#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1298pub struct RootComponentDeletedReceipt {
1299    pub deletion: RootComponentDeletionIntent,
1300    pub deleted_at_ns: u64,
1301}
1302
1303///
1304/// RootComponentMembershipRemovedReceipt
1305///
1306/// Terminal local-membership removal and settled root/Spec accounting authority.
1307///
1308
1309#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1310pub struct RootComponentMembershipRemovedReceipt {
1311    pub deleted: RootComponentDeletedReceipt,
1312    pub allocation_operation_id: [u8; 32],
1313    pub remaining_spec_committed_instances: u32,
1314    pub root_committed_component_instances: u32,
1315    pub root_known_created_component_canisters: u32,
1316    pub root_registry_encoded_bytes: u64,
1317    pub removed_at_ns: u64,
1318    pub removal_hash: [u8; 32],
1319}
1320
1321///
1322/// RootComponentDeletionPhase
1323///
1324/// Monotonic top-level deletion progress through terminal local-membership removal.
1325///
1326
1327#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1328pub enum RootComponentDeletionPhase {
1329    DeleteIntent(RootComponentDeletionIntent),
1330    Deleted(RootComponentDeletedReceipt),
1331    MembershipRemoved(RootComponentMembershipRemovedReceipt),
1332}
1333
1334///
1335/// RootComponentDeletionResponse
1336///
1337/// Current durable deletion progress for one finalized top-level Component.
1338///
1339
1340#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1341pub struct RootComponentDeletionResponse {
1342    pub operation_id: [u8; 32],
1343    pub component: ComponentInstanceId,
1344    pub phase: RootComponentDeletionPhase,
1345}
1346
1347///
1348/// RootComponentChildCommitResponse
1349///
1350/// Exact committed child operation, authoritative Component Registry and next Directory head.
1351///
1352
1353#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1354pub struct RootComponentChildCommitResponse {
1355    pub allocation: RootComponentChildAllocationResponse,
1356    pub registry: ComponentRegistryPartitionResponse,
1357    pub directory: ComponentDirectoryHead,
1358}
1359
1360///
1361/// RootComponentChildDirectoryPreparationResponse
1362///
1363/// Exact child preparation plus stable bounded active-member Directory coverage.
1364///
1365
1366#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1367pub struct RootComponentChildDirectoryPreparationResponse {
1368    pub committed: RootComponentChildCommitResponse,
1369    pub child: ComponentRuntimeStatusResponse,
1370    pub owning_component: ComponentRuntimeDirectoryConvergenceEvidence,
1371    pub parent: Option<ComponentRuntimeDirectoryConvergenceEvidence>,
1372}
1373
1374///
1375/// RootComponentChildRuntimeActivationResponse
1376///
1377/// Exact child commitment plus independently observed Directory-bound runtime activation.
1378///
1379
1380#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1381pub struct RootComponentChildRuntimeActivationResponse {
1382    pub committed: RootComponentChildCommitResponse,
1383    pub child: ComponentRuntimeStatusResponse,
1384}
1385
1386///
1387/// RootComponentChildMembershipActivationResponse
1388///
1389/// Original child commitment plus active Registry, Directory and target convergence evidence.
1390///
1391
1392#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1393pub struct RootComponentChildMembershipActivationResponse {
1394    pub committed: RootComponentChildCommitResponse,
1395    pub registry: ComponentRegistryPartitionResponse,
1396    pub directory: ComponentDirectoryHead,
1397    pub child: ComponentRuntimeStatusResponse,
1398}
1399
1400///
1401/// RootComponentCommitResponse
1402///
1403/// Exact committed allocation, authoritative Registry row and derived Directory head.
1404///
1405
1406#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1407pub struct RootComponentCommitResponse {
1408    pub allocation: RootComponentAllocationResponse,
1409    pub registry: ComponentRegistryPartitionResponse,
1410    pub directory: ComponentDirectoryHead,
1411}
1412
1413///
1414/// RootComponentDirectoryPreparationResponse
1415///
1416/// Exact root authority plus independently observed target-local Directory preparation.
1417///
1418
1419#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1420pub struct RootComponentDirectoryPreparationResponse {
1421    pub committed: RootComponentCommitResponse,
1422    pub target: ComponentRuntimeStatusResponse,
1423}
1424
1425///
1426/// RootComponentRuntimeActivationResponse
1427///
1428/// Exact root authority plus independently observed target-local runtime activation.
1429///
1430
1431#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1432pub struct RootComponentRuntimeActivationResponse {
1433    pub committed: RootComponentCommitResponse,
1434    pub target: ComponentRuntimeStatusResponse,
1435}
1436
1437///
1438/// RootComponentMembershipActivationResponse
1439///
1440/// Exact active Registry authority plus independently observed current target Directory.
1441///
1442
1443#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1444pub struct RootComponentMembershipActivationResponse {
1445    pub allocation: RootComponentAllocationResponse,
1446    pub registry: ComponentRegistryPartitionResponse,
1447    pub directory: ComponentDirectoryHead,
1448    pub target: ComponentRuntimeStatusResponse,
1449}
1450
1451#[cfg(test)]
1452mod tests {
1453    use super::*;
1454    use crate::{
1455        dto::root_store::RootStoreBootstrapRequest,
1456        ids::{
1457            AppId, CanonicalNetworkId, FleetCoordinatorBinding, FleetId, FleetKey,
1458            FleetRegistryAuthority, ReleaseBuildId, ReleaseBuildNonce, ReleaseSetDigest, SubnetId,
1459        },
1460    };
1461
1462    #[test]
1463    fn component_registry_contracts_round_trip_through_candid() {
1464        let request = RootComponentRegistryPreparationRequest {
1465            store_bootstrap: RootStoreBootstrapRequest {
1466                operation_id: [8; 32],
1467                manifest_payload_size_bytes: 128,
1468            },
1469            expected_fleet_registry: FleetRegistryVersion {
1470                authority: fleet_registry_authority(),
1471                revision: 4,
1472                content_hash: [5; 32],
1473            },
1474        };
1475        let response = RootComponentRegistryStatusResponse {
1476            fleet_subnet_root: Principal::from_slice(&[6; 29]),
1477            prepared_against_registry: request.expected_fleet_registry.clone(),
1478            release_set: FleetSubnetRootReleaseSet {
1479                release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
1480                    [7; 32],
1481                )),
1482                manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
1483            },
1484            component_topology_digest: ComponentTopologyDigest::from_bytes([9; 32]),
1485            next_allocation_sequence: 1,
1486            reserved_component_instances: 0,
1487            committed_component_instances: 0,
1488            managed_descendants: 0,
1489            known_created_component_canisters: 0,
1490            encoded_bytes: 0,
1491            initial_inventory: Some(RootComponentInitialInventoryStatus {
1492                fleet_activation_operation_id: [10; 32],
1493                component_count: 0,
1494                inventory_hash: [11; 32],
1495                sealed_at_ns: 12,
1496                directories_converged: true,
1497                root_runtime_activated: true,
1498            }),
1499        };
1500        let allocation = RootComponentAllocationResponse {
1501            operation_id: [10; 32],
1502            allocation_sequence: 1,
1503            component: ComponentInstanceId::from_generated_bytes([11; 32]),
1504            component_spec: "projects".parse().expect("Component Spec ID"),
1505            spec_hash: [12; 32],
1506            role: CanisterRole::new("project_hub"),
1507            provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
1508                caller: Principal::from_slice(&[13; 29]),
1509            },
1510            release_set: response.release_set,
1511            phase: RootComponentAllocationPhase::Reserved,
1512            creation: None,
1513            installation: None,
1514        };
1515        let created = RootComponentAllocationResponse {
1516            phase: RootComponentAllocationPhase::Created,
1517            creation: Some(RootComponentCreationEvidence {
1518                wasm_store: Principal::from_slice(&[14; 29]),
1519                payload_hash: [15; 32],
1520                payload_size_bytes: 4_096,
1521                initial_cycles: Cycles::new(5_000_000_000_000),
1522                controller: Principal::from_slice(&[6; 29]),
1523                canister: Some(Principal::from_slice(&[16; 29])),
1524            }),
1525            installation: None,
1526            ..allocation.clone()
1527        };
1528        let request_bytes = candid::encode_one(&request).expect("encode request");
1529        let response_bytes = candid::encode_one(&response).expect("encode response");
1530        let allocation_bytes = candid::encode_one(&allocation).expect("encode allocation");
1531        let created_bytes = candid::encode_one(&created).expect("encode created allocation");
1532
1533        assert_eq!(
1534            candid::decode_one::<RootComponentRegistryPreparationRequest>(&request_bytes)
1535                .expect("decode request"),
1536            request
1537        );
1538        assert_eq!(
1539            candid::decode_one::<RootComponentRegistryStatusResponse>(&response_bytes)
1540                .expect("decode response"),
1541            response
1542        );
1543        assert_eq!(
1544            candid::decode_one::<RootComponentAllocationResponse>(&allocation_bytes)
1545                .expect("decode allocation"),
1546            allocation
1547        );
1548        assert_eq!(
1549            candid::decode_one::<RootComponentAllocationResponse>(&created_bytes)
1550                .expect("decode created allocation"),
1551            created
1552        );
1553    }
1554
1555    #[test]
1556    fn component_commit_response_round_trips_through_candid() {
1557        let root = Principal::from_slice(&[6; 29]);
1558        let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1559        let component_spec: ComponentSpecId = "projects".parse().expect("Component Spec ID");
1560        let release_set = FleetSubnetRootReleaseSet {
1561            release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
1562                [7; 32],
1563            )),
1564            manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
1565        };
1566        let provisioning_origin = ComponentProvisioningOrigin::FleetAdministrator {
1567            caller: Principal::from_slice(&[13; 29]),
1568        };
1569        let binding = ComponentBinding {
1570            authority: fleet_registry_authority(),
1571            component,
1572            component_spec: component_spec.clone(),
1573            spec_hash: [12; 32],
1574            role: CanisterRole::new("project_hub"),
1575            placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
1576            fleet_subnet_root: root,
1577            canister_id: Principal::from_slice(&[16; 29]),
1578        };
1579        let head = ComponentRegistryHead {
1580            component,
1581            revision: 1,
1582            content_hash: [18; 32],
1583        };
1584        let committed = RootComponentCommitResponse {
1585            allocation: RootComponentAllocationResponse {
1586                operation_id: [10; 32],
1587                allocation_sequence: 1,
1588                component,
1589                component_spec,
1590                spec_hash: binding.spec_hash,
1591                role: binding.role.clone(),
1592                provisioning_origin: provisioning_origin.clone(),
1593                release_set,
1594                phase: RootComponentAllocationPhase::Committed,
1595                creation: Some(RootComponentCreationEvidence {
1596                    wasm_store: Principal::from_slice(&[14; 29]),
1597                    payload_hash: [15; 32],
1598                    payload_size_bytes: 4_096,
1599                    initial_cycles: Cycles::new(5_000_000_000_000),
1600                    controller: root,
1601                    canister: Some(binding.canister_id),
1602                }),
1603                installation: Some(RootComponentInstallEvidence {
1604                    raw_module_hash: [20; 32],
1605                    chunk_hashes: vec![vec![21; 32]],
1606                    binding: binding.clone(),
1607                }),
1608            },
1609            registry: ComponentRegistryPartitionResponse {
1610                head: head.clone(),
1611                binding: binding.clone(),
1612                protocol_profile_digest: ProtocolProfileDigest::from_bytes([24; 32]),
1613                provisioning_origin,
1614                release_set,
1615                status: ComponentLifecycleStatus::Prepared,
1616                reserved_descendants: 0,
1617                committed_descendants: 0,
1618                encoded_bytes: 2_048,
1619            },
1620            directory: ComponentDirectoryHead {
1621                provenance: ComponentDirectoryProvenance {
1622                    component: binding,
1623                    source_fleet_subnet_root: root,
1624                    component_registry_revision: head.revision,
1625                    component_registry_content_hash: head.content_hash,
1626                    synchronized_at_ns: 19,
1627                },
1628                descendant_count: 0,
1629            },
1630        };
1631        let committed_bytes = candid::encode_one(&committed).expect("encode committed allocation");
1632
1633        assert_eq!(
1634            candid::decode_one::<RootComponentCommitResponse>(&committed_bytes)
1635                .expect("decode committed allocation"),
1636            committed
1637        );
1638    }
1639
1640    #[test]
1641    fn component_directory_page_contracts_round_trip_through_candid() {
1642        let root = Principal::from_slice(&[6; 29]);
1643        let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1644        let binding = ComponentBinding {
1645            authority: fleet_registry_authority(),
1646            component,
1647            component_spec: "projects".parse().expect("Component Spec ID"),
1648            spec_hash: [12; 32],
1649            role: CanisterRole::new("project_hub"),
1650            placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
1651            fleet_subnet_root: root,
1652            canister_id: Principal::from_slice(&[16; 29]),
1653        };
1654        let directory = ComponentDirectoryHead {
1655            provenance: ComponentDirectoryProvenance {
1656                component: binding.clone(),
1657                source_fleet_subnet_root: root,
1658                component_registry_revision: 3,
1659                component_registry_content_hash: [18; 32],
1660                synchronized_at_ns: 19,
1661            },
1662            descendant_count: 1,
1663        };
1664        let request = ComponentDirectoryPageRequest {
1665            directory: directory.clone(),
1666            parent_canister_id: Some(binding.canister_id),
1667            role: Some(CanisterRole::new("project_instance")),
1668            status: Some(ComponentLifecycleStatus::Active),
1669            cursor: Some(ComponentDirectoryPageCursor(vec![20; 64])),
1670            limit: 50,
1671        };
1672        let response = ComponentDirectoryPageResponse {
1673            directory,
1674            entries: vec![ComponentDirectoryChildEntry {
1675                binding: ComponentChildBinding {
1676                    component: binding.clone(),
1677                    parent_canister_id: binding.canister_id,
1678                    role: CanisterRole::new("project_instance"),
1679                    canister_id: Principal::from_slice(&[21; 29]),
1680                },
1681                kind: ComponentChildKind::Instance,
1682                installed_artifact_hash: [22; 32],
1683                protocol_profile_digest: ProtocolProfileDigest::from_bytes([24; 32]),
1684                status: ComponentLifecycleStatus::Active,
1685            }],
1686            next_cursor: Some(ComponentDirectoryPageCursor(vec![23; 64])),
1687        };
1688        let request_bytes = candid::encode_one(&request).expect("encode Directory page request");
1689        let response_bytes = candid::encode_one(&response).expect("encode Directory page response");
1690
1691        assert_eq!(
1692            candid::decode_one::<ComponentDirectoryPageRequest>(&request_bytes)
1693                .expect("decode Directory page request"),
1694            request
1695        );
1696        assert_eq!(
1697            candid::decode_one::<ComponentDirectoryPageResponse>(&response_bytes)
1698                .expect("decode Directory page response"),
1699            response
1700        );
1701    }
1702
1703    fn fleet_registry_authority() -> FleetRegistryAuthority {
1704        FleetRegistryAuthority {
1705            binding: FleetCoordinatorBinding {
1706                fleet: crate::ids::FleetBinding {
1707                    fleet: FleetKey {
1708                        canonical_network_id: CanonicalNetworkId::ic_mainnet(),
1709                        fleet_id: FleetId::from_generated_bytes([1; 32]),
1710                    },
1711                    app: AppId::from("toko"),
1712                },
1713                coordinator_subnet: SubnetId::from_principal(Principal::from_slice(&[2; 29])),
1714                coordinator: Principal::from_slice(&[3; 29]),
1715            },
1716            epoch: 1,
1717        }
1718    }
1719
1720    #[test]
1721    fn component_creation_request_round_trips_through_candid() {
1722        let request = RootComponentCreationRequest {
1723            operation_id: [10; 32],
1724        };
1725        let bytes = candid::encode_one(request).expect("encode creation request");
1726
1727        assert_eq!(
1728            candid::decode_one::<RootComponentCreationRequest>(&bytes)
1729                .expect("decode creation request"),
1730            request
1731        );
1732    }
1733
1734    #[test]
1735    fn peer_component_provisioning_origin_round_trips_through_candid() {
1736        let authority = fleet_registry_authority();
1737        let requester_spec: ComponentSpecId =
1738            "projects".parse().expect("requester Component Spec ID");
1739        let target_spec: ComponentSpecId = "users".parse().expect("target Component Spec ID");
1740        let origin = ComponentProvisioningOrigin::Component {
1741            requester: Box::new(ComponentBinding {
1742                authority,
1743                component: ComponentInstanceId::from_generated_bytes([20; 32]),
1744                component_spec: requester_spec.clone(),
1745                spec_hash: [21; 32],
1746                role: CanisterRole::new("project_hub"),
1747                placement_subnet: SubnetId::from_principal(Principal::from_slice(&[22; 29])),
1748                fleet_subnet_root: Principal::from_slice(&[23; 29]),
1749                canister_id: Principal::from_slice(&[24; 29]),
1750            }),
1751            grant: Box::new(crate::config::ComponentProvisioningGrant {
1752                requester_component_spec: requester_spec,
1753                target_component_spec: target_spec,
1754                maximum_instances_per_requester_per_root: 3,
1755            }),
1756        };
1757        let bytes = candid::encode_one(&origin).expect("encode peer provisioning origin");
1758
1759        assert_eq!(
1760            candid::decode_one::<ComponentProvisioningOrigin>(&bytes)
1761                .expect("decode peer provisioning origin"),
1762            origin
1763        );
1764    }
1765
1766    #[test]
1767    fn cross_root_peer_request_and_origin_round_trip_through_candid() {
1768        let authority = fleet_registry_authority();
1769        let requester_spec: ComponentSpecId =
1770            "projects".parse().expect("requester Component Spec ID");
1771        let target_spec: ComponentSpecId = "users".parse().expect("target Component Spec ID");
1772        let registry = FleetRegistryVersion {
1773            authority: authority.clone(),
1774            revision: 7,
1775            content_hash: [30; 32],
1776        };
1777        let request = RootPeerComponentAllocationRequest {
1778            operation_id: [31; 32],
1779            component_spec: target_spec.clone(),
1780            requester: PeerComponentRequester::FleetService {
1781                service: "projects".parse().expect("Fleet service ID"),
1782                expected_registry: Box::new(registry.clone()),
1783            },
1784        };
1785        let origin = ComponentProvisioningOrigin::FleetServiceComponent {
1786            requester: Box::new(FleetServiceComponentRequester {
1787                service: "projects".parse().expect("Fleet service ID"),
1788                member_purpose: FleetServiceMemberPurpose::PoolMember,
1789                group_placement: ComponentGroupPlacementId {
1790                    deployment: "project_hubs".parse().expect("deployment ID"),
1791                    ordinal: 2,
1792                },
1793                member_path: ComponentGroupMemberPath::try_from(vec![
1794                    "hub".parse().expect("member ID"),
1795                ])
1796                .expect("member path"),
1797                component: ComponentBinding {
1798                    authority,
1799                    component: ComponentInstanceId::from_generated_bytes([32; 32]),
1800                    component_spec: requester_spec.clone(),
1801                    spec_hash: [33; 32],
1802                    role: CanisterRole::new("project_hub"),
1803                    placement_subnet: SubnetId::from_principal(Principal::from_slice(&[34; 29])),
1804                    fleet_subnet_root: Principal::from_slice(&[35; 29]),
1805                    canister_id: Principal::from_slice(&[36; 29]),
1806                },
1807            }),
1808            registry: Box::new(registry),
1809            grant: Box::new(crate::config::ComponentProvisioningGrant {
1810                requester_component_spec: requester_spec,
1811                target_component_spec: target_spec,
1812                maximum_instances_per_requester_per_root: 3,
1813            }),
1814        };
1815        let request_bytes = candid::encode_one(&request).expect("encode cross-root request");
1816        let origin_bytes = candid::encode_one(&origin).expect("encode cross-root origin");
1817
1818        assert_eq!(
1819            candid::decode_one::<RootPeerComponentAllocationRequest>(&request_bytes)
1820                .expect("decode cross-root request"),
1821            request
1822        );
1823        assert_eq!(
1824            candid::decode_one::<ComponentProvisioningOrigin>(&origin_bytes)
1825                .expect("decode cross-root origin"),
1826            origin
1827        );
1828    }
1829
1830    #[test]
1831    fn component_group_provisioning_origin_round_trips_through_candid() {
1832        let origin = ComponentProvisioningOrigin::ComponentGroup {
1833            operation_id: [25; 32],
1834            plan_hash: [26; 32],
1835            group_placement: ComponentGroupPlacementId {
1836                deployment: "cells".parse().expect("deployment ID"),
1837                ordinal: 3,
1838            },
1839            member_path: ComponentGroupMemberPath::try_from(vec![
1840                "api".parse().expect("member ID"),
1841            ])
1842            .expect("member path"),
1843        };
1844        let bytes = candid::encode_one(&origin).expect("encode group provisioning origin");
1845
1846        assert_eq!(
1847            candid::decode_one::<ComponentProvisioningOrigin>(&bytes)
1848                .expect("decode group provisioning origin"),
1849            origin
1850        );
1851    }
1852
1853    #[test]
1854    #[expect(
1855        clippy::too_many_lines,
1856        reason = "one Candid contract test covers every subtree-removal phase receipt"
1857    )]
1858    fn component_subtree_removal_contracts_round_trip_through_candid() {
1859        let component = ComponentInstanceId::from_generated_bytes([41; 32]);
1860        let registry = ComponentRegistryHead {
1861            component,
1862            revision: 7,
1863            content_hash: [42; 32],
1864        };
1865        let request = RootComponentSubtreeRemovalRequest {
1866            operation_id: [43; 32],
1867            component,
1868            target_canister_id: Principal::from_slice(&[44; 29]),
1869            expected_registry: registry.clone(),
1870        };
1871        let status_request = RootComponentSubtreeRemovalStatusRequest {
1872            operation_id: request.operation_id,
1873            component,
1874        };
1875        let advance_request = RootComponentSubtreeRemovalAdvanceRequest {
1876            operation_id: request.operation_id,
1877            component,
1878            expected_traversal_steps: 1,
1879        };
1880        let stop_request = RootComponentSubtreeRemovalStopPreparationRequest {
1881            operation_id: request.operation_id,
1882            component,
1883            expected_traversal_steps: 2,
1884            expected_leaf_canister_id: Principal::from_slice(&[46; 29]),
1885            expected_leaf_parent_canister_id: request.target_canister_id,
1886        };
1887        let stopped = RootComponentSubtreeRemovalStoppedReceipt {
1888            observed_module_hash: [49; 32],
1889            stop: RootComponentSubtreeRemovalStopIntent {
1890                controller: Principal::from_slice(&[48; 29]),
1891                leaf: RootComponentSubtreeRemovalNode {
1892                    canister_id: Principal::from_slice(&[46; 29]),
1893                    parent_canister_id: request.target_canister_id,
1894                    role: CanisterRole::new("project_ledger"),
1895                    kind: ComponentChildKind::Singleton,
1896                    installed_artifact_hash: [47; 32],
1897                    status: ComponentLifecycleStatus::Active,
1898                },
1899            },
1900        };
1901        let response = RootComponentSubtreeRemovalResponse {
1902            operation_id: request.operation_id,
1903            component,
1904            target_canister_id: request.target_canister_id,
1905            target_parent_canister_id: Principal::from_slice(&[45; 29]),
1906            target_role: CanisterRole::new("project_instance"),
1907            target_status: ComponentLifecycleStatus::Active,
1908            reserved_against_registry: registry,
1909            maximum_completed_leaves: 4,
1910            completed_leaves: 1,
1911            traversal_steps: 2,
1912            phase: RootComponentSubtreeRemovalPhase::DirectorySynchronized(
1913                RootComponentSubtreeRemovalDirectorySynchronizedReceipt {
1914                    membership_removed: RootComponentSubtreeRemovalMembershipRemovedReceipt {
1915                        deleted: RootComponentSubtreeRemovalDeletedReceipt {
1916                            deletion: RootComponentSubtreeRemovalDeleteIntent { stopped },
1917                        },
1918                        removed_from_registry: ComponentRegistryHead {
1919                            component,
1920                            revision: 8,
1921                            content_hash: [50; 32],
1922                        },
1923                        previous_descendant_content_hash: [51; 32],
1924                        previous_committed_descendants: 4,
1925                        registry: ComponentRegistryHead {
1926                            component,
1927                            revision: 9,
1928                            content_hash: [52; 32],
1929                        },
1930                        descendant_content_hash: [53; 32],
1931                        registry_encoded_bytes: 4_096,
1932                        reserved_descendants: 1,
1933                        committed_descendants: 3,
1934                        directory_synchronized_at_ns: 54,
1935                        directory_authority_hash: [55; 32],
1936                        parent_role_instances: 0,
1937                        root_managed_descendants: 4,
1938                        root_known_created_component_canisters: 4,
1939                    },
1940                    covered_fleet_registry_revision: 6,
1941                    covered_fleet_registry_content_hash: [56; 32],
1942                    covered_component_registry: ComponentRegistryHead {
1943                        component,
1944                        revision: 9,
1945                        content_hash: [52; 32],
1946                    },
1947                    covered_authority_hash: [55; 32],
1948                    owning_component: Some(
1949                        RootComponentSubtreeRemovalDirectoryConvergenceEvidence {
1950                            operation_id: [57; 32],
1951                            canister_id: Principal::from_slice(&[58; 29]),
1952                            activation: ComponentRuntimeActivationEvidence {
1953                                directory_authority_hash: [59; 32],
1954                                activated_at_ns: 60,
1955                            },
1956                        },
1957                    ),
1958                    parent: Some(RootComponentSubtreeRemovalDirectoryConvergenceEvidence {
1959                        operation_id: [61; 32],
1960                        canister_id: request.target_canister_id,
1961                        activation: ComponentRuntimeActivationEvidence {
1962                            directory_authority_hash: [62; 32],
1963                            activated_at_ns: 63,
1964                        },
1965                    }),
1966                },
1967            ),
1968        };
1969
1970        let request_bytes = candid::encode_one(&request).expect("encode subtree removal request");
1971        let advance_bytes =
1972            candid::encode_one(advance_request).expect("encode subtree removal advance request");
1973        let stop_bytes =
1974            candid::encode_one(stop_request).expect("encode subtree removal stop request");
1975        let status_bytes =
1976            candid::encode_one(status_request).expect("encode subtree removal status request");
1977        let response_bytes =
1978            candid::encode_one(&response).expect("encode subtree removal response");
1979
1980        assert_eq!(
1981            candid::decode_one::<RootComponentSubtreeRemovalRequest>(&request_bytes)
1982                .expect("decode subtree removal request"),
1983            request
1984        );
1985        assert_eq!(
1986            candid::decode_one::<RootComponentSubtreeRemovalAdvanceRequest>(&advance_bytes)
1987                .expect("decode subtree removal advance request"),
1988            advance_request
1989        );
1990        assert_eq!(
1991            candid::decode_one::<RootComponentSubtreeRemovalStopPreparationRequest>(&stop_bytes)
1992                .expect("decode subtree removal stop request"),
1993            stop_request
1994        );
1995        assert_eq!(
1996            candid::decode_one::<RootComponentSubtreeRemovalStatusRequest>(&status_bytes)
1997                .expect("decode subtree removal status request"),
1998            status_request
1999        );
2000        assert_eq!(
2001            candid::decode_one::<RootComponentSubtreeRemovalResponse>(&response_bytes)
2002                .expect("decode subtree removal response"),
2003            response
2004        );
2005
2006        let mut quiescent_owner_response = response;
2007        let RootComponentSubtreeRemovalPhase::DirectorySynchronized(receipt) =
2008            &mut quiescent_owner_response.phase
2009        else {
2010            panic!("Directory-synchronized response");
2011        };
2012        receipt.owning_component = None;
2013        let quiescent_owner_bytes = candid::encode_one(&quiescent_owner_response)
2014            .expect("encode quiescent-owner subtree response");
2015        assert_eq!(
2016            candid::decode_one::<RootComponentSubtreeRemovalResponse>(&quiescent_owner_bytes)
2017                .expect("decode quiescent-owner subtree response"),
2018            quiescent_owner_response
2019        );
2020    }
2021
2022    #[test]
2023    fn component_draining_contracts_round_trip_through_candid() {
2024        let component = ComponentInstanceId::from_generated_bytes([60; 32]);
2025        let previous_registry = ComponentRegistryHead {
2026            component,
2027            revision: 7,
2028            content_hash: [61; 32],
2029        };
2030        let request = RootComponentDrainingRequest {
2031            operation_id: [62; 32],
2032            component,
2033            expected_registry: previous_registry.clone(),
2034        };
2035        let status_request = RootComponentDrainingStatusRequest {
2036            operation_id: request.operation_id,
2037            component,
2038        };
2039        let response = RootComponentDrainingResponse {
2040            operation_id: request.operation_id,
2041            component,
2042            previous_registry,
2043            registry: ComponentRegistryHead {
2044                component,
2045                revision: 8,
2046                content_hash: [63; 32],
2047            },
2048            descendant_count: 20_000,
2049            descendant_content_hash: [64; 32],
2050            directory_authority_hash: [65; 32],
2051            started_at_ns: 66,
2052        };
2053
2054        let request_bytes =
2055            candid::encode_one(&request).expect("encode Component draining request");
2056        let status_bytes =
2057            candid::encode_one(status_request).expect("encode Component draining status request");
2058        let response_bytes =
2059            candid::encode_one(&response).expect("encode Component draining response");
2060
2061        assert_eq!(
2062            candid::decode_one::<RootComponentDrainingRequest>(&request_bytes)
2063                .expect("decode Component draining request"),
2064            request
2065        );
2066        assert_eq!(
2067            candid::decode_one::<RootComponentDrainingStatusRequest>(&status_bytes)
2068                .expect("decode Component draining status request"),
2069            status_request
2070        );
2071        assert_eq!(
2072            candid::decode_one::<RootComponentDrainingResponse>(&response_bytes)
2073                .expect("decode Component draining response"),
2074            response
2075        );
2076    }
2077
2078    #[test]
2079    fn component_quiescence_contracts_round_trip_through_candid() {
2080        let component = ComponentInstanceId::from_generated_bytes([67; 32]);
2081        let registry = ComponentRegistryHead {
2082            component,
2083            revision: 9,
2084            content_hash: [68; 32],
2085        };
2086        let request = RootComponentQuiescenceRequest {
2087            operation_id: [69; 32],
2088            component,
2089            expected_registry: registry.clone(),
2090        };
2091        let status_request = RootComponentQuiescenceStatusRequest {
2092            operation_id: request.operation_id,
2093            component,
2094        };
2095        let stop = RootComponentQuiescenceStopIntent {
2096            registry,
2097            descendant_count: 20_000,
2098            descendant_content_hash: [70; 32],
2099            canister_id: Principal::from_slice(&[71; 29]),
2100            controller: Principal::from_slice(&[72; 29]),
2101            expected_module_hash: [73; 32],
2102            covered_fleet_registry_revision: 10,
2103            covered_fleet_registry_content_hash: [74; 32],
2104            covered_authority_hash: [75; 32],
2105            runtime_operation_id: [76; 32],
2106            activation: ComponentRuntimeActivationEvidence {
2107                directory_authority_hash: [77; 32],
2108                activated_at_ns: 78,
2109            },
2110            prepared_at_ns: 79,
2111        };
2112        let response = RootComponentQuiescenceResponse {
2113            operation_id: request.operation_id,
2114            component,
2115            phase: RootComponentQuiescencePhase::Quiescent(RootComponentQuiescentReceipt {
2116                stop,
2117                observed_module_hash: [73; 32],
2118                quiesced_at_ns: 80,
2119            }),
2120        };
2121
2122        let request_bytes = candid::encode_one(&request).expect("encode quiescence request");
2123        let status_bytes =
2124            candid::encode_one(status_request).expect("encode quiescence status request");
2125        let response_bytes = candid::encode_one(&response).expect("encode quiescence response");
2126        assert_eq!(
2127            candid::decode_one::<RootComponentQuiescenceRequest>(&request_bytes)
2128                .expect("decode quiescence request"),
2129            request
2130        );
2131        assert_eq!(
2132            candid::decode_one::<RootComponentQuiescenceStatusRequest>(&status_bytes)
2133                .expect("decode quiescence status request"),
2134            status_request
2135        );
2136        assert_eq!(
2137            candid::decode_one::<RootComponentQuiescenceResponse>(&response_bytes)
2138                .expect("decode quiescence response"),
2139            response
2140        );
2141    }
2142
2143    #[test]
2144    fn component_draining_advance_contracts_round_trip_through_candid() {
2145        let component = ComponentInstanceId::from_generated_bytes([81; 32]);
2146        let registry = ComponentRegistryHead {
2147            component,
2148            revision: 12,
2149            content_hash: [82; 32],
2150        };
2151        let request = RootComponentDrainingAdvanceRequest {
2152            operation_id: [83; 32],
2153            component,
2154        };
2155        let descendant_removal = RootComponentDrainingAdvanceResponse {
2156            operation_id: request.operation_id,
2157            component,
2158            phase: RootComponentDrainingAdvancePhase::DescendantRemoval(
2159                RootComponentSubtreeRemovalResponse {
2160                    operation_id: [84; 32],
2161                    component,
2162                    target_canister_id: Principal::from_slice(&[85; 29]),
2163                    target_parent_canister_id: Principal::from_slice(&[86; 29]),
2164                    target_role: CanisterRole::new("project_instance"),
2165                    target_status: ComponentLifecycleStatus::Active,
2166                    reserved_against_registry: registry.clone(),
2167                    maximum_completed_leaves: 20_000,
2168                    completed_leaves: 0,
2169                    traversal_steps: 0,
2170                    phase: RootComponentSubtreeRemovalPhase::Fenced,
2171                },
2172            ),
2173        };
2174        let descendants_empty = RootComponentDrainingAdvanceResponse {
2175            operation_id: request.operation_id,
2176            component,
2177            phase: RootComponentDrainingAdvancePhase::DescendantsEmpty(
2178                RootComponentDrainingDescendantsEmpty {
2179                    registry,
2180                    descendant_content_hash: [87; 32],
2181                },
2182            ),
2183        };
2184
2185        let request_bytes =
2186            candid::encode_one(request).expect("encode Component draining advance request");
2187        let removal_bytes = candid::encode_one(&descendant_removal)
2188            .expect("encode Component draining removal response");
2189        let empty_bytes = candid::encode_one(&descendants_empty)
2190            .expect("encode Component draining empty response");
2191
2192        assert_eq!(
2193            candid::decode_one::<RootComponentDrainingAdvanceRequest>(&request_bytes)
2194                .expect("decode Component draining advance request"),
2195            request
2196        );
2197        assert_eq!(
2198            candid::decode_one::<RootComponentDrainingAdvanceResponse>(&removal_bytes)
2199                .expect("decode Component draining removal response"),
2200            descendant_removal
2201        );
2202        assert_eq!(
2203            candid::decode_one::<RootComponentDrainingAdvanceResponse>(&empty_bytes)
2204                .expect("decode Component draining empty response"),
2205            descendants_empty
2206        );
2207    }
2208
2209    #[test]
2210    #[expect(
2211        clippy::too_many_lines,
2212        reason = "one wire-contract test keeps final inventory and its deletion authority aligned"
2213    )]
2214    fn component_final_inventory_contracts_round_trip_through_candid() {
2215        let component = ComponentInstanceId::from_generated_bytes([88; 32]);
2216        let registry = ComponentRegistryHead {
2217            component,
2218            revision: 21,
2219            content_hash: [89; 32],
2220        };
2221        let request = RootComponentFinalInventoryRequest {
2222            operation_id: [90; 32],
2223            component,
2224            expected_registry: registry.clone(),
2225        };
2226        let inventory = RootComponentFinalInventory {
2227            registry,
2228            descendant_content_hash: [91; 32],
2229            registry_encoded_bytes: 4_096,
2230            directory_synchronized_at_ns: 92,
2231            covered_fleet_registry_revision: 93,
2232            covered_fleet_registry_content_hash: [94; 32],
2233            directory_authority_hash: [95; 32],
2234            inventory_hash: [96; 32],
2235            finalized_at_ns: 97,
2236        };
2237        let response = RootComponentFinalInventoryResponse {
2238            operation_id: request.operation_id,
2239            component,
2240            inventory: inventory.clone(),
2241        };
2242        let deletion_request = RootComponentDeletionRequest {
2243            operation_id: request.operation_id,
2244            component,
2245            expected_inventory_hash: inventory.inventory_hash,
2246        };
2247        let deletion_status_request = RootComponentDeletionStatusRequest {
2248            operation_id: request.operation_id,
2249            component,
2250        };
2251        let deletion = RootComponentDeletionIntent {
2252            final_inventory: inventory,
2253            quiescence: RootComponentQuiescentReceipt {
2254                stop: RootComponentQuiescenceStopIntent {
2255                    registry: response.inventory.registry.clone(),
2256                    descendant_count: 0,
2257                    descendant_content_hash: response.inventory.descendant_content_hash,
2258                    canister_id: Principal::from_slice(&[98; 29]),
2259                    controller: Principal::from_slice(&[99; 29]),
2260                    expected_module_hash: [100; 32],
2261                    covered_fleet_registry_revision: 93,
2262                    covered_fleet_registry_content_hash: [94; 32],
2263                    covered_authority_hash: [101; 32],
2264                    runtime_operation_id: [102; 32],
2265                    activation: ComponentRuntimeActivationEvidence {
2266                        directory_authority_hash: [103; 32],
2267                        activated_at_ns: 104,
2268                    },
2269                    prepared_at_ns: 105,
2270                },
2271                observed_module_hash: [100; 32],
2272                quiesced_at_ns: 106,
2273            },
2274            prepared_at_ns: 107,
2275        };
2276        let deleted_receipt = RootComponentDeletedReceipt {
2277            deletion,
2278            deleted_at_ns: 108,
2279        };
2280        let deletion_response = RootComponentDeletionResponse {
2281            operation_id: request.operation_id,
2282            component,
2283            phase: RootComponentDeletionPhase::Deleted(deleted_receipt.clone()),
2284        };
2285        let membership_removed_response = RootComponentDeletionResponse {
2286            operation_id: request.operation_id,
2287            component,
2288            phase: RootComponentDeletionPhase::MembershipRemoved(
2289                RootComponentMembershipRemovedReceipt {
2290                    deleted: deleted_receipt,
2291                    allocation_operation_id: [109; 32],
2292                    remaining_spec_committed_instances: 2,
2293                    root_committed_component_instances: 3,
2294                    root_known_created_component_canisters: 4,
2295                    root_registry_encoded_bytes: 5_000,
2296                    removed_at_ns: 110,
2297                    removal_hash: [111; 32],
2298                },
2299            ),
2300        };
2301
2302        let request_bytes =
2303            candid::encode_one(&request).expect("encode Component final inventory request");
2304        let response_bytes =
2305            candid::encode_one(&response).expect("encode Component final inventory response");
2306        let deletion_request_bytes =
2307            candid::encode_one(deletion_request).expect("encode Component deletion request");
2308        let deletion_status_bytes = candid::encode_one(deletion_status_request)
2309            .expect("encode Component deletion status request");
2310        let deletion_response_bytes =
2311            candid::encode_one(&deletion_response).expect("encode Component deletion response");
2312        let membership_removed_response_bytes = candid::encode_one(&membership_removed_response)
2313            .expect("encode Component membership-removal response");
2314        assert_eq!(
2315            candid::decode_one::<RootComponentFinalInventoryRequest>(&request_bytes)
2316                .expect("decode Component final inventory request"),
2317            request
2318        );
2319        assert_eq!(
2320            candid::decode_one::<RootComponentFinalInventoryResponse>(&response_bytes)
2321                .expect("decode Component final inventory response"),
2322            response
2323        );
2324        assert_eq!(
2325            candid::decode_one::<RootComponentDeletionRequest>(&deletion_request_bytes)
2326                .expect("decode Component deletion request"),
2327            deletion_request
2328        );
2329        assert_eq!(
2330            candid::decode_one::<RootComponentDeletionStatusRequest>(&deletion_status_bytes)
2331                .expect("decode Component deletion status request"),
2332            deletion_status_request
2333        );
2334        assert_eq!(
2335            candid::decode_one::<RootComponentDeletionResponse>(&deletion_response_bytes)
2336                .expect("decode Component deletion response"),
2337            deletion_response
2338        );
2339        assert_eq!(
2340            candid::decode_one::<RootComponentDeletionResponse>(&membership_removed_response_bytes)
2341                .expect("decode Component membership-removal response"),
2342            membership_removed_response
2343        );
2344    }
2345
2346    #[test]
2347    fn component_subtree_removal_deletion_requests_round_trip_through_candid() {
2348        let prepare = RootComponentSubtreeRemovalDeletePreparationRequest {
2349            operation_id: [50; 32],
2350            component: ComponentInstanceId::from_generated_bytes([51; 32]),
2351            expected_traversal_steps: 3,
2352            expected_leaf_canister_id: Principal::from_slice(&[52; 29]),
2353            expected_leaf_parent_canister_id: Principal::from_slice(&[53; 29]),
2354        };
2355        let request = RootComponentSubtreeRemovalDeleteRequest {
2356            operation_id: prepare.operation_id,
2357            component: prepare.component,
2358            expected_traversal_steps: prepare.expected_traversal_steps,
2359            expected_leaf_canister_id: prepare.expected_leaf_canister_id,
2360            expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
2361        };
2362        let membership_request = RootComponentSubtreeRemovalMembershipRemovalRequest {
2363            operation_id: prepare.operation_id,
2364            component: prepare.component,
2365            expected_traversal_steps: prepare.expected_traversal_steps,
2366            expected_leaf_canister_id: prepare.expected_leaf_canister_id,
2367            expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
2368        };
2369        let directory_request = RootComponentSubtreeRemovalDirectorySynchronizationRequest {
2370            operation_id: prepare.operation_id,
2371            component: prepare.component,
2372            expected_traversal_steps: prepare.expected_traversal_steps,
2373            expected_leaf_canister_id: prepare.expected_leaf_canister_id,
2374            expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
2375        };
2376        let finalization_request = RootComponentSubtreeRemovalLeafFinalizationRequest {
2377            operation_id: prepare.operation_id,
2378            component: prepare.component,
2379            expected_traversal_steps: prepare.expected_traversal_steps,
2380            expected_leaf_canister_id: prepare.expected_leaf_canister_id,
2381            expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
2382        };
2383        let prepare_bytes = candid::encode_one(prepare)
2384            .expect("encode subtree removal deletion preparation request");
2385        let request_bytes =
2386            candid::encode_one(request).expect("encode subtree removal deletion request");
2387        let membership_request_bytes = candid::encode_one(membership_request)
2388            .expect("encode subtree removal membership-removal request");
2389        let directory_request_bytes = candid::encode_one(directory_request)
2390            .expect("encode subtree removal Directory synchronization request");
2391        let finalization_request_bytes = candid::encode_one(finalization_request)
2392            .expect("encode subtree removal leaf-finalization request");
2393
2394        assert_eq!(
2395            candid::decode_one::<RootComponentSubtreeRemovalDeletePreparationRequest>(
2396                &prepare_bytes
2397            )
2398            .expect("decode subtree removal deletion preparation request"),
2399            prepare
2400        );
2401        assert_eq!(
2402            candid::decode_one::<RootComponentSubtreeRemovalDeleteRequest>(&request_bytes)
2403                .expect("decode subtree removal deletion request"),
2404            request
2405        );
2406        assert_eq!(
2407            candid::decode_one::<RootComponentSubtreeRemovalMembershipRemovalRequest>(
2408                &membership_request_bytes
2409            )
2410            .expect("decode subtree removal membership-removal request"),
2411            membership_request
2412        );
2413        assert_eq!(
2414            candid::decode_one::<RootComponentSubtreeRemovalDirectorySynchronizationRequest>(
2415                &directory_request_bytes
2416            )
2417            .expect("decode subtree removal Directory synchronization request"),
2418            directory_request
2419        );
2420        assert_eq!(
2421            candid::decode_one::<RootComponentSubtreeRemovalLeafFinalizationRequest>(
2422                &finalization_request_bytes
2423            )
2424            .expect("decode subtree removal leaf-finalization request"),
2425            finalization_request
2426        );
2427    }
2428
2429    #[test]
2430    fn component_subtree_removal_stop_request_round_trips_through_candid() {
2431        let request = RootComponentSubtreeRemovalStopRequest {
2432            operation_id: [50; 32],
2433            component: ComponentInstanceId::from_generated_bytes([51; 32]),
2434            expected_traversal_steps: 3,
2435            expected_leaf_canister_id: Principal::from_slice(&[52; 29]),
2436            expected_leaf_parent_canister_id: Principal::from_slice(&[53; 29]),
2437        };
2438        let bytes =
2439            candid::encode_one(request).expect("encode subtree removal stop execution request");
2440
2441        assert_eq!(
2442            candid::decode_one::<RootComponentSubtreeRemovalStopRequest>(&bytes)
2443                .expect("decode subtree removal stop execution request"),
2444            request
2445        );
2446    }
2447
2448    #[test]
2449    #[expect(
2450        clippy::too_many_lines,
2451        reason = "one round-trip test keeps the complete child lifecycle boundary coherent"
2452    )]
2453    fn component_child_lifecycle_contracts_round_trip_through_candid() {
2454        let component = ComponentInstanceId::from_generated_bytes([11; 32]);
2455        let registry = ComponentRegistryHead {
2456            component,
2457            revision: 2,
2458            content_hash: [12; 32],
2459        };
2460        let request = RootComponentChildAllocationRequest {
2461            operation_id: [13; 32],
2462            component,
2463            expected_registry: registry.clone(),
2464            child_role: CanisterRole::new("project_instance"),
2465            application_init_args: Some(vec![9, 8, 7]),
2466        };
2467        let status_request = RootComponentChildAllocationStatusRequest {
2468            operation_id: request.operation_id,
2469            component,
2470        };
2471        let creation_request = RootComponentChildCreationRequest {
2472            operation_id: request.operation_id,
2473            component,
2474        };
2475        let install_request = RootComponentChildInstallRequest {
2476            operation_id: request.operation_id,
2477            component,
2478        };
2479        let commit_request = RootComponentChildCommitRequest {
2480            operation_id: request.operation_id,
2481            component,
2482        };
2483        let directory_request = RootComponentChildDirectoryPreparationRequest {
2484            operation_id: request.operation_id,
2485            component,
2486        };
2487        let activation_request = RootComponentChildRuntimeActivationRequest {
2488            operation_id: request.operation_id,
2489            component,
2490        };
2491        let membership_request = RootComponentChildMembershipActivationRequest {
2492            operation_id: request.operation_id,
2493            component,
2494        };
2495        let root = Principal::from_slice(&[17; 29]);
2496        let parent = Principal::from_slice(&[14; 29]);
2497        let child = Principal::from_slice(&[18; 29]);
2498        let child_binding = ComponentChildBinding {
2499            component: ComponentBinding {
2500                authority: fleet_registry_authority(),
2501                component,
2502                component_spec: "projects".parse().expect("Component Spec"),
2503                spec_hash: [19; 32],
2504                role: CanisterRole::new("project_hub"),
2505                placement_subnet: SubnetId::from_principal(Principal::from_slice(&[20; 29])),
2506                fleet_subnet_root: root,
2507                canister_id: parent,
2508            },
2509            parent_canister_id: parent,
2510            role: request.child_role.clone(),
2511            canister_id: child,
2512        };
2513        let response = RootComponentChildAllocationResponse {
2514            operation_id: request.operation_id,
2515            component,
2516            parent_canister_id: parent,
2517            parent_role: CanisterRole::new("project_hub"),
2518            child_role: request.child_role.clone(),
2519            child_kind: ComponentChildKind::Instance,
2520            maximum_instances_per_parent: 10_000,
2521            maximum_descendants: 20_000,
2522            maximum_registry_bytes: 16_777_216,
2523            reserved_against_registry: registry,
2524            release_set: FleetSubnetRootReleaseSet {
2525                release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
2526                    [15; 32],
2527                )),
2528                manifest_digest: ReleaseSetDigest::from_bytes([16; 32]),
2529            },
2530            phase: RootComponentAllocationPhase::Verified,
2531            creation: Some(RootComponentCreationEvidence {
2532                wasm_store: Principal::from_slice(&[21; 29]),
2533                payload_hash: [22; 32],
2534                payload_size_bytes: 4_096,
2535                initial_cycles: Cycles::new(5_000_000_000_000),
2536                controller: root,
2537                canister: Some(child),
2538            }),
2539            installation: Some(RootComponentChildInstallEvidence {
2540                raw_module_hash: [23; 32],
2541                chunk_hashes: vec![vec![24; 32]],
2542                binding: child_binding.clone(),
2543            }),
2544        };
2545        let commit_response = RootComponentChildCommitResponse {
2546            allocation: response.clone(),
2547            registry: ComponentRegistryPartitionResponse {
2548                head: ComponentRegistryHead {
2549                    component,
2550                    revision: 3,
2551                    content_hash: [25; 32],
2552                },
2553                binding: child_binding.component.clone(),
2554                protocol_profile_digest: ProtocolProfileDigest::from_bytes([27; 32]),
2555                provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
2556                    caller: Principal::from_slice(&[26; 29]),
2557                },
2558                release_set: response.release_set,
2559                status: ComponentLifecycleStatus::Active,
2560                reserved_descendants: 0,
2561                committed_descendants: 1,
2562                encoded_bytes: 8_192,
2563            },
2564            directory: ComponentDirectoryHead {
2565                provenance: ComponentDirectoryProvenance {
2566                    component: child_binding.component.clone(),
2567                    source_fleet_subnet_root: root,
2568                    component_registry_revision: 3,
2569                    component_registry_content_hash: [25; 32],
2570                    synchronized_at_ns: 27,
2571                },
2572                descendant_count: 1,
2573            },
2574        };
2575        let runtime_authority = ComponentRuntimeDirectoryAuthority {
2576            fleet: FleetDirectorySnapshot {
2577                provenance: crate::dto::fleet_registry::FleetDirectoryProvenance {
2578                    registry: FleetRegistryVersion {
2579                        authority: fleet_registry_authority(),
2580                        revision: 4,
2581                        content_hash: [28; 32],
2582                    },
2583                    source_fleet_subnet_root: root,
2584                },
2585                fleet_subnet_roots: vec![
2586                    crate::dto::fleet_registry::FleetSubnetRootDirectoryEntry {
2587                        placement_subnet: commit_response.registry.binding.placement_subnet,
2588                        fleet_subnet_root: root,
2589                        status: crate::dto::fleet_registry::FleetSubnetRootStatus::Active,
2590                    },
2591                ],
2592                services: vec![],
2593            },
2594            component: commit_response.directory.clone(),
2595            component_group: None,
2596        };
2597        let activation = ComponentRuntimeActivationEvidence {
2598            directory_authority_hash: [29; 32],
2599            activated_at_ns: 30,
2600        };
2601        let directory_response = RootComponentChildDirectoryPreparationResponse {
2602            committed: commit_response.clone(),
2603            child: ComponentRuntimeStatusResponse {
2604                operation_id: request.operation_id,
2605                binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
2606                deployment: Box::new(ProtectedComponentDeployment::UngroupedOrdinary {
2607                    binding: child_binding.component.clone(),
2608                }),
2609                phase: ComponentRuntimePhase::DirectoryPrepared,
2610                authority: Some(runtime_authority.clone()),
2611                authority_hash: Some([31; 32]),
2612                direct_children_hash: Some([37; 32]),
2613                activation: None,
2614            },
2615            owning_component: ComponentRuntimeDirectoryConvergenceEvidence {
2616                operation_id: [32; 32],
2617                binding: ManagedCanisterBinding::Component(child_binding.component.clone()),
2618                covered_authority: runtime_authority.clone(),
2619                covered_authority_hash: [31; 32],
2620                activation,
2621            },
2622            parent: None,
2623        };
2624        let activation_response = RootComponentChildRuntimeActivationResponse {
2625            committed: commit_response.clone(),
2626            child: ComponentRuntimeStatusResponse {
2627                operation_id: request.operation_id,
2628                binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
2629                deployment: Box::new(ProtectedComponentDeployment::UngroupedOrdinary {
2630                    binding: child_binding.component.clone(),
2631                }),
2632                phase: ComponentRuntimePhase::Active,
2633                authority: Some(runtime_authority.clone()),
2634                authority_hash: Some([31; 32]),
2635                direct_children_hash: Some([37; 32]),
2636                activation: Some(ComponentRuntimeActivationEvidence {
2637                    directory_authority_hash: [31; 32],
2638                    activated_at_ns: 33,
2639                }),
2640            },
2641        };
2642        let active_directory = ComponentDirectoryHead {
2643            provenance: ComponentDirectoryProvenance {
2644                component: child_binding.component.clone(),
2645                source_fleet_subnet_root: root,
2646                component_registry_revision: 4,
2647                component_registry_content_hash: [34; 32],
2648                synchronized_at_ns: 35,
2649            },
2650            descendant_count: 1,
2651        };
2652        let active_authority = ComponentRuntimeDirectoryAuthority {
2653            fleet: runtime_authority.fleet,
2654            component: active_directory.clone(),
2655            component_group: None,
2656        };
2657        let membership_response = RootComponentChildMembershipActivationResponse {
2658            committed: commit_response.clone(),
2659            registry: ComponentRegistryPartitionResponse {
2660                head: ComponentRegistryHead {
2661                    component,
2662                    revision: 4,
2663                    content_hash: [34; 32],
2664                },
2665                binding: child_binding.component.clone(),
2666                protocol_profile_digest: ProtocolProfileDigest::from_bytes([36; 32]),
2667                provisioning_origin: commit_response.registry.provisioning_origin.clone(),
2668                release_set: commit_response.registry.release_set,
2669                status: ComponentLifecycleStatus::Active,
2670                reserved_descendants: 0,
2671                committed_descendants: 1,
2672                encoded_bytes: 8_256,
2673            },
2674            directory: active_directory,
2675            child: ComponentRuntimeStatusResponse {
2676                operation_id: request.operation_id,
2677                binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
2678                deployment: Box::new(ProtectedComponentDeployment::UngroupedOrdinary {
2679                    binding: child_binding.component,
2680                }),
2681                phase: ComponentRuntimePhase::Active,
2682                authority: Some(active_authority),
2683                authority_hash: Some([36; 32]),
2684                direct_children_hash: Some([38; 32]),
2685                activation: Some(ComponentRuntimeActivationEvidence {
2686                    directory_authority_hash: [31; 32],
2687                    activated_at_ns: 33,
2688                }),
2689            },
2690        };
2691
2692        let request_bytes = candid::encode_one(&request).expect("encode child reservation");
2693        let status_bytes =
2694            candid::encode_one(status_request).expect("encode child reservation status");
2695        let creation_bytes =
2696            candid::encode_one(creation_request).expect("encode child creation request");
2697        let install_bytes =
2698            candid::encode_one(install_request).expect("encode child install request");
2699        let response_bytes = candid::encode_one(&response).expect("encode child response");
2700        let commit_request_bytes =
2701            candid::encode_one(commit_request).expect("encode child commit request");
2702        let directory_request_bytes =
2703            candid::encode_one(directory_request).expect("encode child Directory request");
2704        let activation_request_bytes =
2705            candid::encode_one(activation_request).expect("encode child activation request");
2706        let membership_request_bytes =
2707            candid::encode_one(membership_request).expect("encode child membership request");
2708        let commit_response_bytes =
2709            candid::encode_one(&commit_response).expect("encode child commit response");
2710        let directory_response_bytes =
2711            candid::encode_one(&directory_response).expect("encode child Directory response");
2712        let activation_response_bytes =
2713            candid::encode_one(&activation_response).expect("encode child activation response");
2714        let membership_response_bytes =
2715            candid::encode_one(&membership_response).expect("encode child membership response");
2716
2717        assert_eq!(
2718            candid::decode_one::<RootComponentChildAllocationRequest>(&request_bytes)
2719                .expect("decode child reservation"),
2720            request
2721        );
2722        assert_eq!(
2723            candid::decode_one::<RootComponentChildAllocationStatusRequest>(&status_bytes)
2724                .expect("decode child reservation status"),
2725            status_request
2726        );
2727        assert_eq!(
2728            candid::decode_one::<RootComponentChildCreationRequest>(&creation_bytes)
2729                .expect("decode child creation request"),
2730            creation_request
2731        );
2732        assert_eq!(
2733            candid::decode_one::<RootComponentChildInstallRequest>(&install_bytes)
2734                .expect("decode child install request"),
2735            install_request
2736        );
2737        assert_eq!(
2738            candid::decode_one::<RootComponentChildAllocationResponse>(&response_bytes)
2739                .expect("decode child response"),
2740            response
2741        );
2742        assert_eq!(
2743            candid::decode_one::<RootComponentChildCommitRequest>(&commit_request_bytes)
2744                .expect("decode child commit request"),
2745            commit_request
2746        );
2747        assert_eq!(
2748            candid::decode_one::<RootComponentChildDirectoryPreparationRequest>(
2749                &directory_request_bytes
2750            )
2751            .expect("decode child Directory request"),
2752            directory_request
2753        );
2754        assert_eq!(
2755            candid::decode_one::<RootComponentChildCommitResponse>(&commit_response_bytes)
2756                .expect("decode child commit response"),
2757            commit_response
2758        );
2759        assert_eq!(
2760            candid::decode_one::<RootComponentChildDirectoryPreparationResponse>(
2761                &directory_response_bytes
2762            )
2763            .expect("decode child Directory response"),
2764            directory_response
2765        );
2766        assert_eq!(
2767            candid::decode_one::<RootComponentChildRuntimeActivationRequest>(
2768                &activation_request_bytes
2769            )
2770            .expect("decode child activation request"),
2771            activation_request
2772        );
2773        assert_eq!(
2774            candid::decode_one::<RootComponentChildRuntimeActivationResponse>(
2775                &activation_response_bytes
2776            )
2777            .expect("decode child activation response"),
2778            activation_response
2779        );
2780        assert_eq!(
2781            candid::decode_one::<RootComponentChildMembershipActivationRequest>(
2782                &membership_request_bytes
2783            )
2784            .expect("decode child membership request"),
2785            membership_request
2786        );
2787        assert_eq!(
2788            candid::decode_one::<RootComponentChildMembershipActivationResponse>(
2789                &membership_response_bytes
2790            )
2791            .expect("decode child membership response"),
2792            membership_response
2793        );
2794    }
2795
2796    #[test]
2797    fn component_install_request_round_trips_through_candid() {
2798        let request = RootComponentInstallRequest {
2799            operation_id: [10; 32],
2800        };
2801        let bytes = candid::encode_one(request).expect("encode install request");
2802
2803        assert_eq!(
2804            candid::decode_one::<RootComponentInstallRequest>(&bytes)
2805                .expect("decode install request"),
2806            request
2807        );
2808    }
2809
2810    #[test]
2811    fn component_commit_request_round_trips_through_candid() {
2812        let request = RootComponentCommitRequest {
2813            operation_id: [10; 32],
2814        };
2815        let bytes = candid::encode_one(request).expect("encode commit request");
2816
2817        assert_eq!(
2818            candid::decode_one::<RootComponentCommitRequest>(&bytes)
2819                .expect("decode commit request"),
2820            request
2821        );
2822    }
2823
2824    #[test]
2825    fn component_runtime_activation_requests_round_trip_through_candid() {
2826        let root_request = RootComponentRuntimeActivationRequest {
2827            operation_id: [22; 32],
2828        };
2829        let target_request = ComponentRuntimeActivationRequest {
2830            operation_id: root_request.operation_id,
2831            directory_authority_hash: [23; 32],
2832        };
2833        let membership_request = RootComponentMembershipActivationRequest {
2834            operation_id: root_request.operation_id,
2835        };
2836        let root_bytes = candid::encode_one(root_request).expect("encode root activation request");
2837        let target_bytes =
2838            candid::encode_one(target_request).expect("encode target activation request");
2839        let membership_bytes =
2840            candid::encode_one(membership_request).expect("encode membership activation request");
2841
2842        assert_eq!(
2843            candid::decode_one::<RootComponentRuntimeActivationRequest>(&root_bytes)
2844                .expect("decode root activation request"),
2845            root_request
2846        );
2847        assert_eq!(
2848            candid::decode_one::<ComponentRuntimeActivationRequest>(&target_bytes)
2849                .expect("decode target activation request"),
2850            target_request
2851        );
2852        assert_eq!(
2853            candid::decode_one::<RootComponentMembershipActivationRequest>(&membership_bytes)
2854                .expect("decode membership activation request"),
2855            membership_request
2856        );
2857    }
2858}