virtual-context 0.4.4__tar.gz → 0.5__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 (469) hide show
  1. {virtual_context-0.4.4 → virtual_context-0.5}/.gitignore +5 -0
  2. virtual_context-0.5/OVERNIGHT_ASSUMPTIONS.md +92 -0
  3. {virtual_context-0.4.4 → virtual_context-0.5}/PKG-INFO +2 -2
  4. {virtual_context-0.4.4 → virtual_context-0.5}/README.md +1 -1
  5. {virtual_context-0.4.4 → virtual_context-0.5}/pyproject.toml +2 -1
  6. {virtual_context-0.4.4 → virtual_context-0.5}/tests/REGRESSION_MAP.md +200 -0
  7. {virtual_context-0.4.4 → virtual_context-0.5}/tests/conftest.py +7 -0
  8. virtual_context-0.5/tests/fixtures/reserved_seats_diag_ctx.json +72 -0
  9. virtual_context-0.5/tests/fixtures/target_baseline_redis.json +3 -0
  10. virtual_context-0.5/tests/pg_helpers.py +59 -0
  11. virtual_context-0.5/tests/test_actor_attribution.py +937 -0
  12. virtual_context-0.5/tests/test_actor_backfills.py +394 -0
  13. virtual_context-0.5/tests/test_actor_card_assembly.py +352 -0
  14. virtual_context-0.5/tests/test_actor_cards.py +3576 -0
  15. virtual_context-0.5/tests/test_actor_cards_postgres.py +486 -0
  16. virtual_context-0.5/tests/test_actor_fact_attribution.py +91 -0
  17. virtual_context-0.5/tests/test_actor_id_normalization_postgres.py +190 -0
  18. virtual_context-0.5/tests/test_actor_identity.py +516 -0
  19. virtual_context-0.5/tests/test_admin_authority_catalogue.py +74 -0
  20. virtual_context-0.5/tests/test_admin_backfill_session_state_markers.py +313 -0
  21. virtual_context-0.5/tests/test_anchor_concurrency_postgres.py +367 -0
  22. virtual_context-0.5/tests/test_backfill_sender_profiles.py +136 -0
  23. virtual_context-0.5/tests/test_backlog_sweeper_claim.py +459 -0
  24. virtual_context-0.5/tests/test_backlog_sweeper_claim_postgres.py +134 -0
  25. virtual_context-0.5/tests/test_backlog_sweeper_detection.py +451 -0
  26. virtual_context-0.5/tests/test_backlog_sweeper_detection_postgres.py +162 -0
  27. virtual_context-0.5/tests/test_backlog_sweeper_dispatch.py +372 -0
  28. virtual_context-0.5/tests/test_backlog_sweeper_phase2_helpers.py +306 -0
  29. virtual_context-0.5/tests/test_canonical_admission_parity.py +129 -0
  30. virtual_context-0.5/tests/test_canonical_resequence.py +131 -0
  31. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_canonical_row_epoch_guards_postgres.py +6 -5
  32. virtual_context-0.5/tests/test_canonical_turn_id_stamping.py +198 -0
  33. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_canonical_turns_schema_postgres.py +5 -4
  34. virtual_context-0.5/tests/test_channel_attribution.py +1116 -0
  35. virtual_context-0.5/tests/test_channel_attribution_postgres.py +413 -0
  36. virtual_context-0.5/tests/test_channel_backfill.py +724 -0
  37. virtual_context-0.5/tests/test_channel_quote_search.py +666 -0
  38. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_claim_compaction_lease_signature_postgres.py +5 -4
  39. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_cleanup_abandoned_compaction.py +11 -0
  40. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_cleanup_abandoned_compaction_postgres.py +44 -27
  41. virtual_context-0.5/tests/test_cli.py +91 -0
  42. virtual_context-0.5/tests/test_compaction_begin_with_lock.py +358 -0
  43. virtual_context-0.5/tests/test_compaction_c2r_gate.py +474 -0
  44. virtual_context-0.5/tests/test_compaction_caller_wiring.py +848 -0
  45. virtual_context-0.5/tests/test_compaction_cleanup_extension.py +393 -0
  46. virtual_context-0.5/tests/test_compaction_cleanup_extension_postgres.py +214 -0
  47. virtual_context-0.5/tests/test_compaction_drain_fence.py +280 -0
  48. virtual_context-0.5/tests/test_compaction_fence_inspector.py +762 -0
  49. virtual_context-0.5/tests/test_compaction_fence_modes.py +1017 -0
  50. virtual_context-0.5/tests/test_compaction_fence_schema.py +287 -0
  51. virtual_context-0.5/tests/test_compaction_fence_schema_postgres.py +284 -0
  52. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_compaction_lifecycle_wiring.py +4 -1
  53. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_compaction_operation_crud_postgres.py +6 -5
  54. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_compaction_operation_postgres.py +5 -4
  55. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_compaction_ownership_guards.py +29 -21
  56. virtual_context-0.5/tests/test_compaction_per_write_fence.py +899 -0
  57. virtual_context-0.5/tests/test_compaction_per_write_fence_postgres.py +654 -0
  58. virtual_context-0.5/tests/test_compaction_pipeline_timestamps.py +39 -0
  59. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_compaction_progress_event.py +5 -1
  60. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_compaction_resume_e2e_postgres.py +15 -10
  61. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_compaction_resume_schema_migration_postgres.py +17 -16
  62. virtual_context-0.5/tests/test_compactor.py +607 -0
  63. virtual_context-0.5/tests/test_compactor_fallback_characterization.py +235 -0
  64. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_composite_store.py +28 -1
  65. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_config.py +135 -0
  66. virtual_context-0.5/tests/test_context_hint_prewarm.py +384 -0
  67. virtual_context-0.5/tests/test_context_hint_prewarm_postgres.py +254 -0
  68. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_conversation_identity.py +41 -0
  69. virtual_context-0.5/tests/test_conversation_lifecycle.py +131 -0
  70. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_conversation_schema_postgres.py +4 -3
  71. virtual_context-0.5/tests/test_conversation_unification_admin.py +603 -0
  72. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_conversations_tenant_id_backfill_postgres.py +7 -6
  73. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_conversations_tenant_id_propagation.py +7 -2
  74. virtual_context-0.5/tests/test_embedding_context_guard.py +301 -0
  75. virtual_context-0.5/tests/test_embedding_disablement.py +180 -0
  76. virtual_context-0.5/tests/test_embedding_reserved_seats.py +182 -0
  77. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_engine_alias_resolution.py +277 -0
  78. virtual_context-0.5/tests/test_engine_init_tier1.py +170 -0
  79. virtual_context-0.5/tests/test_engine_state_schema_postgres.py +168 -0
  80. virtual_context-0.5/tests/test_fact_dense_retrieval.py +1136 -0
  81. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_fact_link_checker.py +7 -1
  82. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_find_quote.py +3 -0
  83. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_handle_prepare_payload.py +17 -1
  84. virtual_context-0.5/tests/test_hint_strict_budget.py +59 -0
  85. virtual_context-0.5/tests/test_ingest_anchor_incremental.py +629 -0
  86. virtual_context-0.5/tests/test_ingest_audience_attribution.py +244 -0
  87. virtual_context-0.5/tests/test_ingest_projected_rows.py +532 -0
  88. virtual_context-0.5/tests/test_ingest_reply_target_embedding.py +57 -0
  89. virtual_context-0.5/tests/test_ingest_single_tail_pair.py +247 -0
  90. virtual_context-0.5/tests/test_ingest_sort_key_rebalance.py +307 -0
  91. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_ingestion_episode_crud_postgres.py +5 -4
  92. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_ingestion_episode_postgres.py +10 -6
  93. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_lifecycle_epoch_store_postgres.py +9 -8
  94. virtual_context-0.5/tests/test_link_predecessor.py +207 -0
  95. virtual_context-0.5/tests/test_noncontiguous_compaction_provenance.py +262 -0
  96. virtual_context-0.5/tests/test_normalize_messages_preserves_provider.py +213 -0
  97. virtual_context-0.5/tests/test_normalize_request_turn_collision.py +243 -0
  98. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_openrouter_provider.py +36 -0
  99. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_paging.py +25 -0
  100. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_passthrough_filter.py +1 -0
  101. virtual_context-0.5/tests/test_passthrough_gate_criterion.py +282 -0
  102. virtual_context-0.5/tests/test_pg_helpers.py +46 -0
  103. virtual_context-0.5/tests/test_postgres_fleet_sentinel.py +218 -0
  104. virtual_context-0.5/tests/test_postgres_mirror_store_methods.py +225 -0
  105. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_postgres_store.py +97 -0
  106. virtual_context-0.5/tests/test_presence_flag_postgres.py +144 -0
  107. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_progress_snapshot_postgres.py +12 -11
  108. virtual_context-0.5/tests/test_protected_window_gate.py +858 -0
  109. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_proxy.py +439 -4
  110. virtual_context-0.5/tests/test_proxy_metrics_event_cap.py +65 -0
  111. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_proxy_session.py +49 -3
  112. virtual_context-0.5/tests/test_query_facts_speaker_selection.py +160 -0
  113. virtual_context-0.5/tests/test_quoted_reference_carrier.py +159 -0
  114. virtual_context-0.5/tests/test_recent_conversation_assembly.py +763 -0
  115. virtual_context-0.5/tests/test_recent_conversation_delivery_metadata.py +280 -0
  116. virtual_context-0.5/tests/test_reconcile_lock_timeout.py +96 -0
  117. virtual_context-0.5/tests/test_reindex_canonical_turn_embeddings.py +262 -0
  118. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_request_metadata_postgres.py +6 -5
  119. virtual_context-0.5/tests/test_resummarize_cmd.py +259 -0
  120. virtual_context-0.5/tests/test_resummarize_postgres.py +160 -0
  121. virtual_context-0.5/tests/test_retag_canonical_turns.py +296 -0
  122. virtual_context-0.5/tests/test_retrieval_assembler_post_compaction.py +395 -0
  123. virtual_context-0.5/tests/test_retrieval_assembler_protected_window_merge.py +628 -0
  124. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_retriever.py +77 -0
  125. virtual_context-0.5/tests/test_schema_bootstrap_postgres.py +57 -0
  126. virtual_context-0.5/tests/test_segment_repair.py +139 -0
  127. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_segmenter.py +97 -7
  128. virtual_context-0.5/tests/test_sender_attribution.py +636 -0
  129. virtual_context-0.5/tests/test_sender_attribution_postgres.py +187 -0
  130. virtual_context-0.5/tests/test_sender_backfill.py +347 -0
  131. virtual_context-0.5/tests/test_sender_quote_search.py +252 -0
  132. virtual_context-0.5/tests/test_sender_summarization.py +313 -0
  133. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_session_state.py +44 -0
  134. virtual_context-0.5/tests/test_session_state_provider_get_marker.py +88 -0
  135. virtual_context-0.5/tests/test_sort_key_shift_postgres.py +141 -0
  136. virtual_context-0.5/tests/test_speaker_acceptance.py +313 -0
  137. virtual_context-0.5/tests/test_speaker_actor_secrecy.py +186 -0
  138. virtual_context-0.5/tests/test_speaker_affinity.py +402 -0
  139. virtual_context-0.5/tests/test_speaker_context_threading.py +725 -0
  140. virtual_context-0.5/tests/test_speaker_gate_routing.py +143 -0
  141. virtual_context-0.5/tests/test_speaker_handle_schema.py +138 -0
  142. virtual_context-0.5/tests/test_speaker_handles.py +457 -0
  143. virtual_context-0.5/tests/test_speaker_labels.py +510 -0
  144. virtual_context-0.5/tests/test_speaker_only.py +425 -0
  145. virtual_context-0.5/tests/test_speaker_quote_annotation.py +292 -0
  146. virtual_context-0.5/tests/test_speaker_quote_dedupe.py +195 -0
  147. virtual_context-0.5/tests/test_speaker_request_context.py +249 -0
  148. virtual_context-0.5/tests/test_speaker_result_serialization.py +565 -0
  149. virtual_context-0.5/tests/test_speaker_retrieval_context.py +59 -0
  150. virtual_context-0.5/tests/test_speaker_roster.py +626 -0
  151. virtual_context-0.5/tests/test_speaker_roster_budget.py +542 -0
  152. virtual_context-0.5/tests/test_speaker_semantic_projection.py +431 -0
  153. virtual_context-0.5/tests/test_speaker_storage_provenance.py +435 -0
  154. virtual_context-0.5/tests/test_speaker_store_seams.py +289 -0
  155. virtual_context-0.5/tests/test_sqlite_mirror_store_methods.py +222 -0
  156. virtual_context-0.5/tests/test_sqlite_store.py +32 -0
  157. virtual_context-0.5/tests/test_storage_protocols_cross_channel_mirror.py +90 -0
  158. virtual_context-0.5/tests/test_strict_tagging_tagged_rows.py +262 -0
  159. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_supersession.py +7 -1
  160. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_tag_summary_backfill.py +10 -11
  161. virtual_context-0.5/tests/test_tag_summary_materialization.py +237 -0
  162. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_temporal_resolver.py +0 -1
  163. virtual_context-0.5/tests/test_tool_guard.py +314 -0
  164. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_tool_loop.py +4 -0
  165. virtual_context-0.5/tests/test_turn_tag_index_lru.py +207 -0
  166. virtual_context-0.5/tests/test_turn_tag_index_memory_regression.py +83 -0
  167. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_vcattach.py +191 -0
  168. virtual_context-0.5/tests/test_vcattach_marker_write.py +435 -0
  169. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_vcmerge_body_method.py +395 -0
  170. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_vcmerge_body_method_postgres.py +53 -14
  171. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_vcmerge_schema_postgres.py +9 -8
  172. {virtual_context-0.4.4 → virtual_context-0.5}/virtual-context.yaml.example +20 -0
  173. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/__init__.py +1 -1
  174. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/cli/main.py +1456 -25
  175. virtual_context-0.5/virtual_context/cli/resummarize_cmd.py +540 -0
  176. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/config.py +134 -0
  177. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/conversation_identity.py +9 -0
  178. virtual_context-0.5/virtual_context/core/assembler.py +1261 -0
  179. virtual_context-0.5/virtual_context/core/canonical_resequence.py +202 -0
  180. virtual_context-0.5/virtual_context/core/canonical_turns.py +218 -0
  181. virtual_context-0.5/virtual_context/core/compaction_fence.py +139 -0
  182. virtual_context-0.5/virtual_context/core/compaction_pipeline.py +3823 -0
  183. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/compactor.py +458 -13
  184. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/composite_store.py +846 -16
  185. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/conversation_store.py +1 -0
  186. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/embedding_provider.py +23 -4
  187. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/embedding_tag_generator.py +70 -1
  188. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/exceptions.py +18 -0
  189. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/hint_builder.py +227 -61
  190. virtual_context-0.5/virtual_context/core/ingest_reconciler.py +2177 -0
  191. virtual_context-0.5/virtual_context/core/protected_window.py +654 -0
  192. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/protocols.py +320 -7
  193. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/quote_search.py +451 -24
  194. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/retrieval_assembler.py +499 -33
  195. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/retrieval_scoring.py +138 -8
  196. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/retriever.py +203 -11
  197. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/search_engine.py +50 -1
  198. virtual_context-0.5/virtual_context/core/segment_repair.py +84 -0
  199. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/segmenter.py +34 -10
  200. virtual_context-0.5/virtual_context/core/semantic_search.py +927 -0
  201. virtual_context-0.5/virtual_context/core/speaker_labels.py +367 -0
  202. virtual_context-0.5/virtual_context/core/speaker_roster.py +422 -0
  203. virtual_context-0.5/virtual_context/core/state_recovery.py +327 -0
  204. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/store.py +802 -18
  205. virtual_context-0.5/virtual_context/core/sweeper_backlog.py +172 -0
  206. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/tag_generator.py +13 -0
  207. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/tagging_pipeline.py +558 -21
  208. virtual_context-0.5/virtual_context/core/tool_guard.py +237 -0
  209. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/tool_loop.py +588 -33
  210. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/tool_query.py +33 -2
  211. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/turn_tag_index.py +93 -5
  212. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/engine.py +1811 -51
  213. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/ingest/supersession.py +239 -32
  214. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/mcp/server.py +49 -2
  215. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/providers/base.py +21 -0
  216. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/providers/generic_openai.py +7 -1
  217. virtual_context-0.5/virtual_context/proxy/_envelope.py +398 -0
  218. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/proxy/dashboard.py +30 -2
  219. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/proxy/formats.py +317 -2
  220. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/proxy/handlers.py +183 -27
  221. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/proxy/helpers.py +22 -2
  222. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/proxy/metrics.py +6 -1
  223. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/proxy/server.py +560 -36
  224. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/proxy/session_state.py +166 -16
  225. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/proxy/state.py +336 -82
  226. virtual_context-0.5/virtual_context/proxy/vcattach.py +528 -0
  227. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/storage/falkordb.py +75 -3
  228. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/storage/filesystem.py +118 -4
  229. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/storage/helpers.py +11 -1
  230. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/storage/neo4j.py +75 -3
  231. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/storage/noop_fact_link_store.py +9 -1
  232. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/storage/postgres.py +7258 -764
  233. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/storage/sqlite.py +7096 -904
  234. virtual_context-0.5/virtual_context/types.py +2810 -0
  235. virtual_context-0.4.4/tests/test_compactor.py +0 -295
  236. virtual_context-0.4.4/tests/test_conversation_lifecycle.py +0 -55
  237. virtual_context-0.4.4/virtual_context/core/assembler.py +0 -578
  238. virtual_context-0.4.4/virtual_context/core/canonical_turns.py +0 -96
  239. virtual_context-0.4.4/virtual_context/core/compaction_pipeline.py +0 -1135
  240. virtual_context-0.4.4/virtual_context/core/ingest_reconciler.py +0 -844
  241. virtual_context-0.4.4/virtual_context/core/semantic_search.py +0 -487
  242. virtual_context-0.4.4/virtual_context/proxy/_envelope.py +0 -213
  243. virtual_context-0.4.4/virtual_context/proxy/vcattach.py +0 -203
  244. virtual_context-0.4.4/virtual_context/types.py +0 -1373
  245. {virtual_context-0.4.4 → virtual_context-0.5}/LICENSE +0 -0
  246. {virtual_context-0.4.4 → virtual_context-0.5}/assets/dashboard.png +0 -0
  247. {virtual_context-0.4.4 → virtual_context-0.5}/assets/hero.png +0 -0
  248. {virtual_context-0.4.4 → virtual_context-0.5}/models.yaml +0 -0
  249. {virtual_context-0.4.4 → virtual_context-0.5}/scripts/install.ps1 +0 -0
  250. {virtual_context-0.4.4 → virtual_context-0.5}/scripts/install.sh +0 -0
  251. {virtual_context-0.4.4 → virtual_context-0.5}/tests/docker-compose.test.yml +0 -0
  252. {virtual_context-0.4.4 → virtual_context-0.5}/tests/haiku/__init__.py +0 -0
  253. {virtual_context-0.4.4 → virtual_context-0.5}/tests/haiku/conftest.py +0 -0
  254. {virtual_context-0.4.4 → virtual_context-0.5}/tests/haiku/test_compaction.py +0 -0
  255. {virtual_context-0.4.4 → virtual_context-0.5}/tests/haiku/test_retrieval.py +0 -0
  256. {virtual_context-0.4.4 → virtual_context-0.5}/tests/haiku/test_tagging.py +0 -0
  257. {virtual_context-0.4.4 → virtual_context-0.5}/tests/ollama/__init__.py +0 -0
  258. {virtual_context-0.4.4 → virtual_context-0.5}/tests/ollama/conftest.py +0 -0
  259. {virtual_context-0.4.4 → virtual_context-0.5}/tests/ollama/test_compactor.py +0 -0
  260. {virtual_context-0.4.4 → virtual_context-0.5}/tests/ollama/test_pipeline.py +0 -0
  261. {virtual_context-0.4.4 → virtual_context-0.5}/tests/ollama/test_provider.py +0 -0
  262. {virtual_context-0.4.4 → virtual_context-0.5}/tests/ollama/test_tag_generator.py +0 -0
  263. {virtual_context-0.4.4 → virtual_context-0.5}/tests/proxy/__init__.py +0 -0
  264. {virtual_context-0.4.4 → virtual_context-0.5}/tests/proxy/test_dashboard_cors.py +0 -0
  265. {virtual_context-0.4.4 → virtual_context-0.5}/tests/proxy/test_metrics.py +0 -0
  266. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_active_compaction_op_invariants.py +0 -0
  267. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_admin_backfill_cli.py +0 -0
  268. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_assembler.py +0 -0
  269. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_backend_integration.py +0 -0
  270. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_beam_judge.py +0 -0
  271. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_budget_enforcement.py +0 -0
  272. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_canonical_row_epoch_guards.py +0 -0
  273. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_canonical_turn_grouping.py +0 -0
  274. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_canonical_turns_schema.py +0 -0
  275. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_claim_compaction_lease_signature.py +0 -0
  276. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_cli_init.py +0 -0
  277. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_compacting_phase_takeover.py +0 -0
  278. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_compaction_commit_prune.py +0 -0
  279. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_compaction_heartbeat_sidecar.py +0 -0
  280. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_compaction_lease_lost_abort.py +0 -0
  281. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_compaction_lifecycle.py +0 -0
  282. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_compaction_lifecycle_contract.py +0 -0
  283. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_compaction_operation.py +0 -0
  284. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_compaction_operation_crud.py +0 -0
  285. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_compaction_operation_id_uuid.py +0 -0
  286. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_compaction_resume_schema_migration.py +0 -0
  287. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_compactor_concurrent.py +0 -0
  288. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_context_bleed.py +0 -0
  289. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_conversation_coverage_report.py +0 -0
  290. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_conversation_schema.py +0 -0
  291. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_conversation_scoping.py +0 -0
  292. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_deferred_payload_compaction.py +0 -0
  293. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_delete_resurrect_epoch_races.py +0 -0
  294. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_embedding_tag_generator.py +0 -0
  295. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_empty_turn_skip.py +0 -0
  296. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_engine_event_bus.py +0 -0
  297. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_engine_integration.py +0 -0
  298. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_engine_lifecycle_epoch.py +0 -0
  299. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_engine_lookback.py +0 -0
  300. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_engine_state.py +0 -0
  301. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_engine_sync_turns.py +0 -0
  302. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_fact_enrichment.py +0 -0
  303. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_fact_graph_integration.py +0 -0
  304. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_fact_link_query.py +0 -0
  305. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_fact_link_types.py +0 -0
  306. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_fact_links_sqlite.py +0 -0
  307. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_fact_redesign.py +0 -0
  308. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_filesystem_store_compaction_compat.py +0 -0
  309. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_fill_pass.py +0 -0
  310. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_find_idle_deletable_conversations.py +0 -0
  311. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_find_stale_lease_helpers.py +0 -0
  312. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_format_agnostic.py +0 -0
  313. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_headless.py +0 -0
  314. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_history_filter.py +0 -0
  315. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_idf_retrieval.py +0 -0
  316. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_import_adapters.py +0 -0
  317. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_ingest_index_integrity.py +0 -0
  318. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_ingest_reconciler_append_fastpath.py +0 -0
  319. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_ingest_reconciler_epoch.py +0 -0
  320. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_ingestion_compaction_full_cycle.py +0 -0
  321. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_ingestion_db_sweep_catchup.py +0 -0
  322. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_ingestion_episode.py +0 -0
  323. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_ingestion_episode_crud.py +0 -0
  324. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_ingestion_finalize_self_heal.py +0 -0
  325. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_ingestion_progress_event.py +0 -0
  326. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_ingestion_regression_resume.py +0 -0
  327. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_legacy_ingestion_lease_ownership.py +0 -0
  328. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_lifecycle_epoch.py +0 -0
  329. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_lifecycle_epoch_store.py +0 -0
  330. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_longmemeval_auth.py +0 -0
  331. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_mark_canonical_turns_compacted_all_halves.py +0 -0
  332. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_mcp_server.py +0 -0
  333. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_media.py +0 -0
  334. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_message_filter.py +0 -0
  335. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_metrics_persistence.py +0 -0
  336. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_model_catalog.py +0 -0
  337. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_model_limits.py +0 -0
  338. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_monitor.py +0 -0
  339. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_multi_instance.py +0 -0
  340. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_multi_worker_sliding_window.py +0 -0
  341. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_neo4j_falkordb_compaction_compat.py +0 -0
  342. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_noop_fact_link_store.py +0 -0
  343. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_orphan_compacting_repair.py +0 -0
  344. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_phase_transition_events.py +0 -0
  345. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_presets.py +0 -0
  346. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_prev_context_leak.py +0 -0
  347. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_progress_event_bus.py +0 -0
  348. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_progress_events.py +0 -0
  349. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_progress_snapshot.py +0 -0
  350. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_provider_adapters.py +0 -0
  351. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_proxy_dashboard.py +0 -0
  352. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_proxy_formats.py +0 -0
  353. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_proxy_message_filter.py +0 -0
  354. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_proxy_streaming.py +0 -0
  355. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_raw_content.py +0 -0
  356. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_recall_all.py +0 -0
  357. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_refresh_compaction_heartbeat.py +0 -0
  358. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_registry_lifecycle.py +0 -0
  359. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_request_captures_persistence.py +0 -0
  360. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_request_metadata.py +0 -0
  361. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_rrf_scoring.py +0 -0
  362. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_semantic_search.py +0 -0
  363. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_sender_identity.py +0 -0
  364. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_session_cache.py +0 -0
  365. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_session_date.py +0 -0
  366. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_sqlite_schema_add_column_race.py +0 -0
  367. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_storage_protocols.py +0 -0
  368. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_store_recovery.py +0 -0
  369. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_store_sqlite.py +0 -0
  370. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_stub_turn_handling.py +0 -0
  371. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_supersession_migration.py +0 -0
  372. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_tag_canonicalizer.py +0 -0
  373. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_tag_consolidator.py +0 -0
  374. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_tag_generator.py +0 -0
  375. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_tag_splitter.py +0 -0
  376. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_tagger_loop.py +0 -0
  377. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_telemetry.py +0 -0
  378. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_telemetry_integration.py +0 -0
  379. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_tool_output_interceptor.py +0 -0
  380. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_tool_result_filter.py +0 -0
  381. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_tool_tags.py +0 -0
  382. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_tui.py +0 -0
  383. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_turn_grouping.py +0 -0
  384. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_turn_tag_index.py +0 -0
  385. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_unified_budget.py +0 -0
  386. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_upstream_trim.py +0 -0
  387. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_vcmerge_composite_forwarders.py +0 -0
  388. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_vcmerge_dual_handler_refuse.py +0 -0
  389. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_vcmerge_schema.py +0 -0
  390. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_vcmerge_storage.py +0 -0
  391. {virtual_context-0.4.4 → virtual_context-0.5}/tests/test_verb_expansion.py +0 -0
  392. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/cli/__init__.py +0 -0
  393. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/cli/import_cmd.py +0 -0
  394. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/__init__.py +0 -0
  395. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/alias_resolution.py +0 -0
  396. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/coverage_report.py +0 -0
  397. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/engine_utils.py +0 -0
  398. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/event_bus.py +0 -0
  399. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/fact_query.py +0 -0
  400. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/fts_preprocessor.py +0 -0
  401. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/lifecycle_epoch.py +0 -0
  402. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/llm_utils.py +0 -0
  403. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/math_utils.py +0 -0
  404. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/model_catalog.py +0 -0
  405. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/monitor.py +0 -0
  406. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/paging_manager.py +0 -0
  407. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/progress_events.py +0 -0
  408. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/progress_snapshot.py +0 -0
  409. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/provider_adapters.py +0 -0
  410. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/tag_canonicalizer.py +0 -0
  411. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/tag_consolidator.py +0 -0
  412. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/tag_scoring.py +0 -0
  413. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/tag_splitter.py +0 -0
  414. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/telemetry.py +0 -0
  415. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/core/temporal_resolver.py +0 -0
  416. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/data/anthropic-tokenizer/tokenizer.json +0 -0
  417. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/data/models.yaml +0 -0
  418. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/import_adapters/__init__.py +0 -0
  419. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/import_adapters/base.py +0 -0
  420. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/import_adapters/chatgpt.py +0 -0
  421. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/import_adapters/claude.py +0 -0
  422. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/import_adapters/grok.py +0 -0
  423. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/import_adapters/loader.py +0 -0
  424. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/ingest/__init__.py +0 -0
  425. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/ingest/curator.py +0 -0
  426. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/ingest/date_resolver.py +0 -0
  427. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/ingest/parsers.py +0 -0
  428. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/mcp/__init__.py +0 -0
  429. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/model_limits.py +0 -0
  430. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/openclaw/virtual-context.mjs +0 -0
  431. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/patterns.py +0 -0
  432. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/presets/__init__.py +0 -0
  433. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/presets/agentic.py +0 -0
  434. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/presets/base.py +0 -0
  435. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/presets/coding.py +0 -0
  436. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/providers/__init__.py +0 -0
  437. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/providers/anthropic.py +0 -0
  438. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/providers/ollama_native.py +0 -0
  439. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/proxy/__init__.py +0 -0
  440. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/proxy/dashboard.html +0 -0
  441. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/proxy/media.py +0 -0
  442. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/proxy/message_filter.py +0 -0
  443. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/proxy/multi.py +0 -0
  444. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/proxy/registry.py +0 -0
  445. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/proxy/session_cache.py +0 -0
  446. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/proxy/static/android-chrome-192x192.png +0 -0
  447. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/proxy/static/android-chrome-512x512.png +0 -0
  448. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/proxy/static/apple-touch-icon.png +0 -0
  449. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/proxy/static/favicon-16x16.png +0 -0
  450. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/proxy/static/favicon-32x32.png +0 -0
  451. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/proxy/static/favicon.ico +0 -0
  452. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/proxy/static/site.webmanifest +0 -0
  453. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/proxy/tool_output_interceptor.py +0 -0
  454. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/storage/__init__.py +0 -0
  455. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/token_counter.py +0 -0
  456. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/tui/__init__.py +0 -0
  457. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/tui/app.py +0 -0
  458. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/tui/chat.tcss +0 -0
  459. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/tui/chat_provider.py +0 -0
  460. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/tui/headless.py +0 -0
  461. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/tui/modals/__init__.py +0 -0
  462. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/tui/modals/turn_inspector.py +0 -0
  463. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/tui/state.py +0 -0
  464. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/tui/widgets/__init__.py +0 -0
  465. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/tui/widgets/budget_bar.py +0 -0
  466. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/tui/widgets/chat_view.py +0 -0
  467. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/tui/widgets/input_box.py +0 -0
  468. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/tui/widgets/tag_panel.py +0 -0
  469. {virtual_context-0.4.4 → virtual_context-0.5}/virtual_context/tui/widgets/turn_list.py +0 -0
@@ -60,6 +60,7 @@ extraneous/paper/*.png
60
60
  *.json
61
61
  !pyproject.toml
62
62
  !package.json
63
+ !tests/fixtures/*.json
63
64
  *.autopsy.md
64
65
 
65
66
  # OS
@@ -68,3 +69,7 @@ extraneous/paper/*.png
68
69
 
69
70
  # Riptide artifacts (cloud-synced)
70
71
  .humanlayer/tasks/
72
+
73
+ # Unrelated local content kept in the working tree, never packaged
74
+ discord-native-poker/
75
+ discord-native-poker.zip
@@ -0,0 +1,92 @@
1
+ # Overnight assumptions — cross-channel-mirror engine implementation
2
+
3
+ Branch: `feature/cross-channel-mirror` (off `fix/vcattach-redis-marker-write`).
4
+
5
+ Decisions made without an explicit spec/plan directive. Each entry names the call and the rationale so the reviewer can override quickly.
6
+
7
+ ## 1. Helper-module placement
8
+
9
+ Per spec §2.6 the plan picked `virtual_context/core/protected_window.py`. I added the helpers there as specified (`_merge_protected_window`, `_stamp_canonical_turn_ids`, `_last_already_canonical_turn_number`).
10
+
11
+ Within the merge helper, the `mode == "off"` branch returns `list(payload_history)` (a shallow copy) rather than the input by reference. Mirrors the "side-effect-free" rule from spec §2 even on the no-op path.
12
+
13
+ ## 2. `extract_ingestible_messages` mode parameter — wrapper kwarg
14
+
15
+ `virtual_context/proxy/helpers.py:_extract_ingestible_messages` gained a kw-only `mode: str = "ingest"` that threads through to the underlying `extract_ingestible_messages`. Existing call sites in `proxy/server.py` (5 sites) and `proxy/state.py` (3 sites) do not pass `mode` and therefore inherit the default. No call-site changes needed.
16
+
17
+ The `proxy/formats.py:514` internal self-call now explicitly passes `mode="ingest"` for clarity even though it's the default.
18
+
19
+ ## 3. `_decision` helper in `handle_prepare_payload`
20
+
21
+ The 11 `PhaseDecision(phase=..., started_tagger=...)` return sites inside `handle_prepare_payload` were converted to a local helper `_decision(*, phase, started_tagger)` that closes over the local `canonical_ingest_rows` tuple. Closure uses Python's standard late-binding so the helper reads the current value at call time (after the `ingest_batch` block reassigns it). The original return statements at lines 1230-1445 became `return _decision(...)` calls.
22
+
23
+ The `_decision` body uses an intermediate variable (`_decision_result = PhaseDecision(...)`) so the `return PhaseDecision(...)` text shape only exists once in the helper itself, which let `Edit(replace_all=True)` migrate the 11 return sites by text match without touching the helper body.
24
+
25
+ ## 4. Stamping placement in `proxy/server.py`
26
+
27
+ Per plan §2 Step 7, stamping fires in the ACTIVE-path block at `proxy/server.py:1125-1131`, immediately before the active-tail user message is appended to `state.conversation_history`. Gated on `protected_window_db_source == "merge"` and `_phase_decision.canonical_ingest_rows` being non-empty. Wrapped in a defensive try/except so a stamping failure degrades to Tier 3 fall-through rather than failing the request.
28
+
29
+ Alignment rule (suffix-tail drop on active-tail user) is implemented by calling `_extract_ingestible_messages(body)` + `state._completed_history_messages(...)` and dropping `len(extracted) - len(completed)` rows from the suffix.
30
+
31
+ ## 5. RetrievalAssembler reads `_is_merge_participant` via `getattr(self, ...)`
32
+
33
+ The Tier 1 cached bool lives on the engine. Rather than add a constructor parameter to `RetrievalAssembler.__init__` (which would force every existing test fixture and `__new__` site to pass it), the engine assigns the bool onto the assembler instance after construction:
34
+
35
+ ```python
36
+ self._retrieval._is_merge_participant = self._is_merge_participant
37
+ ```
38
+
39
+ The assembler's gate code reads via `getattr(self, "_is_merge_participant", False)`, so tests that bypass engine construction via `RetrievalAssembler.__new__` (`engine.py:2366`) still work — they default to non-participant.
40
+
41
+ ## 6. Tier 2 INT coercion shape
42
+
43
+ `int(redis_last_turn) == int(anchor_turn)` wrapped in `try/except (TypeError, ValueError)`. A coercion failure (e.g. the marker arrives as a dict instead of an int-coercible scalar) routes to `tier2_legacy_fallthrough` rather than raising. Tests cover both the string-coercible path (`"7"` → 7) and the non-coercible path (dict).
44
+
45
+ ## 7. Engine `__init__` Tier 1 call does NOT catch broad Exception
46
+
47
+ Per plan §2 Step 8 explicit guidance: silently disabling the feature on a store-wrapper failure / missing allowlist / transient DB outage would hide mirror loss as stale-False. Only Tier 0 `off` short-circuits the call; merge mode propagates any exception from `has_any_alias`. This intentionally fails engine construction on misconfigured deploys.
48
+
49
+ ## 8. Tier 1 propagation across re-init paths
50
+
51
+ Engine `__init__` sets `_is_merge_participant` once (line 220 area). The post-resurrect re-init path at engine.py:880+ keeps the same `RetrievalAssembler` instance (lines 941-946 mutate fields in place rather than re-constructing), so the assembler's `_is_merge_participant` attribute survives the re-init unchanged. No re-propagation is needed.
52
+
53
+ The `RetrievalAssembler.__new__` fallback at engine.py:2366 (used by `filter_history` for test stubs) does not exercise `on_message_inbound`, so the gate code is never reached on that path; no Tier 1 propagation is needed there either.
54
+
55
+ ## 9. Observability: dedicated `PROTECTED_WINDOW_GATE` log line
56
+
57
+ Per plan §2 Step 10 + §5.8, the gate observability is rendered INLINE as key=value substrings in the message text (cloud's stdlib-logging formatter drops `extra=`). I added a dedicated `logger.info("PROTECTED_WINDOW_GATE conv=%s ...", ...)` log line right after the existing INBOUND_BREAKDOWN block in `on_message_inbound`. The new line is gated on `_gate_outcome is not None`, so Tier 0 `off` emits nothing.
58
+
59
+ The new line is NOT bound by the existing 500ms INBOUND_BREAKDOWN threshold — it fires on every `on_message_inbound` that runs in merge mode, which is the spec §8 observability contract.
60
+
61
+ `extra=` is included for in-process consumers (tests using `caplog.records[i].my_field` attribute access) but the message text carries the values for grep-based consumption.
62
+
63
+ ## 10. `_gate_budget_evictions` reports 0 in current implementation
64
+
65
+ The merge helper does NOT evict; the downstream context-builder eviction happens later and isn't currently surfaced via a hook to the gate path. The field is emitted as `0` for now; a future change can wire a real counter from the downstream context-builder if metrics analysis surfaces a need.
66
+
67
+ ## 11. Test files use the existing engine_factory pattern but DO NOT exercise filesystem backend for Tier 3
68
+
69
+ `tests/test_engine_init_tier1.py` constructs full engines but seeds aliases via the SQLite path (the only backend that hosts canonical_turns). The filesystem backend's `has_any_alias` is tested at the unit level via `test_storage_protocols_cross_channel_mirror.py` (via SimpleNamespace stub) and is not exercised end-to-end on the engine path.
70
+
71
+ ## 12. Postgres tests skip when `VC_TEST_POSTGRES_URL` is not set
72
+
73
+ `tests/test_postgres_mirror_store_methods.py` mirrors the always-on SQLite suite and uses `pytestmark = pytest.mark.skipif(not PG_URL, reason="VC_TEST_POSTGRES_URL not set")`. The CI environment is expected to set this when Postgres is wired; local dev skips cleanly.
74
+
75
+ ## 13. NO code changes to compaction / tagging / fact-curator / vc_* tools
76
+
77
+ Plan §7 non-goals. Verified by grep — no edits to those modules.
78
+
79
+ ## 14. NO new indexes on conversation_aliases or canonical_turns
80
+
81
+ Plan §1.2 + §7 non-goals. Both Tier 1 OR legs are already indexed (PK on `alias_id` + `idx_conversation_aliases_target_id`). Tier 3 read uses `idx_canonical_turns_conv_order` on `(conversation_id, sort_key)`.
82
+
83
+ ## 15. NO deploy, NO release, NO merge to main
84
+
85
+ User explicitly revoked deploy authority tonight. Per spawn-prompt rule, release-class actions are user-direct only. Branch is pushed to `origin/feature/cross-channel-mirror` and stays there. Next step is for the user to review the diff and decide whether to merge `fix/vcattach-redis-marker-write` to main first, then this branch, OR fold both at once OR roll back.
86
+
87
+ ## Open follow-ups (NOT done tonight — explicit non-action)
88
+
89
+ - Postgres EXPLAIN-plan smoke: I asserted catalog-level index presence in tests rather than `EXPLAIN ANALYZE` plan-node names (per plan §4 risk assessment — query planner choices vary on small test tables).
90
+ - End-to-end harness coverage: the `tests/fixtures/target_baseline.sql` already-shipped baseline can be used by the cloud-side harness to exercise the cross-channel-mirror end-to-end. The cloud-side test authoring is owned by cloud per spec §8.
91
+ - Spec drift on YAML key (`assembler:` vs actual `assembly:`): plan §3.1 documents this as a follow-up amendment to the engine spec + cloud spec. Implementation uses the correct `assembly:` key.
92
+ - `cross-channel-mirror-engine-spec.md` line 261 `cloud-spec §1.1` reference: that section in cloud-spec is currently named `1.4` and `5.5` per plan §3.1. Spec amendment pending.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: virtual-context
3
- Version: 0.4.4
3
+ Version: 0.5
4
4
  Summary: OS-style virtual memory for LLM session context management
5
5
  Project-URL: Homepage, https://virtual-context.com
6
6
  Project-URL: Repository, https://github.com/virtual-context/virtual-context
@@ -51,7 +51,7 @@ Description-Content-Type: text/markdown
51
51
  <!-- [![Python](https://img.shields.io/pypi/pyversions/virtual-context.svg)](https://pypi.org/project/virtual-context/) -->
52
52
  <!-- [![Downloads](https://img.shields.io/pypi/dm/virtual-context.svg)](https://pypistats.org/packages/virtual-context) -->
53
53
  <!-- [![License](https://img.shields.io/badge/license-AGPL--3.0-blue.svg)](https://github.com/yursilkidwai/virtual-context/blob/main/LICENSE) -->
54
- [![Discord](https://img.shields.io/badge/Discord-Chat%20with%20us-5865F2?logo=discord&logoColor=white)](https://discord.gg/YxDHKEZz)
54
+ [![Discord](https://img.shields.io/badge/Discord-Chat%20with%20us-5865F2?logo=discord&logoColor=white)](https://discord.gg/kGJva2D8Ej)
55
55
  [![Twitter](https://img.shields.io/badge/Twitter-Follow-1DA1F2?logo=x&logoColor=white)](https://x.com/virtualctx)
56
56
 
57
57
  <p align="center">
@@ -2,7 +2,7 @@
2
2
  <!-- [![Python](https://img.shields.io/pypi/pyversions/virtual-context.svg)](https://pypi.org/project/virtual-context/) -->
3
3
  <!-- [![Downloads](https://img.shields.io/pypi/dm/virtual-context.svg)](https://pypistats.org/packages/virtual-context) -->
4
4
  <!-- [![License](https://img.shields.io/badge/license-AGPL--3.0-blue.svg)](https://github.com/yursilkidwai/virtual-context/blob/main/LICENSE) -->
5
- [![Discord](https://img.shields.io/badge/Discord-Chat%20with%20us-5865F2?logo=discord&logoColor=white)](https://discord.gg/YxDHKEZz)
5
+ [![Discord](https://img.shields.io/badge/Discord-Chat%20with%20us-5865F2?logo=discord&logoColor=white)](https://discord.gg/kGJva2D8Ej)
6
6
  [![Twitter](https://img.shields.io/badge/Twitter-Follow-1DA1F2?logo=x&logoColor=white)](https://x.com/virtualctx)
7
7
 
8
8
  <p align="center">
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "virtual-context"
7
- version = "0.4.4"
7
+ version = "0.5"
8
8
  description = "OS-style virtual memory for LLM session context management"
9
9
  readme = "README.md"
10
10
  license = "AGPL-3.0-or-later"
@@ -77,6 +77,7 @@ exclude = [
77
77
  ".github/",
78
78
  ".gitignore",
79
79
  ".worktrees/",
80
+ "tests/fixtures/",
80
81
  ]
81
82
  artifacts = [
82
83
  "virtual_context/proxy/static/*",
@@ -374,6 +374,190 @@ Use `pytest -m regression` to run all regression tests.
374
374
  - `test_conversation_scoping.py::TestEndToEndScoping::test_find_quote_scoped_no_cross_conversation_leaks`
375
375
  - `test_conversation_scoping.py::TestEndToEndScoping::test_alias_ride_along_scoped`
376
376
 
377
+ ### BUG-036 — Sort-key gap exhaustion collides with boundary rows
378
+
379
+ - **Symptom**: Prepare-payload ingest aborts with `UNIQUE (conversation_id, sort_key)` violation (Postgres UniqueViolation / SQLite IntegrityError) on mid-insertion into a conversation whose insertion-point rows sit ≤0.002 apart; large mid-inserts also silently mis-order rows past the right boundary.
380
+ - **Root cause**: `IngestReconciler._allocate_sort_keys` clamped its step to 0.001 when the bounded gap was too tight, letting allocated keys land ON or PAST `right_key`. Self-priming: the first clamped allocation writes 0.001-spaced rows, after which every insertion between them collides deterministically.
381
+ - **Fix**: Bounded allocation signals exhaustion (returns `None`) instead of clamping, with a float-precision strict-interior guard. The reconciler then opens the gap via `shift_canonical_turn_sort_keys` (single UPDATE whose delta exceeds the shifted range's spread — transient-collision-safe in any row visit order) and re-allocates; per-row descending fallback for stores without the bulk helper.
382
+ - **Tests**:
383
+ - `test_ingest_sort_key_rebalance.py::TestAllocatorBoundedGap` (exhaustion signaling)
384
+ - `test_ingest_sort_key_rebalance.py::TestMidInsertRebalance` (end-to-end rebalance, self-priming loop, rows_touched consistency, per-row fallback)
385
+ - `test_ingest_sort_key_rebalance.py::TestShiftHelperSQLite`
386
+ - `test_sort_key_shift_postgres.py::TestShiftHelperPostgres` / `TestMidInsertRebalancePostgres`
387
+
388
+ ### BUG-038 — Strict tagging aborts on concurrently-tagged rows
389
+
390
+ - **Symptom**: `RuntimeError: strict canonical tagging expected at least N covered ingestible entries, found M across K rows` on catch-up ingestion of a multi-worker conversation; the whole batch falls through to the row-based DB sweep, losing payload-context tagging for the still-untagged rows.
391
+ - **Root cause**: The strict precondition in `ingest_history` required every payload entry to map to an UNTAGGED canonical row. Rows legitimately tagged between the prepare and the follow-up pass — by another worker's row sweep or a prior pass over an overlapping payload — broke the count.
392
+ - **Fix**: The precondition counts coverage over the conversation's row tail INCLUDING tagged rows. The pair walker gains a hydrate fast-path: pairs whose backing rows are all tagged (matched by per-message `turn_hash`) get their TurnTagIndex entries from the stored row tags, consuming the strict cursor without invoking the tag generator or rewriting rows. Half-tagged pairs fall through to the normal tagger (idempotent). Supporting fix: the canonical-turn full-row loaders now SELECT `covered_ingestible_entries` so legacy combined rows (coverage 2) count correctly.
393
+ - **Tests**: `test_strict_tagging_tagged_rows.py` — prod-signature repro, hydration tag fidelity, zero-tagger-call full hydration, untagged-tail still tagged, half-tagged fall-through, missing-rows and stale-epoch invariants preserved.
394
+
395
+ ### BUG-048 — Reconciliation loads every column of every stored turn, including the text it never reads
396
+
397
+ - **Symptom**: Ingest cost scales with stored conversation size and is independent of request size, and the per-row cost grows with row width rather than staying flat. A no-content projection of the same rows measures flat at 4.2-5.6 us/row across a 7x range in conversation size while the full row goes 22.9 to 95.2 us/row over the same range.
398
+ - **Root cause**: `IngestReconciler` loaded stored history through `get_all_canonical_turns`, which selects every column. Reconciliation keys on `turn_hash`, `sort_key` and identity/provenance columns, and never reads a stored row's text; the only thing it asks of the content is whether a row carries user text at all, as a role gate before taking speaker attribution. The content columns are the widest part of a row and are stored out of line, so on Postgres they dominate a load that never uses them.
399
+ - **Fix**: New `CanonicalTurnReconcileRow` and `get_canonical_turn_reconcile_rows` on both backends, selecting only the reconciliation columns plus `has_user_content` / `has_assistant_content` flags. All three reconciler loads go through `_load_reconcile_rows`, which falls back to the full load when a backend returns None; None is distinguished from an empty list because reading "cannot project" as "no rows" would reconcile every payload against no history. It is deliberately a separate type rather than a `CanonicalTurnRow` with empty text, so a projected row reaching a write path raises instead of blanking stored content, which is what the sort-key rebalance fallback surfaced. `exact_resend` now returns the prepared rows carrying the stored ordinal rather than the stored rows themselves, so `CanonicalIngestResult.rows` is one type on every path.
400
+ - **Tests**:
401
+ - `test_ingest_projected_rows.py::test_presence_flag_agrees_with_python_strip` (the flag and `(value or "").strip()` must never disagree, including whitespace-only content; the default single-argument TRIM strips spaces only and reports a newline-only row as carrying content)
402
+ - `test_ingest_projected_rows.py::test_attribution_still_persists_through_the_projection` (a late-derived sender still reaches storage through the role gate)
403
+ - `test_ingest_projected_rows.py::test_projection_preserves_every_field_the_enrichment_merge_reads` (each merge-read column carries a distinguishing value, so a blanked projection is caught)
404
+ - `test_ingest_projected_rows.py::test_result_rows_never_carry_a_projected_row` (across exact_resend, append and interior_overlap)
405
+ - `test_ingest_projected_rows.py::test_exact_resend_rows_carry_the_stored_ordinal`
406
+ - `test_ingest_projected_rows.py::test_projected_row_cannot_be_written_back`
407
+ - `test_ingest_projected_rows.py::test_unsupported_backend_falls_back_to_the_full_load`
408
+
409
+ ### BUG-047 — Anchor refresh rewrites the whole conversation's anchor table on every ingest
410
+
411
+ - **Symptom**: Ingest cost grows linearly with stored conversation size and is independent of what the caller sent. A 15-message request against a 9,173-row conversation costs the same as a 490 KB one. On the largest production conversation the prepare path exceeded the client's timeout often enough that a quarter of prepares were abandoned before a response was sent.
412
+ - **Root cause**: `_refresh_persisted_anchors` rebuilt the complete anchor set on every call and persisted it through `replace_canonical_turn_anchors`, which is a `DELETE` of every anchor row for the conversation followed by a re-INSERT of the entire rebuilt set. The set holds one row per window start per window size (3, 4, 5), so roughly 3N rows are deleted and 3N re-inserted per ingest. Appending a turn creates exactly one new window start per window size and invalidates none, so nearly the whole rewrite was writing back byte-identical rows. Measured on a 9,200-row conversation: 27,597 anchor rows rewritten, of which 6 were new and 0 removed. Both ingest fast paths (`exact_resend`, `tail_append`) paid it too, and on `exact_resend` the anchor set is provably unchanged, making the entire rewrite dead work.
413
+ - **Fix**: `_build_anchor_rows` extracted so the rebuild and the delta derive their sets with one ruler. `_refresh_persisted_anchors` accepts the row sequence persisted before the ingest, derives the required anchor set for both sequences in memory, and writes only the difference through the new `apply_canonical_turn_anchor_delta` store surface, which deletes and inserts exact `(window_size, anchor_hash, start_turn_id)` triples scoped to the conversation. Because the delta is derived from the caller's prior sequence rather than from a read of stored anchors, it is applied only when `count_canonical_turn_anchors` agrees with the derived prior set; any disagreement (anchors never built, a torn write, a concurrent writer) falls back to the full rebuild, preserving the self-healing property the unconditional rebuild provided. Callers that cannot name the prior sequence keep the rebuild.
414
+ - **Tests**:
415
+ - `test_ingest_anchor_incremental.py::test_incremental_delta_matches_full_rebuild` (the identity property across append, interior insert, prefix insert, in-place modification, interior removal, tail shrink, shrink below window size, and wholesale replacement)
416
+ - `test_ingest_anchor_incremental.py::test_incremental_result_is_order_independent` (both temporal orderings converge on the canonical rebuild)
417
+ - `test_ingest_anchor_incremental.py::test_append_writes_bounded_anchor_rows_not_whole_conversation` (an append writes 3 anchors regardless of conversation length; a full-conversation rewrite fails this)
418
+ - `test_ingest_anchor_incremental.py::test_unchanged_sequence_writes_nothing` (a resend that changes no row issues no anchor write)
419
+ - `test_ingest_anchor_incremental.py::test_diverged_store_falls_back_to_full_rebuild`
420
+ - `test_ingest_anchor_incremental.py::test_anchor_hashes_still_resolve_windows_after_delta` (post-delta digests still map to their start row, and superseded digests do not survive)
421
+ - `test_ingest_anchor_incremental.py::test_sqlite_delta_write_matches_rebuild_write` (the SQL delta leaves the same table state as the wholesale replace)
422
+ ### BUG-044 — Compaction offset splits a recovered cross-channel turn
423
+
424
+ - **Symptom**: A temporary reply preference authored in one Discord guild
425
+ channel is acknowledged there but ignored in another channel. The outbound
426
+ recent-conversation block contains only the assistant acknowledgement, not
427
+ the authoritative user instruction.
428
+ - **Root cause**: Tier 3 inserts `source=db_recent` rows into channel-local
429
+ payload history before `history_offset()` is applied. The payload-owned
430
+ compaction offset is then used as a raw index into the merged list, so it can
431
+ consume the user half of a recovered logical group while leaving the
432
+ assistant half.
433
+ - **Fix**: Build the uncompacted view with a source-aware, active-tail-safe
434
+ slice: consume the offset from payload-owned rows only, never from DB-recent
435
+ rows or the trailing unstamped active-user block.
436
+ - **Tests**:
437
+ - `test_protected_window_gate.py::test_tier3_db_pair_survives_payload_compaction_offset`
438
+
439
+ ### BUG-045 — Payload dedup suppresses the canonical user before offset
440
+
441
+ - **Symptom**: A guild-wide temporary reply preference is acknowledged in its
442
+ source Discord channel but ignored in another channel. Canonical storage
443
+ contains the correctly attributed user instruction and assistant
444
+ acknowledgement, yet the outbound `<recent-conversation>` contains only the
445
+ assistant acknowledgement.
446
+ - **Root cause**: Tier 3 dedup ran before the channel-local payload watermark.
447
+ The payload user suppressed its canonical duplicate by
448
+ `source_message_id`, then `history_offset()` removed the payload copy. The
449
+ unkeyed canonical assistant half survived alone. Separately, the Tier 3
450
+ store query limited physical rows and could split a logical group at the
451
+ oldest window boundary.
452
+ - **Fix**: Slice payload-owned history before canonical merge, force Tier 3
453
+ when a nonzero payload offset makes a Tier-2 equality skip unsafe, and cache
454
+ that exact merged view for paging reassembly. Dedup uses adjacency-scoped
455
+ logical groups so reused historical group numbers cannot suppress unrelated
456
+ turns; incomplete payload fragments are replaced by the complete canonical
457
+ group, then recovered groups are interleaved ahead of newer stamped payload
458
+ turns. Legacy adjacency is never accepted as channel/audience provenance
459
+ proof. A failed/empty canonical read preserves the unsliced payload. SQLite
460
+ and Postgres select the newest N logical groups and return every physical
461
+ row in each.
462
+ - **Tests**:
463
+ - `test_protected_window_gate.py::test_tier3_payload_duplicate_cannot_suppress_db_user_before_offset`
464
+ - `test_protected_window_gate.py::test_tier2_equal_with_payload_offset_forces_canonical_replacement`
465
+ - `test_protected_window_gate.py::test_tier3_read_failure_preserves_unsliced_payload`
466
+ - `test_retrieval_assembler_protected_window_merge.py::test_merge_source_match_suppresses_entire_split_db_group`
467
+ - `test_retrieval_assembler_protected_window_merge.py::test_merge_replaces_incomplete_payload_fragment_with_canonical_pair`
468
+ - `test_retrieval_assembler_protected_window_merge.py::test_merge_replacement_keeps_canonical_group_before_newer_payload`
469
+ - `test_retrieval_assembler_protected_window_merge.py::test_merge_complete_payload_pair_can_span_tool_scaffolding`
470
+ - `test_retrieval_assembler_protected_window_merge.py::test_merge_dedup_does_not_conflate_reused_group_numbers`
471
+ - `test_retrieval_assembler_protected_window_merge.py::test_merge_legacy_adjacency_does_not_inherit_channel_provenance`
472
+ - `test_sqlite_mirror_store_methods.py::test_get_recent_limit_preserves_complete_split_groups`
473
+ - `test_sqlite_mirror_store_methods.py::test_get_recent_limit_does_not_conflate_reused_group_number`
474
+ - `test_postgres_mirror_store_methods.py::test_get_recent_limit_preserves_complete_split_groups`
475
+ - `test_postgres_mirror_store_methods.py::test_get_recent_limit_does_not_conflate_reused_group_number`
476
+ - `test_recent_conversation_assembly.py::test_budget_does_not_conflate_reused_raw_group_numbers`
477
+ - `test_recent_conversation_assembly.py::test_budget_legacy_fallback_evicts_only_leading_contiguous_group`
478
+
479
+ ### BUG-046 — Retained peer-channel history suppresses the canonical copy
480
+
481
+ - **Symptom**: The complete newest preference pair is correct in canonical
482
+ storage but absent from the next channel's `<recent-conversation>`. Older
483
+ groups render normally, and the preference continues to work only in the
484
+ channel where it was authored.
485
+ - **Root cause**: A unified guild reuses one in-memory engine history across
486
+ channels, while Discord sends channel-local model payloads. Tier 3 treated
487
+ any matching retained engine row as proof that the current payload already
488
+ carried the canonical turn, so the immediately preceding peer-channel group
489
+ was suppressed even though it was not model-visible. An active-only ingest
490
+ result could also suffix-stamp that current row's identity onto older
491
+ completed history when the active-tail drop equaled the entire result.
492
+ - **Fix**: When a request has a proved origin channel, only retained rows from
493
+ that exact channel may suppress a canonical copy; production nested channel
494
+ metadata is read through `get_origin_channel`, with the DB-recent top-level
495
+ shape as fallback. Active-tail ingest rows are removed with an empty-safe
496
+ helper before completed-history stamping. Requests without a proved channel
497
+ retain legacy dedup behavior.
498
+ - **Tests**:
499
+ - `test_protected_window_gate.py::test_other_channel_engine_history_cannot_suppress_canonical_recent_pair`
500
+ - `test_protected_window_gate.py::test_same_channel_payload_twin_still_suppresses_canonical_copy`
501
+ - `test_protected_window_gate.py::test_same_channel_active_tail_race_still_suppresses_db_user`
502
+ - `test_canonical_turn_id_stamping.py::test_drop_active_tail_equal_to_all_rows_returns_empty`
503
+ - `test_canonical_turn_id_stamping.py::test_drop_active_tail_keeps_completed_prefix`
504
+ - `test_canonical_turn_id_stamping.py::test_drop_active_tail_zero_is_identity_view`
505
+
506
+ ### BUG-043 — RRF's missing-signal penalty buries embedding-only candidates below the fused top-K
507
+
508
+ - **Symptom**: On analog queries with no keyword overlap, the tag the embedding signal surfaces most strongly lands far outside the fused top-K (embedding candidates observed at fused rank 15-33 while the selection cut is 10), because RRF penalizes every candidate absent from the idf/bm25 signals. The context-augmented embedding signal (BUG-042) surfaces the right tag but fusion then discards it.
509
+ - **Root cause**: `score_candidates` fuses three ranked signals with a missing-signal penalty rank; a strong embedding-only candidate carries penalty ranks for idf and bm25 and cannot reach the top-K on the embedding weight alone.
510
+ - **Fix**: New `retrieval.scoring.embedding_reserved_seats` (default 0 = byte-identical legacy). After fusion/dampening/boost and before the top-K cut, `apply_embedding_reserved_seats` seats the top-N embedding-signal candidates (raw pre-gravity embedding order) that are outside the fused top-K (K = strategy `max_results`) by raising their fused scores into the gap between the last surviving entry and the first displaced one, so the retriever's score-descending cut yields surviving + reserved. Only the seated tags' scores change; a degenerate boundary tie nudges the displaced entries down. Composes with BUG-042.
511
+ - **Tests**:
512
+ - `test_embedding_reserved_seats.py::TestReservedSeatMath` (seating parity vs the rule on a synthetic fixture and an anonymized real prod fused/embedding dump; lowest-of-top-K displaced)
513
+ - `test_embedding_reserved_seats.py::TestLegacyByteIdentical` (N=0 no mutation, everything-already-fits, embedding-already-in-top-K, defaults)
514
+ - `test_embedding_reserved_seats.py::TestNoGoldDisplacement` (buried gold seated without evicting gold already in top-K; seated score lands between survivor and displaced)
515
+ - `test_embedding_reserved_seats.py::TestConfigPlumbing`
516
+
517
+ ### BUG-042 — Under-specified analog queries miss the relevant tag on the bare embedding signal
518
+
519
+ - **Symptom**: The embedding retrieval signal scores only the bare inbound message against tag-summary vectors, so an under-specified query whose surface words name neither the gold tag nor the summary's distinctive nouns ("what should I get her for her birthday") fails to surface the relevant tag even when the elided topic sits in the immediately preceding turns.
520
+ - **Root cause**: `compute_embedding_candidates` had one query vector — the bare message embedding produced by the inbound embedding tagger, which discards the conversational context it receives. Recent-turn context that would disambiguate the query was never folded into signal 3.
521
+ - **Fix**: New `retrieval.scoring.embedding_context_turns` (default 0 = byte-identical legacy) blends the last N conversational turns + the current message into a second query embedding on the same encoder. `embedding_context_guard` (default true) takes the per-tag MAX of the bare and context similarities before threshold/ranking, so a tag can only ever rank at least as well as it would from the bare query alone — irrelevant recent context cannot demote a relevant tag below its bare rank. Guard false scores the context vector alone (plain concat, experimentation).
522
+ - **Tests**:
523
+ - `test_embedding_context_guard.py::TestGuardMathParity` (per-tag max ranking matches the reference guard math; guard sim >= bare sim per tag)
524
+ - `test_embedding_context_guard.py::TestLegacyByteIdentical` (N=0 / no-context path identical to legacy; defaults are 0/true)
525
+ - `test_embedding_context_guard.py::TestCraterRegression` (irrelevant-context crater repaired: plain concat demotes the gold tag, guard recovers it near the bare rank)
526
+ - `test_embedding_context_guard.py::TestConfigPlumbing` / `TestRetrieverContextEmbedding` (YAML parse + retriever concat construction)
527
+
528
+ ### BUG-041 — Compaction materializes tag summaries for only the greedy cover
529
+
530
+ - **Symptom**: Every compaction leaves segment tags with no `tag_summaries` row (~69/day on a live multi-tag conversation); those tags are invisible to the context-hint topic list, absent from the broad/recall-all summary floor, and missing from tag-summary-embedding scoring. Rows materialized by an external repair sweep go permanently stale because later compactions keep skipping the tags. First observed May 30 (tags present in `segment_tags`, absent from `tag_summaries`).
531
+ - **Root cause**: By-design minimality that contradicted the system's own read and repair contracts: `_build_tag_summaries` intersected the greedy set-cover with the compacted segments' tags (plus a primary-tag guarantee), structurally omitting every non-primary secondary tag outside the cover. The backfill repair and the read paths both assume every non-`_general` segment tag has a summary.
532
+ - **Fix**: Compaction materializes a summary for every non-`_general` tag carried by the just-compacted segments; the primary-tag guarantee is unchanged. The pre-existing staleness check inside `compact_tag_summaries` bounds the LLM cost (fresh summaries skip), and secondary tags now also REFRESH on later compactions instead of going stale.
533
+ - **Tests**:
534
+ - `test_tag_summary_materialization.py` — no gap after an organic compaction, `_general` exclusion, report coverage, secondary-tag refresh eligibility on later compactions
535
+ - `test_tag_summary_backfill.py::test_compaction_tag_summaries_populated_when_in_memory_index_empty` (pin updated to the wide contract)
536
+
537
+ ### BUG-040 — Assistant-half ingest duplicates the prepared user row
538
+
539
+ - **Symptom**: Three linked production symptoms on prepare-then-ingest REST conversations: (1) strict tagging fails with "could not map payload messages to existing rows for logical turn N" and turns fall through to the context-free row sweep (observed as a jump in `_general` primary tags on live traffic), (2) canonical rows contain duplicated user halves and mid-inserted copies of already-present content, (3) sort-key gaps at the insertion point halve with every prepare, priming the BUG-036 gap-exhaustion collisions.
540
+ - **Root cause**: `ingest_single` (the completed-pair persist path) prepares a `[user, assistant]` row pair, but the user half is normally already the conversation's LAST row, persisted by the preceding payload prepare. A 2-row incoming fragment has no ≥3-row anchor window and short-overlap matching is disallowed on this path, so alignment failed and `no_overlap_append` duplicated the user row; the duplicate scrambled every subsequent payload alignment.
541
+ - **Fix**: Before falling through to full alignment, `ingest_single` matches the pair's user hash against the tail row; on a match it mirrors the existing row's identity (no rewrite) and appends only the assistant row (`tail_append`). Resend dedup and genuinely-new-pair append behavior are unchanged.
542
+ - **Tests**: `test_ingest_single_tail_pair.py` — no duplication, multi-turn row cleanliness, strict tagging green across a full REST conversation, resend dedup preserved, unprepared-pair append preserved.
543
+
544
+ ### BUG-039 — engine_state saves fail on schema-bootstrapped Postgres
545
+
546
+ - **Symptom**: `Failed to save engine state: column "flushed_prefix_messages" of relation "engine_state" does not exist` on every engine-state save; swallowed as a warning, so session-restore state silently never persists.
547
+ - **Root cause**: The bundled schema created `engine_state` with five columns while `save_engine_state` on Postgres INSERTs seven (`flushed_prefix_messages`, `last_request_time`).
548
+ - **Fix**: Both columns added to the bundled CREATE TABLE, plus idempotent `ADD COLUMN IF NOT EXISTS` migration in the schema bootstrap for tables created by earlier schemas.
549
+ - **Tests**:
550
+ - `test_engine_state_schema_postgres.py::test_engine_state_table_has_save_path_columns`
551
+ - `test_engine_state_schema_postgres.py::test_save_engine_state_round_trips_on_fresh_schema`
552
+
553
+ ### BUG-037 — Schema bootstrap DDL races across workers
554
+
555
+ - **Symptom**: Multi-worker startup logs `tuple concurrently updated` from `CREATE OR REPLACE FUNCTION` (and historically DuplicateObject from trigger DROP+CREATE pairs); the losing worker logs "merge_post_commit_pending bootstrap failed" and skips the rest of its guarded block.
556
+ - **Root cause**: `PostgresStore._ensure_schema` ran all bootstrap DDL with no cross-worker serialization.
557
+ - **Fix**: Session-scoped `pg_advisory_lock` held on a dedicated connection for the whole bootstrap (`_SCHEMA_BOOTSTRAP_LOCK_KEY`); session-scoped rather than xact-scoped because the bootstrap relies on per-statement autocommit for its try/except-guarded statements.
558
+ - **Tests**:
559
+ - `test_schema_bootstrap_postgres.py::TestSchemaBootstrapSerialization::test_concurrent_bootstrap_no_ddl_race_warnings`
560
+
377
561
  ---
378
562
 
379
563
  ## By Test File
@@ -397,3 +581,19 @@ Use `pytest -m regression` to run all regression tests.
397
581
  | `test_verb_expansion.py` | BUG-032 |
398
582
  | `test_engine_integration.py` | BUG-034 |
399
583
  | `test_conversation_scoping.py` | BUG-035 |
584
+ | `test_ingest_sort_key_rebalance.py` | BUG-036 |
585
+ | `test_sort_key_shift_postgres.py` | BUG-036 |
586
+ | `test_schema_bootstrap_postgres.py` | BUG-037 |
587
+ | `test_strict_tagging_tagged_rows.py` | BUG-038 |
588
+ | `test_engine_state_schema_postgres.py` | BUG-039 |
589
+ | `test_ingest_single_tail_pair.py` | BUG-040 |
590
+ | `test_tag_summary_materialization.py` | BUG-041 |
591
+ | `test_embedding_context_guard.py` | BUG-042 |
592
+ | `test_embedding_reserved_seats.py` | BUG-043 |
593
+ | `test_ingest_anchor_incremental.py` | BUG-047 |
594
+ | `test_ingest_projected_rows.py` | BUG-048 |
595
+ | `test_protected_window_gate.py` | BUG-044, BUG-045, BUG-046 |
596
+ | `test_canonical_turn_id_stamping.py` | BUG-046 |
597
+ | `test_retrieval_assembler_protected_window_merge.py` | BUG-045 |
598
+ | `test_sqlite_mirror_store_methods.py` | BUG-045 |
599
+ | `test_postgres_mirror_store_methods.py` | BUG-045 |
@@ -2,7 +2,14 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import os
5
6
  import tempfile
7
+
8
+ # Embedding models are pre-cached; without these flags each model
9
+ # construction performs a network revision check against the hub CDN,
10
+ # which can stall the suite for minutes under a degraded network.
11
+ os.environ.setdefault("HF_HUB_OFFLINE", "1")
12
+ os.environ.setdefault("TRANSFORMERS_OFFLINE", "1")
6
13
  from datetime import datetime, timedelta, timezone
7
14
  from pathlib import Path
8
15
 
@@ -0,0 +1,72 @@
1
+ {
2
+ "embedding_scores": {
3
+ "tag_001": 0.392342209815979,
4
+ "tag_002": 0.42588120698928833,
5
+ "tag_003": 0.39020001888275146,
6
+ "tag_007": 0.41312387585639954,
7
+ "tag_009": 0.3971983790397644,
8
+ "tag_011": 0.45882731676101685,
9
+ "tag_013": 0.451797753572464,
10
+ "tag_020": 0.41635698080062866,
11
+ "tag_027": 0.4214135408401489,
12
+ "tag_028": 0.49210599064826965,
13
+ "tag_030": 0.5612373352050781,
14
+ "tag_031": 0.4008348286151886,
15
+ "tag_033": 0.42422300577163696,
16
+ "tag_034": 0.40469276905059814,
17
+ "tag_035": 0.40652984380722046,
18
+ "tag_036": 0.4652409553527832,
19
+ "tag_038": 0.44060882925987244,
20
+ "tag_039": 0.4001418948173523,
21
+ "tag_040": 0.46100878715515137,
22
+ "tag_043": 0.4096267521381378
23
+ },
24
+ "fused_scores": {
25
+ "tag_000": 0.01095915225070193,
26
+ "tag_001": 0.006983992066864994,
27
+ "tag_002": 0.007404964129628102,
28
+ "tag_003": 0.006951535072382683,
29
+ "tag_004": 0.010115372115697107,
30
+ "tag_005": 0.00901243093922652,
31
+ "tag_006": 0.009519187695983276,
32
+ "tag_007": 0.007236790911213134,
33
+ "tag_008": 0.00864478388040299,
34
+ "tag_009": 0.007017292100165029,
35
+ "tag_010": 0.01057493093922652,
36
+ "tag_011": 0.007544889502762431,
37
+ "tag_012": 0.009255937432733013,
38
+ "tag_013": 0.007496812579685508,
39
+ "tag_014": 0.009905288082083663,
40
+ "tag_015": 0.009091544863277153,
41
+ "tag_016": 0.008576384427598612,
42
+ "tag_017": 0.008935270445399358,
43
+ "tag_018": 0.008714811891607471,
44
+ "tag_019": 0.010698938875734455,
45
+ "tag_020": 0.007277032359905287,
46
+ "tag_021": 0.008786527324768688,
47
+ "tag_022": 0.009341378307647571,
48
+ "tag_023": 0.008859991914836276,
49
+ "tag_024": 0.010826947068258777,
50
+ "tag_025": 0.00917268734948293,
51
+ "tag_026": 0.010454738631534212,
52
+ "tag_027": 0.007318440227400112,
53
+ "tag_028": 0.007698578027352595,
54
+ "tag_029": 0.00980468446035328,
55
+ "tag_030": 0.006919889502762431,
56
+ "tag_031": 0.007086556169429097,
57
+ "tag_032": 0.010225117506390699,
58
+ "tag_033": 0.007361065973350666,
59
+ "tag_034": 0.011606538077832818,
60
+ "tag_035": 0.007159615530159691,
61
+ "tag_036": 0.007645695954375333,
62
+ "tag_037": 0.009706875383670963,
63
+ "tag_038": 0.007450192533065461,
64
+ "tag_039": 0.007051468450130851,
65
+ "tag_040": 0.007594492677365605,
66
+ "tag_041": 0.009429097605893187,
67
+ "tag_042": 0.00961174600771967,
68
+ "tag_043": 0.007197667280540209,
69
+ "tag_044": 0.010338188514984095
70
+ },
71
+ "note": "Anonymized real prod fused/embedding dump (context-augmented analog query). Tag names opaqued; scores preserved verbatim for reserved-seat parity."
72
+ }