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/// ComponentRegistryActivePartitionRequest
808///
809/// Exact top-level allocation authority whose immutable activation partition and current head are
810/// required for terminal Fleet inventory.
811///
812
813#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
814pub struct ComponentRegistryActivePartitionRequest {
815    pub component: ComponentInstanceId,
816    pub provisioning_operation_id: [u8; 32],
817    pub plan_hash: [u8; 32],
818    pub group_placement: ComponentGroupPlacementId,
819    pub member_path: ComponentGroupMemberPath,
820}
821
822///
823/// ComponentRegistryActivePartitionResponse
824///
825/// Root-qualified immutable top-level activation receipt plus the validated current partition.
826/// Later descendant work may advance `current` without changing `activation`.
827///
828
829#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
830pub struct ComponentRegistryActivePartitionResponse {
831    pub allocation_operation_id: [u8; 32],
832    pub prepared: ComponentRegistryPartitionResponse,
833    pub activation: ComponentRegistryPartitionResponse,
834    pub current: ComponentRegistryPartitionResponse,
835}
836
837///
838/// ComponentDirectoryProvenance
839///
840/// Exact Component Registry authority from which one Component Directory is derived.
841///
842
843#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
844pub struct ComponentDirectoryProvenance {
845    pub component: ComponentBinding,
846    pub source_fleet_subnet_root: Principal,
847    pub component_registry_revision: u64,
848    pub component_registry_content_hash: [u8; 32],
849    pub synchronized_at_ns: u64,
850}
851
852///
853/// ComponentDirectoryHead
854///
855/// Compact independently versioned discovery projection for one Component tree.
856///
857
858#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
859pub struct ComponentDirectoryHead {
860    pub provenance: ComponentDirectoryProvenance,
861    pub descendant_count: u32,
862}
863
864///
865/// ComponentDirectoryHeadRequest
866///
867/// Read-only lookup key for one committed Component Directory head.
868///
869
870#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
871pub struct ComponentDirectoryHeadRequest {
872    pub component: ComponentInstanceId,
873}
874
875///
876/// ComponentDirectoryPageCursor
877///
878/// Opaque revision- and filter-bound continuation for one bounded Directory page.
879///
880
881#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
882pub struct ComponentDirectoryPageCursor(pub Vec<u8>);
883
884///
885/// ComponentDirectoryPageRequest
886///
887/// Bounded member query against one exact current Component Directory authority.
888///
889
890#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
891pub struct ComponentDirectoryPageRequest {
892    pub directory: ComponentDirectoryHead,
893    pub parent_canister_id: Option<Principal>,
894    pub role: Option<CanisterRole>,
895    pub status: Option<ComponentLifecycleStatus>,
896    pub cursor: Option<ComponentDirectoryPageCursor>,
897    pub limit: u16,
898}
899
900///
901/// ComponentDirectoryChildEntry
902///
903/// One authoritative normalized child projected with its complete protected binding.
904///
905
906#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
907pub struct ComponentDirectoryChildEntry {
908    pub binding: ComponentChildBinding,
909    pub kind: ComponentChildKind,
910    pub installed_artifact_hash: [u8; 32],
911    pub protocol_profile_digest: ProtocolProfileDigest,
912    pub status: ComponentLifecycleStatus,
913}
914
915///
916/// ComponentDirectoryPageResponse
917///
918/// One bounded caller-scoped page under the exact requested Directory head.
919///
920
921#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
922pub struct ComponentDirectoryPageResponse {
923    pub directory: ComponentDirectoryHead,
924    pub entries: Vec<ComponentDirectoryChildEntry>,
925    pub next_cursor: Option<ComponentDirectoryPageCursor>,
926}
927
928///
929/// ComponentRuntimeDirectoryAuthority
930///
931/// Exact Fleet and Component discovery authority retained by one managed Component-tree node.
932///
933
934#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
935pub struct ComponentRuntimeDirectoryAuthority {
936    pub fleet: FleetDirectorySnapshot,
937    pub component: ComponentDirectoryHead,
938    pub component_group: Option<crate::dto::component_provisioning::ComponentGroupDirectory>,
939}
940
941///
942/// ComponentRuntimeDirectChild
943///
944/// Exact active direct-child projection delivered with one Component Directory authority.
945///
946
947#[derive(CandidType, Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
948pub struct ComponentRuntimeDirectChild {
949    pub canister_id: Principal,
950    pub role: CanisterRole,
951    pub protocol_profile_digest: ProtocolProfileDigest,
952}
953
954///
955/// ComponentRuntimeDirectoryPreparationRequest
956///
957/// Root-issued exact Directory preparation command for one managed Component-tree node.
958///
959
960#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
961pub struct ComponentRuntimeDirectoryPreparationRequest {
962    pub operation_id: [u8; 32],
963    pub authority: ComponentRuntimeDirectoryAuthority,
964    pub direct_children: Vec<ComponentRuntimeDirectChild>,
965}
966
967///
968/// ComponentRuntimeDirectorySynchronizationRequest
969///
970/// Root-issued replacement of one active managed Component node's current Directory authority.
971///
972
973#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
974pub struct ComponentRuntimeDirectorySynchronizationRequest {
975    pub operation_id: [u8; 32],
976    pub authority: ComponentRuntimeDirectoryAuthority,
977    pub direct_children: Vec<ComponentRuntimeDirectChild>,
978}
979
980///
981/// ComponentRuntimePhase
982///
983/// Target-local progress from installation through Component runtime activation.
984///
985
986#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
987pub enum ComponentRuntimePhase {
988    AwaitingDirectory,
989    DirectoryPrepared,
990    Active,
991}
992
993///
994/// ComponentRuntimeActivationEvidence
995///
996/// Exact retained Directory authority under which one Component runtime became Active.
997///
998
999#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
1000pub struct ComponentRuntimeActivationEvidence {
1001    pub directory_authority_hash: [u8; 32],
1002    pub activated_at_ns: u64,
1003}
1004
1005///
1006/// ComponentRuntimeActivationRequest
1007///
1008/// Root-issued exact activation command for one Directory-prepared managed Component node.
1009///
1010
1011#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
1012pub struct ComponentRuntimeActivationRequest {
1013    pub operation_id: [u8; 32],
1014    pub directory_authority_hash: [u8; 32],
1015}
1016
1017///
1018/// ComponentRuntimeStatusResponse
1019///
1020/// Independently observable target-local binding and exact retained Directory authority.
1021///
1022
1023#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1024pub struct ComponentRuntimeStatusResponse {
1025    pub operation_id: [u8; 32],
1026    pub binding: ManagedCanisterBinding,
1027    pub deployment: Box<ProtectedComponentDeployment>,
1028    pub phase: ComponentRuntimePhase,
1029    pub authority: Option<ComponentRuntimeDirectoryAuthority>,
1030    pub authority_hash: Option<[u8; 32]>,
1031    pub direct_children_hash: Option<[u8; 32]>,
1032    pub activation: Option<ComponentRuntimeActivationEvidence>,
1033}
1034
1035///
1036/// ComponentRuntimeDirectoryConvergenceEvidence
1037///
1038/// Stable root evidence that one active member covered at least the required Directory authority.
1039///
1040
1041#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1042pub struct ComponentRuntimeDirectoryConvergenceEvidence {
1043    pub operation_id: [u8; 32],
1044    pub binding: ManagedCanisterBinding,
1045    pub covered_authority: ComponentRuntimeDirectoryAuthority,
1046    pub covered_authority_hash: [u8; 32],
1047    pub activation: ComponentRuntimeActivationEvidence,
1048}
1049
1050///
1051/// RootComponentCreationEvidence
1052///
1053/// Exact Store artifact and root-owned creation settings frozen before the paid effect.
1054///
1055
1056#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1057pub struct RootComponentCreationEvidence {
1058    pub wasm_store: Principal,
1059    pub payload_hash: [u8; 32],
1060    pub payload_size_bytes: u64,
1061    pub initial_cycles: Cycles,
1062    pub controller: Principal,
1063    pub canister: Option<Principal>,
1064}
1065
1066///
1067/// RootComponentInstallEvidence
1068///
1069/// Exact raw artifact, chunk source and immutable target binding frozen before installation.
1070///
1071
1072#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1073pub struct RootComponentInstallEvidence {
1074    pub raw_module_hash: [u8; 32],
1075    pub chunk_hashes: Vec<Vec<u8>>,
1076    pub binding: ComponentBinding,
1077}
1078
1079///
1080/// RootComponentChildInstallEvidence
1081///
1082/// Exact child module and immutable retained binding frozen before installation.
1083///
1084
1085#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1086pub struct RootComponentChildInstallEvidence {
1087    pub raw_module_hash: [u8; 32],
1088    pub chunk_hashes: Vec<Vec<u8>>,
1089    pub binding: ComponentChildBinding,
1090}
1091
1092///
1093/// RootComponentAllocationResponse
1094///
1095/// Durable identity reservation returned identically for exact operation retry.
1096///
1097
1098#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1099pub struct RootComponentAllocationResponse {
1100    pub operation_id: [u8; 32],
1101    pub allocation_sequence: u64,
1102    pub component: ComponentInstanceId,
1103    pub component_spec: ComponentSpecId,
1104    pub spec_hash: [u8; 32],
1105    pub role: CanisterRole,
1106    pub provisioning_origin: ComponentProvisioningOrigin,
1107    pub release_set: FleetSubnetRootReleaseSet,
1108    pub phase: RootComponentAllocationPhase,
1109    pub creation: Option<RootComponentCreationEvidence>,
1110    pub installation: Option<RootComponentInstallEvidence>,
1111}
1112
1113///
1114/// RootComponentChildAllocationResponse
1115///
1116/// Durable direct-child lifecycle progress returned identically for exact parent retry.
1117///
1118
1119#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1120pub struct RootComponentChildAllocationResponse {
1121    pub operation_id: [u8; 32],
1122    pub component: ComponentInstanceId,
1123    pub parent_canister_id: Principal,
1124    pub parent_role: CanisterRole,
1125    pub child_role: CanisterRole,
1126    pub child_kind: ComponentChildKind,
1127    pub maximum_instances_per_parent: u32,
1128    pub maximum_descendants: u32,
1129    pub maximum_registry_bytes: u64,
1130    pub reserved_against_registry: ComponentRegistryHead,
1131    pub release_set: FleetSubnetRootReleaseSet,
1132    pub phase: RootComponentAllocationPhase,
1133    pub creation: Option<RootComponentCreationEvidence>,
1134    pub installation: Option<RootComponentChildInstallEvidence>,
1135}
1136
1137///
1138/// RootComponentSubtreeRemovalResponse
1139///
1140/// Current durable snapshot of one monotonic subtree-removal operation.
1141///
1142
1143#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1144pub struct RootComponentSubtreeRemovalResponse {
1145    pub operation_id: [u8; 32],
1146    pub component: ComponentInstanceId,
1147    pub target_canister_id: Principal,
1148    pub target_parent_canister_id: Principal,
1149    pub target_role: CanisterRole,
1150    pub target_status: ComponentLifecycleStatus,
1151    pub reserved_against_registry: ComponentRegistryHead,
1152    pub maximum_completed_leaves: u32,
1153    pub completed_leaves: u32,
1154    pub traversal_steps: u32,
1155    pub phase: RootComponentSubtreeRemovalPhase,
1156}
1157
1158///
1159/// RootComponentDrainingResponse
1160///
1161/// Exact Registry and Directory authority produced by the durable draining fence.
1162///
1163
1164#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1165pub struct RootComponentDrainingResponse {
1166    pub operation_id: [u8; 32],
1167    pub component: ComponentInstanceId,
1168    pub previous_registry: ComponentRegistryHead,
1169    pub registry: ComponentRegistryHead,
1170    pub descendant_count: u32,
1171    pub descendant_content_hash: [u8; 32],
1172    pub directory_authority_hash: [u8; 32],
1173    pub started_at_ns: u64,
1174}
1175
1176///
1177/// RootComponentQuiescenceStopIntent
1178///
1179/// Exact runtime, Directory, module and controller authority frozen before stopping.
1180///
1181
1182#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1183pub struct RootComponentQuiescenceStopIntent {
1184    pub registry: ComponentRegistryHead,
1185    pub descendant_count: u32,
1186    pub descendant_content_hash: [u8; 32],
1187    pub canister_id: Principal,
1188    pub controller: Principal,
1189    pub expected_module_hash: [u8; 32],
1190    pub covered_fleet_registry_revision: u64,
1191    pub covered_fleet_registry_content_hash: [u8; 32],
1192    pub covered_authority_hash: [u8; 32],
1193    pub runtime_operation_id: [u8; 32],
1194    pub activation: ComponentRuntimeActivationEvidence,
1195    pub prepared_at_ns: u64,
1196}
1197
1198///
1199/// RootComponentQuiescentReceipt
1200///
1201/// Durable evidence that the exact prepared Component was independently observed stopped.
1202///
1203
1204#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1205pub struct RootComponentQuiescentReceipt {
1206    pub stop: RootComponentQuiescenceStopIntent,
1207    pub observed_module_hash: [u8; 32],
1208    pub quiesced_at_ns: u64,
1209}
1210
1211///
1212/// RootComponentQuiescencePhase
1213///
1214/// Monotonic progress from pre-effect stop authority to observed quiescence.
1215///
1216
1217#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1218pub enum RootComponentQuiescencePhase {
1219    StopIntent(RootComponentQuiescenceStopIntent),
1220    Quiescent(RootComponentQuiescentReceipt),
1221}
1222
1223///
1224/// RootComponentQuiescenceResponse
1225///
1226/// Current durable quiescence progress for one draining Component.
1227///
1228
1229#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1230pub struct RootComponentQuiescenceResponse {
1231    pub operation_id: [u8; 32],
1232    pub component: ComponentInstanceId,
1233    pub phase: RootComponentQuiescencePhase,
1234}
1235
1236///
1237/// RootComponentDrainingDescendantsEmpty
1238///
1239/// Exact current Registry proof that one draining Component has no descendants.
1240///
1241
1242#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1243pub struct RootComponentDrainingDescendantsEmpty {
1244    pub registry: ComponentRegistryHead,
1245    pub descendant_content_hash: [u8; 32],
1246}
1247
1248///
1249/// RootComponentDrainingAdvancePhase
1250///
1251/// One bounded driver result: current subtree progress or exact empty inventory.
1252///
1253
1254#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1255#[expect(
1256    clippy::large_enum_variant,
1257    reason = "wire result embeds the current durable subtree snapshot without a Rust-only indirection"
1258)]
1259pub enum RootComponentDrainingAdvancePhase {
1260    DescendantRemoval(RootComponentSubtreeRemovalResponse),
1261    DescendantsEmpty(RootComponentDrainingDescendantsEmpty),
1262}
1263
1264///
1265/// RootComponentDrainingAdvanceResponse
1266///
1267/// Current bounded progress of one terminally quiescent Component drain.
1268///
1269
1270#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1271pub struct RootComponentDrainingAdvanceResponse {
1272    pub operation_id: [u8; 32],
1273    pub component: ComponentInstanceId,
1274    pub phase: RootComponentDrainingAdvancePhase,
1275}
1276
1277///
1278/// RootComponentFinalInventory
1279///
1280/// Exact empty Component Registry and current Fleet Directory authority frozen before deletion.
1281///
1282
1283#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1284pub struct RootComponentFinalInventory {
1285    pub registry: ComponentRegistryHead,
1286    pub descendant_content_hash: [u8; 32],
1287    pub registry_encoded_bytes: u64,
1288    pub directory_synchronized_at_ns: u64,
1289    pub covered_fleet_registry_revision: u64,
1290    pub covered_fleet_registry_content_hash: [u8; 32],
1291    pub directory_authority_hash: [u8; 32],
1292    pub inventory_hash: [u8; 32],
1293    pub finalized_at_ns: u64,
1294}
1295
1296///
1297/// RootComponentFinalInventoryResponse
1298///
1299/// Response-idempotent receipt for one finalized empty Component inventory.
1300///
1301
1302#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1303pub struct RootComponentFinalInventoryResponse {
1304    pub operation_id: [u8; 32],
1305    pub component: ComponentInstanceId,
1306    pub inventory: RootComponentFinalInventory,
1307}
1308
1309///
1310/// RootComponentDeletionIntent
1311///
1312/// Complete final-inventory and quiescence authority frozen before top-level deletion.
1313///
1314
1315#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1316pub struct RootComponentDeletionIntent {
1317    pub final_inventory: RootComponentFinalInventory,
1318    pub quiescence: RootComponentQuiescentReceipt,
1319    pub prepared_at_ns: u64,
1320}
1321
1322///
1323/// RootComponentDeletedReceipt
1324///
1325/// Terminal authority retained after the top-level workload Canister is recycled.
1326///
1327
1328#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1329pub struct RootComponentDeletedReceipt {
1330    pub deletion: RootComponentDeletionIntent,
1331    pub deleted_at_ns: u64,
1332}
1333
1334///
1335/// RootComponentMembershipRemovedReceipt
1336///
1337/// Terminal local-membership removal and settled root/Spec accounting authority.
1338///
1339
1340#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1341pub struct RootComponentMembershipRemovedReceipt {
1342    pub deleted: RootComponentDeletedReceipt,
1343    pub allocation_operation_id: [u8; 32],
1344    pub remaining_spec_committed_instances: u32,
1345    pub root_committed_component_instances: u32,
1346    pub root_known_created_component_canisters: u32,
1347    pub root_registry_encoded_bytes: u64,
1348    pub removed_at_ns: u64,
1349    pub removal_hash: [u8; 32],
1350}
1351
1352///
1353/// RootComponentDeletionPhase
1354///
1355/// Monotonic top-level deletion progress through terminal local-membership removal.
1356///
1357
1358#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1359pub enum RootComponentDeletionPhase {
1360    DeleteIntent(RootComponentDeletionIntent),
1361    Deleted(RootComponentDeletedReceipt),
1362    MembershipRemoved(RootComponentMembershipRemovedReceipt),
1363}
1364
1365///
1366/// RootComponentDeletionResponse
1367///
1368/// Current durable deletion progress for one finalized top-level Component.
1369///
1370
1371#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1372pub struct RootComponentDeletionResponse {
1373    pub operation_id: [u8; 32],
1374    pub component: ComponentInstanceId,
1375    pub phase: RootComponentDeletionPhase,
1376}
1377
1378///
1379/// RootComponentChildCommitResponse
1380///
1381/// Exact committed child operation, authoritative Component Registry and next Directory head.
1382///
1383
1384#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1385pub struct RootComponentChildCommitResponse {
1386    pub allocation: RootComponentChildAllocationResponse,
1387    pub registry: ComponentRegistryPartitionResponse,
1388    pub directory: ComponentDirectoryHead,
1389}
1390
1391///
1392/// RootComponentChildDirectoryPreparationResponse
1393///
1394/// Exact child preparation plus stable bounded active-member Directory coverage.
1395///
1396
1397#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1398pub struct RootComponentChildDirectoryPreparationResponse {
1399    pub committed: RootComponentChildCommitResponse,
1400    pub child: ComponentRuntimeStatusResponse,
1401    pub owning_component: ComponentRuntimeDirectoryConvergenceEvidence,
1402    pub parent: Option<ComponentRuntimeDirectoryConvergenceEvidence>,
1403}
1404
1405///
1406/// RootComponentChildRuntimeActivationResponse
1407///
1408/// Exact child commitment plus independently observed Directory-bound runtime activation.
1409///
1410
1411#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1412pub struct RootComponentChildRuntimeActivationResponse {
1413    pub committed: RootComponentChildCommitResponse,
1414    pub child: ComponentRuntimeStatusResponse,
1415}
1416
1417///
1418/// RootComponentChildMembershipActivationResponse
1419///
1420/// Original child commitment plus active Registry, Directory and target convergence evidence.
1421///
1422
1423#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1424pub struct RootComponentChildMembershipActivationResponse {
1425    pub committed: RootComponentChildCommitResponse,
1426    pub registry: ComponentRegistryPartitionResponse,
1427    pub directory: ComponentDirectoryHead,
1428    pub child: ComponentRuntimeStatusResponse,
1429}
1430
1431///
1432/// RootComponentCommitResponse
1433///
1434/// Exact committed allocation, authoritative Registry row and derived Directory head.
1435///
1436
1437#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1438pub struct RootComponentCommitResponse {
1439    pub allocation: RootComponentAllocationResponse,
1440    pub registry: ComponentRegistryPartitionResponse,
1441    pub directory: ComponentDirectoryHead,
1442}
1443
1444///
1445/// RootComponentDirectoryPreparationResponse
1446///
1447/// Exact root authority plus independently observed target-local Directory preparation.
1448///
1449
1450#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1451pub struct RootComponentDirectoryPreparationResponse {
1452    pub committed: RootComponentCommitResponse,
1453    pub target: ComponentRuntimeStatusResponse,
1454}
1455
1456///
1457/// RootComponentRuntimeActivationResponse
1458///
1459/// Exact root authority plus independently observed target-local runtime activation.
1460///
1461
1462#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1463pub struct RootComponentRuntimeActivationResponse {
1464    pub committed: RootComponentCommitResponse,
1465    pub target: ComponentRuntimeStatusResponse,
1466}
1467
1468///
1469/// RootComponentMembershipActivationResponse
1470///
1471/// Exact active Registry authority plus independently observed current target Directory.
1472///
1473
1474#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1475pub struct RootComponentMembershipActivationResponse {
1476    pub allocation: RootComponentAllocationResponse,
1477    pub registry: ComponentRegistryPartitionResponse,
1478    pub directory: ComponentDirectoryHead,
1479    pub target: ComponentRuntimeStatusResponse,
1480}
1481
1482#[cfg(test)]
1483mod tests {
1484    use super::*;
1485    use crate::{
1486        dto::root_store::RootStoreBootstrapRequest,
1487        ids::{
1488            AppId, CanonicalNetworkId, FleetCoordinatorBinding, FleetId, FleetKey,
1489            FleetRegistryAuthority, ReleaseBuildId, ReleaseBuildNonce, ReleaseSetDigest, SubnetId,
1490        },
1491    };
1492
1493    #[test]
1494    fn component_registry_contracts_round_trip_through_candid() {
1495        let request = RootComponentRegistryPreparationRequest {
1496            store_bootstrap: RootStoreBootstrapRequest {
1497                operation_id: [8; 32],
1498                manifest_payload_size_bytes: 128,
1499            },
1500            expected_fleet_registry: FleetRegistryVersion {
1501                authority: fleet_registry_authority(),
1502                revision: 4,
1503                content_hash: [5; 32],
1504            },
1505        };
1506        let response = RootComponentRegistryStatusResponse {
1507            fleet_subnet_root: Principal::from_slice(&[6; 29]),
1508            prepared_against_registry: request.expected_fleet_registry.clone(),
1509            release_set: FleetSubnetRootReleaseSet {
1510                release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
1511                    [7; 32],
1512                )),
1513                manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
1514            },
1515            component_topology_digest: ComponentTopologyDigest::from_bytes([9; 32]),
1516            next_allocation_sequence: 1,
1517            reserved_component_instances: 0,
1518            committed_component_instances: 0,
1519            managed_descendants: 0,
1520            known_created_component_canisters: 0,
1521            encoded_bytes: 0,
1522            initial_inventory: Some(RootComponentInitialInventoryStatus {
1523                fleet_activation_operation_id: [10; 32],
1524                component_count: 0,
1525                inventory_hash: [11; 32],
1526                sealed_at_ns: 12,
1527                directories_converged: true,
1528                root_runtime_activated: true,
1529            }),
1530        };
1531        let allocation = RootComponentAllocationResponse {
1532            operation_id: [10; 32],
1533            allocation_sequence: 1,
1534            component: ComponentInstanceId::from_generated_bytes([11; 32]),
1535            component_spec: "projects".parse().expect("Component Spec ID"),
1536            spec_hash: [12; 32],
1537            role: CanisterRole::new("project_hub"),
1538            provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
1539                caller: Principal::from_slice(&[13; 29]),
1540            },
1541            release_set: response.release_set,
1542            phase: RootComponentAllocationPhase::Reserved,
1543            creation: None,
1544            installation: None,
1545        };
1546        let created = RootComponentAllocationResponse {
1547            phase: RootComponentAllocationPhase::Created,
1548            creation: Some(RootComponentCreationEvidence {
1549                wasm_store: Principal::from_slice(&[14; 29]),
1550                payload_hash: [15; 32],
1551                payload_size_bytes: 4_096,
1552                initial_cycles: Cycles::new(5_000_000_000_000),
1553                controller: Principal::from_slice(&[6; 29]),
1554                canister: Some(Principal::from_slice(&[16; 29])),
1555            }),
1556            installation: None,
1557            ..allocation.clone()
1558        };
1559        let request_bytes = candid::encode_one(&request).expect("encode request");
1560        let response_bytes = candid::encode_one(&response).expect("encode response");
1561        let allocation_bytes = candid::encode_one(&allocation).expect("encode allocation");
1562        let created_bytes = candid::encode_one(&created).expect("encode created allocation");
1563
1564        assert_eq!(
1565            candid::decode_one::<RootComponentRegistryPreparationRequest>(&request_bytes)
1566                .expect("decode request"),
1567            request
1568        );
1569        assert_eq!(
1570            candid::decode_one::<RootComponentRegistryStatusResponse>(&response_bytes)
1571                .expect("decode response"),
1572            response
1573        );
1574        assert_eq!(
1575            candid::decode_one::<RootComponentAllocationResponse>(&allocation_bytes)
1576                .expect("decode allocation"),
1577            allocation
1578        );
1579        assert_eq!(
1580            candid::decode_one::<RootComponentAllocationResponse>(&created_bytes)
1581                .expect("decode created allocation"),
1582            created
1583        );
1584    }
1585
1586    #[test]
1587    fn component_commit_response_round_trips_through_candid() {
1588        let root = Principal::from_slice(&[6; 29]);
1589        let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1590        let component_spec: ComponentSpecId = "projects".parse().expect("Component Spec ID");
1591        let release_set = FleetSubnetRootReleaseSet {
1592            release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
1593                [7; 32],
1594            )),
1595            manifest_digest: ReleaseSetDigest::from_bytes([8; 32]),
1596        };
1597        let provisioning_origin = ComponentProvisioningOrigin::FleetAdministrator {
1598            caller: Principal::from_slice(&[13; 29]),
1599        };
1600        let binding = ComponentBinding {
1601            authority: fleet_registry_authority(),
1602            component,
1603            component_spec: component_spec.clone(),
1604            spec_hash: [12; 32],
1605            role: CanisterRole::new("project_hub"),
1606            placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
1607            fleet_subnet_root: root,
1608            canister_id: Principal::from_slice(&[16; 29]),
1609        };
1610        let head = ComponentRegistryHead {
1611            component,
1612            revision: 1,
1613            content_hash: [18; 32],
1614        };
1615        let committed = RootComponentCommitResponse {
1616            allocation: RootComponentAllocationResponse {
1617                operation_id: [10; 32],
1618                allocation_sequence: 1,
1619                component,
1620                component_spec,
1621                spec_hash: binding.spec_hash,
1622                role: binding.role.clone(),
1623                provisioning_origin: provisioning_origin.clone(),
1624                release_set,
1625                phase: RootComponentAllocationPhase::Committed,
1626                creation: Some(RootComponentCreationEvidence {
1627                    wasm_store: Principal::from_slice(&[14; 29]),
1628                    payload_hash: [15; 32],
1629                    payload_size_bytes: 4_096,
1630                    initial_cycles: Cycles::new(5_000_000_000_000),
1631                    controller: root,
1632                    canister: Some(binding.canister_id),
1633                }),
1634                installation: Some(RootComponentInstallEvidence {
1635                    raw_module_hash: [20; 32],
1636                    chunk_hashes: vec![vec![21; 32]],
1637                    binding: binding.clone(),
1638                }),
1639            },
1640            registry: ComponentRegistryPartitionResponse {
1641                head: head.clone(),
1642                binding: binding.clone(),
1643                protocol_profile_digest: ProtocolProfileDigest::from_bytes([24; 32]),
1644                provisioning_origin,
1645                release_set,
1646                status: ComponentLifecycleStatus::Prepared,
1647                reserved_descendants: 0,
1648                committed_descendants: 0,
1649                encoded_bytes: 2_048,
1650            },
1651            directory: ComponentDirectoryHead {
1652                provenance: ComponentDirectoryProvenance {
1653                    component: binding,
1654                    source_fleet_subnet_root: root,
1655                    component_registry_revision: head.revision,
1656                    component_registry_content_hash: head.content_hash,
1657                    synchronized_at_ns: 19,
1658                },
1659                descendant_count: 0,
1660            },
1661        };
1662        let committed_bytes = candid::encode_one(&committed).expect("encode committed allocation");
1663
1664        assert_eq!(
1665            candid::decode_one::<RootComponentCommitResponse>(&committed_bytes)
1666                .expect("decode committed allocation"),
1667            committed
1668        );
1669    }
1670
1671    #[test]
1672    fn component_directory_page_contracts_round_trip_through_candid() {
1673        let root = Principal::from_slice(&[6; 29]);
1674        let component = ComponentInstanceId::from_generated_bytes([11; 32]);
1675        let binding = ComponentBinding {
1676            authority: fleet_registry_authority(),
1677            component,
1678            component_spec: "projects".parse().expect("Component Spec ID"),
1679            spec_hash: [12; 32],
1680            role: CanisterRole::new("project_hub"),
1681            placement_subnet: SubnetId::from_principal(Principal::from_slice(&[17; 29])),
1682            fleet_subnet_root: root,
1683            canister_id: Principal::from_slice(&[16; 29]),
1684        };
1685        let directory = ComponentDirectoryHead {
1686            provenance: ComponentDirectoryProvenance {
1687                component: binding.clone(),
1688                source_fleet_subnet_root: root,
1689                component_registry_revision: 3,
1690                component_registry_content_hash: [18; 32],
1691                synchronized_at_ns: 19,
1692            },
1693            descendant_count: 1,
1694        };
1695        let request = ComponentDirectoryPageRequest {
1696            directory: directory.clone(),
1697            parent_canister_id: Some(binding.canister_id),
1698            role: Some(CanisterRole::new("project_instance")),
1699            status: Some(ComponentLifecycleStatus::Active),
1700            cursor: Some(ComponentDirectoryPageCursor(vec![20; 64])),
1701            limit: 50,
1702        };
1703        let response = ComponentDirectoryPageResponse {
1704            directory,
1705            entries: vec![ComponentDirectoryChildEntry {
1706                binding: ComponentChildBinding {
1707                    component: binding.clone(),
1708                    parent_canister_id: binding.canister_id,
1709                    role: CanisterRole::new("project_instance"),
1710                    canister_id: Principal::from_slice(&[21; 29]),
1711                },
1712                kind: ComponentChildKind::Instance,
1713                installed_artifact_hash: [22; 32],
1714                protocol_profile_digest: ProtocolProfileDigest::from_bytes([24; 32]),
1715                status: ComponentLifecycleStatus::Active,
1716            }],
1717            next_cursor: Some(ComponentDirectoryPageCursor(vec![23; 64])),
1718        };
1719        let request_bytes = candid::encode_one(&request).expect("encode Directory page request");
1720        let response_bytes = candid::encode_one(&response).expect("encode Directory page response");
1721
1722        assert_eq!(
1723            candid::decode_one::<ComponentDirectoryPageRequest>(&request_bytes)
1724                .expect("decode Directory page request"),
1725            request
1726        );
1727        assert_eq!(
1728            candid::decode_one::<ComponentDirectoryPageResponse>(&response_bytes)
1729                .expect("decode Directory page response"),
1730            response
1731        );
1732    }
1733
1734    fn fleet_registry_authority() -> FleetRegistryAuthority {
1735        FleetRegistryAuthority {
1736            binding: FleetCoordinatorBinding {
1737                fleet: crate::ids::FleetBinding {
1738                    fleet: FleetKey {
1739                        canonical_network_id: CanonicalNetworkId::ic_mainnet(),
1740                        fleet_id: FleetId::from_generated_bytes([1; 32]),
1741                    },
1742                    app: AppId::from("toko"),
1743                },
1744                coordinator_subnet: SubnetId::from_principal(Principal::from_slice(&[2; 29])),
1745                coordinator: Principal::from_slice(&[3; 29]),
1746            },
1747            epoch: 1,
1748        }
1749    }
1750
1751    #[test]
1752    fn component_creation_request_round_trips_through_candid() {
1753        let request = RootComponentCreationRequest {
1754            operation_id: [10; 32],
1755        };
1756        let bytes = candid::encode_one(request).expect("encode creation request");
1757
1758        assert_eq!(
1759            candid::decode_one::<RootComponentCreationRequest>(&bytes)
1760                .expect("decode creation request"),
1761            request
1762        );
1763    }
1764
1765    #[test]
1766    fn peer_component_provisioning_origin_round_trips_through_candid() {
1767        let authority = fleet_registry_authority();
1768        let requester_spec: ComponentSpecId =
1769            "projects".parse().expect("requester Component Spec ID");
1770        let target_spec: ComponentSpecId = "users".parse().expect("target Component Spec ID");
1771        let origin = ComponentProvisioningOrigin::Component {
1772            requester: Box::new(ComponentBinding {
1773                authority,
1774                component: ComponentInstanceId::from_generated_bytes([20; 32]),
1775                component_spec: requester_spec.clone(),
1776                spec_hash: [21; 32],
1777                role: CanisterRole::new("project_hub"),
1778                placement_subnet: SubnetId::from_principal(Principal::from_slice(&[22; 29])),
1779                fleet_subnet_root: Principal::from_slice(&[23; 29]),
1780                canister_id: Principal::from_slice(&[24; 29]),
1781            }),
1782            grant: Box::new(crate::config::ComponentProvisioningGrant {
1783                requester_component_spec: requester_spec,
1784                target_component_spec: target_spec,
1785                maximum_instances_per_requester_per_root: 3,
1786            }),
1787        };
1788        let bytes = candid::encode_one(&origin).expect("encode peer provisioning origin");
1789
1790        assert_eq!(
1791            candid::decode_one::<ComponentProvisioningOrigin>(&bytes)
1792                .expect("decode peer provisioning origin"),
1793            origin
1794        );
1795    }
1796
1797    #[test]
1798    fn cross_root_peer_request_and_origin_round_trip_through_candid() {
1799        let authority = fleet_registry_authority();
1800        let requester_spec: ComponentSpecId =
1801            "projects".parse().expect("requester Component Spec ID");
1802        let target_spec: ComponentSpecId = "users".parse().expect("target Component Spec ID");
1803        let registry = FleetRegistryVersion {
1804            authority: authority.clone(),
1805            revision: 7,
1806            content_hash: [30; 32],
1807        };
1808        let request = RootPeerComponentAllocationRequest {
1809            operation_id: [31; 32],
1810            component_spec: target_spec.clone(),
1811            requester: PeerComponentRequester::FleetService {
1812                service: "projects".parse().expect("Fleet service ID"),
1813                expected_registry: Box::new(registry.clone()),
1814            },
1815        };
1816        let origin = ComponentProvisioningOrigin::FleetServiceComponent {
1817            requester: Box::new(FleetServiceComponentRequester {
1818                service: "projects".parse().expect("Fleet service ID"),
1819                member_purpose: FleetServiceMemberPurpose::PoolMember,
1820                group_placement: ComponentGroupPlacementId {
1821                    deployment: "project_hubs".parse().expect("deployment ID"),
1822                    ordinal: 2,
1823                },
1824                member_path: ComponentGroupMemberPath::try_from(vec![
1825                    "hub".parse().expect("member ID"),
1826                ])
1827                .expect("member path"),
1828                component: ComponentBinding {
1829                    authority,
1830                    component: ComponentInstanceId::from_generated_bytes([32; 32]),
1831                    component_spec: requester_spec.clone(),
1832                    spec_hash: [33; 32],
1833                    role: CanisterRole::new("project_hub"),
1834                    placement_subnet: SubnetId::from_principal(Principal::from_slice(&[34; 29])),
1835                    fleet_subnet_root: Principal::from_slice(&[35; 29]),
1836                    canister_id: Principal::from_slice(&[36; 29]),
1837                },
1838            }),
1839            registry: Box::new(registry),
1840            grant: Box::new(crate::config::ComponentProvisioningGrant {
1841                requester_component_spec: requester_spec,
1842                target_component_spec: target_spec,
1843                maximum_instances_per_requester_per_root: 3,
1844            }),
1845        };
1846        let request_bytes = candid::encode_one(&request).expect("encode cross-root request");
1847        let origin_bytes = candid::encode_one(&origin).expect("encode cross-root origin");
1848
1849        assert_eq!(
1850            candid::decode_one::<RootPeerComponentAllocationRequest>(&request_bytes)
1851                .expect("decode cross-root request"),
1852            request
1853        );
1854        assert_eq!(
1855            candid::decode_one::<ComponentProvisioningOrigin>(&origin_bytes)
1856                .expect("decode cross-root origin"),
1857            origin
1858        );
1859    }
1860
1861    #[test]
1862    fn component_group_provisioning_origin_round_trips_through_candid() {
1863        let origin = ComponentProvisioningOrigin::ComponentGroup {
1864            operation_id: [25; 32],
1865            plan_hash: [26; 32],
1866            group_placement: ComponentGroupPlacementId {
1867                deployment: "cells".parse().expect("deployment ID"),
1868                ordinal: 3,
1869            },
1870            member_path: ComponentGroupMemberPath::try_from(vec![
1871                "api".parse().expect("member ID"),
1872            ])
1873            .expect("member path"),
1874        };
1875        let bytes = candid::encode_one(&origin).expect("encode group provisioning origin");
1876
1877        assert_eq!(
1878            candid::decode_one::<ComponentProvisioningOrigin>(&bytes)
1879                .expect("decode group provisioning origin"),
1880            origin
1881        );
1882    }
1883
1884    #[test]
1885    #[expect(
1886        clippy::too_many_lines,
1887        reason = "one Candid contract test covers every subtree-removal phase receipt"
1888    )]
1889    fn component_subtree_removal_contracts_round_trip_through_candid() {
1890        let component = ComponentInstanceId::from_generated_bytes([41; 32]);
1891        let registry = ComponentRegistryHead {
1892            component,
1893            revision: 7,
1894            content_hash: [42; 32],
1895        };
1896        let request = RootComponentSubtreeRemovalRequest {
1897            operation_id: [43; 32],
1898            component,
1899            target_canister_id: Principal::from_slice(&[44; 29]),
1900            expected_registry: registry.clone(),
1901        };
1902        let status_request = RootComponentSubtreeRemovalStatusRequest {
1903            operation_id: request.operation_id,
1904            component,
1905        };
1906        let advance_request = RootComponentSubtreeRemovalAdvanceRequest {
1907            operation_id: request.operation_id,
1908            component,
1909            expected_traversal_steps: 1,
1910        };
1911        let stop_request = RootComponentSubtreeRemovalStopPreparationRequest {
1912            operation_id: request.operation_id,
1913            component,
1914            expected_traversal_steps: 2,
1915            expected_leaf_canister_id: Principal::from_slice(&[46; 29]),
1916            expected_leaf_parent_canister_id: request.target_canister_id,
1917        };
1918        let stopped = RootComponentSubtreeRemovalStoppedReceipt {
1919            observed_module_hash: [49; 32],
1920            stop: RootComponentSubtreeRemovalStopIntent {
1921                controller: Principal::from_slice(&[48; 29]),
1922                leaf: RootComponentSubtreeRemovalNode {
1923                    canister_id: Principal::from_slice(&[46; 29]),
1924                    parent_canister_id: request.target_canister_id,
1925                    role: CanisterRole::new("project_ledger"),
1926                    kind: ComponentChildKind::Singleton,
1927                    installed_artifact_hash: [47; 32],
1928                    status: ComponentLifecycleStatus::Active,
1929                },
1930            },
1931        };
1932        let response = RootComponentSubtreeRemovalResponse {
1933            operation_id: request.operation_id,
1934            component,
1935            target_canister_id: request.target_canister_id,
1936            target_parent_canister_id: Principal::from_slice(&[45; 29]),
1937            target_role: CanisterRole::new("project_instance"),
1938            target_status: ComponentLifecycleStatus::Active,
1939            reserved_against_registry: registry,
1940            maximum_completed_leaves: 4,
1941            completed_leaves: 1,
1942            traversal_steps: 2,
1943            phase: RootComponentSubtreeRemovalPhase::DirectorySynchronized(
1944                RootComponentSubtreeRemovalDirectorySynchronizedReceipt {
1945                    membership_removed: RootComponentSubtreeRemovalMembershipRemovedReceipt {
1946                        deleted: RootComponentSubtreeRemovalDeletedReceipt {
1947                            deletion: RootComponentSubtreeRemovalDeleteIntent { stopped },
1948                        },
1949                        removed_from_registry: ComponentRegistryHead {
1950                            component,
1951                            revision: 8,
1952                            content_hash: [50; 32],
1953                        },
1954                        previous_descendant_content_hash: [51; 32],
1955                        previous_committed_descendants: 4,
1956                        registry: ComponentRegistryHead {
1957                            component,
1958                            revision: 9,
1959                            content_hash: [52; 32],
1960                        },
1961                        descendant_content_hash: [53; 32],
1962                        registry_encoded_bytes: 4_096,
1963                        reserved_descendants: 1,
1964                        committed_descendants: 3,
1965                        directory_synchronized_at_ns: 54,
1966                        directory_authority_hash: [55; 32],
1967                        parent_role_instances: 0,
1968                        root_managed_descendants: 4,
1969                        root_known_created_component_canisters: 4,
1970                    },
1971                    covered_fleet_registry_revision: 6,
1972                    covered_fleet_registry_content_hash: [56; 32],
1973                    covered_component_registry: ComponentRegistryHead {
1974                        component,
1975                        revision: 9,
1976                        content_hash: [52; 32],
1977                    },
1978                    covered_authority_hash: [55; 32],
1979                    owning_component: Some(
1980                        RootComponentSubtreeRemovalDirectoryConvergenceEvidence {
1981                            operation_id: [57; 32],
1982                            canister_id: Principal::from_slice(&[58; 29]),
1983                            activation: ComponentRuntimeActivationEvidence {
1984                                directory_authority_hash: [59; 32],
1985                                activated_at_ns: 60,
1986                            },
1987                        },
1988                    ),
1989                    parent: Some(RootComponentSubtreeRemovalDirectoryConvergenceEvidence {
1990                        operation_id: [61; 32],
1991                        canister_id: request.target_canister_id,
1992                        activation: ComponentRuntimeActivationEvidence {
1993                            directory_authority_hash: [62; 32],
1994                            activated_at_ns: 63,
1995                        },
1996                    }),
1997                },
1998            ),
1999        };
2000
2001        let request_bytes = candid::encode_one(&request).expect("encode subtree removal request");
2002        let advance_bytes =
2003            candid::encode_one(advance_request).expect("encode subtree removal advance request");
2004        let stop_bytes =
2005            candid::encode_one(stop_request).expect("encode subtree removal stop request");
2006        let status_bytes =
2007            candid::encode_one(status_request).expect("encode subtree removal status request");
2008        let response_bytes =
2009            candid::encode_one(&response).expect("encode subtree removal response");
2010
2011        assert_eq!(
2012            candid::decode_one::<RootComponentSubtreeRemovalRequest>(&request_bytes)
2013                .expect("decode subtree removal request"),
2014            request
2015        );
2016        assert_eq!(
2017            candid::decode_one::<RootComponentSubtreeRemovalAdvanceRequest>(&advance_bytes)
2018                .expect("decode subtree removal advance request"),
2019            advance_request
2020        );
2021        assert_eq!(
2022            candid::decode_one::<RootComponentSubtreeRemovalStopPreparationRequest>(&stop_bytes)
2023                .expect("decode subtree removal stop request"),
2024            stop_request
2025        );
2026        assert_eq!(
2027            candid::decode_one::<RootComponentSubtreeRemovalStatusRequest>(&status_bytes)
2028                .expect("decode subtree removal status request"),
2029            status_request
2030        );
2031        assert_eq!(
2032            candid::decode_one::<RootComponentSubtreeRemovalResponse>(&response_bytes)
2033                .expect("decode subtree removal response"),
2034            response
2035        );
2036
2037        let mut quiescent_owner_response = response;
2038        let RootComponentSubtreeRemovalPhase::DirectorySynchronized(receipt) =
2039            &mut quiescent_owner_response.phase
2040        else {
2041            panic!("Directory-synchronized response");
2042        };
2043        receipt.owning_component = None;
2044        let quiescent_owner_bytes = candid::encode_one(&quiescent_owner_response)
2045            .expect("encode quiescent-owner subtree response");
2046        assert_eq!(
2047            candid::decode_one::<RootComponentSubtreeRemovalResponse>(&quiescent_owner_bytes)
2048                .expect("decode quiescent-owner subtree response"),
2049            quiescent_owner_response
2050        );
2051    }
2052
2053    #[test]
2054    fn component_draining_contracts_round_trip_through_candid() {
2055        let component = ComponentInstanceId::from_generated_bytes([60; 32]);
2056        let previous_registry = ComponentRegistryHead {
2057            component,
2058            revision: 7,
2059            content_hash: [61; 32],
2060        };
2061        let request = RootComponentDrainingRequest {
2062            operation_id: [62; 32],
2063            component,
2064            expected_registry: previous_registry.clone(),
2065        };
2066        let status_request = RootComponentDrainingStatusRequest {
2067            operation_id: request.operation_id,
2068            component,
2069        };
2070        let response = RootComponentDrainingResponse {
2071            operation_id: request.operation_id,
2072            component,
2073            previous_registry,
2074            registry: ComponentRegistryHead {
2075                component,
2076                revision: 8,
2077                content_hash: [63; 32],
2078            },
2079            descendant_count: 20_000,
2080            descendant_content_hash: [64; 32],
2081            directory_authority_hash: [65; 32],
2082            started_at_ns: 66,
2083        };
2084
2085        let request_bytes =
2086            candid::encode_one(&request).expect("encode Component draining request");
2087        let status_bytes =
2088            candid::encode_one(status_request).expect("encode Component draining status request");
2089        let response_bytes =
2090            candid::encode_one(&response).expect("encode Component draining response");
2091
2092        assert_eq!(
2093            candid::decode_one::<RootComponentDrainingRequest>(&request_bytes)
2094                .expect("decode Component draining request"),
2095            request
2096        );
2097        assert_eq!(
2098            candid::decode_one::<RootComponentDrainingStatusRequest>(&status_bytes)
2099                .expect("decode Component draining status request"),
2100            status_request
2101        );
2102        assert_eq!(
2103            candid::decode_one::<RootComponentDrainingResponse>(&response_bytes)
2104                .expect("decode Component draining response"),
2105            response
2106        );
2107    }
2108
2109    #[test]
2110    fn component_quiescence_contracts_round_trip_through_candid() {
2111        let component = ComponentInstanceId::from_generated_bytes([67; 32]);
2112        let registry = ComponentRegistryHead {
2113            component,
2114            revision: 9,
2115            content_hash: [68; 32],
2116        };
2117        let request = RootComponentQuiescenceRequest {
2118            operation_id: [69; 32],
2119            component,
2120            expected_registry: registry.clone(),
2121        };
2122        let status_request = RootComponentQuiescenceStatusRequest {
2123            operation_id: request.operation_id,
2124            component,
2125        };
2126        let stop = RootComponentQuiescenceStopIntent {
2127            registry,
2128            descendant_count: 20_000,
2129            descendant_content_hash: [70; 32],
2130            canister_id: Principal::from_slice(&[71; 29]),
2131            controller: Principal::from_slice(&[72; 29]),
2132            expected_module_hash: [73; 32],
2133            covered_fleet_registry_revision: 10,
2134            covered_fleet_registry_content_hash: [74; 32],
2135            covered_authority_hash: [75; 32],
2136            runtime_operation_id: [76; 32],
2137            activation: ComponentRuntimeActivationEvidence {
2138                directory_authority_hash: [77; 32],
2139                activated_at_ns: 78,
2140            },
2141            prepared_at_ns: 79,
2142        };
2143        let response = RootComponentQuiescenceResponse {
2144            operation_id: request.operation_id,
2145            component,
2146            phase: RootComponentQuiescencePhase::Quiescent(RootComponentQuiescentReceipt {
2147                stop,
2148                observed_module_hash: [73; 32],
2149                quiesced_at_ns: 80,
2150            }),
2151        };
2152
2153        let request_bytes = candid::encode_one(&request).expect("encode quiescence request");
2154        let status_bytes =
2155            candid::encode_one(status_request).expect("encode quiescence status request");
2156        let response_bytes = candid::encode_one(&response).expect("encode quiescence response");
2157        assert_eq!(
2158            candid::decode_one::<RootComponentQuiescenceRequest>(&request_bytes)
2159                .expect("decode quiescence request"),
2160            request
2161        );
2162        assert_eq!(
2163            candid::decode_one::<RootComponentQuiescenceStatusRequest>(&status_bytes)
2164                .expect("decode quiescence status request"),
2165            status_request
2166        );
2167        assert_eq!(
2168            candid::decode_one::<RootComponentQuiescenceResponse>(&response_bytes)
2169                .expect("decode quiescence response"),
2170            response
2171        );
2172    }
2173
2174    #[test]
2175    fn component_draining_advance_contracts_round_trip_through_candid() {
2176        let component = ComponentInstanceId::from_generated_bytes([81; 32]);
2177        let registry = ComponentRegistryHead {
2178            component,
2179            revision: 12,
2180            content_hash: [82; 32],
2181        };
2182        let request = RootComponentDrainingAdvanceRequest {
2183            operation_id: [83; 32],
2184            component,
2185        };
2186        let descendant_removal = RootComponentDrainingAdvanceResponse {
2187            operation_id: request.operation_id,
2188            component,
2189            phase: RootComponentDrainingAdvancePhase::DescendantRemoval(
2190                RootComponentSubtreeRemovalResponse {
2191                    operation_id: [84; 32],
2192                    component,
2193                    target_canister_id: Principal::from_slice(&[85; 29]),
2194                    target_parent_canister_id: Principal::from_slice(&[86; 29]),
2195                    target_role: CanisterRole::new("project_instance"),
2196                    target_status: ComponentLifecycleStatus::Active,
2197                    reserved_against_registry: registry.clone(),
2198                    maximum_completed_leaves: 20_000,
2199                    completed_leaves: 0,
2200                    traversal_steps: 0,
2201                    phase: RootComponentSubtreeRemovalPhase::Fenced,
2202                },
2203            ),
2204        };
2205        let descendants_empty = RootComponentDrainingAdvanceResponse {
2206            operation_id: request.operation_id,
2207            component,
2208            phase: RootComponentDrainingAdvancePhase::DescendantsEmpty(
2209                RootComponentDrainingDescendantsEmpty {
2210                    registry,
2211                    descendant_content_hash: [87; 32],
2212                },
2213            ),
2214        };
2215
2216        let request_bytes =
2217            candid::encode_one(request).expect("encode Component draining advance request");
2218        let removal_bytes = candid::encode_one(&descendant_removal)
2219            .expect("encode Component draining removal response");
2220        let empty_bytes = candid::encode_one(&descendants_empty)
2221            .expect("encode Component draining empty response");
2222
2223        assert_eq!(
2224            candid::decode_one::<RootComponentDrainingAdvanceRequest>(&request_bytes)
2225                .expect("decode Component draining advance request"),
2226            request
2227        );
2228        assert_eq!(
2229            candid::decode_one::<RootComponentDrainingAdvanceResponse>(&removal_bytes)
2230                .expect("decode Component draining removal response"),
2231            descendant_removal
2232        );
2233        assert_eq!(
2234            candid::decode_one::<RootComponentDrainingAdvanceResponse>(&empty_bytes)
2235                .expect("decode Component draining empty response"),
2236            descendants_empty
2237        );
2238    }
2239
2240    #[test]
2241    #[expect(
2242        clippy::too_many_lines,
2243        reason = "one wire-contract test keeps final inventory and its deletion authority aligned"
2244    )]
2245    fn component_final_inventory_contracts_round_trip_through_candid() {
2246        let component = ComponentInstanceId::from_generated_bytes([88; 32]);
2247        let registry = ComponentRegistryHead {
2248            component,
2249            revision: 21,
2250            content_hash: [89; 32],
2251        };
2252        let request = RootComponentFinalInventoryRequest {
2253            operation_id: [90; 32],
2254            component,
2255            expected_registry: registry.clone(),
2256        };
2257        let inventory = RootComponentFinalInventory {
2258            registry,
2259            descendant_content_hash: [91; 32],
2260            registry_encoded_bytes: 4_096,
2261            directory_synchronized_at_ns: 92,
2262            covered_fleet_registry_revision: 93,
2263            covered_fleet_registry_content_hash: [94; 32],
2264            directory_authority_hash: [95; 32],
2265            inventory_hash: [96; 32],
2266            finalized_at_ns: 97,
2267        };
2268        let response = RootComponentFinalInventoryResponse {
2269            operation_id: request.operation_id,
2270            component,
2271            inventory: inventory.clone(),
2272        };
2273        let deletion_request = RootComponentDeletionRequest {
2274            operation_id: request.operation_id,
2275            component,
2276            expected_inventory_hash: inventory.inventory_hash,
2277        };
2278        let deletion_status_request = RootComponentDeletionStatusRequest {
2279            operation_id: request.operation_id,
2280            component,
2281        };
2282        let deletion = RootComponentDeletionIntent {
2283            final_inventory: inventory,
2284            quiescence: RootComponentQuiescentReceipt {
2285                stop: RootComponentQuiescenceStopIntent {
2286                    registry: response.inventory.registry.clone(),
2287                    descendant_count: 0,
2288                    descendant_content_hash: response.inventory.descendant_content_hash,
2289                    canister_id: Principal::from_slice(&[98; 29]),
2290                    controller: Principal::from_slice(&[99; 29]),
2291                    expected_module_hash: [100; 32],
2292                    covered_fleet_registry_revision: 93,
2293                    covered_fleet_registry_content_hash: [94; 32],
2294                    covered_authority_hash: [101; 32],
2295                    runtime_operation_id: [102; 32],
2296                    activation: ComponentRuntimeActivationEvidence {
2297                        directory_authority_hash: [103; 32],
2298                        activated_at_ns: 104,
2299                    },
2300                    prepared_at_ns: 105,
2301                },
2302                observed_module_hash: [100; 32],
2303                quiesced_at_ns: 106,
2304            },
2305            prepared_at_ns: 107,
2306        };
2307        let deleted_receipt = RootComponentDeletedReceipt {
2308            deletion,
2309            deleted_at_ns: 108,
2310        };
2311        let deletion_response = RootComponentDeletionResponse {
2312            operation_id: request.operation_id,
2313            component,
2314            phase: RootComponentDeletionPhase::Deleted(deleted_receipt.clone()),
2315        };
2316        let membership_removed_response = RootComponentDeletionResponse {
2317            operation_id: request.operation_id,
2318            component,
2319            phase: RootComponentDeletionPhase::MembershipRemoved(
2320                RootComponentMembershipRemovedReceipt {
2321                    deleted: deleted_receipt,
2322                    allocation_operation_id: [109; 32],
2323                    remaining_spec_committed_instances: 2,
2324                    root_committed_component_instances: 3,
2325                    root_known_created_component_canisters: 4,
2326                    root_registry_encoded_bytes: 5_000,
2327                    removed_at_ns: 110,
2328                    removal_hash: [111; 32],
2329                },
2330            ),
2331        };
2332
2333        let request_bytes =
2334            candid::encode_one(&request).expect("encode Component final inventory request");
2335        let response_bytes =
2336            candid::encode_one(&response).expect("encode Component final inventory response");
2337        let deletion_request_bytes =
2338            candid::encode_one(deletion_request).expect("encode Component deletion request");
2339        let deletion_status_bytes = candid::encode_one(deletion_status_request)
2340            .expect("encode Component deletion status request");
2341        let deletion_response_bytes =
2342            candid::encode_one(&deletion_response).expect("encode Component deletion response");
2343        let membership_removed_response_bytes = candid::encode_one(&membership_removed_response)
2344            .expect("encode Component membership-removal response");
2345        assert_eq!(
2346            candid::decode_one::<RootComponentFinalInventoryRequest>(&request_bytes)
2347                .expect("decode Component final inventory request"),
2348            request
2349        );
2350        assert_eq!(
2351            candid::decode_one::<RootComponentFinalInventoryResponse>(&response_bytes)
2352                .expect("decode Component final inventory response"),
2353            response
2354        );
2355        assert_eq!(
2356            candid::decode_one::<RootComponentDeletionRequest>(&deletion_request_bytes)
2357                .expect("decode Component deletion request"),
2358            deletion_request
2359        );
2360        assert_eq!(
2361            candid::decode_one::<RootComponentDeletionStatusRequest>(&deletion_status_bytes)
2362                .expect("decode Component deletion status request"),
2363            deletion_status_request
2364        );
2365        assert_eq!(
2366            candid::decode_one::<RootComponentDeletionResponse>(&deletion_response_bytes)
2367                .expect("decode Component deletion response"),
2368            deletion_response
2369        );
2370        assert_eq!(
2371            candid::decode_one::<RootComponentDeletionResponse>(&membership_removed_response_bytes)
2372                .expect("decode Component membership-removal response"),
2373            membership_removed_response
2374        );
2375    }
2376
2377    #[test]
2378    fn component_subtree_removal_deletion_requests_round_trip_through_candid() {
2379        let prepare = RootComponentSubtreeRemovalDeletePreparationRequest {
2380            operation_id: [50; 32],
2381            component: ComponentInstanceId::from_generated_bytes([51; 32]),
2382            expected_traversal_steps: 3,
2383            expected_leaf_canister_id: Principal::from_slice(&[52; 29]),
2384            expected_leaf_parent_canister_id: Principal::from_slice(&[53; 29]),
2385        };
2386        let request = RootComponentSubtreeRemovalDeleteRequest {
2387            operation_id: prepare.operation_id,
2388            component: prepare.component,
2389            expected_traversal_steps: prepare.expected_traversal_steps,
2390            expected_leaf_canister_id: prepare.expected_leaf_canister_id,
2391            expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
2392        };
2393        let membership_request = RootComponentSubtreeRemovalMembershipRemovalRequest {
2394            operation_id: prepare.operation_id,
2395            component: prepare.component,
2396            expected_traversal_steps: prepare.expected_traversal_steps,
2397            expected_leaf_canister_id: prepare.expected_leaf_canister_id,
2398            expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
2399        };
2400        let directory_request = RootComponentSubtreeRemovalDirectorySynchronizationRequest {
2401            operation_id: prepare.operation_id,
2402            component: prepare.component,
2403            expected_traversal_steps: prepare.expected_traversal_steps,
2404            expected_leaf_canister_id: prepare.expected_leaf_canister_id,
2405            expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
2406        };
2407        let finalization_request = RootComponentSubtreeRemovalLeafFinalizationRequest {
2408            operation_id: prepare.operation_id,
2409            component: prepare.component,
2410            expected_traversal_steps: prepare.expected_traversal_steps,
2411            expected_leaf_canister_id: prepare.expected_leaf_canister_id,
2412            expected_leaf_parent_canister_id: prepare.expected_leaf_parent_canister_id,
2413        };
2414        let prepare_bytes = candid::encode_one(prepare)
2415            .expect("encode subtree removal deletion preparation request");
2416        let request_bytes =
2417            candid::encode_one(request).expect("encode subtree removal deletion request");
2418        let membership_request_bytes = candid::encode_one(membership_request)
2419            .expect("encode subtree removal membership-removal request");
2420        let directory_request_bytes = candid::encode_one(directory_request)
2421            .expect("encode subtree removal Directory synchronization request");
2422        let finalization_request_bytes = candid::encode_one(finalization_request)
2423            .expect("encode subtree removal leaf-finalization request");
2424
2425        assert_eq!(
2426            candid::decode_one::<RootComponentSubtreeRemovalDeletePreparationRequest>(
2427                &prepare_bytes
2428            )
2429            .expect("decode subtree removal deletion preparation request"),
2430            prepare
2431        );
2432        assert_eq!(
2433            candid::decode_one::<RootComponentSubtreeRemovalDeleteRequest>(&request_bytes)
2434                .expect("decode subtree removal deletion request"),
2435            request
2436        );
2437        assert_eq!(
2438            candid::decode_one::<RootComponentSubtreeRemovalMembershipRemovalRequest>(
2439                &membership_request_bytes
2440            )
2441            .expect("decode subtree removal membership-removal request"),
2442            membership_request
2443        );
2444        assert_eq!(
2445            candid::decode_one::<RootComponentSubtreeRemovalDirectorySynchronizationRequest>(
2446                &directory_request_bytes
2447            )
2448            .expect("decode subtree removal Directory synchronization request"),
2449            directory_request
2450        );
2451        assert_eq!(
2452            candid::decode_one::<RootComponentSubtreeRemovalLeafFinalizationRequest>(
2453                &finalization_request_bytes
2454            )
2455            .expect("decode subtree removal leaf-finalization request"),
2456            finalization_request
2457        );
2458    }
2459
2460    #[test]
2461    fn component_subtree_removal_stop_request_round_trips_through_candid() {
2462        let request = RootComponentSubtreeRemovalStopRequest {
2463            operation_id: [50; 32],
2464            component: ComponentInstanceId::from_generated_bytes([51; 32]),
2465            expected_traversal_steps: 3,
2466            expected_leaf_canister_id: Principal::from_slice(&[52; 29]),
2467            expected_leaf_parent_canister_id: Principal::from_slice(&[53; 29]),
2468        };
2469        let bytes =
2470            candid::encode_one(request).expect("encode subtree removal stop execution request");
2471
2472        assert_eq!(
2473            candid::decode_one::<RootComponentSubtreeRemovalStopRequest>(&bytes)
2474                .expect("decode subtree removal stop execution request"),
2475            request
2476        );
2477    }
2478
2479    #[test]
2480    #[expect(
2481        clippy::too_many_lines,
2482        reason = "one round-trip test keeps the complete child lifecycle boundary coherent"
2483    )]
2484    fn component_child_lifecycle_contracts_round_trip_through_candid() {
2485        let component = ComponentInstanceId::from_generated_bytes([11; 32]);
2486        let registry = ComponentRegistryHead {
2487            component,
2488            revision: 2,
2489            content_hash: [12; 32],
2490        };
2491        let request = RootComponentChildAllocationRequest {
2492            operation_id: [13; 32],
2493            component,
2494            expected_registry: registry.clone(),
2495            child_role: CanisterRole::new("project_instance"),
2496            application_init_args: Some(vec![9, 8, 7]),
2497        };
2498        let status_request = RootComponentChildAllocationStatusRequest {
2499            operation_id: request.operation_id,
2500            component,
2501        };
2502        let creation_request = RootComponentChildCreationRequest {
2503            operation_id: request.operation_id,
2504            component,
2505        };
2506        let install_request = RootComponentChildInstallRequest {
2507            operation_id: request.operation_id,
2508            component,
2509        };
2510        let commit_request = RootComponentChildCommitRequest {
2511            operation_id: request.operation_id,
2512            component,
2513        };
2514        let directory_request = RootComponentChildDirectoryPreparationRequest {
2515            operation_id: request.operation_id,
2516            component,
2517        };
2518        let activation_request = RootComponentChildRuntimeActivationRequest {
2519            operation_id: request.operation_id,
2520            component,
2521        };
2522        let membership_request = RootComponentChildMembershipActivationRequest {
2523            operation_id: request.operation_id,
2524            component,
2525        };
2526        let root = Principal::from_slice(&[17; 29]);
2527        let parent = Principal::from_slice(&[14; 29]);
2528        let child = Principal::from_slice(&[18; 29]);
2529        let child_binding = ComponentChildBinding {
2530            component: ComponentBinding {
2531                authority: fleet_registry_authority(),
2532                component,
2533                component_spec: "projects".parse().expect("Component Spec"),
2534                spec_hash: [19; 32],
2535                role: CanisterRole::new("project_hub"),
2536                placement_subnet: SubnetId::from_principal(Principal::from_slice(&[20; 29])),
2537                fleet_subnet_root: root,
2538                canister_id: parent,
2539            },
2540            parent_canister_id: parent,
2541            role: request.child_role.clone(),
2542            canister_id: child,
2543        };
2544        let response = RootComponentChildAllocationResponse {
2545            operation_id: request.operation_id,
2546            component,
2547            parent_canister_id: parent,
2548            parent_role: CanisterRole::new("project_hub"),
2549            child_role: request.child_role.clone(),
2550            child_kind: ComponentChildKind::Instance,
2551            maximum_instances_per_parent: 10_000,
2552            maximum_descendants: 20_000,
2553            maximum_registry_bytes: 16_777_216,
2554            reserved_against_registry: registry,
2555            release_set: FleetSubnetRootReleaseSet {
2556                release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
2557                    [15; 32],
2558                )),
2559                manifest_digest: ReleaseSetDigest::from_bytes([16; 32]),
2560            },
2561            phase: RootComponentAllocationPhase::Verified,
2562            creation: Some(RootComponentCreationEvidence {
2563                wasm_store: Principal::from_slice(&[21; 29]),
2564                payload_hash: [22; 32],
2565                payload_size_bytes: 4_096,
2566                initial_cycles: Cycles::new(5_000_000_000_000),
2567                controller: root,
2568                canister: Some(child),
2569            }),
2570            installation: Some(RootComponentChildInstallEvidence {
2571                raw_module_hash: [23; 32],
2572                chunk_hashes: vec![vec![24; 32]],
2573                binding: child_binding.clone(),
2574            }),
2575        };
2576        let commit_response = RootComponentChildCommitResponse {
2577            allocation: response.clone(),
2578            registry: ComponentRegistryPartitionResponse {
2579                head: ComponentRegistryHead {
2580                    component,
2581                    revision: 3,
2582                    content_hash: [25; 32],
2583                },
2584                binding: child_binding.component.clone(),
2585                protocol_profile_digest: ProtocolProfileDigest::from_bytes([27; 32]),
2586                provisioning_origin: ComponentProvisioningOrigin::FleetAdministrator {
2587                    caller: Principal::from_slice(&[26; 29]),
2588                },
2589                release_set: response.release_set,
2590                status: ComponentLifecycleStatus::Active,
2591                reserved_descendants: 0,
2592                committed_descendants: 1,
2593                encoded_bytes: 8_192,
2594            },
2595            directory: ComponentDirectoryHead {
2596                provenance: ComponentDirectoryProvenance {
2597                    component: child_binding.component.clone(),
2598                    source_fleet_subnet_root: root,
2599                    component_registry_revision: 3,
2600                    component_registry_content_hash: [25; 32],
2601                    synchronized_at_ns: 27,
2602                },
2603                descendant_count: 1,
2604            },
2605        };
2606        let runtime_authority = ComponentRuntimeDirectoryAuthority {
2607            fleet: FleetDirectorySnapshot {
2608                provenance: crate::dto::fleet_registry::FleetDirectoryProvenance {
2609                    registry: FleetRegistryVersion {
2610                        authority: fleet_registry_authority(),
2611                        revision: 4,
2612                        content_hash: [28; 32],
2613                    },
2614                    source_fleet_subnet_root: root,
2615                },
2616                fleet_subnet_roots: vec![
2617                    crate::dto::fleet_registry::FleetSubnetRootDirectoryEntry {
2618                        placement_subnet: commit_response.registry.binding.placement_subnet,
2619                        fleet_subnet_root: root,
2620                        status: crate::dto::fleet_registry::FleetSubnetRootStatus::Active,
2621                    },
2622                ],
2623                services: vec![],
2624            },
2625            component: commit_response.directory.clone(),
2626            component_group: None,
2627        };
2628        let activation = ComponentRuntimeActivationEvidence {
2629            directory_authority_hash: [29; 32],
2630            activated_at_ns: 30,
2631        };
2632        let directory_response = RootComponentChildDirectoryPreparationResponse {
2633            committed: commit_response.clone(),
2634            child: ComponentRuntimeStatusResponse {
2635                operation_id: request.operation_id,
2636                binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
2637                deployment: Box::new(ProtectedComponentDeployment::UngroupedOrdinary {
2638                    binding: child_binding.component.clone(),
2639                }),
2640                phase: ComponentRuntimePhase::DirectoryPrepared,
2641                authority: Some(runtime_authority.clone()),
2642                authority_hash: Some([31; 32]),
2643                direct_children_hash: Some([37; 32]),
2644                activation: None,
2645            },
2646            owning_component: ComponentRuntimeDirectoryConvergenceEvidence {
2647                operation_id: [32; 32],
2648                binding: ManagedCanisterBinding::Component(child_binding.component.clone()),
2649                covered_authority: runtime_authority.clone(),
2650                covered_authority_hash: [31; 32],
2651                activation,
2652            },
2653            parent: None,
2654        };
2655        let activation_response = RootComponentChildRuntimeActivationResponse {
2656            committed: commit_response.clone(),
2657            child: ComponentRuntimeStatusResponse {
2658                operation_id: request.operation_id,
2659                binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
2660                deployment: Box::new(ProtectedComponentDeployment::UngroupedOrdinary {
2661                    binding: child_binding.component.clone(),
2662                }),
2663                phase: ComponentRuntimePhase::Active,
2664                authority: Some(runtime_authority.clone()),
2665                authority_hash: Some([31; 32]),
2666                direct_children_hash: Some([37; 32]),
2667                activation: Some(ComponentRuntimeActivationEvidence {
2668                    directory_authority_hash: [31; 32],
2669                    activated_at_ns: 33,
2670                }),
2671            },
2672        };
2673        let active_directory = ComponentDirectoryHead {
2674            provenance: ComponentDirectoryProvenance {
2675                component: child_binding.component.clone(),
2676                source_fleet_subnet_root: root,
2677                component_registry_revision: 4,
2678                component_registry_content_hash: [34; 32],
2679                synchronized_at_ns: 35,
2680            },
2681            descendant_count: 1,
2682        };
2683        let active_authority = ComponentRuntimeDirectoryAuthority {
2684            fleet: runtime_authority.fleet,
2685            component: active_directory.clone(),
2686            component_group: None,
2687        };
2688        let membership_response = RootComponentChildMembershipActivationResponse {
2689            committed: commit_response.clone(),
2690            registry: ComponentRegistryPartitionResponse {
2691                head: ComponentRegistryHead {
2692                    component,
2693                    revision: 4,
2694                    content_hash: [34; 32],
2695                },
2696                binding: child_binding.component.clone(),
2697                protocol_profile_digest: ProtocolProfileDigest::from_bytes([36; 32]),
2698                provisioning_origin: commit_response.registry.provisioning_origin.clone(),
2699                release_set: commit_response.registry.release_set,
2700                status: ComponentLifecycleStatus::Active,
2701                reserved_descendants: 0,
2702                committed_descendants: 1,
2703                encoded_bytes: 8_256,
2704            },
2705            directory: active_directory,
2706            child: ComponentRuntimeStatusResponse {
2707                operation_id: request.operation_id,
2708                binding: ManagedCanisterBinding::ComponentChild(child_binding.clone()),
2709                deployment: Box::new(ProtectedComponentDeployment::UngroupedOrdinary {
2710                    binding: child_binding.component,
2711                }),
2712                phase: ComponentRuntimePhase::Active,
2713                authority: Some(active_authority),
2714                authority_hash: Some([36; 32]),
2715                direct_children_hash: Some([38; 32]),
2716                activation: Some(ComponentRuntimeActivationEvidence {
2717                    directory_authority_hash: [31; 32],
2718                    activated_at_ns: 33,
2719                }),
2720            },
2721        };
2722
2723        let request_bytes = candid::encode_one(&request).expect("encode child reservation");
2724        let status_bytes =
2725            candid::encode_one(status_request).expect("encode child reservation status");
2726        let creation_bytes =
2727            candid::encode_one(creation_request).expect("encode child creation request");
2728        let install_bytes =
2729            candid::encode_one(install_request).expect("encode child install request");
2730        let response_bytes = candid::encode_one(&response).expect("encode child response");
2731        let commit_request_bytes =
2732            candid::encode_one(commit_request).expect("encode child commit request");
2733        let directory_request_bytes =
2734            candid::encode_one(directory_request).expect("encode child Directory request");
2735        let activation_request_bytes =
2736            candid::encode_one(activation_request).expect("encode child activation request");
2737        let membership_request_bytes =
2738            candid::encode_one(membership_request).expect("encode child membership request");
2739        let commit_response_bytes =
2740            candid::encode_one(&commit_response).expect("encode child commit response");
2741        let directory_response_bytes =
2742            candid::encode_one(&directory_response).expect("encode child Directory response");
2743        let activation_response_bytes =
2744            candid::encode_one(&activation_response).expect("encode child activation response");
2745        let membership_response_bytes =
2746            candid::encode_one(&membership_response).expect("encode child membership response");
2747
2748        assert_eq!(
2749            candid::decode_one::<RootComponentChildAllocationRequest>(&request_bytes)
2750                .expect("decode child reservation"),
2751            request
2752        );
2753        assert_eq!(
2754            candid::decode_one::<RootComponentChildAllocationStatusRequest>(&status_bytes)
2755                .expect("decode child reservation status"),
2756            status_request
2757        );
2758        assert_eq!(
2759            candid::decode_one::<RootComponentChildCreationRequest>(&creation_bytes)
2760                .expect("decode child creation request"),
2761            creation_request
2762        );
2763        assert_eq!(
2764            candid::decode_one::<RootComponentChildInstallRequest>(&install_bytes)
2765                .expect("decode child install request"),
2766            install_request
2767        );
2768        assert_eq!(
2769            candid::decode_one::<RootComponentChildAllocationResponse>(&response_bytes)
2770                .expect("decode child response"),
2771            response
2772        );
2773        assert_eq!(
2774            candid::decode_one::<RootComponentChildCommitRequest>(&commit_request_bytes)
2775                .expect("decode child commit request"),
2776            commit_request
2777        );
2778        assert_eq!(
2779            candid::decode_one::<RootComponentChildDirectoryPreparationRequest>(
2780                &directory_request_bytes
2781            )
2782            .expect("decode child Directory request"),
2783            directory_request
2784        );
2785        assert_eq!(
2786            candid::decode_one::<RootComponentChildCommitResponse>(&commit_response_bytes)
2787                .expect("decode child commit response"),
2788            commit_response
2789        );
2790        assert_eq!(
2791            candid::decode_one::<RootComponentChildDirectoryPreparationResponse>(
2792                &directory_response_bytes
2793            )
2794            .expect("decode child Directory response"),
2795            directory_response
2796        );
2797        assert_eq!(
2798            candid::decode_one::<RootComponentChildRuntimeActivationRequest>(
2799                &activation_request_bytes
2800            )
2801            .expect("decode child activation request"),
2802            activation_request
2803        );
2804        assert_eq!(
2805            candid::decode_one::<RootComponentChildRuntimeActivationResponse>(
2806                &activation_response_bytes
2807            )
2808            .expect("decode child activation response"),
2809            activation_response
2810        );
2811        assert_eq!(
2812            candid::decode_one::<RootComponentChildMembershipActivationRequest>(
2813                &membership_request_bytes
2814            )
2815            .expect("decode child membership request"),
2816            membership_request
2817        );
2818        assert_eq!(
2819            candid::decode_one::<RootComponentChildMembershipActivationResponse>(
2820                &membership_response_bytes
2821            )
2822            .expect("decode child membership response"),
2823            membership_response
2824        );
2825    }
2826
2827    #[test]
2828    fn component_install_request_round_trips_through_candid() {
2829        let request = RootComponentInstallRequest {
2830            operation_id: [10; 32],
2831        };
2832        let bytes = candid::encode_one(request).expect("encode install request");
2833
2834        assert_eq!(
2835            candid::decode_one::<RootComponentInstallRequest>(&bytes)
2836                .expect("decode install request"),
2837            request
2838        );
2839    }
2840
2841    #[test]
2842    fn component_commit_request_round_trips_through_candid() {
2843        let request = RootComponentCommitRequest {
2844            operation_id: [10; 32],
2845        };
2846        let bytes = candid::encode_one(request).expect("encode commit request");
2847
2848        assert_eq!(
2849            candid::decode_one::<RootComponentCommitRequest>(&bytes)
2850                .expect("decode commit request"),
2851            request
2852        );
2853    }
2854
2855    #[test]
2856    fn component_runtime_activation_requests_round_trip_through_candid() {
2857        let root_request = RootComponentRuntimeActivationRequest {
2858            operation_id: [22; 32],
2859        };
2860        let target_request = ComponentRuntimeActivationRequest {
2861            operation_id: root_request.operation_id,
2862            directory_authority_hash: [23; 32],
2863        };
2864        let membership_request = RootComponentMembershipActivationRequest {
2865            operation_id: root_request.operation_id,
2866        };
2867        let root_bytes = candid::encode_one(root_request).expect("encode root activation request");
2868        let target_bytes =
2869            candid::encode_one(target_request).expect("encode target activation request");
2870        let membership_bytes =
2871            candid::encode_one(membership_request).expect("encode membership activation request");
2872
2873        assert_eq!(
2874            candid::decode_one::<RootComponentRuntimeActivationRequest>(&root_bytes)
2875                .expect("decode root activation request"),
2876            root_request
2877        );
2878        assert_eq!(
2879            candid::decode_one::<ComponentRuntimeActivationRequest>(&target_bytes)
2880                .expect("decode target activation request"),
2881            target_request
2882        );
2883        assert_eq!(
2884            candid::decode_one::<RootComponentMembershipActivationRequest>(&membership_bytes)
2885                .expect("decode membership activation request"),
2886            membership_request
2887        );
2888    }
2889}