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