From 4279f817b6ba682397f33519812020ce7c4e8de3 Mon Sep 17 00:00:00 2001 From: kl3inIT Date: Thu, 30 Jul 2026 23:28:22 +0700 Subject: [PATCH] refactor(observability): give the payload boundary its own module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `integrations/graph-rag-observability` opened with `api(project(":components:graph-rag-core"))`, so taking the payload boundary meant taking the graph domain. `apps/mcp` did not take it: its build file had no `project(...)` dependency at all — the only Gradle application here with none — while carrying the OpenTelemetry starter and exporting spans. So the guarantee decision 0018 states across the process held in two deployables and in none of the third's spans. Seven of that module's ten files had nothing to do with GraphRAG, two of them added the same day by the increment that found this. The boundary moves to `integrations/observability`, which depends on no OrgMemory module. That absence is load-bearing: it is what keeps the boundary adoptable by a deployable with no domain dependency, which is the condition that failed. `graph-rag-observability` keeps what implements a core port — the two event sinks and the task decorator — and depends on the new module only in its test source set, where the whole-export allowlist gate drives a GraphRAG span through the sanitizer in the position it occupies in production. `orgmemory.spring-boot-app-conventions` adds the new module, so taking the convention is taking the boundary. `apps/mcp` gains it without its build file changing, and a fourth application cannot omit it by not knowing to ask. No behaviour changed and no test was rewritten: the four boundary test classes moved unedited, and 32 + 28 is the 60 the single module ran before. `ARCHITECTURE.md` and decision 0019 both record that this module is a deliberate exception to `integrations -> core ports` — it implements no core port — so the exception is one that was seen and chosen rather than one that was never noticed. Gates: :integrations:observability:test (32), :integrations:graph-rag-observability:test (28), :apps:api:test (152), :apps:worker:test (47), :apps:mcp:test (55). 314 tests, 0 skipped, 0 failures. Co-Authored-By: Claude Opus 5 (1M context) --- ARCHITECTURE.md | 12 ++- .../ObservationContentBoundaryTests.java | 2 +- .../ObservationContentBoundaryTests.java | 2 +- ...ory.spring-boot-app-conventions.gradle.kts | 8 ++ ...-the-payload-boundary-is-its-own-module.md | 81 +++++++++++++++++++ .../2026-07-29-observability-pipeline/plan.md | 10 ++- .../2026-07-30-observability-platform/plan.md | 13 +-- docs/specs/domains/secure-graph-rag.md | 12 ++- docs/tests/domains/secure-graph-rag.md | 5 ++ .../graph-rag-observability/build.gradle.kts | 15 ++-- ...ot.autoconfigure.AutoConfiguration.imports | 3 - .../WholeExportAllowlistTests.java | 1 + integrations/observability/build.gradle.kts | 29 +++++++ .../ExceptionSanitizingSpanExporter.java | 2 +- ...ationContentBoundaryAutoConfiguration.java | 2 +- .../ObservationContentBoundaryVerifier.java | 2 +- ...viderLoggingBoundaryAutoConfiguration.java | 2 +- .../ProviderLoggingBoundaryVerifier.java | 2 +- ...anExportSanitizationAutoConfiguration.java | 2 +- ...ot.autoconfigure.AutoConfiguration.imports | 3 + .../ExceptionSanitizingSpanExporterTests.java | 2 +- ...servationContentBoundaryVerifierTests.java | 2 +- .../ProviderLoggingBoundaryVerifierTests.java | 2 +- ...ortSanitizationAutoConfigurationTests.java | 2 +- settings.gradle.kts | 1 + 25 files changed, 180 insertions(+), 37 deletions(-) create mode 100644 docs/decisions/0019-the-payload-boundary-is-its-own-module.md create mode 100644 integrations/observability/build.gradle.kts rename integrations/{graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag => observability/src/main/java/com/orgmemory/integrations}/observability/ExceptionSanitizingSpanExporter.java (98%) rename integrations/{graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag => observability/src/main/java/com/orgmemory/integrations}/observability/ObservationContentBoundaryAutoConfiguration.java (94%) rename integrations/{graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag => observability/src/main/java/com/orgmemory/integrations}/observability/ObservationContentBoundaryVerifier.java (98%) rename integrations/{graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag => observability/src/main/java/com/orgmemory/integrations}/observability/ProviderLoggingBoundaryAutoConfiguration.java (93%) rename integrations/{graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag => observability/src/main/java/com/orgmemory/integrations}/observability/ProviderLoggingBoundaryVerifier.java (97%) rename integrations/{graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag => observability/src/main/java/com/orgmemory/integrations}/observability/SpanExportSanitizationAutoConfiguration.java (95%) create mode 100644 integrations/observability/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports rename integrations/{graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag => observability/src/test/java/com/orgmemory/integrations}/observability/ExceptionSanitizingSpanExporterTests.java (98%) rename integrations/{graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag => observability/src/test/java/com/orgmemory/integrations}/observability/ObservationContentBoundaryVerifierTests.java (99%) rename integrations/{graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag => observability/src/test/java/com/orgmemory/integrations}/observability/ProviderLoggingBoundaryVerifierTests.java (98%) rename integrations/{graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag => observability/src/test/java/com/orgmemory/integrations}/observability/SpanExportSanitizationAutoConfigurationTests.java (98%) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 1aab80b2..385f218d 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -26,7 +26,8 @@ The Gradle build contains `core`; the deployable `apps:api`, `apps:mcp`, and `components:graph-rag-testkit`, and `components:scim-protocol-conformance`; and replaceable integrations for OpenFGA authorization, connectors, OpenAI-compatible AI, MinIO object storage, Spring AI GraphRAG, PostgreSQL, -OpenSearch, Neo4j, observability, and sidecar JSON. +OpenSearch, Neo4j, GraphRAG observability, the telemetry payload boundary, and +sidecar JSON. All runnable product surfaces live under `apps/`. The React client in `apps/web`, Fumadocs portal in `apps/docs`, and command-line client in @@ -46,6 +47,15 @@ Dependency direction is `apps/* -> core`. The adapter rule in force is `apps -> core + selected integrations`, `integrations -> core ports` (or the framework-neutral graph core), and never `core -> apps/integrations`. +`integrations/observability` is a deliberate exception to that adapter rule: it +implements no core port and depends on no OrgMemory module. It holds the +telemetry payload boundary — the span sanitizer and the two startup verifiers — +and the absence of a `project(...)` dependency is what keeps that boundary +adoptable by a deployable with no domain dependency. It arrives through +`orgmemory.spring-boot-app-conventions` rather than through each application's +build file, so taking the convention is taking the boundary. See +[decision 0019](docs/decisions/0019-the-payload-boundary-is-its-own-module.md). + ## Current Runtime Responsibilities - `core`: organization, permission, assistant, AI, knowledge, and Asset Registry diff --git a/apps/api/src/test/java/com/orgmemory/api/observability/ObservationContentBoundaryTests.java b/apps/api/src/test/java/com/orgmemory/api/observability/ObservationContentBoundaryTests.java index 9a62b3e3..9d932834 100644 --- a/apps/api/src/test/java/com/orgmemory/api/observability/ObservationContentBoundaryTests.java +++ b/apps/api/src/test/java/com/orgmemory/api/observability/ObservationContentBoundaryTests.java @@ -4,7 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import com.orgmemory.integrations.graphrag.observability.ObservationContentBoundaryVerifier; +import com.orgmemory.integrations.observability.ObservationContentBoundaryVerifier; import java.io.File; import java.io.IOException; import java.io.InputStream; diff --git a/apps/worker/src/test/java/com/orgmemory/worker/observability/ObservationContentBoundaryTests.java b/apps/worker/src/test/java/com/orgmemory/worker/observability/ObservationContentBoundaryTests.java index 17a849e3..2c00788c 100644 --- a/apps/worker/src/test/java/com/orgmemory/worker/observability/ObservationContentBoundaryTests.java +++ b/apps/worker/src/test/java/com/orgmemory/worker/observability/ObservationContentBoundaryTests.java @@ -4,7 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import com.orgmemory.integrations.graphrag.observability.ObservationContentBoundaryVerifier; +import com.orgmemory.integrations.observability.ObservationContentBoundaryVerifier; import java.io.File; import java.io.IOException; import java.io.InputStream; diff --git a/build-logic/src/main/kotlin/orgmemory.spring-boot-app-conventions.gradle.kts b/build-logic/src/main/kotlin/orgmemory.spring-boot-app-conventions.gradle.kts index af527ed9..4dc9051c 100644 --- a/build-logic/src/main/kotlin/orgmemory.spring-boot-app-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/orgmemory.spring-boot-app-conventions.gradle.kts @@ -10,3 +10,11 @@ dependencyManagement { mavenBom("org.springframework.ai:spring-ai-bom:2.0.0") } } + +dependencies { + // The payload boundary arrives with the convention rather than by each application + // remembering to ask for it. `apps/mcp` exported spans with no sanitizer and no verifier + // for exactly as long as taking them meant taking GraphRAG as well; a boundary an + // application can forget is a default. Taking this plugin is taking the boundary. + add("implementation", project(":integrations:observability")) +} diff --git a/docs/decisions/0019-the-payload-boundary-is-its-own-module.md b/docs/decisions/0019-the-payload-boundary-is-its-own-module.md new file mode 100644 index 00000000..75bce137 --- /dev/null +++ b/docs/decisions/0019-the-payload-boundary-is-its-own-module.md @@ -0,0 +1,81 @@ +# 0019 — The payload boundary is its own module + +Status: accepted, 2026-07-30. + +## Context + +[0018](0018-telemetry-carries-counts-never-payload.md) states that telemetry carries +counts and never the text those counts measure, and that the enforcement is structural. +It names `ExceptionSanitizingSpanExporter` as the last gate before egress and says it +applies to every span in the process, not only GraphRAG spans. + +All of that enforcement lived in `integrations/graph-rag-observability`, whose build file +opens with `api(project(":components:graph-rag-core"))`. Taking the boundary therefore +meant taking the graph domain. + +`apps/mcp` did not take it. Its build file has no `project(...)` dependency at all — it +was the only Gradle application in this repository with none — while carrying +`spring-boot-starter-opentelemetry` and exporting spans. So the guarantee 0018 claims +across the process held in two of the three deployables and in none of the third's spans. +`KnowledgeSearchApiClient` chains a `RestClientException` as the cause of its own +constant-message failure, and `exception.stacktrace` is exported unfiltered without the +sanitizer. Whether any particular exception carried payload was not established; that +nothing would have stopped it was. + +By the module inventory, seven of the ten files in that module had nothing to do with +GraphRAG, two of them added the same day by the increment that found this. + +## Decision + +The boundary becomes `integrations/observability`, depending on no OrgMemory module: + +- `ExceptionSanitizingSpanExporter` and `SpanExportSanitizationAutoConfiguration` +- `ProviderLoggingBoundaryVerifier` and its auto-configuration +- `ObservationContentBoundaryVerifier` and its auto-configuration + +`integrations/graph-rag-observability` keeps what implements a core port — the Micrometer +and OpenTelemetry `GraphRagEventSink` adapters and the `GraphRagTaskDecorator` +implementation — and depends on the new module only in its test source set, where +`WholeExportAllowlistTests` asserts a GraphRAG span through the sanitizer in the position +the sanitizer occupies in production. + +`orgmemory.spring-boot-app-conventions` adds the new module. Taking the convention is +taking the boundary. Every Gradle application in this repository already applies that +plugin, so `apps/mcp` gains the boundary without its build file changing, and a fourth +application cannot omit it by not knowing to ask. + +The absence of a `project(...)` line in the new module's build file is load-bearing. It +is what keeps the boundary adoptable by a deployable that has no domain dependency, which +is the condition that failed here. + +## Acknowledged exception to a stated rule + +`ARCHITECTURE.md` states the adapter rule as `integrations -> core ports`. The new module +implements no core port; it implements OpenTelemetry's `SpanExporter` and two startup +checks that implement nothing. By that rule it is not an integration. + +Placing it in `integrations/` anyway is the project owner's decision, taken on 2026-07-30 +after the alternatives were presented. It is recorded here as an exception that was seen +and chosen, not one that was never noticed. `ARCHITECTURE.md` names it in the same terms. + +## Rejected alternatives + +**Add `graph-rag-observability` to `apps/mcp` and change nothing else.** One line, and it +closes the same gap. Rejected because it puts `components/graph-rag-core` into the one +deployable that had no domain dependency, to obtain a guarantee that has nothing to do +with the domain. The dependency would be permanent and the reason for it invisible. + +**Leave the module whole and rely on the convention plugin alone.** The convention makes +the boundary unforgettable wherever it lives, so this closes the `apps/mcp` gap without +any move. Considered seriously and rejected: it would attach the graph domain to every +future application as the price of a payload boundary, which is the coupling rather than +the naming. + +**A fifth top-level directory, `platform/`, for cross-cutting runtime policy.** The +honest placement by the repository's own rules, since the module fits neither +`integrations -> core ports` nor the framework-neutral description of `components/`. +Rejected by the project owner as more structure than roughly two hundred and fifty lines +earns, against a module count already at seventeen. + +**Rename `graph-rag-observability` without splitting it.** Fixes the reading and not the +coupling. The name was the symptom. diff --git a/docs/increments/active/2026-07-29-observability-pipeline/plan.md b/docs/increments/active/2026-07-29-observability-pipeline/plan.md index 37a3c602..d8b50704 100644 --- a/docs/increments/active/2026-07-29-observability-pipeline/plan.md +++ b/docs/increments/active/2026-07-29-observability-pipeline/plan.md @@ -261,10 +261,12 @@ belong with the collector, because the collector is what would show them. - [x] Amend the runbook's "no exception event or stack trace" gate. It demanded something no code enforced and would have failed a correct export. The event survives carrying `exception.type` alone. -- [ ] Resolve where telemetry egress lives. `integrations/graph-rag-observability` - is named for one domain but now owns the span sanitizer, which protects - every span in the process. Renaming it is a module-boundary change and - needs its own challenge, so it is recorded here rather than done quietly. +- [x] Resolve where telemetry egress lives. Settled 2026-07-30, and the answer was + not a rename. The boundary moved to `integrations/observability`, which + depends on no OrgMemory module and arrives through the app convention + plugin. Investigating it found that `apps/mcp` held no payload boundary at + all, because taking one meant taking the graph domain with it. See + [decision 0019](../../../decisions/0019-the-payload-boundary-is-its-own-module.md). - [x] Record the payload-boundary decision: `docs/decisions/0018-telemetry-carries-counts-never-payload.md`. diff --git a/docs/increments/active/2026-07-30-observability-platform/plan.md b/docs/increments/active/2026-07-30-observability-platform/plan.md index 9793f883..7ef34274 100644 --- a/docs/increments/active/2026-07-30-observability-platform/plan.md +++ b/docs/increments/active/2026-07-30-observability-platform/plan.md @@ -54,7 +54,8 @@ along, because they write to the application log. hole that prompted the question. Full reasoning in `design.md`. Gate: `:apps:api:test`, `:apps:worker:test`, -`:integrations:graph-rag-observability:test`. +`:integrations:graph-rag-observability:test`, +`:integrations:observability:test`. ## 2. Time to first token @@ -148,10 +149,12 @@ receiving looks exactly like a system with nothing to report. deletion-then-rebuild drill, and a search of `graph-rag-core`, `core` and `apps/worker` found no deletion path. Establish whether the drill is executable before adding a stage nothing can emit. -4. **Telemetry-egress module naming.** Five of eight files in - `integrations/graph-rag-observability` are not GraphRAG-specific, including - the process-wide span sanitizer with no toggle. Lowest priority; the cost is - that the name understates what disabling it would remove. +4. ~~**Telemetry-egress module naming.**~~ Settled 2026-07-30 and larger than the + name: the boundary now lives in `integrations/observability`, which depends on + no OrgMemory module and reaches every application through the app convention + plugin. Investigating it found that `apps/mcp` had no payload boundary at all, + because taking it meant taking the graph domain. See + [decision 0019](../../../decisions/0019-the-payload-boundary-is-its-own-module.md). ## Deferred, with reasons diff --git a/docs/specs/domains/secure-graph-rag.md b/docs/specs/domains/secure-graph-rag.md index 239496fc..d1175998 100644 --- a/docs/specs/domains/secure-graph-rag.md +++ b/docs/specs/domains/secure-graph-rag.md @@ -3,8 +3,9 @@ Source: `components/graph-rag-core`, `components/graph-rag-testkit`, `integrations/graph-rag-*`, `apps/worker/src/main/java/com/orgmemory/worker/graph`, `core/src/main/java/com/orgmemory/core/knowledge`, -`apps/web/src/features/knowledge`, and — for the payload-boundary configuration -this document states — `apps/api/src/main/resources/application*.yml` and +`apps/web/src/features/knowledge`, `integrations/observability`, and — for the +payload-boundary configuration this document states — +`apps/api/src/main/resources/application*.yml` and `apps/worker/src/main/resources/application*.yml`. Reconciled: `2026-07-30-observability-platform (2b8a9d6)`. @@ -218,6 +219,13 @@ Reconciled: `2026-07-30-observability-platform (2b8a9d6)`. counting it, recording its type and logging once per change of kind, so a backend broken since startup is distinguishable from a quiet one. Only class names are recorded. +- The telemetry payload boundary lives in `integrations/observability`, which depends + on no OrgMemory module and reaches every application through + `orgmemory.spring-boot-app-conventions` rather than through each application's own + build file. `apps/mcp` exports spans and holds no domain dependency; before the + split it could not take the boundary without taking the graph domain, and so held + none of it. `integrations/graph-rag-observability` keeps only what implements a + core port. - Every exported span passes through `ExceptionSanitizingSpanExporter` before leaving the process. It keeps `exception.type` alone from each event and clears the status description, because Micrometer's bridge copies diff --git a/docs/tests/domains/secure-graph-rag.md b/docs/tests/domains/secure-graph-rag.md index 5dc56c16..746af2ec 100644 --- a/docs/tests/domains/secure-graph-rag.md +++ b/docs/tests/domains/secure-graph-rag.md @@ -2,6 +2,7 @@ Source: `components/graph-rag-core/src/test`, `components/graph-rag-testkit/src/test`, `integrations/graph-rag-*/src/test`, +`integrations/observability/src/test`, `apps/worker/src/test/java/com/orgmemory/worker/graph`, `apps/api/src/test/java/com/orgmemory/api/observability`, `apps/worker/src/test/java/com/orgmemory/worker/observability`, @@ -145,6 +146,10 @@ Reconciled: `2026-07-30-observability-platform (2b8a9d6)`. empty classpath cannot make it pass vacuously; removing one flag from the guarded list was confirmed to fail it by name. The same tests read the shipped `application.yml` and every profile file for a flag declared true. +- The boundary module's own tests moved with it and are unchanged, so the split is + a move rather than a rewrite. What proves the split worked is a dependency fact + rather than a test: `apps/mcp` resolves exactly one OrgMemory project on its + runtime classpath, `integrations:observability`, and no longer resolves none. - Storage adapter auto-configuration tests prove PostgreSQL, OpenSearch and Neo4j stay discoverable through their registration files, that PostgreSQL owns the canonical ports without an opt-in, that OpenSearch and Neo4j claim no port diff --git a/integrations/graph-rag-observability/build.gradle.kts b/integrations/graph-rag-observability/build.gradle.kts index d9df3eac..99ab9972 100644 --- a/integrations/graph-rag-observability/build.gradle.kts +++ b/integrations/graph-rag-observability/build.gradle.kts @@ -7,23 +7,18 @@ dependencies { implementation("io.micrometer:context-propagation") implementation("io.micrometer:micrometer-core") implementation("io.opentelemetry:opentelemetry-api") - implementation("io.opentelemetry:opentelemetry-sdk-trace") - implementation("org.slf4j:slf4j-api") implementation("org.springframework.boot:spring-boot-autoconfigure") - // Supplies the SpanExporters collection this module replaces with a sanitized copy. - implementation("org.springframework.boot:spring-boot-micrometer-tracing-opentelemetry") testImplementation("io.opentelemetry:opentelemetry-sdk") testImplementation("io.opentelemetry:opentelemetry-sdk-testing") - // Drives the real bridge path this module sanitizes rather than a copy of it. + // Drives the real bridge path the sanitizer sits in rather than a copy of it. testImplementation("io.micrometer:micrometer-tracing-bridge-otel") + // The allowlist gate asserts a GraphRAG span through the sanitizer in the position the + // sanitizer occupies in production, so it needs the boundary module the applications get + // separately. This is the one place the two modules meet, and it is a test. + testImplementation(project(":integrations:observability")) testImplementation("org.junit.jupiter:junit-jupiter") testImplementation("org.springframework.boot:spring-boot-test") testImplementation("org.assertj:assertj-core") - // The logging boundary is only meaningful against the real leak sites and a real logging - // backend, so the verifier is tested against both rather than against stand-ins. - testImplementation("org.springframework.ai:spring-ai-openai") - testImplementation("org.springframework.ai:spring-ai-anthropic") - testImplementation("ch.qos.logback:logback-classic") testRuntimeOnly("org.junit.platform:junit-platform-launcher") } diff --git a/integrations/graph-rag-observability/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/integrations/graph-rag-observability/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index c322b564..38153248 100644 --- a/integrations/graph-rag-observability/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/integrations/graph-rag-observability/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,4 +1 @@ com.orgmemory.integrations.graphrag.observability.GraphRagObservabilityAutoConfiguration -com.orgmemory.integrations.graphrag.observability.ObservationContentBoundaryAutoConfiguration -com.orgmemory.integrations.graphrag.observability.ProviderLoggingBoundaryAutoConfiguration -com.orgmemory.integrations.graphrag.observability.SpanExportSanitizationAutoConfiguration diff --git a/integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/WholeExportAllowlistTests.java b/integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/WholeExportAllowlistTests.java index a35f3e08..4d7e5969 100644 --- a/integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/WholeExportAllowlistTests.java +++ b/integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/WholeExportAllowlistTests.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import com.orgmemory.graphrag.observability.GraphRagEventSink; +import com.orgmemory.integrations.observability.ExceptionSanitizingSpanExporter; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; diff --git a/integrations/observability/build.gradle.kts b/integrations/observability/build.gradle.kts new file mode 100644 index 00000000..bc7b4b5e --- /dev/null +++ b/integrations/observability/build.gradle.kts @@ -0,0 +1,29 @@ +plugins { + id("orgmemory.spring-library-conventions") +} + +// Deliberately depends on no OrgMemory module. The payload boundary has to be +// adoptable by any deployable, including one that has no domain dependency at all, +// and the absence of a `project(...)` line here is what keeps that true. +dependencies { + implementation("io.opentelemetry:opentelemetry-api") + implementation("io.opentelemetry:opentelemetry-sdk-trace") + implementation("org.slf4j:slf4j-api") + implementation("org.springframework.boot:spring-boot-autoconfigure") + // Supplies the SpanExporters collection this module replaces with a sanitized copy. + implementation("org.springframework.boot:spring-boot-micrometer-tracing-opentelemetry") + + testImplementation("io.opentelemetry:opentelemetry-sdk") + testImplementation("io.opentelemetry:opentelemetry-sdk-testing") + // Drives the real bridge path this module sanitizes rather than a copy of it. + testImplementation("io.micrometer:micrometer-tracing-bridge-otel") + testImplementation("org.junit.jupiter:junit-jupiter") + testImplementation("org.springframework.boot:spring-boot-test") + testImplementation("org.assertj:assertj-core") + // The logging boundary is only meaningful against the real leak sites and a real logging + // backend, so the verifier is tested against both rather than against stand-ins. + testImplementation("org.springframework.ai:spring-ai-openai") + testImplementation("org.springframework.ai:spring-ai-anthropic") + testImplementation("ch.qos.logback:logback-classic") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") +} diff --git a/integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/ExceptionSanitizingSpanExporter.java b/integrations/observability/src/main/java/com/orgmemory/integrations/observability/ExceptionSanitizingSpanExporter.java similarity index 98% rename from integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/ExceptionSanitizingSpanExporter.java rename to integrations/observability/src/main/java/com/orgmemory/integrations/observability/ExceptionSanitizingSpanExporter.java index 6c93aa66..07a6cce1 100644 --- a/integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/ExceptionSanitizingSpanExporter.java +++ b/integrations/observability/src/main/java/com/orgmemory/integrations/observability/ExceptionSanitizingSpanExporter.java @@ -1,4 +1,4 @@ -package com.orgmemory.integrations.graphrag.observability; +package com.orgmemory.integrations.observability; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; diff --git a/integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/ObservationContentBoundaryAutoConfiguration.java b/integrations/observability/src/main/java/com/orgmemory/integrations/observability/ObservationContentBoundaryAutoConfiguration.java similarity index 94% rename from integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/ObservationContentBoundaryAutoConfiguration.java rename to integrations/observability/src/main/java/com/orgmemory/integrations/observability/ObservationContentBoundaryAutoConfiguration.java index 0b865660..a878abf0 100644 --- a/integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/ObservationContentBoundaryAutoConfiguration.java +++ b/integrations/observability/src/main/java/com/orgmemory/integrations/observability/ObservationContentBoundaryAutoConfiguration.java @@ -1,4 +1,4 @@ -package com.orgmemory.integrations.graphrag.observability; +package com.orgmemory.integrations.observability; import org.springframework.beans.factory.InitializingBean; import org.springframework.boot.autoconfigure.AutoConfiguration; diff --git a/integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/ObservationContentBoundaryVerifier.java b/integrations/observability/src/main/java/com/orgmemory/integrations/observability/ObservationContentBoundaryVerifier.java similarity index 98% rename from integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/ObservationContentBoundaryVerifier.java rename to integrations/observability/src/main/java/com/orgmemory/integrations/observability/ObservationContentBoundaryVerifier.java index 953c288d..306df187 100644 --- a/integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/ObservationContentBoundaryVerifier.java +++ b/integrations/observability/src/main/java/com/orgmemory/integrations/observability/ObservationContentBoundaryVerifier.java @@ -1,4 +1,4 @@ -package com.orgmemory.integrations.graphrag.observability; +package com.orgmemory.integrations.observability; import java.util.List; import org.springframework.core.env.Environment; diff --git a/integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/ProviderLoggingBoundaryAutoConfiguration.java b/integrations/observability/src/main/java/com/orgmemory/integrations/observability/ProviderLoggingBoundaryAutoConfiguration.java similarity index 93% rename from integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/ProviderLoggingBoundaryAutoConfiguration.java rename to integrations/observability/src/main/java/com/orgmemory/integrations/observability/ProviderLoggingBoundaryAutoConfiguration.java index f4bc88ea..a22a4bfc 100644 --- a/integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/ProviderLoggingBoundaryAutoConfiguration.java +++ b/integrations/observability/src/main/java/com/orgmemory/integrations/observability/ProviderLoggingBoundaryAutoConfiguration.java @@ -1,4 +1,4 @@ -package com.orgmemory.integrations.graphrag.observability; +package com.orgmemory.integrations.observability; import org.springframework.beans.factory.InitializingBean; import org.springframework.boot.autoconfigure.AutoConfiguration; diff --git a/integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/ProviderLoggingBoundaryVerifier.java b/integrations/observability/src/main/java/com/orgmemory/integrations/observability/ProviderLoggingBoundaryVerifier.java similarity index 97% rename from integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/ProviderLoggingBoundaryVerifier.java rename to integrations/observability/src/main/java/com/orgmemory/integrations/observability/ProviderLoggingBoundaryVerifier.java index c42899a5..08d0cd2e 100644 --- a/integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/ProviderLoggingBoundaryVerifier.java +++ b/integrations/observability/src/main/java/com/orgmemory/integrations/observability/ProviderLoggingBoundaryVerifier.java @@ -1,4 +1,4 @@ -package com.orgmemory.integrations.graphrag.observability; +package com.orgmemory.integrations.observability; import java.util.List; import org.slf4j.LoggerFactory; diff --git a/integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/SpanExportSanitizationAutoConfiguration.java b/integrations/observability/src/main/java/com/orgmemory/integrations/observability/SpanExportSanitizationAutoConfiguration.java similarity index 95% rename from integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/SpanExportSanitizationAutoConfiguration.java rename to integrations/observability/src/main/java/com/orgmemory/integrations/observability/SpanExportSanitizationAutoConfiguration.java index bb1fa880..584f7fe9 100644 --- a/integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/SpanExportSanitizationAutoConfiguration.java +++ b/integrations/observability/src/main/java/com/orgmemory/integrations/observability/SpanExportSanitizationAutoConfiguration.java @@ -1,4 +1,4 @@ -package com.orgmemory.integrations.graphrag.observability; +package com.orgmemory.integrations.observability; import io.opentelemetry.sdk.trace.export.SpanExporter; import org.springframework.beans.factory.ObjectProvider; diff --git a/integrations/observability/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/integrations/observability/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..155d79f4 --- /dev/null +++ b/integrations/observability/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,3 @@ +com.orgmemory.integrations.observability.ObservationContentBoundaryAutoConfiguration +com.orgmemory.integrations.observability.ProviderLoggingBoundaryAutoConfiguration +com.orgmemory.integrations.observability.SpanExportSanitizationAutoConfiguration diff --git a/integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/ExceptionSanitizingSpanExporterTests.java b/integrations/observability/src/test/java/com/orgmemory/integrations/observability/ExceptionSanitizingSpanExporterTests.java similarity index 98% rename from integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/ExceptionSanitizingSpanExporterTests.java rename to integrations/observability/src/test/java/com/orgmemory/integrations/observability/ExceptionSanitizingSpanExporterTests.java index 58263d2f..fc1011b7 100644 --- a/integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/ExceptionSanitizingSpanExporterTests.java +++ b/integrations/observability/src/test/java/com/orgmemory/integrations/observability/ExceptionSanitizingSpanExporterTests.java @@ -1,4 +1,4 @@ -package com.orgmemory.integrations.graphrag.observability; +package com.orgmemory.integrations.observability; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; diff --git a/integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/ObservationContentBoundaryVerifierTests.java b/integrations/observability/src/test/java/com/orgmemory/integrations/observability/ObservationContentBoundaryVerifierTests.java similarity index 99% rename from integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/ObservationContentBoundaryVerifierTests.java rename to integrations/observability/src/test/java/com/orgmemory/integrations/observability/ObservationContentBoundaryVerifierTests.java index 9e367512..1468fef2 100644 --- a/integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/ObservationContentBoundaryVerifierTests.java +++ b/integrations/observability/src/test/java/com/orgmemory/integrations/observability/ObservationContentBoundaryVerifierTests.java @@ -1,4 +1,4 @@ -package com.orgmemory.integrations.graphrag.observability; +package com.orgmemory.integrations.observability; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/ProviderLoggingBoundaryVerifierTests.java b/integrations/observability/src/test/java/com/orgmemory/integrations/observability/ProviderLoggingBoundaryVerifierTests.java similarity index 98% rename from integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/ProviderLoggingBoundaryVerifierTests.java rename to integrations/observability/src/test/java/com/orgmemory/integrations/observability/ProviderLoggingBoundaryVerifierTests.java index a0e2ced4..e65b23e9 100644 --- a/integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/ProviderLoggingBoundaryVerifierTests.java +++ b/integrations/observability/src/test/java/com/orgmemory/integrations/observability/ProviderLoggingBoundaryVerifierTests.java @@ -1,4 +1,4 @@ -package com.orgmemory.integrations.graphrag.observability; +package com.orgmemory.integrations.observability; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/SpanExportSanitizationAutoConfigurationTests.java b/integrations/observability/src/test/java/com/orgmemory/integrations/observability/SpanExportSanitizationAutoConfigurationTests.java similarity index 98% rename from integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/SpanExportSanitizationAutoConfigurationTests.java rename to integrations/observability/src/test/java/com/orgmemory/integrations/observability/SpanExportSanitizationAutoConfigurationTests.java index 2f4e2522..f18b6fea 100644 --- a/integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/SpanExportSanitizationAutoConfigurationTests.java +++ b/integrations/observability/src/test/java/com/orgmemory/integrations/observability/SpanExportSanitizationAutoConfigurationTests.java @@ -1,4 +1,4 @@ -package com.orgmemory.integrations.graphrag.observability; +package com.orgmemory.integrations.observability; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; diff --git a/settings.gradle.kts b/settings.gradle.kts index 3df5b351..8f7d914b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -29,6 +29,7 @@ include(":integrations:graph-rag-postgres") include(":integrations:graph-rag-opensearch") include(":integrations:graph-rag-neo4j") include(":integrations:graph-rag-observability") +include(":integrations:observability") include(":integrations:graph-rag-sidecar-json") include(":integrations:graph-rag-spring-ai") include(":integrations:object-storage-minio")