Skip to content

feat(intelligence): add Connection Path finder#2946

Merged
senamakel merged 12 commits into
tinyhumansai:mainfrom
aashir-athar:feat/connection-path
May 30, 2026
Merged

feat(intelligence): add Connection Path finder#2946
senamakel merged 12 commits into
tinyhumansai:mainfrom
aashir-athar:feat/connection-path

Conversation

@aashir-athar
Copy link
Copy Markdown
Contributor

@aashir-athar aashir-athar commented May 29, 2026

Summary

Adds a new read-only "Paths" tab that traces the shortest chain of relations linking any two entities — answering "how is A connected to B?" with the actual hop-by-hop path, something a single direct-edge lookup can't show.

Pick two entities (type-ahead pickers populated from the graph) and the tab renders the path as a node chain, e.g. You —[works_at]→ Acme ←[founded_by]— Dana, with each hop's predicate and direction.

Design

  • Pure & deterministic (lib/memory/connectionPath.ts): BFS shortest path over the undirected graph (a relation connects its endpoints regardless of arrow direction), but each hop records the predicate and whether it was traversed forward or backward, so the chain reads naturally. No clock, no RNG — ties are broken by a fixed sorted neighbour order, so the same graph + endpoints always yield the same path. Self-loops are ignored, and distinct reasons cover same-entity / missing-endpoint / no-path.
  • Zero new core surface: reuses the already-shipped memoryGraphQuery and memoryListNamespaces wrappers. The graph is fetched once per namespace; path-finding itself runs in the pure synchronous engine via useMemo, so tracing endpoints is instant with no extra round-trips. Read-only.
  • Container/presentational split; the container guards the graph load with a monotonic request token; two <datalist> (type-ahead) entity pickers handle large graphs gracefully; namespace selector. i18n across all 13 locales.

Edge cases (tested)

Forward chain, reversed-direction hops, direct one-hop link, same-entity, missing source/target, disconnected (no path), shortest-path tie-break determinism + relation-order invariance, self-loop ignored, malformed rows dropped.

Test plan

  • vitest — 24 tests (engine: chains/direction/same/missing/no-path/tie-break/self-loop/direct/malformed; api facade; panel states; container load + trace + error)
  • tsc --noEmit — clean
  • eslint — 0 errors
  • prettier --check — clean
  • i18n coverage gate — EXIT 0, no unused connectionPath.* keys

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added a "Paths" tab to Intelligence to find and display shortest connection paths between entities, with namespace filtering, loading/error states, and retry.
    • Deterministic shortest-path computation with clear messages for same/missing/no-path cases and directional relation display.
  • Localization
    • Full translations for the new UI across all supported languages.
  • Tests
    • Added comprehensive tests covering UI, path engine, and API behaviors.

Review Change Stack

@aashir-athar aashir-athar requested a review from a team May 29, 2026 13:54
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a Connection Path feature: deterministic shortest-path engine, RPC graph loader, React tab and panel UI, Intelligence page wiring, tests, and i18n strings across 14 locales.

Changes

Connection Path Feature

Layer / File(s) Summary
Shortest-path algorithm and core types
app/src/lib/memory/connectionPath.ts, app/src/lib/memory/connectionPath.test.ts
Deterministic BFS engine with stable tie-breaking via sorted neighbor traversal; exports ConnectionPathResult, PathHop, and PathReason types; unit tests validate forward chains, bidirectional edges, missing/same/no-path outcomes, self-loop filtering, and malformed relation handling.
Graph data loading service
app/src/services/api/connectionPathApi.ts, app/src/services/api/connectionPathApi.test.ts
RPC facade wrapping memory graph and namespace queries; loadGraph(namespace?) deduplicates and sorts entities from relation endpoints; loadNamespaces() passthrough; tests cover namespace handling, entity deduplication, and error propagation.
Path result presentational component
app/src/components/intelligence/ConnectionPathPanel.tsx, app/src/components/intelligence/ConnectionPathPanel.test.tsx
Renders five states: loading skeleton, error alert with optional retry, empty-graph message, pick-entities prompt, or full path result with directional hops and node labels; derives localized status messages from result reason codes; tests verify all UI states.
Container tab with orchestration
app/src/components/intelligence/ConnectionPathTab.tsx, app/src/components/intelligence/ConnectionPathTab.test.tsx
Loads namespaces and graph data (with monotonic request IDs to prevent stale updates); namespace dropdown and source/target text inputs backed by datalist; computes path via useMemo; wires ConnectionPathPanel; tests cover mount, user input, rendering, and error flows.
Intelligence page integration
app/src/pages/Intelligence.tsx
Adds path tab to IntelligenceTab union; registers tab with i18n label; conditionally renders ConnectionPathTab.
Internationalization
app/src/lib/i18n/en.ts, app/src/lib/i18n/{ar,bn,de,es,fr,hi,id,it,ko,pl,pt,ru,zh-CN}.ts
Adds memory.tab.path and connectionPath.* UI strings across 14 locales covering loading/error/retry, input labels/placeholders, prompts, empty states, result headings, and validation messages.

Sequence Diagram — Tab lifecycle and data flow:

sequenceDiagram
  participant User
  participant ConnectionPathTab
  participant connectionPathApi
  participant findConnectionPath
  participant ConnectionPathPanel
  User->>ConnectionPathTab: mount, set namespace, enter source/target
  ConnectionPathTab->>connectionPathApi: loadNamespaces(), loadGraph(namespace)
  connectionPathApi-->>ConnectionPathTab: {entities, relations}
  ConnectionPathTab->>findConnectionPath: findConnectionPath(relations, source, target)
  findConnectionPath-->>ConnectionPathTab: ConnectionPathResult
  ConnectionPathTab->>ConnectionPathPanel: render(result, hasGraph, loading, error, onRetry)
  ConnectionPathPanel-->>User: display path or status message
Loading

Estimated code review effort:
🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs:

Suggested reviewers:

  • graycyrus
  • sanil-23

Poem

🐇 I hop through nodes both near and far,
Tracing predicates, following each star.
With BFS steps and ties resolved,
Shortest paths are now evolved—
Hooray for paths that show who knows who!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat(intelligence): add Connection Path finder' directly and clearly summarizes the main change: adding a new Connection Path feature to the intelligence module. It matches the core purpose evident across all file changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot added feature Net-new user-facing capability or product behavior. memory Memory store, memory tree, recall, summarization, and embeddings in src/openhuman/memory/. working A PR that is being worked on by the team. labels May 29, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
app/src/components/intelligence/ConnectionPathTab.tsx (1)

60-65: Path recompute rebuilds adjacency on every keystroke.

findConnectionPath reconstructs the full adjacency map and sorts neighbours on each call, and this useMemo re-runs whenever trimmedSource/trimmedTarget change (i.e. per keystroke). For small graphs this is negligible, but if the namespace graph is large this synchronous main-thread work can introduce typing lag. If graphs can grow large, consider precomputing the adjacency once per graph.relations (memoized separately) or debouncing the endpoint inputs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/components/intelligence/ConnectionPathTab.tsx` around lines 60 - 65,
The path recompute builds adjacency on every keystroke; fix by precomputing and
memoizing the adjacency once per graph.relations and/or debouncing endpoint
inputs: add a memoized adjacency (e.g. useMemo(() =>
buildAdjacency(graph.relations), [graph.relations])) and change
ConnectionPathTab to call findConnectionPath with the precomputed adjacency (or
refactor findConnectionPath into two functions: buildAdjacency and findPath) so
trimmedSource/trimmedTarget changes only trigger the search, not adjacency
reconstruction; alternatively add a short debounce for
trimmedSource/trimmedTarget before invoking the search to reduce per-keystroke
work.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@app/src/components/intelligence/ConnectionPathTab.tsx`:
- Around line 60-65: The path recompute builds adjacency on every keystroke; fix
by precomputing and memoizing the adjacency once per graph.relations and/or
debouncing endpoint inputs: add a memoized adjacency (e.g. useMemo(() =>
buildAdjacency(graph.relations), [graph.relations])) and change
ConnectionPathTab to call findConnectionPath with the precomputed adjacency (or
refactor findConnectionPath into two functions: buildAdjacency and findPath) so
trimmedSource/trimmedTarget changes only trigger the search, not adjacency
reconstruction; alternatively add a short debounce for
trimmedSource/trimmedTarget before invoking the search to reduce per-keystroke
work.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6278eb1b-23e7-4d21-89de-43e6e4944ba7

📥 Commits

Reviewing files that changed from the base of the PR and between 3e59548 and 1107f3b.

📒 Files selected for processing (23)
  • app/src/components/intelligence/ConnectionPathPanel.test.tsx
  • app/src/components/intelligence/ConnectionPathPanel.tsx
  • app/src/components/intelligence/ConnectionPathTab.test.tsx
  • app/src/components/intelligence/ConnectionPathTab.tsx
  • app/src/lib/i18n/chunks/ar-1.ts
  • app/src/lib/i18n/chunks/bn-1.ts
  • app/src/lib/i18n/chunks/de-1.ts
  • app/src/lib/i18n/chunks/en-1.ts
  • app/src/lib/i18n/chunks/es-1.ts
  • app/src/lib/i18n/chunks/fr-1.ts
  • app/src/lib/i18n/chunks/hi-1.ts
  • app/src/lib/i18n/chunks/id-1.ts
  • app/src/lib/i18n/chunks/it-1.ts
  • app/src/lib/i18n/chunks/ko-1.ts
  • app/src/lib/i18n/chunks/pt-1.ts
  • app/src/lib/i18n/chunks/ru-1.ts
  • app/src/lib/i18n/chunks/zh-CN-1.ts
  • app/src/lib/i18n/en.ts
  • app/src/lib/memory/connectionPath.test.ts
  • app/src/lib/memory/connectionPath.ts
  • app/src/pages/Intelligence.tsx
  • app/src/services/api/connectionPathApi.test.ts
  • app/src/services/api/connectionPathApi.ts

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 29, 2026
Copy link
Copy Markdown
Contributor

@graycyrus graycyrus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aashir-athar hey! the code looks good to me, but CI is still pending (Frontend Unit Tests, Rust Core Tests, e2e, Build Tauri App). once those are green, i'll come back and approve this. let me know if you need any help!

One minor thing while I was in here: ConnectionPathPanel.tsx lines 199-207 use manual .replace('{entity}', ...) / .replace('{source}', ...) for i18n interpolation. This is fragile — if any translation omits a placeholder, the replacement silently no-ops. If useT supports parameterized keys (e.g. t('key', { entity })), use that instead. If not, not a blocker, but worth a follow-up.

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 29, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 30, 2026
A new read-only "Paths" tab that traces the shortest chain of relations linking
any two entities — answering "how is A connected to B?" with the actual hop-by-
hop path, something a direct-edge lookup can't show.

- Pure deterministic engine (lib/memory/connectionPath.ts): BFS shortest path
  over the UNDIRECTED graph (a relation connects its endpoints regardless of
  arrow direction), but each hop records the predicate and whether it was
  traversed forward or backward so the chain reads naturally. No clock, no RNG;
  ties broken by a fixed sorted neighbour order, so the same graph + endpoints
  always yield the same path. Self-loops ignored; distinct reasons for same /
  missing-endpoint / no-path.
- Zero new core surface: reuses memoryGraphQuery + memoryListNamespaces. The
  graph is fetched once; path-finding runs in the pure sync engine via useMemo,
  so tracing is instant with no extra round-trips. Read-only.
- Container guards the graph load with a request token; two datalist (type-
  ahead) entity pickers; the panel renders the path as a node chain with
  per-hop predicate + direction. i18n across all 13 locales.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
senamakel added 3 commits May 30, 2026 07:05
# Conflicts:
#	app/src/lib/i18n/chunks/ar-1.ts
#	app/src/lib/i18n/chunks/bn-1.ts
#	app/src/lib/i18n/chunks/de-1.ts
#	app/src/lib/i18n/chunks/en-1.ts
#	app/src/lib/i18n/chunks/es-1.ts
#	app/src/lib/i18n/chunks/fr-1.ts
#	app/src/lib/i18n/chunks/hi-1.ts
#	app/src/lib/i18n/chunks/id-1.ts
#	app/src/lib/i18n/chunks/it-1.ts
#	app/src/lib/i18n/chunks/ko-1.ts
#	app/src/lib/i18n/chunks/pt-1.ts
#	app/src/lib/i18n/chunks/ru-1.ts
#	app/src/lib/i18n/chunks/zh-CN-1.ts
Resolve the merge of main (flat-locale i18n layout) with the PR by
adding the 21 Connection Path keys (connectionPath.* + memory.tab.path)
to all 13 non-English locale files. en.ts already carried them via the
PR; main retired the chunks/<locale>-N.ts layout, so the keys move into
the flat per-locale files. Placeholder tokens ({length}/{entity}/{source}/
{target}) preserved. pnpm i18n:check and i18n:english:check both pass.
…derabbitai)

- connectionPath.ts: short-circuit self-paths (source===target) and validate
  both nodes exist in the adjacency before BFS, returning explicit status codes
  (same / missing-source / missing-target / no-path / found) so the UI can show
  the right message instead of silently running BFS on absent nodes.
- ConnectionPathPanel.tsx: memoize adjacency build and BFS (useMemo) so typing
  in either entity field no longer rebuilds the whole graph every render; map the
  new status codes to the existing connectionPath.* i18n messages.
- ConnectionPathTab.tsx: guard the namespace fetch effect with a cancelled flag
  in cleanup so rapid namespace switches can't apply out-of-order results.
- Tests: cover self-path, missing-source, missing-target, and multi-hop found.
}

/** Fetch the graph for a namespace (or all) and derive the sorted entity list. */
export async function loadGraph(namespace?: string): Promise<GraphData> {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re: CodeRabbit's debounce/race suggestion for this module — addressed at the caller instead of here. connectionPathApi.ts is a thin stateless fetch wrapper, so adding an AbortController would change its exported signature for all callers. The actual race (rapid namespace switches applying out-of-order results) is now guarded in ConnectionPathTab.tsx via a cancelled flag in the effect cleanup, so stale responses are dropped. Flagging here for visibility.

Pre-push hook (prettier) wrapped long connectionPath.intro lines across the
13 locale files and normalized ConnectionPathPanel.test.tsx. No content
changes; formatting only.
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 30, 2026
# Conflicts:
#	app/src/pages/Intelligence.tsx
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 30, 2026
# Conflicts:
#	app/src/lib/i18n/ar.ts
#	app/src/lib/i18n/bn.ts
#	app/src/lib/i18n/de.ts
#	app/src/lib/i18n/en.ts
#	app/src/lib/i18n/es.ts
#	app/src/lib/i18n/fr.ts
#	app/src/lib/i18n/hi.ts
#	app/src/lib/i18n/id.ts
#	app/src/lib/i18n/it.ts
#	app/src/lib/i18n/ko.ts
#	app/src/lib/i18n/pl.ts
#	app/src/lib/i18n/pt.ts
#	app/src/lib/i18n/ru.ts
#	app/src/lib/i18n/zh-CN.ts
#	app/src/pages/Intelligence.tsx
senamakel added 4 commits May 30, 2026 08:04
The prior upstream merge commit left unresolved conflict markers in
Intelligence.tsx (the Edit lost a race with the merge). Resolve by keeping
BOTH the upstream Knowledge Freshness tab and this PR's Connection Path tab:
union the IntelligenceTab type, the tab list, and the render switch so both
'freshness' and 'path' tabs render. Typecheck clean, i18n gates pass, all
28 connection-path tests pass.
The mount effect calls load(''), which synchronously sets loading/error.
Since loading initializes to true and error to null, that is a no-op on the
first render with no cascading re-render. Annotate the intentional
fetch-on-mount with a scoped eslint-disable + rationale rather than
restructuring the shared loader.
# Conflicts:
#	app/src/pages/Intelligence.tsx
@senamakel senamakel merged commit 2baf6b8 into tinyhumansai:main May 30, 2026
15 of 19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Net-new user-facing capability or product behavior. memory Memory store, memory tree, recall, summarization, and embeddings in src/openhuman/memory/. working A PR that is being worked on by the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants