conexus 5.4.0__tar.gz → 5.4.2__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 (248) hide show
  1. {conexus-5.4.0 → conexus-5.4.2}/PKG-INFO +1 -1
  2. {conexus-5.4.0 → conexus-5.4.2}/docs/rdr/README.md +1 -0
  3. {conexus-5.4.0 → conexus-5.4.2}/mcpb/pyproject.toml +2 -2
  4. {conexus-5.4.0 → conexus-5.4.2}/pyproject.toml +1 -1
  5. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t2/__init__.py +71 -3
  6. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t2/aspect_extraction_queue.py +162 -117
  7. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/indexer.py +15 -0
  8. {conexus-5.4.0 → conexus-5.4.2}/.beads/README.md +0 -0
  9. {conexus-5.4.0 → conexus-5.4.2}/.gitignore +0 -0
  10. {conexus-5.4.0 → conexus-5.4.2}/LICENSE +0 -0
  11. {conexus-5.4.0 → conexus-5.4.2}/README.md +0 -0
  12. {conexus-5.4.0 → conexus-5.4.2}/conexus/README.md +0 -0
  13. {conexus-5.4.0 → conexus-5.4.2}/conexus/agents/_shared/README.md +0 -0
  14. {conexus-5.4.0 → conexus-5.4.2}/conexus/daemon/com.nexus.t2.plist +0 -0
  15. {conexus-5.4.0 → conexus-5.4.2}/conexus/daemon/com.nexus.t3.plist +0 -0
  16. {conexus-5.4.0 → conexus-5.4.2}/conexus/daemon/nexus-t2.service +0 -0
  17. {conexus-5.4.0 → conexus-5.4.2}/conexus/daemon/nexus-t3.service +0 -0
  18. {conexus-5.4.0 → conexus-5.4.2}/conexus/hooks/scripts/routing/README.md +0 -0
  19. {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/abstract-themes.yml +0 -0
  20. {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/analyze-default.yml +0 -0
  21. {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/citation-traversal.yml +0 -0
  22. {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/debug-default.yml +0 -0
  23. {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/document-default.yml +0 -0
  24. {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/find-by-author.yml +0 -0
  25. {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/hybrid-factual-lookup.yml +0 -0
  26. {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/plan-author-default.yml +0 -0
  27. {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/plan-inspect-default.yml +0 -0
  28. {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/plan-inspect-dimensions.yml +0 -0
  29. {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/plan-promote-propose.yml +0 -0
  30. {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/research-default.yml +0 -0
  31. {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/review-default.yml +0 -0
  32. {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/traverse-then-generate.yml +0 -0
  33. {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/type-scoped-search.yml +0 -0
  34. {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/dimensions.yml +0 -0
  35. {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/purposes.yml +0 -0
  36. {conexus-5.4.0 → conexus-5.4.2}/data/calibration/rdr-109/README.md +0 -0
  37. {conexus-5.4.0 → conexus-5.4.2}/docs/README.md +0 -0
  38. {conexus-5.4.0 → conexus-5.4.2}/docs/migration/README.md +0 -0
  39. {conexus-5.4.0 → conexus-5.4.2}/dt/scripts/Index Current Group in nx.applescript +0 -0
  40. {conexus-5.4.0 → conexus-5.4.2}/dt/scripts/Index Selection in nx (Knowledge).applescript +0 -0
  41. {conexus-5.4.0 → conexus-5.4.2}/dt/scripts/Index Selection in nx.applescript +0 -0
  42. {conexus-5.4.0 → conexus-5.4.2}/mcpb/src/server.py +0 -0
  43. {conexus-5.4.0 → conexus-5.4.2}/scripts/cron/README.md +0 -0
  44. {conexus-5.4.0 → conexus-5.4.2}/scripts/launchd/README.md +0 -0
  45. {conexus-5.4.0 → conexus-5.4.2}/sn/README.md +0 -0
  46. {conexus-5.4.0 → conexus-5.4.2}/sn/hooks/scripts/routing/README.md +0 -0
  47. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/__init__.py +0 -0
  48. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/_git_hooks_meta.py +0 -0
  49. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/_locking.py +0 -0
  50. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/_mineru_pid.py +0 -0
  51. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/_session_end_launcher.py +0 -0
  52. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/aspect_extractor.py +0 -0
  53. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/aspect_promotion.py +0 -0
  54. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/aspect_readers.py +0 -0
  55. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/aspect_worker.py +0 -0
  56. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/bib_enricher.py +0 -0
  57. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/bib_enricher_openalex.py +0 -0
  58. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/bib_extractor.py +0 -0
  59. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/AGENTS.md +0 -0
  60. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/__init__.py +0 -0
  61. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/auto_linker.py +0 -0
  62. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/catalog.py +0 -0
  63. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/catalog_backup.py +0 -0
  64. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/catalog_db.py +0 -0
  65. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/catalog_docs.py +0 -0
  66. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/catalog_git.py +0 -0
  67. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/catalog_links.py +0 -0
  68. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/catalog_spans.py +0 -0
  69. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/catalog_sync.py +0 -0
  70. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/catalog_writes.py +0 -0
  71. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/collection_name.py +0 -0
  72. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/collections_owner_backfill.py +0 -0
  73. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/consolidation.py +0 -0
  74. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/dedupe.py +0 -0
  75. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/event_log.py +0 -0
  76. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/events.py +0 -0
  77. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/link_generator.py +0 -0
  78. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/manifest_backfill.py +0 -0
  79. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/orphan_backfill.py +0 -0
  80. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/projector.py +0 -0
  81. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/store_hook.py +0 -0
  82. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/synthesizer.py +0 -0
  83. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/tumbler.py +0 -0
  84. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/types.py +0 -0
  85. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/checkpoint.py +0 -0
  86. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/chunker.py +0 -0
  87. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/classifier.py +0 -0
  88. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/cli.py +0 -0
  89. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/code_indexer.py +0 -0
  90. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/collection_audit.py +0 -0
  91. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/collection_health.py +0 -0
  92. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/collection_rename.py +0 -0
  93. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/__init__.py +0 -0
  94. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/_helpers.py +0 -0
  95. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/_migration_prompt.py +0 -0
  96. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/_provision.py +0 -0
  97. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/aspects.py +0 -0
  98. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/catalog.py +0 -0
  99. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/collection.py +0 -0
  100. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/command_context.py +0 -0
  101. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/config_cmd.py +0 -0
  102. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/console.py +0 -0
  103. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/context_cmd.py +0 -0
  104. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/daemon.py +0 -0
  105. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/doc.py +0 -0
  106. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/doctor.py +0 -0
  107. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/dt.py +0 -0
  108. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/enrich.py +0 -0
  109. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/hook.py +0 -0
  110. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/hooks.py +0 -0
  111. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/index.py +0 -0
  112. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/memory.py +0 -0
  113. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/mineru.py +0 -0
  114. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/plan.py +0 -0
  115. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/rdr.py +0 -0
  116. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/scratch.py +0 -0
  117. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/search_cmd.py +0 -0
  118. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/store.py +0 -0
  119. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/t3.py +0 -0
  120. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/taxonomy_cmd.py +0 -0
  121. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/tier_status.py +0 -0
  122. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/upgrade.py +0 -0
  123. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/config.py +0 -0
  124. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/__init__.py +0 -0
  125. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/app.py +0 -0
  126. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/config.py +0 -0
  127. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/routes/__init__.py +0 -0
  128. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/routes/activity.py +0 -0
  129. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/routes/campaigns.py +0 -0
  130. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/routes/health.py +0 -0
  131. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/routes/partials.py +0 -0
  132. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/static/alpine.min.js +0 -0
  133. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/static/console.css +0 -0
  134. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/static/htmx.min.js +0 -0
  135. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/static/pico.min.css +0 -0
  136. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/templates/activity/_detail.html +0 -0
  137. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/templates/activity/_stream.html +0 -0
  138. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/templates/activity/index.html +0 -0
  139. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/templates/base.html +0 -0
  140. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/templates/campaigns/detail.html +0 -0
  141. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/templates/campaigns/index.html +0 -0
  142. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/templates/health/_cards.html +0 -0
  143. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/templates/health/index.html +0 -0
  144. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/watchers.py +0 -0
  145. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/context.py +0 -0
  146. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/corpus.py +0 -0
  147. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/cross_encoder.py +0 -0
  148. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/daemon/T2_DAEMON_WIP.md +0 -0
  149. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/daemon/__init__.py +0 -0
  150. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/daemon/discovery.py +0 -0
  151. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/daemon/t2_client.py +0 -0
  152. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/daemon/t2_daemon.py +0 -0
  153. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/daemon/t3_client.py +0 -0
  154. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/daemon/t3_daemon.py +0 -0
  155. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/AGENTS.md +0 -0
  156. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/__init__.py +0 -0
  157. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/chroma_quotas.py +0 -0
  158. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/local_ef.py +0 -0
  159. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/migrations.py +0 -0
  160. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t1.py +0 -0
  161. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t2/_tuning.py +0 -0
  162. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t2/catalog.py +0 -0
  163. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t2/catalog_taxonomy.py +0 -0
  164. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t2/chash_index.py +0 -0
  165. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t2/document_aspects.py +0 -0
  166. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t2/memory_store.py +0 -0
  167. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t2/plan_library.py +0 -0
  168. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t2/telemetry.py +0 -0
  169. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t3.py +0 -0
  170. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t3_reidentify.py +0 -0
  171. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/devonthink.py +0 -0
  172. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/doc/__init__.py +0 -0
  173. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/doc/_common.py +0 -0
  174. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/doc/citations.py +0 -0
  175. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/doc/ref_scanner.py +0 -0
  176. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/doc/render.py +0 -0
  177. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/doc/resolvers.py +0 -0
  178. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/doc/resolvers_corpus.py +0 -0
  179. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/doc/tokens.py +0 -0
  180. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/doc_indexer.py +0 -0
  181. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/doctor_search.py +0 -0
  182. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/dropped_writes.py +0 -0
  183. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/errors.py +0 -0
  184. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/exporter.py +0 -0
  185. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/filters.py +0 -0
  186. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/formatters.py +0 -0
  187. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/frecency.py +0 -0
  188. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/glossary.py +0 -0
  189. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/health.py +0 -0
  190. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/hook_registry.py +0 -0
  191. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/hooks.py +0 -0
  192. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/index_context.py +0 -0
  193. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/indexer_utils.py +0 -0
  194. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/languages.py +0 -0
  195. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/logging_setup.py +0 -0
  196. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/mcp/AGENTS.md +0 -0
  197. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/mcp/__init__.py +0 -0
  198. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/mcp/_first_run.py +0 -0
  199. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/mcp/_t1_state.py +0 -0
  200. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/mcp/catalog.py +0 -0
  201. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/mcp/core.py +0 -0
  202. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/mcp/plan_cache_registry.py +0 -0
  203. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/mcp_infra.py +0 -0
  204. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/mcp_server.py +0 -0
  205. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/md_chunker.py +0 -0
  206. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/merge_candidates.py +0 -0
  207. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/metadata_schema.py +0 -0
  208. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/name_canaries.py +0 -0
  209. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/operators/aspect_sql.py +0 -0
  210. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/operators/dispatch.py +0 -0
  211. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/pdf_chunker.py +0 -0
  212. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/pdf_extractor.py +0 -0
  213. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/phase_review_sentinel.py +0 -0
  214. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/pipeline_buffer.py +0 -0
  215. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/pipeline_stages.py +0 -0
  216. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/__init__.py +0 -0
  217. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/bundle.py +0 -0
  218. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/loader.py +0 -0
  219. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/match.py +0 -0
  220. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/matcher.py +0 -0
  221. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/promote.py +0 -0
  222. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/purposes.py +0 -0
  223. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/repair.py +0 -0
  224. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/runner.py +0 -0
  225. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/schema.py +0 -0
  226. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/scope.py +0 -0
  227. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/seed_loader.py +0 -0
  228. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/session_cache.py +0 -0
  229. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/prose_indexer.py +0 -0
  230. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/registry.py +0 -0
  231. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/repo_identity.py +0 -0
  232. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/repos.py +0 -0
  233. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/retry.py +0 -0
  234. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/ripgrep_cache.py +0 -0
  235. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/routing_stats.py +0 -0
  236. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/salience.py +0 -0
  237. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/scoring.py +0 -0
  238. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/search_clusterer.py +0 -0
  239. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/search_engine.py +0 -0
  240. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/session.py +0 -0
  241. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/stage_timers.py +0 -0
  242. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/storage_boundary_lint.py +0 -0
  243. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/taxonomy.py +0 -0
  244. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/taxonomy_backfill.py +0 -0
  245. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/ttl.py +0 -0
  246. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/types.py +0 -0
  247. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/util/__init__.py +0 -0
  248. {conexus-5.4.0 → conexus-5.4.2}/src/nexus/util/process_group.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conexus
3
- Version: 5.4.0
3
+ Version: 5.4.2
4
4
  Summary: Self-hosted semantic search and knowledge management for LLM-driven development
5
5
  Project-URL: Homepage, https://github.com/Hellblazer/nexus
6
6
  Project-URL: Repository, https://github.com/Hellblazer/nexus
@@ -153,6 +153,7 @@ An RDR (Research-Design-Review) is a short document that records a technical dec
153
153
  | [RDR-135](rdr-135-windowed-aspect-extraction.md) | Windowed Aspect Extraction with Cross-Window Merge: Stop Whole-Paper Single-Shot Extraction from Degrading on Long Inputs | Architecture | Draft | 2026-05-27 |
154
154
  | [RDR-136](rdr-136-messagedisplay-projection-substrate.md) | Display Projection via the MessageDisplay Hook: One-Way Output Mirroring and Routing-Marker Hiding | Architecture | Draft | 2026-05-27 |
155
155
  | [RDR-137](rdr-137-eliminate-repos-json.md) | Eliminate repos.json: Catalog as the Canonical Repo→Collection Source of Truth | Architecture | Closed | 2026-05-28 |
156
+ | [RDR-138](rdr-138-rename-cascade-aspect-worker-coordination.md) | Rename-Cascade / Aspect-Worker Coordination: Serialize T2 Collection Rename Against In-Flight Aspect Extraction | Architecture | Accepted | 2026-05-29 |
156
157
 
157
158
  > **Scrapped 2026-05-19 (RDR-110-119 arc).** Bundled the storage-substrate split with new abstractions (tuplespace, ORB, host-trust, surfaces-as-tuples, UI fabric); scope discipline failed across nine RDRs and 67 stranded beads. Files preserved as tombstones per the "never delete RDR files" rule. Postmortem: [docs/postmortem/2026-05-16-rdr110-113-remediation-chain.md](../postmortem/2026-05-16-rdr110-113-remediation-chain.md). Active substrate work continues as [RDR-120](rdr-120-storage-substrate-split.md) with an explicit moratorium on co-shipped consumers. Numbers RDR-114 through RDR-117 are unused on `main` (drafted on feature branches that never merged).
158
159
 
@@ -1,10 +1,10 @@
1
1
  [project]
2
2
  name = "conexus-mcpb"
3
- version = "5.4.0"
3
+ version = "5.4.2"
4
4
  description = "Conexus packaged as Claude Desktop .mcpb (Desktop Extension)"
5
5
  requires-python = ">=3.12"
6
6
  dependencies = [
7
- "conexus>=5.4.0",
7
+ "conexus>=5.4.2",
8
8
  ]
9
9
 
10
10
  [tool.uv]
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "conexus"
7
- version = "5.4.0"
7
+ version = "5.4.2"
8
8
  description = "Self-hosted semantic search and knowledge management for LLM-driven development"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  requires-python = ">=3.12,<3.14"
@@ -59,6 +59,7 @@ extend the tier.
59
59
 
60
60
  from __future__ import annotations
61
61
 
62
+ import threading
62
63
  from pathlib import Path
63
64
  from typing import TYPE_CHECKING, Any
64
65
 
@@ -306,6 +307,38 @@ class T2Database:
306
307
  if effective:
307
308
  T2Database.bootstrap_schema(path)
308
309
 
310
+ # ── RDR-138 T1.1 (nexus-tgzvt): process-wide rename coordination lock ──
311
+ #
312
+ # RENAME_LOCK is the OUTERMOST lock in the daemon process. It
313
+ # serializes ``rename_collection_cascade`` against every queue and
314
+ # aspect mutator, closing the rename-cascade vs aspect-worker race
315
+ # (Gaps 1-3 per the RDR).
316
+ #
317
+ # Lock type: ``threading.RLock`` (reentrant), NOT ``threading.Lock``.
318
+ # Rationale: T1.2 will guard ``claim_batch`` AND the inner
319
+ # ``claim_next`` it calls in a loop. A plain Lock would self-deadlock
320
+ # when the outer claim_batch acquire re-enters for each claim_next
321
+ # call. RLock allows the same thread to acquire again without blocking.
322
+ # An alternative shape (unlocked ``_claim_next_locked`` helper) is
323
+ # documented in ``AspectExtractionQueue`` but the RLock approach is
324
+ # chosen for its simplicity.
325
+ #
326
+ # Lock ordering (forward constraint for T1.2 authors):
327
+ # RENAME_LOCK -> per-store self._lock (RENAME_LOCK acquired FIRST)
328
+ # NEVER acquire RENAME_LOCK while already inside a self._lock region.
329
+ #
330
+ # The daemon runs exactly ONE T2Database instance (verified by
331
+ # t2_daemon.py's ``_build_dispatch_table`` receiving a single
332
+ # ``self._t2db``). The lock is instance-held: tests that construct
333
+ # T2Database directly each get their own lock, isolating them from each
334
+ # other. Stand-alone AspectExtractionQueue construction (outside
335
+ # T2Database) falls back to its own RLock via the default parameter.
336
+ #
337
+ # The cascade (rename_collection_cascade) bypasses all per-store
338
+ # self._lock regions by design — it uses its own dedicated connection.
339
+ # It acquires only RENAME_LOCK.
340
+ self.RENAME_LOCK: threading.RLock = threading.RLock()
341
+
309
342
  # ── Construct domain stores ───────────────────────────────────
310
343
  self.memory: MemoryStore = MemoryStore(path)
311
344
  self.plans: PlanLibrary = PlanLibrary(path)
@@ -324,7 +357,11 @@ class T2Database:
324
357
  # RDR-089 follow-up (nexus-qeo8): durable queue feeding the
325
358
  # async aspect-extraction worker. The hook fires fast (just
326
359
  # an enqueue); the worker drains in a background thread.
327
- self.aspect_queue: AspectExtractionQueue = AspectExtractionQueue(path)
360
+ # RDR-138 T1.1: inject RENAME_LOCK so the queue shares the same
361
+ # lock instance as the cascade. T1.2 will wrap mutator bodies.
362
+ self.aspect_queue: AspectExtractionQueue = AspectExtractionQueue(
363
+ path, rename_lock=self.RENAME_LOCK
364
+ )
328
365
 
329
366
  # RDR-120 P5.A.1 (nexus-9zmpl): catalog is the eighth domain
330
367
  # store. Constructed lazily via the ``catalog`` property so
@@ -532,7 +569,27 @@ class T2Database:
532
569
  ``_conn`` is a private test-seam parameter. Production callers
533
570
  omit it; the method opens a fresh dedicated connection. Tests
534
571
  pass a wrapper object to inject mid-cascade failures.
572
+
573
+ RDR-138 T1.1 (nexus-tgzvt): acquires ``self.RENAME_LOCK`` for the
574
+ ENTIRE method body — including the connection open, BEGIN, all
575
+ UPDATEs, COMMIT, and connection close. This serializes the cascade
576
+ against every queue/aspect mutator that T1.2 will also guard with
577
+ the same lock. Lock is held across the full SQLite transaction to
578
+ close Gaps 1-3 (aspect-worker observes a consistent view).
579
+ Lock ordering: RENAME_LOCK is the outermost lock. The cascade
580
+ bypasses all per-store ``self._lock`` regions by design (own conn).
535
581
  """
582
+ with self.RENAME_LOCK:
583
+ return self._rename_collection_cascade_locked(old=old, new=new, _conn=_conn)
584
+
585
+ def _rename_collection_cascade_locked(
586
+ self,
587
+ *,
588
+ old: str,
589
+ new: str,
590
+ _conn: "sqlite3.Connection | None" = None,
591
+ ) -> dict[str, int]:
592
+ """Inner implementation — called only while RENAME_LOCK is held."""
536
593
  counts: dict[str, int] = {
537
594
  "chash": 0,
538
595
  "aspects": 0,
@@ -1005,9 +1062,20 @@ class T2Database:
1005
1062
  idempotent, so a reclaim-driven re-extraction after a crash
1006
1063
  between the two writes simply re-upserts — no duplicate, no stuck
1007
1064
  row.
1065
+
1066
+ RDR-138 T1.2 (nexus-ra2vj): wraps the ENTIRE call — both the
1067
+ ``document_aspects.upsert`` AND the ``aspect_queue.mark_done`` —
1068
+ under ONE ``RENAME_LOCK`` acquisition. This closes Gap 3: a
1069
+ ``rename_collection_cascade`` cannot interleave between the two
1070
+ writes (which would rename the document_aspects row under the OLD
1071
+ collection name before mark_done can clear the queue row, leaving
1072
+ an orphaned queue row under OLD). The ``mark_done`` call
1073
+ re-acquires RENAME_LOCK via the now-guarded mutator; the RLock
1074
+ makes the re-entrant acquisition safe.
1008
1075
  """
1009
1076
  from nexus.db.t2.document_aspects import AspectRecord
1010
1077
  record = AspectRecord(**record_fields)
1011
- upserted = self.document_aspects.upsert(record)
1012
- self.aspect_queue.mark_done(record.collection, record.source_path)
1078
+ with self.RENAME_LOCK:
1079
+ upserted = self.document_aspects.upsert(record)
1080
+ self.aspect_queue.mark_done(record.collection, record.source_path)
1013
1081
  return upserted
@@ -146,10 +146,39 @@ class AspectExtractionQueue:
146
146
  """Owns the ``aspect_extraction_queue`` table.
147
147
 
148
148
  See module docstring for state transitions and locking contract.
149
+
150
+ Locking hierarchy (RDR-138 T1.1, nexus-tgzvt):
151
+
152
+ ``rename_lock`` (process-wide ``threading.RLock`` owned by
153
+ ``T2Database``) is the OUTERMOST lock — it must be acquired BEFORE any
154
+ ``self._lock`` region, NEVER while ``self._lock`` is already held.
155
+ Ordering: ``rename_lock`` -> ``self._lock``.
156
+
157
+ ``rename_lock`` is an ``RLock`` (not a plain ``Lock``) because
158
+ ``claim_batch`` calls ``claim_next`` in a loop. When T1.2 wraps both
159
+ with ``rename_lock``, a non-reentrant lock would self-deadlock on the
160
+ inner ``claim_next`` call. The ``RLock`` allows the same thread to
161
+ acquire it again safely. An alternative is an unlocked
162
+ ``_claim_next_locked()`` helper (callable while lock held), but
163
+ ``RLock`` is the simpler shape here given the bounded call depth.
149
164
  """
150
165
 
151
- def __init__(self, path: Path) -> None:
166
+ def __init__(
167
+ self,
168
+ path: Path,
169
+ *,
170
+ rename_lock: "threading.RLock | None" = None,
171
+ ) -> None:
152
172
  self._lock = threading.Lock()
173
+ # RDR-138 T1.1 (nexus-tgzvt): process-wide outermost lock shared
174
+ # with T2Database and the rename_collection_cascade path. Injected
175
+ # by T2Database at construction so all three paths share the SAME
176
+ # lock instance. Falls back to a new RLock when constructed
177
+ # stand-alone (tests / direct construction outside T2Database).
178
+ # T1.2 will wrap every queue mutator body with this lock.
179
+ self.rename_lock: threading.RLock = (
180
+ rename_lock if rename_lock is not None else threading.RLock()
181
+ )
153
182
  self.conn = sqlite3.connect(str(path), check_same_thread=False)
154
183
  # nexus-v4m7y: bumped from 5s to 30s. Nine T2 stores hold their own
155
184
  # sqlite3.Connection against memory.db. Under WAL mode, only one
@@ -273,15 +302,16 @@ class AspectExtractionQueue:
273
302
  if not source_path:
274
303
  raise ValueError("source_path must not be empty")
275
304
  now = datetime.now(UTC).isoformat()
276
- with self._lock:
277
- self.conn.execute(
278
- "INSERT OR REPLACE INTO aspect_extraction_queue "
279
- "(collection, source_path, doc_id, content_hash, content, "
280
- " status, retry_count, enqueued_at, last_attempt_at, last_error) "
281
- "VALUES (?, ?, ?, ?, ?, 'pending', 0, ?, NULL, NULL)",
282
- (collection, source_path, doc_id, content_hash, content, now),
283
- )
284
- self.conn.commit()
305
+ with self.rename_lock:
306
+ with self._lock:
307
+ self.conn.execute(
308
+ "INSERT OR REPLACE INTO aspect_extraction_queue "
309
+ "(collection, source_path, doc_id, content_hash, content, "
310
+ " status, retry_count, enqueued_at, last_attempt_at, last_error) "
311
+ "VALUES (?, ?, ?, ?, ?, 'pending', 0, ?, NULL, NULL)",
312
+ (collection, source_path, doc_id, content_hash, content, now),
313
+ )
314
+ self.conn.commit()
285
315
 
286
316
  def claim_next(self) -> QueueRow | None:
287
317
  """Atomically claim the oldest pending row.
@@ -308,40 +338,41 @@ class AspectExtractionQueue:
308
338
  sequences across separate connections.
309
339
  """
310
340
  now = datetime.now(UTC).isoformat()
311
- for _ in range(_MAX_CAS_RETRIES):
312
- with self._lock:
313
- row = self.conn.execute(
314
- "SELECT collection, source_path, content_hash, content, "
315
- " retry_count, doc_id "
316
- "FROM aspect_extraction_queue "
317
- "WHERE status = 'pending' "
318
- "ORDER BY enqueued_at ASC, source_path ASC "
319
- "LIMIT 1"
320
- ).fetchone()
321
- if row is None:
322
- return None
323
- collection, source_path, content_hash, content, retry_count, doc_id = row
324
- cur = self.conn.execute(
325
- "UPDATE aspect_extraction_queue "
326
- "SET status = 'in_progress', last_attempt_at = ? "
327
- "WHERE collection = ? "
328
- " AND source_path = ? "
329
- " AND status = 'pending'",
330
- (now, collection, source_path),
331
- )
332
- self.conn.commit()
333
- if cur.rowcount == 1:
334
- return QueueRow(
335
- collection=collection,
336
- source_path=source_path,
337
- content_hash=content_hash,
338
- content=content,
339
- retry_count=retry_count,
340
- doc_id=doc_id or "",
341
+ with self.rename_lock:
342
+ for _ in range(_MAX_CAS_RETRIES):
343
+ with self._lock:
344
+ row = self.conn.execute(
345
+ "SELECT collection, source_path, content_hash, content, "
346
+ " retry_count, doc_id "
347
+ "FROM aspect_extraction_queue "
348
+ "WHERE status = 'pending' "
349
+ "ORDER BY enqueued_at ASC, source_path ASC "
350
+ "LIMIT 1"
351
+ ).fetchone()
352
+ if row is None:
353
+ return None
354
+ collection, source_path, content_hash, content, retry_count, doc_id = row
355
+ cur = self.conn.execute(
356
+ "UPDATE aspect_extraction_queue "
357
+ "SET status = 'in_progress', last_attempt_at = ? "
358
+ "WHERE collection = ? "
359
+ " AND source_path = ? "
360
+ " AND status = 'pending'",
361
+ (now, collection, source_path),
341
362
  )
342
- # rowcount == 0 → another process / thread won the
343
- # CAS; loop and try a different row.
344
- return None
363
+ self.conn.commit()
364
+ if cur.rowcount == 1:
365
+ return QueueRow(
366
+ collection=collection,
367
+ source_path=source_path,
368
+ content_hash=content_hash,
369
+ content=content,
370
+ retry_count=retry_count,
371
+ doc_id=doc_id or "",
372
+ )
373
+ # rowcount == 0 → another process / thread won the
374
+ # CAS; loop and try a different row.
375
+ return None
345
376
 
346
377
  def claim_batch(self, limit: int) -> list[QueueRow]:
347
378
  """Claim up to ``limit`` pending rows in FIFO order.
@@ -358,11 +389,12 @@ class AspectExtractionQueue:
358
389
  if limit <= 0:
359
390
  return []
360
391
  out: list[QueueRow] = []
361
- for _ in range(limit):
362
- row = self.claim_next()
363
- if row is None:
364
- break
365
- out.append(row)
392
+ with self.rename_lock:
393
+ for _ in range(limit):
394
+ row = self.claim_next()
395
+ if row is None:
396
+ break
397
+ out.append(row)
366
398
  return out
367
399
 
368
400
  def mark_done(
@@ -383,22 +415,23 @@ class AspectExtractionQueue:
383
415
  Returns deleted row count (0 when the row was already gone —
384
416
  idempotent under concurrent worker invocations).
385
417
  """
386
- with self._lock:
387
- if doc_id:
388
- cur = self.conn.execute(
389
- "DELETE FROM aspect_extraction_queue WHERE doc_id = ?",
390
- (doc_id,),
391
- )
392
- elif collection or source_path:
393
- cur = self.conn.execute(
394
- "DELETE FROM aspect_extraction_queue "
395
- "WHERE collection = ? AND source_path = ?",
396
- (collection, source_path),
397
- )
398
- else:
399
- return 0
400
- self.conn.commit()
401
- return cur.rowcount
418
+ with self.rename_lock:
419
+ with self._lock:
420
+ if doc_id:
421
+ cur = self.conn.execute(
422
+ "DELETE FROM aspect_extraction_queue WHERE doc_id = ?",
423
+ (doc_id,),
424
+ )
425
+ elif collection or source_path:
426
+ cur = self.conn.execute(
427
+ "DELETE FROM aspect_extraction_queue "
428
+ "WHERE collection = ? AND source_path = ?",
429
+ (collection, source_path),
430
+ )
431
+ else:
432
+ return 0
433
+ self.conn.commit()
434
+ return cur.rowcount
402
435
 
403
436
  def mark_failed(
404
437
  self,
@@ -413,16 +446,17 @@ class AspectExtractionQueue:
413
446
  the row — re-extraction triggers can find failed rows and
414
447
  re-enqueue them.
415
448
  """
416
- with self._lock:
417
- self.conn.execute(
418
- "UPDATE aspect_extraction_queue "
419
- "SET status = 'failed', "
420
- " retry_count = retry_count + 1, "
421
- " last_error = ? "
422
- "WHERE collection = ? AND source_path = ?",
423
- (error[:2000], collection, source_path),
424
- )
425
- self.conn.commit()
449
+ with self.rename_lock:
450
+ with self._lock:
451
+ self.conn.execute(
452
+ "UPDATE aspect_extraction_queue "
453
+ "SET status = 'failed', "
454
+ " retry_count = retry_count + 1, "
455
+ " last_error = ? "
456
+ "WHERE collection = ? AND source_path = ?",
457
+ (error[:2000], collection, source_path),
458
+ )
459
+ self.conn.commit()
426
460
 
427
461
  def mark_retry(self, collection: str, source_path: str) -> None:
428
462
  """Reset the row to ``pending`` and increment ``retry_count``.
@@ -431,16 +465,17 @@ class AspectExtractionQueue:
431
465
  and the retry budget has not been exhausted. The next
432
466
  ``claim_next`` call will pick it up.
433
467
  """
434
- with self._lock:
435
- self.conn.execute(
436
- "UPDATE aspect_extraction_queue "
437
- "SET status = 'pending', "
438
- " retry_count = retry_count + 1, "
439
- " last_attempt_at = NULL "
440
- "WHERE collection = ? AND source_path = ?",
441
- (collection, source_path),
442
- )
443
- self.conn.commit()
468
+ with self.rename_lock:
469
+ with self._lock:
470
+ self.conn.execute(
471
+ "UPDATE aspect_extraction_queue "
472
+ "SET status = 'pending', "
473
+ " retry_count = retry_count + 1, "
474
+ " last_attempt_at = NULL "
475
+ "WHERE collection = ? AND source_path = ?",
476
+ (collection, source_path),
477
+ )
478
+ self.conn.commit()
444
479
 
445
480
  # nexus-v4m7y: retry-with-backoff for reclaim_stale. Three attempts
446
481
  # with two inter-attempt sleeps (0.1s, 0.5s) on top of the 30s
@@ -481,21 +516,22 @@ class AspectExtractionQueue:
481
516
  max_attempts = len(sleeps_between) + 1
482
517
  for attempt in range(1, max_attempts + 1):
483
518
  try:
484
- with self._lock:
485
- cur = self.conn.execute(
486
- "UPDATE aspect_extraction_queue "
487
- "SET status = 'pending', last_attempt_at = NULL "
488
- "WHERE status = 'in_progress' "
489
- f" AND datetime(last_attempt_at) < {cutoff_clause}",
490
- )
491
- self.conn.commit()
492
- if attempt > 1:
493
- _log.debug(
494
- "aspect_queue_reclaim_stale_recovered",
495
- attempt=attempt,
496
- rowcount=cur.rowcount,
519
+ with self.rename_lock:
520
+ with self._lock:
521
+ cur = self.conn.execute(
522
+ "UPDATE aspect_extraction_queue "
523
+ "SET status = 'pending', last_attempt_at = NULL "
524
+ "WHERE status = 'in_progress' "
525
+ f" AND datetime(last_attempt_at) < {cutoff_clause}",
497
526
  )
498
- return cur.rowcount
527
+ self.conn.commit()
528
+ if attempt > 1:
529
+ _log.debug(
530
+ "aspect_queue_reclaim_stale_recovered",
531
+ attempt=attempt,
532
+ rowcount=cur.rowcount,
533
+ )
534
+ return cur.rowcount
499
535
  except sqlite3.OperationalError as exc:
500
536
  # Only retry on writer-slot contention. Anything else
501
537
  # (schema corruption, FK violation, etc.) propagates
@@ -583,23 +619,32 @@ class AspectExtractionQueue:
583
619
  Returns row count updated (0 when no rows match -- safe no-op).
584
620
  Idempotent: a second call with the same ``old`` name returns 0
585
621
  without error.
622
+
623
+ RDR-138 follow-up (nexus-k44w4): this standalone queue-rename has no
624
+ production caller — ``rename_collection_cascade`` re-homes the queue
625
+ inline on its own dedicated connection. It survives as a tested store
626
+ primitive. It acquires ``rename_lock`` (outermost, before ``self._lock``)
627
+ so that if it IS ever invoked directly it serializes against the cascade
628
+ and every queue mutator, the same as the 7 guarded mutators. Lock
629
+ ordering matches the rest of the store: ``rename_lock`` -> ``self._lock``.
586
630
  """
587
- with self._lock:
588
- # Drop any pre-existing new-collection rows that would collide
589
- # with the rename (same source_path). Mirrors ChashIndex pattern.
590
- self.conn.execute(
591
- "DELETE FROM aspect_extraction_queue "
592
- "WHERE collection = ? "
593
- " AND source_path IN ("
594
- " SELECT source_path FROM aspect_extraction_queue"
595
- " WHERE collection = ?"
596
- " )",
597
- (new, old),
598
- )
599
- cur = self.conn.execute(
600
- "UPDATE aspect_extraction_queue "
601
- "SET collection = ? WHERE collection = ?",
602
- (new, old),
603
- )
604
- self.conn.commit()
605
- return cur.rowcount
631
+ with self.rename_lock:
632
+ with self._lock:
633
+ # Drop any pre-existing new-collection rows that would collide
634
+ # with the rename (same source_path). Mirrors ChashIndex pattern.
635
+ self.conn.execute(
636
+ "DELETE FROM aspect_extraction_queue "
637
+ "WHERE collection = ? "
638
+ " AND source_path IN ("
639
+ " SELECT source_path FROM aspect_extraction_queue"
640
+ " WHERE collection = ?"
641
+ " )",
642
+ (new, old),
643
+ )
644
+ cur = self.conn.execute(
645
+ "UPDATE aspect_extraction_queue "
646
+ "SET collection = ? WHERE collection = ?",
647
+ (new, old),
648
+ )
649
+ self.conn.commit()
650
+ return cur.rowcount
@@ -2006,6 +2006,21 @@ def _run_index(
2006
2006
  # these with conformant names before the indexer reads from T3.
2007
2007
  code_collection = info.get("code_collection", info["collection"])
2008
2008
  docs_collection = info.get("docs_collection") or _repo_collection_or_legacy(repo, "docs")
2009
+ # nexus-5ut2a: the registry value can carry a legacy 2-segment name
2010
+ # (``code__<owner>``) when repos.json holds a pre-RDR-103 entry for a
2011
+ # repo that has no catalog owner yet (e.g. a stale entry from a prior
2012
+ # failed run, or the RDR-137 migration's un-cataloged residue). The
2013
+ # Phase-4 migration below is a no-op without an owner, so an
2014
+ # un-reconciled legacy name would reach the strict get_or_create_collection
2015
+ # guard (db/t3.py) and crash the whole index. Re-route any
2016
+ # non-conformant value through the path-derived conformant synth, which
2017
+ # builds ``code__<owner>__<model>__v1`` without needing a registered
2018
+ # owner (model segment self-adjusts to local/cloud mode).
2019
+ from nexus.corpus import is_conformant_collection_name # noqa: PLC0415
2020
+ if not is_conformant_collection_name(code_collection):
2021
+ code_collection = _repo_collection_or_legacy(repo, "code")
2022
+ if not is_conformant_collection_name(docs_collection):
2023
+ docs_collection = _repo_collection_or_legacy(repo, "docs")
2009
2024
  _migrated_names: dict[str, str] = {}
2010
2025
 
2011
2026
  # Load config (picks up per-repo .nexus.yml if present)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes