From 0a12075acb711d7f32531197e3a868e21714e541 Mon Sep 17 00:00:00 2001 From: Quanyi Ma Date: Thu, 12 Feb 2026 01:46:47 +0800 Subject: [PATCH 1/5] Update objects of agent flow Signed-off-by: Quanyi Ma --- BUCK | 8 +- Cargo.lock | 34 +- Cargo.toml | 10 +- buckal.snap | 84 +- src/errors.rs | 36 + src/internal/object/commit.rs | 2 +- src/internal/object/context.rs | 80 +- src/internal/object/decision.rs | 40 +- src/internal/object/evidence.rs | 38 +- src/internal/object/header.rs | 637 ---------------- src/internal/object/mod.rs | 1 - src/internal/object/patchset.rs | 42 +- src/internal/object/plan.rs | 40 +- src/internal/object/provenance.rs | 40 +- src/internal/object/run.rs | 40 +- src/internal/object/task.rs | 44 +- src/internal/object/tool.rs | 38 +- src/internal/object/tree.rs | 2 +- src/internal/object/types.rs | 720 +++++++++++++++++- src/internal/pack/decode.rs | 37 +- .../android_system_properties/0.1.5/BUCK | 2 +- third-party/rust/crates/axum-core/0.5.5/BUCK | 2 +- third-party/rust/crates/axum/0.8.8/BUCK | 2 +- .../rust/crates/bytes/{1.11.0 => 1.11.1}/BUCK | 6 +- third-party/rust/crates/cc/1.2.49/BUCK | 4 +- third-party/rust/crates/clang-sys/1.8.1/BUCK | 2 +- .../rust/crates/cpufeatures/0.2.17/BUCK | 2 +- third-party/rust/crates/errno/0.3.14/BUCK | 4 +- .../rust/crates/flate2/{1.1.8 => 1.1.9}/BUCK | 6 +- third-party/rust/crates/getrandom/0.2.16/BUCK | 4 +- third-party/rust/crates/getrandom/0.3.4/BUCK | 4 +- .../rust/crates/http-body-util/0.1.3/BUCK | 2 +- third-party/rust/crates/http-body/1.0.1/BUCK | 2 +- third-party/rust/crates/http/1.4.0/BUCK | 2 +- .../rust/crates/hyper-util/0.1.19/BUCK | 2 +- third-party/rust/crates/hyper/1.8.1/BUCK | 2 +- third-party/rust/crates/jobserver/0.1.34/BUCK | 4 +- .../crates/libc/{0.2.180 => 0.2.181}/BUCK | 8 +- third-party/rust/crates/libredox/0.1.10/BUCK | 2 +- third-party/rust/crates/mio/1.1.1/BUCK | 4 +- third-party/rust/crates/num_cpus/1.17.0/BUCK | 4 +- .../rust/crates/parking_lot_core/0.9.12/BUCK | 4 +- third-party/rust/crates/rand/0.8.5/BUCK | 4 +- .../rust/crates/rand_chacha/0.10.0/BUCK | 36 + .../rust/crates/rand_chacha/0.9.0/BUCK | 5 +- third-party/rust/crates/rand_core/0.10.0/BUCK | 28 + third-party/rust/crates/ring/0.17.14/BUCK | 2 +- third-party/rust/crates/rkyv/0.7.45/BUCK | 4 +- .../rust/crates/rust_decimal/1.39.0/BUCK | 2 +- third-party/rust/crates/rustix/1.1.3/BUCK | 4 +- third-party/rust/crates/sea-orm/1.1.19/BUCK | 2 +- .../rust/crates/sea-query-binder/0.7.0/BUCK | 2 +- third-party/rust/crates/sea-query/0.32.7/BUCK | 2 +- .../crates/signal-hook-registry/1.4.7/BUCK | 2 +- third-party/rust/crates/socket2/0.6.1/BUCK | 4 +- third-party/rust/crates/sqlx-core/0.8.6/BUCK | 4 +- third-party/rust/crates/sqlx-mysql/0.8.6/BUCK | 4 +- .../rust/crates/sqlx-postgres/0.8.6/BUCK | 2 +- .../rust/crates/sqlx-sqlite/0.8.6/BUCK | 2 +- .../rust/crates/tokio-util/0.7.18/BUCK | 2 +- third-party/rust/crates/tokio/1.49.0/BUCK | 4 +- .../rust/crates/uuid/{1.19.0 => 1.20.0}/BUCK | 6 +- 62 files changed, 1301 insertions(+), 867 deletions(-) delete mode 100644 src/internal/object/header.rs rename third-party/rust/crates/bytes/{1.11.0 => 1.11.1}/BUCK (77%) rename third-party/rust/crates/flate2/{1.1.8 => 1.1.9}/BUCK (84%) rename third-party/rust/crates/libc/{0.2.180 => 0.2.181}/BUCK (87%) create mode 100644 third-party/rust/crates/rand_chacha/0.10.0/BUCK create mode 100644 third-party/rust/crates/rand_core/0.10.0/BUCK rename third-party/rust/crates/uuid/{1.19.0 => 1.20.0}/BUCK (84%) diff --git a/BUCK b/BUCK index de046eb4..17a14b2e 100644 --- a/BUCK +++ b/BUCK @@ -33,18 +33,18 @@ rust_library( "//third-party/rust/crates/bincode/2.0.1:bincode", "//third-party/rust/crates/bstr/1.12.1:bstr", "//third-party/rust/crates/byteorder/1.5.0:byteorder", - "//third-party/rust/crates/bytes/1.11.0:bytes", + "//third-party/rust/crates/bytes/1.11.1:bytes", "//third-party/rust/crates/chrono/0.4.43:chrono", "//third-party/rust/crates/colored/3.1.1:colored", "//third-party/rust/crates/crc32fast/1.5.0:crc32fast", "//third-party/rust/crates/dashmap/6.1.0:dashmap", "//third-party/rust/crates/diffs/0.5.1:diffs", "//third-party/rust/crates/encoding_rs/0.8.35:encoding_rs", - "//third-party/rust/crates/flate2/1.1.8:flate2", + "//third-party/rust/crates/flate2/1.1.9:flate2", "//third-party/rust/crates/futures-util/0.3.31:futures-util", "//third-party/rust/crates/futures/0.3.31:futures", "//third-party/rust/crates/hex/0.4.3:hex", - "//third-party/rust/crates/libc/0.2.180:libc", + "//third-party/rust/crates/libc/0.2.181:libc", "//third-party/rust/crates/lru-mem/0.3.0:lru-mem", "//third-party/rust/crates/memchr/2.7.6:memchr", "//third-party/rust/crates/natord/1.0.9:natord", @@ -65,7 +65,7 @@ rust_library( "//third-party/rust/crates/tokio/1.49.0:tokio", "//third-party/rust/crates/tracing-subscriber/0.3.22:tracing-subscriber", "//third-party/rust/crates/tracing/0.1.44:tracing", - "//third-party/rust/crates/uuid/1.19.0:uuid", + "//third-party/rust/crates/uuid/1.20.0:uuid", "//third-party/rust/crates/zstd-sys/2.0.16+zstd.1.5.7:zstd-sys", ], ) diff --git a/Cargo.lock b/Cargo.lock index d22ea71b..69f2c391 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -350,9 +350,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cc" @@ -691,9 +691,9 @@ checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "flate2" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", "libz-sys", @@ -894,7 +894,7 @@ dependencies = [ "path-absolutize", "quickcheck", "rand 0.9.2", - "rand_chacha 0.9.0", + "rand_chacha 0.10.0", "rayon", "ring", "sea-orm", @@ -1292,9 +1292,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.180" +version = "0.2.181" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +checksum = "459427e2af2b9c839b132acb702a1c654d95e10f8c326bfc2ad11310e458b1c5" [[package]] name = "libloading" @@ -1852,6 +1852,16 @@ dependencies = [ "rand_core 0.9.3", ] +[[package]] +name = "rand_chacha" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e6af7f3e25ded52c41df4e0b1af2d047e45896c2f3281792ed68a1c243daedb" +dependencies = [ + "ppv-lite86", + "rand_core 0.10.0", +] + [[package]] name = "rand_core" version = "0.6.4" @@ -1870,6 +1880,12 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "rand_core" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" + [[package]] name = "rayon" version = "1.11.0" @@ -2977,9 +2993,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" +checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f" dependencies = [ "getrandom 0.3.4", "js-sys", diff --git a/Cargo.toml b/Cargo.toml index 59a455b2..d532575b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,13 +22,13 @@ dashmap = "6.1.0" lru-mem = "0.3.0" byteorder = "1.5.0" futures-util = "0.3.31" -bytes = "1.11.0" +bytes = "1.11.1" memchr = "2.7.6" encoding_rs = "0.8.35" rayon = "1.11.0" ahash = "0.8.12" diffs = "0.5.1" -libc = "0.2.180" +libc = "0.2.181" async-trait = "0.1.89" futures = "0.3.31" tokio-stream = "0.1.18" @@ -40,10 +40,10 @@ sha2 = "0.10.9" crc32fast = "1.4" zstd-sys = { version = "2.0.16+zstd.1.5.7", features = ["experimental"] } sea-orm = { version = "1.1.17", features = ["sqlx-sqlite"] } -flate2 = { version = "1.1.8", features = ["zlib"] } +flate2 = { version = "1.1.9", features = ["zlib"] } serde = { version = "1.0.228", features = ["derive"] } chrono = { version = "0.4.43", features = ["serde"] } -uuid = { version = "1.19.0", features = ["serde", "v4", "v7"] } +uuid = { version = "1.20.0", features = ["serde", "v4", "v7"] } tokio = { version = "1.49.0", features = ["fs", "io-util"] } bincode = { version = "2.0.1", features = ["serde"] } axum = { version = "0.8.8", features = ["macros", "json"] } @@ -55,7 +55,7 @@ tokio = { version = "1.49.0", features = ["full"] } tokio-util = { version = "0.7.18", features = ["io"] } quickcheck = "1.0.3" rand = "0.9.2" -rand_chacha = "0.9.0" +rand_chacha = "0.10.0" [features] default = ["diff_mydrs"] diff --git a/buckal.snap b/buckal.snap index 50003419..96858913 100644 --- a/buckal.snap +++ b/buckal.snap @@ -3,14 +3,14 @@ version = 2 [fingerprints] -"path+file://($WORKSPACE)#0.4.1" = "18cbbeebb64505391cc56951ecd06fb5f741ee8f8404ec87a36c248deb142234" +"path+file://($WORKSPACE)#0.4.1" = "cef694fc435342e1e60688775c50184eb2a271ae45e0d282ff392744445a0179" "registry+https://github.com/rust-lang/crates.io-index#adler2@2.0.1" = "7081ce7693ca45bff0e855374e6b7f386805bba1de9f7c288b1c706b99abef7f" "registry+https://github.com/rust-lang/crates.io-index#ahash@0.7.8" = "263bbc26dec8ade458658cf9dae3ef8fc606e85f04c7b47abb4f9255319689ac" "registry+https://github.com/rust-lang/crates.io-index#ahash@0.8.12" = "a383722a12d8343396b1965bfe77f1cebeafb12ccc571747b2b54614e3e1a1ea" "registry+https://github.com/rust-lang/crates.io-index#aho-corasick@1.1.4" = "19271515eef34f896bf45f2ea7790d1cc8ab40987de83c6165ab190857c97fa0" "registry+https://github.com/rust-lang/crates.io-index#aliasable@0.1.3" = "380164ac2bd1024f63312c57faa8fc77464c767b9756cc6ddaf242ca759e86c6" "registry+https://github.com/rust-lang/crates.io-index#allocator-api2@0.2.21" = "75e47cb469e23de4eb2a2d5f7e885087cb963b6d1b2483772130c7ab8518358c" -"registry+https://github.com/rust-lang/crates.io-index#android_system_properties@0.1.5" = "e25e3420dfb934f18ac060548a716b705d013dd47280bb5686bfdf2319f896ed" +"registry+https://github.com/rust-lang/crates.io-index#android_system_properties@0.1.5" = "b8006a15d83f9bc5f6cc5f9c8c0d3e6e3400e43c91c41b7f4feda54d4de461b6" "registry+https://github.com/rust-lang/crates.io-index#arrayvec@0.7.6" = "70ab2871e8658bba895265e101e4b7f303b04214b35587a52bbe019bc2cdba5d" "registry+https://github.com/rust-lang/crates.io-index#async-stream-impl@0.3.6" = "b5abcc786a5e54062153064d17f5f139a7e45b333b4d85badadfeba9e3038e02" "registry+https://github.com/rust-lang/crates.io-index#async-stream@0.3.6" = "7f44d1811be169843bff9cef1b21ace70435686b14caec32fc2ee2cecc97af87" @@ -18,9 +18,9 @@ version = 2 "registry+https://github.com/rust-lang/crates.io-index#atoi@2.0.0" = "a7d39824d7fdaa0f44195f5325522028644a5c56013d5ae6d0a88336decc70ea" "registry+https://github.com/rust-lang/crates.io-index#atomic-waker@1.1.2" = "042a06271ce13a758d633645e3b5e24d696eb7616240c4ef549138587b20b2f4" "registry+https://github.com/rust-lang/crates.io-index#autocfg@1.5.0" = "bb77c9fe6700bdb8368b3560d69b131e91fda567a5bf1d71453b1f1ab579e551" -"registry+https://github.com/rust-lang/crates.io-index#axum-core@0.5.5" = "f6ebee0212c333f10d6ef84beaa26c024ff87386e877cf9522b154cab54b2800" +"registry+https://github.com/rust-lang/crates.io-index#axum-core@0.5.5" = "8d8eb81624b7663a1db267b6614cd6342a360daa937625a5a241194e24fd02e9" "registry+https://github.com/rust-lang/crates.io-index#axum-macros@0.5.0" = "3e5a942bb67e6cb70c2845c0cea03d54ada82430ba49ffd2ee53e1e429b98ebc" -"registry+https://github.com/rust-lang/crates.io-index#axum@0.8.8" = "ad4f1a6208e512e411b8bb9c4ef4a96fa87b93cf3a6a8b18fbce2e4d4068b7f0" +"registry+https://github.com/rust-lang/crates.io-index#axum@0.8.8" = "e6691577e6e15b5db02985578d5e2f61de52aec0fe8bceebed98522b24cbb936" "registry+https://github.com/rust-lang/crates.io-index#base64@0.22.1" = "515d0850a06d4dec36805d42bfd7cb6c8d287e894b16cf7ded43a3d1cdd98192" "registry+https://github.com/rust-lang/crates.io-index#base64ct@1.8.1" = "866a57dd8efb8c621bdf0d32f62488792eb50f0dcdf9374d1abc7a7a77d48182" "registry+https://github.com/rust-lang/crates.io-index#bigdecimal@0.4.9" = "f88bba28db672a65c61e84f5a91f481f570167555da21311619efea9efe3e4c5" @@ -37,18 +37,18 @@ version = 2 "registry+https://github.com/rust-lang/crates.io-index#bytecheck@0.6.12" = "cc1c82108a15bfd9498f5e283fc52b0aa9ce22f82a010fe98960b5b3ee240e80" "registry+https://github.com/rust-lang/crates.io-index#bytecheck_derive@0.6.12" = "8b0c10e17c673205907d056bd4e31e3d89420ca4235e61c243a8892ebd295e77" "registry+https://github.com/rust-lang/crates.io-index#byteorder@1.5.0" = "759111df9ebe307cd1672921dd3a4f812a3a8fb2332bd830dd203ad528986268" -"registry+https://github.com/rust-lang/crates.io-index#bytes@1.11.0" = "b6a4eaed2f314d979a1b7204a5205ce5bac8430ef3283ad5bf99e6754b872eea" -"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.49" = "801b0fdfb8fc8bca06555b88168db847afa1176c62414c3857fc684bb8968995" +"registry+https://github.com/rust-lang/crates.io-index#bytes@1.11.1" = "328316eb40f355e38e2f3ce3500b54390c55d1b69000bfe3a0e7483d52c0bd9c" +"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.49" = "159d7d3031a5a8f25883782156c77dff3cb3b7a082e1c9150d248310f67eb54c" "registry+https://github.com/rust-lang/crates.io-index#cexpr@0.6.0" = "27a638860942f1f3715f0af5325d7466217185dfbb02c0b72fd09e8b40ec1972" "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4" = "a930db23f176aac70c6eb5dc061658a25fec076e9965158b4bb764efd4a97d69" "registry+https://github.com/rust-lang/crates.io-index#cfg_aliases@0.2.1" = "1b65210b78bbd423b4a7dd205bad98ec274c66d7be10fb4181c781cef66ea94c" "registry+https://github.com/rust-lang/crates.io-index#chrono@0.4.43" = "8093e718c1ac742a8db8d725bdea6945616ddf9594e7227b89de8313d6d408ed" -"registry+https://github.com/rust-lang/crates.io-index#clang-sys@1.8.1" = "102d192c31614b9d17866fb1fa6ee1a504301bc06386806782e77e1708d836a0" +"registry+https://github.com/rust-lang/crates.io-index#clang-sys@1.8.1" = "721eb427a512a1eb1ec3d73b1a4b70a495865cec167a78df1893f282b01447e5" "registry+https://github.com/rust-lang/crates.io-index#colored@3.1.1" = "d68ad0083d079d121ea59cf8e31fb7d5bad7578f7e3eee00ee2ce2c6f84a67bf" "registry+https://github.com/rust-lang/crates.io-index#concurrent-queue@2.5.0" = "2be1dabff5ee3eb435dc7777031228eac0cd3c048870348d87293fc83c197266" "registry+https://github.com/rust-lang/crates.io-index#const-oid@0.9.6" = "af3f26910edd91809444779049a4ed3fae2348a7fa97ce6a9b0e5ab38fee3ab4" "registry+https://github.com/rust-lang/crates.io-index#core-foundation-sys@0.8.7" = "e0f74379b2048b7119489f74c4c13e1d6829e0e7705af435bd55ce8afc4b2279" -"registry+https://github.com/rust-lang/crates.io-index#cpufeatures@0.2.17" = "a2a8ddeb0b4fbb1a2b0146176c70902c6d5cda691e47c7cca56b8f2c6af2f36f" +"registry+https://github.com/rust-lang/crates.io-index#cpufeatures@0.2.17" = "acbc43ee53cc9d4cc62535ad0964fd023e8969906dfc991ab0911e4c71773b7f" "registry+https://github.com/rust-lang/crates.io-index#crc-catalog@2.4.0" = "e93bf72ea668bd2b04c0863ad0c2de336e5456787c2a54f271dbd427624a7ecf" "registry+https://github.com/rust-lang/crates.io-index#crc32fast@1.5.0" = "e14e15bad29205465cb17d99db857a8a8afd931c58660efa8e5656266b189f7a" "registry+https://github.com/rust-lang/crates.io-index#crc@3.4.0" = "079844ca55e905768c6951f24234e117403192fac795bfc94bfb8d7e3a47f3a4" @@ -70,12 +70,12 @@ version = 2 "registry+https://github.com/rust-lang/crates.io-index#encoding_rs@0.8.35" = "4f2ca74488af878419d09ec566f92d1d553843a64a61111d2a1acac529ebe6fe" "registry+https://github.com/rust-lang/crates.io-index#env_logger@0.8.4" = "367ed212a2a10a8ca532ecef8977f1883aff7ea51685f76ca6e570fbf58b03a4" "registry+https://github.com/rust-lang/crates.io-index#equivalent@1.0.2" = "5abab9b0ab1fca4d17ce9d4b524c15d1c194ce6618cada03354a3574af2b0c12" -"registry+https://github.com/rust-lang/crates.io-index#errno@0.3.14" = "5458e4d03e1ea3d4d29e6453230ee4cc8227f4b598a2e430510d682df5e08f52" +"registry+https://github.com/rust-lang/crates.io-index#errno@0.3.14" = "d125e87028518a47d829cb606a9eb657edce9e9e1c01d9c806e392891938f418" "registry+https://github.com/rust-lang/crates.io-index#etcetera@0.8.0" = "1b6049c3e827268bd3585c98f66dca16ba7d70b0f6fa1ffde17fe12be4586cab" "registry+https://github.com/rust-lang/crates.io-index#event-listener@5.4.1" = "669248c1dc862d9851b252130df4f070ff91e61d6d314ee8d806ac0d9e079c70" "registry+https://github.com/rust-lang/crates.io-index#fastrand@2.3.0" = "534c43b2eecb8c0ffd628d3ec179befb4764adb1641a87c65bc261205fc4578a" "registry+https://github.com/rust-lang/crates.io-index#find-msvc-tools@0.1.5" = "a8d581a2164697054d3678b3d84be6b095ae855b01f0a22a465091cee18eeb9f" -"registry+https://github.com/rust-lang/crates.io-index#flate2@1.1.8" = "d25e501456fb68d46a7bcd902bfed1205d920bfd202347fec6190d66cc0e7dfc" +"registry+https://github.com/rust-lang/crates.io-index#flate2@1.1.9" = "8116e08143fd7ed79303058c4405f996e07ff0e0fd8978e8fbd9df12f8bae94b" "registry+https://github.com/rust-lang/crates.io-index#flume@0.11.1" = "c2a32f2719da0c6f2096ae1bc60566a88c96571b6df52bf74c5c3e4e4ef35254" "registry+https://github.com/rust-lang/crates.io-index#foldhash@0.1.5" = "1584183bf9589ad30f7866826d5c2ee5899fd60a6f169ce1b0be7b9973bc35a0" "registry+https://github.com/rust-lang/crates.io-index#form_urlencoded@1.2.2" = "7280a0eda0a4e4666f4a01a1b0b0818c3cb48c440fd8f93a0c930e4e4959e0a6" @@ -91,8 +91,8 @@ version = 2 "registry+https://github.com/rust-lang/crates.io-index#futures-util@0.3.31" = "74aa719140bf3e94ff917b9145cf1e7922a8c5180a1704bb46ece4a505d9686e" "registry+https://github.com/rust-lang/crates.io-index#futures@0.3.31" = "d1c72471b3f0d61c2f913c98af68c7045b916bf0feb344ae995e393653b14305" "registry+https://github.com/rust-lang/crates.io-index#generic-array@0.14.7" = "fda989f4c8ee1455f983d4015a69307138f8a32f3ad2134cb48cf2e190db4d85" -"registry+https://github.com/rust-lang/crates.io-index#getrandom@0.2.16" = "eec26d68e8fffeb09ea2b0181153e4ec827887b6ddc74fd4294815fc608cb89a" -"registry+https://github.com/rust-lang/crates.io-index#getrandom@0.3.4" = "b47fcd0d6963ab30675c50c0c9be3482dbbd18b261f49150e045bfc683b691a4" +"registry+https://github.com/rust-lang/crates.io-index#getrandom@0.2.16" = "c818b094a5a5fec6a5c60765476252c6c3e18660379cded7e6713758a566eb39" +"registry+https://github.com/rust-lang/crates.io-index#getrandom@0.3.4" = "eb05c443fd9b6ca421fdb758a4d72ac84ccf9a6b5d9fc3464a958552ffed4b24" "registry+https://github.com/rust-lang/crates.io-index#glob@0.3.3" = "eb79d7bc94e6f5355badd5a8d40f9f3ed1883ee9e8a811419c949d9e142b9054" "registry+https://github.com/rust-lang/crates.io-index#hashbrown@0.12.3" = "945f02f52c9d9763b6095e9369c2b79236e11d3c3becfdcc3800b031788d115f" "registry+https://github.com/rust-lang/crates.io-index#hashbrown@0.14.5" = "9f36a014235dbc170a3a204875840fe6054a7c91bd03bb60f2f29ad20cc356e3" @@ -106,13 +106,13 @@ version = 2 "registry+https://github.com/rust-lang/crates.io-index#hkdf@0.12.4" = "384b40c15e78669d86a84e95c403f0f14328557b9b4aeca8cf85959c1f8508a3" "registry+https://github.com/rust-lang/crates.io-index#hmac@0.12.1" = "950fba9b1ec23a5ad8b960211fd67fbf0278c4c195ff050386665391272612c6" "registry+https://github.com/rust-lang/crates.io-index#home@0.5.12" = "bbf4a7993e9ab73e7ff762707b69002574f2462323a2d0d11580a9bc09b55603" -"registry+https://github.com/rust-lang/crates.io-index#http-body-util@0.1.3" = "79ade658a06ded1aa3fc91c13c85f6a97799c44bb4ce33069d8bbcd3c45bdbbd" -"registry+https://github.com/rust-lang/crates.io-index#http-body@1.0.1" = "2a6091861187859f995f3a3954b94be72490642a6b95ba1fdfe5920a02ee93b6" -"registry+https://github.com/rust-lang/crates.io-index#http@1.4.0" = "3b70bea00eae26ee261c83ad837dea6d65d21879320def24e8678e5157b3152c" +"registry+https://github.com/rust-lang/crates.io-index#http-body-util@0.1.3" = "2ebfbf55e8b812040b4a4c8f62477a520f510c279b7f73e7151b0a5a35e288a9" +"registry+https://github.com/rust-lang/crates.io-index#http-body@1.0.1" = "1e9300f54cc2c82c9e8b1286e5cecf41c92845d5ef011f2b7c29dce365f490ef" +"registry+https://github.com/rust-lang/crates.io-index#http@1.4.0" = "5d53f90c305c7654a54ab5d0d0df575b28ac31480ea080261912a8bda7b2c9e4" "registry+https://github.com/rust-lang/crates.io-index#httparse@1.10.1" = "0624c4d71f0df1fd253efb876badfa614eec1d948bd439dfc5c33194f6ec9d2d" "registry+https://github.com/rust-lang/crates.io-index#httpdate@1.0.3" = "759393f04c8566387c673eb9353fb28f5a93b96529ada6723a1772c1b55a290c" -"registry+https://github.com/rust-lang/crates.io-index#hyper-util@0.1.19" = "8f8aa76febaabd8bca54af4e9c295ef9663754e99c5e0d350f8170b9cb450c5c" -"registry+https://github.com/rust-lang/crates.io-index#hyper@1.8.1" = "e5cb3f15f59b6780f080a1abe0f473a7d560c19d4b197e473decbbf08b249a8e" +"registry+https://github.com/rust-lang/crates.io-index#hyper-util@0.1.19" = "e1cf28934ba549e20b8243f27c57229b20043887568bdf1225cdd25e28a0140f" +"registry+https://github.com/rust-lang/crates.io-index#hyper@1.8.1" = "d8f1de9e689ee10e40ae56195f3ae3ad47de57b2a2b650e2108b28f2cba7f2d1" "registry+https://github.com/rust-lang/crates.io-index#iana-time-zone-haiku@0.1.2" = "5a8aae5f186e7f8c33936a3c528b677b11b3879ae5bce67242e71558f88854a3" "registry+https://github.com/rust-lang/crates.io-index#iana-time-zone@0.1.64" = "d0a36f018c41979b3a8588b09027a920e273fd8714e7192580ae1c9a9f824394" "registry+https://github.com/rust-lang/crates.io-index#icu_collections@2.1.1" = "6626fabc272c09f2f46f40889981488cd75047d9e50d6152cb1c9daa4b97ea4a" @@ -128,13 +128,13 @@ version = 2 "registry+https://github.com/rust-lang/crates.io-index#inherent@1.0.13" = "02bb0185491d7306cd557451b881cb9b99dabbb20b8dd5ccdfa86aae361e0d15" "registry+https://github.com/rust-lang/crates.io-index#itertools@0.13.0" = "e062ccfedbb80efdc226a04d2aa8b036611b6d32bc33fab50b7b4afefcf1107b" "registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.15" = "069c02c3cb8ee706408a014038a3d36c0b097aeda593bc7bcab1b7d88b23334a" -"registry+https://github.com/rust-lang/crates.io-index#jobserver@0.1.34" = "dc1e1a8f4a532a25d7ca9db2697b343dba35b0c0a8c11dad9bd50ed90483a870" +"registry+https://github.com/rust-lang/crates.io-index#jobserver@0.1.34" = "2eee03e2c2e527e05f9c0eb3cf96ae153e943c09a75fdc332b44ca85c4b7bf2e" "registry+https://github.com/rust-lang/crates.io-index#js-sys@0.3.83" = "4a79fd435699556d8bbc4ad702af2da3bb4649273eed260b878154925b6e132e" "registry+https://github.com/rust-lang/crates.io-index#lazy_static@1.5.0" = "020e90a9414ab97c5bf9a8a61728ebbdf541855144aa99309e157a89705e0fbd" -"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.180" = "035dcf18d3095ffdb0f46f737ded2988c75df8f967d3b6cd8ad02643fc4e9bad" +"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.181" = "01e85fe091c0c0d096ef24773a303dedcbe5fdad5dba5879943eadc22267c92a" "registry+https://github.com/rust-lang/crates.io-index#libloading@0.8.9" = "f44726b077f568146166c0f61f392f36298b85b9ae7749ee601b09c6d02473a9" "registry+https://github.com/rust-lang/crates.io-index#libm@0.2.15" = "cd94f7d6d78551309bcf05c2cb102289c9d29e10d6faaaba174722beb8853326" -"registry+https://github.com/rust-lang/crates.io-index#libredox@0.1.10" = "c016462df640ece60b269ecc43359ea6da54ab3d2b369cbdaee0de902bed95b8" +"registry+https://github.com/rust-lang/crates.io-index#libredox@0.1.10" = "8545bdd48913a839438e8149f7b44049ebf4ed72f63cba08c31355e1836a3b82" "registry+https://github.com/rust-lang/crates.io-index#libsqlite3-sys@0.30.1" = "959a4f734559a38d51c41365e765257522d3c14570ccdeb223b6e651dcadab26" "registry+https://github.com/rust-lang/crates.io-index#libz-sys@1.1.23" = "be4d317d7110ec7c1620937af4fbcb724a09cbbe4bd054cdc669c399166a4c84" "registry+https://github.com/rust-lang/crates.io-index#linux-raw-sys@0.11.0" = "5f14c9322eebc368e8f816dccc2d07f2d92ca826d02d6cebdd03a496aad1134f" @@ -148,7 +148,7 @@ version = 2 "registry+https://github.com/rust-lang/crates.io-index#mime@0.3.17" = "874dd444b52333fb0c80c699683e982eb8763067da353cad5039f04907f00658" "registry+https://github.com/rust-lang/crates.io-index#minimal-lexical@0.2.1" = "8094ff1242ec4361f3e4a095c00361eae5975e5bffa058e8d372f8c9c0f952f7" "registry+https://github.com/rust-lang/crates.io-index#miniz_oxide@0.8.9" = "886746246ee7815375aee39029630fe4c34885a6c117fec279ac1ba3ca09d0ca" -"registry+https://github.com/rust-lang/crates.io-index#mio@1.1.1" = "def7cb25436421d172e328a23c0f91c7b068aa277553226ef9827a374c95bbe8" +"registry+https://github.com/rust-lang/crates.io-index#mio@1.1.1" = "588e4d7dee1b123377c616d3afb9dfe104f9a9edbdc3fe1d049d15c81bc437b7" "registry+https://github.com/rust-lang/crates.io-index#natord@1.0.9" = "c0227b24bcf1d15d97f25673ede276e2903ff6d31017d1be062a7a7b94599d05" "registry+https://github.com/rust-lang/crates.io-index#nom@7.1.3" = "882b7b8947c92d5d566ef6e1daada9593061e5ab7f7390849d8b5e8bd0ce4705" "registry+https://github.com/rust-lang/crates.io-index#nu-ansi-term@0.50.3" = "414b7230f93019dee6d24b817afcb96bf8eb0d2ea2ee4b830e5635d4bf3111bd" @@ -158,14 +158,14 @@ version = 2 "registry+https://github.com/rust-lang/crates.io-index#num-integer@0.1.46" = "a0ecdbdf9d7eb162b27a67d77532ec062e39678f13a8f3251733e8ec2d17f1aa" "registry+https://github.com/rust-lang/crates.io-index#num-iter@0.1.45" = "1a7d2dfb41b74c70c2a54f64842104d8523c61e29e4b02a421dbc1747dd788cc" "registry+https://github.com/rust-lang/crates.io-index#num-traits@0.2.19" = "11e2e62ef53e5331d06a9934b786c0e189c394335015ca237e3404848fc7e6b6" -"registry+https://github.com/rust-lang/crates.io-index#num_cpus@1.17.0" = "9284744c907fb050859971c8b0ba028cef1a9eee92b7424337caa36946c2ed46" +"registry+https://github.com/rust-lang/crates.io-index#num_cpus@1.17.0" = "b83a6196e96d237d9fa03e8bae5e5c742d816c461ea809f0ae5a8c2ba396b140" "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.3" = "052c26f905a581c3d943e058df3aa2745bf2209b114d6e48639c7b6f6eb59e2e" "registry+https://github.com/rust-lang/crates.io-index#ordered-float@4.6.0" = "fa73394ac1c216c4e1c7bb1239059215f8db89e8ae555e498f03fd30ca7930fe" "registry+https://github.com/rust-lang/crates.io-index#ouroboros@0.18.5" = "9fcbb04903b779ce55686c83deee486381f909ac382d8b649a3f9fae96bac598" "registry+https://github.com/rust-lang/crates.io-index#ouroboros_macro@0.18.5" = "b76297013bbe68b07f2ba5ddc069e8ad1f852005879c8407c4149c590a351c8a" "registry+https://github.com/rust-lang/crates.io-index#parking@2.2.1" = "bf3dc4ef084e7a56d55e901dda7be6f87e464ace67d2b592faaac1bd4a5cd5b5" "registry+https://github.com/rust-lang/crates.io-index#parking_lot@0.12.5" = "1bbcdd25e546e54087007ab157b32a91d3e0ac50e172e39f37a90d3f4078c29c" -"registry+https://github.com/rust-lang/crates.io-index#parking_lot_core@0.9.12" = "775dda0c122dfdfe43fec5462607540f5488219259187de541f4035d032c698c" +"registry+https://github.com/rust-lang/crates.io-index#parking_lot_core@0.9.12" = "8056362c712e23b1d18567f1f0b0335bc619c896dc278d7e4dc4620f2d0e5b84" "registry+https://github.com/rust-lang/crates.io-index#path-absolutize@3.1.1" = "4e382243d293e25fb016b7971862807652196a48a3fe4654717920fe4b67ef15" "registry+https://github.com/rust-lang/crates.io-index#path-dedot@3.1.1" = "e9b91050d96d701a53fb5657e4e91044e336f05e012e77874dce07886830e651" "registry+https://github.com/rust-lang/crates.io-index#pem-rfc7468@0.7.0" = "a94d480eaf91ffde91e1e83b01044086362ff808c630345154a2e0435c2f1bd9" @@ -190,10 +190,12 @@ version = 2 "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.42" = "91c8d87ed79d624a31a96de45ffb568d706c6d680d9b059666c790d6b8df9a42" "registry+https://github.com/rust-lang/crates.io-index#r-efi@5.3.0" = "8b782f8b39e0573e49058548a27f0982ac72a7029326d2bc61b6df3c194cffaa" "registry+https://github.com/rust-lang/crates.io-index#radium@0.7.0" = "52ab1f0da4ca3712c10b6f2268313ee3f674dace171872ca510a78e825f01b90" -"registry+https://github.com/rust-lang/crates.io-index#rand@0.8.5" = "ec2d04ff57646442e6b999f5c8f929c1307b0219ce09bd59421d6b33cc0708bb" +"registry+https://github.com/rust-lang/crates.io-index#rand@0.8.5" = "12c2bc1a3740ed20daf05a8e67f285f98d57b2a28c19a89506b5e847c169f7db" "registry+https://github.com/rust-lang/crates.io-index#rand@0.9.2" = "878e5168572124872f9ce669597b25ffa4ab4aa8ec02894ad6e5f238a47e6758" +"registry+https://github.com/rust-lang/crates.io-index#rand_chacha@0.10.0" = "fd5094f113fe26db90002b895690fc33ad66ca479492771d71e1de9e7e07eb4e" "registry+https://github.com/rust-lang/crates.io-index#rand_chacha@0.3.1" = "08c84d54e52a16abda285b9ff0dc9ba53275ff0400893d18cadca9e62136117c" -"registry+https://github.com/rust-lang/crates.io-index#rand_chacha@0.9.0" = "24a75837f81f30b0b08f17a21c76397c5a0b135189e8534ce16aff9d9d956e28" +"registry+https://github.com/rust-lang/crates.io-index#rand_chacha@0.9.0" = "4e7405944d2d3059f769ff386f08a93355142c52186b247b221324eb38f3a04f" +"registry+https://github.com/rust-lang/crates.io-index#rand_core@0.10.0" = "84d75f2d2736bceb0126450527b7784ed991be535e32d195acfb510dfa207be3" "registry+https://github.com/rust-lang/crates.io-index#rand_core@0.6.4" = "9a4955d1ebf74d5a34229d7a7dd9a4fcb4d756d25c255a754764e413cb70eb02" "registry+https://github.com/rust-lang/crates.io-index#rand_core@0.9.3" = "606289c70ddccf037a657cec71a2cbd5b7d4ab42b02a5712950e004aebfc8ee0" "registry+https://github.com/rust-lang/crates.io-index#rayon-core@1.13.0" = "b6da241fe5b8738570340d7c787c7fde07961356a3ceccd8ba3f09eb1fb89614" @@ -203,22 +205,22 @@ version = 2 "registry+https://github.com/rust-lang/crates.io-index#regex-syntax@0.8.8" = "b5bd7a8b232100b181ef43ff406fb2382e503828b02a43cd606f98d6ea0ad950" "registry+https://github.com/rust-lang/crates.io-index#regex@1.12.2" = "10f6a5c57952790e35d0c05df1e7eafd9c894d80edd73975f10517c9b6c1a00a" "registry+https://github.com/rust-lang/crates.io-index#rend@0.4.2" = "e1477adda7718f5953efaa45ef12a2e30c0037c5723d544285f1d21d751a092b" -"registry+https://github.com/rust-lang/crates.io-index#ring@0.17.14" = "151d151dbff71a9fc2ab072a6790aaf76e22bae424931ac216e88a09077ca58c" -"registry+https://github.com/rust-lang/crates.io-index#rkyv@0.7.45" = "696526e2a115738bd5270d4fc1fceddf70699a2c77bb1a5f03355cdfb6bdc2a6" +"registry+https://github.com/rust-lang/crates.io-index#ring@0.17.14" = "08b31cc85dc51171a8281f92ef82b8c3f9966ce9945d71093aab677887186bf4" +"registry+https://github.com/rust-lang/crates.io-index#rkyv@0.7.45" = "e686f584b9d7e39561f9b9e66e9220a3847cfa4dc2e6a618afb1cef4ad5b5179" "registry+https://github.com/rust-lang/crates.io-index#rkyv_derive@0.7.45" = "ad51df9d2e223d73130300a8075a8eaeed101ff95d4c9ad1252498f7186db950" "registry+https://github.com/rust-lang/crates.io-index#rsa@0.9.10" = "9e4146e51ce7cb3bfeca2da05259109f47b38350679c086df91d3c492e59a808" -"registry+https://github.com/rust-lang/crates.io-index#rust_decimal@1.39.0" = "f0a399b0fe2c8f68f117a07bd3cf11e2b566fd90897d6e96930a70ca0eb17f1c" +"registry+https://github.com/rust-lang/crates.io-index#rust_decimal@1.39.0" = "49c913cb2f583556a2cb3b988c01520f40aef6996d070f7877705a221b575596" "registry+https://github.com/rust-lang/crates.io-index#rustc-hash@2.1.1" = "0679bb21a175710a797b96bedea9bb8d694b5c2110360190bb128ea90c196143" "registry+https://github.com/rust-lang/crates.io-index#rustc_version@0.4.1" = "151a5417e3d8e8cb75fb66d5633ca22bbf4557ba427427529179e3da85b3d795" -"registry+https://github.com/rust-lang/crates.io-index#rustix@1.1.3" = "0532b5a02648453c48bcdd3d3a0dc32a2161021b1e31819625b9f759feb267ed" +"registry+https://github.com/rust-lang/crates.io-index#rustix@1.1.3" = "f385afbeb86090e1143c7929a486ae7fd8159c9249f376ac771dcf115def8561" "registry+https://github.com/rust-lang/crates.io-index#rustversion@1.0.22" = "6cbed1a03b1abaa0c695a90fcbc859f57e5cfd5394ea17ade5c7fa4274d2c79e" "registry+https://github.com/rust-lang/crates.io-index#ryu@1.0.20" = "0b880d45160a7f70777d7137bcd2c4726589afcce9ec373650dc4da0f33d86f0" "registry+https://github.com/rust-lang/crates.io-index#scopeguard@1.2.0" = "0baa527d86d4a1c5caf29ed5f5bdd5dde46639205aa6cdbbbfb97f5115865ec0" "registry+https://github.com/rust-lang/crates.io-index#sea-bae@0.2.1" = "d7e7b6b7b0615fa6e7efd640b10011eb12875a98760a90a060b0f24b8eaac803" "registry+https://github.com/rust-lang/crates.io-index#sea-orm-macros@1.1.19" = "6db2b7a1cab3825bd3ba21c12186177be99a9ee3fb52227ed095b8c5f49858ac" -"registry+https://github.com/rust-lang/crates.io-index#sea-orm@1.1.19" = "24961fa106eb553928350da8ae4d32b7c71a51859221e72ab79a6068a6139360" -"registry+https://github.com/rust-lang/crates.io-index#sea-query-binder@0.7.0" = "a3ed3cac79137033003cfd3569f577be91ac478ad1188e4273a5d256f9038f4c" -"registry+https://github.com/rust-lang/crates.io-index#sea-query@0.32.7" = "10da5890fac79f3a443eee25763a5ba729957e06783568f423f6d473ce32fbb1" +"registry+https://github.com/rust-lang/crates.io-index#sea-orm@1.1.19" = "4555e59a6beb46b64ccdc800e5dac19b1fa6cbb0459466fa97610c3f6530871d" +"registry+https://github.com/rust-lang/crates.io-index#sea-query-binder@0.7.0" = "19576482834ce58401de3727c14a6cd3022871a76a902200200fee0da8c36e5a" +"registry+https://github.com/rust-lang/crates.io-index#sea-query@0.32.7" = "e8fa50ad012cdd6ddd78c5a5901c721a88a8d6ca350f4c35ee5e57c7ae6052d6" "registry+https://github.com/rust-lang/crates.io-index#seahash@4.1.0" = "5bfb692d1110feebdd6ddd8b054cb74393a5b1127323bc80d0351c460b189393" "registry+https://github.com/rust-lang/crates.io-index#semver@1.0.27" = "38879be2c30d8b28a8ad9bfbe8faf8850b5649e941c4c6dac305b9bd1f362970" "registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228" = "951cc1355c31ddec2cd9225fde7ebfe6a8151b7c614ce9dd13eb4a544d1225b1" @@ -231,22 +233,22 @@ version = 2 "registry+https://github.com/rust-lang/crates.io-index#sha2@0.10.9" = "b1d5cb1bdd8b0212bf6ff1bb87977f8b998be51f055be7fb0a12a9f260e8e301" "registry+https://github.com/rust-lang/crates.io-index#sharded-slab@0.1.7" = "479c23d019d9390a8d0bf81bc13b24ae9b6ffd04b88a9e3802f8d2b513446d80" "registry+https://github.com/rust-lang/crates.io-index#shlex@1.3.0" = "909ebb2a3da0299aa1bebe42ad4a8421fee979c16b64410b5d823ecbb3de1396" -"registry+https://github.com/rust-lang/crates.io-index#signal-hook-registry@1.4.7" = "f75099c9c6111a21a5ece3968b3a6929950ed7a9900fa668f1fbe72e4f80dfff" +"registry+https://github.com/rust-lang/crates.io-index#signal-hook-registry@1.4.7" = "5f7ba0c48d1cdb15bafa5fdcda56ae30ea04fcc1943e28755721a3f554e7870b" "registry+https://github.com/rust-lang/crates.io-index#signature@2.2.0" = "a5f7fc85dcea8c46b9814230efcfa8a4c5263bf966bb03f9dfe458118931339c" "registry+https://github.com/rust-lang/crates.io-index#simd-adler32@0.3.8" = "4a78bafc1c52373dd142b998a46bcfad4c6ad3d8084d8e19a4140be03008f006" "registry+https://github.com/rust-lang/crates.io-index#simdutf8@0.1.5" = "7947b615c6fb1c9785604f0fb4f18504ac6b22ae1083dd679e0b8c67ab5bb0d5" "registry+https://github.com/rust-lang/crates.io-index#similar@2.7.0" = "299b989e0aaab688b237b374c63f72454aae6d654019f8600752371228739459" "registry+https://github.com/rust-lang/crates.io-index#slab@0.4.11" = "286e6092b3b1d9388e8d6bf69f6655531801e4aa5fcf5cf0da20d7f56a1a60fa" "registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.1" = "d5e0d2311111ef4b69869f6b5a5516ed62d237952153d9e87faeb2106af18c28" -"registry+https://github.com/rust-lang/crates.io-index#socket2@0.6.1" = "9cfb96e2c4d1648230607ab6894286d7f530fba54a692c75024de80ad7be4497" +"registry+https://github.com/rust-lang/crates.io-index#socket2@0.6.1" = "43b87959931f74b096ba04a046da884b210f4cc8c5d77a5c894e7c97bd488874" "registry+https://github.com/rust-lang/crates.io-index#spin@0.9.8" = "0a89a1b9fb5520dfef257e4592b0d06a5a693ae6e24f0596674da14e4422e93e" "registry+https://github.com/rust-lang/crates.io-index#spki@0.7.3" = "38cff90801eaeb44ad576151bf270494dbbaafbd3b6f1add2d214ddfa31b4699" -"registry+https://github.com/rust-lang/crates.io-index#sqlx-core@0.8.6" = "152b2bf8d105fec13caf08f36d6a036b64279a2c5be2e86aef363a757420b553" +"registry+https://github.com/rust-lang/crates.io-index#sqlx-core@0.8.6" = "b17026c754ddeb57bc896984c8084d26ae17b3ba615d0e298c1a1577912eb6f7" "registry+https://github.com/rust-lang/crates.io-index#sqlx-macros-core@0.8.6" = "c801330650250cb1edf1a1471ceb7f952e447488811daa9482ca532059d9ffec" "registry+https://github.com/rust-lang/crates.io-index#sqlx-macros@0.8.6" = "1435b9cdd7ff47e9a3cffd206777e1764a38a0d20b44a4bcba0179b27c237658" -"registry+https://github.com/rust-lang/crates.io-index#sqlx-mysql@0.8.6" = "2c8207bb3e585f5e1ba88b6f31f0106a85b082a5bf2cfbee97c0212ee96436e9" -"registry+https://github.com/rust-lang/crates.io-index#sqlx-postgres@0.8.6" = "9a9cf68c98093e294d5ed60fcb9738468d4374073dea051e5276c0b3e8deb66c" -"registry+https://github.com/rust-lang/crates.io-index#sqlx-sqlite@0.8.6" = "4d17b08dc9e15c34768b433f2951b0168840729072c394a312a658e7c8c3f3e4" +"registry+https://github.com/rust-lang/crates.io-index#sqlx-mysql@0.8.6" = "aa2a4739e8674fafb4307d36de711a487e563fcb3fc724bc7456a855aaea629d" +"registry+https://github.com/rust-lang/crates.io-index#sqlx-postgres@0.8.6" = "4356e9b1329a9ae0251495ec598d5508a00159f7c3309385823a803932c27599" +"registry+https://github.com/rust-lang/crates.io-index#sqlx-sqlite@0.8.6" = "5283d14fa29486923c40420850ab8cdb3e6c5ee648d90f647e36ae129d7ba277" "registry+https://github.com/rust-lang/crates.io-index#sqlx@0.8.6" = "add18a2766bae9eee4954835d6f570e51d019acba17cfd4ae54738b657b04095" "registry+https://github.com/rust-lang/crates.io-index#stable_deref_trait@1.2.1" = "228237c0b3c456d90942c2213844fbab55e828d8d6e70955151d4eecf967f2ed" "registry+https://github.com/rust-lang/crates.io-index#static_assertions@1.1.0" = "91cdd3b5b292316acdeffeb470ae2128cf75af4b8fe3f3b51d7c40f5cce8feb9" @@ -271,8 +273,8 @@ version = 2 "registry+https://github.com/rust-lang/crates.io-index#tinyvec_macros@0.1.1" = "36ca6585d9594280628021a7e44ac609ea4571ff32e6af9a85a49fee6a27f159" "registry+https://github.com/rust-lang/crates.io-index#tokio-macros@2.6.0" = "9829e0d22efbf19294b7039375842b633fd6ce1d8cea48a209d3fba971d152e3" "registry+https://github.com/rust-lang/crates.io-index#tokio-stream@0.1.18" = "50f838c2de72fd942d1dd4dd4e63477ac6f65999fd6e33cb7f745f3644e22964" -"registry+https://github.com/rust-lang/crates.io-index#tokio-util@0.7.18" = "bc5d6fbb2ed8d10feb5fe7605e2e9e24e3edb94233aa0e90e18a103a91b48632" -"registry+https://github.com/rust-lang/crates.io-index#tokio@1.49.0" = "6b48044746b1ecffb62438e0d64526ffa29aa7546376beec2805dbc54bddf62f" +"registry+https://github.com/rust-lang/crates.io-index#tokio-util@0.7.18" = "2b39f6896663d43d486167a562f069576a5922062e07b44bfa37b742d3daf167" +"registry+https://github.com/rust-lang/crates.io-index#tokio@1.49.0" = "833b932c0ddfe37c1c93d46e39cce2b733cae9e1a90456c38be2e10bfa8e1141" "registry+https://github.com/rust-lang/crates.io-index#toml_datetime@0.7.3" = "042c456ec5facd2ffc53fc874815c1905cc41a2459e5a5c2d12c0b66001a9d79" "registry+https://github.com/rust-lang/crates.io-index#toml_edit@0.23.9" = "7687c1ab1d008612af62983a3c3cdb8ca4ebc4df543fba08669f6c6c00d94837" "registry+https://github.com/rust-lang/crates.io-index#toml_parser@1.0.4" = "5256925f90c1ff0e22f569a344de72d89987248fbf3e87c1a95e532d1f6bbdee" @@ -294,7 +296,7 @@ version = 2 "registry+https://github.com/rust-lang/crates.io-index#unty@0.0.4" = "5475cd4e774a5dff1d71ec84f3f46bbf2a1ce7ecae45e15713335645a8e14941" "registry+https://github.com/rust-lang/crates.io-index#url@2.5.7" = "41a06dd6e4a64931308619cd9143392e669fe55fff38850784e1a9a09b49bfc2" "registry+https://github.com/rust-lang/crates.io-index#utf8_iter@1.0.4" = "047f25b86fd5f1351043a8dde0aeed66f85eeca3ac56cbb7bec54ed6c023b16c" -"registry+https://github.com/rust-lang/crates.io-index#uuid@1.19.0" = "6527b631b0af7222779488f8c649a9d5f4c4d2180776bf51cfd4973bae8c8824" +"registry+https://github.com/rust-lang/crates.io-index#uuid@1.20.0" = "ca18a8c8302c55cd69dc8510b4550898e502d10917e0751bdd10ab0aa7dd99cb" "registry+https://github.com/rust-lang/crates.io-index#valuable@0.1.1" = "756ad94b5effaecf9973461517320157d1ee5ab3ec28d86e1f99c4915851a45e" "registry+https://github.com/rust-lang/crates.io-index#vcpkg@0.2.15" = "719f7f17537403aaa321d268702fb5039de3e7c1993d88754cca070f8b597174" "registry+https://github.com/rust-lang/crates.io-index#version_check@0.9.5" = "5f3a662254d4a2b2e505904ada0ee24aa8376f7d08d71832a6a0f1ff04f1ad71" diff --git a/src/errors.rs b/src/errors.rs index fb8d70e5..987f451d 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -58,6 +58,42 @@ pub enum GitError { #[error("Not a valid git note object: {0}")] InvalidNoteObject(String), + /// Malformed context snapshot object. + #[error("Not a valid agent context snapshot object: {0}")] + InvalidContextSnapshotObject(String), + + /// Malformed decision object. + #[error("Not a valid agent decision object: {0}")] + InvalidDecisionObject(String), + + /// Malformed evidence object. + #[error("Not a valid agent evidence object: {0}")] + InvalidEvidenceObject(String), + + /// Malformed patch set object. + #[error("Not a valid agent patch set object: {0}")] + InvalidPatchSetObject(String), + + /// Malformed plan object. + #[error("Not a valid agent plan object: {0}")] + InvalidPlanObject(String), + + /// Malformed provenance object. + #[error("Not a valid agent provenance object: {0}")] + InvalidProvenanceObject(String), + + /// Malformed run object. + #[error("Not a valid agent run object: {0}")] + InvalidRunObject(String), + + /// Malformed task object. + #[error("Not a valid agent task object: {0}")] + InvalidTaskObject(String), + + /// Malformed tool invocation object. + #[error("Not a valid agent tool invocation object: {0}")] + InvalidToolInvocationObject(String), + /// Malformed or unsupported index (.idx) file. #[error("The `{0}` is not a valid idx file.")] InvalidIdxFile(String), diff --git a/src/internal/object/commit.rs b/src/internal/object/commit.rs index f16b12d8..ba95a192 100644 --- a/src/internal/object/commit.rs +++ b/src/internal/object/commit.rs @@ -219,7 +219,7 @@ impl ObjectTrait for Commit { } fn get_size(&self) -> usize { - 0 + self.to_data().map(|data| data.len()).unwrap_or(0) } /// [Git-Internals-Git-Objects](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects) diff --git a/src/internal/object/context.rs b/src/internal/object/context.rs index 4b91e3bd..7b6a4f95 100644 --- a/src/internal/object/context.rs +++ b/src/internal/object/context.rs @@ -13,12 +13,19 @@ //! Each item in the snapshot has a content hash (`IntegrityHash`). //! This ensures that if the file changes on disk, we know the snapshot is stale or refers to an older version. +use std::fmt::Display; + use serde::{Deserialize, Serialize}; use uuid::Uuid; -use super::{ - header::{ActorRef, AiObjectType, Header}, - integrity::IntegrityHash, +use crate::{ + errors::GitError, + hash::ObjectHash, + internal::object::{ + ObjectTrait, + integrity::IntegrityHash, + types::{ActorRef, Header, ObjectType}, + }, }; /// Selection strategy for context snapshots. @@ -87,7 +94,7 @@ impl ContextSnapshot { ) -> Result { let base_commit_sha = base_commit_sha.as_ref().parse()?; Ok(Self { - header: Header::new(AiObjectType::ContextSnapshot, repo_id, created_by)?, + header: Header::new(ObjectType::ContextSnapshot, repo_id, created_by)?, base_commit_sha, selection_strategy, items: Vec::new(), @@ -98,62 +105,31 @@ impl ContextSnapshot { pub fn header(&self) -> &Header { &self.header } +} - pub fn base_commit_sha(&self) -> &IntegrityHash { - &self.base_commit_sha - } - - pub fn selection_strategy(&self) -> &SelectionStrategy { - &self.selection_strategy - } - - pub fn items(&self) -> &[ContextItem] { - &self.items - } - - pub fn summary(&self) -> Option<&str> { - self.summary.as_deref() +impl Display for ContextSnapshot { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "ContextSnapshot: {}", self.header.object_id()) } +} - pub fn add_item(&mut self, item: ContextItem) { - self.items.push(item); +impl ObjectTrait for ContextSnapshot { + fn from_bytes(data: &[u8], _hash: ObjectHash) -> Result + where + Self: Sized, + { + serde_json::from_slice(data).map_err(|e| GitError::InvalidObjectInfo(e.to_string())) } - pub fn set_summary(&mut self, summary: Option) { - self.summary = summary; + fn get_type(&self) -> ObjectType { + ObjectType::ContextSnapshot } -} - -#[cfg(test)] -mod tests { - use super::*; - fn test_hash_hex() -> String { - IntegrityHash::compute(b"ai-process-test").to_hex() + fn get_size(&self) -> usize { + serde_json::to_vec(self).map(|v| v.len()).unwrap_or(0) } - #[test] - fn test_context_snapshot_fields() { - let repo_id = Uuid::from_u128(0x0123456789abcdef0123456789abcdef); - let actor = ActorRef::agent("test-agent").expect("actor"); - let base_hash = test_hash_hex(); - - let mut snapshot = - ContextSnapshot::new(repo_id, actor, &base_hash, SelectionStrategy::Explicit) - .expect("snapshot"); - snapshot.set_summary(Some("core files".to_string())); - - snapshot.add_item( - ContextItem::new( - ContextItemKind::File, - "src/lib.rs", - IntegrityHash::compute(b"context-item"), - ) - .expect("context item"), - ); - - assert_eq!(snapshot.items().len(), 1); - assert_eq!(snapshot.items()[0].path, "src/lib.rs"); - assert_eq!(snapshot.summary(), Some("core files")); + fn to_data(&self) -> Result, GitError> { + serde_json::to_vec(self).map_err(|e| GitError::InvalidObjectInfo(e.to_string())) } } diff --git a/src/internal/object/decision.rs b/src/internal/object/decision.rs index 6ff275be..a0ffbdec 100644 --- a/src/internal/object/decision.rs +++ b/src/internal/object/decision.rs @@ -16,9 +16,14 @@ use std::fmt; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use super::{ - header::{ActorRef, AiObjectType, Header}, - integrity::IntegrityHash, +use crate::{ + errors::GitError, + hash::ObjectHash, + internal::object::{ + ObjectTrait, + integrity::IntegrityHash, + types::{ActorRef, Header, ObjectType}, + }, }; /// Type of decision. @@ -101,7 +106,7 @@ impl Decision { decision_type: impl Into, ) -> Result { Ok(Self { - header: Header::new(AiObjectType::Decision, repo_id, created_by)?, + header: Header::new(ObjectType::Decision, repo_id, created_by)?, run_id, decision_type: decision_type.into(), chosen_patchset_id: None, @@ -156,6 +161,33 @@ impl Decision { } } +impl fmt::Display for Decision { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Decision: {}", self.header.object_id()) + } +} + +impl ObjectTrait for Decision { + fn from_bytes(data: &[u8], _hash: ObjectHash) -> Result + where + Self: Sized, + { + serde_json::from_slice(data).map_err(|e| GitError::InvalidObjectInfo(e.to_string())) + } + + fn get_type(&self) -> ObjectType { + ObjectType::Decision + } + + fn get_size(&self) -> usize { + serde_json::to_vec(self).map(|v| v.len()).unwrap_or(0) + } + + fn to_data(&self) -> Result, GitError> { + serde_json::to_vec(self).map_err(|e| GitError::InvalidObjectInfo(e.to_string())) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/internal/object/evidence.rs b/src/internal/object/evidence.rs index 32e2f010..886c2601 100644 --- a/src/internal/object/evidence.rs +++ b/src/internal/object/evidence.rs @@ -14,7 +14,14 @@ use std::fmt; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use super::header::{ActorRef, AiObjectType, ArtifactRef, Header}; +use crate::{ + errors::GitError, + hash::ObjectHash, + internal::object::{ + ObjectTrait, + types::{ActorRef, ArtifactRef, Header, ObjectType}, + }, +}; /// Kind of evidence. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] @@ -89,7 +96,7 @@ impl Evidence { tool: impl Into, ) -> Result { Ok(Self { - header: Header::new(AiObjectType::Evidence, repo_id, created_by)?, + header: Header::new(ObjectType::Evidence, repo_id, created_by)?, run_id, patchset_id: None, kind: kind.into(), @@ -158,6 +165,33 @@ impl Evidence { } } +impl fmt::Display for Evidence { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Evidence: {}", self.header.object_id()) + } +} + +impl ObjectTrait for Evidence { + fn from_bytes(data: &[u8], _hash: ObjectHash) -> Result + where + Self: Sized, + { + serde_json::from_slice(data).map_err(|e| GitError::InvalidObjectInfo(e.to_string())) + } + + fn get_type(&self) -> ObjectType { + ObjectType::Evidence + } + + fn get_size(&self) -> usize { + serde_json::to_vec(self).map(|v| v.len()).unwrap_or(0) + } + + fn to_data(&self) -> Result, GitError> { + serde_json::to_vec(self).map_err(|e| GitError::InvalidObjectInfo(e.to_string())) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/internal/object/header.rs b/src/internal/object/header.rs deleted file mode 100644 index 3039b1ff..00000000 --- a/src/internal/object/header.rs +++ /dev/null @@ -1,637 +0,0 @@ -//! AI Object Header Definition -//! -//! This module defines the common metadata header shared by all AI process objects. -//! It ensures that every object in the AI workflow has a consistent identity, -//! provenance, and integrity verification mechanism. -//! -//! # Core Concepts -//! -//! - **Identity**: Every object has a unique `Uuid` (v7) and `AiObjectType`. -//! - **Provenance**: `ActorRef` tracks who created the object (Human, Agent, System). -//! - **Integrity**: `IntegrityHash` (SHA-256) ensures content has not been tampered with. -//! - **Linking**: Objects reference each other via UUIDs (e.g., Run -> Task). - -use std::{collections::HashMap, fmt}; - -use chrono::{DateTime, Utc}; -use serde::{Deserialize, Serialize}; -use uuid::Uuid; - -use super::integrity::{IntegrityHash, compute_integrity_hash}; - -/// Visibility of an AI process object. -/// -/// Determines whether the object is accessible only within the project (Private) -/// or can be shared externally (Public). -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub enum Visibility { - Private, - Public, -} - -/// AI process object type. -/// -/// Enumerates all supported object types in the AI workflow system. -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub enum AiObjectType { - /// A unit of work to be performed. - Task, - /// An execution instance of an agent working on a task. - Run, - /// A set of code changes proposed by an agent. - PatchSet, - /// A snapshot of the codebase context used for a run. - ContextSnapshot, - /// A record of a tool execution (read, write, cmd, etc.). - ToolInvocation, - /// A step-by-step plan generated by the agent. - Plan, - /// Verification results (test output, lint errors). - Evidence, - /// Metadata about the model and provider used. - Provenance, - /// The final outcome of a run (commit, reject, etc.). - Decision, -} - -impl AiObjectType { - pub fn as_str(&self) -> &'static str { - match self { - AiObjectType::Task => "task", - AiObjectType::Run => "run", - AiObjectType::PatchSet => "patchset", - AiObjectType::ContextSnapshot => "context_snapshot", - AiObjectType::ToolInvocation => "tool_invocation", - AiObjectType::Plan => "plan", - AiObjectType::Evidence => "evidence", - AiObjectType::Provenance => "provenance", - AiObjectType::Decision => "decision", - } - } -} - -impl fmt::Display for AiObjectType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.as_str()) - } -} - -/// Header shared by all AI Process Objects. -/// -/// Contains standard metadata like ID, type, creator, and timestamps. -/// -/// # Usage -/// -/// Every AI object struct should flatten this header: -/// -/// ```rust,ignore -/// #[derive(Serialize, Deserialize)] -/// pub struct MyObject { -/// #[serde(flatten)] -/// header: Header, -/// // specific fields... -/// } -/// ``` -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct Header { - /// Global unique ID (UUID v7) - object_id: Uuid, - /// Object type (task/run/patchset/...) - object_type: AiObjectType, - /// Model version - schema_version: u32, - /// Repository identifier - repo_id: Uuid, - /// Creation time - created_at: DateTime, - /// Creator - created_by: ActorRef, - /// Visibility (fixed to private for Libra) - visibility: Visibility, - /// Search tags - #[serde(default)] - tags: HashMap, - /// External ID mapping - #[serde(default)] - external_ids: HashMap, - /// Content checksum (optional) - #[serde(default)] - checksum: Option, -} - -impl Header { - /// Create a new Header with default values. - /// - /// # Arguments - /// - /// * `object_type` - The specific type of the AI object. - /// * `repo_id` - The UUID of the repository this object belongs to. - /// * `created_by` - The actor (human/agent) creating this object. - pub fn new( - object_type: AiObjectType, - repo_id: Uuid, - created_by: ActorRef, - ) -> Result { - Ok(Self { - object_id: Uuid::now_v7(), - object_type, - schema_version: 1, - repo_id, - created_at: Utc::now(), - created_by, - visibility: Visibility::Private, - tags: HashMap::new(), - external_ids: HashMap::new(), - checksum: None, - }) - } - - pub fn object_id(&self) -> Uuid { - self.object_id - } - - pub fn object_type(&self) -> &AiObjectType { - &self.object_type - } - - pub fn schema_version(&self) -> u32 { - self.schema_version - } - - pub fn repo_id(&self) -> Uuid { - self.repo_id - } - - pub fn created_at(&self) -> DateTime { - self.created_at - } - - pub fn created_by(&self) -> &ActorRef { - &self.created_by - } - - pub fn visibility(&self) -> &Visibility { - &self.visibility - } - - pub fn tags(&self) -> &HashMap { - &self.tags - } - - pub fn tags_mut(&mut self) -> &mut HashMap { - &mut self.tags - } - - pub fn external_ids(&self) -> &HashMap { - &self.external_ids - } - - pub fn external_ids_mut(&mut self) -> &mut HashMap { - &mut self.external_ids - } - - pub fn set_object_id(&mut self, object_id: Uuid) { - self.object_id = object_id; - } - - pub fn set_object_type(&mut self, object_type: AiObjectType) -> Result<(), String> { - self.object_type = object_type; - Ok(()) - } - - pub fn set_schema_version(&mut self, schema_version: u32) -> Result<(), String> { - if schema_version == 0 { - return Err("schema_version must be greater than 0".to_string()); - } - self.schema_version = schema_version; - Ok(()) - } - - pub fn set_created_at(&mut self, created_at: DateTime) { - self.created_at = created_at; - } - - pub fn set_visibility(&mut self, visibility: Visibility) { - self.visibility = visibility; - } - - /// Accessor for checksum - pub fn checksum(&self) -> Option<&IntegrityHash> { - self.checksum.as_ref() - } - - /// Seal the header by calculating and setting the checksum of the provided object. - /// The checksum field is temporarily cleared to keep sealing idempotent. - /// - /// This is typically called just before storing the object to ensure `checksum` matches content. - pub fn seal(&mut self, object: &T) -> Result<(), serde_json::Error> { - let previous = self.checksum.take(); - match compute_integrity_hash(object) { - Ok(checksum) => { - self.checksum = Some(checksum); - Ok(()) - } - Err(err) => { - self.checksum = previous; - Err(err) - } - } - } -} - -/// Actor kind enum -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub enum ActorKind { - Human, - Agent, - System, - McpClient, - #[serde(untagged)] - Other(String), -} - -impl fmt::Display for ActorKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - ActorKind::Human => write!(f, "human"), - ActorKind::Agent => write!(f, "agent"), - ActorKind::System => write!(f, "system"), - ActorKind::McpClient => write!(f, "mcp_client"), - ActorKind::Other(s) => write!(f, "{}", s), - } - } -} - -impl From for ActorKind { - fn from(s: String) -> Self { - match s.as_str() { - "human" => ActorKind::Human, - "agent" => ActorKind::Agent, - "system" => ActorKind::System, - "mcp_client" => ActorKind::McpClient, - _ => ActorKind::Other(s), - } - } -} - -impl From<&str> for ActorKind { - fn from(s: &str) -> Self { - match s { - "human" => ActorKind::Human, - "agent" => ActorKind::Agent, - "system" => ActorKind::System, - "mcp_client" => ActorKind::McpClient, - _ => ActorKind::Other(s.to_string()), - } - } -} - -/// Actor reference (who created/triggered). -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct ActorRef { - /// Kind: human/agent/system/mcp_client - kind: ActorKind, - /// Subject ID (user/agent name or client ID) - id: String, - /// Display name (optional) - display_name: Option, - /// Auth context (optional, Libra usually empty) - auth_context: Option, -} - -impl ActorRef { - /// Create a new ActorRef with validation. - pub fn new(kind: impl Into, id: impl Into) -> Result { - let id_str = id.into(); - if id_str.trim().is_empty() { - return Err("Actor ID cannot be empty".to_string()); - } - Ok(Self { - kind: kind.into(), - id: id_str, - display_name: None, - auth_context: None, - }) - } - - /// Create an MCP client actor reference (MCP writes must use this). - pub fn new_for_mcp(id: impl Into) -> Result { - Self::new(ActorKind::McpClient, id) - } - - /// Validate that this actor is an MCP client. - pub fn ensure_mcp_client(&self) -> Result<(), String> { - if self.kind != ActorKind::McpClient { - return Err("MCP writes must use mcp_client actor kind".to_string()); - } - Ok(()) - } - - pub fn kind(&self) -> &ActorKind { - &self.kind - } - - pub fn id(&self) -> &str { - &self.id - } - - pub fn display_name(&self) -> Option<&str> { - self.display_name.as_deref() - } - - pub fn auth_context(&self) -> Option<&str> { - self.auth_context.as_deref() - } - - pub fn set_display_name(&mut self, display_name: Option) { - self.display_name = display_name; - } - - pub fn set_auth_context(&mut self, auth_context: Option) { - self.auth_context = auth_context; - } - - /// Create a human actor reference. - pub fn human(id: impl Into) -> Result { - Self::new(ActorKind::Human, id) - } - - /// Create an agent actor reference. - pub fn agent(name: impl Into) -> Result { - Self::new(ActorKind::Agent, name) - } - - /// Create a system component actor reference. - pub fn system(component: impl Into) -> Result { - Self::new(ActorKind::System, component) - } - - /// Create an MCP client actor reference. - pub fn mcp_client(client_id: impl Into) -> Result { - Self::new(ActorKind::McpClient, client_id) - } -} - -/// Artifact reference (external content). -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct ArtifactRef { - /// Store type: local_fs/s3 - store: String, - /// Storage key (e.g., path or object key) - key: String, - /// MIME type (optional) - content_type: Option, - /// Size in bytes (optional) - size_bytes: Option, - /// Content hash (strongly recommended) - hash: Option, - /// Expiration time (optional) - expires_at: Option>, -} - -impl ArtifactRef { - pub fn new(store: impl Into, key: impl Into) -> Result { - let store = store.into(); - let key = key.into(); - if store.trim().is_empty() { - return Err("store cannot be empty".to_string()); - } - if key.trim().is_empty() { - return Err("key cannot be empty".to_string()); - } - Ok(Self { - store, - key, - content_type: None, - size_bytes: None, - hash: None, - expires_at: None, - }) - } - - pub fn store(&self) -> &str { - &self.store - } - - pub fn key(&self) -> &str { - &self.key - } - - pub fn content_type(&self) -> Option<&str> { - self.content_type.as_deref() - } - - pub fn size_bytes(&self) -> Option { - self.size_bytes - } - - pub fn hash(&self) -> Option<&IntegrityHash> { - self.hash.as_ref() - } - - pub fn expires_at(&self) -> Option> { - self.expires_at - } - - /// Calculate hash for the given content bytes. - pub fn compute_hash(content: &[u8]) -> IntegrityHash { - IntegrityHash::compute(content) - } - - /// Set the hash directly. - pub fn with_hash(mut self, hash: IntegrityHash) -> Self { - self.hash = Some(hash); - self - } - - /// Set the hash from a hex string. - pub fn with_hash_hex(mut self, hash: impl AsRef) -> Result { - let hash = hash.as_ref().parse()?; - self.hash = Some(hash); - Ok(self) - } - - pub fn set_content_type(&mut self, content_type: Option) { - self.content_type = content_type; - } - - pub fn set_size_bytes(&mut self, size_bytes: Option) { - self.size_bytes = size_bytes; - } - - pub fn set_expires_at(&mut self, expires_at: Option>) { - self.expires_at = expires_at; - } - - /// Verify if the provided content matches the stored checksum - #[must_use = "handle integrity verification result"] - pub fn verify_integrity(&self, content: &[u8]) -> Result { - let stored_hash = self - .hash - .as_ref() - .ok_or_else(|| "No hash stored in ArtifactRef".to_string())?; - - Ok(IntegrityHash::compute(content) == *stored_hash) - } - - /// Check if two artifacts have the same content based on checksum - #[must_use] - pub fn content_eq(&self, other: &Self) -> Option { - match (&self.hash, &other.hash) { - (Some(a), Some(b)) => Some(a == b), - _ => None, - } - } - - /// Check if the artifact has expired - #[must_use] - pub fn is_expired(&self) -> bool { - if let Some(expires_at) = self.expires_at { - expires_at < Utc::now() - } else { - false - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_header_serialization() { - let repo_id = Uuid::from_u128(0x0123456789abcdef0123456789abcdef); - let actor = ActorRef::human("jackie").expect("actor"); - let header = Header::new(AiObjectType::Task, repo_id, actor).expect("header"); - - let json = serde_json::to_string(&header).unwrap(); - let deserialized: Header = serde_json::from_str(&json).unwrap(); - - assert_eq!(header.object_id(), deserialized.object_id()); - assert_eq!(header.object_type(), deserialized.object_type()); - assert_eq!(header.repo_id(), deserialized.repo_id()); - } - - #[test] - fn test_actor_ref() { - let actor = ActorRef::agent("coder").expect("actor"); - assert_eq!(actor.kind(), &ActorKind::Agent); - assert_eq!(actor.id(), "coder"); - - let sys = ActorRef::system("scheduler").expect("system"); - assert_eq!(sys.kind(), &ActorKind::System); - - let client = ActorRef::mcp_client("vscode").expect("client"); - assert_eq!(client.kind(), &ActorKind::McpClient); - assert!(client.ensure_mcp_client().is_ok()); - - let non_mcp = ActorRef::human("jackie").expect("actor"); - assert!(non_mcp.ensure_mcp_client().is_err()); - } - - #[test] - fn test_actor_kind_serialization() { - let k = ActorKind::McpClient; - let s = serde_json::to_string(&k).unwrap(); - assert_eq!(s, "\"mcp_client\""); - - let k2: ActorKind = serde_json::from_str("\"system\"").unwrap(); - assert_eq!(k2, ActorKind::System); - } - - #[test] - fn test_header_checksum() { - let repo_id = Uuid::from_u128(0x0123456789abcdef0123456789abcdef); - let actor = ActorRef::human("jackie").expect("actor"); - let mut header = Header::new(AiObjectType::Task, repo_id, actor).expect("header"); - // Fix time for deterministic checksum - header.set_created_at( - DateTime::parse_from_rfc3339("2026-02-10T00:00:00Z") - .unwrap() - .with_timezone(&Utc), - ); - header.set_object_id(Uuid::from_u128(0x00000000000000000000000000000001)); - - let checksum = compute_integrity_hash(&header).expect("checksum"); - assert_eq!(checksum.to_hex().len(), 64); // SHA256 length - - // Ensure changes change checksum - header - .set_object_type(AiObjectType::Run) - .expect("object_type"); - let checksum2 = compute_integrity_hash(&header).expect("checksum"); - assert_ne!(checksum, checksum2); - } - - #[test] - fn test_artifact_checksum() { - let content = b"hello world"; - let hash = ArtifactRef::compute_hash(content); - // echo -n "hello world" | shasum -a 256 - let expected_str = "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"; - assert_eq!(hash.to_hex(), expected_str); - - let artifact = ArtifactRef::new("s3", "key") - .expect("artifact") - .with_hash(hash); - assert_eq!(artifact.hash(), Some(&hash)); - - // Integrity check - assert!(artifact.verify_integrity(content).unwrap()); - assert!(!artifact.verify_integrity(b"wrong").unwrap()); - - // Deduplication - let artifact2 = ArtifactRef::new("local", "other/path") - .expect("artifact") - .with_hash(IntegrityHash::compute(content)); - assert_eq!(artifact.content_eq(&artifact2), Some(true)); - - let artifact3 = ArtifactRef::new("s3", "diff") - .expect("artifact") - .with_hash(ArtifactRef::compute_hash(b"diff")); - assert_eq!(artifact.content_eq(&artifact3), Some(false)); - } - - #[test] - fn test_invalid_checksum() { - let result = ArtifactRef::new("s3", "key") - .expect("artifact") - .with_hash_hex("bad_hash"); - assert!(result.is_err()); - } - - #[test] - fn test_header_seal() { - let repo_id = Uuid::from_u128(0x0123456789abcdef0123456789abcdef); - let actor = ActorRef::human("jackie").expect("actor"); - let mut header = Header::new(AiObjectType::Task, repo_id, actor).expect("header"); - - let content = serde_json::json!({"key": "value"}); - header.seal(&content).expect("seal"); - - assert!(header.checksum().is_some()); - let expected = compute_integrity_hash(&content).expect("checksum"); - assert_eq!(header.checksum().expect("checksum"), &expected); - } - - #[test] - fn test_empty_actor_id() { - let result = ActorRef::new(ActorKind::Human, " "); - assert!(result.is_err()); - } - - #[test] - fn test_artifact_expiration() { - let mut artifact = ArtifactRef::new("s3", "key").expect("artifact"); - assert!(!artifact.is_expired()); - - artifact.set_expires_at(Some(Utc::now() - chrono::Duration::hours(1))); - assert!(artifact.is_expired()); - - artifact.set_expires_at(Some(Utc::now() + chrono::Duration::hours(1))); - assert!(!artifact.is_expired()); - } -} diff --git a/src/internal/object/mod.rs b/src/internal/object/mod.rs index e7ce1a1e..a6aad8cc 100644 --- a/src/internal/object/mod.rs +++ b/src/internal/object/mod.rs @@ -5,7 +5,6 @@ pub mod commit; pub mod context; pub mod decision; pub mod evidence; -pub mod header; pub mod integrity; pub mod note; pub mod patchset; diff --git a/src/internal/object/patchset.rs b/src/internal/object/patchset.rs index edefdc15..0832ac6d 100644 --- a/src/internal/object/patchset.rs +++ b/src/internal/object/patchset.rs @@ -18,9 +18,14 @@ use std::fmt; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use super::{ - header::{ActorRef, AiObjectType, ArtifactRef, Header}, - integrity::IntegrityHash, +use crate::{ + errors::GitError, + hash::ObjectHash, + internal::object::{ + ObjectTrait, + integrity::IntegrityHash, + types::{ActorRef, ArtifactRef, Header, ObjectType}, + }, }; /// Patch application status. @@ -134,7 +139,7 @@ impl PatchSet { ) -> Result { let base_commit_sha = base_commit_sha.as_ref().parse()?; Ok(Self { - header: Header::new(AiObjectType::PatchSet, repo_id, created_by)?, + header: Header::new(ObjectType::PatchSet, repo_id, created_by)?, run_id, generation, base_commit_sha, @@ -199,6 +204,33 @@ impl PatchSet { } } +impl fmt::Display for PatchSet { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "PatchSet: {}", self.header.object_id()) + } +} + +impl ObjectTrait for PatchSet { + fn from_bytes(data: &[u8], _hash: ObjectHash) -> Result + where + Self: Sized, + { + serde_json::from_slice(data).map_err(|e| GitError::InvalidObjectInfo(e.to_string())) + } + + fn get_type(&self) -> ObjectType { + ObjectType::PatchSet + } + + fn get_size(&self) -> usize { + serde_json::to_vec(self).map(|v| v.len()).unwrap_or(0) + } + + fn to_data(&self) -> Result, GitError> { + serde_json::to_vec(self).map_err(|e| GitError::InvalidObjectInfo(e.to_string())) + } +} + #[cfg(test)] mod tests { use super::*; @@ -216,7 +248,7 @@ mod tests { let patchset = PatchSet::new(repo_id, actor, run_id, &base_hash, 1).expect("patchset"); - assert_eq!(patchset.header().object_type(), &AiObjectType::PatchSet); + assert_eq!(patchset.header().object_type(), &ObjectType::PatchSet); assert_eq!(patchset.generation(), 1); assert_eq!(patchset.diff_format(), &DiffFormat::UnifiedDiff); assert_eq!(patchset.apply_status(), &ApplyStatus::Proposed); diff --git a/src/internal/object/plan.rs b/src/internal/object/plan.rs index 3aa6f837..cf0be4fb 100644 --- a/src/internal/object/plan.rs +++ b/src/internal/object/plan.rs @@ -17,7 +17,14 @@ use std::fmt; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use super::header::{ActorRef, AiObjectType, Header}; +use crate::{ + errors::GitError, + hash::ObjectHash, + internal::object::{ + ObjectTrait, + types::{ActorRef, Header, ObjectType}, + }, +}; /// Plan step status. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] @@ -81,7 +88,7 @@ impl Plan { /// Create a new plan object (version 1) pub fn new(repo_id: Uuid, created_by: ActorRef, run_id: Uuid) -> Result { Ok(Self { - header: Header::new(AiObjectType::Plan, repo_id, created_by)?, + header: Header::new(ObjectType::Plan, repo_id, created_by)?, run_id, plan_version: 1, steps: Vec::new(), @@ -102,7 +109,7 @@ impl Plan { .checked_add(1) .ok_or_else(|| "plan_version overflow".to_string())?; Ok(Self { - header: Header::new(AiObjectType::Plan, repo_id, created_by)?, + header: Header::new(ObjectType::Plan, repo_id, created_by)?, run_id, plan_version: next_version, steps: Vec::new(), @@ -130,6 +137,33 @@ impl Plan { } } +impl fmt::Display for Plan { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Plan: {}", self.header.object_id()) + } +} + +impl ObjectTrait for Plan { + fn from_bytes(data: &[u8], _hash: ObjectHash) -> Result + where + Self: Sized, + { + serde_json::from_slice(data).map_err(|e| GitError::InvalidObjectInfo(e.to_string())) + } + + fn get_type(&self) -> ObjectType { + ObjectType::Plan + } + + fn get_size(&self) -> usize { + serde_json::to_vec(self).map(|v| v.len()).unwrap_or(0) + } + + fn to_data(&self) -> Result, GitError> { + serde_json::to_vec(self).map_err(|e| GitError::InvalidObjectInfo(e.to_string())) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/internal/object/provenance.rs b/src/internal/object/provenance.rs index 1bfffef4..52d1e0d3 100644 --- a/src/internal/object/provenance.rs +++ b/src/internal/object/provenance.rs @@ -10,10 +10,19 @@ //! - **Cost Accounting**: Tracking token usage per run. //! - **Optimization**: Comparing performance across different models or parameters. +use std::fmt; + use serde::{Deserialize, Serialize}; use uuid::Uuid; -use super::header::{ActorRef, AiObjectType, Header}; +use crate::{ + errors::GitError, + hash::ObjectHash, + internal::object::{ + ObjectTrait, + types::{ActorRef, Header, ObjectType}, + }, +}; /// Provenance object for model/provider metadata. /// Captures model/provider settings and usage. @@ -37,7 +46,7 @@ impl Provenance { model: impl Into, ) -> Result { Ok(Self { - header: Header::new(AiObjectType::Provenance, repo_id, created_by)?, + header: Header::new(ObjectType::Provenance, repo_id, created_by)?, run_id, provider: provider.into(), model: model.into(), @@ -79,6 +88,33 @@ impl Provenance { } } +impl fmt::Display for Provenance { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Provenance: {}", self.header.object_id()) + } +} + +impl ObjectTrait for Provenance { + fn from_bytes(data: &[u8], _hash: ObjectHash) -> Result + where + Self: Sized, + { + serde_json::from_slice(data).map_err(|e| GitError::InvalidObjectInfo(e.to_string())) + } + + fn get_type(&self) -> ObjectType { + ObjectType::Provenance + } + + fn get_size(&self) -> usize { + serde_json::to_vec(self).map(|v| v.len()).unwrap_or(0) + } + + fn to_data(&self) -> Result, GitError> { + serde_json::to_vec(self).map_err(|e| GitError::InvalidObjectInfo(e.to_string())) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/internal/object/run.rs b/src/internal/object/run.rs index ca9d4793..38763491 100644 --- a/src/internal/object/run.rs +++ b/src/internal/object/run.rs @@ -21,9 +21,14 @@ use std::{collections::HashMap, fmt}; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use super::{ - header::{ActorRef, AiObjectType, Header}, - integrity::IntegrityHash, +use crate::{ + errors::GitError, + hash::ObjectHash, + internal::object::{ + ObjectTrait, + integrity::IntegrityHash, + types::{ActorRef, Header, ObjectType}, + }, }; /// Run lifecycle status. @@ -129,7 +134,7 @@ impl Run { ) -> Result { let base_commit_sha = base_commit_sha.as_ref().parse()?; Ok(Self { - header: Header::new(AiObjectType::Run, repo_id, created_by)?, + header: Header::new(ObjectType::Run, repo_id, created_by)?, task_id, orchestrator_version: "libra-builtin".to_string(), base_commit_sha, @@ -203,6 +208,33 @@ impl Run { } } +impl fmt::Display for Run { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Run: {}", self.header.object_id()) + } +} + +impl ObjectTrait for Run { + fn from_bytes(data: &[u8], _hash: ObjectHash) -> Result + where + Self: Sized, + { + serde_json::from_slice(data).map_err(|e| GitError::InvalidObjectInfo(e.to_string())) + } + + fn get_type(&self) -> ObjectType { + ObjectType::Run + } + + fn get_size(&self) -> usize { + serde_json::to_vec(self).map(|v| v.len()).unwrap_or(0) + } + + fn to_data(&self) -> Result, GitError> { + serde_json::to_vec(self).map_err(|e| GitError::InvalidObjectInfo(e.to_string())) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/internal/object/task.rs b/src/internal/object/task.rs index 66598054..f541a6e3 100644 --- a/src/internal/object/task.rs +++ b/src/internal/object/task.rs @@ -21,7 +21,7 @@ //! //! ```rust //! use git_internal::internal::object::task::{Task, GoalType}; -//! use git_internal::internal::object::header::ActorRef; +//! use git_internal::internal::object::types::ActorRef; //! use uuid::Uuid; //! //! let repo_id = Uuid::new_v4(); @@ -37,7 +37,14 @@ use std::{fmt, str::FromStr}; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use super::header::{ActorRef, AiObjectType, Header}; +use crate::{ + errors::GitError, + hash::ObjectHash, + internal::object::{ + ObjectTrait, + types::{ActorRef, Header, ObjectType}, + }, +}; /// Task lifecycle status. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] @@ -170,7 +177,7 @@ impl Task { goal_type: Option, ) -> Result { Ok(Self { - header: Header::new(AiObjectType::Task, repo_id, created_by)?, + header: Header::new(ObjectType::Task, repo_id, created_by)?, title: title.into(), description: None, goal_type, @@ -243,10 +250,37 @@ impl Task { } } +impl fmt::Display for Task { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Task: {}", self.header.object_id()) + } +} + +impl ObjectTrait for Task { + fn from_bytes(data: &[u8], _hash: ObjectHash) -> Result + where + Self: Sized, + { + serde_json::from_slice(data).map_err(|e| GitError::InvalidObjectInfo(e.to_string())) + } + + fn get_type(&self) -> ObjectType { + ObjectType::Task + } + + fn get_size(&self) -> usize { + serde_json::to_vec(self).map(|v| v.len()).unwrap_or(0) + } + + fn to_data(&self) -> Result, GitError> { + serde_json::to_vec(self).map_err(|e| GitError::InvalidObjectInfo(e.to_string())) + } +} + #[cfg(test)] mod tests { use super::*; - use crate::internal::object::header::ActorKind; + use crate::internal::object::types::ActorKind; #[test] fn test_task_creation() { @@ -258,7 +292,7 @@ mod tests { let dep_id = Uuid::from_u128(0x00000000000000000000000000000001); task.add_dependency(dep_id); - assert_eq!(task.header().object_type(), &AiObjectType::Task); + assert_eq!(task.header().object_type(), &ObjectType::Task); assert_eq!(task.status(), &TaskStatus::Draft); assert_eq!(task.goal_type(), Some(&GoalType::Bugfix)); assert_eq!(task.dependencies().len(), 1); diff --git a/src/internal/object/tool.rs b/src/internal/object/tool.rs index 6fa1ad08..b64a9c66 100644 --- a/src/internal/object/tool.rs +++ b/src/internal/object/tool.rs @@ -21,7 +21,14 @@ use std::fmt; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use super::header::{ActorRef, AiObjectType, ArtifactRef, Header}; +use crate::{ + errors::GitError, + hash::ObjectHash, + internal::object::{ + ObjectTrait, + types::{ActorRef, ArtifactRef, Header, ObjectType}, + }, +}; /// Tool invocation status. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] @@ -83,7 +90,7 @@ impl ToolInvocation { tool_name: impl Into, ) -> Result { Ok(Self { - header: Header::new(AiObjectType::ToolInvocation, repo_id, created_by)?, + header: Header::new(ObjectType::ToolInvocation, repo_id, created_by)?, run_id, tool_name: tool_name.into(), io_footprint: None, @@ -147,6 +154,33 @@ impl ToolInvocation { } } +impl fmt::Display for ToolInvocation { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ToolInvocation: {}", self.header.object_id()) + } +} + +impl ObjectTrait for ToolInvocation { + fn from_bytes(data: &[u8], _hash: ObjectHash) -> Result + where + Self: Sized, + { + serde_json::from_slice(data).map_err(|e| GitError::InvalidObjectInfo(e.to_string())) + } + + fn get_type(&self) -> ObjectType { + ObjectType::ToolInvocation + } + + fn get_size(&self) -> usize { + serde_json::to_vec(self).map(|v| v.len()).unwrap_or(0) + } + + fn to_data(&self) -> Result, GitError> { + serde_json::to_vec(self).map_err(|e| GitError::InvalidObjectInfo(e.to_string())) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/internal/object/tree.rs b/src/internal/object/tree.rs index ff4c6f00..b942dfd9 100644 --- a/src/internal/object/tree.rs +++ b/src/internal/object/tree.rs @@ -328,7 +328,7 @@ impl ObjectTrait for Tree { } fn get_size(&self) -> usize { - todo!() + self.to_data().map(|data| data.len()).unwrap_or(0) } fn to_data(&self) -> Result, GitError> { diff --git a/src/internal/object/types.rs b/src/internal/object/types.rs index 607d6e5d..c5002613 100644 --- a/src/internal/object/types.rs +++ b/src/internal/object/types.rs @@ -1,12 +1,31 @@ -//! Object type enumeration plus conversions used across pack/object modules for printing, -//! serialization, and delta bookkeeping. +//! Object type enumeration and AI Object Header Definition. +//! +//! This module defines the common metadata header shared by all AI process objects +//! and the object type enumeration used across pack/object modules. -use std::fmt::Display; +use std::{ + collections::HashMap, + fmt::{self, Display}, +}; +use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; +use uuid::Uuid; +use super::integrity::{IntegrityHash, compute_integrity_hash}; use crate::errors::GitError; +/// Visibility of an AI process object. +/// +/// Determines whether the object is accessible only within the project (Private) +/// or can be shared externally (Public). +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum Visibility { + Private, + Public, +} + /// In Git, each object type is assigned a unique integer value, which is used to identify the /// type of the object in Git repositories. /// @@ -26,6 +45,7 @@ use crate::errors::GitError; /// repository, Git can use the integer value of an object's type to determine how to parse /// the object's content. #[derive(PartialEq, Eq, Hash, Debug, Clone, Copy, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] pub enum ObjectType { Commit = 1, Tree, @@ -34,12 +54,30 @@ pub enum ObjectType { OffsetZstdelta, // Private extension for Zstandard-compressed delta objects OffsetDelta, HashDelta, + ContextSnapshot, + Decision, + Evidence, + PatchSet, + Plan, + Provenance, + Run, + Task, + ToolInvocation, } const COMMIT_OBJECT_TYPE: &[u8] = b"commit"; const TREE_OBJECT_TYPE: &[u8] = b"tree"; const BLOB_OBJECT_TYPE: &[u8] = b"blob"; const TAG_OBJECT_TYPE: &[u8] = b"tag"; +const CONTEXT_SNAPSHOT_OBJECT_TYPE: &[u8] = b"snapshot"; +const DECISION_OBJECT_TYPE: &[u8] = b"decision"; +const EVIDENCE_OBJECT_TYPE: &[u8] = b"evidence"; +const PATCH_SET_OBJECT_TYPE: &[u8] = b"patchset"; +const PLAN_OBJECT_TYPE: &[u8] = b"plan"; +const PROVENANCE_OBJECT_TYPE: &[u8] = b"provenance"; +const RUN_OBJECT_TYPE: &[u8] = b"run"; +const TASK_OBJECT_TYPE: &[u8] = b"task"; +const TOOL_INVOCATION_OBJECT_TYPE: &[u8] = b"invocation"; /// Display trait for Git objects type impl Display for ObjectType { @@ -52,6 +90,15 @@ impl Display for ObjectType { ObjectType::OffsetZstdelta => write!(f, "OffsetZstdelta"), ObjectType::OffsetDelta => write!(f, "OffsetDelta"), ObjectType::HashDelta => write!(f, "HashDelta"), + ObjectType::ContextSnapshot => write!(f, "snapshot"), + ObjectType::Decision => write!(f, "decision"), + ObjectType::Evidence => write!(f, "evidence"), + ObjectType::PatchSet => write!(f, "patchset"), + ObjectType::Plan => write!(f, "plan"), + ObjectType::Provenance => write!(f, "provenance"), + ObjectType::Run => write!(f, "run"), + ObjectType::Task => write!(f, "task"), + ObjectType::ToolInvocation => write!(f, "invocation"), } } } @@ -64,6 +111,15 @@ impl ObjectType { ObjectType::Tree => TREE_OBJECT_TYPE, ObjectType::Blob => BLOB_OBJECT_TYPE, ObjectType::Tag => TAG_OBJECT_TYPE, + ObjectType::ContextSnapshot => CONTEXT_SNAPSHOT_OBJECT_TYPE, + ObjectType::Decision => DECISION_OBJECT_TYPE, + ObjectType::Evidence => EVIDENCE_OBJECT_TYPE, + ObjectType::PatchSet => PATCH_SET_OBJECT_TYPE, + ObjectType::Plan => PLAN_OBJECT_TYPE, + ObjectType::Provenance => PROVENANCE_OBJECT_TYPE, + ObjectType::Run => RUN_OBJECT_TYPE, + ObjectType::Task => TASK_OBJECT_TYPE, + ObjectType::ToolInvocation => TOOL_INVOCATION_OBJECT_TYPE, _ => panic!("can put compute the delta hash value"), } } @@ -75,6 +131,15 @@ impl ObjectType { "tree" => Ok(ObjectType::Tree), "commit" => Ok(ObjectType::Commit), "tag" => Ok(ObjectType::Tag), + "snapshot" => Ok(ObjectType::ContextSnapshot), + "decision" => Ok(ObjectType::Decision), + "evidence" => Ok(ObjectType::Evidence), + "patchset" => Ok(ObjectType::PatchSet), + "plan" => Ok(ObjectType::Plan), + "provenance" => Ok(ObjectType::Provenance), + "run" => Ok(ObjectType::Run), + "task" => Ok(ObjectType::Task), + "invocation" => Ok(ObjectType::ToolInvocation), _ => Err(GitError::InvalidObjectType(s.to_string())), } } @@ -82,10 +147,23 @@ impl ObjectType { /// Convert an object type to a byte array. pub fn to_data(self) -> Result, GitError> { match self { - ObjectType::Blob => Ok(vec![0x62, 0x6c, 0x6f, 0x62]), - ObjectType::Tree => Ok(vec![0x74, 0x72, 0x65, 0x65]), - ObjectType::Commit => Ok(vec![0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74]), - ObjectType::Tag => Ok(vec![0x74, 0x61, 0x67]), + ObjectType::Blob => Ok(vec![0x62, 0x6c, 0x6f, 0x62]), // blob + ObjectType::Tree => Ok(vec![0x74, 0x72, 0x65, 0x65]), // tree + ObjectType::Commit => Ok(vec![0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74]), // commit + ObjectType::Tag => Ok(vec![0x74, 0x61, 0x67]), // tag + ObjectType::ContextSnapshot => Ok(vec![0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74]), // snapshot + ObjectType::Decision => Ok(vec![0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e]), // decision + ObjectType::Evidence => Ok(vec![0x65, 0x76, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65]), // evidence + ObjectType::PatchSet => Ok(vec![0x70, 0x61, 0x74, 0x63, 0x68, 0x73, 0x65, 0x74]), // patchset + ObjectType::Plan => Ok(vec![0x70, 0x6c, 0x61, 0x6e]), // plan + ObjectType::Provenance => Ok(vec![ + 0x70, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, + ]), // provenance + ObjectType::Run => Ok(vec![0x72, 0x75, 0x6e]), // run + ObjectType::Task => Ok(vec![0x74, 0x61, 0x73, 0x6b]), // task + ObjectType::ToolInvocation => Ok(vec![ + 0x69, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + ]), // invocation _ => Err(GitError::InvalidObjectType(self.to_string())), } } @@ -100,6 +178,15 @@ impl ObjectType { ObjectType::OffsetZstdelta => 5, // Type 5 is reserved in standard Git packs; we use it for Zstd delta objects. ObjectType::OffsetDelta => 6, ObjectType::HashDelta => 7, + ObjectType::ContextSnapshot => 8, + ObjectType::Decision => 9, + ObjectType::Evidence => 10, + ObjectType::PatchSet => 11, + ObjectType::Plan => 12, + ObjectType::Provenance => 13, + ObjectType::Run => 14, + ObjectType::Task => 15, + ObjectType::ToolInvocation => 16, } } @@ -113,6 +200,15 @@ impl ObjectType { 5 => Ok(ObjectType::OffsetZstdelta), 6 => Ok(ObjectType::OffsetDelta), 7 => Ok(ObjectType::HashDelta), + 8 => Ok(ObjectType::ContextSnapshot), + 9 => Ok(ObjectType::Decision), + 10 => Ok(ObjectType::Evidence), + 11 => Ok(ObjectType::PatchSet), + 12 => Ok(ObjectType::Plan), + 13 => Ok(ObjectType::Provenance), + 14 => Ok(ObjectType::Run), + 15 => Ok(ObjectType::Task), + 16 => Ok(ObjectType::ToolInvocation), _ => Err(GitError::InvalidObjectType(format!( "Invalid object type number: {number}" ))), @@ -128,13 +224,446 @@ impl ObjectType { ObjectType::HashDelta => false, ObjectType::OffsetZstdelta => false, ObjectType::OffsetDelta => false, + ObjectType::ContextSnapshot => true, + ObjectType::Decision => true, + ObjectType::Evidence => true, + ObjectType::PatchSet => true, + ObjectType::Plan => true, + ObjectType::Provenance => true, + ObjectType::Run => true, + ObjectType::Task => true, + ObjectType::ToolInvocation => true, + } + } +} + +/// Actor kind enum +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum ActorKind { + Human, + Agent, + System, + McpClient, + #[serde(untagged)] + Other(String), +} + +impl fmt::Display for ActorKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ActorKind::Human => write!(f, "human"), + ActorKind::Agent => write!(f, "agent"), + ActorKind::System => write!(f, "system"), + ActorKind::McpClient => write!(f, "mcp_client"), + ActorKind::Other(s) => write!(f, "{}", s), + } + } +} + +impl From for ActorKind { + fn from(s: String) -> Self { + match s.as_str() { + "human" => ActorKind::Human, + "agent" => ActorKind::Agent, + "system" => ActorKind::System, + "mcp_client" => ActorKind::McpClient, + _ => ActorKind::Other(s), + } + } +} + +impl From<&str> for ActorKind { + fn from(s: &str) -> Self { + match s { + "human" => ActorKind::Human, + "agent" => ActorKind::Agent, + "system" => ActorKind::System, + "mcp_client" => ActorKind::McpClient, + _ => ActorKind::Other(s.to_string()), + } + } +} + +/// Actor reference (who created/triggered). +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct ActorRef { + /// Kind: human/agent/system/mcp_client + kind: ActorKind, + /// Subject ID (user/agent name or client ID) + id: String, + /// Display name (optional) + display_name: Option, + /// Auth context (optional, Libra usually empty) + auth_context: Option, +} + +impl ActorRef { + /// Create a new ActorRef with validation. + pub fn new(kind: impl Into, id: impl Into) -> Result { + let id_str = id.into(); + if id_str.trim().is_empty() { + return Err("Actor ID cannot be empty".to_string()); + } + Ok(Self { + kind: kind.into(), + id: id_str, + display_name: None, + auth_context: None, + }) + } + + /// Create an MCP client actor reference (MCP writes must use this). + pub fn new_for_mcp(id: impl Into) -> Result { + Self::new(ActorKind::McpClient, id) + } + + /// Validate that this actor is an MCP client. + pub fn ensure_mcp_client(&self) -> Result<(), String> { + if self.kind != ActorKind::McpClient { + return Err("MCP writes must use mcp_client actor kind".to_string()); + } + Ok(()) + } + + pub fn kind(&self) -> &ActorKind { + &self.kind + } + + pub fn id(&self) -> &str { + &self.id + } + + pub fn display_name(&self) -> Option<&str> { + self.display_name.as_deref() + } + + pub fn auth_context(&self) -> Option<&str> { + self.auth_context.as_deref() + } + + pub fn set_display_name(&mut self, display_name: Option) { + self.display_name = display_name; + } + + pub fn set_auth_context(&mut self, auth_context: Option) { + self.auth_context = auth_context; + } + + /// Create a human actor reference. + pub fn human(id: impl Into) -> Result { + Self::new(ActorKind::Human, id) + } + + /// Create an agent actor reference. + pub fn agent(name: impl Into) -> Result { + Self::new(ActorKind::Agent, name) + } + + /// Create a system component actor reference. + pub fn system(component: impl Into) -> Result { + Self::new(ActorKind::System, component) + } + + /// Create an MCP client actor reference. + pub fn mcp_client(client_id: impl Into) -> Result { + Self::new(ActorKind::McpClient, client_id) + } +} + +/// Artifact reference (external content). +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct ArtifactRef { + /// Store type: local_fs/s3 + store: String, + /// Storage key (e.g., path or object key) + key: String, + /// MIME type (optional) + content_type: Option, + /// Size in bytes (optional) + size_bytes: Option, + /// Content hash (strongly recommended) + hash: Option, + /// Expiration time (optional) + expires_at: Option>, +} + +impl ArtifactRef { + pub fn new(store: impl Into, key: impl Into) -> Result { + let store = store.into(); + let key = key.into(); + if store.trim().is_empty() { + return Err("store cannot be empty".to_string()); + } + if key.trim().is_empty() { + return Err("key cannot be empty".to_string()); + } + Ok(Self { + store, + key, + content_type: None, + size_bytes: None, + hash: None, + expires_at: None, + }) + } + + pub fn store(&self) -> &str { + &self.store + } + + pub fn key(&self) -> &str { + &self.key + } + + pub fn content_type(&self) -> Option<&str> { + self.content_type.as_deref() + } + + pub fn size_bytes(&self) -> Option { + self.size_bytes + } + + pub fn hash(&self) -> Option<&IntegrityHash> { + self.hash.as_ref() + } + + pub fn expires_at(&self) -> Option> { + self.expires_at + } + + /// Calculate hash for the given content bytes. + pub fn compute_hash(content: &[u8]) -> IntegrityHash { + IntegrityHash::compute(content) + } + + /// Set the hash directly. + pub fn with_hash(mut self, hash: IntegrityHash) -> Self { + self.hash = Some(hash); + self + } + + /// Set the hash from a hex string. + pub fn with_hash_hex(mut self, hash: impl AsRef) -> Result { + let hash = hash.as_ref().parse()?; + self.hash = Some(hash); + Ok(self) + } + + pub fn set_content_type(&mut self, content_type: Option) { + self.content_type = content_type; + } + + pub fn set_size_bytes(&mut self, size_bytes: Option) { + self.size_bytes = size_bytes; + } + + pub fn set_expires_at(&mut self, expires_at: Option>) { + self.expires_at = expires_at; + } + + /// Verify if the provided content matches the stored checksum + #[must_use = "handle integrity verification result"] + pub fn verify_integrity(&self, content: &[u8]) -> Result { + let stored_hash = self + .hash + .as_ref() + .ok_or_else(|| "No hash stored in ArtifactRef".to_string())?; + + Ok(IntegrityHash::compute(content) == *stored_hash) + } + + /// Check if two artifacts have the same content based on checksum + #[must_use] + pub fn content_eq(&self, other: &Self) -> Option { + match (&self.hash, &other.hash) { + (Some(a), Some(b)) => Some(a == b), + _ => None, + } + } + + /// Check if the artifact has expired + #[must_use] + pub fn is_expired(&self) -> bool { + if let Some(expires_at) = self.expires_at { + expires_at < Utc::now() + } else { + false + } + } +} + +/// Header shared by all AI Process Objects. +/// +/// Contains standard metadata like ID, type, creator, and timestamps. +/// +/// # Usage +/// +/// Every AI object struct should flatten this header: +/// +/// ```rust,ignore +/// #[derive(Serialize, Deserialize)] +/// pub struct MyObject { +/// #[serde(flatten)] +/// header: Header, +/// // specific fields... +/// } +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct Header { + /// Global unique ID (UUID v7) + object_id: Uuid, + /// Object type (task/run/patchset/...) + object_type: ObjectType, + /// Model version + schema_version: u32, + /// Repository identifier + repo_id: Uuid, + /// Creation time + created_at: DateTime, + /// Creator + created_by: ActorRef, + /// Visibility (fixed to private for Libra) + visibility: Visibility, + /// Search tags + #[serde(default)] + tags: HashMap, + /// External ID mapping + #[serde(default)] + external_ids: HashMap, + /// Content checksum (optional) + #[serde(default)] + checksum: Option, +} + +impl Header { + /// Create a new Header with default values. + /// + /// # Arguments + /// + /// * `object_type` - The specific type of the AI object. + /// * `repo_id` - The UUID of the repository this object belongs to. + /// * `created_by` - The actor (human/agent) creating this object. + pub fn new( + object_type: ObjectType, + repo_id: Uuid, + created_by: ActorRef, + ) -> Result { + Ok(Self { + object_id: Uuid::now_v7(), + object_type, + schema_version: 1, + repo_id, + created_at: Utc::now(), + created_by, + visibility: Visibility::Private, + tags: HashMap::new(), + external_ids: HashMap::new(), + checksum: None, + }) + } + + pub fn object_id(&self) -> Uuid { + self.object_id + } + + pub fn object_type(&self) -> &ObjectType { + &self.object_type + } + + pub fn schema_version(&self) -> u32 { + self.schema_version + } + + pub fn repo_id(&self) -> Uuid { + self.repo_id + } + + pub fn created_at(&self) -> DateTime { + self.created_at + } + + pub fn created_by(&self) -> &ActorRef { + &self.created_by + } + + pub fn visibility(&self) -> &Visibility { + &self.visibility + } + + pub fn tags(&self) -> &HashMap { + &self.tags + } + + pub fn tags_mut(&mut self) -> &mut HashMap { + &mut self.tags + } + + pub fn external_ids(&self) -> &HashMap { + &self.external_ids + } + + pub fn external_ids_mut(&mut self) -> &mut HashMap { + &mut self.external_ids + } + + pub fn set_object_id(&mut self, object_id: Uuid) { + self.object_id = object_id; + } + + pub fn set_object_type(&mut self, object_type: ObjectType) -> Result<(), String> { + self.object_type = object_type; + Ok(()) + } + + pub fn set_schema_version(&mut self, schema_version: u32) -> Result<(), String> { + if schema_version == 0 { + return Err("schema_version must be greater than 0".to_string()); + } + self.schema_version = schema_version; + Ok(()) + } + + pub fn set_created_at(&mut self, created_at: DateTime) { + self.created_at = created_at; + } + + pub fn set_visibility(&mut self, visibility: Visibility) { + self.visibility = visibility; + } + + /// Accessor for checksum + pub fn checksum(&self) -> Option<&IntegrityHash> { + self.checksum.as_ref() + } + + /// Seal the header by calculating and setting the checksum of the provided object. + /// The checksum field is temporarily cleared to keep sealing idempotent. + /// + /// This is typically called just before storing the object to ensure `checksum` matches content. + pub fn seal(&mut self, object: &T) -> Result<(), serde_json::Error> { + let previous = self.checksum.take(); + match compute_integrity_hash(object) { + Ok(checksum) => { + self.checksum = Some(checksum); + Ok(()) + } + Err(err) => { + self.checksum = previous; + Err(err) + } } } } #[cfg(test)] mod tests { - use crate::internal::object::types::ObjectType; + use chrono::{DateTime, Utc}; + use uuid::Uuid; + + use crate::internal::object::types::{ + ActorKind, ActorRef, ArtifactRef, Header, IntegrityHash, ObjectType, + }; /// Verify ObjectType::Blob converts to its ASCII byte representation "blob". #[test] @@ -147,8 +676,42 @@ mod tests { /// Verify parsing "tree" string returns ObjectType::Tree. #[test] fn test_object_type_from_string() { - let tree = ObjectType::from_string("tree").unwrap(); - assert_eq!(tree, ObjectType::Tree); + assert_eq!(ObjectType::from_string("blob").unwrap(), ObjectType::Blob); + assert_eq!(ObjectType::from_string("tree").unwrap(), ObjectType::Tree); + assert_eq!( + ObjectType::from_string("commit").unwrap(), + ObjectType::Commit + ); + assert_eq!(ObjectType::from_string("tag").unwrap(), ObjectType::Tag); + assert_eq!( + ObjectType::from_string("snapshot").unwrap(), + ObjectType::ContextSnapshot + ); + assert_eq!( + ObjectType::from_string("decision").unwrap(), + ObjectType::Decision + ); + assert_eq!( + ObjectType::from_string("evidence").unwrap(), + ObjectType::Evidence + ); + assert_eq!( + ObjectType::from_string("patchset").unwrap(), + ObjectType::PatchSet + ); + assert_eq!(ObjectType::from_string("plan").unwrap(), ObjectType::Plan); + assert_eq!( + ObjectType::from_string("provenance").unwrap(), + ObjectType::Provenance + ); + assert_eq!(ObjectType::from_string("run").unwrap(), ObjectType::Run); + assert_eq!(ObjectType::from_string("task").unwrap(), ObjectType::Task); + assert_eq!( + ObjectType::from_string("invocation").unwrap(), + ObjectType::ToolInvocation + ); + + assert!(ObjectType::from_string("invalid_type").is_err()); } /// Verify ObjectType::Commit converts to pack type number 1. @@ -166,4 +729,141 @@ mod tests { let tag = ObjectType::from_u8(tag_number).unwrap(); assert_eq!(tag, ObjectType::Tag); } + + #[test] + fn test_header_serialization() { + let repo_id = Uuid::from_u128(0x0123456789abcdef0123456789abcdef); + let actor = ActorRef::human("jackie").expect("actor"); + let header = Header::new(ObjectType::Task, repo_id, actor).expect("header"); + + let json = serde_json::to_string(&header).unwrap(); + let deserialized: Header = serde_json::from_str(&json).unwrap(); + + assert_eq!(header.object_id(), deserialized.object_id()); + assert_eq!(header.object_type(), deserialized.object_type()); + assert_eq!(header.repo_id(), deserialized.repo_id()); + } + + #[test] + fn test_actor_ref() { + let actor = ActorRef::agent("coder").expect("actor"); + assert_eq!(actor.kind(), &ActorKind::Agent); + assert_eq!(actor.id(), "coder"); + + let sys = ActorRef::system("scheduler").expect("system"); + assert_eq!(sys.kind(), &ActorKind::System); + + let client = ActorRef::mcp_client("vscode").expect("client"); + assert_eq!(client.kind(), &ActorKind::McpClient); + assert!(client.ensure_mcp_client().is_ok()); + + let non_mcp = ActorRef::human("jackie").expect("actor"); + assert!(non_mcp.ensure_mcp_client().is_err()); + } + + #[test] + fn test_actor_kind_serialization() { + let k = ActorKind::McpClient; + let s = serde_json::to_string(&k).unwrap(); + assert_eq!(s, "\"mcp_client\""); + + let k2: ActorKind = serde_json::from_str("\"system\"").unwrap(); + assert_eq!(k2, ActorKind::System); + } + + #[test] + fn test_header_checksum() { + let repo_id = Uuid::from_u128(0x0123456789abcdef0123456789abcdef); + let actor = ActorRef::human("jackie").expect("actor"); + let mut header = Header::new(ObjectType::Task, repo_id, actor).expect("header"); + // Fix time for deterministic checksum + header.set_created_at( + DateTime::parse_from_rfc3339("2026-02-10T00:00:00Z") + .unwrap() + .with_timezone(&Utc), + ); + header.set_object_id(Uuid::from_u128(0x00000000000000000000000000000001)); + + let checksum = + crate::internal::object::integrity::compute_integrity_hash(&header).expect("checksum"); + assert_eq!(checksum.to_hex().len(), 64); // SHA256 length + + // Ensure changes change checksum + header + .set_object_type(ObjectType::Run) + .expect("object_type"); + let checksum2 = + crate::internal::object::integrity::compute_integrity_hash(&header).expect("checksum"); + assert_ne!(checksum, checksum2); + } + + #[test] + fn test_artifact_checksum() { + let content = b"hello world"; + let hash = ArtifactRef::compute_hash(content); + // echo -n "hello world" | shasum -a 256 + let expected_str = "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"; + assert_eq!(hash.to_hex(), expected_str); + + let artifact = ArtifactRef::new("s3", "key") + .expect("artifact") + .with_hash(hash); + assert_eq!(artifact.hash(), Some(&hash)); + + // Integrity check + assert!(artifact.verify_integrity(content).unwrap()); + assert!(!artifact.verify_integrity(b"wrong").unwrap()); + + // Deduplication + let artifact2 = ArtifactRef::new("local", "other/path") + .expect("artifact") + .with_hash(IntegrityHash::compute(content)); + assert_eq!(artifact.content_eq(&artifact2), Some(true)); + + let artifact3 = ArtifactRef::new("s3", "diff") + .expect("artifact") + .with_hash(ArtifactRef::compute_hash(b"diff")); + assert_eq!(artifact.content_eq(&artifact3), Some(false)); + } + + #[test] + fn test_invalid_checksum() { + let result = ArtifactRef::new("s3", "key") + .expect("artifact") + .with_hash_hex("bad_hash"); + assert!(result.is_err()); + } + + #[test] + fn test_header_seal() { + let repo_id = Uuid::from_u128(0x0123456789abcdef0123456789abcdef); + let actor = ActorRef::human("jackie").expect("actor"); + let mut header = Header::new(ObjectType::Task, repo_id, actor).expect("header"); + + let content = serde_json::json!({"key": "value"}); + header.seal(&content).expect("seal"); + + assert!(header.checksum().is_some()); + let expected = + crate::internal::object::integrity::compute_integrity_hash(&content).expect("checksum"); + assert_eq!(header.checksum().expect("checksum"), &expected); + } + + #[test] + fn test_empty_actor_id() { + let result = ActorRef::new(ActorKind::Human, " "); + assert!(result.is_err()); + } + + #[test] + fn test_artifact_expiration() { + let mut artifact = ArtifactRef::new("s3", "key").expect("artifact"); + assert!(!artifact.is_expired()); + + artifact.set_expires_at(Some(Utc::now() - chrono::Duration::hours(1))); + assert!(artifact.is_expired()); + + artifact.set_expires_at(Some(Utc::now() + chrono::Duration::hours(1))); + assert!(!artifact.is_expired()); + } } diff --git a/src/internal/pack/decode.rs b/src/internal/pack/decode.rs index 4159cbf0..701a3a2f 100644 --- a/src/internal/pack/decode.rs +++ b/src/internal/pack/decode.rs @@ -20,7 +20,6 @@ use threadpool::ThreadPool; use tokio::sync::mpsc::UnboundedSender; use uuid::Uuid; -use super::cache_object::CacheObjectInfo; use crate::{ errors::GitError, hash::{ObjectHash, get_hash_kind, set_hash_kind}, @@ -30,7 +29,7 @@ use crate::{ pack::{ DEFAULT_TMP_DIR, Pack, cache::{_Cache, Caches}, - cache_object::{CacheObject, MemSizeRecorder}, + cache_object::{CacheObject, CacheObjectInfo, MemSizeRecorder}, channel_reader::StreamBufReader, entry::Entry, utils, @@ -291,7 +290,7 @@ impl Pack { pub fn decode_pack_object( pack: &mut (impl BufRead + Send), offset: &mut usize, - ) -> Result { + ) -> Result, GitError> { let init_offset = *offset; let mut hasher = crc32fast::Hasher::new(); let mut reader = CrcCountingReader { @@ -319,12 +318,24 @@ impl Pack { let (data, raw_size) = Pack::decompress_data(&mut reader, size)?; *offset += raw_size; let crc32 = hasher.finalize(); - Ok(CacheObject::new_for_undeltified( + Ok(Some(CacheObject::new_for_undeltified( t, data, init_offset, crc32, - )) + ))) + } + ObjectType::ContextSnapshot + | ObjectType::Decision + | ObjectType::Evidence + | ObjectType::PatchSet + | ObjectType::Plan + | ObjectType::Provenance + | ObjectType::Run + | ObjectType::Task + | ObjectType::ToolInvocation => { + // Wait for encode to implement corresponding compression + Ok(None) } ObjectType::OffsetDelta | ObjectType::OffsetZstdelta => { let (delta_offset, bytes) = utils::read_offset_encoding(&mut reader).unwrap(); @@ -354,14 +365,14 @@ impl Pack { _ => unreachable!(), }; let crc32 = hasher.finalize(); - Ok(CacheObject { + Ok(Some(CacheObject { info: obj_info, offset: init_offset, crc32, data_decompressed: data, mem_recorder: None, is_delta_in_pack: true, - }) + })) } ObjectType::HashDelta => { // Read hash bytes to get the reference object hash(size depends on hash kind,e.g.,20 for SHA1,32 for SHA256) @@ -377,14 +388,14 @@ impl Pack { let crc32 = hasher.finalize(); - Ok(CacheObject { + Ok(Some(CacheObject { info: CacheObjectInfo::HashDelta(ref_sha, final_size), offset: init_offset, crc32, data_decompressed: data, mem_recorder: None, is_delta_in_pack: true, - }) + })) } } } @@ -454,10 +465,10 @@ impl Pack { { thread::yield_now(); } - let r: Result = + let r: Result, GitError> = Pack::decode_pack_object(&mut reader, &mut offset); match r { - Ok(mut obj) => { + Ok(Some(mut obj)) => { obj.set_mem_recorder(self.cache_objs_mem.clone()); obj.record_mem_size(); @@ -506,6 +517,7 @@ impl Pack { } }); } + Ok(None) => {} Err(e) => { return Err(e); } @@ -540,7 +552,8 @@ impl Pack { // So that files != self.number assert_eq!(self.waitlist.map_offset.len(), 0); assert_eq!(self.waitlist.map_ref.len(), 0); - assert_eq!(self.number, caches.total_inserted()); + // Because we may skip some objects (e.g. AI objects), we use >= instead of == + assert!(self.number >= caches.total_inserted()); tracing::info!( "The pack file has been decoded successfully, takes: [ {:?} ]", time.elapsed() diff --git a/third-party/rust/crates/android_system_properties/0.1.5/BUCK b/third-party/rust/crates/android_system_properties/0.1.5/BUCK index 9c9b4197..bece16ed 100644 --- a/third-party/rust/crates/android_system_properties/0.1.5/BUCK +++ b/third-party/rust/crates/android_system_properties/0.1.5/BUCK @@ -26,5 +26,5 @@ rust_library( compatible_with = ["prelude//os/constraints:linux"], rustc_flags = ["@$(location :android_system_properties-manifest[env_flags])"], visibility = ["PUBLIC"], - deps = ["//third-party/rust/crates/libc/0.2.180:libc"], + deps = ["//third-party/rust/crates/libc/0.2.181:libc"], ) diff --git a/third-party/rust/crates/axum-core/0.5.5/BUCK b/third-party/rust/crates/axum-core/0.5.5/BUCK index 0c9c6be5..82c58d9e 100644 --- a/third-party/rust/crates/axum-core/0.5.5/BUCK +++ b/third-party/rust/crates/axum-core/0.5.5/BUCK @@ -27,7 +27,7 @@ rust_library( rustc_flags = ["@$(location :axum-core-manifest[env_flags])"], visibility = ["PUBLIC"], deps = [ - "//third-party/rust/crates/bytes/1.11.0:bytes", + "//third-party/rust/crates/bytes/1.11.1:bytes", "//third-party/rust/crates/futures-core/0.3.31:futures-core", "//third-party/rust/crates/http-body-util/0.1.3:http-body-util", "//third-party/rust/crates/http-body/1.0.1:http-body", diff --git a/third-party/rust/crates/axum/0.8.8/BUCK b/third-party/rust/crates/axum/0.8.8/BUCK index 92571112..5860d2ed 100644 --- a/third-party/rust/crates/axum/0.8.8/BUCK +++ b/third-party/rust/crates/axum/0.8.8/BUCK @@ -41,7 +41,7 @@ rust_library( deps = [ "//third-party/rust/crates/axum-core/0.5.5:axum-core", "//third-party/rust/crates/axum-macros/0.5.0:axum-macros", - "//third-party/rust/crates/bytes/1.11.0:bytes", + "//third-party/rust/crates/bytes/1.11.1:bytes", "//third-party/rust/crates/form_urlencoded/1.2.2:form_urlencoded", "//third-party/rust/crates/futures-util/0.3.31:futures-util", "//third-party/rust/crates/http-body-util/0.1.3:http-body-util", diff --git a/third-party/rust/crates/bytes/1.11.0/BUCK b/third-party/rust/crates/bytes/1.11.1/BUCK similarity index 77% rename from third-party/rust/crates/bytes/1.11.0/BUCK rename to third-party/rust/crates/bytes/1.11.1/BUCK index 22aa1304..554d3596 100644 --- a/third-party/rust/crates/bytes/1.11.0/BUCK +++ b/third-party/rust/crates/bytes/1.11.1/BUCK @@ -5,10 +5,10 @@ load("@buckal//:wrapper.bzl", "rust_library") http_archive( name = "bytes-vendor", - urls = ["https://static.crates.io/crates/bytes/bytes-1.11.0.crate"], - sha256 = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3", + urls = ["https://static.crates.io/crates/bytes/bytes-1.11.1.crate"], + sha256 = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33", type = "tar.gz", - strip_prefix = "bytes-1.11.0", + strip_prefix = "bytes-1.11.1", out = "vendor", ) diff --git a/third-party/rust/crates/cc/1.2.49/BUCK b/third-party/rust/crates/cc/1.2.49/BUCK index 6e570ee1..d43edd33 100644 --- a/third-party/rust/crates/cc/1.2.49/BUCK +++ b/third-party/rust/crates/cc/1.2.49/BUCK @@ -26,8 +26,8 @@ rust_library( features = ["parallel"], rustc_flags = ["@$(location :cc-manifest[env_flags])"], os_deps = { - "linux": ["//third-party/rust/crates/libc/0.2.180:libc"], - "macos": ["//third-party/rust/crates/libc/0.2.180:libc"], + "linux": ["//third-party/rust/crates/libc/0.2.181:libc"], + "macos": ["//third-party/rust/crates/libc/0.2.181:libc"], }, visibility = ["PUBLIC"], deps = [ diff --git a/third-party/rust/crates/clang-sys/1.8.1/BUCK b/third-party/rust/crates/clang-sys/1.8.1/BUCK index 329fe50c..f06395c8 100644 --- a/third-party/rust/crates/clang-sys/1.8.1/BUCK +++ b/third-party/rust/crates/clang-sys/1.8.1/BUCK @@ -50,7 +50,7 @@ rust_library( visibility = ["PUBLIC"], deps = [ "//third-party/rust/crates/glob/0.3.3:glob", - "//third-party/rust/crates/libc/0.2.180:libc", + "//third-party/rust/crates/libc/0.2.181:libc", "//third-party/rust/crates/libloading/0.8.9:libloading", ], ) diff --git a/third-party/rust/crates/cpufeatures/0.2.17/BUCK b/third-party/rust/crates/cpufeatures/0.2.17/BUCK index d7b867f0..1d1b7ebb 100644 --- a/third-party/rust/crates/cpufeatures/0.2.17/BUCK +++ b/third-party/rust/crates/cpufeatures/0.2.17/BUCK @@ -25,7 +25,7 @@ rust_library( edition = "2018", rustc_flags = ["@$(location :cpufeatures-manifest[env_flags])"], os_deps = { - "macos": ["//third-party/rust/crates/libc/0.2.180:libc"], + "macos": ["//third-party/rust/crates/libc/0.2.181:libc"], }, visibility = ["PUBLIC"], ) diff --git a/third-party/rust/crates/errno/0.3.14/BUCK b/third-party/rust/crates/errno/0.3.14/BUCK index 8a8dba91..986fdfee 100644 --- a/third-party/rust/crates/errno/0.3.14/BUCK +++ b/third-party/rust/crates/errno/0.3.14/BUCK @@ -26,8 +26,8 @@ rust_library( features = ["std"], rustc_flags = ["@$(location :errno-manifest[env_flags])"], os_deps = { - "linux": ["//third-party/rust/crates/libc/0.2.180:libc"], - "macos": ["//third-party/rust/crates/libc/0.2.180:libc"], + "linux": ["//third-party/rust/crates/libc/0.2.181:libc"], + "macos": ["//third-party/rust/crates/libc/0.2.181:libc"], "windows": ["//third-party/rust/crates/windows-sys/0.61.2:windows-sys"], }, visibility = ["PUBLIC"], diff --git a/third-party/rust/crates/flate2/1.1.8/BUCK b/third-party/rust/crates/flate2/1.1.9/BUCK similarity index 84% rename from third-party/rust/crates/flate2/1.1.8/BUCK rename to third-party/rust/crates/flate2/1.1.9/BUCK index dd64b5bd..a3cb62d2 100644 --- a/third-party/rust/crates/flate2/1.1.8/BUCK +++ b/third-party/rust/crates/flate2/1.1.9/BUCK @@ -5,10 +5,10 @@ load("@buckal//:wrapper.bzl", "rust_library") http_archive( name = "flate2-vendor", - urls = ["https://static.crates.io/crates/flate2/flate2-1.1.8.crate"], - sha256 = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369", + urls = ["https://static.crates.io/crates/flate2/flate2-1.1.9.crate"], + sha256 = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c", type = "tar.gz", - strip_prefix = "flate2-1.1.8", + strip_prefix = "flate2-1.1.9", out = "vendor", ) diff --git a/third-party/rust/crates/getrandom/0.2.16/BUCK b/third-party/rust/crates/getrandom/0.2.16/BUCK index 51b0d168..12ac7be9 100644 --- a/third-party/rust/crates/getrandom/0.2.16/BUCK +++ b/third-party/rust/crates/getrandom/0.2.16/BUCK @@ -26,8 +26,8 @@ rust_library( features = ["std"], rustc_flags = ["@$(location :getrandom-manifest[env_flags])"], os_deps = { - "linux": ["//third-party/rust/crates/libc/0.2.180:libc"], - "macos": ["//third-party/rust/crates/libc/0.2.180:libc"], + "linux": ["//third-party/rust/crates/libc/0.2.181:libc"], + "macos": ["//third-party/rust/crates/libc/0.2.181:libc"], }, visibility = ["PUBLIC"], deps = ["//third-party/rust/crates/cfg-if/1.0.4:cfg-if"], diff --git a/third-party/rust/crates/getrandom/0.3.4/BUCK b/third-party/rust/crates/getrandom/0.3.4/BUCK index 2ade49a3..e083ba6e 100644 --- a/third-party/rust/crates/getrandom/0.3.4/BUCK +++ b/third-party/rust/crates/getrandom/0.3.4/BUCK @@ -32,8 +32,8 @@ rust_library( "@$(location :getrandom-manifest[env_flags])", ], os_deps = { - "linux": ["//third-party/rust/crates/libc/0.2.180:libc"], - "macos": ["//third-party/rust/crates/libc/0.2.180:libc"], + "linux": ["//third-party/rust/crates/libc/0.2.181:libc"], + "macos": ["//third-party/rust/crates/libc/0.2.181:libc"], }, visibility = ["PUBLIC"], deps = [ diff --git a/third-party/rust/crates/http-body-util/0.1.3/BUCK b/third-party/rust/crates/http-body-util/0.1.3/BUCK index 5f48ab77..256fef62 100644 --- a/third-party/rust/crates/http-body-util/0.1.3/BUCK +++ b/third-party/rust/crates/http-body-util/0.1.3/BUCK @@ -27,7 +27,7 @@ rust_library( rustc_flags = ["@$(location :http-body-util-manifest[env_flags])"], visibility = ["PUBLIC"], deps = [ - "//third-party/rust/crates/bytes/1.11.0:bytes", + "//third-party/rust/crates/bytes/1.11.1:bytes", "//third-party/rust/crates/futures-core/0.3.31:futures-core", "//third-party/rust/crates/http-body/1.0.1:http-body", "//third-party/rust/crates/http/1.4.0:http", diff --git a/third-party/rust/crates/http-body/1.0.1/BUCK b/third-party/rust/crates/http-body/1.0.1/BUCK index a058f4d6..e5cabdc2 100644 --- a/third-party/rust/crates/http-body/1.0.1/BUCK +++ b/third-party/rust/crates/http-body/1.0.1/BUCK @@ -26,7 +26,7 @@ rust_library( rustc_flags = ["@$(location :http-body-manifest[env_flags])"], visibility = ["PUBLIC"], deps = [ - "//third-party/rust/crates/bytes/1.11.0:bytes", + "//third-party/rust/crates/bytes/1.11.1:bytes", "//third-party/rust/crates/http/1.4.0:http", ], ) diff --git a/third-party/rust/crates/http/1.4.0/BUCK b/third-party/rust/crates/http/1.4.0/BUCK index 8c76bba4..119714ca 100644 --- a/third-party/rust/crates/http/1.4.0/BUCK +++ b/third-party/rust/crates/http/1.4.0/BUCK @@ -30,7 +30,7 @@ rust_library( rustc_flags = ["@$(location :http-manifest[env_flags])"], visibility = ["PUBLIC"], deps = [ - "//third-party/rust/crates/bytes/1.11.0:bytes", + "//third-party/rust/crates/bytes/1.11.1:bytes", "//third-party/rust/crates/itoa/1.0.15:itoa", ], ) diff --git a/third-party/rust/crates/hyper-util/0.1.19/BUCK b/third-party/rust/crates/hyper-util/0.1.19/BUCK index 8028896d..69a1c060 100644 --- a/third-party/rust/crates/hyper-util/0.1.19/BUCK +++ b/third-party/rust/crates/hyper-util/0.1.19/BUCK @@ -33,7 +33,7 @@ rust_library( rustc_flags = ["@$(location :hyper-util-manifest[env_flags])"], visibility = ["PUBLIC"], deps = [ - "//third-party/rust/crates/bytes/1.11.0:bytes", + "//third-party/rust/crates/bytes/1.11.1:bytes", "//third-party/rust/crates/futures-core/0.3.31:futures-core", "//third-party/rust/crates/http-body/1.0.1:http-body", "//third-party/rust/crates/http/1.4.0:http", diff --git a/third-party/rust/crates/hyper/1.8.1/BUCK b/third-party/rust/crates/hyper/1.8.1/BUCK index 00e830e3..c89574d0 100644 --- a/third-party/rust/crates/hyper/1.8.1/BUCK +++ b/third-party/rust/crates/hyper/1.8.1/BUCK @@ -32,7 +32,7 @@ rust_library( visibility = ["PUBLIC"], deps = [ "//third-party/rust/crates/atomic-waker/1.1.2:atomic-waker", - "//third-party/rust/crates/bytes/1.11.0:bytes", + "//third-party/rust/crates/bytes/1.11.1:bytes", "//third-party/rust/crates/futures-channel/0.3.31:futures-channel", "//third-party/rust/crates/futures-core/0.3.31:futures-core", "//third-party/rust/crates/http-body/1.0.1:http-body", diff --git a/third-party/rust/crates/jobserver/0.1.34/BUCK b/third-party/rust/crates/jobserver/0.1.34/BUCK index 1d21e9b1..f019571b 100644 --- a/third-party/rust/crates/jobserver/0.1.34/BUCK +++ b/third-party/rust/crates/jobserver/0.1.34/BUCK @@ -25,8 +25,8 @@ rust_library( edition = "2021", rustc_flags = ["@$(location :jobserver-manifest[env_flags])"], os_deps = { - "linux": ["//third-party/rust/crates/libc/0.2.180:libc"], - "macos": ["//third-party/rust/crates/libc/0.2.180:libc"], + "linux": ["//third-party/rust/crates/libc/0.2.181:libc"], + "macos": ["//third-party/rust/crates/libc/0.2.181:libc"], "windows": ["//third-party/rust/crates/getrandom/0.3.4:getrandom"], }, visibility = ["PUBLIC"], diff --git a/third-party/rust/crates/libc/0.2.180/BUCK b/third-party/rust/crates/libc/0.2.181/BUCK similarity index 87% rename from third-party/rust/crates/libc/0.2.180/BUCK rename to third-party/rust/crates/libc/0.2.181/BUCK index a3b314f2..c2d45b82 100644 --- a/third-party/rust/crates/libc/0.2.180/BUCK +++ b/third-party/rust/crates/libc/0.2.181/BUCK @@ -5,10 +5,10 @@ load("@buckal//:wrapper.bzl", "buildscript_run", "rust_binary", "rust_library") http_archive( name = "libc-vendor", - urls = ["https://static.crates.io/crates/libc/libc-0.2.180.crate"], - sha256 = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc", + urls = ["https://static.crates.io/crates/libc/libc-0.2.181.crate"], + sha256 = "459427e2af2b9c839b132acb702a1c654d95e10f8c326bfc2ad11310e458b1c5", type = "tar.gz", - strip_prefix = "libc-0.2.180", + strip_prefix = "libc-0.2.181", out = "vendor", ) @@ -60,7 +60,7 @@ buildscript_run( "default", "std", ], - version = "0.2.180", + version = "0.2.181", manifest_dir = ":libc-vendor", visibility = ["PUBLIC"], ) diff --git a/third-party/rust/crates/libredox/0.1.10/BUCK b/third-party/rust/crates/libredox/0.1.10/BUCK index 89784184..eee719ac 100644 --- a/third-party/rust/crates/libredox/0.1.10/BUCK +++ b/third-party/rust/crates/libredox/0.1.10/BUCK @@ -37,6 +37,6 @@ rust_library( visibility = ["PUBLIC"], deps = [ "//third-party/rust/crates/bitflags/2.10.0:bitflags", - "//third-party/rust/crates/libc/0.2.180:libc", + "//third-party/rust/crates/libc/0.2.181:libc", ], ) diff --git a/third-party/rust/crates/mio/1.1.1/BUCK b/third-party/rust/crates/mio/1.1.1/BUCK index 10f4cf77..7f8a2daf 100644 --- a/third-party/rust/crates/mio/1.1.1/BUCK +++ b/third-party/rust/crates/mio/1.1.1/BUCK @@ -30,8 +30,8 @@ rust_library( ], rustc_flags = ["@$(location :mio-manifest[env_flags])"], os_deps = { - "linux": ["//third-party/rust/crates/libc/0.2.180:libc"], - "macos": ["//third-party/rust/crates/libc/0.2.180:libc"], + "linux": ["//third-party/rust/crates/libc/0.2.181:libc"], + "macos": ["//third-party/rust/crates/libc/0.2.181:libc"], "windows": ["//third-party/rust/crates/windows-sys/0.61.2:windows-sys"], }, visibility = ["PUBLIC"], diff --git a/third-party/rust/crates/num_cpus/1.17.0/BUCK b/third-party/rust/crates/num_cpus/1.17.0/BUCK index 9b9f72f6..dd5d76b6 100644 --- a/third-party/rust/crates/num_cpus/1.17.0/BUCK +++ b/third-party/rust/crates/num_cpus/1.17.0/BUCK @@ -25,8 +25,8 @@ rust_library( edition = "2015", rustc_flags = ["@$(location :num_cpus-manifest[env_flags])"], os_deps = { - "linux": ["//third-party/rust/crates/libc/0.2.180:libc"], - "macos": ["//third-party/rust/crates/libc/0.2.180:libc"], + "linux": ["//third-party/rust/crates/libc/0.2.181:libc"], + "macos": ["//third-party/rust/crates/libc/0.2.181:libc"], }, visibility = ["PUBLIC"], ) diff --git a/third-party/rust/crates/parking_lot_core/0.9.12/BUCK b/third-party/rust/crates/parking_lot_core/0.9.12/BUCK index 3e0ce4b0..83450370 100644 --- a/third-party/rust/crates/parking_lot_core/0.9.12/BUCK +++ b/third-party/rust/crates/parking_lot_core/0.9.12/BUCK @@ -31,8 +31,8 @@ rust_library( "@$(location :parking_lot_core-manifest[env_flags])", ], os_deps = { - "linux": ["//third-party/rust/crates/libc/0.2.180:libc"], - "macos": ["//third-party/rust/crates/libc/0.2.180:libc"], + "linux": ["//third-party/rust/crates/libc/0.2.181:libc"], + "macos": ["//third-party/rust/crates/libc/0.2.181:libc"], "windows": ["//third-party/rust/crates/windows-link/0.2.1:windows-link"], }, visibility = ["PUBLIC"], diff --git a/third-party/rust/crates/rand/0.8.5/BUCK b/third-party/rust/crates/rand/0.8.5/BUCK index 1af0bbca..d70aa96c 100644 --- a/third-party/rust/crates/rand/0.8.5/BUCK +++ b/third-party/rust/crates/rand/0.8.5/BUCK @@ -34,8 +34,8 @@ rust_library( ], rustc_flags = ["@$(location :rand-manifest[env_flags])"], os_deps = { - "linux": ["//third-party/rust/crates/libc/0.2.180:libc"], - "macos": ["//third-party/rust/crates/libc/0.2.180:libc"], + "linux": ["//third-party/rust/crates/libc/0.2.181:libc"], + "macos": ["//third-party/rust/crates/libc/0.2.181:libc"], }, visibility = ["PUBLIC"], deps = [ diff --git a/third-party/rust/crates/rand_chacha/0.10.0/BUCK b/third-party/rust/crates/rand_chacha/0.10.0/BUCK new file mode 100644 index 00000000..7f9bb482 --- /dev/null +++ b/third-party/rust/crates/rand_chacha/0.10.0/BUCK @@ -0,0 +1,36 @@ +# @generated by `cargo buckal` + +load("@buckal//:cargo_manifest.bzl", "cargo_manifest") +load("@buckal//:wrapper.bzl", "rust_library") + +http_archive( + name = "rand_chacha-vendor", + urls = ["https://static.crates.io/crates/rand_chacha/rand_chacha-0.10.0.crate"], + sha256 = "3e6af7f3e25ded52c41df4e0b1af2d047e45896c2f3281792ed68a1c243daedb", + type = "tar.gz", + strip_prefix = "rand_chacha-0.10.0", + out = "vendor", +) + +cargo_manifest( + name = "rand_chacha-manifest", + vendor = ":rand_chacha-vendor", +) + +rust_library( + name = "rand_chacha", + srcs = [":rand_chacha-vendor"], + crate = "rand_chacha", + crate_root = "vendor/src/lib.rs", + edition = "2024", + features = [ + "default", + "std", + ], + rustc_flags = ["@$(location :rand_chacha-manifest[env_flags])"], + visibility = ["PUBLIC"], + deps = [ + "//third-party/rust/crates/ppv-lite86/0.2.21:ppv-lite86", + "//third-party/rust/crates/rand_core/0.10.0:rand_core", + ], +) diff --git a/third-party/rust/crates/rand_chacha/0.9.0/BUCK b/third-party/rust/crates/rand_chacha/0.9.0/BUCK index ff40299e..f2b715fe 100644 --- a/third-party/rust/crates/rand_chacha/0.9.0/BUCK +++ b/third-party/rust/crates/rand_chacha/0.9.0/BUCK @@ -23,10 +23,7 @@ rust_library( crate = "rand_chacha", crate_root = "vendor/src/lib.rs", edition = "2021", - features = [ - "default", - "std", - ], + features = ["std"], rustc_flags = ["@$(location :rand_chacha-manifest[env_flags])"], visibility = ["PUBLIC"], deps = [ diff --git a/third-party/rust/crates/rand_core/0.10.0/BUCK b/third-party/rust/crates/rand_core/0.10.0/BUCK new file mode 100644 index 00000000..52d6ac2c --- /dev/null +++ b/third-party/rust/crates/rand_core/0.10.0/BUCK @@ -0,0 +1,28 @@ +# @generated by `cargo buckal` + +load("@buckal//:cargo_manifest.bzl", "cargo_manifest") +load("@buckal//:wrapper.bzl", "rust_library") + +http_archive( + name = "rand_core-vendor", + urls = ["https://static.crates.io/crates/rand_core/rand_core-0.10.0.crate"], + sha256 = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba", + type = "tar.gz", + strip_prefix = "rand_core-0.10.0", + out = "vendor", +) + +cargo_manifest( + name = "rand_core-manifest", + vendor = ":rand_core-vendor", +) + +rust_library( + name = "rand_core", + srcs = [":rand_core-vendor"], + crate = "rand_core", + crate_root = "vendor/src/lib.rs", + edition = "2024", + rustc_flags = ["@$(location :rand_core-manifest[env_flags])"], + visibility = ["PUBLIC"], +) diff --git a/third-party/rust/crates/ring/0.17.14/BUCK b/third-party/rust/crates/ring/0.17.14/BUCK index 428bcc53..78ec25bc 100644 --- a/third-party/rust/crates/ring/0.17.14/BUCK +++ b/third-party/rust/crates/ring/0.17.14/BUCK @@ -36,7 +36,7 @@ rust_library( "@$(location :ring-manifest[env_flags])", ], os_deps = { - "macos": ["//third-party/rust/crates/libc/0.2.180:libc"], + "macos": ["//third-party/rust/crates/libc/0.2.181:libc"], }, visibility = ["PUBLIC"], deps = [ diff --git a/third-party/rust/crates/rkyv/0.7.45/BUCK b/third-party/rust/crates/rkyv/0.7.45/BUCK index c9b67cb4..84983602 100644 --- a/third-party/rust/crates/rkyv/0.7.45/BUCK +++ b/third-party/rust/crates/rkyv/0.7.45/BUCK @@ -40,14 +40,14 @@ rust_library( deps = [ "//third-party/rust/crates/bitvec/1.0.1:bitvec", "//third-party/rust/crates/bytecheck/0.6.12:bytecheck", - "//third-party/rust/crates/bytes/1.11.0:bytes", + "//third-party/rust/crates/bytes/1.11.1:bytes", "//third-party/rust/crates/hashbrown/0.12.3:hashbrown", "//third-party/rust/crates/ptr_meta/0.1.4:ptr_meta", "//third-party/rust/crates/rend/0.4.2:rend", "//third-party/rust/crates/rkyv_derive/0.7.45:rkyv_derive", "//third-party/rust/crates/seahash/4.1.0:seahash", "//third-party/rust/crates/tinyvec/1.10.0:tinyvec", - "//third-party/rust/crates/uuid/1.19.0:uuid", + "//third-party/rust/crates/uuid/1.20.0:uuid", ], ) diff --git a/third-party/rust/crates/rust_decimal/1.39.0/BUCK b/third-party/rust/crates/rust_decimal/1.39.0/BUCK index 3e5f56d4..b411394d 100644 --- a/third-party/rust/crates/rust_decimal/1.39.0/BUCK +++ b/third-party/rust/crates/rust_decimal/1.39.0/BUCK @@ -39,7 +39,7 @@ rust_library( deps = [ "//third-party/rust/crates/arrayvec/0.7.6:arrayvec", "//third-party/rust/crates/borsh/1.6.0:borsh", - "//third-party/rust/crates/bytes/1.11.0:bytes", + "//third-party/rust/crates/bytes/1.11.1:bytes", "//third-party/rust/crates/num-traits/0.2.19:num-traits", "//third-party/rust/crates/rand/0.8.5:rand", "//third-party/rust/crates/rkyv/0.7.45:rkyv", diff --git a/third-party/rust/crates/rustix/1.1.3/BUCK b/third-party/rust/crates/rustix/1.1.3/BUCK index 7e52077f..48dd69fd 100644 --- a/third-party/rust/crates/rustix/1.1.3/BUCK +++ b/third-party/rust/crates/rustix/1.1.3/BUCK @@ -44,8 +44,8 @@ rust_library( }, }, os_deps = { - "linux": ["//third-party/rust/crates/libc/0.2.180:libc"], - "macos": ["//third-party/rust/crates/libc/0.2.180:libc"], + "linux": ["//third-party/rust/crates/libc/0.2.181:libc"], + "macos": ["//third-party/rust/crates/libc/0.2.181:libc"], "windows": ["//third-party/rust/crates/windows-sys/0.61.2:windows-sys"], }, visibility = ["PUBLIC"], diff --git a/third-party/rust/crates/sea-orm/1.1.19/BUCK b/third-party/rust/crates/sea-orm/1.1.19/BUCK index b39ef2d8..10ee9963 100644 --- a/third-party/rust/crates/sea-orm/1.1.19/BUCK +++ b/third-party/rust/crates/sea-orm/1.1.19/BUCK @@ -67,6 +67,6 @@ rust_library( "//third-party/rust/crates/time/0.3.44:time", "//third-party/rust/crates/tracing/0.1.44:tracing", "//third-party/rust/crates/url/2.5.7:url", - "//third-party/rust/crates/uuid/1.19.0:uuid", + "//third-party/rust/crates/uuid/1.20.0:uuid", ], ) diff --git a/third-party/rust/crates/sea-query-binder/0.7.0/BUCK b/third-party/rust/crates/sea-query-binder/0.7.0/BUCK index 88c21c49..0e5bce85 100644 --- a/third-party/rust/crates/sea-query-binder/0.7.0/BUCK +++ b/third-party/rust/crates/sea-query-binder/0.7.0/BUCK @@ -49,6 +49,6 @@ rust_library( "//third-party/rust/crates/serde_json/1.0.145:serde_json", "//third-party/rust/crates/sqlx/0.8.6:sqlx", "//third-party/rust/crates/time/0.3.44:time", - "//third-party/rust/crates/uuid/1.19.0:uuid", + "//third-party/rust/crates/uuid/1.20.0:uuid", ], ) diff --git a/third-party/rust/crates/sea-query/0.32.7/BUCK b/third-party/rust/crates/sea-query/0.32.7/BUCK index e5cbbf26..d56b017e 100644 --- a/third-party/rust/crates/sea-query/0.32.7/BUCK +++ b/third-party/rust/crates/sea-query/0.32.7/BUCK @@ -53,6 +53,6 @@ rust_library( "//third-party/rust/crates/rust_decimal/1.39.0:rust_decimal", "//third-party/rust/crates/serde_json/1.0.145:serde_json", "//third-party/rust/crates/time/0.3.44:time", - "//third-party/rust/crates/uuid/1.19.0:uuid", + "//third-party/rust/crates/uuid/1.20.0:uuid", ], ) diff --git a/third-party/rust/crates/signal-hook-registry/1.4.7/BUCK b/third-party/rust/crates/signal-hook-registry/1.4.7/BUCK index 8929607c..142fd44c 100644 --- a/third-party/rust/crates/signal-hook-registry/1.4.7/BUCK +++ b/third-party/rust/crates/signal-hook-registry/1.4.7/BUCK @@ -25,5 +25,5 @@ rust_library( edition = "2015", rustc_flags = ["@$(location :signal-hook-registry-manifest[env_flags])"], visibility = ["PUBLIC"], - deps = ["//third-party/rust/crates/libc/0.2.180:libc"], + deps = ["//third-party/rust/crates/libc/0.2.181:libc"], ) diff --git a/third-party/rust/crates/socket2/0.6.1/BUCK b/third-party/rust/crates/socket2/0.6.1/BUCK index 39a60a71..8debaa9c 100644 --- a/third-party/rust/crates/socket2/0.6.1/BUCK +++ b/third-party/rust/crates/socket2/0.6.1/BUCK @@ -26,8 +26,8 @@ rust_library( features = ["all"], rustc_flags = ["@$(location :socket2-manifest[env_flags])"], os_deps = { - "linux": ["//third-party/rust/crates/libc/0.2.180:libc"], - "macos": ["//third-party/rust/crates/libc/0.2.180:libc"], + "linux": ["//third-party/rust/crates/libc/0.2.181:libc"], + "macos": ["//third-party/rust/crates/libc/0.2.181:libc"], "windows": ["//third-party/rust/crates/windows-sys/0.60.2:windows-sys"], }, visibility = ["PUBLIC"], diff --git a/third-party/rust/crates/sqlx-core/0.8.6/BUCK b/third-party/rust/crates/sqlx-core/0.8.6/BUCK index 102f0564..1a53d803 100644 --- a/third-party/rust/crates/sqlx-core/0.8.6/BUCK +++ b/third-party/rust/crates/sqlx-core/0.8.6/BUCK @@ -44,7 +44,7 @@ rust_library( deps = [ "//third-party/rust/crates/base64/0.22.1:base64", "//third-party/rust/crates/bigdecimal/0.4.9:bigdecimal", - "//third-party/rust/crates/bytes/1.11.0:bytes", + "//third-party/rust/crates/bytes/1.11.1:bytes", "//third-party/rust/crates/chrono/0.4.43:chrono", "//third-party/rust/crates/crc/3.4.0:crc", "//third-party/rust/crates/crossbeam-queue/0.3.12:crossbeam-queue", @@ -70,6 +70,6 @@ rust_library( "//third-party/rust/crates/time/0.3.44:time", "//third-party/rust/crates/tracing/0.1.44:tracing", "//third-party/rust/crates/url/2.5.7:url", - "//third-party/rust/crates/uuid/1.19.0:uuid", + "//third-party/rust/crates/uuid/1.20.0:uuid", ], ) diff --git a/third-party/rust/crates/sqlx-mysql/0.8.6/BUCK b/third-party/rust/crates/sqlx-mysql/0.8.6/BUCK index 78db00c2..67722d3a 100644 --- a/third-party/rust/crates/sqlx-mysql/0.8.6/BUCK +++ b/third-party/rust/crates/sqlx-mysql/0.8.6/BUCK @@ -45,7 +45,7 @@ rust_library( "//third-party/rust/crates/bigdecimal/0.4.9:bigdecimal", "//third-party/rust/crates/bitflags/2.10.0:bitflags", "//third-party/rust/crates/byteorder/1.5.0:byteorder", - "//third-party/rust/crates/bytes/1.11.0:bytes", + "//third-party/rust/crates/bytes/1.11.1:bytes", "//third-party/rust/crates/chrono/0.4.43:chrono", "//third-party/rust/crates/crc/3.4.0:crc", "//third-party/rust/crates/digest/0.10.7:digest", @@ -76,7 +76,7 @@ rust_library( "//third-party/rust/crates/thiserror/2.0.18:thiserror", "//third-party/rust/crates/time/0.3.44:time", "//third-party/rust/crates/tracing/0.1.44:tracing", - "//third-party/rust/crates/uuid/1.19.0:uuid", + "//third-party/rust/crates/uuid/1.20.0:uuid", "//third-party/rust/crates/whoami/1.6.1:whoami", ], ) diff --git a/third-party/rust/crates/sqlx-postgres/0.8.6/BUCK b/third-party/rust/crates/sqlx-postgres/0.8.6/BUCK index 9c5f312c..dc9ecc83 100644 --- a/third-party/rust/crates/sqlx-postgres/0.8.6/BUCK +++ b/third-party/rust/crates/sqlx-postgres/0.8.6/BUCK @@ -73,7 +73,7 @@ rust_library( "//third-party/rust/crates/thiserror/2.0.18:thiserror", "//third-party/rust/crates/time/0.3.44:time", "//third-party/rust/crates/tracing/0.1.44:tracing", - "//third-party/rust/crates/uuid/1.19.0:uuid", + "//third-party/rust/crates/uuid/1.20.0:uuid", "//third-party/rust/crates/whoami/1.6.1:whoami", ], ) diff --git a/third-party/rust/crates/sqlx-sqlite/0.8.6/BUCK b/third-party/rust/crates/sqlx-sqlite/0.8.6/BUCK index 6b702450..e42cb91a 100644 --- a/third-party/rust/crates/sqlx-sqlite/0.8.6/BUCK +++ b/third-party/rust/crates/sqlx-sqlite/0.8.6/BUCK @@ -54,6 +54,6 @@ rust_library( "//third-party/rust/crates/time/0.3.44:time", "//third-party/rust/crates/tracing/0.1.44:tracing", "//third-party/rust/crates/url/2.5.7:url", - "//third-party/rust/crates/uuid/1.19.0:uuid", + "//third-party/rust/crates/uuid/1.20.0:uuid", ], ) diff --git a/third-party/rust/crates/tokio-util/0.7.18/BUCK b/third-party/rust/crates/tokio-util/0.7.18/BUCK index 3ca6b516..6de82287 100644 --- a/third-party/rust/crates/tokio-util/0.7.18/BUCK +++ b/third-party/rust/crates/tokio-util/0.7.18/BUCK @@ -30,7 +30,7 @@ rust_library( rustc_flags = ["@$(location :tokio-util-manifest[env_flags])"], visibility = ["PUBLIC"], deps = [ - "//third-party/rust/crates/bytes/1.11.0:bytes", + "//third-party/rust/crates/bytes/1.11.1:bytes", "//third-party/rust/crates/futures-core/0.3.31:futures-core", "//third-party/rust/crates/futures-sink/0.3.31:futures-sink", "//third-party/rust/crates/pin-project-lite/0.2.16:pin-project-lite", diff --git a/third-party/rust/crates/tokio/1.49.0/BUCK b/third-party/rust/crates/tokio/1.49.0/BUCK index 6e51c2bd..64d2ae70 100644 --- a/third-party/rust/crates/tokio/1.49.0/BUCK +++ b/third-party/rust/crates/tokio/1.49.0/BUCK @@ -63,8 +63,8 @@ rust_library( }, visibility = ["PUBLIC"], deps = [ - "//third-party/rust/crates/bytes/1.11.0:bytes", - "//third-party/rust/crates/libc/0.2.180:libc", + "//third-party/rust/crates/bytes/1.11.1:bytes", + "//third-party/rust/crates/libc/0.2.181:libc", "//third-party/rust/crates/mio/1.1.1:mio", "//third-party/rust/crates/parking_lot/0.12.5:parking_lot", "//third-party/rust/crates/pin-project-lite/0.2.16:pin-project-lite", diff --git a/third-party/rust/crates/uuid/1.19.0/BUCK b/third-party/rust/crates/uuid/1.20.0/BUCK similarity index 84% rename from third-party/rust/crates/uuid/1.19.0/BUCK rename to third-party/rust/crates/uuid/1.20.0/BUCK index c7de75ba..a32e23da 100644 --- a/third-party/rust/crates/uuid/1.19.0/BUCK +++ b/third-party/rust/crates/uuid/1.20.0/BUCK @@ -5,10 +5,10 @@ load("@buckal//:wrapper.bzl", "rust_library") http_archive( name = "uuid-vendor", - urls = ["https://static.crates.io/crates/uuid/uuid-1.19.0.crate"], - sha256 = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a", + urls = ["https://static.crates.io/crates/uuid/uuid-1.20.0.crate"], + sha256 = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f", type = "tar.gz", - strip_prefix = "uuid-1.19.0", + strip_prefix = "uuid-1.20.0", out = "vendor", ) From 69291ac790b8af67a97a15da55ec9732d45da3fc Mon Sep 17 00:00:00 2001 From: Eli Ma Date: Thu, 12 Feb 2026 10:18:46 +0800 Subject: [PATCH 2/5] Fix zstdelta test Signed-off-by: Eli Ma --- Cargo.lock | 18 +----------------- Cargo.toml | 2 +- src/zstdelta/mod.rs | 3 ++- 3 files changed, 4 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 69f2c391..8142b93a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -894,7 +894,7 @@ dependencies = [ "path-absolutize", "quickcheck", "rand 0.9.2", - "rand_chacha 0.10.0", + "rand_chacha 0.9.0", "rayon", "ring", "sea-orm", @@ -1852,16 +1852,6 @@ dependencies = [ "rand_core 0.9.3", ] -[[package]] -name = "rand_chacha" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e6af7f3e25ded52c41df4e0b1af2d047e45896c2f3281792ed68a1c243daedb" -dependencies = [ - "ppv-lite86", - "rand_core 0.10.0", -] - [[package]] name = "rand_core" version = "0.6.4" @@ -1880,12 +1870,6 @@ dependencies = [ "getrandom 0.3.4", ] -[[package]] -name = "rand_core" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" - [[package]] name = "rayon" version = "1.11.0" diff --git a/Cargo.toml b/Cargo.toml index d532575b..e199b10d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,7 +55,7 @@ tokio = { version = "1.49.0", features = ["full"] } tokio-util = { version = "0.7.18", features = ["io"] } quickcheck = "1.0.3" rand = "0.9.2" -rand_chacha = "0.10.0" +rand_chacha = "0.9.0" [features] default = ["diff_mydrs"] diff --git a/src/zstdelta/mod.rs b/src/zstdelta/mod.rs index 6169342c..4c1f5be9 100644 --- a/src/zstdelta/mod.rs +++ b/src/zstdelta/mod.rs @@ -147,7 +147,8 @@ pub fn apply(base: &[u8], delta: &[u8]) -> io::Result> { #[cfg(test)] mod tests { use quickcheck::quickcheck; - use rand::{RngCore, SeedableRng}; + use rand::RngCore; + use rand::SeedableRng; use rand_chacha::ChaChaRng; use super::*; From e392770bae5abe49fcd8c6a445047e84180ad61c Mon Sep 17 00:00:00 2001 From: Eli Ma Date: Thu, 12 Feb 2026 10:32:21 +0800 Subject: [PATCH 3/5] Fix copilot warnings Signed-off-by: Eli Ma --- src/internal/object/context.rs | 61 +++++++++++++++++++++++++++++ src/internal/object/types.rs | 37 ++++++++++++++++++ src/internal/pack/decode.rs | 2 +- src/internal/pack/encode.rs | 70 +++++++++++++++++++++++++++++----- src/zstdelta/mod.rs | 3 +- 5 files changed, 161 insertions(+), 12 deletions(-) diff --git a/src/internal/object/context.rs b/src/internal/object/context.rs index 7b6a4f95..38248618 100644 --- a/src/internal/object/context.rs +++ b/src/internal/object/context.rs @@ -105,6 +105,30 @@ impl ContextSnapshot { pub fn header(&self) -> &Header { &self.header } + + pub fn base_commit_sha(&self) -> &IntegrityHash { + &self.base_commit_sha + } + + pub fn selection_strategy(&self) -> &SelectionStrategy { + &self.selection_strategy + } + + pub fn items(&self) -> &[ContextItem] { + &self.items + } + + pub fn summary(&self) -> Option<&str> { + self.summary.as_deref() + } + + pub fn add_item(&mut self, item: ContextItem) { + self.items.push(item); + } + + pub fn set_summary(&mut self, summary: Option) { + self.summary = summary; + } } impl Display for ContextSnapshot { @@ -133,3 +157,40 @@ impl ObjectTrait for ContextSnapshot { serde_json::to_vec(self).map_err(|e| GitError::InvalidObjectInfo(e.to_string())) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_context_snapshot_accessors_and_mutators() { + let repo_id = Uuid::from_u128(0x0123456789abcdef0123456789abcdef); + let actor = ActorRef::agent("coder").expect("actor"); + let mut snapshot = ContextSnapshot::new( + repo_id, + actor, + "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9", + SelectionStrategy::Heuristic, + ) + .expect("snapshot"); + + assert_eq!(snapshot.selection_strategy(), &SelectionStrategy::Heuristic); + assert!(snapshot.items().is_empty()); + assert!(snapshot.summary().is_none()); + + let item = ContextItem::new( + ContextItemKind::File, + "src/main.rs", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + .parse() + .expect("hash"), + ) + .expect("item"); + snapshot.add_item(item); + snapshot.set_summary(Some("selected by relevance".to_string())); + + assert_eq!(snapshot.items().len(), 1); + assert_eq!(snapshot.summary(), Some("selected by relevance")); + assert_eq!(snapshot.base_commit_sha().to_hex().len(), 64); + } +} diff --git a/src/internal/object/types.rs b/src/internal/object/types.rs index c5002613..c5be1ef5 100644 --- a/src/internal/object/types.rs +++ b/src/internal/object/types.rs @@ -105,6 +105,43 @@ impl Display for ObjectType { /// Display trait for Git objects type impl ObjectType { + /// Convert object type to 3-bit pack header type id. + /// + /// Git pack headers only carry 3 type bits (values 0..=7). AI object + /// types are not representable in this field and must not be written + /// as regular base objects in a pack entry. + pub fn to_pack_type_u8(&self) -> Result { + match self { + ObjectType::Commit => Ok(1), + ObjectType::Tree => Ok(2), + ObjectType::Blob => Ok(3), + ObjectType::Tag => Ok(4), + ObjectType::OffsetZstdelta => Ok(5), + ObjectType::OffsetDelta => Ok(6), + ObjectType::HashDelta => Ok(7), + _ => Err(GitError::PackEncodeError(format!( + "object type `{}` cannot be encoded in pack header type bits", + self + ))), + } + } + + /// Decode 3-bit pack header type id to object type. + pub fn from_pack_type_u8(number: u8) -> Result { + match number { + 1 => Ok(ObjectType::Commit), + 2 => Ok(ObjectType::Tree), + 3 => Ok(ObjectType::Blob), + 4 => Ok(ObjectType::Tag), + 5 => Ok(ObjectType::OffsetZstdelta), + 6 => Ok(ObjectType::OffsetDelta), + 7 => Ok(ObjectType::HashDelta), + _ => Err(GitError::InvalidObjectType(format!( + "Invalid pack object type number: {number}" + ))), + } + } + pub fn to_bytes(&self) -> &[u8] { match self { ObjectType::Commit => COMMIT_OBJECT_TYPE, diff --git a/src/internal/pack/decode.rs b/src/internal/pack/decode.rs index 701a3a2f..765d1fb7 100644 --- a/src/internal/pack/decode.rs +++ b/src/internal/pack/decode.rs @@ -311,7 +311,7 @@ impl Pack { }; // Check if the object type is valid - let t = ObjectType::from_u8(type_bits)?; + let t = ObjectType::from_pack_type_u8(type_bits)?; match t { ObjectType::Commit | ObjectType::Tree | ObjectType::Blob | ObjectType::Tag => { diff --git a/src/internal/pack/encode.rs b/src/internal/pack/encode.rs index eb6713e9..3dceb8eb 100644 --- a/src/internal/pack/encode.rs +++ b/src/internal/pack/encode.rs @@ -162,7 +162,7 @@ fn encode_one_object(entry: &Entry, offset: Option) -> Result, Gi // try encode as delta let obj_data = &entry.data; let obj_data_len = obj_data.len(); - let obj_type_number = entry.obj_type.to_u8(); + let obj_type_number = entry.obj_type.to_pack_type_u8()?; let mut encoded_data = Vec::new(); @@ -376,7 +376,12 @@ impl PackEncoder { ObjectType::Tag => { tags.push(entry); } - _ => {} + _ => { + return Err(GitError::PackEncodeError(format!( + "object type `{}` is not supported by delta-window pack encoding", + entry.inner.obj_type + ))); + } } } @@ -629,21 +634,20 @@ impl PackEncoder { } // use `collect` will return result in order, refs: https://github.com/rayon-rs/rayon/issues/551#issuecomment-371657900 - let batch_result: Vec<(Vec, IndexEntry)> = + let batch_result: Vec, IndexEntry), GitError>> = time_it!("parallel encode: encode batch", { batch_entries .par_iter() .map(|entry| { - ( - encode_one_object(entry, None).unwrap(), - IndexEntry::new(entry, 0), - ) + encode_one_object(entry, None) + .map(|encoded| (encoded, IndexEntry::new(entry, 0))) }) .collect() }); time_it!("parallel encode: write batch", { - for mut obj_data in batch_result { + for obj_data in batch_result { + let mut obj_data = obj_data?; obj_data.1.offset = self.inner_offset as u64; self.write_all_and_update(&obj_data.0).await; idx_entries.push(obj_data.1); @@ -745,7 +749,7 @@ mod tests { use crate::{ hash::{HashKind, ObjectHash, set_hash_kind_for_test}, internal::{ - object::blob::Blob, + object::{blob::Blob, types::ObjectType}, pack::{Pack, tests::init_logger, utils::read_offset_encoding}, }, time_it, @@ -863,6 +867,54 @@ mod tests { check_format(&pack_with_delta); } + #[tokio::test] + async fn test_pack_encoder_rejects_unencodable_ai_type_parallel() { + let (tx, _rx) = mpsc::channel(8); + let (entry_tx, entry_rx) = mpsc::channel::>(1); + let mut encoder = PackEncoder::new(1, 0, tx); + + let mut entry: Entry = Blob::from_content("ai").into(); + entry.obj_type = ObjectType::Task; + entry_tx + .send(MetaAttached { + inner: entry, + meta: EntryMeta::new(), + }) + .await + .expect("send entry"); + drop(entry_tx); + + let err = encoder + .encode(entry_rx) + .await + .expect_err("must reject AI pack type"); + assert!(matches!(err, GitError::PackEncodeError(_))); + } + + #[tokio::test] + async fn test_pack_encoder_rejects_unencodable_ai_type_delta_window() { + let (tx, _rx) = mpsc::channel(8); + let (entry_tx, entry_rx) = mpsc::channel::>(1); + let mut encoder = PackEncoder::new(1, 10, tx); + + let mut entry: Entry = Blob::from_content("ai").into(); + entry.obj_type = ObjectType::Task; + entry_tx + .send(MetaAttached { + inner: entry, + meta: EntryMeta::new(), + }) + .await + .expect("send entry"); + drop(entry_tx); + + let err = encoder + .encode(entry_rx) + .await + .expect_err("must reject AI pack type"); + assert!(matches!(err, GitError::PackEncodeError(_))); + } + async fn get_entries_for_test() -> Arc>> { let source = PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("tests/data/packs/encode-test-sha1.pack"); diff --git a/src/zstdelta/mod.rs b/src/zstdelta/mod.rs index 4c1f5be9..6169342c 100644 --- a/src/zstdelta/mod.rs +++ b/src/zstdelta/mod.rs @@ -147,8 +147,7 @@ pub fn apply(base: &[u8], delta: &[u8]) -> io::Result> { #[cfg(test)] mod tests { use quickcheck::quickcheck; - use rand::RngCore; - use rand::SeedableRng; + use rand::{RngCore, SeedableRng}; use rand_chacha::ChaChaRng; use super::*; From 3743c70b6d1c22fe86dca7dd21ba220e413c9e98 Mon Sep 17 00:00:00 2001 From: Eli Ma Date: Thu, 12 Feb 2026 10:43:21 +0800 Subject: [PATCH 4/5] Update claude review action Signed-off-by: Eli Ma --- .github/workflows/claude-review.yml | 61 +---------------------------- 1 file changed, 1 insertion(+), 60 deletions(-) diff --git a/.github/workflows/claude-review.yml b/.github/workflows/claude-review.yml index 5426cb2a..af4ff595 100644 --- a/.github/workflows/claude-review.yml +++ b/.github/workflows/claude-review.yml @@ -65,69 +65,10 @@ jobs: steps: # Checkout the repository at the appropriate commit for review - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: # Use PR head SHA for pull_request_target, fallback to current SHA otherwise fetch-depth: 0 - ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }} - - # Handle fork branches for pull_request_target events - - name: Setup Fork Remote (for pull_request_target) - if: ${{ github.event_name == 'pull_request_target' }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PR_NUMBER: ${{ github.event.pull_request.number }} - PR_HEAD_REF: ${{ github.event.pull_request.head.ref }} - PR_HEAD_OWNER: ${{ github.event.pull_request.head.repo.owner.login }} - PR_HEAD_REPO: ${{ github.event.pull_request.head.repo.name }} - REPO_OWNER: ${{ github.repository_owner }} - run: | - PR_NUMBER="$PR_NUMBER" - HEAD_REF="$PR_HEAD_REF" - HEAD_OWNER="$PR_HEAD_OWNER" - HEAD_REPO="$PR_HEAD_REPO" - CURRENT_OWNER="$REPO_OWNER" - - # For forked PRs, temporarily change origin URL to fork repository - # This allows claude-code-action to fetch the PR branch correctly - if [ "$HEAD_OWNER" != "$CURRENT_OWNER" ]; then - echo "PR is from fork: $HEAD_OWNER/$HEAD_REPO" - FORK_URL="https://github.com/$HEAD_OWNER/$HEAD_REPO.git" - echo "Temporarily changing origin URL to fork: $FORK_URL" - git remote set-url origin "$FORK_URL" - git fetch origin "$HEAD_REF" - git branch "$HEAD_REF" "origin/$HEAD_REF" 2>/dev/null || git branch -f "$HEAD_REF" "origin/$HEAD_REF" - fi - - # For comment-driven triggers, ensure we have the correct PR branch checked out - - name: Checkout PR Branch (for comments) - if: ${{ github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment' }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - PR_NUMBER=${{ github.event.issue.number || github.event.pull_request.number }} - - # Fetch PR metadata: head branch name and source repository - PR_DATA=$(gh pr view $PR_NUMBER --json headRefName,headRepositoryOwner,headRepository,baseRefName) - HEAD_REF=$(echo "$PR_DATA" | jq -r '.headRefName') - HEAD_OWNER=$(echo "$PR_DATA" | jq -r '.headRepositoryOwner.login') - HEAD_REPO=$(echo "$PR_DATA" | jq -r '.headRepository.name') - BASE_BRANCH=$(echo "$PR_DATA" | jq -r '.baseRefName') - CURRENT_OWNER="${{ github.repository_owner }}" - - # For forked PRs, temporarily change origin URL to fork repository - # This allows claude-code-action to fetch the PR branch correctly - if [ "$HEAD_OWNER" != "$CURRENT_OWNER" ]; then - echo "PR is from fork: $HEAD_OWNER/$HEAD_REPO" - FORK_URL="https://github.com/$HEAD_OWNER/$HEAD_REPO.git" - echo "Temporarily changing origin URL to fork: $FORK_URL" - git remote set-url origin "$FORK_URL" - fi - - # Fetch and checkout the PR branch - git fetch origin "$HEAD_REF" - git branch "$HEAD_REF" "origin/$HEAD_REF" 2>/dev/null || git branch -f "$HEAD_REF" "origin/$HEAD_REF" - git checkout "$HEAD_REF" # Invoke Claude to perform an automated PR review with progress tracking - name: PR Review with Progress Tracking From 33e32bf216f0c8674858ea2d85c91a724e1a0ec3 Mon Sep 17 00:00:00 2001 From: Eli Ma Date: Thu, 12 Feb 2026 10:46:38 +0800 Subject: [PATCH 5/5] Update claude review action Signed-off-by: Eli Ma --- .github/workflows/claude-review.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/claude-review.yml b/.github/workflows/claude-review.yml index af4ff595..2b5ed4a0 100644 --- a/.github/workflows/claude-review.yml +++ b/.github/workflows/claude-review.yml @@ -67,7 +67,9 @@ jobs: - name: Checkout repository uses: actions/checkout@v6 with: - # Use PR head SHA for pull_request_target, fallback to current SHA otherwise + # Use PR head SHA for pull_request_target to review the actual PR code + # For comment events, this will default to the base branch (PR context is inferred by Claude action) + ref: ${{ github.event.pull_request.head.sha || github.sha }} fetch-depth: 0 # Invoke Claude to perform an automated PR review with progress tracking