Skip to content

feat(observability): response-store metrics on /api/system/memory + read-only dashboard card - #427

Closed
dev-shinyu wants to merge 5 commits into
lidge-jun:devfrom
dev-shinyu:feat/memory-observability
Closed

feat(observability): response-store metrics on /api/system/memory + read-only dashboard card#427
dev-shinyu wants to merge 5 commits into
lidge-jun:devfrom
dev-shinyu:feat/memory-observability

Conversation

@dev-shinyu

@dev-shinyu dev-shinyu commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Per reviewer direction on #388 ("rebase onto the current built-in RSS watchdog", "separate observability from restart policy"), this PR is increment 1 of 2: a thin observe-only layer on top of the existing built-in RSS watchdog. It supersedes the observability half of #388. The opt-in restart policy will follow as a separate focused PR (increment 2).

Changes

  • src/responses/state.ts — side-effect-free responseStateMetrics() (count / totalBytes / largestBytes / oldestAgeMs), reusing the existing sizeBytes accounting. Disk schema unchanged.
  • src/server/management/system-routes.ts — adds a scalar-only responseState field to the existing GET /api/system/memory payload. Same management auth gate; no new endpoints, no PUT.
  • GUI — new read-only MemoryObservabilityCard: headline stats (RSS, JS heap, JSC heap, RSS drift/hour) always visible; secondary diagnostics collapsed under a <details> section. No sliders, no restart controls.
  • i18ndash.mem.* keys for en/de/ko/zh/ru/ja.
  • docs — response-store attribution note in windows-memory.md.
  • testsresponseStateMetrics coverage + /api/system/memory endpoint shape incl. responseState (scalar-only).

Explicitly out of scope (deferred to increment 2)

Restart policy, restart history, supervisor detection, PowerShell commit-axis probe, /api/memory settings surface. memory-watchdog.ts is untouched.

Verification

  • typecheck, lint:gui, build:gui, privacy:scan: all green
  • targeted tests (responses-state, memory-watchdog): 41 pass / 0 fail
  • upstream tests/memory-watchdog.test.ts passes unmodified

Summary by CodeRabbit

  • New Features

    • Added a dashboard “Memory observability” card with RSS, JS heap/JSC heap growth, warning threshold/last warning, and expandable continuation-store retention metrics.
    • Added matching localized memory diagnostics text (EN/DE/JA/KO/RU/ZH).
    • Shows a compact “unavailable” state when diagnostics aren’t supported by older proxies.
  • Documentation

    • Expanded troubleshooting for GET /api/system/memory to explain the new continuation-store responseState fields.
  • Bug Fixes

    • Memory diagnostics now include observe-only continuation-store metrics without eviction/pruning or unintended data loading.
  • Tests

    • Updated tests for the extended response shape and side-effect-free snapshot behavior.

…ead-only dashboard card

