Skip to content

feat(intelligence): add Graph Cohesion#2978

Merged
senamakel merged 5 commits into
tinyhumansai:mainfrom
aashir-athar:feat/graph-cohesion
May 30, 2026
Merged

feat(intelligence): add Graph Cohesion#2978
senamakel merged 5 commits into
tinyhumansai:mainfrom
aashir-athar:feat/graph-cohesion

Conversation

@aashir-athar

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

Copy link
Copy Markdown
Contributor

Summary

Adds a new read-only "Cohesion" tab to the Intelligence view: clustering-coefficient analysis of the memory knowledge graph. Where the Centrality lens answers "which entities are important", this lens answers "how tightly knit is the neighbourhood around each entity" — and surfaces a structural signal neither centrality nor a frequency sort can reveal: brokers.

A broker (structural hole) is an entity whose neighbours are not connected to each other — it's the sole link holding otherwise-separate clusters together (local clustering ≈ 0). These are the single points of failure / brokerage opportunities in the user's accumulated memory.

Design

  • Pure deterministic engine (lib/memory/graphCohesion.ts): treats the (subject)-[predicate]->(object) triples as an undirected simple graph (direction dropped, parallel edges collapsed, self-loops dropped) and computes:
    • per-node local clustering coefficient C(v) = 2·triangles / (deg·(deg-1)),
    • triangleCount, averageClustering (mean of C over degree≥2 nodes), and transitivity (the global clustering coefficient, 3·triangles / connected-triples),
    • findBrokers() — ranks the loosest-neighbourhood entities.
    • No React, no RPC, no clock, no RNG. The result depends only on graph structure, never on insertion order — and averageClustering is summed in canonical (sorted) order so it is byte-identical across input permutations despite IEEE-754 non-associativity.
  • Zero new core surface: composes the already-shipped memoryGraphQuery / memoryListNamespaces JSON-RPC wrappers. Read-only — recomputed live from the graph, never persisted.
  • Container/presentational split; the container guards load-on-mount with a monotonic request token (out-of-order responses can't overwrite the latest). i18n across all 13 locales.

Test plan

  • vitest — 33 tests (engine: empty/triangle/path/4-cycle/star/diamond fixtures with hand-computed clustering, triangle, avg & transitivity values; self-loop drop; parallel-edge & direction collapse; malformed-row drop; no case-folding; byte-identical determinism across permutations; broker ranking & limits — plus api facade, panel states + broker badge + no-brokers note, container load/namespace-requery/error)
  • tsc --noEmit — clean
  • eslint — 0 errors
  • prettier --check — clean
  • i18n coverage gate — EXIT 0, no missing/extra/drifted keys across 13 locales

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a "Cohesion" tab to Intelligence showing graph cohesion analytics.
    • Shows metrics: entities, connections, triangles, average clustering, and transitivity.
    • Ranks "Brokers" (structural-hole entities) with degree and clustering details.
    • Namespace selector to filter cohesion analysis with retry on failure.
  • UX

    • Loading skeletons, clear empty-state messaging, and error alerts with Retry.
  • Localization

    • New translation keys for the Cohesion UI across locales.

Review Change Stack

@aashir-athar aashir-athar requested a review from a team May 29, 2026 19:30
@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

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 deterministic graph-cohesion engine, a read-only API wrapper, a stateful Cohesion tab and presentational panel with loading/error/empty/success states, tests for computation and UI flows, page wiring to expose the Cohesion tab, and i18n keys across locales.

Changes

Graph Cohesion Feature

Layer / File(s) Summary
Core graph cohesion computation
app/src/lib/memory/graphCohesion.ts, app/src/lib/memory/graphCohesion.test.ts
Undirected adjacency from relations (self-loops dropped, parallel edges collapsed); per-node degrees, triangles, local clustering; graph-level edge/triangle counts, transitivity, deterministic averageClustering accumulation; broker ranking by lowest clustering then highest degree. Full tests for shapes, normalization, and determinism.
API facade and page integration
app/src/services/api/graphCohesionApi.ts, app/src/services/api/graphCohesionApi.test.ts, app/src/pages/Intelligence.tsx
RPC wrappers loadCohesion(namespace?) and loadNamespaces() fetch relations and apply the cohesion engine. Intelligence page imports GraphCohesionTab, extends IntelligenceTab union, registers the tab with i18n label, and conditionally renders it. Tests verify namespace passthrough, error propagation, and function signatures.
Container tab with state
app/src/components/intelligence/GraphCohesionTab.tsx, app/src/components/intelligence/GraphCohesionTab.test.tsx
Manages result/loading/error state, namespace list and selection; uses requestId ref to ignore stale responses; loads namespaces and initial cohesion on mount; reloads on namespace change. Tests cover mount, namespace selection, and error flows.
Presentational panel
app/src/components/intelligence/GraphCohesionPanel.tsx, app/src/components/intelligence/GraphCohesionPanel.test.tsx
Loading skeleton, error alert with optional retry, empty state, metric tiles (entity/connection/triangle counts), summary caption (average clustering, transitivity), and ranked brokers table with zero-clustering badge and "no brokers" fallback. Tests verify UI states and broker scenarios (diamond, star, insufficient links).
Internationalization
app/src/lib/i18n/*
Adds memory.tab.cohesion label and graphCohesion.* keys across locales to support the new Cohesion UI copy.

Sequence Diagram

sequenceDiagram
  participant User
  participant IntelligencePage
  participant GraphCohesionTab
  participant graphCohesionApi
  participant memoryGraphQuery
  participant computeGraphCohesion

  User->>IntelligencePage: open Cohesion tab
  IntelligencePage->>GraphCohesionTab: mount
  GraphCohesionTab->>graphCohesionApi: loadCohesion(namespace)
  graphCohesionApi->>memoryGraphQuery: memoryGraphQuery(namespace)
  memoryGraphQuery-->>graphCohesionApi: GraphRelation[]
  graphCohesionApi->>computeGraphCohesion: computeGraphCohesion(relations)
  computeGraphCohesion-->>graphCohesionApi: CohesionResult
  graphCohesionApi-->>GraphCohesionTab: CohesionResult
  GraphCohesionTab-->>User: render metrics & brokers
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • graycyrus
  • sanil-23

🐰 A rabbit hops through the graph with glee,
Counting triangles and brokers merrily,
Deterministic sums and tidy ranks,
Cohesion blooms in modular flanks—
Hop, compute, display—happy harmony!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.15% 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 Graph Cohesion' clearly and accurately summarizes the main change: adding a new Graph Cohesion feature to the Intelligence view with comprehensive clustering analysis and broker identification.
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
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 29, 2026
@aashir-athar aashir-athar force-pushed the feat/graph-cohesion branch from 3182427 to 42888ae Compare May 29, 2026 21:15
aashir-athar added a commit to aashir-athar/openhuman that referenced this pull request May 29, 2026
Rust Core Coverage failed in its post-step with "No space left on device"
during cache save — a runner-disk infra flake unrelated to this TS-only PR
(Rust is byte-identical to main; the same flake hit tinyhumansai#2978 previously).

Co-Authored-By: Claude Opus 4.7 <[email protected]>
@aashir-athar aashir-athar force-pushed the feat/graph-cohesion branch from 42888ae to 71bc99a Compare May 30, 2026 01:27

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@app/src/lib/memory/graphCohesion.test.ts`:
- Around line 206-218: The inline comment in the test "sorts nodes by clustering
DESC, then degree DESC, then id ASC" is incorrect: update the note that
currently says "B,C also 1 here" to reflect that B and C have localClustering
2/3 (degree 3) and only A and D have clustering 1; edit the comment near the
computeGraphCohesion call or the rel(...) list so it accurately documents the
diamond shape and that the ones array (derived from r.nodes.filter(...).map(n =>
n.id)) should be ['A','D'].
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 074eeae0-b6d9-49cc-8b4e-49110efce73e

📥 Commits

Reviewing files that changed from the base of the PR and between 42888ae and 71bc99a.

📒 Files selected for processing (23)
  • app/src/components/intelligence/GraphCohesionPanel.test.tsx
  • app/src/components/intelligence/GraphCohesionPanel.tsx
  • app/src/components/intelligence/GraphCohesionTab.test.tsx
  • app/src/components/intelligence/GraphCohesionTab.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/graphCohesion.test.ts
  • app/src/lib/memory/graphCohesion.ts
  • app/src/pages/Intelligence.tsx
  • app/src/services/api/graphCohesionApi.test.ts
  • app/src/services/api/graphCohesionApi.ts
✅ Files skipped from review due to trivial changes (4)
  • app/src/lib/i18n/chunks/it-1.ts
  • app/src/lib/i18n/en.ts
  • app/src/lib/i18n/chunks/fr-1.ts
  • app/src/lib/i18n/chunks/es-1.ts
🚧 Files skipped from review as they are similar to previous changes (14)
  • app/src/components/intelligence/GraphCohesionTab.test.tsx
  • app/src/pages/Intelligence.tsx
  • app/src/lib/i18n/chunks/bn-1.ts
  • app/src/services/api/graphCohesionApi.ts
  • app/src/lib/i18n/chunks/id-1.ts
  • app/src/lib/i18n/chunks/ar-1.ts
  • app/src/services/api/graphCohesionApi.test.ts
  • app/src/lib/i18n/chunks/en-1.ts
  • app/src/lib/i18n/chunks/ru-1.ts
  • app/src/components/intelligence/GraphCohesionTab.tsx
  • app/src/lib/i18n/chunks/zh-CN-1.ts
  • app/src/components/intelligence/GraphCohesionPanel.test.tsx
  • app/src/lib/memory/graphCohesion.ts
  • app/src/components/intelligence/GraphCohesionPanel.tsx

Comment on lines +206 to +218
it('sorts nodes by clustering DESC, then degree DESC, then id ASC', () => {
const r = computeGraphCohesion([
rel('A', 'B'),
rel('A', 'C'),
rel('B', 'C'), // triangle A-B-C (clustering 1 for A; B,C also 1 here)
rel('B', 'D'),
rel('C', 'D'), // diamond
]);
// top entries are the clustering-1 nodes; A before D by id at equal degree.
expect(r.nodes[0].localClustering).toBe(1);
const ones = r.nodes.filter(n => n.localClustering === 1).map(n => n.id);
expect(ones).toEqual(['A', 'D']);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Inline comment contradicts the assertion below it.

The comment claims B,C also 1 here, but in this diamond B and C have degree 3 and cluster at 2/3 — only A and D are at 1, which is exactly what the ones assertion verifies. The misleading note could prompt a future reader to "fix" a correct test.

✏️ Suggested comment correction
-      rel('B', 'C'), // triangle A-B-C (clustering 1 for A; B,C also 1 here)
+      rel('B', 'C'), // triangle A-B-C; A & D cluster at 1, B & C at 2/3 (diamond spine)
       rel('B', 'D'),
       rel('C', 'D'), // diamond
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('sorts nodes by clustering DESC, then degree DESC, then id ASC', () => {
const r = computeGraphCohesion([
rel('A', 'B'),
rel('A', 'C'),
rel('B', 'C'), // triangle A-B-C (clustering 1 for A; B,C also 1 here)
rel('B', 'D'),
rel('C', 'D'), // diamond
]);
// top entries are the clustering-1 nodes; A before D by id at equal degree.
expect(r.nodes[0].localClustering).toBe(1);
const ones = r.nodes.filter(n => n.localClustering === 1).map(n => n.id);
expect(ones).toEqual(['A', 'D']);
});
it('sorts nodes by clustering DESC, then degree DESC, then id ASC', () => {
const r = computeGraphCohesion([
rel('A', 'B'),
rel('A', 'C'),
rel('B', 'C'), // triangle A-B-C; A & D cluster at 1, B & C at 2/3 (diamond spine)
rel('B', 'D'),
rel('C', 'D'), // diamond
]);
// top entries are the clustering-1 nodes; A before D by id at equal degree.
expect(r.nodes[0].localClustering).toBe(1);
const ones = r.nodes.filter(n => n.localClustering === 1).map(n => n.id);
expect(ones).toEqual(['A', 'D']);
});
🤖 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/lib/memory/graphCohesion.test.ts` around lines 206 - 218, The inline
comment in the test "sorts nodes by clustering DESC, then degree DESC, then id
ASC" is incorrect: update the note that currently says "B,C also 1 here" to
reflect that B and C have localClustering 2/3 (degree 3) and only A and D have
clustering 1; edit the comment near the computeGraphCohesion call or the
rel(...) list so it accurately documents the diamond shape and that the ones
array (derived from r.nodes.filter(...).map(n => n.id)) should be ['A','D'].

aashir-athar added a commit to aashir-athar/openhuman that referenced this pull request May 30, 2026
Rust Core Coverage failed in its post-step with "No space left on device"
during cache save — a runner-disk infra flake unrelated to this TS-only PR
(Rust is byte-identical to main; the same flake hit tinyhumansai#2978 previously).

Co-Authored-By: Claude Opus 4.7 <[email protected]>
aashir-athar added a commit to aashir-athar/openhuman that referenced this pull request May 30, 2026
…rmation)

A new read-only "Specialisation" tab. The vocabulary lens (tinyhumansai#17 Predicate
Diversity) tells you how varied the global predicate set is. The thickness
lens (tinyhumansai#18 Predicate Bundles) tells you which entity pairs share many
predicates. The frequency lens (tinyhumansai#7 Relationship Types) tells you which
predicates dominate overall. None of them answers the question this lens
does: how much does knowing the SUBJECT predict which predicate it will
use? And which entities speak in a specialised vocabulary versus a
generalist one?

Engine (pure, deterministic — no React/RPC/clock/RNG):
- Global: I(S; P) in bits with canonical-order summation; H(S) and H(P);
  normalisedMI = I / min(H(S), H(P)).
- Per-subject: specialisation = 1 - H(P|S=s)/log2(D_s) in [0,1] (1 when
  D_s <= 1 — single-predicate subject is maximally specialised by
  convention), plus dominantPredicate (tie-broken by predicate ASC) and
  dominantPredicateShare.
- All p·log2(p) and joint·log2 ratio sums walk pairs in canonical
  (sortedSubjects, sortedPredicates) order so the result is byte-identical
  regardless of relation insertion order (lesson from tinyhumansai#2978 Cohesion's
  float-order bug).

Selected from the prior loop-19 design workflow's runner-up (8.35/10 — a
genuinely new lens not covered by any of the 19 shipped features).

Adds ZERO new core surface: composes the already-shipped memoryGraphQuery
/ memoryListNamespaces wrappers. Container/presentational split with a
monotonic request-token race guard for load-on-mount; i18n across all 13
locales.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
aashir-athar added a commit to aashir-athar/openhuman that referenced this pull request May 30, 2026
Rust Core Coverage failed in its post-step with "No space left on device"
during cache save — a runner-disk infra flake unrelated to this TS-only PR
(Rust is byte-identical to main; the same flake hit tinyhumansai#2978 previously).

Co-Authored-By: Claude Opus 4.7 <[email protected]>
aashir-athar added a commit to aashir-athar/openhuman that referenced this pull request May 30, 2026
…rmation)

A new read-only "Specialisation" tab. The vocabulary lens (tinyhumansai#17 Predicate
Diversity) tells you how varied the global predicate set is. The thickness
lens (tinyhumansai#18 Predicate Bundles) tells you which entity pairs share many
predicates. The frequency lens (tinyhumansai#7 Relationship Types) tells you which
predicates dominate overall. None of them answers the question this lens
does: how much does knowing the SUBJECT predict which predicate it will
use? And which entities speak in a specialised vocabulary versus a
generalist one?

Engine (pure, deterministic — no React/RPC/clock/RNG):
- Global: I(S; P) in bits with canonical-order summation; H(S) and H(P);
  normalisedMI = I / min(H(S), H(P)).
- Per-subject: specialisation = 1 - H(P|S=s)/log2(D_s) in [0,1] (1 when
  D_s <= 1 — single-predicate subject is maximally specialised by
  convention), plus dominantPredicate (tie-broken by predicate ASC) and
  dominantPredicateShare.
- All p·log2(p) and joint·log2 ratio sums walk pairs in canonical
  (sortedSubjects, sortedPredicates) order so the result is byte-identical
  regardless of relation insertion order (lesson from tinyhumansai#2978 Cohesion's
  float-order bug).

Selected from the prior loop-19 design workflow's runner-up (8.35/10 — a
genuinely new lens not covered by any of the 19 shipped features).

Adds ZERO new core surface: composes the already-shipped memoryGraphQuery
/ memoryListNamespaces wrappers. Container/presentational split with a
monotonic request-token race guard for load-on-mount; i18n across all 13
locales.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
@aashir-athar aashir-athar force-pushed the feat/graph-cohesion branch from 71bc99a to 39bbd8e Compare May 30, 2026 13:10

@staimoorulhassan staimoorulhassan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

CodeRabbit-style Review — PR #2978

Walkthrough

Adds a read-only "Cohesion" tab to the Intelligence view — clustering-coefficient analysis of the memory knowledge graph. Pure frontend engine (graphCohesion.ts), no new RPC surface. 33 tests covering engine, API facade, and UI states.


Findings

🟠 MAJOR — Cross-platform floating-point non-associativity

The PR notes averageClustering is "summed in canonical (sorted) order so it is byte-identical across input permutations." This holds within a single CPU architecture, but not across architectures — x86-64 and ARM (Apple Silicon, iOS) may produce different FP results for the same sum due to differing FPU rounding modes and instruction selection. If the metric is displayed in the UI (it is — the caption shows averageClustering), users on different devices will see silently different values.

Suggested fix: Round to a fixed number of decimal places before display or before storing the value in the return object:

averageClustering: Math.round(sum / count * 1e6) / 1e6,

Alternatively, store the intermediate integer numerator/denominator as a rational and divide only at display time.


🟡 MINOR — findBrokers() has no default result cap

findBrokers(result: CohesionResult, limit?: number): BrokerEntry[]

If limit is omitted, the full ranked entity list is returned. For a memory graph with thousands of nodes this materializes a large array in the browser main thread before the UI slices it. Add a sensible default cap:

findBrokers(result: CohesionResult, limit = 100): BrokerEntry[]

🟡 MINOR — Verify monotonic request token uses strict equality

The container pattern guards against stale responses with a monotonic token. Ensure the comparison uses === (not >=):

if (token !== currentToken) return; // ✓ strict
// vs.
if (token >= currentToken) return;  // ✗ allows a fast burst to overwrite a slow one

A >= comparison can incorrectly drop valid responses when two requests fire in rapid succession.


🟡 MINOR — i18n chunk vs flat-file inconsistency

New keys go into app/src/lib/i18n/en.ts (flat — correct) but the non-English translations only appear in chunks/ files. CLAUDE.md states the chunked layout was retired. Please clarify whether chunks/ is still the live layout, or move translations to the flat per-locale files. (This affects PRs #2975, #2978, #2980, and #2985 identically.)


🔵 NITPICK — GraphCohesionPanel.tsx approaching the 200-line soft cap

At 198 lines it's at the edge. The broker table and the metric tiles are good candidates for extraction into sibling components to keep future growth manageable.


Verdict

One major (cross-platform FP stability), two minors, one shared i18n blocker (see #2975 review). The pure-engine design, determinism argument, and test suite quality are exemplary. Resolve the FP rounding and i18n layout issues before merge.

aashir-athar added a commit to aashir-athar/openhuman that referenced this pull request May 30, 2026
Rust Core Coverage failed in its post-step with "No space left on device"
during cache save — a runner-disk infra flake unrelated to this TS-only PR
(Rust is byte-identical to main; the same flake hit tinyhumansai#2978 previously).

Co-Authored-By: Claude Opus 4.7 <[email protected]>
@aashir-athar aashir-athar force-pushed the feat/graph-cohesion branch from 39bbd8e to 6610563 Compare May 30, 2026 15:55

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🤖 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.

Inline comments:
In `@app/src/lib/i18n/de.ts`:
- Around line 4249-4270: The new graphCohesion.* and memory.tab.cohesion entries
currently contain English text; replace each value with a proper German
translation for the keys 'graphCohesion.brokerBadge',
'graphCohesion.brokerTitle', 'graphCohesion.colCohesion',
'graphCohesion.colEntity', 'graphCohesion.colLinks', 'graphCohesion.colRank',
'graphCohesion.empty', 'graphCohesion.emptyHint', 'graphCohesion.errorPrefix',
'graphCohesion.intro', 'graphCohesion.loading',
'graphCohesion.metricConnections', 'graphCohesion.metricEntities',
'graphCohesion.metricTriangles', 'graphCohesion.namespaceAll',
'graphCohesion.namespaceLabel', 'graphCohesion.noBrokers',
'graphCohesion.rankedHeading', 'graphCohesion.retry',
'graphCohesion.summaryCaption', 'graphCohesion.title', and 'memory.tab.cohesion'
in app/src/lib/i18n/de.ts; update each string to a correct, natural German
translation (preserve placeholders like {avg} and {transitivity} and
punctuation) so the German locale mirrors the new English keys with accurate
localized text.

In `@app/src/lib/i18n/fr.ts`:
- Around line 4232-4253: The French locale currently contains English text for
the new graphCohesion keys; update the values for all graphCohesion.* keys
(e.g., 'graphCohesion.brokerBadge', 'graphCohesion.brokerTitle',
'graphCohesion.colCohesion', 'graphCohesion.colEntity',
'graphCohesion.colLinks', 'graphCohesion.colRank', 'graphCohesion.empty',
'graphCohesion.emptyHint', 'graphCohesion.errorPrefix', 'graphCohesion.intro',
'graphCohesion.loading', 'graphCohesion.metricConnections',
'graphCohesion.metricEntities', 'graphCohesion.metricTriangles',
'graphCohesion.namespaceAll', 'graphCohesion.namespaceLabel',
'graphCohesion.noBrokers', 'graphCohesion.rankedHeading', 'graphCohesion.retry',
'graphCohesion.summaryCaption' (keep the {avg} placeholder),
'graphCohesion.title', and 'memory.tab.cohesion') with correct French
translations matching the meanings in app/src/lib/i18n/en.ts, ensuring
placeholders and punctuation are preserved.

In `@app/src/lib/i18n/hi.ts`:
- Around line 4145-4166: Several graphCohesion i18n entries were added in
English; replace each English value with proper Hindi translations for the keys
such as 'graphCohesion.brokerBadge', 'graphCohesion.brokerTitle',
'graphCohesion.colCohesion', 'graphCohesion.colEntity',
'graphCohesion.colLinks', 'graphCohesion.colRank', 'graphCohesion.empty',
'graphCohesion.emptyHint', 'graphCohesion.errorPrefix', 'graphCohesion.intro',
'graphCohesion.loading', 'graphCohesion.metricConnections',
'graphCohesion.metricEntities', 'graphCohesion.metricTriangles',
'graphCohesion.namespaceAll', 'graphCohesion.namespaceLabel',
'graphCohesion.noBrokers', 'graphCohesion.rankedHeading', 'graphCohesion.retry',
'graphCohesion.summaryCaption', 'graphCohesion.title', and 'memory.tab.cohesion'
with accurate, idiomatic Hindi strings (not English), keeping placeholders like
{avg} and {transitivity} intact where present. Ensure translations follow
existing style in other Hindi locale entries and preserve punctuation and
special characters.

In `@app/src/lib/i18n/id.ts`:
- Around line 4155-4176: Translate all new graphCohesion.* keys and
memory.tab.cohesion in this Indonesian locale file from English into proper
Indonesian equivalents; update the values for 'graphCohesion.brokerBadge',
'graphCohesion.brokerTitle', 'graphCohesion.colCohesion',
'graphCohesion.colEntity', 'graphCohesion.colLinks', 'graphCohesion.colRank',
'graphCohesion.empty', 'graphCohesion.emptyHint', 'graphCohesion.errorPrefix',
'graphCohesion.intro', 'graphCohesion.loading',
'graphCohesion.metricConnections', 'graphCohesion.metricEntities',
'graphCohesion.metricTriangles', 'graphCohesion.namespaceAll',
'graphCohesion.namespaceLabel', 'graphCohesion.noBrokers',
'graphCohesion.rankedHeading', 'graphCohesion.retry',
'graphCohesion.summaryCaption', 'graphCohesion.title', and 'memory.tab.cohesion'
with accurate Indonesian translations (not English) following the style of other
locale entries so the Cohesion tab is localized for id users.

In `@app/src/lib/i18n/pl.ts`:
- Around line 4211-4232: The Polish locale contains English strings for the
cohesion UI keys; replace the English values for all graphCohesion keys (e.g.
'graphCohesion.brokerBadge', 'graphCohesion.brokerTitle',
'graphCohesion.colCohesion', 'graphCohesion.colEntity',
'graphCohesion.colLinks', 'graphCohesion.colRank', 'graphCohesion.empty',
'graphCohesion.emptyHint', 'graphCohesion.errorPrefix', 'graphCohesion.intro',
'graphCohesion.loading', 'graphCohesion.metricConnections',
'graphCohesion.metricEntities', 'graphCohesion.metricTriangles',
'graphCohesion.namespaceAll', 'graphCohesion.namespaceLabel',
'graphCohesion.noBrokers', 'graphCohesion.rankedHeading', 'graphCohesion.retry',
'graphCohesion.summaryCaption', 'graphCohesion.title', and
'memory.tab.cohesion') with correct Polish translations (not English),
preserving placeholders like {avg} and {transitivity} and matching
formatting/quoting of the existing locale file.

In `@app/src/lib/i18n/ru.ts`:
- Around line 4179-4200: The new graphCohesion i18n entries in ru.ts are still
in English; replace the English strings for keys like
'graphCohesion.brokerBadge', 'graphCohesion.brokerTitle',
'graphCohesion.colCohesion', 'graphCohesion.colEntity',
'graphCohesion.colLinks', 'graphCohesion.colRank', 'graphCohesion.empty',
'graphCohesion.emptyHint', 'graphCohesion.errorPrefix', 'graphCohesion.intro',
'graphCohesion.loading', 'graphCohesion.metricConnections',
'graphCohesion.metricEntities', 'graphCohesion.metricTriangles',
'graphCohesion.namespaceAll', 'graphCohesion.namespaceLabel',
'graphCohesion.noBrokers', 'graphCohesion.rankedHeading', 'graphCohesion.retry',
'graphCohesion.summaryCaption', 'graphCohesion.title', and 'memory.tab.cohesion'
with proper Russian translations (preserve placeholders like {avg} and
{transitivity} and any punctuation), following the project's i18n guideline to
keep non-English locales in sync with en.ts. Ensure translations are natural
Russian and update only the values in ru.ts.

In `@app/src/lib/i18n/zh-CN.ts`:
- Around line 3941-3962: The zh-CN i18n block for graph cohesion currently
contains English placeholders; replace all 22 keys (e.g.,
graphCohesion.brokerBadge, graphCohesion.brokerTitle, graphCohesion.colCohesion,
graphCohesion.colEntity, graphCohesion.colLinks, graphCohesion.colRank,
graphCohesion.empty, graphCohesion.emptyHint, graphCohesion.errorPrefix,
graphCohesion.intro, graphCohesion.loading, graphCohesion.metricConnections,
graphCohesion.metricEntities, graphCohesion.metricTriangles,
graphCohesion.namespaceAll, graphCohesion.namespaceLabel,
graphCohesion.noBrokers, graphCohesion.rankedHeading, graphCohesion.retry,
graphCohesion.summaryCaption, graphCohesion.title, memory.tab.cohesion) with
accurate Simplified Chinese translations (e.g., brokerBadge => "中介" or "桥接点",
title => "图谱凝聚度") preserving interpolation tokens like {avg} and {transitivity};
ensure phrases keep meaning and tone consistent with existing zh-CN style and
update any other non-English locale files if these keys were newly added in
en.ts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2ca39024-bcf0-4669-87cb-29cc72a36105

📥 Commits

Reviewing files that changed from the base of the PR and between 39bbd8e and 6610563.

📒 Files selected for processing (14)
  • app/src/lib/i18n/ar.ts
  • app/src/lib/i18n/bn.ts
  • app/src/lib/i18n/de.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
✅ Files skipped from review due to trivial changes (6)
  • app/src/lib/i18n/pt.ts
  • app/src/lib/i18n/ko.ts
  • app/src/lib/i18n/es.ts
  • app/src/lib/i18n/bn.ts
  • app/src/lib/i18n/it.ts
  • app/src/lib/i18n/ar.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/pages/Intelligence.tsx

Comment thread app/src/lib/i18n/de.ts Outdated
Comment thread app/src/lib/i18n/fr.ts Outdated
Comment thread app/src/lib/i18n/hi.ts Outdated
Comment thread app/src/lib/i18n/id.ts
Comment thread app/src/lib/i18n/pl.ts
Comment thread app/src/lib/i18n/ru.ts Outdated
Comment thread app/src/lib/i18n/zh-CN.ts Outdated
aashir-athar added a commit to aashir-athar/openhuman that referenced this pull request May 30, 2026
Rust Core Coverage failed in its post-step with "No space left on device"
during cache save — a runner-disk infra flake unrelated to this TS-only PR
(Rust is byte-identical to main; the same flake hit tinyhumansai#2978 previously).

Co-Authored-By: Claude Opus 4.7 <[email protected]>
aashir-athar added a commit to aashir-athar/openhuman that referenced this pull request May 30, 2026
…rmation)

A new read-only "Specialisation" tab. The vocabulary lens (tinyhumansai#17 Predicate
Diversity) tells you how varied the global predicate set is. The thickness
lens (tinyhumansai#18 Predicate Bundles) tells you which entity pairs share many
predicates. The frequency lens (tinyhumansai#7 Relationship Types) tells you which
predicates dominate overall. None of them answers the question this lens
does: how much does knowing the SUBJECT predict which predicate it will
use? And which entities speak in a specialised vocabulary versus a
generalist one?

Engine (pure, deterministic — no React/RPC/clock/RNG):
- Global: I(S; P) in bits with canonical-order summation; H(S) and H(P);
  normalisedMI = I / min(H(S), H(P)).
- Per-subject: specialisation = 1 - H(P|S=s)/log2(D_s) in [0,1] (1 when
  D_s <= 1 — single-predicate subject is maximally specialised by
  convention), plus dominantPredicate (tie-broken by predicate ASC) and
  dominantPredicateShare.
- All p·log2(p) and joint·log2 ratio sums walk pairs in canonical
  (sortedSubjects, sortedPredicates) order so the result is byte-identical
  regardless of relation insertion order (lesson from tinyhumansai#2978 Cohesion's
  float-order bug).

Selected from the prior loop-19 design workflow's runner-up (8.35/10 — a
genuinely new lens not covered by any of the 19 shipped features).

Adds ZERO new core surface: composes the already-shipped memoryGraphQuery
/ memoryListNamespaces wrappers. Container/presentational split with a
monotonic request-token race guard for load-on-mount; i18n across all 13
locales.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
@senamakel senamakel self-assigned this May 30, 2026
@aashir-athar aashir-athar force-pushed the feat/graph-cohesion branch from 6610563 to f66d58c Compare May 30, 2026 16:31
aashir-athar added a commit to aashir-athar/openhuman that referenced this pull request May 30, 2026
Rust Core Coverage failed in its post-step with "No space left on device"
during cache save — a runner-disk infra flake unrelated to this TS-only PR
(Rust is byte-identical to main; the same flake hit tinyhumansai#2978 previously).

Co-Authored-By: Claude Opus 4.7 <[email protected]>
aashir-athar and others added 2 commits May 30, 2026 21:34
A new read-only "Cohesion" tab for the Intelligence view: clustering-
coefficient analysis of the memory knowledge graph. Treating the triples as an
undirected simple graph, it surfaces a structural signal the centrality and
frequency lenses cannot — BROKERS: entities whose neighbours are not connected
to each other, i.e. the sole link holding otherwise-separate clusters together.

Engine (pure, deterministic — no React/RPC/clock/RNG):
- per-node local clustering coefficient C(v) = 2·triangles / (deg·(deg-1)),
- triangleCount, averageClustering (mean over degree>=2 nodes), and transitivity
  (global clustering coefficient = 3·triangles / connected-triples),
- findBrokers() ranks the loosest-neighbourhood entities (structural holes).

Adds ZERO new core surface: composes the already-shipped memoryGraphQuery /
memoryListNamespaces JSON-RPC wrappers and delegates all math to the engine.
Container/presentational split with a monotonic request-token race guard for
load-on-mount; i18n across all 13 locales.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
The Rust Core Coverage job failed in its post-run cache-save step with
"No space left on device" — a runner-disk infra flake unrelated to this
TS-only change (Rust is byte-identical to main; all other Rust jobs passed).
Empty commit to re-run on a fresh runner.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
Rust Core Coverage failed on an unrelated, flaky upstream Rust unit test
(openhuman::memory_tools::tools::put::tests::execute_defaults_unknown_priority_to_normal
— a non-deterministic "namespace/key cannot contain personal identifiers"
rule). This PR is TS-only; Rust is byte-identical to main and the same job
passes on sibling PRs. Re-running on a fresh runner.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
@aashir-athar aashir-athar force-pushed the feat/graph-cohesion branch from f66d58c to 2b4efcc Compare May 30, 2026 16:34
senamakel added 2 commits May 30, 2026 09:41
The branch rebase resolved the Intelligence.tsx conflict by taking main's
tab set (associations/freshness/timeline/path) and dropping the cohesion
wiring, leaving GraphCohesionTab/Panel/engine/api as dead code that was
never imported or rendered. Restore the import, the 'cohesion' tab union
member, the pill entry, and the render branch (placed after 'centrality',
its sibling graph-analysis lens).
nodeCount: adjacency.size,
edgeCount: edgeDegreeSum / 2,
triangleCount: closedTripleSum / 3,
averageClustering: clusterableCount === 0 ? 0 : clusteringSum / clusterableCount,

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: the MAJOR "cross-platform floating-point non-associativity" finding — I looked into this and don't think a change is warranted:

  1. JS arithmetic is architecture-deterministic. ECMAScript pins Number to IEEE-754 binary64 with round-ties-to-even and no FMA/extended-precision contraction, so a + b on two doubles yields a bit-identical result on x86-64 and ARM (Apple Silicon/iOS). The x87-extended-precision / FMA-contraction divergence the comment describes is a C/C++ concern, not a JS one. The in-file determinism contract (canonical-order summation) already covers the only real source of drift — summand order.
  2. The displayed value is rounded anyway. GraphCohesionPanel.tsx renders averageClustering.toFixed(2) (2 dp), so even a hypothetical sub-ULP difference could never surface to the user.

Rounding inside the engine return object would also conflict with the byte-identical-determinism unit tests. Leaving as-is — happy to revisit if you have a concrete repro of divergence.

* disconnected. Sorted clustering ASC, then degree DESC (a bigger gap brokered
* matters more), then id ASC. Pure; derived entirely from the result.
*/
export function findBrokers(result: CohesionResult, limit = 25): CohesionNode[] {

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: the MINOR "findBrokers() has no default result cap" finding — this is already addressed in the current code: the signature is findBrokers(result: CohesionResult, limit = 25), so an omitted limit caps the result at 25 rather than materializing the full ranked list. The review likely predates that default. No change needed.

@senamakel senamakel merged commit 5972908 into tinyhumansai:main May 30, 2026
18 of 19 checks passed
aashir-athar added a commit to aashir-athar/openhuman that referenced this pull request May 31, 2026
Rust Core Coverage failed in its post-step with "No space left on device"
during cache save — a runner-disk infra flake unrelated to this TS-only PR
(Rust is byte-identical to main; the same flake hit tinyhumansai#2978 previously).

Co-Authored-By: Claude Opus 4.7 <[email protected]>
aashir-athar added a commit to aashir-athar/openhuman that referenced this pull request May 31, 2026
…rmation)

A new read-only "Specialisation" tab. The vocabulary lens (tinyhumansai#17 Predicate
Diversity) tells you how varied the global predicate set is. The thickness
lens (tinyhumansai#18 Predicate Bundles) tells you which entity pairs share many
predicates. The frequency lens (tinyhumansai#7 Relationship Types) tells you which
predicates dominate overall. None of them answers the question this lens
does: how much does knowing the SUBJECT predict which predicate it will
use? And which entities speak in a specialised vocabulary versus a
generalist one?

Engine (pure, deterministic — no React/RPC/clock/RNG):
- Global: I(S; P) in bits with canonical-order summation; H(S) and H(P);
  normalisedMI = I / min(H(S), H(P)).
- Per-subject: specialisation = 1 - H(P|S=s)/log2(D_s) in [0,1] (1 when
  D_s <= 1 — single-predicate subject is maximally specialised by
  convention), plus dominantPredicate (tie-broken by predicate ASC) and
  dominantPredicateShare.
- All p·log2(p) and joint·log2 ratio sums walk pairs in canonical
  (sortedSubjects, sortedPredicates) order so the result is byte-identical
  regardless of relation insertion order (lesson from tinyhumansai#2978 Cohesion's
  float-order bug).

Selected from the prior loop-19 design workflow's runner-up (8.35/10 — a
genuinely new lens not covered by any of the 19 shipped features).

Adds ZERO new core surface: composes the already-shipped memoryGraphQuery
/ memoryListNamespaces wrappers. Container/presentational split with a
monotonic request-token race guard for load-on-mount; i18n across all 13
locales.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
aashir-athar added a commit to aashir-athar/openhuman that referenced this pull request May 31, 2026
Rust Core Coverage failed in its post-step with "No space left on device"
during cache save — a runner-disk infra flake unrelated to this TS-only PR
(Rust is byte-identical to main; the same flake hit tinyhumansai#2978 previously).

Co-Authored-By: Claude Opus 4.7 <[email protected]>
aashir-athar added a commit to aashir-athar/openhuman that referenced this pull request May 31, 2026
…rmation)

A new read-only "Specialisation" tab. The vocabulary lens (tinyhumansai#17 Predicate
Diversity) tells you how varied the global predicate set is. The thickness
lens (tinyhumansai#18 Predicate Bundles) tells you which entity pairs share many
predicates. The frequency lens (tinyhumansai#7 Relationship Types) tells you which
predicates dominate overall. None of them answers the question this lens
does: how much does knowing the SUBJECT predict which predicate it will
use? And which entities speak in a specialised vocabulary versus a
generalist one?

Engine (pure, deterministic — no React/RPC/clock/RNG):
- Global: I(S; P) in bits with canonical-order summation; H(S) and H(P);
  normalisedMI = I / min(H(S), H(P)).
- Per-subject: specialisation = 1 - H(P|S=s)/log2(D_s) in [0,1] (1 when
  D_s <= 1 — single-predicate subject is maximally specialised by
  convention), plus dominantPredicate (tie-broken by predicate ASC) and
  dominantPredicateShare.
- All p·log2(p) and joint·log2 ratio sums walk pairs in canonical
  (sortedSubjects, sortedPredicates) order so the result is byte-identical
  regardless of relation insertion order (lesson from tinyhumansai#2978 Cohesion's
  float-order bug).

Selected from the prior loop-19 design workflow's runner-up (8.35/10 — a
genuinely new lens not covered by any of the 19 shipped features).

Adds ZERO new core surface: composes the already-shipped memoryGraphQuery
/ memoryListNamespaces wrappers. Container/presentational split with a
monotonic request-token race guard for load-on-mount; i18n across all 13
locales.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
aashir-athar added a commit to aashir-athar/openhuman that referenced this pull request May 31, 2026
…rmation)

A new read-only "Specialisation" tab. The vocabulary lens (tinyhumansai#17 Predicate
Diversity) tells you how varied the global predicate set is. The thickness
lens (tinyhumansai#18 Predicate Bundles) tells you which entity pairs share many
predicates. The frequency lens (tinyhumansai#7 Relationship Types) tells you which
predicates dominate overall. None of them answers the question this lens
does: how much does knowing the SUBJECT predict which predicate it will
use? And which entities speak in a specialised vocabulary versus a
generalist one?

Engine (pure, deterministic — no React/RPC/clock/RNG):
- Global: I(S; P) in bits with canonical-order summation; H(S) and H(P);
  normalisedMI = I / min(H(S), H(P)).
- Per-subject: specialisation = 1 - H(P|S=s)/log2(D_s) in [0,1] (1 when
  D_s <= 1 — single-predicate subject is maximally specialised by
  convention), plus dominantPredicate (tie-broken by predicate ASC) and
  dominantPredicateShare.
- All p·log2(p) and joint·log2 ratio sums walk pairs in canonical
  (sortedSubjects, sortedPredicates) order so the result is byte-identical
  regardless of relation insertion order (lesson from tinyhumansai#2978 Cohesion's
  float-order bug).

Selected from the prior loop-19 design workflow's runner-up (8.35/10 — a
genuinely new lens not covered by any of the 19 shipped features).

Adds ZERO new core surface: composes the already-shipped memoryGraphQuery
/ memoryListNamespaces wrappers. Container/presentational split with a
monotonic request-token race guard for load-on-mount; i18n across all 13
locales.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
senamakel added a commit to senamakel/openhuman that referenced this pull request Jun 6, 2026
Co-authored-by: Claude Opus 4.7 <[email protected]>
Co-authored-by: Steven Enamakel <[email protected]>
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