code-context-control 2.153.0__tar.gz → 2.154.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (589) hide show
  1. {code_context_control-2.153.0 → code_context_control-2.154.0}/PKG-INFO +1 -1
  2. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/c3.py +63 -1
  3. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/commands/parser.py +11 -0
  4. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/index.html +4 -0
  5. {code_context_control-2.153.0 → code_context_control-2.154.0}/code_context_control.egg-info/PKG-INFO +1 -1
  6. {code_context_control-2.153.0 → code_context_control-2.154.0}/code_context_control.egg-info/SOURCES.txt +2 -0
  7. {code_context_control-2.153.0 → code_context_control-2.154.0}/pyproject.toml +1 -1
  8. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/embedding_index.py +434 -4
  9. code_context_control-2.154.0/tests/test_embedding_store_safety.py +228 -0
  10. code_context_control-2.154.0/tests/test_quarantine_sweep.py +178 -0
  11. {code_context_control-2.153.0 → code_context_control-2.154.0}/LICENSE +0 -0
  12. {code_context_control-2.153.0 → code_context_control-2.154.0}/README.md +0 -0
  13. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/__init__.py +0 -0
  14. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/_hook_utils.py +0 -0
  15. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/_shell_writes.py +0 -0
  16. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/commands/__init__.py +0 -0
  17. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/commands/common.py +0 -0
  18. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/docs.html +0 -0
  19. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/edits.html +0 -0
  20. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/access.html +0 -0
  21. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/bitbucket.html +0 -0
  22. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/ci.html +0 -0
  23. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/credentials.html +0 -0
  24. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/delegate.html +0 -0
  25. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/getting-started.html +0 -0
  26. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/jira.html +0 -0
  27. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/masking.html +0 -0
  28. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/oracle.html +0 -0
  29. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/sessions.html +0 -0
  30. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/shared.css +0 -0
  31. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/tools.html +0 -0
  32. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/workflow.html +0 -0
  33. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_access_guard.py +0 -0
  34. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_agent_model.py +0 -0
  35. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_artifact.py +0 -0
  36. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_auto_snapshot.py +0 -0
  37. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_c3_signal.py +0 -0
  38. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_c3read.py +0 -0
  39. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_codex_lifecycle.py +0 -0
  40. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_dispatch.py +0 -0
  41. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_edit_ledger.py +0 -0
  42. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_edit_unlock.py +0 -0
  43. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_filter.py +0 -0
  44. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_ghost_files.py +0 -0
  45. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_pretool_enforce.py +0 -0
  46. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_prompt_recall.py +0 -0
  47. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_read.py +0 -0
  48. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_session_end.py +0 -0
  49. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_session_open.py +0 -0
  50. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_session_stats.py +0 -0
  51. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_terse_advisor.py +0 -0
  52. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_server.py +0 -0
  53. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/app.js +0 -0
  54. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/add_project.js +0 -0
  55. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/config_editor.js +0 -0
  56. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/drill_artifacts.js +0 -0
  57. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/drill_health.js +0 -0
  58. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/drill_panel.js +0 -0
  59. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/drill_subprojects.js +0 -0
  60. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/drill_tasks.js +0 -0
  61. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/drill_views.js +0 -0
  62. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/global_search.js +0 -0
  63. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/hub_access.js +0 -0
  64. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/hub_ci.js +0 -0
  65. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/hub_cred_audit.js +0 -0
  66. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/hub_credentials.js +0 -0
  67. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/hub_enforcement.js +0 -0
  68. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/hub_locks.js +0 -0
  69. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/hub_sessions.js +0 -0
  70. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/hub_tokens.js +0 -0
  71. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/mcp_manager.js +0 -0
  72. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/modals.js +0 -0
  73. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/project_card.js +0 -0
  74. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/project_tree.js +0 -0
  75. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/session_drawer.js +0 -0
  76. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/settings_modal.js +0 -0
  77. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/sidebar.js +0 -0
  78. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/summary_bar.js +0 -0
  79. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/task_board.js +0 -0
  80. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/toasts.js +0 -0
  81. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/topbar.js +0 -0
  82. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/state.js +0 -0
  83. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui.html +0 -0
  84. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/mcp_proxy.py +0 -0
  85. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/mcp_server.py +0 -0
  86. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/progress.py +0 -0
  87. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/server.py +0 -0
  88. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/__init__.py +0 -0
  89. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/_edit_report.py +0 -0
  90. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/_grants.py +0 -0
  91. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/_helpers.py +0 -0
  92. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/agent.py +0 -0
  93. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/artifacts.py +0 -0
  94. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/bitbucket.py +0 -0
  95. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/ci.py +0 -0
  96. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/compress.py +0 -0
  97. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/credentials.py +0 -0
  98. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/delegate.py +0 -0
  99. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/edit.py +0 -0
  100. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/edit_verify.py +0 -0
  101. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/edits.py +0 -0
  102. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/federate.py +0 -0
  103. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/filter.py +0 -0
  104. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/impact.py +0 -0
  105. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/jira.py +0 -0
  106. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/locks.py +0 -0
  107. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/memory.py +0 -0
  108. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/override.py +0 -0
  109. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/project.py +0 -0
  110. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/read.py +0 -0
  111. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/search.py +0 -0
  112. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/session.py +0 -0
  113. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/shell.py +0 -0
  114. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/shell_job.py +0 -0
  115. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/shell_nudge.py +0 -0
  116. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/shell_parsers.py +0 -0
  117. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/shell_render.py +0 -0
  118. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/status.py +0 -0
  119. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/tasks.py +0 -0
  120. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/validate.py +0 -0
  121. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/api.js +0 -0
  122. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/app.js +0 -0
  123. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/access.js +0 -0
  124. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/bitbucket.js +0 -0
  125. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/chat.js +0 -0
  126. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/credentials.js +0 -0
  127. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/dashboard.js +0 -0
  128. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/edits.js +0 -0
  129. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/enforcement.js +0 -0
  130. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/instructions.js +0 -0
  131. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/jira.js +0 -0
  132. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/memory.js +0 -0
  133. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/sessions.js +0 -0
  134. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/settings.js +0 -0
  135. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/sidebar.js +0 -0
  136. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/tasks.js +0 -0
  137. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/icons.js +0 -0
  138. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/pm_shared.js +0 -0
  139. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/shared.js +0 -0
  140. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/theme.js +0 -0
  141. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui.html +0 -0
  142. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui_legacy.html +0 -0
  143. {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui_nano.html +0 -0
  144. {code_context_control-2.153.0 → code_context_control-2.154.0}/code_context_control.egg-info/dependency_links.txt +0 -0
  145. {code_context_control-2.153.0 → code_context_control-2.154.0}/code_context_control.egg-info/entry_points.txt +0 -0
  146. {code_context_control-2.153.0 → code_context_control-2.154.0}/code_context_control.egg-info/requires.txt +0 -0
  147. {code_context_control-2.153.0 → code_context_control-2.154.0}/code_context_control.egg-info/top_level.txt +0 -0
  148. {code_context_control-2.153.0 → code_context_control-2.154.0}/core/__init__.py +0 -0
  149. {code_context_control-2.153.0 → code_context_control-2.154.0}/core/codex_patch.py +0 -0
  150. {code_context_control-2.153.0 → code_context_control-2.154.0}/core/config.py +0 -0
  151. {code_context_control-2.153.0 → code_context_control-2.154.0}/core/grok_payload.py +0 -0
  152. {code_context_control-2.153.0 → code_context_control-2.154.0}/core/hook_command.py +0 -0
  153. {code_context_control-2.153.0 → code_context_control-2.154.0}/core/host.py +0 -0
  154. {code_context_control-2.153.0 → code_context_control-2.154.0}/core/ide.py +0 -0
  155. {code_context_control-2.153.0 → code_context_control-2.154.0}/core/mcp_toml.py +0 -0
  156. {code_context_control-2.153.0 → code_context_control-2.154.0}/core/web_security.py +0 -0
  157. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/__init__.py +0 -0
  158. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/config.py +0 -0
  159. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/listeners.py +0 -0
  160. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/mcp_oracle.py +0 -0
  161. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/oracle_server.py +0 -0
  162. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/oracle_ui.html +0 -0
  163. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/__init__.py +0 -0
  164. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/activity_reporter.py +0 -0
  165. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/api_auth.py +0 -0
  166. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/c3_bridge.py +0 -0
  167. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/chat_engine.py +0 -0
  168. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/chat_poll.py +0 -0
  169. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/chat_store.py +0 -0
  170. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/client_tokens.py +0 -0
  171. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/cross_memory.py +0 -0
  172. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/discovery_audit.py +0 -0
  173. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/federated_graph.py +0 -0
  174. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/health_checker.py +0 -0
  175. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/insight_engine.py +0 -0
  176. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/local_session.py +0 -0
  177. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/memory_reader.py +0 -0
  178. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/memory_writer.py +0 -0
  179. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/mobile_api.py +0 -0
  180. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/project_scanner.py +0 -0
  181. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/review_agent.py +0 -0
  182. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/tool_executor.py +0 -0
  183. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/tool_registry.py +0 -0
  184. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/activity.js +0 -0
  185. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/agents.js +0 -0
  186. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/app.js +0 -0
  187. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/busy.js +0 -0
  188. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/chat/conversations.js +0 -0
  189. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/chat/input.js +0 -0
  190. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/chat/markdown.js +0 -0
  191. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/chat/send.js +0 -0
  192. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/chat/stream_renderer.js +0 -0
  193. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/chat/toolbar.js +0 -0
  194. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/core.js +0 -0
  195. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/crossgraph.js +0 -0
  196. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/header.js +0 -0
  197. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/insights.js +0 -0
  198. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/projects.js +0 -0
  199. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/qrcode.js +0 -0
  200. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/settings.js +0 -0
  201. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/suggestions.js +0 -0
  202. {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/theme_tabs.js +0 -0
  203. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/__init__.py +0 -0
  204. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/access_guard.py +0 -0
  205. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/access_telemetry.py +0 -0
  206. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/activity_log.py +0 -0
  207. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/agent_base.py +0 -0
  208. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/agent_downshift.py +0 -0
  209. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/agent_locks.py +0 -0
  210. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/agents.py +0 -0
  211. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/agents_workflow.py +0 -0
  212. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/artifact_defs.py +0 -0
  213. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/artifact_store.py +0 -0
  214. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/atomic_json.py +0 -0
  215. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/auto_memory.py +0 -0
  216. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/background_service.py +0 -0
  217. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/bench/__init__.py +0 -0
  218. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/bench/delegate_eval.py +0 -0
  219. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/bench/delegate_write_eval.py +0 -0
  220. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/bench/external/__init__.py +0 -0
  221. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/bench/external/aider_polyglot.py +0 -0
  222. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/bench/external/swe_bench.py +0 -0
  223. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/bench/map_eval.py +0 -0
  224. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/bench/search_eval.py +0 -0
  225. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/bench/shell_eval.py +0 -0
  226. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/benchmark_dashboard.py +0 -0
  227. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/bitbucket_client.py +0 -0
  228. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/bitbucket_credentials.py +0 -0
  229. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/builtin_leases.py +0 -0
  230. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/ci_act.py +0 -0
  231. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/ci_cache.py +0 -0
  232. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/ci_expr.py +0 -0
  233. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/ci_failures.py +0 -0
  234. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/ci_github.py +0 -0
  235. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/ci_impact.py +0 -0
  236. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/ci_intel.py +0 -0
  237. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/ci_runner.py +0 -0
  238. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/ci_workflow.py +0 -0
  239. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/circuit_breaker.py +0 -0
  240. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/claude_md.py +0 -0
  241. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/codex_integration.py +0 -0
  242. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/codex_transcript.py +0 -0
  243. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/compressor.py +0 -0
  244. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/context_snapshot.py +0 -0
  245. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/conversation_store.py +0 -0
  246. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/cred_audit.py +0 -0
  247. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/cred_telemetry.py +0 -0
  248. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/credential_backup.py +0 -0
  249. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/credential_store.py +0 -0
  250. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/delegate_agents.py +0 -0
  251. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/delegate_hints.py +0 -0
  252. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/delegate_write.py +0 -0
  253. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/dir_map.py +0 -0
  254. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/doc_index.py +0 -0
  255. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/e2e_benchmark.py +0 -0
  256. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/e2e_evaluator.py +0 -0
  257. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/e2e_tasks.py +0 -0
  258. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/edit_blobs.py +0 -0
  259. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/edit_ledger.py +0 -0
  260. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/enforcement_policy.py +0 -0
  261. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/error_reporting.py +0 -0
  262. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/file_map.py +0 -0
  263. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/file_memory.py +0 -0
  264. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/git_context.py +0 -0
  265. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/grok_integration.py +0 -0
  266. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/grok_transcript.py +0 -0
  267. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/host_sessions.py +0 -0
  268. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/hub_service.py +0 -0
  269. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/index_store.py +0 -0
  270. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/indexer.py +0 -0
  271. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/jira_client.py +0 -0
  272. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/jira_cloud.py +0 -0
  273. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/jira_credentials.py +0 -0
  274. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/jira_data_center.py +0 -0
  275. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/jira_links.py +0 -0
  276. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/lexical_index.py +0 -0
  277. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/mask_activation.py +0 -0
  278. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/mask_mirror.py +0 -0
  279. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/mask_presets.py +0 -0
  280. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/memory.py +0 -0
  281. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/memory_consolidator.py +0 -0
  282. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/memory_distiller.py +0 -0
  283. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/memory_graph.py +0 -0
  284. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/memory_grounder.py +0 -0
  285. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/memory_queue.py +0 -0
  286. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/memory_scorer.py +0 -0
  287. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/metrics.py +0 -0
  288. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/notifications.py +0 -0
  289. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/ollama_bridge.py +0 -0
  290. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/ollama_client.py +0 -0
  291. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/ollama_credentials.py +0 -0
  292. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/oracle_service.py +0 -0
  293. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/output_filter.py +0 -0
  294. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/override_costs.py +0 -0
  295. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/override_grants.py +0 -0
  296. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/override_policy.py +0 -0
  297. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/override_requests.py +0 -0
  298. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/override_wake.py +0 -0
  299. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/parser.py +0 -0
  300. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/project_manager.py +0 -0
  301. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/project_runtime.py +0 -0
  302. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/protocol.py +0 -0
  303. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/proxy_state.py +0 -0
  304. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/read_stamps.py +0 -0
  305. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/repo_map.py +0 -0
  306. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/repo_shape.py +0 -0
  307. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/reranker.py +0 -0
  308. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/retention.py +0 -0
  309. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/retrieval.py +0 -0
  310. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/retrieval_broker.py +0 -0
  311. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/router.py +0 -0
  312. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/runtime.py +0 -0
  313. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/scanner.py +0 -0
  314. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/session_benchmark.py +0 -0
  315. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/session_catalog.py +0 -0
  316. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/session_live.py +0 -0
  317. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/session_manager.py +0 -0
  318. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/session_preloader.py +0 -0
  319. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/shell_jobs.py +0 -0
  320. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/shell_output.py +0 -0
  321. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/subprojects.py +0 -0
  322. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/task_store.py +0 -0
  323. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/telemetry.py +0 -0
  324. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/terminal_launch.py +0 -0
  325. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/text_index.py +0 -0
  326. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/time_tracker.py +0 -0
  327. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/tool_classifier.py +0 -0
  328. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/transcript_index.py +0 -0
  329. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/validation_cache.py +0 -0
  330. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/vector_store.py +0 -0
  331. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/watcher.py +0 -0
  332. {code_context_control-2.153.0 → code_context_control-2.154.0}/services/win_subprocess.py +0 -0
  333. {code_context_control-2.153.0 → code_context_control-2.154.0}/setup.cfg +0 -0
  334. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_access_builtin_optout.py +0 -0
  335. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_access_guard.py +0 -0
  336. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_access_guard_meta.py +0 -0
  337. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_access_guard_shell_project.py +0 -0
  338. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_access_guard_surfaces.py +0 -0
  339. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_access_guard_wiring.py +0 -0
  340. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_activity_log_find_last.py +0 -0
  341. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_activity_reporter.py +0 -0
  342. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_agent_locks.py +0 -0
  343. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_aider_polyglot.py +0 -0
  344. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_antigravity_transition.py +0 -0
  345. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_artifact_store.py +0 -0
  346. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_artifact_tool.py +0 -0
  347. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_atomic_json.py +0 -0
  348. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_background_service.py +0 -0
  349. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_bitbucket_cli_smoke.py +0 -0
  350. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_bitbucket_client.py +0 -0
  351. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_bitbucket_config_fallback.py +0 -0
  352. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_bitbucket_credentials.py +0 -0
  353. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_bitbucket_tool.py +0 -0
  354. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_builtin_leases.py +0 -0
  355. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_builtin_modes.py +0 -0
  356. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_c3_bridge_project_artifacts.py +0 -0
  357. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_c3_shell.py +0 -0
  358. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_chat_poll.py +0 -0
  359. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ci_act.py +0 -0
  360. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ci_cache.py +0 -0
  361. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ci_dag_ownership.py +0 -0
  362. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ci_expr.py +0 -0
  363. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ci_impact.py +0 -0
  364. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ci_intel.py +0 -0
  365. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ci_runner.py +0 -0
  366. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ci_workflow.py +0 -0
  367. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_circuit_breaker.py +0 -0
  368. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_claude_md_merge.py +0 -0
  369. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_claude_md_updater.py +0 -0
  370. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_cli_smoke.py +0 -0
  371. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_codex_mcp_transport.py +0 -0
  372. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_codex_native.py +0 -0
  373. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_compressor_large_file.py +0 -0
  374. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_confirm_guard.py +0 -0
  375. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_confirm_guard_wiring.py +0 -0
  376. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_cooccurrence_bounds.py +0 -0
  377. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_cred_audit.py +0 -0
  378. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_cred_telemetry.py +0 -0
  379. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_credential_backup.py +0 -0
  380. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_credential_login_kind.py +0 -0
  381. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_credential_store.py +0 -0
  382. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_credential_ui_parity.py +0 -0
  383. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_credentials_routes.py +0 -0
  384. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_credentials_tool.py +0 -0
  385. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_creds_cli_smoke.py +0 -0
  386. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_adoption_d4.py +0 -0
  387. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_auto.py +0 -0
  388. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_cascade.py +0 -0
  389. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_claude_d1.py +0 -0
  390. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_eval.py +0 -0
  391. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_grok.py +0 -0
  392. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_host_d2.py +0 -0
  393. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_scout_d3.py +0 -0
  394. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_telemetry_d0.py +0 -0
  395. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_thinking_cap.py +0 -0
  396. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_tuning.py +0 -0
  397. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_version_probe.py +0 -0
  398. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_write.py +0 -0
  399. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_write_eval.py +0 -0
  400. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_dir_map_c3.py +0 -0
  401. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_e2e_benchmark.py +0 -0
  402. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_edit_eol_atomic.py +0 -0
  403. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_edit_ledger_deadline.py +0 -0
  404. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_edit_ledger_hook.py +0 -0
  405. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_edit_locking.py +0 -0
  406. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_edit_normalization.py +0 -0
  407. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_edit_outcome.py +0 -0
  408. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_edit_report.py +0 -0
  409. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_edit_revert.py +0 -0
  410. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_edit_stale.py +0 -0
  411. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_edit_verify.py +0 -0
  412. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_embedding_index_corrupt_store.py +0 -0
  413. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_embedding_index_deadlock.py +0 -0
  414. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_enforcement_flip.py +0 -0
  415. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_enforcement_policy.py +0 -0
  416. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_enforcement_routes.py +0 -0
  417. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_env_parser.py +0 -0
  418. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_failed_calls_do_not_unlock.py +0 -0
  419. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_federated_graph.py +0 -0
  420. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_file_map_c1.py +0 -0
  421. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_filter_backoff.py +0 -0
  422. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ghost_files.py +0 -0
  423. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ghost_generation.py +0 -0
  424. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_git_branch_awareness.py +0 -0
  425. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_grok_native.py +0 -0
  426. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hook_access_guard.py +0 -0
  427. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hook_codex_compat.py +0 -0
  428. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hook_dispatch.py +0 -0
  429. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hook_grok_compat.py +0 -0
  430. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hook_pretool_enforce.py +0 -0
  431. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hook_prompt_recall.py +0 -0
  432. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hook_session_end.py +0 -0
  433. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hook_smoke.py +0 -0
  434. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hook_state.py +0 -0
  435. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hook_utf8_stdio.py +0 -0
  436. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_access_routes.py +0 -0
  437. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_builtin_routes.py +0 -0
  438. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_credentials_route.py +0 -0
  439. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_credentials_routes_write.py +0 -0
  440. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_enforcement_routes.py +0 -0
  441. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_hook_migration.py +0 -0
  442. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_ide_cmd.py +0 -0
  443. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_inspect_api.py +0 -0
  444. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_locks_routes.py +0 -0
  445. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_oracle_service_routes.py +0 -0
  446. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_override_policy_routes.py +0 -0
  447. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_override_routes.py +0 -0
  448. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_rules_routes.py +0 -0
  449. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_server_smoke.py +0 -0
  450. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_sessions_routes.py +0 -0
  451. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_subproject_links.py +0 -0
  452. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ide_workflow_adaptation.py +0 -0
  453. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_index_cap_not_hardcoded.py +0 -0
  454. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_init_clear_preserves_user_content.py +0 -0
  455. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_init_scan.py +0 -0
  456. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_install_mcp_entrypoint.py +0 -0
  457. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_jira_cli_smoke.py +0 -0
  458. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_jira_client.py +0 -0
  459. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_jira_config_fallback.py +0 -0
  460. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_jira_credentials.py +0 -0
  461. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_jira_links.py +0 -0
  462. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_jira_routes.py +0 -0
  463. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_jira_tool.py +0 -0
  464. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_large_files_c4.py +0 -0
  465. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_lazy_store_init.py +0 -0
  466. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_map_eval.py +0 -0
  467. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_map_telemetry_c0.py +0 -0
  468. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mask_guard.py +0 -0
  469. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mask_routes.py +0 -0
  470. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mask_surfaces.py +0 -0
  471. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mcp_host_guard.py +0 -0
  472. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mcp_memory_timeout.py +0 -0
  473. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mcp_server_smoke.py +0 -0
  474. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mcp_toml.py +0 -0
  475. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_memory_distiller.py +0 -0
  476. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_memory_graph_api.py +0 -0
  477. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_memory_queue.py +0 -0
  478. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_memory_system.py +0 -0
  479. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mobile_api.py +0 -0
  480. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mobile_clients.py +0 -0
  481. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mobile_extras.py +0 -0
  482. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mobile_jobs_ci.py +0 -0
  483. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mobile_override_costs.py +0 -0
  484. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mobile_override_routes.py +0 -0
  485. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mobile_security_api.py +0 -0
  486. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_no_stale_model_pins.py +0 -0
  487. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_notification_dedup.py +0 -0
  488. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_notification_discipline.py +0 -0
  489. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_notification_kinds.py +0 -0
  490. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ollama_credentials.py +0 -0
  491. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_api_auth.py +0 -0
  492. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_apikey_api.py +0 -0
  493. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_c3_bridge.py +0 -0
  494. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_chat_engine.py +0 -0
  495. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_chat_store.py +0 -0
  496. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_discovery_api.py +0 -0
  497. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_discovery_audit.py +0 -0
  498. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_health_probe.py +0 -0
  499. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_local_auth.py +0 -0
  500. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_loopback.py +0 -0
  501. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_ollama_bridge.py +0 -0
  502. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_scanner_cache.py +0 -0
  503. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_security_fixes.py +0 -0
  504. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_session_bootstrap.py +0 -0
  505. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_subproject_awareness.py +0 -0
  506. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_ui_bundle.py +0 -0
  507. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_output_filter.py +0 -0
  508. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_override_costs.py +0 -0
  509. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_override_grants.py +0 -0
  510. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_override_grants_mcp.py +0 -0
  511. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_override_requests.py +0 -0
  512. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_override_wake.py +0 -0
  513. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_permissions.py +0 -0
  514. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_pm_api.py +0 -0
  515. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_project_manager.py +0 -0
  516. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_project_manager_merge.py +0 -0
  517. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_project_tool.py +0 -0
  518. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_read_coercion.py +0 -0
  519. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_read_edit_parity.py +0 -0
  520. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_read_outside_root.py +0 -0
  521. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_related_facts.py +0 -0
  522. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_repo_map.py +0 -0
  523. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_repo_shape.py +0 -0
  524. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_response_boilerplate.py +0 -0
  525. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_retention.py +0 -0
  526. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_review_digest.py +0 -0
  527. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_search_eval.py +0 -0
  528. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_search_p1.py +0 -0
  529. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_search_p2.py +0 -0
  530. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_search_p2b.py +0 -0
  531. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_search_p3.py +0 -0
  532. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_search_p4.py +0 -0
  533. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_search_p5.py +0 -0
  534. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_service_durability.py +0 -0
  535. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_session_benchmark.py +0 -0
  536. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_session_budget.py +0 -0
  537. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_session_catalog.py +0 -0
  538. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_session_live.py +0 -0
  539. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_session_tool.py +0 -0
  540. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_shell_budget_s1.py +0 -0
  541. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_shell_creds.py +0 -0
  542. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_shell_eval.py +0 -0
  543. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_shell_jobs_s3.py +0 -0
  544. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_shell_nudge_s4.py +0 -0
  545. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_shell_output.py +0 -0
  546. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_shell_parsers_s2.py +0 -0
  547. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_shell_robustness.py +0 -0
  548. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_shell_telemetry_s0.py +0 -0
  549. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_shell_write_ledger.py +0 -0
  550. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_shell_writes.py +0 -0
  551. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_subproject_clear_scope.py +0 -0
  552. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_subproject_depth_consumers.py +0 -0
  553. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_subproject_exclusion.py +0 -0
  554. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_subproject_federation.py +0 -0
  555. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_subproject_linkage_api.py +0 -0
  556. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_subprojects.py +0 -0
  557. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_swe_bench.py +0 -0
  558. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_task_store.py +0 -0
  559. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_task_tool.py +0 -0
  560. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_time_tracker.py +0 -0
  561. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_token_telemetry.py +0 -0
  562. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_token_tracking.py +0 -0
  563. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_tool_registry.py +0 -0
  564. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ui_jsx_syntax.py +0 -0
  565. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_upgrade_and_version.py +0 -0
  566. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_validate.py +0 -0
  567. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_vault_write_guard.py +0 -0
  568. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_version_sync.py +0 -0
  569. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_watcher_pruning.py +0 -0
  570. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_web_security.py +0 -0
  571. {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_windows_reliability.py +0 -0
  572. {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/__init__.py +0 -0
  573. {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/backend.py +0 -0
  574. {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/main.py +0 -0
  575. {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/__init__.py +0 -0
  576. {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/benchmark_view.py +0 -0
  577. {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/claudemd_view.py +0 -0
  578. {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/compress_view.py +0 -0
  579. {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/index_view.py +0 -0
  580. {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/init_view.py +0 -0
  581. {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/mcp_view.py +0 -0
  582. {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/optimize_view.py +0 -0
  583. {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/pipe_view.py +0 -0
  584. {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/projects_view.py +0 -0
  585. {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/search_view.py +0 -0
  586. {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/session_view.py +0 -0
  587. {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/stats.py +0 -0
  588. {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/ui_view.py +0 -0
  589. {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/theme.tcss +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-context-control
3
- Version: 2.153.0
3
+ Version: 2.154.0
4
4
  Summary: Local MCP code-intelligence for AI coding tools: surgical search/read/edit, agent-config version history, path-level access + masking guards, and a multi-project hub.
5
5
  Author-email: Dimitri Tselenchuk <dtselenc@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -91,7 +91,7 @@ console = Console() if HAS_RICH else None
91
91
  # Config
92
92
  CONFIG_DIR = ".c3"
93
93
  CONFIG_FILE = ".c3/config.json"
94
- __version__ = "2.153.0"
94
+ __version__ = "2.154.0"
95
95
 
96
96
  # The PreToolUse matcher for native subagent calls (installer and hub migration).
97
97
  AGENT_MATCHER = "Agent|Task"
@@ -1288,6 +1288,14 @@ def cmd_init(args):
1288
1288
  except Exception:
1289
1289
  pass
1290
1290
 
1291
+ swept = _prune_project_quarantines(project_path)
1292
+ if swept["pruned"]:
1293
+ print(f" Fixed : freed {_format_bytes(swept['bytes'])} from "
1294
+ f"{len(swept['pruned'])} old embedding quarantine dir(s)")
1295
+ for name in swept["failed"]:
1296
+ print(f" [warn] {name} could not be fully removed (in use?); "
1297
+ "run 'c3 prune-quarantine' later")
1298
+
1291
1299
  # Delegate backends
1292
1300
  try:
1293
1301
  from services.ollama_client import OllamaClient as _OC
@@ -1463,6 +1471,59 @@ def cmd_index(args):
1463
1471
  return common_cmd_index(args, _command_deps())
1464
1472
 
1465
1473
 
1474
+ def _format_bytes(n: float) -> str:
1475
+ for unit in ("B", "KB", "MB", "GB"):
1476
+ if n < 1024:
1477
+ return f"{n:.0f} {unit}" if unit == "B" else f"{n:.1f} {unit}"
1478
+ n /= 1024
1479
+ return f"{n:.1f} TB"
1480
+
1481
+
1482
+ def _prune_project_quarantines(project_path: str, keep=None, dry_run: bool = False) -> dict:
1483
+ """Prune one project's ``.c3/embeddings/quarantine_corrupt_*`` dirs; returns the sweep result."""
1484
+ from cli.progress import ProgressLine
1485
+ from services.embedding_index import prune_quarantines
1486
+
1487
+ prog = ProgressLine()
1488
+ try:
1489
+ return prune_quarantines(
1490
+ Path(project_path) / CONFIG_DIR / "embeddings", keep=keep, dry_run=dry_run,
1491
+ on_progress=lambda done, total: prog.update(
1492
+ f" removing quarantine copies {done}/{total}"))
1493
+ finally:
1494
+ prog.done()
1495
+
1496
+
1497
+ def cmd_prune_quarantine(args):
1498
+ """Delete old corrupt-embedding-store quarantine copies, keeping the newest few."""
1499
+ if args.all:
1500
+ from services.project_manager import ProjectManager
1501
+ projects = [p["path"] for p in ProjectManager().list_registered()]
1502
+ else:
1503
+ projects = [str(Path(args.project_path or ".").resolve())]
1504
+
1505
+ verb = "would remove" if args.dry_run else "removed"
1506
+ pruned = failed = freed = 0
1507
+ for project in projects:
1508
+ res = _prune_project_quarantines(project, keep=args.keep, dry_run=args.dry_run)
1509
+ if not (res["pruned"] or res["failed"]):
1510
+ continue
1511
+ pruned += len(res["pruned"])
1512
+ failed += len(res["failed"])
1513
+ freed += res["bytes"]
1514
+ print(f"{project}: {verb} {len(res['pruned'])} quarantine dir(s), "
1515
+ f"{_format_bytes(res['bytes'])}; kept {len(res['kept'])}")
1516
+ for name in res["failed"]:
1517
+ print(f" [warn] {name} could not be fully removed (in use?); run again later")
1518
+
1519
+ if not (pruned or failed):
1520
+ print(f"Nothing to prune in {len(projects)} project(s).")
1521
+ return
1522
+ print(f"\n{verb.capitalize()} {pruned} quarantine dir(s), {_format_bytes(freed)}.")
1523
+ if failed:
1524
+ sys.exit(1)
1525
+
1526
+
1466
1527
  def cmd_compress(args):
1467
1528
  """Compress a file and show results."""
1468
1529
  return common_cmd_compress(args, _command_deps())
@@ -9603,6 +9664,7 @@ def main():
9603
9664
  "enforce": cmd_enforce,
9604
9665
  "override": cmd_override,
9605
9666
  "locks": cmd_locks,
9667
+ "prune-quarantine": cmd_prune_quarantine,
9606
9668
  "ci": cmd_ci,
9607
9669
  "oracle": cmd_oracle,
9608
9670
  "upgrade": cmd_upgrade,
@@ -37,6 +37,17 @@ def build_parser(version: str, parse_cli_ide_arg):
37
37
  p_upgrade.add_argument("--check", action="store_true",
38
38
  help="Only report whether a newer version exists; don't install")
39
39
 
40
+ p_prune = subparsers.add_parser(
41
+ "prune-quarantine",
42
+ help="Delete old corrupt-embedding-store quarantine copies (frees disk)")
43
+ p_prune.add_argument("project_path", nargs="?", default=".")
44
+ p_prune.add_argument("--all", action="store_true",
45
+ help="Every project registered with the hub, not just this one")
46
+ p_prune.add_argument("--keep", type=int, default=None,
47
+ help="Newest quarantine dirs to keep per project (default 2)")
48
+ p_prune.add_argument("--dry-run", action="store_true",
49
+ help="Report what would be removed without deleting")
50
+
40
51
  p_index = subparsers.add_parser("index", help="Rebuild code index")
41
52
  p_index.add_argument("--max-files", type=int, default=None,
42
53
  help="Cap files indexed this run. Unset reads "
@@ -663,6 +663,10 @@
663
663
  <code>c3 map {status|ensure|refresh}</code>
664
664
  <span>Inspect or rebuild the live repo map at .c3/MAP.md</span>
665
665
  </div>
666
+ <div class="quick-item">
667
+ <code>c3 prune-quarantine [--all] [--dry-run]</code>
668
+ <span>Free disk held by old corrupt-embedding-store copies in .c3/embeddings (<code>c3 init</code> runs it too)</span>
669
+ </div>
666
670
  <div class="quick-item">
667
671
  <code>c3 hub</code>
668
672
  <span>Launch the Project Hub web dashboard (localhost:3330)</span>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-context-control
3
- Version: 2.153.0
3
+ Version: 2.154.0
4
4
  Summary: Local MCP code-intelligence for AI coding tools: surgical search/read/edit, agent-config version history, path-level access + masking guards, and a multi-project hub.
5
5
  Author-email: Dimitri Tselenchuk <dtselenc@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -407,6 +407,7 @@ tests/test_edit_stale.py
407
407
  tests/test_edit_verify.py
408
408
  tests/test_embedding_index_corrupt_store.py
409
409
  tests/test_embedding_index_deadlock.py
410
+ tests/test_embedding_store_safety.py
410
411
  tests/test_enforcement_flip.py
411
412
  tests/test_enforcement_policy.py
412
413
  tests/test_enforcement_routes.py
@@ -511,6 +512,7 @@ tests/test_pm_api.py
511
512
  tests/test_project_manager.py
512
513
  tests/test_project_manager_merge.py
513
514
  tests/test_project_tool.py
515
+ tests/test_quarantine_sweep.py
514
516
  tests/test_read_coercion.py
515
517
  tests/test_read_edit_parity.py
516
518
  tests/test_read_outside_root.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "code-context-control"
7
- version = "2.153.0"
7
+ version = "2.154.0"
8
8
  description = "Local MCP code-intelligence for AI coding tools: surgical search/read/edit, agent-config version history, path-level access + masking guards, and a multi-project hub."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -10,8 +10,12 @@ import gc
10
10
  import hashlib
11
11
  import json
12
12
  import logging
13
+ import os
13
14
  import shutil
15
+ import subprocess
16
+ import sys
14
17
  import threading
18
+ import time
15
19
  from datetime import datetime
16
20
  from pathlib import Path
17
21
 
@@ -28,6 +32,336 @@ DEFAULT_MIN_SCORE_OTHER = 0.55
28
32
  # and degrade rather than block. See _acquire_build_lock().
29
33
  _BUILD_LOCK_WAIT_SECONDS = 30.0
30
34
 
35
+ # ── Store safety (issue #180) ─────────────────────────────
36
+ # A damaged HNSW segment does not just fail to load. chromadb's loader
37
+ # allocates as it reads, so one bad segment made a single MCP server commit
38
+ # 140-200 GB before the load gave up, and every new session repeated it
39
+ # (measured 2026-10-02: count() on a copy of the store, 165 GB private, 0.25 GB
40
+ # working set, then "Error loading hnsw index"). An in-process probe cannot
41
+ # undo that commit, so an existing store is first opened in a child process
42
+ # under a memory cap, and only opened here if the child could read it.
43
+ _PROBE_TIMEOUT_SECONDS = 180.0
44
+ _PROBE_POLL_SECONDS = 0.1
45
+ _PROBE_MIN_CAP_BYTES = 4 * 1024 ** 3 # floor; scaled up for big stores below
46
+ _PROBE_CAP_STORE_MULTIPLE = 4
47
+ _PROBE_OK_FILE = "store_probe_ok.json"
48
+ _PROBE_OK_MARKER = "C3_STORE_PROBE_OK"
49
+ # Each quarantine keeps a full copy of the store for post-mortem. Unbounded,
50
+ # that reached 284 copies and 384 GB in one project; keep only the newest few.
51
+ _QUARANTINE_KEEP = 2
52
+ _OWNER_LOCK_FILE = "chromadb.owner.lock"
53
+
54
+ _PROBE_CHILD_CODE = """
55
+ import sys
56
+ import chromadb
57
+ from chromadb.config import Settings
58
+ client = chromadb.PersistentClient(
59
+ path=sys.argv[1], settings=Settings(anonymized_telemetry=False))
60
+ listed = client.list_collections()
61
+ names = {c if isinstance(c, str) else getattr(c, "name", "") for c in listed}
62
+ if sys.argv[2] in names:
63
+ client.get_collection(sys.argv[2]).count()
64
+ print("C3_STORE_PROBE_OK", flush=True)
65
+ """
66
+
67
+
68
+ class StoreBusyError(RuntimeError):
69
+ """Another C3 process owns this project's embedding store."""
70
+
71
+
72
+ def _tree_bytes(path: Path) -> int:
73
+ total = 0
74
+ for root, _dirs, files in os.walk(path):
75
+ for name in files:
76
+ try:
77
+ total += os.lstat(os.path.join(root, name)).st_size
78
+ except OSError:
79
+ pass
80
+ return total
81
+
82
+
83
+ def prune_quarantines(index_dir: Path, keep: int | None = None,
84
+ dry_run: bool = False, on_progress=None) -> dict:
85
+ """Delete all but the newest *keep* ``quarantine_corrupt_*`` dirs in *index_dir*.
86
+
87
+ ``keep=None`` keeps the default of two.
88
+
89
+ Newest is by name, which embeds the quarantine timestamp. Symlinks and
90
+ non-directories matching the pattern are never touched. A dir that cannot
91
+ be fully removed is reported in ``failed`` and retried by the next call.
92
+ ``on_progress(done, total)`` runs after each dir. With ``dry_run`` nothing
93
+ is deleted and ``pruned`` / ``bytes`` describe what a real run would remove.
94
+
95
+ Returns ``{"kept": [...], "pruned": [...], "failed": [...], "bytes": int}``
96
+ with directory names; ``bytes`` is the size freed (or that would be freed).
97
+ """
98
+ try:
99
+ found = sorted(p for p in Path(index_dir).glob("quarantine_corrupt_*")
100
+ if p.is_dir() and not p.is_symlink())
101
+ except OSError:
102
+ found = []
103
+ keep = _QUARANTINE_KEEP if keep is None else max(int(keep), 0)
104
+ doomed = found[:len(found) - keep] if keep < len(found) else []
105
+ result = {"kept": [p.name for p in found[len(doomed):]],
106
+ "pruned": [], "failed": [], "bytes": 0}
107
+ for done, old in enumerate(doomed, 1):
108
+ size = _tree_bytes(old)
109
+ if dry_run:
110
+ result["pruned"].append(old.name)
111
+ result["bytes"] += size
112
+ else:
113
+ shutil.rmtree(old, ignore_errors=True)
114
+ left = _tree_bytes(old) if old.exists() else 0
115
+ result["bytes"] += size - left
116
+ result["failed" if old.exists() else "pruned"].append(old.name)
117
+ if on_progress:
118
+ on_progress(done, len(doomed))
119
+ return result
120
+
121
+
122
+ def _private_bytes(proc) -> int:
123
+ """Committed (Windows) or resident (POSIX) bytes of a psutil.Process."""
124
+ info = proc.memory_info()
125
+ return int(getattr(info, "private", 0) or info.rss)
126
+
127
+
128
+ def _windows_job_memory_cap(pid: int, cap_bytes: int):
129
+ """Put *pid* in a Job Object whose per-process commit limit is *cap_bytes*.
130
+
131
+ Polling alone is not enough here: the corrupt-segment loader committed
132
+ ~100 GB between two 100 ms polls (measured 2026-10-02). Under a job limit
133
+ the allocation fails inside the child instead. Returns the job handle
134
+ (keep it open while the child runs) or None when unsupported.
135
+ """
136
+ if os.name != "nt":
137
+ return None
138
+ try:
139
+ import ctypes
140
+ from ctypes import wintypes
141
+
142
+ k32 = ctypes.WinDLL("kernel32", use_last_error=True)
143
+
144
+ class IO_COUNTERS(ctypes.Structure):
145
+ _fields_ = [(n, ctypes.c_ulonglong) for n in (
146
+ "ReadOperationCount", "WriteOperationCount", "OtherOperationCount",
147
+ "ReadTransferCount", "WriteTransferCount", "OtherTransferCount")]
148
+
149
+ class BASIC(ctypes.Structure):
150
+ _fields_ = [
151
+ ("PerProcessUserTimeLimit", ctypes.c_longlong),
152
+ ("PerJobUserTimeLimit", ctypes.c_longlong),
153
+ ("LimitFlags", wintypes.DWORD),
154
+ ("MinimumWorkingSetSize", ctypes.c_size_t),
155
+ ("MaximumWorkingSetSize", ctypes.c_size_t),
156
+ ("ActiveProcessLimit", wintypes.DWORD),
157
+ ("Affinity", ctypes.c_size_t),
158
+ ("PriorityClass", wintypes.DWORD),
159
+ ("SchedulingClass", wintypes.DWORD),
160
+ ]
161
+
162
+ class EXTENDED(ctypes.Structure):
163
+ _fields_ = [
164
+ ("BasicLimitInformation", BASIC),
165
+ ("IoInfo", IO_COUNTERS),
166
+ ("ProcessMemoryLimit", ctypes.c_size_t),
167
+ ("JobMemoryLimit", ctypes.c_size_t),
168
+ ("PeakProcessMemoryUsed", ctypes.c_size_t),
169
+ ("PeakJobMemoryUsed", ctypes.c_size_t),
170
+ ]
171
+
172
+ JOB_OBJECT_LIMIT_PROCESS_MEMORY = 0x100
173
+ JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x2000
174
+ JobObjectExtendedLimitInformation = 9
175
+ PROCESS_SET_QUOTA, PROCESS_TERMINATE = 0x0100, 0x0001
176
+
177
+ k32.CreateJobObjectW.restype = wintypes.HANDLE
178
+ k32.OpenProcess.restype = wintypes.HANDLE
179
+ job = k32.CreateJobObjectW(None, None)
180
+ if not job:
181
+ return None
182
+ info = EXTENDED()
183
+ info.BasicLimitInformation.LimitFlags = (
184
+ JOB_OBJECT_LIMIT_PROCESS_MEMORY | JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE)
185
+ info.ProcessMemoryLimit = cap_bytes
186
+ if not k32.SetInformationJobObject(
187
+ wintypes.HANDLE(job), JobObjectExtendedLimitInformation,
188
+ ctypes.byref(info), ctypes.sizeof(info)):
189
+ k32.CloseHandle(wintypes.HANDLE(job))
190
+ return None
191
+ proc = k32.OpenProcess(PROCESS_SET_QUOTA | PROCESS_TERMINATE, False, pid)
192
+ if not proc:
193
+ k32.CloseHandle(wintypes.HANDLE(job))
194
+ return None
195
+ try:
196
+ assigned = k32.AssignProcessToJobObject(wintypes.HANDLE(job), wintypes.HANDLE(proc))
197
+ finally:
198
+ k32.CloseHandle(wintypes.HANDLE(proc))
199
+ if not assigned:
200
+ k32.CloseHandle(wintypes.HANDLE(job))
201
+ return None
202
+ return job
203
+ except Exception:
204
+ return None
205
+
206
+
207
+ def _close_job(job) -> None:
208
+ if job is None:
209
+ return
210
+ try:
211
+ import ctypes
212
+ from ctypes import wintypes
213
+ ctypes.WinDLL("kernel32").CloseHandle(wintypes.HANDLE(job))
214
+ except Exception:
215
+ pass
216
+
217
+
218
+ def run_isolated_probe(
219
+ args: list,
220
+ *,
221
+ cap_bytes: int,
222
+ timeout: float = _PROBE_TIMEOUT_SECONDS,
223
+ marker: str = _PROBE_OK_MARKER,
224
+ ) -> tuple:
225
+ """Run ``python <args>`` in a child that is killed if it outgrows *cap_bytes*.
226
+
227
+ Returns ``(ok, reason)``. ``ok`` needs a zero exit AND *marker* on stdout,
228
+ so a child that dies quietly is a failure, not a pass. The cap is enforced
229
+ by polling the child's memory (psutil when installed; RLIMIT_AS on POSIX
230
+ otherwise), which bounds the damage to the cap plus one poll interval.
231
+ """
232
+ try:
233
+ import psutil # optional: C3 does not declare it
234
+ except Exception:
235
+ psutil = None
236
+
237
+ preexec = None
238
+ if psutil is None and os.name != "nt":
239
+ def preexec(): # pragma: no cover - POSIX without psutil
240
+ try: # best effort: macOS refuses to lower RLIMIT_AS
241
+ import resource
242
+ resource.setrlimit(resource.RLIMIT_AS, (cap_bytes, cap_bytes))
243
+ except (ValueError, OSError):
244
+ pass
245
+
246
+ creationflags = getattr(subprocess, "CREATE_NO_WINDOW", 0) if os.name == "nt" else 0
247
+ import tempfile
248
+
249
+ # Files, not pipes: a child that logs more than a pipe buffer while nobody
250
+ # reads it would block and be misreported as a timeout.
251
+ with tempfile.TemporaryFile() as out_f, tempfile.TemporaryFile() as err_f:
252
+ child = subprocess.Popen(
253
+ [sys.executable, *args],
254
+ stdin=subprocess.DEVNULL,
255
+ stdout=out_f,
256
+ stderr=err_f,
257
+ preexec_fn=preexec,
258
+ creationflags=creationflags,
259
+ )
260
+ job = _windows_job_memory_cap(child.pid, cap_bytes)
261
+ try:
262
+ reason = _watch_probe(child, psutil, cap_bytes, timeout)
263
+ finally:
264
+ _close_job(job)
265
+ if reason:
266
+ return False, reason
267
+ out_f.seek(0)
268
+ err_f.seek(0)
269
+ text = out_f.read().decode("utf-8", "replace")
270
+ err = err_f.read().decode("utf-8", "replace")
271
+ if child.returncode == 0 and marker in text:
272
+ return True, ""
273
+ tail = err.strip().splitlines()
274
+ detail = tail[-1] if tail else f"exit code {child.returncode}"
275
+ capped = " (under a hard memory cap)" if job is not None else ""
276
+ return False, f"probe failed{capped}: {detail[:300]}"
277
+
278
+
279
+ def _watch_probe(child, psutil, cap_bytes, timeout) -> str:
280
+ """Wait for *child*; kill it on the cap or the deadline. Returns a reason or ""."""
281
+ watched = None
282
+ if psutil is not None:
283
+ try:
284
+ watched = psutil.Process(child.pid)
285
+ except Exception:
286
+ watched = None
287
+ deadline = time.monotonic() + timeout
288
+ peak = 0
289
+ reason = ""
290
+ while child.poll() is None:
291
+ if watched is not None:
292
+ try:
293
+ peak = max(peak, _private_bytes(watched))
294
+ except Exception:
295
+ pass
296
+ if peak > cap_bytes:
297
+ reason = (f"probe exceeded its memory cap "
298
+ f"({peak / 1024 ** 3:.1f} GB > {cap_bytes / 1024 ** 3:.1f} GB)")
299
+ break
300
+ if time.monotonic() >= deadline:
301
+ reason = f"probe timed out after {timeout:.0f}s"
302
+ break
303
+ time.sleep(_PROBE_POLL_SECONDS)
304
+ if reason:
305
+ child.kill()
306
+ try:
307
+ child.wait(timeout=30)
308
+ except Exception:
309
+ pass
310
+ return reason
311
+
312
+
313
+ class _OwnerLock:
314
+ """Non-blocking, process-lifetime exclusive lock on one file.
315
+
316
+ chromadb's PersistentClient is not safe for several processes at once, yet
317
+ every C3 MCP server for a project opened the same store. The OS drops the
318
+ lock when the holder dies, so a crashed server never strands it.
319
+ """
320
+
321
+ def __init__(self, path: Path):
322
+ self.path = path
323
+ self._fh = None
324
+
325
+ @property
326
+ def held(self) -> bool:
327
+ return self._fh is not None
328
+
329
+ def try_acquire(self) -> bool:
330
+ if self._fh is not None:
331
+ return True
332
+ self.path.parent.mkdir(parents=True, exist_ok=True)
333
+ fh = open(self.path, "ab") # never read/written; fd exists to hold the lock
334
+ try:
335
+ if os.name == "nt":
336
+ import msvcrt
337
+ fh.seek(0)
338
+ msvcrt.locking(fh.fileno(), msvcrt.LK_NBLCK, 1)
339
+ else:
340
+ import fcntl
341
+ fcntl.flock(fh.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
342
+ except OSError:
343
+ fh.close()
344
+ return False
345
+ self._fh = fh
346
+ return True
347
+
348
+ def release(self) -> None:
349
+ fh, self._fh = self._fh, None
350
+ if fh is None:
351
+ return
352
+ try:
353
+ if os.name == "nt":
354
+ import msvcrt
355
+ fh.seek(0)
356
+ msvcrt.locking(fh.fileno(), msvcrt.LK_UNLCK, 1)
357
+ else:
358
+ import fcntl
359
+ fcntl.flock(fh.fileno(), fcntl.LOCK_UN)
360
+ except OSError:
361
+ pass
362
+ finally:
363
+ fh.close()
364
+
31
365
 
32
366
  class EmbeddingIndex:
33
367
  """Semantic code search via embeddings over CodeIndex chunks."""
@@ -62,6 +396,8 @@ class EmbeddingIndex:
62
396
  self._chroma_client = None
63
397
  self._collection = None
64
398
  self._available = False
399
+ self._unavailable_why = "" # set when chromadb is installed but unusable
400
+ self._owner_lock = _OwnerLock(self._index_dir / _OWNER_LOCK_FILE)
65
401
  self._ollama_ok = False
66
402
  self._ollama_up = False
67
403
  self._model_ok = False
@@ -112,9 +448,17 @@ class EmbeddingIndex:
112
448
  self._open_chroma()
113
449
  self._available = True
114
450
  self._drop_legacy_collection()
115
- except Exception as e:
451
+ except StoreBusyError as e:
452
+ log.info("embedding index disabled in this process: %s", e)
453
+ self._unavailable_why = str(e)
454
+ self._available = False
455
+ except ImportError as e:
116
456
  log.debug("chromadb unavailable for embedding index: %s", e)
117
457
  self._available = False
458
+ except Exception as e:
459
+ log.warning("embedding store unusable: %s", e)
460
+ self._unavailable_why = f"embedding store unusable: {e}"
461
+ self._available = False
118
462
 
119
463
  try:
120
464
  self._ollama_up = self.ollama.is_available(timeout=2)
@@ -144,7 +488,19 @@ class EmbeddingIndex:
144
488
  from chromadb.config import Settings
145
489
 
146
490
  persist_dir = self._index_dir / "chromadb"
491
+ if not self._owner_lock.try_acquire():
492
+ raise StoreBusyError(
493
+ "embedding store is in use by another C3 server for this "
494
+ "project; dense search is off in this one")
147
495
  persist_dir.mkdir(parents=True, exist_ok=True)
496
+ if not _retried and self._store_has_segments(persist_dir):
497
+ ok, why = self._probe_store_isolated(persist_dir)
498
+ if not ok:
499
+ log.warning(
500
+ "embedding store failed its isolated probe (%s); "
501
+ "quarantining and rebuilding", why)
502
+ self._quarantine_store(persist_dir)
503
+ persist_dir.mkdir(parents=True, exist_ok=True)
148
504
  self._chroma_client = chromadb.PersistentClient(
149
505
  path=str(persist_dir),
150
506
  settings=Settings(anonymized_telemetry=False),
@@ -179,17 +535,90 @@ class EmbeddingIndex:
179
535
  pass
180
536
  gc.collect()
181
537
 
538
+ @staticmethod
539
+ def _store_has_segments(persist_dir: Path) -> bool:
540
+ """True when the store holds at least one on-disk HNSW segment."""
541
+ try:
542
+ return any(persist_dir.glob("*/header.bin"))
543
+ except OSError:
544
+ return False
545
+
546
+ @staticmethod
547
+ def _store_signature(persist_dir: Path) -> dict:
548
+ sig = {}
549
+ for f in sorted(persist_dir.rglob("*")):
550
+ try:
551
+ if f.is_file():
552
+ st = f.stat()
553
+ sig[f.relative_to(persist_dir).as_posix()] = [st.st_size, st.st_mtime_ns]
554
+ except OSError:
555
+ continue
556
+ return sig
557
+
558
+ def _probe_store_isolated(self, persist_dir: Path) -> tuple:
559
+ """Open the store in a capped child first; skip if unchanged since a pass.
560
+
561
+ Returns ``(ok, reason)``. A pass is remembered against the files' sizes
562
+ and mtimes, so an untouched store is not re-probed on every start.
563
+ """
564
+ sig = self._store_signature(persist_dir)
565
+ ok_file = self._index_dir / _PROBE_OK_FILE
566
+ try:
567
+ if json.loads(ok_file.read_text(encoding="utf-8")) == sig:
568
+ return True, ""
569
+ except Exception:
570
+ pass
571
+ store_bytes = sum(v[0] for v in sig.values())
572
+ cap = max(_PROBE_MIN_CAP_BYTES, _PROBE_CAP_STORE_MULTIPLE * store_bytes)
573
+ started = time.monotonic()
574
+ try:
575
+ ok, why = run_isolated_probe(
576
+ ["-c", _PROBE_CHILD_CODE, str(persist_dir), COLLECTION_NAME],
577
+ cap_bytes=cap,
578
+ )
579
+ except Exception as e: # could not spawn: the in-process probe still runs
580
+ log.warning("isolated store probe could not run (%s); skipping", e)
581
+ return True, ""
582
+ log.info("isolated store probe: ok=%s in %.1fs %s",
583
+ ok, time.monotonic() - started, why)
584
+ if ok:
585
+ try:
586
+ # The child may have compacted the store; record what it left.
587
+ ok_file.write_text(json.dumps(self._store_signature(persist_dir)),
588
+ encoding="utf-8")
589
+ except OSError:
590
+ pass
591
+ return ok, why
592
+
593
+ def _prune_quarantines(self) -> None:
594
+ prune_quarantines(self._index_dir)
595
+
182
596
  def _quarantine_store(self, persist_dir: Path) -> None:
183
597
  """Move a corrupt store and its hash file aside for post-mortem.
184
598
 
185
599
  The hashes go with it: they claim vectors that the fresh store does not
186
600
  have, so leaving them behind would suppress the very rebuild this is
187
601
  meant to trigger.
602
+
603
+ Rename only, never copy. When another process still had the files open,
604
+ ``shutil.move`` fell back to copying the whole store and then failed to
605
+ delete the original, so every session left one more full copy behind
606
+ and reopened the same corrupt store (issue #180). A failed rename now
607
+ raises and the index degrades for this process.
188
608
  """
189
609
  self._close_chroma()
190
- dest = self._index_dir / f"quarantine_corrupt_{datetime.now():%Y%m%d_%H%M%S}"
610
+ dest = self._index_dir / f"quarantine_corrupt_{datetime.now():%Y%m%d_%H%M%S_%f}"
191
611
  dest.mkdir(parents=True, exist_ok=True)
192
- shutil.move(str(persist_dir), str(dest / persist_dir.name))
612
+ try:
613
+ os.rename(str(persist_dir), str(dest / persist_dir.name))
614
+ except OSError as e:
615
+ try:
616
+ dest.rmdir()
617
+ except OSError:
618
+ pass
619
+ raise RuntimeError(
620
+ f"could not move the corrupt embedding store aside ({e})") from e
621
+ (self._index_dir / _PROBE_OK_FILE).unlink(missing_ok=True)
193
622
  try:
194
623
  if self._hash_file.exists():
195
624
  shutil.move(str(self._hash_file), str(dest / self._hash_file.name))
@@ -197,6 +626,7 @@ class EmbeddingIndex:
197
626
  self._hash_file.unlink(missing_ok=True)
198
627
  self._file_hashes = {}
199
628
  log.warning("quarantined corrupt embedding store to %s", dest)
629
+ self._prune_quarantines()
200
630
 
201
631
  def _drop_legacy_collection(self) -> None:
202
632
  """Best-effort removal of the pre-2.108.0 collection and hash file.
@@ -290,7 +720,7 @@ class EmbeddingIndex:
290
720
  def unavailable_reason(self) -> str:
291
721
  """Human-readable reason ``ready`` is False (after probe/init)."""
292
722
  if not self._available:
293
- return "chromadb not installed"
723
+ return self._unavailable_why or "chromadb not installed"
294
724
  if not self._ollama_up:
295
725
  return "Ollama not reachable"
296
726
  if not self._model_ok: