cc-transcript 14.37.1__tar.gz → 14.38.0__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 (197) hide show
  1. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/PKG-INFO +1 -1
  2. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/snapshot_schema/request.schema.json +28 -4
  3. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/snapshot_schema/response.schema.json +14 -0
  4. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/pyproject.toml +1 -1
  5. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/snapshot.rs +156 -9
  6. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/snapshot_prepared_service.rs +53 -18
  7. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/snapshot_root_warm.rs +36 -1
  8. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/Cargo.lock +0 -0
  9. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/Cargo.toml +0 -0
  10. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/LICENSE +0 -0
  11. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/README.md +0 -0
  12. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/__init__.py +0 -0
  13. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/__init__.pyi +0 -0
  14. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/_native.pyi +0 -0
  15. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/activity.py +0 -0
  16. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/activity_probe.py +0 -0
  17. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/builders.py +0 -0
  18. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/codex.py +0 -0
  19. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/command.py +0 -0
  20. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/context.py +0 -0
  21. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/corrections.py +0 -0
  22. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/cost.py +0 -0
  23. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/decision_store.py +0 -0
  24. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/decisions.py +0 -0
  25. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/discovery.py +0 -0
  26. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/disktruth.py +0 -0
  27. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/evidence.py +0 -0
  28. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/extract/__init__.py +0 -0
  29. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/extract/correct.py +0 -0
  30. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/facts.py +0 -0
  31. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/filterspec.py +0 -0
  32. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/heartbeats.py +0 -0
  33. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/ids.py +0 -0
  34. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/judge/__init__.py +0 -0
  35. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/judge/llm.py +0 -0
  36. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/judge/similar.py +0 -0
  37. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/judge/verdicts.py +0 -0
  38. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/ledger.py +0 -0
  39. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/literals.py +0 -0
  40. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/mining/__init__.py +0 -0
  41. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/mining/candidates.py +0 -0
  42. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/mining/confidence.py +0 -0
  43. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/mining/engine.py +0 -0
  44. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/mining/formats.py +0 -0
  45. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/mining/sampling.py +0 -0
  46. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/mining/signals.py +0 -0
  47. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/mining/sourcekind.py +0 -0
  48. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/mining/spec.py +0 -0
  49. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/mining/store.py +0 -0
  50. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/models.py +0 -0
  51. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/nlp.py +0 -0
  52. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/notifications.py +0 -0
  53. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/parser.py +0 -0
  54. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/py.typed +0 -0
  55. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/query.py +0 -0
  56. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/render.py +0 -0
  57. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/sentiment/__init__.py +0 -0
  58. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/sentiment/buckets.py +0 -0
  59. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/sentiment/data/afinn-en-165.tsv +0 -0
  60. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/sentiment/data/domain_overrides.tsv +0 -0
  61. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/sentiment/data/en-ewt.udpipe +0 -0
  62. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/sentiment/engine.py +0 -0
  63. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/sentiment/lexicon.py +0 -0
  64. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/sentiment/scorespec.py +0 -0
  65. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/snapshot_schema/config.schema.json +0 -0
  66. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/snapshot_schema/context.schema.json +0 -0
  67. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/snapshot_schema/tool_registry.schema.json +0 -0
  68. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/snapshots.py +0 -0
  69. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/synthetic.py +0 -0
  70. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/tools.py +0 -0
  71. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/cc_transcript/watch.py +0 -0
  72. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/cli/Cargo.toml +0 -0
  73. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/cli/src/commands/blame.rs +0 -0
  74. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/cli/src/commands/corpus.rs +0 -0
  75. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/cli/src/commands/corrections.rs +0 -0
  76. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/cli/src/commands/digest.rs +0 -0
  77. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/cli/src/commands/facts.rs +0 -0
  78. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/cli/src/commands/grep.rs +0 -0
  79. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/cli/src/commands/list.rs +0 -0
  80. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/cli/src/commands/mod.rs +0 -0
  81. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/cli/src/commands/scratchpad.rs +0 -0
  82. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/cli/src/commands/show.rs +0 -0
  83. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/cli/src/commands/slice.rs +0 -0
  84. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/cli/src/commands/stats.rs +0 -0
  85. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/cli/src/commands/watch.rs +0 -0
  86. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/cli/src/lib.rs +0 -0
  87. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/cli/src/output.rs +0 -0
  88. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/cli/src/target.rs +0 -0
  89. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/cli/src/timearg.rs +0 -0
  90. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/Cargo.toml +0 -0
  91. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/benches/parse.rs +0 -0
  92. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/benches/render.rs +0 -0
  93. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/benches/toolcall.rs +0 -0
  94. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/activity.rs +0 -0
  95. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/actor.rs +0 -0
  96. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/blame.rs +0 -0
  97. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/buckets.rs +0 -0
  98. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/codex/discovery.rs +0 -0
  99. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/codex/lower.rs +0 -0
  100. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/codex/mod.rs +0 -0
  101. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/codex/parse.rs +0 -0
  102. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/codex/probe.rs +0 -0
  103. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/codex/protocol.rs +0 -0
  104. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/codex/types.rs +0 -0
  105. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/command.rs +0 -0
  106. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/context.rs +0 -0
  107. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/corrections.rs +0 -0
  108. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/cost.rs +0 -0
  109. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/discovery.rs +0 -0
  110. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/facts.rs +0 -0
  111. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/feedback.rs +0 -0
  112. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/filter.rs +0 -0
  113. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/gateway.rs +0 -0
  114. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/ids.rs +0 -0
  115. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/judge.rs +0 -0
  116. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/lib.rs +0 -0
  117. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/literals/command.rs +0 -0
  118. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/literals/corrections.rs +0 -0
  119. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/literals/feedback.rs +0 -0
  120. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/literals/mining.rs +0 -0
  121. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/literals/mod.rs +0 -0
  122. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/literals/protocol.rs +0 -0
  123. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/notifications.rs +0 -0
  124. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/parse.rs +0 -0
  125. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/protocol.rs +0 -0
  126. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/pystr.rs +0 -0
  127. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/query.rs +0 -0
  128. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/render.rs +0 -0
  129. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/rng.rs +0 -0
  130. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/scan.rs +0 -0
  131. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/scan_checkpoint.rs +0 -0
  132. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/scan_grep.rs +0 -0
  133. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/scan_grep_stream.rs +0 -0
  134. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/scan_grep_stream_tests.rs +0 -0
  135. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/scan_index.rs +0 -0
  136. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/scan_projection.rs +0 -0
  137. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/scan_regressions.rs +0 -0
  138. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/scan_stream.rs +0 -0
  139. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/schema.rs +0 -0
  140. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/snapshot_activity.rs +0 -0
  141. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/snapshot_codec.rs +0 -0
  142. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/snapshot_codex_append.rs +0 -0
  143. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/snapshot_labels.rs +0 -0
  144. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/snapshot_ledger.rs +0 -0
  145. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/snapshot_ledger_tests.rs +0 -0
  146. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/snapshot_memory.rs +0 -0
  147. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/snapshot_owned.rs +0 -0
  148. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/snapshot_prepared.rs +0 -0
  149. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/snapshot_prepared_disk.rs +0 -0
  150. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/snapshot_projection.rs +0 -0
  151. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/snapshot_regressions.rs +0 -0
  152. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/snapshot_text.rs +0 -0
  153. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/sqlite.rs +0 -0
  154. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/toolcall.rs +0 -0
  155. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/types.rs +0 -0
  156. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/value.rs +0 -0
  157. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/src/watch.rs +0 -0
  158. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/tests/sonic_arena_model.rs +0 -0
  159. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/core/tests/std_allocation_model.rs +0 -0
  160. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/Cargo.toml +0 -0
  161. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/build.rs +0 -0
  162. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/data/command_prefix_pins.tsv +0 -0
  163. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/actor_bridge.rs +0 -0
  164. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/bin/stub_gen.rs +0 -0
  165. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/codex.rs +0 -0
  166. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/command.rs +0 -0
  167. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/context.rs +0 -0
  168. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/corrections.rs +0 -0
  169. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/discovery.rs +0 -0
  170. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/feedback.rs +0 -0
  171. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/judge.rs +0 -0
  172. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/lexicon.rs +0 -0
  173. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/lib.rs +0 -0
  174. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/mining.rs +0 -0
  175. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/nlp.rs +0 -0
  176. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/python.rs +0 -0
  177. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/render.rs +0 -0
  178. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/score.rs +0 -0
  179. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/snapshot_decode.rs +0 -0
  180. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/snapshots.rs +0 -0
  181. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/sqlite.rs +0 -0
  182. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/toolcall.rs +0 -0
  183. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/views/attachment.rs +0 -0
  184. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/views/blocks.rs +0 -0
  185. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/views/command.rs +0 -0
  186. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/views/convert.rs +0 -0
  187. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/views/dunder.rs +0 -0
  188. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/views/events.rs +0 -0
  189. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/views/meta.rs +0 -0
  190. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/views/mod.rs +0 -0
  191. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/views/print.rs +0 -0
  192. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/views/store.rs +0 -0
  193. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/views/system.rs +0 -0
  194. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/views/toolcall.rs +0 -0
  195. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/views/toolresult.rs +0 -0
  196. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/views/transcript.rs +0 -0
  197. {cc_transcript-14.37.1 → cc_transcript-14.38.0}/rust/crates/py/src/watch.rs +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cc-transcript
3
- Version: 14.37.1
3
+ Version: 14.38.0
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -1032,6 +1032,20 @@
1032
1032
  "PrepareGraph": {
1033
1033
  "additionalProperties": false,
1034
1034
  "properties": {
1035
+ "active_since_unix_ms": {
1036
+ "anyOf": [
1037
+ {
1038
+ "exclusiveMinimum": 0,
1039
+ "maximum": 9007199254740991,
1040
+ "type": "integer"
1041
+ },
1042
+ {
1043
+ "type": "null"
1044
+ }
1045
+ ],
1046
+ "default": null,
1047
+ "title": "Active Since Unix Ms"
1048
+ },
1035
1049
  "deadline_unix_ms": {
1036
1050
  "exclusiveMinimum": 0,
1037
1051
  "maximum": 9007199254740991,
@@ -1045,7 +1059,6 @@
1045
1059
  "pattern": "^/",
1046
1060
  "type": "string"
1047
1061
  },
1048
- "maxItems": 1024,
1049
1062
  "title": "Direct Paths",
1050
1063
  "type": "array"
1051
1064
  },
@@ -1085,7 +1098,6 @@
1085
1098
  "minLength": 1,
1086
1099
  "type": "string"
1087
1100
  },
1088
- "maxItems": 1024,
1089
1101
  "title": "Thread Ids",
