commit 55be33f20161caeb012f2d17661735ec13e58eff
parent d88fe5d594e11fe43bdbe8437fbab7835837c1d8
Author: triesap <tyson@radroots.org>
Date: Mon, 21 Sep 2026 10:55:32 +0000
delivery: retain late acceptance after cancellation
- Qualify stop after TCP connect and before relay acknowledgement
- Retain exact late target acceptance across SQLite restart
- Reject stale publication callbacks in replacement scopes
- Qualify native artifacts and standalone consumer checks
Diffstat:
8 files changed, 80 insertions(+), 26 deletions(-)
diff --git a/TeraFFI/provenance.json b/TeraFFI/provenance.json
@@ -94,17 +94,17 @@
{
"bytes": 114576,
"path": "source/aarch64-apple-darwin.json",
- "sha256": "ade6467725e6becfcd10a7b2435dc2c606d51c59eacae98dbecfe51ceada326f"
+ "sha256": "775ae3b8acbc3bf8065299cde99aafd2694dc78f647c66223573eca939c9b768"
},
{
"bytes": 114420,
"path": "source/aarch64-apple-ios-sim.json",
- "sha256": "16d8391ea1655be6d26e55ca0f68d92b7ca4390d31d753948d74d61940c9c543"
+ "sha256": "fa1d306014b9c75af14cd630325119903fb5242d127a94e03267614860dc9060"
},
{
"bytes": 114416,
"path": "source/aarch64-apple-ios.json",
- "sha256": "4d431474b9f2c082210a1af2a9e36548bd63ad253d544513b6f85a50247a9175"
+ "sha256": "5471a6cd1f04ac4b36c4f6ad1a9b5b69486e2bf53c1dd244027f26712b9c7da4"
}
],
"language": "swift",
@@ -112,7 +112,7 @@
"schema": "radroots.artifact-manifest.v2",
"source": {
"repository": "https://github.com/radrootslabs/tera",
- "tree": "e7a3b88cba7e4ab4676694822d7d222f68d36a65"
+ "tree": "6fb1acade84d5425e22521d77f5642658fb8991b"
},
"source_records": {
"aarch64-apple-darwin": "source/aarch64-apple-darwin.json",
@@ -176,17 +176,17 @@
{
"bytes": 114576,
"path": "TeraFFI/source/aarch64-apple-darwin.json",
- "sha256": "ade6467725e6becfcd10a7b2435dc2c606d51c59eacae98dbecfe51ceada326f"
+ "sha256": "775ae3b8acbc3bf8065299cde99aafd2694dc78f647c66223573eca939c9b768"
},
{
"bytes": 114420,
"path": "TeraFFI/source/aarch64-apple-ios-sim.json",
- "sha256": "16d8391ea1655be6d26e55ca0f68d92b7ca4390d31d753948d74d61940c9c543"
+ "sha256": "fa1d306014b9c75af14cd630325119903fb5242d127a94e03267614860dc9060"
},
{
"bytes": 114416,
"path": "TeraFFI/source/aarch64-apple-ios.json",
- "sha256": "4d431474b9f2c082210a1af2a9e36548bd63ad253d544513b6f85a50247a9175"
+ "sha256": "5471a6cd1f04ac4b36c4f6ad1a9b5b69486e2bf53c1dd244027f26712b9c7da4"
}
],
"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 = "e7a3b88cba7e4ab4676694822d7d222f68d36a65"
-manifest_sha256 = "3e7d842546a817d9e9af7c7f690cb7c9dff60b8eb84ff929a1305b68b4657528"
+source_tree = "6fb1acade84d5425e22521d77f5642658fb8991b"
+manifest_sha256 = "f36cab89ff95e94f1eb58134d70f684dc9859660049e6d543857b67b67a03a75"
source_date_epoch = 1787871027
[foundation]
diff --git a/TeraFFI/source/aarch64-apple-darwin.json b/TeraFFI/source/aarch64-apple-darwin.json
@@ -1329,10 +1329,10 @@
"sha256": "27037487bf6b305efc2ba43e75c8ec20c597ebd0a1bac19fcf2a0f80ceb7c7da"
},
"core/crates/tera_core/src/runtime/product_surface/submission/stop_effect_tests.rs": {
- "bytes": 8874,
- "git_blob": "e67764c7a458985d3202874e44cae1de0d90ac7e",
+ "bytes": 9351,
+ "git_blob": "b573241041d60551ec8a74e8076c35ad4537f197",
"mode": "100644",
- "sha256": "2b605aaafeed08af3b310f5ceea454bc273905d6343ed23c9e13c83cc9c2f59a"
+ "sha256": "a4d7273d94f474ecbc5803f895a83489ea1d5bd60e94b7092b2699ee910a8cae"
},
"core/crates/tera_core/src/runtime/product_surface/submission/stop_tests.rs": {
"bytes": 7315,
@@ -2440,6 +2440,6 @@
}
},
"policy": "staged_inputs",
- "tree": "e7a3b88cba7e4ab4676694822d7d222f68d36a65"
+ "tree": "6fb1acade84d5425e22521d77f5642658fb8991b"
}
}
diff --git a/TeraFFI/source/aarch64-apple-ios-sim.json b/TeraFFI/source/aarch64-apple-ios-sim.json
@@ -1325,10 +1325,10 @@
"sha256": "27037487bf6b305efc2ba43e75c8ec20c597ebd0a1bac19fcf2a0f80ceb7c7da"
},
"core/crates/tera_core/src/runtime/product_surface/submission/stop_effect_tests.rs": {
- "bytes": 8874,
- "git_blob": "e67764c7a458985d3202874e44cae1de0d90ac7e",
+ "bytes": 9351,
+ "git_blob": "b573241041d60551ec8a74e8076c35ad4537f197",
"mode": "100644",
- "sha256": "2b605aaafeed08af3b310f5ceea454bc273905d6343ed23c9e13c83cc9c2f59a"
+ "sha256": "a4d7273d94f474ecbc5803f895a83489ea1d5bd60e94b7092b2699ee910a8cae"
},
"core/crates/tera_core/src/runtime/product_surface/submission/stop_tests.rs": {
"bytes": 7315,
@@ -2436,6 +2436,6 @@
}
},
"policy": "staged_inputs",
- "tree": "e7a3b88cba7e4ab4676694822d7d222f68d36a65"
+ "tree": "6fb1acade84d5425e22521d77f5642658fb8991b"
}
}
diff --git a/TeraFFI/source/aarch64-apple-ios.json b/TeraFFI/source/aarch64-apple-ios.json
@@ -1325,10 +1325,10 @@
"sha256": "27037487bf6b305efc2ba43e75c8ec20c597ebd0a1bac19fcf2a0f80ceb7c7da"
},
"core/crates/tera_core/src/runtime/product_surface/submission/stop_effect_tests.rs": {
- "bytes": 8874,
- "git_blob": "e67764c7a458985d3202874e44cae1de0d90ac7e",
+ "bytes": 9351,
+ "git_blob": "b573241041d60551ec8a74e8076c35ad4537f197",
"mode": "100644",
- "sha256": "2b605aaafeed08af3b310f5ceea454bc273905d6343ed23c9e13c83cc9c2f59a"
+ "sha256": "a4d7273d94f474ecbc5803f895a83489ea1d5bd60e94b7092b2699ee910a8cae"
},
"core/crates/tera_core/src/runtime/product_surface/submission/stop_tests.rs": {
"bytes": 7315,
@@ -2436,6 +2436,6 @@
}
},
"policy": "staged_inputs",
- "tree": "e7a3b88cba7e4ab4676694822d7d222f68d36a65"
+ "tree": "6fb1acade84d5425e22521d77f5642658fb8991b"
}
}
diff --git a/TeraTests/TeraSubmissionStoreTests.swift b/TeraTests/TeraSubmissionStoreTests.swift
@@ -243,6 +243,52 @@ final class TeraSubmissionStoreTests: XCTestCase {
}
extension TeraSubmissionStoreTests {
+ func testLateAcceptedPublicationCannotPopulateAReplacementAccountOrNetworkScope() async throws {
+ for replacingAccount in [false, true] {
+ let backend = AddBackend()
+ let client = try await TeraAddStoreTests.startedClient(backend)
+ let store = await make(client, backend: backend)
+ let pause = ResourceTestPause()
+ await backend.submissionBackend.pauseAdvance(pause)
+ store.updateForm(\.content, "Original publication")
+ let task = Task { await store.submit() }
+ await entered(pause)
+ let original = try XCTUnwrap(store.submissions.status)
+ let replacement = TeraComposerScope(
+ authorPublicKey: replacingAccount ? String(repeating: "cd", count: 32) : original.request.scope.authorPublicKey,
+ localNetworkID: replacingAccount ? original.request.scope.localNetworkID : "replacement-network"
+ )
+ let context = TeraLocalNetwork(schemaVersion: 1, id: replacement.localNetworkID, label: "Replacement",
+ relayURLs: [], locality: nil, followedAuthors: [], generation: 2)
+ store.submissions.configure(scope: replacement, context: context)
+ store.submissions.start()
+ await pause.resume.open()
+ await task.value
+ var recovered: TeraSubmissionStatus?
+ let deadline = ContinuousClock.now.advanced(by: .seconds(3))
+ repeat {
+ recovered = try? await client.submissionStatus(request: original.request)
+ if recovered?.delivery.state == .accepted {
+ break
+ }
+ try await Task.sleep(for: .milliseconds(1))
+ } while ContinuousClock.now < deadline
+ let accepted = try XCTUnwrap(recovered)
+ XCTAssertEqual(accepted.delivery.state, .accepted)
+ XCTAssertEqual(accepted.request, original.request)
+ XCTAssertEqual(accepted.operationID, original.operationID)
+ XCTAssertEqual(accepted.captured, original.captured)
+ XCTAssertNil(store.submissions.request)
+ XCTAssertNil(store.submissions.status)
+ XCTAssertNil(store.submissions.message)
+ XCTAssertNil(store.submissions.failureCode)
+ let advances = await backend.submissionBackend.advanceCount
+ XCTAssertEqual(advances, 1)
+ store.stop()
+ _ = try await client.stop()
+ }
+ }
+
func testStopBeforeIntentCommitStaysUnconfirmedUntilOriginalPreparationReturns() async throws {
let backend = AddBackend()
let client = try await TeraAddStoreTests.startedClient(backend)
diff --git a/core/crates/tera_core/src/runtime/product_surface/submission/stop_effect_tests.rs b/core/crates/tera_core/src/runtime/product_surface/submission/stop_effect_tests.rs
@@ -13,7 +13,7 @@ use crate::runtime::product_surface::{
#[tokio::test]
async fn stopped_socket_retains_unknown_then_late_ok_across_sqlite_reopen() {
- for sqlite in [false, true] {
+ for (sqlite, before_handshake) in [(false, false), (true, false), (false, true), (true, true)] {
let root = tempfile::tempdir().unwrap();
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let url = format!("ws://{}", listener.local_addr().unwrap());
@@ -30,13 +30,21 @@ async fn stopped_socket_retains_unknown_then_late_ok_across_sqlite_reopen() {
tokio::spawn(async move {
tokio::time::timeout(Duration::from_secs(15), async move {
let (stream, _) = listener.accept().await.unwrap();
+ if before_handshake {
+ // TCP is connected, but the relay has not admitted a
+ // websocket or received any publication frame yet.
+ entered.notify_one();
+ resume.notified().await;
+ }
let mut socket = accept_async(stream).await.unwrap();
while let Some(message) = socket.next().await {
if let Message::Text(text) = message.unwrap() {
let value: serde_json::Value = serde_json::from_str(&text).unwrap();
if value[0] == "EVENT" {
- entered.notify_one();
- resume.notified().await;
+ if !before_handshake {
+ entered.notify_one();
+ resume.notified().await;
+ }
socket
.send(Message::Text(
serde_json::json!(["OK", value[1]["id"], true, ""])
diff --git a/release/provenance.json b/release/provenance.json
@@ -2,7 +2,7 @@
"artifacts": {
"app_api_sha256": "020924097c0d7efc33128cb8fd3d3b2026d95f57c44da71880e585aff80f070b",
"ffi_api_sha256": "bcedcb4883745ef79dacb26d1b9f0fc852216d21c23e315a7adb39347a2abb23",
- "ffi_provenance_sha256": "3e7d842546a817d9e9af7c7f690cb7c9dff60b8eb84ff929a1305b68b4657528",
+ "ffi_provenance_sha256": "f36cab89ff95e94f1eb58134d70f684dc9859660049e6d543857b67b67a03a75",
"info_plist_sha256": "15ef08b1cdd1096cfb9eeaf5be5bf8f814807a7ca9350bbbb47860fa72ec13ef",
"privacy_manifest_sha256": "a331d51864743ebe4e00dd22360b4a538b6b3ac26a6b3eb54094e60a36959a12",
"sbom_sha256": "d65b792e2be7b95ae70c95aa8f9710074004610f8f252f0fc7340533114f4cbb",
@@ -22,7 +22,7 @@
"lib_revision": "858b9e0d4fc25c3bc53ca9550c898fe8148c1bd8",
"source_date_epoch": 1787871027,
"swift_package_lock_sha256": "a7e31d77d31ecbc1e0e5a78f2681e3fb0c6f367af0edf8b4ceef0af973a78681",
- "tera_ffi_source_tree": "e7a3b88cba7e4ab4676694822d7d222f68d36a65",
+ "tera_ffi_source_tree": "6fb1acade84d5425e22521d77f5642658fb8991b",
"xcode_package_lock_sha256": "d98a614a38dce7b55c2925dee51c2d4bf8dcfc4e31779d0c5fe8561c45d6e3ba"
},
"version": "0.1.0-alpha"