Skip to content

feat(intelligence): add Knowledge Freshness#2932

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

feat(intelligence): add Knowledge Freshness#2932
senamakel merged 5 commits into
tinyhumansai:mainfrom
aashir-athar:feat/memory-freshness

Conversation

@aashir-athar
Copy link
Copy Markdown
Contributor

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

Summary

Adds a new read-only "Freshness" tab on the Intelligence page that scores how current each remembered fact is, using a forgetting-curve decay model. Facts the assistant hasn't reconfirmed lately surface in a re-confirm queue instead of being treated as still-certain.

The assistant accumulates facts over time but treats a thing it learned a year ago the same as something confirmed yesterday. This lens applies the psychology of memory decay to the assistant's own stored facts — a question no mainstream assistant surfaces.

What it computes (pure deterministic engine)

  • Recall probability: recall = 2^(-ageDays / halfLifeDays) — 1.0 the moment a fact is recorded, halving every half-life as it ages un-reinforced.
  • Evidence-scaled half-life: halfLifeDays = 30 · (1 + log2(max(1, evidenceCount))) — a fact corroborated by more evidence decays more slowly, with diminishing returns.
  • Status bands (fresh ≥ 0.7, fading ≥ 0.3, stale otherwise) + a re-confirm queue of the non-fresh facts, most-stale-first.

Design

  • Pure & clock-free (lib/memory/memoryFreshness.ts): the reference time nowSeconds is injected by the caller (minted in an event handler, never during render), so the engine is fully deterministic and every branch is unit-tested. Known decay values (recall halving at each half-life, evidence scaling, status banding, future-timestamp clamp, duplicate-triple collapse) are asserted.
  • Zero new core surface: reuses the already-shipped memoryGraphQuery and memoryListNamespaces RPC wrappers. Read-only — recomputed from the live graph on mount, never persisted.
  • Container/presentational split; namespace selector; status tiles + average-recall caption + the re-confirm queue with per-fact recall bars, status badges, and age. i18n across all 13 locales.

Test plan

  • vitest — 26 tests (engine decay/banding/evidence/dedup/edge cases; api facade; presentational panel; container load/selector/error)
  • tsc --noEmit — clean
  • eslint — 0 errors
  • prettier --check — clean
  • i18n coverage gate — EXIT 0, no unused memoryFreshness.* keys

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features
    • Added "Knowledge Freshness" tab to the Intelligence page, displaying memory freshness metrics categorized as fresh, fading, or stale.
    • Filter freshness data by namespace using a dropdown selector.
    • View a re-confirmation queue listing facts requiring attention, with recall scores and age indicators.
    • Error handling with retry functionality and loading states.

Review Change Stack

@aashir-athar aashir-athar requested a review from a team May 29, 2026 10:39
@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

This PR introduces a memory freshness feature: a deterministic scoring engine that computes fact recall probabilities via exponential decay, classifies facts into fresh/fading/stale bands, and orders a reconfirmation queue. The implementation spans a scoring library, API facade, React presentational and container components, Intelligence page tab integration, and English i18n strings.

Changes

Memory Freshness Feature

