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 243d1666ac66b28f0e11fd9f91c4d76b457993c0
parent f46cc3c510853f0df6efe3660ad4726fb4f086e0
Author: triesap <tyson@radroots.org>
Date:   Sun, 13 Sep 2026 15:48:59 +0000

publication: qualify durable signing preparation

- Verify exact preimages and signer bindings before callbacks
- Prove SQLite writes remain available during suspended signing
- Preserve reserved identity across denial lock and restart recovery
- Regenerate provenance and verify standalone native consumers

Diffstat:
MTeraFFI/provenance.json | 26+++++++++++++-------------
MTeraFFI/source.lock | 4++--
MTeraFFI/source/aarch64-apple-darwin.json | 26++++++++++++++++----------
MTeraFFI/source/aarch64-apple-ios-sim.json | 26++++++++++++++++----------
MTeraFFI/source/aarch64-apple-ios.json | 26++++++++++++++++----------
Mcore/crates/tera_core/src/runtime/product_surface/submission.rs | 2++
Mcore/crates/tera_core/src/runtime/product_surface/submission/operation_test_support.rs | 8++++++++
Acore/crates/tera_core/src/runtime/product_surface/submission/signing_preparation_tests.rs | 260+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mrelease/provenance.json | 4++--
Mtest-fixtures/legacy-identifiers.v1.json | 10+++++++++-
10 files changed, 344 insertions(+), 48 deletions(-)

diff --git a/TeraFFI/provenance.json b/TeraFFI/provenance.json @@ -92,19 +92,19 @@ "sha256": "7fbf82a5f8eb2117ef95fc6354eb267c306c66419883ea57d756f0979362ab06" }, { - "bytes": 93630, + "bytes": 93938, "path": "source/aarch64-apple-darwin.json", - "sha256": "6dab65c86e208238359c15eed8ebd012fe0baba0f57ac3474bc72136da028903" + "sha256": "f6e0b65ea1dc8dc7767c71671078bd4eaba364b1a8f22815468ebdda58baafce" }, { - "bytes": 93474, + "bytes": 93782, "path": "source/aarch64-apple-ios-sim.json", - "sha256": "c1161a538eca24768b9a2bf478a4770dcebecf4145a3cfb132349a3f99aa7d8f" + "sha256": "241534777c5ba0b4b2146966b0aeee06de009542ed9941fdf6dbf09340a0b9a8" }, { - "bytes": 93470, + "bytes": 93778, "path": "source/aarch64-apple-ios.json", - "sha256": "0540e662f1a1e1a093c003059a7e5c4d858aadd6133e1b263b289557ca330e3a" + "sha256": "2ecaa6943e11241ad646751f814760451825c777abde9d634264a5143923f87e" } ], "language": "swift", @@ -112,7 +112,7 @@ "schema": "radroots.artifact-manifest.v2", "source": { "repository": "https://github.com/radrootslabs/tera", - "tree": "b7c0e8adcd745dc848f8ab8f5f55da12e49e2384" + "tree": "b3f79cda46bad95581c429bf99ba63555c782cd5" }, "source_records": { "aarch64-apple-darwin": "source/aarch64-apple-darwin.json", @@ -174,19 +174,19 @@ "sha256": "79c86a44fd77c30b5b55d209cf356d01e6c4b60fe6d1f8aa33cccc5029a58aa4" }, { - "bytes": 93630, + "bytes": 93938, "path": "TeraFFI/source/aarch64-apple-darwin.json", - "sha256": "6dab65c86e208238359c15eed8ebd012fe0baba0f57ac3474bc72136da028903" + "sha256": "f6e0b65ea1dc8dc7767c71671078bd4eaba364b1a8f22815468ebdda58baafce" }, { - "bytes": 93474, + "bytes": 93782, "path": "TeraFFI/source/aarch64-apple-ios-sim.json", - "sha256": "c1161a538eca24768b9a2bf478a4770dcebecf4145a3cfb132349a3f99aa7d8f" + "sha256": "241534777c5ba0b4b2146966b0aeee06de009542ed9941fdf6dbf09340a0b9a8" }, { - "bytes": 93470, + "bytes": 93778, "path": "TeraFFI/source/aarch64-apple-ios.json", - "sha256": "0540e662f1a1e1a093c003059a7e5c4d858aadd6133e1b263b289557ca330e3a" + "sha256": "2ecaa6943e11241ad646751f814760451825c777abde9d634264a5143923f87e" } ], "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 = "b7c0e8adcd745dc848f8ab8f5f55da12e49e2384" -manifest_sha256 = "6fc6194a31471995521cf2f6d4b47e0e3d966b99fdf54fc696ce131a0b2beaab" +source_tree = "b3f79cda46bad95581c429bf99ba63555c782cd5" +manifest_sha256 = "c0c31f9335fdcbe6e6d4bb68fa3f82e9d8f8e1966b0f2db651dec666ea4d1667" source_date_epoch = 1787871027 [foundation] diff --git a/TeraFFI/source/aarch64-apple-darwin.json b/TeraFFI/source/aarch64-apple-darwin.json @@ -861,10 +861,10 @@ "sha256": "76e3bddfc237dac63293d264d68991b489585136fe377e39dbba72aa3a1181f4" }, "core/crates/tera_core/src/runtime/product_surface/submission.rs": { - "bytes": 5744, - "git_blob": "388bc937587f9a7f4c2ea4f33ca2b050a3f5dc2d", + "bytes": 5788, + "git_blob": "34a2c45d8ccd63504fd018f866d31c9c5d7aad74", "mode": "100644", - "sha256": "fde343558bab6cca019fd59e09ab693746e6ee721a371d30665f08a486d61b15" + "sha256": "43b9e796aa3c954961c714af9b9a397f1bf66b3ef92b14ab67cc432627c32dd7" }, "core/crates/tera_core/src/runtime/product_surface/submission/atomic_fault_store.rs": { "bytes": 3189, @@ -1011,10 +1011,10 @@ "sha256": "34053a01079a939b54615f9c39e9f97314f9741bb1ff70fd309257c24ba00acc" }, "core/crates/tera_core/src/runtime/product_surface/submission/operation_test_support.rs": { - "bytes": 6425, - "git_blob": "b035c31234de26dd50a9533b1103ff7caf501b24", + "bytes": 6891, + "git_blob": "6a0a792fc2dec4ae04f83ab818024275db55e8e6", "mode": "100644", - "sha256": "83ec90cbadc617260a9b23e492e2a6fabfbe789eee12a4f237c9390ed9767c4d" + "sha256": "ba8e0fd8b4c85437d89049c3652ce2ba8f05771c114c91a8c4c638bf59aa7ce6" }, "core/crates/tera_core/src/runtime/product_surface/submission/operation_tests.rs": { "bytes": 10339, @@ -1046,6 +1046,12 @@ "mode": "100644", "sha256": "5d05ddf11b0ce9040f9221b7158a50374e4df6c344ce23faa1e14ce2d6c91122" }, + "core/crates/tera_core/src/runtime/product_surface/submission/signing_preparation_tests.rs": { + "bytes": 9603, + "git_blob": "edebc427e882cbacb50419de84ebbebe772af990", + "mode": "100644", + "sha256": "202c93a99d2132ae72b0333a5b3f32e65ce84205537d51f3ba4b872ea5dfd7de" + }, "core/crates/tera_core/src/runtime/product_surface/submission/sqlite_tests.rs": { "bytes": 5247, "git_blob": "fb83a8fd521dce82d91bf1269682fb5731283013", @@ -2001,13 +2007,13 @@ "sha256": "b052a73a824e8f8b26af2646a4758f13655e04de9551a8271890d3cf2b63209d" }, "test-fixtures/legacy-identifiers.v1.json": { - "bytes": 134234, - "git_blob": "d87127249e1943580e522e8c70769062fcc91ad3", + "bytes": 134540, + "git_blob": "65653d77a42b56e472da03e38dbc76f568a84340", "mode": "100644", - "sha256": "dfe64e685cae9a5fec5cb311a590b10415d570a406296dca5fc3998cce9372b5" + "sha256": "0a5f18d3ce7fcdec536dd72504b1e1556d9458d8e215bb54bfd888ad582d7e8c" } }, "policy": "staged_inputs", - "tree": "b7c0e8adcd745dc848f8ab8f5f55da12e49e2384" + "tree": "b3f79cda46bad95581c429bf99ba63555c782cd5" } } diff --git a/TeraFFI/source/aarch64-apple-ios-sim.json b/TeraFFI/source/aarch64-apple-ios-sim.json @@ -857,10 +857,10 @@ "sha256": "76e3bddfc237dac63293d264d68991b489585136fe377e39dbba72aa3a1181f4" }, "core/crates/tera_core/src/runtime/product_surface/submission.rs": { - "bytes": 5744, - "git_blob": "388bc937587f9a7f4c2ea4f33ca2b050a3f5dc2d", + "bytes": 5788, + "git_blob": "34a2c45d8ccd63504fd018f866d31c9c5d7aad74", "mode": "100644", - "sha256": "fde343558bab6cca019fd59e09ab693746e6ee721a371d30665f08a486d61b15" + "sha256": "43b9e796aa3c954961c714af9b9a397f1bf66b3ef92b14ab67cc432627c32dd7" }, "core/crates/tera_core/src/runtime/product_surface/submission/atomic_fault_store.rs": { "bytes": 3189, @@ -1007,10 +1007,10 @@ "sha256": "34053a01079a939b54615f9c39e9f97314f9741bb1ff70fd309257c24ba00acc" }, "core/crates/tera_core/src/runtime/product_surface/submission/operation_test_support.rs": { - "bytes": 6425, - "git_blob": "b035c31234de26dd50a9533b1103ff7caf501b24", + "bytes": 6891, + "git_blob": "6a0a792fc2dec4ae04f83ab818024275db55e8e6", "mode": "100644", - "sha256": "83ec90cbadc617260a9b23e492e2a6fabfbe789eee12a4f237c9390ed9767c4d" + "sha256": "ba8e0fd8b4c85437d89049c3652ce2ba8f05771c114c91a8c4c638bf59aa7ce6" }, "core/crates/tera_core/src/runtime/product_surface/submission/operation_tests.rs": { "bytes": 10339, @@ -1042,6 +1042,12 @@ "mode": "100644", "sha256": "5d05ddf11b0ce9040f9221b7158a50374e4df6c344ce23faa1e14ce2d6c91122" }, + "core/crates/tera_core/src/runtime/product_surface/submission/signing_preparation_tests.rs": { + "bytes": 9603, + "git_blob": "edebc427e882cbacb50419de84ebbebe772af990", + "mode": "100644", + "sha256": "202c93a99d2132ae72b0333a5b3f32e65ce84205537d51f3ba4b872ea5dfd7de" + }, "core/crates/tera_core/src/runtime/product_surface/submission/sqlite_tests.rs": { "bytes": 5247, "git_blob": "fb83a8fd521dce82d91bf1269682fb5731283013", @@ -1997,13 +2003,13 @@ "sha256": "b052a73a824e8f8b26af2646a4758f13655e04de9551a8271890d3cf2b63209d" }, "test-fixtures/legacy-identifiers.v1.json": { - "bytes": 134234, - "git_blob": "d87127249e1943580e522e8c70769062fcc91ad3", + "bytes": 134540, + "git_blob": "65653d77a42b56e472da03e38dbc76f568a84340", "mode": "100644", - "sha256": "dfe64e685cae9a5fec5cb311a590b10415d570a406296dca5fc3998cce9372b5" + "sha256": "0a5f18d3ce7fcdec536dd72504b1e1556d9458d8e215bb54bfd888ad582d7e8c" } }, "policy": "staged_inputs", - "tree": "b7c0e8adcd745dc848f8ab8f5f55da12e49e2384" + "tree": "b3f79cda46bad95581c429bf99ba63555c782cd5" } } diff --git a/TeraFFI/source/aarch64-apple-ios.json b/TeraFFI/source/aarch64-apple-ios.json @@ -857,10 +857,10 @@ "sha256": "76e3bddfc237dac63293d264d68991b489585136fe377e39dbba72aa3a1181f4" }, "core/crates/tera_core/src/runtime/product_surface/submission.rs": { - "bytes": 5744, - "git_blob": "388bc937587f9a7f4c2ea4f33ca2b050a3f5dc2d", + "bytes": 5788, + "git_blob": "34a2c45d8ccd63504fd018f866d31c9c5d7aad74", "mode": "100644", - "sha256": "fde343558bab6cca019fd59e09ab693746e6ee721a371d30665f08a486d61b15" + "sha256": "43b9e796aa3c954961c714af9b9a397f1bf66b3ef92b14ab67cc432627c32dd7" }, "core/crates/tera_core/src/runtime/product_surface/submission/atomic_fault_store.rs": { "bytes": 3189, @@ -1007,10 +1007,10 @@ "sha256": "34053a01079a939b54615f9c39e9f97314f9741bb1ff70fd309257c24ba00acc" }, "core/crates/tera_core/src/runtime/product_surface/submission/operation_test_support.rs": { - "bytes": 6425, - "git_blob": "b035c31234de26dd50a9533b1103ff7caf501b24", + "bytes": 6891, + "git_blob": "6a0a792fc2dec4ae04f83ab818024275db55e8e6", "mode": "100644", - "sha256": "83ec90cbadc617260a9b23e492e2a6fabfbe789eee12a4f237c9390ed9767c4d" + "sha256": "ba8e0fd8b4c85437d89049c3652ce2ba8f05771c114c91a8c4c638bf59aa7ce6" }, "core/crates/tera_core/src/runtime/product_surface/submission/operation_tests.rs": { "bytes": 10339, @@ -1042,6 +1042,12 @@ "mode": "100644", "sha256": "5d05ddf11b0ce9040f9221b7158a50374e4df6c344ce23faa1e14ce2d6c91122" }, + "core/crates/tera_core/src/runtime/product_surface/submission/signing_preparation_tests.rs": { + "bytes": 9603, + "git_blob": "edebc427e882cbacb50419de84ebbebe772af990", + "mode": "100644", + "sha256": "202c93a99d2132ae72b0333a5b3f32e65ce84205537d51f3ba4b872ea5dfd7de" + }, "core/crates/tera_core/src/runtime/product_surface/submission/sqlite_tests.rs": { "bytes": 5247, "git_blob": "fb83a8fd521dce82d91bf1269682fb5731283013", @@ -1997,13 +2003,13 @@ "sha256": "b052a73a824e8f8b26af2646a4758f13655e04de9551a8271890d3cf2b63209d" }, "test-fixtures/legacy-identifiers.v1.json": { - "bytes": 134234, - "git_blob": "d87127249e1943580e522e8c70769062fcc91ad3", + "bytes": 134540, + "git_blob": "65653d77a42b56e472da03e38dbc76f568a84340", "mode": "100644", - "sha256": "dfe64e685cae9a5fec5cb311a590b10415d570a406296dca5fc3998cce9372b5" + "sha256": "0a5f18d3ce7fcdec536dd72504b1e1556d9458d8e215bb54bfd888ad582d7e8c" } }, "policy": "staged_inputs", - "tree": "b7c0e8adcd745dc848f8ab8f5f55da12e49e2384" + "tree": "b3f79cda46bad95581c429bf99ba63555c782cd5" } } diff --git a/core/crates/tera_core/src/runtime/product_surface/submission.rs b/core/crates/tera_core/src/runtime/product_surface/submission.rs @@ -38,6 +38,8 @@ mod operation_test_support; #[cfg(test)] mod operation_tests; #[cfg(test)] +mod signing_preparation_tests; +#[cfg(test)] mod test_support; #[cfg(test)] mod transaction_test_support; diff --git a/core/crates/tera_core/src/runtime/product_surface/submission/operation_test_support.rs b/core/crates/tera_core/src/runtime/product_surface/submission/operation_test_support.rs @@ -29,6 +29,8 @@ pub(super) struct CountingSigner { pub calls: AtomicUsize, pub statuses: AtomicUsize, pub kinds: std::sync::Mutex<Vec<u32>>, + pub requests: std::sync::Mutex<Vec<radroots_signing::SignRequest>>, + pub failure: std::sync::Mutex<Option<radroots_signing::error::Kind>>, pub pause: AtomicBool, pub entered: Notify, pub resume: Notify, @@ -47,6 +49,8 @@ impl CountingSigner { calls: AtomicUsize::new(0), statuses: AtomicUsize::new(0), kinds: std::sync::Mutex::new(Vec::new()), + requests: std::sync::Mutex::new(Vec::new()), + failure: std::sync::Mutex::new(None), pause: AtomicBool::new(false), entered: Notify::new(), resume: Notify::new(), @@ -71,10 +75,14 @@ impl Signer for CountingSigner { Box::pin(async move { self.calls.fetch_add(1, Ordering::SeqCst); self.kinds.lock().unwrap().push(request.kind()); + self.requests.lock().unwrap().push(request.clone()); if self.pause.swap(false, Ordering::SeqCst) { self.entered.notify_one(); self.resume.notified().await; } + if let Some(kind) = self.failure.lock().unwrap().take() { + return Err(radroots_signing::Error::new(kind)); + } self.inner.sign(request).await }) } diff --git a/core/crates/tera_core/src/runtime/product_surface/submission/signing_preparation_tests.rs b/core/crates/tera_core/src/runtime/product_surface/submission/signing_preparation_tests.rs @@ -0,0 +1,260 @@ +//! Qualify the application consumer of the shared durable signing owner. + +use std::{sync::atomic::Ordering, time::Duration}; + +use radroots_signing::{SignRequest, error::Kind}; +use radroots_storage::authored::SigningState; + +use super::{operation_test_support::*, test_support::*, *}; +use crate::runtime::product_surface::{AddCommandType, ComposerEditSequence, ComposerPartialForm}; + +fn assert_frozen(request: &SignRequest, status: &SubmissionOperationStatus) { + let artifact = status.push().artifact(); + let persisted = artifact.plan().unwrap().decode().unwrap().into_plan(); + assert_eq!(request.authored_plan(), Some(&persisted)); + assert_eq!( + *request.expected_author(), + status.receipt().request().scope().author() + ); + assert_eq!(request.expected_event_id(), persisted.expected_event_id()); + assert_eq!( + request.created_at(), + status.receipt().captured_at_unix_ms() / 1000 + ); + assert_eq!( + request.intent_id().operation_id().as_bytes(), + status.receipt().operation_id().as_bytes() + ); + assert_eq!( + request.intent_id().artifact_id().as_bytes(), + artifact.artifact_id().as_bytes() + ); +} + +#[tokio::test] +async fn signing_preparation_sqlite_is_committed_and_unlocked_before_callback() { + let root = tempfile::tempdir().unwrap(); + let signer = CountingSigner::new(); + signer.pause.store(true, Ordering::SeqCst); + let runtime = runtime(Some(root.path()), signer.clone(), "ws://127.0.0.1:19999").await; + let request = request(); + prepare(&runtime, &request, false).await; + let original = runtime.submission_operation_status(&request).await.unwrap(); + assert_eq!(signer.count(), 0); + let task = { + let runtime = runtime.clone(); + let request = request.clone(); + tokio::spawn(async move { runtime.submission_advance(&request, 1).await }) + }; + tokio::time::timeout(Duration::from_secs(5), signer.entered.notified()) + .await + .unwrap(); + let waiting = tokio::time::timeout( + Duration::from_secs(5), + runtime.submission_operation_status(&request), + ) + .await + .unwrap() + .unwrap(); + let callback = signer.requests.lock().unwrap()[0].clone(); + assert_frozen(&callback, &waiting); + assert_eq!( + waiting.push().artifact().plan(), + original.push().artifact().plan() + ); + assert!(waiting.push().artifact().signing_claim().is_some()); + assert!(waiting.push().artifact().signed().is_none()); + assert!(waiting.push().delivery_plan().attempts().is_empty()); + // A real write on the same SQLite owner must finish while the signer remains suspended. + let later = tokio::time::timeout( + Duration::from_secs(5), + runtime.composer_save( + request.scope(), + request.composer_id(), + request.expected_revision(), + ComposerEditSequence::new(2).unwrap(), + ComposerPartialForm::new(input(AddCommandType::CreateAsk)).unwrap(), + ), + ) + .await + .unwrap() + .unwrap(); + task.abort(); + assert!(task.await.unwrap_err().is_cancelled()); + runtime.shutdown().await.unwrap(); + drop(runtime); + let reopened = self::runtime(Some(root.path()), signer.clone(), "ws://127.0.0.1:19998").await; + let recovered = reopened + .submission_operation_status(&request) + .await + .unwrap(); + assert_eq!(recovered, waiting); + assert_frozen(&callback, &recovered); + assert_eq!( + reopened + .composer_load(request.scope(), request.composer_id()) + .await + .unwrap(), + *later.draft() + ); + assert_eq!(signer.count(), 1); + reopened.shutdown().await.unwrap(); +} + +#[tokio::test] +async fn signing_preparation_lock_and_denial_preserve_exact_request_across_restart() { + for failure in [ + Kind::SignerUnavailable, + Kind::AuthorizationDenied, + Kind::SignerRejected, + ] { + let root = tempfile::tempdir().unwrap(); + let signer = CountingSigner::new(); + signer.pause.store(true, Ordering::SeqCst); + let runtime = runtime(Some(root.path()), signer.clone(), "ws://127.0.0.1:19999").await; + let request = request(); + prepare(&runtime, &request, false).await; + let before = runtime.submission_operation_status(&request).await.unwrap(); + let task = { + let runtime = runtime.clone(); + let request = request.clone(); + tokio::spawn(async move { runtime.submission_advance(&request, 1).await }) + }; + tokio::time::timeout(Duration::from_secs(5), signer.entered.notified()) + .await + .unwrap(); + // Model a lock/denial after status was ready and the durable claim was installed. + *signer.failure.lock().unwrap() = Some(failure); + signer.resume.notify_one(); + assert!(task.await.unwrap().is_err()); + let failed = runtime.submission_operation_status(&request).await.unwrap(); + let callback = signer.requests.lock().unwrap()[0].clone(); + assert_frozen(&callback, &failed); + assert_eq!( + failed.push().artifact().plan(), + before.push().artifact().plan() + ); + assert!(failed.push().artifact().signed().is_none()); + assert!(failed.push().delivery_plan().attempts().is_empty()); + runtime.shutdown().await.unwrap(); + drop(runtime); + let reopened = + self::runtime(Some(root.path()), signer.clone(), "ws://127.0.0.1:19998").await; + let recovered = reopened + .submission_operation_status(&request) + .await + .unwrap(); + assert_eq!(recovered, failed); + assert_frozen(&callback, &recovered); + assert!( + reopened + .submission_prepare(&request, vec![]) + .await + .unwrap() + .is_replay() + ); + if failure == Kind::SignerUnavailable { + assert_eq!( + recovered.push().artifact().signing_state(), + SigningState::Retryable + ); + tokio::time::sleep(Duration::from_millis(1100)).await; + let (loaded, _) = reopened.load_submission_operation(&request).await.unwrap(); + reopened + .sync() + .unwrap() + .sign_prepared(loaded.request) + .await + .unwrap(); + let retried = signer.requests.lock().unwrap()[1].clone(); + assert_frozen(&retried, &recovered); + assert_eq!(retried.signer_request_id(), callback.signer_request_id()); + assert_eq!(retried.intent_id(), callback.intent_id()); + let signed = reopened + .submission_operation_status(&request) + .await + .unwrap(); + assert_eq!( + signed.push().artifact().signing_state(), + SigningState::Signed + ); + assert!(signed.push().delivery_plan().attempts().is_empty()); + assert_eq!(signer.count(), 2); + } else { + assert_eq!( + recovered.push().artifact().signing_state(), + SigningState::FailedTerminal + ); + let after = reopened + .submission_advance(&request, recovered.intent().revision().get()) + .await + .unwrap(); + assert_eq!(after, recovered); + assert_eq!(signer.count(), 1); + } + reopened.shutdown().await.unwrap(); + } +} + +#[tokio::test] +async fn signing_preparation_restart_before_commit_retains_reserved_time_and_author() { + let root = tempfile::tempdir().unwrap(); + let signer = CountingSigner::new(); + let runtime = runtime(Some(root.path()), signer.clone(), "ws://127.0.0.1:19999").await; + let request = request(); + runtime + .composer_create( + request.scope(), + request.composer_id(), + ComposerEditSequence::INITIAL, + ComposerPartialForm::new(input(AddCommandType::CreateUpdate)).unwrap(), + ) + .await + .unwrap(); + let captured = runtime.submission_capture(&request, vec![]).await.unwrap(); + let plan = captured.plan().clone(); + assert!( + runtime + .submission_recover(&request) + .await + .unwrap() + .is_none() + ); + assert_eq!(signer.count(), 0); + runtime.shutdown().await.unwrap(); + drop(runtime); + let reopened = self::runtime(Some(root.path()), signer.clone(), "ws://127.0.0.1:19999").await; + assert!( + reopened + .submission_recover(&request) + .await + .unwrap() + .is_none() + ); + let recaptured = reopened.submission_capture(&request, vec![]).await.unwrap(); + assert_eq!(recaptured.plan(), &plan); + let committed = reopened.submission_commit(&recaptured).await.unwrap(); + let status = reopened + .submission_operation_status(&request) + .await + .unwrap(); + assert_eq!( + status + .push() + .artifact() + .plan() + .unwrap() + .decode() + .unwrap() + .into_plan(), + plan + ); + assert_eq!( + committed.captured_at_unix_ms(), + captured.reservation().reserved_at_unix_ms() + ); + assert_eq!(signer.count(), 0); + assert!(status.push().artifact().signing_claim().is_none()); + assert!(status.push().delivery_plan().attempts().is_empty()); + reopened.shutdown().await.unwrap(); +} diff --git a/release/provenance.json b/release/provenance.json @@ -2,7 +2,7 @@ "artifacts": { "app_api_sha256": "020924097c0d7efc33128cb8fd3d3b2026d95f57c44da71880e585aff80f070b", "ffi_api_sha256": "79c86a44fd77c30b5b55d209cf356d01e6c4b60fe6d1f8aa33cccc5029a58aa4", - "ffi_provenance_sha256": "6fc6194a31471995521cf2f6d4b47e0e3d966b99fdf54fc696ce131a0b2beaab", + "ffi_provenance_sha256": "c0c31f9335fdcbe6e6d4bb68fa3f82e9d8f8e1966b0f2db651dec666ea4d1667", "info_plist_sha256": "15ef08b1cdd1096cfb9eeaf5be5bf8f814807a7ca9350bbbb47860fa72ec13ef", "privacy_manifest_sha256": "a331d51864743ebe4e00dd22360b4a538b6b3ac26a6b3eb54094e60a36959a12", "sbom_sha256": "97f830c291d866596afa339171e4f83c5e7b1dc04451e6d87eac1cfb4bfb33ea", @@ -22,7 +22,7 @@ "lib_revision": "041d7ba7b732ed8f8c1da869172297771435d5ff", "source_date_epoch": 1787871027, "swift_package_lock_sha256": "9b831ccdd3a6c697d142f1e865913eeed9553cb2dc97b492b229771c7669571f", - "tera_ffi_source_tree": "b7c0e8adcd745dc848f8ab8f5f55da12e49e2384", + "tera_ffi_source_tree": "b3f79cda46bad95581c429bf99ba63555c782cd5", "xcode_package_lock_sha256": "1a703864d2eb2f4fcb23e78c8552cfe93036af255de866e49756fa59381e730c" }, "version": "0.1.0-alpha" diff --git a/test-fixtures/legacy-identifiers.v1.json b/test-fixtures/legacy-identifiers.v1.json @@ -4776,7 +4776,11 @@ }, { "path": "core/crates/tera_core/src/runtime/product_surface/submission/operation_test_support.rs", - "count": 6 + "count": 9 + }, + { + "path": "core/crates/tera_core/src/runtime/product_surface/submission/signing_preparation_tests.rs", + "count": 1 }, { "path": "core/crates/tera_ffi/Cargo.toml", @@ -4937,6 +4941,10 @@ "count": 1 }, { + "path": "core/crates/tera_core/src/runtime/product_surface/submission/signing_preparation_tests.rs", + "count": 1 + }, + { "path": "core/crates/tera_core/src/runtime/product_surface/submission/transaction_test_support.rs", "count": 1 },