Skip to content

fix(observability): close the payload bypasses the telemetry boundary did not cover - #137

Merged
kl3inIT merged 9 commits into
mainfrom
fix/observability-payload-boundary
Jul 29, 2026
Merged

fix(observability): close the payload bypasses the telemetry boundary did not cover#137
kl3inIT merged 9 commits into
mainfrom
fix/observability-payload-boundary

Conversation

@kl3inIT

@kl3inIT kl3inIT commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Phase 0 of the observability pipeline increment. The runbook has been promising payload-free telemetry; three paths were bypassing it.

What was wrong

Provider libraries log prompts. spring.ai.chat.observations.log-prompt: false governs Spring AI's observation handlers, not the client libraries, which build their own WARN messages by concatenating the prompt or the response content — OpenAiChatModel:222, AnthropicChatModel:554, :1219, and a fourth found during the sweep, :1018. Production runs at root INFO, so nothing suppressed them.

Error spans carry exception text. Micrometer's bridge ends a failed span with recordException(throwable) and setStatus(ERROR, throwable.getMessage()). The runbook's release gate — "confirm error spans contain no exception event or stack trace" — could not have passed.

A broken sink was invisible. Six call sites caught and discarded emission failures, correctly, so a sink failing since startup looked exactly like a quiet one.

Nothing has leaked: the ZM deployment is a POC with no real users or customer data, so this is preventive.

What changed

Provider packages are pinned above WARN in the base configuration of both apps — a development database holds real uploaded documents too — and deliberately without an environment override. Every leak site is guarded by isWarnEnabled(), so the pin stops the message being built, not merely printed.

ExceptionSanitizingSpanExporter keeps exception.type alone and clears the status description, as the last gate before egress. Micrometer's SpanFilter reaches the events but not the description, which is why this sits at the SpanData level. No toggle: a payload guard a deployment can switch off is not a guard.

FailureTolerantGraphRagEventSink keeps absorbing sink failures but counts them, records the failure type and logs once per change of kind. Class names only — a telemetry error can quote the request it failed to send. Uses System.Logger, so graph-rag-core keeps its zero-runtime-dependency property.

Scope this does not cover

Span attributes are not filtered, and no test asserts the whole export against the allowlist. Both are stated in the test matrix rather than glossed; the whole-export test is phase 5.

Also found

Checking LightRAG's tracing against the pinned v1.5.4 checkout corrected an earlier claim in this increment that it has none. It has one integration — lightrag/llm/openai.py swaps in langfuse.openai.AsyncOpenAI, which captures prompts and completions verbatim — for the OpenAI binding only, with no masking anywhere.

The comparison surfaced a gap here: Stage declares fourteen values and production emits ten. PARSE, CHUNK, GLEAN and GENERATE have no producer, and deletion/rebuild has no stage at all. Recorded as phase 2 work.

Verification

:core:test, :components:graph-rag-core:test, :integrations:graph-rag-observability:test, plus compileJava compileTestJava across the repo — all green locally. Specs and test matrix reconciled in the same change, since their source paths cover the code that moved.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Prevented telemetry delivery failures from interrupting graph processing.
    • Reduced sensitive provider prompt and completion details in application logs.
    • Sanitized exported error telemetry by removing exception messages, stack traces, and status descriptions while retaining error types.
  • Tests

    • Added coverage to verify logging safeguards, failure-tolerant telemetry handling, and telemetry sanitization across configurations and exporters.

kl3inIT and others added 7 commits July 29, 2026 22:24
Production has logged an OTLP metrics export failure every minute since
2026-07-25. The cause is a transitive default: the OpenTelemetry starter
resolves micrometer-registry-otlp, whose export is opt-out and defaults
to a localhost URL nothing listens on. OTLP tracing behaves oppositely
and is not involved.

Scoping that work surfaced a larger finding. A proposal to replace the
flat payload-free rule with configurable tiers went through an
independent architecture challenge, which rejected it and produced two
verified bypasses of the boundary the runbook claims: Spring AI provider
libraries concatenate the prompt into WARN logs that log-prompt=false
does not reach, and the Micrometer OTel bridge records exception events
and messages on error spans. Both are live paths carrying ACL-scoped
evidence.

The policy stays unchanged; the repository claims more than it enforces.
Closing those bypasses is phase 0, ahead of the pipeline work. The
challenge brief and verdict are kept with the increment because their
working location is not versioned.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`spring.ai.chat.observations.log-prompt: false` governs Spring AI's own
observation handlers. It does not reach the client libraries, which build their
own WARN messages by concatenating the prompt or the response content:

  org/springframework/ai/openai/OpenAiChatModel.java:222
  org/springframework/ai/anthropic/AnthropicChatModel.java:554
  org/springframework/ai/anthropic/AnthropicChatModel.java:1219
  org/springframework/ai/anthropic/AnthropicChatModel.java:1018

OrgMemory prompts carry the query, the grounded context and chunk content, so
those lines put customer text into a log the deployment retains on the host.
Production runs at root INFO, so nothing was suppressing them.

Every one of those call sites is guarded by isWarnEnabled(), so pinning the two
packages above WARN stops the message from being built rather than only from
being printed. The pin lives in the base configuration, not the production
profile, because a development database holds real uploaded documents too, and
it is deliberately not environment-overridable: the payload-free boundary is not
a per-deployment setting.

The tests read the shipped YAML rather than a copy of it, and fail if a profile
lowers either package back to WARN.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Micrometer's OpenTelemetry bridge ends a failed span by calling
recordException(throwable) and setStatus(ERROR, throwable.getMessage()), so the
message and the stack trace travel with the span. An OrgMemory exception can be
raised while holding a query, an evidence chunk or a provider response body, and
nothing upstream can prove otherwise. The production hardening runbook already
promises that error spans carry no exception event or stack trace; until now
nothing enforced it, and the release gate could not have passed.

ExceptionSanitizingSpanExporter wraps every exporter and drops all event
attributes except exception.type, which is a class name fixed by the source, and
clears the status description. Attribute counts keep their original values so a
stripped attribute still reports as dropped rather than as never recorded.

Micrometer's SpanFilter is the natural hook for the events but cannot reach the
status description, which DelegatingSpanData does not expose as mutable.
Wrapping the exporter covers both and runs after every filter, making it the
last gate before egress. Spring Boot contributes its own SpanExporters only when
one is missing, so declaring the collection ahead of it replaces every exporter
with a wrapped copy; the ordering itself is asserted, because a wrong beforeName
would silently hand the unwrapped collection back.

There is no toggle. A payload guard a deployment can switch off is not a guard.

Span attributes are deliberately left alone: their allowlist is a wider question
than exception handling and belongs with the whole-export test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every producer wraps emit() in a catch that discards the failure, which is the
right call — an observability backend must never decide whether a retrieval or
an indexing job succeeds. The cost was that a sink broken since startup looked
exactly like a sink with nothing to report, across five call sites in
GraphRagKnowledgeRetrievalService and one in GraphIndexingProcessor.

FailureTolerantGraphRagEventSink keeps the absorption and adds the signal that
was missing: a running count, the type of the most recent failure, and one log
line each time the failure changes kind, so a permanently broken sink cannot
flood the log at event rate while one that starts failing differently still
says so.

Only class names are recorded. A telemetry backend's exception message can quote
the request it failed to send, so the message and the stack trace stay out on
the same grounds as any other payload.

Logging goes through System.Logger, so components/graph-rag-core keeps its
property of having no runtime dependencies; Boot's JUL bridge routes it into the
application log.

The wrapper is applied where the composite is built, so production always gets
it. The existing call-site catches stay as defence in depth for anyone
constructing the service with a raw sink.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The semantic port came from LightRAG, so its tracing was read from the pinned
v1.5.4 checkout rather than from recollection. An earlier note in this increment
said it has no observability; that was wrong.

It has exactly one integration: lightrag/llm/openai.py swaps openai.AsyncOpenAI
for langfuse.openai.AsyncOpenAI when both Langfuse keys are set, which captures
requests and responses verbatim. No other provider binding is traced, and there
is no masking, no OpenTelemetry, no metrics and no spans over its own stages.
The upstream therefore does what this increment's decision rejects, through a
vendor drop-in rather than a designed boundary — which supports the finding that
"no comparable system chose never" reflects an absence of deliberation.

The comparison also surfaced a gap on this side. Stage declares fourteen values
and production emits ten: PARSE, CHUNK, GLEAN and GENERATE have no producer
outside tests, so a stage-grouped dashboard would carry four empty series and no
parsing, chunking, gleaning or generation latency. Deletion and rebuild are
absent from the enum altogether, though LightRAG tracks that path and the
hardening runbook requires a drill for it. Both are now phase 2 work.

Phase 0 is recorded as code-complete apart from the production log search, which
needs the owner's go-ahead because it may surface customer content.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The spec's source paths cover components/graph-rag-core, integrations/graph-rag-*,
the worker graph package and core/knowledge, all of which moved in this change, so
reconciliation belongs here rather than at the end of the increment.

Records the span sanitizer, the failure-tolerant sink wrapper and the provider
logger pin as current behaviour, and states plainly what they do not cover: span
attributes are unfiltered, and no test asserts the whole export against the
allowlist, so "payload-free" is enforced at named points rather than proven end
to end.

Also records that Stage declares fourteen values while production emits ten, and
that deletion and rebuild have no stage at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The provider logging fix is preventive, not remedial. The project owner
confirmed on 2026-07-29 that the ZM deployment is a proof of concept with no
real users and no customer data, so the retained logs hold no exposure to scope
and the planned production log search has nothing to find.

Recorded as an attributable statement about today's deployment rather than about
the design, because the same code against a deployment holding real evidence
would have been writing customer text to the host since it shipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@kl3inIT, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 18 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6001a99c-2a77-4fbe-98a8-fb3a646f98ec

📥 Commits

Reviewing files that changed from the base of the PR and between eb01be7 and 77aa276.

⛔ Files ignored due to path filters (3)
  • docs/roadmap.md is excluded by !docs/**
  • docs/specs/domains/secure-graph-rag.md is excluded by !docs/**
  • docs/tests/domains/secure-graph-rag.md is excluded by !docs/**
📒 Files selected for processing (11)
  • apps/api/src/main/resources/application.yml
  • apps/api/src/test/java/com/orgmemory/api/observability/ProviderLoggingBoundaryTests.java
  • apps/worker/src/main/resources/application.yml
  • apps/worker/src/test/java/com/orgmemory/worker/observability/ProviderLoggingBoundaryTests.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/observability/FailureTolerantGraphRagEventSink.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/observability/FailureTolerantGraphRagEventSinkTests.java
  • integrations/graph-rag-observability/build.gradle.kts
  • integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/ProviderLoggingBoundaryAutoConfiguration.java
  • integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/ProviderLoggingBoundaryVerifier.java
  • integrations/graph-rag-observability/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  • integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/ProviderLoggingBoundaryVerifierTests.java
📝 Walkthrough

Walkthrough

Adds Spring AI provider logging boundaries, failure-tolerant GraphRAG event sinks, and OpenTelemetry span export sanitization. Configurations, auto-configuration wiring, and tests validate each observability behavior.

Changes

Provider logging boundaries

Layer / File(s) Summary
Provider logger configuration and validation
apps/api/src/main/resources/application.yml, apps/api/src/test/..., apps/worker/src/main/resources/application.yml, apps/worker/src/test/...
Pins OpenAI and Anthropic Spring AI loggers to ERROR and verifies profile configurations do not lower them below ERROR, FATAL, or OFF.

Failure-tolerant GraphRAG sinks

Layer / File(s) Summary
Failure-tolerant sink implementation
components/graph-rag-core/src/main/java/..., components/graph-rag-core/src/test/...
Adds a factory and wrapper that absorb emission failures, track failure counts and types, rate warning logs by exception class, and preserve successful delegation.
Application event sink wiring
apps/worker/src/main/java/..., core/src/main/java/...
Wraps composed event sinks used by graph indexing and knowledge retrieval with the failure-tolerant sink.

Span export sanitization

Layer / File(s) Summary
Exception span sanitization
integrations/graph-rag-observability/src/main/java/..., integrations/graph-rag-observability/src/test/...
Adds an exporter that retains only exception.type, removes other event attributes, clears status descriptions, and forwards sanitized spans.
Exporter wrapping auto-configuration
integrations/graph-rag-observability/src/main/java/..., integrations/graph-rag-observability/src/main/resources/..., integrations/graph-rag-observability/src/test/...
Registers conditional Spring Boot auto-configuration that wraps all ordered SpanExporter beans with the sanitizing exporter and validates precedence over Boot’s exporter collection.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GraphRagEventSink
  participant FailureTolerantGraphRagEventSink
  participant DelegateSink
  GraphRagEventSink->>FailureTolerantGraphRagEventSink: failureTolerant(sink)
  FailureTolerantGraphRagEventSink->>DelegateSink: emit(event)
  DelegateSink-->>FailureTolerantGraphRagEventSink: success or RuntimeException
  FailureTolerantGraphRagEventSink-->>GraphRagEventSink: complete or record swallowed failure
Loading
sequenceDiagram
  participant SdkTracerProvider
  participant ExceptionSanitizingSpanExporter
  participant SpanExporter
  SdkTracerProvider->>ExceptionSanitizingSpanExporter: export(spanData)
  ExceptionSanitizingSpanExporter->>ExceptionSanitizingSpanExporter: sanitize events and status
  ExceptionSanitizingSpanExporter->>SpanExporter: export(sanitizedSpanData)
  SpanExporter-->>SdkTracerProvider: CompletableResultCode
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.73% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is awkward, but it clearly refers to closing observability/telemetry boundary bypasses, which matches the main changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch fix/observability-payload-boundary
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/observability-payload-boundary

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…load-boundary

# Conflicts:
#	docs/roadmap.md

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/api/src/main/resources/application.yml`:
- Around line 183-193: Enforce the provider logger boundary at runtime by
validating the resolved levels for org.springframework.ai.openai and
org.springframework.ai.anthropic, rejecting startup when either effective level
is below ERROR despite configuration overrides. Apply the corresponding
configuration/validation in apps/api/src/main/resources/application.yml (lines
183-193) and apps/worker/src/main/resources/application.yml (lines 118-128).
Extend ProviderLoggingBoundaryTests in
apps/api/src/test/java/com/orgmemory/api/observability/ProviderLoggingBoundaryTests.java
(lines 39-53) and
apps/worker/src/test/java/com/orgmemory/worker/observability/ProviderLoggingBoundaryTests.java
(lines 39-53) to set higher-precedence WARN overrides and verify they are
rejected.

In
`@apps/api/src/test/java/com/orgmemory/api/observability/ProviderLoggingBoundaryTests.java`:
- Around line 63-75: Update ProviderLoggingBoundaryTests in both API and worker
copies: recognize profile files ending in .yml or .yaml, and match provider
logger entries explicitly so child loggers such as
org.springframework.ai.openai.api.requests are validated even when a parent
logger has a stricter level. Apply the corresponding changes at the listed
profile-discovery and logger-validation sites in both files.

In
`@components/graph-rag-core/src/main/java/com/orgmemory/graphrag/observability/FailureTolerantGraphRagEventSink.java`:
- Around line 61-70: Update FailureTolerantGraphRagEventSink’s failure-reporting
state so logging is rate-limited per failure type rather than only when it
differs from lastFailureType(). Retain lastFailureType() as the latest observed
type, use a bounded policy to prevent unbounded state, and add coverage for an
A→B→A sequence ensuring recurring types are suppressed within the limit.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 389d1bfe-8859-4d34-8e35-e52825f1193e

📥 Commits

Reviewing files that changed from the base of the PR and between 605c872 and eb01be7.

⛔ Files ignored due to path filters (7)
  • docs/increments/active/2026-07-29-observability-pipeline/challenge-brief.md is excluded by !docs/**
  • docs/increments/active/2026-07-29-observability-pipeline/challenge-verdict.md is excluded by !docs/**
  • docs/increments/active/2026-07-29-observability-pipeline/design.md is excluded by !docs/**
  • docs/increments/active/2026-07-29-observability-pipeline/plan.md is excluded by !docs/**
  • docs/roadmap.md is excluded by !docs/**
  • docs/specs/domains/secure-graph-rag.md is excluded by !docs/**
  • docs/tests/domains/secure-graph-rag.md is excluded by !docs/**
📒 Files selected for processing (15)
  • apps/api/src/main/resources/application.yml
  • apps/api/src/test/java/com/orgmemory/api/observability/ProviderLoggingBoundaryTests.java
  • apps/worker/src/main/java/com/orgmemory/worker/graph/GraphIndexingProcessor.java
  • apps/worker/src/main/resources/application.yml
  • apps/worker/src/test/java/com/orgmemory/worker/observability/ProviderLoggingBoundaryTests.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/observability/FailureTolerantGraphRagEventSink.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/observability/GraphRagEventSink.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/observability/FailureTolerantGraphRagEventSinkTests.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphRagKnowledgeRetrievalConfiguration.java
  • integrations/graph-rag-observability/build.gradle.kts
  • integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/ExceptionSanitizingSpanExporter.java
  • integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/SpanExportSanitizationAutoConfiguration.java
  • integrations/graph-rag-observability/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  • integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/ExceptionSanitizingSpanExporterTests.java
  • integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/SpanExportSanitizationAutoConfigurationTests.java
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
**/*

