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