You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Indexing: generalize the single-volume indexer into a per-volume registry (M1, no behavior change)
Replaces the one hardwired `INDEXING: Mutex<IndexPhase>` global with `INDEX_REGISTRY: Mutex<HashMap<VolumeId, IndexInstance>>`, where each `IndexInstance` bundles a volume's `{phase, read_pool, pending_sizes}`. This is the structural foundation for indexing SMB shares and MTP devices (later milestones); M1 lands the refactor in isolation with the local disk (`root`) still the only registered volume, so externally observable behavior is byte-identical.
- **Per-volume lifecycle, keyed independently.** Every invariant the single-volume design held now holds per volume id: single-writer-per-DB, lock-first reservation (`(absent) -> Initializing` claimed atomically per volume before building the heavy `IndexManager`), drop-the-guard-before-`mgr.shutdown()`'s 5 s drain, and reads via `ReadPool` never under the lifecycle lock. Two volumes can't corrupt each other; two starts for the same volume still can't race (the `UNIQUE(parent_id, name_folded)` safety net is unchanged).
- **Disabled is the absence of a key** — there's no `IndexPhase::Disabled` variant; stop/clear remove the instance after the drain.
- **Reads route by volume id.** `get_read_pool_for(vid)` / `get_pending_sizes_for(vid)` route root to the `READ_POOL`/`PENDING_SIZES` globals (the same `Arc` the root instance holds, so they can't drift — kept as globals because search reads them hot and the tests install them directly) and non-root to the registry instance. Enrichment, verification, and IPC dir-stats thread the listing's `volume_id` where they have it.
- **The `should_exclude` early-return in `enrichment.rs` is reframed to "skip if no index is registered for this volume"** (`get_read_pool_for` returns `None`). In M1 only `root` has a pool, so every non-root listing still early-returns exactly as before — same set of listings skipped, new reason. For `root` the local-path `should_exclude` check is also retained so a root-volume listing navigated under `/Volumes`, `/mnt`, or `/proc` still skips (no "Parent path not found" log-spam on network mounts).
- **IPC surface unchanged.** `#[tauri::command]` signatures and `bindings.ts` are byte-identical: path-based commands resolve the volume internally (M1: always `root`, with a `TODO(M2/M4)` for `/Volumes/<share>` and `mtp-*` virtual-path mapping); the scan/status/clear commands pass `ROOT_VOLUME_ID`.
Tests: added `state.rs::tests` for the registry (skip-vs-route gate tracks registration; two volume ids reserve/release independently and route to distinct pools). The existing `integration_tests.rs` enrichment/`ReadPool`/dir-stats/FDA regression tests pass with their assertions unchanged; the `IndexPhase` lifecycle harness tests were ported mechanically to the registry (same assertion intent — "phase Disabled" becomes "instance absent"). Full `pnpm check rust` green (clippy clean, `bindings-fresh` confirms the IPC surface, 2863 + 38 tests on macOS and the Linux Docker lane).
Left for later milestones (TODOs in-code): the memory watchdog still stops only `root` (M2 makes it a global budget), and `volume_id_for_local_path` maps everything to `root` until SMB/MTP backends register (M2/M4).
0 commit comments