feat: SessionConnector::watch live import streaming (F4.1) #20

Open
opened 2026-04-25 03:01:33 +02:00 by product-owner · 0 comments

Problem Statement

Spec F4.1 (terraphim-agent-session-search-spec.md, lines 293-308) defines SessionConnector::watch() returning Option<mpsc::Receiver<Session>> so live AI-coding sessions can stream into the index without a full re-import. The current trait in crates/terraphim_sessions/src/connector/mod.rs (lines 88-105) exposes only source_id, display_name, detect, default_path, and import -- there is no watch method on any connector. As a result, REPL users must manually run /sessions import after each Claude Code or Cursor session, and the Tantivy index (issue #5) cannot receive incremental updates. This blocks the "live cross-tool memory" promise of Phase 2.

Acceptance Criteria

  • Add async fn watch(&self) -> Option<tokio::sync::mpsc::Receiver<Session>> with a default None implementation to the SessionConnector trait
  • Implement watch for NativeClaudeConnector using notify (or equivalent) to detect new/modified ~/.claude/**/*.jsonl files; emit Session items as files appear or grow
  • Add ConnectorRegistry::watch_all() -> mpsc::Receiver<Session> that fans-in receivers from all registered connectors
  • Wire SessionService (in crates/terraphim_sessions/src/service.rs) with a start_watcher() helper that spawns a background task draining the merged stream into the cache
  • Other connectors (AiderConnector, ClineConnector, CLA connectors) keep the default None implementation; they are not required to add file watchers in this PR
  • Unit test in crates/terraphim_sessions/src/connector/native.rs verifies that creating a new JSONL file under a temp dir produces a Session on the watch channel within 2 seconds
  • cargo test -p terraphim_sessions passes
  • cargo clippy -p terraphim_sessions -- -D warnings passes

Proposed Approach

Add watch as a trait method with a default implementation returning None so existing connectors do not break. Use tokio::sync::mpsc::channel(64) for backpressure. For NativeClaudeConnector, spawn a notify::recommended_watcher on the resolved Claude data directory and translate Create/Modify events into import_single_file() calls. ConnectorRegistry::watch_all() collects each connector's optional receiver and forwards items through a single output channel using tokio::spawn per source.

Crates affected:

  • crates/terraphim_sessions -- trait change, NativeClaudeConnector::watch, registry helper, service helper
  • (no changes to terraphim_agent REPL in this PR; REPL wiring follows in a separate issue)

Key types/functions to add:

  • SessionConnector::watch (trait method, default None)
  • NativeClaudeConnector::watch (concrete impl)
  • ConnectorRegistry::watch_all
  • SessionService::start_watcher

Dependencies

None. Independent of the open Tantivy index issue (#5); when both are implemented, a follow-up can push streamed sessions directly into Tantivy.

Verification

cargo test -p terraphim_sessions connector::native::watch
cargo test -p terraphim_sessions
cargo clippy -p terraphim_sessions -- -D warnings

Manual: in a scratch script, call ConnectorRegistry::new().watch_all(), then touch ~/.claude/projects/test/conversation.jsonl and confirm a Session arrives on the channel.

Scope

Single PR, ~200-350 LOC including the trait change, one concrete watch implementation, registry fan-in, service helper, and the unit test. REPL surface (e.g. a /sessions watch command) is intentionally deferred.

## Problem Statement Spec F4.1 (terraphim-agent-session-search-spec.md, lines 293-308) defines `SessionConnector::watch()` returning `Option<mpsc::Receiver<Session>>` so live AI-coding sessions can stream into the index without a full re-import. The current trait in `crates/terraphim_sessions/src/connector/mod.rs` (lines 88-105) exposes only `source_id`, `display_name`, `detect`, `default_path`, and `import` -- there is no `watch` method on any connector. As a result, REPL users must manually run `/sessions import` after each Claude Code or Cursor session, and the Tantivy index (issue #5) cannot receive incremental updates. This blocks the "live cross-tool memory" promise of Phase 2. ## Acceptance Criteria - [ ] Add `async fn watch(&self) -> Option<tokio::sync::mpsc::Receiver<Session>>` with a default `None` implementation to the `SessionConnector` trait - [ ] Implement `watch` for `NativeClaudeConnector` using `notify` (or equivalent) to detect new/modified `~/.claude/**/*.jsonl` files; emit `Session` items as files appear or grow - [ ] Add `ConnectorRegistry::watch_all() -> mpsc::Receiver<Session>` that fans-in receivers from all registered connectors - [ ] Wire `SessionService` (in `crates/terraphim_sessions/src/service.rs`) with a `start_watcher()` helper that spawns a background task draining the merged stream into the cache - [ ] Other connectors (`AiderConnector`, `ClineConnector`, CLA connectors) keep the default `None` implementation; they are not required to add file watchers in this PR - [ ] Unit test in `crates/terraphim_sessions/src/connector/native.rs` verifies that creating a new JSONL file under a temp dir produces a `Session` on the watch channel within 2 seconds - [ ] `cargo test -p terraphim_sessions` passes - [ ] `cargo clippy -p terraphim_sessions -- -D warnings` passes ## Proposed Approach Add `watch` as a trait method with a default implementation returning `None` so existing connectors do not break. Use `tokio::sync::mpsc::channel(64)` for backpressure. For `NativeClaudeConnector`, spawn a `notify::recommended_watcher` on the resolved Claude data directory and translate `Create`/`Modify` events into `import_single_file()` calls. `ConnectorRegistry::watch_all()` collects each connector's optional receiver and forwards items through a single output channel using `tokio::spawn` per source. Crates affected: - `crates/terraphim_sessions` -- trait change, `NativeClaudeConnector::watch`, registry helper, service helper - (no changes to `terraphim_agent` REPL in this PR; REPL wiring follows in a separate issue) Key types/functions to add: - `SessionConnector::watch` (trait method, default `None`) - `NativeClaudeConnector::watch` (concrete impl) - `ConnectorRegistry::watch_all` - `SessionService::start_watcher` ## Dependencies None. Independent of the open Tantivy index issue (#5); when both are implemented, a follow-up can push streamed sessions directly into Tantivy. ## Verification ``` cargo test -p terraphim_sessions connector::native::watch cargo test -p terraphim_sessions cargo clippy -p terraphim_sessions -- -D warnings ``` Manual: in a scratch script, call `ConnectorRegistry::new().watch_all()`, then `touch ~/.claude/projects/test/conversation.jsonl` and confirm a `Session` arrives on the channel. ## Scope Single PR, ~200-350 LOC including the trait change, one concrete `watch` implementation, registry fan-in, service helper, and the unit test. REPL surface (e.g. a `/sessions watch` command) is intentionally deferred.
Sign in to join this conversation.