📄 CodeRabbit inference engine (CLAUDE.md)

**/*: Always read the repository guidance and relevant sections of ARCHITECTURE.md; before changing a domain, read its specification, test-coverage document, and binding decision filenames.
Treat the repository as the engineering system of record; current repository and runtime evidence take precedence over chat or Northstar.
Read docs/guidelines/agent-safety.md before retrieval, AI, MCP, permission, upload, graph, or export work. Never commit secrets or customer data.

Files:

  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/observability/FailureTolerantGraphRagEventSink.java
  • integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/SpanExportSanitizationAutoConfiguration.java
  • integrations/graph-rag-observability/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  • apps/api/src/main/resources/application.yml
  • core/src/main/java/com/orgmemory/core/knowledge/GraphRagKnowledgeRetrievalConfiguration.java
  • integrations/graph-rag-observability/build.gradle.kts
  • integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/SpanExportSanitizationAutoConfigurationTests.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/observability/FailureTolerantGraphRagEventSinkTests.java
  • apps/api/src/test/java/com/orgmemory/api/observability/ProviderLoggingBoundaryTests.java
  • apps/worker/src/main/java/com/orgmemory/worker/graph/GraphIndexingProcessor.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/observability/GraphRagEventSink.java
  • apps/worker/src/test/java/com/orgmemory/worker/observability/ProviderLoggingBoundaryTests.java
  • apps/worker/src/main/resources/application.yml
  • integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/ExceptionSanitizingSpanExporterTests.java
  • integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/ExceptionSanitizingSpanExporter.java
**/*.{java,gradle,gradle.kts,properties,yml,yaml}

📄 CodeRabbit inference engine (CLAUDE.md)

Before using unfamiliar Spring Boot 4, Spring Modulith 2, Spring AI 2, or Gradle APIs, consult current official documentation, Context7, and the relevant project verification skill.

Files:

  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/observability/FailureTolerantGraphRagEventSink.java
  • integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/SpanExportSanitizationAutoConfiguration.java
  • apps/api/src/main/resources/application.yml
  • core/src/main/java/com/orgmemory/core/knowledge/GraphRagKnowledgeRetrievalConfiguration.java
  • integrations/graph-rag-observability/build.gradle.kts
  • integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/SpanExportSanitizationAutoConfigurationTests.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/observability/FailureTolerantGraphRagEventSinkTests.java
  • apps/api/src/test/java/com/orgmemory/api/observability/ProviderLoggingBoundaryTests.java
  • apps/worker/src/main/java/com/orgmemory/worker/graph/GraphIndexingProcessor.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/observability/GraphRagEventSink.java
  • apps/worker/src/test/java/com/orgmemory/worker/observability/ProviderLoggingBoundaryTests.java
  • apps/worker/src/main/resources/application.yml
  • integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/ExceptionSanitizingSpanExporterTests.java
  • integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/ExceptionSanitizingSpanExporter.java
**/*.{java,sql}

📄 CodeRabbit inference engine (CLAUDE.md)

Keep ddl-auto=validate and pair every persisted-model change with a Flyway migration.

Files:

  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/observability/FailureTolerantGraphRagEventSink.java
  • integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/SpanExportSanitizationAutoConfiguration.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphRagKnowledgeRetrievalConfiguration.java
  • integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/SpanExportSanitizationAutoConfigurationTests.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/observability/FailureTolerantGraphRagEventSinkTests.java
  • apps/api/src/test/java/com/orgmemory/api/observability/ProviderLoggingBoundaryTests.java
  • apps/worker/src/main/java/com/orgmemory/worker/graph/GraphIndexingProcessor.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/observability/GraphRagEventSink.java
  • apps/worker/src/test/java/com/orgmemory/worker/observability/ProviderLoggingBoundaryTests.java
  • integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/ExceptionSanitizingSpanExporterTests.java
  • integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/ExceptionSanitizingSpanExporter.java
**/*.{java,gradle,gradle.kts}

📄 CodeRabbit inference engine (CLAUDE.md)

Use the testing harness; a terminating clean test is the JVM context gate, and bootRun is not verification. IDE inspection applies only to edited backend Java.

Files:

  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/observability/FailureTolerantGraphRagEventSink.java
  • integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/SpanExportSanitizationAutoConfiguration.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphRagKnowledgeRetrievalConfiguration.java
  • integrations/graph-rag-observability/build.gradle.kts
  • integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/SpanExportSanitizationAutoConfigurationTests.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/observability/FailureTolerantGraphRagEventSinkTests.java
  • apps/api/src/test/java/com/orgmemory/api/observability/ProviderLoggingBoundaryTests.java
  • apps/worker/src/main/java/com/orgmemory/worker/graph/GraphIndexingProcessor.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/observability/GraphRagEventSink.java
  • apps/worker/src/test/java/com/orgmemory/worker/observability/ProviderLoggingBoundaryTests.java
  • integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/ExceptionSanitizingSpanExporterTests.java
  • integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/ExceptionSanitizingSpanExporter.java
