Extends the previous describeApiError rollout to the rest of the UI.
Two symptom classes covered:
- Bare e.message / err.message in toast descriptions would render
"undefined" on Spring error bodies (plain objects without a proper
Error prototype). Affected: OidcConfigPage (save/test/delete),
ClaimMappingRulesModal (save + test), AgentHealth (dismiss),
RouteControlBar (route action + replay).
- Inline {String(error)} on load-failure banners would render
"[object Object]". Affected: InboxPage, RulesListPage, SilencesPage,
OutboundConnectionsPage.
Not touched: auth-store, AppsTab, UsersTab — they already guard with
`e instanceof Error` and fall back to a static string; replacing the
fallback with describeApiError would be a behavioral change best
evaluated separately.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Correlated exchanges always share the env of the one being viewed —
using the globally-selected env from the picker was wrong if the user
switched envs after opening a detail view (or arrived via permalink).
Thread `environment` through:
- `ExecutionStore.ExecutionRecord` gains `environment` field; the
ClickHouse `executions` table already stores this, just not read back.
- `ClickHouseExecutionStore.findById` SELECT adds the column; mapper
populates it.
- `ExecutionDetail` gains `environment`; `DetailService` passes through.
- `IngestionService.toExecutionRecord` passes null — this legacy PG
ingestion path isn't active when ClickHouse is enabled, and the
read-side is what drives the correlation UI.
- UI `ExchangeHeader` reads `detail.environment ?? storeEnv` and
extends the TS type locally (schema.d.ts catches up on next regen).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The env-scoping migration (P3A) changed useCorrelationChain to require an
environment arg and gate on `enabled: !!correlationId && !!environment`,
but ExchangeHeader was still calling it with one arg. Result: the query
never fired, so the header always rendered "no correlated exchanges
found" even when 4+ exchanges shared a correlationId.
Fix: read the selected env from the Zustand environment store and pass
it through.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
P3A of the taxonomy migration. Env-scoped config and settings endpoints
now live under the env-prefixed URL shape, making env a first-class
path segment instead of a query param. Agent-authoritative config is
split off into a dedicated endpoint so agent env comes from the JWT
only — never spoofable via URL.
Server:
- ApplicationConfigController: @RequestMapping("/api/v1/environments/
{envSlug}"). Handlers use @EnvPath Environment env, appSlug as
@PathVariable. Removed the dual-mode resolveEnvironmentForRead —
user flow only; agent flow moved to AgentConfigController.
- AgentConfigController (new): GET /api/v1/agents/config. Reads
instanceId from JWT subject, resolves (app, env) from registry,
returns AppConfigResponse. Registry miss → falls back to JWT env
claim for environment, but 404s if application cannot be derived
(no other source without registry).
- AppSettingsController: @RequestMapping("/api/v1/environments/
{envSlug}"). List at /app-settings, per-app at /apps/{appSlug}/
settings. Access class-wide PreAuthorize preserved (ADMIN/OPERATOR).
SPA:
- commands.ts: useAllApplicationConfigs, useApplicationConfig,
useUpdateApplicationConfig, useProcessorRouteMapping,
useTestExpression — rewritten URLs to /environments/{env}/apps/
{app}/... shape. environment now required on every call. Query
keys include environment so cache is env-scoped.
- dashboard.ts: useAppSettings, useAllAppSettings, useUpdateAppSettings
rewritten.
- TapConfigModal: new required environment prop; callers updated.
- RouteDetail, ExchangesPage: thread selectedEnv into test-expression
and modal.
Config changes in SecurityConfig for the new shape landed earlier in
P0.2; no security rule changes needed in this commit.
BREAKING CHANGE: /api/v1/config/** and /api/v1/admin/app-settings/**
paths removed. Agents must use /api/v1/agents/config instead of
GET /api/v1/config/{app}; users must use /api/v1/environments/{env}/
apps/{app}/config and /api/v1/environments/{env}/apps/{app}/settings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
BREAKING: wipe dev PostgreSQL before deploying — V1 checksum changes.
Agents must now send environmentId on registration (400 if missing).
Two tables previously keyed on app name alone caused cross-environment
data bleed: writing config for (app=X, env=dev) would overwrite the row
used by (app=X, env=prod) agents, and agent startup fetches ignored env
entirely.
- V1 schema: application_config and app_settings are now PK (app, env).
- Repositories: env-keyed finders/saves; env is the authoritative column,
stamped on the stored JSON so the row agrees with itself.
- ApplicationConfigController.getConfig is dual-mode — AGENT role uses
JWT env claim (agents cannot spoof env); non-agent callers provide env
via ?environment= query param.
- AppSettingsController endpoints now require ?environment=.
- SensitiveKeysAdminController fan-out iterates (app, env) slices so each
env gets its own merged keys.
- DiagramController ingestion stamps env on TaggedDiagram; ClickHouse
route_diagrams INSERT + findProcessorRouteMapping are env-scoped.
- AgentRegistrationController: environmentId is required on register;
removed all "default" fallbacks from register/refresh/heartbeat auto-heal.
- UI hooks (useApplicationConfig, useProcessorRouteMapping, useAppSettings,
useAllAppSettings, useUpdateAppSettings) take env, wired to
useEnvironmentStore at all call sites.
- New ConfigEnvIsolationIT covers env-isolation for both repositories.
Plan in docs/superpowers/plans/2026-04-16-environment-scoping.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- 7.1: Add deployment status badge (StatusDot + Badge) to AppsTab app
list, sourced from catalog.deployment.status via slug lookup
- 7.3: Add X close button to top-right of exchange detail right panel
in ExchangesPage (position:absolute, triggers handleClearSelection)
- 7.5: PunchcardHeatmap shows "Requires at least 2 days of data"
when timeRangeMs < 2 days; DashboardL1 passes the range down
- 7.6: Command palette exchange results truncate IDs to ...{last8}
matching the exchanges table display
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend: AgentRegistryService gains findByApplicationAndEnvironment()
and environment-aware addGroupCommandWithReplies() overload.
AgentCommandController and ApplicationConfigController accept optional
environment query parameter. When set, commands only target agents in
that environment. Backward compatible — null means all environments.
Frontend: All command mutations (config update, route control, traced
processors, tap config, route recording) now pass selectedEnv to the
backend via query parameter.
Prevents cross-environment command leakage — e.g., updating config for
prod no longer pushes to dev agents.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Consolidate 20+ duplicate function definitions across UI components into
three shared util files (format-utils, agent-utils, config-draft-utils).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Consolidate route catalog (agent-driven) and apps table (deployment-
driven) into a single GET /api/v1/catalog?environment={slug} endpoint.
Apps table is authoritative; agent data enriches with live health,
routes, and metrics. Unmanaged apps (agents without App record) appear
with managed=false.
- Add CatalogController merging App records + agent registry + ClickHouse
- Add CatalogApp DTO with deployment summary, managed flag, health
- Change AppController and DeploymentController to accept slugs (not UUIDs)
- Add AppRepository.findBySlug() and AppService.getBySlug()
- Replace useRouteCatalog() with useCatalog() across all UI components
- Navigate to /apps/{slug} instead of /apps/{UUID}
- Update sidebar, search, and all catalog lookups to use slug
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Logs functionality already exists in Runtime tab (AgentHealth/AgentInstance).
Config functionality moved to Deployments tab ConfigSubTab.
Old routes redirect to /runtime and /apps respectively.
Navigation links updated throughout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update @cameleer/design-system to v0.1.38 (12px minimum font size).
Replace all 10px and 11px font sizes with 12px across 25 CSS modules
and 5 TSX inline styles to match the new DS floor.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Hide Admin sidebar section for non-ADMIN users
- Add RequireAdmin route guard — /admin/* redirects to / for non-admin
- Move App Config from admin section to main Config tab (per-app,
visible when app selected). VIEWER sees read-only, OPERATOR+ can edit
- Hide diagram node toolbar for VIEWER (onNodeAction conditional)
- Add useIsAdmin/useCanControl helpers to centralize role checks
- Remove App Config from admin sidebar tree
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When no exchange is selected, the topology-only diagram now shows
the RouteControlBar above it (if the agent supports routeControl
or replay and the user has OPERATOR/ADMIN role). This fixes a gap
where suspended routes with no recent exchanges had no way to be
resumed from the UI.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
On first click, Dashboard was in non-split mode. The click set
selectedId locally then triggered split view, which remounted
Dashboard — losing the selectedId state.
Added activeExchangeId prop passed from ExchangesPage so the
selection survives the remount. Also syncs via useEffect when
parent changes selection (e.g. correlated exchange navigation).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Disabled buttons now show reduced opacity (0.35) and muted icon
color instead of just changing the cursor.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Buttons are disabled based on route state: Started disables
Start/Resume, Stopped disables Stop/Suspend/Resume, Suspended
disables Start/Suspend. State looked up from catalog API.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add CommandGroupResponse and ConfigUpdateResponse types. Switch
useSendGroupCommand and useSendRouteCommand from openapi-fetch to authFetch
returning CommandGroupResponse. Update useUpdateApplicationConfig to return
ConfigUpdateResponse and fix all consumer onSuccess callbacks to access
saved.config.version instead of saved.version.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Stop and suspend route commands now show a ConfirmDialog requiring
typed confirmation before dispatch. Start and resume execute
immediately without confirmation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The AgentInstanceResponse backend DTO uses instanceId, displayName,
applicationId, status — but the stale schema.d.ts still had id, name,
application, state. This caused the runtime table to show no data.
- Update schema.d.ts AgentInstanceResponse fields
- Fix AgentHealth: row.id→instanceId, row.name→displayName,
row.application→applicationId, inst.id→instanceId
- Fix AgentInstance: agent.id→instanceId, agent.name→displayName
- Fix ExchangeHeader: agent.id→instanceId, agent.state→status
- Fix LayoutShell search: agent.state→status, agentTps→tps
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The backend identity rename (applicationName → applicationId,
agentId → instanceId) was not reflected in the frontend. This caused
drilldown to fail (detail.applicationName was undefined, disabling
the diagram fetch) and various display issues.
Updated schema.d.ts, ExchangeHeader, ExecutionDiagram, Dashboard,
AgentHealth, AgentInstance, LayoutShell, LogTab, InfoTab, DetailPanel,
ExchangesPage, and tracing-store.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ExchangesPage ignored the exchangeId URL parameter, so selecting an
exchange from the command palette navigated to the right URL but never
displayed the execution overlay. Now derives selection from URL params
as fallback, and LayoutShell passes selectedExchange in state for
exchange/attribute results.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Detect replayed exchanges via X-Cameleer-Replay header during ingestion,
persist the flag through PostgreSQL and OpenSearch, and surface it in
the dashboard (amber replay icon) and exchange detail chain view.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Exchanges with a _replay attribute now display a small amber
RotateCcw icon between the status dot and route name in the
correlation chain. Tooltip also indicates (replay).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add dedicated POST /agents/{id}/replay endpoint that uses
addCommandWithReply to wait for the agent ACK (30s timeout).
Returns the actual replay result (status, message, data) instead
of just a delivery confirmation.
Frontend toast now reflects the agent's response: "Replay completed"
on success, agent error message on failure, timeout message if the
agent doesn't respond.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add ROUTE_CONTROL command type and route-control mapping in
AgentCommandController. New RouteControlBar component in the exchange
header shows Start/Stop/Suspend/Resume actions (grouped pill bar) and
a Replay button, gated by agent capabilities and OPERATOR/ADMIN role.
Fix useReplayExchange hook to match protocol section 16: payload now
uses { routeId, exchange: { body, headers }, originalExchangeId, nonce }
instead of the flat { headers, body } format.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Render hasTrace/hasTap/status badges inside the node card in both
raw diagram and overlay modes (consistent positioning)
- Pulse only on trace badge in overlay mode when hasTraceData is true
- Fix nodeConfigs to read tracedProcessors from appConfig instead of
never-synced tracing store
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Trace data visibility:
- ProcessorNode now includes hasTraceData flag computed from captured
body/headers during tree conversion
- ConfigBadge shows teal for tracing configured, green when data captured
- Search results show green footprints icon for exchanges with trace data
- New has_trace_data column on executions table (V11 migration with backfill)
- OpenSearch documents and ExecutionSummary include the flag
Inline tap configuration:
- Extracted reusable TapConfigModal component from RouteDetail
- Diagram context menu opens tap modal inline instead of navigating away
- Toggle-trace action works immediately with toast feedback
- Modal closes only on ESC, Cancel, Save, or Delete (not backdrop click)
Detail panel tab gating:
- Headers, Input, Output tabs disabled when no data is available
- Works at both exchange and processor level
- Falls back to Info tab when active tab becomes empty
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Server:
- Add endpointUri to PositionedNode (from RouteNode)
- Add fromEndpointUri to RouteSummary (catalog API)
- Catalog controller resolves endpoint URI from diagram store
UI:
- Build endpointRouteMap from catalog's fromEndpointUri field
- Drill-down uses exact match on node.endpointUri against the map
- Remove label parsing heuristics (extractTargetEndpoint, camelToKebab)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use Sidebar onNavigate callback instead of display:contents click interception
- Use DataTable fillHeight prop instead of manual scroll wrapper divs
- Fix DataTable scroll/pagination by adding overflow:hidden to content container
- Fix left panel in split view to use flex column instead of overflow:auto
- Make error tab stack trace scrollable for large traces
- Add nightly SonarQube workflow with manual trigger support
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Each exchange selection (from table or correlation chain) pushes a
browser history entry with the selected exchange in location.state.
When the user navigates away (to agent details, app scope, etc.) and
presses Back, the previous history entry is restored and the split
view with the diagram reappears exactly as they left it.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Row click no longer navigates to /exchanges/:app/:route/:id which was
changing the search scope. Instead, Dashboard calls onExchangeSelect
callback and ExchangesPage manages the selected exchange as local state.
The search criteria and scope are preserved when selecting an exchange.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>