cc-transcript 14.45.12__tar.gz → 14.45.14__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 (198) hide show
  1. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/PKG-INFO +1 -1
  2. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/pyproject.toml +1 -1
  3. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/snapshot.rs +349 -60
  4. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/snapshot_activity.rs +160 -0
  5. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/snapshot_ledger_tests.rs +71 -10
  6. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/snapshot_prepared_service.rs +16 -12
  7. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/snapshot_projection.rs +81 -60
  8. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/snapshot_root_warm.rs +2 -2
  9. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/Cargo.lock +0 -0
  10. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/Cargo.toml +0 -0
  11. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/LICENSE +0 -0
  12. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/README.md +0 -0
  13. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/__init__.py +0 -0
  14. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/__init__.pyi +0 -0
  15. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/_native.pyi +0 -0
  16. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/activity.py +0 -0
  17. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/activity_probe.py +0 -0
  18. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/builders.py +0 -0
  19. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/codex.py +0 -0
  20. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/command.py +0 -0
  21. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/context.py +0 -0
  22. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/corrections.py +0 -0
  23. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/cost.py +0 -0
  24. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/decision_store.py +0 -0
  25. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/decisions.py +0 -0
  26. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/discovery.py +0 -0
  27. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/disktruth.py +0 -0
  28. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/evidence.py +0 -0
  29. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/extract/__init__.py +0 -0
  30. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/extract/correct.py +0 -0
  31. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/facts.py +0 -0
  32. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/filterspec.py +0 -0
  33. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/heartbeats.py +0 -0
  34. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/ids.py +0 -0
  35. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/judge/__init__.py +0 -0
  36. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/judge/llm.py +0 -0
  37. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/judge/similar.py +0 -0
  38. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/judge/verdicts.py +0 -0
  39. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/ledger.py +0 -0
  40. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/literals.py +0 -0
  41. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/mining/__init__.py +0 -0
  42. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/mining/candidates.py +0 -0
  43. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/mining/confidence.py +0 -0
  44. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/mining/engine.py +0 -0
  45. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/mining/formats.py +0 -0
  46. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/mining/sampling.py +0 -0
  47. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/mining/signals.py +0 -0
  48. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/mining/sourcekind.py +0 -0
  49. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/mining/spec.py +0 -0
  50. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/mining/store.py +0 -0
  51. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/models.py +0 -0
  52. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/nlp.py +0 -0
  53. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/notifications.py +0 -0
  54. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/parser.py +0 -0
  55. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/py.typed +0 -0
  56. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/query.py +0 -0
  57. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/render.py +0 -0
  58. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/sentiment/__init__.py +0 -0
  59. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/sentiment/buckets.py +0 -0
  60. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/sentiment/data/afinn-en-165.tsv +0 -0
  61. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/sentiment/data/domain_overrides.tsv +0 -0
  62. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/sentiment/data/en-ewt.udpipe +0 -0
  63. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/sentiment/engine.py +0 -0
  64. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/sentiment/lexicon.py +0 -0
  65. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/sentiment/scorespec.py +0 -0
  66. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/snapshot_schema/config.schema.json +0 -0
  67. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/snapshot_schema/context.schema.json +0 -0
  68. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/snapshot_schema/request.schema.json +0 -0
  69. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/snapshot_schema/response.schema.json +0 -0
  70. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/snapshot_schema/tool_registry.schema.json +0 -0
  71. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/snapshots.py +0 -0
  72. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/synthetic.py +0 -0
  73. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/tools.py +0 -0
  74. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/cc_transcript/watch.py +0 -0
  75. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/cli/Cargo.toml +0 -0
  76. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/cli/src/commands/blame.rs +0 -0
  77. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/cli/src/commands/corpus.rs +0 -0
  78. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/cli/src/commands/corrections.rs +0 -0
  79. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/cli/src/commands/digest.rs +0 -0
  80. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/cli/src/commands/facts.rs +0 -0
  81. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/cli/src/commands/grep.rs +0 -0
  82. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/cli/src/commands/list.rs +0 -0
  83. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/cli/src/commands/mod.rs +0 -0
  84. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/cli/src/commands/scratchpad.rs +0 -0
  85. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/cli/src/commands/show.rs +0 -0
  86. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/cli/src/commands/slice.rs +0 -0
  87. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/cli/src/commands/stats.rs +0 -0
  88. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/cli/src/commands/watch.rs +0 -0
  89. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/cli/src/lib.rs +0 -0
  90. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/cli/src/output.rs +0 -0
  91. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/cli/src/target.rs +0 -0
  92. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/cli/src/timearg.rs +0 -0
  93. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/Cargo.toml +0 -0
  94. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/benches/parse.rs +0 -0
  95. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/benches/render.rs +0 -0
  96. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/benches/toolcall.rs +0 -0
  97. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/build.rs +0 -0
  98. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/activity.rs +0 -0
  99. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/actor.rs +0 -0
  100. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/blame.rs +0 -0
  101. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/buckets.rs +0 -0
  102. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/codex/discovery.rs +0 -0
  103. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/codex/lower.rs +0 -0
  104. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/codex/mod.rs +0 -0
  105. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/codex/parse.rs +0 -0
  106. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/codex/probe.rs +0 -0
  107. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/codex/protocol.rs +0 -0
  108. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/codex/types.rs +0 -0
  109. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/command.rs +0 -0
  110. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/context.rs +0 -0
  111. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/corrections.rs +0 -0
  112. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/cost.rs +0 -0
  113. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/discovery.rs +0 -0
  114. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/facts.rs +0 -0
  115. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/feedback.rs +0 -0
  116. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/filter.rs +0 -0
  117. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/gateway.rs +0 -0
  118. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/ids.rs +0 -0
  119. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/judge.rs +0 -0
  120. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/lib.rs +0 -0
  121. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/literals/command.rs +0 -0
  122. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/literals/corrections.rs +0 -0
  123. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/literals/feedback.rs +0 -0
  124. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/literals/mining.rs +0 -0
  125. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/literals/mod.rs +0 -0
  126. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/literals/protocol.rs +0 -0
  127. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/notifications.rs +0 -0
  128. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/parse.rs +0 -0
  129. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/protocol.rs +0 -0
  130. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/pystr.rs +0 -0
  131. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/query.rs +0 -0
  132. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/render.rs +0 -0
  133. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/rng.rs +0 -0
  134. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/scan.rs +0 -0
  135. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/scan_checkpoint.rs +0 -0
  136. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/scan_grep.rs +0 -0
  137. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/scan_grep_stream.rs +0 -0
  138. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/scan_grep_stream_tests.rs +0 -0
  139. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/scan_index.rs +0 -0
  140. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/scan_projection.rs +0 -0
  141. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/scan_regressions.rs +0 -0
  142. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/scan_stream.rs +0 -0
  143. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/schema.rs +0 -0
  144. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/snapshot_codec.rs +0 -0
  145. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/snapshot_codex_append.rs +0 -0
  146. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/snapshot_labels.rs +0 -0
  147. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/snapshot_ledger.rs +0 -0
  148. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/snapshot_memory.rs +0 -0
  149. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/snapshot_owned.rs +0 -0
  150. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/snapshot_prepared.rs +0 -0
  151. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/snapshot_prepared_disk.rs +0 -0
  152. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/snapshot_regressions.rs +0 -0
  153. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/snapshot_text.rs +0 -0
  154. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/sqlite.rs +0 -0
  155. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/toolcall.rs +0 -0
  156. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/types.rs +0 -0
  157. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/value.rs +0 -0
  158. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/src/watch.rs +0 -0
  159. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/tests/sonic_arena_model.rs +0 -0
  160. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/core/tests/std_allocation_model.rs +0 -0
  161. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/Cargo.toml +0 -0
  162. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/build.rs +0 -0
  163. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/data/command_prefix_pins.tsv +0 -0
  164. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/actor_bridge.rs +0 -0
  165. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/bin/stub_gen.rs +0 -0
  166. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/codex.rs +0 -0
  167. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/command.rs +0 -0
  168. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/context.rs +0 -0
  169. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/corrections.rs +0 -0
  170. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/discovery.rs +0 -0
  171. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/feedback.rs +0 -0
  172. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/judge.rs +0 -0
  173. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/lexicon.rs +0 -0
  174. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/lib.rs +0 -0
  175. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/mining.rs +0 -0
  176. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/nlp.rs +0 -0
  177. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/python.rs +0 -0
  178. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/render.rs +0 -0
  179. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/score.rs +0 -0
  180. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/snapshot_decode.rs +0 -0
  181. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/snapshots.rs +0 -0
  182. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/sqlite.rs +0 -0
  183. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/toolcall.rs +0 -0
  184. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/views/attachment.rs +0 -0
  185. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/views/blocks.rs +0 -0
  186. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/views/command.rs +0 -0
  187. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/views/convert.rs +0 -0
  188. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/views/dunder.rs +0 -0
  189. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/views/events.rs +0 -0
  190. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/views/meta.rs +0 -0
  191. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/views/mod.rs +0 -0
  192. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/views/print.rs +0 -0
  193. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/views/store.rs +0 -0
  194. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/views/system.rs +0 -0
  195. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/views/toolcall.rs +0 -0
  196. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/views/toolresult.rs +0 -0
  197. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/rust/crates/py/src/views/transcript.rs +0 -0
  198. {cc_transcript-14.45.12 → cc_transcript-14.45.14}/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.45.12
