Skip to content

[OPIK-7352] [BE] reject non-UUIDv7 referenced ids on ingest - #7553

Merged
thiagohora merged 8 commits into
mainfrom
thiaghora/OPIK-7352-reject-non-v7-foreign-ids
Jul 22, 2026
Merged

[OPIK-7352] [BE] reject non-UUIDv7 referenced ids on ingest#7553
thiagohora merged 8 commits into
mainfrom
thiaghora/OPIK-7352-reject-non-v7-foreign-ids

Conversation

@thiagohora

@thiagohora thiagohora commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Details

Retention sweeps spans/traces via an id-range predicate that assumes the referenced id is a time-ordered UUIDv7 (SpanDAO orders spans by the trace_id range; TraceDAO by the id range). Ingest validated an entity's own id but not the ids it references, so a client could store e.g. a span with a non-UUIDv7 trace_id and have those rows ordered lexicographically instead of chronologically — deleted or retained against the wrong window. This validates referenced ids on ingest.

  • Scope rule: validate a referenced id iff the operation persists it to the entity's table. Ids used only to resolve/locate an existing association (e.g. a projectId on thread open/close, whose project_id was written at trace ingestion) or as a read source (dataset copy-from ids) are not validated.
  • Referenced ids use a not-in-future policy: version 7 required, future-dated rejected, past allowed (referencing an older entity — a late span on an old trace, a score on an old trace — is legitimate and common). Renames validateIdForUpdatevalidateIdNotInFuture and reuses it for both the update paths and referenced ids; adds null-safe validateIdNotInFutureIfPresent(Async) helpers.
  • Validated (each written to a column by its create/update): span traceId/parentSpanId (create, batch, update, batch-update, experiment-items bulk); feedback-score entity ids (single + batch) + sourceQueueId; trace/span/thread comment entity ids; attachment entity ids; guardrail entityId + secondaryId; assertion entityId; dataset-item traceId/spanId + batch datasetId; annotation-queue item ids; experiment projectId/optimizationId/datasetVersionId; and projectId on span/trace update + batch-update, optimization, alert, dashboard, prompt (+ version), annotation-queue, guardrail, assertion, feedback batch, automation-rule evaluators.
  • Version enforcement is always on (mirrors own-id validation); the not-in-future check is gated by the existing uuidValidation kill-switch (off in prod), so this ships dormant beyond the version check.
  • Out of scope / tracked separately: some clients emit v7 UUIDs whose embedded timestamp is far in the future; that pre-existing client-side issue is not remediated here for existing rows.

Change checklist

  • User facing
  • Documentation update

Issues

  • OPIK-7352

AI-WATERMARK

AI-WATERMARK: yes

  • Tools: Claude Code
  • Model(s): Claude Opus 4.8
  • Scope: implementation, prod data verification, and tests
  • Human verification: pending review

Testing

  • mvn -o compile / test-compile / spotless:apply clean.
  • Added SpansResourceTest cases (single and batch create): reject non-v7 traceId (400), reject future-dated traceId (400), reject non-v7 parentSpanId (400), and accept an old (past) v7 traceId (spans on old traces are valid). These exercise the shared validateIdNotInFuture mechanism used by every path here.
  • Resource integration tests do not run locally (ClickHouse healthcheck bootstrap quirk) — relying on CI.
  • Verified via read-only production queries that existing rows carry no non-v7 referenced ids on the validated paths, so enforcement rejects no legitimate existing data.
  • Follow-up: dedicated per-path tests for the non-span validated paths (they share the span-tested mechanism).

Documentation

No user-facing docs. The referenced-id validation policy (version 7 + not-in-future, past allowed) is documented in the IdGenerator javadoc.

thiagohora and others added 2 commits July 21, 2026 16:00
Retention sweeps spans/traces via an id-range predicate that assumes the
referenced id is a time-ordered UUIDv7 (SpanDAO orders by the trace_id
range; TraceDAO by the id range). Ingest validated an entity's own id but
not the ids it references, so a client could store a span with a
non-UUIDv7 trace_id and have those rows ordered lexicographically instead
of chronologically — deleted or retained against the wrong window.

Validate referenced ids on ingest with a not-in-future policy (version 7
required, future-dated rejected, past allowed — referencing older
entities such as late spans on old traces is legitimate). Renames
validateIdForUpdate -> validateIdNotInFuture and reuses it for both the
update paths and referenced ids; adds a public sync overload.

Covered paths: span traceId/parentSpanId (create, batch, update,
batch-update, and the experiment-items bulk path via batch create); single
and batch feedback-score entity ids; trace/span/thread comment entity ids;
attachment entity ids; dataset-item trace/span ids (batch, patch,
from-traces, from-spans); annotation-queue item ids.

Version enforcement is always on (mirrors own-id validation); the
not-in-future check is gated by the existing uuidValidation kill-switch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extends referenced-id validation to config-entity references (verify-then-
enforce: confirmed zero non-UUIDv7 ids across prod projects, datasets,
dataset_versions, prompts, prompt_versions, alerts, dashboards, webhooks,
automation_rules, and guardrails, so enforcement rejects no existing data).

Adds null-safe validateIdNotInFutureIfPresent(Async) helpers and validates:
projectId across span/trace update + batch-update, feedback scores (single
+ batch), guardrails, assertion results, annotation queues, experiments,
optimizations, alerts, automation-rule evaluators, prompts + prompt
versions, dashboards, and thread open/close; feedback/guardrail
sourceQueueId; guardrail secondaryId; experiment optimizationId +
datasetVersionId; dataset-item datasetId + copy-from ids. Upgrades the
guardrail/assertion referenced entityId from version-only to the shared
not-in-future policy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@thiagohora
thiagohora requested a review from a team as a code owner July 21, 2026 14:30
@github-actions github-actions Bot added java Pull requests that update Java code Backend tests Including test files, or tests related like configuration. 🟡 size/M labels Jul 21, 2026
@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

⏱️ pre-commit per-hook timing

Hook Description Result Duration
☕ spotless — java backend Format Java code 7.93s
Total (1 ran) 7.93s
⏭️ 40 skipped (no matching files changed)
Hook Description Result
🐍 trim trailing whitespace — python sdk Strip trailing whitespace ⏭️
🐍 fix end of files — python sdk Ensure files end in a newline ⏭️
🐍 ruff — python sdk Lint + autofix Python (ruff) ⏭️
🐍 ruff-format — python sdk Format Python code (ruff) ⏭️
🐍 mypy — python sdk Static type check ⏭️
🤖 trim trailing whitespace — optimizer Strip trailing whitespace ⏭️
🤖 fix end of files — optimizer Ensure files end in a newline ⏭️
🤖 check yaml — optimizer Validate YAML syntax ⏭️
🤖 check json — optimizer Validate JSON syntax ⏭️
🤖 check toml — optimizer Validate TOML syntax ⏭️
🤖 check for added large files — optimizer Block large files (>1MB) ⏭️
🔐 detect private key — optimizer Block committed private keys ⏭️
🤖 check for merge conflicts — optimizer Block merge-conflict markers ⏭️
🤖 check for case conflicts — optimizer Block case-only name clashes ⏭️
🤖 pyupgrade — optimizer Modernize Python syntax ⏭️
🤖 ruff — optimizer Lint + autofix Python (ruff) ⏭️
🤖 ruff-format — optimizer Format Python code (ruff) ⏭️
🤖 mypy — optimizer Static type check ⏭️
📓 nbstripout — optimizer notebooks Strip notebook output ⏭️
📝 markdownlint — optimizer Lint Markdown ⏭️
🔤 codespell — optimizer Fix common misspellings ⏭️
📊 radon cc — optimizer Cyclomatic-complexity gate ⏭️
📊 radon raw — optimizer Raw size metrics gate ⏭️
📊 xenon — optimizer Fail on complexity thresholds ⏭️
📊 lizard — optimizer Cyclomatic-complexity gate ⏭️
🧹 vulture — optimizer Find dead code ⏭️
🛡️ trim trailing whitespace — guardrails Strip trailing whitespace ⏭️
🛡️ fix end of files — guardrails Ensure files end in a newline ⏭️
🛡️ ruff — guardrails Lint + autofix Python (ruff) ⏭️
🛡️ ruff-format — guardrails Format Python code (ruff) ⏭️
🛡️ mypy — guardrails Static type check ⏭️
⚓ helm-docs Regenerate Helm chart README ⏭️
block non-public FE plugins Block non-public FE plugins ⏭️
🧪 pre-commit wrapper smoke tests Self-test the wrapper scripts ⏭️
🌐 eslint — frontend Lint + autofix JS/TS ⏭️
🌐 typecheck — frontend Whole-project tsc type check ⏭️
📘 eslint — typescript sdk Lint + autofix JS/TS ⏭️
📘 typecheck — typescript sdk Whole-project tsc type check ⏭️
⚙️ actionlint — github workflows Lint GitHub Actions workflows ⏭️
🐳 hadolint — dockerfiles Lint Dockerfiles ⏭️

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Backend Tests - Integration Group 6

184 tests  +9   181 ✅ +9   3m 22s ⏱️ -6s
 40 suites +5     3 💤 ±0 
 40 files   +5     0 ❌ ±0 

Results for commit 7df5573. ± Comparison against base commit 4dc6b0c.

This pull request removes 10 and adds 19 tests. Note that renamed tests count towards both.
com.comet.opik.api.resources.v1.priv.PromptResourceProjectScopedPromptsTest ‑ createPromptWithExistingProjectName
com.comet.opik.api.resources.v1.priv.PromptResourceProjectScopedPromptsTest ‑ createPromptWithNonExistingProjectId
com.comet.opik.api.resources.v1.priv.PromptResourceProjectScopedPromptsTest ‑ createPromptWithNonExistingProjectName
com.comet.opik.api.resources.v1.priv.PromptResourceProjectScopedPromptsTest ‑ createPromptWithProjectId
com.comet.opik.api.resources.v1.priv.PromptResourceProjectScopedPromptsTest ‑ findPromptsByProjectId
com.comet.opik.domain.DeletionEventTest ‑ deleteBatchTracesCapturesDeletionEvents(boolean)[1]
com.comet.opik.domain.DeletionEventTest ‑ deleteBatchTracesCapturesDeletionEvents(boolean)[2]
com.comet.opik.domain.DeletionEventTest ‑ deleteTraceCapturesDeletionEvent
com.comet.opik.domain.DeletionEventTest ‑ deleteTraceThreadsCapturesDeletionEvents
com.comet.opik.domain.OptimizationProjectMigrationJobTest ‑ scheduledJobMigratesOrphanOptimizationViaExperiments
com.comet.opik.api.resources.v1.priv.InsightsViewsResourceTest$CreateInsightsView ‑ createInsightsViewForcesScope
com.comet.opik.api.resources.v1.priv.InsightsViewsResourceTest$CreateInsightsView ‑ createInsightsViewWithDefaultType
com.comet.opik.api.resources.v1.priv.InsightsViewsResourceTest$DeleteInsightsView ‑ batchDeleteInsightsViews
com.comet.opik.api.resources.v1.priv.InsightsViewsResourceTest$DeleteInsightsView ‑ deleteInsightsView
com.comet.opik.api.resources.v1.priv.InsightsViewsResourceTest$DeleteInsightsView ‑ deleteInsightsViewDoesNotAffectDashboard
com.comet.opik.api.resources.v1.priv.InsightsViewsResourceTest$FindInsightsViews ‑ findInsightsViewsOnlyReturnsInsightsScope
com.comet.opik.api.resources.v1.priv.InsightsViewsResourceTest$GetInsightsView ‑ getInsightsViewById
com.comet.opik.api.resources.v1.priv.InsightsViewsResourceTest$GetInsightsView ‑ getInsightsViewReturns404ForDashboard
com.comet.opik.api.resources.v1.priv.InsightsViewsResourceTest$UpdateInsightsView ‑ updateInsightsViewName
com.comet.opik.api.resources.v1.priv.InsightsViewsResourceTest$UpdateInsightsView ‑ updateInsightsViewReturns404ForDashboard
…

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Backend Tests - Integration Group 12

297 tests   - 4   297 ✅  - 4   6m 2s ⏱️ -1s
 48 suites +1     0 💤 ±0 
 48 files   +1     0 ❌ ±0 

Results for commit f1e48d3. ± Comparison against base commit 4dc6b0c.

This pull request removes 51 and adds 47 tests. Note that renamed tests count towards both.
com.comet.opik.api.resources.v1.events.OnlineScoringSpanSamplerIntegrationTest ‑ redisProducerAndConsumerBaseFlowForSpans(Source, OnlineScoringSpanSampler)[1]
com.comet.opik.api.resources.v1.events.OnlineScoringSpanSamplerIntegrationTest ‑ redisProducerAndConsumerBaseFlowForSpans(Source, OnlineScoringSpanSampler)[2]
com.comet.opik.api.resources.v1.priv.RecentActivityResourceTest$GetRecentActivity ‑ doesNotReturnActivityFromOtherProjects
com.comet.opik.api.resources.v1.priv.RecentActivityResourceTest$GetRecentActivity ‑ returnsAllEntityTypesSortedByDate
com.comet.opik.api.resources.v1.priv.RecentActivityResourceTest$GetRecentActivity ‑ returnsEmptyForNewProject
com.comet.opik.api.resources.v1.priv.RecentActivityResourceTest$RequiredPermissionsTest ‑ passesRequiredPermissionsToAuthEndpoint
com.comet.opik.api.resources.v1.priv.RecentActivityResourceTest$RequiredPermissionsTest ‑ returnsForbiddenWhenPermissionDenied
com.comet.opik.domain.LocalRunnerReaperIntegrationTest$ReapDeadRunners ‑ cleansRunnerJobsSetOnReap
com.comet.opik.domain.LocalRunnerReaperIntegrationTest$ReapDeadRunners ‑ failsOrphanedActiveJobs
com.comet.opik.domain.LocalRunnerReaperIntegrationTest$ReapDeadRunners ‑ failsOrphanedPendingJobs
…
com.comet.opik.api.resources.v1.internal.UsageResourceTest$Usage ‑ datasetBiInfoTest
com.comet.opik.api.resources.v1.internal.UsageResourceTest$Usage ‑ experimentBiInfoTest
com.comet.opik.api.resources.v1.internal.UsageResourceTest$Usage ‑ mixedWorkspaceExcludesDemoData
com.comet.opik.api.resources.v1.internal.UsageResourceTest$Usage ‑ spanBiInfoTest
com.comet.opik.api.resources.v1.internal.UsageResourceTest$Usage ‑ spanBreakdownForWorkspace
com.comet.opik.api.resources.v1.internal.UsageResourceTest$Usage ‑ spansCountExcludingDemoData
com.comet.opik.api.resources.v1.internal.UsageResourceTest$Usage ‑ spansCountForWorkspace
com.comet.opik.api.resources.v1.internal.UsageResourceTest$Usage ‑ spansCountIncludesPostCutoffActivityInDemoProjects
com.comet.opik.api.resources.v1.internal.UsageResourceTest$Usage ‑ traceBiInfoTest
com.comet.opik.api.resources.v1.internal.UsageResourceTest$Usage ‑ tracesCountExcludingDemoData
…

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Backend Tests - Integration Group 13

325 tests   - 17   323 ✅  - 17   4m 37s ⏱️ - 1m 1s
 43 suites  -  3     2 💤 ± 0 
 43 files    -  3     0 ❌ ± 0 

Results for commit f1e48d3. ± Comparison against base commit 4dc6b0c.

This pull request removes 39 and adds 22 tests. Note that renamed tests count towards both.
com.comet.opik.api.resources.v1.internal.AnalyticsQueriesResourceTest ‑ executeQuery__whenBoundToWorkspaceProject__thenReturnsOnlyThatScope
com.comet.opik.api.resources.v1.internal.AnalyticsQueriesResourceTest ‑ executeQuery__whenInvalid__thenBadRequest(String, String)[1]
com.comet.opik.api.resources.v1.internal.AnalyticsQueriesResourceTest ‑ executeQuery__whenInvalid__thenBadRequest(String, String)[2]
com.comet.opik.api.resources.v1.internal.AnalyticsQueriesResourceTest ‑ executeQuery__whenInvalid__thenBadRequest(String, String)[3]
com.comet.opik.api.resources.v1.internal.AnalyticsQueriesResourceTest ‑ executeQuery__whenInvalid__thenBadRequest(String, String)[4]
com.comet.opik.api.resources.v1.internal.AnalyticsQueriesResourceTest ‑ executeQuery__whenInvalid__thenBadRequest(String, String)[5]
com.comet.opik.api.resources.v1.internal.AnalyticsQueriesResourceTest ‑ executeQuery__whenInvalid__thenBadRequest(String, String)[6]
com.comet.opik.api.resources.v1.internal.AnalyticsQueriesResourceTest ‑ executeQuery__whenInvalid__thenBadRequest(String, String)[7]
com.comet.opik.api.resources.v1.internal.AnalyticsQueriesResourceTest ‑ executeQuery__whenInvalid__thenBadRequest(String, String)[8]
com.comet.opik.api.resources.v1.internal.AnalyticsQueriesResourceTest ‑ executeQuery__whenInvalid__thenBadRequest(String, String)[9]
…
com.comet.opik.api.resources.v1.jobs.ProjectLastUpdatedFlushJobTest ‑ jobFlushesBufferedMarkerToProject
com.comet.opik.api.resources.v1.priv.AssertionResultsResourceTest ‑ multiProjectBatchResolvesIndependently
com.comet.opik.api.resources.v1.priv.AssertionResultsResourceTest ‑ nonV7EntityIdIsRejected
com.comet.opik.api.resources.v1.priv.AssertionResultsResourceTest ‑ traceAssertionsArePersistedAndRetrievableViaExperimentItems
com.comet.opik.api.resources.v1.priv.AssertionResultsResourceTest ‑ unsupportedEntityTypeIsRejected(EntityType)[1]
com.comet.opik.api.resources.v1.priv.AttachmentResourceTest ‑ directS3DownloadShouldFailTest
com.comet.opik.api.resources.v1.priv.AttachmentResourceTest ‑ directS3UploadShouldFailTest
com.comet.opik.api.resources.v1.priv.AttachmentResourceTest ‑ uploadAttachmentWithMultiPartPresignUrl
com.comet.opik.api.resources.v1.priv.DatasetExperimentE2ETest$FilterDatasetsByExperimentWith ‑ when__filteringByDatasetsWithExperimentsAfterAnExperimentIsDeleted__thenShouldReturnTheDatasetWithExperiments
com.comet.opik.api.resources.v1.priv.DatasetExperimentE2ETest$FilterDatasetsByExperimentWith ‑ when__filteringByDatasetsWithExperimentsAfterDeletingExperimentsButDatasetHasMore__thenShouldReturnTheDatasetWithExperiments
…

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Backend Tests - Integration Group 8

370 tests  +1   368 ✅  - 1   5m 16s ⏱️ - 2m 33s
 31 suites ±0     2 💤 +2 
 31 files   ±0     0 ❌ ±0 

Results for commit f1e48d3. ± Comparison against base commit 4dc6b0c.

This pull request removes 70 and adds 71 tests. Note that renamed tests count towards both.
com.comet.opik.api.resources.v1.priv.DatasetsJsonUploadResourceTest ‑ uploadEmptyFile__rejected
com.comet.opik.api.resources.v1.priv.DatasetsJsonUploadResourceTest ‑ uploadEmptyJsonArray__rejected
com.comet.opik.api.resources.v1.priv.DatasetsJsonUploadResourceTest ‑ uploadGarbageAsJson__rejected
com.comet.opik.api.resources.v1.priv.DatasetsJsonUploadResourceTest ‑ uploadGarbageAsJsonl__rejected
com.comet.opik.api.resources.v1.priv.DatasetsJsonUploadResourceTest ‑ uploadJsonArray__invalidId__failsAsync
com.comet.opik.api.resources.v1.priv.DatasetsJsonUploadResourceTest ‑ uploadJsonArray__lowercaseFormat__accepted
com.comet.opik.api.resources.v1.priv.DatasetsJsonUploadResourceTest ‑ uploadJsonArray__nonObjectElement__failsAsync
com.comet.opik.api.resources.v1.priv.DatasetsJsonUploadResourceTest ‑ uploadJsonArray__preservesJsonTypes
com.comet.opik.api.resources.v1.priv.DatasetsJsonUploadResourceTest ‑ uploadJsonArray__reservedFieldsRouted
com.comet.opik.api.resources.v1.priv.DatasetsJsonUploadResourceTest ‑ uploadJsonArray__withBom
…
com.comet.opik.api.resources.v1.events.webhooks.AlertBucketServiceTest ‑ addEventToBucket__whenAddingToSameAlertAfterConfigChange__shouldUseOriginalWindow
com.comet.opik.api.resources.v1.events.webhooks.AlertBucketServiceTest ‑ addEventToBucket__whenConfigChanges__shouldCreateSeparateBucketsWithDifferentWindows
com.comet.opik.api.resources.v1.events.webhooks.AlertBucketServiceTest ‑ addEventToBucket__whenFirstEvent__shouldSetTtl
com.comet.opik.api.resources.v1.events.webhooks.AlertBucketServiceTest ‑ addEventToBucket__whenFirstEvent__shouldStoreWindowSizeFirstSeenAndWorkspaceId
com.comet.opik.api.resources.v1.events.webhooks.AlertBucketServiceTest ‑ addEventToBucket__whenSubsequentEvents__shouldNotRefreshTtl
com.comet.opik.api.resources.v1.events.webhooks.AlertBucketServiceTest ‑ addEventToBucket__whenSubsequentEvents__shouldPreserveOriginalWindowSize
com.comet.opik.api.resources.v1.events.webhooks.AlertBucketServiceTest ‑ deleteBucket__shouldRemoveBucketFromRedis
com.comet.opik.api.resources.v1.events.webhooks.AlertBucketServiceTest ‑ getBucketData__shouldReturnAllEventIdsAndPayloads
com.comet.opik.api.resources.v1.events.webhooks.AlertBucketServiceTest ‑ getBucketsReadyToProcess__afterConfigChange__shouldUseStoredWindowSizes
com.comet.opik.api.resources.v1.internal.AnalyticsQueriesResourceTest ‑ executeQuery__whenBoundToWorkspaceProject__thenReturnsOnlyThatScope
…

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Backend Tests - Integration Group 16

212 tests  +13   212 ✅ +13   5m 34s ⏱️ - 1m 51s
 26 suites ± 0     0 💤 ± 0 
 26 files   ± 0     0 ❌ ± 0 

Results for commit f1e48d3. ± Comparison against base commit 4dc6b0c.

This pull request removes 17 and adds 30 tests. Note that renamed tests count towards both.
com.comet.opik.api.resources.v1.priv.AttachmentResourceMinIOTest ‑ deleteTraceDeletesTraceAndSpanAttachments(Consumer)[1]
com.comet.opik.api.resources.v1.priv.AttachmentResourceMinIOTest ‑ deleteTraceDeletesTraceAndSpanAttachments(Consumer)[2]
com.comet.opik.api.resources.v1.priv.AttachmentResourceMinIOTest ‑ invalidBaseUrlFormatReturnsError
com.comet.opik.api.resources.v1.priv.AttachmentResourceMinIOTest ‑ uploadAttachmentWithMultiPartPresignUrl
com.comet.opik.api.resources.v1.priv.LlmProviderApiKeyResourceBuiltinProviderTest ‑ testBatchDelete_ignoresBuiltinProvider
com.comet.opik.api.resources.v1.priv.LlmProviderApiKeyResourceBuiltinProviderTest ‑ testFindProviders_builtinProviderHasCorrectConfiguration
com.comet.opik.api.resources.v1.priv.LlmProviderApiKeyResourceBuiltinProviderTest ‑ testFindProviders_builtinProviderHasReadOnlyTrue
com.comet.opik.api.resources.v1.priv.LlmProviderApiKeyResourceBuiltinProviderTest ‑ testFindProviders_builtinProviderIsAddedAtEnd
com.comet.opik.api.resources.v1.priv.LlmProviderApiKeyResourceBuiltinProviderTest ‑ testFindProviders_includesVirtualBuiltinProvider_whenEnabled
com.comet.opik.api.resources.v1.priv.LlmProviderApiKeyResourceBuiltinProviderTest ‑ testFindProviders_userProvidersHaveReadOnlyFalse
…
com.comet.opik.api.resources.v1.events.WebhookSubscriberLoggingTest ‑ processEvent_whenSuccessfulWebhook_shouldSendRequestAndCreateLogs
com.comet.opik.api.resources.v1.events.WebhookSubscriberLoggingTest ‑ processEvent_whenWebhookFails_shouldRetryAndCreateErrorLogs
com.comet.opik.api.resources.v1.priv.DashboardsResourceFindProjectDashboardsTest ‑ findProjectDashboardsByNameSearch
com.comet.opik.api.resources.v1.priv.DashboardsResourceFindProjectDashboardsTest ‑ findProjectDashboardsNoFiltersReturnsAll
com.comet.opik.api.resources.v1.priv.DashboardsResourceFindProjectDashboardsTest ‑ findProjectDashboardsWithDefaultParameters
com.comet.opik.api.resources.v1.priv.DashboardsResourceFindProjectDashboardsTest ‑ findProjectDashboardsWithEmptyResult
com.comet.opik.api.resources.v1.priv.DashboardsResourceFindProjectDashboardsTest ‑ findProjectDashboardsWithFilters(Function, Function)[1]
com.comet.opik.api.resources.v1.priv.DashboardsResourceFindProjectDashboardsTest ‑ findProjectDashboardsWithFilters(Function, Function)[2]
com.comet.opik.api.resources.v1.priv.DashboardsResourceFindProjectDashboardsTest ‑ findProjectDashboardsWithFilters(Function, Function)[3]
com.comet.opik.api.resources.v1.priv.DashboardsResourceFindProjectDashboardsTest ‑ findProjectDashboardsWithPagination
…

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Backend Tests - Integration Group 7

1 584 tests   - 7   1 584 ✅  - 7   6m 57s ⏱️ -20s
   14 suites  - 5       0 💤 ±0 
   14 files    - 5       0 ❌ ±0 

Results for commit f1e48d3. ± Comparison against base commit 4dc6b0c.