1090
1102
  "type": "array"
1091
1103
  },
@@ -2341,6 +2353,20 @@
2341
2353
  "WarmRegistered": {
2342
2354
  "additionalProperties": false,
2343
2355
  "properties": {
2356
+ "active_since_unix_ms": {
2357
+ "anyOf": [
2358
+ {
2359
+ "exclusiveMinimum": 0,
2360
+ "maximum": 9007199254740991,
2361
+ "type": "integer"
2362
+ },
2363
+ {
2364
+ "type": "null"
2365
+ }
2366
+ ],
2367
+ "default": null,
2368
+ "title": "Active Since Unix Ms"
2369
+ },
2344
2370
  "classifier": {
2345
2371
  "$ref": "#/$defs/Classifier"
2346
2372
  },
@@ -2357,7 +2383,6 @@
2357
2383
  "pattern": "^/",
2358
2384
  "type": "string"
2359
2385
  },
2360
- "maxItems": 1024,
2361
2386
  "title": "Direct Paths",
2362
2387
  "type": "array"
2363
2388
  },
@@ -2416,7 +2441,6 @@
2416
2441
  "minLength": 1,
2417
2442
  "type": "string"
2418
2443
  },
2419
- "maxItems": 1024,
2420
2444
  "title": "Thread Ids",
2421
2445
  "type": "array"
2422
2446
  }
@@ -293,6 +293,19 @@
293
293
  "minimum": 0,
294
294
  "title": "Window Start",
295
295
  "type": "integer"
296
+ },
297
+ "window_started_unix_ms": {
298
+ "anyOf": [
299
+ {
300
+ "exclusiveMinimum": 0,
301
+ "maximum": 9007199254740991,
302
+ "type": "integer"
303
+ },
304
+ {
305
+ "type": "null"
306
+ }
307
+ ],
308
+ "title": "Window Started Unix Ms"
296
309
  }
297
310
  },
298
311
  "required": [
@@ -308,6 +321,7 @@
308
321
  "parser_version",
309
322
  "source_bytes",
310
323
  "window_start",
324
+ "window_started_unix_ms",
311
325
  "committed_bytes",
312
326
  "event_count",
313
327
  "turn_count",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cc-transcript"
3
- version = "14.37.1" # informational — releases stamp the version from the git tag
3
+ version = "14.38.0" # informational — releases stamp the version from the git tag
4
4
  description = "Grep every Claude Code session you've ever run."
5
5
  readme = "README.md"
6
6
  license = "PolyForm-Noncommercial-1.0.0"
@@ -204,6 +204,10 @@ impl SourceStamp {
204
204
  self
205
205
  }
206
206
 
207
+ pub fn window_bytes(&self) -> u64 {
208
+ self.size - self.identity.window_base
209
+ }
210
+
207
211
  pub fn viewed_as(mut self, pinned: SourceStamp) -> Self {
208
212
  self.identity.window_base = pinned.identity.window_base;
209
213
  self
@@ -365,6 +369,17 @@ impl TranscriptSnapshot {
365
369
  .collect()
366
370
  }
367
371
 
372
+ pub fn window_started_unix_ms(&self) -> Option<i64> {
373
+ if self.window_start == 0 {
374
+ return None;
375
+ }
376
+ self.chunks
377
+ .iter()
378
+ .flat_map(|chunk| chunk.entries.iter())
379
+ .find_map(Entry::meta)
380
+ .map(|meta| meta.timestamp.timestamp_millis())
381
+ }
382
+
368
383
  pub fn entry(&self, position: usize) -> &Entry {
369
384
  let at = self.chunks.partition_point(|chunk| chunk.start <= position) - 1;
370
385
  &self.chunks[at].entries[position - self.chunks[at].start]
@@ -2919,6 +2934,24 @@ fn tail_bytes(request: &Value) -> Result<Option<u64>, SnapshotError> {
2919
2934
  .transpose()
2920
2935
  }
2921
2936
 
2937
+ fn active_since_ns(request: &Value) -> Result<Option<i128>, SnapshotError> {
2938
+ request
2939
+ .get("active_since_unix_ms")
2940
+ .filter(|value| !value.is_null())
2941
+ .map(|value| {
2942
+ value
2943
+ .as_u64()
2944
+ .filter(|since| *since > 0)
2945
+ .map(|since| i128::from(since) * 1_000_000)
2946
+ .ok_or_else(|| invalid("active_since_unix_ms must be a positive integer"))
2947
+ })
2948
+ .transpose()
2949
+ }
2950
+
2951
+ fn active(source: &PreparedSourceRef, since: Option<i128>) -> bool {
2952
+ since.is_none_or(|since| source.stamp.mtime_ns >= since)
2953
+ }
2954
+
2922
2955
  fn io_error(error: std::io::Error) -> SnapshotError {
2923
2956
  let status = match error.kind() {
2924
2957
  std::io::ErrorKind::NotFound => Status::Missing,
@@ -5535,7 +5568,7 @@ impl NativeStore {
5535
5568
  "device": snapshot.stamp.identity.device.to_string(), "inode": snapshot.stamp.identity.inode.to_string(),
5536
5569
  "mtime_ns": snapshot.stamp.mtime_ns.to_string(), "ctime_ns": snapshot.stamp.ctime_ns.to_string(),
5537
5570
  "provider": snapshot.provider.as_str(), "parser_version": PARSER_VERSION,
5538
- "source_bytes": snapshot.stamp.size, "window_start": snapshot.window_start, "committed_bytes": snapshot.committed_bytes,
5571
+ "source_bytes": snapshot.stamp.size, "window_start": snapshot.window_start, "window_started_unix_ms": snapshot.window_started_unix_ms(), "committed_bytes": snapshot.committed_bytes,
5539
5572
  "event_count": snapshot.event_count, "turn_count": snapshot.activity.turn_count(),
5540
5573
  "classifier": classifier, "provisional_tail": snapshot.provisional_tail,"lease_expires_unix_ms":expires})
5541
5574
  }
@@ -6765,13 +6798,10 @@ impl NativeStore {
6765
6798
  return Err(invalid("source must be a regular file"));
6766
6799
  }
6767
6800
  let stamp = SourceStamp::of(&metadata);
6768
- if stamp.size > self.config.source as u64 {
6769
- return Err(SnapshotError::new(
6770
- Status::SourceLimit,
6771
- "source exceeds owner bound",
6772
- ));
6773
- }
6774
- let budget = limits.max_source_read_bytes.min(stamp.size as usize);
6801
+ let budget = limits
6802
+ .max_source_read_bytes
6803
+ .min(self.config.source)
6804
+ .min(stamp.size as usize);
6775
6805
  let mut reservation = self.reserve_projection(context, 0)?;
6776
6806
  let mut pending: Vec<u8> = Vec::new();
6777
6807
  let mut pending_start = stamp.size;
@@ -6962,7 +6992,7 @@ impl NativeStore {
6962
6992
  }
6963
6993
  let window = tail_bytes(request)?;
6964
6994
  let stamp = SourceStamp::of(&metadata).windowed(window);
6965
- if stamp.size > self.config.source as u64 {
6995
+ if stamp.window_bytes() > self.config.source as u64 {
6966
6996
  return Err(SnapshotError::new(
6967
6997
  Status::SourceLimit,
6968
6998
  "source exceeds owner bound",
@@ -11875,6 +11905,22 @@ mod tests {
11875
11905
  );
11876
11906
  }
11877
11907
 
11908
+ #[test]
11909
+ fn tail_reads_the_newest_events_of_a_source_past_the_owner_bound() {
11910
+ let source = Source::new(&users(0..50));
11911
+ let store = NativeStore::new(&json!({"max_read_bytes_per_step":64,"max_source_bytes":users(40..50).len(),"max_retained_bytes":32*1024*1024,"reserved_hook_accounted_bytes":4096})).unwrap();
11912
+ let owner = context("a");
11913
+ let response = store.request(
11914
+ &tail_request(&source.path, 5, 1024 * 1024),
11915
+ &owner,
11916
+ &Cancellation::default(),
11917
+ );
11918
+ assert_eq!(response["status"].as_str(), Some("ok"), "{response:?}");
11919
+ assert_eq!(tail_uuids(&response), ["45", "46", "47", "48", "49"]);
11920
+ let read = response["usage"]["source_bytes_read"].as_u64().unwrap() as usize;
11921
+ assert!(read <= users(40..50).len(), "{read}");
11922
+ }
11923
+
11878
11924
  #[test]
11879
11925
  fn tail_refuses_codex_sources_and_counts_outside_the_budgets() {
11880
11926
  let codex = Source::new(
@@ -11971,6 +12017,10 @@ mod tests {
11971
12017
  let response = windowed(&store, &source.path, 4096, &owner);
11972
12018
  let description = &response["data"]["description"];
11973
12019
  assert_eq!(description["window_start"].as_u64(), Some(start));
12020
+ assert_eq!(
12021
+ description["window_started_unix_ms"].as_u64(),
12022
+ Some(1_767_323_045_000)
12023
+ );
11974
12024
  assert_eq!(description["source_bytes"].as_u64(), Some(size));
11975
12025
  assert_eq!(description["committed_bytes"].as_u64(), Some(size));
11976
12026
  assert_eq!(
@@ -12282,6 +12332,7 @@ mod tests {
12282
12332
  unwindowed["data"]["description"]["window_start"].as_u64(),
12283
12333
  Some(0)
12284
12334
  );
12335
+ assert!(unwindowed["data"]["description"]["window_started_unix_ms"].is_null());
12285
12336
  }
12286
12337
 
12287
12338
  #[test]
@@ -14012,6 +14063,102 @@ mod tests {
14012
14063
  assert_eq!(changed["status"].as_str(), Some("changed"), "{changed:?}");
14013
14064
  }
14014
14065
 
14066
+ #[test]
14067
+ fn registered_graph_admits_only_members_active_since_the_cutoff() {
14068
+ let tool = |name: &str| {
14069
+ format!(
14070
+ r#"{{"type":"assistant","uuid":"{name}","sessionId":"s","timestamp":"2026-01-02T03:04:06Z","message":{{"model":"test","content":[{{"type":"tool_use","id":"{name}","name":"{name}","input":{{}}}}]}}}}"#
14071
+ )
14072
+ };
14073
+ let source = Source::new(&format!("{}\n", user("root")));
14074
+ let stale = std::time::SystemTime::now() - std::time::Duration::from_secs(3600);
14075
+ let ids: Vec<String> = (0..1030).map(|index| format!("thread-{index}")).collect();
14076
+ for id in &ids {
14077
+ let path = source.directory.join(format!("{id}.jsonl"));
14078
+ let body = match id.as_str() {
14079
+ "thread-3" => format!("{}\n{}\n", user(id), tool("Grep")),
14080
+ "thread-7" => format!("{}\n{}\n", user(id), tool("Read")),
14081
+ _ => format!("{}\n", user(id)),
14082
+ };
14083
+ std::fs::write(&path, body).unwrap();
14084
+ if id != "thread-7" {
14085
+ File::options()
14086
+ .write(true)
14087
+ .open(&path)
14088
+ .unwrap()
14089
+ .set_modified(stale)
14090
+ .unwrap();
14091
+ }
14092
+ }
14093
+ let store = store();
14094
+ let owner = context("a");
14095
+ let mut background = owner.clone();
14096
+ background.insert("work_class", json!("background"));
14097
+ let root = finish(
14098
+ &store,
14099
+ store.request(&acquire(&source.path), &owner, &Cancellation::default()),
14100
+ &owner,
14101
+ );
14102
+ let mut template = acquire(&source.path);
14103
+ template["limits"].insert("max_discovery_entries", json!(20_000));
14104
+ let since = now_ms() - 60_000;
14105
+ let warm = || {
14106
+ let mut request = json!({"schema":SCHEMA,"id":"warm","operation":"warm_registered","classifier":{"id":"native","version":"1"},"thread_ids":ids,"roots":[source.directory.to_string_lossy().as_ref()],"direct_paths":[],"start_index":0,"membership_revision":null,"active_since_unix_ms":since,"deadline_unix_ms":template["deadline_unix_ms"],"limits":template["limits"]});
14107
+ for _ in 0..12 {
14108
+ let reply = store.request(&request, &background, &Cancellation::default());
14109
+ assert_eq!(reply["status"].as_str(), Some("ok"), "{reply:?}");
14110
+ if reply["data"]["complete"].as_bool() == Some(true) {
14111
+ return;
14112
+ }
14113
+ request.insert("start_index", reply["data"]["next_index"].clone());
14114
+ request.insert(
14115
+ "membership_revision",
14116
+ reply["data"]["membership_revision"].clone(),
14117
+ );
14118
+ }
14119
+ panic!("registered warming did not complete");
14120
+ };
14121
+ let prepare = json!({"schema":SCHEMA,"id":"prepare","operation":"prepare_graph","view":{"handle":handle(&root),"classifier":{"id":"native","version":"1"},"selectors":[],"attachments":[]},"thread_ids":ids,"roots":[source.directory.to_string_lossy().as_ref()],"direct_paths":[],"active_since_unix_ms":since,"deadline_unix_ms":template["deadline_unix_ms"],"limits":template["limits"]});
14122
+ let has = |graph: &Value, name: &str| {
14123
+ let result = finish_prepared(
14124
+ &store,
14125
+ store.request(&json!({"schema":SCHEMA,"id":"query","operation":"query_graph","handle":graph["data"]["handle"],"selectors":[],"query":{"kind":"has_tool","pattern":name,"subagents":true},"deadline_unix_ms":template["deadline_unix_ms"],"limits":template["limits"]}), &owner, &Cancellation::default()),
14126
+ &owner,
14127
+ );
14128
+ assert_eq!(result["status"].as_str(), Some("ok"), "{result:?}");
14129
+ result["data"]["value"].as_bool().unwrap()
14130
+ };
14131
+ warm();
14132
+ let graph = finish_prepared(
14133
+ &store,
14134
+ store.request(&prepare, &owner, &Cancellation::default()),
14135
+ &owner,
14136
+ );
14137
+ assert_eq!(graph["status"].as_str(), Some("ok"), "{graph:?}");
14138
+ assert!(has(&graph, "Read"));
14139
+ assert!(!has(&graph, "Grep"));
14140
+ std::fs::OpenOptions::new()
14141
+ .append(true)
14142
+ .open(source.directory.join("thread-3.jsonl"))
14143
+ .unwrap()
14144
+ .write_all(format!("{}\n", user("resumed")).as_bytes())
14145
+ .unwrap();
14146
+ let stale_membership = store.request(&prepare, &owner, &Cancellation::default());
14147
+ assert_eq!(
14148
+ stale_membership["status"].as_str(),
14149
+ Some("incomplete"),
14150
+ "{stale_membership:?}"
14151
+ );
14152
+ warm();
14153
+ let resumed = finish_prepared(
14154
+ &store,
14155
+ store.request(&prepare, &owner, &Cancellation::default()),
14156
+ &owner,
14157
+ );
14158
+ assert_eq!(resumed["status"].as_str(), Some("ok"), "{resumed:?}");
14159
+ assert!(has(&resumed, "Grep"));
14160
+ }
14161
+
14015
14162
  #[test]
14016
14163
  fn background_warming_preserves_a_foreground_load_slot() {
14017
14164
  let source = Source::new(&format!("{}\n", user("root")));
@@ -654,6 +654,7 @@ impl NativeStore {
654
654
  "thread_ids": request["thread_ids"],
655
655
  "roots": request["roots"],
656
656
  "direct_paths": request["direct_paths"],
657
+ "active_since_unix_ms": request["active_since_unix_ms"],
657
658
  "registry_generation": context["registry_generation"],
658
659
  "admission": context["admission"],
659
660
  "authority": context["authority"],
@@ -767,13 +768,14 @@ impl NativeStore {
767
768
  .get("roots")
768
769
  .and_then(Value::as_array)
769
770
  .ok_or_else(|| invalid("missing roots"))?;
770
- let direct = request
771
+ request
771
772
  .get("direct_paths")
772
773
  .and_then(Value::as_array)
773
774
  .ok_or_else(|| invalid("missing direct_paths"))?;
774
- if ids.len() > 1024 || roots.len() > 64 || direct.len() > 1024 {
775
+ if roots.len() > 64 {
775
776
  return Err(invalid("prepared registry input exceeds its bound"));
776
777
  }
778
+ let since = active_since_ns(request)?;
777
779
  if !ids.is_empty() {
778
780
  let key = Self::warm_membership_key(request, context)?;
779
781
  let (members, sidechain_dirs, complete) = self
@@ -814,14 +816,20 @@ impl NativeStore {
814
816
  Some(index) => (&members[..index], &members[index + 1..]),
815
817
  None => (&members[..], &members[members.len()..]),
816
818
  };
817
- let others = || before.iter().chain(after);
819
+ let others = || {
820
+ before
821
+ .iter()
822
+ .chain(after)
823
+ .filter(move |source| active(source, since))
824
+ };
825
+ let copied = shared.is_some() || since.is_some();
818
826
  let graph_id = self.token("prepared-graph");
819
827
  let stamps_bytes = (others().count() + 1) * size_of::<(PathBuf, SourceStamp)>()
820
828
  + root.canonical_path.as_os_str().len()
821
829
  + others()
822
830
  .map(|source| source.path.as_os_str().len())
823
831
  .sum::<usize>();
824
- let buffers: usize = if shared.is_some() {
832
+ let buffers: usize = if copied {
825
833
  arc_slice_bytes::<PreparedSourceRef>(others().count())
826
834
  + others()
827
835
  .map(|source| source.path.as_os_str().len())
@@ -845,7 +853,7 @@ impl NativeStore {
845
853
  + stamps_bytes
846
854
  + buffers,
847
855
  )?;
848
- let sources: Arc<[PreparedSourceRef]> = if shared.is_some() {
856
+ let sources: Arc<[PreparedSourceRef]> = if copied {
849
857
  #[cfg(test)]
850
858
  self.registered_sources.fetch_add(1, Ordering::Relaxed);
851
859
  others().cloned().collect()
@@ -1154,7 +1162,26 @@ impl NativeStore {
1154
1162
  if build.seen.contains(&stamp.identity.file()) {
1155
1163
  continue;
1156
1164
  }
1157
- if build.seen.len() >= build.remaining.max_sources {
1165
+ if active_since_ns(&build.request)?.is_some_and(|since| stamp.mtime_ns < since) {
1166
+ self.extend_projection_reservation(
1167
+ reservation,
1168
+ context,
1169
+ set_growth(&build.seen, 1) + canonical.as_os_str().len(),
1170
+ )?;
1171
+ let predicted = (set_capacity_for(&build.seen, 1), build.tasks.capacity());
1172
+ build.seen.insert(stamp.identity.file());
1173
+ build.tasks.push(GraphTask::List {
1174
+ parent: canonical,
1175
+ depth: depth + 1,
1176
+ });
1177
+ assert_eq!(
1178
+ (build.seen.capacity(), build.tasks.capacity()),
1179
+ predicted,
1180
+ "prepared build collections landed off their predicted capacities"
1181
+ );
1182
+ continue;
1183
+ }
1184
+ if build.stamps.len() >= build.remaining.max_sources {
1158
1185
  return Err(SnapshotError::new(
1159
1186
  Status::Incomplete,
1160
1187
  "prepared graph source budget exhausted",
@@ -1363,9 +1390,12 @@ impl NativeStore {
1363
1390
  )?;
1364
1391
  #[cfg(test)]
1365
1392
  self.warm_records.fetch_add(1, Ordering::Relaxed);
1393
+ let since = active_since_ns(request)?;
1366
1394
  let mut located = HashMap::new();
1367
- if !ids.is_empty() {
1368
- let location = json!({"schema":SCHEMA,"id":"warm-registered-locate","operation":"locate","session_ids":ids,"roots":roots,"deadline_unix_ms":remaining.deadline_unix_ms,"limits":remaining.to_json()});
1395
+ for chunk in ids.chunks(1024.min(remaining.max_items)) {
1396
+ let mut bounds = *remaining;
1397
+ bounds.max_sources = chunk.len();
1398
+ let location = json!({"schema":SCHEMA,"id":"warm-registered-locate","operation":"locate","session_ids":chunk,"roots":roots,"deadline_unix_ms":remaining.deadline_unix_ms,"limits":bounds.to_json()});
1369
1399
  let before = usage[17];
1370
1400
  let mut outcome = self.locate(&location, context, cancel, usage)?;
1371
1401
  loop {
@@ -1434,12 +1464,6 @@ impl NativeStore {
1434
1464
  let mut examined = 0usize;
1435
1465
  while examined < members.len() {
1436
1466
  cancel.check(remaining.deadline_unix_ms)?;
1437
- if members.len() > remaining.max_sources {
1438
- return Err(SnapshotError::new(
1439
- Status::Incomplete,
1440
- "registered source bound exhausted",
1441
- ));
1442
- }
1443
1467
  let source = &members[examined];
1444
1468
  let directory = sidechain_directory(
1445
1469
  source
@@ -1522,6 +1546,12 @@ impl NativeStore {
1522
1546
  }
1523
1547
  examined += 1;
1524
1548
  }
1549
+ if members.iter().filter(|member| active(member, since)).count() > remaining.max_sources {
1550
+ return Err(SnapshotError::new(
1551
+ Status::Incomplete,
1552
+ "registered source bound exhausted",
1553
+ ));
1554
+ }
1525
1555
  let mut digest = Sha256::new();
1526
1556
  for source in &members {
1527
1557
  digest.update(source.path.as_os_str().as_encoded_bytes());
@@ -1597,16 +1627,16 @@ impl NativeStore {
1597
1627
  if request["classifier"] != json!({"id":"native","version":"1"}) {
1598
1628
  return Err(invalid("registered warming requires the native classifier"));
1599
1629
  }
1600
- let ids = request["thread_ids"]
1630
+ request["thread_ids"]
1601
1631
  .as_array()
1602
1632
  .ok_or_else(|| invalid("missing registered thread ids"))?;
1603
1633
  let roots = request["roots"]
1604
1634
  .as_array()
1605
1635
  .ok_or_else(|| invalid("missing registered roots"))?;
1606
- let direct = request["direct_paths"]
1636
+ request["direct_paths"]
1607
1637
  .as_array()
1608
1638
  .ok_or_else(|| invalid("missing registered direct paths"))?;
1609
- if ids.len() > 1024 || roots.len() > 64 || direct.len() > 1024 {
1639
+ if roots.len() > 64 {
1610
1640
  return Err(invalid("registered warming input exceeds its bound"));
1611
1641
  }
1612
1642
  let start = number(request, "start_index")?;
@@ -1661,11 +1691,16 @@ impl NativeStore {
1661
1691
  "registered warming membership changed",
1662
1692
  ));
1663
1693
  }
1694
+ let since = active_since_ns(request)?;
1664
1695
  let mut next = start;
1665
1696
  let mut steps = 0usize;
1666
1697
  'warming: while next < members.len() && steps < 8 {
1667
1698
  cancel.check(remaining.deadline_unix_ms)?;
1668
1699
  let source = &members[next];
1700
+ if !active(source, since) {
1701
+ next += 1;
1702
+ continue;
1703
+ }
1669
1704
  let before_read = usage[1];
1670
1705
  let before_events = usage[3];
1671
1706
  let (stamp, mut outcome) = match self.prepared_source(
@@ -1777,7 +1812,7 @@ impl NativeStore {
1777
1812
  "registered warming membership changed",
1778
1813
  ));
1779
1814
  }
1780
- for source in members.iter() {
1815
+ for source in members.iter().filter(|source| active(source, since)) {
1781
1816
  cancel.check(remaining.deadline_unix_ms)?;
1782
1817
  let key = crate::snapshot_prepared_disk::PreparedDiskKey::new(
1783
1818
  source.stamp,
@@ -23,7 +23,7 @@ impl NativeStore {
23
23
  return Err(invalid("root source must be a regular file"));
24
24
  }
25
25
  let current = SourceStamp::of(&metadata).windowed(tail_bytes(request)?);
26
- if current.size > self.config.source as u64 {
26
+ if current.window_bytes() > self.config.source as u64 {
27
27
  return Err(SnapshotError::new(
28
28
  Status::SourceLimit,
29
29
  "root source exceeds owner bound",
@@ -346,6 +346,41 @@ mod root_warm_tests {
346
346
  std::fs::remove_dir_all(directory).unwrap();
347
347
  }
348
348
 
349
+ #[test]
350
+ fn a_trailing_window_of_a_root_past_the_source_bound_warms_and_acquires() {
351
+ let (directory, path) = source(4 * 1024 * 1024);
352
+ let bound = std::fs::metadata(&path).unwrap().len() - 1;
353
+ let store = NativeStore::new(&json!({"max_read_bytes_per_step":256*1024,"max_source_bytes":bound,"max_retained_bytes":128*1024*1024,"reserved_hook_accounted_bytes":4096,"max_leases":16,"reserved_hook_leases":1})).unwrap();
354
+ let context = context("bounded-window");
355
+ let mut request = warm_request(&path, 256 * 1024);
356
+ request.insert("tail_bytes", json!(256 * 1024));
357
+ let mut finished = false;
358
+ for _ in 0..24 {
359
+ let reply = store.request(&request, &context, &Cancellation::default());
360
+ assert_eq!(reply["status"].as_str(), Some("ok"), "{reply:?}");
361
+ if reply["data"]["complete"].as_bool() == Some(true) {
362
+ finished = true;
363
+ break;
364
+ }
365
+ }
366
+ assert!(finished);
367
+ let mut acquire = request.clone();
368
+ acquire.insert("operation", json!("acquire"));
369
+ let acquired = store.request(&acquire, &context, &Cancellation::default());
370
+ assert_eq!(acquired["status"].as_str(), Some("ok"), "{acquired:?}");
371
+ acquire.insert("tail_bytes", json!(null));
372
+ let whole = store.request(&acquire, &context, &Cancellation::default());
373
+ assert_eq!(whole["status"].as_str(), Some("source_limit"), "{whole:?}");
374
+ assert_eq!(whole["reason"].as_str(), Some("source exceeds owner bound"));
375
+ let whole_warm = store.request(
376
+ &warm_request(&path, 256 * 1024),
377
+ &context,
378
+ &Cancellation::default(),
379
+ );
380
+ assert_eq!(whole_warm["status"].as_str(), Some("source_limit"), "{whole_warm:?}");
381
+ std::fs::remove_dir_all(directory).unwrap();
382
+ }
383
+
349
384
  #[test]
350
385
  fn a_base_advance_supersedes_the_previous_window_pin() {
351
386
  let (directory, path) = source(4 * 1024 * 1024);
File without changes