3
+ Version: 14.45.14
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cc-transcript"
3
- version = "14.45.12" # informational — releases stamp the version from the git tag
3
+ version = "14.45.14" # 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"
@@ -32,7 +32,9 @@ use crate::snapshot_memory::{
32
32
  use crate::snapshot_prepared::FactsNeed;
33
33
  use crate::snapshot_prepared_disk::PreparedDiskKey;
34
34
  use crate::snapshot_projection::JSON_LITERAL_OBJECT_CAPACITY;
35
- use crate::types::{Entry, SYNTHETIC_MODEL};
35
+ use crate::types::Entry;
36
+ #[cfg(test)]
37
+ use crate::types::SYNTHETIC_MODEL;
36
38
 
37
39
  pub const SCHEMA: &str = "cc-transcript.snapshot/1";
38
40
  pub const PARSER_VERSION: &str = "cc-transcript.snapshot/1";
@@ -375,23 +377,15 @@ impl TranscriptSnapshot {
375
377
  if self.window_start == 0 {
376
378
  return None;
377
379
  }
378
- self.chunks
379
- .iter()
380
- .flat_map(|chunk| chunk.entries.iter())
381
- .find_map(Entry::meta)
382
- .map(|meta| meta.timestamp.timestamp_millis())
380
+ self.activity.started_unix_ms()
383
381
  }
384
382
 
385
383
  pub fn model(&self) -> Option<&str> {
386
- self.chunks
387
- .iter()
388
- .rev()
389
- .flat_map(|chunk| chunk.entries.iter().rev())
390
- .find_map(|entry| match entry {
391
- Entry::Assistant(assistant) if assistant.model != SYNTHETIC_MODEL => {
392
- Some(assistant.model.as_str())
393
- }
394
- _ => None,
384
+ self.activity
385
+ .last_model_event()
386
+ .map(|position| match self.entry(position) {
387
+ Entry::Assistant(assistant) => assistant.model.as_str(),
388
+ _ => unreachable!("indexed model is an assistant event"),
395
389
  })
396
390
  }
397
391
 
@@ -533,11 +527,10 @@ struct Lease {
533
527
  registry: Arc<crate::toolcall::ToolRegistrySnapshot>,
534
528
  }
535
529
 
536
- struct RenewedScope {
530
+ struct LeasedScope {
537
531
  snapshot: Arc<TranscriptSnapshot>,
538
532
  classifier: Value,
539
533
  expires: u64,
540
- description_bytes: usize,
541
534
  }
542
535
 
543
536
  // Sized exactly like an acquire reply's re-serialized copy, whose bytes admit graph members.
@@ -2889,6 +2882,8 @@ pub struct NativeStore {
2889
2882
  #[cfg(test)]
2890
2883
  pub(crate) fact_builds: AtomicUsize,
2891
2884
  #[cfg(test)]
2885
+ pub(crate) description_builds: AtomicUsize,
2886
+ #[cfg(test)]
2892
2887
  pub(crate) fact_lookups: AtomicUsize,
2893
2888
  #[cfg(test)]
2894
2889
  pub(crate) fact_read_waits: AtomicUsize,
@@ -3372,6 +3367,8 @@ impl NativeStore {
3372
3367
  #[cfg(test)]
3373
3368
  fact_builds: AtomicUsize::new(0),
3374
3369
  #[cfg(test)]
3370
+ description_builds: AtomicUsize::new(0),
3371
+ #[cfg(test)]
3375
3372
  fact_lookups: AtomicUsize::new(0),
3376
3373
  #[cfg(test)]
3377
3374
  fact_read_waits: AtomicUsize::new(0),
@@ -3520,7 +3517,7 @@ impl NativeStore {
3520
3517
  deadline: u64,
3521
3518
  ) -> Result<u64, SnapshotError> {
3522
3519
  for handle in handles {
3523
- self.pin_scope(handle, context)?;
3520
+ self.pin_record(handle, context)?;
3524
3521
  }
3525
3522
  let state = self.lock_state();
3526
3523
  let mut expires = (now_ms() + self.config.ttl).min(deadline);
@@ -3955,11 +3952,9 @@ impl NativeStore {
3955
3952
  mut bounds: WorkLimits,
3956
3953
  ) -> Result<Value, SnapshotError> {
3957
3954
  cancel.check(bounds.deadline_unix_ms)?;
3958
- let (source, description) =
3959
- self.pin_scope_for_work(handle, context, bounds.deadline_unix_ms)?;
3960
- bounds.deadline_unix_ms = bounds
3961
- .deadline_unix_ms
3962
- .min(number(&description, "lease_expires_unix_ms")? as u64);
3955
+ let scope = self.renew_scope_for_work(handle, context, bounds.deadline_unix_ms)?;
3956
+ let source = scope.snapshot;
3957
+ bounds.deadline_unix_ms = bounds.deadline_unix_ms.min(scope.expires);
3963
3958
  bounds.deadline_unix_ms = bounds
3964
3959
  .deadline_unix_ms
3965
3960
  .min(now_ms() + self.config.preparation);
@@ -4983,6 +4978,29 @@ impl NativeStore {
4983
4978
 
4984
4979
  #[cfg(test)]
4985
4980
  fn audit_snapshot_allocations(snapshot: &Arc<TranscriptSnapshot>) -> Vec<(usize, usize)> {
4981
+ let started = if snapshot.window_start == 0 {
4982
+ None
4983
+ } else {
4984
+ snapshot
4985
+ .chunks
4986
+ .iter()
4987
+ .flat_map(|chunk| chunk.entries.iter())
4988
+ .find_map(Entry::meta)
4989
+ .map(|meta| meta.timestamp.timestamp_millis())
4990
+ };
4991
+ assert_eq!(snapshot.window_started_unix_ms(), started);
4992
+ let model = snapshot
4993
+ .chunks
4994
+ .iter()
4995
+ .rev()
4996
+ .flat_map(|chunk| chunk.entries.iter().rev())
4997
+ .find_map(|entry| match entry {
4998
+ Entry::Assistant(assistant) if assistant.model != SYNTHETIC_MODEL => {
4999
+ Some(assistant.model.as_str())
5000
+ }
5001
+ _ => None,
5002
+ });
5003
+ assert_eq!(snapshot.model(), model);
4986
5004
  let TranscriptSnapshot {
4987
5005
  ledger: _,
4988
5006
  id,
@@ -5863,6 +5881,8 @@ impl NativeStore {
5863
5881
  lease: &str,
5864
5882
  expires: u64,
5865
5883
  ) -> Value {
5884
+ #[cfg(test)]
5885
+ self.description_builds.fetch_add(1, Ordering::Relaxed);
5866
5886
  json!({"handle": LeaseHandle {owner_epoch: &self.owner_epoch, snapshot_id: &snapshot.id, generation: &snapshot.id, lease_id: lease},
5867
5887
  "canonical_path": snapshot.canonical_path.to_string_lossy().as_ref(),
5868
5888
  "source_id": format!("{}:{}", snapshot.stamp.identity.device, snapshot.stamp.identity.inode),
@@ -5879,8 +5899,7 @@ impl NativeStore {
5879
5899
  handle: &Value,
5880
5900
  context: &Value,
5881
5901
  ) -> Result<Arc<TranscriptSnapshot>, SnapshotError> {
5882
- self.pin_scope(handle, context)
5883
- .map(|(snapshot, _)| snapshot)
5902
+ self.pin_record(handle, context).map(|scope| scope.snapshot)
5884
5903
  }
5885
5904
 
5886
5905
  pub fn validate_scope(&self, handle: &Value, context: &Value) -> Result<(), SnapshotError> {
@@ -5893,23 +5912,36 @@ impl NativeStore {
5893
5912
  handle: &Value,
5894
5913
  context: &Value,
5895
5914
  ) -> Result<(Arc<TranscriptSnapshot>, Value), SnapshotError> {
5915
+ let scope = self.capture_scope(handle, context)?;
5916
+ let description = self.description(
5917
+ &scope.snapshot,
5918
+ &scope.classifier,
5919
+ str_field(handle, "lease_id")?,
5920
+ scope.expires,
5921
+ );
5922
+ self.authority(context, Some(&scope.snapshot.canonical_path))?;
5923
+ Ok((scope.snapshot, description))
5924
+ }
5925
+
5926
+ fn pin_record(&self, handle: &Value, context: &Value) -> Result<LeasedScope, SnapshotError> {
5927
+ let scope = self.capture_scope(handle, context)?;
5928
+ self.authority(context, Some(&scope.snapshot.canonical_path))?;
5929
+ Ok(scope)
5930
+ }
5931
+
5932
+ fn capture_scope(&self, handle: &Value, context: &Value) -> Result<LeasedScope, SnapshotError> {
5896
5933
  self.authority(context, None)?;
5897
- let (snapshot, description) = {
5934
+ let scope = {
5898
5935
  let mut state = self.lock_state();
5899
5936
  Self::prune(&mut state);
5900
5937
  let lease = self.lease(&state, handle, context)?;
5901
- (
5902
- Arc::clone(&lease.snapshot),
5903
- self.description(
5904
- &lease.snapshot,
5905
- &lease.classifier,
5906
- str_field(handle, "lease_id")?,
5907
- lease.expires,
5908
- ),
5909
- )
5938
+ LeasedScope {
5939
+ snapshot: Arc::clone(&lease.snapshot),
5940
+ classifier: lease.classifier.clone(),
5941
+ expires: lease.expires,
5942
+ }
5910
5943
  };
5911
- self.authority(context, Some(&snapshot.canonical_path))?;
5912
- Ok((snapshot, description))
5944
+ Ok(scope)
5913
5945
  }
5914
5946
 
5915
5947
  pub fn pin_scope_for_work(
@@ -5933,7 +5965,7 @@ impl NativeStore {
5933
5965
  handle: &Value,
5934
5966
  context: &Value,
5935
5967
  deadline: u64,
5936
- ) -> Result<RenewedScope, SnapshotError> {
5968
+ ) -> Result<LeasedScope, SnapshotError> {
5937
5969
  #[cfg(test)]
5938
5970
  {
5939
5971
  let hook = self.pin_hook.lock().expect("pin hook").clone();
@@ -5950,7 +5982,7 @@ impl NativeStore {
5950
5982
  "borrow deadline expired",
5951
5983
  ));
5952
5984
  }
5953
- let (snapshot, probe) = self.pin_scope(handle, context)?;
5985
+ let scope = self.pin_record(handle, context)?;
5954
5986
  let mut state = self.lock_state();
5955
5987
  let lease = self.lease(&state, handle, context)?;
5956
5988
  let expires = lease.expires.max(deadline.min(lease.absolute_deadline));
@@ -5960,11 +5992,10 @@ impl NativeStore {
5960
5992
  .get_mut(str_field(handle, "lease_id")?)
5961
5993
  .expect("validated lease")
5962
5994
  .expires = expires;
5963
- Ok(RenewedScope {
5964
- snapshot,
5995
+ Ok(LeasedScope {
5996
+ snapshot: scope.snapshot,
5965
5997
  classifier,
5966
5998
  expires,
5967
- description_bytes: value_bytes(&probe),
5968
5999
  })
5969
6000
  }
5970
6001
 
@@ -7112,6 +7143,7 @@ impl NativeStore {
7112
7143
  let mut lines: Vec<(u64, Vec<Entry>)> = Vec::new();
7113
7144
  let mut events = 0usize;
7114
7145
  let mut exhausted = false;
7146
+ let mut read_step = self.config.read_step.min(64 * 1024);
7115
7147
  loop {
7116
7148
  while events < count {
7117
7149
  cancel.check(limits.deadline_unix_ms)?;
@@ -7134,11 +7166,7 @@ impl NativeStore {
7134
7166
  }
7135
7167
  break;
7136
7168
  }
7137
- let step = self
7138
- .config
7139
- .read_step
7140
- .min(budget - read)
7141
- .min(pending_start as usize);
7169
+ let step = read_step.min(budget - read).min(pending_start as usize);
7142
7170
  if step == 0 {
7143
7171
  exhausted = true;
7144
7172
  break;
@@ -7152,6 +7180,7 @@ impl NativeStore {
7152
7180
  file.read_exact(&mut block).map_err(io_error)?;
7153
7181
  usage[1] += step as u64;
7154
7182
  read += step;
7183
+ read_step = read_step.saturating_mul(2).min(self.config.read_step);
7155
7184
  block.extend_from_slice(&pending);
7156
7185
  pending = block;
7157
7186
  pending_start -= step as u64;
@@ -8764,6 +8793,12 @@ impl NativeStore {
8764
8793
  let claimant = str_field(context, "claimant")?;
8765
8794
  let lease_id = str_field(root_handle, "lease_id")?;
8766
8795
  let root_path = scope.snapshot.canonical_path.as_os_str().len();
8796
+ let description_bytes = value_bytes(&self.description(
8797
+ &scope.snapshot,
8798
+ &scope.classifier,
8799
+ lease_id,
8800
+ scope.expires,
8801
+ ));
8767
8802
  let mut reservation = self.reserve_projection(
8768
8803
  context,
8769
8804
  size_of::<GraphCursor>()
@@ -8773,7 +8808,7 @@ impl NativeStore {
8773
8808
  + value_bytes(root_handle)
8774
8809
  + size_of::<GraphNode>()
8775
8810
  + root_path
8776
- + scope.description_bytes
8811
+ + description_bytes
8777
8812
  + set_growth(&HashSet::<SourceIdentity>::new(), 1)
8778
8813
  + task_count * size_of::<GraphTask>()
8779
8814
  + attachment_bytes
@@ -8781,7 +8816,7 @@ impl NativeStore {
8781
8816
  )?;
8782
8817
  #[cfg(test)]
8783
8818
  self.graph_records.fetch_add(1, Ordering::Relaxed);
8784
- let RenewedScope {
8819
+ let LeasedScope {
8785
8820
  snapshot: root,
8786
8821
  classifier,
8787
8822
  expires,
@@ -9097,7 +9132,7 @@ impl NativeStore {
9097
9132
  usage: &mut [u64; 20],
9098
9133
  ) -> Result<GraphYield, SnapshotError> {
9099
9134
  cancel.check(graph.remaining.deadline_unix_ms)?;
9100
- self.pin_scope_for_work(
9135
+ self.renew_scope_for_work(
9101
9136
  &graph.root_handle,
9102
9137
  &graph.context,
9103
9138
  graph.remaining.deadline_unix_ms,
@@ -9538,12 +9573,10 @@ impl NativeStore {
9538
9573
  let handle = view
9539
9574
  .get("handle")
9540
9575
  .ok_or_else(|| invalid("missing view handle"))?;
9541
- let (snapshot, description) =
9542
- self.pin_scope_for_work(handle, context, bound.deadline_unix_ms)?;
9543
- bound.deadline_unix_ms = bound
9544
- .deadline_unix_ms
9545
- .min(number(&description, "lease_expires_unix_ms")? as u64);
9546
- if !classifier_eq(&view["classifier"], &description["classifier"])? {
9576
+ let scope = self.renew_scope_for_work(handle, context, bound.deadline_unix_ms)?;
9577
+ let snapshot = scope.snapshot;
9578
+ bound.deadline_unix_ms = bound.deadline_unix_ms.min(scope.expires);
9579
+ if !classifier_eq(&view["classifier"], &scope.classifier)? {
9547
9580
  return Err(invalid("view classifier differs from leased generation"));
9548
9581
  }
9549
9582
  let mut page = bound;
@@ -9671,16 +9704,15 @@ impl NativeStore {
9671
9704
  .ok_or_else(|| invalid("missing windows_json"))?;
9672
9705
  let mut sessions = HashMap::new();
9673
9706
  for handle in handles.iter() {
9674
- let (snapshot, description) = self.pin_scope_for_work(
9707
+ let scope = self.renew_scope_for_work(
9675
9708
  handle
9676
9709
  .get("handle")
9677
9710
  .ok_or_else(|| invalid("missing handle"))?,
9678
9711
  context,
9679
9712
  bound.deadline_unix_ms,
9680
9713
  )?;
9681
- bound.deadline_unix_ms = bound
9682
- .deadline_unix_ms
9683
- .min(number(&description, "lease_expires_unix_ms")? as u64);
9714
+ let snapshot = scope.snapshot;
9715
+ bound.deadline_unix_ms = bound.deadline_unix_ms.min(scope.expires);
9684
9716
  let session = str_field(handle, "session_id")?;
9685
9717
  if session != snapshot.session_id {
9686
9718
  return Err(invalid("hydration session differs from handle"));
@@ -12169,6 +12201,192 @@ mod tests {
12169
12201
  .collect()
12170
12202
  }
12171
12203
 
12204
+ fn tail_with_small_step_reference(source: &Source, count: usize, budget: usize) -> Value {
12205
+ let owner = context("a");
12206
+ let request = tail_request(&source.path, count, budget);
12207
+ let store = NativeStore::new(&json!({})).unwrap();
12208
+ let response = store.request(&request, &owner, &Cancellation::default());
12209
+ let reference = NativeStore::new(&json!({"max_read_bytes_per_step":4096})).unwrap();
12210
+ let reference = reference.request(&request, &owner, &Cancellation::default());
12211
+ assert_eq!(response["status"], reference["status"], "{response:?}");
12212
+ assert_eq!(response["reason"], reference["reason"], "{response:?}");
12213
+ assert_eq!(response["data"], reference["data"], "{response:?}");
12214
+ response
12215
+ }
12216
+
12217
+ #[test]
12218
+ fn tail_default_steps_read_only_the_newest_event_window() {
12219
+ let suffix = (0..256)
12220
+ .map(|index| format!("{}\n", user_text(&index.to_string(), &"x".repeat(1400))))
12221
+ .collect::<String>();
12222
+ assert!((256 * 1024..448 * 1024).contains(&suffix.len()));
12223
+ let prefix = format!("{}\n", " ".repeat(8 * 1024 * 1024));
12224
+ let source = Source::new(&format!("{prefix}{suffix}"));
12225
+ let response = tail_with_small_step_reference(&source, 256, 8 * 1024 * 1024);
12226
+ assert_eq!(response["status"].as_str(), Some("ok"), "{response:?}");
12227
+ assert_eq!(
12228
+ tail_uuids(&response),
12229
+ (0..256).map(|index| index.to_string()).collect::<Vec<_>>()
12230
+ );
12231
+ assert_eq!(
12232
+ response["data"]["source_bytes"].as_u64(),
12233
+ Some((prefix.len() + suffix.len()) as u64)
12234
+ );
12235
+ assert_eq!(
12236
+ response["data"]["window_start_byte"].as_u64(),
12237
+ Some(prefix.len() as u64)
12238
+ );
12239
+ let read = response["usage"]["source_bytes_read"].as_u64().unwrap();
12240
+ assert!(read >= suffix.len() as u64 && read <= 512 * 1024, "{read}");
12241
+ }
12242
+
12243
+ #[test]
12244
+ fn tail_grows_reads_for_a_long_partial_line() {
12245
+ fn count_reads(store: &Arc<NativeStore>, reads: &Arc<AtomicUsize>) {
12246
+ let next = Arc::downgrade(store);
12247
+ let reads = Arc::clone(reads);
12248
+ *store.read_hook.lock().unwrap() = Some(Arc::new(move || {
12249
+ reads.fetch_add(1, Ordering::Relaxed);
12250
+ count_reads(&next.upgrade().unwrap(), &reads);
12251
+ }));
12252
+ }
12253
+
12254
+ let prefix = format!("{}\n", " ".repeat(8 * 1024 * 1024));
12255
+ let suffix = format!("{}\n", user_text("large", &"x".repeat(512 * 1024)));
12256
+ let source = Source::new(&format!("{prefix}{suffix}"));
12257
+ let owner = context("a");
12258
+ let request = tail_request(&source.path, 1, 8 * 1024 * 1024);
12259
+ let store = Arc::new(NativeStore::new(&json!({})).unwrap());
12260
+ let reads = Arc::new(AtomicUsize::new(0));
12261
+ count_reads(&store, &reads);
12262
+ let response = store.request(&request, &owner, &Cancellation::default());
12263
+ assert_eq!(response["status"].as_str(), Some("ok"), "{response:?}");
12264
+ assert_eq!(tail_uuids(&response), ["large"]);
12265
+ assert_eq!(
12266
+ response["data"]["window_start_byte"].as_u64(),
12267
+ Some(prefix.len() as u64)
12268
+ );
12269
+ assert!(response["usage"]["source_bytes_read"].as_u64().unwrap() <= 1024 * 1024);
12270
+ assert!(reads.load(Ordering::Relaxed) <= 4);
12271
+ let bounded = NativeStore::new(&json!({"max_entry_bytes":256*1024})).unwrap();
12272
+ let refused = bounded.request(&request, &owner, &Cancellation::default());
12273
+ assert_eq!(
12274
+ refused["status"].as_str(),
12275
+ Some("entry_limit"),
12276
+ "{refused:?}"
12277
+ );
12278
+ }
12279
+
12280
+ #[test]
12281
+ fn tail_preserves_utf8_across_growing_read_boundaries() {
12282
+ let mut split_scalars = 0;
12283
+ for ending in ["", "\n"] {
12284
+ let contents = format!(
12285
+ "{}\n{}{ending}",
12286
+ user("old"),
12287
+ user_text("unicode", &"é".repeat(128 * 1024))
12288
+ );
12289
+ let boundary = contents.len() - 64 * 1024;
12290
+ split_scalars += usize::from(contents.as_bytes()[boundary] & 0xc0 == 0x80);
12291
+ let source = Source::new(&contents);
12292
+ let response = tail_with_small_step_reference(&source, 1, 1024 * 1024);
12293
+ assert_eq!(response["status"].as_str(), Some("ok"), "{response:?}");
12294
+ assert_eq!(tail_uuids(&response), ["unicode"]);
12295
+ assert_eq!(
12296
+ response["data"]["window_start_byte"].as_u64(),
12297
+ Some((user("old").len() + 1) as u64)
12298
+ );
12299
+ }
12300
+ assert_eq!(split_scalars, 1);
12301
+ }
12302
+
12303
+ #[test]
12304
+ fn tail_read_budget_requires_the_oldest_line_delimiter() {
12305
+ let prefix = format!("{}\n", user("old"));
12306
+ let first = format!("{}\n", user_text("a", &"x".repeat(128 * 1024)));
12307
+ let last = format!("{}\n", user_text("b", &"x".repeat(128 * 1024)));
12308
+ let source = Source::new(&format!("{prefix}{first}{last}"));
12309
+ let budget = first.len() + last.len();
12310
+ let incomplete = tail_with_small_step_reference(&source, 2, budget);
12311
+ assert_eq!(
12312
+ incomplete["status"].as_str(),
12313
+ Some("incomplete"),
12314
+ "{incomplete:?}"
12315
+ );
12316
+ assert_eq!(
12317
+ incomplete["reason"].as_str(),
12318
+ Some("tail read budget exhausted")
12319
+ );
12320
+ assert!(incomplete["cursor"].is_null());
12321
+ assert_eq!(tail_uuids(&incomplete), ["b"]);
12322
+ assert_eq!(
12323
+ incomplete["data"]["window_start_byte"].as_u64(),
12324
+ Some((prefix.len() + first.len()) as u64)
12325
+ );
12326
+ let complete = tail_with_small_step_reference(&source, 2, budget + 1);
12327
+ assert_eq!(complete["status"].as_str(), Some("ok"), "{complete:?}");
12328
+ assert_eq!(tail_uuids(&complete), ["a", "b"]);
12329
+ assert_eq!(
12330
+ complete["data"]["window_start_byte"].as_u64(),
12331
+ Some(prefix.len() as u64)
12332
+ );
12333
+ }
12334
+
12335
+ #[test]
12336
+ fn tail_counts_metadata_events_and_skips_non_events_across_steps() {
12337
+ let source = Source::new(&format!(
12338
+ "{}\n{}\nnot json\n[]\nnull\n42\n{{\"type\":\"queue-operation\",\"operation\":\"enqueue\"}}\n{}\n",
12339
+ user("a"),
12340
+ " ".repeat(96 * 1024),
12341
+ user_text("b", &"x".repeat(128 * 1024)),
12342
+ ));
12343
+ let response = tail_with_small_step_reference(&source, 3, 1024 * 1024);
12344
+ assert_eq!(response["status"].as_str(), Some("ok"), "{response:?}");
12345
+ let records = response["data"]["records_json"]
12346
+ .as_array()
12347
+ .unwrap()
12348
+ .iter()
12349
+ .map(|record| record.as_str().unwrap().to_owned())
12350
+ .collect::<Vec<_>>();
12351
+ let records = crate::snapshot_codec::decode_events(&records, 1024 * 1024).unwrap();
12352
+ assert_eq!(records.len(), 3);
12353
+ assert_eq!(records[0].event.meta().unwrap().uuid, "a");
12354
+ assert!(matches!(&records[1].event, Entry::Other(other) if other.ty == "queue-operation"));
12355
+ assert_eq!(records[2].event.meta().unwrap().uuid, "b");
12356
+ assert_eq!(response["data"]["window_start_byte"].as_u64(), Some(0));
12357
+ }
12358
+
12359
+ #[test]
12360
+ fn tail_preserves_the_pinned_source_while_reads_grow() {
12361
+ let prefix = format!("{}\n", user("old"));
12362
+ let suffix = format!("{}\n", user_text("new", &"x".repeat(256 * 1024)));
12363
+ let source = Source::new(&format!("{prefix}{suffix}"));
12364
+ let path = source.path.clone();
12365
+ let store = NativeStore::new(&json!({})).unwrap();
12366
+ *store.read_hook.lock().unwrap() = Some(Arc::new(move || {
12367
+ std::fs::OpenOptions::new()
12368
+ .append(true)
12369
+ .open(&path)
12370
+ .unwrap()
12371
+ .write_all(format!("{}\n", user("appended")).as_bytes())
12372
+ .unwrap();
12373
+ }));
12374
+ let response = store.request(
12375
+ &tail_request(&source.path, 1, 1024 * 1024),
12376
+ &context("a"),
12377
+ &Cancellation::default(),
12378
+ );
12379
+ assert_eq!(response["status"].as_str(), Some("ok"), "{response:?}");
12380
+ assert_eq!(tail_uuids(&response), ["new"]);
12381
+ assert_eq!(
12382
+ response["data"]["source_bytes"].as_u64(),
12383
+ Some((prefix.len() + suffix.len()) as u64)
12384
+ );
12385
+ assert_eq!(
12386
+ response["data"]["window_start_byte"].as_u64(),
12387
+ Some(prefix.len() as u64)
12388
+ );
12389
+ }
12172
12390
  #[test]
12173
12391
  fn tail_returns_the_newest_events_reading_backwards_in_bounded_steps() {
12174
12392
  let source = Source::new(&users(0..50));
@@ -16144,6 +16362,77 @@ mod tests {
16144
16362
  store.assert_conserved();
16145
16363
  }
16146
16364
 
16365
+ #[test]
16366
+ fn repeated_root_predicates_keep_scope_metadata_typed() {
16367
+ let skill = json!({"type":"assistant","uuid":"skill","sessionId":"s","timestamp":"2026-01-02T03:04:06Z","message":{"model":"m","content":[{"type":"tool_use","id":"skill","name":"Skill","input":{"skill":"skill-4"}}]}});
16368
+ let source = Source::new(&format!("{}\n{skill}\n", user_text("prompt", "ready")));
16369
+ let store = fact_budget_store(16 * 1024 * 1024);
16370
+ let owner = context("a");
16371
+ let template = acquire(&source.path);
16372
+ let root = finish(
16373
+ &store,
16374
+ store.request(&template, &owner, &Cancellation::default()),
16375
+ &owner,
16376
+ );
16377
+ let graph = finish(
16378
+ &store,
16379
+ store.request(&json!({"schema":SCHEMA,"id":"prepare","operation":"prepare_graph","view":{"handle":handle(&root),"classifier":{"id":"native","version":"1"},"selectors":[],"attachments":[]},"thread_ids":[],"roots":[],"direct_paths":[],"deadline_unix_ms":template["deadline_unix_ms"],"limits":template["limits"]}), &owner, &Cancellation::default()),
16380
+ &owner,
16381
+ );
16382
+ assert_eq!(graph["status"].as_str(), Some("ok"), "{graph:?}");
16383
+ let before = store.description_builds.load(Ordering::Relaxed);
16384
+ for index in 0..24 {
16385
+ let request = prepared_graph_request(
16386
+ &graph,
16387
+ &template,
16388
+ json!([]),
16389
+ json!({"kind":"has_skill","values":[format!("skill-{index}")],"subagents":true}),
16390
+ None,
16391
+ );
16392
+ let answer = single_step(store.request(&request, &owner, &Cancellation::default()));
16393
+ assert_eq!(value_of(&answer), Some(index == 4), "{answer:?}");
16394
+ }
16395
+ assert_eq!(store.description_builds.load(Ordering::Relaxed), before);
16396
+ let (_, description) = store
16397
+ .pin_scope_for_work(
16398
+ &handle(&root),
16399
+ &owner,
16400
+ template["deadline_unix_ms"].as_u64().unwrap(),
16401
+ )
16402
+ .unwrap();
16403
+ assert_eq!(store.description_builds.load(Ordering::Relaxed), before + 1);
16404
+ assert_eq!(
16405
+ description["classifier"],
16406
+ json!({"id":"native","version":"1"})
16407
+ );
16408
+ assert_eq!(description["model"].as_str(), Some("m"));
16409
+ assert_eq!(
16410
+ description["canonical_path"].as_str(),
16411
+ std::fs::canonicalize(&source.path).unwrap().to_str()
16412
+ );
16413
+ let later = json!({"type":"assistant","uuid":"later","sessionId":"s","timestamp":"2026-01-02T03:04:07Z","message":{"model":"later","content":[]}});
16414
+ std::fs::OpenOptions::new()
16415
+ .append(true)
16416
+ .open(&source.path)
16417
+ .unwrap()
16418
+ .write_all(format!("{later}\n").as_bytes())
16419
+ .unwrap();
16420
+ let newer = finish(
16421
+ &store,
16422
+ store.request(&acquire(&source.path), &owner, &Cancellation::default()),
16423
+ &owner,
16424
+ );
16425
+ assert_eq!(
16426
+ store.pin(&handle(&root), &owner).unwrap().model(),
16427
+ Some("m")
16428
+ );
16429
+ assert_eq!(
16430
+ store.pin(&handle(&newer), &owner).unwrap().model(),
16431
+ Some("later")
16432
+ );
16433
+ store.assert_conserved();
16434
+ }
16435
+
16147
16436
  #[test]
16148
16437
  fn selected_root_slices_answer_has_override_within_their_view() {
16149
16438
  let source = Source::new(&format!(