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>
The toMap() method was missing the has_trace_data field, so it was
never indexed despite being read back in hitToSummary().
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>
The Dashboard was fetching 50 results without a status filter and
filtering client-side, causing fewer matches when filtering by error
compared to route-specific pages that filter server-side. Now passes
statusFilters to the OpenSearch query. Backend supports comma-separated
status values for multi-select filters.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add attributes_text flattened field to OpenSearch indexing for both
execution and processor levels. Include in full-text search queries,
wildcard matching, and highlighting. Merge processor-level attributes
into ExecutionSummary. Add 'attribute' category to CommandPalette
(design-system 0.1.17) with per-key-value results in the search UI.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DetailService deserializes attributes JSON from ExecutionRecord/ProcessorRecord and
passes them to ExecutionDetail and ProcessorNode constructors. ExecutionDocument and
ProcessorDoc carry attributes as a JSON string. SearchIndexer passes attributes when
building documents. OpenSearchIndex includes attributes in indexed maps and
deserializes them when constructing ExecutionSummary from search hits.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Defensive: use .keyword on the top-level exchangeId field too, in
case indices were created before the explicit keyword mapping was
added to the template.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dynamically mapped string fields in OpenSearch are multi-field
(text + keyword). Term queries require the .keyword sub-field for
exact matching.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Existing log records only have exchangeId inside the mdc object, not
as a top-level indexed field. Use a bool should clause to match on
either exchangeId (new records) or mdc.camel.exchangeId (old records).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Index exchangeId from Camel MDC (camel.exchangeId) as a top-level
keyword field in OpenSearch log indices. Add exchangeId filter to
the log query API and frontend hook. Show a LogViewer on the
ExchangeDetail page filtered to that exchange's logs, with search
input and level filter pills.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add GET /api/v1/logs endpoint to query application logs stored in
OpenSearch with filters for application, agent, level, time range,
and text search. Wire up the AgentInstance LogViewer with real data
and an EventFeed-style toolbar (search input + level filter pills).
Fix agent events timeline autoscroll by reversing the DESC-ordered
events so newest entries appear at the bottom where EventFeed
autoscrolls to.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
LogIndexService in server-core imported LogEntry from cameleer3-common,
but the SNAPSHOT on the registry may not have it yet when the server CI
runs. Moved the dependency to server-app where both the controller and
OpenSearch implementation live.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Agents can now send application log entries in batches via POST /api/v1/data/logs.
Logs are indexed directly into OpenSearch daily indices (logs-{yyyy-MM-dd}) using
the bulk API. Index template defines explicit mappings for full-text search readiness.
New DTOs (LogEntry, LogBatch) added to cameleer3-common in the agent repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add highlight field to ExecutionSummary record
- Request highlight fragments from OpenSearch when full-text search is active
- Pass matchContext to command palette for display
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add application_name filter to OpenSearch query builder — sidebar
app selection now correctly filters the exchange list. The
application field was being resolved to agentIds in the controller
but never applied as a query filter in OpenSearch.
Also restore snake_case sort column mapping since the OpenSearch
toMap() serializer uses snake_case field names (start_time, route_id,
etc.), not camelCase.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Consolidate V1-V7 Flyway migrations into single V1__init.sql with
all columns renamed from group_name to application_name. Requires
fresh database (wipe flyway_schema_history, all data).
- DB columns: executions.group_name → application_name,
processor_executions.group_name → application_name
- Continuous aggregates: all views updated to use application_name
- OpenSearch field: group_name → application_name in index/query
- All Java SQL strings updated to match new column names
- Delete V2-V7 migration files (folded into V1)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The execution-related "group" concept actually represents the
application name. Rename all Java fields, API parameters, and frontend
types from groupName→applicationName and group→application for clarity.
- Java records: ExecutionSummary, ExecutionDetail, ExecutionDocument,
ExecutionRecord, ProcessorRecord
- API params: SearchRequest.group→application, SearchController
@RequestParam group→application
- Services: IngestionService, DetailService, SearchIndexer, StatsStore
- Frontend: schema.d.ts, Dashboard, ExchangeDetail, RouteDetail,
executions query hooks
Database column names (group_name) and OpenSearch field names are
unchanged — only the API-facing Java/TS field names are renamed.
RBAC group references (groups table, GroupRepository, GroupsTab) are
a separate domain concept and are NOT affected by this change.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Added groupName field to ExecutionSummary Java record and OpenSearch mapper
- Dashboard stat cards use locale-formatted numbers (en-US)
- Added inspect column (↗) linking directly to exchange detail page
- Fixed duplicate React key warning from two columns sharing executionId key
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Enable deploying feature branches into isolated environments on the same
k3s cluster. Each branch gets its own namespace (cam-<slug>), PostgreSQL
schema, and OpenSearch index prefix for data isolation while sharing the
underlying infrastructure.
- Make OpenSearch index prefix and DB schema configurable via env vars
(defaults preserve existing behavior)
- Restructure deploy/ into Kustomize base + overlays (main/feature)
- Extend CI to build Docker images for all branches, not just main
- Add deploy-feature job with namespace creation, secret copying,
Traefik Ingress routing (<slug>-api/ui.cameleer.siegeln.net)
- Add cleanup-branch job to remove namespace, PG schema, OS indices
on branch deletion
- Install required tools (git, jq, curl) in CI deploy containers
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use singleton container pattern for PostgreSQL + OpenSearch testcontainers
(fixes container lifecycle issues with @TestInstance(PER_CLASS))
- Fix table name route_executions → executions in DetailControllerIT and
ExecutionControllerIT
- Serialize processor headers as JSON (ObjectMapper) instead of Map.toString()
for JSONB column compatibility
- Add nested mapping for processors field in OpenSearch index template
- Use .keyword sub-field for term queries on dynamically mapped text fields
- Add wildcard fallback queries for all text searches (substring matching)
- Isolate stats tests with unique route names to prevent data contamination
- Wait for OpenSearch indexing in SearchControllerIT with targeted Awaitility
- Reduce OpenSearch debounce to 100ms in test profile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Delete all ClickHouse storage implementations and config
- Delete old core interfaces (ExecutionRepository, DiagramRepository, MetricsRepository, SearchEngine, RawExecutionRow)
- Delete ClickHouse SQL migration files
- Delete AbstractClickHouseIT
- Update controllers to use new store interfaces (DiagramStore, ExecutionStore)
- Fix IngestionService calls in controllers for new synchronous API
- Migrate all ITs from AbstractClickHouseIT to AbstractPostgresIT
- Fix count() syntax and remove ClickHouse-specific test assertions
- Update TreeReconstructionTest for new buildTree() method
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The active-count query scanned all wide rows on the base table, exceeding
the 3.6 GiB memory limit. Use PREWHERE status = 'RUNNING' so ClickHouse
reads only the status column first. Add SETTINGS max_memory_usage = 1 GiB
to all queries so concurrent requests degrade gracefully instead of crashing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The JDBC driver sends java.sql.Timestamp with nanoseconds as a string
(e.g. '2026-03-15 10:13:58.105931162') which DateTime('UTC') rejects.
Add bucketTimestamp() helper that truncates to seconds for all rollup
query parameters.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
JDBC sends Timestamp params as strings, causing toStartOfFiveMinutes()
to fail with 'Illegal type String'. Floor to 5-minute boundaries in
Java instead and pass plain bucket >= ? comparisons.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
countMerge(total_count) in the avg expression resolved to the UInt64
alias 'total_count' instead of the AggregateFunction column. Rename
SELECT aliases (cnt, failed, avg_ms, p99_ms) to avoid shadowing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pre-aggregate route execution stats into 5-minute buckets using a
materialized view with -State/-Merge combinators. Rewrite stats() and
timeseries() to query the rollup table instead of scanning the wide
base table. Active count remains a real-time query since RUNNING is
transient. Includes idempotent backfill migration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backend: Add group filtering to agent list, search, stats, and timeseries
endpoints. Add diagram lookup by group+routeId. Resolve application group
to agent IDs server-side for ClickHouse IN-clause queries.
Frontend: New route detail page at /apps/{group}/routes/{routeId} with
three tabs (Diagram, Performance, Processor Tree). SVG diagram rendering
with panzoom, execution overlay (glow effects, duration/sequence badges,
flow particles, minimap), and processor detail panel. uPlot charts for
performance tab replacing old SVG sparklines. Ctrl+Click from
ExecutionExplorer navigates to route diagram with overlay.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sorting now applies to the entire result set via ClickHouse ORDER BY
instead of only sorting the current page client-side. Default sort
order is timestamp descending. Supported sort columns: startTime,
status, agentId, routeId, correlationId, durationMs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ClickHouse avg() and quantile() return nan/inf on zero rows, which
toInt64() cannot convert. Wrap with ifNotFinite(..., 0) to default to
zero. Applied to both stats and timeseries queries.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Stats endpoint now returns current + previous period (24h shift) values
plus today's total count. UI shows:
- Total Matches: "of 12.3K today"
- Avg Duration: arrow + % vs yesterday
- Failure Rate: percentage of errors vs total, arrow + % vs yesterday
- P99 Latency: arrow + % vs yesterday
- In-Flight: unchanged (running executions)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All stat card values now come from the /search/stats endpoint which
queries the full time window, not just the current page of results.
Consolidated into a single ClickHouse query for efficiency.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
quantile(0.99) returns Float64 which ClickHouse JDBC cannot cast to
Long directly. Same toInt64() pattern already used in timeseries query.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
P99 latency and active count now use the same from/to parameters as the
timeseries sparklines, so all stat cards are consistent with the user's
selected time range.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace toStartOfInterval with intDiv on epoch seconds, and cast
avg/quantile results to Int64 to avoid Float64 JDBC mapping issues.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New /search/stats/timeseries endpoint returns bucketed counts/metrics
over a time window using ClickHouse toStartOfInterval(). Frontend
Sparkline component renders SVG polyline + gradient fill on each
stat card, driven by a useStatsTimeseries query hook.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Status filter now parses comma-separated values into SQL IN clause
instead of exact match, so filtering by multiple statuses works.
Added GET /api/v1/search/stats returning P99 latency (last hour) and
active execution count, wired into the UI stat cards with 10s polling.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Propagate authenticated agent identity through write buffers via
TaggedExecution/TaggedDiagram wrappers so ClickHouse rows get real
agent IDs instead of empty strings
- Add execution_id to text search LIKE clause so selecting an execution
by ID in the palette actually finds it
- Clear status filter to all three statuses on palette selection so the
chosen execution/agent isn't filtered out
- Add disabled Routes and Exchanges scope tabs with "coming soon" state
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backend: add routeId, agentId, processorType filter fields to SearchRequest
and ClickHouseSearchEngine. Expand global text search to match route_id and
agent_id columns.
Frontend: new command palette component (portal overlay, Zustand store,
TanStack Query search hook with 300ms debounce, filter chip parsing,
keyboard navigation, scope tabs). Search bar in SearchFilters and TopNav
now open the palette. Selecting a result writes filters to the execution
search store to drive the results table.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- ClickHouseSearchEngine with dynamic WHERE clause building and LIKE escape
- SearchController with GET (basic filters) and POST (advanced JSON body)
- SearchBeanConfig wiring SearchEngine, SearchService, DetailService beans
- 13 integration tests covering all filter types, combinations, pagination, empty results
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>