straw-queue 0.1.7__tar.gz → 0.1.8__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 (389) hide show
  1. {straw_queue-0.1.7 → straw_queue-0.1.8}/CHANGELOG.md +34 -0
  2. {straw_queue-0.1.7 → straw_queue-0.1.8}/CHANGELOG_zh.md +14 -0
  3. {straw_queue-0.1.7 → straw_queue-0.1.8}/Cargo.lock +1 -1
  4. {straw_queue-0.1.7 → straw_queue-0.1.8}/Cargo.toml +1 -1
  5. {straw_queue-0.1.7 → straw_queue-0.1.8}/PKG-INFO +1 -1
  6. {straw_queue-0.1.7 → straw_queue-0.1.8}/docs/DESIGN.md +17 -2
  7. {straw_queue-0.1.7 → straw_queue-0.1.8}/docs/DESIGN_zh.md +5 -1
  8. {straw_queue-0.1.7 → straw_queue-0.1.8}/docs/USAGE.md +19 -0
  9. {straw_queue-0.1.7 → straw_queue-0.1.8}/docs/USAGE_zh.md +16 -0
  10. {straw_queue-0.1.7 → straw_queue-0.1.8}/pyproject.toml +1 -1
  11. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/coordinator.rs +44 -10
  12. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/gc.rs +45 -0
  13. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/gc_writer.rs +28 -0
  14. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/python.rs +40 -1
  15. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/store.rs +3 -5
  16. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/writer_gate.rs +7 -0
  17. {straw_queue-0.1.7 → straw_queue-0.1.8}/src/straw/coordinator.py +24 -1
  18. {straw_queue-0.1.7 → straw_queue-0.1.8}/src/straw/store.py +17 -2
  19. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_gc_concurrency.py +46 -1
  20. straw_queue-0.1.8/tests/test_supervised_leases.py +82 -0
  21. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_writer_retirement.py +43 -0
  22. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/environment_contracts.json +4 -3
  23. {straw_queue-0.1.7 → straw_queue-0.1.8}/.agents/skills/straw-protocol-review/SKILL.md +0 -0
  24. {straw_queue-0.1.7 → straw_queue-0.1.8}/.agents/skills/straw-protocol-review/agents/openai.yaml +0 -0
  25. {straw_queue-0.1.7 → straw_queue-0.1.8}/.github/workflows/tests.yml +0 -0
  26. {straw_queue-0.1.7 → straw_queue-0.1.8}/.github/workflows/wheels.yml +0 -0
  27. {straw_queue-0.1.7 → straw_queue-0.1.8}/CONTRIBUTING.md +0 -0
  28. {straw_queue-0.1.7 → straw_queue-0.1.8}/CONTRIBUTING_zh.md +0 -0
  29. {straw_queue-0.1.7 → straw_queue-0.1.8}/LICENSE +0 -0
  30. {straw_queue-0.1.7 → straw_queue-0.1.8}/README.md +0 -0
  31. {straw_queue-0.1.7 → straw_queue-0.1.8}/README_zh.md +0 -0
  32. {straw_queue-0.1.7 → straw_queue-0.1.8}/docs/BENCHMARKS.md +0 -0
  33. {straw_queue-0.1.7 → straw_queue-0.1.8}/docs/BENCHMARKS_zh.md +0 -0
  34. {straw_queue-0.1.7 → straw_queue-0.1.8}/docs/FILESYSTEM.md +0 -0
  35. {straw_queue-0.1.7 → straw_queue-0.1.8}/docs/FILESYSTEM_zh.md +0 -0
  36. {straw_queue-0.1.7 → straw_queue-0.1.8}/docs/FORMAT.md +0 -0
  37. {straw_queue-0.1.7 → straw_queue-0.1.8}/docs/FORMAT_zh.md +0 -0
  38. {straw_queue-0.1.7 → straw_queue-0.1.8}/docs/README.md +0 -0
  39. {straw_queue-0.1.7 → straw_queue-0.1.8}/docs/README_zh.md +0 -0
  40. {straw_queue-0.1.7 → straw_queue-0.1.8}/examples/multiprocess.py +0 -0
  41. {straw_queue-0.1.7 → straw_queue-0.1.8}/examples/records.py +0 -0
  42. {straw_queue-0.1.7 → straw_queue-0.1.8}/examples/tensors.py +0 -0
  43. {straw_queue-0.1.7 → straw_queue-0.1.8}/examples/work_queue.py +0 -0
  44. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/access.rs +0 -0
  45. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/bounds.rs +0 -0
  46. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/catalog_append.rs +0 -0
  47. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/catalog_decode.rs +0 -0
  48. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/catalog_event.rs +0 -0
  49. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/catalog_frame.rs +0 -0
  50. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/catalog_json.rs +0 -0
  51. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/catalog_log.rs +0 -0
  52. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/catalog_namespace.rs +0 -0
  53. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/catalog_primitives.rs +0 -0
  54. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/catalog_runtime.rs +0 -0
  55. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/catalog_state.rs +0 -0
  56. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/catalog_target.rs +0 -0
  57. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/catalog_transaction.rs +0 -0
  58. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/closure.rs +0 -0
  59. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/closure_node.rs +0 -0
  60. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/consumption.rs +0 -0
  61. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/directory.rs +0 -0
  62. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/directory_io.rs +0 -0
  63. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/discovery.rs +0 -0
  64. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/extent_retry.rs +0 -0
  65. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/file_adapter.rs +0 -0
  66. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/file_primitives.rs +0 -0
  67. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/finalization.rs +0 -0
  68. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/framing.rs +0 -0
  69. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/gc_batch.rs +0 -0
  70. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/gc_collection.rs +0 -0
  71. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/gc_io.rs +0 -0
  72. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/gc_mark.rs +0 -0
  73. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/gc_plan.rs +0 -0
  74. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/gc_receipt.rs +0 -0
  75. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/graph_proof_tests.rs +0 -0
  76. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/holding.rs +0 -0
  77. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/identities.rs +0 -0
  78. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/incomplete.rs +0 -0
  79. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/journal.rs +0 -0
  80. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/journal_append.rs +0 -0
  81. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/journal_io.rs +0 -0
  82. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/journal_owner.rs +0 -0
  83. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/journal_primitives.rs +0 -0
  84. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/journal_recovery.rs +0 -0
  85. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/journal_state.rs +0 -0
  86. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/json_update.rs +0 -0
  87. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/lib.rs +0 -0
  88. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/namespace_identity.rs +0 -0
  89. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/namespace_step.rs +0 -0
  90. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/namespace_target.rs +0 -0
  91. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/payload.rs +0 -0
  92. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/pending.rs +0 -0
  93. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/protocol.rs +0 -0
  94. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/queue_admin.rs +0 -0
  95. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/queue_application.rs +0 -0
  96. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/queue_commit.rs +0 -0
  97. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/queue_event.rs +0 -0
  98. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/queue_gc.rs +0 -0
  99. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/queue_gc_prepare.rs +0 -0
  100. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/queue_handoff.rs +0 -0
  101. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/queue_history.rs +0 -0
  102. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/queue_metadata.rs +0 -0
  103. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/queue_progress.rs +0 -0
  104. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/queue_reader.rs +0 -0
  105. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/queue_receipt.rs +0 -0
  106. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/queue_replay.rs +0 -0
  107. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/queue_retirement.rs +0 -0
  108. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/queue_roots.rs +0 -0
  109. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/queue_state.rs +0 -0
  110. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/queue_task.rs +0 -0
  111. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/queue_transition.rs +0 -0
  112. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/read_window.rs +0 -0
  113. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/reclamation.rs +0 -0
  114. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/record_index.rs +0 -0
  115. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/record_manifest.rs +0 -0
  116. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/reference_node.rs +0 -0
  117. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/reference_validation.rs +0 -0
  118. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/replay.rs +0 -0
  119. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/request.rs +0 -0
  120. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/selection.rs +0 -0
  121. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/staging_consumption.rs +0 -0
  122. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/stream.rs +0 -0
  123. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/traversal.rs +0 -0
  124. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/typed.rs +0 -0
  125. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/wal.rs +0 -0
  126. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/wal_append.rs +0 -0
  127. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/wal_device.rs +0 -0
  128. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/wal_file.rs +0 -0
  129. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/wal_recovery.rs +0 -0
  130. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/wal_sink.rs +0 -0
  131. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/wal_source.rs +0 -0
  132. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/writer_gate_flow.rs +0 -0
  133. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/writer_ownership.rs +0 -0
  134. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/writer_registry.rs +0 -0
  135. {straw_queue-0.1.7 → straw_queue-0.1.8}/rust/writer_retirement.rs +0 -0
  136. {straw_queue-0.1.7 → straw_queue-0.1.8}/src/straw/__init__.py +0 -0
  137. {straw_queue-0.1.7 → straw_queue-0.1.8}/src/straw/backend.py +0 -0
  138. {straw_queue-0.1.7 → straw_queue-0.1.8}/src/straw/benchmark.py +0 -0
  139. {straw_queue-0.1.7 → straw_queue-0.1.8}/src/straw/errors.py +0 -0
  140. {straw_queue-0.1.7 → straw_queue-0.1.8}/src/straw/inspect.py +0 -0
  141. {straw_queue-0.1.7 → straw_queue-0.1.8}/src/straw/journal.py +0 -0
  142. {straw_queue-0.1.7 → straw_queue-0.1.8}/src/straw/protocol.py +0 -0
  143. {straw_queue-0.1.7 → straw_queue-0.1.8}/src/straw/reporting.py +0 -0
  144. {straw_queue-0.1.7 → straw_queue-0.1.8}/src/straw/rpc.py +0 -0
  145. {straw_queue-0.1.7 → straw_queue-0.1.8}/src/straw/tensor.py +0 -0
  146. {straw_queue-0.1.7 → straw_queue-0.1.8}/src/straw/tracing.py +0 -0
  147. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/native.rs +0 -0
  148. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_artifact_privacy.py +0 -0
  149. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_benchmark_profile.py +0 -0
  150. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_catalog_encoding.py +0 -0
  151. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_commit_consumption.py +0 -0
  152. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_commit_protocol.py +0 -0
  153. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_confined_open.py +0 -0
  154. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_consumer_rewind.py +0 -0
  155. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_dependency_traversal.py +0 -0
  156. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_durable_handoff.py +0 -0
  157. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_extent_bounds.py +0 -0
  158. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_extent_visibility.py +0 -0
  159. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_filesystem_queue.py +0 -0
  160. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_gc_batch.py +0 -0
  161. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_gc_extent_validation.py +0 -0
  162. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_gc_missing_parent.py +0 -0
  163. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_gc_receipts.py +0 -0
  164. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_graph_proof_operations.py +0 -0
  165. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_incomplete_publication.py +0 -0
  166. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_journal_contract.py +0 -0
  167. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_json_roundtrip.py +0 -0
  168. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_lease_selection.py +0 -0
  169. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_metadata_recovery.py +0 -0
  170. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_offline_aliases.py +0 -0
  171. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_offline_inspection.py +0 -0
  172. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_offline_recovery.py +0 -0
  173. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_online_gc.py +0 -0
  174. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_packed_store.py +0 -0
  175. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_parallel_validation.py +0 -0
  176. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_path_identity.py +0 -0
  177. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_pending_publication.py +0 -0
  178. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_provisional_holds.py +0 -0
  179. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_publication_handoff.py +0 -0
  180. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_publication_preparation.py +0 -0
  181. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_queue_gc_roots.py +0 -0
  182. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_read_session.py +0 -0
  183. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_ready_validation_scope.py +0 -0
  184. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_request_replay.py +0 -0
  185. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_resume_workflow.py +0 -0
  186. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_staging_consumption.py +0 -0
  187. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_streaming_write.py +0 -0
  188. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_task_recovery.py +0 -0
  189. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_tensor.py +0 -0
  190. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_tensor_batch_reads.py +0 -0
  191. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_tensor_granularity.py +0 -0
  192. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_tensor_preflight.py +0 -0
  193. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_unlimited_capacity.py +0 -0
  194. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_verification_runner.py +0 -0
  195. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_visibility_probe.py +0 -0
  196. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_wal_recovery.py +0 -0
  197. {straw_queue-0.1.7 → straw_queue-0.1.8}/tests/test_writer_access.py +0 -0
  198. {straw_queue-0.1.7 → straw_queue-0.1.8}/tools/check_artifact_privacy.py +0 -0
  199. {straw_queue-0.1.7 → straw_queue-0.1.8}/tools/check_wheel.py +0 -0
  200. {straw_queue-0.1.7 → straw_queue-0.1.8}/tools/release_build.py +0 -0
  201. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/README.md +0 -0
  202. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/README_zh.md +0 -0
  203. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/common/README.md +0 -0
  204. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/common/probes/runtime_performance.py +0 -0
  205. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/common/tla/EnvironmentCatalog.tla +0 -0
  206. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/common/tla/EnvironmentGraph.tla +0 -0
  207. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/common/tla/EnvironmentInputs.tla +0 -0
  208. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/common/tla/EnvironmentNamespace.tla +0 -0
  209. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/common/tla/EnvironmentPersistence.tla +0 -0
  210. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/common/tla/EnvironmentProgress.tla +0 -0
  211. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/common/tla/EnvironmentRead.tla +0 -0
  212. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/README.md +0 -0
  213. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/probes/catalog_discovery_fault.c +0 -0
  214. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/probes/catalog_discovery_probe.py +0 -0
  215. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/probes/gc_cross_client.py +0 -0
  216. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/probes/gc_history_probe.py +0 -0
  217. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/probes/gc_parent_fault.c +0 -0
  218. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/probes/gc_parent_probe.py +0 -0
  219. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/probes/gc_receipt_probe.py +0 -0
  220. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/probes/gc_scale.py +0 -0
  221. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/probes/gc_stat_fault.c +0 -0
  222. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/probes/gc_stat_probe.py +0 -0
  223. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/tla/EnvironmentParentRepair.tla +0 -0
  224. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/tla/EnvironmentWriter.tla +0 -0
  225. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/tla/EnvironmentWriterRetirement.tla +0 -0
  226. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/tla/GCBatchDeletion.cfg +0 -0
  227. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/tla/GCBatchDeletion.tla +0 -0
  228. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/tla/GCConcurrentDeletion.cfg +0 -0
  229. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/tla/GCConcurrentDeletion.tla +0 -0
  230. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/tla/GCDeletion.cfg +0 -0
  231. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/tla/GCDeletion.tla +0 -0
  232. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/tla/GCDiscovery.cfg +0 -0
  233. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/tla/GCDiscovery.tla +0 -0
  234. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/tla/GCParentRepair.cfg +0 -0
  235. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/tla/GCParentRepair.tla +0 -0
  236. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/tla/GCReceipt.cfg +0 -0
  237. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/tla/GCReceipt.tla +0 -0
  238. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/tla/WriterLifetime.cfg +0 -0
  239. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/tla/WriterLifetime.tla +0 -0
  240. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/tla/WriterRetirement.cfg +0 -0
  241. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/gc/tla/WriterRetirement.tla +0 -0
  242. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/measurements/20261026-release-100gb.json +0 -0
  243. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/measurements/20261026-release-comparison.json +0 -0
  244. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/measurements/20261026-release-visibility.json +0 -0
  245. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/offline/README.md +0 -0
  246. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/offline/probes/offline_alias_probe.py +0 -0
  247. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/offline/probes/offline_guard_fault.c +0 -0
  248. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/offline/probes/offline_guard_probe.py +0 -0
  249. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/offline/tla/EnvironmentBindings.tla +0 -0
  250. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/offline/tla/EnvironmentOffline.tla +0 -0
  251. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/offline/tla/OfflineAliases.cfg +0 -0
  252. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/offline/tla/OfflineAliases.tla +0 -0
  253. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/offline/tla/OfflineCleanup.cfg +0 -0
  254. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/offline/tla/OfflineCleanup.tla +0 -0
  255. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/offline/tla/OfflineRoots.cfg +0 -0
  256. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/offline/tla/OfflineRoots.tla +0 -0
  257. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/README.md +0 -0
  258. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/checks/ownership_model.py +0 -0
  259. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/probes/commit_cleanup_probe.py +0 -0
  260. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/probes/consumption_probe.py +0 -0
  261. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/probes/handoff_extent_probe.rs +0 -0
  262. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/probes/handoff_probe.py +0 -0
  263. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/probes/lease_selection_probe.py +0 -0
  264. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/probes/provisional_hold_probe.py +0 -0
  265. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/probes/queue_record_probe.rs +0 -0
  266. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/probes/reactivation_peer_probe.py +0 -0
  267. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/probes/reactivation_probe.rs +0 -0
  268. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/probes/workflow_extent_probe.rs +0 -0
  269. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/CommitApplication.cfg +0 -0
  270. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/CommitApplication.tla +0 -0
  271. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/CommitCleanup.cfg +0 -0
  272. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/CommitCleanup.tla +0 -0
  273. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/Completion.cfg +0 -0
  274. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/Completion.tla +0 -0
  275. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/CrossLogComposition.cfg +0 -0
  276. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/CrossLogComposition.tla +0 -0
  277. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/DurableHandoff.cfg +0 -0
  278. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/DurableHandoff.tla +0 -0
  279. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/EnvironmentApplication.tla +0 -0
  280. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/EnvironmentClock.tla +0 -0
  281. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/EnvironmentCommitCleanup.tla +0 -0
  282. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/EnvironmentConsumption.tla +0 -0
  283. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/EnvironmentCrossComposition.tla +0 -0
  284. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/EnvironmentCrossLog.tla +0 -0
  285. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/EnvironmentHandoff.tla +0 -0
  286. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/EnvironmentReaders.tla +0 -0
  287. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/EnvironmentRequest.tla +0 -0
  288. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/EnvironmentTraining.tla +0 -0
  289. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/Ownership.cfg +0 -0
  290. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/Ownership.tla +0 -0
  291. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/ProvisionalHold.cfg +0 -0
  292. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/ProvisionalHold.tla +0 -0
  293. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/PublicationHandoff.cfg +0 -0
  294. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/PublicationHandoff.tla +0 -0
  295. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/QueueRecordWorkflow.cfg +0 -0
  296. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/QueueRecordWorkflow.tla +0 -0
  297. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/ReactivationRace.cfg +0 -0
  298. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/ReactivationRace.tla +0 -0
  299. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/ReactivationScan.cfg +0 -0
  300. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/ReactivationScan.tla +0 -0
  301. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/RequestReplay.cfg +0 -0
  302. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/RequestReplay.tla +0 -0
  303. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/SharedRecordWorkflow.cfg +0 -0
  304. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/SharedRecordWorkflow.tla +0 -0
  305. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/StagingConsumption.cfg +0 -0
  306. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/StagingConsumption.tla +0 -0
  307. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/TrainingResume.cfg +0 -0
  308. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/protocol/tla/TrainingResume.tla +0 -0
  309. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/runners/check_mutations.py +0 -0
  310. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/runners/check_tla.py +0 -0
  311. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/runners/environment_manifest.py +0 -0
  312. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/README.md +0 -0
  313. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/catalog_encoding_probe.py +0 -0
  314. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/dependency_probe.py +0 -0
  315. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/directory_sync_fault.c +0 -0
  316. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/directory_sync_probe.py +0 -0
  317. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/extent_visibility_probe.py +0 -0
  318. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/filesystem_bandwidth_probe.py +0 -0
  319. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/incomplete_cleanup_fault.c +0 -0
  320. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/incomplete_publication_probe.py +0 -0
  321. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/large_extent_probe.rs +0 -0
  322. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/metadata_scaling.py +0 -0
  323. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/namespace_lookup_count.c +0 -0
  324. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/path_identity_probe.py +0 -0
  325. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/pending_publication_probe.py +0 -0
  326. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/positioned_extent_probe.rs +0 -0
  327. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/preparation_fault.c +0 -0
  328. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/preparation_probe.py +0 -0
  329. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/publication_finalize_probe.py +0 -0
  330. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/publication_retry_probe.py +0 -0
  331. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/publication_sync_fault.c +0 -0
  332. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/read_latency_probe.py +0 -0
  333. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/read_latency_trace.c +0 -0
  334. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/read_lookup_fault.c +0 -0
  335. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/read_lookup_probe.py +0 -0
  336. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/standalone_extent_probe.rs +0 -0
  337. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/tensor_granularity_probe.py +0 -0
  338. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/typed_preflight_probe.py +0 -0
  339. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/writer_access_probe.py +0 -0
  340. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/probes/writer_lifetime_probe.py +0 -0
  341. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/DependencyTraversal.cfg +0 -0
  342. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/DependencyTraversal.tla +0 -0
  343. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/DirectoryDurability.cfg +0 -0
  344. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/DirectoryDurability.tla +0 -0
  345. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/EnvironmentAuthentication.tla +0 -0
  346. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/EnvironmentIncomplete.tla +0 -0
  347. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/EnvironmentLocalAccess.tla +0 -0
  348. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/EnvironmentLookup.tla +0 -0
  349. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/EnvironmentPreparation.tla +0 -0
  350. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/EnvironmentTyped.tla +0 -0
  351. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/EnvironmentVisibility.tla +0 -0
  352. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/ExtentVisibility.cfg +0 -0
  353. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/ExtentVisibility.tla +0 -0
  354. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/IncompletePublication.cfg +0 -0
  355. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/IncompletePublication.tla +0 -0
  356. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/NamespaceIdentity.cfg +0 -0
  357. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/NamespaceIdentity.tla +0 -0
  358. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/PendingPublication.cfg +0 -0
  359. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/PendingPublication.tla +0 -0
  360. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/PublicationFinalization.cfg +0 -0
  361. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/PublicationFinalization.tla +0 -0
  362. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/PublicationPreparation.cfg +0 -0
  363. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/PublicationPreparation.tla +0 -0
  364. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/PublicationRetry.cfg +0 -0
  365. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/PublicationRetry.tla +0 -0
  366. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/RangeRead.cfg +0 -0
  367. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/RangeRead.tla +0 -0
  368. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/ReadAttempt.cfg +0 -0
  369. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/ReadAttempt.tla +0 -0
  370. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/ReadLookup.cfg +0 -0
  371. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/ReadLookup.tla +0 -0
  372. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/TypedRead.cfg +0 -0
  373. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/TypedRead.tla +0 -0
  374. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/WriterAccess.cfg +0 -0
  375. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/storage/tla/WriterAccess.tla +0 -0
  376. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/wal/README.md +0 -0
  377. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/wal/checks/crash_campaign.py +0 -0
  378. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/wal/probes/catalog_fsync_fault.c +0 -0
  379. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/wal/probes/catalog_history_probe.py +0 -0
  380. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/wal/probes/catalog_replay_probe.py +0 -0
  381. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/wal/tla/CatalogReadBuffer.cfg +0 -0
  382. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/wal/tla/CatalogReadBuffer.tla +0 -0
  383. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/wal/tla/CatalogReplay.cfg +0 -0
  384. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/wal/tla/CatalogReplay.tla +0 -0
  385. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/wal/tla/CatalogTransaction.cfg +0 -0
  386. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/wal/tla/CatalogTransaction.tla +0 -0
  387. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/wal/tla/EnvironmentWal.tla +0 -0
  388. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/wal/tla/WalRecovery.cfg +0 -0
  389. {straw_queue-0.1.7 → straw_queue-0.1.8}/verification/wal/tla/WalRecovery.tla +0 -0
