-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathpyproject.toml
More file actions
370 lines (354 loc) · 20.7 KB
/
Copy pathpyproject.toml
File metadata and controls
370 lines (354 loc) · 20.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
[build-system]
# nexus-d7xuj: bounded so the build backend cannot float across a major on a
# release runner we do not control (the l2ku5 class; the 7.6.0 publish died
# when an unbounded hatchling emitted a Metadata-Version the pinned uploader
# predated). HONEST LIMIT: 1.32 was a MINOR that changed metadata version, so
# <2 does not prevent a recurrence of that exact incident — and the MVV's
# twine check runs a CURRENT twine, which by its own SCOPE HONESTY comment
# cannot see uploader-version skew. The residual protection for minor drift
# is Dependabot keeping the uploader pin current (weekly cadence).
requires = ["hatchling>=1.27,<2"]
build-backend = "hatchling.build"
[project]
name = "conexus"
version = "7.35.0"
description = "Self-hosted semantic search and knowledge management for LLM-driven development"
readme = { file = "README.md", content-type = "text/markdown" }
requires-python = ">=3.12,<3.14"
license = "AGPL-3.0-or-later"
authors = [
{ name = "Hal Hildebrand", email = "hellblazer@me.com" }
]
keywords = [
"semantic-search",
"knowledge-management",
"cli",
"llm",
"rag",
"code-search",
"pgvector",
"voyage-ai",
]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Topic :: Software Development :: Libraries :: Application Frameworks",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Operating System :: OS Independent",
]
dependencies = [
# RDR-155 P4b P3 (2026-07-25): ``chromadb>=0.6,<2`` REMOVED. T3 serving is
# pgvector through the engine in both modes (P4a), the migration read legs
# are deleted (P2), and the in-process substrate every test and the T1
# isolated path used to get from chromadb.EphemeralClient is now
# nexus.db.inmemory_vector_store (P0a) — pinned by 74 differential
# contracts that were written against chroma as the oracle first.
# Frozen Chroma directories on disk are orphaned BY DESIGN (Hal's two-hop
# contract): they stay as rollback sources and there is no cleanup verb.
# A pre-migration box that upgrades straight into this release gets the
# loud two-hop redirect from nexus.stranded_install, never a silent empty
# install.
"click>=8.1,<9",
# RDR-155 P4b P0b (Hal decision 3): first-class, no longer transitive
# via chromadb — consumed by nexus.cross_encoder (salience) and
# nexus.db.minilm_direct (the tier-0 EF). These are the reason the P0b
# ordering mattered: had chromadb left first, the reranker would have lost
# onnxruntime silently at runtime.
"onnxruntime>=1.20,<2",
"tokenizers>=0.20,<1.0",
"llama-index-core>=0.12.7,<0.15",
"markdown-it-py>=4.0.0,<5",
"msgpack>=1.0,<2",
"numpy>=1.24,<3",
"pymupdf>=1.26.6,<2",
"pyyaml>=6.0,<7",
"scikit-learn>=1.3,<2",
"structlog>=24.0,<26",
"tqdm>=4.65,<5",
# nexus-8g79.20 finding 2026-05-12: tree-sitter-language-pack 1.x
# is a COMPLETE API rewrite. The 1.x ``Parser`` C-binding (PyO3)
# exposes no ``parse()`` method; the new flow uses a top-level
# ``process()`` + ``ProcessResult`` type. Nexus's
# ``_extract_context`` (code_indexer.py:262) calls
# ``parser.parse(source)`` directly, so CI fails with
# "Parser object has no attribute 'parse'". Migration is its own
# arc — pinned to 0.x for now; tracked as nexus-8g79.35.
"tree-sitter-language-pack>=0.7.1,<1.0",
"voyageai>=0.2,<1.0",
# Same discipline as mineru: docling is the formula screen and the fallback
# extractor for every PDF; its output shape is gated, so the cap sits at the
# locked minor.
# nexus-jpsn1: the slim distribution with only what nexus's PDF path
# exercises (PDF format, local models, the convert core that owns
# rtree). `docling` itself is a meta-package pulling
# docling-slim[standard]: rapidocr (nexus sets do_ocr=False), faker and
# polyfactory (test-data factories), 17 dists and 53 MB nothing here
# imports. Same import name, same locked minor. Dropped beside it:
# python-pptx (nexus never imports pptx; mineru's own pypptx-with-oxml
# owns the same `pptx/` tree, and two dists owning one package is the
# opencv collision shape again — a venv that had both needs
# `uv sync --reinstall-package pypptx-with-oxml` once, or `nx doctor`
# reports "cannot import name 'Presentation' from 'pptx'"),
# opencv-python-headless (zero `import cv2` here, and mineru requires
# the non-headless build unconditionally) and uuid7-standard (nothing
# references it).
"docling-slim[convert-core,format-pdf,models-local]>=2.125.0,<2.125.1",
# nexus-mt1tj: torch and torchvision are transitive (mineru[pipeline],
# docling) and nexus never imports them; they are declared here ONLY so
# that [tool.uv.sources] below can route the Linux resolution to the
# CPU index — uv applies sources to direct dependencies, never to
# transitive ones. Bounds follow what the lock already resolved.
# Linux pins the exact minor the lock already ran (2.8.0+cpu from the CPU
# index); every other platform keeps PyPI's resolution unchanged, so a
# macOS py3.13 box stays on the torch it had (2.10) rather than being
# dragged down by a Linux-motivated cap.
"torch>=2.8,<2.9; sys_platform == 'linux'",
"torch>=2.8,<2.9; sys_platform != 'linux' and python_version < '3.13'",
"torch>=2.10,<2.11; sys_platform != 'linux' and python_version >= '3.13'",
"torchvision>=0.23,<0.24; sys_platform == 'linux'",
"torchvision>=0.23,<0.24; sys_platform != 'linux' and python_version < '3.13'",
"torchvision>=0.25,<0.26; sys_platform != 'linux' and python_version >= '3.13'",
"httpx>=0.27,<1.0",
# RDR-161: pure-Python keyless-signature verification for
# `nx daemon service install-binary`. Replaces a ~130MB/platform cosign
# binary; verifies the new protobuf .sigstore.json bundle offline. 4.x and
# 3.x both expose Verifier.production(offline=...) + verify_artifact.
"sigstore>=3.0,<5",
# <2 is load-bearing: mcp 2.0.0 (2026-07-28) removed mcp.server.fastmcp,
# and the uv TOOL venv re-resolves fresh at install (uv.lock does not
# protect it) — an unbounded floor shipped 4 days of dead MCP servers to
# every fresh install (nexus-l2ku5). Raise only with a deliberate 2.0
# API migration, gated by the MVV's installed-venv handshake probe.
"mcp>=1.0,<2",
"fastapi>=0.115,<1.0",
# nexus-l2ku5 sibling (2026-08-14, gap 3 of T2
# nexus/release-protocol-gap-audit-2026-08-14 [22511]): was fully
# unconstrained, a second open door to the same "unbounded floor
# resolves fresh off PyPI" class that killed mcp for 4 days. Floor
# taken from uv.lock's resolved 0.41.0; capped at the next major.
# Bare, not [standard] (nexus-jpsn1): the extra's watchfiles / httptools
# / uvloop / websockets serve nothing here and watchfiles alone cost
# 115 ms on every MCP server spawn.
"uvicorn>=0.41,<1.0",
# nexus-2fyb: MinerU is the formula-aware PDF extractor required by
# auto-mode whenever a PDF contains math. Previously gated behind a
# [mineru] extra; the extras game produced silent formula loss for
# weeks because fresh installs and reinstall-tool both skipped it.
# Default-on is the only configuration that doesn't foot-cannon the
# next install.
#
# nexus-8g79.18: pinned to ``mineru[pipeline]`` (not ``mineru[all]``).
# nexus calls ``mineru.cli.common.do_parse`` with ``backend="pipeline"``
# in src/nexus/pdf_extractor.py (single call site, see RDR-046). The
# ``[pipeline]`` extra is exactly the stack that path needs: torch +
# torchvision + transformers + onnxruntime + a few lightweight extras
# for the OCR / formula / table models (~2-3 GB downloaded on first
# use, not at install time).
#
# ``[all]`` pulled ``[core]`` (= vlm + pipeline + gradio) plus the
# platform-specific VLM-inference backend (``mlx-vlm`` on macOS,
# ``vllm`` on linux, ``lmdeploy`` on windows). None of those are
# used: nexus never invokes the vlm- backends. The vendored gradio
# 6.x carried a stale XSS advisory (GHSA-2wxf-49m7-6x5q) and pulled
# 100+ transitive packages; the platform VLM backends pull boto3,
# openai, vllm, lmdeploy, cupy. Replacing ``[all]`` with
# ``[pipeline]`` shaves ~100 packages off a fresh install AND drops
# the gradio XSS surface. Verified locally: ``do_parse`` succeeds
# with ``[pipeline]`` only.
#
# nexus-l2ku5 sibling (2026-08-14): was fully unconstrained, the other
# open door in the unbounded-dependency class. Floor taken from
# uv.lock's resolved 3.1.11; capped at the next major.
# nexus-jd8fi drift: pinned to the minor every fixture and slow gate is
# locked against; tests/test_dependency_bounds_lint.py holds cap and lock
# together. Gated 2026-09-03: 3.4.5 REFUSED (drops one letter from every
# "ff" ligature, "efficient" -> "eficient", 67 words to 0 in one paper);
# 3.1.15 REFUSED (wraps plain text as inline math, "B,C," and "m.", and
# splits "aggregation"). Even a patch changes the output shape, so the cap
# is the exact patch. Bump deliberately, with the -m slow MinerU tests,
# the MVV and the shakedown, never by widening this cap.
"mineru[pipeline]>=3.1.11,<3.1.12",
]
[project.optional-dependencies]
local = ["fastembed>=0.7.0"]
[project.urls]
Homepage = "https://github.com/Hellblazer/nexus"
Repository = "https://github.com/Hellblazer/nexus"
Documentation = "https://github.com/Hellblazer/nexus/tree/main/docs"
"Bug Tracker" = "https://github.com/Hellblazer/nexus/issues"
[project.scripts]
nx = "nexus.cli:main"
nx-mcp = "nexus.mcp.core:main"
nx-mcp-catalog = "nexus.mcp.catalog:main"
nx-session-end-launcher = "nexus._session_end_launcher:main"
[tool.hatch.build.targets.wheel]
packages = ["src/nexus"]
# Exclude the editable-install symlink so hatch does not walk into
# it via the packages= directive and then re-add the same files via
# force-include below, which produces a wheel with duplicate local-
# header filenames that PyPI rejects with a 400 "Duplicate filename
# in local headers." The symlink exists for editable installs to
# resolve ``importlib.resources.files('nexus') / '_resources'`` into
# the repo's ``conexus/plans`` tree; force-include is the wheel-install
# path. Exactly one of the two should land files into the wheel.
exclude = [
"src/nexus/_resources",
]
# RDR-092 nexus-b9f3: ship the conexus plugin's plan YAMLs as package data
# so installed CLIs can resolve them via ``importlib.resources.files
# ("nexus") / "_resources"``. Without this, ``_seed_plan_templates``
# cannot find ``conexus/plans/builtin/*.yml`` from a uv-tool install and
# the Phase 0c fail-loud guard correctly surfaces the deployment gap.
#
# Only ``conexus/plans/`` travels. The rest of ``conexus/`` (agents, skills,
# commands, hooks, .claude-plugin/plugin.json) is consumed by Claude
# Code from the repo at plugin-install time, not from the Python
# wheel.
[tool.hatch.build.targets.wheel.force-include]
"conexus/plans" = "nexus/_resources/plans"
# nexus-tv5u: ship DT-side AppleScripts as package data so
# ``nx dt install-scripts`` can resolve them via
# ``importlib.resources.files("nexus") / "_resources" / "dt-scripts"``
# from a uv-tool / pipx / Homebrew install. Editable installs use a
# ``src/nexus/_resources/dt-scripts`` symlink back into ``dt/scripts``.
"dt/scripts" = "nexus/_resources/dt-scripts"
# RDR-120 nexus-41unl: ship launchd plist + systemd unit templates so
# ``nx daemon t3 install --autostart`` can resolve them via
# ``importlib.resources.files("nexus") / "_resources" / "daemon"``.
# Editable installs use a ``src/nexus/_resources/daemon`` symlink back
# into ``conexus/daemon``.
"conexus/daemon" = "nexus/_resources/daemon"
[tool.hatch.build.targets.sdist]
include = [
"src",
"conexus/plans",
"conexus/daemon",
"dt/scripts",
"README.md",
"LICENSE",
"pyproject.toml",
]
[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["scripts"]
log_level = "WARNING"
# Honour the documented default: integration / slow tests are opt-in.
# Without this, ``pytest tests/`` runs everything including
# ``tests/integration/test_nx_answer_equivalence.py`` which calls real
# Voyage / Chroma APIs in retry loops without credentials and hangs
# pytest indefinitely (239+ CLOSE_WAIT TCP sockets observed locally).
# Run them explicitly with ``pytest -m integration`` or ``-m slow`` — ``slow``
# is also run nightly by local-service-gate-nightly.yml (nexus-s6dei).
#
# nexus-s6dei (2026-08-02): ``stress`` REMOVED from both addopts and markers
# below. tests/stress/ held the RDR-120 storage-substrate-split harness
# (test_t2_daemon_stress.py, test_t3_daemon_stress.py); RDR-120 closed
# 2026-05-27 and the T2/T3 daemons it stress-tested were deleted in the
# subsequent daemon-retirement commits (d295a5bd, 9cfb157e). The directory
# now holds only __init__.py — zero tests carry the marker, so there is
# nothing left to wire up. See tests/test_marker_selection_coverage.py,
# the mechanized check this bead added so a marker going dark again fails
# CI instead of going unnoticed for months.
addopts = "-m 'not integration and not slow and not lint'"
markers = [
"integration: end-to-end tests requiring real services (deselect with '-m not integration')",
"slow: expensive benchmarks/pagination/cold-import tests (deselect with '-m not slow'); excluded from default addopts, runs in the nightly local-service gate leg — see .github/workflows/local-service-gate-nightly.yml",
"no_service_jar: integration test that drives PostgreSQL directly and is exempt from the service-jar freshness gate",
"needs_stamped_jar: integration test that constructs a vector client, so its service jar must carry a release_version (build with scripts/build-gate-jar.sh) — nexus-ao29z",
"lived_in: integration test that dispatches real claude -p or requires seeded lived-in corpora (deselect with '-m \"integration and not lived_in\"')",
"cloud_mode: integration test that needs the SERVICE embedding voyage-* collections, i.e. cloud mode. Incompatible with a local-service install, which RDR-160 pins to bge-768 and which serves one embedding mode at a time (deselect with '-m \"integration and not cloud_mode\"')",
"real_managed_default: opt OUT of the unit suite's black-holed default managed endpoint (nexus-d5aye) — for tests that assert DEFAULT_MANAGED_SERVICE_URL's own value; they must still not reach the network",
"lint: repo-structure/meta lint tests (AST-scan the whole src tree, walk conexus/ agent-skill-command markdown, RDR frontmatter, marker-selection coverage) rather than exercising src behavior. Excluded from default addopts; runs in the dedicated CI lint leg — see .github/workflows/ci.yml's 'pytest (lint markers)' job (test-suite-compression P0, nexus-test-cleanup 2026-08-05).",
"scenario: cross-verb CLI journey against the real per-process engine substrate (tests/conftest.py::t2_service_env) — observes a write through a DIFFERENT verb/store handle than the one that made it. Runs in the DEFAULT loop (deliberately NOT added to addopts' deselection) — see tests/test_scenario_journeys.py and the pytest_sessionfinish non-vacuity guard in tests/conftest.py (test-suite-compression P2-reduced, nexus-test-cleanup 2026-08-05).",
"plan_grow_generalizer: opt OUT of the unit suite's stubbed grow-time match-text generalizer (nexus-93cc6) — for tests exercising _generalize_grown_match_description itself, which MUST patch claude_dispatch and never reach a real subprocess",
"mandatory_regression_pin: a release/CI-gate test (nexus-moht0 vacuous-gate class) that MUST exercise a real external dependency (live git tags, an authenticated network call) rather than a mock, per its bead's own 'measure the detector against a deliberately broken fix' acceptance criterion. Always ALSO carries `integration` (excluded from default addopts, since it needs an environment the unit `test` job deliberately does not provide — full git history / GITHUB_TOKEN). A skip here must never read as a pass — see the pytest_sessionfinish non-vacuity guard in tests/conftest.py, NX_MANDATORY_PIN_SKIP_BUDGET.",
]
[dependency-groups]
dev = [
"pytest>=8.0",
"pytest-asyncio>=0.24",
"pytest-cov>=7.0.0",
# nexus-n0ful: CI-latency sharding. Duration-balanced split over the
# committed .test_durations.json (see that file's header comment for the
# refresh cadence) — chosen over pytest-shard's hash-modulo split because
# the unit suite's ~18.8k tests are wildly uneven in cost (PDF-extraction
# and daemon-lifecycle tests run seconds; most unit tests run
# milliseconds), so a duration-blind split risks one shard absorbing the
# slow tail while siblings idle. Stale durations only degrade balance,
# never correctness — pytest-split falls back to the suite average for
# any test missing from the file (e.g. one added since the last refresh).
"pytest-split>=0.10",
# test-suite-compression P0 (nexus-test-cleanup, 2026-08-05): opt-in local
# dev-loop parallelism via `uv run pytest -n auto`. Deliberately NOT added
# to addopts — CI's pytest-split sharding (above) is matrix parallelism
# across runners and must stay exactly as it is; xdist is an additional,
# independent lever for the local iteration loop only. See tests/AGENTS.md
# for the invocation.
"pytest-xdist>=3.5",
"ruff>=0.15.18",
"syrupy>=4.0",
"twine>=6.2.0",
]
[tool.uv]
# nexus-usppl: exclude PyAV. It is pulled transitively by MinerU's VLM backend
# (mineru -> qwen-vl-utils -> av), which nexus never uses — nexus drives MinerU's
# PIPELINE backend (`mineru.cli.common.do_parse`) and Docling, neither of which
# imports `av`. PyAV vendors ffmpeg 62.x dylibs (libavdevice.62) that collide
# with opencv's bundled ffmpeg 61.x dylibs (libavdevice.61) inside a single
# process, producing the macOS objc "Class AVFFrameReceiver implemented in both
# ... may cause spurious casting failures and mysterious crashes" warning that
# destabilises the PDF extraction stack. Verified safe: `import mineru`,
# `mineru.cli.common`, `qwen_vl_utils`, `docling`, and `cv2` all import cleanly
# with `av` absent (qwen-vl-utils imports av lazily on its unused video path).
# The never-true marker drops `av` from resolution on every platform.
override-dependencies = [
"av; sys_platform == 'never'",
]
# nexus-mt1tj (Sam, 2026-09-04): CPU-only torch on Linux by default. The PyPI
# torch wheel for Linux is the CUDA build (15 nvidia-* dists + triton, ~4.5 GB
# nothing here uses: the engine embeds with ONNX and MinerU's pipeline runs
# CPU on our path). The lock resolves torch/torchvision for Linux from the
# CPU index instead, so every project sync (CI, the publish job, a checkout)
# stops downloading the CUDA tree. Project settings never reach an installed
# wheel, so install_generation.sh passes --torch-backend cpu on Linux for user
# installs (NX_TORCH_BACKEND=auto opts a GPU box back in). macOS wheels carry
# no CUDA payload and keep resolving from PyPI.
[tool.uv.sources]
torch = [
{ index = "pytorch-cpu", marker = "sys_platform == 'linux'" },
]
torchvision = [
{ index = "pytorch-cpu", marker = "sys_platform == 'linux'" },
]
[[tool.uv.index]]
name = "pytorch-cpu"
url = "https://download.pytorch.org/whl/cpu"
explicit = true
[tool.ruff]
# nexus-ls88v (epic nexus-whh61 F7): enforce the structlog-only / no-print
# discipline that was previously convention-only. The selected rules below are
# each audited across src/ — every existing site is either fixed or carries a
# justified per-line `# noqa: <code> — reason`, so a NEW violation fails CI.
target-version = "py312"
src = ["src"]
extend-exclude = [
# RDR-201 P1.1 (nexus-j9z30.1): a read-only archived snapshot of the
# enumcheck research prototype, kept byte-identical for porting
# fidelity ("port; do not rewrite") — not production code, not linted.
"tests/fixtures/tables/_prototype",
]
[tool.ruff.lint]
# T20 flake8-print — no print() in library code (CLAUDE.md hot rule;
# library progress -> structlog, CLI -> click.echo).
# BLE001 flake8-blind-except — `except Exception`/bare-except must be audited.
# PLC0415 import-outside-top-level — lazy imports must be deliberate (circular-
# dep / optional-dep / startup-cost), not accidental.
select = ["T20", "BLE001", "PLC0415"]
[tool.ruff.lint.per-file-ignores]
# CLI command modules emit user-facing output; stdout via print()/click.echo is
# the sanctioned output channel there, not a library no-print violation.
"src/nexus/commands/**" = ["T201", "T203"]