feat(intelligence): add Evidence-Weighted Trust Lens#3000
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (23)
💤 Files with no reviewable changes (19)
🚧 Files skipped from review as they are similar to previous changes (4)
📝 WalkthroughWalkthroughThis PR introduces an Evidence-Weighted Trust lens for the Intelligence page. It adds a deterministic trust computation engine that collapses multigraph duplicates, sanitizes evidence, aggregates entity trust quotients and predicate reliability, calculates global Gini coefficients, detects under-corroborated relations, and flags degraded states. A container component manages loading and namespace selection, delegating UI to a presentational panel that renders metrics, entity rankings, predicate lists, and worklists. ChangesEvidence-Weighted Trust Lens
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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/components/intelligence/EvidenceTrustTab.tsx`:
- Around line 44-46: In the EvidenceTrustTab component, replace the then/catch
promise chain calling loadNamespaces().then(setNamespaces).catch(() =>
setNamespaces([])) inside the useEffect with an async function: define an async
inner function (e.g., async function fetchNamespaces()), await loadNamespaces()
inside a try block and call setNamespaces(result), and in the catch block call
setNamespaces([]); then invoke fetchNamespaces() from the useEffect so behavior
and fallback remain identical while using async/await.
In `@app/src/services/api/evidenceTrustApi.ts`:
- Around line 14-19: The debug log in loadEvidenceTrust currently prints the raw
namespace and a free-form message; change the log call (variable log) to use a
stable grep-friendly prefix `[rpc]` and structured correlation fields instead of
the raw namespace value — e.g., include method="loadEvidenceTrust" and
namespaceProvided=(namespace != null) along with relations.length; update the
log invocation after the memoryGraphQuery call to emit those fields and remove
the verbatim namespace to avoid leaking PII.
🪄 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: 0f675e2b-8723-401b-a518-518386203d74
📒 Files selected for processing (23)
app/src/components/intelligence/EvidenceTrustPanel.test.tsxapp/src/components/intelligence/EvidenceTrustPanel.tsxapp/src/components/intelligence/EvidenceTrustTab.test.tsxapp/src/components/intelligence/EvidenceTrustTab.tsxapp/src/lib/i18n/chunks/ar-1.tsapp/src/lib/i18n/chunks/bn-1.tsapp/src/lib/i18n/chunks/de-1.tsapp/src/lib/i18n/chunks/en-1.tsapp/src/lib/i18n/chunks/es-1.tsapp/src/lib/i18n/chunks/fr-1.tsapp/src/lib/i18n/chunks/hi-1.tsapp/src/lib/i18n/chunks/id-1.tsapp/src/lib/i18n/chunks/it-1.tsapp/src/lib/i18n/chunks/ko-1.tsapp/src/lib/i18n/chunks/pt-1.tsapp/src/lib/i18n/chunks/ru-1.tsapp/src/lib/i18n/chunks/zh-CN-1.tsapp/src/lib/i18n/en.tsapp/src/lib/memory/evidenceTrust.test.tsapp/src/lib/memory/evidenceTrust.tsapp/src/pages/Intelligence.tsxapp/src/services/api/evidenceTrustApi.test.tsapp/src/services/api/evidenceTrustApi.ts
| loadNamespaces() | ||
| .then(setNamespaces) | ||
| .catch(() => setNamespaces([])); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify no promise-chain usage remains in this component.
rg -n '\.then\s*\(|\.catch\s*\(' app/src/components/intelligence/EvidenceTrustTab.tsxRepository: tinyhumansai/openhuman
Length of output: 138
Replace promise chaining with async/await in EvidenceTrustTab effect.
app/src/components/intelligence/EvidenceTrustTab.tsx still uses loadNamespaces().then(...).catch(...); convert the useEffect to async/await while preserving the setNamespaces([]) fallback.
♻️ Proposed fix
- useEffect(() => {
- // Namespaces are optional UI sugar; a failure to list them must not block
- // the trust view, so swallow that error specifically.
- loadNamespaces()
- .then(setNamespaces)
- .catch(() => setNamespaces([]));
- void load('');
- }, [load]);
+ useEffect(() => {
+ // Namespaces are optional UI sugar; a failure to list them must not block
+ // the trust view, so swallow that error specifically.
+ const run = async (): Promise<void> => {
+ try {
+ const nextNamespaces = await loadNamespaces();
+ setNamespaces(nextNamespaces);
+ } catch {
+ setNamespaces([]);
+ }
+ await load('');
+ };
+ void run();
+ }, [load]);🤖 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/EvidenceTrustTab.tsx` around lines 44 - 46,
In the EvidenceTrustTab component, replace the then/catch promise chain calling
loadNamespaces().then(setNamespaces).catch(() => setNamespaces([])) inside the
useEffect with an async function: define an async inner function (e.g., async
function fetchNamespaces()), await loadNamespaces() inside a try block and call
setNamespaces(result), and in the catch block call setNamespaces([]); then
invoke fetchNamespaces() from the useEffect so behavior and fallback remain
identical while using async/await.
| const log = debug('evidence-trust:api'); | ||
|
|
||
| /** Fetch graph relations for a namespace (or all) and compute trust metrics. */ | ||
| export async function loadEvidenceTrust(namespace?: string): Promise<EvidenceTrustResult> { | ||
| const relations = await memoryGraphQuery(namespace); | ||
| log('loadEvidenceTrust namespace=%s relations=%d', namespace ?? '(all)', relations.length); |
There was a problem hiding this comment.
Avoid logging raw namespaces and add a grep-friendly RPC prefix.
Line 19 logs the namespace verbatim and uses a free-form message. Please switch this to a stable [rpc] prefix with correlation fields and avoid emitting the raw namespace value; a boolean like namespaceProvided plus the RPC method name is enough here.
♻️ Proposed fix
const log = debug('evidence-trust:api');
/** Fetch graph relations for a namespace (or all) and compute trust metrics. */
export async function loadEvidenceTrust(namespace?: string): Promise<EvidenceTrustResult> {
const relations = await memoryGraphQuery(namespace);
- log('loadEvidenceTrust namespace=%s relations=%d', namespace ?? '(all)', relations.length);
+ log(
+ '[rpc] method=memoryGraphQuery namespaceProvided=%s relationCount=%d',
+ namespace !== undefined,
+ relations.length
+ );
return computeEvidenceTrust(relations);
}As per coding guidelines, **/*.{rs,ts,tsx} must "Use stable grep-friendly log prefixes ([domain], [rpc], [ui-flow]) and correlation fields (request IDs, method names, entity IDs)" and "Never log secrets or full PII in debug logs; redact sensitive information".
📝 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.
| const log = debug('evidence-trust:api'); | |
| /** Fetch graph relations for a namespace (or all) and compute trust metrics. */ | |
| export async function loadEvidenceTrust(namespace?: string): Promise<EvidenceTrustResult> { | |
| const relations = await memoryGraphQuery(namespace); | |
| log('loadEvidenceTrust namespace=%s relations=%d', namespace ?? '(all)', relations.length); | |
| const log = debug('evidence-trust:api'); | |
| /** Fetch graph relations for a namespace (or all) and compute trust metrics. */ | |
| export async function loadEvidenceTrust(namespace?: string): Promise<EvidenceTrustResult> { | |
| const relations = await memoryGraphQuery(namespace); | |
| log( | |
| '[rpc] method=memoryGraphQuery namespaceProvided=%s relationCount=%d', | |
| namespace !== undefined, | |
| relations.length | |
| ); | |
| return computeEvidenceTrust(relations); | |
| } |
🤖 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/services/api/evidenceTrustApi.ts` around lines 14 - 19, The debug log
in loadEvidenceTrust currently prints the raw namespace and a free-form message;
change the log call (variable log) to use a stable grep-friendly prefix `[rpc]`
and structured correlation fields instead of the raw namespace value — e.g.,
include method="loadEvidenceTrust" and namespaceProvided=(namespace != null)
along with relations.length; update the log invocation after the
memoryGraphQuery call to emit those fields and remove the verbatim namespace to
avoid leaking PII.
e4cb84c to
38ccc5d
Compare
A new read-only "Trust" tab. The 20 shipped lenses all read the
(subject, predicate, object) triple structure or its document provenance.
None of them makes `evidenceCount` — the field that measures how many
times an assertion has been independently corroborated — the PRIMARY
signal. This lens does.
Three orthogonal-to-everything-shipped outputs:
- TRUST QUOTIENT per entity = mean evidence per relation; separates a
prolific entity with many single-witness facts from a quiet entity
whose few relations are heavily corroborated.
- EVIDENCE GINI = inequality of evidence concentration across entities
(closed-form on sorted entity weights — one integer-weighted sum, one
division; no float-associativity hazard).
- UNDER-CORROBORATED WORKLIST = relations whose evidence falls below
max(1, floor(median_positive_evidence / 4)) — a curator's review queue.
Plus Predicate Reliability Index per predicate and a `degraded:true`
banner when every relation has evidenceCount = 1 (the field was never
populated meaningfully).
Picked by the loop-21 judge-panel design workflow (4 fresh angles ×
3 judges + synthesis): weighted score 8.633/10, beating Chunk
Corroboration Atlas, Inferred Edges, and Attention Quadrants.
Engine (pure, deterministic — no React/RPC/clock/RNG):
- evidenceCount coerced via Number(...) then sanitised (non-finite/NaN/
non-positive -> 0; positive floored to integer),
- multigraph collapse: same (namespace, s, p, o) tuple appearing
multiple times SUMS evidence; one distinct relation,
- entity identity = (namespace, name) — orthogonal to Entity Duplicates,
- median for the under-corroboration threshold uses INDEX-PICK (lower
middle on even sets), never the arithmetic mean — preventing float
drift,
- empty-graph and all-zero-evidence guards: globalGini = 0 when
entityCount < 2 OR sum-of-entity-weights === 0,
- all the synthesis-stage refinements applied (Number coercion, dedup
before exclusivity, degraded flag, fixed-decimal output rounding,
threshold exposed in summary).
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; aria-hidden on the decorative
→ glyph paired with an sr-only "points to".
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
38ccc5d to
0e32bec
Compare
Summary
Adds a new read-only "Trust" tab to the Intelligence view. The 20 shipped intelligence lenses all read the
(subject, predicate, object)triple structure or its document provenance. None of them makesevidenceCount— the field that measures how many times an assertion has been independently corroborated — the primary signal.This one does.
Three orthogonal-to-everything-shipped outputs
W(x) / D(x)— the mean evidence per relation an entity is involved in. A prolific entity with many single-witness facts has low TQ; a quiet entity whose few relations are heavily corroborated has high TQ. Degree alone (centrality, k-core) cannot express this.Σ_i (2i − n − 1)·w_i / (n·Σw)on sorted entity weights — one integer-weighted sum, one division, no float-associativity hazard.max(1, floor(median_positive_evidence / 4))— a curator's review queue.Plus Predicate Reliability Index per predicate and a
degraded: truebanner when every relation hasevidenceCount = 1(the field was never populated meaningfully), so the UI can show an empty-state instead of misleading the user with a constant TQ everywhere.Provenance & design discipline
Picked by the loop-21 judge-panel design workflow (4 fresh angles — chunks / evidence / composite / completion — × 3 independent judges + synthesis). Weighted score 8.633 / 10, beating Chunk Corroboration Atlas (8.083), Inferred Edges (8.342), Attention Quadrants (7.767). All eight synthesis-stage refinements applied during build (Number coercion, n<2 guard, multigraph SUM policy, index-pick median, degraded flag, etc).
Two engine bugs were caught during the build: the Gini denominator was using
totalEvidenceinstead of the sum of entity weights (= 2·totalEvidence) — fixed; and a JS default-parameter quirk in the test helper that ate theundefinedargument — fixed to usenull.The subsequent adversarial review caught 2 minors (9 raw → 2 confirmed) — an empty-string namespace rendering as
()in the UI (fixed: tightened guard to.length > 0) and a self-loop comment that framed the dual-credit as edge-case rather than the general undirected-degree rule (reframed).Engine design (pure, deterministic — no React/RPC/clock/RNG)
evidenceCountcoerced viaNumber(...)(handles string-encoded JSON numerics, scientific notation) then sanitised: non-finite/NaN/non-positive → 0; positive values floored to integers so all subsequent sums are exact integer arithmetic.(namespace, s, p, o)tuple appearing multiple times SUMS evidence; counts as one distinct relation.(namespace, name)— orthogonal to Entity Duplicates (Develop #8). A name appearing in two namespaces is two rows.globalGini = 0whenentityCount < 2ORΣw === 0(OR, not AND —&&would crash on an all-NaN graph).Zero new core surface
Composes the already-shipped
memoryGraphQuery/memoryListNamespacesJSON-RPC wrappers. Container/presentational split with a monotonic request-token race guard for load-on-mount; i18n across all 13 locales;aria-hiddenon the decorative→glyph paired with ansr-only"points to" for screen-reader semantics.Test plan
vitest— 34 tests (engine: 4 design-pick hand fixtures + Gini equality/max-inequality/all-zero/guards + threshold index-pick + multigraph evidence SUM + namespace orthogonality + sanitisation (NaN/Infinity/negative/null/string/scientific notation/fractional) + degraded mode + order-independent byte-identical output — plus api facade, panel metric tiles + trust ranking + predicate reliability + worklist + degraded banner + no-worklist caption, container load/namespace-requery/error)tsc --noEmit— cleaneslint— 0 errorsprettier --check— clean🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Internationalization