@@ -2,6 +2,40 @@
2
2
 
3
3
  [中文版](CHANGELOG_zh.md)
4
4
 
5
+ ## 0.1.8
6
+
7
+ ### Supervised workers and reclamation
8
+
9
+ - Add `Coordinator(..., lease_seconds=None)` for externally supervised workers.
10
+ Leases do not expire with elapsed time; attempt/epoch fencing and explicit
11
+ worker retirement remain enforced. The default is still 300 seconds, and
12
+ recovery requires the queue's original lease configuration.
13
+ - Release both Python and native coordinator locks after GC commits root
14
+ retirement and tombstones, allowing heartbeats, acquisitions and completions
15
+ during physical deletion and directory synchronization. GC calls on the same
16
+ coordinator remain serialized. Return separate queue-lock wait, preparation
17
+ and detached-collection timings.
18
+ - Add opt-in `retire_writer(..., discard_staging=True)` to release only that
19
+ writer's registered transfer staging after retirement and sealing. Preserve
20
+ independent queue, checkpoint, reader and other writer owners. Retrying after
21
+ an uncertain retirement remains supported.
22
+ - Add `retention_summary()` for owner categories, retained and reclaimable
23
+ packs, pending deletions and open writers, with bounded examples and no
24
+ payload reads. Category pack counts can overlap.
25
+
26
+ ### Compatibility and limits
27
+
28
+ - WAL and packed-record formats remain unchanged. The new lease and staging
29
+ options require 0.1.8; existing timed leases and staging retention remain the
30
+ defaults. Supervised mode requires external failure detection and confirmation
31
+ that a worker has exited before retirement. Before discarding staging, stop
32
+ its readers and preserve accepted results through independent durable owners;
33
+ returned references no longer have staging protection.
34
+ - Models retain their existing expiration, owner-release and deletion-plan
35
+ abstractions. Concrete regressions cover supervised fencing, concurrent queue
36
+ calls during GC and retirement/staging retries; they do not establish a
37
+ shared-filesystem latency bound or automatic failure detection.
38
+
5
39
  ## 0.1.7
6
40
 
7
41
  ### Batched reads and validation
@@ -2,6 +2,20 @@
2
2
 
3
3
  [English](CHANGELOG.md)
4
4
 
5
+ ## 0.1.8
6
+
7
+ ### 受监督的 worker 与回收
8
+
9
+ - 新增 `Coordinator(..., lease_seconds=None)`,由外部监督器管理 worker 存活性。租约不会随时间自动过期,仍执行 attempt/epoch fencing 和显式 worker 退休;默认仍为 300 秒,恢复时必须沿用队列原有的 lease 配置。
10
+ - GC 持久化根退休和 tombstone 后,释放 Python 与原生 coordinator 锁,使 heartbeat、acquire 和 complete 可以与物理删除、目录同步并行。同一 coordinator 的 GC 调用仍串行;返回队列锁等待、准备和锁外回收的独立耗时。
11
+ - 新增显式选项 `retire_writer(..., discard_staging=True)`,在退休、封口完成后,仅释放该 writer 已登记的 transfer staging。保留独立的 queue、checkpoint、reader 和其他 writer owner;支持退休结果不确定后的重试。
12
+ - 新增 `retention_summary()`,报告 owner 类别、受保留/可回收 pack、待完成删除和未封口 writer,限制示例数量且不读取 payload。不同类别的 pack 计数可能重叠。
13
+
14
+ ### 兼容与限制
15
+
16
+ - WAL 和 packed-record 格式不变。新的 lease 与 staging 选项需要 0.1.8;默认仍使用定时租约并保留 staging。受监督模式要求外部故障探测,并在退休前确认 worker 已退出。放弃 staging 前须停止相关 reader,以独立持久 owner 保留已接受结果;返回引用不再受 staging 保护。
17
+ - 模型沿用既有的过期、owner 释放与删除计划抽象;实际回归覆盖受监督 fencing、GC 期间的队列并发调用和退休/staging 重试,不据此承诺共享文件系统延迟上限或自动故障探测。
18
+
5
19
  ## 0.1.7
