Skip to content

feat: cerebro memory enhancement layer#466

Merged
yacosta738 merged 6 commits into
mainfrom
feat/cerebro-memory-enhancement-layer
Apr 9, 2026
Merged

feat: cerebro memory enhancement layer#466
yacosta738 merged 6 commits into
mainfrom
feat/cerebro-memory-enhancement-layer

Conversation

@yacosta738
Copy link
Copy Markdown
Contributor

This pull request introduces significant improvements to the integration between the agent runtime and the Cerebro memory service, including new HTTP-based discovery and tool invocation, gateway state classification, and expanded admin endpoints. The changes add robust support for Cerebro as a remote memory backend, improve error handling and diagnostics, and expand both backend and frontend code to support advanced Cerebro features.

Cerebro Gateway Backend Integration

  • Added a new cerebro module to the gateway with multiple admin routes for Cerebro operations (status, search, observation, timeline, stats, memory CRUD, prompts, session management, and context) in mod.rs. [1] [2]
  • Implemented HTTP-based Cerebro client support in cerebro.rs and client.rs, including methods for tool listing, invocation, configuration checking, and error classification. [1] [2] [3] [4]
  • Added comprehensive tests for configuration, HTTP discovery, error redaction, and tool allowlisting. [1] [2] [3] [4]

Cerebro Gateway Tooling and Error Handling

  • Defined an allowlist of Cerebro gateway tools and planned tools, with helper functions for classification and user-facing gateway state messages.
  • Introduced CerebroGatewayState enum and classification logic to distinguish between unconfigured, unreachable, unsupported, and not-implemented states for improved diagnostics and UX.

Frontend Support for Cerebro Features

  • Registered new Vue components for Cerebro observation details, search panel, and timeline panel in the dashboard app.
  • Extended application state to support Cerebro search results and memory mode switching between local and Cerebro. [1] [2]

These changes collectively enhance the system’s ability to interact with Cerebro as a remote memory provider, improve error transparency, and lay the groundwork for advanced memory management features in the UI.

Closes: #361

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 9, 2026

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added dual-mode memory experience: local SQLite browser plus semantic Cerebro search with observation details and timeline exploration
    • New remote memory statistics and insights panels for advanced discovery
    • Added session-level context lookup and remote session actions for operators
    • Comprehensive capability-aware degradation: features gracefully disable when Cerebro is unconfigured or unreachable
  • Localization

    • Expanded English and Spanish translations for new memory and session workflows

Walkthrough

Adds an admin-only Cerebro gateway proxy, MCP HTTP discovery/calls and normalization helpers, and dashboard support (types, composables, components, tests, and i18n) to surface Cerebro memory workflows (search, observation, timeline, stats, memory/session actions) with capability gating and graceful degradation.

Changes

Cohort / File(s) Summary
Gateway module
clients/agent-runtime/src/gateway/cerebro.rs, clients/agent-runtime/src/gateway/mod.rs
New Axum admin routes/handlers under /web/admin/cerebro/*: status, search, observations, timeline, stats, memories, prompts, and session/context actions. Adds DTOs, input validation, admin origin/auth guards, allowlist/planned-tool checks, normalized success/error shapes, and gateway-state mapping.
MCP client & helpers
clients/agent-runtime/src/tools/mcp/client.rs, clients/agent-runtime/src/tools/mcp/cerebro.rs, clients/agent-runtime/src/tools/mcp/normalize.rs
Adds HTTP-based MCP discovery/call paths, Cerebro client helpers, tool-name constants, allowlists and planned-tool handling, CerebroGatewayState enum and error-classification, runtime fallback, auth redaction, and unit/integration tests.
Frontend types & composable
clients/web/apps/dashboard/src/types/admin-sessions.ts, clients/web/apps/dashboard/src/composables/useAdmin.ts
Adds TypeScript Cerebro types and admin composable state: cerebroStatus/stats/search/observation/timeline/lastAction, bucketed loading, fetch helpers (including Cerebro-normalized error handling), and invocation helpers for Cerebro actions.
Frontend components & pages
clients/web/apps/dashboard/src/components/memory/CerebroStatusCard.vue, .../CerebroSearchPanel.vue, .../CerebroObservationDetail.vue, .../CerebroTimelinePanel.vue, .../sessions/CerebroSessionActions.vue, clients/web/apps/dashboard/src/App.vue, .../MemoryList.vue, .../MemoryStats.vue, .../SessionDetail.vue
Adds Cerebro UI panels and integrates dual-mode Local/Cerebro flows: status card, semantic search panel, observation detail, timeline panel, and session action controls; wiring into memory and session views and remote stats rendering.
Tests
clients/web/apps/dashboard/src/components/.../*.spec.ts, clients/web/apps/dashboard/src/composables/useAdmin.spec.ts, Rust tests in MCP/gateway files
Adds Vitest suites for new components/composables and Rust tests using a mock Cerebro HTTP server covering admin auth, payload validation, discovery, normalization, and degraded-state handling.
Docs / specs / locales
openspec/.../cerebro-memory-enhancement-layer/*, openspec/specs/*, clients/web/packages/locales/src/{en,es}.json
Adds design/proposal/spec/task/verify archives and bumps specs; introduces i18n strings and documentation for normalized gateway contracts and admin-only Cerebro API surface.

Sequence Diagram(s)

sequenceDiagram
    participant Admin as Admin User
    participant Dash as Dashboard
    participant Gateway as Axum Gateway
    participant AppState as AppState
    participant MCP as MCP Client
    participant Cerebro as Cerebro HTTP

    Admin->>Dash: open Memory / search
    Dash->>Gateway: GET /web/admin/cerebro/status
    Gateway->>AppState: read Cerebro config
    AppState-->>Gateway: config (endpoint, token)
    Gateway->>MCP: cerebro_list_tools()
    MCP->>Cerebro: HTTP JSON-RPC tools/list
    Cerebro-->>MCP: tools manifest
    MCP-->>Gateway: parsed tool list
    Gateway-->>Dash: { service_state, tools }

    Dash->>Gateway: POST /web/admin/cerebro/search
    Gateway->>Gateway: validate allowlist & egress
    Gateway->>MCP: cerebro_call_tool("mem_search", args)
    MCP->>Cerebro: HTTP JSON-RPC call_tool
    Cerebro-->>MCP: { result: [...] }
    MCP-->>Gateway: parsed result
    Gateway-->>Dash: { state: "available", results: [...] }
Loading
sequenceDiagram
    participant Dash as Dashboard
    participant Gateway as Axum Gateway
    participant AppState as AppState

    Dash->>Gateway: GET /web/admin/cerebro/status
    Gateway->>AppState: read Cerebro config
    AppState-->>Gateway: missing/invalid
    Gateway->>Gateway: classify -> unconfigured
    Gateway-->>Dash: { service_state: "unconfigured", tools: {...} }
    Dash->>Dash: disable Cerebro UI, keep local views active
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

area:rust, area:web, area:docs, risk:high

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 24.07% 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
Title check ✅ Passed Title 'feat: cerebro memory enhancement layer' follows Conventional Commits style with 'feat' prefix and clearly describes the main change (38 chars, within 72-char limit).
Description check ✅ Passed PR description covers purpose, backend/frontend changes with references, and links to #361, though some template sections (Tested Information, Breaking Changes detail) are minimal.
Linked Issues check ✅ Passed PR implements all acceptance criteria: gateway proxies Cerebro tools via new /web/admin/cerebro/* endpoints, dashboard shows Cerebro-enhanced memory views, and non-Cerebro deployments remain unchanged via graceful degradation.
Out of Scope Changes check ✅ Passed All changes align with issue #361 scope: gateway Cerebro proxy module, HTTP client support, frontend Cerebro components, state management, and localization strings are all directly tied to the memory enhancement objective.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cerebro-memory-enhancement-layer

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.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 9, 2026

✅ Contributor Report

User: @yacosta738
Status: Passed (12/13 metrics passed)

Metric Description Value Threshold Status
PR Merge Rate PRs merged vs closed 90% >= 30%
Repo Quality Repos with ≥100 stars 0 >= 0
Positive Reactions Positive reactions received 10 >= 1
Negative Reactions Negative reactions received 0 <= 5
Account Age GitHub account age 3086 days >= 30 days
Activity Consistency Regular activity over time 108% >= 0%
Issue Engagement Issues with community engagement 0 >= 0
Code Reviews Code reviews given to others 533 >= 0
Merger Diversity Unique maintainers who merged PRs 2 >= 0
Repo History Merge Rate Merge rate in this repo 91% >= 0%
Repo History Min PRs Previous PRs in this repo 217 >= 0
Profile Completeness Profile richness (bio, followers) 90 >= 0
Suspicious Patterns Spam-like activity detection 1 N/A

Contributor Report evaluates based on public GitHub activity. Analysis period: 2025-04-09 to 2026-04-09

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: 32

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@clients/agent-runtime/src/gateway/cerebro.rs`:
- Around line 243-256: The status response currently may return raw inventory
errors by using error.to_string() when building message in the match for
normalize::CerebroGatewayState; change the logic in the message construction
(the match creating variable message that references service_error) so that
instead of returning error.to_string() for any non-Available state you always
call normalize::cerebro_gateway_message(state, tool) (i.e., remove the branch
that returns the raw error and use normalize::cerebro_gateway_message(...) for
unsupported/not_implemented and all other non-Available cases) so upstream
transport/auth details are not echoed.
- Around line 405-419: The current success path fabricates defaults for
missing/invalid Cerebro payload fields (data, results array, truncated) and
returns success with AdminCerebroSearchResponse { state:
normalize::CerebroGatewayState::Available }, which hides upstream contract
failures; update the handler that builds AdminCerebroSearchResponse to strictly
validate that response.1 .0 contains a "data" object and that data["results"] is
an array and data["truncated"] is a bool (or absent but not malformed) and if
any of these validations fail, return a gateway error (e.g., propagate an Err or
call the existing failure/err path to produce a 502) instead of calling success;
apply the same strict validation logic to the other similar blocks that
construct AdminCerebroSearchResponse (the blocks around the other mentioned
locations) so malformed payloads surface as errors rather than producing
fabricated Available responses.

In `@clients/agent-runtime/src/gateway/mod.rs`:
- Around line 1333-1381: The mounted /web/admin/cerebro/* routes in
gateway::mod.rs (route handlers: cerebro::handle_admin_cerebro_status,
handle_admin_cerebro_search, handle_admin_cerebro_observation,
handle_admin_cerebro_timeline, handle_admin_cerebro_stats,
handle_admin_cerebro_create_memory, handle_admin_cerebro_update_memory,
handle_admin_cerebro_delete_memory, handle_admin_cerebro_prompt,
handle_admin_cerebro_session_start, handle_admin_cerebro_session_end,
handle_admin_cerebro_session_summary, handle_admin_cerebro_context) currently
lack uniform admin-origin/auth guards and some downstream handlers don’t accept
HeaderMap so they cannot self-enforce policies; fix by applying the existing
admin_origin_guard and admin_requires_auth middleware at the route mounting
level in gateway::mod.rs (wrap this /web/admin/cerebro/* route group with the
same tower/axum layer or ServiceBuilder used elsewhere for admin routes),
ensuring every request to these handlers passes through those guards before
dispatch, and update any handler signatures only if necessary to accept headers
after the middleware is applied.

In `@clients/agent-runtime/src/tools/mcp/cerebro.rs`:
- Around line 110-117: The test
cerebro_client_uses_http_transport_for_gateway_calls currently asserts on
specific error message substrings from client.list_tools(), which is brittle;
change the test to assert the call returns an Err (or matches a well-defined
error enum/variant) instead of relying on exact textual content from
list_tools(), or, if matching text is necessary, broaden the check to a stable
marker (e.g., an error kind like mcp_transport_error enum or an Error::kind())
so use cerebro_client(), configured_cerebro(), and list_tools() to locate the
test and update the assertion to check Result::is_err() or match on a canonical
error variant rather than fragile substrings.

In `@clients/agent-runtime/src/tools/mcp/client.rs`:
- Around line 550-555: The current MCP discovery code calls
response.text().await which buffers the entire body unbounded; replace this with
streaming read that enforces the same output_limit_bytes policy used by
call_tool_http(): use response.bytes_stream() (or response.chunk()) and
accumulate chunks up to a configured output_limit_bytes, returning an error if
the limit is exceeded, then decode the collected bytes into a string; update the
code paths referencing response.text().await in client.rs (the MCP discovery
block around the response variable) to the new streaming+limit logic to prevent
unbounded allocation.
- Around line 531-534: The reqwest client used during tool discovery is
incorrectly using self.server.call_timeout_ms which can let discovery exceed the
intended startup budget; update the client construction in the discovery path
(e.g., inside list_tools_from_command() where `let client =
reqwest::Client::builder()` is created) to use self.server.startup_timeout_ms
(Duration::from_millis(self.server.startup_timeout_ms)) instead of
self.server.call_timeout_ms so discovery adheres to the startup_timeout_ms limit
and does not stall startup.

In `@clients/agent-runtime/src/tools/mcp/normalize.rs`:
- Around line 84-93: The current broad substring checks on the `normalized`
string (inside the block that returns `CerebroGatewayState::Unreachable`) risk
false positives because tokens like "failed" are too generic; change the
condition for "failed" to a more specific pattern (e.g., check for word
boundaries, full phrases like "connection failed" or "request failed", or use a
regex) or move the generic "failed" check later in the decision chain so more
specific checks run first; update the condition that inspects `normalized` (the
same block that also checks "timeout", "transport", "unreachable", "egress",
"http 4", "http 5") to use the narrower matching logic for "failed" and preserve
returning `CerebroGatewayState::Unreachable` only when a clear network/transport
failure is detected.
- Around line 89-90: The current contains checks against normalized
("normalized.contains(\"http 4\")" / "http 5") miss variants like "http4", "http
400" or "http4xx"; update the pattern matching on the normalized string to be
more robust—either add checks for "http4" and "http5" or replace the contains
calls with a small regex match (e.g., r"http\s*4" / r"http\s*5" or
r"http\s*\d{3}") applied to the normalized variable so it catches "http4", "http
4", "http 400", and "http4xx". Ensure you reference and change the existing
normalized.contains(...) usage in normalize.rs.

In `@clients/web/apps/dashboard/src/App.vue`:
- Around line 446-447: When toggling the memory mode via the click that sets
memoryMode = 'local', clear the persisted Cerebro selection by resetting
selectedCerebroResult to null (or equivalent) so stale Cerebro
observation/timeline data doesn't remain; implement this either by adding the
clear to the click handler that sets memoryMode or by adding a watcher on
memoryMode that sets selectedCerebroResult.value = null when newMode === 'local'
(ensure you reference the existing selectedCerebroResult and memoryMode symbols
and update any UI bindings that depend on selectedCerebroResult).

In
`@clients/web/apps/dashboard/src/components/memory/CerebroObservationDetail.vue`:
- Around line 40-46: The "Relationship Insights" block currently only shows
placeholder text while gating on
admin.cerebroObservation.value.observation.relationships or .ontology; update
the CerebroObservationDetail.vue component to either render the actual data from
admin.cerebroObservation.value.observation.relationships and/or
admin.cerebroObservation.value.observation.ontology (e.g., iterate and display
relationship items and ontology entries) or, if left as scaffolded UI, add a
clear TODO comment inside the <div> noting that rendering is pending and what
fields (observation.relationships, observation.ontology) should be displayed;
ensure you reference the existing conditional and DOM block (the "Relationship
Insights" <div>) so the change is placed in the same component.
- Around line 14-22: The watcher in CerebroObservationDetail.vue that calls
admin.fetchCerebroObservation when props.selected?.memory_id changes should wrap
the await call in a try/catch to prevent uncaught promise rejections; inside the
async watcher callback (the function passed to watch) catch any error from
admin.fetchCerebroObservation(memoryId) and handle it consistently (e.g.,
processLogger.error or admin.handleError) or at minimum log the error, mirroring
the pattern used in CerebroTimelinePanel.

In `@clients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.spec.ts`:
- Around line 63-64: Add assertions in the CerebroSearchPanel.spec.ts test
(after the existing expect(fetchMock).toHaveBeenCalledTimes(1)) to validate the
request shape: inspect fetchMock.mock.calls[0] and assert the requested URL path
includes the expected endpoint, the HTTP method equals the expected method
(e.g., "POST"/"GET") and the request body (parsed JSON from the call's body
argument) contains the submitted query parameters you expect from the component;
use the same test-local identifiers (fetchMock and wrapper) to locate the call
and keep the new assertions tight so they fail when the endpoint, method, or
payload shape changes.

In `@clients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.vue`:
- Line 47: The disabled binding can throw if tools or mem_search are missing;
update the condition in CerebroSearchPanel.vue to safely guard each access (e.g.
use effectiveStatus?.tools?.mem_search?.state) and combine with the existing
admin.loadingBuckets.value.cerebroSearch check so the expression becomes a safe
null-aware comparison (or use a helper like const isAvailable =
effectiveStatus?.tools?.mem_search?.state === 'available' ?? false and then
:disabled="!isAvailable || admin.loadingBuckets.value.cerebroSearch"); ensure
you reference effectiveStatus, tools, mem_search, and
admin.loadingBuckets.value.cerebroSearch when making the change.
- Around line 54-56: The template currently accesses
effectiveStatus?.tools.mem_search.state and
effectiveStatus?.tools.mem_search.message without guarding that tools or
mem_search exist; update the v-if and message binding to use optional chaining
for both properties (e.g., effectiveStatus?.tools?.mem_search?.state and
effectiveStatus?.tools?.mem_search?.message) so the v-if and fallback message
are safe when tools or mem_search are undefined, modifying the expressions in
the CerebroSearchPanel.vue template where effectiveStatus, tools, mem_search,
state, and message are referenced.

In `@clients/web/apps/dashboard/src/components/memory/CerebroStatusCard.vue`:
- Around line 10-20: The component's orderedTools array in CerebroStatusCard.vue
omits three allowlisted tools ("mem_save", "mem_update", "mem_delete"), so
update the orderedTools: CerebroToolName[] definition to include those three
names (in the appropriate order you want displayed) alongside the existing
entries so the card renders all 12 allowlisted tools and surfaces
write-capability regressions to admins.

In `@clients/web/apps/dashboard/src/components/memory/CerebroTimelinePanel.vue`:
- Line 37: The v-for in CerebroTimelinePanel uses the loop index as the key
which is fragile; update the :key on the <li> to use a stable identifier from
each timeline entry (e.g., item.id or item.timestamp) instead of index so Vue
can reconcile changes correctly; locate the v-for on
admin.cerebroTimeline.value.items and change :key="index" to something like
:key="item.id || item.timestamp" (or another unique property on the item) and
ensure that property is present/unique for all items.
- Around line 14-22: The watch callback for () => props.selected?.memory_id
calls the async admin.fetchCerebroTimeline without handling errors; wrap the
await admin.fetchCerebroTimeline({ memory_id: memoryId }) invocation in a
try/catch inside the watcher callback (the watcher defined around
props.selected?.memory_id) and handle failures by either setting/propagating the
composable's error state on admin or logging/reporting the error (e.g., process
via admin's error handling method or console/processLogger) so rejected promises
are not left unhandled.

In `@clients/web/apps/dashboard/src/components/memory/MemoryList.vue`:
- Around line 96-101: Replace the hardcoded header and helper copy in
MemoryList.vue ("Local Memory", "SQLite-backed memory browser", and the helper
paragraph) with vue-i18n lookups using t(...) so they match the rest of the
component; add keys such as memory.kicker, memory.title, and memory.helper to
the locale files and call t('memory.kicker'), t('memory.title'), and
t('memory.helper') (or use the useI18n hook's t) where the elements render to
restore localization and avoid the i18n regression.

In `@clients/web/apps/dashboard/src/components/memory/MemoryStats.spec.ts`:
- Around line 33-48: The fixture in MemoryStats.spec.ts is creating a
service_state of "unreachable" but leaves some tools (mem_save, mem_update,
mem_delete) as "available" and planned tools as "not_implemented", which doesn't
match how tool_status_map() will mark every allowlisted tool as "unreachable";
update the mock so when options?.remoteUnavailable is true the tools object sets
every tool entry (mem_search, mem_get_observation, mem_timeline, mem_stats,
mem_save, mem_update, mem_delete, mem_save_prompt, mem_session_start,
mem_session_end, mem_session_summary, mem_context, etc.) to state: "unreachable"
rather than mixing "available" or "not_implemented", ensuring the fixture
mirrors the gateway contract.

In `@clients/web/apps/dashboard/src/components/memory/MemoryStats.vue`:
- Around line 17-23: The onMounted handler currently uses
Promise.all([admin.fetchMemoryStats(), admin.fetchCerebroStatus(),
admin.fetchCerebroStats()]) so any failure in fetchCerebroStatus() or
fetchCerebroStats() rejects the whole mount; change this so
admin.fetchMemoryStats() always runs and Cerebro calls are guarded (e.g., wrap
admin.fetchCerebroStatus() and admin.fetchCerebroStats() with .catch(...) or use
Promise.allSettled for those two) so network/malformed-response errors are
swallowed or logged but do not prevent mount; keep function names
admin.fetchMemoryStats, admin.fetchCerebroStatus, and admin.fetchCerebroStats in
your change and ensure errors are handled (logged via console or admin logger)
and do not propagate.

In
`@clients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.spec.ts`:
- Around line 79-83: Update the test in CerebroSessionActions.spec.ts to assert
the request contract more fully: after grabbing const [url, init] =
fetchMock.mock.calls[0] validate that init.headers includes the expected auth
header (e.g., Authorization or whatever auth header key your app uses) and that
init.body (parse JSON from init.body) contains session_id and any optional
params (use toEqual/toMatchObject or similar to check session_id and present
optional keys). Keep the existing URL and method assertions and replace the
loose wrapper check with these explicit header and payload assertions so
endpoint regressions are caught earlier.

In
`@clients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.vue`:
- Around line 23-32: The invoke function can issue duplicate remote mutations
when called multiple times quickly; add an in-flight guard (e.g., a reactive
boolean like isInvoking or a Set pendingActions) and check it at the top of
invoke to short-circuit duplicate calls, set it before awaiting the remote calls
and clear it in a finally block so it always resets; apply the same guard when
calling admin.invokeCerebroContext and admin.invokeCerebroSessionAction
(referencing invoke, invokeCerebroContext, invokeCerebroSessionAction, and
props.sessionId) and also use this flag to disable the related buttons (the UI
buttons at the other call sites) while the request is pending.

In `@clients/web/apps/dashboard/src/components/sessions/SessionDetail.vue`:
- Line 25: The parallel fetch currently uses await
Promise.all([admin.fetchSessionDetail(props.sessionId),
admin.fetchCerebroStatus()]) which causes a Cerebro failure to abort session
detail loading; replace this with Promise.allSettled so each promise outcome is
handled independently: call
Promise.allSettled([admin.fetchSessionDetail(props.sessionId),
admin.fetchCerebroStatus()]) and then inspect the returned results array to
extract the fulfilled value for admin.fetchSessionDetail and separately
handle/log a rejected admin.fetchCerebroStatus result (gracefully degrade UI),
ensuring you still surface errors from fetchSessionDetail as appropriate.

In `@clients/web/apps/dashboard/src/composables/useAdmin.spec.ts`:
- Around line 404-406: The test asserts on admin.cerebroStatus.value but
useAdmin.fetchCerebroStatus() currently returns the status payload without
writing to the cerebroStatus ref, so update one of two places: either change the
test to assert only on the returned result (e.g.,
expect(result.service_state).toBe(...) and
expect(result.tools.mem_search.state).toBe(...)) and remove reliance on
admin.cerebroStatus, or modify useAdmin.ts fetchCerebroStatus() to persist the
response by assigning cerebroStatus.value = response (or the appropriate mapped
shape) before returning so the test can continue to assert on
admin.cerebroStatus.value; reference symbols: fetchCerebroStatus, cerebroStatus,
useAdmin.ts, and the test expectations in useAdmin.spec.ts.

In `@clients/web/apps/dashboard/src/composables/useAdmin.ts`:
- Around line 390-400: In invokeCerebroSessionAction, validate that sessionId is
provided when tool is "mem_session_end" or "mem_session_summary" and throw or
return a clear validation error before constructing the path; update the early
logic that currently builds
`/web/admin/cerebro/sessions/${encodeURIComponent(sessionId ?? "")}/end` or
`/summary` to instead check (tool === "mem_session_end" || tool ===
"mem_session_summary") && !sessionId and fail fast with a descriptive message so
the UI surfaces a validation error rather than generating paths with empty IDs
and producing 404s.
- Around line 185-206: fetchCerebroJson leaves previous Cerebro error messages
set on error.value, so add explicit clearing of the stale error at the start of
the request (e.g., right after setLoading(bucket, true) or before creating the
AbortController) by resetting error.value to an empty string/null; update the
fetchCerebroJson implementation (the same place where fetchJson resets
error.value) to clear error.value before performing the fetch so a subsequent
successful response won't display an old error banner.
- Around line 152-158: The fetch helper in useAdmin.ts is forwarding
JSON.stringify bodies without setting Content-Type, so Axum Json extractors (in
Cerebro handlers) reject requests; update the fetch call (the block that calls
fetch(buildUrl(path, params), { ...init, headers: { ...authHeaders(),
...(init?.headers ?? {}), ... } , signal: controller.signal })) to ensure that
when init?.body is a string or when a caller intends JSON you add Content-Type:
application/json (or application/*+json) to the merged headers (without
overwriting existing explicit header), and apply the same change to the other
similar fetch block around lines 189-195; use buildUrl, authHeaders, and the
controller.signal fetch call as anchors to find both spots.

In
`@openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/design.md`:
- Around line 87-92: The implementation in
clients/agent-runtime/src/gateway/cerebro.rs currently returns non-2xx (503/501)
and uses a "state" field and endpoint-specific bodies; update the handler logic
(see functions around lines referenced: the Cerebro gateway handlers at
cerebro.rs:226-237, 272-289, 340-348) to conform to the design: always return
200 OK for Cerebro action/readiness outcomes, use the standardized envelope {
status, data } (rename "state" → "status"), map existing enum values to the
normalized statuses (available, unconfigured, unreachable, unsupported,
not_implemented), and wrap endpoint-specific payloads under "data" so all read
responses share the common typed action envelope while keeping non-2xx reserved
for input/auth/serialization failures.
- Around line 169-180: The documented Cerebro routes in design.md are out of
sync with the gateway router: replace references to GET
/web/admin/cerebro/capabilities, the GET timeline-by-id flow, and
/sessions/end|summary (missing :session_id) with the actual routes exposed by
the gateway router — /web/admin/cerebro/status (GET),
/web/admin/cerebro/timeline (POST), and
/web/admin/cerebro/sessions/:session_id/end and
/web/admin/cerebro/sessions/:session_id/summary — and update the same mismatched
references elsewhere in the doc (the other blocks noted) so all examples and
flow diagrams match the router handlers.

In
`@openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.md`:
- Around line 42-89: The verification markdown has MD022/MD029 warnings: ensure
there is a blank line before each "### Rust focused verification" and "###
Dashboard focused verification" heading and a blank line after each heading, and
restart the ordered list numbering at the start of the Dashboard section by
changing the "4." list item to "1." (and similarly confirm each verification
subsection's ordered list begins at 1) so headings are surrounded by blank lines
and each ordered list restarts correctly to satisfy MD022/MD029.
- Around line 137-164: Add a concise rollback strategy section to the
verify-report.md archive that outlines operational rollback steps and risks for
the Cerebro gateway/runtime changes: describe threat/risk notes for
security/runtime/gateway, explicit rollback steps such as toggling the feature
flag or config, reverting the gateway behavior (reference gateway/cerebro.rs),
restoring prior normalize behavior (reference normalize.rs), and undoing
client-side fetch changes (reference useAdmin.ts); include required
monitoring/health checks, alerting thresholds, DB or state cleanup actions, and
an ordered runbook for on-call (who to notify, quick revert commands, and smoke
tests to validate rollback—include adding tests for boundary/failure modes and
references to component tests for CerebroObservationDetail.vue and
CerebroTimelinePanel.vue to verify post-rollback UI behavior).
🪄 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

Run ID: 3aabb48f-623e-44c0-9a18-ba04f0f454dc

📥 Commits

Reviewing files that changed from the base of the PR and between f0fa596 and 6db38c3.

📒 Files selected for processing (31)
  • clients/agent-runtime/src/gateway/cerebro.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/tools/mcp/cerebro.rs
  • clients/agent-runtime/src/tools/mcp/client.rs
  • clients/agent-runtime/src/tools/mcp/normalize.rs
  • clients/web/apps/dashboard/src/App.vue
  • clients/web/apps/dashboard/src/components/memory/CerebroObservationDetail.vue
  • clients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.spec.ts
  • clients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.vue
  • clients/web/apps/dashboard/src/components/memory/CerebroStatusCard.vue
  • clients/web/apps/dashboard/src/components/memory/CerebroTimelinePanel.vue
  • clients/web/apps/dashboard/src/components/memory/MemoryList.vue
  • clients/web/apps/dashboard/src/components/memory/MemoryStats.spec.ts
  • clients/web/apps/dashboard/src/components/memory/MemoryStats.vue
  • clients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.spec.ts
  • clients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.vue
  • clients/web/apps/dashboard/src/components/sessions/SessionDetail.spec.ts
  • clients/web/apps/dashboard/src/components/sessions/SessionDetail.vue
  • clients/web/apps/dashboard/src/composables/useAdmin.spec.ts
  • clients/web/apps/dashboard/src/composables/useAdmin.ts
  • clients/web/apps/dashboard/src/types/admin-sessions.ts
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/archive-report.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/design.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/proposal.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/specs/client-surfaces/spec.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/specs/memory-visibility/spec.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/state.yaml
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/tasks.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.md
  • openspec/specs/client-surfaces/spec.md
  • openspec/specs/memory-visibility/spec.md
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: report / Contributor Quality Report
  • GitHub Check: pr-checks
  • GitHub Check: sonar
  • GitHub Check: Cloudflare Pages
🧰 Additional context used
📓 Path-based instructions (9)
**/*.vue

⚙️ CodeRabbit configuration file

**/*.vue: Enforce Vue 3 Composition API with <script setup>.
Ensure accessibility (A11y) and proper use of Tailwind CSS classes.
Check for proper prop validation and emitted events documentation.

Files:

  • clients/web/apps/dashboard/src/components/memory/MemoryList.vue
  • clients/web/apps/dashboard/src/components/sessions/SessionDetail.vue
  • clients/web/apps/dashboard/src/components/memory/CerebroStatusCard.vue
  • clients/web/apps/dashboard/src/components/memory/CerebroTimelinePanel.vue
  • clients/web/apps/dashboard/src/components/memory/MemoryStats.vue
  • clients/web/apps/dashboard/src/App.vue
  • clients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.vue
  • clients/web/apps/dashboard/src/components/memory/CerebroObservationDetail.vue
  • clients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.vue
**/*

⚙️ CodeRabbit configuration file

**/*: Security first, performance second.
Validate input boundaries, auth/authz implications, and secret management.
Look for behavioral regressions, missing tests, and contract breaks across modules.

Files:

  • clients/web/apps/dashboard/src/components/memory/MemoryList.vue
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/state.yaml
  • clients/web/apps/dashboard/src/components/sessions/SessionDetail.vue
  • clients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.spec.ts
  • clients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.spec.ts
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/archive-report.md
  • clients/web/apps/dashboard/src/composables/useAdmin.spec.ts
  • clients/web/apps/dashboard/src/components/memory/CerebroStatusCard.vue
  • clients/web/apps/dashboard/src/components/memory/MemoryStats.spec.ts
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/tasks.md
  • clients/web/apps/dashboard/src/components/memory/CerebroTimelinePanel.vue
  • clients/agent-runtime/src/tools/mcp/client.rs
  • clients/web/apps/dashboard/src/components/memory/MemoryStats.vue
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/web/apps/dashboard/src/components/sessions/SessionDetail.spec.ts
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/proposal.md
  • clients/web/apps/dashboard/src/App.vue
  • clients/agent-runtime/src/tools/mcp/cerebro.rs
  • clients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.vue
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/design.md
  • clients/agent-runtime/src/tools/mcp/normalize.rs
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/specs/memory-visibility/spec.md
  • clients/web/apps/dashboard/src/components/memory/CerebroObservationDetail.vue
  • clients/web/apps/dashboard/src/composables/useAdmin.ts
  • openspec/specs/client-surfaces/spec.md
  • clients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.vue
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/specs/client-surfaces/spec.md
  • clients/web/apps/dashboard/src/types/admin-sessions.ts
  • openspec/specs/memory-visibility/spec.md
  • clients/agent-runtime/src/gateway/cerebro.rs
**/*.{md,mdx}

⚙️ CodeRabbit configuration file

**/*.{md,mdx}: Verify technical accuracy and that docs stay aligned with code changes.
For user-facing docs, check EN/ES parity or explicitly note pending translation gaps.

Files:

  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/archive-report.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/tasks.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/proposal.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/design.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/specs/memory-visibility/spec.md
  • openspec/specs/client-surfaces/spec.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/specs/client-surfaces/spec.md
  • openspec/specs/memory-visibility/spec.md
clients/agent-runtime/src/tools/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Implement Tool trait in src/tools/ with strict parameter schema, validate and sanitize all inputs, and return structured ToolResult without panics in runtime path

Files:

  • clients/agent-runtime/src/tools/mcp/client.rs
  • clients/agent-runtime/src/tools/mcp/cerebro.rs
  • clients/agent-runtime/src/tools/mcp/normalize.rs
clients/agent-runtime/src/{security,gateway,tools}/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Treat src/security/, src/gateway/, src/tools/ as high-risk surfaces and never broaden filesystem/network execution scope without explicit policy checks

Files:

  • clients/agent-runtime/src/tools/mcp/client.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/tools/mcp/cerebro.rs
  • clients/agent-runtime/src/tools/mcp/normalize.rs
  • clients/agent-runtime/src/gateway/cerebro.rs
clients/agent-runtime/src/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

clients/agent-runtime/src/**/*.rs: Never log secrets, tokens, raw credentials, or sensitive payloads in any logging statements
Avoid unnecessary allocations, clones, and blocking operations to maintain performance and efficiency

Files:

  • clients/agent-runtime/src/tools/mcp/client.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/tools/mcp/cerebro.rs
  • clients/agent-runtime/src/tools/mcp/normalize.rs
  • clients/agent-runtime/src/gateway/cerebro.rs
clients/agent-runtime/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Run cargo fmt --all -- --check, cargo clippy --all-targets -- -D warnings, and cargo test for code validation, or document which checks were skipped and why

Files:

  • clients/agent-runtime/src/tools/mcp/client.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/tools/mcp/cerebro.rs
  • clients/agent-runtime/src/tools/mcp/normalize.rs
  • clients/agent-runtime/src/gateway/cerebro.rs
clients/agent-runtime/src/{security,gateway,tools,config}/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Do not silently weaken security policy or access constraints; keep default behavior secure-by-default with deny-by-default where applicable

Files:

  • clients/agent-runtime/src/tools/mcp/client.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/tools/mcp/cerebro.rs
  • clients/agent-runtime/src/tools/mcp/normalize.rs
  • clients/agent-runtime/src/gateway/cerebro.rs
**/*.rs

⚙️ CodeRabbit configuration file

**/*.rs: Focus on Rust idioms, memory safety, and ownership/borrowing correctness.
Flag unnecessary clones, unchecked panics in production paths, and weak error context.
Prioritize unsafe blocks, FFI boundaries, concurrency races, and secret handling.

Files:

  • clients/agent-runtime/src/tools/mcp/client.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/tools/mcp/cerebro.rs
  • clients/agent-runtime/src/tools/mcp/normalize.rs
  • clients/agent-runtime/src/gateway/cerebro.rs
🧠 Learnings (6)
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Include threat/risk notes and rollback strategy for security, runtime, and gateway changes; add or update tests for boundary checks and failure modes

Applied to files:

  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/tasks.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/proposal.md
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/tools/**/*.rs : Implement `Tool` trait in `src/tools/` with strict parameter schema, validate and sanitize all inputs, and return structured `ToolResult` without panics in runtime path

Applied to files:

  • clients/agent-runtime/src/tools/mcp/client.rs
  • clients/agent-runtime/src/tools/mcp/cerebro.rs
  • clients/agent-runtime/src/tools/mcp/normalize.rs
  • clients/agent-runtime/src/gateway/cerebro.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/main.rs : Preserve CLI contract unless change is intentional and documented; prefer explicit errors over silent fallback for unsupported critical paths

Applied to files:

  • clients/agent-runtime/src/tools/mcp/client.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/tools/mcp/cerebro.rs
  • clients/agent-runtime/src/tools/mcp/normalize.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/{security,gateway,tools}/**/*.rs : Treat `src/security/`, `src/gateway/`, `src/tools/` as high-risk surfaces and never broaden filesystem/network execution scope without explicit policy checks

Applied to files:

  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/tools/mcp/normalize.rs
  • clients/agent-runtime/src/gateway/cerebro.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/{security,gateway,tools,config}/**/*.rs : Do not silently weaken security policy or access constraints; keep default behavior secure-by-default with deny-by-default where applicable

Applied to files:

  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/tools/mcp/normalize.rs
  • clients/agent-runtime/src/gateway/cerebro.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/providers/**/*.rs : Implement `Provider` trait in `src/providers/` and register in `src/providers/mod.rs` factory when adding a new provider

Applied to files:

  • clients/agent-runtime/src/gateway/mod.rs
🪛 LanguageTool
openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.md

[grammar] ~42-~42: Use a hyphen to join words.
Context: ...rification (minimal, no build) ### Rust focused verification 1. `cargo test --ma...

(QB_NEW_EN_HYPHEN)


[grammar] ~71-~71: Use a hyphen to join words.
Context: ...orted, not_implemented ### Dashboard focused verification 4.pnpm exec vites...

(QB_NEW_EN_HYPHEN)

🪛 markdownlint-cli2 (0.22.0)
openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.md

[warning] 42-42: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 71-71: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 72-72: Ordered list item prefix
Expected: 1; Actual: 4; Style: 1/1/1

(MD029, ol-prefix)


[warning] 87-87: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 107-107: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 124-124: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 139-139: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 145-145: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 153-153: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 156-156: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 161-161: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🔇 Additional comments (20)
openspec/specs/client-surfaces/spec.md (1)

506-677: Spec update is coherent and implementation-aligned.

The added Cerebro requirements preserve local-first behavior, enforce admin-only visibility, and define explicit degraded states/contracts clearly.

Also applies to: 744-792, 819-819

clients/web/apps/dashboard/src/components/memory/MemoryList.vue (1)

340-357: Header style additions look clean and low-risk.

Layout and spacing changes are scoped and consistent with existing component styling.

openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/state.yaml (1)

1-12: Archive state metadata is complete and consistent.

Phase progression and terminal archive status are clearly captured.

clients/agent-runtime/src/gateway/mod.rs (1)

48-48: Module exposure is clean and scoped.

pub mod cerebro; is a straightforward, minimal integration point and keeps the routing extension explicit.

clients/web/apps/dashboard/src/components/sessions/SessionDetail.vue (1)

40-40: Good UI integration: scoped loading + Cerebro action panel wiring.

Using loadingBuckets.sessionDetail avoids unrelated loading flicker, and the new CerebroSessionActions props are wired cleanly for per-session actions.

Also applies to: 104-109

clients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.spec.ts (1)

49-56: Planned-tool behavior test is valuable and aligned with UX contract.

This protects the “show degraded Cerebro state without blocking local session UI” behavior.

clients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.spec.ts (1)

67-73: Degraded-state test is solid.

Disabling submit in unconfigured state and surfacing explicit messaging is exactly the right guardrail.

openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/archive-report.md (1)

1-51: Archive report is clear and actionable.

The structure, synced-spec mapping, and explicit warning carry-forward make this easy to audit.

clients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.vue (1)

58-65: Good accessibility: aria-live="polite" and role="status" on loading state.

clients/agent-runtime/src/tools/mcp/cerebro.rs (2)

32-41: Good defensive JSON parsing with fallback.

The or_else fallback wrapping raw response as Value::String handles non-JSON tool outputs gracefully without panicking.


43-54: Configuration check is thorough.

Properly trims whitespace and requires both endpoint and auth_token to be non-empty. This prevents misconfiguration with whitespace-only values.

clients/web/apps/dashboard/src/App.vue (2)

440-459: Tab navigation has correct ARIA attributes.

role="tab", aria-selected, and role="tablist" are properly applied for accessibility.


477-494: Cerebro layout correctly wires props and events.

Components receive consistent gatewayUrl, authHeaders, and share selectedCerebroResult state. The @select event properly updates the shared selection.

openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/proposal.md (1)

1-164: Comprehensive proposal with clear scope, risks, and rollback strategy.

The document aligns well with the implemented code and addresses security concerns (allowlist approach, admin auth enforcement). The rollback plan is clean since changes are additive.

openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/tasks.md (1)

1-28: Task breakdown is well-structured and aligns with implementation.

The RED/GREEN TDD approach and phased rollout (contracts → handlers → UI → verification) demonstrate disciplined engineering practice.

clients/agent-runtime/src/tools/mcp/normalize.rs (4)

7-15: Tool constants are well-organized and follow naming convention.


17-30: Allowlist of 12 tools matches proposal scope.

The explicit allowlist prevents arbitrary MCP passthrough, addressing the security concern in the proposal.


98-114: User-facing messages are clear and provide fallback context.

Messages consistently note that "Local memory remains available" for degraded states, supporting the graceful degradation goal.


366-380: Tests verify allowlist integrity and planned-tool tracking.