Thin observe-only layer on top of the built-in RSS watchdog (PR lidge-jun#388 increment 1).

- state.ts: side-effect-free responseStateMetrics() (count/totalBytes/largestBytes/oldestAgeMs), reuses existing sizeBytes accounting; disk schema unchanged

- system-routes.ts: add scalar-only responseState field to the existing GET /api/system/memory payload; same management auth gate, no new endpoints

- gui: new read-only MemoryObservabilityCard (RSS ring samples, jscHeap, response-store metrics, RSS drift/hour); no sliders, no restart controls

- i18n: dash.mem.* keys for en/de/ko/zh/ru/ja

- docs: response-store attribution note in windows-memory.md

- tests: responseStateMetrics coverage + endpoint shape (responseState, scalar-only)
@github-actions github-actions Bot added the enhancement New feature or request label Jul 24, 2026
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The system memory endpoint now reports continuation-store metrics. A dashboard card polls and displays these diagnostics with localized labels, while tests cover metric accuracy and side-effect-free observation. Troubleshooting documentation explains the payload and dashboard view.

Changes

Memory observability

Layer / File(s) Summary
Response-state metrics and API payload
src/responses/state.ts, src/server/management/system-routes.ts, tests/responses-state.test.ts, tests/memory-watchdog.test.ts
The continuation store exposes count, byte, and age metrics; /api/system/memory returns them, with tests covering values, scalar types, and side-effect-free behavior.
Dashboard memory diagnostics
gui/src/components/MemoryObservabilityCard.tsx, gui/src/pages/Dashboard.tsx, gui/src/i18n/{en,de,ja,ko,ru,zh}.ts
The dashboard polls the endpoint every five seconds, displays memory and response-store statistics, derives RSS growth, handles unavailable endpoints, and adds localized strings.
Memory observability documentation
docs-site/src/content/docs/troubleshooting/windows-memory.md
The troubleshooting page documents the responseState fields, their relationship to heap and RSS growth, side-effect behavior, and dashboard parity.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Dashboard
  participant MemoryObservabilityCard
  participant SystemMemoryRoute
  participant responseStateMetrics

  Dashboard->>MemoryObservabilityCard: Render with apiBase
  MemoryObservabilityCard->>SystemMemoryRoute: Poll GET /api/system/memory
  SystemMemoryRoute->>responseStateMetrics: Read continuation-store snapshot
  responseStateMetrics-->>SystemMemoryRoute: Return count, bytes, and age
  SystemMemoryRoute-->>MemoryObservabilityCard: Return memory diagnostics
  MemoryObservabilityCard-->>Dashboard: Render formatted metrics
Loading

Possibly related PRs

  • lidge-jun/opencodex#388: Introduces related observe-only response-state memory diagnostics across the state, management, and UI layers.

Suggested reviewers: ingwannu, lidge-jun

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% 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 clearly summarizes the main change: response-store metrics on /api/system/memory and a read-only dashboard card.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 `@gui/src/components/MemoryObservabilityCard.tsx`:
- Around line 28-35: Make SystemMemory.responseState optional and update the
Details rendering that reads data.responseState.count to safely handle its
absence, displaying — for successful legacy payloads without responseState while
preserving the existing value for newer responses.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 679a5e4b-7cbc-4004-82ff-1211c99de580

📥 Commits

Reviewing files that changed from the base of the PR and between dbed8c1 and d127822.

📒 Files selected for processing (13)
  • docs-site/src/content/docs/troubleshooting/windows-memory.md
  • gui/src/components/MemoryObservabilityCard.tsx
  • gui/src/i18n/de.ts
  • gui/src/i18n/en.ts
  • gui/src/i18n/ja.ts
  • gui/src/i18n/ko.ts
  • gui/src/i18n/ru.ts
  • gui/src/i18n/zh.ts
  • gui/src/pages/Dashboard.tsx
  • src/responses/state.ts
  • src/server/management/system-routes.ts
  • tests/memory-watchdog.test.ts
  • tests/responses-state.test.ts

Comment thread gui/src/components/MemoryObservabilityCard.tsx

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d127822d59

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

<Stat label={t("dash.mem.jscHeap")} value={data?.jscHeap ? formatBytes(data.jscHeap.heapSize) : "—"} />
<Stat
label={t("dash.mem.growth")}
value={growth === null ? "—" : `${growth >= 0 ? "+" : ""}${formatBytes(Math.abs(growth))}/h`}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve negative RSS drift in the dashboard

When the watchdog samples span a drop in RSS, such as after GC or the OS returns pages, this expression removes the negative sign with Math.abs(growth) and only prepends + for positive values, so a -200 MB/h drift renders as 200.0 MB/h. That makes the new observability card report memory recovery as continuing growth; keep the sign or add an explicit - branch when formatting growth.

Useful? React with 👍 / 👎.

<div className="muted text-label" style={{ margin: "14px 0 6px" }}>{t("dash.mem.store")}</div>
<div className="muted text-control" style={{ marginBottom: 10 }}>{t("dash.mem.storeHint")}</div>
<div className="stat-row">
<Stat label={t("dash.mem.storeEntries")} value={data ? new Intl.NumberFormat(locale).format(data.responseState.count) : "—"} />

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Guard responseState before rendering old memory payloads

When this dashboard talks to a proxy version that already has GET /api/system/memory but predates the new responseState field, the fetch succeeds and data is set, then render immediately dereferences data.responseState.count and trips the page error boundary instead of showing the intended unavailable/degraded card. Default the response-state block or validate it before rendering these stats so older-but-compatible memory payloads do not crash the dashboard.

Useful? React with 👍 / 👎.

…SS drift sign

Review follow-up (lidge-jun#427):

- responseState is now optional: a 200 from an older proxy that predates the field renders em-dashes instead of crashing the card (CodeRabbit major, Codex)

- negative RSS drift keeps its sign so memory recovery no longer renders as growth (Codex)

- docstrings for formatBytes/formatAge/Stat (docstring-coverage warning)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
gui/src/components/MemoryObservabilityCard.tsx (2)

78-99: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Prevent overlapping memory polls.

setInterval starts a new fetchMemory() without waiting for the previous request. If /api/system/memory stalls or responses complete out of order, requests can accumulate and an older payload can overwrite newer metrics. Serialize polls with an in-flight guard, or abort/time out the previous request before starting another.

🤖 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 `@gui/src/components/MemoryObservabilityCard.tsx` around lines 78 - 99, Update
the MemoryObservabilityCard useEffect polling logic around fetchMemory to
prevent concurrent requests: track whether a memory fetch is in flight and skip
interval-triggered polls until it completes, while preserving cancellation
cleanup and existing unavailable handling.

47-62: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve zero age for non-empty stores.

A store entry created in the same clock tick can legitimately have oldestAgeMs === 0, but formatAge renders every non-positive value as "—". Distinguish responseState.count === 0 at Line 146 from invalid/negative ages so a non-empty store can display 0s or its locale equivalent.

Also applies to: 146-146

🤖 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 `@gui/src/components/MemoryObservabilityCard.tsx` around lines 47 - 62, Update
formatAge and its call site near responseState.count so zero is rendered as the
locale-equivalent 0-second duration for non-empty stores, while empty stores
continue displaying "—". Keep invalid and negative ages treated as unavailable,
and use responseState.count === 0 to distinguish empty stores from valid zero
ages.
🤖 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 `@gui/src/components/MemoryObservabilityCard.tsx`:
- Line 131: Update the growth display in MemoryObservabilityCard to replace the
hardcoded “/h” suffix with the localized dash.mem.perHour translation via
t(...). Add the dash.mem.perHour key with the appropriate per-hour value to
every locale file used by gui, preserving the existing sign, formatting, and
null behavior.

---

Outside diff comments:
In `@gui/src/components/MemoryObservabilityCard.tsx`:
- Around line 78-99: Update the MemoryObservabilityCard useEffect polling logic
around fetchMemory to prevent concurrent requests: track whether a memory fetch
is in flight and skip interval-triggered polls until it completes, while
preserving cancellation cleanup and existing unavailable handling.
- Around line 47-62: Update formatAge and its call site near responseState.count
so zero is rendered as the locale-equivalent 0-second duration for non-empty
stores, while empty stores continue displaying "—". Keep invalid and negative
ages treated as unavailable, and use responseState.count === 0 to distinguish
empty stores from valid zero ages.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 97a358d2-43fe-46d3-b312-4278063f66f4

📥 Commits

Reviewing files that changed from the base of the PR and between d127822 and 174a79d.

📒 Files selected for processing (1)
  • gui/src/components/MemoryObservabilityCard.tsx

Comment thread gui/src/components/MemoryObservabilityCard.tsx Outdated
… store age

Review follow-up round 2 (lidge-jun#427, CodeRabbit minors):

- in-flight guard so a stalled /api/system/memory poll cannot stack up or let an older payload overwrite a newer one

- oldest-age cell distinguishes an empty store (em-dash) from a legitimate same-tick zero age (0s)

- growth suffix moved to a localized dash.mem.perHour key (en/de/ko/zh/ru/ja)
@dev-shinyu

Copy link
Copy Markdown
Contributor Author

All bot review findings have been addressed in two follow-up commits:

174a79d (round 1 — CodeRabbit major + Codex):

  • responseState is now optional; a 200 from an older proxy that predates the field renders "—" instead of crashing the card
  • negative RSS drift keeps its sign, so memory recovery no longer renders as growth
  • docstrings added for the card helpers (docstring-coverage warning)

759f586 (round 2 — CodeRabbit minors):

  • polls are serialized with an in-flight guard, so a stalled request can no longer stack up or let an older payload overwrite a newer one
  • the oldest-age cell distinguishes an empty store ("—") from a legitimate same-tick zero age ("0s"), keyed off responseState.count
  • the growth suffix is localized via a new dash.mem.perHour key (en/de/ko/zh/ru/ja)

Each round re-verified locally: typecheck, lint:gui, build:gui all green; targeted tests 41 pass / 0 fail.

@coderabbitai resolve

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
gui/src/components/MemoryObservabilityCard.tsx (2)

38-50: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Route memory-card fallback values through i18n.

The changed helper block and empty-store branch emit hardcoded user-visible values (0 B / ). Add locale keys such as dash.mem.zeroBytes and dash.mem.unavailableValue, pass them into the helpers, and use the translated value at Line 156.

As per path instructions, user-visible strings in gui/** must go through the i18n locale files rather than hardcoded text.

Also applies to: 153-157

🤖 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 `@gui/src/components/MemoryObservabilityCard.tsx` around lines 38 - 50, Route
the user-visible fallback values in formatBytes and formatAge through i18n: add
locale keys such as dash.mem.zeroBytes and dash.mem.unavailableValue, pass the
translated strings into these helpers, and use the translated unavailable value
in the empty-store branch around the affected render logic. Remove the hardcoded
"0 B" and "—" literals while preserving the existing formatting behavior for
valid values.

Source: Path instructions


80-98: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Bound serialized polls with a timeout.

If the current fetch never settles, inFlight remains true forever and every 5-second poll is skipped. The initial card can remain blank indefinitely, bypassing the unavailable fallback. Abort each request after a bounded timeout and abort the active request during effect cleanup.

This conflicts with the supplied PR objective that the card handle unavailable endpoints.

Proposed fix
     let cancelled = false;
     let inFlight = false;
+    let activeController: AbortController | null = null;

     const fetchMemory = async () => {
       if (inFlight) return;
       inFlight = true;
+      const controller = new AbortController();
+      activeController = controller;
+      const timeout = setTimeout(() => controller.abort(), 10_000);
       try {
-        const res = await fetch(`${apiBase}/api/system/memory`);
+        const res = await fetch(`${apiBase}/api/system/memory`, {
+          signal: controller.signal,
+        });
         ...
       } finally {
+        clearTimeout(timeout);
+        if (activeController === controller) activeController = null;
         inFlight = false;
       }
     };

     return () => {
       cancelled = true;
+      activeController?.abort();
       clearInterval(interval);
     };
🤖 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 `@gui/src/components/MemoryObservabilityCard.tsx` around lines 80 - 98, Update
the fetchMemory polling flow in MemoryObservabilityCard to use an
AbortController with a bounded timeout for each request, ensuring stalled calls
reach the existing unavailable fallback. Track the active controller and abort
it during effect cleanup, while preserving the inFlight serialization and
avoiding state updates after cancellation.
🤖 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.

Outside diff comments:
In `@gui/src/components/MemoryObservabilityCard.tsx`:
- Around line 38-50: Route the user-visible fallback values in formatBytes and
formatAge through i18n: add locale keys such as dash.mem.zeroBytes and
dash.mem.unavailableValue, pass the translated strings into these helpers, and
use the translated unavailable value in the empty-store branch around the
affected render logic. Remove the hardcoded "0 B" and "—" literals while
preserving the existing formatting behavior for valid values.
- Around line 80-98: Update the fetchMemory polling flow in
MemoryObservabilityCard to use an AbortController with a bounded timeout for
each request, ensuring stalled calls reach the existing unavailable fallback.
Track the active controller and abort it during effect cleanup, while preserving
the inFlight serialization and avoiding state updates after cancellation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5eb196b7-873f-4cb6-b2fc-4976e3168602

📥 Commits

Reviewing files that changed from the base of the PR and between 174a79d and 759f586.

📒 Files selected for processing (7)
  • gui/src/components/MemoryObservabilityCard.tsx
  • gui/src/i18n/de.ts
  • gui/src/i18n/en.ts
  • gui/src/i18n/ja.ts
  • gui/src/i18n/ko.ts
  • gui/src/i18n/ru.ts
  • gui/src/i18n/zh.ts

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

@dev-shinyu, acknowledged. The follow-up commits address the reported compatibility, polling-ordering, display-state, drift-sign, and localization findings. Resolving my review comments.

🐇 ✅

✅ Action performed

Comments resolved. Approval is disabled; enable reviews.request_changes_workflow to allow explicit top-level @coderabbitai resolve or @coderabbitai approve commands.

Review follow-up round 3 (lidge-jun#427, CodeRabbit major):

- a hung /api/system/memory request could pin the in-flight guard forever, skipping every later poll and leaving the card blank; each poll now aborts after 10s so the unavailable fallback is reachable

- effect cleanup aborts the active request to avoid late state updates

Skipped (with reason): localizing the em-dash/0 B fallbacks - the em-dash placeholder is the established convention across the dashboard (Dashboard.tsx, Storage.tsx, ProviderOverview.tsx) and byte units are locale-neutral there as well.
Resolve Dashboard.tsx conflict from the upstream dashboard workspace-view refactor (section rail + named section consts).

- take upstream's Dashboard.tsx wholesale, then re-mount MemoryObservabilityCard at the end of overviewSection so it renders in both the workspace and classic dashboard views

- i18n dash.mem.* keys (incl. perHour/details) auto-merged cleanly

Re-verified: GUI typecheck/lint/build green; responses-state 34/0 + memory-watchdog 7/0.
@dev-shinyu

Copy link
Copy Markdown
Contributor Author

Rebased conflict resolution: merged the latest dev to pick up the dashboard
workspace-view refactor (section rail + named section consts). Took dev's
Dashboard.tsx wholesale and re-mounted the card at the end of overviewSection,
so it renders in both the workspace and classic dashboard views. The dash.mem.*
keys merged cleanly. Re-verified: GUI typecheck/lint/build green; response-state
34/0 + memory-watchdog 7/0.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
gui/src/components/MemoryObservabilityCard.tsx (1)

39-44: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use correct and locale-aware byte formatting.

Lines [39-44] divide by 1024 but label the results KB/MB/..., which misstates binary values; use KiB/MiB/... or divide by 1000. The toFixed output also hardcodes . decimal separators for every locale. Pass the locale into this helper and use localized unit/number formatting for all RSS, heap, store, and growth values.

As per path instructions, gui/** user-visible strings must go through the i18n locale files rather than hardcoded text.

🤖 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 `@gui/src/components/MemoryObservabilityCard.tsx` around lines 39 - 44, Update
formatBytes in MemoryObservabilityCard to use locale-aware number formatting and
binary unit labels (B, KiB, MiB, GiB, TiB) consistent with division by 1024.
Pass the active locale into the helper and update every RSS, heap, store, and
growth call site to use it; route any user-visible unit strings through the gui
i18n locale files instead of hardcoding them.

Source: Path instructions

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

Outside diff comments:
In `@gui/src/components/MemoryObservabilityCard.tsx`:
- Around line 39-44: Update formatBytes in MemoryObservabilityCard to use
locale-aware number formatting and binary unit labels (B, KiB, MiB, GiB, TiB)
consistent with division by 1024. Pass the active locale into the helper and
update every RSS, heap, store, and growth call site to use it; route any
user-visible unit strings through the gui i18n locale files instead of
hardcoding them.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 034204ce-d289-4653-a376-81d1933868cd

📥 Commits

Reviewing files that changed from the base of the PR and between 759f586 and a4a212d.

📒 Files selected for processing (8)
  • gui/src/components/MemoryObservabilityCard.tsx
  • gui/src/i18n/de.ts
  • gui/src/i18n/en.ts
  • gui/src/i18n/ja.ts
  • gui/src/i18n/ko.ts
  • gui/src/i18n/ru.ts
  • gui/src/i18n/zh.ts
  • gui/src/pages/Dashboard.tsx

@lidge-jun

Copy link
Copy Markdown
Owner

🔒 Maintainer integration in progress — please hold off on merging

@lidge-jun (maintainer) is actively integrating this PR into dev as part of a coordinated
integration pass. Please do not merge, rebase, force-push, or close this PR until this
marker is removed.

What is happening

This PR is being integrated on the maintainer branch codex/260725-pr-rework, which is
based on dev. The pass applies the contributed change, adds any missing regression
coverage, and repairs review-identified defects before a single verified merge into dev.
Every integrated change goes through bun run typecheck, the full bun run test suite,
bun run privacy:scan, and bun run lint:gui, followed by exact-SHA hosted
Cross-platform CI and Service lifecycle runs.

What this means for you

  • Your authorship and commits are preserved. Nothing is being rewritten under your name.
  • No action is needed from you right now. If a defect repair changes your intended
    behavior, it will be described explicitly in a follow-up comment before the merge.
  • If you have work in flight on this branch, please comment here instead of pushing, so we
    do not race each other.

This PR will be closed with a merge receipt (integration commit SHA, verification output,
and hosted CI links) once the pass lands on dev. Thanks for the contribution.

Integration tracker: devlog/_plan/260725_pr_issue_rework · marker posted by the maintainer integration pass

lidge-jun added a commit that referenced this pull request Jul 25, 2026
PR #427 (head a4a212d, author dev-shinyu)과 PR #385 (head ac0260b, author
latemonk)을 통합하고 단위 표기 결함과 테스트 공백을 함께 고친다.

#427은 previous_response_id 연속성 저장소의 count/bytes/age를
/api/system/memory에 얹어, RAM 증가가 대화 보존 때문인지 런타임 할당자
때문인지 구분할 수 있게 한다. 노출값은 숫자뿐이고 스냅샷은 lazy-load, prune,
evict를 하지 않는다.

단위 표기 수정: 카드가 1024로 나눈 값을 KB/MB로 표시해 단계마다 약 2.4%씩
어긋났다. 이제 KiB/MiB 표기를 쓰고 숫자도 활성 로케일을 따른다. RSS, JS heap,
JSC heap, 증가량, 저장소 total/largest, watchdog 임계값 7곳을 모두 바꿨다.

rendered 회귀 3건: 정상 payload가 이진 단위로 표시됨, unmount 후 폴링이
멈춤, non-OK 응답이 unavailable 노트로 degrade됨. formatter는 모듈 내부로
두었다 — export하면 react-refresh 규칙에 걸린다.

#385 BizRouter는 registry 목록 존재만 검사되고 있었다. 엔드포인트, adapter,
authKind, 기본 모델이 seed에 있음, vendor 네임스페이스 ID가 그대로
통과함(카탈로그 슬러그만 평탄화)을 고정하는 테스트를 추가했다.

Co-authored-by: dev-shinyu <dev-shinyu@users.noreply.github.com>
Co-authored-by: latemonk <latemonk@users.noreply.github.com>
@lidge-jun

Copy link
Copy Markdown
Owner

✅ Integrated into dev

Thank you for this. It has been integrated into dev as part of a verified integration pass.

Integration commit: 15dfa270feat(observability): response-store metrics + BizRouter preset
Merged into dev at: ebc62d1f
Your authorship is preserved via Co-authored-by.

Privacy review

Passed. responseStateMetrics() returns four numbers (count, total bytes, largest bytes, oldest age) and nothing else, the payload stays scalar-only, and the snapshot is observe-only — it does not lazy-load the disk snapshot, prune, evict, or re-serialize the store. That last property matters: a probe that re-serialized everything would allocate hardest exactly when memory is already under pressure. bun run privacy:scan clean.

What was changed on top

The card divided by 1024 but labelled the result KB/MB, which misreports by roughly 2.4% per step — not something a memory diagnostic should introduce. It now uses KiB/MiB and formats the number through the active locale, matching the rest of the dashboard. All seven call sites were updated: RSS, JS heap used/total, JSC heap, growth rate, response-store total and largest, and the watchdog threshold.

Dashboard.tsx needed a three-way merge because dev had moved on, but it composed without conflict markers and the card landed inside the Overview stack as intended.

Verification

  • Three rendered regressions were added, because a formatter unit test cannot tell whether the card ever fetches, mounts, or unmounts: a healthy payload renders binary units, unmounting stops the poll, and a non-OK response degrades to the unavailable note
  • The formatter stays module-private — exporting it for the test tripped the react-refresh/only-export-components rule, so the rendered tests carry that coverage instead, which is stronger evidence anyway
  • GUI suite: 148 pass / 0 fail; build clean. Full suite at merge: 4222 pass / 0 fail
  • Cross-platform CI on ebc62d1f: green on ubuntu, macOS, Windows

@lidge-jun

Copy link
Copy Markdown
Owner

Closing: integrated into dev at ebc62d1f with authorship preserved. See the receipt comment above for the integration commit, the changes made on top, and the verification evidence.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants