commit ca0f36d84bfb6db55a4de61b2cb2357dd1ac590c parent 3f301cba1e50b6bd43d72d7b0ef8bbd11de871bf Author: triesap <tyson@radroots.org> Date: Mon, 21 Sep 2026 19:52:19 +0000 revision: handle replacement timestamps and skew - Compare frozen candidates with shared Nostr head ordering - Hold clock rollback without advancing captured timestamps - Preserve signed bytes and frozen delivery deadlines - Qualify ordering, clock effects and native consumers Diffstat:
24 files changed, 746 insertions(+), 185 deletions(-)
diff --git a/README.md b/README.md @@ -306,3 +306,12 @@ If the known current revision changes, publication is held while the captured form, signed bytes and relay evidence remain available. A new request must be reviewed against the current revision. Addressable replacement does not create a broad deletion request. + +Equal-second replacements use the shared Nostr event-ID tie-break rule. Tera +preserves the captured event timestamp and never advances it artificially to +win a replacement. A timestamp ahead of the current clock, or one that loses +to the known revision, holds publication for review. A forward clock jump +does not extend the saved delivery window. Review the device clock and current +revision before choosing another intentional request. Civil calendar dates +remain unchanged. This local policy cannot establish globally accurate time +or prevent an unseen remote replacement. diff --git a/Tera/Runtime/TeraPublicationRetry.swift b/Tera/Runtime/TeraPublicationRetry.swift @@ -8,7 +8,7 @@ enum TeraPublicationActionReason: Sendable, Equatable { var explanation: String { switch self { - case .coordinateChanged: "Another saved request owns this address, or its known revision changed. Review the current revision before publishing. Your captured form and any signed evidence are retained." + case .coordinateChanged: "Another request owns this address, the saved timestamp does not win against its known revision, or the clock is behind the captured time. Review the current revision and device clock before publishing. Your captured form and signed evidence are retained." case .deadlineExceeded: "The saved delivery window does not allow another attempt. The captured form and any signed publication or relay evidence are retained. Review them before choosing another submission." case .attemptLimit: "The delivery attempt limit was reached. The saved publication and relay evidence are retained for review." case .authenticationRequired: "A saved relay requires authentication. Review its access requirements before authorizing further publication. This request is retained." diff --git a/TeraFFI/provenance.json b/TeraFFI/provenance.json @@ -22,9 +22,9 @@ "sha256": "3ed9b7ece2f86e5b5d1ddc6ecbb97deb49c46ca2ea91a930eaec1b580232345c" }, { - "bytes": 77606408, + "bytes": 77626640, "path": "TeraFFI.xcframework/ios-arm64-simulator/libtera_ffi.a", - "sha256": "92ce6d8bd62d49ba37cd8a6e80303df22a8c58a83c6dac7c711734c10a2db8d5" + "sha256": "8ce3ff37fd19da343d3027e71a2a913c2563b4ff6c884b40090a9809f68cf5a2" }, { "bytes": 88720, @@ -37,9 +37,9 @@ "sha256": "3ed9b7ece2f86e5b5d1ddc6ecbb97deb49c46ca2ea91a930eaec1b580232345c" }, { - "bytes": 77670032, + "bytes": 77693008, "path": "TeraFFI.xcframework/ios-arm64/libtera_ffi.a", - "sha256": "e3050e2d6c6e83899bb3f38d235d9a2c7b5e06bfc2f280a0fa72de4acecc9827" + "sha256": "e7ec700d624c53c8c11e143e3819fd65215af4dadb2117b76c0a4e7d33ba712e" }, { "bytes": 55165, @@ -77,34 +77,34 @@ "sha256": "3ed9b7ece2f86e5b5d1ddc6ecbb97deb49c46ca2ea91a930eaec1b580232345c" }, { - "bytes": 22607248, + "bytes": 22625552, "path": "native/aarch64-apple-darwin/libtera_ffi.dylib", - "sha256": "77c74b3a13dfdb8f29cb6efc49f1dda6c72a3672b9903ce3a36acd903ac1bdb6" + "sha256": "99c97e184256c6119518290fd859a2e8b31bbd0a78bf9eb63488e88f56eb6686" }, { - "bytes": 77606408, + "bytes": 77626640, "path": "native/aarch64-apple-ios-sim/libtera_ffi.a", - "sha256": "92ce6d8bd62d49ba37cd8a6e80303df22a8c58a83c6dac7c711734c10a2db8d5" + "sha256": "8ce3ff37fd19da343d3027e71a2a913c2563b4ff6c884b40090a9809f68cf5a2" }, { - "bytes": 77670032, + "bytes": 77693008, "path": "native/aarch64-apple-ios/libtera_ffi.a", - "sha256": "e3050e2d6c6e83899bb3f38d235d9a2c7b5e06bfc2f280a0fa72de4acecc9827" + "sha256": "e7ec700d624c53c8c11e143e3819fd65215af4dadb2117b76c0a4e7d33ba712e" }, { - "bytes": 121615, + "bytes": 122523, "path": "source/aarch64-apple-darwin.json", - "sha256": "f69c95093fff2ea55e8499b3ed1ae9e02925230cd9c93c87259437f7ebea7d35" + "sha256": "fdfa46afe9595e8bc058336938efeafc6900cd8699c0d267707f0cdfedb6a66d" }, { - "bytes": 121459, + "bytes": 122367, "path": "source/aarch64-apple-ios-sim.json", - "sha256": "7dfb61a95fcda921516030a41b146cdf43e86546993b17291c712fdfc9a351a4" + "sha256": "3640eb7a42aa71c5444c6d08c1313e2245b06eb443665c6de03daa5154075901" }, { - "bytes": 121455, + "bytes": 122363, "path": "source/aarch64-apple-ios.json", - "sha256": "285f5845f7baa9a475455934d68d8e4c5e4959ca5ed626205f69bc8dc7ede2aa" + "sha256": "e4d5eabf0d110c4c43328b8203df2a51394bfea1340af16a4a015a279f041c12" } ], "language": "swift", @@ -112,7 +112,7 @@ "schema": "radroots.artifact-manifest.v2", "source": { "repository": "https://github.com/radrootslabs/tera", - "tree": "f69196d4ddd33fca9b74ee5559ee8265fdfd8c25" + "tree": "681e757519909bf8e177ed3238398988c9d0a89d" }, "source_records": { "aarch64-apple-darwin": "source/aarch64-apple-darwin.json", @@ -139,9 +139,9 @@ "sha256": "3ed9b7ece2f86e5b5d1ddc6ecbb97deb49c46ca2ea91a930eaec1b580232345c" }, { - "bytes": 77606408, + "bytes": 77626640, "path": "Tera/Frameworks/TeraFFI.xcframework/ios-arm64-simulator/libtera_ffi.a", - "sha256": "92ce6d8bd62d49ba37cd8a6e80303df22a8c58a83c6dac7c711734c10a2db8d5" + "sha256": "8ce3ff37fd19da343d3027e71a2a913c2563b4ff6c884b40090a9809f68cf5a2" }, { "bytes": 88720, @@ -154,9 +154,9 @@ "sha256": "3ed9b7ece2f86e5b5d1ddc6ecbb97deb49c46ca2ea91a930eaec1b580232345c" }, { - "bytes": 77670032, + "bytes": 77693008, "path": "Tera/Frameworks/TeraFFI.xcframework/ios-arm64/libtera_ffi.a", - "sha256": "e3050e2d6c6e83899bb3f38d235d9a2c7b5e06bfc2f280a0fa72de4acecc9827" + "sha256": "e7ec700d624c53c8c11e143e3819fd65215af4dadb2117b76c0a4e7d33ba712e" }, { "bytes": 730192, @@ -174,19 +174,19 @@ "sha256": "81a943ef98405676b4f65932a4a8d4bdb4011923f65fa2e3496fcb58e1d68be8" }, { - "bytes": 121615, + "bytes": 122523, "path": "TeraFFI/source/aarch64-apple-darwin.json", - "sha256": "f69c95093fff2ea55e8499b3ed1ae9e02925230cd9c93c87259437f7ebea7d35" + "sha256": "fdfa46afe9595e8bc058336938efeafc6900cd8699c0d267707f0cdfedb6a66d" }, { - "bytes": 121459, + "bytes": 122367, "path": "TeraFFI/source/aarch64-apple-ios-sim.json", - "sha256": "7dfb61a95fcda921516030a41b146cdf43e86546993b17291c712fdfc9a351a4" + "sha256": "3640eb7a42aa71c5444c6d08c1313e2245b06eb443665c6de03daa5154075901" }, { - "bytes": 121455, + "bytes": 122363, "path": "TeraFFI/source/aarch64-apple-ios.json", - "sha256": "285f5845f7baa9a475455934d68d8e4c5e4959ca5ed626205f69bc8dc7ede2aa" + "sha256": "e4d5eabf0d110c4c43328b8203df2a51394bfea1340af16a4a015a279f041c12" } ], "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 = "f69196d4ddd33fca9b74ee5559ee8265fdfd8c25" -manifest_sha256 = "15b0370f4f0ac81fbe2247a295b649fea1fbb017843aee65f54246bb3dfce5bb" +source_tree = "681e757519909bf8e177ed3238398988c9d0a89d" +manifest_sha256 = "c184a6b1dd38541e27f792fbd41f90690e6c63c89165951a4526c06b60cf5800" source_date_epoch = 1787871027 [foundation] diff --git a/TeraFFI/source/aarch64-apple-darwin.json b/TeraFFI/source/aarch64-apple-darwin.json @@ -759,10 +759,10 @@ "sha256": "e20fc6c5ec40fd904fb8a84a406be1ac01a706c59c7a67eb895127a76ad9496e" }, "core/crates/tera_core/src/runtime/product_surface/coordinate.rs": { - "bytes": 4194, - "git_blob": "d6917a25fee622369d7826232a2709e32a3b9740", + "bytes": 4939, + "git_blob": "d1f3d83e00870c9b7873a0455ae8c3b4a2ce79f5", "mode": "100644", - "sha256": "c50e4ebb87d43a81970379ac32906c060fbcb9166c5880eeee233032d981ba4b" + "sha256": "0dbd634dff1399b2574df808e58a0776745a8394226e2103e7a8c482d247b694" }, "core/crates/tera_core/src/runtime/product_surface/coordinate/admission.rs": { "bytes": 5239, @@ -771,10 +771,16 @@ "sha256": "90dd6235887c55bad5feef5445d0c2d78b9fbc6a99c2f7a931b98c06edc3defe" }, "core/crates/tera_core/src/runtime/product_surface/coordinate/authority.rs": { - "bytes": 6522, - "git_blob": "b50f91cbb14b9c6d1acc316986f1eae4ffa90c24", + "bytes": 6376, + "git_blob": "10d3d70389c18584d7d299b5c06b97de796387a8", "mode": "100644", - "sha256": "c1a583372ca13b508d3adee31ce876519abb9bf36075b7cd008c66b28b7f9551" + "sha256": "67c2224e9a5d68d89e437c6c548f57fe423f42f6cce9c9aea2e69ed4079b06de" + }, + "core/crates/tera_core/src/runtime/product_surface/coordinate/ordering.rs": { + "bytes": 2687, + "git_blob": "697145856c654a601d779bdcfd9b63b79bc520a8", + "mode": "100644", + "sha256": "e19a74f94ad5c93d636bbd65751c564225860acf6e77159dcea67ad794e32de9" }, "core/crates/tera_core/src/runtime/product_surface/coordinate/record.rs": { "bytes": 2150, @@ -843,16 +849,16 @@ "sha256": "0dc99c1cc9cba832448368fe295a2a2b0ade4bc160d7f8f81c948b2460398794" }, "core/crates/tera_core/src/runtime/product_surface/outbox.rs": { - "bytes": 163006, - "git_blob": "a4218b7036b5cf50a3cc687476864991159825d9", + "bytes": 163071, + "git_blob": "e9c6244cf23a722dc08973a3fb69178e3787226c", "mode": "100644", - "sha256": "5cf534d4668cb0f8da8d70fdcc159c0fc9418df8a9a3c7e65b3d385023af1b30" + "sha256": "aced3db673ba9f356416d269020ab3b5f8346d50cfccf19e4c2d914556ecf64e" }, "core/crates/tera_core/src/runtime/product_surface/outbox/advance.rs": { - "bytes": 5405, - "git_blob": "a7fdfd5fdf81770e9b7cbf13c9f47e6284d43feb", + "bytes": 6023, + "git_blob": "17357131136396c6b25cb60b85e586e9356f84bb", "mode": "100644", - "sha256": "40ea5656890f2c6cdd5cc9ff566325d92291f23bd5e654ca4ce7a8f1893f72b3" + "sha256": "03deb13b477a7897c7f8da921fcefe35ddb3b6b2324da49c90bea1a457cdec72" }, "core/crates/tera_core/src/runtime/product_surface/outbox/configuration.rs": { "bytes": 5088, @@ -861,10 +867,10 @@ "sha256": "60e185523bacac3123fabb45214bf76a1b04a45b00fe326216e88785e54059b9" }, "core/crates/tera_core/src/runtime/product_surface/outbox/coordinate.rs": { - "bytes": 3396, - "git_blob": "e8a98bcd72a4939bce7c733a22dbf620dea9cbb8", + "bytes": 3386, + "git_blob": "fbf6865903177f279d8cff868e5757ad8cf4a30a", "mode": "100644", - "sha256": "79b0c8d18c9d32f488b36ebdc6ccfc3c930297430e2a707e684b506b1da50d9e" + "sha256": "e08f8ca6432b2b592aa3dc93af358c6bcee6cc9c09b341ab194088107f1b745c" }, "core/crates/tera_core/src/runtime/product_surface/outbox/inventory.rs": { "bytes": 9243, @@ -921,10 +927,10 @@ "sha256": "e8853d0e5413c7d1168d5e89696cff8760a5e743a35e086de1e78745f86d4b86" }, "core/crates/tera_core/src/runtime/product_surface/outbox/revision_preparation.rs": { - "bytes": 4066, - "git_blob": "20854297807e8d30b7c8526c053b61aa344fc5d7", + "bytes": 4492, + "git_blob": "6a373bb9e273c7dc1039061783adc46472defdbe", "mode": "100644", - "sha256": "258ab5917d39ee3c7fc4dcdfc42f3771550ab746989ab02363ca35afe03d7cc0" + "sha256": "1ffe8f3e29aae5b876a5e0e314117c197f628a615d2f39173bd9af6bbb47e790" }, "core/crates/tera_core/src/runtime/product_surface/outbox/revision_source.rs": { "bytes": 4500, @@ -938,6 +944,12 @@ "mode": "100644", "sha256": "0e83616701321035cb2c83fbd6477d03d083316bb22596924ea2d69d1606b2a0" }, + "core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_clock_tests.rs": { + "bytes": 2937, + "git_blob": "743187d1aad0a631d85404882ab632480eeab56c", + "mode": "100644", + "sha256": "16a6377c6ac8e76a5a53beafae99c28e1e11c9e25782c211ea203ff7ffadb1d6" + }, "core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_effect_support.rs": { "bytes": 3474, "git_blob": "fe040d73f84f23e7288e32cfef5cfc1336f7c47c", @@ -950,6 +962,12 @@ "mode": "100644", "sha256": "b14400ebf5629dd2ecbd32c213d3d11431b442e9ccddd733c396cc83fd8303b0" }, + "core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_ordering_tests.rs": { + "bytes": 9065, + "git_blob": "3cd9ae1f5de370bef08b1393f7bdade80ffafb73", + "mode": "100644", + "sha256": "903a51fc8b06aeeb100b132e5ff6e939fad5bfad35e6f2ea95b176021d8242ef" + }, "core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_recovery_tests.rs": { "bytes": 4971, "git_blob": "d25d43524147982909902ad1e521d97a4660a028", @@ -957,10 +975,10 @@ "sha256": "0adf7e8a9cf1f831bb7febbdf86edbea7e0919607390dd4283ae392bcf504bae" }, "core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_support.rs": { - "bytes": 4400, - "git_blob": "84c6dbdeeecbfd82136089798318e06e0e36b011", + "bytes": 4627, + "git_blob": "2fbfc048dd92fb4888cc96d6c618c3f8d94b461c", "mode": "100644", - "sha256": "a691632c5a9aec53f988179fbc0fde95493c5b8f0868d4c471d39e2e33f0b334" + "sha256": "07b179070fad060da515da036925091404117490b91f5f7fc59fd6c99816b04c" }, "core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_tests.rs": { "bytes": 9940, @@ -1125,10 +1143,10 @@ "sha256": "9b830c7db167db51e6c193c3ddd1fac3bdcaf48b8546be8497d823aec0869f12" }, "core/crates/tera_core/src/runtime/product_surface/submission.rs": { - "bytes": 6701, - "git_blob": "b333ef4a63d25e6857dc4da8bdb26fe4b387edd7", + "bytes": 6699, + "git_blob": "fe1b60751bc2faa598423196f18c37ffd11f5eb8", "mode": "100644", - "sha256": "8ae35534638a513e44376215b303d033d9349c55ee9547314ec1ee544705ed98" + "sha256": "bb1200a59a952a477a5c7db3bd74ae659e8c2026a866df7ecb1d4d8e4c17bbd1" }, "core/crates/tera_core/src/runtime/product_surface/submission/atomic_fault_store.rs": { "bytes": 3427, @@ -1215,10 +1233,10 @@ "sha256": "ebedb6a978e2a0682a58f03c24a14e60cea1df8b90b89cbc5f10206635ebc0db" }, "core/crates/tera_core/src/runtime/product_surface/submission/coordinate.rs": { - "bytes": 1225, - "git_blob": "253c6ef0d34484c567bb2ab128ea9e9ceee9a206", + "bytes": 1217, + "git_blob": "c797a864d00a56dca30eee6a24c8c83a69205476", "mode": "100644", - "sha256": "9de36a60bacdb21a595406c8a7f984f0a0817561182726616a85d840cb777222" + "sha256": "2663a23f32b0530ac320d3f2d0997227d98ed5272d40b47562ccbcf58a6fbf00" }, "core/crates/tera_core/src/runtime/product_surface/submission/fault_store.rs": { "bytes": 3779, @@ -1227,10 +1245,10 @@ "sha256": "23fab4dd866cf8d4acf87c9091c8858312dc7789d5fe08c250694a69a02248ed" }, "core/crates/tera_core/src/runtime/product_surface/submission/intent.rs": { - "bytes": 11842, - "git_blob": "5802763904b1c45454231bec3e120d23ff2a38ad", + "bytes": 11836, + "git_blob": "91cd1237ca8a7242dc2aecb931c47ef8a9be72c5", "mode": "100644", - "sha256": "9daa97039ce8f3f2158552e85a1b98e36ab8ba897f7435ce7d362123d63f4fb3" + "sha256": "512f0978923a9aac78406853c8a5afd5d9c340716ccd3d81c9129e9777872a13" }, "core/crates/tera_core/src/runtime/product_surface/submission/intent_inventory.rs": { "bytes": 2440, @@ -2577,13 +2595,13 @@ "sha256": "b052a73a824e8f8b26af2646a4758f13655e04de9551a8271890d3cf2b63209d" }, "test-fixtures/legacy-identifiers.v1.json": { - "bytes": 167226, - "git_blob": "0a6e8c7169bcd3f0369be47d7b820e100c7dc8ae", + "bytes": 167808, + "git_blob": "1c21e5b73d9effa6ebcf4cad298868574b9ceee9", "mode": "100644", - "sha256": "01ff305a8eddbcf2f88c07a8146503a3ddee7bcb0cda81d152852716c9aecb1c" + "sha256": "4362152b23a985ae966ed67b08635454a65434b5d440fd862224bb08316199a9" } }, "policy": "staged_inputs", - "tree": "f69196d4ddd33fca9b74ee5559ee8265fdfd8c25" + "tree": "681e757519909bf8e177ed3238398988c9d0a89d" } } diff --git a/TeraFFI/source/aarch64-apple-ios-sim.json b/TeraFFI/source/aarch64-apple-ios-sim.json @@ -755,10 +755,10 @@ "sha256": "e20fc6c5ec40fd904fb8a84a406be1ac01a706c59c7a67eb895127a76ad9496e" }, "core/crates/tera_core/src/runtime/product_surface/coordinate.rs": { - "bytes": 4194, - "git_blob": "d6917a25fee622369d7826232a2709e32a3b9740", + "bytes": 4939, + "git_blob": "d1f3d83e00870c9b7873a0455ae8c3b4a2ce79f5", "mode": "100644", - "sha256": "c50e4ebb87d43a81970379ac32906c060fbcb9166c5880eeee233032d981ba4b" + "sha256": "0dbd634dff1399b2574df808e58a0776745a8394226e2103e7a8c482d247b694" }, "core/crates/tera_core/src/runtime/product_surface/coordinate/admission.rs": { "bytes": 5239, @@ -767,10 +767,16 @@ "sha256": "90dd6235887c55bad5feef5445d0c2d78b9fbc6a99c2f7a931b98c06edc3defe" }, "core/crates/tera_core/src/runtime/product_surface/coordinate/authority.rs": { - "bytes": 6522, - "git_blob": "b50f91cbb14b9c6d1acc316986f1eae4ffa90c24", + "bytes": 6376, + "git_blob": "10d3d70389c18584d7d299b5c06b97de796387a8", "mode": "100644", - "sha256": "c1a583372ca13b508d3adee31ce876519abb9bf36075b7cd008c66b28b7f9551" + "sha256": "67c2224e9a5d68d89e437c6c548f57fe423f42f6cce9c9aea2e69ed4079b06de" + }, + "core/crates/tera_core/src/runtime/product_surface/coordinate/ordering.rs": { + "bytes": 2687, + "git_blob": "697145856c654a601d779bdcfd9b63b79bc520a8", + "mode": "100644", + "sha256": "e19a74f94ad5c93d636bbd65751c564225860acf6e77159dcea67ad794e32de9" }, "core/crates/tera_core/src/runtime/product_surface/coordinate/record.rs": { "bytes": 2150, @@ -839,16 +845,16 @@ "sha256": "0dc99c1cc9cba832448368fe295a2a2b0ade4bc160d7f8f81c948b2460398794" }, "core/crates/tera_core/src/runtime/product_surface/outbox.rs": { - "bytes": 163006, - "git_blob": "a4218b7036b5cf50a3cc687476864991159825d9", + "bytes": 163071, + "git_blob": "e9c6244cf23a722dc08973a3fb69178e3787226c", "mode": "100644", - "sha256": "5cf534d4668cb0f8da8d70fdcc159c0fc9418df8a9a3c7e65b3d385023af1b30" + "sha256": "aced3db673ba9f356416d269020ab3b5f8346d50cfccf19e4c2d914556ecf64e" }, "core/crates/tera_core/src/runtime/product_surface/outbox/advance.rs": { - "bytes": 5405, - "git_blob": "a7fdfd5fdf81770e9b7cbf13c9f47e6284d43feb", + "bytes": 6023, + "git_blob": "17357131136396c6b25cb60b85e586e9356f84bb", "mode": "100644", - "sha256": "40ea5656890f2c6cdd5cc9ff566325d92291f23bd5e654ca4ce7a8f1893f72b3" + "sha256": "03deb13b477a7897c7f8da921fcefe35ddb3b6b2324da49c90bea1a457cdec72" }, "core/crates/tera_core/src/runtime/product_surface/outbox/configuration.rs": { "bytes": 5088, @@ -857,10 +863,10 @@ "sha256": "60e185523bacac3123fabb45214bf76a1b04a45b00fe326216e88785e54059b9" }, "core/crates/tera_core/src/runtime/product_surface/outbox/coordinate.rs": { - "bytes": 3396, - "git_blob": "e8a98bcd72a4939bce7c733a22dbf620dea9cbb8", + "bytes": 3386, + "git_blob": "fbf6865903177f279d8cff868e5757ad8cf4a30a", "mode": "100644", - "sha256": "79b0c8d18c9d32f488b36ebdc6ccfc3c930297430e2a707e684b506b1da50d9e" + "sha256": "e08f8ca6432b2b592aa3dc93af358c6bcee6cc9c09b341ab194088107f1b745c" }, "core/crates/tera_core/src/runtime/product_surface/outbox/inventory.rs": { "bytes": 9243, @@ -917,10 +923,10 @@ "sha256": "e8853d0e5413c7d1168d5e89696cff8760a5e743a35e086de1e78745f86d4b86" }, "core/crates/tera_core/src/runtime/product_surface/outbox/revision_preparation.rs": { - "bytes": 4066, - "git_blob": "20854297807e8d30b7c8526c053b61aa344fc5d7", + "bytes": 4492, + "git_blob": "6a373bb9e273c7dc1039061783adc46472defdbe", "mode": "100644", - "sha256": "258ab5917d39ee3c7fc4dcdfc42f3771550ab746989ab02363ca35afe03d7cc0" + "sha256": "1ffe8f3e29aae5b876a5e0e314117c197f628a615d2f39173bd9af6bbb47e790" }, "core/crates/tera_core/src/runtime/product_surface/outbox/revision_source.rs": { "bytes": 4500, @@ -934,6 +940,12 @@ "mode": "100644", "sha256": "0e83616701321035cb2c83fbd6477d03d083316bb22596924ea2d69d1606b2a0" }, + "core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_clock_tests.rs": { + "bytes": 2937, + "git_blob": "743187d1aad0a631d85404882ab632480eeab56c", + "mode": "100644", + "sha256": "16a6377c6ac8e76a5a53beafae99c28e1e11c9e25782c211ea203ff7ffadb1d6" + }, "core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_effect_support.rs": { "bytes": 3474, "git_blob": "fe040d73f84f23e7288e32cfef5cfc1336f7c47c", @@ -946,6 +958,12 @@ "mode": "100644", "sha256": "b14400ebf5629dd2ecbd32c213d3d11431b442e9ccddd733c396cc83fd8303b0" }, + "core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_ordering_tests.rs": { + "bytes": 9065, + "git_blob": "3cd9ae1f5de370bef08b1393f7bdade80ffafb73", + "mode": "100644", + "sha256": "903a51fc8b06aeeb100b132e5ff6e939fad5bfad35e6f2ea95b176021d8242ef" + }, "core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_recovery_tests.rs": { "bytes": 4971, "git_blob": "d25d43524147982909902ad1e521d97a4660a028", @@ -953,10 +971,10 @@ "sha256": "0adf7e8a9cf1f831bb7febbdf86edbea7e0919607390dd4283ae392bcf504bae" }, "core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_support.rs": { - "bytes": 4400, - "git_blob": "84c6dbdeeecbfd82136089798318e06e0e36b011", + "bytes": 4627, + "git_blob": "2fbfc048dd92fb4888cc96d6c618c3f8d94b461c", "mode": "100644", - "sha256": "a691632c5a9aec53f988179fbc0fde95493c5b8f0868d4c471d39e2e33f0b334" + "sha256": "07b179070fad060da515da036925091404117490b91f5f7fc59fd6c99816b04c" }, "core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_tests.rs": { "bytes": 9940, @@ -1121,10 +1139,10 @@ "sha256": "9b830c7db167db51e6c193c3ddd1fac3bdcaf48b8546be8497d823aec0869f12" }, "core/crates/tera_core/src/runtime/product_surface/submission.rs": { - "bytes": 6701, - "git_blob": "b333ef4a63d25e6857dc4da8bdb26fe4b387edd7", + "bytes": 6699, + "git_blob": "fe1b60751bc2faa598423196f18c37ffd11f5eb8", "mode": "100644", - "sha256": "8ae35534638a513e44376215b303d033d9349c55ee9547314ec1ee544705ed98" + "sha256": "bb1200a59a952a477a5c7db3bd74ae659e8c2026a866df7ecb1d4d8e4c17bbd1" }, "core/crates/tera_core/src/runtime/product_surface/submission/atomic_fault_store.rs": { "bytes": 3427, @@ -1211,10 +1229,10 @@ "sha256": "ebedb6a978e2a0682a58f03c24a14e60cea1df8b90b89cbc5f10206635ebc0db" }, "core/crates/tera_core/src/runtime/product_surface/submission/coordinate.rs": { - "bytes": 1225, - "git_blob": "253c6ef0d34484c567bb2ab128ea9e9ceee9a206", + "bytes": 1217, + "git_blob": "c797a864d00a56dca30eee6a24c8c83a69205476", "mode": "100644", - "sha256": "9de36a60bacdb21a595406c8a7f984f0a0817561182726616a85d840cb777222" + "sha256": "2663a23f32b0530ac320d3f2d0997227d98ed5272d40b47562ccbcf58a6fbf00" }, "core/crates/tera_core/src/runtime/product_surface/submission/fault_store.rs": { "bytes": 3779, @@ -1223,10 +1241,10 @@ "sha256": "23fab4dd866cf8d4acf87c9091c8858312dc7789d5fe08c250694a69a02248ed" }, "core/crates/tera_core/src/runtime/product_surface/submission/intent.rs": { - "bytes": 11842, - "git_blob": "5802763904b1c45454231bec3e120d23ff2a38ad", + "bytes": 11836, + "git_blob": "91cd1237ca8a7242dc2aecb931c47ef8a9be72c5", "mode": "100644", - "sha256": "9daa97039ce8f3f2158552e85a1b98e36ab8ba897f7435ce7d362123d63f4fb3" + "sha256": "512f0978923a9aac78406853c8a5afd5d9c340716ccd3d81c9129e9777872a13" }, "core/crates/tera_core/src/runtime/product_surface/submission/intent_inventory.rs": { "bytes": 2440, @@ -2573,13 +2591,13 @@ "sha256": "b052a73a824e8f8b26af2646a4758f13655e04de9551a8271890d3cf2b63209d" }, "test-fixtures/legacy-identifiers.v1.json": { - "bytes": 167226, - "git_blob": "0a6e8c7169bcd3f0369be47d7b820e100c7dc8ae", + "bytes": 167808, + "git_blob": "1c21e5b73d9effa6ebcf4cad298868574b9ceee9", "mode": "100644", - "sha256": "01ff305a8eddbcf2f88c07a8146503a3ddee7bcb0cda81d152852716c9aecb1c" + "sha256": "4362152b23a985ae966ed67b08635454a65434b5d440fd862224bb08316199a9" } }, "policy": "staged_inputs", - "tree": "f69196d4ddd33fca9b74ee5559ee8265fdfd8c25" + "tree": "681e757519909bf8e177ed3238398988c9d0a89d" } } diff --git a/TeraFFI/source/aarch64-apple-ios.json b/TeraFFI/source/aarch64-apple-ios.json @@ -755,10 +755,10 @@ "sha256": "e20fc6c5ec40fd904fb8a84a406be1ac01a706c59c7a67eb895127a76ad9496e" }, "core/crates/tera_core/src/runtime/product_surface/coordinate.rs": { - "bytes": 4194, - "git_blob": "d6917a25fee622369d7826232a2709e32a3b9740", + "bytes": 4939, + "git_blob": "d1f3d83e00870c9b7873a0455ae8c3b4a2ce79f5", "mode": "100644", - "sha256": "c50e4ebb87d43a81970379ac32906c060fbcb9166c5880eeee233032d981ba4b" + "sha256": "0dbd634dff1399b2574df808e58a0776745a8394226e2103e7a8c482d247b694" }, "core/crates/tera_core/src/runtime/product_surface/coordinate/admission.rs": { "bytes": 5239, @@ -767,10 +767,16 @@ "sha256": "90dd6235887c55bad5feef5445d0c2d78b9fbc6a99c2f7a931b98c06edc3defe" }, "core/crates/tera_core/src/runtime/product_surface/coordinate/authority.rs": { - "bytes": 6522, - "git_blob": "b50f91cbb14b9c6d1acc316986f1eae4ffa90c24", + "bytes": 6376, + "git_blob": "10d3d70389c18584d7d299b5c06b97de796387a8", "mode": "100644", - "sha256": "c1a583372ca13b508d3adee31ce876519abb9bf36075b7cd008c66b28b7f9551" + "sha256": "67c2224e9a5d68d89e437c6c548f57fe423f42f6cce9c9aea2e69ed4079b06de" + }, + "core/crates/tera_core/src/runtime/product_surface/coordinate/ordering.rs": { + "bytes": 2687, + "git_blob": "697145856c654a601d779bdcfd9b63b79bc520a8", + "mode": "100644", + "sha256": "e19a74f94ad5c93d636bbd65751c564225860acf6e77159dcea67ad794e32de9" }, "core/crates/tera_core/src/runtime/product_surface/coordinate/record.rs": { "bytes": 2150, @@ -839,16 +845,16 @@ "sha256": "0dc99c1cc9cba832448368fe295a2a2b0ade4bc160d7f8f81c948b2460398794" }, "core/crates/tera_core/src/runtime/product_surface/outbox.rs": { - "bytes": 163006, - "git_blob": "a4218b7036b5cf50a3cc687476864991159825d9", + "bytes": 163071, + "git_blob": "e9c6244cf23a722dc08973a3fb69178e3787226c", "mode": "100644", - "sha256": "5cf534d4668cb0f8da8d70fdcc159c0fc9418df8a9a3c7e65b3d385023af1b30" + "sha256": "aced3db673ba9f356416d269020ab3b5f8346d50cfccf19e4c2d914556ecf64e" }, "core/crates/tera_core/src/runtime/product_surface/outbox/advance.rs": { - "bytes": 5405, - "git_blob": "a7fdfd5fdf81770e9b7cbf13c9f47e6284d43feb", + "bytes": 6023, + "git_blob": "17357131136396c6b25cb60b85e586e9356f84bb", "mode": "100644", - "sha256": "40ea5656890f2c6cdd5cc9ff566325d92291f23bd5e654ca4ce7a8f1893f72b3" + "sha256": "03deb13b477a7897c7f8da921fcefe35ddb3b6b2324da49c90bea1a457cdec72" }, "core/crates/tera_core/src/runtime/product_surface/outbox/configuration.rs": { "bytes": 5088, @@ -857,10 +863,10 @@ "sha256": "60e185523bacac3123fabb45214bf76a1b04a45b00fe326216e88785e54059b9" }, "core/crates/tera_core/src/runtime/product_surface/outbox/coordinate.rs": { - "bytes": 3396, - "git_blob": "e8a98bcd72a4939bce7c733a22dbf620dea9cbb8", + "bytes": 3386, + "git_blob": "fbf6865903177f279d8cff868e5757ad8cf4a30a", "mode": "100644", - "sha256": "79b0c8d18c9d32f488b36ebdc6ccfc3c930297430e2a707e684b506b1da50d9e" + "sha256": "e08f8ca6432b2b592aa3dc93af358c6bcee6cc9c09b341ab194088107f1b745c" }, "core/crates/tera_core/src/runtime/product_surface/outbox/inventory.rs": { "bytes": 9243, @@ -917,10 +923,10 @@ "sha256": "e8853d0e5413c7d1168d5e89696cff8760a5e743a35e086de1e78745f86d4b86" }, "core/crates/tera_core/src/runtime/product_surface/outbox/revision_preparation.rs": { - "bytes": 4066, - "git_blob": "20854297807e8d30b7c8526c053b61aa344fc5d7", + "bytes": 4492, + "git_blob": "6a373bb9e273c7dc1039061783adc46472defdbe", "mode": "100644", - "sha256": "258ab5917d39ee3c7fc4dcdfc42f3771550ab746989ab02363ca35afe03d7cc0" + "sha256": "1ffe8f3e29aae5b876a5e0e314117c197f628a615d2f39173bd9af6bbb47e790" }, "core/crates/tera_core/src/runtime/product_surface/outbox/revision_source.rs": { "bytes": 4500, @@ -934,6 +940,12 @@ "mode": "100644", "sha256": "0e83616701321035cb2c83fbd6477d03d083316bb22596924ea2d69d1606b2a0" }, + "core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_clock_tests.rs": { + "bytes": 2937, + "git_blob": "743187d1aad0a631d85404882ab632480eeab56c", + "mode": "100644", + "sha256": "16a6377c6ac8e76a5a53beafae99c28e1e11c9e25782c211ea203ff7ffadb1d6" + }, "core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_effect_support.rs": { "bytes": 3474, "git_blob": "fe040d73f84f23e7288e32cfef5cfc1336f7c47c", @@ -946,6 +958,12 @@ "mode": "100644", "sha256": "b14400ebf5629dd2ecbd32c213d3d11431b442e9ccddd733c396cc83fd8303b0" }, + "core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_ordering_tests.rs": { + "bytes": 9065, + "git_blob": "3cd9ae1f5de370bef08b1393f7bdade80ffafb73", + "mode": "100644", + "sha256": "903a51fc8b06aeeb100b132e5ff6e939fad5bfad35e6f2ea95b176021d8242ef" + }, "core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_recovery_tests.rs": { "bytes": 4971, "git_blob": "d25d43524147982909902ad1e521d97a4660a028", @@ -953,10 +971,10 @@ "sha256": "0adf7e8a9cf1f831bb7febbdf86edbea7e0919607390dd4283ae392bcf504bae" }, "core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_support.rs": { - "bytes": 4400, - "git_blob": "84c6dbdeeecbfd82136089798318e06e0e36b011", + "bytes": 4627, + "git_blob": "2fbfc048dd92fb4888cc96d6c618c3f8d94b461c", "mode": "100644", - "sha256": "a691632c5a9aec53f988179fbc0fde95493c5b8f0868d4c471d39e2e33f0b334" + "sha256": "07b179070fad060da515da036925091404117490b91f5f7fc59fd6c99816b04c" }, "core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_tests.rs": { "bytes": 9940, @@ -1121,10 +1139,10 @@ "sha256": "9b830c7db167db51e6c193c3ddd1fac3bdcaf48b8546be8497d823aec0869f12" }, "core/crates/tera_core/src/runtime/product_surface/submission.rs": { - "bytes": 6701, - "git_blob": "b333ef4a63d25e6857dc4da8bdb26fe4b387edd7", + "bytes": 6699, + "git_blob": "fe1b60751bc2faa598423196f18c37ffd11f5eb8", "mode": "100644", - "sha256": "8ae35534638a513e44376215b303d033d9349c55ee9547314ec1ee544705ed98" + "sha256": "bb1200a59a952a477a5c7db3bd74ae659e8c2026a866df7ecb1d4d8e4c17bbd1" }, "core/crates/tera_core/src/runtime/product_surface/submission/atomic_fault_store.rs": { "bytes": 3427, @@ -1211,10 +1229,10 @@ "sha256": "ebedb6a978e2a0682a58f03c24a14e60cea1df8b90b89cbc5f10206635ebc0db" }, "core/crates/tera_core/src/runtime/product_surface/submission/coordinate.rs": { - "bytes": 1225, - "git_blob": "253c6ef0d34484c567bb2ab128ea9e9ceee9a206", + "bytes": 1217, + "git_blob": "c797a864d00a56dca30eee6a24c8c83a69205476", "mode": "100644", - "sha256": "9de36a60bacdb21a595406c8a7f984f0a0817561182726616a85d840cb777222" + "sha256": "2663a23f32b0530ac320d3f2d0997227d98ed5272d40b47562ccbcf58a6fbf00" }, "core/crates/tera_core/src/runtime/product_surface/submission/fault_store.rs": { "bytes": 3779, @@ -1223,10 +1241,10 @@ "sha256": "23fab4dd866cf8d4acf87c9091c8858312dc7789d5fe08c250694a69a02248ed" }, "core/crates/tera_core/src/runtime/product_surface/submission/intent.rs": { - "bytes": 11842, - "git_blob": "5802763904b1c45454231bec3e120d23ff2a38ad", + "bytes": 11836, + "git_blob": "91cd1237ca8a7242dc2aecb931c47ef8a9be72c5", "mode": "100644", - "sha256": "9daa97039ce8f3f2158552e85a1b98e36ab8ba897f7435ce7d362123d63f4fb3" + "sha256": "512f0978923a9aac78406853c8a5afd5d9c340716ccd3d81c9129e9777872a13" }, "core/crates/tera_core/src/runtime/product_surface/submission/intent_inventory.rs": { "bytes": 2440, @@ -2573,13 +2591,13 @@ "sha256": "b052a73a824e8f8b26af2646a4758f13655e04de9551a8271890d3cf2b63209d" }, "test-fixtures/legacy-identifiers.v1.json": { - "bytes": 167226, - "git_blob": "0a6e8c7169bcd3f0369be47d7b820e100c7dc8ae", + "bytes": 167808, + "git_blob": "1c21e5b73d9effa6ebcf4cad298868574b9ceee9", "mode": "100644", - "sha256": "01ff305a8eddbcf2f88c07a8146503a3ddee7bcb0cda81d152852716c9aecb1c" + "sha256": "4362152b23a985ae966ed67b08635454a65434b5d440fd862224bb08316199a9" } }, "policy": "staged_inputs", - "tree": "f69196d4ddd33fca9b74ee5559ee8265fdfd8c25" + "tree": "681e757519909bf8e177ed3238398988c9d0a89d" } } diff --git a/TeraTests/TeraCalendarComposerFFITests.swift b/TeraTests/TeraCalendarComposerFFITests.swift @@ -13,8 +13,8 @@ final class TeraCalendarComposerFFITests: XCTestCase { let form = try form(mode) let input = TeraAddRuntimeInput(form: form, media: []).generatedValue let draft = try await first.phase1SaveDraft(draftId: String(repeating: index == 0 ? "51" : "52", count: 16), - input: input, authoredAtUnixS: 1_900_000_000, expectedRevision: nil, - persistedAtUnixMs: 1_900_000_000_000) + input: input, authoredAtUnixS: 1_700_000_000, expectedRevision: nil, + persistedAtUnixMs: 1_700_000_000_000) XCTAssertEqual(draft.form?.eventStartDate, input.eventStartDate) XCTAssertEqual(draft.form?.eventEndDate, input.eventEndDate) XCTAssertEqual(input.eventStartDate, "2026-09-05") @@ -24,7 +24,7 @@ final class TeraCalendarComposerFFITests: XCTestCase { let queued = try await first.phase1QueueDraft(draftId: draft.draftId, expectedRevision: draft.revision, policy: FfiQueuePolicyRecord(schemaVersion: 1, relayUrls: ["wss://relay.example"], satisfaction: .allAccepted, deliveryDeadlineUnixMs: 2_000_000_000_000, - cancellation: .localCooperative), queuedAtUnixMs: 1_900_000_000_001) + cancellation: .localCooperative), queuedAtUnixMs: 1_700_000_000_001) XCTAssertEqual(queued.state, .queued) XCTAssertEqual(queued.form, draft.form) XCTAssertNotNil(queued.operationId) diff --git a/TeraTests/TeraCalendarFFICompatibilityTests.swift b/TeraTests/TeraCalendarFFICompatibilityTests.swift @@ -20,9 +20,9 @@ final class TeraCalendarFFICompatibilityTests: XCTestCase { let saved = try await first.phase1SaveDraft( draftId: String(repeating: index == 0 ? "01" : "02", count: 16), input: input, - authoredAtUnixS: 1_900_000_000, + authoredAtUnixS: 1_700_000_000, expectedRevision: nil, - persistedAtUnixMs: 1_900_000_000_000 + persistedAtUnixMs: 1_700_000_000_000 ) let form = try XCTUnwrap(saved.form) XCTAssertEqual(form.commandType, .createEvent) @@ -42,7 +42,7 @@ final class TeraCalendarFFICompatibilityTests: XCTestCase { deliveryDeadlineUnixMs: 2_000_000_000_000, cancellation: .localCooperative ), - queuedAtUnixMs: 1_900_000_000_001 + queuedAtUnixMs: 1_700_000_000_001 ) XCTAssertEqual(status.state, .queued) XCTAssertNotNil(status.operationId) diff --git a/core/crates/tera_core/src/runtime/product_surface/coordinate.rs b/core/crates/tera_core/src/runtime/product_surface/coordinate.rs @@ -13,11 +13,33 @@ use super::Phase1DraftError as E; mod admission; mod authority; +mod ordering; mod record; pub(super) const CLAIM_SCHEMA: &str = "tera.coordinate_claim.v1"; pub(super) const BINDING_SCHEMA: &str = "tera.coordinate_binding.v1"; +/// Ephemeral view of the existing validated capture, never another persisted schema. +pub(super) struct CoordinatePlan { + pub intent: CoordinateIntent, + pub created_at: u64, +} + +impl CoordinatePlan { + pub(super) fn from_plan( + draft: &AuthoredDraft, + plan: &AuthoredEventPlan, + prior_event_id: Option<[u8; 32]>, + ) -> Result<Option<Self>, E> { + Ok( + CoordinateIntent::from_plan(draft, plan, prior_event_id)?.map(|intent| Self { + intent, + created_at: plan.created_at(), + }), + ) + } +} + #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] #[serde(deny_unknown_fields)] pub(super) struct CoordinateIntent { @@ -115,9 +137,13 @@ pub(super) fn metadata_is_valid(draft: &AuthoredDraft) -> bool { } pub(super) fn intent_from_draft(draft: &AuthoredDraft) -> Result<Option<CoordinateIntent>, E> { + Ok(plan_from_draft(draft)?.map(|plan| plan.intent)) +} + +pub(super) fn plan_from_draft(draft: &AuthoredDraft) -> Result<Option<CoordinatePlan>, E> { match draft.payload_schema() { - super::outbox::DRAFT_PAYLOAD_SCHEMA => super::outbox::coordinate_intent(draft), - super::SUBMISSION_INTENT_PAYLOAD_SCHEMA => super::submission::coordinate_intent(draft), + super::outbox::DRAFT_PAYLOAD_SCHEMA => super::outbox::coordinate_plan(draft), + super::SUBMISSION_INTENT_PAYLOAD_SCHEMA => super::submission::coordinate_plan(draft), _ => Err(E::Corrupt), } } diff --git a/core/crates/tera_core/src/runtime/product_surface/coordinate/authority.rs b/core/crates/tera_core/src/runtime/product_surface/coordinate/authority.rs @@ -2,31 +2,9 @@ use super::{record::OwnershipRecord, *}; use crate::TeraRuntime; -use radroots_storage::{authored_draft::AuthoredDraftRevision, event::EventStore}; +use radroots_storage::authored_draft::AuthoredDraftRevision; impl TeraRuntime { - pub(in crate::runtime::product_surface) async fn coordinate_known_winner_matches( - &self, - intent: &CoordinateIntent, - ) -> Result<bool, E> { - let store = self.client.storage().map_err(|_| E::Storage)?; - let coordinate = intent.coordinate()?; - let snapshot = EventStore::rebuild_visibility(store) - .await - .map_err(|_| E::Storage)?; - let head = snapshot - .current_heads() - .iter() - .find(|head| head.coordinate == coordinate); - Ok(match head { - Some(head) => { - head.event_id.as_bytes() == &intent.event_id - || intent.prior_event_id.as_ref() == Some(head.event_id.as_bytes()) - } - None => intent.prior_event_id.is_none(), - }) - } - pub(super) async fn coordinate_binding( &self, intent: &CoordinateIntent, @@ -72,6 +50,18 @@ impl TeraRuntime { &self, intent: &CoordinateIntent, ) -> Result<bool, E> { + self.coordinate_is_current_at( + intent, + super::super::phase1_operation_now_unix_ms()? / 1_000, + ) + .await + } + + async fn coordinate_is_current_at( + &self, + intent: &CoordinateIntent, + now_unix_s: u64, + ) -> Result<bool, E> { let Some(binding) = self.coordinate_binding(intent).await? else { return Ok(false); }; @@ -82,7 +72,10 @@ impl TeraRuntime { .map_err(|_| E::Storage)? .ok_or(E::Corrupt)?; let current = OwnershipRecord::decode(&claim)?; - Ok(current == binding && self.coordinate_known_winner_matches(intent).await?) + Ok(current == binding + && self + .coordinate_known_winner_matches_at(intent, now_unix_s) + .await?) } pub(in crate::runtime::product_surface) async fn require_coordinate_current( @@ -95,6 +88,17 @@ impl TeraRuntime { Ok(()) } + pub(in crate::runtime::product_surface) async fn require_coordinate_current_at( + &self, + intent: &CoordinateIntent, + now_unix_s: u64, + ) -> Result<(), E> { + if !self.coordinate_is_current_at(intent, now_unix_s).await? { + return Err(E::RevisionConflict); + } + Ok(()) + } + pub(in crate::runtime::product_surface) async fn coordinate_may_resume( &self, head: &AuthoredDraft, diff --git a/core/crates/tera_core/src/runtime/product_surface/coordinate/ordering.rs b/core/crates/tera_core/src/runtime/product_surface/coordinate/ordering.rs @@ -0,0 +1,73 @@ +//! Frozen plans consume shared Nostr ordering before acquiring effect authority. + +use super::*; +use crate::TeraRuntime; +use radroots_event::{ + EventId, + envelope::event_head::{EventHeadCandidate, EventHeadDecision, select_event_head}, +}; +use radroots_storage::event::EventStore; + +impl TeraRuntime { + pub(in crate::runtime::product_surface) async fn coordinate_known_winner_matches( + &self, + intent: &CoordinateIntent, + ) -> Result<bool, E> { + self.coordinate_known_winner_matches_at( + intent, + super::super::phase1_operation_now_unix_ms()? / 1_000, + ) + .await + } + + pub(in crate::runtime::product_surface) async fn coordinate_known_winner_matches_at( + &self, + intent: &CoordinateIntent, + now_unix_s: u64, + ) -> Result<bool, E> { + let store = self.client.storage().map_err(|_| E::Storage)?; + let draft = store + .authored_draft_head(AuthoredDraftId::new(intent.draft_id).map_err(|_| E::Corrupt)?) + .await + .map_err(|_| E::Storage)? + .ok_or(E::Corrupt)?; + let plan = plan_from_draft(&draft)?.ok_or(E::Corrupt)?; + if plan.intent != *intent { + return Err(E::RevisionConflict); + } + // The bounded policy permits zero synthetic advancement and no future + // captured timestamp relative to the current clock. Never repair clock + // rollback by changing a saved preimage. Existing delivery deadlines + // separately bound attempts after a forward jump. + if plan.created_at > now_unix_s { + return Ok(false); + } + let coordinate = intent.coordinate()?; + let snapshot = EventStore::rebuild_visibility(store) + .await + .map_err(|_| E::Storage)?; + let current = snapshot + .current_heads() + .iter() + .find(|head| head.coordinate == coordinate); + let selected = match current { + Some(head) => { + head.event_id.as_bytes() == &intent.event_id + || intent.prior_event_id.as_ref() == Some(head.event_id.as_bytes()) + } + None => intent.prior_event_id.is_none(), + }; + if !selected { + return Ok(false); + } + let candidate = EventHeadCandidate { + coordinate, + event_id: EventId::parse(hex::encode(intent.event_id)).map_err(|_| E::Corrupt)?, + created_at: plan.created_at, + }; + Ok(matches!( + select_event_head(candidate, current), + EventHeadDecision::Applied(_) | EventHeadDecision::SkippedDuplicate + )) + } +} diff --git a/core/crates/tera_core/src/runtime/product_surface/outbox.rs b/core/crates/tera_core/src/runtime/product_surface/outbox.rs @@ -47,7 +47,7 @@ use crate::runtime::TeraRuntime; mod advance; mod coordinate; -pub(super) use coordinate::coordinate_intent; +pub(super) use coordinate::coordinate_plan; #[path = "outbox/revision_coordinator.rs"] mod revision_coordinator; mod revision_status; @@ -3212,8 +3212,10 @@ fn phase1_random_id() -> Result<[u8; 16], Phase1DraftError> { #[cfg(test)] mod tests { + mod coordinate_clock_tests; mod coordinate_effect_support; mod coordinate_effect_tests; + mod coordinate_ordering_tests; mod coordinate_recovery_tests; mod coordinate_support; mod coordinate_tests; diff --git a/core/crates/tera_core/src/runtime/product_surface/outbox/advance.rs b/core/crates/tera_core/src/runtime/product_surface/outbox/advance.rs @@ -32,6 +32,17 @@ impl TeraRuntime { .await } + #[cfg(test)] + pub(super) async fn advance_owned_push_request_with_clock( + &self, + request: PushRequest, + head: &AuthoredDraft, + clock: impl Fn() -> Result<u64, Phase1DraftError> + Send + Sync, + ) -> Result<(), Phase1DraftError> { + self.advance_push_request_inner(request, None, Some(head), clock) + .await + } + pub(in crate::runtime::product_surface) async fn advance_push_request_selected( &self, request: PushRequest, @@ -73,7 +84,8 @@ impl TeraRuntime { .map_err(|_| Phase1DraftError::Operation)?; return Ok(()); } - if !self.publication_retry_at(&status, clock()?)?.may_start() { + let now = clock()?; + if !self.publication_retry_at(&status, now)?.may_start() { return Ok(()); } @@ -93,6 +105,10 @@ impl TeraRuntime { Some(head) => self.admit_coordinate(head).await?, None => None, }; + if let Some(intent) = &intent { + self.require_coordinate_current_at(intent, now / 1_000) + .await?; + } if matches!( status.artifact().signing_state(), @@ -114,7 +130,8 @@ impl TeraRuntime { ) { if let Some(intent) = &intent { - self.require_coordinate_current(intent).await?; + self.require_coordinate_current_at(intent, clock()? / 1_000) + .await?; } sync.admit_signed(operation_id) .await @@ -133,7 +150,8 @@ impl TeraRuntime { ) { if let Some(intent) = &intent { - self.require_coordinate_current(intent).await?; + self.require_coordinate_current_at(intent, clock()? / 1_000) + .await?; } match selected { Some(targets) => sync.deliver_push_selected(operation_id, targets).await, diff --git a/core/crates/tera_core/src/runtime/product_surface/outbox/coordinate.rs b/core/crates/tera_core/src/runtime/product_surface/outbox/coordinate.rs @@ -1,11 +1,11 @@ //! Legacy capture adapter for the shared application coordinate authority. use super::*; -use crate::runtime::product_surface::coordinate::CoordinateIntent; +use crate::runtime::product_surface::coordinate::CoordinatePlan; -pub(in crate::runtime::product_surface) fn coordinate_intent( +pub(in crate::runtime::product_surface) fn coordinate_plan( draft: &AuthoredDraft, -) -> Result<Option<CoordinateIntent>, Phase1DraftError> { +) -> Result<Option<CoordinatePlan>, Phase1DraftError> { let payload = Phase1DraftPayload::decode(draft)?; let plan = PlanWireV1::from_json(&payload.plan_wire_json).map_err(|_| Phase1DraftError::Corrupt)?; @@ -19,7 +19,7 @@ pub(in crate::runtime::product_surface) fn coordinate_intent( .map_err(|_| Phase1DraftError::Corrupt) }) .transpose()?; - CoordinateIntent::from_plan(draft, plan.plan(), prior) + CoordinatePlan::from_plan(draft, plan.plan(), prior) } impl TeraRuntime { @@ -27,7 +27,7 @@ impl TeraRuntime { &self, head: &AuthoredDraft, ) -> Result<bool, Phase1DraftError> { - coordinate_intent(head)?.ok_or(Phase1DraftError::Corrupt)?; + coordinate_plan(head)?.ok_or(Phase1DraftError::Corrupt)?; if head.stage() == AuthoredDraftStage::Cancelled { return Ok(true); } diff --git a/core/crates/tera_core/src/runtime/product_surface/outbox/revision_preparation.rs b/core/crates/tera_core/src/runtime/product_surface/outbox/revision_preparation.rs @@ -10,6 +10,20 @@ impl TeraRuntime { request_id: [u8; 16], intent: Phase1ReviseIntent, ) -> Result<Phase1RevisionStatus, Phase1DraftError> { + Box::pin(self.prepare_revision_intent_with_clock( + request_id, + intent, + phase1_operation_now_unix_ms, + )) + .await + } + + pub(super) async fn prepare_revision_intent_with_clock( + &self, + request_id: [u8; 16], + intent: Phase1ReviseIntent, + clock: impl Fn() -> Result<u64, Phase1DraftError> + Send + Sync, + ) -> Result<Phase1RevisionStatus, Phase1DraftError> { let _command = self.lifecycle.enter()?; let author = self.draft_author()?; if request_id == [0; 16] || intent.target.author_public_key != hex::encode(author) { @@ -26,7 +40,7 @@ impl TeraRuntime { .map_err(|_| Phase1DraftError::Storage)?; let now = match &original { Some(original) => original.created_at_unix_ms(), - None => phase1_operation_now_unix_ms()?, + None => clock()?, }; let plan = intent .command diff --git a/core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_clock_tests.rs b/core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_clock_tests.rs @@ -0,0 +1,74 @@ +use super::{coordinate_effect_support as effects, coordinate_support::*, *}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::time::Duration; + +const AT: u64 = 1_750_000_000; +const DEADLINE: u64 = 2_000_000_000_000; + +#[tokio::test] +async fn coordinate_clock_changes_at_effect_boundaries_preserve_one_preimage_and_saved_deadline() { + for (rollback, before_sign) in [(true, false), (true, true), (false, false), (false, true)] { + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let relay = format!("ws://{}", listener.local_addr().unwrap()); + let signer = effects::PausedSigner::new(); + signer.pause.store(false, Ordering::SeqCst); + let runtime = effects::runtime(signer.clone(), &relay); + let saved = saved(&runtime, [211; 16], "clock:effects").await; + let policy = Phase1QueuePolicy::new( + vec![relay], + Phase1RelaySatisfaction::AllAccepted, + DEADLINE, + Phase1CancellationPolicy::LocalCooperative, + ) + .unwrap(); + let queued = runtime + .phase1_queue_draft([211; 16], 1, policy, AT * 1_000 + 1) + .await + .unwrap(); + assert_eq!( + queued.draft().created_at_unix_ms(), + saved.draft().created_at_unix_ms() + ); + let before = queued.draft().clone(); + let calls = AtomicUsize::new(0); + let result = runtime + .advance_owned_push_request_with_clock(push_request(&before).unwrap(), &before, || { + let call = calls.fetch_add(1, Ordering::SeqCst); + Ok(if call == 0 && !before_sign { + AT * 1_000 + 1 + } else if rollback { + (AT - 1) * 1_000 + } else { + DEADLINE + 1_000_000 + }) + }) + .await; + if rollback { + assert_eq!(result.unwrap_err(), Phase1DraftError::RevisionConflict); + } else { + result.unwrap(); + } + let after = runtime.phase1_draft_status([211; 16]).await.unwrap(); + assert_eq!(after.draft(), &before); + let push = after.push().unwrap(); + assert_eq!(push.delivery_plan().intent().deadline_unix_ms(), DEADLINE); + assert!(push.delivery_plan().attempts().is_empty()); + assert_eq!( + signer.calls.load(Ordering::SeqCst), + usize::from(!before_sign) + ); + assert_eq!( + push.artifact().admission_state().is_admitted(), + !rollback && !before_sign + ); + match push.artifact().signed() { + Some(signed) => assert_eq!(signed.event().envelope().created_at_u64(), AT), + None => assert!(before_sign), + } + assert!( + tokio::time::timeout(Duration::from_millis(50), listener.accept()) + .await + .is_err() + ); + } +} diff --git a/core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_ordering_tests.rs b/core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_ordering_tests.rs @@ -0,0 +1,256 @@ +use super::{coordinate_support::*, *}; +use crate::runtime::product_surface::coordinate::{intent_from_draft, plan_from_draft}; +use radroots_event::SignedEvent; + +const AT: u64 = 1_750_000_000; + +fn equal_second_intent(prior: &SignedEvent, wins: bool) -> Phase1ReviseIntent { + (0..128) + .map(|index| intent(prior, &format!("Equal-second market {index}"))) + .find(|value| { + let plan = value.command.authored_plan(AT, AUTHOR).unwrap(); + (*plan.expected_event_id() < *prior.id()) == wins + }) + .expect("deterministic fixture has both canonical ID orders") +} + +#[tokio::test] +async fn coordinate_equal_second_uses_shared_order_without_rewriting_capture_or_broad_delete() { + for wins in [false, true] { + let runtime = signing_runtime(); + let prior = signed_head(SECRET, 31_923, "equal:second", AT); + retain(&runtime, prior.clone()).await; + let captured = equal_second_intent(&prior, wins); + let first = runtime + .prepare_revision_intent_with_clock([201; 16], captured.clone(), || Ok(AT * 1_000)) + .await; + assert_eq!(first.is_ok(), wins); + if let Err(error) = first { + assert_eq!(error, Phase1DraftError::RevisionConflict); + } + let replay = runtime + .prepare_revision_intent_with_clock([201; 16], captured, || Ok((AT + 100) * 1_000)) + .await + .unwrap(); + let draft = replay.replacement(); + assert_eq!(draft.coordinate_writable(), wins); + assert_eq!( + plan_from_draft(draft.draft()).unwrap().unwrap().created_at, + AT + ); + assert!(replay.retraction().is_none()); + if wins { + let queued = queue(&runtime, draft).await.unwrap(); + let signed = runtime + .phase1_sign_queued_draft( + *queued.draft().draft_id().as_bytes(), + queued.draft().revision().get(), + ) + .await + .unwrap(); + let event = signed.push().unwrap().artifact().signed().unwrap().event(); + assert_eq!(event.envelope().created_at_u64(), AT); + assert!(event.id() < prior.id()); + assert_eq!(event.envelope().kind_u32(), 31_923); + } else { + assert_eq!( + queue(&runtime, draft).await.unwrap_err(), + Phase1DraftError::RevisionConflict + ); + assert!(draft.push().is_none()); + } + assert_eq!(runtime.phase1_draft_heads(100).await.unwrap().len(), 1); + } +} + +#[tokio::test] +async fn coordinate_clock_rollback_and_older_capture_hold_without_changing_the_saved_event() { + let runtime = runtime(); + let prior = signed_head(SECRET, 31_923, "rollback:clock", AT); + retain(&runtime, prior.clone()).await; + let captured = intent(&prior, "Older clock"); + assert_eq!( + runtime + .prepare_revision_intent_with_clock( + [202; 16], + captured.clone(), + || Ok((AT - 1) * 1_000) + ) + .await + .unwrap_err(), + Phase1DraftError::RevisionConflict + ); + let held = runtime + .prepare_revision_intent_with_clock([202; 16], captured, || Ok((AT + 10_000) * 1_000)) + .await + .unwrap(); + let draft = held.replacement().draft(); + let capture = plan_from_draft(draft).unwrap().unwrap(); + assert_eq!(capture.created_at, AT - 1); + assert!(!held.can_resume()); + assert!( + !runtime + .coordinate_known_winner_matches_at(&capture.intent, AT + 10_000) + .await + .unwrap() + ); + assert!(held.retraction().is_none()); + + let fresh = saved(&runtime, [203; 16], "rollback:new").await; + let original = fresh.draft().clone(); + let capture = intent_from_draft(&original).unwrap().unwrap(); + assert!( + !runtime + .coordinate_known_winner_matches_at(&capture, AT - 1) + .await + .unwrap() + ); + assert!( + runtime + .coordinate_known_winner_matches_at(&capture, AT) + .await + .unwrap() + ); + assert!( + runtime + .coordinate_known_winner_matches_at(&capture, AT + 1_000_000) + .await + .unwrap() + ); + assert_eq!( + runtime + .phase1_draft_status([203; 16]) + .await + .unwrap() + .draft(), + &original + ); +} + +#[tokio::test] +async fn coordinate_received_equal_second_winner_reconciles_without_reviving_the_old_capture() { + let runtime = signing_runtime(); + let prior = signed_head(SECRET, 31_923, "received:winner", AT); + retain(&runtime, prior.clone()).await; + let request = equal_second_intent(&prior, true); + let prepared = runtime + .prepare_revision_intent_with_clock([204; 16], request.clone(), || Ok(AT * 1_000)) + .await + .unwrap(); + let queued = queue(&runtime, prepared.replacement()).await.unwrap(); + let id = *queued.draft().draft_id().as_bytes(); + let signed = runtime + .phase1_sign_queued_draft(id, queued.draft().revision().get()) + .await + .unwrap(); + let artifact = signed.push().unwrap().artifact().signed().unwrap().clone(); + retain(&runtime, artifact.event().clone()).await; + let intent = intent_from_draft(signed.draft()).unwrap().unwrap(); + assert!(runtime.coordinate_is_current(&intent).await.unwrap()); + let remote = (0..128) + .map(|index| { + signed_head_content( + SECRET, + 31_923, + "received:winner", + AT, + &format!("Remote winner {index}"), + ) + }) + .find(|event| event.id() < artifact.event().id()) + .expect("bounded deterministic lower ID fixture"); + retain(&runtime, remote.clone()).await; + let replay = runtime + .prepare_revision_intent_with_clock([204; 16], request, || Ok((AT + 500) * 1_000)) + .await + .unwrap(); + assert_eq!(replay.replacement().draft(), signed.draft()); + assert!(!replay.can_resume()); + assert!(!runtime.coordinate_is_current(&intent).await.unwrap()); + assert_eq!( + replay + .replacement() + .push() + .unwrap() + .artifact() + .signed() + .unwrap(), + &artifact + ); + assert_eq!( + runtime + .phase1_sign_queued_draft(id, signed.draft().revision().get()) + .await + .unwrap_err(), + Phase1DraftError::RevisionConflict + ); + let snapshot = + radroots_storage::event::EventStore::rebuild_visibility(runtime.client.storage().unwrap()) + .await + .unwrap(); + assert_eq!(snapshot.current_heads().len(), 1); + assert_eq!(snapshot.current_heads()[0].event_id, *remote.id()); + assert!(replay.retraction().is_none()); +} + +#[tokio::test] +async fn coordinate_event_timestamp_policy_never_rewrites_civil_calendar_dates() { + let runtime = runtime(); + let identifier = "civil:date"; + let prior = signed_head(SECRET, 31_922, identifier, AT - 10); + retain(&runtime, prior.clone()).await; + let event = AuthoredCalendarDateEvent::new( + identifier, + "All-day market", + CalendarDate::parse("2026-12-31").unwrap(), + ) + .unwrap(); + let target = Phase1RevisionTarget::from_source( + AddCommandType::CreateEvent, + CardId::derive( + TodayCardType::Event, + &CardSourceIdentity::address(31_922, AUTHOR, identifier).unwrap(), + ), + prior.id().to_hex(), + Some(format!("31922:{AUTHOR}:{identifier}")), + AUTHOR, + ) + .unwrap(); + let form = Phase1DraftFormSnapshot { + command_type: AddCommandType::CreateEvent, + content: String::new(), + identifier: Some(identifier.into()), + title: Some("All-day market".into()), + event_timing: Some(Phase1DraftEventTiming::AllDay), + event_start_date: Some("2026-12-31".into()), + ..update_form() + }; + let request = Phase1ReviseIntent::new( + target, + Phase1AddCommand::CreateEvent(CreateEvent::date(event)), + vec![], + form.clone(), + ) + .unwrap(); + let prepared = runtime + .prepare_revision_intent_with_clock([205; 16], request.clone(), || Ok(AT * 1_000)) + .await + .unwrap(); + let capture = plan_from_draft(prepared.replacement().draft()) + .unwrap() + .unwrap(); + assert!( + !runtime + .coordinate_known_winner_matches_at(&capture.intent, AT - 1) + .await + .unwrap() + ); + let replay = runtime + .prepare_revision_intent_with_clock([205; 16], request, || Ok((AT + 1_000_000) * 1_000)) + .await + .unwrap(); + assert_eq!(replay.replacement().draft(), prepared.replacement().draft()); + assert_eq!(replay.replacement().form(), Some(&form)); + assert_eq!(capture.created_at, AT); + assert!(replay.retraction().is_none()); +} diff --git a/core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_support.rs b/core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_support.rs @@ -9,11 +9,26 @@ use radroots_transport::{ }; pub(super) fn signed_head(secret: &str, kind: u32, identifier: &str, at: u64) -> SignedEvent { + signed_head_content( + secret, + kind, + identifier, + at, + "Not a renderable calendar or food record", + ) +} + +pub(super) fn signed_head_content( + secret: &str, + kind: u32, + identifier: &str, + at: u64, + content: &str, +) -> SignedEvent { let keys = nostr::Keys::parse(secret).unwrap(); let author = keys.public_key().to_string(); // Deliberately unsupported product content still has protocol head authority. let tags = vec![vec!["d".into(), identifier.into()]]; - let content = "Not a renderable calendar or food record"; let id = radroots_event::wire::compute_canonical_nip01_event_id(&author, at, kind, &tags, content) .unwrap(); diff --git a/core/crates/tera_core/src/runtime/product_surface/submission.rs b/core/crates/tera_core/src/runtime/product_surface/submission.rs @@ -4,7 +4,7 @@ mod capture; mod commit; mod coordinate; -pub(super) use coordinate::coordinate_intent; +pub(super) use coordinate::coordinate_plan; pub(crate) mod configuration; #[cfg(test)] mod configuration_effect_tests; diff --git a/core/crates/tera_core/src/runtime/product_surface/submission/coordinate.rs b/core/crates/tera_core/src/runtime/product_surface/submission/coordinate.rs @@ -3,14 +3,14 @@ use super::intent::IntentPayload; use crate::{ TeraRuntime, - runtime::product_surface::{Phase1DraftError as E, coordinate::CoordinateIntent}, + runtime::product_surface::{Phase1DraftError as E, coordinate::CoordinatePlan}, }; use radroots_storage::authored_draft::AuthoredDraft; -pub(in crate::runtime::product_surface) fn coordinate_intent( +pub(in crate::runtime::product_surface) fn coordinate_plan( head: &AuthoredDraft, -) -> Result<Option<CoordinateIntent>, E> { - IntentPayload::coordinate_intent(head).map_err(|_| E::Corrupt) +) -> Result<Option<CoordinatePlan>, E> { + IntentPayload::coordinate_plan(head).map_err(|_| E::Corrupt) } impl TeraRuntime { diff --git a/core/crates/tera_core/src/runtime/product_surface/submission/intent.rs b/core/crates/tera_core/src/runtime/product_surface/submission/intent.rs @@ -92,9 +92,9 @@ pub(super) fn commit_id(request: &SubmissionReservationRequest) -> AtomicCommitI } impl IntentPayload { - pub(super) fn coordinate_intent( + pub(super) fn coordinate_plan( head: &AuthoredDraft, - ) -> Result<Option<crate::runtime::product_surface::coordinate::CoordinateIntent>, E> { + ) -> Result<Option<crate::runtime::product_surface::coordinate::CoordinatePlan>, E> { let value: Self = serde_json::from_slice(head.payload()).map_err(|_| E::CorruptRecord)?; if head.payload_schema() != SUBMISSION_INTENT_PAYLOAD_SCHEMA || value.schema_version != SUBMISSION_INTENT_SCHEMA_VERSION @@ -104,7 +104,7 @@ impl IntentPayload { return Err(E::CorruptRecord); } let wire = PlanWireV1::from_json(&value.plan_wire_json).map_err(|_| E::CorruptRecord)?; - crate::runtime::product_surface::coordinate::CoordinateIntent::from_plan( + crate::runtime::product_surface::coordinate::CoordinatePlan::from_plan( head, wire.plan(), None, diff --git a/release/provenance.json b/release/provenance.json @@ -2,7 +2,7 @@ "artifacts": { "app_api_sha256": "020924097c0d7efc33128cb8fd3d3b2026d95f57c44da71880e585aff80f070b", "ffi_api_sha256": "81a943ef98405676b4f65932a4a8d4bdb4011923f65fa2e3496fcb58e1d68be8", - "ffi_provenance_sha256": "15b0370f4f0ac81fbe2247a295b649fea1fbb017843aee65f54246bb3dfce5bb", + "ffi_provenance_sha256": "c184a6b1dd38541e27f792fbd41f90690e6c63c89165951a4526c06b60cf5800", "info_plist_sha256": "15ef08b1cdd1096cfb9eeaf5be5bf8f814807a7ca9350bbbb47860fa72ec13ef", "privacy_manifest_sha256": "a331d51864743ebe4e00dd22360b4a538b6b3ac26a6b3eb54094e60a36959a12", "sbom_sha256": "bfc57ed1f259dad8dcff7ec061b0889942a27bfdc9f9f01ebb631e25b715267b", @@ -22,7 +22,7 @@ "lib_revision": "e6f296604363e5293d6abc6e18bf5eaea2177b9f", "source_date_epoch": 1787871027, "swift_package_lock_sha256": "a7e31d77d31ecbc1e0e5a78f2681e3fb0c6f367af0edf8b4ceef0af973a78681", - "tera_ffi_source_tree": "f69196d4ddd33fca9b74ee5559ee8265fdfd8c25", + "tera_ffi_source_tree": "681e757519909bf8e177ed3238398988c9d0a89d", "xcode_package_lock_sha256": "d98a614a38dce7b55c2925dee51c2d4bf8dcfc4e31779d0c5fe8561c45d6e3ba" }, "version": "0.1.0-alpha" diff --git a/test-fixtures/legacy-identifiers.v1.json b/test-fixtures/legacy-identifiers.v1.json @@ -4875,6 +4875,10 @@ "count": 2 }, { + "path": "core/crates/tera_core/src/runtime/product_surface/coordinate/ordering.rs", + "count": 1 + }, + { "path": "core/crates/tera_core/src/runtime/product_surface/identity.rs", "count": 1 }, @@ -4887,6 +4891,10 @@ "count": 1 }, { + "path": "core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_ordering_tests.rs", + "count": 1 + }, + { "path": "core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_support.rs", "count": 2 }, @@ -5803,6 +5811,10 @@ "count": 1 }, { + "path": "core/crates/tera_core/src/runtime/product_surface/coordinate/ordering.rs", + "count": 1 + }, + { "path": "core/crates/tera_core/src/runtime/product_surface/coordinate/record.rs", "count": 1 }, @@ -5827,6 +5839,10 @@ "count": 1 }, { + "path": "core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_ordering_tests.rs", + "count": 1 + }, + { "path": "core/crates/tera_core/src/runtime/product_surface/outbox/tests/coordinate_recovery_tests.rs", "count": 1 },