This pull request removes 19 and adds 12 tests. Note that renamed tests count towards both.
com.comet.opik.api.resources.v1.priv.InsightsViewsResourceTest$CreateInsightsView ‑ createInsightsViewForcesScope
com.comet.opik.api.resources.v1.priv.InsightsViewsResourceTest$CreateInsightsView ‑ createInsightsViewWithDefaultType
com.comet.opik.api.resources.v1.priv.InsightsViewsResourceTest$DeleteInsightsView ‑ batchDeleteInsightsViews
com.comet.opik.api.resources.v1.priv.InsightsViewsResourceTest$DeleteInsightsView ‑ deleteInsightsView
com.comet.opik.api.resources.v1.priv.InsightsViewsResourceTest$DeleteInsightsView ‑ deleteInsightsViewDoesNotAffectDashboard
com.comet.opik.api.resources.v1.priv.InsightsViewsResourceTest$FindInsightsViews ‑ findInsightsViewsOnlyReturnsInsightsScope
com.comet.opik.api.resources.v1.priv.InsightsViewsResourceTest$GetInsightsView ‑ getInsightsViewById
com.comet.opik.api.resources.v1.priv.InsightsViewsResourceTest$GetInsightsView ‑ getInsightsViewReturns404ForDashboard
com.comet.opik.api.resources.v1.priv.InsightsViewsResourceTest$UpdateInsightsView ‑ updateInsightsViewName
com.comet.opik.api.resources.v1.priv.InsightsViewsResourceTest$UpdateInsightsView ‑ updateInsightsViewReturns404ForDashboard
…
com.comet.opik.api.resources.v1.priv.GuardrailsResourceTest ‑ getTraceStats_containsGuardrails
com.comet.opik.api.resources.v1.priv.GuardrailsResourceTest ‑ testCreateGuardrails_findTraces
com.comet.opik.api.resources.v1.priv.GuardrailsResourceTest ‑ testCreateGuardrails_getTraceById
com.comet.opik.api.resources.v1.priv.PromptResourceProjectScopedPromptsTest ‑ createPromptWithExistingProjectName
com.comet.opik.api.resources.v1.priv.PromptResourceProjectScopedPromptsTest ‑ createPromptWithNonExistingProjectId
com.comet.opik.api.resources.v1.priv.PromptResourceProjectScopedPromptsTest ‑ createPromptWithNonExistingProjectName
com.comet.opik.api.resources.v1.priv.PromptResourceProjectScopedPromptsTest ‑ createPromptWithProjectId
com.comet.opik.api.resources.v1.priv.PromptResourceProjectScopedPromptsTest ‑ findPromptsByProjectId
com.comet.opik.domain.DeletionEventTest ‑ deleteBatchTracesCapturesDeletionEvents(boolean)[1]
com.comet.opik.domain.DeletionEventTest ‑ deleteBatchTracesCapturesDeletionEvents(boolean)[2]
…

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Backend Tests - Integration Group 1

426 tests  ±0   426 ✅ ±0   10m 32s ⏱️ -42s
 25 suites ±0     0 💤 ±0 
 25 files   ±0     0 ❌ ±0 

Results for commit 7a47cac. ± Comparison against base commit 4dc6b0c.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Backend Tests - Integration Group 14

650 tests  ±0   650 ✅ ±0   7m 5s ⏱️ -10s
 22 suites +2     0 💤 ±0 
 22 files   +2     0 ❌ ±0 

Results for commit f1e48d3. ± Comparison against base commit 4dc6b0c.

This pull request removes 41 and adds 41 tests. Note that renamed tests count towards both.
com.comet.opik.api.resources.v1.priv.OllieStateResourceTest ‑ deleteSucceedsWhenNoState
com.comet.opik.api.resources.v1.priv.OllieStateResourceTest ‑ downloadReturns404WhenNoState
com.comet.opik.api.resources.v1.priv.OllieStateResourceTest ‑ uploadDownloadDeleteOllieState
com.comet.opik.api.resources.v1.priv.OllieStateResourceTest ‑ uploadRejectsNonGzipData
com.comet.opik.api.resources.v1.priv.OllieStateResourceTest ‑ uploadReplacesExistingState
com.comet.opik.domain.AlertProjectMigrationServiceTest ‑ allProjectsDeletedAlertAssignedToDefault
com.comet.opik.domain.AlertProjectMigrationServiceTest ‑ envExcludedWorkspacesAreNotMigrated
com.comet.opik.domain.AlertProjectMigrationServiceTest ‑ malformedScopeProjectIdTreatedAsWorkspaceWide
com.comet.opik.domain.AlertProjectMigrationServiceTest ‑ missingDefaultProjectIsAutoCreatedDuringMigration
com.comet.opik.domain.AlertProjectMigrationServiceTest ‑ multiProjectAlertSplitByProject
…
com.comet.opik.api.resources.v1.events.OnlineScoringSpanSamplerIntegrationTest ‑ redisProducerAndConsumerBaseFlowForSpans(Source, OnlineScoringSpanSampler)[1]
com.comet.opik.api.resources.v1.events.OnlineScoringSpanSamplerIntegrationTest ‑ redisProducerAndConsumerBaseFlowForSpans(Source, OnlineScoringSpanSampler)[2]
com.comet.opik.api.resources.v1.priv.DashboardsResourceProjectScopedTest ‑ createDashboardWithExistingProjectName
com.comet.opik.api.resources.v1.priv.DashboardsResourceProjectScopedTest ‑ createDashboardWithNonExistingProjectId
com.comet.opik.api.resources.v1.priv.DashboardsResourceProjectScopedTest ‑ createDashboardWithNonExistingProjectName
com.comet.opik.api.resources.v1.priv.DashboardsResourceProjectScopedTest ‑ createDashboardWithProjectId
com.comet.opik.api.resources.v1.priv.DashboardsResourceProjectScopedTest ‑ findDashboardsByProjectId
com.comet.opik.api.resources.v1.priv.DatasetsJsonUploadResourceTest ‑ uploadEmptyFile__rejected
com.comet.opik.api.resources.v1.priv.DatasetsJsonUploadResourceTest ‑ uploadEmptyJsonArray__rejected
com.comet.opik.api.resources.v1.priv.DatasetsJsonUploadResourceTest ‑ uploadGarbageAsJson__rejected
…

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Backend Tests - Integration Group 2

282 tests  ±0   282 ✅ ±0   13m 27s ⏱️ -22s
 20 suites ±0     0 💤 ±0 
 20 files   ±0     0 ❌ ±0 

Results for commit 7df5573. ± Comparison against base commit 4dc6b0c.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Backend Tests - Integration Group 11

 39 files  + 1   39 suites  +1   7m 56s ⏱️ -33s
306 tests  - 10  306 ✅  - 10  0 💤 ±0  0 ❌ ±0 
268 runs   - 48  268 ✅  - 48  0 💤 ±0  0 ❌ ±0 

Results for commit 7df5573. ± Comparison against base commit 4dc6b0c.

