diff --git a/packages/sie_server/bundle-locks/README.md b/packages/sie_server/bundle-locks/README.md index 7e1777769..43a544c57 100644 --- a/packages/sie_server/bundle-locks/README.md +++ b/packages/sie_server/bundle-locks/README.md @@ -24,9 +24,13 @@ A saved inventory or CPU import check does not establish GPU namespace isolation; a missing cu130 `nvidia` submodule may fall through to the base. Live GPU qualification remains separate from lock validation. -Before installing, the verifier binds the lock to the root lock, resolved -bundle declarations, target, uv version, exact exception manifest, and the -compiler/verifier/declaration-resolver source hashes. After installation it +Before installing, the verifier binds the lock to the exact third-party versions +of the root lock, resolved bundle declarations, target, uv version, exact +exception manifest, and the compiler/verifier/declaration-resolver source +hashes. Workspace package versions and lock serialization are not part of that +binding, so a release version bump does not require recompilation; every check +still compares each locked artifact with the current root lock through the +exception policy. After installation it checks the actual distribution set, versions, dependency metadata, requested extras, and recorded direct-source identities without importing package code. Missing, additional, duplicate, incompatible, or substituted distributions fail diff --git a/packages/sie_server/bundle-locks/cpu-ctranslate2-amd64/manifest.json b/packages/sie_server/bundle-locks/cpu-ctranslate2-amd64/manifest.json index b7d1bcfaa..f59c9ab9b 100644 --- a/packages/sie_server/bundle-locks/cpu-ctranslate2-amd64/manifest.json +++ b/packages/sie_server/bundle-locks/cpu-ctranslate2-amd64/manifest.json @@ -12,10 +12,10 @@ ], "inputs": { "requirements_sha256": "06013808500bdd0b62b916d46f4b7a308290f5c07567315faf967cfe9f7f5888", - "uv_lock_sha256": "75848a1dc0e423ddb313eda2d12208568628efcae6d0eea7b6d99be5edc45d8f" + "root_versions_sha256": "3d69200d45fb5e395d3e4b707eac86b4b2a9ce22214bfacd85dd19f73c98a765" }, "lock_sha256": "1f2350156ca95ae0ed398f20d94458135d6c9ac1fd4985419e1ea6873576e718", - "policy_sha256": "dbc9ef2b65ea54bebe46021a7813897c378207508e4d84caf45e93d31faf15d4", + "policy_sha256": "6c835f64b277b782d89eb5f2374924124cc3ddc058bb3232c7e0362ff907f377", "schema_version": 1, "source_pins_sha256": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", "target": { diff --git a/packages/sie_server/bundle-locks/cpu-ctranslate2-arm64/manifest.json b/packages/sie_server/bundle-locks/cpu-ctranslate2-arm64/manifest.json index cc3174c05..fc9b6f55b 100644 --- a/packages/sie_server/bundle-locks/cpu-ctranslate2-arm64/manifest.json +++ b/packages/sie_server/bundle-locks/cpu-ctranslate2-arm64/manifest.json @@ -12,10 +12,10 @@ ], "inputs": { "requirements_sha256": "06013808500bdd0b62b916d46f4b7a308290f5c07567315faf967cfe9f7f5888", - "uv_lock_sha256": "75848a1dc0e423ddb313eda2d12208568628efcae6d0eea7b6d99be5edc45d8f" + "root_versions_sha256": "3d69200d45fb5e395d3e4b707eac86b4b2a9ce22214bfacd85dd19f73c98a765" }, "lock_sha256": "1bc8a0f417323c67420e71cf16b71e562c7db5842b50d4c5cd60efea8106141b", - "policy_sha256": "dbc9ef2b65ea54bebe46021a7813897c378207508e4d84caf45e93d31faf15d4", + "policy_sha256": "6c835f64b277b782d89eb5f2374924124cc3ddc058bb3232c7e0362ff907f377", "schema_version": 1, "source_pins_sha256": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", "target": { diff --git a/packages/sie_server/bundle-locks/cpu-default-amd64/manifest.json b/packages/sie_server/bundle-locks/cpu-default-amd64/manifest.json index ad2346898..94bb3774e 100644 --- a/packages/sie_server/bundle-locks/cpu-default-amd64/manifest.json +++ b/packages/sie_server/bundle-locks/cpu-default-amd64/manifest.json @@ -23,10 +23,10 @@ ], "inputs": { "requirements_sha256": "318d20931c1c977a77887959d8d99438c05406095022aa9a3a4ca4e8f4d30b52", - "uv_lock_sha256": "75848a1dc0e423ddb313eda2d12208568628efcae6d0eea7b6d99be5edc45d8f" + "root_versions_sha256": "3d69200d45fb5e395d3e4b707eac86b4b2a9ce22214bfacd85dd19f73c98a765" }, "lock_sha256": "27a63c27869eb36f251e586323e4d470ea739e7cf105bcc60c886d34df4a5fe4", - "policy_sha256": "dbc9ef2b65ea54bebe46021a7813897c378207508e4d84caf45e93d31faf15d4", + "policy_sha256": "6c835f64b277b782d89eb5f2374924124cc3ddc058bb3232c7e0362ff907f377", "schema_version": 1, "source_pins_sha256": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", "target": { diff --git a/packages/sie_server/bundle-locks/cpu-default-arm64/manifest.json b/packages/sie_server/bundle-locks/cpu-default-arm64/manifest.json index 1c31a5633..9d8aefcbf 100644 --- a/packages/sie_server/bundle-locks/cpu-default-arm64/manifest.json +++ b/packages/sie_server/bundle-locks/cpu-default-arm64/manifest.json @@ -23,10 +23,10 @@ ], "inputs": { "requirements_sha256": "318d20931c1c977a77887959d8d99438c05406095022aa9a3a4ca4e8f4d30b52", - "uv_lock_sha256": "75848a1dc0e423ddb313eda2d12208568628efcae6d0eea7b6d99be5edc45d8f" + "root_versions_sha256": "3d69200d45fb5e395d3e4b707eac86b4b2a9ce22214bfacd85dd19f73c98a765" }, "lock_sha256": "7fbe48060336480af530b56634c04d4cb7968b84509d0e4da443b1e19a569ed9", - "policy_sha256": "dbc9ef2b65ea54bebe46021a7813897c378207508e4d84caf45e93d31faf15d4", + "policy_sha256": "6c835f64b277b782d89eb5f2374924124cc3ddc058bb3232c7e0362ff907f377", "schema_version": 1, "source_pins_sha256": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", "target": { diff --git a/packages/sie_server/bundle-locks/cpu-sglang-amd64/manifest.json b/packages/sie_server/bundle-locks/cpu-sglang-amd64/manifest.json index eb171c9f2..4915deb78 100644 --- a/packages/sie_server/bundle-locks/cpu-sglang-amd64/manifest.json +++ b/packages/sie_server/bundle-locks/cpu-sglang-amd64/manifest.json @@ -28,10 +28,10 @@ ], "inputs": { "requirements_sha256": "943a74a3f4565e0b7b3bab2f87080526604d1e3923a39ed287d444630d33b94c", - "uv_lock_sha256": "75848a1dc0e423ddb313eda2d12208568628efcae6d0eea7b6d99be5edc45d8f" + "root_versions_sha256": "3d69200d45fb5e395d3e4b707eac86b4b2a9ce22214bfacd85dd19f73c98a765" }, "lock_sha256": "11fd40b389103f700749005ecc1e10cee548a74e117e1177e91ca19870cd4cac", - "policy_sha256": "dbc9ef2b65ea54bebe46021a7813897c378207508e4d84caf45e93d31faf15d4", + "policy_sha256": "6c835f64b277b782d89eb5f2374924124cc3ddc058bb3232c7e0362ff907f377", "schema_version": 1, "source_pins_sha256": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", "target": { diff --git a/packages/sie_server/bundle-locks/cpu-sglang-arm64/manifest.json b/packages/sie_server/bundle-locks/cpu-sglang-arm64/manifest.json index 7d10ed5ea..3e436c3ea 100644 --- a/packages/sie_server/bundle-locks/cpu-sglang-arm64/manifest.json +++ b/packages/sie_server/bundle-locks/cpu-sglang-arm64/manifest.json @@ -28,10 +28,10 @@ ], "inputs": { "requirements_sha256": "943a74a3f4565e0b7b3bab2f87080526604d1e3923a39ed287d444630d33b94c", - "uv_lock_sha256": "75848a1dc0e423ddb313eda2d12208568628efcae6d0eea7b6d99be5edc45d8f" + "root_versions_sha256": "3d69200d45fb5e395d3e4b707eac86b4b2a9ce22214bfacd85dd19f73c98a765" }, "lock_sha256": "1315d6d8b3beac2917feab7728cc91a8bcae5f926c0dd1aa0f3f568770d1eefb", - "policy_sha256": "dbc9ef2b65ea54bebe46021a7813897c378207508e4d84caf45e93d31faf15d4", + "policy_sha256": "6c835f64b277b782d89eb5f2374924124cc3ddc058bb3232c7e0362ff907f377", "schema_version": 1, "source_pins_sha256": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", "target": { diff --git a/packages/sie_server/bundle-locks/cpu-transformers5-amd64/manifest.json b/packages/sie_server/bundle-locks/cpu-transformers5-amd64/manifest.json index 85b52a5e8..6149c60df 100644 --- a/packages/sie_server/bundle-locks/cpu-transformers5-amd64/manifest.json +++ b/packages/sie_server/bundle-locks/cpu-transformers5-amd64/manifest.json @@ -23,10 +23,10 @@ ], "inputs": { "requirements_sha256": "f9e0f7979fcd2920254e6e9a62bdd767d87ceea7794d7df2d9af2ece4e02a324", - "uv_lock_sha256": "75848a1dc0e423ddb313eda2d12208568628efcae6d0eea7b6d99be5edc45d8f" + "root_versions_sha256": "3d69200d45fb5e395d3e4b707eac86b4b2a9ce22214bfacd85dd19f73c98a765" }, "lock_sha256": "85571638f5441da4e2633e383d1c61e6ba68bd18bf7e3f3a496c7ae3aafb612a", - "policy_sha256": "dbc9ef2b65ea54bebe46021a7813897c378207508e4d84caf45e93d31faf15d4", + "policy_sha256": "6c835f64b277b782d89eb5f2374924124cc3ddc058bb3232c7e0362ff907f377", "schema_version": 1, "source_pins_sha256": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", "target": { diff --git a/packages/sie_server/bundle-locks/cpu-transformers5-arm64/manifest.json b/packages/sie_server/bundle-locks/cpu-transformers5-arm64/manifest.json index 2a80d85b4..74cbad7c7 100644 --- a/packages/sie_server/bundle-locks/cpu-transformers5-arm64/manifest.json +++ b/packages/sie_server/bundle-locks/cpu-transformers5-arm64/manifest.json @@ -23,10 +23,10 @@ ], "inputs": { "requirements_sha256": "f9e0f7979fcd2920254e6e9a62bdd767d87ceea7794d7df2d9af2ece4e02a324", - "uv_lock_sha256": "75848a1dc0e423ddb313eda2d12208568628efcae6d0eea7b6d99be5edc45d8f" + "root_versions_sha256": "3d69200d45fb5e395d3e4b707eac86b4b2a9ce22214bfacd85dd19f73c98a765" }, "lock_sha256": "e371b5d89247efb1cfd7d4b1d100b8888c6a1acbbddb5a3482c29c48c8c9894e", - "policy_sha256": "dbc9ef2b65ea54bebe46021a7813897c378207508e4d84caf45e93d31faf15d4", + "policy_sha256": "6c835f64b277b782d89eb5f2374924124cc3ddc058bb3232c7e0362ff907f377", "schema_version": 1, "source_pins_sha256": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", "target": { diff --git a/packages/sie_server/bundle-locks/cuda12-ctranslate2-amd64/manifest.json b/packages/sie_server/bundle-locks/cuda12-ctranslate2-amd64/manifest.json index 1f2664781..3170865b7 100644 --- a/packages/sie_server/bundle-locks/cuda12-ctranslate2-amd64/manifest.json +++ b/packages/sie_server/bundle-locks/cuda12-ctranslate2-amd64/manifest.json @@ -8,10 +8,10 @@ ], "inputs": { "requirements_sha256": "06013808500bdd0b62b916d46f4b7a308290f5c07567315faf967cfe9f7f5888", - "uv_lock_sha256": "75848a1dc0e423ddb313eda2d12208568628efcae6d0eea7b6d99be5edc45d8f" + "root_versions_sha256": "3d69200d45fb5e395d3e4b707eac86b4b2a9ce22214bfacd85dd19f73c98a765" }, "lock_sha256": "1f2350156ca95ae0ed398f20d94458135d6c9ac1fd4985419e1ea6873576e718", - "policy_sha256": "dbc9ef2b65ea54bebe46021a7813897c378207508e4d84caf45e93d31faf15d4", + "policy_sha256": "6c835f64b277b782d89eb5f2374924124cc3ddc058bb3232c7e0362ff907f377", "schema_version": 1, "source_pins_sha256": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", "target": { diff --git a/packages/sie_server/bundle-locks/cuda12-default-amd64/manifest.json b/packages/sie_server/bundle-locks/cuda12-default-amd64/manifest.json index a082f7273..3a0697fb2 100644 --- a/packages/sie_server/bundle-locks/cuda12-default-amd64/manifest.json +++ b/packages/sie_server/bundle-locks/cuda12-default-amd64/manifest.json @@ -8,10 +8,10 @@ ], "inputs": { "requirements_sha256": "1da4cc3c38305188df3a38287ca48a750637d2b316f6c7eb933e07e1ae665ee0", - "uv_lock_sha256": "75848a1dc0e423ddb313eda2d12208568628efcae6d0eea7b6d99be5edc45d8f" + "root_versions_sha256": "3d69200d45fb5e395d3e4b707eac86b4b2a9ce22214bfacd85dd19f73c98a765" }, "lock_sha256": "c4c48a529487092c6e3d7ac042d17a16709b68c1ae20046f97555d25baf5fa09", - "policy_sha256": "dbc9ef2b65ea54bebe46021a7813897c378207508e4d84caf45e93d31faf15d4", + "policy_sha256": "6c835f64b277b782d89eb5f2374924124cc3ddc058bb3232c7e0362ff907f377", "schema_version": 1, "source_pins_sha256": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", "target": { diff --git a/packages/sie_server/bundle-locks/cuda12-sglang-amd64/manifest.json b/packages/sie_server/bundle-locks/cuda12-sglang-amd64/manifest.json index 3a4b5b894..b2feff337 100644 --- a/packages/sie_server/bundle-locks/cuda12-sglang-amd64/manifest.json +++ b/packages/sie_server/bundle-locks/cuda12-sglang-amd64/manifest.json @@ -12,10 +12,10 @@ ], "inputs": { "requirements_sha256": "943a74a3f4565e0b7b3bab2f87080526604d1e3923a39ed287d444630d33b94c", - "uv_lock_sha256": "75848a1dc0e423ddb313eda2d12208568628efcae6d0eea7b6d99be5edc45d8f" + "root_versions_sha256": "3d69200d45fb5e395d3e4b707eac86b4b2a9ce22214bfacd85dd19f73c98a765" }, "lock_sha256": "d70cdf482df2f4b25275b4ef20aaf274dfe432379cc2ed18b77c841399e652bd", - "policy_sha256": "dbc9ef2b65ea54bebe46021a7813897c378207508e4d84caf45e93d31faf15d4", + "policy_sha256": "6c835f64b277b782d89eb5f2374924124cc3ddc058bb3232c7e0362ff907f377", "schema_version": 1, "source_pins_sha256": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", "target": { diff --git a/packages/sie_server/bundle-locks/cuda12-sglang-embedding-amd64/manifest.json b/packages/sie_server/bundle-locks/cuda12-sglang-embedding-amd64/manifest.json index 91d55d263..de446c2c7 100644 --- a/packages/sie_server/bundle-locks/cuda12-sglang-embedding-amd64/manifest.json +++ b/packages/sie_server/bundle-locks/cuda12-sglang-embedding-amd64/manifest.json @@ -12,10 +12,10 @@ ], "inputs": { "requirements_sha256": "378dabe9b77bc2c593a05e1113534e8f50093ca205d3344a7ba2e20fe660e38a", - "uv_lock_sha256": "75848a1dc0e423ddb313eda2d12208568628efcae6d0eea7b6d99be5edc45d8f" + "root_versions_sha256": "3d69200d45fb5e395d3e4b707eac86b4b2a9ce22214bfacd85dd19f73c98a765" }, "lock_sha256": "e250fa531458fcdb9a3f2dff6fbb7e6b6e0144e114a835bbcd999b371ba0da60", - "policy_sha256": "dbc9ef2b65ea54bebe46021a7813897c378207508e4d84caf45e93d31faf15d4", + "policy_sha256": "6c835f64b277b782d89eb5f2374924124cc3ddc058bb3232c7e0362ff907f377", "schema_version": 1, "source_pins_sha256": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", "target": { diff --git a/packages/sie_server/bundle-locks/cuda12-sglang-vision-extract-amd64/manifest.json b/packages/sie_server/bundle-locks/cuda12-sglang-vision-extract-amd64/manifest.json index 62842752e..38046a555 100644 --- a/packages/sie_server/bundle-locks/cuda12-sglang-vision-extract-amd64/manifest.json +++ b/packages/sie_server/bundle-locks/cuda12-sglang-vision-extract-amd64/manifest.json @@ -12,10 +12,10 @@ ], "inputs": { "requirements_sha256": "378dabe9b77bc2c593a05e1113534e8f50093ca205d3344a7ba2e20fe660e38a", - "uv_lock_sha256": "75848a1dc0e423ddb313eda2d12208568628efcae6d0eea7b6d99be5edc45d8f" + "root_versions_sha256": "3d69200d45fb5e395d3e4b707eac86b4b2a9ce22214bfacd85dd19f73c98a765" }, "lock_sha256": "e250fa531458fcdb9a3f2dff6fbb7e6b6e0144e114a835bbcd999b371ba0da60", - "policy_sha256": "dbc9ef2b65ea54bebe46021a7813897c378207508e4d84caf45e93d31faf15d4", + "policy_sha256": "6c835f64b277b782d89eb5f2374924124cc3ddc058bb3232c7e0362ff907f377", "schema_version": 1, "source_pins_sha256": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", "target": { diff --git a/packages/sie_server/bundle-locks/cuda12-transformers5-amd64/manifest.json b/packages/sie_server/bundle-locks/cuda12-transformers5-amd64/manifest.json index 1047ef98b..7bdafe1f6 100644 --- a/packages/sie_server/bundle-locks/cuda12-transformers5-amd64/manifest.json +++ b/packages/sie_server/bundle-locks/cuda12-transformers5-amd64/manifest.json @@ -8,10 +8,10 @@ ], "inputs": { "requirements_sha256": "5a97f31f45bddf41d827566173ad0200797fecd4a0037f70db40885e0ab5dbb3", - "uv_lock_sha256": "75848a1dc0e423ddb313eda2d12208568628efcae6d0eea7b6d99be5edc45d8f" + "root_versions_sha256": "3d69200d45fb5e395d3e4b707eac86b4b2a9ce22214bfacd85dd19f73c98a765" }, "lock_sha256": "95975ca37ffabefd6fcafca1d943379536a0b79a67e44f7df42ca07f25473b8a", - "policy_sha256": "dbc9ef2b65ea54bebe46021a7813897c378207508e4d84caf45e93d31faf15d4", + "policy_sha256": "6c835f64b277b782d89eb5f2374924124cc3ddc058bb3232c7e0362ff907f377", "schema_version": 1, "source_pins_sha256": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", "target": { diff --git a/packages/sie_server/bundle-locks/cuda13-sglang-cu130-amd64/manifest.json b/packages/sie_server/bundle-locks/cuda13-sglang-cu130-amd64/manifest.json index 36c2d8479..e1913aaa4 100644 --- a/packages/sie_server/bundle-locks/cuda13-sglang-cu130-amd64/manifest.json +++ b/packages/sie_server/bundle-locks/cuda13-sglang-cu130-amd64/manifest.json @@ -27,10 +27,10 @@ ], "inputs": { "requirements_sha256": "6d29b7815d5f5105dbc70e23f5c3c51a225c6b2e0127eca3834a327d94eeec4a", - "uv_lock_sha256": "75848a1dc0e423ddb313eda2d12208568628efcae6d0eea7b6d99be5edc45d8f" + "root_versions_sha256": "3d69200d45fb5e395d3e4b707eac86b4b2a9ce22214bfacd85dd19f73c98a765" }, "lock_sha256": "8b8f649c11f84b8f8e64a0dc7b2145d62cdcd0931610089a254d6f4a72e9914a", - "policy_sha256": "dbc9ef2b65ea54bebe46021a7813897c378207508e4d84caf45e93d31faf15d4", + "policy_sha256": "6c835f64b277b782d89eb5f2374924124cc3ddc058bb3232c7e0362ff907f377", "schema_version": 1, "source_pins_sha256": "6808e668b2e2a0982ffa734b251936b07fab343a4b201d549e85333691f7ec85", "target": { diff --git a/packages/sie_server/bundle-locks/cuda13-tensorrt-llm-amd64/manifest.json b/packages/sie_server/bundle-locks/cuda13-tensorrt-llm-amd64/manifest.json index 45f8d6f27..971b5dcf7 100644 --- a/packages/sie_server/bundle-locks/cuda13-tensorrt-llm-amd64/manifest.json +++ b/packages/sie_server/bundle-locks/cuda13-tensorrt-llm-amd64/manifest.json @@ -27,10 +27,10 @@ ], "inputs": { "requirements_sha256": "16719ba49f177504ae160a1872b8d0c23e0a4b6d176ecad85c882977d27be9fe", - "uv_lock_sha256": "75848a1dc0e423ddb313eda2d12208568628efcae6d0eea7b6d99be5edc45d8f" + "root_versions_sha256": "3d69200d45fb5e395d3e4b707eac86b4b2a9ce22214bfacd85dd19f73c98a765" }, "lock_sha256": "645a3f57ca3933838d1e9a1cc4098738219507102a99fef5301afe22de8eae5d", - "policy_sha256": "dbc9ef2b65ea54bebe46021a7813897c378207508e4d84caf45e93d31faf15d4", + "policy_sha256": "6c835f64b277b782d89eb5f2374924124cc3ddc058bb3232c7e0362ff907f377", "schema_version": 1, "source_pins_sha256": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", "target": { diff --git a/packages/sie_server/src/sie_server/bundle_lock.py b/packages/sie_server/src/sie_server/bundle_lock.py index de52aad29..9629d152d 100644 --- a/packages/sie_server/src/sie_server/bundle_lock.py +++ b/packages/sie_server/src/sie_server/bundle_lock.py @@ -129,6 +129,18 @@ def root_packages(root_lock: Path) -> dict[str, list[dict[str, Any]]]: return result +def root_versions_digest(root_lock: Path) -> str: + """Bind the exact third-party root versions that constrain compilation. + + Workspace members and lock serialization never reach ``bundle-libs``. + Root artifact sources are excluded here because every check revalidates + them against the current root lock through the exception policy. + """ + return canonical_digest( + {name: sorted({row["version"] for row in rows}) for name, rows in root_packages(root_lock).items()} + ) + + def root_constraints(root_lock: Path, exceptions: Mapping[str, Mapping[str, str]]) -> list[str]: """Pin every root package, except an explicitly recorded exact alternative. @@ -348,7 +360,10 @@ def check_lock( msg = "bundle compilation or verification policy changed; recompile and review" raise ValueError(msg) requirements = bundle_requirements(bundles, target) - expected_inputs = {"uv_lock_sha256": digest(root_lock), "requirements_sha256": canonical_digest(requirements)} + expected_inputs = { + "root_versions_sha256": root_versions_digest(root_lock), + "requirements_sha256": canonical_digest(requirements), + } exceptions = exception_file["targets"].get(target.name, {}) source_pins = exception_file.get("source_pins", {}).get(target.name, {}) if manifest.get("inputs") != expected_inputs or manifest.get("exceptions_sha256") != canonical_digest(exceptions): diff --git a/packages/sie_server/tests/core/test_bundle_lock.py b/packages/sie_server/tests/core/test_bundle_lock.py index e1686741b..7641dc6e0 100644 --- a/packages/sie_server/tests/core/test_bundle_lock.py +++ b/packages/sie_server/tests/core/test_bundle_lock.py @@ -18,6 +18,7 @@ read_packages, required_exceptions, root_constraints, + root_versions_digest, source_constraints, validate_exceptions, validate_source_pins, @@ -64,7 +65,10 @@ def locked(tmp_path): "uv_version": "0.9.28", "policy_sha256": policy_digest(), "target": TARGET.as_dict(), - "inputs": {"uv_lock_sha256": digest(root_lock), "requirements_sha256": canonical_digest(["demo>=1,<2"])}, + "inputs": { + "root_versions_sha256": root_versions_digest(root_lock), + "requirements_sha256": canonical_digest(["demo>=1,<2"]), + }, "lock_sha256": digest(lock), "exceptions_sha256": canonical_digest({}), "source_pins_sha256": canonical_digest({}), @@ -90,7 +94,10 @@ def test_stale_inputs_and_wrong_target_fail_before_install(locked, mutation): manifest_path = locks / TARGET.name / "manifest.json" manifest = json.loads(manifest_path.read_text()) if mutation == "root": - root_lock.write_text(root_lock.read_text() + "\n# different root lock\n") + root_lock.write_text( + root_lock.read_text() + + '[[package]]\nname = "other"\nversion = "2.0"\nsource = { registry = "https://pypi.org/simple" }\n' + ) elif mutation == "requirements": (bundles / "default.yaml").write_text('deps:\n demo: ">=1.1,<2"\n') elif mutation == "target": @@ -114,6 +121,33 @@ def test_stale_inputs_and_wrong_target_fail_before_install(locked, mutation): check_lock(*locked, TARGET) +def test_workspace_versions_and_lock_serialization_do_not_invalidate_the_closure(locked): + _locks, _bundles, root_lock = locked + root_lock.write_text( + root_lock.read_text() + .replace("version = 1\n", "version = 1\nrevision = 3\n", 1) + .replace(f'hash = "sha256:{HASH}"', f'hash = "sha256:{HASH}", upload-time = "2026-10-01T00:00:00Z"') + + '[[package]]\nname = "sie-server"\nversion = "0.11.0"\nsource = { editable = "packages/sie_server" }\n' + ) + check_lock(*locked, TARGET) + + +def test_another_root_source_row_at_a_locked_version_does_not_invalidate_the_closure(locked): + _locks, _bundles, root_lock = locked + root_lock.write_text( + root_lock.read_text() + + '[[package]]\nname = "demo"\nversion = "1.0"\nsource = { registry = "https://download.pytorch.org/whl/cpu" }\n' + ) + check_lock(*locked, TARGET) + + +def test_root_artifact_substitution_at_the_same_version_still_fails(locked): + _locks, _bundles, root_lock = locked + root_lock.write_text(root_lock.read_text().replace(HASH, "b" * 64)) + with pytest.raises(ValueError, match="bundle exceptions differ"): + check_lock(*locked, TARGET) + + @pytest.mark.parametrize("mutation", ["missing", "extra", "version", "duplicate", "source", "dependency"]) def test_actual_inventory_drift_is_rejected(locked, tmp_path, mutation): checked = check_lock(*locked, TARGET) diff --git a/tools/ci/bundle_lock.py b/tools/ci/bundle_lock.py index 51456dcf8..0825d6a95 100644 --- a/tools/ci/bundle_lock.py +++ b/tools/ci/bundle_lock.py @@ -32,6 +32,7 @@ policy_digest, read_packages, root_constraints, + root_versions_digest, source_constraints, validate_exceptions, validate_source_pins, @@ -195,7 +196,7 @@ def compile_target(root: Path, target: Target, uv: str, cutoff: str, cache: Path "indexes": index_arguments(target), "completed_artifact_hashes": completed_hashes, "inputs": { - "uv_lock_sha256": digest(root / "uv.lock"), + "root_versions_sha256": root_versions_digest(root / "uv.lock"), "requirements_sha256": canonical_digest(requirements), }, "exceptions_sha256": canonical_digest(exceptions),