chore: rename cameleer3 to cameleer
Some checks failed
CI / cleanup-branch (push) Has been skipped
CI / build (push) Failing after 18s
CI / docker (push) Has been skipped
CI / deploy (push) Has been skipped
CI / deploy-feature (push) Has been skipped

Rename Java packages from com.cameleer3 to com.cameleer, module
directories from cameleer3-* to cameleer-*, and all references
throughout workflows, Dockerfiles, docs, migrations, and pom.xml.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-15 15:28:42 +02:00
parent 1077293343
commit cb3ebfea7c
569 changed files with 4356 additions and 3245 deletions

View File

@@ -93,24 +93,24 @@ jobs:
- name: Build and push server - name: Build and push server
run: | run: |
docker buildx create --use --name cibuilder docker buildx create --use --name cibuilder
TAGS="-t gitea.siegeln.net/cameleer/cameleer3-server:${{ github.sha }}" TAGS="-t gitea.siegeln.net/cameleer/cameleer-server:${{ github.sha }}"
for TAG in $IMAGE_TAGS; do for TAG in $IMAGE_TAGS; do
TAGS="$TAGS -t gitea.siegeln.net/cameleer/cameleer3-server:$TAG" TAGS="$TAGS -t gitea.siegeln.net/cameleer/cameleer-server:$TAG"
done done
docker buildx build --platform linux/amd64 \ docker buildx build --platform linux/amd64 \
--build-arg REGISTRY_TOKEN="$REGISTRY_TOKEN" \ --build-arg REGISTRY_TOKEN="$REGISTRY_TOKEN" \
$TAGS \ $TAGS \
--cache-from type=registry,ref=gitea.siegeln.net/cameleer/cameleer3-server:buildcache \ --cache-from type=registry,ref=gitea.siegeln.net/cameleer/cameleer-server:buildcache \
--cache-to type=registry,ref=gitea.siegeln.net/cameleer/cameleer3-server:buildcache,mode=max \ --cache-to type=registry,ref=gitea.siegeln.net/cameleer/cameleer-server:buildcache,mode=max \
--provenance=false \ --provenance=false \
--push . --push .
env: env:
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
- name: Build and push UI - name: Build and push UI
run: | run: |
TAGS="-t gitea.siegeln.net/cameleer/cameleer3-server-ui:${{ github.sha }}" TAGS="-t gitea.siegeln.net/cameleer/cameleer-server-ui:${{ github.sha }}"
for TAG in $IMAGE_TAGS; do for TAG in $IMAGE_TAGS; do
TAGS="$TAGS -t gitea.siegeln.net/cameleer/cameleer3-server-ui:$TAG" TAGS="$TAGS -t gitea.siegeln.net/cameleer/cameleer-server-ui:$TAG"
done done
SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7) SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7)
docker buildx build --platform linux/amd64 \ docker buildx build --platform linux/amd64 \
@@ -118,8 +118,8 @@ jobs:
--build-arg REGISTRY_TOKEN="$REGISTRY_TOKEN" \ --build-arg REGISTRY_TOKEN="$REGISTRY_TOKEN" \
--build-arg VITE_APP_VERSION="$SHORT_SHA" \ --build-arg VITE_APP_VERSION="$SHORT_SHA" \
$TAGS \ $TAGS \
--cache-from type=registry,ref=gitea.siegeln.net/cameleer/cameleer3-server-ui:buildcache \ --cache-from type=registry,ref=gitea.siegeln.net/cameleer/cameleer-server-ui:buildcache \
--cache-to type=registry,ref=gitea.siegeln.net/cameleer/cameleer3-server-ui:buildcache,mode=max \ --cache-to type=registry,ref=gitea.siegeln.net/cameleer/cameleer-server-ui:buildcache,mode=max \
--provenance=false \ --provenance=false \
--push ui/ --push ui/
env: env:
@@ -137,7 +137,7 @@ jobs:
if [ "$BRANCH_SLUG" != "main" ]; then if [ "$BRANCH_SLUG" != "main" ]; then
KEEP_TAGS="$KEEP_TAGS branch-$BRANCH_SLUG" KEEP_TAGS="$KEEP_TAGS branch-$BRANCH_SLUG"
fi fi
for PKG in cameleer3-server cameleer3-server-ui; do for PKG in cameleer-server cameleer-server-ui; do
curl -sf -H "$AUTH" "$API/packages/cameleer/container/$PKG" | \ curl -sf -H "$AUTH" "$API/packages/cameleer/container/$PKG" | \
jq -r '.[] | "\(.id) \(.version)"' | \ jq -r '.[] | "\(.id) \(.version)"' | \
while read id version; do while read id version; do
@@ -229,13 +229,13 @@ jobs:
kubectl -n cameleer rollout status deployment/cameleer-logto --timeout=180s kubectl -n cameleer rollout status deployment/cameleer-logto --timeout=180s
kubectl apply -k deploy/overlays/main kubectl apply -k deploy/overlays/main
kubectl -n cameleer set image deployment/cameleer3-server \ kubectl -n cameleer set image deployment/cameleer-server \
server=gitea.siegeln.net/cameleer/cameleer3-server:${{ github.sha }} server=gitea.siegeln.net/cameleer/cameleer-server:${{ github.sha }}
kubectl -n cameleer rollout status deployment/cameleer3-server --timeout=120s kubectl -n cameleer rollout status deployment/cameleer-server --timeout=120s
kubectl -n cameleer set image deployment/cameleer3-ui \ kubectl -n cameleer set image deployment/cameleer-ui \
ui=gitea.siegeln.net/cameleer/cameleer3-server-ui:${{ github.sha }} ui=gitea.siegeln.net/cameleer/cameleer-server-ui:${{ github.sha }}
kubectl -n cameleer rollout status deployment/cameleer3-ui --timeout=120s kubectl -n cameleer rollout status deployment/cameleer-ui --timeout=120s
env: env:
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
CAMELEER_AUTH_TOKEN: ${{ secrets.CAMELEER_AUTH_TOKEN }} CAMELEER_AUTH_TOKEN: ${{ secrets.CAMELEER_AUTH_TOKEN }}
@@ -309,9 +309,9 @@ jobs:
kubectl -n "$BRANCH_NS" wait --for=condition=complete job/init-schema --timeout=60s || \ kubectl -n "$BRANCH_NS" wait --for=condition=complete job/init-schema --timeout=60s || \
echo "Warning: init-schema job did not complete in time" echo "Warning: init-schema job did not complete in time"
- name: Wait for server rollout - name: Wait for server rollout
run: kubectl -n "$BRANCH_NS" rollout status deployment/cameleer3-server --timeout=120s run: kubectl -n "$BRANCH_NS" rollout status deployment/cameleer-server --timeout=120s
- name: Wait for UI rollout - name: Wait for UI rollout
run: kubectl -n "$BRANCH_NS" rollout status deployment/cameleer3-ui --timeout=60s run: kubectl -n "$BRANCH_NS" rollout status deployment/cameleer-ui --timeout=60s
- name: Print deployment URLs - name: Print deployment URLs
run: | run: |
echo "====================================" echo "===================================="
@@ -359,7 +359,7 @@ jobs:
--image=postgres:16 \ --image=postgres:16 \
--restart=Never \ --restart=Never \
--env="PGPASSWORD=$(kubectl get secret cameleer-postgres-credentials -n cameleer -o jsonpath='{.data.POSTGRES_PASSWORD}' | base64 -d)" \ --env="PGPASSWORD=$(kubectl get secret cameleer-postgres-credentials -n cameleer -o jsonpath='{.data.POSTGRES_PASSWORD}' | base64 -d)" \
--command -- sh -c "psql -h cameleer-postgres -U $(kubectl get secret cameleer-postgres-credentials -n cameleer -o jsonpath='{.data.POSTGRES_USER}' | base64 -d) -d cameleer3 -c 'DROP SCHEMA IF EXISTS ${BRANCH_SCHEMA} CASCADE'" --command -- sh -c "psql -h cameleer-postgres -U $(kubectl get secret cameleer-postgres-credentials -n cameleer -o jsonpath='{.data.POSTGRES_USER}' | base64 -d) -d cameleer -c 'DROP SCHEMA IF EXISTS ${BRANCH_SCHEMA} CASCADE'"
kubectl wait --for=condition=Ready pod/cleanup-schema-${BRANCH_SLUG} -n cameleer --timeout=30s || true kubectl wait --for=condition=Ready pod/cleanup-schema-${BRANCH_SLUG} -n cameleer --timeout=30s || true
kubectl wait --for=jsonpath='{.status.phase}'=Succeeded pod/cleanup-schema-${BRANCH_SLUG} -n cameleer --timeout=60s || true kubectl wait --for=jsonpath='{.status.phase}'=Succeeded pod/cleanup-schema-${BRANCH_SLUG} -n cameleer --timeout=60s || true
kubectl delete pod cleanup-schema-${BRANCH_SLUG} -n cameleer --ignore-not-found kubectl delete pod cleanup-schema-${BRANCH_SLUG} -n cameleer --ignore-not-found
@@ -367,7 +367,7 @@ jobs:
run: | run: |
API="https://gitea.siegeln.net/api/v1" API="https://gitea.siegeln.net/api/v1"
AUTH="Authorization: token ${REGISTRY_TOKEN}" AUTH="Authorization: token ${REGISTRY_TOKEN}"
for PKG in cameleer3-server cameleer3-server-ui; do for PKG in cameleer-server cameleer-server-ui; do
# Delete branch-specific tag # Delete branch-specific tag
curl -sf -X DELETE -H "$AUTH" "$API/packages/cameleer/container/$PKG/branch-${BRANCH_SLUG}" || true curl -sf -X DELETE -H "$AUTH" "$API/packages/cameleer/container/$PKG/branch-${BRANCH_SLUG}" || true
done done

View File

@@ -59,5 +59,5 @@ jobs:
mvn clean verify sonar:sonar -DskipITs -U --batch-mode \ mvn clean verify sonar:sonar -DskipITs -U --batch-mode \
-Dsonar.host.url=${{ secrets.SONAR_HOST_URL }} \ -Dsonar.host.url=${{ secrets.SONAR_HOST_URL }} \
-Dsonar.token=${{ secrets.SONAR_TOKEN }} \ -Dsonar.token=${{ secrets.SONAR_TOKEN }} \
-Dsonar.projectKey=cameleer3-server \ -Dsonar.projectKey=cameleer-server \
-Dsonar.projectName="Cameleer3 Server" -Dsonar.projectName="Cameleer Server"

2
.gitignore vendored
View File

@@ -39,5 +39,7 @@ logs/
# Claude # Claude
.claude/ .claude/
.superpowers/
.playwright-mcp/
.worktrees/ .worktrees/
.gitnexus .gitnexus

View File

@@ -1,8 +1,8 @@
# Cameleer3 Server # Cameleer Server
## What This Is ## What This Is
An observability server that receives, stores, and serves Apache Camel route execution data from distributed Cameleer3 agents. Think njams Server (by Integration Matters) — but built incrementally, API-first, with a modern stack. Users can search through millions of recorded transactions by state, time, duration, full text, and correlate executions across multiple Camel instances. The server also pushes configuration, tracing controls, and ad-hoc commands to agents via SSE. An observability server that receives, stores, and serves Apache Camel route execution data from distributed Cameleer agents. Think njams Server (by Integration Matters) — but built incrementally, API-first, with a modern stack. Users can search through millions of recorded transactions by state, time, duration, full text, and correlate executions across multiple Camel instances. The server also pushes configuration, tracing controls, and ad-hoc commands to agents via SSE.
## Core Value ## Core Value
@@ -16,7 +16,7 @@ Users can reliably search and find any transaction across all connected Camel in
### Active ### Active
- [ ] Receive and ingest transaction/activity data from Cameleer3 agents via HTTP POST - [ ] Receive and ingest transaction/activity data from Cameleer agents via HTTP POST
- [ ] Store transactions in a high-volume, horizontally scalable data store with 30-day retention - [ ] Store transactions in a high-volume, horizontally scalable data store with 30-day retention
- [ ] Search transactions by state, execution date/time, duration, and full-text content - [ ] Search transactions by state, execution date/time, duration, and full-text content
- [ ] Correlate activities across multiple routes and Camel instances within a single transaction - [ ] Correlate activities across multiple routes and Camel instances within a single transaction
@@ -38,8 +38,8 @@ Users can reliably search and find any transaction across all connected Camel in
## Context ## Context
- **Agent side**: cameleer3 agent (`https://gitea.siegeln.net/cameleer/cameleer3`) is under active development; already supports creating diagrams and capturing executions - **Agent side**: cameleer agent (`https://gitea.siegeln.net/cameleer/cameleer`) is under active development; already supports creating diagrams and capturing executions
- **Shared library**: `com.cameleer3:cameleer3-common` contains shared models and the graph API; protocol defined in `cameleer3-common/PROTOCOL.md` - **Shared library**: `com.cameleer:cameleer-common` contains shared models and the graph API; protocol defined in `cameleer-common/PROTOCOL.md`
- **Data model**: Hierarchical — a **transaction** represents a message's full journey, containing **activities** per route execution. Transactions can span multiple Camel instances (e.g., route A calls route B on another instance via endpoint) - **Data model**: Hierarchical — a **transaction** represents a message's full journey, containing **activities** per route execution. Transactions can span multiple Camel instances (e.g., route A calls route B on another instance via endpoint)
- **Scale target**: Millions of transactions per day, 50+ connected agents, 30-day data retention - **Scale target**: Millions of transactions per day, 50+ connected agents, 30-day data retention
- **Query pattern**: Incident-driven — mostly recent data queries, with deep historical dives during incidents - **Query pattern**: Incident-driven — mostly recent data queries, with deep historical dives during incidents
@@ -49,7 +49,7 @@ Users can reliably search and find any transaction across all connected Camel in
## Constraints ## Constraints
- **Tech stack**: Java 17+, Spring Boot 3.4.3, Maven multi-module — already established - **Tech stack**: Java 17+, Spring Boot 3.4.3, Maven multi-module — already established
- **Dependency**: Must consume `com.cameleer3:cameleer3-common` from Gitea Maven registry - **Dependency**: Must consume `com.cameleer:cameleer-common` from Gitea Maven registry
- **Protocol**: Agent protocol is still evolving — server must adapt as it stabilizes - **Protocol**: Agent protocol is still evolving — server must adapt as it stabilizes
- **Incremental delivery**: Build step by step; storage and search first, then layer features on top - **Incremental delivery**: Build step by step; storage and search first, then layer features on top

View File

@@ -1,4 +1,4 @@
# Requirements: Cameleer3 Server # Requirements: Cameleer Server
**Defined:** 2026-03-11 **Defined:** 2026-03-11
**Core Value:** Users can reliably search and find any transaction across all connected Camel instances — by any combination of state, time, duration, or content — even at millions of transactions per day with 30-day retention. **Core Value:** Users can reliably search and find any transaction across all connected Camel instances — by any combination of state, time, duration, or content — even at millions of transactions per day with 30-day retention.

View File

@@ -1,4 +1,4 @@
# Roadmap: Cameleer3 Server # Roadmap: Cameleer Server
## Overview ## Overview

View File

@@ -75,7 +75,7 @@ Recent decisions affecting current work:
- [Roadmap]: Phases 2 and 3 can execute in parallel (both depend only on Phase 1) - [Roadmap]: Phases 2 and 3 can execute in parallel (both depend only on Phase 1)
- [Roadmap]: Web UI deferred to v2 - [Roadmap]: Web UI deferred to v2
- [Phase 01]: Used spring-boot-starter-jdbc for JdbcTemplate + HikariCP auto-config - [Phase 01]: Used spring-boot-starter-jdbc for JdbcTemplate + HikariCP auto-config
- [Phase 01]: Created MetricsSnapshot record in core module (cameleer3-common has no metrics model) - [Phase 01]: Created MetricsSnapshot record in core module (cameleer-common has no metrics model)
- [Phase 01]: Upgraded testcontainers to 2.0.3 for Docker Desktop 29.x compatibility - [Phase 01]: Upgraded testcontainers to 2.0.3 for Docker Desktop 29.x compatibility
- [Phase 01]: Changed error_message/error_stacktrace to non-nullable String for tokenbf_v1 index compat - [Phase 01]: Changed error_message/error_stacktrace to non-nullable String for tokenbf_v1 index compat
- [Phase 01]: TTL expressions require toDateTime() cast for DateTime64 columns in ClickHouse 25.3 - [Phase 01]: TTL expressions require toDateTime() cast for DateTime64 columns in ClickHouse 25.3
@@ -121,7 +121,7 @@ None yet.
### Blockers/Concerns ### Blockers/Concerns
- [Phase 1]: ClickHouse Java client API needs phase-specific research (library has undergone changes) - [Phase 1]: ClickHouse Java client API needs phase-specific research (library has undergone changes)
- [Phase 1]: Must read cameleer3-common PROTOCOL.md before designing ClickHouse schema - [Phase 1]: Must read cameleer-common PROTOCOL.md before designing ClickHouse schema
- [Phase 2]: Diagram rendering library selection is an open question (Batik, jsvg, JGraphX, or client-side) - [Phase 2]: Diagram rendering library selection is an open question (Batik, jsvg, JGraphX, or client-side)
- [Phase 2]: ClickHouse skip indexes may not suffice for full-text; decision point during Phase 2 - [Phase 2]: ClickHouse skip indexes may not suffice for full-text; decision point during Phase 2

View File

@@ -6,18 +6,18 @@ wave: 1
depends_on: [] depends_on: []
files_modified: files_modified:
- pom.xml - pom.xml
- cameleer3-server-core/pom.xml - cameleer-server-core/pom.xml
- cameleer3-server-app/pom.xml - cameleer-server-app/pom.xml
- docker-compose.yml - docker-compose.yml
- clickhouse/init/01-schema.sql - clickhouse/init/01-schema.sql
- cameleer3-server-app/src/main/resources/application.yml - cameleer-server-app/src/main/resources/application.yml
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/ClickHouseConfig.java - cameleer-server-app/src/main/java/com/cameleer/server/app/config/ClickHouseConfig.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/IngestionConfig.java - cameleer-server-app/src/main/java/com/cameleer/server/app/config/IngestionConfig.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/ingestion/WriteBuffer.java - cameleer-server-core/src/main/java/com/cameleer/server/core/ingestion/WriteBuffer.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/ExecutionRepository.java - cameleer-server-core/src/main/java/com/cameleer/server/core/storage/ExecutionRepository.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/DiagramRepository.java - cameleer-server-core/src/main/java/com/cameleer/server/core/storage/DiagramRepository.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/MetricsRepository.java - cameleer-server-core/src/main/java/com/cameleer/server/core/storage/MetricsRepository.java
- cameleer3-server-core/src/test/java/com/cameleer3/server/core/ingestion/WriteBufferTest.java - cameleer-server-core/src/test/java/com/cameleer/server/core/ingestion/WriteBufferTest.java
autonomous: true autonomous: true
requirements: requirements:
- INGST-04 - INGST-04
@@ -32,7 +32,7 @@ must_haves:
- "TTL clause on tables removes data older than configured days" - "TTL clause on tables removes data older than configured days"
- "Docker Compose starts ClickHouse and initializes the schema" - "Docker Compose starts ClickHouse and initializes the schema"
artifacts: artifacts:
- path: "cameleer3-server-core/src/main/java/com/cameleer3/server/core/ingestion/WriteBuffer.java" - path: "cameleer-server-core/src/main/java/com/cameleer/server/core/ingestion/WriteBuffer.java"
provides: "Generic bounded write buffer with offer/drain/isFull" provides: "Generic bounded write buffer with offer/drain/isFull"
min_lines: 30 min_lines: 30
- path: "clickhouse/init/01-schema.sql" - path: "clickhouse/init/01-schema.sql"
@@ -41,15 +41,15 @@ must_haves:
- path: "docker-compose.yml" - path: "docker-compose.yml"
provides: "Local ClickHouse service" provides: "Local ClickHouse service"
contains: "clickhouse-server" contains: "clickhouse-server"
- path: "cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/ExecutionRepository.java" - path: "cameleer-server-core/src/main/java/com/cameleer/server/core/storage/ExecutionRepository.java"
provides: "Repository interface for execution batch inserts" provides: "Repository interface for execution batch inserts"
exports: ["insertBatch"] exports: ["insertBatch"]
key_links: key_links:
- from: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/ClickHouseConfig.java" - from: "cameleer-server-app/src/main/java/com/cameleer/server/app/config/ClickHouseConfig.java"
to: "application.yml" to: "application.yml"
via: "spring.datasource properties" via: "spring.datasource properties"
pattern: "spring\\.datasource" pattern: "spring\\.datasource"
- from: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/IngestionConfig.java" - from: "cameleer-server-app/src/main/java/com/cameleer/server/app/config/IngestionConfig.java"
to: "application.yml" to: "application.yml"
via: "ingestion.* properties" via: "ingestion.* properties"
pattern: "ingestion\\." pattern: "ingestion\\."
@@ -74,8 +74,8 @@ Output: Working ClickHouse via Docker Compose, DDL with TTL, WriteBuffer with un
@.planning/phases/01-ingestion-pipeline-api-foundation/01-RESEARCH.md @.planning/phases/01-ingestion-pipeline-api-foundation/01-RESEARCH.md
@pom.xml @pom.xml
@cameleer3-server-core/pom.xml @cameleer-server-core/pom.xml
@cameleer3-server-app/pom.xml @cameleer-server-app/pom.xml
</context> </context>
<tasks> <tasks>
@@ -84,11 +84,11 @@ Output: Working ClickHouse via Docker Compose, DDL with TTL, WriteBuffer with un
<name>Task 1: Dependencies, Docker Compose, ClickHouse schema, and application config</name> <name>Task 1: Dependencies, Docker Compose, ClickHouse schema, and application config</name>
<files> <files>
pom.xml, pom.xml,
cameleer3-server-core/pom.xml, cameleer-server-core/pom.xml,
cameleer3-server-app/pom.xml, cameleer-server-app/pom.xml,
docker-compose.yml, docker-compose.yml,
clickhouse/init/01-schema.sql, clickhouse/init/01-schema.sql,
cameleer3-server-app/src/main/resources/application.yml cameleer-server-app/src/main/resources/application.yml
</files> </files>
<behavior> <behavior>
- docker compose up -d starts ClickHouse on ports 8123/9000 - docker compose up -d starts ClickHouse on ports 8123/9000
@@ -101,7 +101,7 @@ Output: Working ClickHouse via Docker Compose, DDL with TTL, WriteBuffer with un
- Maven compile succeeds with new dependencies - Maven compile succeeds with new dependencies
</behavior> </behavior>
<action> <action>
1. Add dependencies to cameleer3-server-app/pom.xml per research: 1. Add dependencies to cameleer-server-app/pom.xml per research:
- clickhouse-jdbc 0.9.7 (classifier: all) - clickhouse-jdbc 0.9.7 (classifier: all)
- spring-boot-starter-actuator - spring-boot-starter-actuator
- springdoc-openapi-starter-webmvc-ui 2.8.6 - springdoc-openapi-starter-webmvc-ui 2.8.6
@@ -109,13 +109,13 @@ Output: Working ClickHouse via Docker Compose, DDL with TTL, WriteBuffer with un
- junit-jupiter from testcontainers 2.0.2 (test scope) - junit-jupiter from testcontainers 2.0.2 (test scope)
- awaitility (test scope) - awaitility (test scope)
2. Add slf4j-api dependency to cameleer3-server-core/pom.xml. 2. Add slf4j-api dependency to cameleer-server-core/pom.xml.
3. Create docker-compose.yml at project root with ClickHouse service: 3. Create docker-compose.yml at project root with ClickHouse service:
- Image: clickhouse/clickhouse-server:25.3 - Image: clickhouse/clickhouse-server:25.3
- Ports: 8123:8123, 9000:9000 - Ports: 8123:8123, 9000:9000
- Volume mount ./clickhouse/init to /docker-entrypoint-initdb.d - Volume mount ./clickhouse/init to /docker-entrypoint-initdb.d
- Environment: CLICKHOUSE_USER=cameleer, CLICKHOUSE_PASSWORD=cameleer_dev, CLICKHOUSE_DB=cameleer3 - Environment: CLICKHOUSE_USER=cameleer, CLICKHOUSE_PASSWORD=cameleer_dev, CLICKHOUSE_DB=cameleer
- ulimits nofile 262144 - ulimits nofile 262144
4. Create clickhouse/init/01-schema.sql with the three tables from research: 4. Create clickhouse/init/01-schema.sql with the three tables from research:
@@ -124,9 +124,9 @@ Output: Working ClickHouse via Docker Compose, DDL with TTL, WriteBuffer with un
- agent_metrics: MergeTree, daily partitioning on collected_at, ORDER BY (agent_id, metric_name, collected_at), TTL collected_at + INTERVAL 30 DAY, SETTINGS ttl_only_drop_parts=1. - agent_metrics: MergeTree, daily partitioning on collected_at, ORDER BY (agent_id, metric_name, collected_at), TTL collected_at + INTERVAL 30 DAY, SETTINGS ttl_only_drop_parts=1.
- All DateTime fields use DateTime64(3, 'UTC'). - All DateTime fields use DateTime64(3, 'UTC').
5. Create cameleer3-server-app/src/main/resources/application.yml with config from research: 5. Create cameleer-server-app/src/main/resources/application.yml with config from research:
- server.port: 8081 - server.port: 8081
- spring.datasource: url=jdbc:ch://localhost:8123/cameleer3, username/password, driver-class-name - spring.datasource: url=jdbc:ch://localhost:8123/cameleer, username/password, driver-class-name
- spring.jackson: write-dates-as-timestamps=false, fail-on-unknown-properties=false - spring.jackson: write-dates-as-timestamps=false, fail-on-unknown-properties=false
- ingestion: buffer-capacity=50000, batch-size=5000, flush-interval-ms=1000 - ingestion: buffer-capacity=50000, batch-size=5000, flush-interval-ms=1000
- clickhouse.ttl-days: 30 - clickhouse.ttl-days: 30
@@ -144,13 +144,13 @@ Output: Working ClickHouse via Docker Compose, DDL with TTL, WriteBuffer with un
<task type="auto" tdd="true"> <task type="auto" tdd="true">
<name>Task 2: WriteBuffer, repository interfaces, IngestionConfig, and ClickHouseConfig</name> <name>Task 2: WriteBuffer, repository interfaces, IngestionConfig, and ClickHouseConfig</name>
<files> <files>
cameleer3-server-core/src/main/java/com/cameleer3/server/core/ingestion/WriteBuffer.java, cameleer-server-core/src/main/java/com/cameleer/server/core/ingestion/WriteBuffer.java,
cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/ExecutionRepository.java, cameleer-server-core/src/main/java/com/cameleer/server/core/storage/ExecutionRepository.java,
cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/DiagramRepository.java, cameleer-server-core/src/main/java/com/cameleer/server/core/storage/DiagramRepository.java,
cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/MetricsRepository.java, cameleer-server-core/src/main/java/com/cameleer/server/core/storage/MetricsRepository.java,
cameleer3-server-core/src/test/java/com/cameleer3/server/core/ingestion/WriteBufferTest.java, cameleer-server-core/src/test/java/com/cameleer/server/core/ingestion/WriteBufferTest.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/ClickHouseConfig.java, cameleer-server-app/src/main/java/com/cameleer/server/app/config/ClickHouseConfig.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/IngestionConfig.java cameleer-server-app/src/main/java/com/cameleer/server/app/config/IngestionConfig.java
</files> </files>
<behavior> <behavior>
- WriteBuffer(capacity=10): offer() returns true for first 10 items, false on 11th - WriteBuffer(capacity=10): offer() returns true for first 10 items, false on 11th
@@ -181,7 +181,7 @@ Output: Working ClickHouse via Docker Compose, DDL with TTL, WriteBuffer with un
3. Create repository interfaces in core module: 3. Create repository interfaces in core module:
- ExecutionRepository: void insertBatch(List<RouteExecution> executions) - ExecutionRepository: void insertBatch(List<RouteExecution> executions)
- DiagramRepository: void store(RouteGraph graph), Optional<RouteGraph> findByContentHash(String hash), Optional<String> findContentHashForRoute(String routeId, String agentId) - DiagramRepository: void store(RouteGraph graph), Optional<RouteGraph> findByContentHash(String hash), Optional<String> findContentHashForRoute(String routeId, String agentId)
- MetricsRepository: void insertBatch(List<MetricsSnapshot> metrics) -- use a generic type or the cameleer3-common metrics model if available; if not, create a simple MetricsData record in core module - MetricsRepository: void insertBatch(List<MetricsSnapshot> metrics) -- use a generic type or the cameleer-common metrics model if available; if not, create a simple MetricsData record in core module
4. Create IngestionConfig as @ConfigurationProperties("ingestion"): 4. Create IngestionConfig as @ConfigurationProperties("ingestion"):
- bufferCapacity (int, default 50000) - bufferCapacity (int, default 50000)
@@ -193,7 +193,7 @@ Output: Working ClickHouse via Docker Compose, DDL with TTL, WriteBuffer with un
- No custom bean needed if relying on auto-config; only create if explicit JdbcTemplate customization required - No custom bean needed if relying on auto-config; only create if explicit JdbcTemplate customization required
</action> </action>
<verify> <verify>
<automated>mvn test -pl cameleer3-server-core -Dtest=WriteBufferTest -q 2>&1 | tail -10</automated> <automated>mvn test -pl cameleer-server-core -Dtest=WriteBufferTest -q 2>&1 | tail -10</automated>
</verify> </verify>
<done>WriteBuffer passes all unit tests. Repository interfaces exist with correct method signatures. IngestionConfig reads from application.yml.</done> <done>WriteBuffer passes all unit tests. Repository interfaces exist with correct method signatures. IngestionConfig reads from application.yml.</done>
</task> </task>
@@ -201,7 +201,7 @@ Output: Working ClickHouse via Docker Compose, DDL with TTL, WriteBuffer with un
</tasks> </tasks>
<verification> <verification>
- `mvn test -pl cameleer3-server-core -q` -- all WriteBuffer unit tests pass - `mvn test -pl cameleer-server-core -q` -- all WriteBuffer unit tests pass
- `mvn clean compile -q` -- full project compiles with new dependencies - `mvn clean compile -q` -- full project compiles with new dependencies
- `docker compose config` -- validates Docker Compose file - `docker compose config` -- validates Docker Compose file
- clickhouse/init/01-schema.sql contains CREATE TABLE for all three tables with correct ENGINE, ORDER BY, PARTITION BY, and TTL - clickhouse/init/01-schema.sql contains CREATE TABLE for all three tables with correct ENGINE, ORDER BY, PARTITION BY, and TTL

View File

@@ -26,22 +26,22 @@ key-files:
created: created:
- docker-compose.yml - docker-compose.yml
- clickhouse/init/01-schema.sql - clickhouse/init/01-schema.sql
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/ingestion/WriteBuffer.java - cameleer-server-core/src/main/java/com/cameleer/server/core/ingestion/WriteBuffer.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/ExecutionRepository.java - cameleer-server-core/src/main/java/com/cameleer/server/core/storage/ExecutionRepository.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/DiagramRepository.java - cameleer-server-core/src/main/java/com/cameleer/server/core/storage/DiagramRepository.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/MetricsRepository.java - cameleer-server-core/src/main/java/com/cameleer/server/core/storage/MetricsRepository.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/model/MetricsSnapshot.java - cameleer-server-core/src/main/java/com/cameleer/server/core/storage/model/MetricsSnapshot.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/IngestionConfig.java - cameleer-server-app/src/main/java/com/cameleer/server/app/config/IngestionConfig.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/ClickHouseConfig.java - cameleer-server-app/src/main/java/com/cameleer/server/app/config/ClickHouseConfig.java
- cameleer3-server-core/src/test/java/com/cameleer3/server/core/ingestion/WriteBufferTest.java - cameleer-server-core/src/test/java/com/cameleer/server/core/ingestion/WriteBufferTest.java
modified: modified:
- cameleer3-server-core/pom.xml - cameleer-server-core/pom.xml
- cameleer3-server-app/pom.xml - cameleer-server-app/pom.xml
- cameleer3-server-app/src/main/resources/application.yml - cameleer-server-app/src/main/resources/application.yml
key-decisions: key-decisions:
- "Used spring-boot-starter-jdbc for JdbcTemplate + HikariCP auto-config rather than manual DataSource" - "Used spring-boot-starter-jdbc for JdbcTemplate + HikariCP auto-config rather than manual DataSource"
- "Created MetricsSnapshot record in core module since cameleer3-common has no metrics model" - "Created MetricsSnapshot record in core module since cameleer-common has no metrics model"
- "ClickHouseConfig exposes JdbcTemplate bean; relies on Spring Boot DataSource auto-config" - "ClickHouseConfig exposes JdbcTemplate bean; relies on Spring Boot DataSource auto-config"
patterns-established: patterns-established:
@@ -84,21 +84,21 @@ Each task was committed atomically:
## Files Created/Modified ## Files Created/Modified
- `docker-compose.yml` - ClickHouse service with ports 8123/9000, init volume mount - `docker-compose.yml` - ClickHouse service with ports 8123/9000, init volume mount
- `clickhouse/init/01-schema.sql` - DDL for route_executions, route_diagrams, agent_metrics - `clickhouse/init/01-schema.sql` - DDL for route_executions, route_diagrams, agent_metrics
- `cameleer3-server-core/src/main/java/.../ingestion/WriteBuffer.java` - Bounded queue with offer/offerBatch/drain - `cameleer-server-core/src/main/java/.../ingestion/WriteBuffer.java` - Bounded queue with offer/offerBatch/drain
- `cameleer3-server-core/src/main/java/.../storage/ExecutionRepository.java` - Batch insert interface for RouteExecution - `cameleer-server-core/src/main/java/.../storage/ExecutionRepository.java` - Batch insert interface for RouteExecution
- `cameleer3-server-core/src/main/java/.../storage/DiagramRepository.java` - Store/find interface for RouteGraph - `cameleer-server-core/src/main/java/.../storage/DiagramRepository.java` - Store/find interface for RouteGraph
- `cameleer3-server-core/src/main/java/.../storage/MetricsRepository.java` - Batch insert interface for MetricsSnapshot - `cameleer-server-core/src/main/java/.../storage/MetricsRepository.java` - Batch insert interface for MetricsSnapshot
- `cameleer3-server-core/src/main/java/.../storage/model/MetricsSnapshot.java` - Metrics data record - `cameleer-server-core/src/main/java/.../storage/model/MetricsSnapshot.java` - Metrics data record
- `cameleer3-server-app/src/main/java/.../config/IngestionConfig.java` - Buffer capacity, batch size, flush interval - `cameleer-server-app/src/main/java/.../config/IngestionConfig.java` - Buffer capacity, batch size, flush interval
- `cameleer3-server-app/src/main/java/.../config/ClickHouseConfig.java` - JdbcTemplate bean - `cameleer-server-app/src/main/java/.../config/ClickHouseConfig.java` - JdbcTemplate bean
- `cameleer3-server-core/src/test/java/.../ingestion/WriteBufferTest.java` - 10 unit tests for WriteBuffer - `cameleer-server-core/src/test/java/.../ingestion/WriteBufferTest.java` - 10 unit tests for WriteBuffer
- `cameleer3-server-core/pom.xml` - Added slf4j-api - `cameleer-server-core/pom.xml` - Added slf4j-api
- `cameleer3-server-app/pom.xml` - Added clickhouse-jdbc, springdoc, actuator, testcontainers, awaitility - `cameleer-server-app/pom.xml` - Added clickhouse-jdbc, springdoc, actuator, testcontainers, awaitility
- `cameleer3-server-app/src/main/resources/application.yml` - Full config with datasource, ingestion, springdoc, actuator - `cameleer-server-app/src/main/resources/application.yml` - Full config with datasource, ingestion, springdoc, actuator
## Decisions Made ## Decisions Made
- Used spring-boot-starter-jdbc to get JdbcTemplate and HikariCP auto-configuration rather than manually wiring a DataSource - Used spring-boot-starter-jdbc to get JdbcTemplate and HikariCP auto-configuration rather than manually wiring a DataSource
- Created MetricsSnapshot record in core module since cameleer3-common does not include a metrics model - Created MetricsSnapshot record in core module since cameleer-common does not include a metrics model
- ClickHouseConfig is minimal -- relies on Spring Boot auto-configuring DataSource from spring.datasource properties - ClickHouseConfig is minimal -- relies on Spring Boot auto-configuring DataSource from spring.datasource properties
## Deviations from Plan ## Deviations from Plan

View File

@@ -5,18 +5,18 @@ type: execute
wave: 3 wave: 3
depends_on: ["01-01", "01-03"] depends_on: ["01-01", "01-03"]
files_modified: files_modified:
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/ExecutionController.java - cameleer-server-app/src/main/java/com/cameleer/server/app/controller/ExecutionController.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/DiagramController.java - cameleer-server-app/src/main/java/com/cameleer/server/app/controller/DiagramController.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/MetricsController.java - cameleer-server-app/src/main/java/com/cameleer/server/app/controller/MetricsController.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java - cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseExecutionRepository.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseDiagramRepository.java - cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseDiagramRepository.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseMetricsRepository.java - cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseMetricsRepository.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/ingestion/ClickHouseFlushScheduler.java - cameleer-server-app/src/main/java/com/cameleer/server/app/ingestion/ClickHouseFlushScheduler.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/ingestion/IngestionService.java - cameleer-server-core/src/main/java/com/cameleer/server/core/ingestion/IngestionService.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/ExecutionControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/ExecutionControllerIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/DiagramControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DiagramControllerIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/MetricsControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/MetricsControllerIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/BackpressureIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/BackpressureIT.java
autonomous: true autonomous: true
requirements: requirements:
- INGST-01 - INGST-01
@@ -32,16 +32,16 @@ must_haves:
- "Data posted to endpoints appears in ClickHouse after flush interval" - "Data posted to endpoints appears in ClickHouse after flush interval"
- "When buffer is full, endpoints return 503 with Retry-After header" - "When buffer is full, endpoints return 503 with Retry-After header"
artifacts: artifacts:
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/ExecutionController.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/controller/ExecutionController.java"
provides: "POST /api/v1/data/executions endpoint" provides: "POST /api/v1/data/executions endpoint"
min_lines: 20 min_lines: 20
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseExecutionRepository.java"
provides: "Batch insert to route_executions table via JdbcTemplate" provides: "Batch insert to route_executions table via JdbcTemplate"
min_lines: 30 min_lines: 30
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/ingestion/ClickHouseFlushScheduler.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/ingestion/ClickHouseFlushScheduler.java"
provides: "Scheduled drain of WriteBuffer into ClickHouse" provides: "Scheduled drain of WriteBuffer into ClickHouse"
min_lines: 20 min_lines: 20
- path: "cameleer3-server-core/src/main/java/com/cameleer3/server/core/ingestion/IngestionService.java" - path: "cameleer-server-core/src/main/java/com/cameleer/server/core/ingestion/IngestionService.java"
provides: "Routes data to appropriate WriteBuffer instances" provides: "Routes data to appropriate WriteBuffer instances"
min_lines: 20 min_lines: 20
key_links: key_links:
@@ -92,7 +92,7 @@ Output: Working ingestion flow verified by integration tests against Testcontain
<!-- Interfaces from Plan 01 that this plan depends on --> <!-- Interfaces from Plan 01 that this plan depends on -->
<interfaces> <interfaces>
From cameleer3-server-core WriteBuffer.java: From cameleer-server-core WriteBuffer.java:
```java ```java
public class WriteBuffer<T> { public class WriteBuffer<T> {
public WriteBuffer(int capacity); public WriteBuffer(int capacity);
@@ -106,7 +106,7 @@ public class WriteBuffer<T> {
} }
``` ```
From cameleer3-server-core repository interfaces: From cameleer-server-core repository interfaces:
```java ```java
public interface ExecutionRepository { public interface ExecutionRepository {
void insertBatch(List<RouteExecution> executions); void insertBatch(List<RouteExecution> executions);
@@ -138,11 +138,11 @@ public class IngestionConfig {
<task type="auto" tdd="false"> <task type="auto" tdd="false">
<name>Task 1: IngestionService, ClickHouse repositories, and flush scheduler</name> <name>Task 1: IngestionService, ClickHouse repositories, and flush scheduler</name>
<files> <files>
cameleer3-server-core/src/main/java/com/cameleer3/server/core/ingestion/IngestionService.java, cameleer-server-core/src/main/java/com/cameleer/server/core/ingestion/IngestionService.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java, cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseExecutionRepository.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseDiagramRepository.java, cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseDiagramRepository.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseMetricsRepository.java, cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseMetricsRepository.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/ingestion/ClickHouseFlushScheduler.java cameleer-server-app/src/main/java/com/cameleer/server/app/ingestion/ClickHouseFlushScheduler.java
</files> </files>
<action> <action>
1. Create IngestionService in core module (no Spring annotations -- it's a plain class): 1. Create IngestionService in core module (no Spring annotations -- it's a plain class):
@@ -188,13 +188,13 @@ public class IngestionConfig {
<task type="auto" tdd="true"> <task type="auto" tdd="true">
<name>Task 2: Ingestion REST controllers and integration tests</name> <name>Task 2: Ingestion REST controllers and integration tests</name>
<files> <files>
cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/ExecutionController.java, cameleer-server-app/src/main/java/com/cameleer/server/app/controller/ExecutionController.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/DiagramController.java, cameleer-server-app/src/main/java/com/cameleer/server/app/controller/DiagramController.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/MetricsController.java, cameleer-server-app/src/main/java/com/cameleer/server/app/controller/MetricsController.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/ExecutionControllerIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/controller/ExecutionControllerIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/DiagramControllerIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DiagramControllerIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/MetricsControllerIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/controller/MetricsControllerIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/BackpressureIT.java cameleer-server-app/src/test/java/com/cameleer/server/app/controller/BackpressureIT.java
</files> </files>
<behavior> <behavior>
- POST /api/v1/data/executions with single RouteExecution JSON returns 202 - POST /api/v1/data/executions with single RouteExecution JSON returns 202
@@ -245,7 +245,7 @@ public class IngestionConfig {
Note: All integration tests must include X-Cameleer-Protocol-Version:1 header (API-04 will be enforced by Plan 03's interceptor, but include the header now for forward compatibility). Note: All integration tests must include X-Cameleer-Protocol-Version:1 header (API-04 will be enforced by Plan 03's interceptor, but include the header now for forward compatibility).
</action> </action>
<verify> <verify>
<automated>mvn test -pl cameleer3-server-app -Dtest="ExecutionControllerIT,DiagramControllerIT,MetricsControllerIT,BackpressureIT" -q 2>&1 | tail -15</automated> <automated>mvn test -pl cameleer-server-app -Dtest="ExecutionControllerIT,DiagramControllerIT,MetricsControllerIT,BackpressureIT" -q 2>&1 | tail -15</automated>
</verify> </verify>
<done>All three ingestion endpoints return 202 on valid data. Data arrives in ClickHouse after flush. Buffer-full returns 503 with Retry-After. Unknown JSON fields accepted. Integration tests green.</done> <done>All three ingestion endpoints return 202 on valid data. Data arrives in ClickHouse after flush. Buffer-full returns 503 with Retry-After. Unknown JSON fields accepted. Integration tests green.</done>
</task> </task>
@@ -253,7 +253,7 @@ public class IngestionConfig {
</tasks> </tasks>
<verification> <verification>
- `mvn test -pl cameleer3-server-app -Dtest="ExecutionControllerIT,DiagramControllerIT,MetricsControllerIT,BackpressureIT" -q` -- all integration tests pass - `mvn test -pl cameleer-server-app -Dtest="ExecutionControllerIT,DiagramControllerIT,MetricsControllerIT,BackpressureIT" -q` -- all integration tests pass
- POST to /api/v1/data/executions returns 202 - POST to /api/v1/data/executions returns 202
- POST to /api/v1/data/diagrams returns 202 - POST to /api/v1/data/diagrams returns 202
- POST to /api/v1/data/metrics returns 202 - POST to /api/v1/data/metrics returns 202

View File

@@ -30,21 +30,21 @@ tech-stack:
key-files: key-files:
created: created:
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/ingestion/IngestionService.java - cameleer-server-core/src/main/java/com/cameleer/server/core/ingestion/IngestionService.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java - cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseExecutionRepository.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseDiagramRepository.java - cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseDiagramRepository.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseMetricsRepository.java - cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseMetricsRepository.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/ingestion/ClickHouseFlushScheduler.java - cameleer-server-app/src/main/java/com/cameleer/server/app/ingestion/ClickHouseFlushScheduler.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/IngestionBeanConfig.java - cameleer-server-app/src/main/java/com/cameleer/server/app/config/IngestionBeanConfig.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/ExecutionController.java - cameleer-server-app/src/main/java/com/cameleer/server/app/controller/ExecutionController.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/DiagramController.java - cameleer-server-app/src/main/java/com/cameleer/server/app/controller/DiagramController.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/MetricsController.java - cameleer-server-app/src/main/java/com/cameleer/server/app/controller/MetricsController.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/ExecutionControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/ExecutionControllerIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/DiagramControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DiagramControllerIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/MetricsControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/MetricsControllerIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/BackpressureIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/BackpressureIT.java
modified: modified:
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/IngestionConfig.java - cameleer-server-app/src/main/java/com/cameleer/server/app/config/IngestionConfig.java
key-decisions: key-decisions:
- "Controllers accept raw String body and detect single vs array JSON to support both payload formats" - "Controllers accept raw String body and detect single vs array JSON to support both payload formats"
@@ -91,20 +91,20 @@ Each task was committed atomically:
3. **Task 2 GREEN: Ingestion REST controllers with backpressure** - `8fe65f0` (feat) 3. **Task 2 GREEN: Ingestion REST controllers with backpressure** - `8fe65f0` (feat)
## Files Created/Modified ## Files Created/Modified
- `cameleer3-server-core/.../ingestion/IngestionService.java` - Routes data to WriteBuffer instances - `cameleer-server-core/.../ingestion/IngestionService.java` - Routes data to WriteBuffer instances
- `cameleer3-server-app/.../storage/ClickHouseExecutionRepository.java` - Batch insert with parallel processor arrays - `cameleer-server-app/.../storage/ClickHouseExecutionRepository.java` - Batch insert with parallel processor arrays
- `cameleer3-server-app/.../storage/ClickHouseDiagramRepository.java` - JSON storage with SHA-256 content-hash dedup - `cameleer-server-app/.../storage/ClickHouseDiagramRepository.java` - JSON storage with SHA-256 content-hash dedup
- `cameleer3-server-app/.../storage/ClickHouseMetricsRepository.java` - Batch insert for agent_metrics - `cameleer-server-app/.../storage/ClickHouseMetricsRepository.java` - Batch insert for agent_metrics
- `cameleer3-server-app/.../ingestion/ClickHouseFlushScheduler.java` - Scheduled drain + SmartLifecycle shutdown - `cameleer-server-app/.../ingestion/ClickHouseFlushScheduler.java` - Scheduled drain + SmartLifecycle shutdown
- `cameleer3-server-app/.../config/IngestionBeanConfig.java` - WriteBuffer and IngestionService bean wiring - `cameleer-server-app/.../config/IngestionBeanConfig.java` - WriteBuffer and IngestionService bean wiring
- `cameleer3-server-app/.../controller/ExecutionController.java` - POST /api/v1/data/executions - `cameleer-server-app/.../controller/ExecutionController.java` - POST /api/v1/data/executions
- `cameleer3-server-app/.../controller/DiagramController.java` - POST /api/v1/data/diagrams - `cameleer-server-app/.../controller/DiagramController.java` - POST /api/v1/data/diagrams
- `cameleer3-server-app/.../controller/MetricsController.java` - POST /api/v1/data/metrics - `cameleer-server-app/.../controller/MetricsController.java` - POST /api/v1/data/metrics
- `cameleer3-server-app/.../config/IngestionConfig.java` - Removed @Configuration (fix duplicate bean) - `cameleer-server-app/.../config/IngestionConfig.java` - Removed @Configuration (fix duplicate bean)
- `cameleer3-server-app/.../controller/ExecutionControllerIT.java` - 4 tests: single, array, flush, unknown fields - `cameleer-server-app/.../controller/ExecutionControllerIT.java` - 4 tests: single, array, flush, unknown fields
- `cameleer3-server-app/.../controller/DiagramControllerIT.java` - 3 tests: single, array, flush - `cameleer-server-app/.../controller/DiagramControllerIT.java` - 3 tests: single, array, flush
- `cameleer3-server-app/.../controller/MetricsControllerIT.java` - 2 tests: POST, flush - `cameleer-server-app/.../controller/MetricsControllerIT.java` - 2 tests: POST, flush
- `cameleer3-server-app/.../controller/BackpressureIT.java` - 2 tests: 503 response, data not lost - `cameleer-server-app/.../controller/BackpressureIT.java` - 2 tests: 503 response, data not lost
## Decisions Made ## Decisions Made
- Controllers accept raw String body and detect single vs array JSON (starts with `[`), supporting both payload formats per protocol spec - Controllers accept raw String body and detect single vs array JSON (starts with `[`), supporting both payload formats per protocol spec
@@ -119,7 +119,7 @@ Each task was committed atomically:
- **Found during:** Task 2 (integration test context startup) - **Found during:** Task 2 (integration test context startup)
- **Issue:** IngestionConfig had both `@Configuration` and `@ConfigurationProperties`, while `@EnableConfigurationProperties(IngestionConfig.class)` on the app class created a second bean, causing "expected single matching bean but found 2" - **Issue:** IngestionConfig had both `@Configuration` and `@ConfigurationProperties`, while `@EnableConfigurationProperties(IngestionConfig.class)` on the app class created a second bean, causing "expected single matching bean but found 2"
- **Fix:** Removed `@Configuration` from IngestionConfig, relying solely on `@EnableConfigurationProperties` - **Fix:** Removed `@Configuration` from IngestionConfig, relying solely on `@EnableConfigurationProperties`
- **Files modified:** cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/IngestionConfig.java - **Files modified:** cameleer-server-app/src/main/java/com/cameleer/server/app/config/IngestionConfig.java
- **Verification:** Application context starts successfully, all tests pass - **Verification:** Application context starts successfully, all tests pass
- **Committed in:** 8fe65f0 - **Committed in:** 8fe65f0

View File

@@ -5,15 +5,15 @@ type: execute
wave: 2 wave: 2
depends_on: ["01-01"] depends_on: ["01-01"]
files_modified: files_modified:
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/interceptor/ProtocolVersionInterceptor.java - cameleer-server-app/src/main/java/com/cameleer/server/app/interceptor/ProtocolVersionInterceptor.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/WebConfig.java - cameleer-server-app/src/main/java/com/cameleer/server/app/config/WebConfig.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/Cameleer3ServerApplication.java - cameleer-server-app/src/main/java/com/cameleer/server/app/CameleerServerApplication.java
- cameleer3-server-app/src/test/resources/application-test.yml - cameleer-server-app/src/test/resources/application-test.yml
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/AbstractClickHouseIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/AbstractClickHouseIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/interceptor/ProtocolVersionIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/interceptor/ProtocolVersionIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/HealthControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/HealthControllerIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/OpenApiIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/OpenApiIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/ForwardCompatIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/ForwardCompatIT.java
autonomous: true autonomous: true
requirements: requirements:
- API-01 - API-01
@@ -34,13 +34,13 @@ must_haves:
- "Unknown JSON fields in request body do not cause deserialization errors" - "Unknown JSON fields in request body do not cause deserialization errors"
- "ClickHouse tables have TTL clause for 30-day retention" - "ClickHouse tables have TTL clause for 30-day retention"
artifacts: artifacts:
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/interceptor/ProtocolVersionInterceptor.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/interceptor/ProtocolVersionInterceptor.java"
provides: "Validates X-Cameleer-Protocol-Version:1 header on data endpoints" provides: "Validates X-Cameleer-Protocol-Version:1 header on data endpoints"
min_lines: 20 min_lines: 20
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/WebConfig.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/config/WebConfig.java"
provides: "Registers interceptor with path patterns" provides: "Registers interceptor with path patterns"
min_lines: 10 min_lines: 10
- path: "cameleer3-server-app/src/test/java/com/cameleer3/server/app/AbstractClickHouseIT.java" - path: "cameleer-server-app/src/test/java/com/cameleer/server/app/AbstractClickHouseIT.java"
provides: "Shared Testcontainers base class for integration tests" provides: "Shared Testcontainers base class for integration tests"
min_lines: 20 min_lines: 20
key_links: key_links:
@@ -83,11 +83,11 @@ Output: AbstractClickHouseIT base class, working health, Swagger UI, protocol he
<task type="auto"> <task type="auto">
<name>Task 1: Test infrastructure, protocol version interceptor, WebConfig, and Spring Boot application class</name> <name>Task 1: Test infrastructure, protocol version interceptor, WebConfig, and Spring Boot application class</name>
<files> <files>
cameleer3-server-app/src/test/resources/application-test.yml, cameleer-server-app/src/test/resources/application-test.yml,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/AbstractClickHouseIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/AbstractClickHouseIT.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/interceptor/ProtocolVersionInterceptor.java, cameleer-server-app/src/main/java/com/cameleer/server/app/interceptor/ProtocolVersionInterceptor.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/WebConfig.java, cameleer-server-app/src/main/java/com/cameleer/server/app/config/WebConfig.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/Cameleer3ServerApplication.java cameleer-server-app/src/main/java/com/cameleer/server/app/CameleerServerApplication.java
</files> </files>
<action> <action>
1. Create application-test.yml for test profile: 1. Create application-test.yml for test profile:
@@ -113,15 +113,15 @@ Output: AbstractClickHouseIT base class, working health, Swagger UI, protocol he
- Override addInterceptors: register interceptor with pathPatterns "/api/v1/data/**" and "/api/v1/agents/**" - Override addInterceptors: register interceptor with pathPatterns "/api/v1/data/**" and "/api/v1/agents/**"
- Explicitly EXCLUDE: "/api/v1/health", "/api/v1/api-docs/**", "/api/v1/swagger-ui/**", "/api/v1/swagger-ui.html" - Explicitly EXCLUDE: "/api/v1/health", "/api/v1/api-docs/**", "/api/v1/swagger-ui/**", "/api/v1/swagger-ui.html"
5. Create or update Cameleer3ServerApplication: 5. Create or update CameleerServerApplication:
- @SpringBootApplication in package com.cameleer3.server.app - @SpringBootApplication in package com.cameleer.server.app
- @EnableScheduling (needed for ClickHouseFlushScheduler from Plan 02) - @EnableScheduling (needed for ClickHouseFlushScheduler from Plan 02)
- @EnableConfigurationProperties(IngestionConfig.class) - @EnableConfigurationProperties(IngestionConfig.class)
- Main method with SpringApplication.run() - Main method with SpringApplication.run()
- Ensure package scanning covers com.cameleer3.server.app and com.cameleer3.server.core - Ensure package scanning covers com.cameleer.server.app and com.cameleer.server.core
</action> </action>
<verify> <verify>
<automated>mvn clean compile -pl cameleer3-server-app -q 2>&1 | tail -5</automated> <automated>mvn clean compile -pl cameleer-server-app -q 2>&1 | tail -5</automated>
</verify> </verify>
<done>AbstractClickHouseIT base class ready for integration tests. ProtocolVersionInterceptor validates header on data/agent paths. Health, swagger, and api-docs paths excluded. Application class enables scheduling and config properties.</done> <done>AbstractClickHouseIT base class ready for integration tests. ProtocolVersionInterceptor validates header on data/agent paths. Health, swagger, and api-docs paths excluded. Application class enables scheduling and config properties.</done>
</task> </task>
@@ -129,10 +129,10 @@ Output: AbstractClickHouseIT base class, working health, Swagger UI, protocol he
<task type="auto" tdd="true"> <task type="auto" tdd="true">
<name>Task 2: Health, OpenAPI, protocol version, forward compat, and TTL integration tests</name> <name>Task 2: Health, OpenAPI, protocol version, forward compat, and TTL integration tests</name>
<files> <files>
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/HealthControllerIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/controller/HealthControllerIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/OpenApiIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/controller/OpenApiIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/interceptor/ProtocolVersionIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/interceptor/ProtocolVersionIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/ForwardCompatIT.java cameleer-server-app/src/test/java/com/cameleer/server/app/controller/ForwardCompatIT.java
</files> </files>
<behavior> <behavior>
- GET /api/v1/health returns 200 with JSON containing status field - GET /api/v1/health returns 200 with JSON containing status field
@@ -178,7 +178,7 @@ Output: AbstractClickHouseIT base class, working health, Swagger UI, protocol he
Note: All tests that POST to data endpoints must include X-Cameleer-Protocol-Version:1 header. Note: All tests that POST to data endpoints must include X-Cameleer-Protocol-Version:1 header.
</action> </action>
<verify> <verify>
<automated>mvn test -pl cameleer3-server-app -Dtest="HealthControllerIT,OpenApiIT,ProtocolVersionIT,ForwardCompatIT" -q 2>&1 | tail -15</automated> <automated>mvn test -pl cameleer-server-app -Dtest="HealthControllerIT,OpenApiIT,ProtocolVersionIT,ForwardCompatIT" -q 2>&1 | tail -15</automated>
</verify> </verify>
<done>Health returns 200. OpenAPI docs are available and list endpoints. Protocol version header enforced on data paths, not on health/docs. Unknown JSON fields accepted. TTL confirmed in ClickHouse DDL via HealthControllerIT test methods.</done> <done>Health returns 200. OpenAPI docs are available and list endpoints. Protocol version header enforced on data paths, not on health/docs. Unknown JSON fields accepted. TTL confirmed in ClickHouse DDL via HealthControllerIT test methods.</done>
</task> </task>
@@ -186,7 +186,7 @@ Output: AbstractClickHouseIT base class, working health, Swagger UI, protocol he
</tasks> </tasks>
<verification> <verification>
- `mvn test -pl cameleer3-server-app -Dtest="HealthControllerIT,OpenApiIT,ProtocolVersionIT,ForwardCompatIT" -q` -- all tests pass - `mvn test -pl cameleer-server-app -Dtest="HealthControllerIT,OpenApiIT,ProtocolVersionIT,ForwardCompatIT" -q` -- all tests pass
- GET /api/v1/health returns 200 - GET /api/v1/health returns 200
- GET /api/v1/api-docs returns OpenAPI spec - GET /api/v1/api-docs returns OpenAPI spec
- Missing protocol header returns 400 on data endpoints - Missing protocol header returns 400 on data endpoints

View File

@@ -12,7 +12,7 @@ provides:
- AbstractClickHouseIT base class for all integration tests - AbstractClickHouseIT base class for all integration tests
- ProtocolVersionInterceptor enforcing X-Cameleer-Protocol-Version:1 on data/agent paths - ProtocolVersionInterceptor enforcing X-Cameleer-Protocol-Version:1 on data/agent paths
- WebConfig with interceptor registration and path exclusions - WebConfig with interceptor registration and path exclusions
- Cameleer3ServerApplication with @EnableScheduling and component scanning - CameleerServerApplication with @EnableScheduling and component scanning
- 12 passing integration tests (health, OpenAPI, protocol version, forward compat, TTL) - 12 passing integration tests (health, OpenAPI, protocol version, forward compat, TTL)
affects: [01-02, 02-search, 03-agent-registry] affects: [01-02, 02-search, 03-agent-registry]
@@ -23,17 +23,17 @@ tech-stack:
key-files: key-files:
created: created:
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/Cameleer3ServerApplication.java - cameleer-server-app/src/main/java/com/cameleer/server/app/CameleerServerApplication.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/interceptor/ProtocolVersionInterceptor.java - cameleer-server-app/src/main/java/com/cameleer/server/app/interceptor/ProtocolVersionInterceptor.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/WebConfig.java - cameleer-server-app/src/main/java/com/cameleer/server/app/config/WebConfig.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/AbstractClickHouseIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/AbstractClickHouseIT.java
- cameleer3-server-app/src/test/resources/application-test.yml - cameleer-server-app/src/test/resources/application-test.yml
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/HealthControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/HealthControllerIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/OpenApiIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/OpenApiIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/interceptor/ProtocolVersionIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/interceptor/ProtocolVersionIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/ForwardCompatIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/ForwardCompatIT.java
modified: modified:
- cameleer3-server-app/pom.xml - cameleer-server-app/pom.xml
- pom.xml - pom.xml
- clickhouse/init/01-schema.sql - clickhouse/init/01-schema.sql
@@ -70,7 +70,7 @@ completed: 2026-03-11
- ProtocolVersionInterceptor validates X-Cameleer-Protocol-Version:1 on /api/v1/data/** and /api/v1/agents/** paths, returning 400 JSON error for missing or wrong version - ProtocolVersionInterceptor validates X-Cameleer-Protocol-Version:1 on /api/v1/data/** and /api/v1/agents/** paths, returning 400 JSON error for missing or wrong version
- AbstractClickHouseIT base class with Testcontainers ClickHouse 25.3, shared static container, schema init from 01-schema.sql - AbstractClickHouseIT base class with Testcontainers ClickHouse 25.3, shared static container, schema init from 01-schema.sql
- 12 integration tests: health endpoint (2), OpenAPI docs (2), protocol version enforcement (5), forward compatibility (1), TTL verification (2) - 12 integration tests: health endpoint (2), OpenAPI docs (2), protocol version enforcement (5), forward compatibility (1), TTL verification (2)
- Cameleer3ServerApplication with @EnableScheduling, @EnableConfigurationProperties, and dual package scanning - CameleerServerApplication with @EnableScheduling, @EnableConfigurationProperties, and dual package scanning
## Task Commits ## Task Commits
@@ -80,17 +80,17 @@ Each task was committed atomically:
2. **Task 2: Integration tests for health, OpenAPI, protocol version, forward compat, and TTL** - `2d3fde3` (test) 2. **Task 2: Integration tests for health, OpenAPI, protocol version, forward compat, and TTL** - `2d3fde3` (test)
## Files Created/Modified ## Files Created/Modified
- `cameleer3-server-app/src/main/java/.../Cameleer3ServerApplication.java` - Spring Boot entry point with scheduling and config properties - `cameleer-server-app/src/main/java/.../CameleerServerApplication.java` - Spring Boot entry point with scheduling and config properties
- `cameleer3-server-app/src/main/java/.../interceptor/ProtocolVersionInterceptor.java` - Validates protocol version header on data/agent paths - `cameleer-server-app/src/main/java/.../interceptor/ProtocolVersionInterceptor.java` - Validates protocol version header on data/agent paths
- `cameleer3-server-app/src/main/java/.../config/WebConfig.java` - Registers interceptor with path patterns and exclusions - `cameleer-server-app/src/main/java/.../config/WebConfig.java` - Registers interceptor with path patterns and exclusions
- `cameleer3-server-app/src/test/java/.../AbstractClickHouseIT.java` - Shared Testcontainers base class for ITs - `cameleer-server-app/src/test/java/.../AbstractClickHouseIT.java` - Shared Testcontainers base class for ITs
- `cameleer3-server-app/src/test/resources/application-test.yml` - Test profile with small buffer config - `cameleer-server-app/src/test/resources/application-test.yml` - Test profile with small buffer config
- `cameleer3-server-app/src/test/java/.../controller/HealthControllerIT.java` - Health endpoint and TTL tests - `cameleer-server-app/src/test/java/.../controller/HealthControllerIT.java` - Health endpoint and TTL tests
- `cameleer3-server-app/src/test/java/.../controller/OpenApiIT.java` - OpenAPI and Swagger UI tests - `cameleer-server-app/src/test/java/.../controller/OpenApiIT.java` - OpenAPI and Swagger UI tests
- `cameleer3-server-app/src/test/java/.../interceptor/ProtocolVersionIT.java` - Protocol header enforcement tests - `cameleer-server-app/src/test/java/.../interceptor/ProtocolVersionIT.java` - Protocol header enforcement tests
- `cameleer3-server-app/src/test/java/.../controller/ForwardCompatIT.java` - Unknown JSON fields test - `cameleer-server-app/src/test/java/.../controller/ForwardCompatIT.java` - Unknown JSON fields test
- `pom.xml` - Override testcontainers.version to 2.0.3 - `pom.xml` - Override testcontainers.version to 2.0.3
- `cameleer3-server-app/pom.xml` - Remove junit-jupiter, upgrade testcontainers-clickhouse to 2.0.3 - `cameleer-server-app/pom.xml` - Remove junit-jupiter, upgrade testcontainers-clickhouse to 2.0.3
- `clickhouse/init/01-schema.sql` - Fix TTL expressions and error column types - `clickhouse/init/01-schema.sql` - Fix TTL expressions and error column types
## Decisions Made ## Decisions Made
@@ -107,7 +107,7 @@ Each task was committed atomically:
- **Found during:** Task 2 (compilation) - **Found during:** Task 2 (compilation)
- **Issue:** org.testcontainers:junit-jupiter:2.0.2 does not exist in Maven Central - **Issue:** org.testcontainers:junit-jupiter:2.0.2 does not exist in Maven Central
- **Fix:** Removed junit-jupiter dependency, upgraded to TC 2.0.3, managed container lifecycle manually via static initializer - **Fix:** Removed junit-jupiter dependency, upgraded to TC 2.0.3, managed container lifecycle manually via static initializer
- **Files modified:** cameleer3-server-app/pom.xml, pom.xml, AbstractClickHouseIT.java - **Files modified:** cameleer-server-app/pom.xml, pom.xml, AbstractClickHouseIT.java
- **Verification:** All tests compile and pass - **Verification:** All tests compile and pass
- **Committed in:** 2d3fde3 - **Committed in:** 2d3fde3

View File

@@ -6,7 +6,7 @@
## Summary ## Summary
Phase 1 establishes the data pipeline and API skeleton for Cameleer3 Server. Agents POST execution data, diagrams, and metrics to REST endpoints; the server buffers these in memory and batch-flushes to ClickHouse. The ClickHouse schema design is the most critical and least reversible decision in this phase -- ORDER BY and partitioning cannot be changed without table recreation. Phase 1 establishes the data pipeline and API skeleton for Cameleer Server. Agents POST execution data, diagrams, and metrics to REST endpoints; the server buffers these in memory and batch-flushes to ClickHouse. The ClickHouse schema design is the most critical and least reversible decision in this phase -- ORDER BY and partitioning cannot be changed without table recreation.
The ClickHouse Java ecosystem has undergone significant changes. The recommended approach is **clickhouse-jdbc v0.9.7** (JDBC V2 driver) with Spring Boot's JdbcTemplate for batch inserts. An alternative is the standalone **client-v2** artifact which offers a POJO-based insert API, but JDBC integration with Spring Boot is more conventional and better documented. ClickHouse now has a native full-text index (TYPE text, GA as of March 2026) that supersedes the older tokenbf_v1 bloom filter approach -- this is relevant for Phase 2 but should be accounted for in schema design now. The ClickHouse Java ecosystem has undergone significant changes. The recommended approach is **clickhouse-jdbc v0.9.7** (JDBC V2 driver) with Spring Boot's JdbcTemplate for batch inserts. An alternative is the standalone **client-v2** artifact which offers a POJO-based insert API, but JDBC integration with Spring Boot is more conventional and better documented. ClickHouse now has a native full-text index (TYPE text, GA as of March 2026) that supersedes the older tokenbf_v1 bloom filter approach -- this is relevant for Phase 2 but should be accounted for in schema design now.
@@ -17,7 +17,7 @@ The ClickHouse Java ecosystem has undergone significant changes. The recommended
| ID | Description | Research Support | | ID | Description | Research Support |
|----|-------------|-----------------| |----|-------------|-----------------|
| INGST-01 (#1) | Accept RouteExecution via POST /api/v1/data/executions, return 202 | REST controller + async write buffer pattern; Jackson deserialization of cameleer3-common models | | INGST-01 (#1) | Accept RouteExecution via POST /api/v1/data/executions, return 202 | REST controller + async write buffer pattern; Jackson deserialization of cameleer-common models |
| INGST-02 (#2) | Accept RouteGraph via POST /api/v1/data/diagrams, return 202 | Same pattern; separate ClickHouse table for diagrams with content-hash dedup | | INGST-02 (#2) | Accept RouteGraph via POST /api/v1/data/diagrams, return 202 | Same pattern; separate ClickHouse table for diagrams with content-hash dedup |
| INGST-03 (#3) | Accept metrics via POST /api/v1/data/metrics, return 202 | Same pattern; separate ClickHouse table for metrics | | INGST-03 (#3) | Accept metrics via POST /api/v1/data/metrics, return 202 | Same pattern; separate ClickHouse table for metrics |
| INGST-04 (#4) | In-memory batch buffer with configurable flush interval/size | ArrayBlockingQueue + @Scheduled flush; configurable via application.yml | | INGST-04 (#4) | In-memory batch buffer with configurable flush interval/size | ArrayBlockingQueue + @Scheduled flush; configurable via application.yml |
@@ -60,7 +60,7 @@ The ClickHouse Java ecosystem has undergone significant changes. The recommended
| ArrayBlockingQueue | LMAX Disruptor | Disruptor is faster under extreme contention but adds complexity; ABQ is sufficient for this throughput | | ArrayBlockingQueue | LMAX Disruptor | Disruptor is faster under extreme contention but adds complexity; ABQ is sufficient for this throughput |
| Spring JdbcTemplate | Raw JDBC PreparedStatement | JdbcTemplate provides cleaner error handling and resource management; no meaningful overhead | | Spring JdbcTemplate | Raw JDBC PreparedStatement | JdbcTemplate provides cleaner error handling and resource management; no meaningful overhead |
**Installation (add to cameleer3-server-app/pom.xml):** **Installation (add to cameleer-server-app/pom.xml):**
```xml ```xml
<!-- ClickHouse JDBC V2 --> <!-- ClickHouse JDBC V2 -->
<dependency> <dependency>
@@ -103,7 +103,7 @@ The ClickHouse Java ecosystem has undergone significant changes. The recommended
</dependency> </dependency>
``` ```
**Add to cameleer3-server-core/pom.xml:** **Add to cameleer-server-core/pom.xml:**
```xml ```xml
<!-- SLF4J for logging (no Spring dependency) --> <!-- SLF4J for logging (no Spring dependency) -->
<dependency> <dependency>
@@ -117,7 +117,7 @@ The ClickHouse Java ecosystem has undergone significant changes. The recommended
### Recommended Project Structure ### Recommended Project Structure
``` ```
cameleer3-server-core/src/main/java/com/cameleer3/server/core/ cameleer-server-core/src/main/java/com/cameleer/server/core/
ingestion/ ingestion/
WriteBuffer.java # Bounded queue + flush logic WriteBuffer.java # Bounded queue + flush logic
IngestionService.java # Accepts data, routes to buffer IngestionService.java # Accepts data, routes to buffer
@@ -126,9 +126,9 @@ cameleer3-server-core/src/main/java/com/cameleer3/server/core/
DiagramRepository.java # Interface: store/retrieve diagrams DiagramRepository.java # Interface: store/retrieve diagrams
MetricsRepository.java # Interface: store metrics MetricsRepository.java # Interface: store metrics
model/ model/
(extend/complement cameleer3-common models as needed) (extend/complement cameleer-common models as needed)
cameleer3-server-app/src/main/java/com/cameleer3/server/app/ cameleer-server-app/src/main/java/com/cameleer/server/app/
config/ config/
ClickHouseConfig.java # DataSource + JdbcTemplate bean ClickHouseConfig.java # DataSource + JdbcTemplate bean
IngestionConfig.java # Buffer size, flush interval from YAML IngestionConfig.java # Buffer size, flush interval from YAML
@@ -424,7 +424,7 @@ services:
environment: environment:
CLICKHOUSE_USER: cameleer CLICKHOUSE_USER: cameleer
CLICKHOUSE_PASSWORD: cameleer_dev CLICKHOUSE_PASSWORD: cameleer_dev
CLICKHOUSE_DB: cameleer3 CLICKHOUSE_DB: cameleer
ulimits: ulimits:
nofile: nofile:
soft: 262144 soft: 262144
@@ -442,7 +442,7 @@ server:
spring: spring:
datasource: datasource:
url: jdbc:ch://localhost:8123/cameleer3 url: jdbc:ch://localhost:8123/cameleer
username: cameleer username: cameleer
password: cameleer_dev password: cameleer_dev
driver-class-name: com.clickhouse.jdbc.ClickHouseDriver driver-class-name: com.clickhouse.jdbc.ClickHouseDriver
@@ -493,10 +493,10 @@ management:
## Open Questions ## Open Questions
1. **Exact cameleer3-common model structure** 1. **Exact cameleer-common model structure**
- What we know: Models include RouteExecution, ProcessorExecution, ExchangeSnapshot, RouteGraph, RouteNode, RouteEdge - What we know: Models include RouteExecution, ProcessorExecution, ExchangeSnapshot, RouteGraph, RouteNode, RouteEdge
- What's unclear: Exact field names, types, nesting structure -- needed to design ClickHouse schema precisely - What's unclear: Exact field names, types, nesting structure -- needed to design ClickHouse schema precisely
- Recommendation: Read cameleer3-common source code before implementing schema. Schema must match the wire format. - Recommendation: Read cameleer-common source code before implementing schema. Schema must match the wire format.
2. **ClickHouse JDBC V2 + HikariCP compatibility** 2. **ClickHouse JDBC V2 + HikariCP compatibility**
- What we know: clickhouse-jdbc 0.9.7 implements JDBC spec; HikariCP is Spring Boot default - What we know: clickhouse-jdbc 0.9.7 implements JDBC spec; HikariCP is Spring Boot default
@@ -515,36 +515,36 @@ management:
| Property | Value | | Property | Value |
|----------|-------| |----------|-------|
| Framework | JUnit 5 (Spring Boot managed) + Testcontainers 2.0.2 | | Framework | JUnit 5 (Spring Boot managed) + Testcontainers 2.0.2 |
| Config file | cameleer3-server-app/src/test/resources/application-test.yml (Wave 0) | | Config file | cameleer-server-app/src/test/resources/application-test.yml (Wave 0) |
| Quick run command | `mvn test -pl cameleer3-server-core -Dtest=WriteBufferTest -q` | | Quick run command | `mvn test -pl cameleer-server-core -Dtest=WriteBufferTest -q` |
| Full suite command | `mvn verify` | | Full suite command | `mvn verify` |
### Phase Requirements -> Test Map ### Phase Requirements -> Test Map
| Req ID | Behavior | Test Type | Automated Command | File Exists? | | Req ID | Behavior | Test Type | Automated Command | File Exists? |
|--------|----------|-----------|-------------------|-------------| |--------|----------|-----------|-------------------|-------------|
| INGST-01 | POST /api/v1/data/executions returns 202, data in ClickHouse | integration | `mvn test -pl cameleer3-server-app -Dtest=ExecutionControllerIT -q` | Wave 0 | | INGST-01 | POST /api/v1/data/executions returns 202, data in ClickHouse | integration | `mvn test -pl cameleer-server-app -Dtest=ExecutionControllerIT -q` | Wave 0 |
| INGST-02 | POST /api/v1/data/diagrams returns 202 | integration | `mvn test -pl cameleer3-server-app -Dtest=DiagramControllerIT -q` | Wave 0 | | INGST-02 | POST /api/v1/data/diagrams returns 202 | integration | `mvn test -pl cameleer-server-app -Dtest=DiagramControllerIT -q` | Wave 0 |
| INGST-03 | POST /api/v1/data/metrics returns 202 | integration | `mvn test -pl cameleer3-server-app -Dtest=MetricsControllerIT -q` | Wave 0 | | INGST-03 | POST /api/v1/data/metrics returns 202 | integration | `mvn test -pl cameleer-server-app -Dtest=MetricsControllerIT -q` | Wave 0 |
| INGST-04 | Buffer flushes at interval/size | unit | `mvn test -pl cameleer3-server-core -Dtest=WriteBufferTest -q` | Wave 0 | | INGST-04 | Buffer flushes at interval/size | unit | `mvn test -pl cameleer-server-core -Dtest=WriteBufferTest -q` | Wave 0 |
| INGST-05 | 503 when buffer full | unit+integration | `mvn test -pl cameleer3-server-app -Dtest=BackpressureIT -q` | Wave 0 | | INGST-05 | 503 when buffer full | unit+integration | `mvn test -pl cameleer-server-app -Dtest=BackpressureIT -q` | Wave 0 |
| INGST-06 | TTL removes old data | integration | `mvn test -pl cameleer3-server-app -Dtest=ClickHouseTtlIT -q` | Wave 0 | | INGST-06 | TTL removes old data | integration | `mvn test -pl cameleer-server-app -Dtest=ClickHouseTtlIT -q` | Wave 0 |
| API-01 | Endpoints under /api/v1/ | integration | Covered by controller ITs | Wave 0 | | API-01 | Endpoints under /api/v1/ | integration | Covered by controller ITs | Wave 0 |
| API-02 | OpenAPI docs available | integration | `mvn test -pl cameleer3-server-app -Dtest=OpenApiIT -q` | Wave 0 | | API-02 | OpenAPI docs available | integration | `mvn test -pl cameleer-server-app -Dtest=OpenApiIT -q` | Wave 0 |
| API-03 | GET /api/v1/health responds | integration | `mvn test -pl cameleer3-server-app -Dtest=HealthControllerIT -q` | Wave 0 | | API-03 | GET /api/v1/health responds | integration | `mvn test -pl cameleer-server-app -Dtest=HealthControllerIT -q` | Wave 0 |
| API-04 | Protocol version header validated | integration | `mvn test -pl cameleer3-server-app -Dtest=ProtocolVersionIT -q` | Wave 0 | | API-04 | Protocol version header validated | integration | `mvn test -pl cameleer-server-app -Dtest=ProtocolVersionIT -q` | Wave 0 |
| API-05 | Unknown JSON fields accepted | unit | `mvn test -pl cameleer3-server-app -Dtest=ForwardCompatIT -q` | Wave 0 | | API-05 | Unknown JSON fields accepted | unit | `mvn test -pl cameleer-server-app -Dtest=ForwardCompatIT -q` | Wave 0 |
### Sampling Rate ### Sampling Rate
- **Per task commit:** `mvn test -pl cameleer3-server-core -q` (unit tests, fast) - **Per task commit:** `mvn test -pl cameleer-server-core -q` (unit tests, fast)
- **Per wave merge:** `mvn verify` (full suite with Testcontainers integration tests) - **Per wave merge:** `mvn verify` (full suite with Testcontainers integration tests)
- **Phase gate:** Full suite green before verification - **Phase gate:** Full suite green before verification
### Wave 0 Gaps ### Wave 0 Gaps
- [ ] `cameleer3-server-app/src/test/resources/application-test.yml` -- test ClickHouse config - [ ] `cameleer-server-app/src/test/resources/application-test.yml` -- test ClickHouse config
- [ ] `cameleer3-server-core/src/test/java/.../WriteBufferTest.java` -- buffer unit tests - [ ] `cameleer-server-core/src/test/java/.../WriteBufferTest.java` -- buffer unit tests
- [ ] `cameleer3-server-app/src/test/java/.../AbstractClickHouseIT.java` -- shared Testcontainers base class - [ ] `cameleer-server-app/src/test/java/.../AbstractClickHouseIT.java` -- shared Testcontainers base class
- [ ] `cameleer3-server-app/src/test/java/.../ExecutionControllerIT.java` -- ingestion integration test - [ ] `cameleer-server-app/src/test/java/.../ExecutionControllerIT.java` -- ingestion integration test
- [ ] Docker available on test machine for Testcontainers - [ ] Docker available on test machine for Testcontainers
## Sources ## Sources

View File

@@ -18,8 +18,8 @@ created: 2026-03-11
| Property | Value | | Property | Value |
|----------|-------| |----------|-------|
| **Framework** | JUnit 5 (Spring Boot managed) + Testcontainers 2.0.2 | | **Framework** | JUnit 5 (Spring Boot managed) + Testcontainers 2.0.2 |
| **Config file** | cameleer3-server-app/src/test/resources/application-test.yml (Wave 0) | | **Config file** | cameleer-server-app/src/test/resources/application-test.yml (Wave 0) |
| **Quick run command** | `mvn test -pl cameleer3-server-core -Dtest=WriteBufferTest -q` | | **Quick run command** | `mvn test -pl cameleer-server-core -Dtest=WriteBufferTest -q` |
| **Full suite command** | `mvn verify` | | **Full suite command** | `mvn verify` |
| **Estimated runtime** | ~30 seconds | | **Estimated runtime** | ~30 seconds |
@@ -27,7 +27,7 @@ created: 2026-03-11
## Sampling Rate ## Sampling Rate
- **After every task commit:** Run `mvn test -pl cameleer3-server-core -q` - **After every task commit:** Run `mvn test -pl cameleer-server-core -q`
- **After every plan wave:** Run `mvn verify` - **After every plan wave:** Run `mvn verify`
- **Before `/gsd:verify-work`:** Full suite must be green - **Before `/gsd:verify-work`:** Full suite must be green
- **Max feedback latency:** 30 seconds - **Max feedback latency:** 30 seconds
@@ -38,17 +38,17 @@ created: 2026-03-11
| Task ID | Plan | Wave | Requirement | Test Type | Automated Command | File Exists | Status | | Task ID | Plan | Wave | Requirement | Test Type | Automated Command | File Exists | Status |
|---------|------|------|-------------|-----------|-------------------|-------------|--------| |---------|------|------|-------------|-----------|-------------------|-------------|--------|
| 1-01-01 | 01 | 1 | INGST-04 | unit | `mvn test -pl cameleer3-server-core -Dtest=WriteBufferTest -q` | no W0 | pending | | 1-01-01 | 01 | 1 | INGST-04 | unit | `mvn test -pl cameleer-server-core -Dtest=WriteBufferTest -q` | no W0 | pending |
| 1-01-02 | 01 | 1 | INGST-01 | integration | `mvn test -pl cameleer3-server-app -Dtest=ExecutionControllerIT -q` | no W0 | pending | | 1-01-02 | 01 | 1 | INGST-01 | integration | `mvn test -pl cameleer-server-app -Dtest=ExecutionControllerIT -q` | no W0 | pending |
| 1-01-03 | 01 | 1 | INGST-05 | integration | `mvn test -pl cameleer3-server-app -Dtest=BackpressureIT -q` | no W0 | pending | | 1-01-03 | 01 | 1 | INGST-05 | integration | `mvn test -pl cameleer-server-app -Dtest=BackpressureIT -q` | no W0 | pending |
| 1-01-04 | 01 | 1 | INGST-06 | integration | `mvn test -pl cameleer3-server-app -Dtest=HealthControllerIT#ttlConfigured* -q` | no W0 | pending | | 1-01-04 | 01 | 1 | INGST-06 | integration | `mvn test -pl cameleer-server-app -Dtest=HealthControllerIT#ttlConfigured* -q` | no W0 | pending |
| 1-02-01 | 02 | 1 | INGST-01 | integration | `mvn test -pl cameleer3-server-app -Dtest=ExecutionControllerIT -q` | no W0 | pending | | 1-02-01 | 02 | 1 | INGST-01 | integration | `mvn test -pl cameleer-server-app -Dtest=ExecutionControllerIT -q` | no W0 | pending |
| 1-02-02 | 02 | 1 | INGST-02 | integration | `mvn test -pl cameleer3-server-app -Dtest=DiagramControllerIT -q` | no W0 | pending | | 1-02-02 | 02 | 1 | INGST-02 | integration | `mvn test -pl cameleer-server-app -Dtest=DiagramControllerIT -q` | no W0 | pending |
| 1-02-03 | 02 | 1 | INGST-03 | integration | `mvn test -pl cameleer3-server-app -Dtest=MetricsControllerIT -q` | no W0 | pending | | 1-02-03 | 02 | 1 | INGST-03 | integration | `mvn test -pl cameleer-server-app -Dtest=MetricsControllerIT -q` | no W0 | pending |
| 1-02-04 | 02 | 1 | API-02 | integration | `mvn test -pl cameleer3-server-app -Dtest=OpenApiIT -q` | no W0 | pending | | 1-02-04 | 02 | 1 | API-02 | integration | `mvn test -pl cameleer-server-app -Dtest=OpenApiIT -q` | no W0 | pending |
| 1-02-05 | 02 | 1 | API-03 | integration | `mvn test -pl cameleer3-server-app -Dtest=HealthControllerIT -q` | no W0 | pending | | 1-02-05 | 02 | 1 | API-03 | integration | `mvn test -pl cameleer-server-app -Dtest=HealthControllerIT -q` | no W0 | pending |
| 1-02-06 | 02 | 1 | API-04 | integration | `mvn test -pl cameleer3-server-app -Dtest=ProtocolVersionIT -q` | no W0 | pending | | 1-02-06 | 02 | 1 | API-04 | integration | `mvn test -pl cameleer-server-app -Dtest=ProtocolVersionIT -q` | no W0 | pending |
| 1-02-07 | 02 | 1 | API-05 | unit | `mvn test -pl cameleer3-server-app -Dtest=ForwardCompatIT -q` | no W0 | pending | | 1-02-07 | 02 | 1 | API-05 | unit | `mvn test -pl cameleer-server-app -Dtest=ForwardCompatIT -q` | no W0 | pending |
*Status: pending / green / red / flaky* *Status: pending / green / red / flaky*
@@ -56,10 +56,10 @@ created: 2026-03-11
## Wave 0 Requirements ## Wave 0 Requirements
- [ ] `cameleer3-server-app/src/test/resources/application-test.yml` — test ClickHouse config - [ ] `cameleer-server-app/src/test/resources/application-test.yml` — test ClickHouse config
- [ ] `cameleer3-server-core/src/test/java/.../WriteBufferTest.java` — buffer unit tests - [ ] `cameleer-server-core/src/test/java/.../WriteBufferTest.java` — buffer unit tests
- [ ] `cameleer3-server-app/src/test/java/.../AbstractClickHouseIT.java` — shared Testcontainers base class - [ ] `cameleer-server-app/src/test/java/.../AbstractClickHouseIT.java` — shared Testcontainers base class
- [ ] `cameleer3-server-app/src/test/java/.../ExecutionControllerIT.java` — ingestion integration test - [ ] `cameleer-server-app/src/test/java/.../ExecutionControllerIT.java` — ingestion integration test
- [ ] Docker available on test machine for Testcontainers - [ ] Docker available on test machine for Testcontainers
*If none: "Existing infrastructure covers all phase requirements."* *If none: "Existing infrastructure covers all phase requirements."*

View File

@@ -35,27 +35,27 @@ re_verification: false
| Artifact | Expected | Status | Details | | Artifact | Expected | Status | Details |
|---|---|---|---| |---|---|---|---|
| `cameleer3-server-core/src/main/java/com/cameleer3/server/core/ingestion/WriteBuffer.java` | Generic bounded write buffer with offer/drain/isFull | VERIFIED | 80 lines; `ArrayBlockingQueue`-backed; implements `offer`, `offerBatch` (all-or-nothing), `drain`, `isFull`, `size`, `capacity`, `remainingCapacity` | | `cameleer-server-core/src/main/java/com/cameleer/server/core/ingestion/WriteBuffer.java` | Generic bounded write buffer with offer/drain/isFull | VERIFIED | 80 lines; `ArrayBlockingQueue`-backed; implements `offer`, `offerBatch` (all-or-nothing), `drain`, `isFull`, `size`, `capacity`, `remainingCapacity` |
| `clickhouse/init/01-schema.sql` | ClickHouse DDL for all three tables | VERIFIED | Contains `CREATE TABLE route_executions`, `route_diagrams`, `agent_metrics`; correct ENGINE, ORDER BY, PARTITION BY, TTL with `toDateTime()` cast | | `clickhouse/init/01-schema.sql` | ClickHouse DDL for all three tables | VERIFIED | Contains `CREATE TABLE route_executions`, `route_diagrams`, `agent_metrics`; correct ENGINE, ORDER BY, PARTITION BY, TTL with `toDateTime()` cast |
| `docker-compose.yml` | Local ClickHouse service | VERIFIED | `clickhouse/clickhouse-server:25.3`; ports 8123/9000; init volume mount; credentials configured | | `docker-compose.yml` | Local ClickHouse service | VERIFIED | `clickhouse/clickhouse-server:25.3`; ports 8123/9000; init volume mount; credentials configured |
| `cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/ExecutionRepository.java` | Repository interface for execution batch inserts | VERIFIED | Declares `void insertBatch(List<RouteExecution> executions)` | | `cameleer-server-core/src/main/java/com/cameleer/server/core/storage/ExecutionRepository.java` | Repository interface for execution batch inserts | VERIFIED | Declares `void insertBatch(List<RouteExecution> executions)` |
#### Plan 01-02 Artifacts #### Plan 01-02 Artifacts
| Artifact | Expected | Status | Details | | Artifact | Expected | Status | Details |
|---|---|---|---| |---|---|---|---|
| `cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/ExecutionController.java` | POST /api/v1/data/executions endpoint | VERIFIED | 79 lines; `@PostMapping("/executions")`; handles single/array via raw String parsing; returns 202 or 503 + Retry-After | | `cameleer-server-app/src/main/java/com/cameleer/server/app/controller/ExecutionController.java` | POST /api/v1/data/executions endpoint | VERIFIED | 79 lines; `@PostMapping("/executions")`; handles single/array via raw String parsing; returns 202 or 503 + Retry-After |
| `cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java` | Batch insert to route_executions via JdbcTemplate | VERIFIED | 118 lines; `@Repository`; `BatchPreparedStatementSetter`; flattens processor tree to parallel arrays | | `cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseExecutionRepository.java` | Batch insert to route_executions via JdbcTemplate | VERIFIED | 118 lines; `@Repository`; `BatchPreparedStatementSetter`; flattens processor tree to parallel arrays |
| `cameleer3-server-app/src/main/java/com/cameleer3/server/app/ingestion/ClickHouseFlushScheduler.java` | Scheduled drain of WriteBuffer into ClickHouse | VERIFIED | 160 lines; `@Scheduled(fixedDelayString="${ingestion.flush-interval-ms:1000}")`; implements `SmartLifecycle` for shutdown drain | | `cameleer-server-app/src/main/java/com/cameleer/server/app/ingestion/ClickHouseFlushScheduler.java` | Scheduled drain of WriteBuffer into ClickHouse | VERIFIED | 160 lines; `@Scheduled(fixedDelayString="${ingestion.flush-interval-ms:1000}")`; implements `SmartLifecycle` for shutdown drain |
| `cameleer3-server-core/src/main/java/com/cameleer3/server/core/ingestion/IngestionService.java` | Routes data to appropriate WriteBuffer instances | VERIFIED | 115 lines; plain class; `acceptExecution`, `acceptExecutions`, `acceptDiagram`, `acceptDiagrams`, `acceptMetrics`; delegates to typed `WriteBuffer` instances | | `cameleer-server-core/src/main/java/com/cameleer/server/core/ingestion/IngestionService.java` | Routes data to appropriate WriteBuffer instances | VERIFIED | 115 lines; plain class; `acceptExecution`, `acceptExecutions`, `acceptDiagram`, `acceptDiagrams`, `acceptMetrics`; delegates to typed `WriteBuffer` instances |
#### Plan 01-03 Artifacts #### Plan 01-03 Artifacts
| Artifact | Expected | Status | Details | | Artifact | Expected | Status | Details |
|---|---|---|---| |---|---|---|---|
| `cameleer3-server-app/src/main/java/com/cameleer3/server/app/interceptor/ProtocolVersionInterceptor.java` | Validates X-Cameleer-Protocol-Version:1 header on data endpoints | VERIFIED | 47 lines; implements `HandlerInterceptor.preHandle`; returns 400 JSON on missing/wrong version | | `cameleer-server-app/src/main/java/com/cameleer/server/app/interceptor/ProtocolVersionInterceptor.java` | Validates X-Cameleer-Protocol-Version:1 header on data endpoints | VERIFIED | 47 lines; implements `HandlerInterceptor.preHandle`; returns 400 JSON on missing/wrong version |
| `cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/WebConfig.java` | Registers interceptor with path patterns | VERIFIED | 35 lines; `addInterceptors` registers interceptor on `/api/v1/data/**` and `/api/v1/agents/**`; excludes health, api-docs, swagger-ui | | `cameleer-server-app/src/main/java/com/cameleer/server/app/config/WebConfig.java` | Registers interceptor with path patterns | VERIFIED | 35 lines; `addInterceptors` registers interceptor on `/api/v1/data/**` and `/api/v1/agents/**`; excludes health, api-docs, swagger-ui |
| `cameleer3-server-app/src/test/java/com/cameleer3/server/app/AbstractClickHouseIT.java` | Shared Testcontainers base class for integration tests | VERIFIED | 73 lines; static `ClickHouseContainer`; `@DynamicPropertySource`; `@BeforeAll` schema init from SQL file; `JdbcTemplate` exposed to subclasses | | `cameleer-server-app/src/test/java/com/cameleer/server/app/AbstractClickHouseIT.java` | Shared Testcontainers base class for integration tests | VERIFIED | 73 lines; static `ClickHouseContainer`; `@DynamicPropertySource`; `@BeforeAll` schema init from SQL file; `JdbcTemplate` exposed to subclasses |
--- ---
@@ -113,7 +113,7 @@ No orphaned requirements — all 11 IDs declared in plan frontmatter match the R
### Anti-Patterns Found ### Anti-Patterns Found
No anti-patterns detected. Scanned all source files in `cameleer3-server-app/src/main` and `cameleer3-server-core/src/main` for TODO/FIXME/PLACEHOLDER/stub return patterns. None found. No anti-patterns detected. Scanned all source files in `cameleer-server-app/src/main` and `cameleer-server-core/src/main` for TODO/FIXME/PLACEHOLDER/stub return patterns. None found.
One minor observation (not a blocker): One minor observation (not a blocker):

View File

@@ -6,17 +6,17 @@ wave: 1
depends_on: [] depends_on: []
files_modified: files_modified:
- clickhouse/init/02-search-columns.sql - clickhouse/init/02-search-columns.sql
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/search/SearchRequest.java - cameleer-server-core/src/main/java/com/cameleer/server/core/search/SearchRequest.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/search/SearchResult.java - cameleer-server-core/src/main/java/com/cameleer/server/core/search/SearchResult.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/search/SearchEngine.java - cameleer-server-core/src/main/java/com/cameleer/server/core/search/SearchEngine.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/search/SearchService.java - cameleer-server-core/src/main/java/com/cameleer/server/core/search/SearchService.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/search/ExecutionSummary.java - cameleer-server-core/src/main/java/com/cameleer/server/core/search/ExecutionSummary.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/DetailService.java - cameleer-server-core/src/main/java/com/cameleer/server/core/detail/DetailService.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/ExecutionDetail.java - cameleer-server-core/src/main/java/com/cameleer/server/core/detail/ExecutionDetail.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/ProcessorNode.java - cameleer-server-core/src/main/java/com/cameleer/server/core/detail/ProcessorNode.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/ExecutionRepository.java - cameleer-server-core/src/main/java/com/cameleer/server/core/storage/ExecutionRepository.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java - cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseExecutionRepository.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/AbstractClickHouseIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/AbstractClickHouseIT.java
autonomous: true autonomous: true
requirements: requirements:
- SRCH-01 - SRCH-01
@@ -38,21 +38,21 @@ must_haves:
- path: "clickhouse/init/02-search-columns.sql" - path: "clickhouse/init/02-search-columns.sql"
provides: "Schema extension DDL for Phase 2 columns and skip indexes" provides: "Schema extension DDL for Phase 2 columns and skip indexes"
contains: "exchange_bodies" contains: "exchange_bodies"
- path: "cameleer3-server-core/src/main/java/com/cameleer3/server/core/search/SearchEngine.java" - path: "cameleer-server-core/src/main/java/com/cameleer/server/core/search/SearchEngine.java"
provides: "Search backend abstraction interface" provides: "Search backend abstraction interface"
exports: ["SearchEngine"] exports: ["SearchEngine"]
- path: "cameleer3-server-core/src/main/java/com/cameleer3/server/core/search/SearchRequest.java" - path: "cameleer-server-core/src/main/java/com/cameleer/server/core/search/SearchRequest.java"
provides: "Immutable search criteria record" provides: "Immutable search criteria record"
exports: ["SearchRequest"] exports: ["SearchRequest"]
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseExecutionRepository.java"
provides: "Extended with new columns in INSERT, plus query methods" provides: "Extended with new columns in INSERT, plus query methods"
min_lines: 100 min_lines: 100
key_links: key_links:
- from: "cameleer3-server-core/src/main/java/com/cameleer3/server/core/search/SearchService.java" - from: "cameleer-server-core/src/main/java/com/cameleer/server/core/search/SearchService.java"
to: "SearchEngine" to: "SearchEngine"
via: "constructor injection" via: "constructor injection"
pattern: "SearchEngine" pattern: "SearchEngine"
- from: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java" - from: "cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseExecutionRepository.java"
to: "clickhouse/init/02-search-columns.sql" to: "clickhouse/init/02-search-columns.sql"
via: "INSERT and SELECT SQL matching schema" via: "INSERT and SELECT SQL matching schema"
pattern: "exchange_bodies|processor_depths|diagram_content_hash" pattern: "exchange_bodies|processor_depths|diagram_content_hash"
@@ -79,22 +79,22 @@ Output: Schema migration SQL, updated ingestion INSERT with new columns, core se
@.planning/phases/02-transaction-search-diagrams/02-RESEARCH.md @.planning/phases/02-transaction-search-diagrams/02-RESEARCH.md
@clickhouse/init/01-schema.sql @clickhouse/init/01-schema.sql
@cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/ExecutionRepository.java @cameleer-server-core/src/main/java/com/cameleer/server/core/storage/ExecutionRepository.java
@cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/DiagramRepository.java @cameleer-server-core/src/main/java/com/cameleer/server/core/storage/DiagramRepository.java
@cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java @cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseExecutionRepository.java
@cameleer3-server-app/src/test/java/com/cameleer3/server/app/AbstractClickHouseIT.java @cameleer-server-app/src/test/java/com/cameleer/server/app/AbstractClickHouseIT.java
<interfaces> <interfaces>
<!-- Existing interfaces the executor needs --> <!-- Existing interfaces the executor needs -->
From cameleer3-server-core/.../storage/ExecutionRepository.java: From cameleer-server-core/.../storage/ExecutionRepository.java:
```java ```java
public interface ExecutionRepository { public interface ExecutionRepository {
void insertBatch(List<RouteExecution> executions); void insertBatch(List<RouteExecution> executions);
} }
``` ```
From cameleer3-server-core/.../storage/DiagramRepository.java: From cameleer-server-core/.../storage/DiagramRepository.java:
```java ```java
public interface DiagramRepository { public interface DiagramRepository {
void store(RouteGraph graph); void store(RouteGraph graph);
@@ -103,7 +103,7 @@ public interface DiagramRepository {
} }
``` ```
From cameleer3-common (decompiled — key fields): From cameleer-common (decompiled — key fields):
```java ```java
// RouteExecution: routeId, status (ExecutionStatus enum: COMPLETED/FAILED/RUNNING), // RouteExecution: routeId, status (ExecutionStatus enum: COMPLETED/FAILED/RUNNING),
// startTime (Instant), endTime (Instant), durationMs (long), correlationId, exchangeId, // startTime (Instant), endTime (Instant), durationMs (long), correlationId, exchangeId,
@@ -145,15 +145,15 @@ Existing ClickHouse schema (01-schema.sql):
<name>Task 1: Schema extension and core domain types</name> <name>Task 1: Schema extension and core domain types</name>
<files> <files>
clickhouse/init/02-search-columns.sql, clickhouse/init/02-search-columns.sql,
cameleer3-server-core/src/main/java/com/cameleer3/server/core/search/SearchRequest.java, cameleer-server-core/src/main/java/com/cameleer/server/core/search/SearchRequest.java,
cameleer3-server-core/src/main/java/com/cameleer3/server/core/search/SearchResult.java, cameleer-server-core/src/main/java/com/cameleer/server/core/search/SearchResult.java,
cameleer3-server-core/src/main/java/com/cameleer3/server/core/search/SearchEngine.java, cameleer-server-core/src/main/java/com/cameleer/server/core/search/SearchEngine.java,
cameleer3-server-core/src/main/java/com/cameleer3/server/core/search/SearchService.java, cameleer-server-core/src/main/java/com/cameleer/server/core/search/SearchService.java,
cameleer3-server-core/src/main/java/com/cameleer3/server/core/search/ExecutionSummary.java, cameleer-server-core/src/main/java/com/cameleer/server/core/search/ExecutionSummary.java,
cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/DetailService.java, cameleer-server-core/src/main/java/com/cameleer/server/core/detail/DetailService.java,
cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/ExecutionDetail.java, cameleer-server-core/src/main/java/com/cameleer/server/core/detail/ExecutionDetail.java,
cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/ProcessorNode.java, cameleer-server-core/src/main/java/com/cameleer/server/core/detail/ProcessorNode.java,
cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/ExecutionRepository.java cameleer-server-core/src/main/java/com/cameleer/server/core/storage/ExecutionRepository.java
</files> </files>
<action> <action>
1. Create `clickhouse/init/02-search-columns.sql` with ALTER TABLE statements to add Phase 2 columns to route_executions: 1. Create `clickhouse/init/02-search-columns.sql` with ALTER TABLE statements to add Phase 2 columns to route_executions:
@@ -172,14 +172,14 @@ Existing ClickHouse schema (01-schema.sql):
- Add tokenbf_v1 skip indexes on exchange_bodies and exchange_headers (GRANULARITY 4, same as idx_error) - Add tokenbf_v1 skip indexes on exchange_bodies and exchange_headers (GRANULARITY 4, same as idx_error)
- Add tokenbf_v1 skip index on error_stacktrace (it has no index yet, needed for SRCH-05 full-text search across stack traces) - Add tokenbf_v1 skip index on error_stacktrace (it has no index yet, needed for SRCH-05 full-text search across stack traces)
2. Create core search domain types in `com.cameleer3.server.core.search`: 2. Create core search domain types in `com.cameleer.server.core.search`:
- `SearchRequest` record: status (String, nullable), timeFrom (Instant), timeTo (Instant), durationMin (Long), durationMax (Long), correlationId (String), text (String — global full-text), textInBody (String), textInHeaders (String), textInErrors (String), offset (int), limit (int). Compact constructor validates: limit defaults to 50 if <= 0, capped at 500; offset defaults to 0 if < 0. - `SearchRequest` record: status (String, nullable), timeFrom (Instant), timeTo (Instant), durationMin (Long), durationMax (Long), correlationId (String), text (String — global full-text), textInBody (String), textInHeaders (String), textInErrors (String), offset (int), limit (int). Compact constructor validates: limit defaults to 50 if <= 0, capped at 500; offset defaults to 0 if < 0.
- `SearchResult<T>` record: data (List<T>), total (long), offset (int), limit (int). Include static factory `empty(int offset, int limit)`. - `SearchResult<T>` record: data (List<T>), total (long), offset (int), limit (int). Include static factory `empty(int offset, int limit)`.
- `ExecutionSummary` record: executionId (String), routeId (String), agentId (String), status (String), startTime (Instant), endTime (Instant), durationMs (long), correlationId (String), errorMessage (String), diagramContentHash (String). This is the lightweight list-view DTO — NOT the full processor arrays. - `ExecutionSummary` record: executionId (String), routeId (String), agentId (String), status (String), startTime (Instant), endTime (Instant), durationMs (long), correlationId (String), errorMessage (String), diagramContentHash (String). This is the lightweight list-view DTO — NOT the full processor arrays.
- `SearchEngine` interface with methods: `SearchResult<ExecutionSummary> search(SearchRequest request)` and `long count(SearchRequest request)`. This is the swappable backend (ClickHouse now, OpenSearch later per user decision). - `SearchEngine` interface with methods: `SearchResult<ExecutionSummary> search(SearchRequest request)` and `long count(SearchRequest request)`. This is the swappable backend (ClickHouse now, OpenSearch later per user decision).
- `SearchService` class: plain class (no Spring annotations, same pattern as IngestionService). Constructor takes SearchEngine. `search(SearchRequest)` delegates to engine.search(). This thin orchestration layer allows adding cross-cutting concerns later. - `SearchService` class: plain class (no Spring annotations, same pattern as IngestionService). Constructor takes SearchEngine. `search(SearchRequest)` delegates to engine.search(). This thin orchestration layer allows adding cross-cutting concerns later.
3. Create core detail domain types in `com.cameleer3.server.core.detail`: 3. Create core detail domain types in `com.cameleer.server.core.detail`:
- `ProcessorNode` record: processorId (String), processorType (String), status (String), startTime (Instant), endTime (Instant), durationMs (long), diagramNodeId (String), errorMessage (String), errorStackTrace (String), children (List<ProcessorNode>). This is the nested tree node. - `ProcessorNode` record: processorId (String), processorType (String), status (String), startTime (Instant), endTime (Instant), durationMs (long), diagramNodeId (String), errorMessage (String), errorStackTrace (String), children (List<ProcessorNode>). This is the nested tree node.
- `ExecutionDetail` record: executionId (String), routeId (String), agentId (String), status (String), startTime (Instant), endTime (Instant), durationMs (long), correlationId (String), exchangeId (String), errorMessage (String), errorStackTrace (String), diagramContentHash (String), processors (List<ProcessorNode>). This is the full detail response. - `ExecutionDetail` record: executionId (String), routeId (String), agentId (String), status (String), startTime (Instant), endTime (Instant), durationMs (long), correlationId (String), exchangeId (String), errorMessage (String), errorStackTrace (String), diagramContentHash (String), processors (List<ProcessorNode>). This is the full detail response.
- `DetailService` class: plain class (no Spring annotations). Constructor takes ExecutionRepository. Method `getDetail(String executionId)` returns `Optional<ExecutionDetail>`. Calls repository's new `findDetailById` method, then calls `reconstructTree()` to convert flat arrays into nested ProcessorNode tree. The `reconstructTree` method: takes parallel arrays (ids, types, statuses, starts, ends, durations, diagramNodeIds, errorMessages, errorStackTraces, depths, parentIndexes), creates ProcessorNode[] array, then wires children using parentIndexes (parentIndex == -1 means root). - `DetailService` class: plain class (no Spring annotations). Constructor takes ExecutionRepository. Method `getDetail(String executionId)` returns `Optional<ExecutionDetail>`. Calls repository's new `findDetailById` method, then calls `reconstructTree()` to convert flat arrays into nested ProcessorNode tree. The `reconstructTree` method: takes parallel arrays (ids, types, statuses, starts, ends, durations, diagramNodeIds, errorMessages, errorStackTraces, depths, parentIndexes), creates ProcessorNode[] array, then wires children using parentIndexes (parentIndex == -1 means root).
@@ -190,7 +190,7 @@ Existing ClickHouse schema (01-schema.sql):
Actually, use a different approach per the layering: add a `findRawById(String executionId)` method that returns `Optional<RawExecutionRow>` — a new record containing all parallel arrays. DetailService takes this and reconstructs. Create `RawExecutionRow` as a record in the detail package with all fields needed for reconstruction. Actually, use a different approach per the layering: add a `findRawById(String executionId)` method that returns `Optional<RawExecutionRow>` — a new record containing all parallel arrays. DetailService takes this and reconstructs. Create `RawExecutionRow` as a record in the detail package with all fields needed for reconstruction.
</action> </action>
<verify> <verify>
<automated>cd C:/Users/Hendrik/Documents/projects/cameleer3-server && mvn compile -pl cameleer3-server-core</automated> <automated>cd C:/Users/Hendrik/Documents/projects/cameleer-server && mvn compile -pl cameleer-server-core</automated>
</verify> </verify>
<done>Schema migration SQL exists, all core domain types compile, SearchEngine interface and SearchService defined, ExecutionRepository extended with query method, DetailService has tree reconstruction logic</done> <done>Schema migration SQL exists, all core domain types compile, SearchEngine interface and SearchService defined, ExecutionRepository extended with query method, DetailService has tree reconstruction logic</done>
</task> </task>
@@ -198,9 +198,9 @@ Existing ClickHouse schema (01-schema.sql):
<task type="auto" tdd="true"> <task type="auto" tdd="true">
<name>Task 2: Update ingestion to populate new columns and verify with integration test</name> <name>Task 2: Update ingestion to populate new columns and verify with integration test</name>
<files> <files>
cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java, cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseExecutionRepository.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/AbstractClickHouseIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/AbstractClickHouseIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/storage/IngestionSchemaIT.java cameleer-server-app/src/test/java/com/cameleer/server/app/storage/IngestionSchemaIT.java
</files> </files>
<behavior> <behavior>
- Test: After inserting a RouteExecution with processors that have exchange snapshots and nested children, the route_executions row has non-empty exchange_bodies, exchange_headers, processor_depths (correct depth values), processor_parent_indexes (correct parent wiring), processor_input_bodies, processor_output_bodies, processor_input_headers, processor_output_headers, processor_diagram_node_ids, and diagram_content_hash columns - Test: After inserting a RouteExecution with processors that have exchange snapshots and nested children, the route_executions row has non-empty exchange_bodies, exchange_headers, processor_depths (correct depth values), processor_parent_indexes (correct parent wiring), processor_input_bodies, processor_output_bodies, processor_input_headers, processor_output_headers, processor_diagram_node_ids, and diagram_content_hash columns
@@ -231,7 +231,7 @@ Existing ClickHouse schema (01-schema.sql):
- Verifies a second insertion with null snapshots succeeds with empty defaults - Verifies a second insertion with null snapshots succeeds with empty defaults
</action> </action>
<verify> <verify>
<automated>cd C:/Users/Hendrik/Documents/projects/cameleer3-server && mvn test -pl cameleer3-server-app -Dtest=IngestionSchemaIT</automated> <automated>cd C:/Users/Hendrik/Documents/projects/cameleer-server && mvn test -pl cameleer-server-app -Dtest=IngestionSchemaIT</automated>
</verify> </verify>
<done>All new columns populated correctly during ingestion, tree metadata (depth/parent) correct for nested processors, exchange data concatenated for search, existing ingestion tests still pass</done> <done>All new columns populated correctly during ingestion, tree metadata (depth/parent) correct for nested processors, exchange data concatenated for search, existing ingestion tests still pass</done>
</task> </task>
@@ -239,9 +239,9 @@ Existing ClickHouse schema (01-schema.sql):
</tasks> </tasks>
<verification> <verification>
- `mvn compile -pl cameleer3-server-core` succeeds (core domain types compile) - `mvn compile -pl cameleer-server-core` succeeds (core domain types compile)
- `mvn test -pl cameleer3-server-app -Dtest=IngestionSchemaIT` passes (new columns populated correctly) - `mvn test -pl cameleer-server-app -Dtest=IngestionSchemaIT` passes (new columns populated correctly)
- `mvn test -pl cameleer3-server-app` passes (all existing tests still green with schema extension) - `mvn test -pl cameleer-server-app` passes (all existing tests still green with schema extension)
</verification> </verification>
<success_criteria> <success_criteria>

View File

@@ -22,22 +22,22 @@ tech-stack:
key-files: key-files:
created: created:
- clickhouse/init/02-search-columns.sql - clickhouse/init/02-search-columns.sql
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/search/SearchRequest.java - cameleer-server-core/src/main/java/com/cameleer/server/core/search/SearchRequest.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/search/SearchResult.java - cameleer-server-core/src/main/java/com/cameleer/server/core/search/SearchResult.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/search/ExecutionSummary.java - cameleer-server-core/src/main/java/com/cameleer/server/core/search/ExecutionSummary.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/search/SearchEngine.java - cameleer-server-core/src/main/java/com/cameleer/server/core/search/SearchEngine.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/search/SearchService.java - cameleer-server-core/src/main/java/com/cameleer/server/core/search/SearchService.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/DetailService.java - cameleer-server-core/src/main/java/com/cameleer/server/core/detail/DetailService.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/ExecutionDetail.java - cameleer-server-core/src/main/java/com/cameleer/server/core/detail/ExecutionDetail.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/ProcessorNode.java - cameleer-server-core/src/main/java/com/cameleer/server/core/detail/ProcessorNode.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/RawExecutionRow.java - cameleer-server-core/src/main/java/com/cameleer/server/core/detail/RawExecutionRow.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/diagram/DiagramRenderer.java - cameleer-server-core/src/main/java/com/cameleer/server/core/diagram/DiagramRenderer.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/diagram/DiagramLayout.java - cameleer-server-core/src/main/java/com/cameleer/server/core/diagram/DiagramLayout.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/storage/IngestionSchemaIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/storage/IngestionSchemaIT.java
modified: modified:
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/ExecutionRepository.java - cameleer-server-core/src/main/java/com/cameleer/server/core/storage/ExecutionRepository.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java - cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseExecutionRepository.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/AbstractClickHouseIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/AbstractClickHouseIT.java
key-decisions: key-decisions:
- "FlatProcessor record captures depth and parentIndex during DFS traversal" - "FlatProcessor record captures depth and parentIndex during DFS traversal"
@@ -86,21 +86,21 @@ Each task was committed atomically:
## Files Created/Modified ## Files Created/Modified
- `clickhouse/init/02-search-columns.sql` - ALTER TABLE adding 12 columns + 3 skip indexes - `clickhouse/init/02-search-columns.sql` - ALTER TABLE adding 12 columns + 3 skip indexes
- `cameleer3-server-core/.../search/SearchRequest.java` - Immutable search criteria record with validation - `cameleer-server-core/.../search/SearchRequest.java` - Immutable search criteria record with validation
- `cameleer3-server-core/.../search/SearchResult.java` - Paginated result envelope - `cameleer-server-core/.../search/SearchResult.java` - Paginated result envelope
- `cameleer3-server-core/.../search/ExecutionSummary.java` - Lightweight list-view DTO - `cameleer-server-core/.../search/ExecutionSummary.java` - Lightweight list-view DTO
- `cameleer3-server-core/.../search/SearchEngine.java` - Swappable search backend interface - `cameleer-server-core/.../search/SearchEngine.java` - Swappable search backend interface
- `cameleer3-server-core/.../search/SearchService.java` - Search orchestration layer - `cameleer-server-core/.../search/SearchService.java` - Search orchestration layer
- `cameleer3-server-core/.../detail/DetailService.java` - Tree reconstruction from flat arrays - `cameleer-server-core/.../detail/DetailService.java` - Tree reconstruction from flat arrays
- `cameleer3-server-core/.../detail/ExecutionDetail.java` - Full execution detail record - `cameleer-server-core/.../detail/ExecutionDetail.java` - Full execution detail record
- `cameleer3-server-core/.../detail/ProcessorNode.java` - Nested tree node (mutable children) - `cameleer-server-core/.../detail/ProcessorNode.java` - Nested tree node (mutable children)
- `cameleer3-server-core/.../detail/RawExecutionRow.java` - DB-to-domain intermediate record - `cameleer-server-core/.../detail/RawExecutionRow.java` - DB-to-domain intermediate record
- `cameleer3-server-core/.../diagram/DiagramRenderer.java` - Diagram rendering interface (stub) - `cameleer-server-core/.../diagram/DiagramRenderer.java` - Diagram rendering interface (stub)
- `cameleer3-server-core/.../diagram/DiagramLayout.java` - JSON layout record (stub) - `cameleer-server-core/.../diagram/DiagramLayout.java` - JSON layout record (stub)
- `cameleer3-server-core/.../storage/ExecutionRepository.java` - Extended with findRawById - `cameleer-server-core/.../storage/ExecutionRepository.java` - Extended with findRawById
- `cameleer3-server-app/.../storage/ClickHouseExecutionRepository.java` - INSERT extended with 12 new columns - `cameleer-server-app/.../storage/ClickHouseExecutionRepository.java` - INSERT extended with 12 new columns
- `cameleer3-server-app/src/test/.../AbstractClickHouseIT.java` - Loads 02-search-columns.sql - `cameleer-server-app/src/test/.../AbstractClickHouseIT.java` - Loads 02-search-columns.sql
- `cameleer3-server-app/src/test/.../storage/IngestionSchemaIT.java` - 3 integration tests - `cameleer-server-app/src/test/.../storage/IngestionSchemaIT.java` - 3 integration tests
## Decisions Made ## Decisions Made
- Used FlatProcessor record to carry depth and parentIndex alongside the ProcessorExecution during DFS flattening -- single pass, no separate traversal - Used FlatProcessor record to carry depth and parentIndex alongside the ProcessorExecution during DFS flattening -- single pass, no separate traversal
@@ -116,9 +116,9 @@ Each task was committed atomically:
**1. [Rule 3 - Blocking] Created DiagramRenderer and DiagramLayout stub interfaces** **1. [Rule 3 - Blocking] Created DiagramRenderer and DiagramLayout stub interfaces**
- **Found during:** Task 2 (compilation step) - **Found during:** Task 2 (compilation step)
- **Issue:** Pre-existing `ElkDiagramRenderer` in app module referenced `DiagramRenderer` and `DiagramLayout` interfaces that did not exist in core module, causing compilation failure - **Issue:** Pre-existing `ElkDiagramRenderer` in app module referenced `DiagramRenderer` and `DiagramLayout` interfaces that did not exist in core module, causing compilation failure
- **Fix:** Created minimal stub interfaces in `com.cameleer3.server.core.diagram` package - **Fix:** Created minimal stub interfaces in `com.cameleer.server.core.diagram` package
- **Files created:** DiagramRenderer.java, DiagramLayout.java - **Files created:** DiagramRenderer.java, DiagramLayout.java
- **Verification:** `mvn compile -pl cameleer3-server-core` and `mvn compile -pl cameleer3-server-app` succeed - **Verification:** `mvn compile -pl cameleer-server-core` and `mvn compile -pl cameleer-server-app` succeed
- **Committed in:** f6ff279 (Task 2 GREEN commit) - **Committed in:** f6ff279 (Task 2 GREEN commit)
**2. [Rule 1 - Bug] Fixed ClickHouse Array type handling in IngestionSchemaIT** **2. [Rule 1 - Bug] Fixed ClickHouse Array type handling in IngestionSchemaIT**

View File

@@ -5,16 +5,16 @@ type: execute
wave: 1 wave: 1
depends_on: [] depends_on: []
files_modified: files_modified:
- cameleer3-server-app/pom.xml - cameleer-server-app/pom.xml
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/diagram/DiagramRenderer.java - cameleer-server-core/src/main/java/com/cameleer/server/core/diagram/DiagramRenderer.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/diagram/DiagramLayout.java - cameleer-server-core/src/main/java/com/cameleer/server/core/diagram/DiagramLayout.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/diagram/PositionedNode.java - cameleer-server-core/src/main/java/com/cameleer/server/core/diagram/PositionedNode.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/diagram/PositionedEdge.java - cameleer-server-core/src/main/java/com/cameleer/server/core/diagram/PositionedEdge.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/diagram/ElkDiagramRenderer.java - cameleer-server-app/src/main/java/com/cameleer/server/app/diagram/ElkDiagramRenderer.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/DiagramRenderController.java - cameleer-server-app/src/main/java/com/cameleer/server/app/controller/DiagramRenderController.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/DiagramBeanConfig.java - cameleer-server-app/src/main/java/com/cameleer/server/app/config/DiagramBeanConfig.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/DiagramRenderControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DiagramRenderControllerIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/diagram/ElkDiagramRendererTest.java - cameleer-server-app/src/test/java/com/cameleer/server/app/diagram/ElkDiagramRendererTest.java
autonomous: true autonomous: true
requirements: requirements:
- DIAG-03 - DIAG-03
@@ -27,13 +27,13 @@ must_haves:
- "Node colors match the route-diagram-example.html style: blue endpoints, green processors, red error handlers, purple EIPs" - "Node colors match the route-diagram-example.html style: blue endpoints, green processors, red error handlers, purple EIPs"
- "Nested processors (inside split, choice, try-catch) are rendered in compound/swimlane groups" - "Nested processors (inside split, choice, try-catch) are rendered in compound/swimlane groups"
artifacts: artifacts:
- path: "cameleer3-server-core/src/main/java/com/cameleer3/server/core/diagram/DiagramRenderer.java" - path: "cameleer-server-core/src/main/java/com/cameleer/server/core/diagram/DiagramRenderer.java"
provides: "Renderer interface for SVG and JSON layout output" provides: "Renderer interface for SVG and JSON layout output"
exports: ["DiagramRenderer"] exports: ["DiagramRenderer"]
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/diagram/ElkDiagramRenderer.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/diagram/ElkDiagramRenderer.java"
provides: "ELK + JFreeSVG implementation of DiagramRenderer" provides: "ELK + JFreeSVG implementation of DiagramRenderer"
min_lines: 100 min_lines: 100
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/DiagramRenderController.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/controller/DiagramRenderController.java"
provides: "GET /api/v1/diagrams/{hash} with content negotiation" provides: "GET /api/v1/diagrams/{hash} with content negotiation"
exports: ["DiagramRenderController"] exports: ["DiagramRenderController"]
key_links: key_links:
@@ -70,14 +70,14 @@ Output: DiagramRenderer interface in core, ElkDiagramRenderer implementation in
@.planning/phases/02-transaction-search-diagrams/02-CONTEXT.md @.planning/phases/02-transaction-search-diagrams/02-CONTEXT.md
@.planning/phases/02-transaction-search-diagrams/02-RESEARCH.md @.planning/phases/02-transaction-search-diagrams/02-RESEARCH.md
@cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/DiagramRepository.java @cameleer-server-core/src/main/java/com/cameleer/server/core/storage/DiagramRepository.java
@cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseDiagramRepository.java @cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseDiagramRepository.java
@cameleer3-server-app/pom.xml @cameleer-server-app/pom.xml
<interfaces> <interfaces>
<!-- Existing interfaces needed --> <!-- Existing interfaces needed -->
From cameleer3-server-core/.../storage/DiagramRepository.java: From cameleer-server-core/.../storage/DiagramRepository.java:
```java ```java
public interface DiagramRepository { public interface DiagramRepository {
void store(RouteGraph graph); void store(RouteGraph graph);
@@ -86,7 +86,7 @@ public interface DiagramRepository {
} }
``` ```
From cameleer3-common (decompiled — diagram models): From cameleer-common (decompiled — diagram models):
```java ```java
// RouteGraph: routeId (String), nodes (List<RouteNode>), edges (List<RouteEdge>), // RouteGraph: routeId (String), nodes (List<RouteNode>), edges (List<RouteEdge>),
// processorNodeMapping (Map<String,String>) // processorNodeMapping (Map<String,String>)
@@ -114,14 +114,14 @@ NodeType color mapping (from CONTEXT.md, matching route-diagram-example.html):
<task type="auto"> <task type="auto">
<name>Task 1: Add ELK/JFreeSVG dependencies and create core diagram rendering interfaces</name> <name>Task 1: Add ELK/JFreeSVG dependencies and create core diagram rendering interfaces</name>
<files> <files>
cameleer3-server-app/pom.xml, cameleer-server-app/pom.xml,
cameleer3-server-core/src/main/java/com/cameleer3/server/core/diagram/DiagramRenderer.java, cameleer-server-core/src/main/java/com/cameleer/server/core/diagram/DiagramRenderer.java,
cameleer3-server-core/src/main/java/com/cameleer3/server/core/diagram/DiagramLayout.java, cameleer-server-core/src/main/java/com/cameleer/server/core/diagram/DiagramLayout.java,
cameleer3-server-core/src/main/java/com/cameleer3/server/core/diagram/PositionedNode.java, cameleer-server-core/src/main/java/com/cameleer/server/core/diagram/PositionedNode.java,
cameleer3-server-core/src/main/java/com/cameleer3/server/core/diagram/PositionedEdge.java cameleer-server-core/src/main/java/com/cameleer/server/core/diagram/PositionedEdge.java
</files> </files>
<action> <action>
1. Add Maven dependencies to `cameleer3-server-app/pom.xml`: 1. Add Maven dependencies to `cameleer-server-app/pom.xml`:
```xml ```xml
<dependency> <dependency>
<groupId>org.eclipse.elk</groupId> <groupId>org.eclipse.elk</groupId>
@@ -140,7 +140,7 @@ NodeType color mapping (from CONTEXT.md, matching route-diagram-example.html):
</dependency> </dependency>
``` ```
2. Create core diagram rendering interfaces in `com.cameleer3.server.core.diagram`: 2. Create core diagram rendering interfaces in `com.cameleer.server.core.diagram`:
- `PositionedNode` record: id (String), label (String), type (String — NodeType name), x (double), y (double), width (double), height (double), children (List<PositionedNode> — for compound/swimlane groups). JSON-serializable for the JSON layout response. - `PositionedNode` record: id (String), label (String), type (String — NodeType name), x (double), y (double), width (double), height (double), children (List<PositionedNode> — for compound/swimlane groups). JSON-serializable for the JSON layout response.
@@ -154,7 +154,7 @@ NodeType color mapping (from CONTEXT.md, matching route-diagram-example.html):
Both methods take a RouteGraph and produce output. The interface lives in core so it can be swapped (e.g., for a different renderer). Both methods take a RouteGraph and produce output. The interface lives in core so it can be swapped (e.g., for a different renderer).
</action> </action>
<verify> <verify>
<automated>cd C:/Users/Hendrik/Documents/projects/cameleer3-server && mvn compile -pl cameleer3-server-core && mvn dependency:resolve -pl cameleer3-server-app -q</automated> <automated>cd C:/Users/Hendrik/Documents/projects/cameleer-server && mvn compile -pl cameleer-server-core && mvn dependency:resolve -pl cameleer-server-app -q</automated>
</verify> </verify>
<done>ELK and JFreeSVG dependencies resolve, DiagramRenderer interface and layout DTOs compile in core module</done> <done>ELK and JFreeSVG dependencies resolve, DiagramRenderer interface and layout DTOs compile in core module</done>
</task> </task>
@@ -162,11 +162,11 @@ NodeType color mapping (from CONTEXT.md, matching route-diagram-example.html):
<task type="auto" tdd="true"> <task type="auto" tdd="true">
<name>Task 2: Implement ElkDiagramRenderer, DiagramRenderController, and integration tests</name> <name>Task 2: Implement ElkDiagramRenderer, DiagramRenderController, and integration tests</name>
<files> <files>
cameleer3-server-app/src/main/java/com/cameleer3/server/app/diagram/ElkDiagramRenderer.java, cameleer-server-app/src/main/java/com/cameleer/server/app/diagram/ElkDiagramRenderer.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/DiagramRenderController.java, cameleer-server-app/src/main/java/com/cameleer/server/app/controller/DiagramRenderController.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/DiagramBeanConfig.java, cameleer-server-app/src/main/java/com/cameleer/server/app/config/DiagramBeanConfig.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/diagram/ElkDiagramRendererTest.java, cameleer-server-app/src/test/java/com/cameleer/server/app/diagram/ElkDiagramRendererTest.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/DiagramRenderControllerIT.java cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DiagramRenderControllerIT.java
</files> </files>
<behavior> <behavior>
- Unit test: ElkDiagramRenderer.renderSvg with a simple 3-node graph (from->process->to) produces valid SVG containing svg element, rect elements for nodes, line/path elements for edges - Unit test: ElkDiagramRenderer.renderSvg with a simple 3-node graph (from->process->to) produces valid SVG containing svg element, rect elements for nodes, line/path elements for edges
@@ -179,7 +179,7 @@ NodeType color mapping (from CONTEXT.md, matching route-diagram-example.html):
- Integration test: GET /api/v1/diagrams/{hash} with no Accept preference defaults to SVG - Integration test: GET /api/v1/diagrams/{hash} with no Accept preference defaults to SVG
</behavior> </behavior>
<action> <action>
1. Create `ElkDiagramRenderer` implementing `DiagramRenderer` in `com.cameleer3.server.app.diagram`: 1. Create `ElkDiagramRenderer` implementing `DiagramRenderer` in `com.cameleer.server.app.diagram`:
**Layout phase (shared by both SVG and JSON):** **Layout phase (shared by both SVG and JSON):**
- Convert RouteGraph to ELK graph: create ElkNode root, set properties for LayeredOptions.ALGORITHM_ID, Direction.DOWN (top-to-bottom per user decision), spacing 40px node-node, 20px edge-node. - Convert RouteGraph to ELK graph: create ElkNode root, set properties for LayeredOptions.ALGORITHM_ID, Direction.DOWN (top-to-bottom per user decision), spacing 40px node-node, 20px edge-node.
@@ -206,10 +206,10 @@ NodeType color mapping (from CONTEXT.md, matching route-diagram-example.html):
**JSON layout (layoutJson):** **JSON layout (layoutJson):**
- Run layout phase, return DiagramLayout directly. Jackson will serialize it to JSON. - Run layout phase, return DiagramLayout directly. Jackson will serialize it to JSON.
2. Create `DiagramBeanConfig` in `com.cameleer3.server.app.config`: 2. Create `DiagramBeanConfig` in `com.cameleer.server.app.config`:
- @Configuration class that creates DiagramRenderer bean (ElkDiagramRenderer) and SearchService bean wiring (prepare for Plan 03). - @Configuration class that creates DiagramRenderer bean (ElkDiagramRenderer) and SearchService bean wiring (prepare for Plan 03).
3. Create `DiagramRenderController` in `com.cameleer3.server.app.controller`: 3. Create `DiagramRenderController` in `com.cameleer.server.app.controller`:
- `GET /api/v1/diagrams/{contentHash}/render` — renders the diagram - `GET /api/v1/diagrams/{contentHash}/render` — renders the diagram
- Inject DiagramRepository and DiagramRenderer. - Inject DiagramRepository and DiagramRenderer.
- Look up RouteGraph via `diagramRepository.findByContentHash(contentHash)`. If empty, return 404. - Look up RouteGraph via `diagramRepository.findByContentHash(contentHash)`. If empty, return 404.
@@ -233,7 +233,7 @@ NodeType color mapping (from CONTEXT.md, matching route-diagram-example.html):
- GET /api/v1/diagrams/{hash}/render with no Accept header -> assert SVG response (default). - GET /api/v1/diagrams/{hash}/render with no Accept header -> assert SVG response (default).
</action> </action>
<verify> <verify>
<automated>cd C:/Users/Hendrik/Documents/projects/cameleer3-server && mvn test -pl cameleer3-server-app -Dtest="ElkDiagramRendererTest,DiagramRenderControllerIT"</automated> <automated>cd C:/Users/Hendrik/Documents/projects/cameleer-server && mvn test -pl cameleer-server-app -Dtest="ElkDiagramRendererTest,DiagramRenderControllerIT"</automated>
</verify> </verify>
<done>Diagram rendering produces color-coded top-to-bottom SVG and JSON layout, content negotiation works via Accept header, compound nodes group nested processors, all tests pass</done> <done>Diagram rendering produces color-coded top-to-bottom SVG and JSON layout, content negotiation works via Accept header, compound nodes group nested processors, all tests pass</done>
</task> </task>
@@ -241,8 +241,8 @@ NodeType color mapping (from CONTEXT.md, matching route-diagram-example.html):
</tasks> </tasks>
<verification> <verification>
- `mvn test -pl cameleer3-server-app -Dtest=ElkDiagramRendererTest` passes (unit tests for layout and SVG) - `mvn test -pl cameleer-server-app -Dtest=ElkDiagramRendererTest` passes (unit tests for layout and SVG)
- `mvn test -pl cameleer3-server-app -Dtest=DiagramRenderControllerIT` passes (integration tests for REST endpoint) - `mvn test -pl cameleer-server-app -Dtest=DiagramRenderControllerIT` passes (integration tests for REST endpoint)
- `mvn clean verify` passes (all existing tests still green) - `mvn clean verify` passes (all existing tests still green)
- SVG output contains color-coded nodes matching the NodeType color scheme - SVG output contains color-coded nodes matching the NodeType color scheme
</verification> </verification>

View File

@@ -21,17 +21,17 @@ tech-stack:
key-files: key-files:
created: created:
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/diagram/DiagramRenderer.java - cameleer-server-core/src/main/java/com/cameleer/server/core/diagram/DiagramRenderer.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/diagram/DiagramLayout.java - cameleer-server-core/src/main/java/com/cameleer/server/core/diagram/DiagramLayout.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/diagram/PositionedNode.java - cameleer-server-core/src/main/java/com/cameleer/server/core/diagram/PositionedNode.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/diagram/PositionedEdge.java - cameleer-server-core/src/main/java/com/cameleer/server/core/diagram/PositionedEdge.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/diagram/ElkDiagramRenderer.java - cameleer-server-app/src/main/java/com/cameleer/server/app/diagram/ElkDiagramRenderer.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/DiagramRenderController.java - cameleer-server-app/src/main/java/com/cameleer/server/app/controller/DiagramRenderController.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/DiagramBeanConfig.java - cameleer-server-app/src/main/java/com/cameleer/server/app/config/DiagramBeanConfig.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/diagram/ElkDiagramRendererTest.java - cameleer-server-app/src/test/java/com/cameleer/server/app/diagram/ElkDiagramRendererTest.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/DiagramRenderControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DiagramRenderControllerIT.java
modified: modified:
- cameleer3-server-app/pom.xml - cameleer-server-app/pom.xml
key-decisions: key-decisions:
- "Used ELK layered algorithm with top-to-bottom direction for route diagram layout" - "Used ELK layered algorithm with top-to-bottom direction for route diagram layout"
@@ -78,16 +78,16 @@ Each task was committed atomically:
2. **Task 2: Implement ElkDiagramRenderer, DiagramRenderController, and integration tests** - `c1bc32d` (feat, TDD) 2. **Task 2: Implement ElkDiagramRenderer, DiagramRenderController, and integration tests** - `c1bc32d` (feat, TDD)
## Files Created/Modified ## Files Created/Modified
- `cameleer3-server-core/.../diagram/DiagramRenderer.java` - Renderer interface with renderSvg and layoutJson - `cameleer-server-core/.../diagram/DiagramRenderer.java` - Renderer interface with renderSvg and layoutJson
- `cameleer3-server-core/.../diagram/DiagramLayout.java` - Layout record (width, height, nodes, edges) - `cameleer-server-core/.../diagram/DiagramLayout.java` - Layout record (width, height, nodes, edges)
- `cameleer3-server-core/.../diagram/PositionedNode.java` - Node record with position, dimensions, children - `cameleer-server-core/.../diagram/PositionedNode.java` - Node record with position, dimensions, children
- `cameleer3-server-core/.../diagram/PositionedEdge.java` - Edge record with waypoints - `cameleer-server-core/.../diagram/PositionedEdge.java` - Edge record with waypoints
- `cameleer3-server-app/.../diagram/ElkDiagramRenderer.java` - ELK + JFreeSVG implementation (~400 lines) - `cameleer-server-app/.../diagram/ElkDiagramRenderer.java` - ELK + JFreeSVG implementation (~400 lines)
- `cameleer3-server-app/.../controller/DiagramRenderController.java` - GET /api/v1/diagrams/{hash}/render - `cameleer-server-app/.../controller/DiagramRenderController.java` - GET /api/v1/diagrams/{hash}/render
- `cameleer3-server-app/.../config/DiagramBeanConfig.java` - Spring bean wiring for DiagramRenderer - `cameleer-server-app/.../config/DiagramBeanConfig.java` - Spring bean wiring for DiagramRenderer
- `cameleer3-server-app/pom.xml` - Added ELK, JFreeSVG, xtext dependencies - `cameleer-server-app/pom.xml` - Added ELK, JFreeSVG, xtext dependencies
- `cameleer3-server-app/.../diagram/ElkDiagramRendererTest.java` - 11 unit tests - `cameleer-server-app/.../diagram/ElkDiagramRendererTest.java` - 11 unit tests
- `cameleer3-server-app/.../controller/DiagramRenderControllerIT.java` - 4 integration tests - `cameleer-server-app/.../controller/DiagramRenderControllerIT.java` - 4 integration tests
## Decisions Made ## Decisions Made
- Used ELK layered algorithm (org.eclipse.elk.alg.layered) -- well-maintained, supports compound nodes natively - Used ELK layered algorithm (org.eclipse.elk.alg.layered) -- well-maintained, supports compound nodes natively
@@ -104,7 +104,7 @@ Each task was committed atomically:
- **Found during:** Task 2 (ElkDiagramRenderer implementation) - **Found during:** Task 2 (ElkDiagramRenderer implementation)
- **Issue:** ELK 0.11.0 LayeredMetaDataProvider references org.eclipse.xtext.xbase.lib.CollectionLiterals at class initialization, causing NoClassDefFoundError - **Issue:** ELK 0.11.0 LayeredMetaDataProvider references org.eclipse.xtext.xbase.lib.CollectionLiterals at class initialization, causing NoClassDefFoundError
- **Fix:** Added org.eclipse.xtext:org.eclipse.xtext.xbase.lib:2.37.0 dependency to app pom.xml - **Fix:** Added org.eclipse.xtext:org.eclipse.xtext.xbase.lib:2.37.0 dependency to app pom.xml
- **Files modified:** cameleer3-server-app/pom.xml - **Files modified:** cameleer-server-app/pom.xml
- **Verification:** All unit tests pass after adding dependency - **Verification:** All unit tests pass after adding dependency
- **Committed in:** c1bc32d (Task 2 commit) - **Committed in:** c1bc32d (Task 2 commit)
@@ -119,7 +119,7 @@ Each task was committed atomically:
**3. [Rule 1 - Bug] Adapted to actual NodeType enum naming (EIP_ prefix)** **3. [Rule 1 - Bug] Adapted to actual NodeType enum naming (EIP_ prefix)**
- **Found during:** Task 2 (ElkDiagramRenderer implementation) - **Found during:** Task 2 (ElkDiagramRenderer implementation)
- **Issue:** Plan referenced CHOICE, SPLIT etc. but actual enum values are EIP_CHOICE, EIP_SPLIT etc. - **Issue:** Plan referenced CHOICE, SPLIT etc. but actual enum values are EIP_CHOICE, EIP_SPLIT etc.
- **Fix:** Used correct enum names from decompiled cameleer3-common jar in all color mapping sets - **Fix:** Used correct enum names from decompiled cameleer-common jar in all color mapping sets
- **Files modified:** ElkDiagramRenderer.java - **Files modified:** ElkDiagramRenderer.java
- **Verification:** Unit tests verify correct colors for endpoint and processor nodes - **Verification:** Unit tests verify correct colors for endpoint and processor nodes
- **Committed in:** c1bc32d (Task 2 commit) - **Committed in:** c1bc32d (Task 2 commit)

View File

@@ -6,14 +6,14 @@ wave: 2
depends_on: depends_on:
- "02-01" - "02-01"
files_modified: files_modified:
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/search/ClickHouseSearchEngine.java - cameleer-server-app/src/main/java/com/cameleer/server/app/search/ClickHouseSearchEngine.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/SearchController.java - cameleer-server-app/src/main/java/com/cameleer/server/app/controller/SearchController.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/DetailController.java - cameleer-server-app/src/main/java/com/cameleer/server/app/controller/DetailController.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/SearchBeanConfig.java - cameleer-server-app/src/main/java/com/cameleer/server/app/config/SearchBeanConfig.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java - cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseExecutionRepository.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/SearchControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/SearchControllerIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/DetailControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DetailControllerIT.java
- cameleer3-server-core/src/test/java/com/cameleer3/server/core/detail/TreeReconstructionTest.java - cameleer-server-core/src/test/java/com/cameleer/server/core/detail/TreeReconstructionTest.java
autonomous: true autonomous: true
requirements: requirements:
- SRCH-01 - SRCH-01
@@ -35,16 +35,16 @@ must_haves:
- "Detail response includes diagramContentHash for linking to diagram endpoint" - "Detail response includes diagramContentHash for linking to diagram endpoint"
- "Search results are paginated with total count, offset, and limit" - "Search results are paginated with total count, offset, and limit"
artifacts: artifacts:
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/search/ClickHouseSearchEngine.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/search/ClickHouseSearchEngine.java"
provides: "ClickHouse implementation of SearchEngine with dynamic WHERE building" provides: "ClickHouse implementation of SearchEngine with dynamic WHERE building"
min_lines: 80 min_lines: 80
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/SearchController.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/controller/SearchController.java"
provides: "GET + POST /api/v1/search/executions endpoints" provides: "GET + POST /api/v1/search/executions endpoints"
exports: ["SearchController"] exports: ["SearchController"]
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/DetailController.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/controller/DetailController.java"
provides: "GET /api/v1/executions/{id} endpoint returning nested tree" provides: "GET /api/v1/executions/{id} endpoint returning nested tree"
exports: ["DetailController"] exports: ["DetailController"]
- path: "cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/SearchControllerIT.java" - path: "cameleer-server-app/src/test/java/com/cameleer/server/app/controller/SearchControllerIT.java"
provides: "Integration tests for all search filter combinations" provides: "Integration tests for all search filter combinations"
min_lines: 100 min_lines: 100
key_links: key_links:
@@ -92,13 +92,13 @@ Output: SearchController (GET + POST), DetailController, ClickHouseSearchEngine,
@clickhouse/init/01-schema.sql @clickhouse/init/01-schema.sql
@clickhouse/init/02-search-columns.sql @clickhouse/init/02-search-columns.sql
@cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java @cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseExecutionRepository.java
@cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/ExecutionController.java @cameleer-server-app/src/main/java/com/cameleer/server/app/controller/ExecutionController.java
<interfaces> <interfaces>
<!-- Core types created by Plan 01 — executor reads these from plan 01 SUMMARY --> <!-- Core types created by Plan 01 — executor reads these from plan 01 SUMMARY -->
From cameleer3-server-core/.../search/SearchEngine.java: From cameleer-server-core/.../search/SearchEngine.java:
```java ```java
public interface SearchEngine { public interface SearchEngine {
SearchResult<ExecutionSummary> search(SearchRequest request); SearchResult<ExecutionSummary> search(SearchRequest request);
@@ -106,7 +106,7 @@ public interface SearchEngine {
} }
``` ```
From cameleer3-server-core/.../search/SearchRequest.java: From cameleer-server-core/.../search/SearchRequest.java:
```java ```java
public record SearchRequest( public record SearchRequest(
String status, // nullable, filter by ExecutionStatus name String status, // nullable, filter by ExecutionStatus name
@@ -124,14 +124,14 @@ public record SearchRequest(
) { /* compact constructor with validation */ } ) { /* compact constructor with validation */ }
``` ```
From cameleer3-server-core/.../search/SearchResult.java: From cameleer-server-core/.../search/SearchResult.java:
```java ```java
public record SearchResult<T>(List<T> data, long total, int offset, int limit) { public record SearchResult<T>(List<T> data, long total, int offset, int limit) {
public static <T> SearchResult<T> empty(int offset, int limit); public static <T> SearchResult<T> empty(int offset, int limit);
} }
``` ```
From cameleer3-server-core/.../search/ExecutionSummary.java: From cameleer-server-core/.../search/ExecutionSummary.java:
```java ```java
public record ExecutionSummary( public record ExecutionSummary(
String executionId, String routeId, String agentId, String status, String executionId, String routeId, String agentId, String status,
@@ -140,7 +140,7 @@ public record ExecutionSummary(
) {} ) {}
``` ```
From cameleer3-server-core/.../detail/DetailService.java: From cameleer-server-core/.../detail/DetailService.java:
```java ```java
public class DetailService { public class DetailService {
// Constructor takes ExecutionRepository (or a query interface) // Constructor takes ExecutionRepository (or a query interface)
@@ -149,7 +149,7 @@ public class DetailService {
} }
``` ```
From cameleer3-server-core/.../detail/ExecutionDetail.java: From cameleer-server-core/.../detail/ExecutionDetail.java:
```java ```java
public record ExecutionDetail( public record ExecutionDetail(
String executionId, String routeId, String agentId, String status, String executionId, String routeId, String agentId, String status,
@@ -160,7 +160,7 @@ public record ExecutionDetail(
) {} ) {}
``` ```
From cameleer3-server-core/.../detail/ProcessorNode.java: From cameleer-server-core/.../detail/ProcessorNode.java:
```java ```java
public record ProcessorNode( public record ProcessorNode(
String processorId, String processorType, String status, String processorId, String processorType, String status,
@@ -201,10 +201,10 @@ Established controller pattern (from Phase 1):
<task type="auto" tdd="true"> <task type="auto" tdd="true">
<name>Task 1: ClickHouseSearchEngine, SearchController, and search integration tests</name> <name>Task 1: ClickHouseSearchEngine, SearchController, and search integration tests</name>
<files> <files>
cameleer3-server-app/src/main/java/com/cameleer3/server/app/search/ClickHouseSearchEngine.java, cameleer-server-app/src/main/java/com/cameleer/server/app/search/ClickHouseSearchEngine.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/SearchController.java, cameleer-server-app/src/main/java/com/cameleer/server/app/controller/SearchController.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/SearchBeanConfig.java, cameleer-server-app/src/main/java/com/cameleer/server/app/config/SearchBeanConfig.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/SearchControllerIT.java cameleer-server-app/src/test/java/com/cameleer/server/app/controller/SearchControllerIT.java
</files> </files>
<behavior> <behavior>
- Test searchByStatus: Insert 3 executions (COMPLETED, FAILED, RUNNING). GET /api/v1/search/executions?status=FAILED returns only the FAILED execution. Response has envelope: {"data":[...],"total":1,"offset":0,"limit":50} - Test searchByStatus: Insert 3 executions (COMPLETED, FAILED, RUNNING). GET /api/v1/search/executions?status=FAILED returns only the FAILED execution. Response has envelope: {"data":[...],"total":1,"offset":0,"limit":50}
@@ -221,7 +221,7 @@ Established controller pattern (from Phase 1):
- Test emptyResults: Search with no matches returns {"data":[],"total":0,"offset":0,"limit":50} - Test emptyResults: Search with no matches returns {"data":[],"total":0,"offset":0,"limit":50}
</behavior> </behavior>
<action> <action>
1. Create `ClickHouseSearchEngine` in `com.cameleer3.server.app.search`: 1. Create `ClickHouseSearchEngine` in `com.cameleer.server.app.search`:
- Implements SearchEngine interface from core module. - Implements SearchEngine interface from core module.
- Constructor takes JdbcTemplate. - Constructor takes JdbcTemplate.
- `search(SearchRequest)` method: - `search(SearchRequest)` method:
@@ -244,13 +244,13 @@ Established controller pattern (from Phase 1):
- `escapeLike(String)` utility: escape `%`, `_`, `\` characters in user input to prevent LIKE injection. Replace `\` with `\\`, `%` with `\%`, `_` with `\_`. - `escapeLike(String)` utility: escape `%`, `_`, `\` characters in user input to prevent LIKE injection. Replace `\` with `\\`, `%` with `\%`, `_` with `\_`.
- `count(SearchRequest)` method: same WHERE building, just count query. - `count(SearchRequest)` method: same WHERE building, just count query.
2. Create `SearchBeanConfig` in `com.cameleer3.server.app.config`: 2. Create `SearchBeanConfig` in `com.cameleer.server.app.config`:
- @Configuration class that creates: - @Configuration class that creates:
- `ClickHouseSearchEngine` bean (takes JdbcTemplate) - `ClickHouseSearchEngine` bean (takes JdbcTemplate)
- `SearchService` bean (takes SearchEngine) - `SearchService` bean (takes SearchEngine)
- `DetailService` bean (takes the execution query interface from Plan 01) - `DetailService` bean (takes the execution query interface from Plan 01)
3. Create `SearchController` in `com.cameleer3.server.app.controller`: 3. Create `SearchController` in `com.cameleer.server.app.controller`:
- Inject SearchService. - Inject SearchService.
- `GET /api/v1/search/executions` with @RequestParam for basic filters: - `GET /api/v1/search/executions` with @RequestParam for basic filters:
- status (optional String) - status (optional String)
@@ -274,7 +274,7 @@ Established controller pattern (from Phase 1):
- Assert response structure matches the envelope format. - Assert response structure matches the envelope format.
</action> </action>
<verify> <verify>
<automated>cd C:/Users/Hendrik/Documents/projects/cameleer3-server && mvn test -pl cameleer3-server-app -Dtest=SearchControllerIT</automated> <automated>cd C:/Users/Hendrik/Documents/projects/cameleer-server && mvn test -pl cameleer-server-app -Dtest=SearchControllerIT</automated>
</verify> </verify>
<done>All search filter types work independently and in combination, response envelope has correct format, pagination works correctly, full-text search finds matches in all text fields, LIKE patterns are properly escaped</done> <done>All search filter types work independently and in combination, response envelope has correct format, pagination works correctly, full-text search finds matches in all text fields, LIKE patterns are properly escaped</done>
</task> </task>
@@ -282,10 +282,10 @@ Established controller pattern (from Phase 1):
<task type="auto" tdd="true"> <task type="auto" tdd="true">
<name>Task 2: DetailController, tree reconstruction, exchange snapshot endpoint, and integration tests</name> <name>Task 2: DetailController, tree reconstruction, exchange snapshot endpoint, and integration tests</name>
<files> <files>
cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java, cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseExecutionRepository.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/DetailController.java, cameleer-server-app/src/main/java/com/cameleer/server/app/controller/DetailController.java,
cameleer3-server-core/src/test/java/com/cameleer3/server/core/detail/TreeReconstructionTest.java, cameleer-server-core/src/test/java/com/cameleer/server/core/detail/TreeReconstructionTest.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/DetailControllerIT.java cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DetailControllerIT.java
</files> </files>
<behavior> <behavior>
- Unit test: reconstructTree with [root, child, grandchild], depths=[0,1,2], parents=[-1,0,1] produces single root with one child that has one grandchild - Unit test: reconstructTree with [root, child, grandchild], depths=[0,1,2], parents=[-1,0,1] produces single root with one child that has one grandchild
@@ -308,7 +308,7 @@ Established controller pattern (from Phase 1):
- Add `findRawById(String executionId)` method that queries all columns from route_executions WHERE execution_id = ?. Return Optional<RawExecutionRow> (use the record created in Plan 01 or create it here if needed). The RawExecutionRow should contain ALL columns including the parallel arrays for processors. - Add `findRawById(String executionId)` method that queries all columns from route_executions WHERE execution_id = ?. Return Optional<RawExecutionRow> (use the record created in Plan 01 or create it here if needed). The RawExecutionRow should contain ALL columns including the parallel arrays for processors.
- Add `findProcessorSnapshot(String executionId, int processorIndex)` method: queries processor_input_bodies[index+1], processor_output_bodies[index+1], processor_input_headers[index+1], processor_output_headers[index+1] for the given execution. Returns a DTO with inputBody, outputBody, inputHeaders, outputHeaders. ClickHouse arrays are 1-indexed in SQL, so add 1 to the Java 0-based index. - Add `findProcessorSnapshot(String executionId, int processorIndex)` method: queries processor_input_bodies[index+1], processor_output_bodies[index+1], processor_input_headers[index+1], processor_output_headers[index+1] for the given execution. Returns a DTO with inputBody, outputBody, inputHeaders, outputHeaders. ClickHouse arrays are 1-indexed in SQL, so add 1 to the Java 0-based index.
3. Create `DetailController` in `com.cameleer3.server.app.controller`: 3. Create `DetailController` in `com.cameleer.server.app.controller`:
- Inject DetailService. - Inject DetailService.
- `GET /api/v1/executions/{executionId}`: call detailService.getDetail(executionId). If empty, return 404. Otherwise return 200 with ExecutionDetail JSON. The processors field is a nested tree of ProcessorNode objects. - `GET /api/v1/executions/{executionId}`: call detailService.getDetail(executionId). If empty, return 404. Otherwise return 200 with ExecutionDetail JSON. The processors field is a nested tree of ProcessorNode objects.
- `GET /api/v1/executions/{executionId}/processors/{index}/snapshot`: call repository's findProcessorSnapshot. If execution not found or index out of bounds, return 404. Return JSON with inputBody, outputBody, inputHeaders, outputHeaders. Per user decision: exchange snapshot data fetched separately per processor, not inlined in detail response. - `GET /api/v1/executions/{executionId}/processors/{index}/snapshot`: call repository's findProcessorSnapshot. If execution not found or index out of bounds, return 404. Return JSON with inputBody, outputBody, inputHeaders, outputHeaders. Per user decision: exchange snapshot data fetched separately per processor, not inlined in detail response.
@@ -323,7 +323,7 @@ Established controller pattern (from Phase 1):
- Test GET /api/v1/executions/{id}/processors/999/snapshot: returns 404 for out-of-bounds index. - Test GET /api/v1/executions/{id}/processors/999/snapshot: returns 404 for out-of-bounds index.
</action> </action>
<verify> <verify>
<automated>cd C:/Users/Hendrik/Documents/projects/cameleer3-server && mvn test -pl cameleer3-server-core -Dtest=TreeReconstructionTest && mvn test -pl cameleer3-server-app -Dtest=DetailControllerIT</automated> <automated>cd C:/Users/Hendrik/Documents/projects/cameleer-server && mvn test -pl cameleer-server-core -Dtest=TreeReconstructionTest && mvn test -pl cameleer-server-app -Dtest=DetailControllerIT</automated>
</verify> </verify>
<done>Tree reconstruction correctly rebuilds nested processor trees from flat arrays, detail endpoint returns nested tree with all fields, snapshot endpoint returns per-processor exchange data, diagram hash included in detail response, all tests pass</done> <done>Tree reconstruction correctly rebuilds nested processor trees from flat arrays, detail endpoint returns nested tree with all fields, snapshot endpoint returns per-processor exchange data, diagram hash included in detail response, all tests pass</done>
</task> </task>
@@ -331,9 +331,9 @@ Established controller pattern (from Phase 1):
</tasks> </tasks>
<verification> <verification>
- `mvn test -pl cameleer3-server-core -Dtest=TreeReconstructionTest` passes (unit test for tree rebuild) - `mvn test -pl cameleer-server-core -Dtest=TreeReconstructionTest` passes (unit test for tree rebuild)
- `mvn test -pl cameleer3-server-app -Dtest=SearchControllerIT` passes (all search filters) - `mvn test -pl cameleer-server-app -Dtest=SearchControllerIT` passes (all search filters)
- `mvn test -pl cameleer3-server-app -Dtest=DetailControllerIT` passes (detail + snapshot) - `mvn test -pl cameleer-server-app -Dtest=DetailControllerIT` passes (detail + snapshot)
- `mvn clean verify` passes (full suite green) - `mvn clean verify` passes (full suite green)
</verification> </verification>

View File

@@ -21,16 +21,16 @@ tech-stack:
key-files: key-files:
created: created:
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/search/ClickHouseSearchEngine.java - cameleer-server-app/src/main/java/com/cameleer/server/app/search/ClickHouseSearchEngine.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/SearchController.java - cameleer-server-app/src/main/java/com/cameleer/server/app/controller/SearchController.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/DetailController.java - cameleer-server-app/src/main/java/com/cameleer/server/app/controller/DetailController.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/SearchBeanConfig.java - cameleer-server-app/src/main/java/com/cameleer/server/app/config/SearchBeanConfig.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/SearchControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/SearchControllerIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/DetailControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DetailControllerIT.java
- cameleer3-server-core/src/test/java/com/cameleer3/server/core/detail/TreeReconstructionTest.java - cameleer-server-core/src/test/java/com/cameleer/server/core/detail/TreeReconstructionTest.java
modified: modified:
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java - cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseExecutionRepository.java
- cameleer3-server-core/pom.xml - cameleer-server-core/pom.xml
key-decisions: key-decisions:
- "Search tests use correlationId scoping and >= assertions for shared ClickHouse isolation" - "Search tests use correlationId scoping and >= assertions for shared ClickHouse isolation"
@@ -77,15 +77,15 @@ Each task was committed atomically:
3. **Task 2 fix: Test isolation for shared ClickHouse** - `079dce5` (fix) 3. **Task 2 fix: Test isolation for shared ClickHouse** - `079dce5` (fix)
## Files Created/Modified ## Files Created/Modified
- `cameleer3-server-app/.../search/ClickHouseSearchEngine.java` - Dynamic SQL search with LIKE escape, implements SearchEngine - `cameleer-server-app/.../search/ClickHouseSearchEngine.java` - Dynamic SQL search with LIKE escape, implements SearchEngine
- `cameleer3-server-app/.../controller/SearchController.java` - GET + POST /api/v1/search/executions endpoints - `cameleer-server-app/.../controller/SearchController.java` - GET + POST /api/v1/search/executions endpoints
- `cameleer3-server-app/.../controller/DetailController.java` - GET /api/v1/executions/{id} and processor snapshot endpoints - `cameleer-server-app/.../controller/DetailController.java` - GET /api/v1/executions/{id} and processor snapshot endpoints
- `cameleer3-server-app/.../config/SearchBeanConfig.java` - Wires SearchEngine, SearchService, DetailService beans - `cameleer-server-app/.../config/SearchBeanConfig.java` - Wires SearchEngine, SearchService, DetailService beans
- `cameleer3-server-app/.../storage/ClickHouseExecutionRepository.java` - Added findRawById, findProcessorSnapshot, array extraction helpers - `cameleer-server-app/.../storage/ClickHouseExecutionRepository.java` - Added findRawById, findProcessorSnapshot, array extraction helpers
- `cameleer3-server-app/.../controller/SearchControllerIT.java` - 13 integration tests for search - `cameleer-server-app/.../controller/SearchControllerIT.java` - 13 integration tests for search
- `cameleer3-server-app/.../controller/DetailControllerIT.java` - 6 integration tests for detail/snapshot - `cameleer-server-app/.../controller/DetailControllerIT.java` - 6 integration tests for detail/snapshot
- `cameleer3-server-core/.../detail/TreeReconstructionTest.java` - 5 unit tests for tree reconstruction - `cameleer-server-core/.../detail/TreeReconstructionTest.java` - 5 unit tests for tree reconstruction
- `cameleer3-server-core/pom.xml` - Added assertj and mockito test dependencies - `cameleer-server-core/pom.xml` - Added assertj and mockito test dependencies
## Decisions Made ## Decisions Made
- Search tests use correlationId scoping and >= assertions to remain stable when other test classes seed data in the shared ClickHouse container - Search tests use correlationId scoping and >= assertions to remain stable when other test classes seed data in the shared ClickHouse container
@@ -99,8 +99,8 @@ Each task was committed atomically:
**1. [Rule 3 - Blocking] Added assertj and mockito test dependencies to core module** **1. [Rule 3 - Blocking] Added assertj and mockito test dependencies to core module**
- **Found during:** Task 2 (TreeReconstructionTest compilation) - **Found during:** Task 2 (TreeReconstructionTest compilation)
- **Issue:** Core module only had JUnit Jupiter as test dependency, TreeReconstructionTest needed assertj for assertions and mockito for mock(ExecutionRepository.class) - **Issue:** Core module only had JUnit Jupiter as test dependency, TreeReconstructionTest needed assertj for assertions and mockito for mock(ExecutionRepository.class)
- **Fix:** Added assertj-core and mockito-core test-scoped dependencies to cameleer3-server-core/pom.xml - **Fix:** Added assertj-core and mockito-core test-scoped dependencies to cameleer-server-core/pom.xml
- **Files modified:** cameleer3-server-core/pom.xml - **Files modified:** cameleer-server-core/pom.xml
- **Committed in:** 0615a98 (Task 2 commit) - **Committed in:** 0615a98 (Task 2 commit)
**2. [Rule 1 - Bug] Fixed search tests failing with shared ClickHouse data** **2. [Rule 1 - Bug] Fixed search tests failing with shared ClickHouse data**

View File

@@ -5,9 +5,9 @@ type: execute
wave: 1 wave: 1
depends_on: ["02-01", "02-02", "02-03"] depends_on: ["02-01", "02-02", "02-03"]
files_modified: files_modified:
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java - cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseExecutionRepository.java
- cameleer3-server-app/pom.xml - cameleer-server-app/pom.xml
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/storage/DiagramLinkingIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/storage/DiagramLinkingIT.java
autonomous: true autonomous: true
gap_closure: true gap_closure: true
requirements: ["DIAG-02"] requirements: ["DIAG-02"]
@@ -17,13 +17,13 @@ must_haves:
- "Each transaction links to the RouteGraph version that was active at execution time" - "Each transaction links to the RouteGraph version that was active at execution time"
- "Full test suite passes with mvn clean verify (no classloader failures)" - "Full test suite passes with mvn clean verify (no classloader failures)"
artifacts: artifacts:
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseExecutionRepository.java"
provides: "Diagram hash lookup during batch insert" provides: "Diagram hash lookup during batch insert"
contains: "findContentHashForRoute" contains: "findContentHashForRoute"
- path: "cameleer3-server-app/pom.xml" - path: "cameleer-server-app/pom.xml"
provides: "Surefire fork configuration isolating ELK classloader" provides: "Surefire fork configuration isolating ELK classloader"
contains: "reuseForks" contains: "reuseForks"
- path: "cameleer3-server-app/src/test/java/com/cameleer3/server/app/storage/DiagramLinkingIT.java" - path: "cameleer-server-app/src/test/java/com/cameleer/server/app/storage/DiagramLinkingIT.java"
provides: "Integration test proving diagram hash is stored during ingestion" provides: "Integration test proving diagram hash is stored during ingestion"
key_links: key_links:
- from: "ClickHouseExecutionRepository" - from: "ClickHouseExecutionRepository"
@@ -57,18 +57,18 @@ Prior plan summaries (needed — touches same files):
<interfaces> <interfaces>
<!-- Key types and contracts the executor needs. --> <!-- Key types and contracts the executor needs. -->
From cameleer3-server-core/.../storage/DiagramRepository.java: From cameleer-server-core/.../storage/DiagramRepository.java:
```java ```java
Optional<String> findContentHashForRoute(String routeId, String agentId); Optional<String> findContentHashForRoute(String routeId, String agentId);
``` ```
From cameleer3-server-app/.../storage/ClickHouseExecutionRepository.java (line 141): From cameleer-server-app/.../storage/ClickHouseExecutionRepository.java (line 141):
```java ```java
ps.setString(col++, ""); // diagram_content_hash (wired later) ps.setString(col++, ""); // diagram_content_hash (wired later)
``` ```
The class is @Repository annotated, constructor takes JdbcTemplate only. It needs DiagramRepository injected to perform the lookup. The class is @Repository annotated, constructor takes JdbcTemplate only. It needs DiagramRepository injected to perform the lookup.
From cameleer3-server-app/.../storage/ClickHouseDiagramRepository.java: From cameleer-server-app/.../storage/ClickHouseDiagramRepository.java:
```java ```java
@Repository @Repository
public class ClickHouseDiagramRepository implements DiagramRepository { public class ClickHouseDiagramRepository implements DiagramRepository {
@@ -83,9 +83,9 @@ public class ClickHouseDiagramRepository implements DiagramRepository {
<task type="auto" tdd="true"> <task type="auto" tdd="true">
<name>Task 1: Populate diagram_content_hash during ingestion and fix Surefire forks</name> <name>Task 1: Populate diagram_content_hash during ingestion and fix Surefire forks</name>
<files> <files>
cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java, cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseExecutionRepository.java,
cameleer3-server-app/pom.xml, cameleer-server-app/pom.xml,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/storage/DiagramLinkingIT.java cameleer-server-app/src/test/java/com/cameleer/server/app/storage/DiagramLinkingIT.java
</files> </files>
<behavior> <behavior>
- Test 1: When a RouteGraph is ingested before a RouteExecution for the same routeId+agentId, the execution's diagram_content_hash column contains the SHA-256 hash of the diagram (not empty string) - Test 1: When a RouteGraph is ingested before a RouteExecution for the same routeId+agentId, the execution's diagram_content_hash column contains the SHA-256 hash of the diagram (not empty string)
@@ -117,7 +117,7 @@ public class ClickHouseDiagramRepository implements DiagramRepository {
**Gap 2 — Surefire classloader isolation:** **Gap 2 — Surefire classloader isolation:**
5. In `cameleer3-server-app/pom.xml`, add a `<build><plugins>` section (after the existing `spring-boot-maven-plugin`) with `maven-surefire-plugin` configuration: 5. In `cameleer-server-app/pom.xml`, add a `<build><plugins>` section (after the existing `spring-boot-maven-plugin`) with `maven-surefire-plugin` configuration:
```xml ```xml
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
@@ -131,12 +131,12 @@ public class ClickHouseDiagramRepository implements DiagramRepository {
This forces Surefire to fork a fresh JVM for each test class, isolating ELK's static initializer (LayeredMetaDataProvider + xtext CollectionLiterals) from Spring Boot's classloader. Trade-off: slightly slower test execution, but correct results. This forces Surefire to fork a fresh JVM for each test class, isolating ELK's static initializer (LayeredMetaDataProvider + xtext CollectionLiterals) from Spring Boot's classloader. Trade-off: slightly slower test execution, but correct results.
</action> </action>
<verify> <verify>
<automated>cd C:/Users/Hendrik/Documents/projects/cameleer3-server && mvn clean verify -pl cameleer3-server-app -am 2>&1 | tail -30</automated> <automated>cd C:/Users/Hendrik/Documents/projects/cameleer-server && mvn clean verify -pl cameleer-server-app -am 2>&1 | tail -30</automated>
</verify> </verify>
<done> <done>
- diagram_content_hash is populated with the active diagram's SHA-256 hash during ingestion (not empty string) - diagram_content_hash is populated with the active diagram's SHA-256 hash during ingestion (not empty string)
- DiagramLinkingIT passes with both positive and negative cases - DiagramLinkingIT passes with both positive and negative cases
- `mvn clean verify` passes for cameleer3-server-app (no classloader failures from ElkDiagramRendererTest) - `mvn clean verify` passes for cameleer-server-app (no classloader failures from ElkDiagramRendererTest)
</done> </done>
</task> </task>

View File

@@ -20,12 +20,12 @@ tech-stack:
key-files: key-files:
created: created:
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/storage/DiagramLinkingIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/storage/DiagramLinkingIT.java
modified: modified:
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java - cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseExecutionRepository.java
- cameleer3-server-app/pom.xml - cameleer-server-app/pom.xml
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/storage/IngestionSchemaIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/storage/IngestionSchemaIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/SearchControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/SearchControllerIT.java
key-decisions: key-decisions:
- "DiagramRepository injected via constructor into ClickHouseExecutionRepository for diagram hash lookup during batch insert" - "DiagramRepository injected via constructor into ClickHouseExecutionRepository for diagram hash lookup during batch insert"
@@ -69,11 +69,11 @@ Each task was committed atomically:
**Plan metadata:** (pending) **Plan metadata:** (pending)
## Files Created/Modified ## Files Created/Modified
- `cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java` - Added DiagramRepository injection, diagram hash lookup in insertBatch - `cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseExecutionRepository.java` - Added DiagramRepository injection, diagram hash lookup in insertBatch
- `cameleer3-server-app/pom.xml` - Added maven-surefire-plugin and maven-failsafe-plugin with reuseForks=false - `cameleer-server-app/pom.xml` - Added maven-surefire-plugin and maven-failsafe-plugin with reuseForks=false
- `cameleer3-server-app/src/test/java/com/cameleer3/server/app/storage/DiagramLinkingIT.java` - Integration test for diagram hash linking - `cameleer-server-app/src/test/java/com/cameleer/server/app/storage/DiagramLinkingIT.java` - Integration test for diagram hash linking
- `cameleer3-server-app/src/test/java/com/cameleer3/server/app/storage/IngestionSchemaIT.java` - Added ignoreExceptions + increased timeouts - `cameleer-server-app/src/test/java/com/cameleer/server/app/storage/IngestionSchemaIT.java` - Added ignoreExceptions + increased timeouts
- `cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/SearchControllerIT.java` - Adjusted pagination assertion count - `cameleer-server-app/src/test/java/com/cameleer/server/app/controller/SearchControllerIT.java` - Adjusted pagination assertion count
## Decisions Made ## Decisions Made
- DiagramRepository injected via constructor into ClickHouseExecutionRepository -- both are @Repository Spring beans, so constructor injection autowires cleanly - DiagramRepository injected via constructor into ClickHouseExecutionRepository -- both are @Repository Spring beans, so constructor injection autowires cleanly

View File

@@ -44,7 +44,7 @@ Users can find any transaction by status, time, duration, correlation ID, or con
- No execution overlay in server-rendered SVG — the UI handles overlay with theme support (dark/light) - No execution overlay in server-rendered SVG — the UI handles overlay with theme support (dark/light)
- Top-to-bottom node layout flow - Top-to-bottom node layout flow
- Nested processors (inside for-each, split, try-catch) rendered in swimlanes to highlight nesting/scope - Nested processors (inside for-each, split, try-catch) rendered in swimlanes to highlight nesting/scope
- Reference: cameleer3 agent repo `examples/route-diagram-example.html` for visual style inspiration (color-coded node types, EIP icons) - Reference: cameleer agent repo `examples/route-diagram-example.html` for visual style inspiration (color-coded node types, EIP icons)
### Claude's Discretion ### Claude's Discretion
- Pagination implementation details (offset/limit vs cursor) - Pagination implementation details (offset/limit vs cursor)
@@ -58,7 +58,7 @@ Users can find any transaction by status, time, duration, correlation ID, or con
<specifics> <specifics>
## Specific Ideas ## Specific Ideas
- "We want a cmd+k type of search in the UI" — see `cameleer3/examples/cmd-k-search-example.html` for the target UX. Key features: - "We want a cmd+k type of search in the UI" — see `cameleer/examples/cmd-k-search-example.html` for the target UX. Key features:
- Cross-entity search: single query hits Executions, Routes, Exchanges, Agents with scope tabs and counts - Cross-entity search: single query hits Executions, Routes, Exchanges, Agents with scope tabs and counts
- Filter chips in the input (e.g., `route:order` prefix filtering) - Filter chips in the input (e.g., `route:order` prefix filtering)
- Inline preview pane with JSON syntax highlighting for selected result - Inline preview pane with JSON syntax highlighting for selected result

View File

@@ -112,7 +112,7 @@ The existing Phase 1 code provides a solid foundation: `ClickHouseExecutionRepos
### Recommended Project Structure (additions for Phase 2) ### Recommended Project Structure (additions for Phase 2)
``` ```
cameleer3-server-core/src/main/java/com/cameleer3/server/core/ cameleer-server-core/src/main/java/com/cameleer/server/core/
├── search/ ├── search/
│ ├── SearchService.java # Orchestrates search, delegates to SearchEngine │ ├── SearchService.java # Orchestrates search, delegates to SearchEngine
│ ├── SearchEngine.java # Interface for search backends (ClickHouse now, OpenSearch later) │ ├── SearchEngine.java # Interface for search backends (ClickHouse now, OpenSearch later)
@@ -127,7 +127,7 @@ cameleer3-server-core/src/main/java/com/cameleer3/server/core/
├── ExecutionRepository.java # Extended with query methods ├── ExecutionRepository.java # Extended with query methods
└── DiagramRepository.java # Extended with lookup methods └── DiagramRepository.java # Extended with lookup methods
cameleer3-server-app/src/main/java/com/cameleer3/server/app/ cameleer-server-app/src/main/java/com/cameleer/server/app/
├── controller/ ├── controller/
│ ├── SearchController.java # GET + POST /api/v1/search/executions │ ├── SearchController.java # GET + POST /api/v1/search/executions
│ ├── DetailController.java # GET /api/v1/executions/{id} │ ├── DetailController.java # GET /api/v1/executions/{id}
@@ -517,25 +517,25 @@ private void populateExchangeColumns(PreparedStatement ps, List<FlatProcessor> p
| Property | Value | | Property | Value |
|----------|-------| |----------|-------|
| Framework | JUnit 5 + Spring Boot Test + Testcontainers ClickHouse 25.3 | | Framework | JUnit 5 + Spring Boot Test + Testcontainers ClickHouse 25.3 |
| Config file | cameleer3-server-app/pom.xml (testcontainers dep), AbstractClickHouseIT base class | | Config file | cameleer-server-app/pom.xml (testcontainers dep), AbstractClickHouseIT base class |
| Quick run command | `mvn test -pl cameleer3-server-app -Dtest=SearchControllerIT -Dfailsafe.skip=true` | | Quick run command | `mvn test -pl cameleer-server-app -Dtest=SearchControllerIT -Dfailsafe.skip=true` |
| Full suite command | `mvn clean verify` | | Full suite command | `mvn clean verify` |
### Phase Requirements -> Test Map ### Phase Requirements -> Test Map
| Req ID | Behavior | Test Type | Automated Command | File Exists? | | Req ID | Behavior | Test Type | Automated Command | File Exists? |
|--------|----------|-----------|-------------------|-------------| |--------|----------|-----------|-------------------|-------------|
| SRCH-01 | Filter by status returns matching executions | integration | `mvn test -pl cameleer3-server-app -Dtest=SearchControllerIT#searchByStatus` | No -- Wave 0 | | SRCH-01 | Filter by status returns matching executions | integration | `mvn test -pl cameleer-server-app -Dtest=SearchControllerIT#searchByStatus` | No -- Wave 0 |
| SRCH-02 | Filter by time range returns matching executions | integration | `mvn test -pl cameleer3-server-app -Dtest=SearchControllerIT#searchByTimeRange` | No -- Wave 0 | | SRCH-02 | Filter by time range returns matching executions | integration | `mvn test -pl cameleer-server-app -Dtest=SearchControllerIT#searchByTimeRange` | No -- Wave 0 |
| SRCH-03 | Filter by duration range returns matching | integration | `mvn test -pl cameleer3-server-app -Dtest=SearchControllerIT#searchByDuration` | No -- Wave 0 | | SRCH-03 | Filter by duration range returns matching | integration | `mvn test -pl cameleer-server-app -Dtest=SearchControllerIT#searchByDuration` | No -- Wave 0 |
| SRCH-04 | Filter by correlationId returns correlated | integration | `mvn test -pl cameleer3-server-app -Dtest=SearchControllerIT#searchByCorrelationId` | No -- Wave 0 | | SRCH-04 | Filter by correlationId returns correlated | integration | `mvn test -pl cameleer-server-app -Dtest=SearchControllerIT#searchByCorrelationId` | No -- Wave 0 |
| SRCH-05 | Full-text search across bodies/headers/errors | integration | `mvn test -pl cameleer3-server-app -Dtest=SearchControllerIT#fullTextSearch` | No -- Wave 0 | | SRCH-05 | Full-text search across bodies/headers/errors | integration | `mvn test -pl cameleer-server-app -Dtest=SearchControllerIT#fullTextSearch` | No -- Wave 0 |
| SRCH-06 | Detail returns nested processor tree | integration | `mvn test -pl cameleer3-server-app -Dtest=DetailControllerIT#detailReturnsNestedTree` | No -- Wave 0 | | SRCH-06 | Detail returns nested processor tree | integration | `mvn test -pl cameleer-server-app -Dtest=DetailControllerIT#detailReturnsNestedTree` | No -- Wave 0 |
| DIAG-01 | Content-hash dedup stores identical defs once | integration | `mvn test -pl cameleer3-server-app -Dtest=DiagramControllerIT#contentHashDedup` | Partial (ingestion test exists) | | DIAG-01 | Content-hash dedup stores identical defs once | integration | `mvn test -pl cameleer-server-app -Dtest=DiagramControllerIT#contentHashDedup` | Partial (ingestion test exists) |
| DIAG-02 | Transaction links to active diagram version | integration | `mvn test -pl cameleer3-server-app -Dtest=DetailControllerIT#detailIncludesDiagramHash` | No -- Wave 0 | | DIAG-02 | Transaction links to active diagram version | integration | `mvn test -pl cameleer-server-app -Dtest=DetailControllerIT#detailIncludesDiagramHash` | No -- Wave 0 |
| DIAG-03 | Diagram rendered as SVG or JSON layout | integration | `mvn test -pl cameleer3-server-app -Dtest=DiagramRenderControllerIT#renderSvg` | No -- Wave 0 | | DIAG-03 | Diagram rendered as SVG or JSON layout | integration | `mvn test -pl cameleer-server-app -Dtest=DiagramRenderControllerIT#renderSvg` | No -- Wave 0 |
### Sampling Rate ### Sampling Rate
- **Per task commit:** `mvn test -pl cameleer3-server-app -Dtest=<relevant>IT` - **Per task commit:** `mvn test -pl cameleer-server-app -Dtest=<relevant>IT`
- **Per wave merge:** `mvn clean verify` - **Per wave merge:** `mvn clean verify`
- **Phase gate:** Full suite green before `/gsd:verify-work` - **Phase gate:** Full suite green before `/gsd:verify-work`
@@ -551,7 +551,7 @@ private void populateExchangeColumns(PreparedStatement ps, List<FlatProcessor> p
### Primary (HIGH confidence) ### Primary (HIGH confidence)
- ClickHouse JDBC 0.9.7, ClickHouse 25.3 -- verified from project pom.xml and AbstractClickHouseIT - ClickHouse JDBC 0.9.7, ClickHouse 25.3 -- verified from project pom.xml and AbstractClickHouseIT
- cameleer3-common 1.0-SNAPSHOT JAR -- decompiled to verify RouteGraph, RouteNode, RouteEdge, NodeType, ProcessorExecution, ExchangeSnapshot field structures - cameleer-common 1.0-SNAPSHOT JAR -- decompiled to verify RouteGraph, RouteNode, RouteEdge, NodeType, ProcessorExecution, ExchangeSnapshot field structures
- Existing Phase 1 codebase -- ClickHouseExecutionRepository, ClickHouseDiagramRepository, schema, test patterns - Existing Phase 1 codebase -- ClickHouseExecutionRepository, ClickHouseDiagramRepository, schema, test patterns
### Secondary (MEDIUM confidence) ### Secondary (MEDIUM confidence)

View File

@@ -18,8 +18,8 @@ created: 2026-03-11
| Property | Value | | Property | Value |
|----------|-------| |----------|-------|
| **Framework** | JUnit 5 + Spring Boot Test + Testcontainers ClickHouse 25.3 | | **Framework** | JUnit 5 + Spring Boot Test + Testcontainers ClickHouse 25.3 |
| **Config file** | cameleer3-server-app/pom.xml (testcontainers dep), AbstractClickHouseIT base class | | **Config file** | cameleer-server-app/pom.xml (testcontainers dep), AbstractClickHouseIT base class |
| **Quick run command** | `mvn test -pl cameleer3-server-app -Dtest=SearchControllerIT` | | **Quick run command** | `mvn test -pl cameleer-server-app -Dtest=SearchControllerIT` |
| **Full suite command** | `mvn clean verify` | | **Full suite command** | `mvn clean verify` |
| **Estimated runtime** | ~45 seconds | | **Estimated runtime** | ~45 seconds |
@@ -27,7 +27,7 @@ created: 2026-03-11
## Sampling Rate ## Sampling Rate
- **After every task commit:** Run `mvn test -pl cameleer3-server-app -Dtest=<relevant>IT` - **After every task commit:** Run `mvn test -pl cameleer-server-app -Dtest=<relevant>IT`
- **After every plan wave:** Run `mvn clean verify` - **After every plan wave:** Run `mvn clean verify`
- **Before `/gsd:verify-work`:** Full suite must be green - **Before `/gsd:verify-work`:** Full suite must be green
- **Max feedback latency:** 45 seconds - **Max feedback latency:** 45 seconds
@@ -38,15 +38,15 @@ created: 2026-03-11
| Task ID | Plan | Wave | Requirement | Test Type | Automated Command | File Exists | Status | | Task ID | Plan | Wave | Requirement | Test Type | Automated Command | File Exists | Status |
|---------|------|------|-------------|-----------|-------------------|-------------|--------| |---------|------|------|-------------|-----------|-------------------|-------------|--------|
| 02-01-01 | 01 | 1 | SRCH-01 | integration | `mvn test -pl cameleer3-server-app -Dtest=SearchControllerIT#searchByStatus` | ❌ W0 | ⬜ pending | | 02-01-01 | 01 | 1 | SRCH-01 | integration | `mvn test -pl cameleer-server-app -Dtest=SearchControllerIT#searchByStatus` | ❌ W0 | ⬜ pending |
| 02-01-02 | 01 | 1 | SRCH-02 | integration | `mvn test -pl cameleer3-server-app -Dtest=SearchControllerIT#searchByTimeRange` | ❌ W0 | ⬜ pending | | 02-01-02 | 01 | 1 | SRCH-02 | integration | `mvn test -pl cameleer-server-app -Dtest=SearchControllerIT#searchByTimeRange` | ❌ W0 | ⬜ pending |
| 02-01-03 | 01 | 1 | SRCH-03 | integration | `mvn test -pl cameleer3-server-app -Dtest=SearchControllerIT#searchByDuration` | ❌ W0 | ⬜ pending | | 02-01-03 | 01 | 1 | SRCH-03 | integration | `mvn test -pl cameleer-server-app -Dtest=SearchControllerIT#searchByDuration` | ❌ W0 | ⬜ pending |
| 02-01-04 | 01 | 1 | SRCH-04 | integration | `mvn test -pl cameleer3-server-app -Dtest=SearchControllerIT#searchByCorrelationId` | ❌ W0 | ⬜ pending | | 02-01-04 | 01 | 1 | SRCH-04 | integration | `mvn test -pl cameleer-server-app -Dtest=SearchControllerIT#searchByCorrelationId` | ❌ W0 | ⬜ pending |
| 02-01-05 | 01 | 1 | SRCH-05 | integration | `mvn test -pl cameleer3-server-app -Dtest=SearchControllerIT#fullTextSearch` | ❌ W0 | ⬜ pending | | 02-01-05 | 01 | 1 | SRCH-05 | integration | `mvn test -pl cameleer-server-app -Dtest=SearchControllerIT#fullTextSearch` | ❌ W0 | ⬜ pending |
| 02-01-06 | 01 | 1 | SRCH-06 | integration | `mvn test -pl cameleer3-server-app -Dtest=DetailControllerIT#detailReturnsNestedTree` | ❌ W0 | ⬜ pending | | 02-01-06 | 01 | 1 | SRCH-06 | integration | `mvn test -pl cameleer-server-app -Dtest=DetailControllerIT#detailReturnsNestedTree` | ❌ W0 | ⬜ pending |
| 02-02-01 | 02 | 1 | DIAG-01 | integration | `mvn test -pl cameleer3-server-app -Dtest=DiagramControllerIT#contentHashDedup` | Partial | ⬜ pending | | 02-02-01 | 02 | 1 | DIAG-01 | integration | `mvn test -pl cameleer-server-app -Dtest=DiagramControllerIT#contentHashDedup` | Partial | ⬜ pending |
| 02-02-02 | 02 | 1 | DIAG-02 | integration | `mvn test -pl cameleer3-server-app -Dtest=DetailControllerIT#detailIncludesDiagramHash` | ❌ W0 | ⬜ pending | | 02-02-02 | 02 | 1 | DIAG-02 | integration | `mvn test -pl cameleer-server-app -Dtest=DetailControllerIT#detailIncludesDiagramHash` | ❌ W0 | ⬜ pending |
| 02-02-03 | 02 | 1 | DIAG-03 | integration | `mvn test -pl cameleer3-server-app -Dtest=DiagramRenderControllerIT#renderSvg` | ❌ W0 | ⬜ pending | | 02-02-03 | 02 | 1 | DIAG-03 | integration | `mvn test -pl cameleer-server-app -Dtest=DiagramRenderControllerIT#renderSvg` | ❌ W0 | ⬜ pending |
*Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky* *Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky*

View File

@@ -50,9 +50,9 @@ human_verification:
| Artifact | Status | Notes | | Artifact | Status | Notes |
|----------|--------|-------| |----------|--------|-------|
| `cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java` | VERIFIED | DiagramRepository injected via constructor (line 59); `findContentHashForRoute` called in `setValues()` (lines 144147); former `""` placeholder removed | | `cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseExecutionRepository.java` | VERIFIED | DiagramRepository injected via constructor (line 59); `findContentHashForRoute` called in `setValues()` (lines 144147); former `""` placeholder removed |
| `cameleer3-server-app/pom.xml` | VERIFIED | `maven-surefire-plugin` with `forkCount=1` `reuseForks=false` at lines 95100; `maven-failsafe-plugin` same config at lines 103108 | | `cameleer-server-app/pom.xml` | VERIFIED | `maven-surefire-plugin` with `forkCount=1` `reuseForks=false` at lines 95100; `maven-failsafe-plugin` same config at lines 103108 |
| `cameleer3-server-app/src/test/java/com/cameleer3/server/app/storage/DiagramLinkingIT.java` | VERIFIED | 152 lines; 2 integration tests; positive case asserts 64-char hex hash; negative case asserts empty string; uses `ignoreExceptions()` for ClickHouse eventual consistency | | `cameleer-server-app/src/test/java/com/cameleer/server/app/storage/DiagramLinkingIT.java` | VERIFIED | 152 lines; 2 integration tests; positive case asserts 64-char hex hash; negative case asserts empty string; uses `ignoreExceptions()` for ClickHouse eventual consistency |
### Key Link Verification ### Key Link Verification
@@ -62,7 +62,7 @@ human_verification:
| `SearchController` | `SearchService` | constructor injection, `searchService.search()` | WIRED | Previously verified; no regression | | `SearchController` | `SearchService` | constructor injection, `searchService.search()` | WIRED | Previously verified; no regression |
| `DetailController` | `DetailService` | constructor injection, `detailService.getDetail()` | WIRED | Previously verified; no regression | | `DetailController` | `DetailService` | constructor injection, `detailService.getDetail()` | WIRED | Previously verified; no regression |
| `DiagramRenderController` | `DiagramRepository` + `DiagramRenderer` | `findByContentHash()` + `renderSvg()`/`layoutJson()` | WIRED | Previously verified; no regression | | `DiagramRenderController` | `DiagramRepository` + `DiagramRenderer` | `findByContentHash()` + `renderSvg()`/`layoutJson()` | WIRED | Previously verified; no regression |
| `Surefire/Failsafe` | ELK classloader isolation | `reuseForks=false` forces fresh JVM per test class | WIRED | Lines 95116 in `cameleer3-server-app/pom.xml` | | `Surefire/Failsafe` | ELK classloader isolation | `reuseForks=false` forces fresh JVM per test class | WIRED | Lines 95116 in `cameleer-server-app/pom.xml` |
### Requirements Coverage ### Requirements Coverage
@@ -108,7 +108,7 @@ Two blockers from the initial verification (2026-03-11T16:00:00Z) have been reso
**Gap 1 resolved — DIAG-02 diagram hash linking:** `ClickHouseExecutionRepository` now injects `DiagramRepository` via constructor and calls `findContentHashForRoute(exec.getRouteId(), "")` in `insertBatch()`. Both the diagram store path and the execution ingest path use `agent_id=""` consistently, so the lookup is correct. `DiagramLinkingIT` provides integration test coverage for both the positive case (hash populated when diagram exists) and negative case (empty string when no diagram exists for the route). **Gap 1 resolved — DIAG-02 diagram hash linking:** `ClickHouseExecutionRepository` now injects `DiagramRepository` via constructor and calls `findContentHashForRoute(exec.getRouteId(), "")` in `insertBatch()`. Both the diagram store path and the execution ingest path use `agent_id=""` consistently, so the lookup is correct. `DiagramLinkingIT` provides integration test coverage for both the positive case (hash populated when diagram exists) and negative case (empty string when no diagram exists for the route).
**Gap 2 resolved — Test suite stability:** Both `maven-surefire-plugin` and `maven-failsafe-plugin` in `cameleer3-server-app/pom.xml` are now configured with `forkCount=1` `reuseForks=false`. This forces a fresh JVM per test class, isolating ELK's `LayeredMetaDataProvider` static initializer from Spring Boot's classloader. The SUMMARY reports 51 tests, 0 failures. Test count across 16 test files totals 80 `@Test` methods; the difference from 51 reflects how Surefire/Failsafe counts parameterized and nested tests vs. raw annotation count. **Gap 2 resolved — Test suite stability:** Both `maven-surefire-plugin` and `maven-failsafe-plugin` in `cameleer-server-app/pom.xml` are now configured with `forkCount=1` `reuseForks=false`. This forces a fresh JVM per test class, isolating ELK's `LayeredMetaDataProvider` static initializer from Spring Boot's classloader. The SUMMARY reports 51 tests, 0 failures. Test count across 16 test files totals 80 `@Test` methods; the difference from 51 reflects how Surefire/Failsafe counts parameterized and nested tests vs. raw annotation count.
No regressions were introduced. All 10 observable truths and all 9 phase requirements are now satisfied. Two items remain for human visual verification (SVG rendering correctness). No regressions were introduced. All 10 observable truths and all 9 phase requirements are now satisfied. Two items remain for human visual verification (SVG rendering correctness).

View File

@@ -5,21 +5,21 @@ type: execute
wave: 1 wave: 1
depends_on: [] depends_on: []
files_modified: files_modified:
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentInfo.java - cameleer-server-core/src/main/java/com/cameleer/server/core/agent/AgentInfo.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentState.java - cameleer-server-core/src/main/java/com/cameleer/server/core/agent/AgentState.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentCommand.java - cameleer-server-core/src/main/java/com/cameleer/server/core/agent/AgentCommand.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/CommandStatus.java - cameleer-server-core/src/main/java/com/cameleer/server/core/agent/CommandStatus.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/CommandType.java - cameleer-server-core/src/main/java/com/cameleer/server/core/agent/CommandType.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentRegistryService.java - cameleer-server-core/src/main/java/com/cameleer/server/core/agent/AgentRegistryService.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentEventListener.java - cameleer-server-core/src/main/java/com/cameleer/server/core/agent/AgentEventListener.java
- cameleer3-server-core/src/test/java/com/cameleer3/server/core/agent/AgentRegistryServiceTest.java - cameleer-server-core/src/test/java/com/cameleer/server/core/agent/AgentRegistryServiceTest.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/AgentRegistryConfig.java - cameleer-server-app/src/main/java/com/cameleer/server/app/config/AgentRegistryConfig.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/AgentRegistryBeanConfig.java - cameleer-server-app/src/main/java/com/cameleer/server/app/config/AgentRegistryBeanConfig.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/agent/AgentLifecycleMonitor.java - cameleer-server-app/src/main/java/com/cameleer/server/app/agent/AgentLifecycleMonitor.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentRegistrationController.java - cameleer-server-app/src/main/java/com/cameleer/server/app/controller/AgentRegistrationController.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/Cameleer3ServerApplication.java - cameleer-server-app/src/main/java/com/cameleer/server/app/CameleerServerApplication.java
- cameleer3-server-app/src/main/resources/application.yml - cameleer-server-app/src/main/resources/application.yml
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentRegistrationControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/AgentRegistrationControllerIT.java
autonomous: true autonomous: true
requirements: requirements:
- AGNT-01 - AGNT-01
@@ -34,13 +34,13 @@ must_haves:
- "Server transitions agents LIVE->STALE after 90s without heartbeat, STALE->DEAD 5 minutes after staleTransitionTime" - "Server transitions agents LIVE->STALE after 90s without heartbeat, STALE->DEAD 5 minutes after staleTransitionTime"
- "Agent list endpoint GET /api/v1/agents returns all agents, filterable by ?status=LIVE|STALE|DEAD" - "Agent list endpoint GET /api/v1/agents returns all agents, filterable by ?status=LIVE|STALE|DEAD"
artifacts: artifacts:
- path: "cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentRegistryService.java" - path: "cameleer-server-core/src/main/java/com/cameleer/server/core/agent/AgentRegistryService.java"
provides: "Agent registration, heartbeat, lifecycle transitions, find/filter" provides: "Agent registration, heartbeat, lifecycle transitions, find/filter"
- path: "cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentInfo.java" - path: "cameleer-server-core/src/main/java/com/cameleer/server/core/agent/AgentInfo.java"
provides: "Agent record with id, name, group, version, routeIds, capabilities, state, timestamps" provides: "Agent record with id, name, group, version, routeIds, capabilities, state, timestamps"
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentRegistrationController.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/controller/AgentRegistrationController.java"
provides: "POST /register, POST /{id}/heartbeat, GET /agents endpoints" provides: "POST /register, POST /{id}/heartbeat, GET /agents endpoints"
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/agent/AgentLifecycleMonitor.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/agent/AgentLifecycleMonitor.java"
provides: "@Scheduled lifecycle transitions LIVE->STALE->DEAD" provides: "@Scheduled lifecycle transitions LIVE->STALE->DEAD"
key_links: key_links:
- from: "AgentRegistrationController" - from: "AgentRegistrationController"
@@ -76,14 +76,14 @@ Output: Core domain types (AgentInfo, AgentState, AgentCommand, CommandStatus, C
@.planning/phases/03-agent-registry-sse-push/03-CONTEXT.md @.planning/phases/03-agent-registry-sse-push/03-CONTEXT.md
@.planning/phases/03-agent-registry-sse-push/03-RESEARCH.md @.planning/phases/03-agent-registry-sse-push/03-RESEARCH.md
@cameleer3-server-core/src/main/java/com/cameleer3/server/core/ingestion/IngestionService.java @cameleer-server-core/src/main/java/com/cameleer/server/core/ingestion/IngestionService.java
@cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/IngestionBeanConfig.java @cameleer-server-app/src/main/java/com/cameleer/server/app/config/IngestionBeanConfig.java
@cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/IngestionConfig.java @cameleer-server-app/src/main/java/com/cameleer/server/app/config/IngestionConfig.java
@cameleer3-server-app/src/main/java/com/cameleer3/server/app/ingestion/ClickHouseFlushScheduler.java @cameleer-server-app/src/main/java/com/cameleer/server/app/ingestion/ClickHouseFlushScheduler.java
@cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/WebConfig.java @cameleer-server-app/src/main/java/com/cameleer/server/app/config/WebConfig.java
@cameleer3-server-app/src/main/java/com/cameleer3/server/app/Cameleer3ServerApplication.java @cameleer-server-app/src/main/java/com/cameleer/server/app/CameleerServerApplication.java
@cameleer3-server-app/src/main/resources/application.yml @cameleer-server-app/src/main/resources/application.yml
@cameleer3-server-app/src/test/java/com/cameleer3/server/app/AbstractClickHouseIT.java @cameleer-server-app/src/test/java/com/cameleer/server/app/AbstractClickHouseIT.java
<interfaces> <interfaces>
<!-- Established codebase patterns the executor must follow --> <!-- Established codebase patterns the executor must follow -->
@@ -99,10 +99,10 @@ Pattern: Controller accepts raw String body:
Pattern: @Scheduled for periodic tasks: Pattern: @Scheduled for periodic tasks:
- ClickHouseFlushScheduler uses @Scheduled(fixedDelayString = "${ingestion.flush-interval-ms:1000}") - ClickHouseFlushScheduler uses @Scheduled(fixedDelayString = "${ingestion.flush-interval-ms:1000}")
- @EnableScheduling already on Cameleer3ServerApplication - @EnableScheduling already on CameleerServerApplication
Pattern: @EnableConfigurationProperties registration: Pattern: @EnableConfigurationProperties registration:
- Cameleer3ServerApplication has @EnableConfigurationProperties(IngestionConfig.class) - CameleerServerApplication has @EnableConfigurationProperties(IngestionConfig.class)
- New config classes must be added to this annotation - New config classes must be added to this annotation
Pattern: ProtocolVersionInterceptor: Pattern: ProtocolVersionInterceptor:
@@ -116,14 +116,14 @@ Pattern: ProtocolVersionInterceptor:
<task type="auto" tdd="true"> <task type="auto" tdd="true">
<name>Task 1: Core domain types and AgentRegistryService with unit tests</name> <name>Task 1: Core domain types and AgentRegistryService with unit tests</name>
<files> <files>
cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentInfo.java, cameleer-server-core/src/main/java/com/cameleer/server/core/agent/AgentInfo.java,
cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentState.java, cameleer-server-core/src/main/java/com/cameleer/server/core/agent/AgentState.java,
cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentCommand.java, cameleer-server-core/src/main/java/com/cameleer/server/core/agent/AgentCommand.java,
cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/CommandStatus.java, cameleer-server-core/src/main/java/com/cameleer/server/core/agent/CommandStatus.java,
cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/CommandType.java, cameleer-server-core/src/main/java/com/cameleer/server/core/agent/CommandType.java,
cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentRegistryService.java, cameleer-server-core/src/main/java/com/cameleer/server/core/agent/AgentRegistryService.java,
cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentEventListener.java, cameleer-server-core/src/main/java/com/cameleer/server/core/agent/AgentEventListener.java,
cameleer3-server-core/src/test/java/com/cameleer3/server/core/agent/AgentRegistryServiceTest.java cameleer-server-core/src/test/java/com/cameleer/server/core/agent/AgentRegistryServiceTest.java
</files> </files>
<behavior> <behavior>
- register: new agent ID creates AgentInfo with state LIVE, returns AgentInfo - register: new agent ID creates AgentInfo with state LIVE, returns AgentInfo
@@ -142,7 +142,7 @@ Pattern: ProtocolVersionInterceptor:
- findPendingCommands: returns PENDING commands for given agentId - findPendingCommands: returns PENDING commands for given agentId
</behavior> </behavior>
<action> <action>
Create the agent domain model in the core module (package com.cameleer3.server.core.agent): Create the agent domain model in the core module (package com.cameleer.server.core.agent):
1. **AgentState enum**: LIVE, STALE, DEAD 1. **AgentState enum**: LIVE, STALE, DEAD
@@ -182,7 +182,7 @@ Pattern: ProtocolVersionInterceptor:
Write tests FIRST (RED), then implement (GREEN). Test class: AgentRegistryServiceTest. Write tests FIRST (RED), then implement (GREEN). Test class: AgentRegistryServiceTest.
</action> </action>
<verify> <verify>
<automated>mvn test -pl cameleer3-server-core -Dtest=AgentRegistryServiceTest</automated> <automated>mvn test -pl cameleer-server-core -Dtest=AgentRegistryServiceTest</automated>
</verify> </verify>
<done>All unit tests pass: registration (new + re-register), heartbeat (known + unknown), lifecycle transitions (LIVE->STALE->DEAD, heartbeat revives STALE), findAll/findByState/findById, command add/acknowledge/expire. AgentEventListener interface defined.</done> <done>All unit tests pass: registration (new + re-register), heartbeat (known + unknown), lifecycle transitions (LIVE->STALE->DEAD, heartbeat revives STALE), findAll/findByState/findById, command add/acknowledge/expire. AgentEventListener interface defined.</done>
</task> </task>
@@ -190,13 +190,13 @@ Pattern: ProtocolVersionInterceptor:
<task type="auto"> <task type="auto">
<name>Task 2: Registration/heartbeat/list controllers, config, lifecycle monitor, integration tests</name> <name>Task 2: Registration/heartbeat/list controllers, config, lifecycle monitor, integration tests</name>
<files> <files>
cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/AgentRegistryConfig.java, cameleer-server-app/src/main/java/com/cameleer/server/app/config/AgentRegistryConfig.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/AgentRegistryBeanConfig.java, cameleer-server-app/src/main/java/com/cameleer/server/app/config/AgentRegistryBeanConfig.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/agent/AgentLifecycleMonitor.java, cameleer-server-app/src/main/java/com/cameleer/server/app/agent/AgentLifecycleMonitor.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentRegistrationController.java, cameleer-server-app/src/main/java/com/cameleer/server/app/controller/AgentRegistrationController.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/Cameleer3ServerApplication.java, cameleer-server-app/src/main/java/com/cameleer/server/app/CameleerServerApplication.java,
cameleer3-server-app/src/main/resources/application.yml, cameleer-server-app/src/main/resources/application.yml,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentRegistrationControllerIT.java cameleer-server-app/src/test/java/com/cameleer/server/app/controller/AgentRegistrationControllerIT.java
</files> </files>
<action> <action>
Wire the agent registry into the Spring Boot app and create REST endpoints: Wire the agent registry into the Spring Boot app and create REST endpoints:
@@ -214,7 +214,7 @@ Pattern: ProtocolVersionInterceptor:
- @Bean AgentRegistryService: `new AgentRegistryService(config.getStaleThresholdMs(), config.getDeadThresholdMs(), config.getCommandExpiryMs())` - @Bean AgentRegistryService: `new AgentRegistryService(config.getStaleThresholdMs(), config.getDeadThresholdMs(), config.getCommandExpiryMs())`
Follow IngestionBeanConfig pattern. Follow IngestionBeanConfig pattern.
3. **Update Cameleer3ServerApplication**: Add AgentRegistryConfig.class to @EnableConfigurationProperties. 3. **Update CameleerServerApplication**: Add AgentRegistryConfig.class to @EnableConfigurationProperties.
4. **Update application.yml**: Add agent-registry section with all defaults (see RESEARCH.md code example). Also add `spring.mvc.async.request-timeout: -1` for SSE support (Plan 02 needs it, but set it now). 4. **Update application.yml**: Add agent-registry section with all defaults (see RESEARCH.md code example). Also add `spring.mvc.async.request-timeout: -1` for SSE support (Plan 02 needs it, but set it now).
@@ -242,7 +242,7 @@ Pattern: ProtocolVersionInterceptor:
- Use TestRestTemplate (already available from AbstractClickHouseIT's @SpringBootTest) - Use TestRestTemplate (already available from AbstractClickHouseIT's @SpringBootTest)
</action> </action>
<verify> <verify>
<automated>mvn test -pl cameleer3-server-core,cameleer3-server-app -Dtest="Agent*"</automated> <automated>mvn test -pl cameleer-server-core,cameleer-server-app -Dtest="Agent*"</automated>
</verify> </verify>
<done>POST /register returns 200 with agentId + sseEndpoint + heartbeatIntervalMs. POST /{id}/heartbeat returns 200 for known agents, 404 for unknown. GET /agents returns all agents with optional ?status= filter. AgentLifecycleMonitor runs on schedule. All integration tests pass. mvn clean verify passes.</done> <done>POST /register returns 200 with agentId + sseEndpoint + heartbeatIntervalMs. POST /{id}/heartbeat returns 200 for known agents, 404 for unknown. GET /agents returns all agents with optional ?status= filter. AgentLifecycleMonitor runs on schedule. All integration tests pass. mvn clean verify passes.</done>
</task> </task>

View File

@@ -27,22 +27,22 @@ tech-stack:
key-files: key-files:
created: created:
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentInfo.java - cameleer-server-core/src/main/java/com/cameleer/server/core/agent/AgentInfo.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentState.java - cameleer-server-core/src/main/java/com/cameleer/server/core/agent/AgentState.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentCommand.java - cameleer-server-core/src/main/java/com/cameleer/server/core/agent/AgentCommand.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/CommandStatus.java - cameleer-server-core/src/main/java/com/cameleer/server/core/agent/CommandStatus.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/CommandType.java - cameleer-server-core/src/main/java/com/cameleer/server/core/agent/CommandType.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentRegistryService.java - cameleer-server-core/src/main/java/com/cameleer/server/core/agent/AgentRegistryService.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentEventListener.java - cameleer-server-core/src/main/java/com/cameleer/server/core/agent/AgentEventListener.java
- cameleer3-server-core/src/test/java/com/cameleer3/server/core/agent/AgentRegistryServiceTest.java - cameleer-server-core/src/test/java/com/cameleer/server/core/agent/AgentRegistryServiceTest.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/AgentRegistryConfig.java - cameleer-server-app/src/main/java/com/cameleer/server/app/config/AgentRegistryConfig.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/AgentRegistryBeanConfig.java - cameleer-server-app/src/main/java/com/cameleer/server/app/config/AgentRegistryBeanConfig.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/agent/AgentLifecycleMonitor.java - cameleer-server-app/src/main/java/com/cameleer/server/app/agent/AgentLifecycleMonitor.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentRegistrationController.java - cameleer-server-app/src/main/java/com/cameleer/server/app/controller/AgentRegistrationController.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentRegistrationControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/AgentRegistrationControllerIT.java
modified: modified:
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/Cameleer3ServerApplication.java - cameleer-server-app/src/main/java/com/cameleer/server/app/CameleerServerApplication.java
- cameleer3-server-app/src/main/resources/application.yml - cameleer-server-app/src/main/resources/application.yml
key-decisions: key-decisions:
- "AgentInfo as Java record with wither-style methods for immutable ConcurrentHashMap swapping" - "AgentInfo as Java record with wither-style methods for immutable ConcurrentHashMap swapping"
@@ -103,7 +103,7 @@ _Note: Task 1 used TDD with separate RED/GREEN commits_
- `AgentRegistrationController.java` - REST endpoints for agents - `AgentRegistrationController.java` - REST endpoints for agents
- `AgentRegistryServiceTest.java` - 23 unit tests - `AgentRegistryServiceTest.java` - 23 unit tests
- `AgentRegistrationControllerIT.java` - 7 integration tests - `AgentRegistrationControllerIT.java` - 7 integration tests
- `Cameleer3ServerApplication.java` - Added AgentRegistryConfig to @EnableConfigurationProperties - `CameleerServerApplication.java` - Added AgentRegistryConfig to @EnableConfigurationProperties
- `application.yml` - Added agent-registry config section and spring.mvc.async.request-timeout - `application.yml` - Added agent-registry config section and spring.mvc.async.request-timeout
## Decisions Made ## Decisions Made

View File

@@ -5,12 +5,12 @@ type: execute
wave: 2 wave: 2
depends_on: ["03-01"] depends_on: ["03-01"]
files_modified: files_modified:
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/agent/SseConnectionManager.java - cameleer-server-app/src/main/java/com/cameleer/server/app/agent/SseConnectionManager.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentSseController.java - cameleer-server-app/src/main/java/com/cameleer/server/app/controller/AgentSseController.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentCommandController.java - cameleer-server-app/src/main/java/com/cameleer/server/app/controller/AgentCommandController.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/WebConfig.java - cameleer-server-app/src/main/java/com/cameleer/server/app/config/WebConfig.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentSseControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/AgentSseControllerIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentCommandControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/AgentCommandControllerIT.java
autonomous: true autonomous: true
requirements: requirements:
- AGNT-04 - AGNT-04
@@ -30,11 +30,11 @@ must_haves:
- "SSE events include event ID for Last-Event-ID reconnection support (no replay of missed events)" - "SSE events include event ID for Last-Event-ID reconnection support (no replay of missed events)"
- "Agent can acknowledge command receipt via POST /api/v1/agents/{id}/commands/{commandId}/ack" - "Agent can acknowledge command receipt via POST /api/v1/agents/{id}/commands/{commandId}/ack"
artifacts: artifacts:
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/agent/SseConnectionManager.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/agent/SseConnectionManager.java"
provides: "Per-agent SseEmitter management, event sending, ping keepalive" provides: "Per-agent SseEmitter management, event sending, ping keepalive"
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentSseController.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/controller/AgentSseController.java"
provides: "GET /{id}/events SSE endpoint" provides: "GET /{id}/events SSE endpoint"
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentCommandController.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/controller/AgentCommandController.java"
provides: "POST command endpoints (single, group, broadcast) + ack endpoint" provides: "POST command endpoints (single, group, broadcast) + ack endpoint"
key_links: key_links:
- from: "AgentCommandController" - from: "AgentCommandController"
@@ -75,49 +75,49 @@ Output: SseConnectionManager, SSE endpoint, command controller (single/group/bro
@.planning/phases/03-agent-registry-sse-push/03-RESEARCH.md @.planning/phases/03-agent-registry-sse-push/03-RESEARCH.md
@.planning/phases/03-agent-registry-sse-push/03-01-SUMMARY.md @.planning/phases/03-agent-registry-sse-push/03-01-SUMMARY.md
@cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/WebConfig.java @cameleer-server-app/src/main/java/com/cameleer/server/app/config/WebConfig.java
@cameleer3-server-app/src/main/resources/application.yml @cameleer-server-app/src/main/resources/application.yml
@cameleer3-server-app/src/test/java/com/cameleer3/server/app/AbstractClickHouseIT.java @cameleer-server-app/src/test/java/com/cameleer/server/app/AbstractClickHouseIT.java
<interfaces> <interfaces>
<!-- From Plan 01 (must exist before this plan executes) --> <!-- From Plan 01 (must exist before this plan executes) -->
From cameleer3-server-core/.../agent/AgentInfo.java: From cameleer-server-core/.../agent/AgentInfo.java:
```java ```java
// Record or class with fields: // Record or class with fields:
// id, name, group, version, routeIds, capabilities, state, registeredAt, lastHeartbeat, staleTransitionTime // id, name, group, version, routeIds, capabilities, state, registeredAt, lastHeartbeat, staleTransitionTime
// Methods: withState(), withLastHeartbeat(), etc. // Methods: withState(), withLastHeartbeat(), etc.
``` ```
From cameleer3-server-core/.../agent/AgentState.java: From cameleer-server-core/.../agent/AgentState.java:
```java ```java
public enum AgentState { LIVE, STALE, DEAD } public enum AgentState { LIVE, STALE, DEAD }
``` ```
From cameleer3-server-core/.../agent/CommandType.java: From cameleer-server-core/.../agent/CommandType.java:
```java ```java
public enum CommandType { CONFIG_UPDATE, DEEP_TRACE, REPLAY } public enum CommandType { CONFIG_UPDATE, DEEP_TRACE, REPLAY }
``` ```
From cameleer3-server-core/.../agent/CommandStatus.java: From cameleer-server-core/.../agent/CommandStatus.java:
```java ```java
public enum CommandStatus { PENDING, DELIVERED, ACKNOWLEDGED, EXPIRED } public enum CommandStatus { PENDING, DELIVERED, ACKNOWLEDGED, EXPIRED }
``` ```
From cameleer3-server-core/.../agent/AgentCommand.java: From cameleer-server-core/.../agent/AgentCommand.java:
```java ```java
// Record: id (UUID string), type (CommandType), payload (String JSON), targetAgentId, createdAt, status // Record: id (UUID string), type (CommandType), payload (String JSON), targetAgentId, createdAt, status
// Method: withStatus() // Method: withStatus()
``` ```
From cameleer3-server-core/.../agent/AgentEventListener.java: From cameleer-server-core/.../agent/AgentEventListener.java:
```java ```java
public interface AgentEventListener { public interface AgentEventListener {
void onCommandReady(String agentId, AgentCommand command); void onCommandReady(String agentId, AgentCommand command);
} }
``` ```
From cameleer3-server-core/.../agent/AgentRegistryService.java: From cameleer-server-core/.../agent/AgentRegistryService.java:
```java ```java
// Key methods: // Key methods:
// register(id, name, group, version, routeIds, capabilities) -> AgentInfo // register(id, name, group, version, routeIds, capabilities) -> AgentInfo
@@ -131,7 +131,7 @@ From cameleer3-server-core/.../agent/AgentRegistryService.java:
// setEventListener(listener) -> void // setEventListener(listener) -> void
``` ```
From cameleer3-server-app/.../config/AgentRegistryConfig.java: From cameleer-server-app/.../config/AgentRegistryConfig.java:
```java ```java
// @ConfigurationProperties(prefix = "agent-registry") // @ConfigurationProperties(prefix = "agent-registry")
// getPingIntervalMs(), getCommandExpiryMs(), etc. // getPingIntervalMs(), getCommandExpiryMs(), etc.
@@ -144,11 +144,11 @@ From cameleer3-server-app/.../config/AgentRegistryConfig.java:
<task type="auto"> <task type="auto">
<name>Task 1: SseConnectionManager, SSE controller, and command controller</name> <name>Task 1: SseConnectionManager, SSE controller, and command controller</name>
<files> <files>
cameleer3-server-app/src/main/java/com/cameleer3/server/app/agent/SseConnectionManager.java, cameleer-server-app/src/main/java/com/cameleer/server/app/agent/SseConnectionManager.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentSseController.java, cameleer-server-app/src/main/java/com/cameleer/server/app/controller/AgentSseController.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentCommandController.java, cameleer-server-app/src/main/java/com/cameleer/server/app/controller/AgentCommandController.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/AgentRegistryBeanConfig.java, cameleer-server-app/src/main/java/com/cameleer/server/app/config/AgentRegistryBeanConfig.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/WebConfig.java cameleer-server-app/src/main/java/com/cameleer/server/app/config/WebConfig.java
</files> </files>
<action> <action>
Build the SSE infrastructure and command delivery system: Build the SSE infrastructure and command delivery system:
@@ -181,7 +181,7 @@ From cameleer3-server-app/.../config/AgentRegistryConfig.java:
5. **Update WebConfig**: The SSE endpoint GET /api/v1/agents/{id}/events is already covered by the interceptor pattern "/api/v1/agents/**". Agents send the protocol version header on all requests (per research recommendation), so no exclusion needed. However, if the SSE GET causes issues because browsers/clients may not easily add custom headers to EventSource, add the SSE events path to excludePathPatterns: `/api/v1/agents/*/events`. This is a practical consideration -- add the exclusion to be safe. 5. **Update WebConfig**: The SSE endpoint GET /api/v1/agents/{id}/events is already covered by the interceptor pattern "/api/v1/agents/**". Agents send the protocol version header on all requests (per research recommendation), so no exclusion needed. However, if the SSE GET causes issues because browsers/clients may not easily add custom headers to EventSource, add the SSE events path to excludePathPatterns: `/api/v1/agents/*/events`. This is a practical consideration -- add the exclusion to be safe.
</action> </action>
<verify> <verify>
<automated>mvn compile -pl cameleer3-server-core,cameleer3-server-app</automated> <automated>mvn compile -pl cameleer-server-core,cameleer-server-app</automated>
</verify> </verify>
<done>SseConnectionManager, AgentSseController, and AgentCommandController compile. SSE endpoint returns SseEmitter. Command endpoints accept type/payload and deliver via SSE. Ping keepalive scheduled. WebConfig updated if needed.</done> <done>SseConnectionManager, AgentSseController, and AgentCommandController compile. SSE endpoint returns SseEmitter. Command endpoints accept type/payload and deliver via SSE. Ping keepalive scheduled. WebConfig updated if needed.</done>
</task> </task>
@@ -189,8 +189,8 @@ From cameleer3-server-app/.../config/AgentRegistryConfig.java:
<task type="auto"> <task type="auto">
<name>Task 2: Integration tests for SSE, commands, and full flow</name> <name>Task 2: Integration tests for SSE, commands, and full flow</name>
<files> <files>
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentSseControllerIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/controller/AgentSseControllerIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentCommandControllerIT.java cameleer-server-app/src/test/java/com/cameleer/server/app/controller/AgentCommandControllerIT.java
</files> </files>
<action> <action>
Write integration tests covering SSE connection, command delivery, ping, and acknowledgement: Write integration tests covering SSE connection, command delivery, ping, and acknowledgement:
@@ -224,7 +224,7 @@ From cameleer3-server-app/.../config/AgentRegistryConfig.java:
**Test configuration**: If ping interval needs to be shorter for tests, add to test application.yml or use @TestPropertySource with agent-registry.ping-interval-ms=1000. **Test configuration**: If ping interval needs to be shorter for tests, add to test application.yml or use @TestPropertySource with agent-registry.ping-interval-ms=1000.
</action> </action>
<verify> <verify>
<automated>mvn test -pl cameleer3-server-core,cameleer3-server-app -Dtest="Agent*"</automated> <automated>mvn test -pl cameleer-server-core,cameleer-server-app -Dtest="Agent*"</automated>
</verify> </verify>
<done>All SSE integration tests pass: connect/disconnect, config-update/deep-trace/replay delivery via SSE, ping keepalive received, Last-Event-ID accepted, command targeting (single/group/broadcast), command acknowledgement. mvn clean verify passes with all existing tests still green.</done> <done>All SSE integration tests pass: connect/disconnect, config-update/deep-trace/replay delivery via SSE, ping keepalive received, Last-Event-ID accepted, command targeting (single/group/broadcast), command acknowledgement. mvn clean verify passes with all existing tests still green.</done>
</task> </task>

View File

@@ -24,14 +24,14 @@ tech-stack:
key-files: key-files:
created: created:
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/agent/SseConnectionManager.java - cameleer-server-app/src/main/java/com/cameleer/server/app/agent/SseConnectionManager.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentSseController.java - cameleer-server-app/src/main/java/com/cameleer/server/app/controller/AgentSseController.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentCommandController.java - cameleer-server-app/src/main/java/com/cameleer/server/app/controller/AgentCommandController.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentSseControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/AgentSseControllerIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentCommandControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/AgentCommandControllerIT.java
modified: modified:
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/WebConfig.java - cameleer-server-app/src/main/java/com/cameleer/server/app/config/WebConfig.java
- cameleer3-server-app/src/test/resources/application-test.yml - cameleer-server-app/src/test/resources/application-test.yml
key-decisions: key-decisions:
- "SSE events path excluded from ProtocolVersionInterceptor for EventSource client compatibility" - "SSE events path excluded from ProtocolVersionInterceptor for EventSource client compatibility"

View File

@@ -6,7 +6,7 @@
## Summary ## Summary
This phase adds agent registration, heartbeat-based lifecycle management (LIVE/STALE/DEAD), and real-time command push via SSE to the Cameleer3 server. The technology stack is straightforward: Spring MVC's `SseEmitter` for server-push, `ConcurrentHashMap` for the in-memory agent registry, and `@Scheduled` for periodic lifecycle checks (same pattern already used by `ClickHouseFlushScheduler`). This phase adds agent registration, heartbeat-based lifecycle management (LIVE/STALE/DEAD), and real-time command push via SSE to the Cameleer server. The technology stack is straightforward: Spring MVC's `SseEmitter` for server-push, `ConcurrentHashMap` for the in-memory agent registry, and `@Scheduled` for periodic lifecycle checks (same pattern already used by `ClickHouseFlushScheduler`).
The main architectural challenge is managing per-agent SSE connections reliably -- handling disconnections, timeouts, and cleanup without leaking threads or emitters. The command delivery model (PENDING with 60s expiry, acknowledgement) adds a second concurrent data structure to manage alongside the registry itself. The main architectural challenge is managing per-agent SSE connections reliably -- handling disconnections, timeouts, and cleanup without leaking threads or emitters. The command delivery model (PENDING with 60s expiry, acknowledgement) adds a second concurrent data structure to manage alongside the registry itself.
@@ -93,7 +93,7 @@ No new dependencies required. Everything is already on the classpath.
### Recommended Project Structure ### Recommended Project Structure
``` ```
cameleer3-server-core/src/main/java/com/cameleer3/server/core/ cameleer-server-core/src/main/java/com/cameleer/server/core/
├── agent/ ├── agent/
│ ├── AgentInfo.java # Record: id, name, group, version, routeIds, capabilities, state, timestamps │ ├── AgentInfo.java # Record: id, name, group, version, routeIds, capabilities, state, timestamps
│ ├── AgentState.java # Enum: LIVE, STALE, DEAD │ ├── AgentState.java # Enum: LIVE, STALE, DEAD
@@ -101,7 +101,7 @@ cameleer3-server-core/src/main/java/com/cameleer3/server/core/
│ ├── AgentCommand.java # Record: id, type, payload, targetAgentId, createdAt, status │ ├── AgentCommand.java # Record: id, type, payload, targetAgentId, createdAt, status
│ └── CommandStatus.java # Enum: PENDING, DELIVERED, ACKNOWLEDGED, EXPIRED │ └── CommandStatus.java # Enum: PENDING, DELIVERED, ACKNOWLEDGED, EXPIRED
cameleer3-server-app/src/main/java/com/cameleer3/server/app/ cameleer-server-app/src/main/java/com/cameleer/server/app/
├── config/ ├── config/
│ ├── AgentRegistryConfig.java # @ConfigurationProperties(prefix = "agent-registry") │ ├── AgentRegistryConfig.java # @ConfigurationProperties(prefix = "agent-registry")
│ └── AgentRegistryBeanConfig.java # @Configuration: wires AgentRegistryService as bean │ └── AgentRegistryBeanConfig.java # @Configuration: wires AgentRegistryService as bean
@@ -452,30 +452,30 @@ spring:
|----------|-------| |----------|-------|
| Framework | JUnit 5 + Spring Boot Test (via spring-boot-starter-test) | | Framework | JUnit 5 + Spring Boot Test (via spring-boot-starter-test) |
| Config file | pom.xml (Surefire + Failsafe configured) | | Config file | pom.xml (Surefire + Failsafe configured) |
| Quick run command | `mvn test -pl cameleer3-server-core -Dtest=AgentRegistryServiceTest` | | Quick run command | `mvn test -pl cameleer-server-core -Dtest=AgentRegistryServiceTest` |
| Full suite command | `mvn clean verify` | | Full suite command | `mvn clean verify` |
### Phase Requirements to Test Map ### Phase Requirements to Test Map
| Req ID | Behavior | Test Type | Automated Command | File Exists? | | Req ID | Behavior | Test Type | Automated Command | File Exists? |
|--------|----------|-----------|-------------------|-------------| |--------|----------|-----------|-------------------|-------------|
| AGNT-01 | Agent registers and gets response | integration | `mvn test -pl cameleer3-server-app -Dtest=AgentRegistrationControllerIT#registerAgent*` | No - Wave 0 | | AGNT-01 | Agent registers and gets response | integration | `mvn test -pl cameleer-server-app -Dtest=AgentRegistrationControllerIT#registerAgent*` | No - Wave 0 |
| AGNT-02 | Lifecycle transitions LIVE/STALE/DEAD | unit | `mvn test -pl cameleer3-server-core -Dtest=AgentRegistryServiceTest#lifecycle*` | No - Wave 0 | | AGNT-02 | Lifecycle transitions LIVE/STALE/DEAD | unit | `mvn test -pl cameleer-server-core -Dtest=AgentRegistryServiceTest#lifecycle*` | No - Wave 0 |
| AGNT-03 | Heartbeat updates timestamp, returns 200/404 | integration | `mvn test -pl cameleer3-server-app -Dtest=AgentRegistrationControllerIT#heartbeat*` | No - Wave 0 | | AGNT-03 | Heartbeat updates timestamp, returns 200/404 | integration | `mvn test -pl cameleer-server-app -Dtest=AgentRegistrationControllerIT#heartbeat*` | No - Wave 0 |
| AGNT-04 | Config-update pushed via SSE | integration | `mvn test -pl cameleer3-server-app -Dtest=AgentSseControllerIT#configUpdate*` | No - Wave 0 | | AGNT-04 | Config-update pushed via SSE | integration | `mvn test -pl cameleer-server-app -Dtest=AgentSseControllerIT#configUpdate*` | No - Wave 0 |
| AGNT-05 | Deep-trace command pushed via SSE | integration | `mvn test -pl cameleer3-server-app -Dtest=AgentSseControllerIT#deepTrace*` | No - Wave 0 | | AGNT-05 | Deep-trace command pushed via SSE | integration | `mvn test -pl cameleer-server-app -Dtest=AgentSseControllerIT#deepTrace*` | No - Wave 0 |
| AGNT-06 | Replay command pushed via SSE | integration | `mvn test -pl cameleer3-server-app -Dtest=AgentSseControllerIT#replay*` | No - Wave 0 | | AGNT-06 | Replay command pushed via SSE | integration | `mvn test -pl cameleer-server-app -Dtest=AgentSseControllerIT#replay*` | No - Wave 0 |
| AGNT-07 | SSE ping keepalive + Last-Event-ID | integration | `mvn test -pl cameleer3-server-app -Dtest=AgentSseControllerIT#pingKeepalive*` | No - Wave 0 | | AGNT-07 | SSE ping keepalive + Last-Event-ID | integration | `mvn test -pl cameleer-server-app -Dtest=AgentSseControllerIT#pingKeepalive*` | No - Wave 0 |
### Sampling Rate ### Sampling Rate
- **Per task commit:** `mvn test -pl cameleer3-server-core,cameleer3-server-app -Dtest="Agent*"` (agent-related tests only) - **Per task commit:** `mvn test -pl cameleer-server-core,cameleer-server-app -Dtest="Agent*"` (agent-related tests only)
- **Per wave merge:** `mvn clean verify` - **Per wave merge:** `mvn clean verify`
- **Phase gate:** Full suite green before /gsd:verify-work - **Phase gate:** Full suite green before /gsd:verify-work
### Wave 0 Gaps ### Wave 0 Gaps
- [ ] `cameleer3-server-core/.../agent/AgentRegistryServiceTest.java` -- covers AGNT-02, AGNT-03 (unit tests for registry logic) - [ ] `cameleer-server-core/.../agent/AgentRegistryServiceTest.java` -- covers AGNT-02, AGNT-03 (unit tests for registry logic)
- [ ] `cameleer3-server-app/.../controller/AgentRegistrationControllerIT.java` -- covers AGNT-01, AGNT-03 - [ ] `cameleer-server-app/.../controller/AgentRegistrationControllerIT.java` -- covers AGNT-01, AGNT-03
- [ ] `cameleer3-server-app/.../controller/AgentSseControllerIT.java` -- covers AGNT-04, AGNT-05, AGNT-06, AGNT-07 - [ ] `cameleer-server-app/.../controller/AgentSseControllerIT.java` -- covers AGNT-04, AGNT-05, AGNT-06, AGNT-07
- [ ] `cameleer3-server-app/.../controller/AgentCommandControllerIT.java` -- covers command targeting (single, group, all) - [ ] `cameleer-server-app/.../controller/AgentCommandControllerIT.java` -- covers command targeting (single, group, all)
- [ ] No new framework install needed -- JUnit 5 + Spring Boot Test + Awaitility already in place - [ ] No new framework install needed -- JUnit 5 + Spring Boot Test + Awaitility already in place
### SSE Test Strategy ### SSE Test Strategy

View File

@@ -18,8 +18,8 @@ created: 2026-03-11
| Property | Value | | Property | Value |
|----------|-------| |----------|-------|
| **Framework** | JUnit 5 + Spring Boot Test + Testcontainers ClickHouse 25.3 | | **Framework** | JUnit 5 + Spring Boot Test + Testcontainers ClickHouse 25.3 |
| **Config file** | cameleer3-server-app/pom.xml (Surefire + Failsafe configured) | | **Config file** | cameleer-server-app/pom.xml (Surefire + Failsafe configured) |
| **Quick run command** | `mvn test -pl cameleer3-server-core -Dtest=AgentRegistryServiceTest` | | **Quick run command** | `mvn test -pl cameleer-server-core -Dtest=AgentRegistryServiceTest` |
| **Full suite command** | `mvn clean verify` | | **Full suite command** | `mvn clean verify` |
| **Estimated runtime** | ~50 seconds | | **Estimated runtime** | ~50 seconds |
@@ -27,7 +27,7 @@ created: 2026-03-11
## Sampling Rate ## Sampling Rate
- **After every task commit:** Run `mvn test -pl cameleer3-server-core,cameleer3-server-app -Dtest="Agent*"` - **After every task commit:** Run `mvn test -pl cameleer-server-core,cameleer-server-app -Dtest="Agent*"`
- **After every plan wave:** Run `mvn clean verify` - **After every plan wave:** Run `mvn clean verify`
- **Before `/gsd:verify-work`:** Full suite must be green - **Before `/gsd:verify-work`:** Full suite must be green
- **Max feedback latency:** 50 seconds - **Max feedback latency:** 50 seconds
@@ -38,13 +38,13 @@ created: 2026-03-11
| Task ID | Plan | Wave | Requirement | Test Type | Automated Command | File Exists | Status | | Task ID | Plan | Wave | Requirement | Test Type | Automated Command | File Exists | Status |
|---------|------|------|-------------|-----------|-------------------|-------------|--------| |---------|------|------|-------------|-----------|-------------------|-------------|--------|
| 03-01-01 | 01 | 1 | AGNT-01 | integration | `mvn test -pl cameleer3-server-app -Dtest=AgentRegistrationControllerIT#registerAgent*` | ❌ W0 | ⬜ pending | | 03-01-01 | 01 | 1 | AGNT-01 | integration | `mvn test -pl cameleer-server-app -Dtest=AgentRegistrationControllerIT#registerAgent*` | ❌ W0 | ⬜ pending |
| 03-01-02 | 01 | 1 | AGNT-02 | unit | `mvn test -pl cameleer3-server-core -Dtest=AgentRegistryServiceTest#lifecycle*` | ❌ W0 | ⬜ pending | | 03-01-02 | 01 | 1 | AGNT-02 | unit | `mvn test -pl cameleer-server-core -Dtest=AgentRegistryServiceTest#lifecycle*` | ❌ W0 | ⬜ pending |
| 03-01-03 | 01 | 1 | AGNT-03 | integration | `mvn test -pl cameleer3-server-app -Dtest=AgentRegistrationControllerIT#heartbeat*` | ❌ W0 | ⬜ pending | | 03-01-03 | 01 | 1 | AGNT-03 | integration | `mvn test -pl cameleer-server-app -Dtest=AgentRegistrationControllerIT#heartbeat*` | ❌ W0 | ⬜ pending |
| 03-02-01 | 02 | 1 | AGNT-04 | integration | `mvn test -pl cameleer3-server-app -Dtest=AgentSseControllerIT#configUpdate*` | ❌ W0 | ⬜ pending | | 03-02-01 | 02 | 1 | AGNT-04 | integration | `mvn test -pl cameleer-server-app -Dtest=AgentSseControllerIT#configUpdate*` | ❌ W0 | ⬜ pending |
| 03-02-02 | 02 | 1 | AGNT-05 | integration | `mvn test -pl cameleer3-server-app -Dtest=AgentSseControllerIT#deepTrace*` | ❌ W0 | ⬜ pending | | 03-02-02 | 02 | 1 | AGNT-05 | integration | `mvn test -pl cameleer-server-app -Dtest=AgentSseControllerIT#deepTrace*` | ❌ W0 | ⬜ pending |
| 03-02-03 | 02 | 1 | AGNT-06 | integration | `mvn test -pl cameleer3-server-app -Dtest=AgentSseControllerIT#replay*` | ❌ W0 | ⬜ pending | | 03-02-03 | 02 | 1 | AGNT-06 | integration | `mvn test -pl cameleer-server-app -Dtest=AgentSseControllerIT#replay*` | ❌ W0 | ⬜ pending |
| 03-02-04 | 02 | 1 | AGNT-07 | integration | `mvn test -pl cameleer3-server-app -Dtest=AgentSseControllerIT#pingKeepalive*` | ❌ W0 | ⬜ pending | | 03-02-04 | 02 | 1 | AGNT-07 | integration | `mvn test -pl cameleer-server-app -Dtest=AgentSseControllerIT#pingKeepalive*` | ❌ W0 | ⬜ pending |
*Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky* *Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky*

View File

@@ -51,18 +51,18 @@ re_verification: false
| Artifact | Expected | Status | Details | | Artifact | Expected | Status | Details |
|----------|----------|--------|---------| |----------|----------|--------|---------|
| `cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentRegistryService.java` | Registration, heartbeat, lifecycle, find/filter, commands | VERIFIED | 281 lines; full implementation with ConcurrentHashMap, compute-based atomic swaps, eventListener bridge | | `cameleer-server-core/src/main/java/com/cameleer/server/core/agent/AgentRegistryService.java` | Registration, heartbeat, lifecycle, find/filter, commands | VERIFIED | 281 lines; full implementation with ConcurrentHashMap, compute-based atomic swaps, eventListener bridge |
| `cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentInfo.java` | Immutable record with all fields and wither methods | VERIFIED | 63 lines; record with 10 fields and 5 wither-style methods | | `cameleer-server-core/src/main/java/com/cameleer/server/core/agent/AgentInfo.java` | Immutable record with all fields and wither methods | VERIFIED | 63 lines; record with 10 fields and 5 wither-style methods |
| `cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentRegistrationController.java` | POST /register, POST /{id}/heartbeat, GET /agents | VERIFIED | 153 lines; all three endpoints implemented with OpenAPI annotations | | `cameleer-server-app/src/main/java/com/cameleer/server/app/controller/AgentRegistrationController.java` | POST /register, POST /{id}/heartbeat, GET /agents | VERIFIED | 153 lines; all three endpoints implemented with OpenAPI annotations |
| `cameleer3-server-app/src/main/java/com/cameleer3/server/app/agent/AgentLifecycleMonitor.java` | @Scheduled LIVE->STALE->DEAD transitions | VERIFIED | 37 lines; calls `registryService.checkLifecycle()` and `expireOldCommands()` on schedule | | `cameleer-server-app/src/main/java/com/cameleer/server/app/agent/AgentLifecycleMonitor.java` | @Scheduled LIVE->STALE->DEAD transitions | VERIFIED | 37 lines; calls `registryService.checkLifecycle()` and `expireOldCommands()` on schedule |
### Plan 02 Artifacts ### Plan 02 Artifacts
| Artifact | Expected | Status | Details | | Artifact | Expected | Status | Details |
|----------|----------|--------|---------| |----------|----------|--------|---------|
| `cameleer3-server-app/src/main/java/com/cameleer3/server/app/agent/SseConnectionManager.java` | Per-agent SseEmitter management, event sending, ping | VERIFIED | 158 lines; implements AgentEventListener, reference-equality removal, @PostConstruct registration | | `cameleer-server-app/src/main/java/com/cameleer/server/app/agent/SseConnectionManager.java` | Per-agent SseEmitter management, event sending, ping | VERIFIED | 158 lines; implements AgentEventListener, reference-equality removal, @PostConstruct registration |
| `cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentSseController.java` | GET /{id}/events SSE endpoint | VERIFIED | 67 lines; checks agent exists, delegates to connectionManager.connect() | | `cameleer-server-app/src/main/java/com/cameleer/server/app/controller/AgentSseController.java` | GET /{id}/events SSE endpoint | VERIFIED | 67 lines; checks agent exists, delegates to connectionManager.connect() |
| `cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentCommandController.java` | POST commands (single/group/broadcast) + ack | VERIFIED | 182 lines; all four endpoints implemented | | `cameleer-server-app/src/main/java/com/cameleer/server/app/controller/AgentCommandController.java` | POST commands (single/group/broadcast) + ack | VERIFIED | 182 lines; all four endpoints implemented |
### Supporting Artifacts (confirmed present) ### Supporting Artifacts (confirmed present)
@@ -77,7 +77,7 @@ re_verification: false
| `AgentRegistryBeanConfig.java` (@Configuration) | VERIFIED — creates AgentRegistryService with config values | | `AgentRegistryBeanConfig.java` (@Configuration) | VERIFIED — creates AgentRegistryService with config values |
| `application.yml` | VERIFIED — agent-registry section present; `spring.mvc.async.request-timeout: -1` present | | `application.yml` | VERIFIED — agent-registry section present; `spring.mvc.async.request-timeout: -1` present |
| `application-test.yml` | VERIFIED — `agent-registry.ping-interval-ms: 1000` for fast SSE test assertions | | `application-test.yml` | VERIFIED — `agent-registry.ping-interval-ms: 1000` for fast SSE test assertions |
| `Cameleer3ServerApplication.java` | VERIFIED — `AgentRegistryConfig.class` added to `@EnableConfigurationProperties` | | `CameleerServerApplication.java` | VERIFIED — `AgentRegistryConfig.class` added to `@EnableConfigurationProperties` |
--- ---

View File

@@ -5,19 +5,19 @@ type: execute
wave: 1 wave: 1
depends_on: [] depends_on: []
files_modified: files_modified:
- cameleer3-server-app/pom.xml - cameleer-server-app/pom.xml
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/security/JwtService.java - cameleer-server-core/src/main/java/com/cameleer/server/core/security/JwtService.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/security/Ed25519SigningService.java - cameleer-server-core/src/main/java/com/cameleer/server/core/security/Ed25519SigningService.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/JwtServiceImpl.java - cameleer-server-app/src/main/java/com/cameleer/server/app/security/JwtServiceImpl.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/Ed25519SigningServiceImpl.java - cameleer-server-app/src/main/java/com/cameleer/server/app/security/Ed25519SigningServiceImpl.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/BootstrapTokenValidator.java - cameleer-server-app/src/main/java/com/cameleer/server/app/security/BootstrapTokenValidator.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/SecurityProperties.java - cameleer-server-app/src/main/java/com/cameleer/server/app/security/SecurityProperties.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/SecurityBeanConfig.java - cameleer-server-app/src/main/java/com/cameleer/server/app/security/SecurityBeanConfig.java
- cameleer3-server-app/src/main/resources/application.yml - cameleer-server-app/src/main/resources/application.yml
- cameleer3-server-app/src/test/resources/application-test.yml - cameleer-server-app/src/test/resources/application-test.yml
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/JwtServiceTest.java - cameleer-server-app/src/test/java/com/cameleer/server/app/security/JwtServiceTest.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/Ed25519SigningServiceTest.java - cameleer-server-app/src/test/java/com/cameleer/server/app/security/Ed25519SigningServiceTest.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/BootstrapTokenValidatorTest.java - cameleer-server-app/src/test/java/com/cameleer/server/app/security/BootstrapTokenValidatorTest.java
autonomous: true autonomous: true
requirements: requirements:
- SECU-03 - SECU-03
@@ -31,15 +31,15 @@ must_haves:
- "BootstrapTokenValidator accepts CAMELEER_AUTH_TOKEN and optionally CAMELEER_AUTH_TOKEN_PREVIOUS using constant-time comparison" - "BootstrapTokenValidator accepts CAMELEER_AUTH_TOKEN and optionally CAMELEER_AUTH_TOKEN_PREVIOUS using constant-time comparison"
- "Server fails fast on startup if CAMELEER_AUTH_TOKEN is not set" - "Server fails fast on startup if CAMELEER_AUTH_TOKEN is not set"
artifacts: artifacts:
- path: "cameleer3-server-core/src/main/java/com/cameleer3/server/core/security/JwtService.java" - path: "cameleer-server-core/src/main/java/com/cameleer/server/core/security/JwtService.java"
provides: "JWT service interface with createAccessToken, createRefreshToken, validateAndExtractAgentId" provides: "JWT service interface with createAccessToken, createRefreshToken, validateAndExtractAgentId"
- path: "cameleer3-server-core/src/main/java/com/cameleer3/server/core/security/Ed25519SigningService.java" - path: "cameleer-server-core/src/main/java/com/cameleer/server/core/security/Ed25519SigningService.java"
provides: "Ed25519 signing interface with sign(payload) and getPublicKeyBase64()" provides: "Ed25519 signing interface with sign(payload) and getPublicKeyBase64()"
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/JwtServiceImpl.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/security/JwtServiceImpl.java"
provides: "Nimbus JOSE+JWT HMAC-SHA256 implementation" provides: "Nimbus JOSE+JWT HMAC-SHA256 implementation"
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/Ed25519SigningServiceImpl.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/security/Ed25519SigningServiceImpl.java"
provides: "JDK 17 Ed25519 KeyPairGenerator implementation" provides: "JDK 17 Ed25519 KeyPairGenerator implementation"
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/BootstrapTokenValidator.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/security/BootstrapTokenValidator.java"
provides: "Constant-time bootstrap token validation with dual-token rotation" provides: "Constant-time bootstrap token validation with dual-token rotation"
key_links: key_links:
- from: "JwtServiceImpl" - from: "JwtServiceImpl"
@@ -76,10 +76,10 @@ Output: Working JwtService, Ed25519SigningService, BootstrapTokenValidator with
@.planning/phases/04-security/04-RESEARCH.md @.planning/phases/04-security/04-RESEARCH.md
@.planning/phases/04-security/04-VALIDATION.md @.planning/phases/04-security/04-VALIDATION.md
@cameleer3-server-app/pom.xml @cameleer-server-app/pom.xml
@cameleer3-server-app/src/main/resources/application.yml @cameleer-server-app/src/main/resources/application.yml
@cameleer3-server-app/src/test/resources/application-test.yml @cameleer-server-app/src/test/resources/application-test.yml
@cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/AgentRegistryConfig.java @cameleer-server-app/src/main/java/com/cameleer/server/app/config/AgentRegistryConfig.java
<interfaces> <interfaces>
<!-- Existing patterns to follow: core module = interfaces/domain, app module = Spring implementations --> <!-- Existing patterns to follow: core module = interfaces/domain, app module = Spring implementations -->
@@ -106,19 +106,19 @@ public class AgentRegistryConfig { ... }
<task type="auto" tdd="true"> <task type="auto" tdd="true">
<name>Task 1: Core interfaces + app implementations + Maven deps</name> <name>Task 1: Core interfaces + app implementations + Maven deps</name>
<files> <files>
cameleer3-server-app/pom.xml, cameleer-server-app/pom.xml,
cameleer3-server-core/src/main/java/com/cameleer3/server/core/security/JwtService.java, cameleer-server-core/src/main/java/com/cameleer/server/core/security/JwtService.java,
cameleer3-server-core/src/main/java/com/cameleer3/server/core/security/Ed25519SigningService.java, cameleer-server-core/src/main/java/com/cameleer/server/core/security/Ed25519SigningService.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/JwtServiceImpl.java, cameleer-server-app/src/main/java/com/cameleer/server/app/security/JwtServiceImpl.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/Ed25519SigningServiceImpl.java, cameleer-server-app/src/main/java/com/cameleer/server/app/security/Ed25519SigningServiceImpl.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/BootstrapTokenValidator.java, cameleer-server-app/src/main/java/com/cameleer/server/app/security/BootstrapTokenValidator.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/SecurityProperties.java, cameleer-server-app/src/main/java/com/cameleer/server/app/security/SecurityProperties.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/SecurityBeanConfig.java, cameleer-server-app/src/main/java/com/cameleer/server/app/security/SecurityBeanConfig.java,
cameleer3-server-app/src/main/resources/application.yml, cameleer-server-app/src/main/resources/application.yml,
cameleer3-server-app/src/test/resources/application-test.yml, cameleer-server-app/src/test/resources/application-test.yml,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/JwtServiceTest.java, cameleer-server-app/src/test/java/com/cameleer/server/app/security/JwtServiceTest.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/Ed25519SigningServiceTest.java, cameleer-server-app/src/test/java/com/cameleer/server/app/security/Ed25519SigningServiceTest.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/BootstrapTokenValidatorTest.java cameleer-server-app/src/test/java/com/cameleer/server/app/security/BootstrapTokenValidatorTest.java
</files> </files>
<behavior> <behavior>
JwtService tests: JwtService tests:
@@ -145,7 +145,7 @@ public class AgentRegistryConfig { ... }
- Uses constant-time comparison (MessageDigest.isEqual) - Uses constant-time comparison (MessageDigest.isEqual)
</behavior> </behavior>
<action> <action>
1. Add Maven dependencies to cameleer3-server-app/pom.xml: 1. Add Maven dependencies to cameleer-server-app/pom.xml:
- `spring-boot-starter-security` (managed version) - `spring-boot-starter-security` (managed version)
- `com.nimbusds:nimbus-jose-jwt:9.47` (explicit, may not be transitive without OAuth2 resource server) - `com.nimbusds:nimbus-jose-jwt:9.47` (explicit, may not be transitive without OAuth2 resource server)
- `spring-security-test` scope test (managed version) - `spring-security-test` scope test (managed version)
@@ -165,12 +165,12 @@ public class AgentRegistryConfig { ... }
5. Update application-test.yml: Add `security.bootstrap-token: test-bootstrap-token`, `security.bootstrap-token-previous: old-bootstrap-token`. Also set `CAMELEER_AUTH_TOKEN: test-bootstrap-token` as an env override if needed. 5. Update application-test.yml: Add `security.bootstrap-token: test-bootstrap-token`, `security.bootstrap-token-previous: old-bootstrap-token`. Also set `CAMELEER_AUTH_TOKEN: test-bootstrap-token` as an env override if needed.
6. IMPORTANT: Adding spring-boot-starter-security will break ALL existing tests immediately (401 on all endpoints). To prevent this during Plan 01 (before the security filter chain is configured in Plan 02), add a temporary test security config class `src/test/java/com/cameleer3/server/app/security/TestSecurityConfig.java` annotated `@TestConfiguration` that creates a `SecurityFilterChain` permitting all requests. This keeps existing tests green while security services are built. Plan 02 will replace this with real security config and update tests. 6. IMPORTANT: Adding spring-boot-starter-security will break ALL existing tests immediately (401 on all endpoints). To prevent this during Plan 01 (before the security filter chain is configured in Plan 02), add a temporary test security config class `src/test/java/com/cameleer/server/app/security/TestSecurityConfig.java` annotated `@TestConfiguration` that creates a `SecurityFilterChain` permitting all requests. This keeps existing tests green while security services are built. Plan 02 will replace this with real security config and update tests.
7. Write unit tests per the behavior spec above. Tests should NOT require Spring context -- construct implementations directly with test SecurityProperties. 7. Write unit tests per the behavior spec above. Tests should NOT require Spring context -- construct implementations directly with test SecurityProperties.
</action> </action>
<verify> <verify>
<automated>cd /c/Users/Hendrik/Documents/projects/cameleer3-server && mvn test -pl cameleer3-server-app -Dtest="JwtServiceTest,Ed25519SigningServiceTest,BootstrapTokenValidatorTest" -Dsurefire.reuseForks=false</automated> <automated>cd /c/Users/Hendrik/Documents/projects/cameleer-server && mvn test -pl cameleer-server-app -Dtest="JwtServiceTest,Ed25519SigningServiceTest,BootstrapTokenValidatorTest" -Dsurefire.reuseForks=false</automated>
</verify> </verify>
<done> <done>
- JwtService creates and validates access/refresh JWTs with correct claims and expiry - JwtService creates and validates access/refresh JWTs with correct claims and expiry

View File

@@ -25,22 +25,22 @@ tech-stack:
key-files: key-files:
created: created:
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/security/JwtService.java - cameleer-server-core/src/main/java/com/cameleer/server/core/security/JwtService.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/security/Ed25519SigningService.java - cameleer-server-core/src/main/java/com/cameleer/server/core/security/Ed25519SigningService.java
- cameleer3-server-core/src/main/java/com/cameleer3/server/core/security/InvalidTokenException.java - cameleer-server-core/src/main/java/com/cameleer/server/core/security/InvalidTokenException.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/JwtServiceImpl.java - cameleer-server-app/src/main/java/com/cameleer/server/app/security/JwtServiceImpl.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/Ed25519SigningServiceImpl.java - cameleer-server-app/src/main/java/com/cameleer/server/app/security/Ed25519SigningServiceImpl.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/BootstrapTokenValidator.java - cameleer-server-app/src/main/java/com/cameleer/server/app/security/BootstrapTokenValidator.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/SecurityProperties.java - cameleer-server-app/src/main/java/com/cameleer/server/app/security/SecurityProperties.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/SecurityBeanConfig.java - cameleer-server-app/src/main/java/com/cameleer/server/app/security/SecurityBeanConfig.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/TestSecurityConfig.java - cameleer-server-app/src/test/java/com/cameleer/server/app/security/TestSecurityConfig.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/JwtServiceTest.java - cameleer-server-app/src/test/java/com/cameleer/server/app/security/JwtServiceTest.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/Ed25519SigningServiceTest.java - cameleer-server-app/src/test/java/com/cameleer/server/app/security/Ed25519SigningServiceTest.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/BootstrapTokenValidatorTest.java - cameleer-server-app/src/test/java/com/cameleer/server/app/security/BootstrapTokenValidatorTest.java
modified: modified:
- cameleer3-server-app/pom.xml - cameleer-server-app/pom.xml
- cameleer3-server-app/src/main/resources/application.yml - cameleer-server-app/src/main/resources/application.yml
- cameleer3-server-app/src/test/resources/application-test.yml - cameleer-server-app/src/test/resources/application-test.yml
key-decisions: key-decisions:
- "HMAC-SHA256 with ephemeral 256-bit secret for JWT signing (simpler than Ed25519 for tokens, Ed25519 reserved for config signing)" - "HMAC-SHA256 with ephemeral 256-bit secret for JWT signing (simpler than Ed25519 for tokens, Ed25519 reserved for config signing)"
@@ -91,21 +91,21 @@ _No REFACTOR commit needed -- implementations are clean and minimal._
## Files Created/Modified ## Files Created/Modified
- `cameleer3-server-core/.../security/JwtService.java` - JWT service interface with create/validate methods - `cameleer-server-core/.../security/JwtService.java` - JWT service interface with create/validate methods
- `cameleer3-server-core/.../security/Ed25519SigningService.java` - Ed25519 signing interface with sign/getPublicKeyBase64 - `cameleer-server-core/.../security/Ed25519SigningService.java` - Ed25519 signing interface with sign/getPublicKeyBase64
- `cameleer3-server-core/.../security/InvalidTokenException.java` - Runtime exception for invalid/expired/wrong-type tokens - `cameleer-server-core/.../security/InvalidTokenException.java` - Runtime exception for invalid/expired/wrong-type tokens
- `cameleer3-server-app/.../security/JwtServiceImpl.java` - Nimbus JOSE+JWT HMAC-SHA256 implementation - `cameleer-server-app/.../security/JwtServiceImpl.java` - Nimbus JOSE+JWT HMAC-SHA256 implementation
- `cameleer3-server-app/.../security/Ed25519SigningServiceImpl.java` - JDK 17 Ed25519 KeyPairGenerator implementation - `cameleer-server-app/.../security/Ed25519SigningServiceImpl.java` - JDK 17 Ed25519 KeyPairGenerator implementation
- `cameleer3-server-app/.../security/BootstrapTokenValidator.java` - Constant-time bootstrap token validation - `cameleer-server-app/.../security/BootstrapTokenValidator.java` - Constant-time bootstrap token validation
- `cameleer3-server-app/.../security/SecurityProperties.java` - Config properties for token expiry and bootstrap tokens - `cameleer-server-app/.../security/SecurityProperties.java` - Config properties for token expiry and bootstrap tokens
- `cameleer3-server-app/.../security/SecurityBeanConfig.java` - Bean wiring with fail-fast startup validation - `cameleer-server-app/.../security/SecurityBeanConfig.java` - Bean wiring with fail-fast startup validation
- `cameleer3-server-app/.../security/TestSecurityConfig.java` - Temporary permit-all for existing test compatibility - `cameleer-server-app/.../security/TestSecurityConfig.java` - Temporary permit-all for existing test compatibility
- `cameleer3-server-app/pom.xml` - Added nimbus-jose-jwt, spring-boot-starter-security, spring-security-test - `cameleer-server-app/pom.xml` - Added nimbus-jose-jwt, spring-boot-starter-security, spring-security-test
- `cameleer3-server-app/.../application.yml` - Security config section with env var mapping - `cameleer-server-app/.../application.yml` - Security config section with env var mapping
- `cameleer3-server-app/.../application-test.yml` - Test bootstrap token values - `cameleer-server-app/.../application-test.yml` - Test bootstrap token values
- `cameleer3-server-app/.../security/JwtServiceTest.java` - 7 unit tests for JWT creation/validation - `cameleer-server-app/.../security/JwtServiceTest.java` - 7 unit tests for JWT creation/validation
- `cameleer3-server-app/.../security/Ed25519SigningServiceTest.java` - 5 unit tests for signing/verification - `cameleer-server-app/.../security/Ed25519SigningServiceTest.java` - 5 unit tests for signing/verification
- `cameleer3-server-app/.../security/BootstrapTokenValidatorTest.java` - 6 unit tests for token matching - `cameleer-server-app/.../security/BootstrapTokenValidatorTest.java` - 6 unit tests for token matching
## Decisions Made ## Decisions Made

View File

@@ -5,17 +5,17 @@ type: execute
wave: 2 wave: 2
depends_on: ["04-01"] depends_on: ["04-01"]
files_modified: files_modified:
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/JwtAuthenticationFilter.java - cameleer-server-app/src/main/java/com/cameleer/server/app/security/JwtAuthenticationFilter.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/SecurityConfig.java - cameleer-server-app/src/main/java/com/cameleer/server/app/security/SecurityConfig.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentRegistrationController.java - cameleer-server-app/src/main/java/com/cameleer/server/app/controller/AgentRegistrationController.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentSseController.java - cameleer-server-app/src/main/java/com/cameleer/server/app/controller/AgentSseController.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/WebConfig.java - cameleer-server-app/src/main/java/com/cameleer/server/app/config/WebConfig.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/SecurityFilterIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/security/SecurityFilterIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/JwtRefreshIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/security/JwtRefreshIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/RegistrationSecurityIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/security/RegistrationSecurityIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/BootstrapTokenIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/security/BootstrapTokenIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/TestSecurityHelper.java - cameleer-server-app/src/test/java/com/cameleer/server/app/TestSecurityHelper.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/TestSecurityConfig.java - cameleer-server-app/src/test/java/com/cameleer/server/app/security/TestSecurityConfig.java
autonomous: true autonomous: true
requirements: requirements:
- SECU-01 - SECU-01
@@ -30,11 +30,11 @@ must_haves:
- "SSE endpoint accepts JWT via ?token= query parameter" - "SSE endpoint accepts JWT via ?token= query parameter"
- "Health endpoint and Swagger UI remain publicly accessible" - "Health endpoint and Swagger UI remain publicly accessible"
artifacts: artifacts:
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/JwtAuthenticationFilter.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/security/JwtAuthenticationFilter.java"
provides: "OncePerRequestFilter extracting JWT from header or query param" provides: "OncePerRequestFilter extracting JWT from header or query param"
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/SecurityConfig.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/security/SecurityConfig.java"
provides: "SecurityFilterChain with permitAll for public paths, authenticated for rest" provides: "SecurityFilterChain with permitAll for public paths, authenticated for rest"
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentRegistrationController.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/controller/AgentRegistrationController.java"
provides: "Updated register endpoint with bootstrap token validation, JWT issuance, public key" provides: "Updated register endpoint with bootstrap token validation, JWT issuance, public key"
key_links: key_links:
- from: "JwtAuthenticationFilter" - from: "JwtAuthenticationFilter"
@@ -76,11 +76,11 @@ Output: Working security filter chain with protected/public endpoints, registrat
@.planning/phases/04-security/04-VALIDATION.md @.planning/phases/04-security/04-VALIDATION.md
@.planning/phases/04-security/04-01-SUMMARY.md @.planning/phases/04-security/04-01-SUMMARY.md
@cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentRegistrationController.java @cameleer-server-app/src/main/java/com/cameleer/server/app/controller/AgentRegistrationController.java
@cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentSseController.java @cameleer-server-app/src/main/java/com/cameleer/server/app/controller/AgentSseController.java
@cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/WebConfig.java @cameleer-server-app/src/main/java/com/cameleer/server/app/config/WebConfig.java
@cameleer3-server-app/src/test/java/com/cameleer3/server/app/AbstractClickHouseIT.java @cameleer-server-app/src/test/java/com/cameleer/server/app/AbstractClickHouseIT.java
@cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentRegistrationControllerIT.java @cameleer-server-app/src/test/java/com/cameleer/server/app/controller/AgentRegistrationControllerIT.java
<interfaces> <interfaces>
<!-- From Plan 01 (will exist after execution): --> <!-- From Plan 01 (will exist after execution): -->
@@ -136,11 +136,11 @@ public class AgentRegistryService {
<task type="auto"> <task type="auto">
<name>Task 1: SecurityFilterChain + JwtAuthenticationFilter + registration/refresh integration</name> <name>Task 1: SecurityFilterChain + JwtAuthenticationFilter + registration/refresh integration</name>
<files> <files>
cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/JwtAuthenticationFilter.java, cameleer-server-app/src/main/java/com/cameleer/server/app/security/JwtAuthenticationFilter.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/SecurityConfig.java, cameleer-server-app/src/main/java/com/cameleer/server/app/security/SecurityConfig.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentRegistrationController.java, cameleer-server-app/src/main/java/com/cameleer/server/app/controller/AgentRegistrationController.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentSseController.java, cameleer-server-app/src/main/java/com/cameleer/server/app/controller/AgentSseController.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/WebConfig.java cameleer-server-app/src/main/java/com/cameleer/server/app/config/WebConfig.java
</files> </files>
<action> <action>
1. Create `JwtAuthenticationFilter extends OncePerRequestFilter` (NOT annotated @Component -- constructed in SecurityConfig to avoid double registration): 1. Create `JwtAuthenticationFilter extends OncePerRequestFilter` (NOT annotated @Component -- constructed in SecurityConfig to avoid double registration):
@@ -176,7 +176,7 @@ public class AgentRegistryService {
6. Update `WebConfig` if needed: The `ProtocolVersionInterceptor` excluded paths should align with Spring Security public paths. The SSE events path is already excluded from protocol version check (Phase 3 decision). Verify no conflicts. 6. Update `WebConfig` if needed: The `ProtocolVersionInterceptor` excluded paths should align with Spring Security public paths. The SSE events path is already excluded from protocol version check (Phase 3 decision). Verify no conflicts.
</action> </action>
<verify> <verify>
<automated>cd /c/Users/Hendrik/Documents/projects/cameleer3-server && mvn clean compile -pl cameleer3-server-app</automated> <automated>cd /c/Users/Hendrik/Documents/projects/cameleer-server && mvn clean compile -pl cameleer-server-app</automated>
</verify> </verify>
<done> <done>
- SecurityConfig creates stateless filter chain with correct public/protected path split - SecurityConfig creates stateless filter chain with correct public/protected path split
@@ -190,28 +190,28 @@ public class AgentRegistryService {
<task type="auto"> <task type="auto">
<name>Task 2: Security integration tests + existing test adaptation</name> <name>Task 2: Security integration tests + existing test adaptation</name>
<files> <files>
cameleer3-server-app/src/test/java/com/cameleer3/server/app/TestSecurityHelper.java, cameleer-server-app/src/test/java/com/cameleer/server/app/TestSecurityHelper.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/TestSecurityConfig.java, cameleer-server-app/src/test/java/com/cameleer/server/app/security/TestSecurityConfig.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/SecurityFilterIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/security/SecurityFilterIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/JwtRefreshIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/security/JwtRefreshIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/RegistrationSecurityIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/security/RegistrationSecurityIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/BootstrapTokenIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/security/BootstrapTokenIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentRegistrationControllerIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/controller/AgentRegistrationControllerIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/ExecutionControllerIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/controller/ExecutionControllerIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/DiagramControllerIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DiagramControllerIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/MetricsControllerIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/controller/MetricsControllerIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/BackpressureIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/controller/BackpressureIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/DiagramRenderControllerIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DiagramRenderControllerIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/DetailControllerIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DetailControllerIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/SearchControllerIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/controller/SearchControllerIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentCommandControllerIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/controller/AgentCommandControllerIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentSseControllerIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/controller/AgentSseControllerIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/storage/DiagramLinkingIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/storage/DiagramLinkingIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/storage/IngestionSchemaIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/storage/IngestionSchemaIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/interceptor/ProtocolVersionIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/interceptor/ProtocolVersionIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/OpenApiIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/controller/OpenApiIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/ForwardCompatIT.java, cameleer-server-app/src/test/java/com/cameleer/server/app/controller/ForwardCompatIT.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/HealthControllerIT.java cameleer-server-app/src/test/java/com/cameleer/server/app/controller/HealthControllerIT.java
</files> </files>
<action> <action>
1. Replace the Plan 01 temporary `TestSecurityConfig` (permit-all) with real security active in tests. Remove the permit-all override so tests run with actual security enforcement. 1. Replace the Plan 01 temporary `TestSecurityConfig` (permit-all) with real security active in tests. Remove the permit-all override so tests run with actual security enforcement.
@@ -259,7 +259,7 @@ public class AgentRegistryService {
- Test: New access token from refresh can access protected endpoints - Test: New access token from refresh can access protected endpoints
</action> </action>
<verify> <verify>
<automated>cd /c/Users/Hendrik/Documents/projects/cameleer3-server && mvn clean verify</automated> <automated>cd /c/Users/Hendrik/Documents/projects/cameleer-server && mvn clean verify</automated>
</verify> </verify>
<done> <done>
- All 17 existing ITs pass with JWT authentication - All 17 existing ITs pass with JWT authentication

View File

@@ -25,31 +25,31 @@ tech-stack:
key-files: key-files:
created: created:
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/JwtAuthenticationFilter.java - cameleer-server-app/src/main/java/com/cameleer/server/app/security/JwtAuthenticationFilter.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/SecurityConfig.java - cameleer-server-app/src/main/java/com/cameleer/server/app/security/SecurityConfig.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/TestSecurityHelper.java - cameleer-server-app/src/test/java/com/cameleer/server/app/TestSecurityHelper.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/SecurityFilterIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/security/SecurityFilterIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/BootstrapTokenIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/security/BootstrapTokenIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/RegistrationSecurityIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/security/RegistrationSecurityIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/JwtRefreshIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/security/JwtRefreshIT.java
modified: modified:
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentRegistrationController.java - cameleer-server-app/src/main/java/com/cameleer/server/app/controller/AgentRegistrationController.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/WebConfig.java - cameleer-server-app/src/main/java/com/cameleer/server/app/config/WebConfig.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/TestSecurityConfig.java - cameleer-server-app/src/test/java/com/cameleer/server/app/security/TestSecurityConfig.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentRegistrationControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/AgentRegistrationControllerIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/ExecutionControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/ExecutionControllerIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/DiagramControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DiagramControllerIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/MetricsControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/MetricsControllerIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/BackpressureIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/BackpressureIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/DiagramRenderControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DiagramRenderControllerIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/DetailControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DetailControllerIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/SearchControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/SearchControllerIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentCommandControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/AgentCommandControllerIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentSseControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/AgentSseControllerIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/storage/DiagramLinkingIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/storage/DiagramLinkingIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/storage/IngestionSchemaIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/storage/IngestionSchemaIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/interceptor/ProtocolVersionIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/interceptor/ProtocolVersionIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/ForwardCompatIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/ForwardCompatIT.java
key-decisions: key-decisions:
- "Added /error to SecurityConfig permitAll to allow Spring Boot error page forwarding through security" - "Added /error to SecurityConfig permitAll to allow Spring Boot error page forwarding through security"

View File

@@ -5,10 +5,10 @@ type: execute
wave: 2 wave: 2
depends_on: ["04-01"] depends_on: ["04-01"]
files_modified: files_modified:
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/agent/SseConnectionManager.java - cameleer-server-app/src/main/java/com/cameleer/server/app/agent/SseConnectionManager.java
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/agent/SsePayloadSigner.java - cameleer-server-app/src/main/java/com/cameleer/server/app/agent/SsePayloadSigner.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/SseSigningIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/security/SseSigningIT.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/agent/SsePayloadSignerTest.java - cameleer-server-app/src/test/java/com/cameleer/server/app/agent/SsePayloadSignerTest.java
autonomous: true autonomous: true
requirements: requirements:
- SECU-04 - SECU-04
@@ -19,9 +19,9 @@ must_haves:
- "Signature is computed over the payload JSON without the signature field, then added as a 'signature' field" - "Signature is computed over the payload JSON without the signature field, then added as a 'signature' field"
- "Agent can verify the signature using the public key received at registration" - "Agent can verify the signature using the public key received at registration"
artifacts: artifacts:
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/agent/SsePayloadSigner.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/agent/SsePayloadSigner.java"
provides: "Component that signs SSE command payloads before delivery" provides: "Component that signs SSE command payloads before delivery"
- path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/agent/SseConnectionManager.java" - path: "cameleer-server-app/src/main/java/com/cameleer/server/app/agent/SseConnectionManager.java"
provides: "Updated onCommandReady with signing before sendEvent" provides: "Updated onCommandReady with signing before sendEvent"
key_links: key_links:
- from: "SseConnectionManager.onCommandReady" - from: "SseConnectionManager.onCommandReady"
@@ -54,7 +54,7 @@ Output: All SSE command events carry verifiable Ed25519 signatures.
@.planning/phases/04-security/04-RESEARCH.md @.planning/phases/04-security/04-RESEARCH.md
@.planning/phases/04-security/04-01-SUMMARY.md @.planning/phases/04-security/04-01-SUMMARY.md
@cameleer3-server-app/src/main/java/com/cameleer3/server/app/agent/SseConnectionManager.java @cameleer-server-app/src/main/java/com/cameleer/server/app/agent/SseConnectionManager.java
<interfaces> <interfaces>
<!-- From Plan 01 (will exist after execution): --> <!-- From Plan 01 (will exist after execution): -->
@@ -98,10 +98,10 @@ public record AgentCommand(String id, CommandType type, String payload, String a
<task type="auto" tdd="true"> <task type="auto" tdd="true">
<name>Task 1: SsePayloadSigner + signing integration in SseConnectionManager</name> <name>Task 1: SsePayloadSigner + signing integration in SseConnectionManager</name>
<files> <files>
cameleer3-server-app/src/main/java/com/cameleer3/server/app/agent/SsePayloadSigner.java, cameleer-server-app/src/main/java/com/cameleer/server/app/agent/SsePayloadSigner.java,
cameleer3-server-app/src/main/java/com/cameleer3/server/app/agent/SseConnectionManager.java, cameleer-server-app/src/main/java/com/cameleer/server/app/agent/SseConnectionManager.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/agent/SsePayloadSignerTest.java, cameleer-server-app/src/test/java/com/cameleer/server/app/agent/SsePayloadSignerTest.java,
cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/SseSigningIT.java cameleer-server-app/src/test/java/com/cameleer/server/app/security/SseSigningIT.java
</files> </files>
<behavior> <behavior>
SsePayloadSigner unit tests: SsePayloadSigner unit tests:
@@ -155,7 +155,7 @@ public record AgentCommand(String id, CommandType type, String payload, String a
- NOTE: This test depends on Plan 02's bootstrap token and JWT auth being in place. If Plan 03 executes before Plan 02, the test will need the TestSecurityHelper or a different auth approach. Since both are Wave 2 but independent, document this: "If Plan 02 is not yet complete, use TestSecurityHelper from Plan 01's temporary permit-all config." - NOTE: This test depends on Plan 02's bootstrap token and JWT auth being in place. If Plan 03 executes before Plan 02, the test will need the TestSecurityHelper or a different auth approach. Since both are Wave 2 but independent, document this: "If Plan 02 is not yet complete, use TestSecurityHelper from Plan 01's temporary permit-all config."
</action> </action>
<verify> <verify>
<automated>cd /c/Users/Hendrik/Documents/projects/cameleer3-server && mvn test -pl cameleer3-server-app -Dtest="SsePayloadSignerTest,SseSigningIT" -Dsurefire.reuseForks=false</automated> <automated>cd /c/Users/Hendrik/Documents/projects/cameleer-server && mvn test -pl cameleer-server-app -Dtest="SsePayloadSignerTest,SseSigningIT" -Dsurefire.reuseForks=false</automated>
</verify> </verify>
<done> <done>
- SsePayloadSigner signs JSON payloads with Ed25519 and adds signature field - SsePayloadSigner signs JSON payloads with Ed25519 and adds signature field

View File

@@ -22,11 +22,11 @@ tech-stack:
key-files: key-files:
created: created:
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/agent/SsePayloadSigner.java - cameleer-server-app/src/main/java/com/cameleer/server/app/agent/SsePayloadSigner.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/agent/SsePayloadSignerTest.java - cameleer-server-app/src/test/java/com/cameleer/server/app/agent/SsePayloadSignerTest.java
- cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/SseSigningIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/security/SseSigningIT.java
modified: modified:
- cameleer3-server-app/src/main/java/com/cameleer3/server/app/agent/SseConnectionManager.java - cameleer-server-app/src/main/java/com/cameleer/server/app/agent/SseConnectionManager.java
key-decisions: key-decisions:
- "Signed payload parsed to JsonNode before passing to SseEmitter to avoid double-quoting raw JSON strings" - "Signed payload parsed to JsonNode before passing to SseEmitter to avoid double-quoting raw JSON strings"
@@ -73,10 +73,10 @@ _No REFACTOR commit needed -- implementation is clean and minimal._
## Files Created/Modified ## Files Created/Modified
- `cameleer3-server-app/.../agent/SsePayloadSigner.java` - Component that signs JSON payloads with Ed25519 and adds signature field - `cameleer-server-app/.../agent/SsePayloadSigner.java` - Component that signs JSON payloads with Ed25519 and adds signature field
- `cameleer3-server-app/.../agent/SseConnectionManager.java` - Updated onCommandReady to sign payload before SSE delivery - `cameleer-server-app/.../agent/SseConnectionManager.java` - Updated onCommandReady to sign payload before SSE delivery
- `cameleer3-server-app/.../agent/SsePayloadSignerTest.java` - 7 unit tests for signing behavior and edge cases - `cameleer-server-app/.../agent/SsePayloadSignerTest.java` - 7 unit tests for signing behavior and edge cases
- `cameleer3-server-app/.../security/SseSigningIT.java` - 2 integration tests for end-to-end signature verification - `cameleer-server-app/.../security/SseSigningIT.java` - 2 integration tests for end-to-end signature verification
## Decisions Made ## Decisions Made

View File

@@ -6,7 +6,7 @@
## Summary ## Summary
This phase adds authentication and integrity protection to the Cameleer3 server. The implementation uses Spring Security 6.4.3 (managed by Spring Boot 3.4.3) with a custom `OncePerRequestFilter` for JWT validation, JDK 17 built-in Ed25519 for signing SSE payloads, and environment variable-based bootstrap tokens for agent registration. The approach is deliberately simple -- no OAuth2 resource server, no external identity provider, just symmetric HMAC JWTs for access control and Ed25519 signatures for payload integrity. This phase adds authentication and integrity protection to the Cameleer server. The implementation uses Spring Security 6.4.3 (managed by Spring Boot 3.4.3) with a custom `OncePerRequestFilter` for JWT validation, JDK 17 built-in Ed25519 for signing SSE payloads, and environment variable-based bootstrap tokens for agent registration. The approach is deliberately simple -- no OAuth2 resource server, no external identity provider, just symmetric HMAC JWTs for access control and Ed25519 signatures for payload integrity.
The existing codebase has clear integration points: `AgentRegistrationController.register()` already returns `serverPublicKey: null` as a placeholder, `SseConnectionManager.onCommandReady()` is the signing hook for SSE events, and `WebConfig` already defines excluded paths that align with the public endpoint list. Spring Security's `SecurityFilterChain` replaces the need for hand-rolled authorization logic -- endpoints are protected by default, with explicit `permitAll()` for health, register, and docs. The existing codebase has clear integration points: `AgentRegistrationController.register()` already returns `serverPublicKey: null` as a placeholder, `SseConnectionManager.onCommandReady()` is the signing hook for SSE events, and `WebConfig` already defines excluded paths that align with the public endpoint list. Spring Security's `SecurityFilterChain` replaces the need for hand-rolled authorization logic -- endpoints are protected by default, with explicit `permitAll()` for health, register, and docs.
@@ -89,7 +89,7 @@ The existing codebase has clear integration points: `AgentRegistrationController
- **Ed25519 library:** Use JDK built-in. Zero external dependencies, native performance, well-tested in JDK 17+. - **Ed25519 library:** Use JDK built-in. Zero external dependencies, native performance, well-tested in JDK 17+.
- **Refresh token storage:** Use stateless signed refresh tokens (also HMAC-signed JWTs with different claims/expiry). This avoids any in-memory storage for refresh tokens and scales naturally. The refresh token is just a JWT with `type=refresh`, `sub=agentId`, and 7-day expiry. On refresh, validate the refresh JWT, check agent still exists, issue new access JWT. - **Refresh token storage:** Use stateless signed refresh tokens (also HMAC-signed JWTs with different claims/expiry). This avoids any in-memory storage for refresh tokens and scales naturally. The refresh token is just a JWT with `type=refresh`, `sub=agentId`, and 7-day expiry. On refresh, validate the refresh JWT, check agent still exists, issue new access JWT.
**Installation (add to cameleer3-server-app pom.xml):** **Installation (add to cameleer-server-app pom.xml):**
```xml ```xml
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@@ -108,12 +108,12 @@ Note: If `spring-boot-starter-security` brings Nimbus transitively (via `spring-
### Recommended Project Structure ### Recommended Project Structure
``` ```
cameleer3-server-core/src/main/java/com/cameleer3/server/core/ cameleer-server-core/src/main/java/com/cameleer/server/core/
security/ security/
JwtService.java # Interface: createAccessToken, createRefreshToken, validateToken, extractAgentId JwtService.java # Interface: createAccessToken, createRefreshToken, validateToken, extractAgentId
Ed25519SigningService.java # Interface: sign(payload) -> signature, getPublicKeyBase64() Ed25519SigningService.java # Interface: sign(payload) -> signature, getPublicKeyBase64()
cameleer3-server-app/src/main/java/com/cameleer3/server/app/ cameleer-server-app/src/main/java/com/cameleer/server/app/
security/ security/
JwtServiceImpl.java # Nimbus JOSE+JWT HMAC implementation JwtServiceImpl.java # Nimbus JOSE+JWT HMAC implementation
Ed25519SigningServiceImpl.java # JDK Ed25519 keypair + signing implementation Ed25519SigningServiceImpl.java # JDK Ed25519 keypair + signing implementation
@@ -439,23 +439,23 @@ public boolean validateBootstrapToken(String provided) {
| Property | Value | | Property | Value |
|----------|-------| |----------|-------|
| Framework | JUnit 5 + Spring Boot Test (spring-boot-starter-test) | | Framework | JUnit 5 + Spring Boot Test (spring-boot-starter-test) |
| Config file | `cameleer3-server-app/src/test/resources/application-test.yml` | | Config file | `cameleer-server-app/src/test/resources/application-test.yml` |
| Quick run command | `mvn test -pl cameleer3-server-app -Dtest=Security*Test -Dsurefire.reuseForks=false` | | Quick run command | `mvn test -pl cameleer-server-app -Dtest=Security*Test -Dsurefire.reuseForks=false` |
| Full suite command | `mvn clean verify` | | Full suite command | `mvn clean verify` |
### Phase Requirements to Test Map ### Phase Requirements to Test Map
| Req ID | Behavior | Test Type | Automated Command | File Exists? | | Req ID | Behavior | Test Type | Automated Command | File Exists? |
|--------|----------|-----------|-------------------|-------------| |--------|----------|-----------|-------------------|-------------|
| SECU-01 | Protected endpoints reject requests without JWT; public endpoints accessible | integration | `mvn test -pl cameleer3-server-app -Dtest=SecurityFilterIT -Dsurefire.reuseForks=false` | No -- Wave 0 | | SECU-01 | Protected endpoints reject requests without JWT; public endpoints accessible | integration | `mvn test -pl cameleer-server-app -Dtest=SecurityFilterIT -Dsurefire.reuseForks=false` | No -- Wave 0 |
| SECU-02 | Refresh endpoint issues new access JWT from valid refresh token | integration | `mvn test -pl cameleer3-server-app -Dtest=JwtRefreshIT -Dsurefire.reuseForks=false` | No -- Wave 0 | | SECU-02 | Refresh endpoint issues new access JWT from valid refresh token | integration | `mvn test -pl cameleer-server-app -Dtest=JwtRefreshIT -Dsurefire.reuseForks=false` | No -- Wave 0 |
| SECU-03 | Ed25519 keypair generated at startup; public key in registration response | integration | `mvn test -pl cameleer3-server-app -Dtest=RegistrationSecurityIT -Dsurefire.reuseForks=false` | No -- Wave 0 | | SECU-03 | Ed25519 keypair generated at startup; public key in registration response | integration | `mvn test -pl cameleer-server-app -Dtest=RegistrationSecurityIT -Dsurefire.reuseForks=false` | No -- Wave 0 |
| SECU-04 | SSE payloads carry valid Ed25519 signature | integration | `mvn test -pl cameleer3-server-app -Dtest=SseSigningIT -Dsurefire.reuseForks=false` | No -- Wave 0 | | SECU-04 | SSE payloads carry valid Ed25519 signature | integration | `mvn test -pl cameleer-server-app -Dtest=SseSigningIT -Dsurefire.reuseForks=false` | No -- Wave 0 |
| SECU-05 | Bootstrap token required for registration; rejects invalid/missing tokens | integration | `mvn test -pl cameleer3-server-app -Dtest=BootstrapTokenIT -Dsurefire.reuseForks=false` | No -- Wave 0 | | SECU-05 | Bootstrap token required for registration; rejects invalid/missing tokens | integration | `mvn test -pl cameleer-server-app -Dtest=BootstrapTokenIT -Dsurefire.reuseForks=false` | No -- Wave 0 |
| N/A | JWT creation, validation, expiry logic | unit | `mvn test -pl cameleer3-server-app -Dtest=JwtServiceTest -Dsurefire.reuseForks=false` | No -- Wave 0 | | N/A | JWT creation, validation, expiry logic | unit | `mvn test -pl cameleer-server-app -Dtest=JwtServiceTest -Dsurefire.reuseForks=false` | No -- Wave 0 |
| N/A | Ed25519 signing and verification roundtrip | unit | `mvn test -pl cameleer3-server-app -Dtest=Ed25519SigningServiceTest -Dsurefire.reuseForks=false` | No -- Wave 0 | | N/A | Ed25519 signing and verification roundtrip | unit | `mvn test -pl cameleer-server-app -Dtest=Ed25519SigningServiceTest -Dsurefire.reuseForks=false` | No -- Wave 0 |
### Sampling Rate ### Sampling Rate
- **Per task commit:** `mvn test -pl cameleer3-server-app -Dsurefire.reuseForks=false` - **Per task commit:** `mvn test -pl cameleer-server-app -Dsurefire.reuseForks=false`
- **Per wave merge:** `mvn clean verify` - **Per wave merge:** `mvn clean verify`
- **Phase gate:** Full suite green before `/gsd:verify-work` - **Phase gate:** Full suite green before `/gsd:verify-work`

View File

@@ -18,8 +18,8 @@ created: 2026-03-11
| Property | Value | | Property | Value |
|----------|-------| |----------|-------|
| **Framework** | JUnit 5 + Spring Boot Test + Spring Security Test | | **Framework** | JUnit 5 + Spring Boot Test + Spring Security Test |
| **Config file** | cameleer3-server-app/src/test/resources/application-test.yml | | **Config file** | cameleer-server-app/src/test/resources/application-test.yml |
| **Quick run command** | `mvn test -pl cameleer3-server-app -Dtest="Security*,Jwt*,Bootstrap*,Ed25519*" -Dsurefire.reuseForks=false` | | **Quick run command** | `mvn test -pl cameleer-server-app -Dtest="Security*,Jwt*,Bootstrap*,Ed25519*" -Dsurefire.reuseForks=false` |
| **Full suite command** | `mvn clean verify` | | **Full suite command** | `mvn clean verify` |
| **Estimated runtime** | ~60 seconds | | **Estimated runtime** | ~60 seconds |
@@ -27,7 +27,7 @@ created: 2026-03-11
## Sampling Rate ## Sampling Rate
- **After every task commit:** Run `mvn test -pl cameleer3-server-app -Dsurefire.reuseForks=false` - **After every task commit:** Run `mvn test -pl cameleer-server-app -Dsurefire.reuseForks=false`
- **After every plan wave:** Run `mvn clean verify` - **After every plan wave:** Run `mvn clean verify`
- **Before `/gsd:verify-work`:** Full suite must be green - **Before `/gsd:verify-work`:** Full suite must be green
- **Max feedback latency:** 60 seconds - **Max feedback latency:** 60 seconds
@@ -38,13 +38,13 @@ created: 2026-03-11
| Task ID | Plan | Wave | Requirement | Test Type | Automated Command | File Exists | Status | | Task ID | Plan | Wave | Requirement | Test Type | Automated Command | File Exists | Status |
|---------|------|------|-------------|-----------|-------------------|-------------|--------| |---------|------|------|-------------|-----------|-------------------|-------------|--------|
| 04-01-01 | 01 | 1 | SECU-03 | unit | `mvn test -pl cameleer3-server-app -Dtest=Ed25519SigningServiceTest -Dsurefire.reuseForks=false` | ❌ W0 | ⬜ pending | | 04-01-01 | 01 | 1 | SECU-03 | unit | `mvn test -pl cameleer-server-app -Dtest=Ed25519SigningServiceTest -Dsurefire.reuseForks=false` | ❌ W0 | ⬜ pending |
| 04-01-02 | 01 | 1 | SECU-01 | unit | `mvn test -pl cameleer3-server-app -Dtest=JwtServiceTest -Dsurefire.reuseForks=false` | ❌ W0 | ⬜ pending | | 04-01-02 | 01 | 1 | SECU-01 | unit | `mvn test -pl cameleer-server-app -Dtest=JwtServiceTest -Dsurefire.reuseForks=false` | ❌ W0 | ⬜ pending |
| 04-01-03 | 01 | 1 | SECU-05 | integration | `mvn test -pl cameleer3-server-app -Dtest=BootstrapTokenIT -Dsurefire.reuseForks=false` | ❌ W0 | ⬜ pending | | 04-01-03 | 01 | 1 | SECU-05 | integration | `mvn test -pl cameleer-server-app -Dtest=BootstrapTokenIT -Dsurefire.reuseForks=false` | ❌ W0 | ⬜ pending |
| 04-01-04 | 01 | 1 | SECU-01 | integration | `mvn test -pl cameleer3-server-app -Dtest=SecurityFilterIT -Dsurefire.reuseForks=false` | ❌ W0 | ⬜ pending | | 04-01-04 | 01 | 1 | SECU-01 | integration | `mvn test -pl cameleer-server-app -Dtest=SecurityFilterIT -Dsurefire.reuseForks=false` | ❌ W0 | ⬜ pending |
| 04-01-05 | 01 | 1 | SECU-02 | integration | `mvn test -pl cameleer3-server-app -Dtest=JwtRefreshIT -Dsurefire.reuseForks=false` | ❌ W0 | ⬜ pending | | 04-01-05 | 01 | 1 | SECU-02 | integration | `mvn test -pl cameleer-server-app -Dtest=JwtRefreshIT -Dsurefire.reuseForks=false` | ❌ W0 | ⬜ pending |
| 04-01-06 | 01 | 1 | SECU-04 | integration | `mvn test -pl cameleer3-server-app -Dtest=SseSigningIT -Dsurefire.reuseForks=false` | ❌ W0 | ⬜ pending | | 04-01-06 | 01 | 1 | SECU-04 | integration | `mvn test -pl cameleer-server-app -Dtest=SseSigningIT -Dsurefire.reuseForks=false` | ❌ W0 | ⬜ pending |
| 04-01-07 | 01 | 1 | N/A | integration | `mvn test -pl cameleer3-server-app -Dtest=RegistrationSecurityIT -Dsurefire.reuseForks=false` | ❌ W0 | ⬜ pending | | 04-01-07 | 01 | 1 | N/A | integration | `mvn test -pl cameleer-server-app -Dtest=RegistrationSecurityIT -Dsurefire.reuseForks=false` | ❌ W0 | ⬜ pending |
*Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky* *Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky*

View File

@@ -39,19 +39,19 @@ All truths drawn from PLAN frontmatter must_haves across plans 01, 02, and 03.
| Artifact | Provides | Status | Details | | Artifact | Provides | Status | Details |
|----------|----------|--------|---------| |----------|----------|--------|---------|
| `cameleer3-server-core/.../security/JwtService.java` | JWT interface: createAccessToken, createRefreshToken, validateAndExtractAgentId, validateRefreshToken | VERIFIED | 49 lines, substantive interface with 4 methods | | `cameleer-server-core/.../security/JwtService.java` | JWT interface: createAccessToken, createRefreshToken, validateAndExtractAgentId, validateRefreshToken | VERIFIED | 49 lines, substantive interface with 4 methods |
| `cameleer3-server-core/.../security/Ed25519SigningService.java` | Ed25519 interface: sign(payload), getPublicKeyBase64() | VERIFIED | 29 lines, substantive interface with 2 methods | | `cameleer-server-core/.../security/Ed25519SigningService.java` | Ed25519 interface: sign(payload), getPublicKeyBase64() | VERIFIED | 29 lines, substantive interface with 2 methods |
| `cameleer3-server-app/.../security/JwtServiceImpl.java` | Nimbus JOSE+JWT HMAC-SHA256 implementation | VERIFIED | 120 lines; uses `MACSigner`/`MACVerifier`, ephemeral 256-bit secret, correct claims | | `cameleer-server-app/.../security/JwtServiceImpl.java` | Nimbus JOSE+JWT HMAC-SHA256 implementation | VERIFIED | 120 lines; uses `MACSigner`/`MACVerifier`, ephemeral 256-bit secret, correct claims |
| `cameleer3-server-app/.../security/Ed25519SigningServiceImpl.java` | JDK 17 Ed25519 KeyPairGenerator implementation | VERIFIED | 54 lines; `KeyPairGenerator.getInstance("Ed25519")`, `Signature.getInstance("Ed25519")`, Base64-encoded output | | `cameleer-server-app/.../security/Ed25519SigningServiceImpl.java` | JDK 17 Ed25519 KeyPairGenerator implementation | VERIFIED | 54 lines; `KeyPairGenerator.getInstance("Ed25519")`, `Signature.getInstance("Ed25519")`, Base64-encoded output |
| `cameleer3-server-app/.../security/BootstrapTokenValidator.java` | Constant-time bootstrap token validation with dual-token rotation | VERIFIED | 50 lines; `MessageDigest.isEqual()`, checks current and previous token, null/blank guard | | `cameleer-server-app/.../security/BootstrapTokenValidator.java` | Constant-time bootstrap token validation with dual-token rotation | VERIFIED | 50 lines; `MessageDigest.isEqual()`, checks current and previous token, null/blank guard |
| `cameleer3-server-app/.../security/SecurityProperties.java` | Config binding with env var mapping | VERIFIED | 48 lines; `@ConfigurationProperties(prefix="security")`; all 4 fields with defaults | | `cameleer-server-app/.../security/SecurityProperties.java` | Config binding with env var mapping | VERIFIED | 48 lines; `@ConfigurationProperties(prefix="security")`; all 4 fields with defaults |
| `cameleer3-server-app/.../security/SecurityBeanConfig.java` | Bean wiring with fail-fast validation | VERIFIED | 43 lines; `@EnableConfigurationProperties`, all 3 service beans, `InitializingBean` check | | `cameleer-server-app/.../security/SecurityBeanConfig.java` | Bean wiring with fail-fast validation | VERIFIED | 43 lines; `@EnableConfigurationProperties`, all 3 service beans, `InitializingBean` check |
| `cameleer3-server-app/.../security/JwtAuthenticationFilter.java` | OncePerRequestFilter extracting JWT from header or query param | VERIFIED | 72 lines; extracts from `Authorization: Bearer` then `?token=` query param; sets `SecurityContextHolder` | | `cameleer-server-app/.../security/JwtAuthenticationFilter.java` | OncePerRequestFilter extracting JWT from header or query param | VERIFIED | 72 lines; extracts from `Authorization: Bearer` then `?token=` query param; sets `SecurityContextHolder` |
| `cameleer3-server-app/.../security/SecurityConfig.java` | SecurityFilterChain with permitAll for public paths, authenticated for rest | VERIFIED | 54 lines; stateless, CSRF disabled, correct permitAll list, `addFilterBefore` JwtAuthenticationFilter | | `cameleer-server-app/.../security/SecurityConfig.java` | SecurityFilterChain with permitAll for public paths, authenticated for rest | VERIFIED | 54 lines; stateless, CSRF disabled, correct permitAll list, `addFilterBefore` JwtAuthenticationFilter |
| `cameleer3-server-app/.../controller/AgentRegistrationController.java` | Updated register endpoint with bootstrap token validation, JWT issuance, public key; refresh endpoint | VERIFIED | 230 lines; both `/register` and `/{id}/refresh` endpoints fully wired | | `cameleer-server-app/.../controller/AgentRegistrationController.java` | Updated register endpoint with bootstrap token validation, JWT issuance, public key; refresh endpoint | VERIFIED | 230 lines; both `/register` and `/{id}/refresh` endpoints fully wired |
| `cameleer3-server-app/.../agent/SsePayloadSigner.java` | Component that signs SSE command payloads | VERIFIED | 77 lines; `@Component`, signs then adds field, defensive null/blank handling | | `cameleer-server-app/.../agent/SsePayloadSigner.java` | Component that signs SSE command payloads | VERIFIED | 77 lines; `@Component`, signs then adds field, defensive null/blank handling |
| `cameleer3-server-app/.../agent/SseConnectionManager.java` | Updated onCommandReady with signing before sendEvent | VERIFIED | `onCommandReady()` calls `ssePayloadSigner.signPayload()`, parses to `JsonNode` to avoid double-quoting | | `cameleer-server-app/.../agent/SseConnectionManager.java` | Updated onCommandReady with signing before sendEvent | VERIFIED | `onCommandReady()` calls `ssePayloadSigner.signPayload()`, parses to `JsonNode` to avoid double-quoting |
| `cameleer3-server-app/.../resources/application.yml` | Security config with env var mapping | VERIFIED | `security.bootstrap-token: ${CAMELEER_AUTH_TOKEN:}` and `security.bootstrap-token-previous: ${CAMELEER_AUTH_TOKEN_PREVIOUS:}` present | | `cameleer-server-app/.../resources/application.yml` | Security config with env var mapping | VERIFIED | `security.bootstrap-token: ${CAMELEER_AUTH_TOKEN:}` and `security.bootstrap-token-previous: ${CAMELEER_AUTH_TOKEN_PREVIOUS:}` present |
### Key Link Verification ### Key Link Verification

View File

@@ -57,7 +57,7 @@ Agents (50+) Users / UI
Agent POST /api/v1/ingest Agent POST /api/v1/ingest
| |
v v
[IngestController] -- validates JWT, deserializes using cameleer3-common models [IngestController] -- validates JWT, deserializes using cameleer-common models
| |
v v
[IngestionService.accept(batch)] -- accepts TransactionData/ActivityData [IngestionService.accept(batch)] -- accepts TransactionData/ActivityData
@@ -126,7 +126,7 @@ Each registered SseEmitter sends event to connected agent
Agent POST /api/v1/diagrams (on startup or route change) Agent POST /api/v1/diagrams (on startup or route change)
| |
v v
[DiagramController] -- receives route definition (XML/YAML/JSON from cameleer3-common) [DiagramController] -- receives route definition (XML/YAML/JSON from cameleer-common)
| |
v v
[DiagramService.storeVersion(definition)] [DiagramService.storeVersion(definition)]
@@ -341,11 +341,11 @@ public record PageCursor(Instant timestamp, String id) {}
## Module Boundary Design ## Module Boundary Design
### Core Module (`cameleer3-server-core`) ### Core Module (`cameleer-server-core`)
The core module is the domain layer. It contains: The core module is the domain layer. It contains:
- **Domain models** -- Transaction, Activity, Agent, DiagramVersion, etc. (may extend or complement cameleer3-common models) - **Domain models** -- Transaction, Activity, Agent, DiagramVersion, etc. (may extend or complement cameleer-common models)
- **Service interfaces and implementations** -- TransactionService, AgentRegistryService, DiagramService, QueryEngine - **Service interfaces and implementations** -- TransactionService, AgentRegistryService, DiagramService, QueryEngine
- **Repository interfaces** -- TransactionRepository, DiagramRepository, AgentRepository (interfaces only, no implementations) - **Repository interfaces** -- TransactionRepository, DiagramRepository, AgentRepository (interfaces only, no implementations)
- **Ingestion logic** -- WriteBuffer, batch assembly, backpressure signaling - **Ingestion logic** -- WriteBuffer, batch assembly, backpressure signaling
@@ -356,7 +356,7 @@ The core module is the domain layer. It contains:
**No Spring Boot dependencies.** Jackson is acceptable (already present). JUnit for tests. **No Spring Boot dependencies.** Jackson is acceptable (already present). JUnit for tests.
### App Module (`cameleer3-server-app`) ### App Module (`cameleer-server-app`)
The app module is the infrastructure/adapter layer. It contains: The app module is the infrastructure/adapter layer. It contains:
@@ -376,8 +376,8 @@ The app module is the infrastructure/adapter layer. It contains:
``` ```
app --> core (allowed) app --> core (allowed)
core --> app (NEVER) core --> app (NEVER)
core --> cameleer3-common (allowed) core --> cameleer-common (allowed)
app --> cameleer3-common (transitively via core) app --> cameleer-common (transitively via core)
``` ```
## Ingestion Pipeline Detail ## Ingestion Pipeline Detail
@@ -393,7 +393,7 @@ Use a two-stage approach:
- WriteBuffer has a bounded capacity (configurable, default 50,000 items). - WriteBuffer has a bounded capacity (configurable, default 50,000 items).
- When buffer is >80% full, respond with HTTP 429 + `Retry-After` header. - When buffer is >80% full, respond with HTTP 429 + `Retry-After` header.
- Agents (cameleer3) should implement exponential backoff on 429. - Agents (cameleer) should implement exponential backoff on 429.
- Monitor buffer fill level as a metric. - Monitor buffer fill level as a metric.
### Batch Size Tuning ### Batch Size Tuning

View File

@@ -1,6 +1,6 @@
# Domain Pitfalls # Domain Pitfalls
**Domain:** Transaction monitoring / observability server (Cameleer3 Server) **Domain:** Transaction monitoring / observability server (Cameleer Server)
**Researched:** 2026-03-11 **Researched:** 2026-03-11
**Confidence:** MEDIUM (based on established patterns for ClickHouse, SSE, high-volume ingestion; no web verification available) **Confidence:** MEDIUM (based on established patterns for ClickHouse, SSE, high-volume ingestion; no web verification available)

View File

@@ -1,6 +1,6 @@
# Technology Stack # Technology Stack
**Project:** Cameleer3 Server **Project:** Cameleer Server
**Researched:** 2026-03-11 **Researched:** 2026-03-11
**Overall confidence:** MEDIUM (no live source verification available; versions based on training data up to May 2025) **Overall confidence:** MEDIUM (no live source verification available; versions based on training data up to May 2025)

View File

@@ -1,4 +1,4 @@
# Research Summary: Cameleer3 Server # Research Summary: Cameleer Server
**Domain:** Transaction observability server for Apache Camel integrations **Domain:** Transaction observability server for Apache Camel integrations
**Researched:** 2026-03-11 **Researched:** 2026-03-11
@@ -6,7 +6,7 @@
## Executive Summary ## Executive Summary
Cameleer3 Server is a write-heavy, read-occasional observability system that receives millions of transaction records per day from distributed Apache Camel agents, stores them with 30-day retention, and provides structured + full-text search. The architecture closely parallels established observability platforms like Jaeger, Zipkin, and njams Server, with the key differentiator being Camel route diagram visualization tied to individual transactions. Cameleer Server is a write-heavy, read-occasional observability system that receives millions of transaction records per day from distributed Apache Camel agents, stores them with 30-day retention, and provides structured + full-text search. The architecture closely parallels established observability platforms like Jaeger, Zipkin, and njams Server, with the key differentiator being Camel route diagram visualization tied to individual transactions.
The recommended stack centers on **ClickHouse** as the primary data store. ClickHouse's columnar MergeTree engine provides the exact properties this project needs: massive batch insert throughput, excellent time-range query performance, native TTL-based retention, and 10-20x compression on structured observability data. This is a well-established pattern used by production observability platforms (SigNoz, Uptrace, PostHog all run on ClickHouse). The recommended stack centers on **ClickHouse** as the primary data store. ClickHouse's columnar MergeTree engine provides the exact properties this project needs: massive batch insert throughput, excellent time-range query performance, native TTL-based retention, and 10-20x compression on structured observability data. This is a well-established pattern used by production observability platforms (SigNoz, Uptrace, PostHog all run on ClickHouse).
@@ -93,9 +93,9 @@ Based on research, suggested phase structure:
## Gaps to Address ## Gaps to Address
- **ClickHouse Java client API:** The clickhouse-java library has undergone significant changes. Exact API, connection pooling, and Spring Boot integration patterns need phase-specific research - **ClickHouse Java client API:** The clickhouse-java library has undergone significant changes. Exact API, connection pooling, and Spring Boot integration patterns need phase-specific research
- **cameleer3-common PROTOCOL.md:** Must read the agent protocol definition before designing ClickHouse schema -- this defines the exact data structures being ingested - **cameleer-common PROTOCOL.md:** Must read the agent protocol definition before designing ClickHouse schema -- this defines the exact data structures being ingested
- **ClickHouse Docker setup:** Optimal ClickHouse Docker configuration (memory limits, merge settings) for development and production - **ClickHouse Docker setup:** Optimal ClickHouse Docker configuration (memory limits, merge settings) for development and production
- **Full-text search decision:** ClickHouse skip indexes may or may not meet the "search by any content" requirement. This needs prototyping with realistic data - **Full-text search decision:** ClickHouse skip indexes may or may not meet the "search by any content" requirement. This needs prototyping with realistic data
- **Diagram rendering library:** Server-side route diagram rendering is a significant unknown; needs prototyping with actual Camel route graph data from cameleer3-common - **Diagram rendering library:** Server-side route diagram rendering is a significant unknown; needs prototyping with actual Camel route graph data from cameleer-common
- **Frontend framework:** No research on UI technology -- deferred to UI phase - **Frontend framework:** No research on UI technology -- deferred to UI phase
- **Agent protocol stability:** The cameleer3-common protocol is still evolving. Schema evolution strategy needs alignment with agent development - **Agent protocol stability:** The cameleer-common protocol is still evolving. Schema evolution strategy needs alignment with agent development

101
AGENTS.md Normal file
View File

@@ -0,0 +1,101 @@
<!-- gitnexus:start -->
# GitNexus — Code Intelligence
This project is indexed by GitNexus as **cameleer-server** (6298 symbols, 15869 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.
## Always Do
- **MUST run impact analysis before editing any symbol.** Before modifying a function, class, or method, run `gitnexus_impact({target: "symbolName", direction: "upstream"})` and report the blast radius (direct callers, affected processes, risk level) to the user.
- **MUST run `gitnexus_detect_changes()` before committing** to verify your changes only affect expected symbols and execution flows.
- **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
- When exploring unfamiliar code, use `gitnexus_query({query: "concept"})` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance.
- When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use `gitnexus_context({name: "symbolName"})`.
## When Debugging
1. `gitnexus_query({query: "<error or symptom>"})` — find execution flows related to the issue
2. `gitnexus_context({name: "<suspect function>"})` — see all callers, callees, and process participation
3. `READ gitnexus://repo/cameleer-server/process/{processName}` — trace the full execution flow step by step
4. For regressions: `gitnexus_detect_changes({scope: "compare", base_ref: "main"})` — see what your branch changed
## When Refactoring
- **Renaming**: MUST use `gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})` first. Review the preview — graph edits are safe, text_search edits need manual review. Then run with `dry_run: false`.
- **Extracting/Splitting**: MUST run `gitnexus_context({name: "target"})` to see all incoming/outgoing refs, then `gitnexus_impact({target: "target", direction: "upstream"})` to find all external callers before moving code.
- After any refactor: run `gitnexus_detect_changes({scope: "all"})` to verify only expected files changed.
## Never Do
- NEVER edit a function, class, or method without first running `gitnexus_impact` on it.
- NEVER ignore HIGH or CRITICAL risk warnings from impact analysis.
- NEVER rename symbols with find-and-replace — use `gitnexus_rename` which understands the call graph.
- NEVER commit changes without running `gitnexus_detect_changes()` to check affected scope.
## Tools Quick Reference
| Tool | When to use | Command |
|------|-------------|---------|
| `query` | Find code by concept | `gitnexus_query({query: "auth validation"})` |
| `context` | 360-degree view of one symbol | `gitnexus_context({name: "validateUser"})` |
| `impact` | Blast radius before editing | `gitnexus_impact({target: "X", direction: "upstream"})` |
| `detect_changes` | Pre-commit scope check | `gitnexus_detect_changes({scope: "staged"})` |
| `rename` | Safe multi-file rename | `gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})` |
| `cypher` | Custom graph queries | `gitnexus_cypher({query: "MATCH ..."})` |
## Impact Risk Levels
| Depth | Meaning | Action |
|-------|---------|--------|
| d=1 | WILL BREAK — direct callers/importers | MUST update these |
| d=2 | LIKELY AFFECTED — indirect deps | Should test |
| d=3 | MAY NEED TESTING — transitive | Test if critical path |
## Resources
| Resource | Use for |
|----------|---------|
| `gitnexus://repo/cameleer-server/context` | Codebase overview, check index freshness |
| `gitnexus://repo/cameleer-server/clusters` | All functional areas |
| `gitnexus://repo/cameleer-server/processes` | All execution flows |
| `gitnexus://repo/cameleer-server/process/{name}` | Step-by-step execution trace |
## Self-Check Before Finishing
Before completing any code modification task, verify:
1. `gitnexus_impact` was run for all modified symbols
2. No HIGH/CRITICAL risk warnings were ignored
3. `gitnexus_detect_changes()` confirms changes match expected scope
4. All d=1 (WILL BREAK) dependents were updated
## Keeping the Index Fresh
After committing code changes, the GitNexus index becomes stale. Re-run analyze to update it:
```bash
npx gitnexus analyze
```
If the index previously included embeddings, preserve them by adding `--embeddings`:
```bash
npx gitnexus analyze --embeddings
```
To check whether embeddings exist, inspect `.gitnexus/meta.json` — the `stats.embeddings` field shows the count (0 means no embeddings). **Running analyze without `--embeddings` will delete any previously generated embeddings.**
> Claude Code users: A PostToolUse hook handles this automatically after `git commit` and `git merge`.
## CLI
| Task | Read this skill file |
|------|---------------------|
| Understand architecture / "How does X work?" | `.claude/skills/gitnexus/gitnexus-exploring/SKILL.md` |
| Blast radius / "What breaks if I change X?" | `.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md` |
| Trace bugs / "Why is X failing?" | `.claude/skills/gitnexus/gitnexus-debugging/SKILL.md` |
| Rename / extract / split / refactor | `.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md` |
| Tools, resources, schema reference | `.claude/skills/gitnexus/gitnexus-guide/SKILL.md` |
| Index, status, clean, wiki CLI commands | `.claude/skills/gitnexus/gitnexus-cli/SKILL.md` |
<!-- gitnexus:end -->

View File

@@ -4,18 +4,18 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Project ## Project
Cameleer3 Server — observability server that receives, stores, and serves Camel route execution data and route diagrams from Cameleer3 agents. Pushes config and commands to agents via SSE. Also orchestrates Docker container deployments when running under cameleer-saas. Cameleer Server — observability server that receives, stores, and serves Camel route execution data and route diagrams from Cameleer agents. Pushes config and commands to agents via SSE. Also orchestrates Docker container deployments when running under cameleer-saas.
## Related Project ## Related Project
- **cameleer3** (`https://gitea.siegeln.net/cameleer/cameleer3`) — the Java agent that instruments Camel applications - **cameleer** (`https://gitea.siegeln.net/cameleer/cameleer`) — the Java agent that instruments Camel applications
- Protocol defined in `cameleer3-common/PROTOCOL.md` in the agent repo - Protocol defined in `cameleer-common/PROTOCOL.md` in the agent repo
- This server depends on `com.cameleer3:cameleer3-common` (shared models and graph API) - This server depends on `com.cameleer:cameleer-common` (shared models and graph API)
## Modules ## Modules
- `cameleer3-server-core` — domain logic, storage interfaces, services (no Spring dependencies) - `cameleer-server-core` — domain logic, storage interfaces, services (no Spring dependencies)
- `cameleer3-server-app` — Spring Boot web app, REST controllers, SSE, persistence, Docker orchestration - `cameleer-server-app` — Spring Boot web app, REST controllers, SSE, persistence, Docker orchestration
## Build Commands ## Build Commands
@@ -27,12 +27,12 @@ mvn clean verify # Full build with tests
## Run ## Run
```bash ```bash
java -jar cameleer3-server-app/target/cameleer3-server-app-1.0-SNAPSHOT.jar java -jar cameleer-server-app/target/cameleer-server-app-1.0-SNAPSHOT.jar
``` ```
## Key Classes by Package ## Key Classes by Package
### Core Module (`cameleer3-server-core/src/main/java/com/cameleer3/server/core/`) ### Core Module (`cameleer-server-core/src/main/java/com/cameleer/server/core/`)
**agent/** — Agent lifecycle and commands **agent/** — Agent lifecycle and commands
- `AgentRegistryService` — in-memory registry (ConcurrentHashMap), register/heartbeat/lifecycle - `AgentRegistryService` — in-memory registry (ConcurrentHashMap), register/heartbeat/lifecycle
@@ -81,7 +81,7 @@ java -jar cameleer3-server-app/target/cameleer3-server-app-1.0-SNAPSHOT.jar
- `IngestionService` — ingestExecution, ingestMetric, ingestLog, ingestDiagram - `IngestionService` — ingestExecution, ingestMetric, ingestLog, ingestDiagram
- `ChunkAccumulator` — batches data for efficient flush - `ChunkAccumulator` — batches data for efficient flush
### App Module (`cameleer3-server-app/src/main/java/com/cameleer3/server/app/`) ### App Module (`cameleer-server-app/src/main/java/com/cameleer/server/app/`)
**controller/** — REST endpoints **controller/** — REST endpoints
- `AgentRegistrationController` — POST /register, POST /heartbeat, GET / (list), POST /refresh-token - `AgentRegistrationController` — POST /register, POST /heartbeat, GET / (list), POST /refresh-token
@@ -160,7 +160,7 @@ java -jar cameleer3-server-app/target/cameleer3-server-app-1.0-SNAPSHOT.jar
- Java 17+ required - Java 17+ required
- Spring Boot 3.4.3 parent POM - Spring Boot 3.4.3 parent POM
- Depends on `com.cameleer3:cameleer3-common` from Gitea Maven registry - Depends on `com.cameleer:cameleer-common` from Gitea Maven registry
- Jackson `JavaTimeModule` for `Instant` deserialization - Jackson `JavaTimeModule` for `Instant` deserialization
- Communication: receives HTTP POST data from agents (executions, diagrams, metrics, logs), serves SSE event streams for config push/commands (config-update, deep-trace, replay, route-control) - Communication: receives HTTP POST data from agents (executions, diagrams, metrics, logs), serves SSE event streams for config push/commands (config-update, deep-trace, replay, route-control)
- Environment filtering: all data queries (exchanges, dashboard stats, route metrics, agent events, correlation) filter by the selected environment. All commands (config-update, route-control, set-traced-processors, replay) target only agents in the selected environment when one is selected. `AgentRegistryService.findByApplicationAndEnvironment()` for environment-scoped command dispatch. Backend endpoints accept optional `environment` query parameter; null = all environments (backward compatible). - Environment filtering: all data queries (exchanges, dashboard stats, route metrics, agent events, correlation) filter by the selected environment. All commands (config-update, route-control, set-traced-processors, replay) target only agents in the selected environment when one is selected. `AgentRegistryService.findByApplicationAndEnvironment()` for environment-scoped command dispatch. Backend endpoints accept optional `environment` query parameter; null = all environments (backward compatible).
@@ -177,7 +177,7 @@ java -jar cameleer3-server-app/target/cameleer3-server-app-1.0-SNAPSHOT.jar
## Database Migrations ## Database Migrations
PostgreSQL (Flyway): `cameleer3-server-app/src/main/resources/db/migration/` PostgreSQL (Flyway): `cameleer-server-app/src/main/resources/db/migration/`
- V1 — RBAC (users, roles, groups, audit_log) - V1 — RBAC (users, roles, groups, audit_log)
- V2 — Claim mappings (OIDC) - V2 — Claim mappings (OIDC)
- V3 — Runtime management (apps, environments, deployments, app_versions) - V3 — Runtime management (apps, environments, deployments, app_versions)
@@ -189,15 +189,15 @@ PostgreSQL (Flyway): `cameleer3-server-app/src/main/resources/db/migration/`
- V9 — Password hardening (failed_login_attempts, locked_until, token_revoked_before on users) - V9 — Password hardening (failed_login_attempts, locked_until, token_revoked_before on users)
- V10 — Runtime type detection (detected_runtime_type, detected_main_class on app_versions) - V10 — Runtime type detection (detected_runtime_type, detected_main_class on app_versions)
ClickHouse: `cameleer3-server-app/src/main/resources/clickhouse/init.sql` (run idempotently on startup) ClickHouse: `cameleer-server-app/src/main/resources/clickhouse/init.sql` (run idempotently on startup)
## CI/CD & Deployment ## CI/CD & Deployment
- CI workflow: `.gitea/workflows/ci.yml` — build -> docker -> deploy on push to main or feature branches - CI workflow: `.gitea/workflows/ci.yml` — build -> docker -> deploy on push to main or feature branches
- Build step skips integration tests (`-DskipITs`) — Testcontainers needs Docker daemon - Build step skips integration tests (`-DskipITs`) — Testcontainers needs Docker daemon
- Docker: multi-stage build (`Dockerfile`), `$BUILDPLATFORM` for native Maven on ARM64 runner, amd64 runtime. `docker-entrypoint.sh` imports `/certs/ca.pem` into JVM truststore before starting the app (supports custom CAs for OIDC discovery without `CAMELEER_SERVER_SECURITY_OIDCTLSSKIPVERIFY`). - Docker: multi-stage build (`Dockerfile`), `$BUILDPLATFORM` for native Maven on ARM64 runner, amd64 runtime. `docker-entrypoint.sh` imports `/certs/ca.pem` into JVM truststore before starting the app (supports custom CAs for OIDC discovery without `CAMELEER_SERVER_SECURITY_OIDCTLSSKIPVERIFY`).
- `REGISTRY_TOKEN` build arg required for `cameleer3-common` dependency resolution - `REGISTRY_TOKEN` build arg required for `cameleer-common` dependency resolution
- Registry: `gitea.siegeln.net/cameleer/cameleer3-server` (container images) - Registry: `gitea.siegeln.net/cameleer/cameleer-server` (container images)
- K8s manifests in `deploy/` — Kustomize base + overlays (main/feature), shared infra (PostgreSQL, ClickHouse, Logto) as top-level manifests - K8s manifests in `deploy/` — Kustomize base + overlays (main/feature), shared infra (PostgreSQL, ClickHouse, Logto) as top-level manifests
- Deployment target: k3s at 192.168.50.86, namespace `cameleer` (main), `cam-<slug>` (feature branches) - Deployment target: k3s at 192.168.50.86, namespace `cameleer` (main), `cam-<slug>` (feature branches)
- Feature branches: isolated namespace, PG schema; Traefik Ingress at `<slug>-api.cameleer.siegeln.net` - Feature branches: isolated namespace, PG schema; Traefik Ingress at `<slug>-api.cameleer.siegeln.net`
@@ -243,7 +243,7 @@ The UI has 4 main tabs: **Exchanges**, **Dashboard**, **Runtime**, **Deployments
- Shared `PageLoader` component replaces copy-pasted spinner patterns. - Shared `PageLoader` component replaces copy-pasted spinner patterns.
- Design system components used consistently: `Select`, `Tabs`, `Toggle`, `Button`, `LogViewer`, `Label` — prefer DS components over raw HTML elements. `LogViewer` renders optional source badges (`container`, `app`, `agent`) via `LogEntry.source` field (DS v0.1.49+). - Design system components used consistently: `Select`, `Tabs`, `Toggle`, `Button`, `LogViewer`, `Label` — prefer DS components over raw HTML elements. `LogViewer` renders optional source badges (`container`, `app`, `agent`) via `LogEntry.source` field (DS v0.1.49+).
- Environment slugs are auto-computed from display name (read-only in UI). - Environment slugs are auto-computed from display name (read-only in UI).
- Brand assets: `@cameleer/design-system/assets/` provides `camel-logo.svg` (currentColor), `cameleer3-{16,32,48,192,512}.png`, and `cameleer3-logo.png`. Copied to `ui/public/` for use as favicon (`favicon-16.png`, `favicon-32.png`) and logo (`camel-logo.svg` — login dialog 36px, sidebar 28x24px). - Brand assets: `@cameleer/design-system/assets/` provides `camel-logo.svg` (currentColor), `cameleer-{16,32,48,192,512}.png`, and `cameleer-logo.png`. Copied to `ui/public/` for use as favicon (`favicon-16.png`, `favicon-32.png`) and logo (`camel-logo.svg` — login dialog 36px, sidebar 28x24px).
- Sidebar generates `/exchanges/` paths directly (no legacy `/apps/` redirects). basePath is centralized in `ui/src/config.ts`; router.tsx imports it instead of re-reading `<base>` tag. - Sidebar generates `/exchanges/` paths directly (no legacy `/apps/` redirects). basePath is centralized in `ui/src/config.ts`; router.tsx imports it instead of re-reading `<base>` tag.
- Global user preferences (environment selection) use Zustand stores with localStorage persistence — never URL search params. URL params are for page-specific state only (e.g. `?text=` search query). Switching environment resets all filters and remounts pages. - Global user preferences (environment selection) use Zustand stores with localStorage persistence — never URL search params. URL params are for page-specific state only (e.g. `?text=` search query). Switching environment resets all filters and remounts pages.
@@ -255,9 +255,9 @@ When deployed via the cameleer-saas platform, this server orchestrates customer
- **TraefikLabelBuilder** (`app/runtime/TraefikLabelBuilder.java`) — generates Traefik Docker labels for path-based (`/{envSlug}/{appSlug}/`) or subdomain-based (`{appSlug}-{envSlug}.{domain}`) routing. Supports strip-prefix and SSL offloading toggles. Also sets per-replica identity labels: `cameleer.replica` (index) and `cameleer.instance-id` (`{envSlug}-{appSlug}-{replicaIndex}`). Internal processing uses labels (not container name parsing) for extensibility. - **TraefikLabelBuilder** (`app/runtime/TraefikLabelBuilder.java`) — generates Traefik Docker labels for path-based (`/{envSlug}/{appSlug}/`) or subdomain-based (`{appSlug}-{envSlug}.{domain}`) routing. Supports strip-prefix and SSL offloading toggles. Also sets per-replica identity labels: `cameleer.replica` (index) and `cameleer.instance-id` (`{envSlug}-{appSlug}-{replicaIndex}`). Internal processing uses labels (not container name parsing) for extensibility.
- **PrometheusLabelBuilder** (`app/runtime/PrometheusLabelBuilder.java`) — generates Prometheus `docker_sd_configs` labels per resolved runtime type: Spring Boot `/actuator/prometheus:8081`, Quarkus/native `/q/metrics:9000`, plain Java `/metrics:9464`. Labels merged into container metadata alongside Traefik labels at deploy time. - **PrometheusLabelBuilder** (`app/runtime/PrometheusLabelBuilder.java`) — generates Prometheus `docker_sd_configs` labels per resolved runtime type: Spring Boot `/actuator/prometheus:8081`, Quarkus/native `/q/metrics:9000`, plain Java `/metrics:9464`. Labels merged into container metadata alongside Traefik labels at deploy time.
- **DockerNetworkManager** (`app/runtime/DockerNetworkManager.java`) — manages two Docker network tiers: - **DockerNetworkManager** (`app/runtime/DockerNetworkManager.java`) — manages two Docker network tiers:
- `cameleer-traefik` — shared network; Traefik, server, and all app containers attach here. Server joined via docker-compose with `cameleer3-server` DNS alias. - `cameleer-traefik` — shared network; Traefik, server, and all app containers attach here. Server joined via docker-compose with `cameleer-server` DNS alias.
- `cameleer-env-{slug}` — per-environment isolated network; containers in the same environment discover each other via Docker DNS. In SaaS mode, env networks are tenant-scoped: `cameleer-env-{tenantId}-{envSlug}` (overloaded `envNetworkName(tenantId, envSlug)` method) to prevent cross-tenant collisions when multiple tenants have identically-named environments. - `cameleer-env-{slug}` — per-environment isolated network; containers in the same environment discover each other via Docker DNS. In SaaS mode, env networks are tenant-scoped: `cameleer-env-{tenantId}-{envSlug}` (overloaded `envNetworkName(tenantId, envSlug)` method) to prevent cross-tenant collisions when multiple tenants have identically-named environments.
- **DockerEventMonitor** (`app/runtime/DockerEventMonitor.java`) — persistent Docker event stream listener for containers with `managed-by=cameleer3-server` label. Detects die/oom/start/stop events and updates deployment replica states. Periodic reconciliation (@Scheduled every 30s) inspects actual container state and corrects deployment status mismatches (fixes stale DEGRADED with all replicas healthy). - **DockerEventMonitor** (`app/runtime/DockerEventMonitor.java`) — persistent Docker event stream listener for containers with `managed-by=cameleer-server` label. Detects die/oom/start/stop events and updates deployment replica states. Periodic reconciliation (@Scheduled every 30s) inspects actual container state and corrects deployment status mismatches (fixes stale DEGRADED with all replicas healthy).
- **DeploymentProgress** (`ui/src/components/DeploymentProgress.tsx`) — UI step indicator showing 7 deploy stages with amber active/green completed styling. - **DeploymentProgress** (`ui/src/components/DeploymentProgress.tsx`) — UI step indicator showing 7 deploy stages with amber active/green completed styling.
- **ContainerLogForwarder** (`app/runtime/ContainerLogForwarder.java`) — streams Docker container stdout/stderr to ClickHouse `logs` table with `source='container'`. Uses `docker logs --follow` per container, batches lines every 2s or 50 lines. Parses Docker timestamp prefix, infers log level via regex. `DeploymentExecutor` starts capture after each replica launches with the replica's `instanceId` (`{envSlug}-{appSlug}-{replicaIndex}`); `DockerEventMonitor` stops capture on die/oom. 60-second max capture timeout with 30s cleanup scheduler. Thread pool of 10 daemon threads. Container logs use the same `instanceId` as the agent (set via `CAMELEER_AGENT_INSTANCEID` env var) for unified log correlation at the instance level. - **ContainerLogForwarder** (`app/runtime/ContainerLogForwarder.java`) — streams Docker container stdout/stderr to ClickHouse `logs` table with `source='container'`. Uses `docker logs --follow` per container, batches lines every 2s or 50 lines. Parses Docker timestamp prefix, infers log level via regex. `DeploymentExecutor` starts capture after each replica launches with the replica's `instanceId` (`{envSlug}-{appSlug}-{replicaIndex}`); `DockerEventMonitor` stops capture on die/oom. 60-second max capture timeout with 30s cleanup scheduler. Thread pool of 10 daemon threads. Container logs use the same `instanceId` as the agent (set via `CAMELEER_AGENT_INSTANCEID` env var) for unified log correlation at the instance level.
- **StartupLogPanel** (`ui/src/components/StartupLogPanel.tsx`) — collapsible log panel rendered below `DeploymentProgress`. Queries `/api/v1/logs?source=container&application={appSlug}&environment={envSlug}`. Auto-polls every 3s while deployment is STARTING; shows green "live" badge during polling, red "stopped" badge on FAILED. Uses `useStartupLogs` hook and `LogViewer` (design system). - **StartupLogPanel** (`ui/src/components/StartupLogPanel.tsx`) — collapsible log panel rendered below `DeploymentProgress`. Queries `/api/v1/logs?source=container&application={appSlug}&environment={envSlug}`. Auto-polls every 3s while deployment is STARTING; shows green "live" badge during polling, red "stopped" badge on FAILED. Uses `useStartupLogs` hook and `LogViewer` (design system).
@@ -397,7 +397,7 @@ Mean processing time = `camel.route.policy.total_time / camel.route.policy.count
<!-- gitnexus:start --> <!-- gitnexus:start -->
# GitNexus — Code Intelligence # GitNexus — Code Intelligence
This project is indexed by GitNexus as **cameleer3-server** (6300 symbols, 15883 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. This project is indexed by GitNexus as **cameleer-server** (6298 symbols, 15869 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first. > If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.
@@ -413,7 +413,7 @@ This project is indexed by GitNexus as **cameleer3-server** (6300 symbols, 15883
1. `gitnexus_query({query: "<error or symptom>"})` — find execution flows related to the issue 1. `gitnexus_query({query: "<error or symptom>"})` — find execution flows related to the issue
2. `gitnexus_context({name: "<suspect function>"})` — see all callers, callees, and process participation 2. `gitnexus_context({name: "<suspect function>"})` — see all callers, callees, and process participation
3. `READ gitnexus://repo/cameleer3-server/process/{processName}` — trace the full execution flow step by step 3. `READ gitnexus://repo/cameleer-server/process/{processName}` — trace the full execution flow step by step
4. For regressions: `gitnexus_detect_changes({scope: "compare", base_ref: "main"})` — see what your branch changed 4. For regressions: `gitnexus_detect_changes({scope: "compare", base_ref: "main"})` — see what your branch changed
## When Refactoring ## When Refactoring
@@ -452,10 +452,10 @@ This project is indexed by GitNexus as **cameleer3-server** (6300 symbols, 15883
| Resource | Use for | | Resource | Use for |
|----------|---------| |----------|---------|
| `gitnexus://repo/cameleer3-server/context` | Codebase overview, check index freshness | | `gitnexus://repo/cameleer-server/context` | Codebase overview, check index freshness |
| `gitnexus://repo/cameleer3-server/clusters` | All functional areas | | `gitnexus://repo/cameleer-server/clusters` | All functional areas |
| `gitnexus://repo/cameleer3-server/processes` | All execution flows | | `gitnexus://repo/cameleer-server/processes` | All execution flows |
| `gitnexus://repo/cameleer3-server/process/{name}` | Step-by-step execution trace | | `gitnexus://repo/cameleer-server/process/{name}` | Step-by-step execution trace |
## Self-Check Before Finishing ## Self-Check Before Finishing

View File

@@ -1,14 +1,14 @@
FROM --platform=$BUILDPLATFORM maven:3.9-eclipse-temurin-17 AS build FROM --platform=$BUILDPLATFORM maven:3.9-eclipse-temurin-17 AS build
WORKDIR /build WORKDIR /build
# Configure Gitea Maven Registry for cameleer3-common dependency # Configure Gitea Maven Registry for cameleer-common dependency
ARG REGISTRY_TOKEN ARG REGISTRY_TOKEN
RUN mkdir -p ~/.m2 && \ RUN mkdir -p ~/.m2 && \
echo '<settings><servers><server><id>gitea</id><username>cameleer</username><password>'${REGISTRY_TOKEN}'</password></server></servers></settings>' > ~/.m2/settings.xml echo '<settings><servers><server><id>gitea</id><username>cameleer</username><password>'${REGISTRY_TOKEN}'</password></server></servers></settings>' > ~/.m2/settings.xml
COPY pom.xml . COPY pom.xml .
COPY cameleer3-server-core/pom.xml cameleer3-server-core/ COPY cameleer-server-core/pom.xml cameleer-server-core/
COPY cameleer3-server-app/pom.xml cameleer3-server-app/ COPY cameleer-server-app/pom.xml cameleer-server-app/
# Cache deps — only re-downloaded when POMs change # Cache deps — only re-downloaded when POMs change
RUN mvn dependency:go-offline -B || true RUN mvn dependency:go-offline -B || true
COPY . . COPY . .
@@ -16,7 +16,7 @@ RUN mvn clean package -DskipTests -U -B
FROM eclipse-temurin:17-jre FROM eclipse-temurin:17-jre
WORKDIR /app WORKDIR /app
COPY --from=build /build/cameleer3-server-app/target/cameleer3-server-app-*.jar /app/server.jar COPY --from=build /build/cameleer-server-app/target/cameleer-server-app-*.jar /app/server.jar
COPY docker-entrypoint.sh /app/ COPY docker-entrypoint.sh /app/
RUN chmod +x /app/docker-entrypoint.sh RUN chmod +x /app/docker-entrypoint.sh

View File

@@ -1,4 +1,4 @@
# HOWTO — Cameleer3 Server # HOWTO — Cameleer Server
## Prerequisites ## Prerequisites
@@ -6,7 +6,7 @@
- Maven 3.9+ - Maven 3.9+
- Node.js 22+ and npm - Node.js 22+ and npm
- Docker & Docker Compose - Docker & Docker Compose
- Access to the Gitea Maven registry (for `cameleer3-common` dependency) - Access to the Gitea Maven registry (for `cameleer-common` dependency)
## Build ## Build
@@ -33,17 +33,17 @@ This starts PostgreSQL 16. The database schema is applied automatically via Flyw
|------------|------|----------------------| |------------|------|----------------------|
| PostgreSQL | 5432 | JDBC (Spring JDBC) | | PostgreSQL | 5432 | JDBC (Spring JDBC) |
PostgreSQL credentials: `cameleer` / `cameleer_dev`, database `cameleer3`. PostgreSQL credentials: `cameleer` / `cameleer_dev`, database `cameleer`.
## Run the Server ## Run the Server
```bash ```bash
mvn clean package -DskipTests mvn clean package -DskipTests
SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/cameleer3 \ SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/cameleer \
SPRING_DATASOURCE_USERNAME=cameleer \ SPRING_DATASOURCE_USERNAME=cameleer \
SPRING_DATASOURCE_PASSWORD=cameleer_dev \ SPRING_DATASOURCE_PASSWORD=cameleer_dev \
CAMELEER_SERVER_SECURITY_BOOTSTRAPTOKEN=my-secret-token \ CAMELEER_SERVER_SECURITY_BOOTSTRAPTOKEN=my-secret-token \
java -jar cameleer3-server-app/target/cameleer3-server-app-1.0-SNAPSHOT.jar java -jar cameleer-server-app/target/cameleer-server-app-1.0-SNAPSHOT.jar
``` ```
> **Note:** The Docker image no longer includes default database credentials. When running via `docker run`, pass `-e SPRING_DATASOURCE_URL=...` etc. The docker-compose setup provides these automatically. > **Note:** The Docker image no longer includes default database credentials. When running via `docker run`, pass `-e SPRING_DATASOURCE_URL=...` etc. The docker-compose setup provides these automatically.
@@ -378,7 +378,7 @@ When the write buffer is full (default capacity: 50,000), ingestion endpoints re
## Configuration ## Configuration
Key settings in `cameleer3-server-app/src/main/resources/application.yml`. All custom properties live under `cameleer.server.*`. Env vars are a mechanical 1:1 mapping (dots to underscores, uppercase). Key settings in `cameleer-server-app/src/main/resources/application.yml`. All custom properties live under `cameleer.server.*`. Env vars are a mechanical 1:1 mapping (dots to underscores, uppercase).
**Security** (`cameleer.server.security.*`): **Security** (`cameleer.server.security.*`):
@@ -487,10 +487,10 @@ Integration tests use Testcontainers (starts PostgreSQL automatically — requir
mvn verify mvn verify
# Unit tests only (no Docker needed) # Unit tests only (no Docker needed)
mvn test -pl cameleer3-server-core mvn test -pl cameleer-server-core
# Specific integration test # Specific integration test
mvn test -pl cameleer3-server-app -Dtest=ExecutionControllerIT mvn test -pl cameleer-server-app -Dtest=ExecutionControllerIT
``` ```
## Verify Database Data ## Verify Database Data
@@ -498,7 +498,7 @@ mvn test -pl cameleer3-server-app -Dtest=ExecutionControllerIT
After posting data and waiting for the flush interval (1s default): After posting data and waiting for the flush interval (1s default):
```bash ```bash
docker exec -it cameleer3-server-postgres-1 psql -U cameleer -d cameleer3 \ docker exec -it cameleer-server-postgres-1 psql -U cameleer -d cameleer \
-c "SELECT count(*) FROM route_executions" -c "SELECT count(*) FROM route_executions"
``` ```
@@ -512,8 +512,8 @@ The full stack is deployed to k3s via CI/CD on push to `main`. K8s manifests are
cameleer namespace: cameleer namespace:
PostgreSQL (StatefulSet, 10Gi PVC) ← cameleer-postgres:5432 (ClusterIP) PostgreSQL (StatefulSet, 10Gi PVC) ← cameleer-postgres:5432 (ClusterIP)
ClickHouse (StatefulSet, 10Gi PVC) ← cameleer-clickhouse:8123 (ClusterIP) ClickHouse (StatefulSet, 10Gi PVC) ← cameleer-clickhouse:8123 (ClusterIP)
cameleer3-server (Deployment) ← NodePort 30081 cameleer-server (Deployment) ← NodePort 30081
cameleer3-ui (Deployment, Nginx) ← NodePort 30090 cameleer-ui (Deployment, Nginx) ← NodePort 30090
cameleer-deploy-demo (Deployment) ← NodePort 30092 cameleer-deploy-demo (Deployment) ← NodePort 30092
Logto Server (Deployment) ← NodePort 30951/30952 Logto Server (Deployment) ← NodePort 30951/30952
Logto PostgreSQL (StatefulSet, 1Gi) ← ClusterIP Logto PostgreSQL (StatefulSet, 1Gi) ← ClusterIP
@@ -546,7 +546,7 @@ Required Gitea org secrets: `REGISTRY_TOKEN`, `KUBECONFIG_BASE64`, `CAMELEER_SER
kubectl -n cameleer get pods kubectl -n cameleer get pods
# View server logs # View server logs
kubectl -n cameleer logs -f deploy/cameleer3-server kubectl -n cameleer logs -f deploy/cameleer-server
# View PostgreSQL logs # View PostgreSQL logs
kubectl -n cameleer logs -f statefulset/cameleer-postgres kubectl -n cameleer logs -f statefulset/cameleer-postgres
@@ -555,5 +555,5 @@ kubectl -n cameleer logs -f statefulset/cameleer-postgres
kubectl -n cameleer logs -f statefulset/cameleer-clickhouse kubectl -n cameleer logs -f statefulset/cameleer-clickhouse
# Restart server # Restart server
kubectl -n cameleer rollout restart deployment/cameleer3-server kubectl -n cameleer rollout restart deployment/cameleer-server
``` ```

View File

@@ -1,6 +1,6 @@
> **Status: RESOLVED** — All phases (1-5) executed on 2026-04-09. Remaining: responsive design (separate initiative). > **Status: RESOLVED** — All phases (1-5) executed on 2026-04-09. Remaining: responsive design (separate initiative).
# UI Consistency Audit — cameleer3-server # UI Consistency Audit — cameleer-server
**Date:** 2026-04-09 **Date:** 2026-04-09
**Scope:** All files under `ui/src/` (26 CSS modules, ~45 TSX components, ~15 pages) **Scope:** All files under `ui/src/` (26 CSS modules, ~45 TSX components, ~15 pages)

View File

@@ -1,4 +1,4 @@
# UI/UX Evaluation Report — Cameleer3 Server # UI/UX Evaluation Report — Cameleer Server
**Date:** 2026-03-25 **Date:** 2026-03-25
**Evaluated URL:** http://192.168.50.86:30090/ **Evaluated URL:** http://192.168.50.86:30090/
@@ -8,7 +8,7 @@
## Executive Summary ## Executive Summary
The Cameleer3 dashboard has a **distinctive, well-crafted warm amber design language** that stands out in the observability space. The core monitoring pages (Dashboard, Exchange Detail, Routes, Agents) are polished and consistent. The design system provides a solid foundation. The Cameleer dashboard has a **distinctive, well-crafted warm amber design language** that stands out in the observability space. The core monitoring pages (Dashboard, Exchange Detail, Routes, Agents) are polished and consistent. The design system provides a solid foundation.
**Key strengths:** KPI strip pattern, command palette (Ctrl+K), agent card grouping, dark mode token system, cohesive brand identity. **Key strengths:** KPI strip pattern, command palette (Ctrl+K), agent card grouping, dark mode token system, cohesive brand identity.

BIN
audit/01-exchanges-list.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

BIN
audit/07-dashboard-full.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

BIN
audit/07-dashboard-tab.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

BIN
audit/09-runtime-full.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

BIN
audit/09-runtime-tab.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
audit/13-groups-tab.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

BIN
audit/14-roles-tab.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
audit/15-audit-log.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

BIN
audit/16-clickhouse.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

BIN
audit/17-database.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

BIN
audit/18-environments.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

BIN
audit/19-oidc-full.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

BIN
audit/19-oidc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Some files were not shown because too many files have changed in this diff Show More