feat: cerebro memory enhancement layer#466
Conversation
📝 WalkthroughSummary by CodeRabbit
WalkthroughAdds 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
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: [...] }
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
✅ Contributor ReportUser: @yacosta738
Contributor Report evaluates based on public GitHub activity. Analysis period: 2025-04-09 to 2026-04-09 |
There was a problem hiding this comment.
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
📒 Files selected for processing (31)
clients/agent-runtime/src/gateway/cerebro.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/tools/mcp/cerebro.rsclients/agent-runtime/src/tools/mcp/client.rsclients/agent-runtime/src/tools/mcp/normalize.rsclients/web/apps/dashboard/src/App.vueclients/web/apps/dashboard/src/components/memory/CerebroObservationDetail.vueclients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.spec.tsclients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.vueclients/web/apps/dashboard/src/components/memory/CerebroStatusCard.vueclients/web/apps/dashboard/src/components/memory/CerebroTimelinePanel.vueclients/web/apps/dashboard/src/components/memory/MemoryList.vueclients/web/apps/dashboard/src/components/memory/MemoryStats.spec.tsclients/web/apps/dashboard/src/components/memory/MemoryStats.vueclients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.spec.tsclients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.vueclients/web/apps/dashboard/src/components/sessions/SessionDetail.spec.tsclients/web/apps/dashboard/src/components/sessions/SessionDetail.vueclients/web/apps/dashboard/src/composables/useAdmin.spec.tsclients/web/apps/dashboard/src/composables/useAdmin.tsclients/web/apps/dashboard/src/types/admin-sessions.tsopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/archive-report.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/design.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/proposal.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/specs/client-surfaces/spec.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/specs/memory-visibility/spec.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/state.yamlopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/tasks.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.mdopenspec/specs/client-surfaces/spec.mdopenspec/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.vueclients/web/apps/dashboard/src/components/sessions/SessionDetail.vueclients/web/apps/dashboard/src/components/memory/CerebroStatusCard.vueclients/web/apps/dashboard/src/components/memory/CerebroTimelinePanel.vueclients/web/apps/dashboard/src/components/memory/MemoryStats.vueclients/web/apps/dashboard/src/App.vueclients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.vueclients/web/apps/dashboard/src/components/memory/CerebroObservationDetail.vueclients/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.vueopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/state.yamlclients/web/apps/dashboard/src/components/sessions/SessionDetail.vueclients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.spec.tsclients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.spec.tsopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/archive-report.mdclients/web/apps/dashboard/src/composables/useAdmin.spec.tsclients/web/apps/dashboard/src/components/memory/CerebroStatusCard.vueclients/web/apps/dashboard/src/components/memory/MemoryStats.spec.tsopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/tasks.mdclients/web/apps/dashboard/src/components/memory/CerebroTimelinePanel.vueclients/agent-runtime/src/tools/mcp/client.rsclients/web/apps/dashboard/src/components/memory/MemoryStats.vueclients/agent-runtime/src/gateway/mod.rsclients/web/apps/dashboard/src/components/sessions/SessionDetail.spec.tsopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/proposal.mdclients/web/apps/dashboard/src/App.vueclients/agent-runtime/src/tools/mcp/cerebro.rsclients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.vueopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/design.mdclients/agent-runtime/src/tools/mcp/normalize.rsopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/specs/memory-visibility/spec.mdclients/web/apps/dashboard/src/components/memory/CerebroObservationDetail.vueclients/web/apps/dashboard/src/composables/useAdmin.tsopenspec/specs/client-surfaces/spec.mdclients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.vueopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/specs/client-surfaces/spec.mdclients/web/apps/dashboard/src/types/admin-sessions.tsopenspec/specs/memory-visibility/spec.mdclients/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.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/tasks.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/proposal.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/design.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/specs/memory-visibility/spec.mdopenspec/specs/client-surfaces/spec.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/specs/client-surfaces/spec.mdopenspec/specs/memory-visibility/spec.md
clients/agent-runtime/src/tools/**/*.rs
📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)
Implement
Tooltrait insrc/tools/with strict parameter schema, validate and sanitize all inputs, and return structuredToolResultwithout panics in runtime path
Files:
clients/agent-runtime/src/tools/mcp/client.rsclients/agent-runtime/src/tools/mcp/cerebro.rsclients/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.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/tools/mcp/cerebro.rsclients/agent-runtime/src/tools/mcp/normalize.rsclients/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.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/tools/mcp/cerebro.rsclients/agent-runtime/src/tools/mcp/normalize.rsclients/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, andcargo testfor code validation, or document which checks were skipped and why
Files:
clients/agent-runtime/src/tools/mcp/client.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/tools/mcp/cerebro.rsclients/agent-runtime/src/tools/mcp/normalize.rsclients/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.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/tools/mcp/cerebro.rsclients/agent-runtime/src/tools/mcp/normalize.rsclients/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.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/tools/mcp/cerebro.rsclients/agent-runtime/src/tools/mcp/normalize.rsclients/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.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.mdopenspec/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.rsclients/agent-runtime/src/tools/mcp/cerebro.rsclients/agent-runtime/src/tools/mcp/normalize.rsclients/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.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/tools/mcp/cerebro.rsclients/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.rsclients/agent-runtime/src/tools/mcp/normalize.rsclients/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.rsclients/agent-runtime/src/tools/mcp/normalize.rsclients/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.sessionDetailavoids unrelated loading flicker, and the newCerebroSessionActionsprops 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
unconfiguredstate 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"androle="status"on loading state.clients/agent-runtime/src/tools/mcp/cerebro.rs (2)
32-41: Good defensive JSON parsing with fallback.The
or_elsefallback wrapping raw response asValue::Stringhandles non-JSON tool outputs gracefully without panicking.
43-54: Configuration check is thorough.Properly trims whitespace and requires both
endpointandauth_tokento 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, androle="tablist"are properly applied for accessibility.
477-494: Cerebro layout correctly wires props and events.Components receive consistent
gatewayUrl,authHeaders, and shareselectedCerebroResultstate. The@selectevent 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_contextis 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.valueaccess 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.valueaccess. The component's patternadmin.loadingBuckets.value.cerebroTimelineandadmin.cerebroTimeline.valueis correct and necessary.> Likely an incorrect or invalid review comment.
| 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() | ||
| } |
There was a problem hiding this comment.
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.
| 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, | ||
| }) |
There was a problem hiding this comment.
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.
| #[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") | ||
| ); | ||
| } |
There was a problem hiding this comment.
🧹 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.
| let client = reqwest::Client::builder() | ||
| .timeout(Duration::from_millis(self.server.call_timeout_ms)) | ||
| .build() | ||
| .context("failed to build MCP HTTP client")?; |
There was a problem hiding this comment.
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.
| 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")?; |
There was a problem hiding this comment.
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.
| 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`; |
There was a problem hiding this comment.
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.
| ### 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. |
There was a problem hiding this comment.
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.
| ### 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 |
There was a problem hiding this comment.
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.
| ### 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`. |
There was a problem hiding this comment.
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.
| ## 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. |
There was a problem hiding this comment.
🧹 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).
Deploying corvus with
|
| 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 |
There was a problem hiding this comment.
Actionable comments posted: 11
♻️ Duplicate comments (3)
clients/web/apps/dashboard/src/App.vue (1)
115-119:⚠️ Potential issue | 🟡 MinorClear 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 | 🟡 MinorResolve 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 | 🟠 MajorDiscovery still uses
call_timeout_msinstead ofstartup_timeout_ms.The past review flagged that HTTP discovery should respect
startup_timeout_msto match the command-based discovery budget. Line 532 still usescall_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
📒 Files selected for processing (35)
clients/agent-runtime/src/gateway/cerebro.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/tools/mcp/cerebro.rsclients/agent-runtime/src/tools/mcp/client.rsclients/agent-runtime/src/tools/mcp/normalize.rsclients/web/apps/dashboard/src/App.vueclients/web/apps/dashboard/src/components/memory/CerebroObservationDetail.spec.tsclients/web/apps/dashboard/src/components/memory/CerebroObservationDetail.vueclients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.spec.tsclients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.vueclients/web/apps/dashboard/src/components/memory/CerebroStatusCard.vueclients/web/apps/dashboard/src/components/memory/CerebroTimelinePanel.spec.tsclients/web/apps/dashboard/src/components/memory/CerebroTimelinePanel.vueclients/web/apps/dashboard/src/components/memory/MemoryList.vueclients/web/apps/dashboard/src/components/memory/MemoryStats.spec.tsclients/web/apps/dashboard/src/components/memory/MemoryStats.vueclients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.spec.tsclients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.vueclients/web/apps/dashboard/src/components/sessions/SessionDetail.spec.tsclients/web/apps/dashboard/src/components/sessions/SessionDetail.vueclients/web/apps/dashboard/src/composables/useAdmin.spec.tsclients/web/apps/dashboard/src/composables/useAdmin.tsclients/web/apps/dashboard/src/types/admin-sessions.tsclients/web/packages/locales/src/en.jsonclients/web/packages/locales/src/es.jsonopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/archive-report.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/design.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/proposal.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/specs/client-surfaces/spec.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/specs/memory-visibility/spec.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/state.yamlopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/tasks.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.mdopenspec/specs/client-surfaces/spec.mdopenspec/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.tsclients/agent-runtime/src/tools/mcp/cerebro.rsclients/web/apps/dashboard/src/components/memory/CerebroTimelinePanel.spec.tsopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/state.yamlclients/web/apps/dashboard/src/components/memory/CerebroObservationDetail.spec.tsclients/web/apps/dashboard/src/components/sessions/SessionDetail.vueclients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.spec.tsclients/web/apps/dashboard/src/components/memory/MemoryStats.spec.tsopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/archive-report.mdclients/web/apps/dashboard/src/components/memory/CerebroStatusCard.vueclients/web/packages/locales/src/es.jsonclients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.spec.tsclients/web/packages/locales/src/en.jsonopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.mdclients/web/apps/dashboard/src/components/memory/CerebroTimelinePanel.vueclients/web/apps/dashboard/src/components/memory/CerebroObservationDetail.vueclients/web/apps/dashboard/src/components/memory/MemoryList.vueclients/agent-runtime/src/gateway/mod.rsclients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.vueopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/design.mdclients/agent-runtime/src/tools/mcp/client.rsclients/web/apps/dashboard/src/App.vueopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/proposal.mdclients/web/apps/dashboard/src/composables/useAdmin.spec.tsclients/web/apps/dashboard/src/components/memory/MemoryStats.vueclients/web/apps/dashboard/src/components/memory/CerebroSearchPanel.vueclients/agent-runtime/src/tools/mcp/normalize.rsopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/tasks.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/specs/client-surfaces/spec.mdopenspec/specs/client-surfaces/spec.mdclients/web/apps/dashboard/src/composables/useAdmin.tsopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/specs/memory-visibility/spec.mdopenspec/specs/memory-visibility/spec.mdclients/web/apps/dashboard/src/types/admin-sessions.tsclients/agent-runtime/src/gateway/cerebro.rs
clients/agent-runtime/src/tools/**/*.rs
📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)
Implement
Tooltrait insrc/tools/with strict parameter schema, validate and sanitize all inputs, and return structuredToolResultwithout panics in runtime path
Files:
clients/agent-runtime/src/tools/mcp/cerebro.rsclients/agent-runtime/src/tools/mcp/client.rsclients/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.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/tools/mcp/client.rsclients/agent-runtime/src/tools/mcp/normalize.rsclients/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.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/tools/mcp/client.rsclients/agent-runtime/src/tools/mcp/normalize.rsclients/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, andcargo testfor code validation, or document which checks were skipped and why
Files:
clients/agent-runtime/src/tools/mcp/cerebro.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/tools/mcp/client.rsclients/agent-runtime/src/tools/mcp/normalize.rsclients/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.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/tools/mcp/client.rsclients/agent-runtime/src/tools/mcp/normalize.rsclients/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.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/tools/mcp/client.rsclients/agent-runtime/src/tools/mcp/normalize.rsclients/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.vueclients/web/apps/dashboard/src/components/memory/CerebroStatusCard.vueclients/web/apps/dashboard/src/components/memory/CerebroTimelinePanel.vueclients/web/apps/dashboard/src/components/memory/CerebroObservationDetail.vueclients/web/apps/dashboard/src/components/memory/MemoryList.vueclients/web/apps/dashboard/src/components/sessions/CerebroSessionActions.vueclients/web/apps/dashboard/src/App.vueclients/web/apps/dashboard/src/components/memory/MemoryStats.vueclients/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.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/design.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/proposal.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/tasks.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/specs/client-surfaces/spec.mdopenspec/specs/client-surfaces/spec.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/specs/memory-visibility/spec.mdopenspec/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.rsclients/agent-runtime/src/tools/mcp/client.rsclients/agent-runtime/src/tools/mcp/normalize.rsclients/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.rsclients/agent-runtime/src/gateway/mod.rsopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/design.mdclients/agent-runtime/src/tools/mcp/client.rsclients/agent-runtime/src/tools/mcp/normalize.rsclients/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.rsopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/verify-report.mdclients/agent-runtime/src/gateway/mod.rsclients/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.mdopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/proposal.mdopenspec/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.mdclients/agent-runtime/src/gateway/mod.rsopenspec/changes/archive/2026-04-09-cerebro-memory-enhancement-layer/design.mdclients/agent-runtime/src/tools/mcp/client.rsclients/agent-runtime/src/tools/mcp/normalize.rsclients/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.mdclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/tools/mcp/client.rsclients/agent-runtime/src/tools/mcp/normalize.rsclients/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
Resulttypes properly, gracefully handles JSON parse failures by falling back to string value, and avoids panics in the runtime path. Based on learnings: "return structuredToolResultwithout panics in runtime path".
43-54: LGTM!Validation logic correctly requires both
endpointandauth_tokento 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, andmemorysections 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.sessionDetailinstead of the globalloading.value, ensuring the loading indicator is scoped to this specific request.
115-120: LGTM!
CerebroSessionActionsintegration 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, andmem_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
remoteToolStatein 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
timelineItemKeyare 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 showGET /cerebro/observations/:idandGET /cerebro/timeline/:id. Verify these match the actual router paths, as past comments indicated timeline isPOSTnotGET.#!/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
200responses and the implementation's use of503/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_idpath parameters.
226-237:⚠️ Potential issue | 🟡 MinorNormalization path diagram shows
200responses 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
fetchCerebroObservationin try/catch (lines 18-22), and the "Relationship Insights" section (lines 50-57) renders actualrelationshipsandontologydata 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 infetchCerebroStatus()orfetchCerebroStats()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
pendingActionsSet guards against duplicate in-flight requests, and thefinallyblocks ensure cleanup even on errors. The button's:disabledbinding (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 incall_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?.stateand?.message) at lines 47, 54, and 55, preventing potential TypeErrors whentoolsormem_searchis 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 toUnreachableis reasonable for unclassified network errors.
17-37: Allowlist and planned-tool sets are well-defined.The explicit
CEREBRO_GATEWAY_ALLOWLISTenforces a deny-by-default posture for tool exposure, andCEREBRO_PLANNED_TOOLSenables gracefulnot_implementedhandling 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 setsContent-Type: application/jsonfor JSON bodies.This resolves the previous issue where Fetch API defaulted to
text/plainfor string bodies, which Axum'sJsonextractor would reject.
194-225: LGTM -fetchCerebroJson()now clears stale errors at request start.Line 201 resets
error.value = nullbefore the fetch, matchingfetchJson()behavior and preventing stale error banners from persisting after successful requests.
403-426: LGTM -invokeCerebroSessionAction()validatessessionIdearly.Lines 408-412 now fail fast with a descriptive error when
sessionIdis missing formem_session_endormem_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 whetherservice_errorcontains 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_egresswith the actualoperation(Read vs Act), correctly gating write operations even ifinventory_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, andoptional_bool_fieldto validate Cerebro's response structure, returning502 BAD_GATEWAYfor 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_errorsexplicitly verifies thattool_status_mapproduces normalized messages even whenservice_errorcontains sensitive data like "upstream token leaked".
| 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())) | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
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.
| .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), | ||
| ) |
There was a problem hiding this comment.
🧩 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 || trueRepository: 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 || trueRepository: 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 -20Repository: 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.
| let client = reqwest::Client::builder() | ||
| .timeout(Duration::from_millis(self.server.call_timeout_ms)) | ||
| .timeout(Duration::from_millis(self.server.startup_timeout_ms)) |
There was a problem hiding this comment.
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.
| 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).
| 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()) |
There was a problem hiding this comment.
🧹 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.
| 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.
| <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> |
There was a problem hiding this comment.
🧹 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.
| <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.
| <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> |
There was a problem hiding this comment.
🧹 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.
| <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.
| await admin.invokeCerebroSessionAction( | ||
| tool as "mem_session_start" | "mem_session_end" | "mem_session_summary", | ||
| props.sessionId | ||
| ); |
There was a problem hiding this comment.
🧩 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.tsRepository: 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 /sessionswith body{ id, project, directory }—sosessionIdcorresponds to the request body fieldid. Ending a session isPOST /sessions/{id}/end. [2] - Plugin integrations (example: OpenCode): the session identifier is provided by the host tool as
input.sessionIDand 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".
| .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" } } | ||
| ) | ||
| ) | ||
| ); |
There was a problem hiding this comment.
🧹 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.
| 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" }, | ||
| }, |
There was a problem hiding this comment.
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.
| 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.
| 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; | ||
| } |
There was a problem hiding this comment.
🧩 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 -20Repository: 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 -40Repository: 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.vueRepository: 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.vueRepository: 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.
There was a problem hiding this comment.
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
📒 Files selected for processing (3)
clients/web/apps/dashboard/src/composables/useCostGovernance.spec.tsclients/web/packages/locales/src/en.jsonclients/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.tsclients/web/packages/locales/src/es.jsonclients/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
headersthroughHeadersbefore checkingAuthorizationmakes 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.perPagefills 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:navandsessionsadditions 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/helpermessaging communicates behavior when Cerebro is unavailable.
384-384: Pagination label addition is correct.
pagination.perPageis properly localized and fits existing terminology.
| "actions": { | ||
| "close": "Cerrar" | ||
| }, |
There was a problem hiding this comment.
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.
| "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.
|


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
cerebromodule to the gateway with multiple admin routes for Cerebro operations (status, search, observation, timeline, stats, memory CRUD, prompts, session management, and context) inmod.rs. [1] [2]cerebro.rsandclient.rs, including methods for tool listing, invocation, configuration checking, and error classification. [1] [2] [3] [4]Cerebro Gateway Tooling and Error Handling
CerebroGatewayStateenum and classification logic to distinguish between unconfigured, unreachable, unsupported, and not-implemented states for improved diagnostics and UX.Frontend Support for Cerebro Features
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