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:
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"));