Skip to content

feat(graph-rag): publish what one answer costs and what the budget refused - #142

Merged
kl3inIT merged 2 commits into
mainfrom
feat/graph-rag-context-token-metrics
Jul 30, 2026
Merged

feat(graph-rag): publish what one answer costs and what the budget refused#142
kl3inIT merged 2 commits into
mainfrom
feat/graph-rag-context-token-metrics

Conversation

@kl3inIT

@kl3inIT kl3inIT commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Phase 2 of the observability increment. Core has always measured the token cost of an assembled context and always evicted contributions that did not fit the budget. Neither number left the process.

What this closes

Cost is invisible. A deployment cannot tell an expensive question from a cheap one, and cannot see a prompt growing toward its ceiling until it hits it.

Truncation is invisible. LightRagGroundingAssembler drops contributions in two places — the per-channel allocator and the total-budget fitting loop. An answer cut down to fit looked exactly like a whole one, in the logs, in the traces and in the metrics.

Shape

GraphRagEvent gains an optional TokenUsage: rendered prompt size, the ceiling it was fitted to, a breakdown across system prompt / query / entity / relation / chunk, and the number of evicted contributions.

Counts only. That is what lets it cross a boundary that refuses queries, prompts and evidence — a number cannot reconstruct the text it measured. The span test asserts this by type rather than by name, because a substring guard would reject query_tokens for what it is called rather than for what it holds.

It is a projection of ContextTokenUsage rather than a reference to it. That record exists to enforce a retrieval budget and is free to change shape for retrieval reasons; a telemetry contract moving with it would rewrite dashboards for a decision that had nothing to do with them.

Existing construction sites keep their arity through an overload, so a stage that measures no tokens says so by omission rather than by a zero that would read as a measurement.

Eviction is counted after merging

Consolidating two copies of one grounding drops nothing the model would have seen — that is deduplication, not budget pressure. Counting it as truncation would have put a permanent false positive on the dashboard. The count is taken after merge and before any budget runs, and a test pins both halves.

I got this wrong first: my initial test asserted that a tight budget over a doubled grounding must evict. It reported zero, correctly, and the test was the thing that was wrong.

Cardinality

Tokens are not tagged by organization. This settles the open question the design left when it asked for a "tokens by organization" board: one series per tenant per channel grows for as long as the product sells, and an unbounded tag is paid for by the metrics backend forever rather than by the request that created it. That board is a span query — the span already carries the organization identifier — or a billing record, which is a product feature rather than a side effect of telemetry.

Truncation gets two meters on purpose. How much context was refused, and how many answers were affected; the second is not recoverable from a sum of the first.

Also

SecureContextBudget.maximumInputTokens() names a rule that was written out three times.

Verification

:core:test, :components:graph-rag-core:test, :components:graph-rag-testkit:test, :integrations:graph-rag-observability:test, plus :apps:worker:compileTestJava and :apps:api:compileTestJava. All green.

Not in scope

finish_reason=length is output-side truncation and this stage cannot see it: ChatModelPort streams Flux<String> and the adapter calls .stream().content(), discarding the ChatResponse that holds the finish reason. It needs a port change and lands with the GENERATE stage.

This is also the first commit since #140 that touches application code, so its deployment finally applies the merged-but-unapplied ORGMEMORY_SERVICE_VERSION / deployment.environment.name fix. I will re-check both on the containers after it deploys.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added detailed token-usage reporting for context assembly, including prompt components, budgets, and dropped contributions.
    • Added truncation indicators when context exceeds the configured token budget.
    • Added context token metrics to Micrometer and OpenTelemetry observability data.
    • Added a public method for retrieving the maximum input-token budget.
  • Bug Fixes

    • Stages without token measurements now omit token usage instead of reporting zero values.
    • Added validation to reject invalid token counts and budgets.

…fused

The assembler has always measured the token cost of an assembled context and
always evicted contributions that did not fit. Nothing published either number,
so a deployment could not tell an expensive question from a cheap one, and an
answer cut down to fit the budget looked exactly like a whole one.

The event gains an optional TokenUsage: the rendered prompt size, the ceiling it
was fitted to, the per-channel breakdown, and how many contributions were
evicted. Counts only, which is what lets it cross a boundary that refuses
queries, prompts and evidence — a number cannot reconstruct the text it
measured. It is a projection of the query model's own token record rather than a
reference to it, so a retrieval-driven change to that record does not rewrite
dashboards.