This pull request removes 15 and adds 5 tests. Note that renamed tests count towards both.
com.comet.opik.api.resources.v1.internal.UsageResourceTest$Usage ‑ datasetBiInfoTest
com.comet.opik.api.resources.v1.internal.UsageResourceTest$Usage ‑ experimentBiInfoTest
com.comet.opik.api.resources.v1.internal.UsageResourceTest$Usage ‑ mixedWorkspaceExcludesDemoData
com.comet.opik.api.resources.v1.internal.UsageResourceTest$Usage ‑ spanBiInfoTest
com.comet.opik.api.resources.v1.internal.UsageResourceTest$Usage ‑ spanBreakdownForWorkspace
com.comet.opik.api.resources.v1.internal.UsageResourceTest$Usage ‑ spansCountExcludingDemoData
com.comet.opik.api.resources.v1.internal.UsageResourceTest$Usage ‑ spansCountForWorkspace
com.comet.opik.api.resources.v1.internal.UsageResourceTest$Usage ‑ spansCountIncludesPostCutoffActivityInDemoProjects
com.comet.opik.api.resources.v1.internal.UsageResourceTest$Usage ‑ traceBiInfoTest
com.comet.opik.api.resources.v1.internal.UsageResourceTest$Usage ‑ tracesCountExcludingDemoData
…
com.comet.opik.domain.OptimizationProjectMigrationJobTest ‑ scheduledJobMigratesOrphanOptimizationViaExperiments
com.comet.opik.infrastructure.bi.DailyUsageReportJobTest$CredentialsEnabledScenario ‑ test
com.comet.opik.infrastructure.bi.DailyUsageReportJobTest$NoCredentialsEnabledScenario ‑ dailyUsageReportExcludesDemoData
com.comet.opik.infrastructure.bi.DailyUsageReportJobTest$NoCredentialsEnabledScenario ‑ dailyUsageReportMixedDataExcludesOnlyDemo
com.comet.opik.infrastructure.bi.DailyUsageReportJobTest$NoCredentialsEnabledScenario ‑ test

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Backend Tests - Integration Group 9

520 tests  +16   519 ✅ +16   10m 25s ⏱️ -29s
 40 suites  -  1     1 💤 ± 0 
 40 files    -  1     0 ❌ ± 0 

Results for commit 7df5573. ± Comparison against base commit 4dc6b0c.

This pull request removes 17 and adds 33 tests. Note that renamed tests count towards both.
com.comet.opik.api.resources.v1.priv.AttachmentResourceTest ‑ directS3DownloadShouldFailTest
com.comet.opik.api.resources.v1.priv.AttachmentResourceTest ‑ directS3UploadShouldFailTest
com.comet.opik.api.resources.v1.priv.AttachmentResourceTest ‑ uploadAttachmentWithMultiPartPresignUrl
com.comet.opik.api.resources.v1.priv.DashboardsResourceProjectScopedTest ‑ createDashboardWithExistingProjectName
com.comet.opik.api.resources.v1.priv.DashboardsResourceProjectScopedTest ‑ createDashboardWithNonExistingProjectId
com.comet.opik.api.resources.v1.priv.DashboardsResourceProjectScopedTest ‑ createDashboardWithNonExistingProjectName
com.comet.opik.api.resources.v1.priv.DashboardsResourceProjectScopedTest ‑ createDashboardWithProjectId
com.comet.opik.api.resources.v1.priv.DashboardsResourceProjectScopedTest ‑ findDashboardsByProjectId
com.comet.opik.api.resources.v1.priv.ProjectOptimizationsResourceTest$RequiredPermissionsTest ‑ findOptimizationsByProjectPassesRequiredPermissionsToAuthEndpoint
com.comet.opik.api.resources.v1.priv.ProjectOptimizationsResourceTest$RequiredPermissionsTest ‑ findOptimizationsByProjectReturnsForbiddenWhenPermissionDenied
…
com.comet.opik.api.resources.v1.priv.AssertionResultsResourceTest ‑ multiProjectBatchResolvesIndependently
com.comet.opik.api.resources.v1.priv.AssertionResultsResourceTest ‑ nonV7EntityIdIsRejected
com.comet.opik.api.resources.v1.priv.AssertionResultsResourceTest ‑ traceAssertionsArePersistedAndRetrievableViaExperimentItems
com.comet.opik.api.resources.v1.priv.AssertionResultsResourceTest ‑ unsupportedEntityTypeIsRejected(EntityType)[1]
com.comet.opik.api.resources.v1.priv.AuthenticationResourceTest$ApiKey ‑ checkAccessForDefaultWorkspace__whenApiKeyIsPresent__thenReturnProperResponse(String, int, String)[1]
com.comet.opik.api.resources.v1.priv.AuthenticationResourceTest$ApiKey ‑ checkAccessForDefaultWorkspace__whenApiKeyIsPresent__thenReturnProperResponse(String, int, String)[2]
com.comet.opik.api.resources.v1.priv.AuthenticationResourceTest$ApiKey ‑ checkAccessForDefaultWorkspace__whenApiKeyIsPresent__thenReturnProperResponse(String, int, String)[3]
com.comet.opik.api.resources.v1.priv.AuthenticationResourceTest$ApiKey ‑ checkAccess__whenApiKeyIsPresent__thenReturnProperResponse(String, int, String)[1]
com.comet.opik.api.resources.v1.priv.AuthenticationResourceTest$ApiKey ‑ checkAccess__whenApiKeyIsPresent__thenReturnProperResponse(String, int, String)[2]
com.comet.opik.api.resources.v1.priv.AuthenticationResourceTest$ApiKey ‑ checkAccess__whenApiKeyIsPresent__thenReturnProperResponse(String, int, String)[3]
…

♻️ This comment has been updated with latest results.

Comment thread apps/opik-backend/src/main/java/com/comet/opik/domain/AssertionResultService.java Outdated
Comment thread apps/opik-backend/src/main/java/com/comet/opik/domain/SpanService.java Outdated
@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Backend Tests - Integration Group 4

1 823 tests  +21   1 823 ✅ +21   9m 18s ⏱️ +2s
   19 suites + 2       0 💤 ± 0 
   19 files   + 2       0 ❌ ± 0 

Results for commit f1e48d3. ± Comparison against base commit 4dc6b0c.