6
20
 
7
21
  ### 批量读取与校验
@@ -370,7 +370,7 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
370
370
 
371
371
  [[package]]
372
372
  name = "straw"
373
- version = "0.1.7"
373
+ version = "0.1.8"
374
374
  dependencies = [
375
375
  "libc",
376
376
  "pyo3",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "straw"
3
- version = "0.1.7"
3
+ version = "0.1.8"
4
4
  edition = "2024"
5
5
  rust-version = "1.89"
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: straw-queue
3
- Version: 0.1.7
3
+ Version: 0.1.8
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Operating System :: POSIX :: Linux
6
6
  Classifier: Programming Language :: Python :: 3
@@ -109,6 +109,9 @@ payload may already have been collected. Conflicting content is an error. A new
109
109
  request must pass current task/attempt/epoch and expiry checks. Recovery revokes
110
110
  old work authority but preserves outstanding reader roots. External fencing must
111
111
  exclude concurrent coordinators; an `exclusive_owner` string is not enforcement.
112
+ With `lease_seconds=None`, time does not expire a lease: an external supervisor
113
+ must detect failure and confirm process exit before `retire_worker`. Attempt and
114
+ epoch checks remain mandatory, and recovery must preserve the lease mode.
112
115
 
113
116
  <a id="c4"></a>
114
117
 
@@ -272,8 +275,11 @@ tombstone; late retain cannot revive that path. Live extents protect whole packs
272
275
  Collectors may overlap: unlink is idempotent, and every collector completes its
273
276
  own parent barriers. Never include targets added after this attempt's snapshot
274
277
  in its receipt. A failed attempt leaves durable pending work for a later call.
275
- The lower `LockedCatalog::collect` compatibility API retains its enclosing lock;
276
- one coordinator's own calls also remain serialized.
278
+ The lower `LockedCatalog::collect` compatibility API retains its enclosing lock.
279
+ Python coordinator GC calls serialize with each other, but release both queue
280
+ locks after root retirement and tombstone preparation. Other queue calls may
281
+ proceed during physical deletion; receipt completion refreshes the catalog and
282
+ uses only the captured plan.
277
283
  Offline cleanup requires stopped clients, no shared catalog/queue markers, a
278
284
  complete accepted-root/path-binding trace and a fresh unchanged inspection.
279
285
 
@@ -288,6 +294,15 @@ persists the retirement receipt. Recover authenticates complete results and stag
288
294
  them before release; abandon makes no readable-result claim. A retry may repeat
289
295
  any already-effective step. PID, age and timeout never establish safe retirement.
290
296
 
297
+ Explicit `discard_staging=True` releases the exact staging owners of that
298
+ writer's registered results in a separate catalog transaction after retirement.
299
+ It does not recursively release dependency writers' staging. The caller must
300
+ first stop relevant readers and independently retain results still needed;
301
+ returned references have no staging protection. A retry must finish retirement
302
+ before releasing staging again and must preserve other owners. `WriterRetirement`
303
+ models through the retirement receipt; subsequent explicit abandonment uses the
304
+ owner-release rules, with concrete regressions checking the API composition.
305
+
291
306
  Safety holds while owners remain and errors recur. Eventual reclamation concerns
292
307
  sealed **permanently unowned** objects and additionally requires continued GC calls,
293
308
  returning lock/I/O operations, eventual stabilization and fair scheduling. No
@@ -72,6 +72,8 @@ Catalog 与队列日志独立。步骤之间崩溃可以多保留保护,不能
72
72
 
73
73
  **C2 — 身份与重试。** 稳定请求 ID 绑定内容和结果。先查询匹配的已记录回执,再考虑旧 payload 或 lease 校验;payload 可能已经回收。内容冲突必须报错。新请求必须校验当前 task/attempt/epoch 和过期状态。恢复撤销旧工作权限,但保留尚未退出的 reader 根。外部 fencing 必须排除并发协调器;`exclusive_owner` 字符串不执行 fencing。
74
74
 
75
+ `lease_seconds=None` 时,时间不会使 lease 过期:外部监督器必须探测故障,并在调用 `retire_worker` 前确认进程退出。Attempt/epoch 检查仍然必需,恢复时必须保留原有 lease 模式。
76
+
75
77
  <a id="c4"></a>
76
78
 
77
79
  **C4 — Checkpoint 回退。** 持久保存 model/optimizer/RNG checkpoint 及其匹配的 consumer 位置。该 checkpoint 之后已经处理的结果,需要在确认处理前有独立 replay owner。恢复 checkpoint 的位置,而非队列最新 cursor。回退 WAL 更新前先重新保留变为必需的 receipt/batch 根;保护缺失或数据损坏必须拒绝更新。替代版本持久化后才释放旧 checkpoint/replay owner。真实 checkpoint 提交和全部 rank 完成信号由应用提供。
@@ -172,12 +174,14 @@ unlink 本轮全部目标(包括 advisory stat 声称不存在的目标)
172
174
 
173
175
  Unlink `NotFound` 是幂等缺失,不是持久删除。已经缺失的 pending 文件仍需父目录屏障。其他错误上报并保留待办。回执停止重复删除/计数,但保留永久 tombstone;迟到 retain 不得复活路径。存活 extent 保护整个 pack。离线清理要求客户端停止、无共享 catalog/queue 标记、完整接受根及路径绑定追踪,以及新的未变化检查结果。
174
176
 
175
- 多个 collector 可以重叠执行:unlink 幂等,各自完成自己目标的父目录屏障。回执不能包含本轮快照之后加入的目标。失败后持久 pending 留给后续调用处理。底层 `LockedCatalog::collect` 兼容 API 仍持有外层锁;同一个 coordinator 自身的调用也仍然串行。
177
+ 多个 collector 可以重叠执行:unlink 幂等,各自完成自己目标的父目录屏障。回执不能包含本轮快照之后加入的目标。失败后持久 pending 留给后续调用处理。底层 `LockedCatalog::collect` 兼容 API 仍持有外层锁。Python coordinator 的 GC 调用彼此串行,但在根退休和 tombstone 准备完成后释放两层队列锁;物理删除期间其他队列调用可以继续。回执阶段重新获取新鲜 catalog,只使用已捕获的计划。
176
178
 
177
179
  <a id="g4"></a>
178
180
 
179
181
  **G4 — Writer 退休与进展。** 每次 writer 修改持有准确 incarnation 的 gate 并检查持久 fence。产生效果前先登记操作身份、准备 owner 和依赖。退休获取相同 gate,持久化 fence,排空旧 I/O,对账已登记操作,然后释放准备 owner、记录结算、seal 预留文件、持久化退休回执。Recover 在释放前认证完整结果并 staging;abandon 不声称结果可读。重试可能重复任意已生效步骤。PID、年龄和超时都不能证明退休安全。
180
182
 
183
+ 显式 `discard_staging=True` 在上述退休完成后,以独立 catalog 事务释放该 writer 已登记结果的准确 staging owner,不递归释放依赖 writer 的 staging。调用者必须先停止相关 reader,并以独立持久 owner 保留仍需使用的结果;此时返回引用不受 staging 保护。错误后的重试仍须完成退休后再释放,其他 owner 保持不变。`WriterRetirement` 模型覆盖到退休回执;后续显式放弃沿用 owner 释放规则,由实际回归检查接口组合。
184
+
181
185
  Owner 持续存在或错误反复发生时,安全性仍须成立。最终回收只针对 sealed 且**永久无主**的对象,还要求持续调用 GC、锁/I/O 返回、故障最终稳定及公平调度。这不提供延迟上限,也不意味着存在自动后台回收器。
182
186
 
183
187
  <details>
@@ -463,3 +463,22 @@ matching queue view, pending/buffered/prefetched dependencies and post-checkpoin
463
463
  replay owners. A latest marker or queue acknowledgement cannot replace it. See
464
464
  [restart APIs](#what-resumes-after-a-restart). Application tests cover real schemas,
465
465
  abort/resume, shared tensors, multiple-reader completion and whole-job recovery.
466
+
467
+ ### Externally supervised workers
468
+
469
+ `Coordinator(..., lease_seconds=None)` delegates liveness to an external supervisor;
470
+ attempt and epoch fences remain enforced. The supervisor must detect failures,
471
+ confirm the old process has exited, and call `retire_worker`. The default remains
472
+ 300 seconds, and recovery cannot change this queue identity setting.
473
+ Queue GC serializes root retirement and durable tombstones, then releases queue
474
+ locks before deleting files and syncing directories. Its result includes
475
+ `queue_lock_wait_seconds`, `queue_prepare_seconds`, and `detached_collection_seconds`.
476
+
477
+ After readers and writers have exited and accepted results have durable owners,
478
+ `retire_writer(writer_id, policy="abandon", discard_staging=True)` also relinquishes
479
+ that writer's registered transfer staging after retirement and sealing. This is
480
+ opt-in; other queue, checkpoint, reader, and writer owners are preserved. Returned
481
+ references have no staging protection in this mode and are not recovery handles.
482
+ `retention_summary()` reports owner categories and open packs with bounded
483
+ examples, without reading tensor payloads. Category pack counts can overlap and
484
+ must not be added together.
@@ -272,3 +272,19 @@ receipt = peer.publication_receipt(
272
272
  Rollout 的路由/分数采集必须完整、一致后才能发布 continuation,不能仅以 token 生成结束为准。所有必要消费者完成后才确认原始输出,全部训练 rank 读完后才确认 ready batch。应用监督进程应锁存并传播后台 GC 失败、停止接收新任务、保留不确定存储,并对远端在途工作实施 fencing。
273
273
 
274
274
  联合 checkpoint 的提交与回退遵循 [C4](DESIGN_zh.md#c4):包含模型/优化器/RNG 和匹配队列视图,保留 pending/buffered/prefetched 依赖及 post-checkpoint replay owner;不能只用 latest 标记或队列确认代替。重启 API 见[恢复说明](#重启后分别续上什么)。应用集成测试应覆盖真实 schema、中断续跑、共享张量、多 reader 完成信号与整任务恢复。
275
+
276
+ ### 由外部监督器管理的 worker
277
+
278
+ `Coordinator(..., lease_seconds=None)` 显式关闭墙钟租约过期,仍保留 attempt/epoch fencing。
279
+ 只有外部监督器负责故障探测、确认旧进程退出并调用 `retire_worker` 时才使用此模式;
280
+ 默认仍为 300 秒。已有队列恢复时不能改变该配置。
281
+ 队列 GC 的持久化根/墓碑准备仍串行,物理删除和目录同步在队列锁外执行。
282
+ `collect_garbage()` 返回 `queue_lock_wait_seconds`、`queue_prepare_seconds` 和 `detached_collection_seconds`。
283
+
284
+ 监督器确认 writer 的读写进程已退出、已接受结果由队列或其他 durable owner 接管后,
285
+ 可调用 `retire_writer(writer_id, policy="abandon", discard_staging=True)`:
286
+ 先完整退役、封口,再放弃这个 writer 已登记的 transfer staging;默认不放弃。
287
+ 这不会释放队列、checkpoint、reader 或其他 writer 的 staging。
288
+ 该模式返回的引用没有 staging 保护,不应当作待恢复的裸引用读取。
289
+ `retention_summary()` 提供 owner 类别和未封口 pack 的元数据摘要,限制示例数量且不读取 tensor payload。
290
+ 不同类别的 pack 计数可能重叠,不能直接相加。
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "straw-queue"
7
- version = "0.1.7"
7
+ version = "0.1.8"
8
8
  description = "Rust durable queues for AI tensors and large immutable records"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -78,7 +78,7 @@ pub struct Coordinator {
78
78
  owners: HashMap<String, String>,
79
79
  queue_id: String,
80
80
  storage_prefix: String,
81
- lease_seconds: f64,
81
+ lease_seconds: Option<f64>,
82
82
  sealed: bool,
83
83
  draining: bool,
84
84
  storage_failed: bool,
@@ -391,8 +391,18 @@ impl Coordinator {
391
391
  .as_str()
392
392
  .unwrap_or("rollout")
393
393
  .to_string();
394
- let lease_seconds = options["lease_seconds"].as_f64().unwrap_or(300.);
395
- if lease_seconds <= 0. || queue_id.is_empty() {
394
+ // Null explicitly delegates failure detection to a process supervisor.
395
+ // Attempt/epoch fencing and reader lifetime checks still apply.
396
+ let lease_seconds = match options.get("lease_seconds") {
397
+ None => Some(300.),
398
+ Some(Value::Null) => None,
399
+ Some(value) => Some(value.as_f64().ok_or_else(|| {
400
+ Error::new("ValueError", "Lease duration must be a number or null")
401
+ })?),
402
+ };
403
+ if lease_seconds.is_some_and(|seconds| !seconds.is_finite() || seconds <= 0.)
404
+ || queue_id.is_empty()
405
+ {
396
406
  return fail("ValueError", "Invalid lease duration/queue ID");
397
407
  }
398
408
  let defaults = json!({"pending_tasks":10000,"inflight_tasks":256,"control_tasks":1,"accepted_records":100000,"accepted_bytes":68719476736u64,"accepted_tokens":100000000,"ready_bytes":68719476736u64,"max_result_bytes":4294967296u64,"max_result_tokens":100000000,"metadata_bytes":262144});
@@ -686,7 +696,7 @@ impl Coordinator {
686
696
  self.count("stale_attempts");
687
697
  return fail("StaleAttempt", "Lease is not current authorization");
688
698
  }
689
- if self.deadlines.get(id).copied().unwrap_or(0.) <= now {
699
+ if self.lease_seconds.is_some() && self.deadlines.get(id).copied().unwrap_or(0.) <= now {
690
700
  return fail("LeaseExpired", "Lease expired");
691
701
  };
692
702
  Ok(task.clone())
@@ -1018,9 +1028,11 @@ impl Coordinator {
1018
1028
  fault,
1019
1029
  Some(graph),
1020
1030
  )?;
1021
- for lease in leases {
1022
- self.deadlines
1023
- .insert(field(&lease, "task_id")?.into(), now + self.lease_seconds);
1031
+ if let Some(seconds) = self.lease_seconds {
1032
+ for lease in leases {
1033
+ self.deadlines
1034
+ .insert(field(&lease, "task_id")?.into(), now + seconds);
1035
+ }
1024
1036
  }
1025
1037
  Ok(json!({"status":"acquired","assignments":assignments}))
1026
1038
  }
@@ -1126,8 +1138,10 @@ impl Coordinator {
1126
1138
  for lease in leases {
1127
1139
  match self.valid_lease(lease, now) {
1128
1140
  Ok(_) => {
1129
- self.deadlines
1130
- .insert(field(lease, "task_id")?.into(), now + self.lease_seconds);
1141
+ if let Some(seconds) = self.lease_seconds {
1142
+ self.deadlines
1143
+ .insert(field(lease, "task_id")?.into(), now + seconds);
1144
+ }
1131
1145
  results.push(json!("extended"));
1132
1146
  }
1133
1147
  Err(e) if ["StaleAttempt", "LeaseExpired"].contains(&e.kind.as_str()) => {
@@ -1587,6 +1601,26 @@ impl Coordinator {
1587
1601
  .map_err(root_error)
1588
1602
  }
1589
1603
  fn collect_garbage(&mut self, fault: &mut Fault<'_>) -> Result<Value> {
1604
+ let plan = self.prepare_garbage_collection(fault)?;
1605
+ plan.execute(&self.store.config, fault)
1606
+ }
1607
+ /// Only durable queue/root admission needs exclusive queue access. Once
1608
+ /// tombstones are committed, physical deletion can run without this owner.
1609
+ pub(crate) fn prepare_garbage_collection(
1610
+ &mut self,
1611
+ fault: &mut Fault<'_>,
1612
+ ) -> Result<crate::gc::CollectionPlan> {
1613
+ if !crate::queue_application::permitted(
1614
+ &self.journal,
1615
+ self.journal.closed(),
1616
+ "collect_garbage",
1617
+ ) {
1618
+ return fail(
1619
+ "UnsafeRecovery",
1620
+ "Coordinator unavailable; stop owner and recover",
1621
+ );
1622
+ }
1623
+ self.finish_consumption(fault)?;
1590
1624
  if !self.store.config.gc_enabled()? {
1591
1625
  return fail("ValueError", "Online GC is not enabled");
1592
1626
  }
@@ -1610,7 +1644,7 @@ impl Coordinator {
1610
1644
  .map_err(gc_prepare_error)?;
1611
1645
  self.store
1612
1646
  .config
1613
- .collect_queue(prepared, fault)?
1647
+ .plan_queue_collection(prepared, fault)?
1614
1648
  .ok_or_else(|| Error::new("ValueError", "Online GC is not enabled"))
1615
1649
  }
1616
1650
  pub fn close(&mut self) {
@@ -23,6 +23,7 @@ use std::path::{Path, PathBuf};
23
23
 
24
24
  #[path = "gc_plan.rs"]
25
25
  mod collection;
26
+ pub(crate) use collection::Plan as CollectionPlan;
26
27
 
27
28
  fn catalog_append_error(error: crate::catalog_append::Error<Error, Error>) -> Error {
28
29
  match error {
@@ -649,6 +650,10 @@ impl LockedCatalog<'_> {
649
650
  pub fn collect(&mut self, config: &Config, fault: &mut Fault<'_>) -> Result<Value> {
650
651
  self.catalog.collect(config, fault)
651
652
  }
653
+ #[cfg(feature = "python")]
654
+ pub(crate) fn retention_summary(&self) -> Value {
655
+ self.catalog.retention_summary()
656
+ }
652
657
  #[cfg(test)]
653
658
  pub(crate) fn collect_queue(
654
659
  &mut self,
@@ -1482,6 +1487,46 @@ impl Catalog {
1482
1487
  "retained_packs":result.retained,"tombstones":self.log.indexes().deleted.len()})
1483
1488
  }
1484
1489
 
1490
+ /// Metadata diagnostics with bounded examples; never reads payload files.
1491
+ #[cfg(feature = "python")]
1492
+ pub(crate) fn retention_summary(&self) -> Value {
1493
+ let indexes = self.log.indexes();
1494
+ let live = indexes.roots();
1495
+ let mut categories =
1496
+ std::collections::BTreeMap::<String, (usize, HashSet<String>, Vec<Value>)>::new();
1497
+ for (owner, paths) in &indexes.owners {
1498
+ let kind = owner.split(':').next().unwrap_or(owner).to_owned();
1499
+ let entry = categories.entry(kind).or_default();
1500
+ entry.0 += 1;
1501
+ entry.1.extend(paths.iter().cloned());
1502
+ if entry.2.len() < 4 {
1503
+ entry.2.push(json!({"owner":owner,"packs":paths.len(),"pack_examples":paths.iter().take(3).collect::<Vec<_>>()}));
1504
+ }
1505
+ }
1506
+ let categories = categories
1507
+ .into_iter()
1508
+ .map(|(kind, (owners, packs, examples))| {
1509
+ (
1510
+ kind,
1511
+ json!({"owners":owners,"retained_packs":packs.len(),"examples":examples}),
1512
+ )
1513
+ })
1514
+ .collect::<serde_json::Map<_, _>>();
1515
+ let mut open = std::collections::BTreeMap::<String, usize>::new();
1516
+ for (path, sealed) in &indexes.packs {
1517
+ if !sealed && !indexes.deleted.contains(path) {
1518
+ let writer = path.split('/').nth(2).unwrap_or("unknown").to_owned();
1519
+ *open.entry(writer).or_default() += 1;
1520
+ }
1521
+ }
1522
+ json!({"owners":indexes.owners.len(),"retained_packs":live.len(),
1523
+ "unowned_sealed_packs":indexes.candidates(&live).len(),
1524
+ "pending_deletions":indexes.pending_deletions.len(),"tombstones":indexes.deleted.len(),
1525
+ "open_packs":open.values().sum::<usize>(),"open_writers":open.len(),
1526
+ "open_writer_examples":open.into_iter().take(64).collect::<std::collections::BTreeMap<_,_>>(),
1527
+ "categories":categories})
1528
+ }
1529
+
1485
1530
  #[cfg(test)]
1486
1531
  fn collect_queue(
1487
1532
  &mut self,
@@ -390,6 +390,18 @@ impl Config {
390
390
  writer: &str,
391
391
  policy: RetirementPolicy,
392
392
  fault: &mut Fault<'_>,
393
+ ) -> Result<Vec<Value>> {
394
+ self.retire_writer_with_staging(writer, policy, false, fault)
395
+ }
396
+
397
+ /// Explicitly relinquish this writer's transfer staging only after its
398
+ /// retirement has committed. Queue/checkpoint/reader owners are untouched.
399
+ pub(crate) fn retire_writer_with_staging(
400
+ &self,
401
+ writer: &str,
402
+ policy: RetirementPolicy,
403
+ discard_staging: bool,
404
+ fault: &mut Fault<'_>,
393
405
  ) -> Result<Vec<Value>> {
394
406
  let mut permit = Permit::retire(&self.root, &self.run_id, writer)?;
395
407
  let registry = permit.registry(None, fault)?;
@@ -406,6 +418,22 @@ impl Config {
406
418
  pending,
407
419
  };
408
420
  crate::writer_retirement::execute(&mut retirement)?;
421
+ if discard_staging {
422
+ // Use exact durable result descriptors, not file age or path
423
+ // overlap. Do not recursively consume another writer's staging.
424
+ let owners = retirement
425
+ .registry
426
+ .described_publications()
427
+ .map(Catalog::staged)
428
+ .collect::<Result<BTreeSet<_>>>()?;
429
+ retirement.config.catalog(|c| {
430
+ let held = owners
431
+ .into_iter()
432
+ .filter(|owner| c.catalog.log.indexes().owners.contains_key(owner))
433
+ .collect::<Vec<_>>();
434
+ c.release(retirement.config, &held, retirement.fault)
435
+ })?;
436
+ }
409
437
  Ok(retirement.recovered)
410
438
  }
411
439
  }
@@ -675,6 +675,7 @@ impl NativeStore {
675
675
  return Ok(serde_json::to_string(&self.config.collect_garbage(fault)?)?);
676
676
  }
677
677
  let result = self.config.catalog(|c| match method {
678
+ "retention_summary" => Ok(c.retention_summary()),
678
679
  "retain" => {
679
680
  c.retain(
680
681
  &self.config,
@@ -706,12 +707,14 @@ impl NativeStore {
706
707
  py.allow_threads(|| Ok(self.writer_access()?.writer_id().to_owned()))
707
708
  .map_err(|e| exception(py, e))
708
709
  }
710
+ #[pyo3(signature = (writer_id, policy, fault, discard_staging=false))]
709
711
  fn retire_writer(
710
712
  &self,
711
713
  py: Python<'_>,
712
714
  writer_id: &str,
713
715
  policy: &str,
714
716
  fault: Py<PyAny>,
717
+ discard_staging: bool,
715
718
  ) -> PyResult<String> {
716
719
  self.check_writer_process()?;
717
720
  let policy = match policy {
@@ -729,7 +732,12 @@ impl NativeStore {
729
732
  };
730
733
  run(py, &fault, |fault| {
731
734
  Ok(serde_json::to_string(
732
- &self.config.retire_writer(writer_id, policy, fault)?,
735
+ &self.config.retire_writer_with_staging(
736
+ writer_id,
737
+ policy,
738
+ discard_staging,
739
+ fault,
740
+ )?,
733
741
  )?)
734
742
  })
735
743
  }
@@ -758,6 +766,23 @@ impl NativeStore {
758
766
  }
759
767
  }
760
768
  #[pyclass]
769
+ struct NativeQueueCollection {
770
+ config: Config,
771
+ plan: Mutex<Option<crate::gc::CollectionPlan>>,
772
+ }
773
+ #[pymethods]
774
+ impl NativeQueueCollection {
775
+ fn execute(&self, py: Python<'_>, fault: Py<PyAny>) -> PyResult<String> {
776
+ run(py, &fault, |f| {
777
+ let plan =
778
+ self.plan.lock().unwrap().take().ok_or_else(|| {
779
+ Error::new("ValueError", "Collection plan was already executed")
780
+ })?;
781
+ Ok(serde_json::to_string(&plan.execute(&self.config, f)?)?)
782
+ })
783
+ }
784
+ }
785
+ #[pyclass]
761
786
  struct NativeCoordinator {
762
787
  owner: Mutex<Coordinator>,
763
788
  }
@@ -809,6 +834,20 @@ impl NativeCoordinator {
809
834
  fn diagnostics(&self, py: Python<'_>) -> String {
810
835
  py.allow_threads(||{let q=self.owner.lock().unwrap();json!({"path":q.journal.path,"sequence":q.journal.sequence,"poisoned":q.journal.poisoned,"closed":q.journal.closed(),"recovery_seconds":q.recovery_seconds}).to_string()})
811
836
  }
837
+ fn prepare_garbage_collection(
838
+ &self,
839
+ py: Python<'_>,
840
+ fault: Py<PyAny>,
841
+ ) -> PyResult<NativeQueueCollection> {
842
+ run(py, &fault, |f| {
843
+ let mut owner = self.owner.lock().unwrap();
844
+ let plan = owner.prepare_garbage_collection(f)?;
845
+ Ok(NativeQueueCollection {
846
+ config: owner.store.config.clone(),
847
+ plan: Mutex::new(Some(plan)),
848
+ })
849
+ })
850
+ }
812
851
  fn close(&self, py: Python<'_>) {
813
852
  py.allow_threads(|| self.owner.lock().unwrap().close());
814
853
  }
@@ -559,14 +559,12 @@ impl Config {
559
559
  ) -> Result<Option<T>> {
560
560
  self.with_catalog(|catalog| catalog.transaction(self, call))
561
561
  }
562
- pub(crate) fn collect_queue(
562
+ pub(crate) fn plan_queue_collection(
563
563
  &self,
564
564
  prepared: crate::queue_gc::Prepared<'_>,
565
565
  fault: &mut Fault<'_>,
566
- ) -> Result<Option<Value>> {
567
- self.catalog(|catalog| catalog.plan_collection(self, Some(prepared), fault))?
568
- .map(|plan| plan.execute(self, fault))
569
- .transpose()
566
+ ) -> Result<Option<crate::gc::CollectionPlan>> {
567
+ self.catalog(|catalog| catalog.plan_collection(self, Some(prepared), fault))
570
568
  }
571
569
  /// Collect with the physical deletion and directory barriers outside the
572
570
  /// pool-wide catalog lock. Durable tombstones fence every selected name.
@@ -536,6 +536,13 @@ impl Registry {
536
536
  .cloned()
537
537
  .collect()
538
538
  }
539
+
540
+ pub(crate) fn described_publications(&self) -> impl Iterator<Item = &Value> {
541
+ self.operations
542
+ .iter()
543
+ .filter_map(|op| op.results.as_ref())
544
+ .flat_map(|results| results.roots.iter().chain(results.intermediates.iter()))
545
+ }
539
546
  }
540
547
 
541
548
  #[cfg(test)]
@@ -30,6 +30,8 @@ class _JournalView:
30
30
 
31
31
 
32
32
  class Coordinator:
33
+ supports_supervised_leases = True
34
+
33
35
  def __init__(
34
36
  self,
35
37
  store,
@@ -46,6 +48,7 @@ class Coordinator:
46
48
  self.limits = limits or Limits()
47
49
  self.lease_seconds, self.clock = lease_seconds, clock
48
50
  self._call_lock = threading.RLock()
51
+ self._gc_lock = threading.Lock()
49
52
  self._validation_context = threading.local()
50
53
  self._cpu_started = time.process_time()
51
54
  options = dict(
@@ -272,7 +275,27 @@ class Coordinator:
272
275
  raw results. Its finished_batches acknowledge training reads on all ranks.
273
276
  Other queues and explicit storage owners independently retain their roots.
274
277
  """
275
- return self._call("collect_garbage", {})
278
+ # Only GC passes serialize with each other. The queue lock protects
279
+ # root retirement and durable tombstones, never physical filesystem I/O.
280
+ with self._gc_lock:
281
+ started = time.monotonic()
282
+ try:
283
+ with self._call_lock:
284
+ acquired = time.monotonic()
285
+ plan = self._native.prepare_garbage_collection(self.store._fault)
286
+ prepared = time.monotonic()
287
+ result = json.loads(plan.execute(self.store._fault))
288
+ except BaseException:
289
+ scope = getattr(self._validation_context, "scope", None)
290
+ if scope is not None:
291
+ scope.close()
292
+ raise
293
+ result.update(
294
+ queue_lock_wait_seconds=acquired - started,
295
+ queue_prepare_seconds=prepared - acquired,
296
+ detached_collection_seconds=time.monotonic() - prepared,
297
+ )
298
+ return result
276
299
 
277
300
  def snapshot(self):
278
301
  result = self._call("snapshot", locals())
@@ -114,6 +114,8 @@ class SharedFilesystemStore(_RecordReader):
114
114
  max_buffer_bytes controls temporary copy memory, never logical record size.
115
115
  """
116
116
 
117
+ supports_writer_staging_discard = True
118
+
117
119
  def __init__(
118
120
  self,
119
121
  root,
@@ -215,14 +217,18 @@ class SharedFilesystemStore(_RecordReader):
215
217
  """Persistent incarnation used to explicitly retire this writer."""
216
218
  return self._native.writer_id()
217
219
 
218
- def retire_writer(self, writer_id, *, policy="recover"):
220
+ def retire_writer(self, writer_id, *, policy="recover", discard_staging=False):
219
221
  """Fence an exact incarnation and reconcile its unfinished publications.
220
222
 
221
223
  A busy writer raises WriterBusy. Recovery returns staged results;
222
224
  explicit abandonment preserves existing queue, reader and staging roots.
223
225
  The same writer cannot resume after retirement. Create a fresh writer.
226
+ discard_staging=True explicitly relinquishes that writer's registered
227
+ transfer publications after retirement. The supervisor must first stop
228
+ its readers and adopt accepted results. Other durable owners survive;
229
+ returned references are not protected by staging in this mode.
224
230
  """
225
- values = json.loads(self._native.retire_writer(writer_id, policy, self._fault))
231
+ values = json.loads(self._native.retire_writer(writer_id, policy, self._fault, discard_staging))
226
232
  return [
227
233
  RecordSetRef.from_dict(value) if "manifest" in value else RecordRef.from_dict(value) for value in values
228
234
  ]
@@ -277,6 +283,15 @@ class SharedFilesystemStore(_RecordReader):
277
283
  """Reclaim unowned sealed packs; retry deletions without a durable receipt."""
278
284
  return self._storage("collect")
279
285
 
286
+ def retention_summary(self):
287
+ """Report owner/pack metadata with bounded examples and no payload reads.
288
+
289
+ Category pack counts can overlap and must not be added together.
290
+ Open writers and retained ownership require lifecycle confirmation;
291
+ elapsed file age alone does not authorize reclamation.
292
+ """
293
+ return self._storage("retention_summary")
294
+
280
295
  def _inputs(self, records):
281
296
  metadata, payloads = [], []
282
297
  for record in records: