commit 059927533a174e91d12851eca8c8bc26f4d7eb51
parent 27d111ac59140eb023423a2d0e872650d676de56
Author: triesap <tyson@radroots.org>
Date: Mon, 27 Jul 2026 07:49:11 +0000
release: define approved public package allowlist
- Bind the exact nineteen-package catalog to the synchronized v1 architecture spec.
- Partition local and external ownership across the unchanged lib and sdk repositories.
- Classify every current workspace package while publication remains frozen.
- Reject catalog drift, unclassified members, and private publication with fixtures.
Diffstat:
5 files changed, 494 insertions(+), 54 deletions(-)
diff --git a/contracts/releases/publish_policy.toml b/contracts/releases/publish_policy.toml
@@ -5,26 +5,70 @@ version = "1.0.0-alpha.1"
frozen = true
registry = "crates-io"
final_enablement_step = 305
+spec_id = "radroots.crates.release.v1"
+approved_packages = [
+ "radroots-core",
+ "radroots-identity",
+ "radroots-blossom",
+ "radroots-protocol",
+ "radroots-event",
+ "radroots-event-codec",
+ "radroots-trade",
+ "radroots-signing",
+ "radroots-transport",
+ "radroots-nostr",
+ "radroots-nostr-connect",
+ "radroots-secrets",
+ "radroots-storage",
+ "radroots-storage-sqlite",
+ "radroots-transport-nostr",
+ "radroots-sync",
+ "radroots-geonames",
+ "radroots-sdk",
+ "radroots",
+]
+local_packages = [
+ "radroots-core",
+ "radroots-identity",
+ "radroots-blossom",
+ "radroots-protocol",
+ "radroots-event",
+ "radroots-event-codec",
+ "radroots-trade",
+ "radroots-signing",
+ "radroots-transport",
+ "radroots-nostr",
+ "radroots-nostr-connect",
+ "radroots-secrets",
+ "radroots-storage",
+ "radroots-storage-sqlite",
+ "radroots-transport-nostr",
+ "radroots-sync",
+ "radroots-geonames",
+]
+external_packages = ["radroots-sdk", "radroots"]
-[classification]
-public = [
+[workspace_classification]
+private = [
+ "radroots_authority",
"radroots_blossom",
"radroots_core",
"radroots_event",
"radroots_event_codec",
"radroots_event_index",
+ "radroots_event_store",
"radroots_geocoder",
"radroots_identity",
"radroots_log",
+ "radroots_net",
"radroots_nostr",
"radroots_nostr_accounts",
"radroots_nostr_connect",
+ "radroots_nostr_runtime",
"radroots_nostr_signer",
+ "radroots_outbox",
"radroots_protected_store",
"radroots_protocol_contract_v1",
- "radroots_replica_schema",
- "radroots_replica_store",
- "radroots_replica_sync",
"radroots_runtime",
"radroots_runtime_distribution",
"radroots_runtime_manager",
@@ -33,17 +77,20 @@ public = [
"radroots_secret_vault",
"radroots_sql_core",
"radroots_trade",
+ "radroots_transport",
+ "radroots_transport_nostr",
+ "radroots_transport_publish_protocol",
]
-internal = [
- "radroots_authority",
- "radroots_event_store",
+build_codegen = ["xtask"]
+test_support = ["radroots_test_fixtures"]
+preview = [
"radroots_mesh",
"radroots_mesh_agent_client",
"radroots_mesh_agent_proto",
- "radroots_net",
- "radroots_nostr_runtime",
"radroots_nostrdb",
- "radroots_outbox",
+ "radroots_replica_schema",
+ "radroots_replica_store",
+ "radroots_replica_sync",
"radroots_simplex_agent_proto",
"radroots_simplex_agent_runtime",
"radroots_simplex_agent_store",
@@ -53,44 +100,11 @@ internal = [
"radroots_simplex_smp_crypto",
"radroots_simplex_smp_proto",
"radroots_simplex_smp_transport",
- "radroots_test_fixtures",
"radroots_trade_sp1_guest",
"radroots_trade_sp1_host",
- "radroots_transport",
- "radroots_transport_nostr",
- "radroots_transport_publish_protocol",
"radroots_transport_reticulum",
- "xtask",
]
-deferred = []
retired = []
-yank_only = []
[publish_order]
-crates = [
- "radroots_runtime_distribution",
- "radroots_replica_schema",
- "radroots_protocol_contract_v1",
- "radroots_nostr_connect",
- "radroots_sql_core",
- "radroots_runtime_store",
- "radroots_runtime_paths",
- "radroots_log",
- "radroots_secret_vault",
- "radroots_protected_store",
- "radroots_event_index",
- "radroots_blossom",
- "radroots_core",
- "radroots_runtime_manager",
- "radroots_replica_store",
- "radroots_geocoder",
- "radroots_event",
- "radroots_runtime",
- "radroots_identity",
- "radroots_event_codec",
- "radroots_trade",
- "radroots_replica_sync",
- "radroots_nostr",
- "radroots_nostr_signer",
- "radroots_nostr_accounts",
-]
+crates = []
diff --git a/crates/event_store/contracts/source_maintenance_v1.manifest.json b/crates/event_store/contracts/source_maintenance_v1.manifest.json
@@ -257,8 +257,8 @@
{
"role": "contract_command_authority",
"path": "tools/xtask/src/contract.rs",
- "byte_length": 484443,
- "sha256": "3ca21a35c0fa412ee8788a7593645b6cb30187daae83e0b7e4af5ced6ca00ed6",
+ "byte_length": 499430,
+ "sha256": "866fc167be2e6a618c23ef515fd5e85c64fc60cc6178dca77439dcece64e2505",
"hash_algorithm": "sha256_bytes_v1"
},
{
diff --git a/crates/event_store/contracts/source_maintenance_v1.manifest.sha256 b/crates/event_store/contracts/source_maintenance_v1.manifest.sha256
@@ -1 +1 @@
-e76e00dba774c095c5abdc822bfaa73d287fd45cb1ba990855ce8725d370f619
+45361811f77fffe3882603c13bd67e8cbecc9855d91643e5d4e5edbe90427916
diff --git a/crates/event_store/src/generated/source_maintenance_manifest.rs b/crates/event_store/src/generated/source_maintenance_manifest.rs
@@ -1,8 +1,8 @@
// @generated by `cargo xtask contract source-maintenance-manifest --write`; do not edit.
-pub(crate) const SOURCE_MAINTENANCE_MANIFEST_JSON: &str = "{\n \"schema_version\": 1,\n \"contract_id\": \"radroots_event_store.source_maintenance_v1\",\n \"hook_id\": \"source_maintenance_v1\",\n \"manifest_schema\": {\n \"path\": \"crates/event_store/contracts/source_maintenance_v1.manifest.schema.json\",\n \"byte_length\": 12315,\n \"sha256\": \"ad4a6c8ae9488fc8033792bc6952af04687f312901c1847d8c668a62913bb642\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n \"predecessor\": {\n \"hook_id\": \"food_availability_projection_v1\",\n \"manifest\": {\n \"path\": \"crates/event_store/contracts/food_availability_projection_v1.manifest.json\",\n \"byte_length\": 17455,\n \"sha256\": \"33b93a3c87ce428e8aa6f5e92643c77203d9aa006c53ce96f3562fe6d68ffd23\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n }\n },\n \"migration\": {\n \"version\": 4,\n \"name\": \"source_maintenance\",\n \"up\": {\n \"path\": \"crates/event_store/migrations/0004_source_maintenance.up.sql\",\n \"byte_length\": 19841,\n \"sha256\": \"425dc799f392b87f265a6fb81f89c4a1c7a5db8391ab0380970708cb9c66704d\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n \"down\": {\n \"path\": \"crates/event_store/migrations/0004_source_maintenance.down.sql\",\n \"byte_length\": 5172,\n \"sha256\": \"fe44fd53c51545c08ea479b385e6781079dab70fc63da2a3c205d727a00ce860\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n \"schema_sha256\": \"d526d96ea02be12b4b0aed99e97cfdde17c4474ace67111506a7b900ee78b186\",\n \"catalog\": {\n \"objects\": [\n \"radroots_event_store_source_capacity_delete_guard\",\n \"radroots_event_store_source_capacity_insert_guard\",\n \"radroots_event_store_source_capacity_marker_close_guard\",\n \"radroots_event_store_source_capacity_update_guard\",\n \"radroots_event_store_source_capacity_v1\",\n \"radroots_event_store_source_generation_capacity_advance\",\n \"radroots_event_store_source_generation_capacity_guard\"\n ],\n \"replaced_objects\": [\n \"radroots_event_store_food_availability_image_delete_guard\",\n \"radroots_event_store_food_availability_projection_delete_guard\",\n \"radroots_event_store_source_rebuild_marker_insert_guard\"\n ],\n \"tables\": [\n \"radroots_event_store_source_capacity_v1\"\n ],\n \"fts5_tables\": []\n }\n },\n \"source_maintenance\": {\n \"version\": 1,\n \"event_contract_registry_version\": 7,\n \"capacity_authority_id\": \"radroots_event_store_source_capacity_v1\",\n \"accounting\": {\n \"algorithm\": \"sqlite_cast_blob_octet_sum_v1\",\n \"raw_event_columns\": [\n \"event_id\",\n \"pubkey\",\n \"tags_json\",\n \"content\",\n \"sig\",\n \"raw_json\"\n ],\n \"raw_tag_columns\": [\n \"event_id\",\n \"tag_name\",\n \"tag_value\",\n \"tag_json\"\n ],\n \"nullable_raw_tag_columns\": [\n \"tag_value\"\n ]\n },\n \"limits\": {\n \"raw_events\": 25000,\n \"raw_tags\": 250000,\n \"raw_event_text_bytes\": 67108864,\n \"raw_tag_text_bytes\": 33554432,\n \"retained_source_generations\": 8\n },\n \"reopen_validation\": {\n \"mode\": \"bounded_full_raw_recount_v1\",\n \"raw_event_rejection_scan_bound\": 25001,\n \"raw_tag_rejection_scan_bound\": 250001,\n \"generation_history_validation\": \"bounded_count_plus_active_ordinal_v1\",\n \"retained_generation_rejection_scan_bound\": 9\n },\n \"rebuild_seal\": {\n \"nip09_hook_id\": \"nip09_reconciliation_v1\",\n \"nip09_manifest_sha256\": \"74af832420ffbaa9805e89df3c0b34f126a443e1598f757e3372f407f9003b77\",\n \"food_hook_id\": \"food_availability_projection_v1\",\n \"food_manifest_sha256\": \"33b93a3c87ce428e8aa6f5e92643c77203d9aa006c53ce96f3562fe6d68ffd23\",\n \"food_scope_fingerprint_sha256\": \"8b63c5ddc48a2cc7db69295238b96d5f814dba50427c80b4d0079f061e6d3de0\",\n \"active_generation_authority\": \"radroots_event_store_source_state\",\n \"marker_close_authority\": \"radroots_event_store_source_capacity_marker_close_guard\"\n }\n },\n \"entry_points\": [\n {\n \"role\": \"migration_registry\",\n \"rust_path\": \"radroots_event_store::migrations::EVENT_STORE_MIGRATIONS[3]\"\n },\n {\n \"role\": \"migration_apply_hook\",\n \"rust_path\": \"radroots_event_store::schema::apply_migration_hook\"\n },\n {\n \"role\": \"migration_validation_hook\",\n \"rust_path\": \"radroots_event_store::schema::validate_migration_hook_state\"\n },\n {\n \"role\": \"capacity_query\",\n \"rust_path\": \"radroots_event_store::RadrootsEventStore::source_capacity_v1\"\n },\n {\n \"role\": \"raw_append_preflight\",\n \"rust_path\": \"radroots_event_store::source_maintenance_v1::preflight_unique_raw_source_append_v1\"\n },\n {\n \"role\": \"raw_append_advance\",\n \"rust_path\": \"radroots_event_store::source_maintenance_v1::advance_source_capacity_after_insert_v1\"\n },\n {\n \"role\": \"generation_append_preflight\",\n \"rust_path\": \"radroots_event_store::source_maintenance_v1::preflight_source_generation_append_v1\"\n },\n {\n \"role\": \"generation_rebuild_bind\",\n \"rust_path\": \"radroots_event_store::source_maintenance_v1::bind_source_capacity_to_generation_v1\"\n },\n {\n \"role\": \"sqlite_encoding_preflight\",\n \"rust_path\": \"radroots_event_store::store::validate_main_database_encoding\"\n },\n {\n \"role\": \"source_generation_history_rollback_guard\",\n \"rust_path\": \"radroots_event_store::schema::validate_rollback_preserves_source_generation_history\"\n },\n {\n \"role\": \"result_vector_executor\",\n \"rust_path\": \"source_maintenance_v1_result_vector\"\n }\n ],\n \"source_files\": [\n {\n \"role\": \"workspace_dependency_authority\",\n \"path\": \"Cargo.toml\",\n \"byte_length\": 10725,\n \"sha256\": \"e1975711f349abc7651b29dd1144613865084206eba9f59c92da05ff9dabb6b5\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_store_error_and_limits\",\n \"path\": \"crates/event_store/src/error.rs\",\n \"byte_length\": 19421,\n \"sha256\": \"4772e041cb20a4963afb2f3159804c777e2f2be61bfdb6ee267e7a7c04258972\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"generated_descriptor_registration\",\n \"path\": \"crates/event_store/src/generated.rs\",\n \"byte_length\": 144,\n \"sha256\": \"6b0a8d6f249bd4fc3f878d37cb5e418680f0f1be2d9eec2518dedf03efc47121\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"public_surface\",\n \"path\": \"crates/event_store/src/lib.rs\",\n \"byte_length\": 3844,\n \"sha256\": \"3cd9653bcb752fb3c4442d4904b98a0a6208011a9a238125b7b7073d7f4e312b\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"migration_registry\",\n \"path\": \"crates/event_store/src/migrations.rs\",\n \"byte_length\": 73585,\n \"sha256\": \"a47477d04759ec6e71d14d3d05459864eae78fce0e3c2481a1b03147f04637f7\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_model_public_surface\",\n \"path\": \"crates/event_store/src/model.rs\",\n \"byte_length\": 33617,\n \"sha256\": \"79296b8f263aa06d17005795e4515f769f064ea6fd971eeb1296e1151debaf20\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"source_generation_rebuild_authority\",\n \"path\": \"crates/event_store/src/nip09/reconciliation_v1.rs\",\n \"byte_length\": 184407,\n \"sha256\": \"c455d40fc736e3db264f567c7809af7bd897d89be8a33dfb21667a6ef6b8d6c6\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"schema_migration_and_reopen_authority\",\n \"path\": \"crates/event_store/src/schema.rs\",\n \"byte_length\": 146146,\n \"sha256\": \"93b060e80d3edd73f86208e4bf698fa9d53eaf1eeb04526c9261fb8b5726fb0d\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"public_store_and_transaction_authority\",\n \"path\": \"crates/event_store/src/store.rs\",\n \"byte_length\": 394574,\n \"sha256\": \"db57dc3e35e64c7194683142fe55edba853671a829269449dd2273056dfc3a0e\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"raw_ingest_capacity_authority\",\n \"path\": \"crates/event_store/src/store/protocol_reconciliation_v1.rs\",\n \"byte_length\": 30140,\n \"sha256\": \"210112eeaa6975a3b4fbb97d5c52588f8c6d8d07975e531d39737fd11235de51\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"source_maintenance_runtime\",\n \"path\": \"crates/event_store/src/source_maintenance_v1.rs\",\n \"byte_length\": 51756,\n \"sha256\": \"f8d5b62f0613104aa86658d5bf1baade92c7df83f00ef0cddadd734b9797afca\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"artifact_transaction_authority\",\n \"path\": \"tools/xtask/src/contract/artifact_bundle.rs\",\n \"byte_length\": 38279,\n \"sha256\": \"f326ea57b56d40135f95b6b1e15961f66eed363337180a6d12a5ea903e1a9a29\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_successor_governance\",\n \"path\": \"tools/xtask/src/contract/food_availability_projection.rs\",\n \"byte_length\": 194901,\n \"sha256\": \"01fe9546c95244b4197b3d2a6cec3d72e1f7c48f088bc93f168b5e2e4977b048\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"transitive_predecessor_membership_governance\",\n \"path\": \"tools/xtask/src/contract/nip09_reconciliation.rs\",\n \"byte_length\": 833937,\n \"sha256\": \"0d326a4e459b2026f7ae9fc57a9741def98a2cd1ee6fbdf89713157d964d2547\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"source_maintenance_governance\",\n \"path\": \"tools/xtask/src/contract/source_maintenance.rs\",\n \"byte_length\": 176811,\n \"sha256\": \"c22612787364ae417fd4da582130f5fe934820ba0f40f92a47b4eeeb6dffbf16\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"contract_command_authority\",\n \"path\": \"tools/xtask/src/contract.rs\",\n \"byte_length\": 484443,\n \"sha256\": \"3ca21a35c0fa412ee8788a7593645b6cb30187daae83e0b7e4af5ced6ca00ed6\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"xtask_dispatch_and_release_preflight\",\n \"path\": \"tools/xtask/src/main.rs\",\n \"byte_length\": 14077,\n \"sha256\": \"d815e65241e47143a51dd87d95e014d975be1d9b94075f3920e4812f41188353\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n }\n ],\n \"public_api\": {\n \"inherited_predecessor_symbols\": [\n \"RADROOTS_ADDRESSABLE_TRANSITION_CURSOR_JSON_MAX_BYTES_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_D_TAG_MAX_BYTES_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_FEED_VERSION_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_PAGE_LIMIT_MAX_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_PAGE_RAW_JSON_MAX_BYTES_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_PAGE_SCAN_MAX_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_SCOPE_KIND_MAX_V1\",\n \"RADROOTS_FOOD_AVAILABILITY_PROJECTION_APPLY_PAGE_LIMIT_V1\",\n \"RADROOTS_FOOD_AVAILABILITY_PROJECTION_VERSION_V1\",\n \"RADROOTS_FOOD_AVAILABILITY_SEARCH_QUERY_MAX_BYTES_V1\",\n \"RADROOTS_FOOD_AVAILABILITY_SEARCH_QUERY_MAX_TERMS_V1\",\n \"RadrootsAddressableTransitionCauseV1\",\n \"RadrootsAddressableTransitionCoordinateV1\",\n \"RadrootsAddressableTransitionCursorV1\",\n \"RadrootsAddressableTransitionEventReferenceV1\",\n \"RadrootsAddressableTransitionOriginV1\",\n \"RadrootsAddressableTransitionPageV1\",\n \"RadrootsAddressableTransitionRawHeadDecisionV1\",\n \"RadrootsAddressableTransitionScopeFingerprintV1\",\n \"RadrootsAddressableTransitionScopeV1\",\n \"RadrootsAddressableTransitionV1\",\n \"RadrootsAddressableTransitionVisibilityV1\",\n \"RadrootsCurrentEventVisibilityV1\",\n \"RadrootsCurrentVisibilityDecisionV1\",\n \"RadrootsFoodAvailabilitySearchQueryV1\",\n \"RadrootsFoodAvailabilityStatusFilterV1\",\n \"RadrootsNip09SuppressionEvidenceV1\",\n \"RadrootsNip09SuppressionOutcome\",\n \"RadrootsNip09SuppressionReason\",\n \"RadrootsStoreProducedCanonicalEventV1\",\n \"RadrootsStoredFoodAvailabilityImageV1\",\n \"RadrootsStoredFoodAvailabilityV1\"\n ],\n \"added_symbols\": [\n \"RADROOTS_EVENT_STORE_RAW_EVENT_COUNT_LIMIT_V1\",\n \"RADROOTS_EVENT_STORE_RAW_EVENT_TEXT_BYTES_LIMIT_V1\",\n \"RADROOTS_EVENT_STORE_RAW_TAG_COUNT_LIMIT_V1\",\n \"RADROOTS_EVENT_STORE_RAW_TAG_TEXT_BYTES_LIMIT_V1\",\n \"RADROOTS_EVENT_STORE_RETAINED_SOURCE_GENERATION_LIMIT_V1\",\n \"RadrootsEventStoreSourceCapacityResourceV1\",\n \"RadrootsEventStoreSourceCapacityV1\"\n ],\n \"methods\": [\n \"RadrootsEventStore::source_capacity_v1\",\n \"RadrootsEventStoreSourceCapacityResourceV1::as_str\",\n \"RadrootsEventStoreSourceCapacityV1::source_generation\",\n \"RadrootsEventStoreSourceCapacityV1::raw_event_count\",\n \"RadrootsEventStoreSourceCapacityV1::raw_tag_count\",\n \"RadrootsEventStoreSourceCapacityV1::raw_event_text_bytes\",\n \"RadrootsEventStoreSourceCapacityV1::raw_tag_text_bytes\",\n \"RadrootsEventStoreSourceCapacityV1::raw_high_water_seq\",\n \"RadrootsEventStoreSourceCapacityV1::retained_generation_count\",\n \"RadrootsEventStoreSourceCapacityV1::retained_generation_limit\"\n ],\n \"error_variants\": [\n \"SourceCapacityExceeded\",\n \"SourceGenerationHistoryLimitReached\",\n \"PersistedEphemeralRawEvent\",\n \"SourceCapacityStateDrift\",\n \"SqliteMainDatabaseEncodingNotUtf8\",\n \"RollbackWouldDiscardSourceGenerationHistory\"\n ],\n \"removed_symbols\": [\n \"RadrootsEventStoreReconciliationResource\",\n \"RadrootsEventStoreError::ReconciliationCapacityExceeded\"\n ],\n \"breaking_replacements\": [\n {\n \"removed\": \"RadrootsEventStoreReconciliationResource\",\n \"replacement\": \"RadrootsEventStoreSourceCapacityResourceV1\"\n },\n {\n \"removed\": \"RadrootsEventStoreError::ReconciliationCapacityExceeded\",\n \"replacement\": \"RadrootsEventStoreError::SourceCapacityExceeded\"\n }\n ]\n },\n \"result_vector\": {\n \"canonical_path\": \"contracts/conformance/vectors/event_store/source_maintenance.v1.json\",\n \"mirror_path\": \"crates/event_store/tests/fixtures/source_maintenance.v1.json\",\n \"byte_length\": 16253,\n \"sha256\": \"997aba2604a2b9d199fb87dc9d07942ca50d91863aeadcf3eeacf16d191dd71f\",\n \"hash_algorithm\": \"sha256_bytes_v1\",\n \"executor_id\": \"radroots_event_store.source_maintenance_v1.result_vector_executor.v1\",\n \"executor_path\": \"crates/event_store/tests/source_maintenance_v1_result_vector.rs\",\n \"executor_test\": \"source_maintenance_v1_result_vector\",\n \"executor_byte_length\": 23510,\n \"executor_sha256\": \"a7487afdfe19fc5fc794811d0f0e6035203e1aabcf0a33a1d398f6b3555d38f3\",\n \"executor_hash_algorithm\": \"sha256_bytes_v1\"\n }\n}\n";
+pub(crate) const SOURCE_MAINTENANCE_MANIFEST_JSON: &str = "{\n \"schema_version\": 1,\n \"contract_id\": \"radroots_event_store.source_maintenance_v1\",\n \"hook_id\": \"source_maintenance_v1\",\n \"manifest_schema\": {\n \"path\": \"crates/event_store/contracts/source_maintenance_v1.manifest.schema.json\",\n \"byte_length\": 12315,\n \"sha256\": \"ad4a6c8ae9488fc8033792bc6952af04687f312901c1847d8c668a62913bb642\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n \"predecessor\": {\n \"hook_id\": \"food_availability_projection_v1\",\n \"manifest\": {\n \"path\": \"crates/event_store/contracts/food_availability_projection_v1.manifest.json\",\n \"byte_length\": 17455,\n \"sha256\": \"33b93a3c87ce428e8aa6f5e92643c77203d9aa006c53ce96f3562fe6d68ffd23\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n }\n },\n \"migration\": {\n \"version\": 4,\n \"name\": \"source_maintenance\",\n \"up\": {\n \"path\": \"crates/event_store/migrations/0004_source_maintenance.up.sql\",\n \"byte_length\": 19841,\n \"sha256\": \"425dc799f392b87f265a6fb81f89c4a1c7a5db8391ab0380970708cb9c66704d\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n \"down\": {\n \"path\": \"crates/event_store/migrations/0004_source_maintenance.down.sql\",\n \"byte_length\": 5172,\n \"sha256\": \"fe44fd53c51545c08ea479b385e6781079dab70fc63da2a3c205d727a00ce860\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n \"schema_sha256\": \"d526d96ea02be12b4b0aed99e97cfdde17c4474ace67111506a7b900ee78b186\",\n \"catalog\": {\n \"objects\": [\n \"radroots_event_store_source_capacity_delete_guard\",\n \"radroots_event_store_source_capacity_insert_guard\",\n \"radroots_event_store_source_capacity_marker_close_guard\",\n \"radroots_event_store_source_capacity_update_guard\",\n \"radroots_event_store_source_capacity_v1\",\n \"radroots_event_store_source_generation_capacity_advance\",\n \"radroots_event_store_source_generation_capacity_guard\"\n ],\n \"replaced_objects\": [\n \"radroots_event_store_food_availability_image_delete_guard\",\n \"radroots_event_store_food_availability_projection_delete_guard\",\n \"radroots_event_store_source_rebuild_marker_insert_guard\"\n ],\n \"tables\": [\n \"radroots_event_store_source_capacity_v1\"\n ],\n \"fts5_tables\": []\n }\n },\n \"source_maintenance\": {\n \"version\": 1,\n \"event_contract_registry_version\": 7,\n \"capacity_authority_id\": \"radroots_event_store_source_capacity_v1\",\n \"accounting\": {\n \"algorithm\": \"sqlite_cast_blob_octet_sum_v1\",\n \"raw_event_columns\": [\n \"event_id\",\n \"pubkey\",\n \"tags_json\",\n \"content\",\n \"sig\",\n \"raw_json\"\n ],\n \"raw_tag_columns\": [\n \"event_id\",\n \"tag_name\",\n \"tag_value\",\n \"tag_json\"\n ],\n \"nullable_raw_tag_columns\": [\n \"tag_value\"\n ]\n },\n \"limits\": {\n \"raw_events\": 25000,\n \"raw_tags\": 250000,\n \"raw_event_text_bytes\": 67108864,\n \"raw_tag_text_bytes\": 33554432,\n \"retained_source_generations\": 8\n },\n \"reopen_validation\": {\n \"mode\": \"bounded_full_raw_recount_v1\",\n \"raw_event_rejection_scan_bound\": 25001,\n \"raw_tag_rejection_scan_bound\": 250001,\n \"generation_history_validation\": \"bounded_count_plus_active_ordinal_v1\",\n \"retained_generation_rejection_scan_bound\": 9\n },\n \"rebuild_seal\": {\n \"nip09_hook_id\": \"nip09_reconciliation_v1\",\n \"nip09_manifest_sha256\": \"74af832420ffbaa9805e89df3c0b34f126a443e1598f757e3372f407f9003b77\",\n \"food_hook_id\": \"food_availability_projection_v1\",\n \"food_manifest_sha256\": \"33b93a3c87ce428e8aa6f5e92643c77203d9aa006c53ce96f3562fe6d68ffd23\",\n \"food_scope_fingerprint_sha256\": \"8b63c5ddc48a2cc7db69295238b96d5f814dba50427c80b4d0079f061e6d3de0\",\n \"active_generation_authority\": \"radroots_event_store_source_state\",\n \"marker_close_authority\": \"radroots_event_store_source_capacity_marker_close_guard\"\n }\n },\n \"entry_points\": [\n {\n \"role\": \"migration_registry\",\n \"rust_path\": \"radroots_event_store::migrations::EVENT_STORE_MIGRATIONS[3]\"\n },\n {\n \"role\": \"migration_apply_hook\",\n \"rust_path\": \"radroots_event_store::schema::apply_migration_hook\"\n },\n {\n \"role\": \"migration_validation_hook\",\n \"rust_path\": \"radroots_event_store::schema::validate_migration_hook_state\"\n },\n {\n \"role\": \"capacity_query\",\n \"rust_path\": \"radroots_event_store::RadrootsEventStore::source_capacity_v1\"\n },\n {\n \"role\": \"raw_append_preflight\",\n \"rust_path\": \"radroots_event_store::source_maintenance_v1::preflight_unique_raw_source_append_v1\"\n },\n {\n \"role\": \"raw_append_advance\",\n \"rust_path\": \"radroots_event_store::source_maintenance_v1::advance_source_capacity_after_insert_v1\"\n },\n {\n \"role\": \"generation_append_preflight\",\n \"rust_path\": \"radroots_event_store::source_maintenance_v1::preflight_source_generation_append_v1\"\n },\n {\n \"role\": \"generation_rebuild_bind\",\n \"rust_path\": \"radroots_event_store::source_maintenance_v1::bind_source_capacity_to_generation_v1\"\n },\n {\n \"role\": \"sqlite_encoding_preflight\",\n \"rust_path\": \"radroots_event_store::store::validate_main_database_encoding\"\n },\n {\n \"role\": \"source_generation_history_rollback_guard\",\n \"rust_path\": \"radroots_event_store::schema::validate_rollback_preserves_source_generation_history\"\n },\n {\n \"role\": \"result_vector_executor\",\n \"rust_path\": \"source_maintenance_v1_result_vector\"\n }\n ],\n \"source_files\": [\n {\n \"role\": \"workspace_dependency_authority\",\n \"path\": \"Cargo.toml\",\n \"byte_length\": 10725,\n \"sha256\": \"e1975711f349abc7651b29dd1144613865084206eba9f59c92da05ff9dabb6b5\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_store_error_and_limits\",\n \"path\": \"crates/event_store/src/error.rs\",\n \"byte_length\": 19421,\n \"sha256\": \"4772e041cb20a4963afb2f3159804c777e2f2be61bfdb6ee267e7a7c04258972\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"generated_descriptor_registration\",\n \"path\": \"crates/event_store/src/generated.rs\",\n \"byte_length\": 144,\n \"sha256\": \"6b0a8d6f249bd4fc3f878d37cb5e418680f0f1be2d9eec2518dedf03efc47121\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"public_surface\",\n \"path\": \"crates/event_store/src/lib.rs\",\n \"byte_length\": 3844,\n \"sha256\": \"3cd9653bcb752fb3c4442d4904b98a0a6208011a9a238125b7b7073d7f4e312b\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"migration_registry\",\n \"path\": \"crates/event_store/src/migrations.rs\",\n \"byte_length\": 73585,\n \"sha256\": \"a47477d04759ec6e71d14d3d05459864eae78fce0e3c2481a1b03147f04637f7\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_model_public_surface\",\n \"path\": \"crates/event_store/src/model.rs\",\n \"byte_length\": 33617,\n \"sha256\": \"79296b8f263aa06d17005795e4515f769f064ea6fd971eeb1296e1151debaf20\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"source_generation_rebuild_authority\",\n \"path\": \"crates/event_store/src/nip09/reconciliation_v1.rs\",\n \"byte_length\": 184407,\n \"sha256\": \"c455d40fc736e3db264f567c7809af7bd897d89be8a33dfb21667a6ef6b8d6c6\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"schema_migration_and_reopen_authority\",\n \"path\": \"crates/event_store/src/schema.rs\",\n \"byte_length\": 146146,\n \"sha256\": \"93b060e80d3edd73f86208e4bf698fa9d53eaf1eeb04526c9261fb8b5726fb0d\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"public_store_and_transaction_authority\",\n \"path\": \"crates/event_store/src/store.rs\",\n \"byte_length\": 394574,\n \"sha256\": \"db57dc3e35e64c7194683142fe55edba853671a829269449dd2273056dfc3a0e\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"raw_ingest_capacity_authority\",\n \"path\": \"crates/event_store/src/store/protocol_reconciliation_v1.rs\",\n \"byte_length\": 30140,\n \"sha256\": \"210112eeaa6975a3b4fbb97d5c52588f8c6d8d07975e531d39737fd11235de51\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"source_maintenance_runtime\",\n \"path\": \"crates/event_store/src/source_maintenance_v1.rs\",\n \"byte_length\": 51756,\n \"sha256\": \"f8d5b62f0613104aa86658d5bf1baade92c7df83f00ef0cddadd734b9797afca\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"artifact_transaction_authority\",\n \"path\": \"tools/xtask/src/contract/artifact_bundle.rs\",\n \"byte_length\": 38279,\n \"sha256\": \"f326ea57b56d40135f95b6b1e15961f66eed363337180a6d12a5ea903e1a9a29\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_successor_governance\",\n \"path\": \"tools/xtask/src/contract/food_availability_projection.rs\",\n \"byte_length\": 194901,\n \"sha256\": \"01fe9546c95244b4197b3d2a6cec3d72e1f7c48f088bc93f168b5e2e4977b048\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"transitive_predecessor_membership_governance\",\n \"path\": \"tools/xtask/src/contract/nip09_reconciliation.rs\",\n \"byte_length\": 833937,\n \"sha256\": \"0d326a4e459b2026f7ae9fc57a9741def98a2cd1ee6fbdf89713157d964d2547\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"source_maintenance_governance\",\n \"path\": \"tools/xtask/src/contract/source_maintenance.rs\",\n \"byte_length\": 176811,\n \"sha256\": \"c22612787364ae417fd4da582130f5fe934820ba0f40f92a47b4eeeb6dffbf16\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"contract_command_authority\",\n \"path\": \"tools/xtask/src/contract.rs\",\n \"byte_length\": 499430,\n \"sha256\": \"866fc167be2e6a618c23ef515fd5e85c64fc60cc6178dca77439dcece64e2505\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"xtask_dispatch_and_release_preflight\",\n \"path\": \"tools/xtask/src/main.rs\",\n \"byte_length\": 14077,\n \"sha256\": \"d815e65241e47143a51dd87d95e014d975be1d9b94075f3920e4812f41188353\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n }\n ],\n \"public_api\": {\n \"inherited_predecessor_symbols\": [\n \"RADROOTS_ADDRESSABLE_TRANSITION_CURSOR_JSON_MAX_BYTES_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_D_TAG_MAX_BYTES_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_FEED_VERSION_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_PAGE_LIMIT_MAX_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_PAGE_RAW_JSON_MAX_BYTES_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_PAGE_SCAN_MAX_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_SCOPE_KIND_MAX_V1\",\n \"RADROOTS_FOOD_AVAILABILITY_PROJECTION_APPLY_PAGE_LIMIT_V1\",\n \"RADROOTS_FOOD_AVAILABILITY_PROJECTION_VERSION_V1\",\n \"RADROOTS_FOOD_AVAILABILITY_SEARCH_QUERY_MAX_BYTES_V1\",\n \"RADROOTS_FOOD_AVAILABILITY_SEARCH_QUERY_MAX_TERMS_V1\",\n \"RadrootsAddressableTransitionCauseV1\",\n \"RadrootsAddressableTransitionCoordinateV1\",\n \"RadrootsAddressableTransitionCursorV1\",\n \"RadrootsAddressableTransitionEventReferenceV1\",\n \"RadrootsAddressableTransitionOriginV1\",\n \"RadrootsAddressableTransitionPageV1\",\n \"RadrootsAddressableTransitionRawHeadDecisionV1\",\n \"RadrootsAddressableTransitionScopeFingerprintV1\",\n \"RadrootsAddressableTransitionScopeV1\",\n \"RadrootsAddressableTransitionV1\",\n \"RadrootsAddressableTransitionVisibilityV1\",\n \"RadrootsCurrentEventVisibilityV1\",\n \"RadrootsCurrentVisibilityDecisionV1\",\n \"RadrootsFoodAvailabilitySearchQueryV1\",\n \"RadrootsFoodAvailabilityStatusFilterV1\",\n \"RadrootsNip09SuppressionEvidenceV1\",\n \"RadrootsNip09SuppressionOutcome\",\n \"RadrootsNip09SuppressionReason\",\n \"RadrootsStoreProducedCanonicalEventV1\",\n \"RadrootsStoredFoodAvailabilityImageV1\",\n \"RadrootsStoredFoodAvailabilityV1\"\n ],\n \"added_symbols\": [\n \"RADROOTS_EVENT_STORE_RAW_EVENT_COUNT_LIMIT_V1\",\n \"RADROOTS_EVENT_STORE_RAW_EVENT_TEXT_BYTES_LIMIT_V1\",\n \"RADROOTS_EVENT_STORE_RAW_TAG_COUNT_LIMIT_V1\",\n \"RADROOTS_EVENT_STORE_RAW_TAG_TEXT_BYTES_LIMIT_V1\",\n \"RADROOTS_EVENT_STORE_RETAINED_SOURCE_GENERATION_LIMIT_V1\",\n \"RadrootsEventStoreSourceCapacityResourceV1\",\n \"RadrootsEventStoreSourceCapacityV1\"\n ],\n \"methods\": [\n \"RadrootsEventStore::source_capacity_v1\",\n \"RadrootsEventStoreSourceCapacityResourceV1::as_str\",\n \"RadrootsEventStoreSourceCapacityV1::source_generation\",\n \"RadrootsEventStoreSourceCapacityV1::raw_event_count\",\n \"RadrootsEventStoreSourceCapacityV1::raw_tag_count\",\n \"RadrootsEventStoreSourceCapacityV1::raw_event_text_bytes\",\n \"RadrootsEventStoreSourceCapacityV1::raw_tag_text_bytes\",\n \"RadrootsEventStoreSourceCapacityV1::raw_high_water_seq\",\n \"RadrootsEventStoreSourceCapacityV1::retained_generation_count\",\n \"RadrootsEventStoreSourceCapacityV1::retained_generation_limit\"\n ],\n \"error_variants\": [\n \"SourceCapacityExceeded\",\n \"SourceGenerationHistoryLimitReached\",\n \"PersistedEphemeralRawEvent\",\n \"SourceCapacityStateDrift\",\n \"SqliteMainDatabaseEncodingNotUtf8\",\n \"RollbackWouldDiscardSourceGenerationHistory\"\n ],\n \"removed_symbols\": [\n \"RadrootsEventStoreReconciliationResource\",\n \"RadrootsEventStoreError::ReconciliationCapacityExceeded\"\n ],\n \"breaking_replacements\": [\n {\n \"removed\": \"RadrootsEventStoreReconciliationResource\",\n \"replacement\": \"RadrootsEventStoreSourceCapacityResourceV1\"\n },\n {\n \"removed\": \"RadrootsEventStoreError::ReconciliationCapacityExceeded\",\n \"replacement\": \"RadrootsEventStoreError::SourceCapacityExceeded\"\n }\n ]\n },\n \"result_vector\": {\n \"canonical_path\": \"contracts/conformance/vectors/event_store/source_maintenance.v1.json\",\n \"mirror_path\": \"crates/event_store/tests/fixtures/source_maintenance.v1.json\",\n \"byte_length\": 16253,\n \"sha256\": \"997aba2604a2b9d199fb87dc9d07942ca50d91863aeadcf3eeacf16d191dd71f\",\n \"hash_algorithm\": \"sha256_bytes_v1\",\n \"executor_id\": \"radroots_event_store.source_maintenance_v1.result_vector_executor.v1\",\n \"executor_path\": \"crates/event_store/tests/source_maintenance_v1_result_vector.rs\",\n \"executor_test\": \"source_maintenance_v1_result_vector\",\n \"executor_byte_length\": 23510,\n \"executor_sha256\": \"a7487afdfe19fc5fc794811d0f0e6035203e1aabcf0a33a1d398f6b3555d38f3\",\n \"executor_hash_algorithm\": \"sha256_bytes_v1\"\n }\n}\n";
pub(crate) const SOURCE_MAINTENANCE_MANIFEST_BYTE_LENGTH: usize = 14459;
pub(crate) const SOURCE_MAINTENANCE_MANIFEST_SHA256: &str =
- "e76e00dba774c095c5abdc822bfaa73d287fd45cb1ba990855ce8725d370f619";
+ "45361811f77fffe3882603c13bd67e8cbecc9855d91643e5d4e5edbe90427916";
pub(crate) const SOURCE_MAINTENANCE_MANIFEST_SCHEMA_VERSION: u32 = 1;
pub(crate) const SOURCE_MAINTENANCE_CONTRACT_ID: &str =
"radroots_event_store.source_maintenance_v1";
diff --git a/tools/xtask/src/contract.rs b/tools/xtask/src/contract.rs
@@ -3079,6 +3079,8 @@ struct ReleaseContractFile {
#[serde(default)]
publication: Option<PublicationControl>,
#[serde(default)]
+ workspace_classification: Option<WorkspaceReleaseClassification>,
+ #[serde(default)]
classification: ReleaseClassification,
#[serde(default)]
publish: Option<ReleaseCrateSet>,
@@ -3111,6 +3113,52 @@ struct PublicationControl {
frozen: bool,
registry: String,
final_enablement_step: u16,
+ #[serde(default)]
+ spec_id: String,
+ #[serde(default)]
+ approved_packages: Vec<String>,
+ #[serde(default)]
+ local_packages: Vec<String>,
+ #[serde(default)]
+ external_packages: Vec<String>,
+}
+
+#[derive(Debug, Deserialize)]
+struct WorkspaceReleaseClassification {
+ #[serde(default)]
+ private: Vec<String>,
+ #[serde(default)]
+ build_codegen: Vec<String>,
+ #[serde(default)]
+ test_support: Vec<String>,
+ #[serde(default)]
+ preview: Vec<String>,
+ #[serde(default)]
+ retired: Vec<String>,
+}
+
+#[derive(Debug, Deserialize)]
+struct CratesReleaseArchitecture {
+ spec_id: String,
+ package_count: usize,
+ repositories: CratesReleaseRepositories,
+ package: Vec<CratesReleasePackage>,
+}
+
+#[derive(Debug, Deserialize)]
+struct CratesReleaseRepositories {
+ lib: CratesReleaseRepository,
+ sdk: CratesReleaseRepository,
+}
+
+#[derive(Debug, Deserialize)]
+struct CratesReleaseRepository {
+ packages: Vec<String>,
+}
+
+#[derive(Debug, Deserialize)]
+struct CratesReleasePackage {
+ name: String,
}
#[derive(Debug, Deserialize)]
@@ -8192,6 +8240,252 @@ fn validate_publication_control(
Ok(true)
}
+fn validate_v1_release_policy(
+ workspace_root: &Path,
+ release: &ReleaseContractFile,
+ workspace_packages: &BTreeSet<String>,
+ publish_configs: &BTreeMap<String, Option<PackagePublish>>,
+ require_v1: bool,
+) -> Result<Option<BTreeSet<String>>, String> {
+ let Some(control) = release.publication.as_ref() else {
+ if require_v1 {
+ return Err("publication control is required".to_string());
+ }
+ return Ok(None);
+ };
+ let declares_v1 = !control.spec_id.is_empty()
+ || !control.approved_packages.is_empty()
+ || !control.local_packages.is_empty()
+ || !control.external_packages.is_empty()
+ || release.workspace_classification.is_some();
+ if !declares_v1 {
+ if require_v1 {
+ return Err("publication must define the v1 approved package authority".to_string());
+ }
+ return Ok(None);
+ }
+
+ let architecture_path = workspace_root.join("docs/specs/radroots_crates_release_v1.toml");
+ let architecture = parse_toml::<CratesReleaseArchitecture>(&architecture_path)?;
+ let expected_approved = collect_unique_set(
+ &architecture
+ .package
+ .iter()
+ .map(|package| package.name.clone())
+ .collect::<Vec<_>>(),
+ "architecture.package.name",
+ )?;
+ if architecture.package_count != expected_approved.len() || architecture.package_count != 19 {
+ return Err(format!(
+ "release architecture must define exactly 19 unique packages, found package_count {} and {} unique package records",
+ architecture.package_count,
+ expected_approved.len()
+ ));
+ }
+ if control.spec_id != architecture.spec_id || control.spec_id != "radroots.crates.release.v1" {
+ return Err(format!(
+ "publication.spec_id {} must match architecture id {}",
+ control.spec_id, architecture.spec_id
+ ));
+ }
+
+ let approved = collect_unique_set(&control.approved_packages, "publication.approved_packages")?;
+ let local = collect_unique_set(&control.local_packages, "publication.local_packages")?;
+ let external = collect_unique_set(&control.external_packages, "publication.external_packages")?;
+ let expected_local = collect_unique_set(
+ &architecture.repositories.lib.packages,
+ "architecture.repositories.lib.packages",
+ )?;
+ let expected_external = collect_unique_set(
+ &architecture.repositories.sdk.packages,
+ "architecture.repositories.sdk.packages",
+ )?;
+ for (field, actual, expected) in [
+ (
+ "publication.approved_packages",
+ &approved,
+ &expected_approved,
+ ),
+ ("publication.local_packages", &local, &expected_local),
+ (
+ "publication.external_packages",
+ &external,
+ &expected_external,
+ ),
+ ] {
+ if actual != expected {
+ let missing = expected
+ .difference(actual)
+ .cloned()
+ .collect::<BTreeSet<_>>();
+ let extra = actual
+ .difference(expected)
+ .cloned()
+ .collect::<BTreeSet<_>>();
+ return Err(format!(
+ "{field} is missing approved packages: {}; {field} has unapproved packages: {}",
+ join_set(&missing),
+ join_set(&extra)
+ ));
+ }
+ }
+ let ownership_overlap = local
+ .intersection(&external)
+ .cloned()
+ .collect::<BTreeSet<_>>();
+ if !ownership_overlap.is_empty() {
+ return Err(format!(
+ "local and external approved package ownership overlaps: {}",
+ join_set(&ownership_overlap)
+ ));
+ }
+ let mut owned = local.clone();
+ owned.extend(external.iter().cloned());
+ if owned != approved {
+ return Err(
+ "local and external package ownership must partition approved packages".to_string(),
+ );
+ }
+ let external_in_workspace = external
+ .intersection(workspace_packages)
+ .cloned()
+ .collect::<BTreeSet<_>>();
+ if !external_in_workspace.is_empty() {
+ return Err(format!(
+ "externally owned approved packages must not be workspace members: {}",
+ join_set(&external_in_workspace)
+ ));
+ }
+
+ let classification = release.workspace_classification.as_ref().ok_or_else(|| {
+ "workspace_classification is required for the v1 release policy".to_string()
+ })?;
+ let private = collect_unique_set(&classification.private, "workspace_classification.private")?;
+ let build_codegen = collect_unique_set(
+ &classification.build_codegen,
+ "workspace_classification.build_codegen",
+ )?;
+ let test_support = collect_unique_set(
+ &classification.test_support,
+ "workspace_classification.test_support",
+ )?;
+ let preview = collect_unique_set(&classification.preview, "workspace_classification.preview")?;
+ let retired = collect_unique_set(&classification.retired, "workspace_classification.retired")?;
+ let classes = [
+ ("private", &private),
+ ("build-codegen", &build_codegen),
+ ("test-support", &test_support),
+ ("preview", &preview),
+ ("retired", &retired),
+ ];
+ for index in 0..classes.len() {
+ for other_index in (index + 1)..classes.len() {
+ let overlap = classes[index]
+ .1
+ .intersection(classes[other_index].1)
+ .cloned()
+ .collect::<BTreeSet<_>>();
+ if !overlap.is_empty() {
+ return Err(format!(
+ "workspace classification overlap is not allowed between {} and {}: {}",
+ classes[index].0,
+ classes[other_index].0,
+ join_set(&overlap)
+ ));
+ }
+ }
+ }
+ let mut classified = BTreeSet::new();
+ for (_, entries) in classes {
+ classified.extend(entries.iter().cloned());
+ }
+ let local_workspace_packages = local
+ .intersection(workspace_packages)
+ .cloned()
+ .collect::<BTreeSet<_>>();
+ let public_classification_overlap = classified
+ .intersection(&local_workspace_packages)
+ .cloned()
+ .collect::<BTreeSet<_>>();
+ if !public_classification_overlap.is_empty() {
+ return Err(format!(
+ "approved local packages must not be classified as private workspace packages: {}",
+ join_set(&public_classification_overlap)
+ ));
+ }
+ let mut accounted = classified.clone();
+ accounted.extend(local_workspace_packages.iter().cloned());
+ if accounted != *workspace_packages {
+ let missing = workspace_packages
+ .difference(&accounted)
+ .cloned()
+ .collect::<BTreeSet<_>>();
+ let extra = accounted
+ .difference(workspace_packages)
+ .cloned()
+ .collect::<BTreeSet<_>>();
+ return Err(format!(
+ "workspace classification is missing packages: {}; workspace classification has unknown packages: {}",
+ join_set(&missing),
+ join_set(&extra)
+ ));
+ }
+
+ if control.registry != "crates-io" {
+ return Err("publication.registry must be crates-io".to_string());
+ }
+ if control.final_enablement_step != 305 {
+ return Err("publication.final_enablement_step must be 305".to_string());
+ }
+ let publish_order = collect_unique_set(&release.publish_order.crates, "publish_order.crates")?;
+ if control.frozen {
+ if !publish_order.is_empty() {
+ return Err(
+ "publish_order.crates must remain empty while publication is frozen".to_string(),
+ );
+ }
+ for (crate_name, publish) in publish_configs {
+ if !publish_config_is_non_public(publish.as_ref()) {
+ return Err(format!(
+ "publication freeze requires workspace crate {} to set publish = false",
+ crate_name
+ ));
+ }
+ }
+ return Ok(Some(BTreeSet::new()));
+ }
+
+ if local_workspace_packages != local {
+ let missing = local
+ .difference(&local_workspace_packages)
+ .cloned()
+ .collect::<BTreeSet<_>>();
+ return Err(format!(
+ "publication enablement is missing approved local workspace packages: {}",
+ join_set(&missing)
+ ));
+ }
+ if publish_order != local {
+ return Err("publish_order.crates must contain exactly the approved local packages when publication is enabled".to_string());
+ }
+ for (crate_name, publish) in publish_configs {
+ if local.contains(crate_name) {
+ if !publish_config_is_public(publish.as_ref()) {
+ return Err(format!(
+ "approved local crate {} must set publish = [\"crates-io\"]",
+ crate_name
+ ));
+ }
+ } else if !publish_config_is_non_public(publish.as_ref()) {
+ return Err(format!(
+ "private workspace crate {} must set publish = false",
+ crate_name
+ ));
+ }
+ }
+ Ok(Some(local))
+}
+
#[cfg(test)]
fn validate_release_publish_policy(
workspace_root: &Path,
@@ -8212,6 +8506,19 @@ fn validate_release_publish_policy(
let workspace_packages = workspace_package_names(workspace_root)?
.into_iter()
.collect::<BTreeSet<_>>();
+ let publish_configs = workspace_package_publish_configs(workspace_root)
+ .expect("workspace publish configs are stable");
+ if validate_v1_release_policy(
+ workspace_root,
+ &release,
+ &workspace_packages,
+ &publish_configs,
+ false,
+ )?
+ .is_some()
+ {
+ return Ok(());
+ }
let uses_classification = release.uses_classification();
let public_field = if uses_classification {
"classification.public"
@@ -8319,8 +8626,6 @@ fn validate_release_publish_policy(
}
}
- let publish_configs = workspace_package_publish_configs(workspace_root)
- .expect("workspace publish configs are stable");
if validate_publication_control(&release, &publish_configs, false)? {
return Ok(());
}
@@ -8493,6 +8798,10 @@ fn validate_contract_bundle_with_release_policy_override_and_profile(
authority_profile,
OperationAuthorityProfile::CapsuleCanonical
),
+ matches!(
+ authority_profile,
+ OperationAuthorityProfile::CapsuleCanonical
+ ),
)?;
Ok(())
}
@@ -8510,6 +8819,7 @@ fn validate_release_publish_policy_with_override(
contract_version,
release_policy_override,
true,
+ false,
)
}
@@ -8519,6 +8829,7 @@ fn validate_release_publish_policy_with_override_and_control(
contract_version: &str,
release_policy_override: Option<PathBuf>,
require_publication_control: bool,
+ require_v1_policy: bool,
) -> Result<(), String> {
let release = load_release_contract_with_override(
workspace_root,
@@ -8538,6 +8849,19 @@ fn validate_release_publish_policy_with_override_and_control(
let workspace_packages = workspace_package_names(workspace_root)?
.into_iter()
.collect::<BTreeSet<_>>();
+ let publish_configs = workspace_package_publish_configs(workspace_root)
+ .expect("workspace publish configs are stable");
+ if validate_v1_release_policy(
+ workspace_root,
+ &release,
+ &workspace_packages,
+ &publish_configs,
+ require_v1_policy,
+ )?
+ .is_some()
+ {
+ return Ok(());
+ }
let uses_classification = release.uses_classification();
let public_field = if uses_classification {
"classification.public"
@@ -8645,8 +8969,6 @@ fn validate_release_publish_policy_with_override_and_control(
}
}
- let publish_configs = workspace_package_publish_configs(workspace_root)
- .expect("workspace publish configs are stable");
if validate_publication_control(&release, &publish_configs, require_publication_control)? {
return Ok(());
}
@@ -11324,6 +11646,7 @@ readme = { workspace = true }
version: release.version.clone(),
},
publication: None,
+ workspace_classification: None,
classification: ReleaseClassification::default(),
publish: Some(ReleaseCrateSet {
crates: vec!["radroots_public".to_string()],
@@ -11347,6 +11670,7 @@ readme = { workspace = true }
version: release.version.clone(),
},
publication: None,
+ workspace_classification: None,
classification: ReleaseClassification::default(),
publish: None,
internal: None,
@@ -11363,6 +11687,7 @@ readme = { workspace = true }
version: release.version.clone(),
},
publication: None,
+ workspace_classification: None,
classification: ReleaseClassification {
internal: vec!["radroots_internal_only".to_string()],
..ReleaseClassification::default()
@@ -11380,6 +11705,7 @@ readme = { workspace = true }
version: release.version.clone(),
},
publication: None,
+ workspace_classification: None,
classification: ReleaseClassification {
deferred: vec!["radroots_deferred".to_string()],
..ReleaseClassification::default()
@@ -11401,6 +11727,7 @@ readme = { workspace = true }
version: release.version.clone(),
},
publication: None,
+ workspace_classification: None,
classification: ReleaseClassification {
retired: vec!["radroots_retired".to_string()],
..ReleaseClassification::default()
@@ -11420,6 +11747,7 @@ readme = { workspace = true }
let yank_only = ReleaseContractFile {
release,
publication: None,
+ workspace_classification: None,
classification: ReleaseClassification {
yank_only: vec!["radroots_yank_only".to_string()],
..ReleaseClassification::default()
@@ -12083,6 +12411,104 @@ crates = ["radroots_a"]
}
#[test]
+ fn v1_release_policy_covers_approved_unapproved_unclassified_and_private_fixtures() {
+ let root = create_synthetic_workspace("v1_release_policy");
+ let contract_root = root.join("contracts");
+ let release_policy_path = root_release_policy_path(&root);
+ for member in ["a", "b"] {
+ write_file(
+ &root.join("crates").join(member).join("Cargo.toml"),
+ &format!(
+ "[package]\nname = \"radroots_{member}\"\nversion = \"1.0.0\"\nedition = \"2024\"\npublish = false\n"
+ ),
+ );
+ }
+
+ let approved = (1..=19)
+ .map(|index| format!("package-{index:02}"))
+ .collect::<Vec<_>>();
+ let approved_toml = approved
+ .iter()
+ .map(|name| format!("\"{name}\""))
+ .collect::<Vec<_>>()
+ .join(", ");
+ let external_toml = approved[1..]
+ .iter()
+ .map(|name| format!("\"{name}\""))
+ .collect::<Vec<_>>()
+ .join(", ");
+ let mut architecture = format!(
+ "spec_id = \"radroots.crates.release.v1\"\npackage_count = 19\n\n[repositories.lib]\npackages = [\"package-01\"]\n\n[repositories.sdk]\npackages = [{external_toml}]\n"
+ );
+ for name in &approved {
+ architecture.push_str(&format!("\n[[package]]\nname = \"{name}\"\n"));
+ }
+ write_file(
+ &root.join("docs/specs/radroots_crates_release_v1.toml"),
+ &architecture,
+ );
+
+ let policy = |approved_packages: &str, test_support: &str| {
+ format!(
+ r#"[release]
+version = "1.0.0"
+
+[publication]
+frozen = true
+registry = "crates-io"
+final_enablement_step = 305
+spec_id = "radroots.crates.release.v1"
+approved_packages = [{approved_packages}]
+local_packages = ["package-01"]
+external_packages = [{external_toml}]
+
+[workspace_classification]
+private = ["radroots_a"]
+build_codegen = []
+test_support = [{test_support}]
+preview = []
+retired = []
+
+[publish_order]
+crates = []
+"#
+ )
+ };
+
+ write_file(
+ &release_policy_path,
+ &policy(&approved_toml, "\"radroots_b\""),
+ );
+ validate_release_publish_policy(&root, &contract_root, "1.0.0")
+ .expect("approved and exhaustively classified fixture must pass");
+
+ let unapproved = format!("{approved_toml}, \"unapproved-public\"");
+ write_file(&release_policy_path, &policy(&unapproved, "\"radroots_b\""));
+ let unapproved_error = validate_release_publish_policy(&root, &contract_root, "1.0.0")
+ .expect_err("unapproved public package must fail");
+ assert!(unapproved_error.contains("unapproved packages: unapproved-public"));
+
+ write_file(&release_policy_path, &policy(&approved_toml, ""));
+ let unclassified = validate_release_publish_policy(&root, &contract_root, "1.0.0")
+ .expect_err("unclassified workspace package must fail");
+ assert!(unclassified.contains("workspace classification is missing packages: radroots_b"));
+
+ write_file(
+ &release_policy_path,
+ &policy(&approved_toml, "\"radroots_b\""),
+ );
+ write_file(
+ &root.join("crates/a/Cargo.toml"),
+ "[package]\nname = \"radroots_a\"\nversion = \"1.0.0\"\nedition = \"2024\"\npublish = [\"crates-io\"]\n",
+ );
+ let private = validate_release_publish_policy(&root, &contract_root, "1.0.0")
+ .expect_err("private package must remain non-publishable");
+ assert!(private.contains("publication freeze requires workspace crate radroots_a"));
+
+ let _ = fs::remove_dir_all(root);
+ }
+
+ #[test]
fn release_preflight_rejects_public_dto_tooling_git_or_path_sources() {
let root = create_synthetic_workspace("release_policy_dto_tooling_sources");