core/src/main/java/com/orgmemory/core/{authorization,knowledge,permission}/**/*.java

⚙️ CodeRabbit configuration file

core/src/main/java/com/orgmemory/core/{authorization,knowledge,permission}/**/*.java: Treat PostgreSQL ACL evidence as canonical and OpenFGA as the relationship
authorization decision point. Authorization must fail closed. Filtering
must happen before ranking, LIMIT, graph traversal, answer generation,
export, and citation rendering. Flag metadata or timing leak paths.

Files:

  • core/src/main/java/com/orgmemory/core/knowledge/GraphRagKnowledgeRetrievalConfiguration.java
🔇 Additional comments (11)
components/graph-rag-core/src/main/java/com/orgmemory/graphrag/observability/GraphRagEventSink.java (1)

63-71: LGTM!

components/graph-rag-core/src/main/java/com/orgmemory/graphrag/observability/FailureTolerantGraphRagEventSink.java (1)

26-57: LGTM!

Also applies to: 74-78

components/graph-rag-core/src/test/java/com/orgmemory/graphrag/observability/FailureTolerantGraphRagEventSinkTests.java (1)

21-82: 📐 Maintainability & Code Quality

Verify with the JVM test harness.

No terminating clean test result is included for this new sink behavior. Run the relevant Gradle tests to completion; bootRun is not verification.

As per coding guidelines, “Use the testing harness; a terminating clean test is the JVM context gate, and bootRun is not verification.”

Source: Coding guidelines

apps/worker/src/main/java/com/orgmemory/worker/graph/GraphIndexingProcessor.java (1)

82-83: LGTM!

core/src/main/java/com/orgmemory/core/knowledge/GraphRagKnowledgeRetrievalConfiguration.java (1)

43-44: LGTM!

integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/ExceptionSanitizingSpanExporter.java (1)

41-136: LGTM!

integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/ExceptionSanitizingSpanExporterTests.java (1)

35-112: LGTM!

integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/SpanExportSanitizationAutoConfiguration.java (1)

18-30: LGTM!

integrations/graph-rag-observability/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (1)

2-2: LGTM!

integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/SpanExportSanitizationAutoConfigurationTests.java (1)

30-86: LGTM!

integrations/graph-rag-observability/build.gradle.kts (1)

8-19: 📐 Maintainability & Code Quality

Run the module test harness before merging.

#!/bin/bash
set -euo pipefail
./gradlew :integrations:graph-rag-observability:test --no-daemon

As per coding guidelines, “Use the testing harness; a terminating clean test is the JVM context gate, and bootRun is not verification.”

Comment thread apps/api/src/main/resources/application.yml
Addresses three review findings, one of which corrects a claim this branch made
about its own change.

**The YAML pin is a default, not enforcement.** The previous commit described it
as "deliberately not environment-overridable". That was wrong:
LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_AI_OPENAI, a system property, a logback
configuration, or a level on a more specific logger all outrank
application.yml. Configuration cannot enforce a boundary configuration can undo.

ProviderLoggingBoundaryVerifier therefore checks the resolved state instead of
any source of it: each class holding a payload-logging call site is asked whether
WARN is enabled on its own logger — the exact question its isWarnEnabled() guard
will ask, with inheritance already applied — and startup fails if any answers
yes. Failing beats starting, because the alternative is an application that looks
healthy while writing customer text to disk. No disable property.

**The sink log could still flood.** Reporting on a change of kind meant a backend
alternating between two exceptions logged at event rate, which a timeout retrying
as a connection failure produces immediately. Reporting is now keyed on the set
of kinds seen, bounded at ten, with an A->B->A test.

**The profile scan had two holes.** It skipped `.yaml` files, and it matched only
ancestors of the guarded packages, so a child such as
`org.springframework.ai.openai.api: WARN` under an ERROR parent passed.

The YAML comments and test javadoc that repeated the overclaim now say plainly
that they cover the shipped default and point at the verifier for enforcement.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@kl3inIT
kl3inIT merged commit 1bfd990 into main Jul 29, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant