* feat: assorted improvements for CLI/daemon to be resilient and ergonomic
* tests: add more e2e tests
* fix: resolve path comparison and LMDB cleanup issues on Windows/3.14t
Three root causes fixed:
1. Path resolution mismatch on Windows: init and auto_init_project used
unresolved Path.cwd() but find_parent_with_marker/find_project_root
resolve internally, causing comparison failures and daemon key mismatches.
2. LMDB not released on remove_project: The daemon ProjectRegistry
dropped the Project from dicts without closing its SQLite connection or
forcing GC of the Rust LMDB environment. On free-threaded Python (3.14t)
and Windows, deferred GC kept the LMDB open, causing environment already
open errors and PermissionErrors when deleting db files.
3. Silent connection close on streaming errors: When update_index async
iteration failed in the daemon, the connection was closed without sending
an ErrorResponse, causing the client to get an unhelpful EOFError.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: break LMDB reference chain explicitly and resolve cwd in path filter
On 3.14t (free-threaded Python), gc.collect() alone does not release the
Rust LMDB environment because deferred reference counting keeps
core.Environment alive through App._core_env_app, ContextProvider._core_env,
and Environment._core_env. Explicitly null these internal references in
Project.close() before gc.collect() so the Rust object is freed promptly.
Also resolve Path.cwd() in resolve_default_path() — on Windows, the
unresolved cwd did not match the resolved project_root, causing
relative_to() to fail and the subdirectory path filter to be skipped.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: use double gc.collect() for layered deferred refcount on 3.14t
Revert fragile internal attribute clearing. On free-threaded Python, the
first gc.collect() frees Python wrappers whose Rust Drop implementations
issue further deferred Py_DECREF calls on core.Environment; a second
gc.collect() flushes those and actually drops the LMDB handle.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add sleep in tests for LMDB release on free-threaded Python
On 3.14t, deferred reference counting means the Rust LMDB environment
is not released immediately after remove_project + gc.collect(). Add a
1-second sleep in the two tests that reset and re-index, giving the
runtime time to process pending deferred Py_DECREF calls.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: restart daemon after reset in tests, increase daemon wait timeout
Instead of sleeping, restart the daemon after reset in the two tests
that re-index after removing databases. This reliably releases the LMDB
environment on all platforms including free-threaded Python (3.14t).
Also increase _wait_for_daemon timeout from 5s to 10s — Windows CI
runners occasionally need longer to start the daemon subprocess.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>