Eviction is counted after merging, not before. Consolidating two copies of one
grounding drops nothing the model would have seen, and reporting that as
truncation would have put a permanent false positive on the dashboard.

Meters accumulate tokens by channel and summarise the prompt size, so headroom
is readable before truncation starts. Truncation is counted twice on purpose:
how much context was refused, and how many answers were affected — the second is
not recoverable from a sum of the first. Tokens are not tagged by organization,
which settles the design's "tokens by organization" board against a meter and in
favour of a span query; an unbounded tag is paid for by the metrics backend
forever rather than by the request that created it.

Existing construction sites keep their arity through an overload, so a stage
that measures no tokens says so by omission rather than by a zero that would
read as a measurement.

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

coderabbitai Bot commented Jul 30, 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: 50 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: 35cb9248-08e8-47b4-b604-a06bef938200

📥 Commits

Reviewing files that changed from the base of the PR and between 96dadd7 and 690c711.

📒 Files selected for processing (1)
  • integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/MicrometerGraphRagEventSinkTests.java
📝 Walkthrough

Walkthrough

The change adds validated token-usage events, records dropped grounding contributions during budget fitting, emits context assembly telemetry, and exports token metrics through Micrometer and OpenTelemetry integrations with corresponding test coverage.

Changes

Context Token Telemetry

Layer / File(s) Summary
Grounding budget and token contracts
components/graph-rag-core/src/main/java/com/orgmemory/graphrag/observability/GraphRagEventSink.java, components/graph-rag-core/src/main/java/com/orgmemory/graphrag/query/*, components/graph-rag-core/src/test/*, components/graph-rag-testkit/src/test/*
Token usage validation, maximum input-token access, dropped-contribution tracking, and budget-fitting conformance tests are added.
Context assembly event emission
core/src/main/java/com/orgmemory/core/knowledge/GraphRagKnowledgeRetrievalService.java, core/src/test/java/com/orgmemory/core/knowledge/GraphRagKnowledgeRetrievalServiceTests.java
Context assembly emits token breakdowns, budget limits, and dropped-contribution counts while other stages remain without token usage.
Token metrics and span attributes
integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/*, integrations/graph-rag-observability/src/test/*
Micrometer records token counters and truncation metrics, while OpenTelemetry records numeric token attributes; tests cover populated and absent measurements.

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

Sequence Diagram(s)

sequenceDiagram
  participant RetrievalService
  participant GroundingAssembler
  participant EventSink
  participant ObservabilityBackend
  RetrievalService->>GroundingAssembler: Prepare budgeted grounding
  GroundingAssembler-->>RetrievalService: Token counts and dropped contributions
  RetrievalService->>EventSink: Emit ASSEMBLE_CONTEXT TokenUsage
  EventSink->>ObservabilityBackend: Record token metrics and span attributes
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.90% 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 clearly summarizes the main change: adding observability for answer cost and budget-refused context.
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 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/graph-rag-context-token-metrics

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.

@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: 1

🤖 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
`@integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/MicrometerGraphRagEventSinkTests.java`:
- Around line 107-168: Extend
carriesNoIdentifierThatWouldGrowASeriesPerTenantOrRequest to emit an assembled
context event that exercises the new token and truncation meters, rather than
only a plain RETRIEVE event. Ensure the assertions continue checking every meter
in registry.getMeters() for forbidden org or operation tags, covering
CONTEXT_TOKEN_COUNTER, CONTEXT_PROMPT_TOKENS, CONTEXT_DROPPED_COUNTER, and
CONTEXT_TRUNCATION_COUNTER.
🪄 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: 1ea9db0f-ff36-498e-9ea8-34970e83883d

📥 Commits

Reviewing files that changed from the base of the PR and between 22345d2 and 96dadd7.

⛔ Files ignored due to path filters (3)
  • docs/increments/active/2026-07-29-observability-pipeline/plan.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)
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/observability/GraphRagEventSink.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/query/LightRagGroundingAssembler.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/query/SecureContextBudget.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/observability/GraphRagEventSinkTests.java
  • components/graph-rag-testkit/src/test/java/com/orgmemory/graphrag/testkit/LightRagQueryRuntimeConformanceTests.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphRagKnowledgeRetrievalService.java
  • core/src/test/java/com/orgmemory/core/knowledge/GraphRagKnowledgeRetrievalServiceTests.java
  • integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/MicrometerGraphRagEventSink.java
  • integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/OpenTelemetryGraphRagEventSink.java
  • integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/MicrometerGraphRagEventSinkTests.java
  • integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/OpenTelemetryGraphRagEventSinkTests.java
📜 Review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: OpenSearch adapter
  • GitHub Check: Neo4j adapter
  • GitHub Check: PostgreSQL GraphRAG
  • GitHub Check: Backend · Java 25
🧰 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/query/SecureContextBudget.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/observability/GraphRagEventSinkTests.java
  • integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/OpenTelemetryGraphRagEventSinkTests.java
  • integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/OpenTelemetryGraphRagEventSink.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphRagKnowledgeRetrievalService.java
  • integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/MicrometerGraphRagEventSink.java
  • integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/MicrometerGraphRagEventSinkTests.java
  • components/graph-rag-testkit/src/test/java/com/orgmemory/graphrag/testkit/LightRagQueryRuntimeConformanceTests.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/query/LightRagGroundingAssembler.java
  • core/src/test/java/com/orgmemory/core/knowledge/GraphRagKnowledgeRetrievalServiceTests.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/observability/GraphRagEventSink.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/query/SecureContextBudget.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/observability/GraphRagEventSinkTests.java
  • integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/OpenTelemetryGraphRagEventSinkTests.java
  • integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/OpenTelemetryGraphRagEventSink.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphRagKnowledgeRetrievalService.java
  • integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/MicrometerGraphRagEventSink.java
  • integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/MicrometerGraphRagEventSinkTests.java
  • components/graph-rag-testkit/src/test/java/com/orgmemory/graphrag/testkit/LightRagQueryRuntimeConformanceTests.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/query/LightRagGroundingAssembler.java
  • core/src/test/java/com/orgmemory/core/knowledge/GraphRagKnowledgeRetrievalServiceTests.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/observability/GraphRagEventSink.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/query/SecureContextBudget.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/observability/GraphRagEventSinkTests.java
  • integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/OpenTelemetryGraphRagEventSinkTests.java
  • integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/OpenTelemetryGraphRagEventSink.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphRagKnowledgeRetrievalService.java
  • integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/MicrometerGraphRagEventSink.java
  • integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/MicrometerGraphRagEventSinkTests.java
  • components/graph-rag-testkit/src/test/java/com/orgmemory/graphrag/testkit/LightRagQueryRuntimeConformanceTests.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/query/LightRagGroundingAssembler.java
  • core/src/test/java/com/orgmemory/core/knowledge/GraphRagKnowledgeRetrievalServiceTests.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/observability/GraphRagEventSink.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/query/SecureContextBudget.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/observability/GraphRagEventSinkTests.java
  • integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/OpenTelemetryGraphRagEventSinkTests.java
  • integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/OpenTelemetryGraphRagEventSink.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphRagKnowledgeRetrievalService.java
  • integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/MicrometerGraphRagEventSink.java
  • integrations/graph-rag-observability/src/test/java/com/orgmemory/integrations/graphrag/observability/MicrometerGraphRagEventSinkTests.java
  • components/graph-rag-testkit/src/test/java/com/orgmemory/graphrag/testkit/LightRagQueryRuntimeConformanceTests.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/query/LightRagGroundingAssembler.java
  • core/src/test/java/com/orgmemory/core/knowledge/GraphRagKnowledgeRetrievalServiceTests.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/observability/GraphRagEventSink.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/GraphRagKnowledgeRetrievalService.java
🪛 ast-grep (0.45.0)
integrations/graph-rag-observability/src/main/java/com/orgmemory/integrations/graphrag/observability/OpenTelemetryGraphRagEventSink.java

[warning] 118-118: Avoid using untrusted input as a setAttribute() name (trust boundary violation)
Context: span.setAttribute(PROMPT_TOKENS, usage.promptTokens())
Note: [CWE-501] Trust Boundary Violation.

(trust-boundaries-java)


[warning] 119-119: Avoid using untrusted input as a setAttribute() name (trust boundary violation)
Context: span.setAttribute(SYSTEM_PROMPT_TOKENS, usage.systemPromptTokens())
Note: [CWE-501] Trust Boundary Violation.

(trust-boundaries-java)


[warning] 120-120: Avoid using untrusted input as a setAttribute() name (trust boundary violation)
Context: span.setAttribute(QUERY_TOKENS, usage.queryTokens())
Note: [CWE-501] Trust Boundary Violation.

(trust-boundaries-java)


[warning] 121-121: Avoid using untrusted input as a setAttribute() name (trust boundary violation)
Context: span.setAttribute(ENTITY_TOKENS, usage.entityTokens())
Note: [CWE-501] Trust Boundary Violation.

(trust-boundaries-java)


[warning] 122-122: Avoid using untrusted input as a setAttribute() name (trust boundary violation)
Context: span.setAttribute(RELATION_TOKENS, usage.relationTokens())
Note: [CWE-501] Trust Boundary Violation.

(trust-boundaries-java)


[warning] 123-123: Avoid using untrusted input as a setAttribute() name (trust boundary violation)
Context: span.setAttribute(CHUNK_TOKENS, usage.chunkTokens())
Note: [CWE-501] Trust Boundary Violation.

(trust-boundaries-java)


[warning] 124-124: Avoid using untrusted input as a setAttribute() name (trust boundary violation)
Context: span.setAttribute(BUDGET_TOKENS, usage.budgetTokens())
Note: [CWE-501] Trust Boundary Violation.

(trust-boundaries-java)


[warning] 125-125: Avoid using untrusted input as a setAttribute() name (trust boundary violation)
Context: span.setAttribute(DROPPED_CONTRIBUTIONS, usage.droppedContributions())
Note: [CWE-501] Trust Boundary Violation.

(trust-boundaries-java)

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

85-122: LGTM!

Also applies to: 161-218

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

25-32: LGTM!

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

81-86: LGTM!

Also applies to: 110-124, 126-169, 242-269, 591-635

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

35-62: LGTM!

components/graph-rag-testkit/src/test/java/com/orgmemory/graphrag/testkit/LightRagQueryRuntimeConformanceTests.java (1)

295-414: LGTM!

core/src/main/java/com/orgmemory/core/knowledge/GraphRagKnowledgeRetrievalService.java (2)

18-25: LGTM!

Also applies to: 588-632


248-260: 🔒 Security & Privacy

Confirm token/chunk telemetry emitted before final authorization recheck is acceptable.

emitAssembledContext fires right after consolidateGrounding, before the post-consolidation scope re-resolve, evidence-closure-size check, and verifyOpenFga recheck later in this method. The position itself predates this PR (it replaced a generic emitStage call in the same slot), but this change now attaches a much richer payload (per-channel token counts, chunk counts, dropped contributions) at that pre-recheck point. Since a later scope change/canonical-evidence change can still invalidate this request (see revocationBetweenRetrievalAndCitationCausesAFullRetryWithoutEgress), please confirm that emitting this expanded metadata before the authorization freshness recheck is intentional and acceptable for this pipeline.

As per path instructions, "Filtering must happen before ranking, LIMIT, graph traversal, answer generation, export, and citation rendering. Flag metadata or timing leak paths."

Source: Path instructions

core/src/test/java/com/orgmemory/core/knowledge/GraphRagKnowledgeRetrievalServiceTests.java (1)

432-514: LGTM!

Also applies to: 825-850

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

25-44: LGTM!

Also applies to: 76-106

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

46-61: LGTM!

Also applies to: 104-128

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

190-212: LGTM!

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

84-184: LGTM!

The guard loops over every registered meter, but only a plain retrieval event
had been emitted, so the meters this change introduces were never instantiated
and never checked. Verified by mutation: tagging the token counter with
organization_id now fails it.

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

kl3inIT commented Jul 30, 2026

Copy link
Copy Markdown
Owner Author

Applied — the finding is right, and it is the one meter test that mattered here: the guard iterates registry.getMeters(), so a meter it never instantiates is a meter it never guards, and the token counters are exactly where a per-organization tag would be most tempting.

Verified it is not a vacuous assertion by mutation: adding organization_id to CONTEXT_TOKEN_COUNTER fails carriesNoIdentifierThatWouldGrowASeriesPerTenantOrRequest, and reverting it passes.

@kl3inIT
kl3inIT merged commit b4ea630 into main Jul 30, 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