field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | README | LICENSE

commit d2cea073047adefac2f0d3cd0ace0a65a89bbc2d
parent 4543a6be2295704660fd2d8a2e182f4ea2d1ee04
Author: triesap <tyson@radroots.org>
Date:   Tue,  8 Sep 2026 22:25:54 +0000

tera: verify the real application dependency graph

- retire the inert source-lock crate and old mobile producer dependency
- validate exact resolved foundation pins and the native application defaults
- preserve the production feature graph and reject shim or profile regressions
- refresh owned provenance and verify native, Linux, package and simulator lanes

Diffstat:
MCargo.lock | 57---------------------------------------------------------
MCargo.toml | 5++---
MREADME.md | 8++++++--
MTeraFFI/provenance.json | 26+++++++++++++-------------
MTeraFFI/source.lock | 4++--
MTeraFFI/source/aarch64-apple-darwin.json | 58+++++++++++++++++++++++++++++++++++-----------------------
MTeraFFI/source/aarch64-apple-ios-sim.json | 58+++++++++++++++++++++++++++++++++++-----------------------
MTeraFFI/source/aarch64-apple-ios.json | 58+++++++++++++++++++++++++++++++++++-----------------------
Dcrates/source_lock/Cargo.toml | 16----------------
Dcrates/source_lock/src/lib.rs | 1-
Mrelease/provenance.json | 8++++----
Mrelease/sbom.cdx.json | 116-------------------------------------------------------------------------------
Ascripts/app_dependency_graph.py | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mscripts/linux-shared-rust.sh | 2+-
Mscripts/package_contract.py | 27++++++++++-----------------
Mscripts/swift-quality.sh | 2++
Ascripts/test_app_dependency_graph.py | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mscripts/test_package_contract.py | 6+++---
Mscripts/verify-package-contract.sh | 1+
19 files changed, 368 insertions(+), 304 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -1852,63 +1852,6 @@ dependencies = [ ] [[package]] -name = "radroots_ios_source_lock" -version = "0.1.0-alpha" -dependencies = [ - "radroots_mobile_ffi", -] - -[[package]] -name = "radroots_mobile_core" -version = "0.1.0-alpha" -source = "git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb" -dependencies = [ - "chrono", - "hex", - "radroots_blossom", - "radroots_event", - "radroots_event_codec", - "radroots_identity", - "radroots_protocol", - "radroots_sdk", - "radroots_signing", - "radroots_storage", - "radroots_sync", - "radroots_transport", - "radroots_transport_nostr", - "serde", - "serde_json", - "sha2", - "thiserror 1.0.69", - "tokio", - "url", - "uuid", -] - -[[package]] -name = "radroots_mobile_ffi" -version = "0.1.0-alpha" -source = "git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb" -dependencies = [ - "async-trait", - "hex", - "libc", - "radroots_blossom", - "radroots_event", - "radroots_mobile_core", - "radroots_sdk", - "radroots_signing", - "radroots_storage", - "rustix", - "serde_json", - "thiserror 1.0.69", - "tracing", - "tracing-appender", - "tracing-subscriber", - "uniffi", -] - -[[package]] name = "radroots_nostr" version = "0.1.0-alpha" source = "git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb" diff --git a/Cargo.toml b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] -members = ["crates/source_lock", "core/crates/tera_core", "core/crates/tera_ffi", "core/crates/tera_bindgen", "core/crates/tera_wasm"] -default-members = ["crates/source_lock", "core/crates/tera_core", "core/crates/tera_ffi", "core/crates/tera_bindgen"] +members = ["core/crates/tera_core", "core/crates/tera_ffi", "core/crates/tera_bindgen", "core/crates/tera_wasm"] +default-members = ["core/crates/tera_core", "core/crates/tera_ffi", "core/crates/tera_bindgen"] resolver = "3" [workspace.package] @@ -25,7 +25,6 @@ todo = "deny" unimplemented = "deny" [workspace.dependencies] -radroots_mobile_ffi = { git = "https://github.com/radrootslabs/lib", rev = "ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb", version = "=0.1.0-alpha" } chrono = { version = "0.4" } hex = { version = "0.4" } nostr = { version = "0.44.7", default-features = false } diff --git a/README.md b/README.md @@ -204,8 +204,12 @@ the TOML, plist, JSON, xcconfig, project-package, and lock inputs as structured, bounded data. It also runs the locked fixture and verifier unit suites. The check evaluates Cargo's workspace graph and rejects members or local dependencies outside this standalone repository, including implicit sibling -checkouts. Human specifications and execution evidence remain outside the -capsule and are never required by these checks. +checkouts. The default Rust lane selects `tera_core`, `tera_ffi`, and +`tera_bindgen`; `tera_wasm` remains non-default. The resolved graph must use +the exact shared foundation lock, activate the FFI mobile-social profile, and +contain no retired source-lock shim or old mobile producer. Human specifications +and execution evidence remain outside the capsule and are never required by +these checks. Comments, examples, unreachable source, and arbitrary matching text cannot satisfy a behavior-bearing package assertion; application behavior is proven by the compiled Swift and simulator test lanes. diff --git a/TeraFFI/provenance.json b/TeraFFI/provenance.json @@ -92,19 +92,19 @@ "sha256": "a2b3b65e4e3c6359d342ae802326ab755f87f837e15f2c53927be48b3d7f7587" }, { - "bytes": 52290, + "bytes": 52795, "path": "source/aarch64-apple-darwin.json", - "sha256": "1a0e6e7fe48eed14f38bb484b6468881042803345b10b72cb11f55916086dd4e" + "sha256": "2dd0558e69f6119b49edb04b70754b7a61315dfbb0062292c7c8ad4106c3d522" }, { - "bytes": 52291, + "bytes": 52796, "path": "source/aarch64-apple-ios-sim.json", - "sha256": "97fa307acc0a6547427a06e58bdb6fd8499c4fff4bdf962c4aaeca79dbe45a16" + "sha256": "da7f20d4c59c4de9db00d19f042ec2d54e8c07acbf1362f478888ced9e2c41df" }, { - "bytes": 52287, + "bytes": 52792, "path": "source/aarch64-apple-ios.json", - "sha256": "c615140649fbc7ba5ed54623fc3e256717be733880a3d28955c4ffebc2bb16ab" + "sha256": "e62015a85cb77e336f9b898136ab0cf5a2679742c99233ea86f9599de1c723d6" } ], "language": "swift", @@ -112,7 +112,7 @@ "schema": "radroots.artifact-manifest.v2", "source": { "repository": "https://github.com/radrootslabs/tera", - "tree": "53766008d73e0e507621e6e4a9177b08edb0bd10" + "tree": "b9b252434caf06c74f2d6bd9fbfd569784f22745" }, "source_records": { "aarch64-apple-darwin": "source/aarch64-apple-darwin.json", @@ -174,19 +174,19 @@ "sha256": "6542c8294920b3c41e987619de58690e2af16896079910b657129d3573b34e68" }, { - "bytes": 52290, + "bytes": 52795, "path": "TeraFFI/source/aarch64-apple-darwin.json", - "sha256": "1a0e6e7fe48eed14f38bb484b6468881042803345b10b72cb11f55916086dd4e" + "sha256": "2dd0558e69f6119b49edb04b70754b7a61315dfbb0062292c7c8ad4106c3d522" }, { - "bytes": 52291, + "bytes": 52796, "path": "TeraFFI/source/aarch64-apple-ios-sim.json", - "sha256": "97fa307acc0a6547427a06e58bdb6fd8499c4fff4bdf962c4aaeca79dbe45a16" + "sha256": "da7f20d4c59c4de9db00d19f042ec2d54e8c07acbf1362f478888ced9e2c41df" }, { - "bytes": 52287, + "bytes": 52792, "path": "TeraFFI/source/aarch64-apple-ios.json", - "sha256": "c615140649fbc7ba5ed54623fc3e256717be733880a3d28955c4ffebc2bb16ab" + "sha256": "e62015a85cb77e336f9b898136ab0cf5a2679742c99233ea86f9599de1c723d6" } ], "schema": "tera.installed-native-artifacts.v1" diff --git a/TeraFFI/source.lock b/TeraFFI/source.lock @@ -1,7 +1,7 @@ schema = "tera.installed-source.v1" repository = "https://github.com/radrootslabs/tera" -source_tree = "53766008d73e0e507621e6e4a9177b08edb0bd10" -manifest_sha256 = "69da908ebf070fb9a9d8727ec44a16e8ea2eb79687328127d991142a1a12ea4e" +source_tree = "b9b252434caf06c74f2d6bd9fbfd569784f22745" +manifest_sha256 = "9179e5238481b79193477fe555c886d9825440942bd8013fd0c399fc5b547147" source_date_epoch = 1787871027 [foundation] diff --git a/TeraFFI/source/aarch64-apple-darwin.json b/TeraFFI/source/aarch64-apple-darwin.json @@ -378,7 +378,7 @@ "symbol_reader": "llvm-nm, compatible with GNU nm\nLLVM (http://llvm.org/):\n LLVM version 22.1.6-rust-1.97.1-stable\n Optimized build.", "target": "aarch64-apple-darwin" }, - "cargo_lock_sha256": "079959dfa1a878ada18599878f25a0d53505253ab6996141be6ff31495baf166", + "cargo_lock_sha256": "12cf54a9530f957d9b1721ff66d83ded16d0f38b0170b0e4ba8648c6c168524c", "disposition": "local_staged_source_only", "foundation": { "architecture": "radroots.crates.release.v2", @@ -494,16 +494,16 @@ "source": { "files": { "Cargo.lock": { - "bytes": 93741, - "git_blob": "a011509b28e98939378e4ddad028c71b0880e40e", + "bytes": 92572, + "git_blob": "229ab16873cb6a2218fc364833b55dd311e17aef", "mode": "100644", - "sha256": "079959dfa1a878ada18599878f25a0d53505253ab6996141be6ff31495baf166" + "sha256": "12cf54a9530f957d9b1721ff66d83ded16d0f38b0170b0e4ba8648c6c168524c" }, "Cargo.toml": { - "bytes": 4287, - "git_blob": "384a352a5c82726e5f7e387edde30ff460453485", + "bytes": 4097, + "git_blob": "f9ede664bf374ab8c117c4f5ac508a5085447b37", "mode": "100644", - "sha256": "84715e8fd768b59b2ac9da43fb8abc1d7fdee61e64da679b12bc9beebec7f151" + "sha256": "d21288656485d0812e1328a65bfccbdd29fe70b44258cc236a036f844cb6b532" }, "Makefile": { "bytes": 3697, @@ -961,6 +961,12 @@ "mode": "100755", "sha256": "6e657582fa823c6ae702f7241c9770c7ef152f3c403b8bbc800964061b35e829" }, + "scripts/app_dependency_graph.py": { + "bytes": 4524, + "git_blob": "dfae24f744b85ad68bf84c6ba982e7786c93bb62", + "mode": "100644", + "sha256": "307d03f30d75c9a88b323328926c92624ae02b699cd1df10674aa183bac0ee81" + }, "scripts/ffi-artifacts.sh": { "bytes": 210, "git_blob": "be7f39d8f358ae34087232abe318d13ab867471c", @@ -1010,10 +1016,10 @@ "sha256": "36f5b334f88928c2bb96d5b0f9e560b148c0b4d449696c13ef0d201f1355db3e" }, "scripts/linux-shared-rust.sh": { - "bytes": 1341, - "git_blob": "e406f7f4175af3d19b5a6757445c570c770806a8", + "bytes": 1313, + "git_blob": "cba195bbea7afecf58375b8b2d72906057219e47", "mode": "100755", - "sha256": "39b116f2ba7cb9e5a1c49d37823005076d061a21c8d83e2b4eedbde995025edd" + "sha256": "ec59d7d3a71d055d5cc9f42ba78f3a9854410ac91da1ef16ee34e0898649bd4c" }, "scripts/local-social-fixture.py": { "bytes": 100995, @@ -1028,10 +1034,10 @@ "sha256": "fff99e755f35fa9f00708427b1b29290c0ecc2733c0ea070f5807ec7c37f6f11" }, "scripts/package_contract.py": { - "bytes": 26232, - "git_blob": "88aafd04761a4cc5fb38532f9a7e61b58754fe58", + "bytes": 26134, + "git_blob": "5dae06288f14200c699d8f043feb7050bd903dd8", "mode": "100755", - "sha256": "c3898cd7aef5a9dcf506adf95dcb7191ba8988de48fe5eb981249d479711bed5" + "sha256": "d89ea3b8cd0fbc0ef179b8a07c0ef75ed11218fe7565c058fd94b4e4a6707874" }, "scripts/persona-verifier.sh": { "bytes": 240, @@ -1070,10 +1076,16 @@ "sha256": "1803572abdb2e22bedd275724edae5f09c523b132bc59086aacca0f5183905d0" }, "scripts/swift-quality.sh": { - "bytes": 1847, - "git_blob": "533a74081ab542b183e4fd3797945dbb7b758a7f", + "bytes": 1920, + "git_blob": "ba64f9f2d37b66e4d7ec3dd3488b9947bd3a2c68", "mode": "100755", - "sha256": "b7af93ad40124f4ddd46b0e6627a1bb9c0553a38c907d87d258306f9962ed9de" + "sha256": "b57d6a6499f65ee7ce2c94b2b103c5de84be7a4ff79af2c8e5fde9ac827ed4f9" + }, + "scripts/test_app_dependency_graph.py": { + "bytes": 4007, + "git_blob": "615b4054a6b968bdde3c2107e349b352dfb0fa34", + "mode": "100644", + "sha256": "d336abd6368ad3b0d0171b32aaa4a44d9c1a8551899d39a1445eca05b67d8b30" }, "scripts/test_ffi_artifacts.py": { "bytes": 7460, @@ -1106,16 +1118,16 @@ "sha256": "1a5384a6109d495477dd7848a367c2452f87c36e46dbab2c20b98940a4d4fed6" }, "scripts/test_package_contract.py": { - "bytes": 9134, - "git_blob": "09075fc17980006b33c6a65a62aa7190a09d13a1", + "bytes": 9119, + "git_blob": "2136f76eb701b68ec9dca85d4c8d25c9bcf6e4fa", "mode": "100644", - "sha256": "eb32b03d89cdf0e7519594272ed6217b795cfcd820c977f7c139dc71f3b5b3a2" + "sha256": "c4d602dc30b0865d6eab40b4703712d237911e64ec95f450f316faea7e123b49" }, "scripts/verify-package-contract.sh": { - "bytes": 1347, - "git_blob": "3851d42e53d12ea4f0d79d8bb50000c58bcca1da", + "bytes": 1388, + "git_blob": "4a20535a8810a4d03d7ebf0cec5a2f55c1f3d2ac", "mode": "100755", - "sha256": "a8ec486ce0b2163faccfe43ae27a2c4f59610ce1c3a7572f23020902d1654237" + "sha256": "0bceebe7678eb44ab8518bda8cfcfd76190ee341982d66ba1c940c3461f0944d" }, "scripts/xcode.sh": { "bytes": 21183, @@ -1125,6 +1137,6 @@ } }, "policy": "staged_inputs", - "tree": "53766008d73e0e507621e6e4a9177b08edb0bd10" + "tree": "b9b252434caf06c74f2d6bd9fbfd569784f22745" } } diff --git a/TeraFFI/source/aarch64-apple-ios-sim.json b/TeraFFI/source/aarch64-apple-ios-sim.json @@ -378,7 +378,7 @@ "symbol_reader": "llvm-nm, compatible with GNU nm\nLLVM (http://llvm.org/):\n LLVM version 22.1.6-rust-1.97.1-stable\n Optimized build.", "target": "aarch64-apple-ios-sim" }, - "cargo_lock_sha256": "079959dfa1a878ada18599878f25a0d53505253ab6996141be6ff31495baf166", + "cargo_lock_sha256": "12cf54a9530f957d9b1721ff66d83ded16d0f38b0170b0e4ba8648c6c168524c", "disposition": "local_staged_source_only", "foundation": { "architecture": "radroots.crates.release.v2", @@ -494,16 +494,16 @@ "source": { "files": { "Cargo.lock": { - "bytes": 93741, - "git_blob": "a011509b28e98939378e4ddad028c71b0880e40e", + "bytes": 92572, + "git_blob": "229ab16873cb6a2218fc364833b55dd311e17aef", "mode": "100644", - "sha256": "079959dfa1a878ada18599878f25a0d53505253ab6996141be6ff31495baf166" + "sha256": "12cf54a9530f957d9b1721ff66d83ded16d0f38b0170b0e4ba8648c6c168524c" }, "Cargo.toml": { - "bytes": 4287, - "git_blob": "384a352a5c82726e5f7e387edde30ff460453485", + "bytes": 4097, + "git_blob": "f9ede664bf374ab8c117c4f5ac508a5085447b37", "mode": "100644", - "sha256": "84715e8fd768b59b2ac9da43fb8abc1d7fdee61e64da679b12bc9beebec7f151" + "sha256": "d21288656485d0812e1328a65bfccbdd29fe70b44258cc236a036f844cb6b532" }, "Makefile": { "bytes": 3697, @@ -961,6 +961,12 @@ "mode": "100755", "sha256": "6e657582fa823c6ae702f7241c9770c7ef152f3c403b8bbc800964061b35e829" }, + "scripts/app_dependency_graph.py": { + "bytes": 4524, + "git_blob": "dfae24f744b85ad68bf84c6ba982e7786c93bb62", + "mode": "100644", + "sha256": "307d03f30d75c9a88b323328926c92624ae02b699cd1df10674aa183bac0ee81" + }, "scripts/ffi-artifacts.sh": { "bytes": 210, "git_blob": "be7f39d8f358ae34087232abe318d13ab867471c", @@ -1010,10 +1016,10 @@ "sha256": "36f5b334f88928c2bb96d5b0f9e560b148c0b4d449696c13ef0d201f1355db3e" }, "scripts/linux-shared-rust.sh": { - "bytes": 1341, - "git_blob": "e406f7f4175af3d19b5a6757445c570c770806a8", + "bytes": 1313, + "git_blob": "cba195bbea7afecf58375b8b2d72906057219e47", "mode": "100755", - "sha256": "39b116f2ba7cb9e5a1c49d37823005076d061a21c8d83e2b4eedbde995025edd" + "sha256": "ec59d7d3a71d055d5cc9f42ba78f3a9854410ac91da1ef16ee34e0898649bd4c" }, "scripts/local-social-fixture.py": { "bytes": 100995, @@ -1028,10 +1034,10 @@ "sha256": "fff99e755f35fa9f00708427b1b29290c0ecc2733c0ea070f5807ec7c37f6f11" }, "scripts/package_contract.py": { - "bytes": 26232, - "git_blob": "88aafd04761a4cc5fb38532f9a7e61b58754fe58", + "bytes": 26134, + "git_blob": "5dae06288f14200c699d8f043feb7050bd903dd8", "mode": "100755", - "sha256": "c3898cd7aef5a9dcf506adf95dcb7191ba8988de48fe5eb981249d479711bed5" + "sha256": "d89ea3b8cd0fbc0ef179b8a07c0ef75ed11218fe7565c058fd94b4e4a6707874" }, "scripts/persona-verifier.sh": { "bytes": 240, @@ -1070,10 +1076,16 @@ "sha256": "1803572abdb2e22bedd275724edae5f09c523b132bc59086aacca0f5183905d0" }, "scripts/swift-quality.sh": { - "bytes": 1847, - "git_blob": "533a74081ab542b183e4fd3797945dbb7b758a7f", + "bytes": 1920, + "git_blob": "ba64f9f2d37b66e4d7ec3dd3488b9947bd3a2c68", "mode": "100755", - "sha256": "b7af93ad40124f4ddd46b0e6627a1bb9c0553a38c907d87d258306f9962ed9de" + "sha256": "b57d6a6499f65ee7ce2c94b2b103c5de84be7a4ff79af2c8e5fde9ac827ed4f9" + }, + "scripts/test_app_dependency_graph.py": { + "bytes": 4007, + "git_blob": "615b4054a6b968bdde3c2107e349b352dfb0fa34", + "mode": "100644", + "sha256": "d336abd6368ad3b0d0171b32aaa4a44d9c1a8551899d39a1445eca05b67d8b30" }, "scripts/test_ffi_artifacts.py": { "bytes": 7460, @@ -1106,16 +1118,16 @@ "sha256": "1a5384a6109d495477dd7848a367c2452f87c36e46dbab2c20b98940a4d4fed6" }, "scripts/test_package_contract.py": { - "bytes": 9134, - "git_blob": "09075fc17980006b33c6a65a62aa7190a09d13a1", + "bytes": 9119, + "git_blob": "2136f76eb701b68ec9dca85d4c8d25c9bcf6e4fa", "mode": "100644", - "sha256": "eb32b03d89cdf0e7519594272ed6217b795cfcd820c977f7c139dc71f3b5b3a2" + "sha256": "c4d602dc30b0865d6eab40b4703712d237911e64ec95f450f316faea7e123b49" }, "scripts/verify-package-contract.sh": { - "bytes": 1347, - "git_blob": "3851d42e53d12ea4f0d79d8bb50000c58bcca1da", + "bytes": 1388, + "git_blob": "4a20535a8810a4d03d7ebf0cec5a2f55c1f3d2ac", "mode": "100755", - "sha256": "a8ec486ce0b2163faccfe43ae27a2c4f59610ce1c3a7572f23020902d1654237" + "sha256": "0bceebe7678eb44ab8518bda8cfcfd76190ee341982d66ba1c940c3461f0944d" }, "scripts/xcode.sh": { "bytes": 21183, @@ -1125,6 +1137,6 @@ } }, "policy": "staged_inputs", - "tree": "53766008d73e0e507621e6e4a9177b08edb0bd10" + "tree": "b9b252434caf06c74f2d6bd9fbfd569784f22745" } } diff --git a/TeraFFI/source/aarch64-apple-ios.json b/TeraFFI/source/aarch64-apple-ios.json @@ -378,7 +378,7 @@ "symbol_reader": "llvm-nm, compatible with GNU nm\nLLVM (http://llvm.org/):\n LLVM version 22.1.6-rust-1.97.1-stable\n Optimized build.", "target": "aarch64-apple-ios" }, - "cargo_lock_sha256": "079959dfa1a878ada18599878f25a0d53505253ab6996141be6ff31495baf166", + "cargo_lock_sha256": "12cf54a9530f957d9b1721ff66d83ded16d0f38b0170b0e4ba8648c6c168524c", "disposition": "local_staged_source_only", "foundation": { "architecture": "radroots.crates.release.v2", @@ -494,16 +494,16 @@ "source": { "files": { "Cargo.lock": { - "bytes": 93741, - "git_blob": "a011509b28e98939378e4ddad028c71b0880e40e", + "bytes": 92572, + "git_blob": "229ab16873cb6a2218fc364833b55dd311e17aef", "mode": "100644", - "sha256": "079959dfa1a878ada18599878f25a0d53505253ab6996141be6ff31495baf166" + "sha256": "12cf54a9530f957d9b1721ff66d83ded16d0f38b0170b0e4ba8648c6c168524c" }, "Cargo.toml": { - "bytes": 4287, - "git_blob": "384a352a5c82726e5f7e387edde30ff460453485", + "bytes": 4097, + "git_blob": "f9ede664bf374ab8c117c4f5ac508a5085447b37", "mode": "100644", - "sha256": "84715e8fd768b59b2ac9da43fb8abc1d7fdee61e64da679b12bc9beebec7f151" + "sha256": "d21288656485d0812e1328a65bfccbdd29fe70b44258cc236a036f844cb6b532" }, "Makefile": { "bytes": 3697, @@ -961,6 +961,12 @@ "mode": "100755", "sha256": "6e657582fa823c6ae702f7241c9770c7ef152f3c403b8bbc800964061b35e829" }, + "scripts/app_dependency_graph.py": { + "bytes": 4524, + "git_blob": "dfae24f744b85ad68bf84c6ba982e7786c93bb62", + "mode": "100644", + "sha256": "307d03f30d75c9a88b323328926c92624ae02b699cd1df10674aa183bac0ee81" + }, "scripts/ffi-artifacts.sh": { "bytes": 210, "git_blob": "be7f39d8f358ae34087232abe318d13ab867471c", @@ -1010,10 +1016,10 @@ "sha256": "36f5b334f88928c2bb96d5b0f9e560b148c0b4d449696c13ef0d201f1355db3e" }, "scripts/linux-shared-rust.sh": { - "bytes": 1341, - "git_blob": "e406f7f4175af3d19b5a6757445c570c770806a8", + "bytes": 1313, + "git_blob": "cba195bbea7afecf58375b8b2d72906057219e47", "mode": "100755", - "sha256": "39b116f2ba7cb9e5a1c49d37823005076d061a21c8d83e2b4eedbde995025edd" + "sha256": "ec59d7d3a71d055d5cc9f42ba78f3a9854410ac91da1ef16ee34e0898649bd4c" }, "scripts/local-social-fixture.py": { "bytes": 100995, @@ -1028,10 +1034,10 @@ "sha256": "fff99e755f35fa9f00708427b1b29290c0ecc2733c0ea070f5807ec7c37f6f11" }, "scripts/package_contract.py": { - "bytes": 26232, - "git_blob": "88aafd04761a4cc5fb38532f9a7e61b58754fe58", + "bytes": 26134, + "git_blob": "5dae06288f14200c699d8f043feb7050bd903dd8", "mode": "100755", - "sha256": "c3898cd7aef5a9dcf506adf95dcb7191ba8988de48fe5eb981249d479711bed5" + "sha256": "d89ea3b8cd0fbc0ef179b8a07c0ef75ed11218fe7565c058fd94b4e4a6707874" }, "scripts/persona-verifier.sh": { "bytes": 240, @@ -1070,10 +1076,16 @@ "sha256": "1803572abdb2e22bedd275724edae5f09c523b132bc59086aacca0f5183905d0" }, "scripts/swift-quality.sh": { - "bytes": 1847, - "git_blob": "533a74081ab542b183e4fd3797945dbb7b758a7f", + "bytes": 1920, + "git_blob": "ba64f9f2d37b66e4d7ec3dd3488b9947bd3a2c68", "mode": "100755", - "sha256": "b7af93ad40124f4ddd46b0e6627a1bb9c0553a38c907d87d258306f9962ed9de" + "sha256": "b57d6a6499f65ee7ce2c94b2b103c5de84be7a4ff79af2c8e5fde9ac827ed4f9" + }, + "scripts/test_app_dependency_graph.py": { + "bytes": 4007, + "git_blob": "615b4054a6b968bdde3c2107e349b352dfb0fa34", + "mode": "100644", + "sha256": "d336abd6368ad3b0d0171b32aaa4a44d9c1a8551899d39a1445eca05b67d8b30" }, "scripts/test_ffi_artifacts.py": { "bytes": 7460, @@ -1106,16 +1118,16 @@ "sha256": "1a5384a6109d495477dd7848a367c2452f87c36e46dbab2c20b98940a4d4fed6" }, "scripts/test_package_contract.py": { - "bytes": 9134, - "git_blob": "09075fc17980006b33c6a65a62aa7190a09d13a1", + "bytes": 9119, + "git_blob": "2136f76eb701b68ec9dca85d4c8d25c9bcf6e4fa", "mode": "100644", - "sha256": "eb32b03d89cdf0e7519594272ed6217b795cfcd820c977f7c139dc71f3b5b3a2" + "sha256": "c4d602dc30b0865d6eab40b4703712d237911e64ec95f450f316faea7e123b49" }, "scripts/verify-package-contract.sh": { - "bytes": 1347, - "git_blob": "3851d42e53d12ea4f0d79d8bb50000c58bcca1da", + "bytes": 1388, + "git_blob": "4a20535a8810a4d03d7ebf0cec5a2f55c1f3d2ac", "mode": "100755", - "sha256": "a8ec486ce0b2163faccfe43ae27a2c4f59610ce1c3a7572f23020902d1654237" + "sha256": "0bceebe7678eb44ab8518bda8cfcfd76190ee341982d66ba1c940c3461f0944d" }, "scripts/xcode.sh": { "bytes": 21183, @@ -1125,6 +1137,6 @@ } }, "policy": "staged_inputs", - "tree": "53766008d73e0e507621e6e4a9177b08edb0bd10" + "tree": "b9b252434caf06c74f2d6bd9fbfd569784f22745" } } diff --git a/crates/source_lock/Cargo.toml b/crates/source_lock/Cargo.toml @@ -1,16 +0,0 @@ -[package] -name = "radroots_ios_source_lock" -version.workspace = true -edition.workspace = true -rust-version.workspace = true -license.workspace = true -repository.workspace = true -homepage.workspace = true -authors.workspace = true -publish = false - -[dependencies] -radroots_mobile_ffi = { workspace = true } - -[lints] -workspace = true diff --git a/crates/source_lock/src/lib.rs b/crates/source_lock/src/lib.rs @@ -1 +0,0 @@ -#![no_std] diff --git a/release/provenance.json b/release/provenance.json @@ -2,10 +2,10 @@ "artifacts": { "app_api_sha256": "020924097c0d7efc33128cb8fd3d3b2026d95f57c44da71880e585aff80f070b", "ffi_api_sha256": "6542c8294920b3c41e987619de58690e2af16896079910b657129d3573b34e68", - "ffi_provenance_sha256": "69da908ebf070fb9a9d8727ec44a16e8ea2eb79687328127d991142a1a12ea4e", + "ffi_provenance_sha256": "9179e5238481b79193477fe555c886d9825440942bd8013fd0c399fc5b547147", "info_plist_sha256": "15ef08b1cdd1096cfb9eeaf5be5bf8f814807a7ca9350bbbb47860fa72ec13ef", "privacy_manifest_sha256": "a331d51864743ebe4e00dd22360b4a538b6b3ac26a6b3eb54094e60a36959a12", - "sbom_sha256": "694d7a3b79d804eaa80557678ad2cb2a5bb5ff199f93d2db4cfe37cefde8d394", + "sbom_sha256": "6b5ce897d5273290febc8b831663e12dea759cb0084f182ba7949e1c593fc410", "xcode_project_sha256": "8539067560a291fde4d1acc083fe3810dd37eae99505a2532abb440a7ad0cf0d" }, "disposition": "unsigned", @@ -17,12 +17,12 @@ "repository": "https://github.com/radrootslabs/tera", "schema": "tera.release-provenance.v1", "source": { - "cargo_lock_sha256": "079959dfa1a878ada18599878f25a0d53505253ab6996141be6ff31495baf166", + "cargo_lock_sha256": "12cf54a9530f957d9b1721ff66d83ded16d0f38b0170b0e4ba8648c6c168524c", "consumer_source_lock_sha256": "ed9b959d4de479b8cc4eb58b8f584bc179019f95fdb3c32c3934098a9ce89cca", "lib_revision": "ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb", "source_date_epoch": 1787871027, "swift_package_lock_sha256": "94ae067a374726cdaf6b4ca0a5e44663c57fcdc5334060c5ffef5e79cfbf04c0", - "tera_ffi_source_tree": "53766008d73e0e507621e6e4a9177b08edb0bd10", + "tera_ffi_source_tree": "b9b252434caf06c74f2d6bd9fbfd569784f22745", "xcode_package_lock_sha256": "c7f41934ea25f7a287bdc4f3a6ecabbf09a3a0bdd0f5a3e58183f355ca814096" }, "version": "0.1.0-alpha" diff --git a/release/sbom.cdx.json b/release/sbom.cdx.json @@ -3950,69 +3950,6 @@ "version": "0.1.0-alpha" }, { - "bom-ref": "cargo:radroots_ios_source_lock@0.1.0-alpha?source=workspace", - "licenses": [ - { - "expression": "GPL-3.0-or-later" - } - ], - "name": "radroots_ios_source_lock", - "properties": [ - { - "name": "radroots.package.manager", - "value": "cargo" - }, - { - "name": "radroots.package.source", - "value": "workspace" - } - ], - "type": "library", - "version": "0.1.0-alpha" - }, - { - "bom-ref": "cargo:radroots_mobile_core@0.1.0-alpha?source=git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb", - "licenses": [ - { - "expression": "GPL-3.0-or-later" - } - ], - "name": "radroots_mobile_core", - "properties": [ - { - "name": "radroots.package.manager", - "value": "cargo" - }, - { - "name": "radroots.package.source", - "value": "git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb" - } - ], - "type": "library", - "version": "0.1.0-alpha" - }, - { - "bom-ref": "cargo:radroots_mobile_ffi@0.1.0-alpha?source=git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb", - "licenses": [ - { - "expression": "GPL-3.0-or-later" - } - ], - "name": "radroots_mobile_ffi", - "properties": [ - { - "name": "radroots.package.manager", - "value": "cargo" - }, - { - "name": "radroots.package.source", - "value": "git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb" - } - ], - "type": "library", - "version": "0.1.0-alpha" - }, - { "bom-ref": "cargo:radroots_nostr@0.1.0-alpha?source=git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb", "licenses": [ { @@ -9134,58 +9071,6 @@ }, { "dependsOn": [ - "cargo:radroots_mobile_ffi@0.1.0-alpha?source=git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb" - ], - "ref": "cargo:radroots_ios_source_lock@0.1.0-alpha?source=workspace" - }, - { - "dependsOn": [ - "cargo:chrono@0.4.45?source=registry+https://github.com/rust-lang/crates.io-index", - "cargo:hex@0.4.3?source=registry+https://github.com/rust-lang/crates.io-index", - "cargo:radroots_blossom@0.1.0-alpha?source=git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb", - "cargo:radroots_event@0.1.0-alpha?source=git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb", - "cargo:radroots_event_codec@0.1.0-alpha?source=git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb", - "cargo:radroots_identity@0.1.0-alpha?source=git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb", - "cargo:radroots_protocol@0.1.0-alpha?source=git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb", - "cargo:radroots_sdk@0.1.0-alpha?source=git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb", - "cargo:radroots_signing@0.1.0-alpha?source=git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb", - "cargo:radroots_storage@0.1.0-alpha?source=git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb", - "cargo:radroots_sync@0.1.0-alpha?source=git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb", - "cargo:radroots_transport@0.1.0-alpha?source=git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb", - "cargo:radroots_transport_nostr@0.1.0-alpha?source=git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb", - "cargo:serde@1.0.229?source=registry+https://github.com/rust-lang/crates.io-index", - "cargo:serde_json@1.0.151?source=registry+https://github.com/rust-lang/crates.io-index", - "cargo:sha2@0.10.9?source=registry+https://github.com/rust-lang/crates.io-index", - "cargo:thiserror@1.0.69?source=registry+https://github.com/rust-lang/crates.io-index", - "cargo:tokio@1.53.1?source=registry+https://github.com/rust-lang/crates.io-index", - "cargo:url@2.5.8?source=registry+https://github.com/rust-lang/crates.io-index", - "cargo:uuid@1.24.0?source=registry+https://github.com/rust-lang/crates.io-index" - ], - "ref": "cargo:radroots_mobile_core@0.1.0-alpha?source=git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb" - }, - { - "dependsOn": [ - "cargo:async-trait@0.1.91?source=registry+https://github.com/rust-lang/crates.io-index", - "cargo:hex@0.4.3?source=registry+https://github.com/rust-lang/crates.io-index", - "cargo:libc@0.2.189?source=registry+https://github.com/rust-lang/crates.io-index", - "cargo:radroots_blossom@0.1.0-alpha?source=git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb", - "cargo:radroots_event@0.1.0-alpha?source=git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb", - "cargo:radroots_mobile_core@0.1.0-alpha?source=git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb", - "cargo:radroots_sdk@0.1.0-alpha?source=git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb", - "cargo:radroots_signing@0.1.0-alpha?source=git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb", - "cargo:radroots_storage@0.1.0-alpha?source=git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb", - "cargo:rustix@1.1.4?source=registry+https://github.com/rust-lang/crates.io-index", - "cargo:serde_json@1.0.151?source=registry+https://github.com/rust-lang/crates.io-index", - "cargo:thiserror@1.0.69?source=registry+https://github.com/rust-lang/crates.io-index", - "cargo:tracing-appender@0.2.5?source=registry+https://github.com/rust-lang/crates.io-index", - "cargo:tracing-subscriber@0.3.23?source=registry+https://github.com/rust-lang/crates.io-index", - "cargo:tracing@0.1.44?source=registry+https://github.com/rust-lang/crates.io-index", - "cargo:uniffi@0.29.5?source=registry+https://github.com/rust-lang/crates.io-index" - ], - "ref": "cargo:radroots_mobile_ffi@0.1.0-alpha?source=git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb" - }, - { - "dependsOn": [ "cargo:base64@0.22.1?source=registry+https://github.com/rust-lang/crates.io-index", "cargo:nostr@0.44.8?source=registry+https://github.com/rust-lang/crates.io-index", "cargo:radroots_blossom@0.1.0-alpha?source=git+https://github.com/radrootslabs/lib?rev=ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb#ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb", @@ -10622,7 +10507,6 @@ }, { "dependsOn": [ - "cargo:radroots_ios_source_lock@0.1.0-alpha?source=workspace", "cargo:tera_bindgen@0.1.0-alpha?source=workspace", "cargo:tera_core@0.1.0-alpha?source=workspace", "cargo:tera_ffi@0.1.0-alpha?source=workspace", diff --git a/scripts/app_dependency_graph.py b/scripts/app_dependency_graph.py @@ -0,0 +1,121 @@ +"""Validate the real locked application graph after the proxy crate is retired.""" + +from __future__ import annotations + +from typing import Any + +OWNED = {"tera_core", "tera_ffi", "tera_bindgen", "tera_wasm"} +MOBILE = OWNED - {"tera_wasm"} +RETIRED = { + "radroots_ios_source_lock", + "radroots_mobile_core", + "radroots_mobile_ffi", + "radroots_mobile_bindgen", + "radroots_mobile_wasm", +} + + +class GraphError(ValueError): + """A stable rejection without dependency source paths or command output.""" + + +def require(condition: bool, reason: str) -> None: + if not condition: + raise GraphError(reason) + + +def indexed(rows: Any, label: str) -> dict[str, dict[str, Any]]: + require(isinstance(rows, list) and bool(rows), f"{label} inventory is absent") + result = {} + for row in rows: + require(isinstance(row, dict), f"{label} entry is malformed") + identifier = row.get("id") + require( + isinstance(identifier, str) and bool(identifier), + f"{label} identity is absent", + ) + require(identifier not in result, f"{label} identity is duplicated") + result[identifier] = row + return result + + +def owned_packages( + document: dict[str, Any], packages: dict[str, Any] +) -> dict[str, Any]: + members = document.get("workspace_members") + require(isinstance(members, list), "owned workspace members are absent") + require(len(members) == len(OWNED), "owned workspace inventory differs") + require( + all(identifier in packages for identifier in members), "owned package is absent" + ) + owned = { + packages[identifier]["name"]: packages[identifier] for identifier in members + } + require(set(owned) == OWNED, "owned workspace inventory differs") + defaults = document.get("workspace_default_members") + require(isinstance(defaults, list), "mobile defaults are absent") + require( + sorted(defaults) == sorted(owned[name]["id"] for name in MOBILE), + "mobile defaults must select the real native packages only", + ) + return owned + + +def foundation_sources(packages: dict[str, Any], foundation: dict[str, Any]) -> None: + revision = foundation["revision"] + expected = f"git+{foundation['repository']}?rev={revision}#{revision}" + shared = [] + for package in packages.values(): + name = package.get("name") + require(isinstance(name, str), "resolved package name is absent") + require( + name not in RETIRED, + "retired mobile producer or source-lock shim is present", + ) + if name.startswith("radroots_"): + shared.append(package) + require( + package.get("source") == expected, "resolved foundation source differs" + ) + require( + package.get("version") == foundation["version"], + "resolved foundation version differs", + ) + elif package.get("source") is None: + require( + name in OWNED, "unowned local package entered the application graph" + ) + require(bool(shared), "resolved foundation inventory is empty") + + +def mobile_profile(owned: dict[str, Any], nodes: dict[str, Any]) -> None: + ffi = owned["tera_ffi"] + edges = ffi.get("dependencies") + require(isinstance(edges, list), "owned FFI dependency inventory is absent") + core_edges = [edge for edge in edges if edge.get("name") == "tera_core"] + require(len(core_edges) == 1, "owned FFI must select one application core") + edge = core_edges[0] + require( + edge.get("source") is None and edge.get("kind") is None, + "FFI core must be an owned runtime dependency", + ) + require( + "mobile-social" in edge.get("features", []), + "owned FFI mobile-social profile is absent", + ) + core_node = nodes.get(owned["tera_core"]["id"], {}) + require( + "mobile-social" in core_node.get("features", []), + "resolved mobile-social feature is absent", + ) + + +def validate(document: dict[str, Any], foundation: dict[str, Any]) -> None: + packages = indexed(document.get("packages"), "resolved package") + owned = owned_packages(document, packages) + foundation_sources(packages, foundation) + resolution = document.get("resolve") + require(isinstance(resolution, dict), "resolved dependency graph is absent") + nodes = indexed(resolution.get("nodes"), "resolved node") + require(set(nodes) == set(packages), "resolved node inventory differs") + mobile_profile(owned, nodes) diff --git a/scripts/linux-shared-rust.sh b/scripts/linux-shared-rust.sh @@ -31,7 +31,7 @@ docker run --rm --init --platform linux/amd64 \ set -euo pipefail [[ "$(uname -s)" == "Linux" ]] [[ "$(uname -m)" == "x86_64" ]] - packages=(-p radroots_ios_source_lock -p tera_core -p tera_ffi -p tera_bindgen) + packages=(-p tera_core -p tera_ffi -p tera_bindgen) cargo check "${packages[@]}" --all-targets --locked cargo test "${packages[@]}" --all-targets --locked ' diff --git a/scripts/package_contract.py b/scripts/package_contract.py @@ -17,6 +17,8 @@ from collections.abc import Mapping from pathlib import Path from typing import Any +import app_dependency_graph + MAX_CONTRACT_BYTES = 2 * 1024 * 1024 GIT_REVISION = re.compile(r"^[0-9a-f]{40}$") SHA256 = re.compile(r"^[0-9a-f]{64}$") @@ -336,7 +338,7 @@ def _verify_repository_layout(root: Path) -> None: raise PackageContractError("forbidden public repository root exists") -def _cargo_workspace(root: Path) -> dict[str, Any]: +def _cargo_workspace(root: Path, *, resolved: bool = False) -> dict[str, Any]: with tempfile.TemporaryFile() as stdout, tempfile.TemporaryFile() as stderr: try: result = subprocess.run( @@ -346,7 +348,8 @@ def _cargo_workspace(root: Path) -> dict[str, Any]: "--manifest-path", str(root / "Cargo.toml"), "--locked", - "--no-deps", + *([] if resolved else ["--no-deps"]), + "--offline", "--format-version", "1", ], @@ -417,9 +420,7 @@ def _validate_mobile_defaults( def _validate_app_package(package: Mapping[str, Any], root: Path) -> None: manifest = _local_cargo_path(package.get("manifest_path"), root) directory = manifest.parent - if directory != Path("crates/source_lock") and not directory.is_relative_to( - "core/crates" - ): + if not directory.is_relative_to("core/crates"): raise PackageContractError("application Rust package is outside its owned root") dependencies = package.get("dependencies") if not isinstance(dependencies, list): @@ -439,13 +440,6 @@ def _verify_cargo_and_source(root: Path) -> tuple[str, str]: "https://github.com/radrootslabs/tera", "Cargo repository", ) - ffi_dependency = _mapping( - _mapping(workspace.get("dependencies"), "Cargo workspace dependencies").get( - "radroots_mobile_ffi" - ), - "Cargo FFI dependency", - ) - consumer = _read_toml(root / "radroots.lib.source-lock.v1.toml") _exact(consumer.get("repository"), LIB_REMOTE, "consumer Lib remote") lib_revision = consumer.get("revision") @@ -456,11 +450,10 @@ def _verify_cargo_and_source(root: Path) -> tuple[str, str]: raise PackageContractError("consumer Lib revision is invalid") release_version = consumer.get("version") _exact(release_version, "0.1.0-alpha", "consumer Lib version") - _exact( - ffi_dependency, - {"git": LIB_REMOTE, "rev": lib_revision, "version": "=0.1.0-alpha"}, - "transition shim FFI dependency", - ) + try: + app_dependency_graph.validate(_cargo_workspace(root, resolved=True), consumer) + except app_dependency_graph.GraphError as error: + raise PackageContractError(str(error)) from error _verify_owned_source_lock(root, consumer) return release_version, lib_revision diff --git a/scripts/swift-quality.sh b/scripts/swift-quality.sh @@ -30,6 +30,8 @@ readonly -a PYTHON_QUALITY_PATHS=( scripts/test_ffi_provenance.py scripts/maintainability_ratchet.py scripts/package_contract.py + scripts/app_dependency_graph.py + scripts/test_app_dependency_graph.py scripts/test_maintainability_ratchet.py scripts/test_package_contract.py ) diff --git a/scripts/test_app_dependency_graph.py b/scripts/test_app_dependency_graph.py @@ -0,0 +1,98 @@ +from __future__ import annotations + +import copy +import json +import sys +import unittest +from pathlib import Path + +SCRIPTS = Path(__file__).resolve().parent +if str(SCRIPTS) not in sys.path: + sys.path.insert(0, str(SCRIPTS)) + +import app_dependency_graph as graph # noqa: E402 +import package_contract as contract # noqa: E402 + + +class AppDependencyGraphTests(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.root = SCRIPTS.parent + cls.metadata = contract._cargo_workspace(cls.root, resolved=True) + cls.foundation = contract._read_toml( + cls.root / "radroots.lib.source-lock.v1.toml" + ) + + def test_actual_locked_application_graph(self) -> None: + graph.validate(self.metadata, self.foundation) + + def test_actual_graph_rejects_retired_producer_and_duplicate_resolution( + self, + ) -> None: + for name in graph.RETIRED: + with self.subTest(name=name): + changed = copy.deepcopy(self.metadata) + changed["packages"].append({"id": "retired", "name": name}) + with self.assertRaisesRegex(graph.GraphError, "retired"): + graph.validate(changed, self.foundation) + changed = copy.deepcopy(self.metadata) + changed["packages"].append(changed["packages"][0]) + with self.assertRaisesRegex(graph.GraphError, "duplicated"): + graph.validate(changed, self.foundation) + + def test_actual_shared_resolution_rejects_pin_version_and_local_override( + self, + ) -> None: + for field, value in ( + ("source", None), + ("source", "git+https://example.invalid/lib"), + ("version", "9.9.9"), + ): + with self.subTest(field=field, value=value): + changed = copy.deepcopy(self.metadata) + package = next( + p for p in changed["packages"] if p["name"] == "radroots_sdk" + ) + package[field] = value + with self.assertRaisesRegex(graph.GraphError, "foundation"): + graph.validate(changed, self.foundation) + + def test_default_lane_rejects_wasm_or_omitted_native_package(self) -> None: + wasm = next( + p["id"] for p in self.metadata["packages"] if p["name"] == "tera_wasm" + ) + for defaults in ( + [], + self.metadata["workspace_default_members"][:-1], + [*self.metadata["workspace_default_members"], wasm], + ): + with self.subTest(defaults=defaults): + changed = {**self.metadata, "workspace_default_members": defaults} + with self.assertRaisesRegex(graph.GraphError, "mobile defaults"): + graph.validate(changed, self.foundation) + + def test_mobile_profile_requires_executable_ffi_edge(self) -> None: + changed = copy.deepcopy(self.metadata) + ffi = next(p for p in changed["packages"] if p["name"] == "tera_ffi") + edge = next(d for d in ffi["dependencies"] if d["name"] == "tera_core") + edge["features"] = [] + with self.assertRaisesRegex(graph.GraphError, "mobile-social"): + graph.validate(changed, self.foundation) + + def test_mobile_profile_requires_resolved_feature(self) -> None: + changed = copy.deepcopy(self.metadata) + core = next(p["id"] for p in changed["packages"] if p["name"] == "tera_core") + node = next(n for n in changed["resolve"]["nodes"] if n["id"] == core) + node["features"] = [] + with self.assertRaisesRegex(graph.GraphError, "mobile-social"): + graph.validate(changed, self.foundation) + + def test_resolution_is_required_and_machine_readable(self) -> None: + changed = json.loads(json.dumps(self.metadata)) + changed["resolve"] = None + with self.assertRaisesRegex(graph.GraphError, "resolved dependency graph"): + graph.validate(changed, self.foundation) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/test_package_contract.py b/scripts/test_package_contract.py @@ -29,9 +29,9 @@ class PackageContractTests(unittest.TestCase): ], } - def test_application_workspace_accepts_owned_rust_and_transition_shim(self) -> None: + def test_application_workspace_accepts_owned_rust(self) -> None: root = SCRIPTS.parent.resolve() - for member in ("core/crates/tera_core", "crates/source_lock"): + for member in ("core/crates/tera_core", "core/crates/tera_ffi"): contract._validate_app_workspace(self.workspace(root, member), root) def test_application_workspace_rejects_hidden_sibling_dependency(self) -> None: @@ -70,7 +70,7 @@ class PackageContractTests(unittest.TestCase): root = SCRIPTS.parent.resolve() with self.assertRaisesRegex(contract.PackageContractError, "owned root"): contract._validate_app_workspace( - self.workspace(root, "private/runtime"), root + self.workspace(root, "crates/source_lock"), root ) def test_application_workspace_rejects_implicit_parent_workspace(self) -> None: diff --git a/scripts/verify-package-contract.sh b/scripts/verify-package-contract.sh @@ -14,6 +14,7 @@ sh "$repo_root/scripts/ffi-provenance.sh" contract-check uv run --project "$python_project" --offline --frozen \ python -m unittest \ scripts/test_package_contract.py \ + scripts/test_app_dependency_graph.py \ scripts/test_ffi_provenance.py \ scripts/test_ffi_artifacts.py \ scripts/test_ffi_installed.py \