lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

commit fab75a9d3950b92ed70e7e3d8cec0d55d1caf34b
parent 059927533a174e91d12851eca8c8bc26f4d7eb51
Author: triesap <tyson@radroots.org>
Date:   Mon, 27 Jul 2026 08:02:06 +0000

architecture: add deviation and traceability controls

- Record both approved sequence deviations in a strict machine-readable ledger.
- Validate identifiers, statuses, step ranges, evidence, and local spec anchors.
- Add executable architecture checks with complete and incomplete fixtures.
- Install traceability and step-report templates and refresh governed outputs.

Diffstat:
MAGENTS.md | 7++++++-
MCONTRIBUTING.md | 8++++++--
Mcrates/event_store/contracts/source_maintenance_v1.manifest.json | 10+++++-----
Mcrates/event_store/contracts/source_maintenance_v1.manifest.sha256 | 2+-
Mcrates/event_store/src/generated/source_maintenance_manifest.rs | 4++--
Mdocs/implementation/DEVIATIONS.md | 51++++++++++++++++++++++++++++++++++++---------------
Adocs/implementation/STEP_REPORT_TEMPLATE.md | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adocs/implementation/TRACEABILITY.md | 20++++++++++++++++++++
Adocs/implementation/deviations.toml | 47+++++++++++++++++++++++++++++++++++++++++++++++
Atools/xtask/src/architecture.rs | 269+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtools/xtask/src/contract.rs | 6++++++
Mtools/xtask/src/contract/source_maintenance.rs | 2+-
Mtools/xtask/src/main.rs | 6++++++
13 files changed, 463 insertions(+), 27 deletions(-)

diff --git a/AGENTS.md b/AGENTS.md @@ -23,7 +23,9 @@ This file exists for compatibility with tools that look for AGENTS.md. - Current source and tests are implementation evidence. They do not silently override `radroots.crates.release.v1`. - Record any evidence-based plan deviation in - `docs/implementation/DEVIATIONS.md` before proceeding. + `docs/implementation/deviations.toml`, following + `docs/implementation/DEVIATIONS.md`, before proceeding. Validate it with + `cargo xtask architecture`. ## 3. Repository operating model @@ -54,6 +56,8 @@ Before editing code: - `nix flake check` - `nix run .#contract` - `nix run .#release-preflight` +- `cargo xtask architecture` for controlled deviation records and local spec + anchors - targeted `cargo check -p <crate>` and `cargo test -p <crate>` only inside the Nix shell - `cargo xtask dto-roots --write` after changing configured DTO exports and `cargo xtask dto-roots --check` for exact generated-root freshness @@ -111,6 +115,7 @@ trusted-publisher configuration without explicit authorization. - Split unrelated changes into separate commits. - If repository evidence proves a planned step obsolete or unsafe, record the evidence, affected specification anchor, disposition, and validation in + `docs/implementation/deviations.toml`, following `docs/implementation/DEVIATIONS.md`. A normative architecture change also requires an approved decision record. Never silently skip or reorder work. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md @@ -43,5 +43,9 @@ private checkout. Do not publish, tag, merge, or change registry ownership without explicit authorization. When current evidence proves a planned step obsolete or unsafe, follow -`docs/implementation/DEVIATIONS.md`. Record the evidence and affected spec -anchor before changing the plan; do not silently redefine the architecture. +`docs/implementation/DEVIATIONS.md` and validate the machine-readable ledger +with `cargo xtask architecture`. Complete +`docs/implementation/STEP_REPORT_TEMPLATE.md`, and keep +`docs/implementation/TRACEABILITY.md` aligned with durable requirements. +Record the evidence and affected spec anchor before changing the plan; do not +silently redefine the architecture. diff --git a/crates/event_store/contracts/source_maintenance_v1.manifest.json b/crates/event_store/contracts/source_maintenance_v1.manifest.json @@ -251,21 +251,21 @@ "role": "source_maintenance_governance", "path": "tools/xtask/src/contract/source_maintenance.rs", "byte_length": 176811, - "sha256": "c22612787364ae417fd4da582130f5fe934820ba0f40f92a47b4eeeb6dffbf16", + "sha256": "cc6125aaeba8d7dab83c2413ca0f1a80d6f9f26876f0117e4c8fceae156304e5", "hash_algorithm": "sha256_bytes_v1" }, { "role": "contract_command_authority", "path": "tools/xtask/src/contract.rs", - "byte_length": 499430, - "sha256": "866fc167be2e6a618c23ef515fd5e85c64fc60cc6178dca77439dcece64e2505", + "byte_length": 499596, + "sha256": "ece5990259eeacac8bb12a27e89f889f82f8641b958103a0ef97eaa6d2f66fb0", "hash_algorithm": "sha256_bytes_v1" }, { "role": "xtask_dispatch_and_release_preflight", "path": "tools/xtask/src/main.rs", - "byte_length": 14077, - "sha256": "d815e65241e47143a51dd87d95e014d975be1d9b94075f3920e4812f41188353", + "byte_length": 14364, + "sha256": "27096ad2c226c6402313621712db6af23a4fdda5117c3e732cf3a3fdf3d9b434", "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 @@ -45361811f77fffe3882603c13bd67e8cbecc9855d91643e5d4e5edbe90427916 +304880398e97d8d978150a7252ad6932fc7f10dc8ef5c03f7172f51eff36349d 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\": 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_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\": \"cc6125aaeba8d7dab83c2413ca0f1a80d6f9f26876f0117e4c8fceae156304e5\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"contract_command_authority\",\n \"path\": \"tools/xtask/src/contract.rs\",\n \"byte_length\": 499596,\n \"sha256\": \"ece5990259eeacac8bb12a27e89f889f82f8641b958103a0ef97eaa6d2f66fb0\",\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\": 14364,\n \"sha256\": \"27096ad2c226c6402313621712db6af23a4fdda5117c3e732cf3a3fdf3d9b434\",\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 = - "45361811f77fffe3882603c13bd67e8cbecc9855d91643e5d4e5edbe90427916"; + "304880398e97d8d978150a7252ad6932fc7f10dc8ef5c03f7172f51eff36349d"; 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/docs/implementation/DEVIATIONS.md b/docs/implementation/DEVIATIONS.md @@ -1,22 +1,43 @@ # Implementation deviations -This ledger records evidence-based deviations from -`radroots.crates.release.v1` implementation planning. It does not authorize a -change to the normative package architecture. +The machine-readable authority is [`deviations.toml`](deviations.toml). +Repository checks validate it on every architecture and full check lane. This +ledger records evidence-based changes to implementation planning; it does not +silently change `radroots.crates.release.v1`. -No deviations are currently recorded. +## Active records -## Required record +| ID | Affected steps | Approved disposition | +| --- | --- | --- | +| `RCRV1-DEV-001` | 015-023 | Preserve the existing standalone `lib` and `sdk` repositories; replace repository import/unification with independent qualification. | +| `RCRV1-DEV-002` | 249 | Pull only the facade scaffold forward to immediately after Step 014 in `sdk`; do not repeat it later. | -Every deviation entry must include: +## Record template -- a stable identifier and date; -- the affected plan step and normative specification anchor; -- current repository evidence proving the planned action obsolete or unsafe; -- the smallest safe disposition and any temporary compatibility boundary; -- validation performed and unresolved risk; -- the approving decision record when normative architecture changes. +Add one `[[deviation]]` table to `deviations.toml`: -Do not silently skip, merge, reorder, or broaden implementation steps. Keep -the repository at a known-good checkpoint and obtain approval for any -normative change before proceeding. +```toml +[[deviation]] +id = "RCRV1-DEV-NNN" +date = "YYYY-MM-DD" +status = "active" # active | closed | superseded +approval = "Explicit approving decision." +affected_steps = ["NNN"] +spec_anchors = ["docs/specs/<durable-spec>#<anchor>"] +source_evidence = ["Committed source evidence."] +replacement_action = "Smallest safe disposition." +verification = ["Command or review evidence."] +unresolved_risk = "none, or a concrete bounded risk" +normative_architecture_change = false +adr_required = false +closure_evidence = [] # omit while active; required when closed or superseded +``` + +Every field is mandatory except `closure_evidence` on active records. Spec +anchors must resolve inside `docs/specs/`; affected steps must be three-digit +IDs in 001-315. A normative architecture change needs explicit approval and +the appropriate ADR decision before the record can be accepted. + +Do not silently skip, merge, reorder, or broaden implementation steps. Keep a +red checkpoint uncommitted and mark the next step blocked until its evidence or +approval is complete. diff --git a/docs/implementation/STEP_REPORT_TEMPLATE.md b/docs/implementation/STEP_REPORT_TEMPLATE.md @@ -0,0 +1,58 @@ +# Commit-step report template + +Complete this record in the owning rolling-commit document after verification +and before the next handoff step begins. + +```text +Step: +Title: +Repository: +Branch: +Commit SHA: + +Spec anchors: +- ... + +Files changed: +- ... + +Behavior implemented: +- ... + +Tests and verification: +- command: + result: +- command: + result: + +Self-review: +- public API review: +- architecture-boundary review: +- error/secret review: +- feature/target review: +- documentation review: +- generated/lockfile diff review: + +Deviations: +- none +or +- RCRV1-DEV-NNN and evidence + +Unresolved issues: +- none +or +- ... + +Known pre-existing failures: +- none +or +- command, exact failure, evidence, and why it is outside this step + +Next-step safety: +- SAFE / BLOCKED +- reason: +``` + +A step is not complete without its commit SHA, exact command outcomes, +self-review, deviation disposition, and next-step safety decision. A blocked +step does not authorize later work. diff --git a/docs/implementation/TRACEABILITY.md b/docs/implementation/TRACEABILITY.md @@ -0,0 +1,20 @@ +# Release-v1 requirement traceability + +This matrix maps durable architecture requirements to implementation ownership +and verification. It adds no product requirements; the synchronized +`docs/specs/` bundle remains normative. + +| Durable requirement | Owning package or control | Handoff steps | Required evidence | +| --- | --- | --- | --- | +| Exactly 19 public packages with a 17/2 repository split | release policy and architecture catalog | 013, 015-026, 304-305 | Cargo-resolved graph report and exact allowlist validation | +| Public-only identity and separated signing/secrets | `radroots-identity`, `radroots-signing`, `radroots-secrets` | 052-054, 099-111, 147-155 | public API, feature, dependency, and redaction tests | +| One canonical `TradeId` | `radroots-event`, `radroots-trade` | 073-098 | compile/API inventory and trade conformance | +| Version-neutral protocol ownership | `radroots-protocol` and private generators | 055-064, 261-268 | contract vectors and generated freshness | +| Independent transport source/sink with extensible identity | `radroots-transport` and adapters | 112-134, 190-207 | transport conformance and forward-compatibility fixtures | +| Storage SPI with SQLite backend | `radroots-storage`, `radroots-storage-sqlite` | 156-189 | backend conformance, migration, recovery, and leakage gates | +| Shared sync engine and explicit lifecycle | `radroots-sync` | 208-225 | pull/push, idempotency, cancellation, and close tests | +| Safe SDK defaults and curated facade | `radroots-sdk`, `radroots` | 226-260 | clean-project package smoke tests and compile-time surface guards | +| Preview and implementation packages remain private | release policy and graph validator | 013, 023-026, 304-305 | private-closure and forbidden-edge fixtures | +| Package-realistic reproducible release | release tooling in both repositories | 295-315 | locked zero-diff package, extracted, local-registry, target, and coverage gates | +| Every first-party consumer migrates | downstream cutover matrix | 269-294 | discovered consumer inventory and canary results | +| Deviations remain explicit and reviewable | `docs/implementation/deviations.toml` | 014 and every affected step | `cargo xtask architecture` plus step report evidence | diff --git a/docs/implementation/deviations.toml b/docs/implementation/deviations.toml @@ -0,0 +1,47 @@ +schema_version = 1 +architecture_id = "radroots.crates.release.v1" + +[[deviation]] +id = "RCRV1-DEV-001" +date = "2026-07-27" +status = "active" +approval = "Explicit user correction dated 2026-07-27." +affected_steps = ["015", "016", "017", "018", "019", "020", "021", "022", "023"] +spec_anchors = [ + "docs/specs/radroots_crates_release_v1.md#repository-ownership", + "docs/specs/radroots_crates_release_v1.toml#repository_policy", +] +source_evidence = [ + "The final v1 specification allocates 17 public packages to radrootslabs/lib and 2 to radrootslabs/sdk.", + "Both existing repositories have independent histories, workspaces, lockfiles, remotes, and standalone release boundaries.", +] +replacement_action = "Retain the two existing standalone repositories; replace import and monorepo-unification work with independent workspace, lockfile, metadata, dependency, and release qualification." +verification = [ + "Both repository-local architecture validators resolve every spec anchor.", + "The synchronized architecture catalog enforces the exact 17/2 ownership partition.", +] +unresolved_risk = "Parent gitlinks cannot advance until the new standalone commits are public-remote reachable under separate authorization." +normative_architecture_change = false +adr_required = false +[[deviation]] +id = "RCRV1-DEV-002" +date = "2026-07-27" +status = "active" +approval = "Explicit user correction dated 2026-07-27." +affected_steps = ["249"] +spec_anchors = [ + "docs/specs/radroots_crates_release_v1.md#radroots", + "docs/specs/radroots_crates_release_v1.toml#repositories.sdk", +] +source_evidence = [ + "The final v1 specification assigns the radroots facade to the existing sdk repository.", + "The approved sequence requires radroots to be the first crate-surface mutation after architecture controls are green.", +] +replacement_action = "Scaffold radroots in the sdk repository immediately after Step 014, then execute Steps 250-260 in their original order without repeating the scaffold portion of Step 249." +verification = [ + "The sdk release policy reserves radroots as an approved local package while publication remains frozen.", + "The facade scaffold checkpoint must add radroots only to the sdk workspace and architecture policy.", +] +unresolved_risk = "The facade remains non-publishable until the package-realistic Step 305 enablement gate." +normative_architecture_change = false +adr_required = false diff --git a/tools/xtask/src/architecture.rs b/tools/xtask/src/architecture.rs @@ -0,0 +1,269 @@ +use std::{ + collections::BTreeSet, + fs, + path::{Component, Path}, +}; + +use serde::Deserialize; + +const DEVIATIONS_RELATIVE: &str = "docs/implementation/deviations.toml"; +const ARCHITECTURE_RELATIVE: &str = "docs/specs/radroots_crates_release_v1.toml"; +const ARCHITECTURE_ID: &str = "radroots.crates.release.v1"; + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct DeviationLedger { + schema_version: u16, + architecture_id: String, + deviation: Vec<DeviationRecord>, +} + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct DeviationRecord { + id: String, + date: String, + status: String, + approval: String, + affected_steps: Vec<String>, + spec_anchors: Vec<String>, + source_evidence: Vec<String>, + replacement_action: String, + verification: Vec<String>, + unresolved_risk: String, + normative_architecture_change: bool, + adr_required: bool, + #[serde(default)] + closure_evidence: Vec<String>, +} + +#[derive(Debug, Deserialize)] +struct ArchitectureIdentity { + spec_id: String, +} + +pub fn validate(workspace_root: &Path) -> Result<(), String> { + let architecture_path = workspace_root.join(ARCHITECTURE_RELATIVE); + let architecture_raw = fs::read_to_string(&architecture_path) + .map_err(|error| format!("read {}: {error}", architecture_path.display()))?; + let architecture = toml::from_str::<ArchitectureIdentity>(&architecture_raw) + .map_err(|error| format!("parse {}: {error}", architecture_path.display()))?; + + let ledger_path = workspace_root.join(DEVIATIONS_RELATIVE); + let ledger_raw = fs::read_to_string(&ledger_path) + .map_err(|error| format!("read {}: {error}", ledger_path.display()))?; + validate_ledger(workspace_root, &architecture.spec_id, &ledger_raw) +} + +fn validate_ledger( + workspace_root: &Path, + expected_architecture_id: &str, + raw: &str, +) -> Result<(), String> { + let ledger = toml::from_str::<DeviationLedger>(raw) + .map_err(|error| format!("parse {DEVIATIONS_RELATIVE}: {error}"))?; + if ledger.schema_version != 1 { + return Err("deviation ledger schema_version must be 1".to_owned()); + } + if ledger.architecture_id != expected_architecture_id + || ledger.architecture_id != ARCHITECTURE_ID + { + return Err(format!( + "deviation ledger architecture_id {} must match {}", + ledger.architecture_id, expected_architecture_id + )); + } + + let mut ids = BTreeSet::new(); + for record in &ledger.deviation { + validate_record(workspace_root, record)?; + if !ids.insert(record.id.as_str()) { + return Err(format!("duplicate deviation id {}", record.id)); + } + } + Ok(()) +} + +fn validate_record(workspace_root: &Path, record: &DeviationRecord) -> Result<(), String> { + if !is_deviation_id(&record.id) { + return Err(format!("deviation id {} must use RCRV1-DEV-NNN", record.id)); + } + if !is_iso_date(&record.date) { + return Err(format!("deviation {} date must use YYYY-MM-DD", record.id)); + } + if !matches!(record.status.as_str(), "active" | "closed" | "superseded") { + return Err(format!( + "deviation {} status must be active, closed, or superseded", + record.id + )); + } + require_text(&record.id, "approval", &record.approval)?; + require_text(&record.id, "replacement_action", &record.replacement_action)?; + require_text(&record.id, "unresolved_risk", &record.unresolved_risk)?; + require_nonempty_list(&record.id, "affected_steps", &record.affected_steps)?; + require_nonempty_list(&record.id, "spec_anchors", &record.spec_anchors)?; + require_nonempty_list(&record.id, "source_evidence", &record.source_evidence)?; + require_nonempty_list(&record.id, "verification", &record.verification)?; + + for step in &record.affected_steps { + let valid = step.len() == 3 + && step.bytes().all(|byte| byte.is_ascii_digit()) + && step + .parse::<u16>() + .is_ok_and(|value| (1..=315).contains(&value)); + if !valid { + return Err(format!( + "deviation {} affected step {} must be in 001..315", + record.id, step + )); + } + } + for anchor in &record.spec_anchors { + validate_spec_anchor(workspace_root, &record.id, anchor)?; + } + if record.status == "active" && !record.closure_evidence.is_empty() { + return Err(format!( + "active deviation {} must not carry closure_evidence", + record.id + )); + } + if record.status != "active" { + require_nonempty_list(&record.id, "closure_evidence", &record.closure_evidence)?; + } + + let _ = (record.normative_architecture_change, record.adr_required); + Ok(()) +} + +fn validate_spec_anchor( + workspace_root: &Path, + deviation_id: &str, + anchor: &str, +) -> Result<(), String> { + let (relative, fragment) = anchor + .split_once('#') + .map_or((anchor, None), |(path, fragment)| (path, Some(fragment))); + if relative.trim().is_empty() || fragment.is_some_and(|value| value.trim().is_empty()) { + return Err(format!( + "deviation {deviation_id} has invalid spec anchor {anchor}" + )); + } + let path = Path::new(relative); + if path.components().any(|component| { + matches!( + component, + Component::ParentDir | Component::RootDir | Component::Prefix(_) + ) + }) { + return Err(format!( + "deviation {deviation_id} spec anchor must be repository-relative: {anchor}" + )); + } + if !relative.starts_with("docs/specs/") || !workspace_root.join(path).is_file() { + return Err(format!( + "deviation {deviation_id} spec anchor does not resolve to a local spec: {anchor}" + )); + } + Ok(()) +} + +fn require_text(deviation_id: &str, field: &str, value: &str) -> Result<(), String> { + if value.trim().is_empty() { + return Err(format!( + "deviation {deviation_id} field {field} must not be empty" + )); + } + Ok(()) +} + +fn require_nonempty_list(deviation_id: &str, field: &str, values: &[String]) -> Result<(), String> { + if values.is_empty() || values.iter().any(|value| value.trim().is_empty()) { + return Err(format!( + "deviation {deviation_id} field {field} must contain non-empty values" + )); + } + Ok(()) +} + +fn is_deviation_id(value: &str) -> bool { + value + .strip_prefix("RCRV1-DEV-") + .is_some_and(|suffix| suffix.len() == 3 && suffix.bytes().all(|byte| byte.is_ascii_digit())) +} + +fn is_iso_date(value: &str) -> bool { + value.len() == 10 + && value.as_bytes()[4] == b'-' + && value.as_bytes()[7] == b'-' + && value + .bytes() + .enumerate() + .all(|(index, byte)| matches!(index, 4 | 7) || byte.is_ascii_digit()) +} + +#[cfg(test)] +mod tests { + use std::{ + fs, + path::PathBuf, + time::{SystemTime, UNIX_EPOCH}, + }; + + use super::validate_ledger; + + fn test_root(label: &str) -> PathBuf { + let nonce = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("clock") + .as_nanos(); + let root = std::env::temp_dir().join(format!("radroots_architecture_{label}_{nonce}")); + fs::create_dir_all(root.join("docs/specs")).expect("create spec root"); + fs::write( + root.join("docs/specs/radroots_crates_release_v1.md"), + "# Architecture\n", + ) + .expect("write spec"); + root + } + + fn complete_ledger() -> &'static str { + r#"schema_version = 1 +architecture_id = "radroots.crates.release.v1" + +[[deviation]] +id = "RCRV1-DEV-001" +date = "2026-07-27" +status = "active" +approval = "Explicit user correction dated 2026-07-27." +affected_steps = ["015", "016"] +spec_anchors = ["docs/specs/radroots_crates_release_v1.md#repository-topology"] +source_evidence = ["The approved architecture assigns packages to the existing lib and sdk repositories."] +replacement_action = "Keep both standalone repositories and verify them independently." +verification = ["Repository-local architecture validation passes."] +unresolved_risk = "Remote publication remains separately authorized." +normative_architecture_change = false +adr_required = false +"# + } + + #[test] + fn accepts_complete_active_deviation() { + let root = test_root("complete"); + validate_ledger(&root, "radroots.crates.release.v1", complete_ledger()) + .expect("complete deviation"); + let _ = fs::remove_dir_all(root); + } + + #[test] + fn rejects_incomplete_active_deviation() { + let root = test_root("incomplete"); + let incomplete = complete_ledger().replace( + "spec_anchors = [\"docs/specs/radroots_crates_release_v1.md#repository-topology\"]", + "spec_anchors = []", + ); + let error = validate_ledger(&root, "radroots.crates.release.v1", &incomplete) + .expect_err("missing anchor must fail"); + assert!(error.contains("field spec_anchors must contain non-empty values")); + let _ = fs::remove_dir_all(root); + } +} diff --git a/tools/xtask/src/contract.rs b/tools/xtask/src/contract.rs @@ -8789,6 +8789,12 @@ fn validate_contract_bundle_with_release_policy_override_and_profile( validate_core_unit_dimension_variant_order(workspace_root)?; validate_coverage_policy_parity(workspace_root, &bundle.root)?; validate_version_governance(bundle, workspace_root)?; + if matches!( + authority_profile, + OperationAuthorityProfile::CapsuleCanonical + ) { + crate::architecture::validate(workspace_root)?; + } validate_release_publish_policy_with_override_and_control( workspace_root, &bundle.root, diff --git a/tools/xtask/src/contract/source_maintenance.rs b/tools/xtask/src/contract/source_maintenance.rs @@ -76,7 +76,7 @@ const RESULT_VECTOR_EXECUTOR_TEST: &str = "source_maintenance_v1_result_vector"; const CONTRACT_COMMAND_SOURCE_RELATIVE: &str = "tools/xtask/src/contract.rs"; const XTASK_MAIN_SOURCE_RELATIVE: &str = "tools/xtask/src/main.rs"; const XTASK_MAIN_FULL_AST_SHA256: &str = - "b48c71c7f40f45c89bd7c83935d48eac3a1a367c8f73f62262e8ee14404616b4"; + "888df7c6f0df0ce0df255d0f563eb366faa2c53b64f9ac1814ab33dbeeebdb03"; const RAW_EVENT_COLUMNS: &[&str] = &[ "event_id", diff --git a/tools/xtask/src/main.rs b/tools/xtask/src/main.rs @@ -3,6 +3,8 @@ #![recursion_limit = "256"] #[cfg_attr(coverage_nightly, coverage(off))] +mod architecture; +#[cfg_attr(coverage_nightly, coverage(off))] mod contract; mod coverage; #[cfg_attr(coverage_nightly, coverage(off))] @@ -16,6 +18,7 @@ use std::process::ExitCode; fn usage() { eprintln!("usage:"); + eprintln!(" cargo xtask architecture"); eprintln!(" cargo xtask contract validate"); eprintln!(" cargo xtask contract event-contract-registry-v7 [--write]"); eprintln!(" cargo xtask contract nip09-reconciliation-manifest [--write]"); @@ -141,6 +144,7 @@ fn run_contract(args: &[String]) -> Result<(), String> { fn run(args: &[String]) -> Result<(), String> { match args.first().map(String::as_str) { + Some("architecture") if args.len() == 1 => architecture::validate(&workspace_root()), Some("contract") => run_contract(&args[1..]), Some("coverage") => coverage::run(&args[1..]), Some("dto-roots") => dto_roots::run(&args[1..], &workspace_root()), @@ -252,6 +256,8 @@ mod tests { let unknown_root = run(&["unknown".to_string()]).expect_err("unknown command"); assert!(unknown_root.contains("unknown command")); + run(&["architecture".to_string()]).expect("architecture ledger validates"); + let invalid_dto_roots = run(&["dto-roots".to_string()]).expect_err("dto-roots requires an explicit mode"); assert!(invalid_dto_roots.contains("--check|--write"));