feat: /sessions cluster --algorithm kmeans --k N for concept-based session grouping (F5.2) #23

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

Problem Statement

The session-search spec (F5.2, lines 432-444) defines three concept-discovery commands. Two are tracked: /sessions by-concept --connected-to (#22) and /sessions path (#12). The third -- /sessions cluster --algorithm kmeans --k 5 -- is not tracked and not implemented. Verified:

rg "kmeans|cluster_sessions|by_concept_cluster" crates/terraphim_sessions/   -> 0 matches

Without clustering, agents cannot answer "group my last 30 days of work into 5 thematic buckets" -- a key cross-session retrospective query that complements /sessions analyze (#16) and /sessions timeline. Clustering reuses concept vectors already produced by SessionEnricher and the rolegraph's concept-frequency data, so this is an analyser, not new infrastructure.

Upstream: spec F5.2 third bullet, Phase 3 Task 3.1.3 dominant-topic identification "concept clustering" callout.

Acceptance Criteria

  • Add SessionsSubcommand::Cluster { algorithm: ClusterAlgorithm, k: usize, since: Option<String>, top_n_concepts: Option<usize> } parsed from /sessions cluster --algorithm kmeans --k 5
  • ClusterAlgorithm enum with at least Kmeans (extensible; default kmeans)
  • Implement crates/terraphim_sessions/src/enrichment/cluster.rs::SessionClusterer that:
    • Builds a sparse concept-frequency vector per session from SessionEnrichment::concepts
    • Runs k-means (use the existing kmeans = "..." crate or simple in-tree implementation; no new heavy deps)
    • Returns Vec<SessionCluster { id: usize, dominant_concepts: Vec<String>, session_ids: Vec<SessionId>, centroid_size: usize }>
  • Validate k: clamp to [2, sessions.len().min(20)]; return ExitCode::ERROR_USAGE with suggestion if input invalid
  • Empty session set returns ExitCode::ERROR_NOT_FOUND with suggestion "run /sessions import first"
  • Robot-mode output: RobotResponse { data: { algorithm, k, cluster_count, clusters: [{ id, dominant_concepts, session_ids, size }] } }
  • Interactive mode renders one table per cluster with rank, session id, title preview
  • Schema entries added to robot/docs.rs so robot schemas sessions cluster works (matches Task 1.3 self-doc pattern)
  • Unit tests with deterministic fixture sessions (seeded RNG; assert cluster membership for k=2)
  • cargo test -p terraphim_sessions --features enrichment cluster passes
  • cargo test -p terraphim_agent --features repl-sessions,enrichment sessions_cluster passes
  • cargo clippy -p terraphim_sessions --features enrichment -- -D warnings passes

Proposed Approach

  • Crates touched: terraphim_sessions (new enrichment/cluster.rs), terraphim_agent (repl/commands.rs, repl/handler.rs, robot/docs.rs)
  • Reuse SessionEnrichment::concepts for vectors; index concepts via BTreeMap<String, usize> for sparse-to-dense mapping
  • For k-means: small in-tree implementation (Lloyd's algorithm, ~80 LOC) over Vec<f32> cosine distance -- avoids adding a crate just for one algorithm
  • Seed RNG with a constant for reproducible clusters in tests; expose --seed flag (hidden) for determinism
  • Dominant concepts per cluster = top N by centroid weight

Dependencies

None. Concept enrichment is already shipped per Phase 3 status. Independent of #16 (analyse), #12 (path), #22 (by-concept).

Verification

cargo test -p terraphim_sessions --features enrichment cluster
cargo test -p terraphim_agent --features repl-sessions,enrichment sessions_cluster
cargo clippy -p terraphim_sessions --features enrichment -- -D warnings
./target/debug/terraphim-agent --robot sessions cluster --algorithm kmeans --k 5
./target/debug/terraphim-agent --robot sessions cluster --algorithm kmeans --k 3 --since 2026-04-01

Scope

Single PR, ~350-450 lines (clusterer ~150, command/handler ~120, robot docs ~30, tests ~100). Closes the third F5.2 command and brings spec coverage of F5.2 from 2/3 to 3/3.

## Problem Statement The session-search spec (F5.2, lines 432-444) defines three concept-discovery commands. Two are tracked: `/sessions by-concept --connected-to` (#22) and `/sessions path` (#12). The third -- `/sessions cluster --algorithm kmeans --k 5` -- is not tracked and not implemented. Verified: ``` rg "kmeans|cluster_sessions|by_concept_cluster" crates/terraphim_sessions/ -> 0 matches ``` Without clustering, agents cannot answer "group my last 30 days of work into 5 thematic buckets" -- a key cross-session retrospective query that complements `/sessions analyze` (#16) and `/sessions timeline`. Clustering reuses concept vectors already produced by `SessionEnricher` and the rolegraph's concept-frequency data, so this is an analyser, not new infrastructure. Upstream: spec F5.2 third bullet, Phase 3 Task 3.1.3 dominant-topic identification "concept clustering" callout. ## Acceptance Criteria - [ ] Add `SessionsSubcommand::Cluster { algorithm: ClusterAlgorithm, k: usize, since: Option<String>, top_n_concepts: Option<usize> }` parsed from `/sessions cluster --algorithm kmeans --k 5` - [ ] `ClusterAlgorithm` enum with at least `Kmeans` (extensible; default `kmeans`) - [ ] Implement `crates/terraphim_sessions/src/enrichment/cluster.rs::SessionClusterer` that: - Builds a sparse concept-frequency vector per session from `SessionEnrichment::concepts` - Runs k-means (use the existing `kmeans = "..."` crate or simple in-tree implementation; no new heavy deps) - Returns `Vec<SessionCluster { id: usize, dominant_concepts: Vec<String>, session_ids: Vec<SessionId>, centroid_size: usize }>` - [ ] Validate `k`: clamp to `[2, sessions.len().min(20)]`; return `ExitCode::ERROR_USAGE` with suggestion if input invalid - [ ] Empty session set returns `ExitCode::ERROR_NOT_FOUND` with suggestion "run /sessions import first" - [ ] Robot-mode output: `RobotResponse { data: { algorithm, k, cluster_count, clusters: [{ id, dominant_concepts, session_ids, size }] } }` - [ ] Interactive mode renders one table per cluster with rank, session id, title preview - [ ] Schema entries added to `robot/docs.rs` so `robot schemas sessions cluster` works (matches Task 1.3 self-doc pattern) - [ ] Unit tests with deterministic fixture sessions (seeded RNG; assert cluster membership for k=2) - [ ] `cargo test -p terraphim_sessions --features enrichment cluster` passes - [ ] `cargo test -p terraphim_agent --features repl-sessions,enrichment sessions_cluster` passes - [ ] `cargo clippy -p terraphim_sessions --features enrichment -- -D warnings` passes ## Proposed Approach - Crates touched: `terraphim_sessions` (new `enrichment/cluster.rs`), `terraphim_agent` (`repl/commands.rs`, `repl/handler.rs`, `robot/docs.rs`) - Reuse `SessionEnrichment::concepts` for vectors; index concepts via `BTreeMap<String, usize>` for sparse-to-dense mapping - For k-means: small in-tree implementation (Lloyd's algorithm, ~80 LOC) over `Vec<f32>` cosine distance -- avoids adding a crate just for one algorithm - Seed RNG with a constant for reproducible clusters in tests; expose `--seed` flag (hidden) for determinism - Dominant concepts per cluster = top N by centroid weight ## Dependencies None. Concept enrichment is already shipped per Phase 3 status. Independent of #16 (analyse), #12 (path), #22 (by-concept). ## Verification ``` cargo test -p terraphim_sessions --features enrichment cluster cargo test -p terraphim_agent --features repl-sessions,enrichment sessions_cluster cargo clippy -p terraphim_sessions --features enrichment -- -D warnings ./target/debug/terraphim-agent --robot sessions cluster --algorithm kmeans --k 5 ./target/debug/terraphim-agent --robot sessions cluster --algorithm kmeans --k 3 --since 2026-04-01 ``` ## Scope Single PR, ~350-450 lines (clusterer ~150, command/handler ~120, robot docs ~30, tests ~100). Closes the third F5.2 command and brings spec coverage of F5.2 from 2/3 to 3/3.
Sign in to join this conversation.