diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/DiagramController.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/DiagramController.java index 75943f96..3a447d30 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/DiagramController.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/DiagramController.java @@ -11,8 +11,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @@ -34,8 +32,6 @@ import java.util.List; @Tag(name = "Ingestion", description = "Data ingestion endpoints") public class DiagramController { - private static final Logger log = LoggerFactory.getLogger(DiagramController.class); - private final IngestionService ingestionService; private final AgentRegistryService registryService; private final ObjectMapper objectMapper; diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/ExecutionController.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/ExecutionController.java index 72c1e8b2..9bf09a15 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/ExecutionController.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/ExecutionController.java @@ -11,8 +11,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; @@ -39,8 +37,6 @@ import java.util.List; @Tag(name = "Ingestion", description = "Data ingestion endpoints") public class ExecutionController { - private static final Logger log = LoggerFactory.getLogger(ExecutionController.class); - private final IngestionService ingestionService; private final AgentRegistryService registryService; private final ObjectMapper objectMapper; diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/GroupAdminController.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/GroupAdminController.java index 94ec2fdd..d9244f0e 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/GroupAdminController.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/GroupAdminController.java @@ -6,7 +6,6 @@ import com.cameleer3.server.core.admin.AuditService; import com.cameleer3.server.core.rbac.GroupDetail; import com.cameleer3.server.core.rbac.GroupRepository; import com.cameleer3.server.core.rbac.GroupSummary; -import com.cameleer3.server.core.rbac.RbacService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; @@ -39,13 +38,10 @@ import java.util.UUID; public class GroupAdminController { private final GroupRepository groupRepository; - private final RbacService rbacService; private final AuditService auditService; - public GroupAdminController(GroupRepository groupRepository, RbacService rbacService, - AuditService auditService) { + public GroupAdminController(GroupRepository groupRepository, AuditService auditService) { this.groupRepository = groupRepository; - this.rbacService = rbacService; this.auditService = auditService; } diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/RoleAdminController.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/RoleAdminController.java index 7b05a0c5..cf365812 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/RoleAdminController.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/RoleAdminController.java @@ -3,7 +3,6 @@ package com.cameleer3.server.app.controller; import com.cameleer3.server.core.admin.AuditCategory; import com.cameleer3.server.core.admin.AuditResult; import com.cameleer3.server.core.admin.AuditService; -import com.cameleer3.server.core.rbac.RbacService; import com.cameleer3.server.core.rbac.RoleDetail; import com.cameleer3.server.core.rbac.RoleRepository; import com.cameleer3.server.core.rbac.SystemRole; @@ -37,13 +36,10 @@ import java.util.UUID; public class RoleAdminController { private final RoleRepository roleRepository; - private final RbacService rbacService; private final AuditService auditService; - public RoleAdminController(RoleRepository roleRepository, RbacService rbacService, - AuditService auditService) { + public RoleAdminController(RoleRepository roleRepository, AuditService auditService) { this.roleRepository = roleRepository; - this.rbacService = rbacService; this.auditService = auditService; } diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/RouteCatalogController.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/RouteCatalogController.java index bc3c28b6..f0082eef 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/RouteCatalogController.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/RouteCatalogController.java @@ -82,8 +82,6 @@ public class RouteCatalogController { Instant now = Instant.now(); Instant rangeFrom = from != null ? Instant.parse(from) : now.minus(24, ChronoUnit.HOURS); Instant rangeTo = to != null ? Instant.parse(to) : now; - Instant from1m = now.minus(1, ChronoUnit.MINUTES); - // Route exchange counts from AggregatingMergeTree (literal SQL — ClickHouse JDBC driver // wraps prepared statements in sub-queries that strip AggregateFunction column types) Map routeExchangeCounts = new LinkedHashMap<>(); @@ -113,20 +111,6 @@ public class RouteCatalogController { } } - // Per-agent TPS from the last minute - Map agentTps = new LinkedHashMap<>(); - try { - jdbc.query( - "SELECT application_id, countMerge(total_count) AS cnt " + - "FROM stats_1m_route WHERE bucket >= " + lit(from1m) + " AND bucket < " + lit(now) + - " GROUP BY application_id", - rs -> { - // This gives per-app TPS; we'll distribute among agents below - }); - } catch (Exception e) { - // AggregatingMergeTree table may not exist yet - } - // Build catalog entries — merge apps from agent registry + ClickHouse data Set allAppIds = new LinkedHashSet<>(agentsByApp.keySet()); allAppIds.addAll(routesByApp.keySet()); diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/RouteMetricsController.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/RouteMetricsController.java index 41ac7a99..0da3c675 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/RouteMetricsController.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/RouteMetricsController.java @@ -67,10 +67,6 @@ public class RouteMetricsController { } sql.append(" GROUP BY application_id, route_id ORDER BY application_id, route_id"); - // Key struct for sparkline lookup - record RouteKey(String appId, String routeId) {} - List routeKeys = new ArrayList<>(); - List metrics = jdbc.query(sql.toString(), (rs, rowNum) -> { String applicationId = rs.getString("application_id"); String routeId = rs.getString("route_id"); @@ -83,7 +79,6 @@ public class RouteMetricsController { double errorRate = total > 0 ? (double) failed / total : 0.0; double tps = windowSeconds > 0 ? (double) total / windowSeconds : 0.0; - routeKeys.add(new RouteKey(applicationId, routeId)); return new RouteMetrics(routeId, applicationId, total, successRate, avgDur, p99Dur, errorRate, tps, List.of(), -1.0); }); diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/SearchController.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/SearchController.java index e84067e5..e7733d77 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/SearchController.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/SearchController.java @@ -136,7 +136,7 @@ public class SearchController { return ResponseEntity.ok(searchService.timeseriesForApp(from, end, buckets, application)); } List agentIds = resolveApplicationToAgentIds(application); - if (routeId == null && agentIds == null) { + if (routeId == null && agentIds.isEmpty()) { return ResponseEntity.ok(searchService.timeseries(from, end, buckets)); } return ResponseEntity.ok(searchService.timeseries(from, end, buckets, routeId, agentIds)); @@ -192,11 +192,11 @@ public class SearchController { /** * Resolve an application name to agent IDs. - * Returns null if application is null/blank (no filtering). + * Returns empty list if application is null/blank (no filtering). */ private List resolveApplicationToAgentIds(String application) { if (application == null || application.isBlank()) { - return null; + return List.of(); } return registryService.findByApplication(application).stream() .map(AgentInfo::instanceId) diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/dto/AgentEventResponse.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/dto/AgentEventResponse.java index 3a58fec2..7a56b29d 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/dto/AgentEventResponse.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/dto/AgentEventResponse.java @@ -15,10 +15,10 @@ public record AgentEventResponse( String detail, @NotNull Instant timestamp ) { - public static AgentEventResponse from(AgentEventRecord record) { + public static AgentEventResponse from(AgentEventRecord event) { return new AgentEventResponse( - record.id(), record.instanceId(), record.applicationId(), - record.eventType(), record.detail(), record.timestamp() + event.id(), event.instanceId(), event.applicationId(), + event.eventType(), event.detail(), event.timestamp() ); } } diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/rbac/RbacServiceImpl.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/rbac/RbacServiceImpl.java index ba99bf87..d304b970 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/rbac/RbacServiceImpl.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/rbac/RbacServiceImpl.java @@ -1,6 +1,12 @@ package com.cameleer3.server.app.rbac; -import com.cameleer3.server.core.rbac.*; +import com.cameleer3.server.core.rbac.GroupRepository; +import com.cameleer3.server.core.rbac.GroupSummary; +import com.cameleer3.server.core.rbac.RbacService; +import com.cameleer3.server.core.rbac.RbacStats; +import com.cameleer3.server.core.rbac.RoleSummary; +import com.cameleer3.server.core.rbac.UserDetail; +import com.cameleer3.server.core.rbac.UserSummary; import com.cameleer3.server.core.security.UserInfo; import com.cameleer3.server.core.security.UserRepository; import org.springframework.jdbc.core.JdbcTemplate; @@ -14,14 +20,12 @@ public class RbacServiceImpl implements RbacService { private final JdbcTemplate jdbc; private final UserRepository userRepository; private final GroupRepository groupRepository; - private final RoleRepository roleRepository; public RbacServiceImpl(JdbcTemplate jdbc, UserRepository userRepository, - GroupRepository groupRepository, RoleRepository roleRepository) { + GroupRepository groupRepository) { this.jdbc = jdbc; this.userRepository = userRepository; this.groupRepository = groupRepository; - this.roleRepository = roleRepository; } @Override diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/BootstrapTokenValidator.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/BootstrapTokenValidator.java index 8019f6fc..7fbe52e3 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/BootstrapTokenValidator.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/BootstrapTokenValidator.java @@ -31,20 +31,12 @@ public class BootstrapTokenValidator { byte[] providedBytes = provided.getBytes(StandardCharsets.UTF_8); - // Check current token + // Check current token, then previous token (rotation support) String currentToken = properties.getBootstrapToken(); - if (currentToken != null - && MessageDigest.isEqual(providedBytes, currentToken.getBytes(StandardCharsets.UTF_8))) { - return true; - } - - // Check previous token (rotation support) String previousToken = properties.getBootstrapTokenPrevious(); - if (previousToken != null - && MessageDigest.isEqual(providedBytes, previousToken.getBytes(StandardCharsets.UTF_8))) { - return true; - } - - return false; + return (currentToken != null + && MessageDigest.isEqual(providedBytes, currentToken.getBytes(StandardCharsets.UTF_8))) + || (previousToken != null + && MessageDigest.isEqual(providedBytes, previousToken.getBytes(StandardCharsets.UTF_8))); } } diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/PostgresApplicationConfigRepository.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/PostgresApplicationConfigRepository.java index 8274ea74..7716190c 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/PostgresApplicationConfigRepository.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/PostgresApplicationConfigRepository.java @@ -61,7 +61,7 @@ public class PostgresApplicationConfigRepository { } // Upsert: insert or update, auto-increment version - int updated = jdbc.update(""" + jdbc.update(""" INSERT INTO application_config (application, config_val, version, updated_at, updated_by) VALUES (?, ?::jsonb, 1, now(), ?) ON CONFLICT (application) DO UPDATE SET diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/PostgresAuditRepository.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/PostgresAuditRepository.java index c03084ce..351b08d3 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/PostgresAuditRepository.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/PostgresAuditRepository.java @@ -34,11 +34,11 @@ public class PostgresAuditRepository implements AuditRepository { } @Override - public void insert(AuditRecord record) { + public void insert(AuditRecord auditRecord) { String detailJson = null; - if (record.detail() != null) { + if (auditRecord.detail() != null) { try { - detailJson = objectMapper.writeValueAsString(record.detail()); + detailJson = objectMapper.writeValueAsString(auditRecord.detail()); } catch (JsonProcessingException e) { throw new RuntimeException("Failed to serialize audit detail", e); } @@ -48,11 +48,11 @@ public class PostgresAuditRepository implements AuditRepository { INSERT INTO audit_log (username, action, category, target, detail, result, ip_address, user_agent) VALUES (?, ?, ?, ?, ?::jsonb, ?, ?, ?) """, - record.username(), record.action(), - record.category() != null ? record.category().name() : null, - record.target(), detailJson, - record.result() != null ? record.result().name() : null, - record.ipAddress(), record.userAgent()); + auditRecord.username(), auditRecord.action(), + auditRecord.category() != null ? auditRecord.category().name() : null, + auditRecord.target(), detailJson, + auditRecord.result() != null ? auditRecord.result().name() : null, + auditRecord.ipAddress(), auditRecord.userAgent()); } @Override diff --git a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentCommandControllerIT.java b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentCommandControllerIT.java index 2a15a1e0..7dc50349 100644 --- a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentCommandControllerIT.java +++ b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentCommandControllerIT.java @@ -9,7 +9,6 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentRegistrationControllerIT.java b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentRegistrationControllerIT.java index 046c5a12..63b294bc 100644 --- a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentRegistrationControllerIT.java +++ b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentRegistrationControllerIT.java @@ -9,7 +9,6 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentSseControllerIT.java b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentSseControllerIT.java index 4d62c867..0eaba5c9 100644 --- a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentSseControllerIT.java +++ b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentSseControllerIT.java @@ -9,7 +9,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import java.io.BufferedReader; diff --git a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/ExecutionControllerIT.java b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/ExecutionControllerIT.java index 1ee376e2..1b183863 100644 --- a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/ExecutionControllerIT.java +++ b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/ExecutionControllerIT.java @@ -10,7 +10,6 @@ import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.jdbc.core.JdbcTemplate; import static java.util.concurrent.TimeUnit.SECONDS; import static org.assertj.core.api.Assertions.assertThat; diff --git a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/SecurityFilterIT.java b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/SecurityFilterIT.java index a55c7190..11ffd4bb 100644 --- a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/SecurityFilterIT.java +++ b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/SecurityFilterIT.java @@ -27,12 +27,11 @@ class SecurityFilterIT extends AbstractPostgresIT { @Autowired private TestSecurityHelper securityHelper; - private String jwt; private String viewerJwt; @BeforeEach void setUp() { - jwt = securityHelper.registerTestAgent("test-agent-security-filter-it"); + securityHelper.registerTestAgent("test-agent-security-filter-it"); viewerJwt = securityHelper.viewerToken(); } diff --git a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/admin/AuditRepository.java b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/admin/AuditRepository.java index a7bfdce0..76b5a721 100644 --- a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/admin/AuditRepository.java +++ b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/admin/AuditRepository.java @@ -5,7 +5,7 @@ import java.util.List; public interface AuditRepository { - void insert(AuditRecord record); + void insert(AuditRecord auditRecord); record AuditQuery( String username, diff --git a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/admin/AuditService.java b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/admin/AuditService.java index 5dde748a..82b25595 100644 --- a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/admin/AuditService.java +++ b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/admin/AuditService.java @@ -30,9 +30,9 @@ public class AuditService { HttpServletRequest request) { String ip = request != null ? request.getRemoteAddr() : null; String userAgent = request != null ? request.getHeader("User-Agent") : null; - AuditRecord record = AuditRecord.create(username, action, category, target, detail, result, ip, userAgent); + AuditRecord auditRecord = AuditRecord.create(username, action, category, target, detail, result, ip, userAgent); - repository.insert(record); + repository.insert(auditRecord); if (request != null) { request.setAttribute("audit.logged", true); diff --git a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentRegistryService.java b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentRegistryService.java index 72bf5512..9f8fd9b5 100644 --- a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentRegistryService.java +++ b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentRegistryService.java @@ -13,7 +13,7 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.stream.Collectors; + /** * In-memory agent registry managing agent lifecycle, heartbeats, and commands. @@ -53,7 +53,7 @@ public class AgentRegistryService { List.copyOf(routeIds), Map.copyOf(capabilities), AgentState.LIVE, now, now, null); - AgentInfo result = agents.compute(id, (key, existing) -> { + return agents.compute(id, (key, existing) -> { if (existing != null) { // Re-registration: update metadata, reset to LIVE log.info("Agent {} re-registering (was {})", id, existing.state()); @@ -67,8 +67,6 @@ public class AgentRegistryService { log.info("Agent {} registered (name={}, application={})", id, name, application); return newAgent; }); - - return result; } /** @@ -196,7 +194,7 @@ public class AgentRegistryService { public List findByState(AgentState state) { return agents.values().stream() .filter(a -> a.state() == state) - .collect(Collectors.toList()); + .toList(); } /** @@ -205,7 +203,7 @@ public class AgentRegistryService { public List findByApplication(String application) { return agents.values().stream() .filter(a -> application.equals(a.applicationId())) - .collect(Collectors.toList()); + .toList(); } /** @@ -276,7 +274,7 @@ public class AgentRegistryService { } return queue.stream() .filter(cmd -> cmd.status() == CommandStatus.PENDING) - .collect(Collectors.toList()); + .toList(); } /** diff --git a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/DetailService.java b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/DetailService.java index e71d45e9..855d8a02 100644 --- a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/DetailService.java +++ b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/DetailService.java @@ -29,7 +29,7 @@ public class DetailService { // Prefer the raw processor tree (faithful to agent data) over // flat-record reconstruction (which loses iteration context). List processors = parseProcessorsJson(exec.processorsJson()); - if (processors == null) { + if (processors.isEmpty()) { // Fallback for executions ingested before processors_json was added List records = executionStore.findProcessors(executionId); processors = buildTree(records); @@ -78,12 +78,12 @@ public class DetailService { /** Parse the raw processor tree JSON stored alongside the execution. */ private List parseProcessorsJson(String json) { - if (json == null || json.isBlank()) return null; + if (json == null || json.isBlank()) return List.of(); try { List executions = JSON.readValue(json, PROCESSOR_EXEC_LIST); return convertProcessors(executions); } catch (Exception e) { - return null; + return List.of(); } }