Skip to content

v7.86.0

Choose a tag to compare

@github-actions github-actions released this 25 Oct 10:24
· 483 commits to main since this release
52d8a0c

Pairs with engine-service-v0.1.164 (REQUIRED_ENGINE_VERSION unchanged from 7.85.0), already deployed to the managed service; no engine changesets. On Windows, PDF support needs Microsoft's Visual C++ redistributable on this engine: 0.1.164 does not carry msvcp140_atomic_wait.dll, so a machine without it installs without PDF support and says so (see Fixed, nexus-vyy65).

Added

  • The Claude Desktop extension can carry PDF support, opt-in (nexus-dzc61). Its new "Include PDF support" setting (off by default) makes the launcher install conexus[pdf] at the next start, about 0.25 GB more to download and 0.85 GB more on disk on macOS; turning it off removes the PDF packages again. With it off, a start resolves exactly what it did before, and a sync that fails with it on falls back to bare so the extension still starts. Indexing a PDF in the extension with the setting off names the setting. scripts/check_mcpb_resolve.py resolves both variants (live, 2026-10-24: bare 139 packages, PDF 219, 0 pins moved), and a real sync on macOS installed, imported and removed the PDF stack.

  • A weekly vulnerability audit of the shipped install lock (nexus-2k66t.4.5). .github/workflows/dependency-drift-watch.yml gains a lock-audit job: pip-audit over src/nexus/_install/constraints.txt (every install is pinned to it since nexus-2k66t.4.1, so a transitive security fix reaches users only through a release), summarised by scripts/audit_install_lock.py, one line per vulnerable package with its advisories and the highest fixed version. The run is red only for an advisory that has a fix and is not acknowledged (with a reason) in the script's ACKNOWLEDGED; an advisory with no fix is shown and never fails; an audit that examined fewer than 50 packages fails. Its first run (2026-10-24) found 258 advisories across 34 of 208 pinned packages, which the lock refresh nexus-13z79 clears.

  • A PR-time resolve check for the .mcpb bundle's dependency lock (nexus-zzkko). scripts/check_mcpb_resolve.py ran only in the release workflow, after the tag was pushed, so a lock that stopped resolving cost a workflow_dispatch retry or a moved tag. ci.yml now has an mcpb-resolve-gate job on pull requests to develop and main and on develop pushes. It always reports, and runs the resolve (against the checkout, nothing installed) only when src/nexus/_install/constraints.txt, uv.lock, pyproject.toml, mcpb/pyproject.toml, mcpb/src/bootstrap.py or one of the two scripts changed; an unusable filter output fails the job instead of skipping the resolve.

  • Release-battery leg resolve: the shipped dependency lock must resolve on every supported target (nexus-2k66t.4.4). tests/test_install_constraints_pin.py proves constraints.txt equals uv.lock but not that the lock installs anywhere (the onnxruntime wheel set starts at macOS 14). tests/e2e/constraints-resolve-gate.sh runs uv pip compile with the shipped overrides.txt and constraints.txt, installing nothing: 22 compiles, 8 for the default install (Linux x86_64 and aarch64 and Windows x64 and macOS 14 arm64 on Python 3.12 and 3.13, torch backend cpu on Linux) and 14 for the pdf extra (the same, plus the cu130 backend on Linux 3.12 and 3.13 and cu126 on Linux 3.12), any unresolvable target named and the leg red. macOS 14 is expressed through MACOSX_DEPLOYMENT_TARGET. The requirements come from pyproject.toml as plain dependencies, because uv would otherwise apply [tool.uv.sources] and resolve CPU torch for every GPU target. Non-vacuity: the expected count is a literal, so deleting a cell fails the leg; a compile must reach a pin count near the real lock's (110 default, 180 pdf); every pdf compile must pin docling-slim and mineru; each Linux pdf cell must resolve torch to its backend's local version; a probe fails closed with "uv too old for " before any compile; and a macOS 13 negative control must stay unresolvable with uv's onnxruntime platform-tag error (if it resolves, the verdict says the macOS floor moved and names docs/getting-started.md and platform_failure_hint). Wired into release-battery.sh (group, network; release battery and by hand, never the default suite); tests/e2e/constraints_resolve_gate_test.sh is the offline self-test (stub uv, 82 checks).

  • LLM steps can run on a direct Anthropic API key or an OpenAI-compatible server, not only the claude CLI (nexus-2k66t.3, phases 1 and 2). Every LLM step (the nx_answer operators, aspect extraction, topic labels, memory roll-up) goes through one dispatch with three backends: cli (unchanged), anthropic (the Messages API on httpx, no new dependency) and openai_compat (Chat Completions for Ollama, vLLM, OpenRouter). llm.backend / NX_LLM_BACKEND is auto by default: the CLI when claude is on PATH (today's behaviour and billing), else the API when an anthropic_api_key resolves (nx config set anthropic_api_key --stdin, or ANTHROPIC_API_KEY), else no backend, with an error naming both remedies. nx_plan_audit and nx_enrich_beads use tools and always run on the CLI. The setting, the openai_compat endpoint (llm.openai_compat.base_url / .model) and the keys are read from the environment and the global config.yml only, never a repository's .nexus.yml. The direct path forces a StructuredOutput tool with the operator's JSON schema where the model allows it (Claude Opus 5.5, Sonnet 5.5 and Fable 5.1 reject a forced tool choice, so they get tool_choice: auto plus an instruction), validates the answer locally with jsonschema and sends one repair turn on a failure; it keeps the CLI's two-phase timeouts with partial output in the timeout log, retries 429/529/5xx/connection failures twice honouring retry-after, caps in-flight requests at NX_LLM_MAX_CONCURRENCY (default 4) and the output at NX_LLM_MAX_TOKENS (default 16000), enforces max_budget_usd as a pre-flight ceiling, and prices calls from a table (an unknown model reports cost_usd: None and a warning, never 0.0). A key kept in config.yml never reaches os.environ, a log line, an exception message or a claude child; an ANTHROPIC_API_KEY exported in the shell is still inherited by the claude -p children nexus starts, as before this change, and a malformed config.yml is reported by line number only. NX_LLM_OPENAI_API_KEY is removed from every claude child's environment. An openai_compat call under a max_budget_usd / NX_DISPATCH_MAX_BUDGET_USD ceiling is refused unless llm.openai_compat.price_in_per_mtok / price_out_per_mtok are set (then it is enforced and priced) or llm.openai_compat.unpriced_ok: true accepts it unenforced. When auto finds no backend the caller receives NoBackendAvailableError, at the spawn path too. Aspect rows written through an API backend carry the model that produced them in model_version. nx doctor gains an "LLM backend" row (which backend and why; not applicable on a box with neither) and an "LLM key persistence (GUI spawn)" row. The README's no-API-key claim is narrowed to search and storage. llm_backend_available, backend_status and selection flip together, and the aspect extractor routes through the selected API backend with its own 3-attempt retry kept (a 429 costs 3 requests, not 9). Prerequisites from the phase 1 reviews landed with it: the operator exceptions and DispatchUsage moved below both modules into operators/llm_types.py (still re-exported from dispatch), and one usage-recording helper serves every backend. Before this release the four model aliases were checked live through claude -p (each resolved to the model ID the direct backend maps it to); the anthropic and openai_compat backends themselves were tested only against a local fake server, with no live API call.

Changed

  • The Claude Desktop .mcpb bundle installs under the release's dependency lock (nexus-2k66t.4.6). The bundle's uv sync resolved every dependency fresh, so a bad transitive release reached Desktop users, who are the least able to recover (the nexus-l2ku5 shape). The release workflow now stages src/nexus/_install/constraints.txt (the file the wheel and every installer ship, pinned equal to uv.lock) into the bundle with scripts/stage_mcpb_constraints.py before mcpb pack, appends conexus==<release> so the bundle installs the release it was built from, and fails the release if the packed zip lacks the file. The stager reads the source with the launcher's own reader (any line the launcher would reject refuses the stage; floor 200 pins, the lock has 219). mcpb/src/bootstrap.py writes it into the bundle's pyproject.toml as [tool.uv] constraint-dependencies before the sync (a project uv sync has no constraints flag and ignores UV_CONSTRAINT), and says so on stderr. The edited file is parsed with tomllib before it is written and is never written if it would not parse (an existing constraint-dependencies, a begin marker with no end): it stays as it was, the line says "not pinned" and, when an earlier constraint is still in force, that. An end marker at the end of the file with no newline is now replaced cleanly. The scratch file is pyproject.toml.nx-tmp.<pid> and is removed on every path. If the sync fails with the pin in force for any reason except the release-propagation window (or the pin names no conexus to wait for), the launcher removes the pins, says so, and syncs once unpinned instead of failing the start. The opt-out: NX_INSTALL_UNPINNED=1 (the CLI installers' variable; launchctl setenv on macOS, a user environment variable on Windows), the extension's own install_unpinned setting in Claude Desktop (manifest user_config, which reaches the launcher as NX_MCPB_INSTALL_UNPINNED=true; 1 and true both count), or deleting constraints.txt from the extension directory. A bundle without constraints.txt (a hand-packed one), or with one that is not a list of pins, installs unpinned as before and states that on stderr; it never fails the start. A pre-publish gate, scripts/check_mcpb_resolve.py, runs in the release workflow's pack step after the wheel is built: it stages and pins a scratch copy of the bundle exactly as the launcher does, points conexus at the fresh wheel (the release is not on PyPI yet), runs uv lock, and fails the release if the lock does not resolve, conexus is not locked at the release version, or a pin moved. Measured against a scratch copy of the real bundle: the pinned resolve holds mcp at 1.27.1 as locked, the opted-out resolve takes 1.30.0 and moves 127 of the 213 pinned packages. Bare conexus stays the bundle's dependency; the [pdf] extra question is nexus-dzc61.

  • The install sizes are measured, not derived (nexus-2k66t.4.10). Measured on 7.85.0 on macOS (hellmini) and Linux (chas, qwent WSL) on 2026-10-24; Windows was not measured. Local about 0.8 GB down (0.9 GB on Linux), 2.5 to 2.9 GB on disk; Cloud and CLI only about 0.45 GB down (0.6 GB on Linux), 1.8 to 2.2 GB on disk. Slim saves about 0.25 GB of download on macOS and 0.37 GB on Linux, and 0.85 GB on disk on macOS and 1.3 GB on Linux (the page said 0.8 GB for both). The uv cache is about 2.1 GB on Linux and 1.8 GB on macOS (the page said 2.4 and 3.6), and on Linux it shares files with the install, so the two together are about 2.2 GB. Corrected in the README, the install guides, the setup command, the stand-in and the setup tool description.

  • PDF support is the pdf extra; a bare install leaves it out, the first PDF index installs it, and the installers and the plugin's setup add it up front (nexus-2k66t.4.2, nexus-2k66t.4.9, Sam 2026-10-23). docling, MinerU, torch, torchvision and pdftext were 57 to 65% of the download and 49% of the installed size (T2 nexus/install-weight-measurement-2026-10-23: macOS 428 to 186 MB, Linux 567 to 197 MB, 792 of 1605 MB installed), and search and store never import them. They moved from [project].dependencies to [project.optional-dependencies] pdf; the dev group includes conexus[pdf], so uv sync, CI and the suite keep the stack; uv.lock and src/nexus/_install/constraints.txt are regenerated (219 pins, unchanged, since the file already covers every extra). nx index pdf, nx index pdf --dir, nx dt index of a PDF record, nx dt capture of a PDF (before DEVONthink is touched, so the re-run cannot capture twice), nx collection reindex of a docs or knowledge collection with a PDF source (before the purge), nx index repo when its walk finds a PDF, and nx mineru start call pdf_stack.ensure BEFORE any catalog or T3 write; doc_indexer.index_pdf itself calls pdf_stack.require before it registers anything, and a census test fails on any ungated caller. The default policy installs, on a terminal and off one (the commonest caller is an agent's Bash tool): one stderr line names the size and uv's progress streams, then a new generation is built with the pdf extra at the RUNNING version through the generation builder's own arguments (perform_self_install(add_extras=("pdf",), version=<running>): CPU torch on Linux, the packaged overrides, the constraints the new package ships; a bare uv pip install conexus[pdf] would resolve CUDA torch on Linux, and without the version pin the fetch would be an upgrade), and the command re-runs as a child of <tools>/current/bin/nx (NX_PDF_STACK_REEXEC=1 stops a loop; nx index repo unwinds first so its index lock is free). A file lock serializes concurrent fetches. It is never silent: exit 69 when the install is refused (NX_PDF_STACK=refuse or pdf_stack.auto_install: false; a checkout, a directory-source generation or a legacy uv tree, which get their own command; Windows without the VC++ runtime), 75 on a network or TLS failure, 70 when the installer fails or the stack is still absent afterwards, each before anything is written, each naming the command. PDFExtractor.extract refuses by name when the stack is absent in every extractor mode, and its two ImportError messages say "PDF support is not installed" with the command when that is the cause; nothing falls back to pymupdf_normalized. The post-commit hook stanza exports NX_PDF_STACK=refuse: a background nx index repo indexes code and prose, does not register or index the PDFs (they still count as present for the catalog's orphan sweep, and a --since-head base does not advance past them), logs an ERROR line naming each, exits 69, and nx doctor counts them. nx doctor has a "PDF support" row: a pass with the pin check when present, informational (never red) when absent, red with the repair command when broken; the MinerU server row and --check-mineru report not applicable without the stack.

  • Every ordinary install path installs PDF support by default; --slim opts out (nexus-2k66t.4.9, Sam 2026-10-23: install everything by default). nx self install --bootstrap now accepts --extras (it still refuses --version): the throwaway uv environment that runs it stays slim and the generation gets the extras, and an interrupted slim build is rebuilt, not finished, when --extras pdf is asked. web/install.sh and web/install.ps1 run ... nx self install --bootstrap --extras pdf and take --slim to leave it out; the plugin's setup (nx_setup.py, start --slim) does the same, and the setup tool, on the stand-in and the real server, has a boolean slim argument (default false, recorded in the setup status so a resume passes it on). The REPAIR command the stand-in prints carries --extras pdf. A bare uv tool install conexus, the Claude Desktop .mcpb bundle and --slim still arrive slim and fetch the stack on the first PDF index (nexus-2k66t.4.2); nx self install on a legacy uv tree already carried it. The size rows everywhere a user chooses an install (the stand-in and the setup tool, /conexus:setup, README, getting-started, windows-install, desktop-deployment, RDR-228, both installers) show the default: Local about 0.8 GB down (0.9 GB on Linux), 2.4 to 3.1 GB on disk; Cloud and CLI only about 0.45 GB down (0.6 GB on Linux), 2.1 to 2.3 GB on disk, with a slim line (0.25 GB less down on macOS, 0.37 GB on Linux, 0.8 GB less on disk). The download figures are derived from the 2026-10-23 measurement of 7.85.0 (T2 nexus/install-weight-measurement-2026-10-23), the disk figures from the 7.81.0 measurement; tests/test_install_size_rows.py pins every place. The release gate follows the default: tests/e2e/fresh-install-mvv.sh builds its generation with --extras pdf (the probe's pdf branches and the receipt's extras now run) and adds leg 9b, a slim generation in its own virgin home (NX_MVV_SKIP_SLIM_LEG=1 skips it and the final line says so); rehearse_package_upgrade.sh stage 6 builds with the extra too (NX_REHEARSE_SLIM=1 for the bare build). nx doctor's "PDF support" row is a warning, not a plain pass, when an upgrade owes an install the stack (it names the pending carry-forward, or when the last one failed and the retry command); the legacy-uv-tree remedy is one command, nx self install (it moved the tree and carried PDF support already), not two. ORDER: the installer pages call --bootstrap --extras, which conexus@latest accepts only from this release on, so web/install.sh and web/install.ps1 publish after the client release is on PyPI.

  • An existing install ALWAYS carries the PDF stack forward; only a fresh install goes without it (nexus-2k66t.4.2). The receipt's installer_schema is now 2 ("pdf is an extra"); a receipt below 2, or without the field, was written by an installer that built the stack into every generation, so perform_self_install, the uv-takeover repair, scripts/reinstall-tool.sh (which also reads list-valued extras now, and takes NX_REINSTALL_EXTRAS), migrate_legacy.sh and the Windows generation_core.migrate_legacy add pdf to the extras they build. The older nx self install that moves a box INTO this release cannot know the rule and builds a slim generation of the new package, so nx upgrade (the SessionStart lockstep runs it) builds the generation that adds pdf when it sees a pre-split receipt; a failure leaves the install working, is retried by the next nx upgrade, and the automatic run waits six hours between attempts.

  • nx self install builds the new generation with the installer of the release it installs, not the running release's (nexus-2k66t.4.3). The lock pin (nexus-2k66t.4.1) lives in the installer a release ships, so an upgrade built by the old installer resolved the new release fresh and only the upgrade after it was pinned. COVERAGE: upgrades FROM a release that carries this code. The hop out of 7.85.0, which does not, is still built by 7.85.0's installer; that generation is re-pinned only by the PDF carry-forward (nexus-2k66t.4.2), which rebuilds at the running version on the first nx upgrade after it. Every route that builds a generation uses one selection rule: the upgrade, the uv-takeover repair (the uv tree's own nexus/_install/, already on disk, no fetch) and the legacy uv-tool convergence (the newest release, or --version, fetched; on POSIX migrate_legacy.sh is handed the target's install_generation.sh through NX_MIGRATE_BUILD_SCRIPT and keeps its own flip and shim steps; on Windows the target's generation_core.migrate_legacy runs whole, flip and PATH entry included). A registry target is fetched alone with uv pip install --no-deps --target at the version asked for, with the Python the build uses (3.12); a checkout is used where it stands; a wheel file is unpacked. Elsewhere only the build changes hands: flip, shims and reap stay with the running code. A target that predates this (no installer in the package, a support file missing, or an installer with no arm for a flag the running code passes) builds with the running installer; one stderr line says why and the same note is logged at warning level. A failed fetch exits before anything is built, leaving current as it was, with the package-index diagnosis when the network is the cause; an unreadable local wheel says so rather than "could not fetch". A build the target's installer fails names NX_INSTALL_RUNNING_INSTALLER=1 (build with the running release's installer) and NX_INSTALL_UNPINNED=1 (resolve fresh). No fetch happens when the pinned version is the running one. After the build, a warning names both versions when the generation holds a different conexus version than the installer was read at (the index moved in between); no --version pin is added, which would change the receipt's spec. nx self install --dry-run prints the running installer's argv: the target's is read only when the build runs, since reading it is a fetch.

  • nx upgrade rebuilds a generation that was built without the release's dependency lock (nexus-au0s3). The receipt now records constraints: applied (the release's constraints.txt, or an explicit --constraints file, was used), unpinned (NX_INSTALL_UNPINNED=1 skipped it on purpose) or none (the package shipped none); a receipt that predates the field reads as unknown. Both builders write it (install_generation.sh, generation_core.py); installer_schema stays 2 and RECEIPT_SCHEMA stays 1, so an older nx reads the new receipts and drops the key. When current is a registry build of the running version whose receipt says none or nothing, and this package ships a lock, nx upgrade (and its --auto run) builds one generation at the same version with EXACTLY current's extras (not the running generation's) and the running installer, flips, and reaps by the keep-3 rule. It is checked afterwards: if current does not record applied, the attempt counts as failed and backs off six hours under --auto on its own marker (installer-rebuild-failed), so a build that succeeds without the lock cannot repeat every session start. A deliberate unpinned install is never re-pinned silently; the next plain nx self install pins it again. COVERAGE: the hop out of 7.85.0 is NOT this rung's. Its receipts predate the PDF split, so the PDF carry-forward (nexus-2k66t.4.2) builds the one generation that re-pins it, and this rung stands aside while that is owed. The rung covers later generations built at the running version without the lock, which in practice means an installer fallback (NX_INSTALL_RUNNING_INSTALLER=1, or a target that ships no usable installer); it is a safety net with a small population, since it requires the running package to ship the lock. --skip-t3 defers it. nx doctor shows a "Dependency lock" warning when the rebuild is owed or its last attempt failed, with nx upgrade as the retry; a box that owes nothing shows no row. The install-lock waiter now names the build that holds it ("rebuilding the current generation", not always "installing PDF support").

  • A receipt written by a newer installer is read, not refused (nexus-lvvhm, nexus-ff366). An upgrade is built by the TARGET release's installer, which writes the receipt, while the running release flips, shims, reaps and reads it back; RECEIPT_SCHEMA was compared for equality, so a target that bumped it would have broken the running code mid-upgrade. The reader now compares a separate MIN_READABLE_RECEIPT_SCHEMA (1), so the writer's stamp can move without making any receipt already on disk unreadable. It accepts a schema at or above the minimum, takes the fields it needs and drops the rest, and refuses, always as the layout error, a schema below the minimum or not an integer, an installer_schema that is not an integer, and a required field that is missing or not a string (a dict or null version is no longer turned into a string). The compatibility rule is written on RECEIPT_SCHEMA, on Receipt and in layout.sh, where the shell writer's author works: an additive field needs NO bump (released clients, 7.85.0 and earlier, compare the number strictly, so any bump locks them out of every receipt written after it); a change in what an existing field means needs a new field name; widening source_kind breaks older readers; an unknown constraints word reads as unknown, which the installer-rebuild rung treats as owed. nx self install reads the built generation's receipt before it moves current: a receipt this nx cannot read stops the install with current, previous and the shims untouched, naming NX_INSTALL_RUNNING_INSTALLER=1. The same read-back now sits between the build and the flip in the legacy uv-tool convergence on both platforms: migrate_legacy.sh checks through layout.sh's nx_check_receipt, and on Windows the check is the verify argument of generation_core.migrate_legacy (the flip is inside that call, so a check around it could only run after it); a target release's core that predates the argument is replaced by the running release's core for that convergence, with one stderr line. On the upgrade path (the --auto run included) an unreadable receipt no longer repeats at every session start: the unreadable generation is removed and the generation is built once more with the running release's installer, saying so on stderr; NX_INSTALL_RUNNING_INSTALLER=1 and a second refusal still end the install. scripts/reinstall-tool.sh reads the receipt through the same reader instead of two inline copies, and stops with REFUSING on a receipt it cannot read rather than dropping the box's extras.

  • The shipped dependency lock is refreshed: the install audit goes from 34 vulnerable packages and 258 advisories to 2 packages and 46 (nexus-13z79). pip-audit over src/nexus/_install/constraints.txt (208 pins examined before, 215 after; the audit counts every platform's pin) now flags only pillow 12.3.0 (37 advisories, all fixed solely in pillow 13.0.0, which fastembed through 0.9.0 and docling-slim cap below, and which was released 2026-10-16) and transformers 4.57.6 (9, three with no fix; the rest need transformers 5, which mineru[pipeline] forbids). uv lock --upgrade moved 147 packages and added 10 and dropped 3; uv.lock and constraints.txt agree. Bounds changed, each with its reason in pyproject.toml: torch and torchvision from the minors the lock already ran (2.8 on Linux, 2.10 on macOS py3.13) to 2.13 and 0.28 on every platform (2.8.0 carried eight advisories, one fixed only in 2.13.0; torch 2.13 has no cu128 build: see the next entry); fastembed <0.8 to >=0.8.1,<0.9 (0.7.x capped pillow below 12; bge-base-en-v1.5 vectors from fastembed 0.7.4 and 0.8.1 differ by at most 1.3e-4 per component, minimum cosine 0.9999996, measured with onnxruntime also moving from 1.24.2 to 1.31.0; stored vectors come from the engine's own ONNX runtime, which this does not touch, so nothing re-embeds); docling-slim from the exact patch 2.125.0 to the exact patch 2.132.0 (seven advisories in backends nexus never calls). Major moves in the lock: pillow 11 to 12, starlette 0.52 to 1.9, cryptography 48 to 50, protobuf 6 to 7, opencv 4 to 5, rich 14 to 15, filelock 3 to 4, attrs 25 to 26, multidict 6 to 7, reportlab 4 to 5, deprecated 1 to 3, more-itertools 10 to 11, xxhash 3 to 4, aiofiles 24 to 25, av 18 to 19, soupsieve 2 to 3, twine 6 to 7, syrupy 5 to 6, readme-renderer 44 to 46, and the 0.x minors torch 2.13, torchvision 0.28, mcp 1.30, fastapi 0.162, uvicorn 0.54, llama-index-core 0.14.25, onnxruntime 1.31, numpy 2.5. llama-index-core and tree-sitter-language-pack stay inside their caps (0.14.x, 0.13.x). Checked on chas: the full unit suite and the lint bucket, the docling extraction tests with none skipped, ci_warm_docling.py, both -m slow MinerU fixtures with their exact locked values. The MVV and the release shakedown are still the final gate for the docling and torch moves.

  • NX_TORCH_BACKEND=cu128 no longer resolves (nexus-13z79). The torch the lock now carries (2.13) has no cu128 build. The default is still cpu. auto picks a build from the NVIDIA driver, so on a driver that supports only CUDA 12.8 it can pick cu128 and fail to resolve: name cu126 there (it covers CUDA 12.6 to 12.8 drivers). Newer drivers can use cu129 or cu130; cu132 also resolves. All of these resolve on x86_64 and aarch64, which closes the earlier aarch64 cu128 gap. See docs/getting-started.md and docs/cli-reference.md.

Fixed

  • Doctor rows and error messages name commands that exist (nexus-t99tw). Fix suggestions say nx init instead of the deprecated nx init --service (plain nx init runs the same local provisioning); on a managed or cloud box the footer says NX_LOCAL=1 nx init. The vector-write retry summary says "vector store" and nx doctor --check-quotas says vector: instead of chroma; nx collection audit --live help names the T3 service store; nx doctor --fix on the service store says HNSW tuning is server-side instead of "Updated HNSW search_ef on 0 collection(s)".

  • On Windows, PDF support needs msvcp140_atomic_wait.dll; a machine without it gets nexus without PDF support and is told (nexus-vyy65). torch 2.13 imports that DLL and import torch fails with WinError 126 without it. The gate checked only msvcp140.dll and msvcp140_1.dll, so the stack installed and then could not load. The gate now needs all three DLLs in System32 (Microsoft's Visual C++ redistributable) or together in one app-local directory (the engine directory, the PG bundle bin, <config>/vcrt). On a machine with neither, every build path leaves PDF support out and prints one line that says so and how to get the runtime: nx self install and its --bootstrap form, the Windows legacy convergence, the uv-takeover repair, the PDF carry-forward and the installer rebuild nx upgrade runs. The line is worded as what will happen, so it is true on --dry-run too. Where it reaches the user: interactive commands print it; the plugin setup (conexus/launch/nx_setup.py, which with the stand-in's repair command is the only caller that passes --bootstrap --extras pdf; web/install.ps1 and web/install.sh pass no extras and so build slim already) copies it into its done message; nx upgrade --auto writes it to the log only (upgrade_pdf_stack, upgrade_installer_rebuild), and the SessionStart lockstep action writes it to lockstep.log as lockstep_pdf_slim. The new receipt does not list pdf, so no later upgrade is held back by it. A typed nx self install --extras pdf builds the same way and says PDF support will not be installed and why. The first PDF index refuses with the download link and installs the stack on first use once the runtime is in place; PDFExtractor.extract refuses an installed stack that cannot load instead of failing with a raw WinError 126; nx doctor shows "PDF support" red, with the redistributable as the fix, on an install that has the stack but not the DLL, and amber (not installed, and why) on one that lacks both. The Desktop extension's opt-in PDF setting still applies, and its launcher adds a stderr warning when this machine lacks the runtime. The runtime needs no redistributable once an engine release carries the DLL, on every mode: the Windows PG bundle and the engine archive built from this source ship msvcp140_atomic_wait.dll (engine 0.1.164, which this release pins, does not carry it, so on 7.86.0 a Windows machine without the DLL installs the redistributable for PDF support) beside the other runtime DLLs (copied from the same redist folder, with the same Microsoft signature check); a local install places it beside the engine from an engine archive that has it, and nx upgrade (and nx init on a cloud or managed install) top it up into <config>/vcrt from the pinned PG bundle on a machine that has the base pair (in System32 or an earlier <config>/vcrt) or nothing, at most once per pinned engine tag (the tag checked is recorded in the vcrt provenance, so an older bundle is not fetched again until the pin moves). An older pinned archive or bundle without the DLL still installs. The DLL search path takes an app-local directory that has all three even when System32 has an old redistributable with only the base pair. A machine that bootstraps before nx init or the engine install has placed anything builds without PDF support, and PDF support installs on first use once they have. Until an engine release carries the DLL, PDF support on Windows needs the redistributable.

  • NX_MCPB_SKIP_RESOLVE_RETRY=1 no longer applies the .mcpb dependency pin (nexus-puzdu). The pin was still written into the bundle's pyproject.toml, but its unpinned fallback lives in the sync step the variable skips, and the launch uv run is an exec with no process left to retry, so a lock that cannot resolve on a machine would end the start where the unpinned install would have survived. With the variable set the launcher now applies no pin, removes a pin block left by an earlier launch, and says so on stderr.

  • A failed installer rebuild puts current and previous back (nexus-ff366). nx upgrade flipped to the rebuilt generation before checking that its receipt records the dependency lock as applied, so a build that did not take left current on a generation no better than the one it replaced, and each six-hourly retry built another. The check now runs after the flip and BEFORE the keep-3 reap (perform_self_install(after_flip=...); the reap still runs afterwards, also when the check fails), and a failure re-points current at the generation it replaced and previous at what it named before, through the same flip helpers (junctions and the logon task on Windows), keeps the backoff marker, and says so in the line. The line is worded from a re-read of both pointers: a previous that could not be restored, or was removed since the flip, is named as such, and a rollback that could not move current says it stays on the rebuilt generation.

  • On Windows the on-demand PDF install shows progress, and the upgrade's carry-forward checks the VC++ runtime (nexus-2k66t.4.7). The Windows generation build captured uv's output until uv exited, so the PDF-stack install (a download of several hundred MB) sat silent for minutes after its notice line while the POSIX path streamed. With progress set, generation_core.build_generation now copies uv's stderr through as it arrives and prints an install_generation: ... still running (Ns elapsed) heartbeat every 20 s of silence (uv writes no progress bar when its output is a pipe); both go to stderr, so callers that capture stdout are unaffected. The heartbeat waits for what is left of the silence window, so output landing just after a check no longer stretches the gap to twice 20 s. A dead stderr (closed pipe, none under pythonw) is ignored, so uv's pipe stays drained and the build's own error is reported; uv is killed and its pipes closed on Ctrl-C. The request now reaches every Windows build a person waits on: nx self install (so install.ps1's --bootstrap and the setup tool's build) and the legacy-tree convergence (migrate_legacy). On POSIX nx self install now also copies the builder's stderr through. pdf_stack.carry_forward skipped the Visual C++ runtime check ensure() runs, so a Windows box without it installed the stack and then showed PDF support as broken in nx doctor; both paths now run one check and refuse with the same words before building (the carry returns the refusal as its line, as it never raises, and leaves the auto-run backoff clear). nx upgrade provisions the runtime (_converge_vc_runtime) before the carry-forward, so the first upgrade on a cloud-mode box is not refused for want of the DLLs it is about to place.

  • PDF-stack and setup follow-ups (nexus-2k66t.4.8, nexus-5kvjb, nexus-w8ug5). A process waiting on the PDF-stack install lock prints one line saying so instead of blocking silently. nx index repo no longer installs the PDF stack when every PDF in its walk is already indexed: a catalog-only check clears a PDF only when its document is complete at the file's current sha256, and the indexer's own staleness check then runs on the cleared ones before any file is dispatched (one shared helper decides both); PDFs held early and late are recorded together for nx doctor, and the refusal prints once. nx self install --bootstrap removes the generation it superseded when that was a leftover (no current, the same version, or an interpreter that is gone); a working older generation, and a previous rollback target, are left to the normal keep-3 prune, and the bootstrap never removes uv's launchers. The setup tool's slim is now omitted, true or false: omitted resumes an unfinished slim run as slim, slim false switches it to the full install, and a slim run's resume and failure text say so.

  • Every install path installs the versions the release's uv.lock resolved, not a fresh package-index resolution (nexus-2k66t.4.1). install_generation.sh constrained an install only when handed a constraints file, and nothing in src/ or scripts/ ever handed it one, so a registry install resolved afresh: the 7.85.0 generation on the measuring box ran onnxruntime 1.31.0, opencv-python-headless 5.0 and pymupdf 1.28.2 against locked 1.24.2, 4.13 and 1.27.1 (nexus-l2ku5 was this class: mcp 2.0.0 resolved off PyPI and killed both MCP servers for four days). The wheel now ships nexus/_install/constraints.txt, generated from uv.lock by scripts/regen-install-constraints.sh (runtime set plus every extra, platform markers kept, no +cpu local label in the shipped file, so NX_TORCH_BACKEND=auto still resolves from it; scripts/reinstall-tool.sh's checkout flow exports the raw lock with its +cpu labels and does not, nexus-jd8fi). With no --constraints given, the POSIX builder and the Windows generation_core.build_generation fetch the package alone (uv pip install --no-deps), read the constraints it shipped out of the new venv, and install again with the dependencies under them; the file comes from the package being installed, never from the nx running the build, so an upgrade is held to the new release's lock. The installer that runs is still the running nx's own, so the pin starts once the running nx carries this change: an install of this release and every later upgrade are pinned, but the upgrade INTO this release from an older nx is built by the older, unpinned installer and is not. That covers nx self install, --bootstrap (the plugin's setup), the legacy-tree convergence and the session hook's nx self install on a generation box, which all go through the builder. scripts/reinstall-tool.sh from a checkout still exports the checkout's own lock; with a registry source (--allow-registry-over-dev) it takes the shipped file. A package that predates the file (a --version rollback) installs as before and the build prints a warning that no constraints.txt was found in it and its dependencies were resolved unconstrained. NX_INSTALL_UNPINNED=1 skips the pin on purpose (one unconstrained install and a warning that the install is not pinned to the release's lock), the way to take a transitive security fix ahead of a release; an explicit --constraints file still wins. macOS 14 or later on Apple Silicon is now the declared floor: the pinned onnxruntime 1.24.2 ships only macOS 14 wheels, so a macOS 13 Mac fails the resolve, and the failure message now says so. Not covered: a bare uv tool install conexus / pip install conexus (and the hook's uv tool upgrade on an unmigrated legacy tree), the throwaway environment uv tool run makes to run the bootstrap (it is discarded; the generation it builds is pinned), and the .mcpb bundle's uv sync, none of which can read a lock before the package is fetched. tests/test_install_constraints_pin.py fails the default suite while the shipped file and uv.lock disagree (regenerate after any lock change; the release skill's Step 5 does it), and the fresh-install MVV (leg 9) and the package-upgrade MVV (Stage 6) now assert that an installed generation equals the lock for onnxruntime, opencv-python-headless, pymupdf and mcp.