tensorfs 0.3.68__tar.gz → 0.3.69__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. {tensorfs-0.3.68 → tensorfs-0.3.69}/Cargo.lock +2 -2
  2. {tensorfs-0.3.68 → tensorfs-0.3.69}/Cargo.toml +1 -1
  3. {tensorfs-0.3.68 → tensorfs-0.3.69}/PKG-INFO +1 -1
  4. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/derived/part_rows_tests.rs +47 -0
  5. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/derived/retention.rs +31 -2
  6. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/derived.rs +30 -0
  7. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-py/src/store.rs +42 -16
  8. {tensorfs-0.3.68 → tensorfs-0.3.69}/pyproject.toml +1 -1
  9. {tensorfs-0.3.68 → tensorfs-0.3.69}/python/tensorfs/_ext.pyi +9 -3
  10. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/Cargo.toml +0 -0
  11. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/build.rs +0 -0
  12. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/examples/h3-swiglu-layout.md +0 -0
  13. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/examples/optional-cache-backfill.md +0 -0
  14. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/examples/resident-conversion.md +0 -0
  15. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/examples/resident_conversion_profile.rs +0 -0
  16. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/asset_update.rs +0 -0
  17. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/b64.rs +0 -0
  18. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/bin/tfs/ckpt.rs +0 -0
  19. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/bin/tfs/conform.rs +0 -0
  20. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/bin/tfs/fetch.rs +0 -0
  21. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/bin/tfs/ingest.rs +0 -0
  22. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/bin/tfs/main.rs +0 -0
  23. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/bin/tfs/rd.rs +0 -0
  24. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/bin/tfs/repo.rs +0 -0
  25. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/bin/tfs/snap.rs +0 -0
  26. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/bin/tfs/watch.rs +0 -0
  27. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/cache_roots.rs +0 -0
  28. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/canon.rs +0 -0
  29. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/capability.rs +0 -0
  30. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/catalog.rs +0 -0
  31. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/cbor.rs +0 -0
  32. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/checkpoint.rs +0 -0
  33. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/checkpoint_root.rs +0 -0
  34. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/corpus.rs +0 -0
  35. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/derived/adoption.rs +0 -0
  36. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/derived/companion_tests.rs +0 -0
  37. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/derived/estimate.rs +0 -0
  38. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/derived/graft_tests.rs +0 -0
  39. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/derived/progress.rs +0 -0
  40. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/derived/resume_tests.rs +0 -0
  41. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/derived/roots.rs +0 -0
  42. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/descriptors.rs +0 -0
  43. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/disk.rs +0 -0
  44. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/dtype.rs +0 -0
  45. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/durability.rs +0 -0
  46. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/err.rs +0 -0
  47. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/fetch.rs +0 -0
  48. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/fill.rs +0 -0
  49. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/fit.rs +0 -0
  50. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/gc.rs +0 -0
  51. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/header.rs +0 -0
  52. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/home.rs +0 -0
  53. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/ids.rs +0 -0
  54. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/ingest/carrier.rs +0 -0
  55. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/ingest/convert.rs +0 -0
  56. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/ingest/custody.rs +0 -0
  57. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/ingest/fingerprint.rs +0 -0
  58. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/ingest/gguf.rs +0 -0
  59. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/ingest/h3_lora.rs +0 -0
  60. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/ingest/journal.rs +0 -0
  61. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/ingest/mod.rs +0 -0
  62. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/ingest/preflight.rs +0 -0
  63. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/ingest/source.rs +0 -0
  64. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/ingest/stamp.rs +0 -0
  65. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/ingest/transaction.rs +0 -0
  66. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/jcs.rs +0 -0
  67. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/lib.rs +0 -0
  68. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/limits.rs +0 -0
  69. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/machine.rs +0 -0
  70. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/manifest.rs +0 -0
  71. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/meta.rs +0 -0
  72. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/project.rs +0 -0
  73. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/providers.rs +0 -0
  74. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/read.rs +0 -0
  75. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/receipt.rs +0 -0
  76. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/reclaim.rs +0 -0
  77. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/registry.rs +0 -0
  78. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/relation.rs +0 -0
  79. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/repo_cache/identity.rs +0 -0
  80. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/repo_cache.rs +0 -0
  81. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/repository.rs +0 -0
  82. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/sha256.rs +0 -0
  83. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/source_artifact.rs +0 -0
  84. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/spec.rs +0 -0
  85. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/staging.rs +0 -0
  86. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/stats.rs +0 -0
  87. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/storage.rs +0 -0
  88. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/store.rs +0 -0
  89. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/transport/api.rs +0 -0
  90. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/transport/http.rs +0 -0
  91. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/transport/http_reuse_tests.rs +0 -0
  92. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/transport/hub.rs +0 -0
  93. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/transport/ledger.rs +0 -0
  94. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/transport/memory.rs +0 -0
  95. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/transport/mod.rs +0 -0
  96. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/transport/pace.rs +0 -0
  97. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/transport/policy.rs +0 -0
  98. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/transport/prefix.rs +0 -0
  99. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/transport/pull.rs +0 -0
  100. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/transport/push.rs +0 -0
  101. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/transport/ranged.rs +0 -0
  102. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/transport/speculate.rs +0 -0
  103. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/transport/tests.rs +0 -0
  104. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/unsynced.rs +0 -0
  105. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/src/vectors.rs +0 -0
  106. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/additive_fields.rs +0 -0
  107. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/anima_hf_profile.rs +0 -0
  108. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/checkpoint_roots.rs +0 -0
  109. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/civitai_sdxl_profile.rs +0 -0
  110. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/fd_budget.rs +0 -0
  111. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/fd_headroom.rs +0 -0
  112. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/fetch_url_progress.rs +0 -0
  113. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/fit.rs +0 -0
  114. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/fixtures/repo_apply/blobs.jsonl +0 -0
  115. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_list.jsonl +0 -0
  116. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_repository.json +0 -0
  117. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_rows.jsonl +0 -0
  118. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/fixtures/repo_apply/header.cbor +0 -0
  119. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/fixtures/repo_apply/manifest.json +0 -0
  120. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/fixtures/repo_apply/mutation.json +0 -0
  121. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/foreign_json.rs +0 -0
  122. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/h3_lora.rs +0 -0
  123. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/h3_swiglu.rs +0 -0
  124. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/home.rs +0 -0
  125. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/import_tree.rs +0 -0
  126. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/instrument_profiles.rs +0 -0
  127. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/liveness.rs +0 -0
  128. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/qwen_image21_profile.rs +0 -0
  129. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/read_plan.rs +0 -0
  130. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/reclaim.rs +0 -0
  131. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/reclaim_run.rs +0 -0
  132. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/repo_cache.rs +0 -0
  133. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/repo_store.rs +0 -0
  134. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/resident_admission.rs +0 -0
  135. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/sha256_backends.rs +0 -0
  136. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/sharded_cas.rs +0 -0
  137. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/source_artifacts.rs +0 -0
  138. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/source_download.rs +0 -0
  139. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/source_plan.rs +0 -0
  140. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/source_prefixes.rs +0 -0
  141. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/source_preflight.rs +0 -0
  142. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/staging.rs +0 -0
  143. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/store_boundaries.rs +0 -0
  144. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/tensor_schema.rs +0 -0
  145. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/transfer_memory.rs +0 -0
  146. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/trust_index.rs +0 -0
  147. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/version.rs +0 -0
  148. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-core/tests/write_through.rs +0 -0
  149. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-py/Cargo.toml +0 -0
  150. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-py/src/docs.rs +0 -0
  151. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-py/src/errors.rs +0 -0
  152. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-py/src/ingest.rs +0 -0
  153. {tensorfs-0.3.68 → tensorfs-0.3.69}/crates/tensorfs-py/src/lib.rs +0 -0
  154. {tensorfs-0.3.68 → tensorfs-0.3.69}/python/tensorfs/__init__.py +0 -0
  155. {tensorfs-0.3.68 → tensorfs-0.3.69}/python/tensorfs/_derived_channel.py +0 -0
  156. {tensorfs-0.3.68 → tensorfs-0.3.69}/python/tensorfs/_source_channel.py +0 -0
  157. {tensorfs-0.3.68 → tensorfs-0.3.69}/python/tensorfs/_tensors.py +0 -0
  158. {tensorfs-0.3.68 → tensorfs-0.3.69}/python/tensorfs/derived.py +0 -0
  159. {tensorfs-0.3.68 → tensorfs-0.3.69}/python/tensorfs/errors.py +0 -0
  160. {tensorfs-0.3.68 → tensorfs-0.3.69}/python/tensorfs/py.typed +0 -0
@@ -439,7 +439,7 @@ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
439
439
 
440
440
  [[package]]
441
441
  name = "tensorfs-core"
442
- version = "0.3.68"
442
+ version = "0.3.69"
443
443
  dependencies = [
444
444
  "fs2",
445
445
  "libc",
@@ -454,7 +454,7 @@ dependencies = [
454
454
 
455
455
  [[package]]
456
456
  name = "tensorfs-py"
457
- version = "0.3.68"
457
+ version = "0.3.69"
458
458
  dependencies = [
459
459
  "pyo3",
460
460
  "tensorfs-core",
@@ -3,5 +3,5 @@ resolver = "2"
3
3
  members = ["crates/tensorfs-core", "crates/tensorfs-py"]
4
4
 
5
5
  [workspace.package]
6
- version = "0.3.68"
6
+ version = "0.3.69"
7
7
  edition = "2021"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tensorfs
3
- Version: 0.3.68
3
+ Version: 0.3.69
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Rust
@@ -324,3 +324,50 @@ fn an_indexed_checkpoint_does_not_reread_its_chain() {
324
324
  release_guards(&meta, Some(writer.writer_hold), writer.source_leases);
325
325
  std::fs::remove_dir_all(store.root()).unwrap();
326
326
  }
327
+
328
+ #[test]
329
+ fn a_digest_receipt_names_the_declaration_the_intent_bound() {
330
+ let (store, meta, id, declaration) = fixture("receipt-digest");
331
+ let intent = declaration.clone().canonical_work_bytes().unwrap();
332
+ let writer = begin(&store, &meta, &id, 1, declaration, None).unwrap();
333
+ for (index, key) in ["weight_0", "weight_1", "weight_2"].into_iter().enumerate() {
334
+ add(&store, &meta, &id, &writer, key, index as u8 + 1).unwrap();
335
+ }
336
+ let receipt = commit(&store, &meta, &id, 1).unwrap();
337
+ let inline = receipt.value(ReceiptDeclaration::Inline);
338
+ let digest = receipt.value(ReceiptDeclaration::Digest);
339
+ assert_eq!(inline, receipt.to_value());
340
+ let field = |value: &Value, name: &str| {
341
+ as_obj("receipt", "facts", value)
342
+ .unwrap()
343
+ .iter()
344
+ .find(|(key, _)| key == name)
345
+ .map(|(_, value)| value.clone())
346
+ };
347
+ assert_eq!(
348
+ crate::canon::write(&field(&inline, "declaration").unwrap()),
349
+ intent
350
+ );
351
+ assert!(field(&digest, "declaration").is_none());
352
+ assert_eq!(
353
+ field(&digest, "declaration_digest"),
354
+ Some(Value::str(ObjectRef::of(&intent).id()))
355
+ );
356
+ assert_eq!(
357
+ field(&digest, "declaration_length"),
358
+ Some(Value::uint(intent.len() as u64))
359
+ );
360
+ // Every other fact is identical in both forms.
361
+ let others = |value: &Value| {
362
+ as_obj("receipt", "facts", value)
363
+ .unwrap()
364
+ .iter()
365
+ .filter(|(key, _)| !key.starts_with("declaration"))
366
+ .cloned()
367
+ .collect::<Vec<_>>()
368
+ };
369
+ assert_eq!(others(&inline), others(&digest));
370
+ let _ = fence(&meta, &id, 1);
371
+ release_guards(&meta, Some(writer.writer_hold), writer.source_leases);
372
+ std::fs::remove_dir_all(store.root()).unwrap();
373
+ }
@@ -5,7 +5,8 @@ use std::io::{Read, Write};
5
5
  use std::path::PathBuf;
6
6
 
7
7
  use super::{
8
- checkpoint, receipt_from, require_payload_record, roots, Disposition, TransactionState,
8
+ checkpoint, receipt_from, require_payload_record, roots, Disposition, ReceiptDeclaration,
9
+ TransactionState,
9
10
  };
10
11
  use crate::canon::{Fields, Value};
11
12
  use crate::catalog::WriterGuard;
@@ -217,7 +218,12 @@ pub fn retain_result(
217
218
  );
218
219
  }
219
220
  let facts = receipt_from(store, row)?;
220
- if object_id(&crate::canon::write(&facts.to_value())) != receipt {
221
+ // A receipt's identity is the digest of whichever form its caller was handed.
222
+ let forms = [ReceiptDeclaration::Inline, ReceiptDeclaration::Digest];
223
+ if !forms
224
+ .iter()
225
+ .any(|form| object_id(&crate::canon::write(&facts.value(*form))) == receipt)
226
+ {
221
227
  return refuse(
222
228
  Code::OBJECT_ID_MISMATCH,
223
229
  "retained result receipt differs from committed computation",
@@ -369,6 +375,29 @@ mod tests {
369
375
  }
370
376
  }
371
377
 
378
+ #[test]
379
+ fn a_result_is_retained_under_either_receipt_form_and_nothing_else() {
380
+ let f = fixture();
381
+ let facts = derived::lookup(&f.store, &f.meta, &f.transaction).unwrap();
382
+ let derived::Lookup::Committed(committed) = facts else {
383
+ panic!("committed result expected")
384
+ };
385
+ let digest_form = object_id(&crate::canon::write(
386
+ &committed.receipt().value(ReceiptDeclaration::Digest),
387
+ ));
388
+ assert_ne!(digest_form, f.receipt);
389
+ let retained =
390
+ retain_result(&f.store, &f.meta, &f.transaction, &digest_form, &id("b1")).unwrap();
391
+ assert_eq!(retained.receipt_digest, digest_form);
392
+ retain_result(&f.store, &f.meta, &f.transaction, &f.receipt, &id("b2")).unwrap();
393
+ assert_eq!(
394
+ retain_result(&f.store, &f.meta, &f.transaction, &id("99"), &id("b3"))
395
+ .unwrap_err()
396
+ .code,
397
+ Code::OBJECT_ID_MISMATCH
398
+ );
399
+ }
400
+
372
401
  #[test]
373
402
  fn independent_owners_keep_original_bytes_and_receipt_through_disposal_and_gc() {
374
403
  let f = fixture();
@@ -3421,8 +3421,38 @@ pub struct ReceiptFacts {
3421
3421
  pub inherited_payload_bytes: u64,
3422
3422
  }
3423
3423
 
3424
+ /// How a receipt names its declaration: the whole document, or its digest and length.
3425
+ /// The whole document grows with the tensors declared; a digest keeps receipts constant.
3426
+ #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
3427
+ pub enum ReceiptDeclaration {
3428
+ #[default]
3429
+ Inline,
3430
+ Digest,
3431
+ }
3432
+
3424
3433
  impl ReceiptFacts {
3425
3434
  pub fn to_value(&self) -> Value {
3435
+ self.value(ReceiptDeclaration::Inline)
3436
+ }
3437
+
3438
+ pub fn value(&self, form: ReceiptDeclaration) -> Value {
3439
+ let mut value = self.inline_value();
3440
+ if form == ReceiptDeclaration::Digest {
3441
+ if let Value::Obj(fields) = &mut value {
3442
+ let bytes = crate::canon::write(&self.declaration.to_value());
3443
+ fields.retain(|(key, _)| key != "declaration");
3444
+ fields.push((
3445
+ "declaration_digest".into(),
3446
+ Value::str(ObjectRef::of(&bytes).id()),
3447
+ ));
3448
+ fields.push(("declaration_length".into(), Value::uint(bytes.len() as u64)));
3449
+ fields.sort_by(|left, right| left.0.cmp(&right.0));
3450
+ }
3451
+ }
3452
+ value
3453
+ }
3454
+
3455
+ fn inline_value(&self) -> Value {
3426
3456
  Value::obj(vec![
3427
3457
  (
3428
3458
  "added_objects",
@@ -454,8 +454,26 @@ fn source_component<'p>(
454
454
  Ok(rows)
455
455
  }
456
456
 
457
- fn receipt<'p>(py: Python<'p>, receipt: &derived::ReceiptFacts) -> PyResult<Bound<'p, PyAny>> {
458
- marshal(py, &receipt.to_value())
457
+ fn receipt<'p>(
458
+ py: Python<'p>,
459
+ receipt: &derived::ReceiptFacts,
460
+ form: derived::ReceiptDeclaration,
461
+ ) -> PyResult<Bound<'p, PyAny>> {
462
+ marshal(py, &receipt.value(form))
463
+ }
464
+
465
+ fn receipt_form(py: Python<'_>, name: &str) -> PyResult<derived::ReceiptDeclaration> {
466
+ match name {
467
+ "inline" => Ok(derived::ReceiptDeclaration::Inline),
468
+ "digest" => Ok(derived::ReceiptDeclaration::Digest),
469
+ _ => Err(to_py(
470
+ py,
471
+ &Refusal {
472
+ code: Code::UNKNOWN_FIELD,
473
+ detail: format!("receipts must be \"inline\" or \"digest\", not {name:?}"),
474
+ },
475
+ )),
476
+ }
459
477
  }
460
478
 
461
479
  // ---------------------------------------------------------------- store
@@ -559,42 +577,48 @@ fn checkpoint_root_dict<'p>(
559
577
  pub struct PyStore {
560
578
  pub store: Arc<CoreStore>,
561
579
  pub meta: Arc<Meta>,
580
+ /// How derived receipts from this handle name their declaration. A caller that reads
581
+ /// the digest form opts in; the default keeps the declaration inline.
582
+ receipts: derived::ReceiptDeclaration,
562
583
  }
563
584
 
564
585
  fn open_store(
565
586
  py: Python<'_>,
566
587
  root: PathBuf,
567
588
  open: fn(&Path) -> tensorfs_core::err::Result<CoreStore>,
589
+ receipts: &str,
568
590
  ) -> PyResult<PyStore> {
591
+ let receipts = receipt_form(py, receipts)?;
569
592
  let s = open(&root).or_refuse(py)?;
570
593
  let m = Meta::open(&s).or_refuse(py)?;
571
594
  Ok(PyStore {
572
595
  store: Arc::new(s),
573
596
  meta: Arc::new(m),
597
+ receipts,
574
598
  })
575
599
  }
576
600
 
577
601
  #[pymethods]
578
602
  impl PyStore {
579
603
  #[staticmethod]
580
- #[pyo3(signature = (root = None))]
581
- fn ensure(py: Python<'_>, root: Option<PathBuf>) -> PyResult<PyStore> {
604
+ #[pyo3(signature = (root = None, *, receipts = "inline"))]
605
+ fn ensure(py: Python<'_>, root: Option<PathBuf>, receipts: &str) -> PyResult<PyStore> {
582
606
  let root = tensorfs_core::home::store_root(root.as_deref()).or_refuse(py)?;
583
- open_store(py, root, CoreStore::ensure)
607
+ open_store(py, root, CoreStore::ensure, receipts)
584
608
  }
585
609
 
586
610
  #[staticmethod]
587
- #[pyo3(signature = (root = None))]
588
- fn open(py: Python<'_>, root: Option<PathBuf>) -> PyResult<PyStore> {
611
+ #[pyo3(signature = (root = None, *, receipts = "inline"))]
612
+ fn open(py: Python<'_>, root: Option<PathBuf>, receipts: &str) -> PyResult<PyStore> {
589
613
  let root = tensorfs_core::home::store_root(root.as_deref()).or_refuse(py)?;
590
- open_store(py, root, CoreStore::open)
614
+ open_store(py, root, CoreStore::open, receipts)
591
615
  }
592
616
 
593
617
  #[staticmethod]
594
- #[pyo3(signature = (root = None))]
595
- fn init(py: Python<'_>, root: Option<PathBuf>) -> PyResult<PyStore> {
618
+ #[pyo3(signature = (root = None, *, receipts = "inline"))]
619
+ fn init(py: Python<'_>, root: Option<PathBuf>, receipts: &str) -> PyResult<PyStore> {
596
620
  let root = tensorfs_core::home::store_root(root.as_deref()).or_refuse(py)?;
597
- open_store(py, root, CoreStore::init)
621
+ open_store(py, root, CoreStore::init, receipts)
598
622
  }
599
623
 
600
624
  #[getter]
@@ -2278,6 +2302,7 @@ impl PyStore {
2278
2302
  source_views: begin.source_views,
2279
2303
  additions: begin.additions,
2280
2304
  cursor: Mutex::new(None),
2305
+ receipts: self.receipts,
2281
2306
  guards: Mutex::new(Some(DerivedGuards {
2282
2307
  writer_hold: begin.writer_hold,
2283
2308
  source_leases: begin.source_leases,
@@ -2347,7 +2372,7 @@ impl PyStore {
2347
2372
  }
2348
2373
  derived::Lookup::Committed(value) => {
2349
2374
  out.set_item("state", "committed")?;
2350
- out.set_item("receipt", receipt(py, value.receipt())?)?;
2375
+ out.set_item("receipt", receipt(py, value.receipt(), self.receipts)?)?;
2351
2376
  out.set_item("disposition", marshal(py, &value.disposition_value())?)?;
2352
2377
  }
2353
2378
  derived::Lookup::Abandoned => out.set_item("state", "abandoned")?,
@@ -2381,7 +2406,7 @@ impl PyStore {
2381
2406
  None => out.set_item("state", "abandoned")?,
2382
2407
  Some(value) => {
2383
2408
  out.set_item("state", "committed")?;
2384
- out.set_item("receipt", receipt(py, &value)?)?;
2409
+ out.set_item("receipt", receipt(py, &value, self.receipts)?)?;
2385
2410
  }
2386
2411
  }
2387
2412
  Ok(out)
@@ -2399,7 +2424,7 @@ impl PyStore {
2399
2424
  .or_refuse(py)?;
2400
2425
  let out = marshal(py, &value.to_value())?;
2401
2426
  out.cast::<PyDict>()?
2402
- .set_item("receipt", receipt(py, value.receipt())?)?;
2427
+ .set_item("receipt", receipt(py, value.receipt(), self.receipts)?)?;
2403
2428
  Ok(out)
2404
2429
  }
2405
2430
 
@@ -2458,7 +2483,7 @@ impl PyStore {
2458
2483
  .or_refuse(py)?;
2459
2484
  let out = marshal(py, &value.to_value())?;
2460
2485
  let out_dict = out.cast::<PyDict>()?;
2461
- out_dict.set_item("receipt", receipt(py, value.receipt())?)?;
2486
+ out_dict.set_item("receipt", receipt(py, value.receipt(), self.receipts)?)?;
2462
2487
  Ok(out)
2463
2488
  }
2464
2489
 
@@ -2568,6 +2593,7 @@ pub struct PyDerivedWriter {
2568
2593
  source_views: Vec<derived::SourceView>,
2569
2594
  additions: derived::Additions,
2570
2595
  cursor: Mutex<Option<derived::CheckpointCursor>>,
2596
+ receipts: derived::ReceiptDeclaration,
2571
2597
  guards: Mutex<Option<DerivedGuards>>,
2572
2598
  }
2573
2599
 
@@ -2873,7 +2899,7 @@ impl PyDerivedWriter {
2873
2899
  })
2874
2900
  .or_refuse(py)?;
2875
2901
  self.release_holds(py);
2876
- receipt(py, &value)
2902
+ receipt(py, &value, self.receipts)
2877
2903
  }
2878
2904
 
2879
2905
  fn fence(&self, py: Python<'_>) -> PyResult<bool> {
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "tensorfs"
7
- version = "0.3.68"
7
+ version = "0.3.69"
8
8
  description = "TensorFS — canonical tensor artifact substrate"
9
9
  requires-python = ">=3.12"
10
10
  classifiers = [
@@ -433,11 +433,17 @@ class Store:
433
433
  refuses an absent root, `ensure` creates one, `init` demands a fresh one (tfs-053)."""
434
434
 
435
435
  @staticmethod
436
- def ensure(root: StrPath | None = None) -> Store: ...
436
+ def ensure(
437
+ root: StrPath | None = None, *, receipts: Literal["inline", "digest"] = "inline"
438
+ ) -> Store: ...
437
439
  @staticmethod
438
- def open(root: StrPath | None = None) -> Store: ...
440
+ def open(
441
+ root: StrPath | None = None, *, receipts: Literal["inline", "digest"] = "inline"
442
+ ) -> Store: ...
439
443
  @staticmethod
440
- def init(root: StrPath | None = None) -> Store: ...
444
+ def init(
445
+ root: StrPath | None = None, *, receipts: Literal["inline", "digest"] = "inline"
446
+ ) -> Store: ...
441
447
  @property
442
448
  def root(self) -> str: ...
443
449
  def bind_fingerprint_registry(self, data: bytes | None) -> None: ...