Good coverage ensuring mem_context is NOT in planned tools (it's implemented) while session tools are tracked as planned.

clients/web/apps/dashboard/src/components/memory/CerebroTimelinePanel.vue (1)

29-31: No changes needed. The .value access pattern is correct for nested refs.

The composable returns an object with individual ref-wrapped properties (cerebroTimeline: ref(...), loadingBuckets: ref(...)). In Vue 3 templates, refs are auto-unwrapped only at the top level—nested refs in objects require explicit .value access. The component's pattern admin.loadingBuckets.value.cerebroTimeline and admin.cerebroTimeline.value is correct and necessary.

			> Likely an incorrect or invalid review comment.

Comment on lines +243 to +256
let message = match state {
normalize::CerebroGatewayState::Available => None,
_ => Some(service_error.map_or_else(
|| normalize::cerebro_gateway_message(state, tool),
|error| {
if matches!(
state,
normalize::CerebroGatewayState::Unconfigured
| normalize::CerebroGatewayState::Unreachable
) {
normalize::cerebro_gateway_message(state, tool)
} else {
error.to_string()
}
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 | 🟠 Major

Don't echo raw Cerebro inventory errors in the status response.

For unsupported/not_implemented states this returns error.to_string() to the dashboard. That bypasses the redaction this module uses everywhere else and can expose upstream transport/auth details in an admin response. Use the normalized cerebro_gateway_message(...) here as well.

As per coding guidelines "Security first, performance second. Validate input boundaries, auth/authz implications, and secret management."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/gateway/cerebro.rs` around lines 243 - 256, The
status response currently may return raw inventory errors by using
error.to_string() when building message in the match for
normalize::CerebroGatewayState; change the logic in the message construction
(the match creating variable message that references service_error) so that
instead of returning error.to_string() for any non-Available state you always
call normalize::cerebro_gateway_message(state, tool) (i.e., remove the branch
that returns the raw error and use normalize::cerebro_gateway_message(...) for
unsupported/not_implemented and all other non-Available cases) so upstream
transport/auth details are not echoed.

Comment on lines +405 to +419
let data = response.1 .0["data"].clone();
let results = data
.get("results")
.and_then(Value::as_array)
.cloned()
.unwrap_or_default();
success(&AdminCerebroSearchResponse {
state: normalize::CerebroGatewayState::Available,
results_count: results.len(),
truncated: data
.get("truncated")
.and_then(Value::as_bool)
.unwrap_or(false),
results,
})
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 | 🟠 Major

Reject malformed success payloads instead of fabricating empty available responses.

These wrappers default missing or invalid tool output to [], false, null, or an opaque Value and still return 200 with state: available. A broken upstream contract should surface as a gateway error (for example 502) so the dashboard does not treat corrupted Cerebro data as a successful read.

As per coding guidelines "Security first, performance second. Validate input boundaries, auth/authz implications, and secret management. Look for behavioral regressions, missing tests, and contract breaks across modules."

Also applies to: 455-459, 497-506, 538-541

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/gateway/cerebro.rs` around lines 405 - 419, The
current success path fabricates defaults for missing/invalid Cerebro payload
fields (data, results array, truncated) and returns success with
AdminCerebroSearchResponse { state: normalize::CerebroGatewayState::Available },
which hides upstream contract failures; update the handler that builds
AdminCerebroSearchResponse to strictly validate that response.1 .0 contains a
"data" object and that data["results"] is an array and data["truncated"] is a
bool (or absent but not malformed) and if any of these validations fail, return
a gateway error (e.g., propagate an Err or call the existing failure/err path to
produce a 502) instead of calling success; apply the same strict validation
logic to the other similar blocks that construct AdminCerebroSearchResponse (the
blocks around the other mentioned locations) so malformed payloads surface as
errors rather than producing fabricated Available responses.

Comment on lines +110 to +117
#[test]
fn cerebro_client_uses_http_transport_for_gateway_calls() {
let client = cerebro_client(&configured_cerebro()).unwrap();
let tools = client.list_tools().unwrap_err().to_string();
assert!(
tools.contains("MCP HTTP discovery failed") || tools.contains("mcp_transport_error")
);
}
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.

🧹 Nitpick | 🔵 Trivial

Test asserts on error message content - consider brittleness.

The test relies on specific error message substrings. If upstream error messages change, this test breaks. This is acceptable for integration-style tests but worth noting.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/tools/mcp/cerebro.rs` around lines 110 - 117, The
test cerebro_client_uses_http_transport_for_gateway_calls currently asserts on
specific error message substrings from client.list_tools(), which is brittle;
change the test to assert the call returns an Err (or matches a well-defined
error enum/variant) instead of relying on exact textual content from
list_tools(), or, if matching text is necessary, broaden the check to a stable
marker (e.g., an error kind like mcp_transport_error enum or an Error::kind())
so use cerebro_client(), configured_cerebro(), and list_tools() to locate the
test and update the assertion to check Result::is_err() or match on a canonical
error variant rather than fragile substrings.

Comment on lines +531 to +534
let client = reqwest::Client::builder()
.timeout(Duration::from_millis(self.server.call_timeout_ms))
.build()
.context("failed to build MCP HTTP client")?;
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 | 🟠 Major

Use startup_timeout_ms for discovery, not call_timeout_ms.

list_tools_from_command() respects the startup discovery budget, but the new HTTP path switches to call_timeout_ms. That means a slow Cerebro inventory endpoint can now stall startup longer than the configured discovery timeout.

As per coding guidelines, "Security first, performance second" and "Look for behavioral regressions, missing tests, and contract breaks across modules."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/tools/mcp/client.rs` around lines 531 - 534, The
reqwest client used during tool discovery is incorrectly using
self.server.call_timeout_ms which can let discovery exceed the intended startup
budget; update the client construction in the discovery path (e.g., inside
list_tools_from_command() where `let client = reqwest::Client::builder()` is
created) to use self.server.startup_timeout_ms
(Duration::from_millis(self.server.startup_timeout_ms)) instead of
self.server.call_timeout_ms so discovery adheres to the startup_timeout_ms limit
and does not stall startup.

Comment on lines +550 to +555
let response = req.send().await.context("MCP HTTP discovery failed")?;
let status = response.status();
let body = response
.text()
.await
.context("failed to read MCP HTTP discovery body")?;
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 | 🔴 Critical

Cap discovery response size before reading the whole body.

response.text().await buffers the full body with no output_limit_bytes check. A misbehaving or hostile endpoint can force unbounded allocation during startup discovery, while call_tool_http() already streams with size enforcement.

As per coding guidelines, "Security first, performance second" and "Avoid unnecessary allocations, clones, and blocking operations to maintain performance and efficiency."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/tools/mcp/client.rs` around lines 550 - 555, The
current MCP discovery code calls response.text().await which buffers the entire
body unbounded; replace this with streaming read that enforces the same
output_limit_bytes policy used by call_tool_http(): use response.bytes_stream()
(or response.chunk()) and accumulate chunks up to a configured
output_limit_bytes, returning an error if the limit is exceeded, then decode the
collected bytes into a string; update the code paths referencing
response.text().await in client.rs (the MCP discovery block around the response
variable) to the new streaming+limit logic to prevent unbounded allocation.

Comment on lines +390 to +400
async function invokeCerebroSessionAction(
tool: Extract<CerebroToolName, "mem_session_start" | "mem_session_end" | "mem_session_summary">,
sessionId?: string,
payload: Record<string, unknown> = {}
) {
const path =
tool === "mem_session_start"
? "/web/admin/cerebro/sessions/start"
: tool === "mem_session_end"
? `/web/admin/cerebro/sessions/${encodeURIComponent(sessionId ?? "")}/end`
: `/web/admin/cerebro/sessions/${encodeURIComponent(sessionId ?? "")}/summary`;
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 | 🟠 Major

Reject missing sessionId for end/summary actions locally.

For mem_session_end and mem_session_summary, sessionId is still optional here, so the function can build /web/admin/cerebro/sessions//end or /summary. Fail fast before constructing the path so the UI reports a validation error instead of a misleading 404.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/web/apps/dashboard/src/composables/useAdmin.ts` around lines 390 -
400, In invokeCerebroSessionAction, validate that sessionId is provided when
tool is "mem_session_end" or "mem_session_summary" and throw or return a clear
validation error before constructing the path; update the early logic that
currently builds `/web/admin/cerebro/sessions/${encodeURIComponent(sessionId ??
"")}/end` or `/summary` to instead check (tool === "mem_session_end" || tool ===
"mem_session_summary") && !sessionId and fail fast with a descriptive message so
the UI surfaces a validation error rather than generating paths with empty IDs
and producing 404s.

Comment on lines +87 to +92
### Decision: Reserve non-2xx responses for gateway/auth/validation failures; return typed domain states for Cerebro readiness failures

**Choice**: Cerebro action endpoints return `200 OK` with a typed `status` for normalized remote
states such as `available`, `unconfigured`, `unreachable`, `unsupported`, and `not_implemented`.
Use non-2xx only for invalid input, unauthorized access, forbidden origin, and unexpected gateway
serialization/runtime failures.
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 | 🟠 Major

The normalized response contract is out of sync with the implementation.

These sections still describe degraded Cerebro outcomes as 200 responses with a status field, but clients/agent-runtime/src/gateway/cerebro.rs:272-289 returns HTTP 503/501 and uses state. The typed action envelope here also no longer matches the actual read responses, which are returned as typed endpoint-specific bodies rather than a common { status, data } wrapper.

Also applies to: 226-237, 340-348

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/design.md`
around lines 87 - 92, The implementation in
clients/agent-runtime/src/gateway/cerebro.rs currently returns non-2xx (503/501)
and uses a "state" field and endpoint-specific bodies; update the handler logic
(see functions around lines referenced: the Cerebro gateway handlers at
cerebro.rs:226-237, 272-289, 340-348) to conform to the design: always return
200 OK for Cerebro action/readiness outcomes, use the standardized envelope {
status, data } (rename "state" → "status"), map existing enum values to the
normalized statuses (available, unconfigured, unreachable, unsupported,
not_implemented), and wrap endpoint-specific payloads under "data" so all read
responses share the common typed action envelope while keeping non-2xx reserved
for input/auth/serialization failures.

Comment on lines +169 to +180
### Capability detection flow

```text
Dashboard ── GET /web/admin/cerebro/capabilities ──→ Gateway
│ │
│ ├── validate memory.cerebro config
│ ├── enforce egress/read boundary
│ ├── fetch live tool inventory
│ ├── merge with allowlist + planned-tool overrides
│ └── emit normalized status map
└── enables / disables Cerebro panels without blocking Local Memory UI
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 | 🟠 Major

Update the documented Cerebro routes to match the gateway.

This still documents GET /web/admin/cerebro/capabilities, a GET timeline-by-id flow, and /sessions/end|summary paths without :session_id. The router in clients/agent-runtime/src/gateway/mod.rs:1334-1380 exposes /web/admin/cerebro/status, POST /web/admin/cerebro/timeline, and /web/admin/cerebro/sessions/:session_id/{end,summary} instead.

Also applies to: 183-206, 314-329

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/design.md`
around lines 169 - 180, The documented Cerebro routes in design.md are out of
sync with the gateway router: replace references to GET
/web/admin/cerebro/capabilities, the GET timeline-by-id flow, and
/sessions/end|summary (missing :session_id) with the actual routes exposed by
the gateway router — /web/admin/cerebro/status (GET),
/web/admin/cerebro/timeline (POST), and
/web/admin/cerebro/sessions/:session_id/end and
/web/admin/cerebro/sessions/:session_id/summary — and update the same mismatched
references elsewhere in the doc (the other blocks noted) so all examples and
flow diagrams match the router handlers.

Comment on lines +42 to +89
### Rust focused verification
1. `cargo test --manifest-path clients/agent-runtime/Cargo.toml gateway::cerebro::tests`
- **PASS**
- Evidence: 8/8 passed in `src/lib.rs`; 8/8 passed in `src/main.rs`
- Runtime-covered behaviors:
- admin auth enforced
- unconfigured status response
- available/planned status mix
- typed search success
- raw MCP passthrough rejection
- unreachable stats normalization
- `mem_session_summary` normalized `not_implemented`
- Notable assertion now present: `json["tools"][normalize::CEREBRO_TOOL_CONTEXT]["state"] == "available"`

2. `cargo test --manifest-path clients/agent-runtime/Cargo.toml tools::mcp::client::tests::list_tools`
- **PASS**
- Evidence: 2/2 passed in `src/lib.rs`; 2/2 passed in `src/main.rs`
- Runtime-covered behaviors:
- live HTTP `tools/list` discovery works
- auth token is redacted from failures

3. `cargo test --manifest-path clients/agent-runtime/Cargo.toml tools::mcp::normalize::tests`
- **PASS**
- Evidence: 20/20 passed in `src/lib.rs`; 20/20 passed in `src/main.rs`
- Covered:
- allowlist contents
- planned-tool tracking
- normalized classification for `unconfigured`, `unreachable`, `unsupported`, `not_implemented`

### Dashboard focused verification
4. `pnpm exec vitest run --environment happy-dom src/composables/useAdmin.spec.ts src/components/memory/MemoryStats.spec.ts src/components/memory/MemoryList.spec.ts src/components/memory/CerebroSearchPanel.spec.ts src/components/sessions/CerebroSessionActions.spec.ts src/components/sessions/SessionDetail.spec.ts`
- **PASS**
- Evidence: **6 test files passed, 45 tests passed, 0 failed**
- Passing suites:
- `src/composables/useAdmin.spec.ts` — 23 passed
- `src/components/memory/MemoryStats.spec.ts` — 2 passed
- `src/components/memory/MemoryList.spec.ts` — 9 passed
- `src/components/memory/CerebroSearchPanel.spec.ts` — 2 passed
- `src/components/sessions/CerebroSessionActions.spec.ts` — 2 passed
- `src/components/sessions/SessionDetail.spec.ts` — 7 passed
- Newly fixed blocker confirmed:
- `useAdmin.spec.ts` delete success path is now green
- Non-blocking note:
- output still emits missing-i18n-key warnings during tests

### Build / typecheck / coverage
- **Build:** not run, per user instruction: do not build.
- **Coverage:** not configured in `openspec/config.yaml`.
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

Fix the markdownlint warnings in the verification sections.

This block still trips the reported MD022/MD029 issues: missing blank lines around headings and continuing the ordered list with 4. instead of 1.. It’s minor, but it keeps the archived report noisy in linted docs.

🧰 Tools
🪛 LanguageTool

[grammar] ~42-~42: Use a hyphen to join words.
Context: ...rification (minimal, no build) ### Rust focused verification 1. `cargo test --ma...

(QB_NEW_EN_HYPHEN)


[grammar] ~71-~71: Use a hyphen to join words.
Context: ...orted, not_implemented ### Dashboard focused verification 4.pnpm exec vites...

(QB_NEW_EN_HYPHEN)

🪛 markdownlint-cli2 (0.22.0)

[warning] 42-42: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 71-71: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 72-72: Ordered list item prefix
Expected: 1; Actual: 4; Style: 1/1/1

(MD029, ol-prefix)


[warning] 87-87: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.md`
around lines 42 - 89, The verification markdown has MD022/MD029 warnings: ensure
there is a blank line before each "### Rust focused verification" and "###
Dashboard focused verification" heading and a blank line after each heading, and
restart the ordered list numbering at the start of the Dashboard section by
changing the "4." list item to "1." (and similarly confirm each verification
subsection's ordered list begins at 1) so headings are surrounded by blank lines
and each ordered list restarts correctly to satisfy MD022/MD029.

Comment on lines +137 to +164
## Static Correctness / Design Coherence

### Confirmed improvements since prior report
- `normalize.rs` now tracks only 4 planned tools; `mem_context` is no longer hardcoded as planned.
- `gateway/cerebro.rs` test now verifies `mem_context` reports `available` in the available/planned status mix.
- `client.rs` live discovery tests now run on a multi-threaded Tokio runtime and pass.
- `useAdmin.ts` `fetchJson()` now safely handles `204` and empty response bodies, which resolves the prior delete-path test failure.

### Design alignment
- Dedicated gateway Cerebro module remains in place.
- Typed REST wrappers remain in place; no raw JSON-RPC passthrough was introduced.
- Local Memory and Cerebro Memory remain parallel, clearly segmented views.
- Live HTTP `tools/list` discovery is now both implemented and verified.

## Issues Found

### CRITICAL
- None.

### WARNING
- Docs are still not updated to describe the operator-facing gateway/dashboard Cerebro capability semantics (`clients/web/apps/docs/src/content/docs/cerebro/mcp-tools.md` remains focused on raw MCP inventory).
- Some delta-spec scenarios still lack direct executed proof, especially older-backend `unsupported`, end-user exclusion, and observation/timeline/relationship rendering specifics.
- Dashboard-focused tests still emit missing-i18n warnings; not a blocker for this verify phase, but noisy.

### SUGGESTION
- Add a focused gateway test for the older-backend `unsupported` status path.
- Add dedicated component tests for `CerebroObservationDetail.vue` and `CerebroTimelinePanel.vue` to convert current static evidence into runtime evidence.
- Add one explicit end-user/non-admin regression test proving Cerebro admin capability data does not appear on non-admin routes.
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.

🧹 Nitpick | 🔵 Trivial

Add an explicit rollback strategy for the gateway/runtime changes.

The report covers test evidence and remaining warnings, but it still doesn’t spell out how to back out the Cerebro gateway/runtime path if the remote service causes operational issues after release. A short rollback section would make this archive much more actionable.

Based on learnings, "Include threat/risk notes and rollback strategy for security, runtime, and gateway changes; add or update tests for boundary checks and failure modes".

🧰 Tools
🪛 markdownlint-cli2 (0.22.0)

[warning] 139-139: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 145-145: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 153-153: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 156-156: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 161-161: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.md`
around lines 137 - 164, Add a concise rollback strategy section to the
verify-report.md archive that outlines operational rollback steps and risks for
the Cerebro gateway/runtime changes: describe threat/risk notes for
security/runtime/gateway, explicit rollback steps such as toggling the feature
flag or config, reverting the gateway behavior (reference gateway/cerebro.rs),
restoring prior normalize behavior (reference normalize.rs), and undoing
client-side fetch changes (reference useAdmin.ts); include required
monitoring/health checks, alerting thresholds, DB or state cleanup actions, and
an ordered runbook for on-call (who to notify, quick revert commands, and smoke
tests to validate rollback—include adding tests for boundary/failure modes and
references to component tests for CerebroObservationDetail.vue and
CerebroTimelinePanel.vue to verify post-rollback UI behavior).

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Apr 9, 2026

Deploying corvus with  Cloudflare Pages  Cloudflare Pages

Latest commit: 3500dc7
Status: ✅  Deploy successful!
Preview URL: https://650e06ca.corvus-42x.pages.dev
Branch Preview URL: https://feat-cerebro-memory-enhancem.corvus-42x.pages.dev

View logs

@coderabbitai coderabbitai Bot added area:vue and removed area:rust labels Apr 9, 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: 11

♻️ Duplicate comments (3)
clients/web/apps/dashboard/src/App.vue (1)

115-119: ⚠️ Potential issue | 🟡 Minor

Clear Cerebro selection in programmatic local-mode navigation too.

Line 117 switches to local mode, but Line 119 path doesn’t clear selectedCerebroResult; stale Cerebro detail can reappear when the user toggles back to Cerebro mode later.

Suggested fix
 function onViewSessionMemory(sessionId: string) {
   memorySessionIdFilter.value = sessionId;
   memoryMode.value = "local";
+  selectedCerebroResult.value = null;
   currentPage.value = "memory";
   selectedSession.value = null;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/web/apps/dashboard/src/App.vue` around lines 115 - 119, The
onViewSessionMemory function sets memorySessionIdFilter, memoryMode, currentPage
and clears selectedSession but fails to clear selectedCerebroResult, leaving
stale Cerebro details; update onViewSessionMemory to also set
selectedCerebroResult.value = null (or equivalent clearing) whenever memoryMode
is set to "local" so Cerebro state is reset during programmatic local-mode
navigation, referencing the onViewSessionMemory function and the
selectedCerebroResult and memoryMode symbols.
openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.md (1)

42-163: ⚠️ Potential issue | 🟡 Minor

Resolve remaining markdownlint structural warnings in the verification sections.

This report still contains heading/list formatting issues (MD022/MD005), which keeps archived docs noisy in linted pipelines.

Suggested cleanup
-### Rust focused verification
+### Rust focused verification
+
 1. `cargo test --manifest-path clients/agent-runtime/Cargo.toml gateway::cerebro::tests`
    - **PASS**
...
-### Dashboard focused verification
+### Dashboard focused verification
+
 1. `pnpm exec vitest run --environment happy-dom ...`
    - **PASS**
    - Evidence: **8 test files passed, 48 tests passed, 0 failed**
    - Passing suites:
      - `src/composables/useAdmin.spec.ts` — 23 passed
      - `src/components/memory/MemoryStats.spec.ts` — 2 passed
      - `src/components/memory/MemoryList.spec.ts` — 9 passed
      - `src/components/memory/CerebroSearchPanel.spec.ts` — 2 passed
-      - `src/components/memory/CerebroObservationDetail.spec.ts` — 1 passed
-      - `src/components/memory/CerebroTimelinePanel.spec.ts` — 1 passed
-      - `src/components/sessions/CerebroSessionActions.spec.ts` — 2 passed
-      - `src/components/sessions/SessionDetail.spec.ts` — 7 passed
+     - `src/components/memory/CerebroObservationDetail.spec.ts` — 1 passed
+     - `src/components/memory/CerebroTimelinePanel.spec.ts` — 1 passed
+     - `src/components/sessions/CerebroSessionActions.spec.ts` — 2 passed
+     - `src/components/sessions/SessionDetail.spec.ts` — 7 passed
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.md`
around lines 42 - 163, The file has markdownlint warnings (MD022/MD005) due to
heading/list structural issues; fix by adding a single blank line before and
after each ATX heading (e.g. "### Rust focused verification", "### Dashboard
focused verification", "## Acceptance Criteria Traceability") and ensure lists
directly under headings are separated by one blank line and use consistent list
indentation/markers (hyphens or bullets) so nested items align; remove the stray
"[duplicate_comment]" token and ensure the <details> block and table rows also
have proper blank lines above/below to satisfy MD022/MD005.
clients/agent-runtime/src/tools/mcp/client.rs (1)

531-534: ⚠️ Potential issue | 🟠 Major

Discovery still uses call_timeout_ms instead of startup_timeout_ms.

The past review flagged that HTTP discovery should respect startup_timeout_ms to match the command-based discovery budget. Line 532 still uses call_timeout_ms, which could let discovery stall startup longer than intended.

🔧 Proposed fix
         let client = reqwest::Client::builder()
-            .timeout(Duration::from_millis(self.server.call_timeout_ms))
+            .timeout(Duration::from_millis(self.server.startup_timeout_ms))
             .build()
             .context("failed to build MCP HTTP client")?;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/tools/mcp/client.rs` around lines 531 - 534, The
MCP HTTP client builder in clients/agent-runtime/src/tools/mcp/client.rs is
using self.server.call_timeout_ms for discovery but should use the startup
timeout; change the timeout Duration to use self.server.startup_timeout_ms
(i.e., replace call_timeout_ms with startup_timeout_ms where the
reqwest::Client::builder() timeout is set) so discovery honors the
startup_timeout budget used by command-based discovery (keep
Duration::from_millis and the existing context("failed to build MCP HTTP
client")?).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@clients/agent-runtime/src/gateway/cerebro.rs`:
- Around line 200-222: inventory_status currently assumes ToolOperation::Read
when checking availability, causing write-only tools to be reported as
available; update inventory_status to compute per-tool effective operation
(mirroring the logic in execute_tool) and evaluate egress/policy for that
specific operation when building the BTreeSet of tool names from
cerebro_list_tools, or else annotate each tool with its allowed operations in
the status response so availability reflects read vs write/act restrictions;
ensure you still use normalize::classify_cerebro_error for errors.

In `@clients/agent-runtime/src/gateway/mod.rs`:
- Around line 1333-1381: The gateway registers admin routes under
"/web/admin/cerebro/*" which will 404 when the frontend requests
"/api/web/admin/cerebro/*"; update the route mounts to include the "/api" prefix
(e.g., change "/web/admin/cerebro/status" -> "/api/web/admin/cerebro/status" for
all routes shown) so handlers like cerebro::handle_admin_cerebro_status,
cerebro::handle_admin_cerebro_search, cerebro::handle_admin_cerebro_observation,
cerebro::handle_admin_cerebro_timeline, cerebro::handle_admin_cerebro_stats,
cerebro::handle_admin_cerebro_create_memory,
cerebro::handle_admin_cerebro_update_memory,
cerebro::handle_admin_cerebro_delete_memory,
cerebro::handle_admin_cerebro_prompt,
cerebro::handle_admin_cerebro_session_start,
cerebro::handle_admin_cerebro_session_end,
cerebro::handle_admin_cerebro_session_summary and
cerebro::handle_admin_cerebro_context are reachable in production;
alternatively, if you prefer not to change paths, add/document a required
reverse-proxy rewrite that strips "/api" before proxying to the gateway.

In `@clients/agent-runtime/src/tools/mcp/client.rs`:
- Around line 503-521: The list_tools_http function currently spawns a dedicated
thread and builds a new current-thread Tokio runtime when
tokio::runtime::Handle::try_current().is_ok() to avoid blocking an existing
Tokio executor; add a concise clarifying comment above that conditional
explaining the pattern and rationale (i.e., when already inside a Tokio runtime
we cannot block the executor so we create a new runtime on a separate thread and
block_on the async helper list_tools_http_async), referencing the decision
points: tokio::runtime::Handle::try_current, std::thread::spawn, building a new
runtime, and list_tools_http_async so future maintainers understand why this
non-obvious approach is used.
- Around line 285-286: In call_tool_http in client.rs the reqwest client is
using self.server.startup_timeout_ms for the request timeout; change it to use
self.server.call_timeout_ms instead by replacing the timeout reference passed
into reqwest::Client::builder().timeout(...) so tool invocations use the
configured call timeout (keep the rest of the builder unchanged and ensure the
duration conversion matches existing Duration::from_millis usage).

In
`@clients/web/apps/dashboard/src/components/memory/CerebroObservationDetail.vue`:
- Around line 32-33: The dynamic helper/loading <p> elements in
CerebroObservationDetail.vue (the <p v-if="!selected" class="helper"> and <p
v-else-if="admin.loadingBuckets.value.cerebroObservation" class="helper">)
should include accessibility attributes so screen readers announce updates; add
aria-live="polite" and role="status" to both elements (the helper and the
loading message) to ensure polite, programmatic announcement of content changes.

In `@clients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.vue`:
- Around line 20-24: The onMounted callback in CerebroSearchPanel.vue awaits
admin.fetchCerebroStatus() without handling errors, causing unhandled promise
rejections when the fetch fails; wrap the await call in a try/catch inside the
onMounted handler (check props.status first as before) and call an appropriate
error handler or log via the existing admin or console logger so failures are
caught and handled instead of propagating.

In `@clients/web/apps/dashboard/src/components/memory/MemoryStats.vue`:
- Around line 63-68: The template assumes admin.cerebroStats.value has a message
when state !== 'available', which can render undefined; update the render logic
in MemoryStats.vue to guard the message by checking for 'message' in
admin.cerebroStats.value (or using a fallback) before printing it—i.e., change
the v-if to require either a message property or use a safe expression for {{
admin.cerebroStats.value.message }} (fallback string or empty string) so
admin.cerebroStats.value.state and admin.cerebroStats.value.message are both
validated before rendering.

In
`@clients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.vue`:
- Around line 44-47: The invokeCerebroSessionAction call currently passes
sessionId but the implementation ignores it for "mem_session_start"; update the
invokeCerebroSessionAction function so that when tool === "mem_session_start" it
validates that sessionId is present and includes it in the POST payload as { id:
sessionId } (instead of omitting or sending an empty body), while preserving
existing handling for "mem_session_end" and "mem_session_summary"; locate the
logic that switches on tool (or builds the request body) and add the branch that
constructs and sends { id: sessionId } for "mem_session_start".

In `@clients/web/apps/dashboard/src/components/sessions/SessionDetail.spec.ts`:
- Around line 230-278: The repeated large service_state/tools JSON should be
moved to a single shared fixture (e.g., const CEREBRO_TOOL_STATUS or a helper
getCerebroToolStatus()) and reused wherever the tests currently inline that
payload (the mockImplementationOnce responses, the mockDetailResponse and the
out-of-order setups referencing secondResponse.promise). Replace the duplicated
JSON blocks in the mockImplementationOnce calls with the shared constant/helper,
define it near the top of SessionDetail.spec.ts so all mocks (including any uses
of secondResponse.promise and mockDetailResponse) reference the same object to
avoid brittleness when tool inventory changes.

In `@clients/web/apps/dashboard/src/composables/useAdmin.spec.ts`:
- Around line 390-395: The available-status fixture currently sets mem_context
to "not_implemented", which violates the contract; update the fixture so
mem_context uses a contract-valid state (e.g., "available" or the canonical
allowed state used elsewhere) instead of "not_implemented". Locate the fixture
object where mem_context is declared (the available-status / available fixture
around the mem_* entries such as mem_save_prompt, mem_session_start,
mem_session_end, mem_session_summary) and replace the mem_context value with the
appropriate allowlisted state used by other tests to ensure classification
behaves correctly.

In `@clients/web/apps/dashboard/src/types/admin-sessions.ts`:
- Around line 106-118: The template in MemoryStats.vue currently assumes fields
on AdminCerebroStatsData exist; update the component to guard each stat access
(memory_count, session_count, prompt_count, worker_queue_depth) either by using
optional chaining (e.g., admin.cerebroStats.value?.stats?.memory_count) or
explicit presence checks before rendering and provide sensible fallbacks (0 or
'-' shown when missing) so undefined never appears; also remove the unused
worker_enabled property from AdminCerebroStatsData or stop exporting it if the
template doesn't use it.

---

Duplicate comments:
In `@clients/agent-runtime/src/tools/mcp/client.rs`:
- Around line 531-534: The MCP HTTP client builder in
clients/agent-runtime/src/tools/mcp/client.rs is using
self.server.call_timeout_ms for discovery but should use the startup timeout;
change the timeout Duration to use self.server.startup_timeout_ms (i.e., replace
call_timeout_ms with startup_timeout_ms where the reqwest::Client::builder()
timeout is set) so discovery honors the startup_timeout budget used by
command-based discovery (keep Duration::from_millis and the existing
context("failed to build MCP HTTP client")?).

In `@clients/web/apps/dashboard/src/App.vue`:
- Around line 115-119: The onViewSessionMemory function sets
memorySessionIdFilter, memoryMode, currentPage and clears selectedSession but
fails to clear selectedCerebroResult, leaving stale Cerebro details; update
onViewSessionMemory to also set selectedCerebroResult.value = null (or
equivalent clearing) whenever memoryMode is set to "local" so Cerebro state is
reset during programmatic local-mode navigation, referencing the
onViewSessionMemory function and the selectedCerebroResult and memoryMode
symbols.

In
`@openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.md`:
- Around line 42-163: The file has markdownlint warnings (MD022/MD005) due to
heading/list structural issues; fix by adding a single blank line before and
after each ATX heading (e.g. "### Rust focused verification", "### Dashboard
focused verification", "## Acceptance Criteria Traceability") and ensure lists
directly under headings are separated by one blank line and use consistent list
indentation/markers (hyphens or bullets) so nested items align; remove the stray
"[duplicate_comment]" token and ensure the <details> block and table rows also
have proper blank lines above/below to satisfy MD022/MD005.
🪄 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

Run ID: 0f13caa4-da62-4fc7-8acc-6bd127df51fd

📥 Commits

Reviewing files that changed from the base of the PR and between f0fa596 and eea1fc5.

📒 Files selected for processing (35)
  • clients/agent-runtime/src/gateway/cerebro.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/tools/mcp/cerebro.rs
  • clients/agent-runtime/src/tools/mcp/client.rs
  • clients/agent-runtime/src/tools/mcp/normalize.rs
  • clients/web/apps/dashboard/src/App.vue
  • clients/web/apps/dashboard/src/components/memory/CerebroObservationDetail.spec.ts
  • clients/web/apps/dashboard/src/components/memory/CerebroObservationDetail.vue
  • clients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.spec.ts
  • clients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.vue
  • clients/web/apps/dashboard/src/components/memory/CerebroStatusCard.vue
  • clients/web/apps/dashboard/src/components/memory/CerebroTimelinePanel.spec.ts
  • clients/web/apps/dashboard/src/components/memory/CerebroTimelinePanel.vue
  • clients/web/apps/dashboard/src/components/memory/MemoryList.vue
  • clients/web/apps/dashboard/src/components/memory/MemoryStats.spec.ts
  • clients/web/apps/dashboard/src/components/memory/MemoryStats.vue
  • clients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.spec.ts
  • clients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.vue
  • clients/web/apps/dashboard/src/components/sessions/SessionDetail.spec.ts
  • clients/web/apps/dashboard/src/components/sessions/SessionDetail.vue
  • clients/web/apps/dashboard/src/composables/useAdmin.spec.ts
  • clients/web/apps/dashboard/src/composables/useAdmin.ts
  • clients/web/apps/dashboard/src/types/admin-sessions.ts
  • clients/web/packages/locales/src/en.json
  • clients/web/packages/locales/src/es.json
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/archive-report.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/design.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/proposal.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/specs/client-surfaces/spec.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/specs/memory-visibility/spec.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/state.yaml
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/tasks.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.md
  • openspec/specs/client-surfaces/spec.md
  • openspec/specs/memory-visibility/spec.md
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: submit-gradle
  • GitHub Check: pr-checks
  • GitHub Check: Cloudflare Pages
🧰 Additional context used
📓 Path-based instructions (9)
**/*

⚙️ CodeRabbit configuration file

**/*: Security first, performance second.
Validate input boundaries, auth/authz implications, and secret management.
Look for behavioral regressions, missing tests, and contract breaks across modules.

Files:

  • clients/web/apps/dashboard/src/components/sessions/SessionDetail.spec.ts
  • clients/agent-runtime/src/tools/mcp/cerebro.rs
  • clients/web/apps/dashboard/src/components/memory/CerebroTimelinePanel.spec.ts
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/state.yaml
  • clients/web/apps/dashboard/src/components/memory/CerebroObservationDetail.spec.ts
  • clients/web/apps/dashboard/src/components/sessions/SessionDetail.vue
  • clients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.spec.ts
  • clients/web/apps/dashboard/src/components/memory/MemoryStats.spec.ts
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/archive-report.md
  • clients/web/apps/dashboard/src/components/memory/CerebroStatusCard.vue
  • clients/web/packages/locales/src/es.json
  • clients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.spec.ts
  • clients/web/packages/locales/src/en.json
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.md
  • clients/web/apps/dashboard/src/components/memory/CerebroTimelinePanel.vue
  • clients/web/apps/dashboard/src/components/memory/CerebroObservationDetail.vue
  • clients/web/apps/dashboard/src/components/memory/MemoryList.vue
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.vue
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/design.md
  • clients/agent-runtime/src/tools/mcp/client.rs
  • clients/web/apps/dashboard/src/App.vue
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/proposal.md
  • clients/web/apps/dashboard/src/composables/useAdmin.spec.ts
  • clients/web/apps/dashboard/src/components/memory/MemoryStats.vue
  • clients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.vue
  • clients/agent-runtime/src/tools/mcp/normalize.rs
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/tasks.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/specs/client-surfaces/spec.md
  • openspec/specs/client-surfaces/spec.md
  • clients/web/apps/dashboard/src/composables/useAdmin.ts
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/specs/memory-visibility/spec.md
  • openspec/specs/memory-visibility/spec.md
  • clients/web/apps/dashboard/src/types/admin-sessions.ts
  • clients/agent-runtime/src/gateway/cerebro.rs
clients/agent-runtime/src/tools/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Implement Tool trait in src/tools/ with strict parameter schema, validate and sanitize all inputs, and return structured ToolResult without panics in runtime path

Files:

  • clients/agent-runtime/src/tools/mcp/cerebro.rs
  • clients/agent-runtime/src/tools/mcp/client.rs
  • clients/agent-runtime/src/tools/mcp/normalize.rs
clients/agent-runtime/src/{security,gateway,tools}/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Treat src/security/, src/gateway/, src/tools/ as high-risk surfaces and never broaden filesystem/network execution scope without explicit policy checks

Files:

  • clients/agent-runtime/src/tools/mcp/cerebro.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/tools/mcp/client.rs
  • clients/agent-runtime/src/tools/mcp/normalize.rs
  • clients/agent-runtime/src/gateway/cerebro.rs
clients/agent-runtime/src/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

clients/agent-runtime/src/**/*.rs: Never log secrets, tokens, raw credentials, or sensitive payloads in any logging statements
Avoid unnecessary allocations, clones, and blocking operations to maintain performance and efficiency

Files:

  • clients/agent-runtime/src/tools/mcp/cerebro.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/tools/mcp/client.rs
  • clients/agent-runtime/src/tools/mcp/normalize.rs
  • clients/agent-runtime/src/gateway/cerebro.rs
clients/agent-runtime/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Run cargo fmt --all -- --check, cargo clippy --all-targets -- -D warnings, and cargo test for code validation, or document which checks were skipped and why

Files:

  • clients/agent-runtime/src/tools/mcp/cerebro.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/tools/mcp/client.rs
  • clients/agent-runtime/src/tools/mcp/normalize.rs
  • clients/agent-runtime/src/gateway/cerebro.rs
clients/agent-runtime/src/{security,gateway,tools,config}/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Do not silently weaken security policy or access constraints; keep default behavior secure-by-default with deny-by-default where applicable

Files:

  • clients/agent-runtime/src/tools/mcp/cerebro.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/tools/mcp/client.rs
  • clients/agent-runtime/src/tools/mcp/normalize.rs
  • clients/agent-runtime/src/gateway/cerebro.rs
**/*.rs

⚙️ CodeRabbit configuration file

**/*.rs: Focus on Rust idioms, memory safety, and ownership/borrowing correctness.
Flag unnecessary clones, unchecked panics in production paths, and weak error context.
Prioritize unsafe blocks, FFI boundaries, concurrency races, and secret handling.

Files:

  • clients/agent-runtime/src/tools/mcp/cerebro.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/tools/mcp/client.rs
  • clients/agent-runtime/src/tools/mcp/normalize.rs
  • clients/agent-runtime/src/gateway/cerebro.rs
**/*.vue

⚙️ CodeRabbit configuration file

**/*.vue: Enforce Vue 3 Composition API with <script setup>.
Ensure accessibility (A11y) and proper use of Tailwind CSS classes.
Check for proper prop validation and emitted events documentation.

Files:

  • clients/web/apps/dashboard/src/components/sessions/SessionDetail.vue
  • clients/web/apps/dashboard/src/components/memory/CerebroStatusCard.vue
  • clients/web/apps/dashboard/src/components/memory/CerebroTimelinePanel.vue
  • clients/web/apps/dashboard/src/components/memory/CerebroObservationDetail.vue
  • clients/web/apps/dashboard/src/components/memory/MemoryList.vue
  • clients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.vue
  • clients/web/apps/dashboard/src/App.vue
  • clients/web/apps/dashboard/src/components/memory/MemoryStats.vue
  • clients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.vue
**/*.{md,mdx}

⚙️ CodeRabbit configuration file

**/*.{md,mdx}: Verify technical accuracy and that docs stay aligned with code changes.
For user-facing docs, check EN/ES parity or explicitly note pending translation gaps.

Files:

  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/archive-report.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/design.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/proposal.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/tasks.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/specs/client-surfaces/spec.md
  • openspec/specs/client-surfaces/spec.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/specs/memory-visibility/spec.md
  • openspec/specs/memory-visibility/spec.md
🧠 Learnings (10)
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/tools/**/*.rs : Implement `Tool` trait in `src/tools/` with strict parameter schema, validate and sanitize all inputs, and return structured `ToolResult` without panics in runtime path

Applied to files:

  • clients/agent-runtime/src/tools/mcp/cerebro.rs
  • clients/agent-runtime/src/tools/mcp/client.rs
  • clients/agent-runtime/src/tools/mcp/normalize.rs
  • clients/agent-runtime/src/gateway/cerebro.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/main.rs : Preserve CLI contract unless change is intentional and documented; prefer explicit errors over silent fallback for unsupported critical paths

Applied to files:

  • clients/agent-runtime/src/tools/mcp/cerebro.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/design.md
  • clients/agent-runtime/src/tools/mcp/client.rs
  • clients/agent-runtime/src/tools/mcp/normalize.rs
  • clients/agent-runtime/src/gateway/cerebro.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/**/*.rs : Run `cargo fmt --all -- --check`, `cargo clippy --all-targets -- -D warnings`, and `cargo test` for code validation, or document which checks were skipped and why

Applied to files:

  • clients/agent-runtime/src/tools/mcp/cerebro.rs
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.md
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/gateway/cerebro.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Include threat/risk notes and rollback strategy for security, runtime, and gateway changes; add or update tests for boundary checks and failure modes

Applied to files:

  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/proposal.md
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/tasks.md
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/{security,gateway,tools,config}/**/*.rs : Do not silently weaken security policy or access constraints; keep default behavior secure-by-default with deny-by-default where applicable

Applied to files:

  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.md
  • clients/agent-runtime/src/gateway/mod.rs
  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/design.md
  • clients/agent-runtime/src/tools/mcp/client.rs
  • clients/agent-runtime/src/tools/mcp/normalize.rs
  • clients/agent-runtime/src/gateway/cerebro.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Follow `.github/pull_request_template.md` and keep PR descriptions concrete with problem, change, non-goals, risk, and rollback information

Applied to files:

  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.md
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Keep each iteration reversible with small commits and clear rollback strategy; validate assumptions with code search before implementing

Applied to files:

  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.md
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/{security,gateway,tools}/**/*.rs : Treat `src/security/`, `src/gateway/`, `src/tools/` as high-risk surfaces and never broaden filesystem/network execution scope without explicit policy checks

Applied to files:

  • openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.md
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/tools/mcp/client.rs
  • clients/agent-runtime/src/tools/mcp/normalize.rs
  • clients/agent-runtime/src/gateway/cerebro.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/main.rs : Keep startup path lean and avoid heavy initialization in command parsing flow

Applied to files:

  • clients/agent-runtime/src/tools/mcp/client.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/**/*.rs : Avoid unnecessary allocations, clones, and blocking operations to maintain performance and efficiency

Applied to files:

  • clients/agent-runtime/src/tools/mcp/client.rs
🪛 LanguageTool
openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.md

[grammar] ~42-~42: Use a hyphen to join words.
Context: ...rification (minimal, no build) ### Rust focused verification 1. `cargo test --ma...

(QB_NEW_EN_HYPHEN)


[grammar] ~71-~71: Use a hyphen to join words.
Context: ...orted, not_implemented ### Dashboard focused verification 1.pnpm exec vites...

(QB_NEW_EN_HYPHEN)


[style] ~180-~180: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...or redaction behavior is implicated. 4. Revert the dashboard/admin fetch changes in `c...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

🪛 markdownlint-cli2 (0.22.0)
openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.md

[warning] 42-42: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 71-71: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 80-80: Inconsistent indentation for list items at the same level
Expected: 5; Actual: 6

(MD005, list-indent)


[warning] 81-81: Inconsistent indentation for list items at the same level
Expected: 5; Actual: 6

(MD005, list-indent)


[warning] 82-82: Inconsistent indentation for list items at the same level
Expected: 5; Actual: 6

(MD005, list-indent)


[warning] 83-83: Inconsistent indentation for list items at the same level
Expected: 5; Actual: 6

(MD005, list-indent)


[warning] 89-89: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 109-109: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 128-128: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 141-141: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 147-147: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 155-155: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 158-158: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 163-163: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🔇 Additional comments (39)
clients/agent-runtime/src/tools/mcp/cerebro.rs (3)

110-114: Test asserts error occurrence rather than message content - good improvement.

The test now checks is_err() rather than matching on specific error message substrings, which addresses the brittleness concern from prior feedback.


24-41: LGTM!

Clean implementation following Rust idioms. Returns Result types properly, gracefully handles JSON parse failures by falling back to string value, and avoids panics in the runtime path. Based on learnings: "return structured ToolResult without panics in runtime path".


43-54: LGTM!

Validation logic correctly requires both endpoint and auth_token to be non-empty after trimming. The boolean return is appropriate for a configuration check predicate.

clients/web/packages/locales/src/en.json (1)

313-348: LGTM!

New locale keys for actions, sessions, and memory sections are well-structured and consistent with the existing i18n patterns. The keys correctly align with their usage in the Vue components.

clients/web/packages/locales/src/es.json (1)

313-348: LGTM!

Spanish translations are complete and structurally aligned with the English locale. Translations are grammatically appropriate.

clients/web/apps/dashboard/src/components/memory/MemoryList.vue (2)

94-107: LGTM!

The header now correctly uses t() for all user-facing strings with appropriate fallbacks, addressing the prior i18n feedback. Semantic HTML structure is appropriate.


345-362: LGTM!

CSS additions follow existing patterns with scoped styles and CSS variables. Flexbox layout is appropriate for the header.

openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/state.yaml (1)

1-12: LGTM!

Archive state file correctly marks all phases as completed with appropriate metadata.

clients/web/apps/dashboard/src/components/memory/CerebroTimelinePanel.spec.ts (1)

1-39: LGTM!

Test properly mocks fetch, resets between tests, and verifies the component renders timeline data from the API response. The type assertion for the mock is a common Vitest pattern.

clients/web/apps/dashboard/src/components/sessions/SessionDetail.vue (3)

25-36: LGTM!

Excellent implementation of graceful degradation using Promise.allSettled. Session detail failures correctly throw to surface errors, while Cerebro status failures are logged without blocking the primary UI. This addresses the prior review feedback.


51-51: LGTM!

Correctly uses the bucketed loading state loadingBuckets.value.sessionDetail instead of the global loading.value, ensuring the loading indicator is scoped to this specific request.


115-120: LGTM!

CerebroSessionActions integration passes all required props correctly, including the Cerebro status for conditional rendering of actions.

clients/web/apps/dashboard/src/components/memory/CerebroObservationDetail.spec.ts (1)

1-43: LGTM!

Test follows consistent patterns with other Cerebro panel tests. Properly mocks the observation endpoint response and verifies that relationship insights and ontology data render correctly.

clients/web/apps/dashboard/src/components/sessions/SessionDetail.spec.ts (1)

16-50: Good test coverage update for the new Cerebro status dependency.

Nice addition validating the extra status fetch path and the new Session Enhancements rendering behavior.

Also applies to: 143-145

clients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.spec.ts (1)

45-69: Strong request-contract assertions in the success-path test.

Validating endpoint, method, and payload shape here materially improves regression detection for Cerebro search wiring.

clients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.spec.ts (1)

79-85: Request-contract coverage is now explicit and robust.

Nice improvement: this test now verifies auth header and typed payload (session_id, limit) in addition to URL/method, which hardens endpoint-regression detection.

clients/web/apps/dashboard/src/components/memory/CerebroStatusCard.vue (1)

10-23: Tool ordering now reflects the full allowlist.

Good fix including mem_save, mem_update, and mem_delete; this preserves admin visibility for write-capability regressions.

clients/web/apps/dashboard/src/components/memory/MemoryStats.spec.ts (1)

15-79: Degraded-state fixture is contract-aligned now.

Setting all tool states via remoteToolState in unreachable mode is a solid fix and makes these tests much more representative.

clients/web/apps/dashboard/src/components/memory/CerebroTimelinePanel.vue (1)

20-32: Watcher error handling and stable list keys look good.

Catching fetch failures in the watcher and keying timeline rows via timelineItemKey are both solid reliability improvements.

Also applies to: 47-49

openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/design.md (4)

169-206: Route references need verification against implementation.

The past review noted route mismatches. Line 172 shows GET /web/admin/cerebro/status (correct), and lines 197, 202 show GET /cerebro/observations/:id and GET /cerebro/timeline/:id. Verify these match the actual router paths, as past comments indicated timeline is POST not GET.

#!/bin/bash
# Verify actual Cerebro routes in the gateway router
rg -n "cerebro" clients/agent-runtime/src/gateway/mod.rs | head -50

87-102: Design decision documents the non-2xx response strategy.

The past review flagged a mismatch between documented 200 responses and the implementation's use of 503/501. The design now explicitly states the decision to "use normalized non-2xx responses for degraded remote states" (lines 87-92). This is intentional and documented.


312-327: Endpoint strategy table looks aligned with implementation.

The documented routes (POST /cerebro/timeline, /sessions/:session_id/end, /sessions/:session_id/summary) match the expected implementation pattern. The design correctly shows session-scoped routes with :session_id path parameters.


226-237: ⚠️ Potential issue | 🟡 Minor

Normalization path diagram shows 200 responses but decision uses non-2xx.

Lines 232-236 show -> 200 { status: ... } for degraded states, but the architecture decision (lines 87-102) specifies non-2xx responses. Consider updating the diagram to reflect actual HTTP status codes for consistency.

📝 Suggested update to normalization diagram
-   ├── missing Cerebro config             -> 200 { status: "unconfigured" }
-   ├── egress/transport failure           -> 200 { status: "unreachable" }
-   ├── tool not allowlisted / not in inv. -> 200 { status: "unsupported" }
-   ├── upstream NotImplemented            -> 200 { status: "not_implemented" }
+   ├── missing Cerebro config             -> 503 { state: "unconfigured", ... }
+   ├── egress/transport failure           -> 503 { state: "unreachable", ... }
+   ├── tool not allowlisted / not in inv. -> 501 { state: "unsupported", ... }
+   ├── upstream NotImplemented            -> 501 { state: "not_implemented", ... }
			> Likely an incorrect or invalid review comment.
clients/web/apps/dashboard/src/components/memory/CerebroObservationDetail.vue (1)

14-26: Previous issues addressed.

The watcher now wraps fetchCerebroObservation in try/catch (lines 18-22), and the "Relationship Insights" section (lines 50-57) renders actual relationships and ontology data instead of placeholder text. Both past review comments have been resolved.

clients/web/apps/dashboard/src/components/memory/MemoryStats.vue (1)

17-21: Previous issue addressed.

The Cerebro fetch calls now use Promise.allSettled (line 20), ensuring that failures in fetchCerebroStatus() or fetchCerebroStats() won't reject the entire mount path. Local stats loading proceeds independently.

clients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.vue (1)

25-53: Previous issue addressed and implementation is solid.

The pendingActions Set guards against duplicate in-flight requests, and the finally blocks ensure cleanup even on errors. The button's :disabled binding (line 73) respects both tool availability and pending state.

clients/agent-runtime/src/tools/mcp/client.rs (1)

554-581: Output limit enforcement is now in place.

The previous review flagged unbounded body reads. This is now addressed with streaming reads that enforce output_limit_bytes (lines 570-581), matching the pattern in call_tool_http().

clients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.vue (1)

46-55: Previous issues addressed.

The template now uses proper optional chaining (effectiveStatus?.tools?.mem_search?.state and ?.message) at lines 47, 54, and 55, preventing potential TypeErrors when tools or mem_search is undefined.

openspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/proposal.md (1)

1-164: Proposal document is comprehensive and well-aligned with implementation.

The document clearly defines scope boundaries, risks with mitigations, a phased rollout strategy, and a non-destructive rollback plan. The success criteria are measurable and map to the implemented gateway endpoints and dashboard components.

clients/agent-runtime/src/tools/mcp/normalize.rs (2)

84-98: Previous nitpicks addressed.

The error classification now uses specific patterns like "connection failed" and "request failed" (lines 88-89) instead of bare "failed", and includes "http4" / "http5" variants (lines 91, 93) to catch compact status formats. The default fallback to Unreachable is reasonable for unclassified network errors.


17-37: Allowlist and planned-tool sets are well-defined.

The explicit CEREBRO_GATEWAY_ALLOWLIST enforces a deny-by-default posture for tool exposure, and CEREBRO_PLANNED_TOOLS enables graceful not_implemented handling for tools that exist in the spec but aren't yet backend-ready. This aligns with the secure-by-default guideline. Based on learnings: "Do not silently weaken security policy or access constraints; keep default behavior secure-by-default with deny-by-default where applicable."

clients/web/apps/dashboard/src/composables/useAdmin.ts (3)

141-157: LGTM - buildHeaders() correctly sets Content-Type: application/json for JSON bodies.

This resolves the previous issue where Fetch API defaulted to text/plain for string bodies, which Axum's Json extractor would reject.


194-225: LGTM - fetchCerebroJson() now clears stale errors at request start.

Line 201 resets error.value = null before the fetch, matching fetchJson() behavior and preventing stale error banners from persisting after successful requests.


403-426: LGTM - invokeCerebroSessionAction() validates sessionId early.

Lines 408-412 now fail fast with a descriptive error when sessionId is missing for mem_session_end or mem_session_summary, preventing malformed paths like /sessions//end.

clients/agent-runtime/src/gateway/cerebro.rs (5)

224-253: LGTM - tool_status_map() no longer echoes raw inventory errors.

Lines 245-248 always use cerebro_gateway_message(state, tool) regardless of whether service_error contains a value, preventing upstream transport/auth details from leaking into status responses.


334-375: LGTM - execute_tool() enforces per-operation egress policy.

Lines 349-353 re-check enforce_cerebro_egress with the actual operation (Read vs Act), correctly gating write operations even if inventory_status() only checked Read during status discovery.


438-455: LGTM - Search handler validates upstream response shape.

Lines 438-449 use require_data_object, require_array_field, and optional_bool_field to validate Cerebro's response structure, returning 502 BAD_GATEWAY for malformed payloads instead of fabricating empty success responses.


847-888: LGTM - Test infrastructure properly mocks Cerebro and validates security guards.

The test helpers (test_state, admin_headers, spawn_mock_cerebro) provide good coverage for auth/origin enforcement, unknown-field rejection, and normalized error handling without exposing real credentials.


1066-1087: LGTM - Test confirms status messages don't echo raw inventory errors.

status_messages_do_not_echo_raw_inventory_errors explicitly verifies that tool_status_map produces normalized messages even when service_error contains sensitive data like "upstream token leaked".

Comment on lines +200 to +222
fn inventory_status(
config: &MemoryCerebroConfig,
) -> (
normalize::CerebroGatewayState,
BTreeSet<String>,
Option<String>,
) {
if let Err(state) = config_or_status(config) {
return (state, BTreeSet::new(), None);
}

match cerebro::cerebro_list_tools(config) {
Ok(tools) => (
normalize::CerebroGatewayState::Available,
tools.into_iter().map(|tool| tool.name).collect(),
None,
),
Err(error) => {
let state = normalize::classify_cerebro_error(&error.to_string());
(state, BTreeSet::new(), Some(error.to_string()))
}
}
}
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

Status discovery only checks Read egress; write-only tools may show misleading availability.

inventory_status() at line 194 uses ToolOperation::Read, so if read egress is allowed but write/act egress is denied, the status endpoint will show mem_save/mem_update/mem_delete as available. While execute_tool() correctly enforces the actual operation policy, the status response can mislead operators into thinking write tools are ready.

Consider computing per-tool status with the effective operation, or document this behavior clearly in the status response.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/gateway/cerebro.rs` around lines 200 - 222,
inventory_status currently assumes ToolOperation::Read when checking
availability, causing write-only tools to be reported as available; update
inventory_status to compute per-tool effective operation (mirroring the logic in
execute_tool) and evaluate egress/policy for that specific operation when
building the BTreeSet of tool names from cerebro_list_tools, or else annotate
each tool with its allowed operations in the status response so availability
reflects read vs write/act restrictions; ensure you still use
normalize::classify_cerebro_error for errors.

Comment on lines +1333 to +1381
.route(
"/web/admin/cerebro/status",
get(cerebro::handle_admin_cerebro_status),
)
.route(
"/web/admin/cerebro/search",
post(cerebro::handle_admin_cerebro_search),
)
.route(
"/web/admin/cerebro/observations/:memory_id",
get(cerebro::handle_admin_cerebro_observation),
)
.route(
"/web/admin/cerebro/timeline",
post(cerebro::handle_admin_cerebro_timeline),
)
.route(
"/web/admin/cerebro/stats",
get(cerebro::handle_admin_cerebro_stats),
)
.route(
"/web/admin/cerebro/memories",
post(cerebro::handle_admin_cerebro_create_memory),
)
.route(
"/web/admin/cerebro/memories/:memory_id",
axum::routing::patch(cerebro::handle_admin_cerebro_update_memory)
.delete(cerebro::handle_admin_cerebro_delete_memory),
)
.route(
"/web/admin/cerebro/prompts",
post(cerebro::handle_admin_cerebro_prompt),
)
.route(
"/web/admin/cerebro/sessions/start",
post(cerebro::handle_admin_cerebro_session_start),
)
.route(
"/web/admin/cerebro/sessions/:session_id/end",
post(cerebro::handle_admin_cerebro_session_end),
)
.route(
"/web/admin/cerebro/sessions/:session_id/summary",
post(cerebro::handle_admin_cerebro_session_summary),
)
.route(
"/web/admin/cerebro/context",
post(cerebro::handle_admin_cerebro_context),
)
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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Gateway Cerebro route registrations =="
rg -n --type=rust '"/(api/)?web/admin/cerebro' clients/agent-runtime/src/gateway/mod.rs clients/agent-runtime/src/gateway/cerebro.rs || true

echo
echo "== Dashboard URL construction for gateway calls =="
rg -n --type=ts --type=vue 'gatewayUrl:.*`http://localhost:3000/api\$\{path\}`|/web/admin/cerebro|/api/web/admin/cerebro' clients/web/apps/dashboard/src || true

echo
echo "== Proxy/rewrite config candidates in dashboard app =="
fd -i 'vite.config.*' clients/web/apps/dashboard -x sh -c 'echo "--- {} ---"; sed -n "1,240p" "{}"'
rg -n --type=ts --type=js --type=json 'proxy|rewrite|/api' clients/web/apps/dashboard || true

Repository: dallay/corvus

Length of output: 8504


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Check for /api/web/admin/cerebro routes in gateway ===="
rg -n --type=rust '/api/web/admin/cerebro' clients/agent-runtime/src/gateway/ || echo "No /api/web/admin/cerebro routes found"

echo
echo "== Check entire gateway/mod.rs for route mounting patterns ===="
rg -n --type=rust '\.route\(' clients/agent-runtime/src/gateway/mod.rs | grep -i 'web/admin' | head -20

echo
echo "== Search for any nginx, server, or proxy config ===="
fd -i -e conf -e yaml -e yml -e json -e toml 'nginx|proxy|server|docker' . 2>/dev/null | head -20 || true
find . -maxdepth 3 -type f \( -name 'nginx.conf' -o -name 'docker-compose*' -o -name 'Dockerfile' -o -name '.env*' -o -name 'proxy*' \) 2>/dev/null | head -10 || true

echo
echo "== Check if there's a /api prefix wrapping the entire router ===="
rg -n --type=rust 'nest.*\.route\(.*"/api' clients/agent-runtime/src/gateway/mod.rs || echo "No /api prefix wrapping found"
rg -n --type=rust 'Router::.*web/admin' clients/agent-runtime/src/gateway/mod.rs | head -5 || true

Repository: dallay/corvus

Length of output: 933


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Check docker-compose.yml for reverse proxy config ===="
for f in ./clients/agent-runtime/docker-compose.yml ./dev/docker-compose.yml; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    cat "$f"
    echo
  fi
done

echo
echo "== Check if dashboard tests pass with /api/web/admin/* paths ===="
rg -n --type=ts 'web/admin/(options|channels|scheduler|health|memory)' clients/web/apps/dashboard/src --context=2 | head -20 || echo "No references found"
</parameter>

Repository: dallay/corvus

Length of output: 6477


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Check Caddyfile for reverse proxy rewrite rules ===="
if [ -f "./dev/Caddyfile.active" ]; then
  cat ./dev/Caddyfile.active
else
  echo "Caddyfile.active not found; searching for Caddyfile variants:"
  find ./dev -name 'Caddyfile*' -type f 2>/dev/null
fi

echo
echo "== Check how gatewayUrl() function constructs paths ===="
rg -n --type=ts 'gatewayUrl.*=' clients/web/apps/dashboard/src/composables/useConfig.ts -A 5 | head -30

echo
echo "== Verify if /api/web/admin/memory endpoint works in production setup ===="
rg -n --type=ts 'gatewayUrl.*web/admin' clients/web/apps/dashboard/src --context=1 | head -15
</parameter>

Repository: dallay/corvus

Length of output: 282


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Check Caddyfile variants for reverse proxy setup ===="
for f in ./dev/Caddyfile.dashboard ./dev/Caddyfile.landing; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    cat "$f"
    echo
  fi
done

echo
echo "== Check useConfig.ts gatewayUrl implementation ===="
rg -n --type=ts 'gatewayUrl' clients/web/apps/dashboard/src/composables/useConfig.ts -B 2 -A 10 | head -50

echo
echo "== Check how endpoints call gatewayUrl with /web/admin paths ===="
rg -n --type=ts 'gatewayUrl\("\/web\/admin' clients/web/apps/dashboard/src -A 1 | head -20

Repository: dallay/corvus

Length of output: 2637


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Verify gatewayUrl() with /web/admin endpoint calls ===="
rg -n --type=ts 'gatewayUrl\(["\x27]/web/admin' clients/web/apps/dashboard/src -A 1

echo
echo "== Check production docker-compose for any reverse proxy layer ===="
head -80 ./clients/agent-runtime/docker-compose.yml

echo
echo "== Search for any nginx, traefik, or other proxy config in repo ===="
fd -i -e conf -e yaml -e yml 'nginx|traefik|proxy' . 2>/dev/null || true

echo
echo "== Check if dashboard connects directly or through /api proxy ===="
rg -n --type=ts 'baseUrl|gatewayUrl|/api' clients/web/apps/dashboard/src/composables/useConfig.ts | head -20
</parameter>

Repository: dallay/corvus

Length of output: 3514


Route-contract mismatch: /api/web/admin/cerebro/* requests will fail in production.

Gateway mounts endpoints at /web/admin/cerebro/* (no /api prefix). Dashboard constructs URLs via gatewayUrl("/web/admin/cerebro/...") with default baseUrl of /api, resulting in calls to /api/web/admin/cerebro/*.

In the dev environment, Caddy's handle_path /api/* rule strips the prefix before proxying to the gateway, masking the mismatch. The production docker-compose lacks this reverse proxy layer—dashboard and gateway communicate directly on ports 4324 and 3000 respectively with no rewrite. All requests to these endpoints will 404.

This affects all /web/admin/* routes (options, channels, scheduler, health, memory, cerebro). Either mount routes at /api/web/admin/* in the gateway, or ensure a reverse proxy layer is documented as required for all deployments.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/gateway/mod.rs` around lines 1333 - 1381, The
gateway registers admin routes under "/web/admin/cerebro/*" which will 404 when
the frontend requests "/api/web/admin/cerebro/*"; update the route mounts to
include the "/api" prefix (e.g., change "/web/admin/cerebro/status" ->
"/api/web/admin/cerebro/status" for all routes shown) so handlers like
cerebro::handle_admin_cerebro_status, cerebro::handle_admin_cerebro_search,
cerebro::handle_admin_cerebro_observation,
cerebro::handle_admin_cerebro_timeline, cerebro::handle_admin_cerebro_stats,
cerebro::handle_admin_cerebro_create_memory,
cerebro::handle_admin_cerebro_update_memory,
cerebro::handle_admin_cerebro_delete_memory,
cerebro::handle_admin_cerebro_prompt,
cerebro::handle_admin_cerebro_session_start,
cerebro::handle_admin_cerebro_session_end,
cerebro::handle_admin_cerebro_session_summary and
cerebro::handle_admin_cerebro_context are reachable in production;
alternatively, if you prefer not to change paths, add/document a required
reverse-proxy rewrite that strips "/api" before proxying to the gateway.

Comment on lines 285 to +286
let client = reqwest::Client::builder()
.timeout(Duration::from_millis(self.server.call_timeout_ms))
.timeout(Duration::from_millis(self.server.startup_timeout_ms))
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 | 🟠 Major

Tool calls use startup_timeout_ms but should use call_timeout_ms.

call_tool_http() uses startup_timeout_ms (line 286) for tool invocation, but tool calls should respect call_timeout_ms per the config semantics. This appears swapped with the discovery path.

🔧 Proposed fix
         let client = reqwest::Client::builder()
-            .timeout(Duration::from_millis(self.server.startup_timeout_ms))
+            .timeout(Duration::from_millis(self.server.call_timeout_ms))
             .build()
             .context("failed to build MCP HTTP client")?;
📝 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
let client = reqwest::Client::builder()
.timeout(Duration::from_millis(self.server.call_timeout_ms))
.timeout(Duration::from_millis(self.server.startup_timeout_ms))
let client = reqwest::Client::builder()
.timeout(Duration::from_millis(self.server.call_timeout_ms))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/tools/mcp/client.rs` around lines 285 - 286, In
call_tool_http in client.rs the reqwest client is using
self.server.startup_timeout_ms for the request timeout; change it to use
self.server.call_timeout_ms instead by replacing the timeout reference passed
into reqwest::Client::builder().timeout(...) so tool invocations use the
configured call timeout (keep the rest of the builder unchanged and ensure the
duration conversion matches existing Duration::from_millis usage).

Comment on lines +503 to +521
fn list_tools_http(&self) -> anyhow::Result<Vec<McpToolManifest>> {
if tokio::runtime::Handle::try_current().is_ok() {
let this = self.clone();
return std::thread::spawn(move || {
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.context("failed to create runtime for MCP HTTP discovery")?;
runtime.block_on(this.list_tools_http_async())
})
.join()
.map_err(|_| anyhow::anyhow!("MCP HTTP discovery worker thread panicked"))?;
}

let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.context("failed to create runtime for MCP HTTP discovery")?;
runtime.block_on(self.list_tools_http_async())
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.

🧹 Nitpick | 🔵 Trivial

Thread spawn for async runtime is correct but consider documenting the pattern.

Spawning a dedicated thread with a new runtime when already inside a Tokio context avoids blocking the executor. This is a valid pattern, but a brief comment explaining why would help future maintainers.

📝 Add clarifying comment
     fn list_tools_http(&self) -> anyhow::Result<Vec<McpToolManifest>> {
+        // If we're already in a Tokio runtime, spawn a dedicated thread with its own
+        // single-threaded runtime to avoid blocking the current executor.
         if tokio::runtime::Handle::try_current().is_ok() {
📝 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
fn list_tools_http(&self) -> anyhow::Result<Vec<McpToolManifest>> {
if tokio::runtime::Handle::try_current().is_ok() {
let this = self.clone();
return std::thread::spawn(move || {
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.context("failed to create runtime for MCP HTTP discovery")?;
runtime.block_on(this.list_tools_http_async())
})
.join()
.map_err(|_| anyhow::anyhow!("MCP HTTP discovery worker thread panicked"))?;
}
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.context("failed to create runtime for MCP HTTP discovery")?;
runtime.block_on(self.list_tools_http_async())
fn list_tools_http(&self) -> anyhow::Result<Vec<McpToolManifest>> {
// If we're already in a Tokio runtime, spawn a dedicated thread with its own
// single-threaded runtime to avoid blocking the current executor.
if tokio::runtime::Handle::try_current().is_ok() {
let this = self.clone();
return std::thread::spawn(move || {
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.context("failed to create runtime for MCP HTTP discovery")?;
runtime.block_on(this.list_tools_http_async())
})
.join()
.map_err(|_| anyhow::anyhow!("MCP HTTP discovery worker thread panicked"))?;
}
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.context("failed to create runtime for MCP HTTP discovery")?;
runtime.block_on(self.list_tools_http_async())
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/tools/mcp/client.rs` around lines 503 - 521, The
list_tools_http function currently spawns a dedicated thread and builds a new
current-thread Tokio runtime when tokio::runtime::Handle::try_current().is_ok()
to avoid blocking an existing Tokio executor; add a concise clarifying comment
above that conditional explaining the pattern and rationale (i.e., when already
inside a Tokio runtime we cannot block the executor so we create a new runtime
on a separate thread and block_on the async helper list_tools_http_async),
referencing the decision points: tokio::runtime::Handle::try_current,
std::thread::spawn, building a new runtime, and list_tools_http_async so future
maintainers understand why this non-obvious approach is used.

Comment on lines +32 to +33
<p v-if="!selected" class="helper">Pick a Cerebro result to inspect its full observation.</p>
<p v-else-if="admin.loadingBuckets.value.cerebroObservation" class="helper">Loading detail…</p>
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.

🧹 Nitpick | 🔵 Trivial

Consider adding aria-live to dynamic status messages.

The loading and helper messages update dynamically but lack accessibility attributes. Adding aria-live="polite" and role="status" would improve screen reader announcements.

♿ Proposed accessibility improvement
-    <p v-if="!selected" class="helper">Pick a Cerebro result to inspect its full observation.</p>
-    <p v-else-if="admin.loadingBuckets.value.cerebroObservation" class="helper">Loading detail…</p>
+    <p v-if="!selected" class="helper" aria-live="polite">Pick a Cerebro result to inspect its full observation.</p>
+    <p v-else-if="admin.loadingBuckets.value.cerebroObservation" class="helper" aria-live="polite" role="status">Loading detail…</p>
📝 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
<p v-if="!selected" class="helper">Pick a Cerebro result to inspect its full observation.</p>
<p v-else-if="admin.loadingBuckets.value.cerebroObservation" class="helper">Loading detail…</p>
<p v-if="!selected" class="helper" aria-live="polite">Pick a Cerebro result to inspect its full observation.</p>
<p v-else-if="admin.loadingBuckets.value.cerebroObservation" class="helper" aria-live="polite" role="status">Loading detail…</p>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@clients/web/apps/dashboard/src/components/memory/CerebroObservationDetail.vue`
around lines 32 - 33, The dynamic helper/loading <p> elements in
CerebroObservationDetail.vue (the <p v-if="!selected" class="helper"> and <p
v-else-if="admin.loadingBuckets.value.cerebroObservation" class="helper">)
should include accessibility attributes so screen readers announce updates; add
aria-live="polite" and role="status" to both elements (the helper and the
loading message) to ensure polite, programmatic announcement of content changes.

Comment on lines +63 to +68
<p
v-if="admin.cerebroStats.value && 'state' in admin.cerebroStats.value && admin.cerebroStats.value.state !== 'available'"
class="helper"
>
{{ admin.memoryStats.value.cerebro_configured
? t("memory.cerebroConfigured", "Configured")
: t("memory.cerebroNotConfigured", "Not configured") }}
</span>
<span class="stat-label">{{ t("memory.statCerebro", "Cerebro") }}</span>
</div>
{{ admin.cerebroStats.value.message }}
</p>
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.

🧹 Nitpick | 🔵 Trivial

Guard against missing stats property.

If admin.cerebroStats.value exists but has state !== 'available' and no message field, the template renders nothing. The conditional at line 64 checks for state, but if an unexpected shape arrives, line 67 could render undefined.

🛡️ Defensive fallback for message
-          {{ admin.cerebroStats.value.message }}
+          {{ admin.cerebroStats.value.message ?? "Remote stats are not currently available." }}
📝 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
<p
v-if="admin.cerebroStats.value && 'state' in admin.cerebroStats.value && admin.cerebroStats.value.state !== 'available'"
class="helper"
>
{{ admin.memoryStats.value.cerebro_configured
? t("memory.cerebroConfigured", "Configured")
: t("memory.cerebroNotConfigured", "Not configured") }}
</span>
<span class="stat-label">{{ t("memory.statCerebro", "Cerebro") }}</span>
</div>
{{ admin.cerebroStats.value.message }}
</p>
<p
v-if="admin.cerebroStats.value && 'state' in admin.cerebroStats.value && admin.cerebroStats.value.state !== 'available'"
class="helper"
>
{{ admin.cerebroStats.value.message ?? "Remote stats are not currently available." }}
</p>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/web/apps/dashboard/src/components/memory/MemoryStats.vue` around
lines 63 - 68, The template assumes admin.cerebroStats.value has a message when
state !== 'available', which can render undefined; update the render logic in
MemoryStats.vue to guard the message by checking for 'message' in
admin.cerebroStats.value (or using a fallback) before printing it—i.e., change
the v-if to require either a message property or use a safe expression for {{
admin.cerebroStats.value.message }} (fallback string or empty string) so
admin.cerebroStats.value.state and admin.cerebroStats.value.message are both
validated before rendering.

Comment on lines +44 to +47
await admin.invokeCerebroSessionAction(
tool as "mem_session_start" | "mem_session_end" | "mem_session_summary",
props.sessionId
);
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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how invokeCerebroSessionAction handles sessionId for mem_session_start
rg -n -A 10 "mem_session_start" clients/web/apps/dashboard/src/composables/useAdmin.ts

Repository: dallay/corvus

Length of output: 1063


🌐 Web query:

mem_session_start Cerebro API documentation sessionId parameter

💡 Result:

In the Engram/Cerebro “memory” API, the sessionId you pass/see around mem_session_start is simply the unique identifier for the session record (sessions.id, a TEXT primary key) that all subsequent data is linked to via session_id foreign keys (e.g., observations, prompts). [1]

How it’s used in the docs:

  • HTTP API: creating a session is POST /sessions with body { id, project, directory }—so sessionId corresponds to the request body field id. Ending a session is POST /sessions/{id}/end. [2]
  • Plugin integrations (example: OpenCode): the session identifier is provided by the host tool as input.sessionID and is used to (re)create/ensure the session exists. [3]

Practical rule: treat sessionId as an opaque, stable string for the lifetime of the conversation/coding run; reuse the same value when writing observations/prompts and when ending the session. [1][2]

Sources
[1] https://github.com/Gentleman-Programming/engram/blob/v1.11.0/DOCS.md
[2] https://github.com/Gentleman-Programming/engram/blob/v1.11.0/DOCS.md#http-api-endpoints
[3] https://github.com/Gentleman-Programming/engram/blob/v1.11.0/DOCS.md (OpenCode plugin input.sessionID description)


mem_session_start requires sessionId in request body, but current code never includes it in the payload.

The sessionId is passed to invokeCerebroSessionAction() but the function ignores it for mem_session_start—the sessionId is neither validated nor added to the request body. Per Cerebro API, mem_session_start (POST /sessions) expects the sessionId as the id field in the request body. The function should construct the payload as { id: sessionId } when tool === "mem_session_start".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.vue`
around lines 44 - 47, The invokeCerebroSessionAction call currently passes
sessionId but the implementation ignores it for "mem_session_start"; update the
invokeCerebroSessionAction function so that when tool === "mem_session_start" it
validates that sessionId is present and includes it in the POST payload as { id:
sessionId } (instead of omitting or sending an empty body), while preserving
existing handling for "mem_session_end" and "mem_session_summary"; locate the
logic that switches on tool (or builds the request body) and add the branch that
constructs and sends { id: sessionId } for "mem_session_start".

Comment on lines +230 to +278
.mockImplementationOnce(() =>
Promise.resolve(
new Response(
JSON.stringify({
service_state: "available",
tools: {
mem_search: { state: "available" },
mem_get_observation: { state: "available" },
mem_timeline: { state: "available" },
mem_stats: { state: "available" },
mem_save: { state: "available" },
mem_update: { state: "available" },
mem_delete: { state: "available" },
mem_save_prompt: { state: "not_implemented" },
mem_session_start: { state: "not_implemented" },
mem_session_end: { state: "not_implemented" },
mem_session_summary: { state: "not_implemented" },
mem_context: { state: "available" },
},
}),
{ status: 200, headers: { "Content-Type": "application/json" } }
)
)
)
.mockImplementationOnce(() => secondResponse.promise)
.mockImplementationOnce(() =>
Promise.resolve(
new Response(
JSON.stringify({
service_state: "available",
tools: {
mem_search: { state: "available" },
mem_get_observation: { state: "available" },
mem_timeline: { state: "available" },
mem_stats: { state: "available" },
mem_save: { state: "available" },
mem_update: { state: "available" },
mem_delete: { state: "available" },
mem_save_prompt: { state: "not_implemented" },
mem_session_start: { state: "not_implemented" },
mem_session_end: { state: "not_implemented" },
mem_session_summary: { state: "not_implemented" },
mem_context: { state: "available" },
},
}),
{ status: 200, headers: { "Content-Type": "application/json" } }
)
)
);
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.

🧹 Nitpick | 🔵 Trivial

Deduplicate the repeated Cerebro tool-status fixture.

The same large service_state/tools payload is repeated across tests, which is brittle when tool inventory changes. Extract a shared helper/constant and reuse it in both mockDetailResponse and out-of-order setups.

♻️ Suggested refactor
+const CEREBRO_STATUS_AVAILABLE = {
+  service_state: "available",
+  tools: {
+    mem_search: { state: "available" },
+    mem_get_observation: { state: "available" },
+    mem_timeline: { state: "available" },
+    mem_stats: { state: "available" },
+    mem_save: { state: "available" },
+    mem_update: { state: "available" },
+    mem_delete: { state: "available" },
+    mem_save_prompt: { state: "not_implemented" },
+    mem_session_start: { state: "not_implemented" },
+    mem_session_end: { state: "not_implemented" },
+    mem_session_summary: { state: "not_implemented" },
+    mem_context: { state: "available" },
+  },
+} as const;
+
 function mockDetailResponse(detail: Record<string, unknown>) {
   fetchMock
     .mockResolvedValueOnce(/* session detail response */)
     .mockResolvedValueOnce(
-      new Response(JSON.stringify({ service_state: "available", tools: { ... } }), ...)
+      new Response(JSON.stringify(CEREBRO_STATUS_AVAILABLE), ...)
     );
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/web/apps/dashboard/src/components/sessions/SessionDetail.spec.ts`
around lines 230 - 278, The repeated large service_state/tools JSON should be
moved to a single shared fixture (e.g., const CEREBRO_TOOL_STATUS or a helper
getCerebroToolStatus()) and reused wherever the tests currently inline that
payload (the mockImplementationOnce responses, the mockDetailResponse and the
out-of-order setups referencing secondResponse.promise). Replace the duplicated
JSON blocks in the mockImplementationOnce calls with the shared constant/helper,
define it near the top of SessionDetail.spec.ts so all mocks (including any uses
of secondResponse.promise and mockDetailResponse) reference the same object to
avoid brittleness when tool inventory changes.

Comment on lines +390 to +395
mem_save_prompt: { state: "not_implemented" },
mem_session_start: { state: "not_implemented" },
mem_session_end: { state: "not_implemented" },
mem_session_summary: { state: "not_implemented" },
mem_context: { state: "not_implemented" },
},
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

Use a contract-valid mem_context state in the available-status fixture.

In an available status fixture, setting mem_context to not_implemented can hide classification regressions; this tool is treated as a normal allowlisted capability in this change set.

Suggested fixture adjustment
-              mem_context: { state: "not_implemented" },
+              mem_context: { state: "available" },
📝 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
mem_save_prompt: { state: "not_implemented" },
mem_session_start: { state: "not_implemented" },
mem_session_end: { state: "not_implemented" },
mem_session_summary: { state: "not_implemented" },
mem_context: { state: "not_implemented" },
},
mem_save_prompt: { state: "not_implemented" },
mem_session_start: { state: "not_implemented" },
mem_session_end: { state: "not_implemented" },
mem_session_summary: { state: "not_implemented" },
mem_context: { state: "available" },
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/web/apps/dashboard/src/composables/useAdmin.spec.ts` around lines 390
- 395, The available-status fixture currently sets mem_context to
"not_implemented", which violates the contract; update the fixture so
mem_context uses a contract-valid state (e.g., "available" or the canonical
allowed state used elsewhere) instead of "not_implemented". Locate the fixture
object where mem_context is declared (the available-status / available fixture
around the mem_* entries such as mem_save_prompt, mem_session_start,
mem_session_end, mem_session_summary) and replace the mem_context value with the
appropriate allowlisted state used by other tests to ensure classification
behaves correctly.

Comment on lines +106 to +118
export interface AdminCerebroStatsData {
memory_count: number;
session_count: number;
prompt_count: number;
worker_enabled: boolean;
worker_queue_depth: number;
[key: string]: unknown;
}

export interface AdminCerebroStatsResponse {
state: "available";
stats: AdminCerebroStatsData;
}
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how stats are consumed in dashboard components
rg -n "cerebroStats" --type=vue --type=ts -C 3 clients/web/apps/dashboard/src/

Repository: dallay/corvus

Length of output: 84


🏁 Script executed:

rg -n "AdminCerebroStats|cerebroStats" --type=ts --type=tsx -C 3 clients/web/apps/dashboard/src/

Repository: dallay/corvus

Length of output: 84


🏁 Script executed:

# Also search for how the stats fields are accessed
rg -n "stats\.(memory_count|session_count|prompt_count|worker_enabled|worker_queue_depth)" --type=ts --type=tsx clients/web/apps/dashboard/src/

Repository: dallay/corvus

Length of output: 84


🏁 Script executed:

# Find where AdminCerebroStatsResponse is imported and used
fd -e ts -e tsx clients/web/apps/dashboard/src/ | xargs grep -l "AdminCerebroStatsResponse"

Repository: dallay/corvus

Length of output: 488


🏁 Script executed:

rg -n "AdminCerebroStats" clients/web/apps/dashboard/src/

Repository: dallay/corvus

Length of output: 840


🏁 Script executed:

rg -n "memory_count|session_count|prompt_count|worker_enabled|worker_queue_depth" clients/web/apps/dashboard/src/

Repository: dallay/corvus

Length of output: 1692


🏁 Script executed:

fd --full-path 'clients/web/apps/dashboard/src/.*\.ts$' | head -20

Repository: dallay/corvus

Length of output: 1495


🏁 Script executed:

cat -n clients/web/apps/dashboard/src/composables/useAdmin.ts | sed -n '340,360p'

Repository: dallay/corvus

Length of output: 771


🏁 Script executed:

rg -n "fetchCerebroJson|cerebroStats" clients/web/apps/dashboard/src/composables/useAdmin.ts -A 5 -B 2 | head -40

Repository: dallay/corvus

Length of output: 1467


🏁 Script executed:

cat -n clients/web/apps/dashboard/src/composables/useAdmin.ts | sed -n '194,250p'

Repository: dallay/corvus

Length of output: 2219


🏁 Script executed:

rg -n "MemoryStats.vue" clients/web/apps/dashboard/src/components/memory/MemoryStats.vue -A 2 -B 2 | grep -E "(memory_count|session_count|prompt_count|worker_queue_depth|worker_enabled)"

Repository: dallay/corvus

Length of output: 39


🏁 Script executed:

cat -n clients/web/apps/dashboard/src/components/memory/MemoryStats.vue | sed -n '65,90p'

Repository: dallay/corvus

Length of output: 1464


🏁 Script executed:

rg -n "cerebroStats.value.stats" clients/web/apps/dashboard/src/components/memory/MemoryStats.vue

Repository: dallay/corvus

Length of output: 459


🏁 Script executed:

# Check if any optional chaining (?.) is used when accessing stats fields
rg -n "stats\?" clients/web/apps/dashboard/src/components/memory/MemoryStats.vue

Repository: dallay/corvus

Length of output: 39


Add field presence checks in MemoryStats.vue or use optional chaining for stats fields.

The component has a runtime guard for the stats object ('stats' in admin.cerebroStats.value), but then accesses fields directly without checking if they exist individually. If the backend omits memory_count, session_count, prompt_count, or worker_queue_depth, the template will render undefined instead of gracefully handling the missing data. Either add checks for each field or use optional chaining (?.) to prevent undefined values from appearing in the UI.

Also note: worker_enabled is defined in AdminCerebroStatsData but never used in the template—consider removing it if it's not needed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/web/apps/dashboard/src/types/admin-sessions.ts` around lines 106 -
118, The template in MemoryStats.vue currently assumes fields on
AdminCerebroStatsData exist; update the component to guard each stat access
(memory_count, session_count, prompt_count, worker_queue_depth) either by using
optional chaining (e.g., admin.cerebroStats.value?.stats?.memory_count) or
explicit presence checks before rendering and provide sensible fallbacks (0 or
'-' shown when missing) so undefined never appears; also remove the unused
worker_enabled property from AdminCerebroStatsData or stop exporting it if the
template doesn't use it.

@coderabbitai coderabbitai Bot added area:rust and removed area:vue labels Apr 9, 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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@clients/web/packages/locales/src/es.json`:
- Around line 313-315: The Spanish locale is missing the actions.delete key
which breaks parity with en.json; add an "actions.delete" entry to
clients/web/packages/locales/src/es.json alongside the existing "actions.close"
(use the correct Spanish label, e.g., "Eliminar" or preferred translation),
ensuring the actions object matches the English contract so UI fallback-to-key
is avoided.
🪄 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

Run ID: b764eeba-43ee-4de6-91de-ffadac342b55

📥 Commits

Reviewing files that changed from the base of the PR and between eea1fc5 and 3500dc7.

📒 Files selected for processing (3)
  • clients/web/apps/dashboard/src/composables/useCostGovernance.spec.ts
  • clients/web/packages/locales/src/en.json
  • clients/web/packages/locales/src/es.json
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: sonar
  • GitHub Check: Cloudflare Pages
🧰 Additional context used
📓 Path-based instructions (1)
**/*

⚙️ CodeRabbit configuration file

**/*: Security first, performance second.
Validate input boundaries, auth/authz implications, and secret management.
Look for behavioral regressions, missing tests, and contract breaks across modules.

Files:

  • clients/web/apps/dashboard/src/composables/useCostGovernance.spec.ts
  • clients/web/packages/locales/src/es.json
  • clients/web/packages/locales/src/en.json
🔇 Additional comments (7)
clients/web/apps/dashboard/src/composables/useCostGovernance.spec.ts (1)

269-274: Auth-header assertions are correctly hardened.

Normalizing mocked headers through Headers before checking Authorization makes these tests robust across header shapes while still enforcing the bearer-token contract for both admin POST calls.

clients/web/packages/locales/src/en.json (3)

313-344: Good i18n coverage for new navigation and session surfaces.

These additions are well-scoped and consistent with the new session/memory UI namespaces, reducing missing-translation fallback risk.


360-362: Clear fallback messaging for local memory mode.

The new memory copy communicates graceful degradation when Cerebro is unavailable, which aligns with the non-Cerebro compatibility objective.


384-384: pagination.perPage fills a useful UI text gap.

Nice additive key to complete pagination localization for list/table views.

clients/web/packages/locales/src/es.json (3)

316-344: nav and sessions additions look consistent and complete.

Good coverage of list/detail states, filters, sort labels, and memory-related session actions.


360-362: Local-memory fallback copy is clear and aligned with graceful degradation.

The kicker/title/helper messaging communicates behavior when Cerebro is unavailable.


384-384: Pagination label addition is correct.

pagination.perPage is properly localized and fits existing terminology.

Comment on lines +313 to +315
"actions": {
"close": "Cerrar"
},
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

Add missing actions.delete key to keep locale contracts aligned.

Line 313 introduces actions, but it only defines close. In clients/web/packages/locales/src/en.json (Line 316-343), actions.delete exists; missing it here can cause fallback-to-key behavior in Spanish UI.

Proposed fix
   "actions": {
-    "close": "Cerrar"
+    "close": "Cerrar",
+    "delete": "Eliminar"
   },

As per coding guidelines, "Look for behavioral regressions, missing tests, and contract breaks across modules."

📝 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
"actions": {
"close": "Cerrar"
},
"actions": {
"close": "Cerrar",
"delete": "Eliminar"
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/web/packages/locales/src/es.json` around lines 313 - 315, The Spanish
locale is missing the actions.delete key which breaks parity with en.json; add
an "actions.delete" entry to clients/web/packages/locales/src/es.json alongside
the existing "actions.close" (use the correct Spanish label, e.g., "Eliminar" or
preferred translation), ensuring the actions object matches the English contract
so UI fallback-to-key is avoided.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Apr 9, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
70.7% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@yacosta738 yacosta738 merged commit cd5838f into main Apr 9, 2026
11 of 13 checks passed
@yacosta738 yacosta738 deleted the feat/cerebro-memory-enhancement-layer branch April 9, 2026 16:44
@dallay-bot dallay-bot Bot mentioned this pull request Apr 9, 2026
@dallay-bot dallay-bot Bot mentioned this pull request May 3, 2026
@dallay-bot dallay-bot Bot mentioned this pull request May 6, 2026
This was referenced May 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cerebro memory enhancement layer for dashboard and gateway

1 participant