# Crate Boundaries And Migration Guardrails These rules apply to architecture-migration PRs linked to [`rustfs/backlog#660`](https://github.com/rustfs/backlog/issues/660). ## PR Types Every PR must declare exactly one type: - `docs-only` - `test-only` - `contract` - `api-extraction` - `pure-move` - `consumer-migration` - `dependency-migration` - `security-change` - `behavior-change` - `ci-gate` Do not mix directory movement, security tightening, and behavior changes in one PR. ## Dependency Direction Contract crates must stay below implementation crates. Initial forbidden edges: - `storage-api -> ecstore` - `security-governance -> rustfs` - `extension-schema -> rustfs` - `extension-schema -> ecstore` Existing migration checks live in: - `scripts/check_layer_dependencies.sh` - `scripts/check_architecture_migration_rules.sh` Extend these guardrails instead of adding a parallel system. ## Pre-Push Expert Review Before pushing any PR branch, record three expert reviews in the task notes: | Expert | Required focus | |---|---| | Quality/architecture | Structure, naming, dependency direction, PR type, scope, and over-abstraction risk | | Migration preservation | Startup order, readiness, quorum, reader semantics, AppContext/global fallback, notify/audit lifecycle, IAM/KMS boundaries, and compatibility | | Testing/verification | Focused tests, regression tests, commands run, missing coverage, and whether tests are forcing business-logic drift | Push is allowed only when all three experts return `pass` or `pass-with-nonblocking-follow-up`. Any `blocker` prevents push until the issue is fixed and the relevant review is repeated. ## Temporary Compatibility Code Temporary compatibility code that must be removed later must include a searchable source comment and a cleanup-register entry. Use this source-comment format: ```rust // RUSTFS_COMPAT_TODO(API-005): keep old ecstore::store_api path during storage-api migration. Remove after all consumers use rustfs-storage-api. ``` Rules: - Add the marker only to temporary compatibility paths, not permanent APIs. - Include the task ID in the marker. - State why the compatibility path exists and when it can be removed. - Use this for temporary re-exports, wrappers, fallbacks, legacy action mappings, and old endpoint compatibility layers. - Delete compatibility layers in their own cleanup PR. ## Config Model First `ecstore::config::{Config, KV, KVS}` should move before extension config adapters or config-schema work. First inventory consumers, then decide whether existing `crates/config` is enough or whether a smaller model crate is required. The current decision is recorded in [`config-model-boundary-adr.md`](config-model-boundary-adr.md): use the existing `rustfs-config` package for the pure server-config model, keep persistence and global server-config state in `ecstore`, and preserve the old `rustfs_ecstore::config::*` path with a temporary compatibility marker during the first extraction. ## Loss-Prevention Coverage Architecture migration checks must keep public contract re-exports and ECStore compatibility coverage from silently drifting during cleanup PRs. Required `rustfs-storage-api` public re-exports: - `pub use admin::{DiskSetSelector, StorageAdminApi};` - `pub use bucket::{BucketInfo, BucketOperations, BucketOptions, DeleteBucketOptions, MakeBucketOptions, SRBucketDeleteOp};` - `pub use capability::{CapabilitySnapshotError, CapabilityState, CapabilityStatus};` - `pub use error::{StorageErrorCode, StorageResult};` - `pub use multipart::{CompletePart, ListMultipartsInfo, ListPartsInfo, MultipartInfo, MultipartUploadResult, PartInfo};` - `pub use observability::{MemorySamplingState, ObservabilitySnapshot, ObservabilitySnapshotProvider, PlatformSupport, UserspaceProfilingCapability};` - `pub use object::{HTTPPreconditions, HTTPRangeError, HTTPRangeSpec, ObjectLockRetentionOptions};` - `pub use object::{ExpirationOptions, TransitionedObject};` - `pub use object::{HealOperations, MultipartOperations, NamespaceLocking, ObjectIO, ObjectOperations};` - `pub use object::{ListObjectVersionsInfo, ListObjectsInfo, ListObjectsV2Info, ListOperations, ObjectInfoOrErr};` - `pub use object::{ObjectPreconditionError, ObjectPreconditionPart, ObjectPreconditionState};` - `pub use object::{VersionMarker, WalkOptions, WalkVersionsSortOrder};` - `pub use topology::{DiskCapabilities, TopologyCapabilities, TopologyDisk, TopologyLabels, TopologyPool, TopologySet, TopologySnapshot, TopologySnapshotProvider};` Required `rustfs-concurrency` public workload admission contract re-exports: - `pub use workload::{AdmissionState, WorkloadAdmissionRegistrySnapshot, WorkloadAdmissionSnapshot, WorkloadAdmissionSnapshotProvider, WorkloadClass};` ECStore must keep compile-time coverage for `StorageAdminApi`, `HealOperations`, and the separate `NamespaceLocking` operation group. The old `StorageAPI` aggregate facade must not reappear in production `crates/ecstore/src` or `rustfs/src` code after the storage operation groups have been made explicit. Outer RustFS/IAM consumers must use `rustfs-storage-api` generic list response contracts directly for `ListObjectsV2Info`, `ListObjectVersionsInfo`, and `ObjectInfoOrErr`; ECStore keeps the concrete aliases only for internal implementation and compatibility. Outer RustFS/scanner consumers must use `rustfs-storage-api` operation traits directly for `ObjectIO`, `ObjectOperations`, `ListOperations`, `MultipartOperations`, `HealOperations`, and `NamespaceLocking`; ECStore keeps the concrete compatibility traits only for internal implementation and downstream compatibility. Outer compatibility boundary modules must use `rustfs_ecstore::api` for ECStore public facade surfaces such as layout, storage owner, admin, metrics, notification, capacity, bucket/config helpers, disk/error contracts, global state accessors, RPC constants/clients, reader helpers, tier helpers, and rebalance status contracts. Any non-ECStore `storage_compat.rs` import from `rustfs_ecstore` must route through the `rustfs_ecstore::api` facade. The legacy ECStore root `endpoints` and `disks_layout` compatibility modules must remain crate-private; public layout access goes through `rustfs_ecstore::api::layout`. Facade-covered ECStore root modules must remain crate-private after this boundary is established; outer crates should use `rustfs_ecstore::api::*` instead of legacy root module paths. This includes storage/layout surfaces as well as remaining bitrot, erasure coding, object DTO/reader, event, list, and batch processor root modules once their facade groups exist. ECStore root `global` re-exports must also stay removed once consumers use `rustfs_ecstore::api::global` or crate-internal `crate::global` paths. RustFS root `storage_compat.rs` must expose bucket metadata and quota contracts as explicit aliases only. Broad `metadata`, `metadata_sys`, and `quota` module passthroughs are reserved to narrower app/admin/storage compatibility boundaries that still need module-local owner cleanup. Root runtime storage config initialization and disk endpoint contracts must also stay explicit aliases. The root compatibility boundary must not restore `com`, bare `init`, or grouped `endpoint::Endpoint` passthroughs. RustFS admin `storage_compat.rs` must expose config IO and default initialization through explicit aliases. The admin compatibility boundary must not restore broad `com` or bare `init` passthroughs. RustFS admin and app `storage_compat.rs` bucket-facing compatibility contracts must stay explicitly whitelisted. They must not restore broad bucket module, client object API, client transition API, or storage-class module passthroughs once a local compatibility boundary has narrowed them to specific aliases. RustFS storage `storage_compat.rs` must expose bucket metadata, object-lock, policy, replication, tagging, versioning, object API, and test-only storage-class config contracts through explicit aliases. The storage compatibility boundary must not restore broad `metadata`, `metadata_sys`, `object_lock`, `policy_sys`, `replication`, `tagging`, `utils`, `versioning`, `versioning_sys`, `object_api_utils`, or `com` passthroughs. Scanner, notify, observability, and e2e `storage_compat.rs` boundaries must also stay narrow. Scanner must not restore grouped bucket compatibility exports for target, lifecycle, metadata, replication, or versioning modules. Notify must not restore broad `config`/`global` module imports. Observability must consume data usage through a local DTO projection instead of re-exporting the ECStore data-usage loader. The e2e harness must not restore grouped RPC passthroughs. ECStore ClusterControlPlane read models must stay owned by the crate-private `cluster` module. Public access goes through `rustfs_ecstore::api::cluster` so outer crates cannot depend on ECStore root control-plane internals. Pool-state, local-node storage, and peer-health status projections are part of the same facade boundary and must remain read-only until a later controller slice explicitly wires dynamic health or membership behavior. RustFS startup internals must stay crate-private after the startup owner split. Only `startup_entrypoint` remains a public startup module for the binary entrypoint; IAM bootstrap, optional runtime, and profiling startup shims must not be re-exported as public library modules. Items inside crate-private startup modules must also use crate visibility rather than bare public visibility. ECStore internal consumers must use `rustfs-storage-api` lifecycle helper DTOs directly for `ExpirationOptions` and `TransitionedObject`; ECStore keeps the old lifecycle paths only as downstream compatibility re-exports.