feat: progressive drill-down dashboard with RED metrics and SLA compliance (#94)
Three-level dashboard driven by sidebar selection:
- L1 (no selection): all-apps overview with health table, per-app charts
- L2 (app selected): route performance table, error velocity, top errors
- L3 (route selected): processor table, latency heatmap data, bottleneck KPI
Backend: 3 new endpoints (timeseries/by-app, timeseries/by-route, errors/top),
per-app SLA settings (app_settings table, V12 migration), exact SLA compliance
from executions hypertable, error velocity with acceleration detection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 23:29:20 +02:00
|
|
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
|
|
|
import { config } from '../../config';
|
|
|
|
|
import { useAuthStore } from '../../auth/auth-store';
|
|
|
|
|
import { useRefreshInterval } from './use-refresh-interval';
|
|
|
|
|
|
|
|
|
|
function authHeaders() {
|
|
|
|
|
const token = useAuthStore.getState().accessToken;
|
|
|
|
|
return {
|
|
|
|
|
Authorization: `Bearer ${token}`,
|
|
|
|
|
'X-Cameleer-Protocol-Version': '1',
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function fetchJson<T>(path: string, params?: Record<string, string | undefined>): Promise<T> {
|
|
|
|
|
const qs = new URLSearchParams();
|
|
|
|
|
if (params) {
|
|
|
|
|
for (const [k, v] of Object.entries(params)) {
|
|
|
|
|
if (v != null) qs.set(k, v);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const url = `${config.apiBaseUrl}${path}${qs.toString() ? `?${qs}` : ''}`;
|
|
|
|
|
const res = await fetch(url, { headers: authHeaders() });
|
|
|
|
|
if (!res.ok) throw new Error(`Failed to fetch ${path}`);
|
|
|
|
|
return res.json();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── Timeseries by app (L1 charts) ─────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
export interface TimeseriesBucket {
|
|
|
|
|
time: string;
|
|
|
|
|
totalCount: number;
|
|
|
|
|
failedCount: number;
|
|
|
|
|
avgDurationMs: number;
|
|
|
|
|
p99DurationMs: number;
|
|
|
|
|
activeCount: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface GroupedTimeseries {
|
|
|
|
|
[key: string]: { buckets: TimeseriesBucket[] };
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 15:42:26 +02:00
|
|
|
export function useTimeseriesByApp(from?: string, to?: string, environment?: string) {
|
feat: progressive drill-down dashboard with RED metrics and SLA compliance (#94)
Three-level dashboard driven by sidebar selection:
- L1 (no selection): all-apps overview with health table, per-app charts
- L2 (app selected): route performance table, error velocity, top errors
- L3 (route selected): processor table, latency heatmap data, bottleneck KPI
Backend: 3 new endpoints (timeseries/by-app, timeseries/by-route, errors/top),
per-app SLA settings (app_settings table, V12 migration), exact SLA compliance
from executions hypertable, error velocity with acceleration detection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 23:29:20 +02:00
|
|
|
const refetchInterval = useRefreshInterval(30_000);
|
|
|
|
|
return useQuery({
|
2026-04-04 15:42:26 +02:00
|
|
|
queryKey: ['dashboard', 'timeseries-by-app', from, to, environment],
|
feat: progressive drill-down dashboard with RED metrics and SLA compliance (#94)
Three-level dashboard driven by sidebar selection:
- L1 (no selection): all-apps overview with health table, per-app charts
- L2 (app selected): route performance table, error velocity, top errors
- L3 (route selected): processor table, latency heatmap data, bottleneck KPI
Backend: 3 new endpoints (timeseries/by-app, timeseries/by-route, errors/top),
per-app SLA settings (app_settings table, V12 migration), exact SLA compliance
from executions hypertable, error velocity with acceleration detection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 23:29:20 +02:00
|
|
|
queryFn: () => fetchJson<GroupedTimeseries>('/search/stats/timeseries/by-app', {
|
2026-04-04 15:42:26 +02:00
|
|
|
from, to, buckets: '24', environment,
|
feat: progressive drill-down dashboard with RED metrics and SLA compliance (#94)
Three-level dashboard driven by sidebar selection:
- L1 (no selection): all-apps overview with health table, per-app charts
- L2 (app selected): route performance table, error velocity, top errors
- L3 (route selected): processor table, latency heatmap data, bottleneck KPI
Backend: 3 new endpoints (timeseries/by-app, timeseries/by-route, errors/top),
per-app SLA settings (app_settings table, V12 migration), exact SLA compliance
from executions hypertable, error velocity with acceleration detection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 23:29:20 +02:00
|
|
|
}),
|
|
|
|
|
enabled: !!from,
|
|
|
|
|
placeholderData: (prev: GroupedTimeseries | undefined) => prev,
|
|
|
|
|
refetchInterval,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── Timeseries by route (L2 charts) ───────────────────────────────────
|
|
|
|
|
|
2026-04-04 15:42:26 +02:00
|
|
|
export function useTimeseriesByRoute(from?: string, to?: string, application?: string, environment?: string) {
|
feat: progressive drill-down dashboard with RED metrics and SLA compliance (#94)
Three-level dashboard driven by sidebar selection:
- L1 (no selection): all-apps overview with health table, per-app charts
- L2 (app selected): route performance table, error velocity, top errors
- L3 (route selected): processor table, latency heatmap data, bottleneck KPI
Backend: 3 new endpoints (timeseries/by-app, timeseries/by-route, errors/top),
per-app SLA settings (app_settings table, V12 migration), exact SLA compliance
from executions hypertable, error velocity with acceleration detection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 23:29:20 +02:00
|
|
|
const refetchInterval = useRefreshInterval(30_000);
|
|
|
|
|
return useQuery({
|
2026-04-04 15:42:26 +02:00
|
|
|
queryKey: ['dashboard', 'timeseries-by-route', from, to, application, environment],
|
feat: progressive drill-down dashboard with RED metrics and SLA compliance (#94)
Three-level dashboard driven by sidebar selection:
- L1 (no selection): all-apps overview with health table, per-app charts
- L2 (app selected): route performance table, error velocity, top errors
- L3 (route selected): processor table, latency heatmap data, bottleneck KPI
Backend: 3 new endpoints (timeseries/by-app, timeseries/by-route, errors/top),
per-app SLA settings (app_settings table, V12 migration), exact SLA compliance
from executions hypertable, error velocity with acceleration detection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 23:29:20 +02:00
|
|
|
queryFn: () => fetchJson<GroupedTimeseries>('/search/stats/timeseries/by-route', {
|
2026-04-04 15:42:26 +02:00
|
|
|
from, to, application, buckets: '24', environment,
|
feat: progressive drill-down dashboard with RED metrics and SLA compliance (#94)
Three-level dashboard driven by sidebar selection:
- L1 (no selection): all-apps overview with health table, per-app charts
- L2 (app selected): route performance table, error velocity, top errors
- L3 (route selected): processor table, latency heatmap data, bottleneck KPI
Backend: 3 new endpoints (timeseries/by-app, timeseries/by-route, errors/top),
per-app SLA settings (app_settings table, V12 migration), exact SLA compliance
from executions hypertable, error velocity with acceleration detection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 23:29:20 +02:00
|
|
|
}),
|
|
|
|
|
enabled: !!from && !!application,
|
|
|
|
|
placeholderData: (prev: GroupedTimeseries | undefined) => prev,
|
|
|
|
|
refetchInterval,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── Top errors (L2/L3) ────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
export interface TopError {
|
|
|
|
|
errorType: string;
|
|
|
|
|
routeId: string | null;
|
|
|
|
|
processorId: string | null;
|
|
|
|
|
count: number;
|
|
|
|
|
velocity: number;
|
|
|
|
|
trend: 'accelerating' | 'stable' | 'decelerating';
|
|
|
|
|
lastSeen: string;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 15:42:26 +02:00
|
|
|
export function useTopErrors(from?: string, to?: string, application?: string, routeId?: string, environment?: string) {
|
feat: progressive drill-down dashboard with RED metrics and SLA compliance (#94)
Three-level dashboard driven by sidebar selection:
- L1 (no selection): all-apps overview with health table, per-app charts
- L2 (app selected): route performance table, error velocity, top errors
- L3 (route selected): processor table, latency heatmap data, bottleneck KPI
Backend: 3 new endpoints (timeseries/by-app, timeseries/by-route, errors/top),
per-app SLA settings (app_settings table, V12 migration), exact SLA compliance
from executions hypertable, error velocity with acceleration detection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 23:29:20 +02:00
|
|
|
const refetchInterval = useRefreshInterval(10_000);
|
|
|
|
|
return useQuery({
|
2026-04-04 15:42:26 +02:00
|
|
|
queryKey: ['dashboard', 'top-errors', from, to, application, routeId, environment],
|
feat: progressive drill-down dashboard with RED metrics and SLA compliance (#94)
Three-level dashboard driven by sidebar selection:
- L1 (no selection): all-apps overview with health table, per-app charts
- L2 (app selected): route performance table, error velocity, top errors
- L3 (route selected): processor table, latency heatmap data, bottleneck KPI
Backend: 3 new endpoints (timeseries/by-app, timeseries/by-route, errors/top),
per-app SLA settings (app_settings table, V12 migration), exact SLA compliance
from executions hypertable, error velocity with acceleration detection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 23:29:20 +02:00
|
|
|
queryFn: () => fetchJson<TopError[]>('/search/errors/top', {
|
2026-04-04 15:42:26 +02:00
|
|
|
from, to, application, routeId, limit: '5', environment,
|
feat: progressive drill-down dashboard with RED metrics and SLA compliance (#94)
Three-level dashboard driven by sidebar selection:
- L1 (no selection): all-apps overview with health table, per-app charts
- L2 (app selected): route performance table, error velocity, top errors
- L3 (route selected): processor table, latency heatmap data, bottleneck KPI
Backend: 3 new endpoints (timeseries/by-app, timeseries/by-route, errors/top),
per-app SLA settings (app_settings table, V12 migration), exact SLA compliance
from executions hypertable, error velocity with acceleration detection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 23:29:20 +02:00
|
|
|
}),
|
|
|
|
|
enabled: !!from,
|
|
|
|
|
placeholderData: (prev: TopError[] | undefined) => prev,
|
|
|
|
|
refetchInterval,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-30 10:26:26 +02:00
|
|
|
// ── Punchcard (weekday x hour heatmap, rolling 7 days) ────────────────
|
|
|
|
|
|
|
|
|
|
export interface PunchcardCell {
|
|
|
|
|
weekday: number;
|
|
|
|
|
hour: number;
|
|
|
|
|
totalCount: number;
|
|
|
|
|
failedCount: number;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 15:42:26 +02:00
|
|
|
export function usePunchcard(application?: string, environment?: string) {
|
2026-03-30 10:26:26 +02:00
|
|
|
const refetchInterval = useRefreshInterval(60_000);
|
|
|
|
|
return useQuery({
|
2026-04-04 15:42:26 +02:00
|
|
|
queryKey: ['dashboard', 'punchcard', application, environment],
|
|
|
|
|
queryFn: () => fetchJson<PunchcardCell[]>('/search/stats/punchcard', { application, environment }),
|
2026-03-30 15:42:02 +02:00
|
|
|
placeholderData: (prev: PunchcardCell[] | undefined) => prev ?? [],
|
2026-03-30 10:26:26 +02:00
|
|
|
refetchInterval,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
feat: progressive drill-down dashboard with RED metrics and SLA compliance (#94)
Three-level dashboard driven by sidebar selection:
- L1 (no selection): all-apps overview with health table, per-app charts
- L2 (app selected): route performance table, error velocity, top errors
- L3 (route selected): processor table, latency heatmap data, bottleneck KPI
Backend: 3 new endpoints (timeseries/by-app, timeseries/by-route, errors/top),
per-app SLA settings (app_settings table, V12 migration), exact SLA compliance
from executions hypertable, error velocity with acceleration detection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 23:29:20 +02:00
|
|
|
// ── App settings ──────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
export interface AppSettings {
|
|
|
|
|
appId: string;
|
feat!: scope per-app config and settings by environment
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>
2026-04-16 22:25:21 +02:00
|
|
|
environment: string;
|
feat: progressive drill-down dashboard with RED metrics and SLA compliance (#94)
Three-level dashboard driven by sidebar selection:
- L1 (no selection): all-apps overview with health table, per-app charts
- L2 (app selected): route performance table, error velocity, top errors
- L3 (route selected): processor table, latency heatmap data, bottleneck KPI
Backend: 3 new endpoints (timeseries/by-app, timeseries/by-route, errors/top),
per-app SLA settings (app_settings table, V12 migration), exact SLA compliance
from executions hypertable, error velocity with acceleration detection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 23:29:20 +02:00
|
|
|
slaThresholdMs: number;
|
|
|
|
|
healthErrorWarn: number;
|
|
|
|
|
healthErrorCrit: number;
|
|
|
|
|
healthSlaWarn: number;
|
|
|
|
|
healthSlaCrit: number;
|
|
|
|
|
createdAt: string;
|
|
|
|
|
updatedAt: string;
|
|
|
|
|
}
|
|
|
|
|
|
feat!: scope per-app config and settings by environment
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>
2026-04-16 22:25:21 +02:00
|
|
|
export function useAppSettings(appId?: string, environment?: string) {
|
feat: progressive drill-down dashboard with RED metrics and SLA compliance (#94)
Three-level dashboard driven by sidebar selection:
- L1 (no selection): all-apps overview with health table, per-app charts
- L2 (app selected): route performance table, error velocity, top errors
- L3 (route selected): processor table, latency heatmap data, bottleneck KPI
Backend: 3 new endpoints (timeseries/by-app, timeseries/by-route, errors/top),
per-app SLA settings (app_settings table, V12 migration), exact SLA compliance
from executions hypertable, error velocity with acceleration detection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 23:29:20 +02:00
|
|
|
return useQuery({
|
feat!: move config & settings under /api/v1/environments/{envSlug}/...
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>
2026-04-16 23:33:25 +02:00
|
|
|
queryKey: ['app-settings', environment, appId],
|
feat!: scope per-app config and settings by environment
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>
2026-04-16 22:25:21 +02:00
|
|
|
queryFn: () => fetchJson<AppSettings>(
|
feat!: move config & settings under /api/v1/environments/{envSlug}/...
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>
2026-04-16 23:33:25 +02:00
|
|
|
`/environments/${encodeURIComponent(environment!)}/apps/${encodeURIComponent(appId!)}/settings`),
|
feat!: scope per-app config and settings by environment
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>
2026-04-16 22:25:21 +02:00
|
|
|
enabled: !!appId && !!environment,
|
feat: progressive drill-down dashboard with RED metrics and SLA compliance (#94)
Three-level dashboard driven by sidebar selection:
- L1 (no selection): all-apps overview with health table, per-app charts
- L2 (app selected): route performance table, error velocity, top errors
- L3 (route selected): processor table, latency heatmap data, bottleneck KPI
Backend: 3 new endpoints (timeseries/by-app, timeseries/by-route, errors/top),
per-app SLA settings (app_settings table, V12 migration), exact SLA compliance
from executions hypertable, error velocity with acceleration detection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 23:29:20 +02:00
|
|
|
staleTime: 60_000,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
feat!: scope per-app config and settings by environment
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>
2026-04-16 22:25:21 +02:00
|
|
|
export function useAllAppSettings(environment?: string) {
|
feat: progressive drill-down dashboard with RED metrics and SLA compliance (#94)
Three-level dashboard driven by sidebar selection:
- L1 (no selection): all-apps overview with health table, per-app charts
- L2 (app selected): route performance table, error velocity, top errors
- L3 (route selected): processor table, latency heatmap data, bottleneck KPI
Backend: 3 new endpoints (timeseries/by-app, timeseries/by-route, errors/top),
per-app SLA settings (app_settings table, V12 migration), exact SLA compliance
from executions hypertable, error velocity with acceleration detection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 23:29:20 +02:00
|
|
|
return useQuery({
|
feat!: scope per-app config and settings by environment
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>
2026-04-16 22:25:21 +02:00
|
|
|
queryKey: ['app-settings', 'all', environment],
|
|
|
|
|
queryFn: () => fetchJson<AppSettings[]>(
|
feat!: move config & settings under /api/v1/environments/{envSlug}/...
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>
2026-04-16 23:33:25 +02:00
|
|
|
`/environments/${encodeURIComponent(environment!)}/app-settings`),
|
feat!: scope per-app config and settings by environment
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>
2026-04-16 22:25:21 +02:00
|
|
|
enabled: !!environment,
|
feat: progressive drill-down dashboard with RED metrics and SLA compliance (#94)
Three-level dashboard driven by sidebar selection:
- L1 (no selection): all-apps overview with health table, per-app charts
- L2 (app selected): route performance table, error velocity, top errors
- L3 (route selected): processor table, latency heatmap data, bottleneck KPI
Backend: 3 new endpoints (timeseries/by-app, timeseries/by-route, errors/top),
per-app SLA settings (app_settings table, V12 migration), exact SLA compliance
from executions hypertable, error velocity with acceleration detection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 23:29:20 +02:00
|
|
|
staleTime: 60_000,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function useUpdateAppSettings() {
|
|
|
|
|
const queryClient = useQueryClient();
|
|
|
|
|
return useMutation({
|
feat!: scope per-app config and settings by environment
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>
2026-04-16 22:25:21 +02:00
|
|
|
mutationFn: async ({ appId, environment, settings }:
|
|
|
|
|
{ appId: string; environment: string; settings: Omit<AppSettings, 'appId' | 'createdAt' | 'updatedAt'> }) => {
|
|
|
|
|
const res = await fetch(
|
feat!: move config & settings under /api/v1/environments/{envSlug}/...
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>
2026-04-16 23:33:25 +02:00
|
|
|
`${config.apiBaseUrl}/environments/${encodeURIComponent(environment)}/apps/${encodeURIComponent(appId)}/settings`,
|
feat!: scope per-app config and settings by environment
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>
2026-04-16 22:25:21 +02:00
|
|
|
{
|
|
|
|
|
method: 'PUT',
|
|
|
|
|
headers: { ...authHeaders(), 'Content-Type': 'application/json' },
|
|
|
|
|
body: JSON.stringify(settings),
|
|
|
|
|
});
|
feat: progressive drill-down dashboard with RED metrics and SLA compliance (#94)
Three-level dashboard driven by sidebar selection:
- L1 (no selection): all-apps overview with health table, per-app charts
- L2 (app selected): route performance table, error velocity, top errors
- L3 (route selected): processor table, latency heatmap data, bottleneck KPI
Backend: 3 new endpoints (timeseries/by-app, timeseries/by-route, errors/top),
per-app SLA settings (app_settings table, V12 migration), exact SLA compliance
from executions hypertable, error velocity with acceleration detection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 23:29:20 +02:00
|
|
|
if (!res.ok) throw new Error('Failed to update app settings');
|
|
|
|
|
return res.json();
|
|
|
|
|
},
|
|
|
|
|
onSuccess: () => {
|
|
|
|
|
queryClient.invalidateQueries({ queryKey: ['app-settings'] });
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|