Layer / File(s) Summary
Freshness scoring engine and tests
app/src/lib/memory/memoryFreshness.ts, app/src/lib/memory/memoryFreshness.test.ts
Exports FreshnessStatus, FactFreshness, and FreshnessReport types; implements pure helpers (strengthFactor, recallProbability, classify) that compute evidence-scaled half-life and exponential recall decay; computeFreshness deduplicates facts by (subject, predicate, object), filters malformed relations, normalizes evidence to minimum 1, derives per-fact recall and status, aggregates counts/average, sorts deterministically by recall, and populates a stale queue. Tests validate log-based decay, evidence scaling, deduplication, edge cases (future dates, non-positive half-life).
API facade and tests
app/src/services/api/memoryFreshnessApi.ts, app/src/services/api/memoryFreshnessApi.test.ts
Exports loadFreshness(nowSeconds, namespace?) that queries relations via memoryGraphQuery, logs namespace and count, computes freshness using computeFreshness, and returns the report; exports loadNamespaces() that delegates to memoryListNamespaces. Tests verify namespace passthrough, error propagation, empty-result behavior, and public API shape.
Presentational panel and tests
app/src/components/intelligence/MemoryFreshnessPanel.tsx, app/src/components/intelligence/MemoryFreshnessPanel.test.tsx
Renders loading skeleton with ARIA markers, error callout with optional retry button, empty state, metric tiles (fresh/fading/stale counts with status-specific styling), average recall caption, and bounded reconfirm queue listing non-fresh facts with subject/predicate/object labels, status badges, recall progress bars, age labels, and a truncation notice when stale queue exceeds row cap (50). Tests cover all states, queue ordering, and truncation logic.
State container and tests
app/src/components/intelligence/MemoryFreshnessTab.tsx, app/src/components/intelligence/MemoryFreshnessTab.test.tsx
Container component owns namespace selection and freshness loading: calls loadNamespaces() on mount, uses memoized async load(ns) that injects runtime nowSeconds() and gates via monotonic request ID to prevent stale response overwrites; wires namespace combobox to reload; delegates rendering to MemoryFreshnessPanel with report, loading, error, and onRetry. Tests verify initial load, namespace-change reloading, and error-to-alert rendering.
Intelligence page tab integration
app/src/pages/Intelligence.tsx
Imports MemoryFreshnessTab, extends IntelligenceTab union with 'freshness', adds tab to tabs array with i18n label, and conditionally renders MemoryFreshnessTab when active.
English internationalization
app/src/lib/i18n/en.ts
Adds memory.tab.freshness tab label and memoryFreshness.* namespace covering screen title/intro, loading/error/retry/empty messaging, namespace selector labels, freshness metric band labels ("Fresh", "Fading", "Stale"), recall caption, reconfirm queue heading/truncation text, all-fresh message, status labels, and age/recall placeholder strings.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • tinyhumansai/openhuman#2687: Both PRs extend the IntelligenceTab union and tabs array in app/src/pages/Intelligence.tsx to integrate a new tab feature—freshness scoring in this PR, diagram viewer in the retrieved PR.

Suggested reviewers

  • graycyrus
  • sanil-23

Poem

🐰 A freshness tracker hops to view,

Facts aging fast, or staying true,

With metrics bright and queues to tend,

Recall decay we comprehend! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.33% 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 title 'feat(intelligence): add Knowledge Freshness' clearly and concisely summarizes the main change—adding a new Knowledge Freshness feature to the Intelligence page—matching the comprehensive changeset across UI components, backend utilities, tests, and i18n.
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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/src/lib/memory/memoryFreshness.test.ts (1)

6-28: 💤 Low value

Consider sharing the GraphRelation test factory.

rel()/NOW/DAY are duplicated here and in memoryFreshnessApi.test.ts (with a diverging rel signature). A small shared factory under app/src/test/ would keep them in sync as the relation shape evolves.

As per coding guidelines: "use helpers from app/src/test/ before adding new harness code".

🤖 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/memoryFreshness.test.ts` around lines 6 - 28, Tests
duplicate the GraphRelation test factory (NOW, DAY and rel) across
memoryFreshness.test.ts and memoryFreshnessApi.test.ts; extract a shared factory
module under app/src/test/ (e.g., export NOW, DAY and a unified rel(subject,
object, agoDays, evidenceCount?, predicate?) that returns GraphRelation) and
update both tests to import those helpers, reconcile the diverging rel
signatures so both tests use the same parameter order and defaults, and export
any needed types (GraphRelation) from the shared helper so callers stay
type-safe.
🤖 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/MemoryFreshnessTab.tsx`:
- Around line 23-33: The load function can be overwritten by slower earlier
requests; add a request-tracking token (e.g., a numeric latestLoadId in a React
ref) and increment it at the start of load(ns), capture the current id in the
async closure, then after await loadFreshness(...) but before calling
setReport/setError/setLoading, verify the captured id === latestLoadId.current
and only update state when they match; reference the load function,
loadFreshness, and state setters setReport/setError/setLoading when making this
change.

---

Nitpick comments:
In `@app/src/lib/memory/memoryFreshness.test.ts`:
- Around line 6-28: Tests duplicate the GraphRelation test factory (NOW, DAY and
rel) across memoryFreshness.test.ts and memoryFreshnessApi.test.ts; extract a
shared factory module under app/src/test/ (e.g., export NOW, DAY and a unified
rel(subject, object, agoDays, evidenceCount?, predicate?) that returns
GraphRelation) and update both tests to import those helpers, reconcile the
diverging rel signatures so both tests use the same parameter order and
defaults, and export any needed types (GraphRelation) from the shared helper so
callers stay type-safe.
🪄 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: e918d157-0eed-4e3d-a449-ea79862a1e52

📥 Commits

Reviewing files that changed from the base of the PR and between 04286cb and d96fcb1.

📒 Files selected for processing (23)
  • app/src/components/intelligence/MemoryFreshnessPanel.test.tsx
  • app/src/components/intelligence/MemoryFreshnessPanel.tsx
  • app/src/components/intelligence/MemoryFreshnessTab.test.tsx
  • app/src/components/intelligence/MemoryFreshnessTab.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/memoryFreshness.test.ts
  • app/src/lib/memory/memoryFreshness.ts
  • app/src/pages/Intelligence.tsx
  • app/src/services/api/memoryFreshnessApi.test.ts
  • app/src/services/api/memoryFreshnessApi.ts

Comment thread app/src/components/intelligence/MemoryFreshnessTab.tsx
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 clean to me — well-structured feature. CI is still pending so I'll hold off on the approval until those pass, but I spotted a couple of things while reviewing:

Design looks solid. Pure engine with injected clock, container/presentational split, 26 tests covering decay math / edge cases / error paths, debug logging via the debug package — all matches project patterns. Zero new core surface, fully read-only.

i18n completeness — the PR description says "i18n across all 13 locales" but all 12 non-English locale files (ar, bn, de, es, fr, hi, id, it, ko, pt, ru, zh-CN) have English strings verbatim. Not a blocker if translations are deferred by convention here, but worth confirming — users in Arabic, Bengali, Hindi etc. will see English UI copy for this entire tab.

Re-confirm queue truncationMAX_QUEUE_ROWS = 50 silently caps the queue. There's no "showing 50 of N" label or pagination, so a user with 51+ stale facts has no way of knowing the list is incomplete. A simple truncation note would fix it.

Once CI is green I'll come back and approve this.

Comment thread app/src/components/intelligence/MemoryFreshnessPanel.tsx
aashir-athar added a commit to aashir-athar/openhuman that referenced this pull request May 29, 2026
- MemoryFreshnessTab guards against out-of-order load responses with a
  monotonic request token (CodeRabbit), so a slow earlier query can't overwrite
  a newer namespace result.
- The re-confirm queue now shows a "Showing N of M — address these first." note
  when it is truncated past the 50-row cap (graycyrus), so a user with more
  stale facts isn't misled into thinking the list is complete.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@aashir-athar
Copy link
Copy Markdown
Contributor Author

Thanks @coderabbitai and @graycyrus — both addressed in 59d2016:

  1. Out-of-order responses (CodeRabbit, MemoryFreshnessTab)load now stamps a monotonic latestRequestId ref and ignores its result/error/loading writes if a newer load has started, so a slow earlier query (e.g. the mount-time load('')) can't overwrite a later namespace result.

  2. Silent queue truncation (graycyrus, MemoryFreshnessPanel) — when the re-confirm queue exceeds the 50-row cap it now renders "Showing 50 of {total} — address these first.", so a user with 60 stale facts isn't misled into thinking the list is complete. Added an i18n key (mirrored across all 13 locales) and a test asserting the note appears for a 60-fact report.

Appreciate the kind words on the structure, @graycyrus. Local suite green: 27 freshness tests, tsc --noEmit, ESLint (0 errors), Prettier, i18n coverage EXIT 0.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

Tip

For best results, initiate chat on the files or code changes.

@aashir-athar Both fixes look well-considered:

  • The monotonic latestRequestId guard is exactly the right pattern for this kind of async fire-and-forget loader — it cleanly prevents stale-closure overwrites without needing to cancel the in-flight request.
  • The truncation disclosure ("Showing 50 of {total} — address these first") keeps the UX honest and the added test + i18n parity across all 13 locales is a clean touch.

Thanks for the thorough follow-through. 🎉