This pull request removes 49 and adds 70 tests. Note that renamed tests count towards both.
com.comet.opik.api.resources.v1.events.DatasetEventListenerTest ‑ when__newOptimizationIsCreated__shouldBeSavedInTheDatabase
com.comet.opik.api.resources.v1.events.DatasetEventListenerTest ‑ when__optimizationIsDeleted__lastCreatedOptimizationDateShouldBeUpdated_
com.comet.opik.api.resources.v1.events.DatasetEventListenerTest$CreateExperimentEvent ‑ when__newExperimentIsCreated__shouldBeSavedInTheDatabase
com.comet.opik.api.resources.v1.events.DatasetEventListenerTest$DeleteExperimentEvent ‑ when__experimentIsDeleted__lastCreatedExperimentDateShouldBeUpdated
com.comet.opik.api.resources.v1.events.DatasetEventListenerTest$DeleteExperimentEvent ‑ when__experimentIsDeleted__lastCreatedExperimentDateShouldBeUpdated_
com.comet.opik.api.resources.v1.events.TraceThreadOnlineScoringSamplerListenerIntegrationTest ‑ processesThreadsWithSdkOrNullSource(Source, TraceThreadOnlineScoringSamplerListener)[1]
com.comet.opik.api.resources.v1.events.TraceThreadOnlineScoringSamplerListenerIntegrationTest ‑ processesThreadsWithSdkOrNullSource(Source, TraceThreadOnlineScoringSamplerListener)[2]
com.comet.opik.api.resources.v1.events.TraceThreadOnlineScoringSamplerListenerIntegrationTest ‑ skipsNonSdkThreads(Source, TraceThreadOnlineScoringSamplerListener)[1]
com.comet.opik.api.resources.v1.events.TraceThreadOnlineScoringSamplerListenerIntegrationTest ‑ skipsNonSdkThreads(Source, TraceThreadOnlineScoringSamplerListener)[2]
com.comet.opik.api.resources.v1.events.TraceThreadOnlineScoringSamplerListenerIntegrationTest ‑ skipsNonSdkThreads(Source, TraceThreadOnlineScoringSamplerListener)[3]
…
com.comet.opik.api.resources.v1.priv.AgentInsightsJobsResourceTest ‑ create__firstThenConflict
com.comet.opik.api.resources.v1.priv.AgentInsightsJobsResourceTest ‑ create__projectMissing__returns404
com.comet.opik.api.resources.v1.priv.AgentInsightsJobsResourceTest ‑ cronSweep__triggersJobsWithTraces
com.comet.opik.api.resources.v1.priv.AgentInsightsJobsResourceTest ‑ get__afterCreateAndWhenAbsent
com.comet.opik.api.resources.v1.priv.AgentInsightsJobsResourceTest ‑ reportFailure__createAndRead
com.comet.opik.api.resources.v1.priv.AgentInsightsJobsResourceTest ‑ reportFailure__invalidBody__returns422(String, ReportFailure)[1]
com.comet.opik.api.resources.v1.priv.AgentInsightsJobsResourceTest ‑ reportFailure__invalidBody__returns422(String, ReportFailure)[2]
com.comet.opik.api.resources.v1.priv.AgentInsightsJobsResourceTest ‑ reportFailure__invalidBody__returns422(String, ReportFailure)[3]
com.comet.opik.api.resources.v1.priv.AgentInsightsJobsResourceTest ‑ reportFailure__unsupportedType__returns400
com.comet.opik.api.resources.v1.priv.AgentInsightsJobsResourceTest ‑ runFailure__multipleFailures__latestSurfaced
…

♻️ This comment has been updated with latest results.

thiagohora and others added 3 commits July 21, 2026 17:10
Refine scope to the rule: validate a referenced id iff the operation
persists it to the entity's table. Drop validation where the id is not
written by the operation:

- Thread open/close: projectId only resolves/locates an already-existing
  thread (its project_id was written at trace ingestion), so nothing is
  persisted here.
- Dataset item batch copy-from ids: only a read source for carry-forward
  rows, never stored as version lineage.

All create/update paths that write the referenced id to a column keep their
validation (spans trace_id/parent_span_id, feedback/comment/attachment/
guardrail/assertion entity ids, dataset-item trace/span + dataset ids,
annotation-queue items, and projectId/optimizationId/datasetVersionId/
sourceQueueId on the rows that store them).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Existing tests assigned random v4 UUIDs to referenced ids that are now
validated as UUIDv7 (guardrail secondaryId, experiment optimizationId /
datasetVersionId). Switch that test data to generator.generate() so the
tests exercise the intended paths (the invalid-version conflict test now
uses a v7-but-nonexistent id, preserving its 409 expectation).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d; fix test data

Address reviewer + CI findings:

- Remove projectId validation in GuardrailsService and AssertionResultService:
  both resolve the project by name and overwrite projectId with the resolved
  project.id() before persistence, so the client-supplied projectId is never
  stored — validating it only breaks clients that send arbitrary project ids.
- Fix remaining test data to use current UUIDv7 (never v4 or future-dated) for
  validated referenced ids: span traceId, dataset-item trace/span ids,
  annotation-queue item ids, feedback sourceQueueId, alert projectId,
  automation-rule projectIds, experiment optimizationId. 404/409 negative
  tests use a v7-but-nonexistent id to preserve their intent.
- Strengthen createWithOldTraceIdSucceeds to round-trip and assert the old
  traceId is persisted verbatim; add a batch non-v7 parentSpanId rejection test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…checks

Addresses reviewer findings:

- create(SpanBatch) validated traceId/parentSpanId inside bindSpanToProjectAndId,
  which runs AFTER deleteAutoStrippedAttachments + project getOrCreate — so a bad
  batch could delete attachments / create projects before failing 400. Move the id
  checks up front (before any side effect) so a rejected batch never mutates state.
- Extract a shared validateSpanReferences(traceId, parentSpanId) helper used by every
  span write path (single/batch create, single/batch update) so the reference-id
  rules can't drift between them.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
andrescrz
andrescrz previously approved these changes Jul 22, 2026

@andrescrz andrescrz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Some comments, but no blockers.

Comment thread apps/opik-backend/src/main/java/com/comet/opik/domain/IdGenerator.java Outdated
Comment thread apps/opik-backend/src/main/java/com/comet/opik/domain/ExperimentService.java Outdated
…er fixes

Address reviewer feedback (andrescrz):

- Drop UUIDv7 validation on referenced config ids that are already existence-checked
  (so a bad id fails with the proper 404/409, not 400): projectId on span/trace update,
  prompt, experiment, optimization, dashboard (all go through validateProjectIdExists /
  resolveProjectIdOrCreate / get); feedback-batch projectId (overwritten by the
  name-resolved project); datasetVersionId (FK-checked) and datasetId (findById).
- Keep it where the ref is persisted WITHOUT an existence check, so a UUIDv4 orphan
  can't be ingested: alert projectId, annotation-queue projectId, automation-rule
  projectIds, experiment optimizationId, feedback sourceQueueId.
- Move validateIdNotInFutureIfPresent(Async) bodies out of the IdGenerator interface
  into IdGeneratorImpl.
- Tests: route added generators through TestIdGeneratorFactory (static final) instead of
  local TimeBasedEpochGenerator fields; parameterize the span invalid-reference tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@thiagohora
thiagohora requested a review from andrescrz July 22, 2026 13:56
@thiagohora
thiagohora merged commit 3e57379 into main Jul 22, 2026
74 of 76 checks passed
@thiagohora
thiagohora deleted the thiaghora/OPIK-7352-reject-non-v7-foreign-ids branch July 22, 2026 15:30
thiagohora added a commit that referenced this pull request Jul 22, 2026
…d validation

Resolve the semantic overlap after merging main (#7553): unify IdGenerator on
the merged API — implement validateIdNotInFuture / validateIdNotInFutureAsync
(and the IfPresent variants) and drop the superseded validateIdForUpdate*.
Own-id validateId stays workspace-tagged for the audit metric; referenced-id
validateIdNotInFuture falls back to UNKNOWN on the sync path and resolves the
workspace from the reactive context on the async path.

Keep #7553's fail-fast (validate before side effects) for span batches, now run
inside deferContextual so the batch's own ids attribute to the request workspace;
bindSpanToProjectAndId no longer re-validates. Trace batches keep validating in
bindTraceToProjectAndId with the workspace threaded through.

Update the async test to the renamed methods.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
thiagohora added a commit that referenced this pull request Jul 22, 2026
bindTraceToProjectAndId validated own ids only after deleteAutoStrippedAttachments
and project getOrCreate, so a bad trace id in a batch still mutated state before
failing (unlike spans, which #7553 validates fail-fast). Hoist trace own-id
validation into a leading deferContextual that runs before any side effect and
attributes the audit metric to the request workspace, and stop re-validating in
bindTraceToProjectAndId — mirroring create(SpanBatch).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@thiagohora thiagohora added the test-environment Deploy Opik adhoc environment label Jul 23, 2026
thiagohora added a commit that referenced this pull request Jul 24, 2026
…on validation (#7573)

* [OPIK-7402] [BE] Add audit (shadow / log-only) mode to UUIDv7 ingestion validation

Adds a third `auditOnly` state to uuidValidation on top of the `enabled`
kill-switch. When enabled=true and auditOnly=true, out-of-window UUIDv7 ids
are counted and logged but NOT rejected, so clients emitting them surface in
real time without breaking ingestion. Effective mode:
enabled=false -> disabled; enabled=true & auditOnly=true -> audit;
enabled=true & auditOnly=false -> reject (HTTP 400, unchanged).

- New UuidValidationMetrics records opik.ingestion.uuid_v7.rejected in audit
  mode, tagged mode=audit + reason + resource + workspace_id (shares the
  instrument with InvalidUUIDExceptionMapper's reject-path counter).
- Validator gains resource/workspaceId params; audit branch emits + logs
  instead of throwing. Scope is the window check; the always-on NOT_V7
  version check is unchanged.
- workspaceId is read from the reactive context on the async paths and
  threaded through the batch bind on the sync path.
- Unit test covers disabled/reject/audit.

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

* [OPIK-7402] [BE] Tag the reject path with mode=reject on the shared counter

Address review: InvalidUUIDExceptionMapper recorded opik.ingestion.uuid_v7.rejected
without a mode label, so the shared counter had series with and without mode and a
`mode=reject` query would miss enforced rejections. Add mode=reject on the reject
path (workspace_id stays audit-only, as the mapper has no threaded workspace), and
align the UuidValidationMetrics contract javadoc: mode is always present; workspace_id
is audit-only; the reject path additionally carries http_route.

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

* [OPIK-7402] [BE] Address review: async test coverage, log prefix, trimmed javadoc

- Add IdGeneratorAsyncValidationTest covering validateIdAsync /
  validateIdForUpdateAsync reject+audit behavior, with and without
  RequestContext.WORKSPACE_ID in the reactive context (the deferContextual
  workspace lookup + UNKNOWN fallback).
- Keep a fixed, searchable prefix on the audit log line and move the variable
  fields to the end.
- Trim the IdGenerator#validateId javadoc to validation semantics +
  InvalidUUIDException; keep workspaceId threading (the sync/batch path is the
  primary ingestion route where per-workspace attribution is the goal).

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

* [OPIK-7402] [BE] Reconcile audit mode with merged OPIK-7352 foreign-id validation

Resolve the semantic overlap after merging main (#7553): unify IdGenerator on
the merged API — implement validateIdNotInFuture / validateIdNotInFutureAsync
(and the IfPresent variants) and drop the superseded validateIdForUpdate*.
Own-id validateId stays workspace-tagged for the audit metric; referenced-id
validateIdNotInFuture falls back to UNKNOWN on the sync path and resolves the
workspace from the reactive context on the async path.

Keep #7553's fail-fast (validate before side effects) for span batches, now run
inside deferContextual so the batch's own ids attribute to the request workspace;
bindSpanToProjectAndId no longer re-validates. Trace batches keep validating in
bindTraceToProjectAndId with the workspace threaded through.

Update the async test to the renamed methods.

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

* [OPIK-7402] [BE] Make trace batch validation symmetric with spans

bindTraceToProjectAndId validated own ids only after deleteAutoStrippedAttachments
and project getOrCreate, so a bad trace id in a batch still mutated state before
failing (unlike spans, which #7553 validates fail-fast). Hoist trace own-id
validation into a leading deferContextual that runs before any side effect and
attributes the audit metric to the request workspace, and stop re-validating in
bindTraceToProjectAndId — mirroring create(SpanBatch).

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

* [OPIK-7402] [BE] Address review: centralize counter, simplify async, tidy nits

- Centralize the opik.ingestion.uuid_v7.rejected counter and its label constants
  in UuidValidationMetrics; InvalidUUIDExceptionMapper now injects it and delegates
  via recordReject(reason, httpRoute), dropping its duplicate counter/constants.
- Drop the redundant nested Mono.fromCallable inside deferContextual on the async
  validation paths (validate has no blocking work and there is no subscribeOn, so
  it ran on the subscribing thread either way; deferContextual already maps a thrown
  exception to onError).
- Let resource fall back to unknown like workspaceId (relax @nonnull); the metric
  recorder defaults blank values.
- Test: make the id generators static final and rename to constant case.

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

* [OPIK-7402] [BE] Review: centralize IdGenerator + workspace overloads; factor metric assembly

- IdGenerator: single core validateIdNotInFuture(id, resource, workspaceId); the
  2-arg form and the async form delegate to it (no more duplicated version+window
  logic), and a workspace-parameterized overload is offered for callers that carry
  the request workspace. Same for validateIdNotInFutureIfPresent. The many
  config-entity callers keep the 2-arg form, defaulting to unknown by choice.
- UuidValidationMetrics: factor the shared counter/mode/reason assembly into a
  private record(...) helper; recordAudit/recordReject only supply their
  path-specific tags.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backend java Pull requests that update Java code 🟠 size/L test-environment Deploy Opik adhoc environment tests Including test files, or tests related like configuration.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants