Files
bc9fa6b3c8 fix(memory): surface clear error when openjarvis_rust missing instead of silent no-op (#527)
Memory tools degraded silently and misleadingly when the mandatory
`openjarvis_rust` extension was absent from the *serving* venv:

- `POST /v1/memory/store` returned HTTP 200 `{"status":"stored","note":
  "no backend available"}` and stored nothing (silent data loss).
- `POST /v1/memory/index` returned a generic "No memory backend available",
  and the desktop frontend discarded the server `detail` and threw a blanket
  "Failed to index path", blaming the path instead of the real cause.
- `GET /v1/memory/config` reported `backend_type: sqlite` even though no
  backend could be constructed.

Root cause: `SQLiteMemory.__init__` calls `get_rust_module()` (which raises
ImportError by design — the Rust ext is mandatory, no Python fallback), and
`_get_memory_backend` swallowed that ImportError and returned `None`,
conflating "native extension missing" (a hard install error) with "memory
intentionally disabled" (benign). A chunking floor also silently dropped whole
short documents, and the installer never verified the extension imported from
the serving venv before writing its success marker.

Fix (no fake Python fallback — the Rust ext stays mandatory by design):

- Add `MemoryBackendUnavailable` + `RUST_MISSING_HINT` in tools/storage/_stubs.
  `SQLiteMemory.__init__` translates the bridge ImportError into this clear,
  actionable error ("run `uv run maturin develop ...`").
- `_get_memory_backend` distinguishes the two cases: a missing native ext
  raises HTTP 503 with the actionable hint; a benign unconfigured backend
  still returns `None` (graceful path preserved for search/stats).
- `/store` now returns 503 instead of a 200 silent no-op.
- `/config` reports `available: false` + `detail` instead of falsely claiming
  a healthy `backend_type`.
- `/index` adds a `note` when `chunks_indexed == 0` so "indexed" never
  silently means "stored nothing".
- chunk_text no longer drops an entire short document below `min_chunk_size`
  (the floor only discards tiny *trailing* fragments now).
- Frontend `storeMemory`/`indexMemoryPath` surface the server `detail` instead
  of blanket strings; `MemoryConfig` gains optional `available`/`detail`.
  (Left the pre-existing `backend` vs `backend_type` mismatch untouched.)
- build-extension.sh verifies `import openjarvis_rust` succeeds in the serving
  venv before writing the `extension-built` marker.

Regression tests: tests/server/test_api_routes.py::TestMemoryRustMissing mocks
`get_rust_module` to raise ImportError and asserts /store (503, not 200 no-op),
/index (actionable detail, not "Failed to index path"), and /config
(available:false) all surface the clear error; tests/memory/test_chunking.py
asserts short-only docs are kept while tiny trailing fragments are still
filtered.

Fixes #502

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 13:25:10 -07:00
..