(∧ ﻌ ∧)♡


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

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 the fix commit addressed both open items from the first pass:

  • Race condition (CodeRabbit): the request-id token is in place, stale responses are properly dropped.
  • Truncation note (mine): the "Showing {shown} of {total}" note is rendered and has test coverage for the 50-of-60 case.

Code is clean. One E2E check (Windows/Appium Chromium) is still pending — once that goes green, this is good to merge.

aashir-athar added a commit to aashir-athar/openhuman that referenced this pull request May 29, 2026
- MemoryFreshnessTab guards against out-of-order load responses with a
  monotonic request token (CodeRabbit), so a slow earlier query can't overwrite
  a newer namespace result.
- The re-confirm queue now shows a "Showing N of M — address these first." note
  when it is truncated past the 50-row cap (graycyrus), so a user with more
  stale facts isn't misled into thinking the list is complete.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@aashir-athar aashir-athar force-pushed the feat/memory-freshness branch from 59d2016 to fac7825 Compare May 29, 2026 14:41
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 29, 2026
@aashir-athar
Copy link
Copy Markdown
Contributor Author

CI note: the Rust Core Coverage lane went red after the rebase, but this PR is TypeScript-only (Rust core unchanged from main) and the job failed at the infra level (no test output). Re-triggered via an empty commit; a maintainer re-run would also clear it.

aashir-athar added a commit to aashir-athar/openhuman that referenced this pull request May 30, 2026
- MemoryFreshnessTab guards against out-of-order load responses with a
  monotonic request token (CodeRabbit), so a slow earlier query can't overwrite
  a newer namespace result.
- The re-confirm queue now shows a "Showing N of M — address these first." note
  when it is truncated past the 50-row cap (graycyrus), so a user with more
  stale facts isn't misled into thinking the list is complete.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@aashir-athar aashir-athar force-pushed the feat/memory-freshness branch from 78dca83 to 62ed0f4 Compare May 30, 2026 01:47
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 30, 2026
aashir-athar and others added 3 commits May 30, 2026 17:50
A new read-only "Freshness" tab on the Intelligence page that scores how current
each remembered fact is, using a forgetting-curve decay model — so facts the
assistant hasn't reconfirmed lately surface for re-checking instead of being
treated as still-certain.

- Pure deterministic engine (lib/memory/memoryFreshness.ts):
  recall = 2^(-ageDays / halfLifeDays), where halfLifeDays scales with
  evidenceCount via 1 + log2(evidence) (more corroboration decays slower, with
  diminishing returns). The engine never reads the clock — `nowSeconds` is
  injected — so it is fully deterministic and unit-tested. Facts are banded
  fresh / fading / stale and the non-fresh ones form a "re-confirm queue".
- Zero new core surface: reuses the already-shipped memoryGraphQuery and
  memoryListNamespaces RPC wrappers. Read-only — recomputed from the live graph.
- Container/presentational split; namespace selector; status tiles + average
  recall + the re-confirm queue with per-fact recall bars and age. i18n across
  all 13 locales.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- MemoryFreshnessTab guards against out-of-order load responses with a
  monotonic request token (CodeRabbit), so a slow earlier query can't overwrite
  a newer namespace result.
- The re-confirm queue now shows a "Showing N of M — address these first." note
  when it is truncated past the 50-row cap (graycyrus), so a user with more
  stale facts isn't misled into thinking the list is complete.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@aashir-athar aashir-athar force-pushed the feat/memory-freshness branch from f5117dc to 59a88a3 Compare May 30, 2026 12:51
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 30, 2026
en.ts carried the 21 memory.tab.freshness / memoryFreshness.* keys but the
other 13 locale files did not, breaking the i18n:check key-parity gate after
the rebase. Add the keys (English placeholders per the repo convention —
translators fill in later) so every locale is at parity.
@coderabbitai on memoryFreshness.test.ts)

CodeRabbit flagged rel()/NOW/DAY duplicated across memoryFreshness.test.ts and
memoryFreshnessApi.test.ts with a diverging rel signature. Move them to
app/src/test/memoryRelationFactory.ts with a unified signature so both suites
stay in sync as GraphRelation evolves, per the 'use helpers from app/src/test/'
guideline.
@senamakel senamakel merged commit 26f418a 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