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.
- {conexus-5.4.0 → conexus-5.4.2}/PKG-INFO +1 -1
- {conexus-5.4.0 → conexus-5.4.2}/docs/rdr/README.md +1 -0
- {conexus-5.4.0 → conexus-5.4.2}/mcpb/pyproject.toml +2 -2
- {conexus-5.4.0 → conexus-5.4.2}/pyproject.toml +1 -1
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t2/__init__.py +71 -3
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t2/aspect_extraction_queue.py +162 -117
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/indexer.py +15 -0
- {conexus-5.4.0 → conexus-5.4.2}/.beads/README.md +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/.gitignore +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/LICENSE +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/README.md +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/conexus/README.md +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/conexus/agents/_shared/README.md +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/conexus/daemon/com.nexus.t2.plist +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/conexus/daemon/com.nexus.t3.plist +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/conexus/daemon/nexus-t2.service +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/conexus/daemon/nexus-t3.service +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/conexus/hooks/scripts/routing/README.md +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/abstract-themes.yml +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/analyze-default.yml +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/citation-traversal.yml +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/debug-default.yml +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/document-default.yml +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/find-by-author.yml +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/hybrid-factual-lookup.yml +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/plan-author-default.yml +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/plan-inspect-default.yml +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/plan-inspect-dimensions.yml +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/plan-promote-propose.yml +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/research-default.yml +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/review-default.yml +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/traverse-then-generate.yml +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/builtin/type-scoped-search.yml +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/dimensions.yml +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/conexus/plans/purposes.yml +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/data/calibration/rdr-109/README.md +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/docs/README.md +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/docs/migration/README.md +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/dt/scripts/Index Current Group in nx.applescript +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/dt/scripts/Index Selection in nx (Knowledge).applescript +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/dt/scripts/Index Selection in nx.applescript +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/mcpb/src/server.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/scripts/cron/README.md +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/scripts/launchd/README.md +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/sn/README.md +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/sn/hooks/scripts/routing/README.md +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/__init__.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/_git_hooks_meta.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/_locking.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/_mineru_pid.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/_session_end_launcher.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/aspect_extractor.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/aspect_promotion.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/aspect_readers.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/aspect_worker.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/bib_enricher.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/bib_enricher_openalex.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/bib_extractor.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/AGENTS.md +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/__init__.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/auto_linker.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/catalog.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/catalog_backup.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/catalog_db.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/catalog_docs.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/catalog_git.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/catalog_links.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/catalog_spans.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/catalog_sync.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/catalog_writes.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/collection_name.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/collections_owner_backfill.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/consolidation.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/dedupe.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/event_log.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/events.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/link_generator.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/manifest_backfill.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/orphan_backfill.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/projector.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/store_hook.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/synthesizer.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/tumbler.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/catalog/types.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/checkpoint.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/chunker.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/classifier.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/cli.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/code_indexer.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/collection_audit.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/collection_health.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/collection_rename.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/__init__.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/_helpers.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/_migration_prompt.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/_provision.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/aspects.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/catalog.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/collection.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/command_context.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/config_cmd.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/console.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/context_cmd.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/daemon.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/doc.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/doctor.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/dt.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/enrich.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/hook.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/hooks.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/index.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/memory.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/mineru.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/plan.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/rdr.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/scratch.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/search_cmd.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/store.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/t3.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/taxonomy_cmd.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/tier_status.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/commands/upgrade.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/config.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/__init__.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/app.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/config.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/routes/__init__.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/routes/activity.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/routes/campaigns.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/routes/health.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/routes/partials.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/static/alpine.min.js +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/static/console.css +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/static/htmx.min.js +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/static/pico.min.css +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/templates/activity/_detail.html +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/templates/activity/_stream.html +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/templates/activity/index.html +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/templates/base.html +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/templates/campaigns/detail.html +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/templates/campaigns/index.html +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/templates/health/_cards.html +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/templates/health/index.html +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/console/watchers.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/context.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/corpus.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/cross_encoder.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/daemon/T2_DAEMON_WIP.md +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/daemon/__init__.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/daemon/discovery.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/daemon/t2_client.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/daemon/t2_daemon.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/daemon/t3_client.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/daemon/t3_daemon.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/AGENTS.md +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/__init__.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/chroma_quotas.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/local_ef.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/migrations.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t1.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t2/_tuning.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t2/catalog.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t2/catalog_taxonomy.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t2/chash_index.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t2/document_aspects.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t2/memory_store.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t2/plan_library.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t2/telemetry.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t3.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/db/t3_reidentify.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/devonthink.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/doc/__init__.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/doc/_common.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/doc/citations.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/doc/ref_scanner.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/doc/render.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/doc/resolvers.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/doc/resolvers_corpus.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/doc/tokens.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/doc_indexer.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/doctor_search.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/dropped_writes.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/errors.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/exporter.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/filters.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/formatters.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/frecency.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/glossary.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/health.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/hook_registry.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/hooks.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/index_context.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/indexer_utils.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/languages.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/logging_setup.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/mcp/AGENTS.md +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/mcp/__init__.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/mcp/_first_run.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/mcp/_t1_state.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/mcp/catalog.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/mcp/core.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/mcp/plan_cache_registry.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/mcp_infra.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/mcp_server.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/md_chunker.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/merge_candidates.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/metadata_schema.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/name_canaries.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/operators/aspect_sql.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/operators/dispatch.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/pdf_chunker.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/pdf_extractor.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/phase_review_sentinel.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/pipeline_buffer.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/pipeline_stages.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/__init__.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/bundle.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/loader.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/match.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/matcher.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/promote.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/purposes.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/repair.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/runner.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/schema.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/scope.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/seed_loader.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/plans/session_cache.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/prose_indexer.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/registry.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/repo_identity.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/repos.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/retry.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/ripgrep_cache.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/routing_stats.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/salience.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/scoring.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/search_clusterer.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/search_engine.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/session.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/stage_timers.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/storage_boundary_lint.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/taxonomy.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/taxonomy_backfill.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/ttl.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/types.py +0 -0
- {conexus-5.4.0 → conexus-5.4.2}/src/nexus/util/__init__.py +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
1012
|
-
|
|
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__(
|
|
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.
|
|
277
|
-
self.
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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
|
-
|
|
343
|
-
|
|
344
|
-
|
|
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
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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.
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
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.
|
|
417
|
-
self.
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
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.
|
|
435
|
-
self.
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
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.
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
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
|
-
|
|
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.
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|