Skip to content

feat(graph-rag): add lifecycle, curation, export, and exact caches#31

Merged
kl3inIT merged 1 commit into
light-ragfrom
kl3inIT/light-rag-pr06-lifecycle-cache
Jul 24, 2026
Merged

feat(graph-rag): add lifecycle, curation, export, and exact caches#31
kl3inIT merged 1 commit into
light-ragfrom
kl3inIT/light-rag-pr06-lifecycle-cache

Conversation

@kl3inIT

@kl3inIT kl3inIT commented Jul 24, 2026

Copy link
Copy Markdown
Owner

What changed

  • adds durable graph-index lifecycle transitions for retry/resume/cancel/supersede, bounded extraction timeouts, manifest-bound idempotency, and pre-publication revalidation
  • adds fail-closed Knowledge Asset retirement and revision-scoped graph cleanup
  • adds append-only graph curation for curated entities/relations, reversible aliases, and reversible suppressions
  • adds permission-scoped audited graph export in JSON, CSV, Markdown, and text
  • adds canonical exact model/retrieval caches with namespace invalidation and tenant-safe evidence validation
  • centralizes PostgreSQL graph read authorization across retrieval, curation, and export, including current sealed ACL-head checks
  • adds OpenFGA management relations, Flyway V33, API contract updates, and parity documentation

Why

PR6 closes the LightRAG v1.5.4 lifecycle/cache parity slice while preserving OrgMemory's stricter evidence-level authorization. Derived graph rows and caches remain rebuildable and cannot grant access.

Validation

  • ./gradlew.bat --no-daemon clean build
  • PostgreSQL GraphRAG integration suite, including ACL revocation and shared-identity delete cases
  • OpenFGA: 6/6 tests, 27/27 checks, 5/5 ListObjects
  • pnpm -C web check:api
  • pnpm -C web typecheck
  • pnpm -C web build
  • migration/package/zero-byte mechanical checks

Target is intentionally light-rag; no LightRAG program PR targets main directly.

Summary by CodeRabbit

  • New Features

    • Added permission-aware graph curation, including entity and relationship editing, identity aliasing, suppression, and reversible changes.
    • Added graph export in JSON, CSV, Markdown, and plain-text formats.
    • Added knowledge asset retirement and graph indexing job status, cancellation, and resume controls.
    • Added authorization-aware caching for model and retrieval results.
  • Bug Fixes

    • Improved indexing timeout handling, cancellation behavior, publication consistency, and cache invalidation.
    • Strengthened authorization and evidence-scope validation for graph data and exports.
  • Tests

    • Added coverage for curation, exports, caching, lifecycle controls, authorization, and publication reliability.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds authorized graph curation and export APIs, PostgreSQL-backed graph caches and curation storage, deterministic cache and projection identities, cancellable indexing jobs with extraction deadlines, publication invalidation, and database schema support with extensive unit and integration coverage.

Changes

Graph contracts and representations

Layer / File(s) Summary
Core graph contracts
components/graph-rag-core/...
Adds validated curation records, effective alias/suppression resolution, deterministic cache keys, projection fingerprints, export documents and formatters, and batch publication lifecycle handling.
Indexing lifecycle
core/src/main/java/com/orgmemory/core/knowledge/*
Adds cancellation, resume, status views, manifest binding, organization-scoped job lookup, asset retirement, curation, and permission-aware graph export services.
PostgreSQL graph persistence
integrations/graph-rag-postgres/..., core/src/main/resources/db/migration/*
Adds authorized SQL visibility fragments, PostgreSQL cache and curation stores, export aggregation, projection publication conflict handling, and schema migration V33.
Worker publication controls
apps/worker/src/main/java/com/orgmemory/worker/graph/*
Adds extraction deadlines, stop-specific handling, knowledge-space publication identity, and model/retrieval cache invalidation around publication.
HTTP and authorization integration
apps/api/src/main/java/com/orgmemory/api/knowledge/*, integrations/authorization-openfga/*
Exposes asset, indexing-job, graph-curation, and graph-export endpoints and adds corresponding graph-management, deletion, and rebuild permissions.
Validation and integration coverage
*/src/test/*
Adds tests for cache contracts, curation overlays, export formatting, lifecycle transitions, authorization, PostgreSQL persistence, ACL revocation, and publication conflicts.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  actor Client
  participant KnowledgeGraphExportService
  participant PostgresGraphExportReader
  participant PostgresAuthorizedGraphSql
  participant GraphExportFormatter

  Client->>KnowledgeGraphExportService: request graph export
  KnowledgeGraphExportService->>PostgresAuthorizedGraphSql: build authorized scope parameters
  KnowledgeGraphExportService->>PostgresGraphExportReader: read authorized graph
  PostgresGraphExportReader->>PostgresAuthorizedGraphSql: query visible identities and contributions
  PostgresGraphExportReader-->>KnowledgeGraphExportService: GraphExportDocument
  KnowledgeGraphExportService->>GraphExportFormatter: format document
  GraphExportFormatter-->>Client: export artifact
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.51% 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 matches the main change set: graph-RAG lifecycle, curation, export, and exact caches.
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
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch kl3inIT/light-rag-pr06-lifecycle-cache

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.

@kl3inIT
kl3inIT merged commit cdc03ef into light-rag Jul 24, 2026
4 of 5 checks passed

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
core/src/main/java/com/orgmemory/core/knowledge/GraphIndexJob.java (1)

106-120: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

claim()'s new cancellation guard can permanently poison a job once its lease expires.

claim() now throws when cancellationRequested is true, but neither caller that can reach this state handles it:

  • GraphIndexingCoordinator.claimNext(...) calls job.claim(workerId, now, leaseDuration) directly (no try/catch for this specific call), so if a PROCESSING job's lease expires while cancellationRequested is true, every subsequent poll throws an uncaught IllegalStateException instead of transitioning the job to CANCELLED.
  • GraphIndexingCoordinator.fail(...) doesn't check cancellationRequested() before calling retry(...), so a job that fails for an unrelated reason while cancellation is pending goes back to PENDING with the flag still set — hitting the same throw on its next claim attempt.

The job only terminates once attempts are exhausted via failExpiredLease, ending up FAILED/LEASE_EXPIRED instead of CANCELLED, after repeatedly throwing on the worker's poll loop.

🛠️ Proposed fix: cancel instead of throwing when re-encountered
 void claim(String workerId, Instant now, Duration leaseDuration) {
-        if (cancellationRequested) {
-            throw new IllegalStateException("a cancelled graph job cannot be claimed");
-        }
+        if (cancellationRequested) {
+            cancel(now);
+            throw new GraphIndexingStoppedException(
+                    GraphIndexingStoppedException.Reason.CANCELLED,
+                    "graph indexing was cancelled before it could be re-claimed");
+        }
         status = GraphIndexJobStatus.PROCESSING;

Also add an equivalent cancellationRequested() check ahead of retry(...) in GraphIndexingCoordinator.fail(...) so a pending cancellation always wins over a normal retry.

🤖 Prompt for 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.

In `@core/src/main/java/com/orgmemory/core/knowledge/GraphIndexJob.java` around
lines 106 - 120, Update GraphIndexJob.claim so a cancellation-requested job
transitions to CANCELLED instead of throwing, including when reclaiming an
expired lease. In GraphIndexingCoordinator.fail, check cancellationRequested()
before calling retry(...) and cancel the job when requested; preserve normal
retry behavior otherwise.
🤖 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/java/com/orgmemory/api/knowledge/KnowledgeGraphManagementController.java`:
- Around line 216-231: Remove organizationId from EvidenceRequest and stop
copying request-supplied tenant data in toReference(). Derive the
organization/tenant identifier from the authorized knowledge space or canonical
evidence record when constructing EvidenceReference, while preserving the
remaining request fields and rejecting cross-tenant claims.

In
`@components/graph-rag-core/src/main/java/com/orgmemory/graphrag/cache/RetrievalResultCache.java`:
- Around line 27-38: Make validation mandatory in the cache put contract by
changing the public put(Key, Entry) flow to call requireValidEntry before
storage and delegate to a separate abstract or protected implementation method.
Update existing implementations to override only that delegated storage method,
ensuring custom implementations cannot bypass tenant-scoping validation.

In
`@components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphCurationFingerprint.java`:
- Line 45: The fingerprint construction uses ambiguous delimiter-based
concatenation for nested values. In
components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphCurationFingerprint.java#L45-L45,
update the keyword encoding to use length-prefixing or structured canonical
serialization. In
components/graph-rag-core/src/main/java/com/orgmemory/graphrag/port/GraphProjectionManifest.java#L29-L59,
frame each serialized entity, relation, and embedding item before combining the
collection; in `#L74-L124`, replace pipe and unit-separator joins with escaped or
length-prefixed field encoding, preserving deterministic output.

In
`@components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphCurationRecord.java`:
- Around line 85-122: Update the IdentityAlias and IdentitySuppression record
definitions in GraphCurationRecord to include a required EvidenceReference
governing-evidence field, validate that reference’s organization consistently
with the other curation variants, and propagate it through all construction and
persistence paths. Update GraphCurationStore.append and overlay filtering so
each alias or suppression is persisted and applied only when its governing
evidence is allowed; do not add a separate append evidence parameter.

In
`@components/graph-rag-core/src/main/java/com/orgmemory/graphrag/export/GraphExportFormatter.java`:
- Around line 143-145: Update the csv method to prefix values beginning with
“=”, “+”, “-”, or “@” with a text marker before applying quote escaping and CSV
quoting, so untrusted names and descriptions are treated as text by spreadsheet
applications.
- Around line 84-114: Update the markdown and text methods to include each
EntityRow and RelationRow’s evidence references, matching the deterministic
rendering and formatting already used by the JSON and CSV export branches.
Preserve the existing fields and ordering, and render evidence for both entity
and relation rows so provenance remains auditable.

In
`@components/graph-rag-core/src/main/java/com/orgmemory/graphrag/storage/ProjectionBatchLifecycle.java`:
- Around line 38-45: In the preparation loop of ProjectionBatchLifecycle, add
each retrieved Preparation to prepared before invoking
preparation.prepare(batch). Keep publications.markPrepared(batch, kind, now)
after successful preparation, so abortAndDiscard can clean up partially staged
state if prepare throws.

In `@core/src/main/java/com/orgmemory/core/knowledge/GraphIndexJob.java`:
- Around line 63-70: Add a migration-backed unique constraint or unique index
for graph_index_jobs.idempotency_key, matching the existing database schema
conventions used for graph_projection_heads. Keep the
GraphIndexJob.idempotencyKey mapping aligned with this constraint so duplicate
graph index jobs are rejected at the database level.

In
`@core/src/main/java/com/orgmemory/core/knowledge/KnowledgeGraphCurationService.java`:
- Around line 150-166: Update requireGoverningEvidence to perform an
authorization.check(...) for can_view on the resolved knowledge asset after
validating organization and Knowledge Space ownership. Ensure this per-asset
authorization occurs before the curation is created or cached, while preserving
the existing ownership checks and not accepting forbidden evidence.

In
`@core/src/main/java/com/orgmemory/core/knowledge/KnowledgeGraphExportService.java`:
- Around line 77-100: Update the export authorization flow in the method
containing the space, subject, and entry checks to record
PermissionAuditDecision.DENY before throwing accessDenied() for missing/inactive
spaces, invalid subjects, and denied authorization entries. Also record a
separate audit/result for policy-version resolution failures before propagating
that exception, while preserving the existing successful export audit.

In
`@core/src/main/java/com/orgmemory/core/knowledge/SourceAclSnapshotRepository.java`:
- Around line 16-32: Add migration coverage for both export query shapes: index
the tenant-scoped join/filter columns used by
SourceAclSnapshotRepository.maximumCurrentAclGeneration, relying on existing
foreign-key or unique constraints where they already cover the join keys, and
add a B-tree index for KnowledgeAssetRepository.findActiveIdsInKnowledgeSpace
covering knowledge_assets(organization_id, knowledge_space_id, archived_at)
without removing existing constraints or indexes. Affected sites:
core/src/main/java/com/orgmemory/core/knowledge/SourceAclSnapshotRepository.java:16-32
and
core/src/main/java/com/orgmemory/core/knowledge/KnowledgeAssetRepository.java:18-29;
these repository sites require no direct changes, only the migration indexes.

In
`@core/src/test/java/com/orgmemory/core/knowledge/GraphIndexingCoordinatorTests.java`:
- Around line 212-234: Bound the retry loop in
failedCurrentJobCanResumeWithFreshRetryBudget with a finite iteration cap, such
as a for-loop or explicit counter, while preserving the existing
fail-and-reclaim behavior. Ensure the test fails clearly if job.getStatus()
never reaches GraphIndexJobStatus.FAILED instead of hanging indefinitely.

In
`@core/src/test/java/com/orgmemory/core/knowledge/KnowledgeAssetLifecycleServiceTests.java`:
- Around line 79-90: Update
deleteRetiresCanonicalLedgerThenRemovesDerivedGraphAndCaches to verify the
lifecycle calls with an InOrder verifier, asserting ingestion.retire occurs
before graph.removeRevision and both cache invalidations. Keep the existing
authorization setup and call arguments, but replace the independent interaction
checks with ordered verification.

In `@integrations/authorization-openfga/src/test/openfga/store.fga.yaml`:
- Around line 189-211: Extend the “Graph management is explicit and resource
scoped” test block with ListObjects assertions for can_curate_graph,
can_export_graph, can_delete, and can_rebuild. Add positive enumeration cases
for Laura and empty-result cases for Minh, covering both knowledge_space and
knowledge_asset resources without changing the existing Check assertions.

In
`@integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresGraphCurationStore.java`:
- Around line 44-55: The transaction in the idempotent append flow must handle
concurrent first inserts: update the logic around existing(...) and insert(...)
to catch a duplicate-key DataIntegrityViolationException, re-read the record
with existing(...), and return it when the fingerprint matches or raise
CurationConflictException for a conflicting record. Preserve the current
validation and normal insert behavior.

In
`@integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresGraphRagCacheStore.java`:
- Around line 39-128: Implement scheduled cleanup for expired rows in both
graph_model_invocation_cache and graph_retrieval_result_cache. Add a maintenance
method near the existing ModelInvocationCache read/write/invalidate logic that
deletes rows where expires_at is at or before the current time, and register it
with the application’s established scheduling mechanism; preserve namespace
invalidation behavior and use the existing JDBC access patterns.

---

Outside diff comments:
In `@core/src/main/java/com/orgmemory/core/knowledge/GraphIndexJob.java`:
- Around line 106-120: Update GraphIndexJob.claim so a cancellation-requested
job transitions to CANCELLED instead of throwing, including when reclaiming an
expired lease. In GraphIndexingCoordinator.fail, check cancellationRequested()
before calling retry(...) and cancel the job when requested; preserve normal
retry behavior otherwise.
🪄 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: 025ee588-27cd-47e1-9e12-a0d938844d55

📥 Commits

Reviewing files that changed from the base of the PR and between d92f615 and 83d720b.

⛔ Files ignored due to path filters (5)
  • contracts/openapi.json is excluded by !contracts/openapi.json
  • docs/decisions/0014-lightrag-lifecycle-curation-and-cache.md is excluded by !docs/**
  • docs/increments/active/2026-07-23-full-lightrag-semantic-port/design.md is excluded by !docs/**
  • docs/increments/active/2026-07-23-full-lightrag-semantic-port/plan.md is excluded by !docs/**
  • docs/research/lightrag-v1.5.4-parity-manifest.md is excluded by !docs/**
📒 Files selected for processing (67)
  • apps/api/build.gradle.kts
  • apps/api/src/main/java/com/orgmemory/api/knowledge/KnowledgeAssetLifecycleController.java
  • apps/api/src/main/java/com/orgmemory/api/knowledge/KnowledgeGraphManagementController.java
  • apps/worker/src/main/java/com/orgmemory/worker/graph/GraphIndexingProcessor.java
  • apps/worker/src/main/java/com/orgmemory/worker/graph/GraphIndexingProperties.java
  • apps/worker/src/main/java/com/orgmemory/worker/graph/GraphPublicationCommitter.java
  • apps/worker/src/main/resources/application.yml
  • apps/worker/src/test/java/com/orgmemory/worker/graph/GraphIndexingProcessorTests.java
  • apps/worker/src/test/java/com/orgmemory/worker/graph/GraphPublicationCommitterTests.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/cache/CanonicalCacheKeyHasher.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/cache/ModelInvocationCache.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/cache/ModelInvocationCacheKeys.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/cache/RetrievalResultCache.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/cache/RetrievalResultCacheKeys.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/CurationProvenance.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/EffectiveGraphCuration.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphCurationFingerprint.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphCurationOverlay.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphCurationRecord.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphCurationStore.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphIdentityKind.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphIdentityRef.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/export/GraphExportDocument.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/export/GraphExportFormat.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/export/GraphExportFormatter.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/export/GraphExportReader.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/port/GraphProjectionManifest.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/port/GraphRevisionProjection.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/storage/ProjectionBatchLifecycle.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/cache/CacheKeyContractTests.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/curation/EffectiveGraphCurationTests.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/export/GraphExportFormatterTests.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/port/GraphProjectionManifestTests.java
  • components/graph-rag-testkit/src/main/java/com/orgmemory/graphrag/testkit/InMemoryRetrievalResultCache.java
  • components/graph-rag-testkit/src/test/java/com/orgmemory/graphrag/testkit/ProjectionBatchLifecycleTests.java
  • components/graph-rag-testkit/src/test/java/com/orgmemory/graphrag/testkit/ProjectionContractTests.java
  • core/build.gradle.kts
  • core/src/main/java/com/orgmemory/core/knowledge/ClaimedGraphIndex.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexJob.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexJobRepository.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexJobStatus.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexJobView.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexLifecycleService.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexingCoordinator.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexingStoppedException.java
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeAssetLifecycleService.java
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeAssetRepository.java
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeGraphCurationCommand.java
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeGraphCurationService.java
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeGraphExportService.java
  • core/src/main/java/com/orgmemory/core/knowledge/SourceAclSnapshotRepository.java
  • core/src/main/resources/db/migration/V33__graph_lifecycle_curation_and_cache.sql
  • core/src/test/java/com/orgmemory/core/knowledge/GraphIndexingCoordinatorTests.java
  • core/src/test/java/com/orgmemory/core/knowledge/KnowledgeAssetLifecycleServiceTests.java
  • core/src/test/java/com/orgmemory/core/knowledge/KnowledgeGraphCurationServiceTests.java
  • core/src/test/java/com/orgmemory/core/knowledge/KnowledgeGraphExportServiceTests.java
  • integrations/authorization-openfga/src/main/openfga/model.fga
  • integrations/authorization-openfga/src/test/openfga/store.fga.yaml
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresAuthorizedGraphSql.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresGraphCurationStore.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresGraphExportReader.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresGraphProjectionStore.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresGraphRagAutoConfiguration.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresGraphRagCacheStore.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresModelInvocationCache.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresRetrievalResultCache.java
  • integrations/graph-rag-postgres/src/test/java/com/orgmemory/graphrag/postgres/PostgresGraphProjectionStoreIntegrationTests.java
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Backend · Java 25
🧰 Additional context used
📓 Path-based instructions (8)
**/*

📄 CodeRabbit inference engine (CLAUDE.md)

**/*: Before changing unfamiliar Spring Boot 4, Spring Modulith 2, Spring AI 2, Gradle, React, Vite, Tailwind, or TypeScript APIs, consult Context7/current official documentation and the project orgmemory-* verification skills.
Read docs/guidelines/agent-safety.md before retrieval, AI, MCP, permission, upload, graph, or export work.
Never commit .env files, provider keys, tokens, or customer data.
Run the relevant gates from docs/guidelines/testing-harness.md; use a terminating clean test as the context gate, and do not treat bootRun as verification.
Current behavior belongs in architecture/specs only after it exists in code; intent belongs in vision, roadmap, or an active increment, and repository state must not be duplicated across documents.

Files:

  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/export/GraphExportReader.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/port/GraphProjectionManifestTests.java
  • apps/worker/src/main/resources/application.yml
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphIdentityKind.java
  • core/src/test/java/com/orgmemory/core/knowledge/KnowledgeGraphCurationServiceTests.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexJobView.java
  • apps/worker/src/test/java/com/orgmemory/worker/graph/GraphPublicationCommitterTests.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/export/GraphExportFormat.java
  • apps/api/build.gradle.kts
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/CurationProvenance.java
  • integrations/authorization-openfga/src/test/openfga/store.fga.yaml
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/cache/ModelInvocationCache.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphIdentityRef.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexJobStatus.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresRetrievalResultCache.java
  • components/graph-rag-testkit/src/main/java/com/orgmemory/graphrag/testkit/InMemoryRetrievalResultCache.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/port/GraphRevisionProjection.java
  • core/build.gradle.kts
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/export/GraphExportFormatterTests.java
  • core/src/test/java/com/orgmemory/core/knowledge/KnowledgeAssetLifecycleServiceTests.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexJobRepository.java
  • core/src/main/java/com/orgmemory/core/knowledge/SourceAclSnapshotRepository.java
  • apps/worker/src/main/java/com/orgmemory/worker/graph/GraphIndexingProperties.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/cache/CanonicalCacheKeyHasher.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphCurationStore.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/cache/ModelInvocationCacheKeys.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/cache/CacheKeyContractTests.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresModelInvocationCache.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphCurationFingerprint.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexingStoppedException.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/cache/RetrievalResultCache.java
  • components/graph-rag-testkit/src/test/java/com/orgmemory/graphrag/testkit/ProjectionBatchLifecycleTests.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/curation/EffectiveGraphCurationTests.java
  • core/src/test/java/com/orgmemory/core/knowledge/KnowledgeGraphExportServiceTests.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphCurationRecord.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/export/GraphExportDocument.java
  • apps/api/src/main/java/com/orgmemory/api/knowledge/KnowledgeGraphManagementController.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexLifecycleService.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/port/GraphProjectionManifest.java
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeAssetLifecycleService.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/storage/ProjectionBatchLifecycle.java
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeGraphExportService.java
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeGraphCurationCommand.java
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeAssetRepository.java
  • apps/worker/src/main/java/com/orgmemory/worker/graph/GraphPublicationCommitter.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/EffectiveGraphCuration.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/export/GraphExportFormatter.java
  • components/graph-rag-testkit/src/test/java/com/orgmemory/graphrag/testkit/ProjectionContractTests.java
  • core/src/test/java/com/orgmemory/core/knowledge/GraphIndexingCoordinatorTests.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresAuthorizedGraphSql.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphCurationOverlay.java
  • apps/api/src/main/java/com/orgmemory/api/knowledge/KnowledgeAssetLifecycleController.java
  • integrations/authorization-openfga/src/main/openfga/model.fga
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresGraphExportReader.java
  • core/src/main/resources/db/migration/V33__graph_lifecycle_curation_and_cache.sql
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeGraphCurationService.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresGraphRagAutoConfiguration.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/cache/RetrievalResultCacheKeys.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexingCoordinator.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresGraphProjectionStore.java
  • apps/worker/src/test/java/com/orgmemory/worker/graph/GraphIndexingProcessorTests.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresGraphRagCacheStore.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresGraphCurationStore.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexJob.java
  • apps/worker/src/main/java/com/orgmemory/worker/graph/GraphIndexingProcessor.java
  • integrations/graph-rag-postgres/src/test/java/com/orgmemory/graphrag/postgres/PostgresGraphProjectionStoreIntegrationTests.java
  • core/src/main/java/com/orgmemory/core/knowledge/ClaimedGraphIndex.java
**/*.{java,sql}

📄 CodeRabbit inference engine (CLAUDE.md)

Pair JPA schema changes with a Flyway migration.

Files:

  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/export/GraphExportReader.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/port/GraphProjectionManifestTests.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphIdentityKind.java
  • core/src/test/java/com/orgmemory/core/knowledge/KnowledgeGraphCurationServiceTests.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexJobView.java
  • apps/worker/src/test/java/com/orgmemory/worker/graph/GraphPublicationCommitterTests.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/export/GraphExportFormat.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/CurationProvenance.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/cache/ModelInvocationCache.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphIdentityRef.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexJobStatus.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresRetrievalResultCache.java
  • components/graph-rag-testkit/src/main/java/com/orgmemory/graphrag/testkit/InMemoryRetrievalResultCache.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/port/GraphRevisionProjection.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/export/GraphExportFormatterTests.java
  • core/src/test/java/com/orgmemory/core/knowledge/KnowledgeAssetLifecycleServiceTests.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexJobRepository.java
  • core/src/main/java/com/orgmemory/core/knowledge/SourceAclSnapshotRepository.java
  • apps/worker/src/main/java/com/orgmemory/worker/graph/GraphIndexingProperties.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/cache/CanonicalCacheKeyHasher.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphCurationStore.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/cache/ModelInvocationCacheKeys.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/cache/CacheKeyContractTests.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresModelInvocationCache.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphCurationFingerprint.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexingStoppedException.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/cache/RetrievalResultCache.java
  • components/graph-rag-testkit/src/test/java/com/orgmemory/graphrag/testkit/ProjectionBatchLifecycleTests.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/curation/EffectiveGraphCurationTests.java
  • core/src/test/java/com/orgmemory/core/knowledge/KnowledgeGraphExportServiceTests.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphCurationRecord.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/export/GraphExportDocument.java
  • apps/api/src/main/java/com/orgmemory/api/knowledge/KnowledgeGraphManagementController.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexLifecycleService.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/port/GraphProjectionManifest.java
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeAssetLifecycleService.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/storage/ProjectionBatchLifecycle.java
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeGraphExportService.java
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeGraphCurationCommand.java
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeAssetRepository.java
  • apps/worker/src/main/java/com/orgmemory/worker/graph/GraphPublicationCommitter.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/EffectiveGraphCuration.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/export/GraphExportFormatter.java
  • components/graph-rag-testkit/src/test/java/com/orgmemory/graphrag/testkit/ProjectionContractTests.java
  • core/src/test/java/com/orgmemory/core/knowledge/GraphIndexingCoordinatorTests.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresAuthorizedGraphSql.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphCurationOverlay.java
  • apps/api/src/main/java/com/orgmemory/api/knowledge/KnowledgeAssetLifecycleController.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresGraphExportReader.java
  • core/src/main/resources/db/migration/V33__graph_lifecycle_curation_and_cache.sql
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeGraphCurationService.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresGraphRagAutoConfiguration.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/cache/RetrievalResultCacheKeys.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexingCoordinator.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresGraphProjectionStore.java
  • apps/worker/src/test/java/com/orgmemory/worker/graph/GraphIndexingProcessorTests.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresGraphRagCacheStore.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresGraphCurationStore.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexJob.java
  • apps/worker/src/main/java/com/orgmemory/worker/graph/GraphIndexingProcessor.java
  • integrations/graph-rag-postgres/src/test/java/com/orgmemory/graphrag/postgres/PostgresGraphProjectionStoreIntegrationTests.java
  • core/src/main/java/com/orgmemory/core/knowledge/ClaimedGraphIndex.java
**/*.java

📄 CodeRabbit inference engine (CLAUDE.md)

JetBrains IDE inspection is a Java-backend gate only.

Files:

  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/export/GraphExportReader.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/port/GraphProjectionManifestTests.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphIdentityKind.java
  • core/src/test/java/com/orgmemory/core/knowledge/KnowledgeGraphCurationServiceTests.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexJobView.java
  • apps/worker/src/test/java/com/orgmemory/worker/graph/GraphPublicationCommitterTests.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/export/GraphExportFormat.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/CurationProvenance.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/cache/ModelInvocationCache.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphIdentityRef.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexJobStatus.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresRetrievalResultCache.java
  • components/graph-rag-testkit/src/main/java/com/orgmemory/graphrag/testkit/InMemoryRetrievalResultCache.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/port/GraphRevisionProjection.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/export/GraphExportFormatterTests.java
  • core/src/test/java/com/orgmemory/core/knowledge/KnowledgeAssetLifecycleServiceTests.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexJobRepository.java
  • core/src/main/java/com/orgmemory/core/knowledge/SourceAclSnapshotRepository.java
  • apps/worker/src/main/java/com/orgmemory/worker/graph/GraphIndexingProperties.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/cache/CanonicalCacheKeyHasher.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphCurationStore.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/cache/ModelInvocationCacheKeys.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/cache/CacheKeyContractTests.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresModelInvocationCache.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphCurationFingerprint.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexingStoppedException.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/cache/RetrievalResultCache.java
  • components/graph-rag-testkit/src/test/java/com/orgmemory/graphrag/testkit/ProjectionBatchLifecycleTests.java
  • components/graph-rag-core/src/test/java/com/orgmemory/graphrag/curation/EffectiveGraphCurationTests.java
  • core/src/test/java/com/orgmemory/core/knowledge/KnowledgeGraphExportServiceTests.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphCurationRecord.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/export/GraphExportDocument.java
  • apps/api/src/main/java/com/orgmemory/api/knowledge/KnowledgeGraphManagementController.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexLifecycleService.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/port/GraphProjectionManifest.java
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeAssetLifecycleService.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/storage/ProjectionBatchLifecycle.java
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeGraphExportService.java
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeGraphCurationCommand.java
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeAssetRepository.java
  • apps/worker/src/main/java/com/orgmemory/worker/graph/GraphPublicationCommitter.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/EffectiveGraphCuration.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/export/GraphExportFormatter.java
  • components/graph-rag-testkit/src/test/java/com/orgmemory/graphrag/testkit/ProjectionContractTests.java
  • core/src/test/java/com/orgmemory/core/knowledge/GraphIndexingCoordinatorTests.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresAuthorizedGraphSql.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphCurationOverlay.java
  • apps/api/src/main/java/com/orgmemory/api/knowledge/KnowledgeAssetLifecycleController.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresGraphExportReader.java
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeGraphCurationService.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresGraphRagAutoConfiguration.java
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/cache/RetrievalResultCacheKeys.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexingCoordinator.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresGraphProjectionStore.java
  • apps/worker/src/test/java/com/orgmemory/worker/graph/GraphIndexingProcessorTests.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresGraphRagCacheStore.java
  • integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresGraphCurationStore.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexJob.java
  • apps/worker/src/main/java/com/orgmemory/worker/graph/GraphIndexingProcessor.java
  • integrations/graph-rag-postgres/src/test/java/com/orgmemory/graphrag/postgres/PostgresGraphProjectionStoreIntegrationTests.java
  • core/src/main/java/com/orgmemory/core/knowledge/ClaimedGraphIndex.java
**/*.{yml,yaml,properties}

📄 CodeRabbit inference engine (CLAUDE.md)

Keep ddl-auto=validate in application configuration.

Files:

  • apps/worker/src/main/resources/application.yml
  • integrations/authorization-openfga/src/test/openfga/store.fga.yaml
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/GraphIndexJobView.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexJobStatus.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexJobRepository.java
  • core/src/main/java/com/orgmemory/core/knowledge/SourceAclSnapshotRepository.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexingStoppedException.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexLifecycleService.java
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeAssetLifecycleService.java
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeGraphExportService.java
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeGraphCurationCommand.java
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeAssetRepository.java
  • core/src/main/java/com/orgmemory/core/knowledge/KnowledgeGraphCurationService.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexingCoordinator.java
  • core/src/main/java/com/orgmemory/core/knowledge/GraphIndexJob.java
  • core/src/main/java/com/orgmemory/core/knowledge/ClaimedGraphIndex.java
integrations/authorization-openfga/**/*

⚙️ CodeRabbit configuration file

integrations/authorization-openfga/**/*: Source-native ACL is a hard ceiling. Flag any parent, organization, role,
or wildcard relation that can broaden source access. Require negative
Check and ListObjects coverage for every new permission path.

Files:

  • integrations/authorization-openfga/src/test/openfga/store.fga.yaml
  • integrations/authorization-openfga/src/main/openfga/model.fga
apps/api/src/main/java/**/*.java

⚙️ CodeRabbit configuration file

apps/api/src/main/java/**/*.java: Enforce the browser-BFF and resource-server boundaries. Authentication
must resolve an active internal actor through the explicit issuer and
subject binding. Reject identity, tenant, roles, or permissions supplied
by request payloads, JWT email, or untrusted JWT role claims.

Files:

  • apps/api/src/main/java/com/orgmemory/api/knowledge/KnowledgeGraphManagementController.java
  • apps/api/src/main/java/com/orgmemory/api/knowledge/KnowledgeAssetLifecycleController.java
core/src/main/resources/db/migration/*.sql

⚙️ CodeRabbit configuration file

core/src/main/resources/db/migration/*.sql: Flyway migrations are immutable after release. Check tenant isolation,
foreign keys, uniqueness, indexes, append-only evidence semantics, safe
defaults, and compatibility with PostgreSQL 18 plus pgvector.

Files:

  • core/src/main/resources/db/migration/V33__graph_lifecycle_curation_and_cache.sql
🧠 Learnings (1)
📚 Learning: 2026-07-23T23:30:44.585Z
Learnt from: kl3inIT
Repo: kl3inIT/OrgMemory PR: 30
File: core/src/main/resources/db/migration/V32__evidence_scoped_graph_semantics.sql:0-0
Timestamp: 2026-07-23T23:30:44.585Z
Learning: For OrgMemory PostgreSQL Flyway migrations under core/src/main/resources/db/migration, do not recommend using `CREATE INDEX CONCURRENTLY` or `DROP INDEX CONCURRENTLY` inside application-owned Flyway migration SQL. Flyway’s schema-history connection may hold a transaction that can cause concurrent index operations to wait indefinitely (e.g., on a `virtualxid`), and docs/conventions.md forbids this pattern. If you need large production-table index replacement, pre-stage online index operations via the deployment pipeline (outside Flyway) rather than inside the migration; “ordinary” index replacement is acceptable for unreleased projections before production traffic.

Applied to files:

  • core/src/main/resources/db/migration/V33__graph_lifecycle_curation_and_cache.sql
🪛 ast-grep (0.44.1)
components/graph-rag-core/src/main/java/com/orgmemory/graphrag/cache/CanonicalCacheKeyHasher.java

[warning] 50-50: Use a randomly-generated IV
Context: byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
Note: [CWE-329] Generation of Predictable IV with CBC Mode.

(random-iv)

🪛 PMD (7.26.0)
apps/worker/src/main/java/com/orgmemory/worker/graph/GraphIndexingProcessor.java

[Medium] 212-213: PreserveStackTrace (Best Practices): Thrown exception does not preserve the stack trace of exception 'timeout' on all code paths

(PreserveStackTrace (Best Practices))

🪛 SQLFluff (4.2.2)
core/src/main/resources/db/migration/V33__graph_lifecycle_curation_and_cache.sql

[error] 101-102: CREATE INDEX should use CONCURRENTLY to avoid locking the table during the build.

(PG01)


[error] 144-145: CREATE INDEX should use CONCURRENTLY to avoid locking the table during the build.

(PG01)


[error] 271-279: CREATE INDEX should use CONCURRENTLY to avoid locking the table during the build.

(PG01)

🪛 Squawk (2.59.0)
core/src/main/resources/db/migration/V33__graph_lifecycle_curation_and_cache.sql

[warning] 2-2: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 3-3: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 13-13: Setting a column NOT NULL blocks reads while the table is scanned. Make the field nullable and use a CHECK constraint instead.

(adding-not-nullable-field)


[warning] 18-26: By default new constraints require a table scan and block writes to the table while that scan occurs. Use NOT VALID with a later VALIDATE CONSTRAINT call.

(constraint-missing-not-valid)


[warning] 27-37: By default new constraints require a table scan and block writes to the table while that scan occurs. Use NOT VALID with a later VALIDATE CONSTRAINT call.

(constraint-missing-not-valid)


[warning] 38-42: By default new constraints require a table scan and block writes to the table while that scan occurs. Use NOT VALID with a later VALIDATE CONSTRAINT call.

(constraint-missing-not-valid)


[warning] 43-53: By default new constraints require a table scan and block writes to the table while that scan occurs. Use NOT VALID with a later VALIDATE CONSTRAINT call.

(constraint-missing-not-valid)


[warning] 56-56: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 57-57: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 58-68: By default new constraints require a table scan and block writes to the table while that scan occurs. Use NOT VALID with a later VALIDATE CONSTRAINT call.

(constraint-missing-not-valid)


[warning] 70-72: During normal index creation, table updates are blocked, but reads are still allowed. Use concurrently to avoid blocking writes.

(require-concurrent-index-creation)


[warning] 76-76: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 77-77: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 78-78: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 79-79: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 80-80: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 81-81: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 82-82: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 107-107: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 108-108: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 111-111: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 112-112: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 113-113: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 114-114: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 115-115: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 116-116: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 117-117: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 150-150: Using 32-bit integer fields can result in hitting the max int limit. Use 64-bit integer values instead to prevent hitting this limit.

(prefer-bigint-over-int)


[warning] 183-183: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 184-184: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 185-185: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 186-186: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 192-192: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 202-202: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 206-206: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)


[warning] 207-207: Changing the size of a varchar field requires an ACCESS EXCLUSIVE lock, that will prevent all reads and writes to the table. Use a TEXT field with a CHECK constraint.

(prefer-text-field)

Comment on lines +216 to +231
record EvidenceRequest(
UUID organizationId,
UUID knowledgeAssetId,
UUID sourceRevisionId,
UUID chunkId,
UUID aclSnapshotId,
long aclGeneration) {

EvidenceReference toReference() {
return new EvidenceReference(
organizationId,
knowledgeAssetId,
sourceRevisionId,
chunkId,
aclSnapshotId,
aclGeneration);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not accept the evidence tenant from the request body.

organizationId is client-controlled and is copied directly into EvidenceReference. Derive the tenant from the authorized knowledge space or canonical evidence record instead, and remove it from this DTO; otherwise callers can submit cross-tenant evidence claims.

As per path instructions, “Reject identity, tenant, roles, or permissions supplied by request payloads.”

🤖 Prompt for 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.

In
`@apps/api/src/main/java/com/orgmemory/api/knowledge/KnowledgeGraphManagementController.java`
around lines 216 - 231, Remove organizationId from EvidenceRequest and stop
copying request-supplied tenant data in toReference(). Derive the
organization/tenant identifier from the authorized knowledge space or canonical
evidence record when constructing EvidenceReference, while preserving the
remaining request fields and rejecting cross-tenant claims.

Source: Path instructions

Comment on lines +27 to +38
static void requireValidEntry(Key key, Entry entry) {
Objects.requireNonNull(key, "key");
Objects.requireNonNull(entry, "entry");
var organizationId = key.snapshot().namespace().organizationId();
if (entry.evidence().stream()
.anyMatch(evidence ->
!organizationId.equals(evidence.organizationId()))) {
throw new IllegalArgumentException(
"retrieval cache evidence must belong to the key organization");
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C4 '\brequireValidEntry\s*\(' --type=java
rg -n -C6 'implements\s+RetrievalResultCache\b' --type=java

Repository: kl3inIT/OrgMemory

Length of output: 155


🏁 Script executed:

#!/bin/bash
set -u

echo "Files named RetrievalResultCache.java:"
fd -a 'RetrievalResultCache\.java$' . || true

echo
echo "All calls/usages of requireValidEntry:"
rg -n 'requireValidEntry\b' . || true

echo
echo "All RetrievalResultCache declarations/implementations:"
rg -n 'class .*implements .*RetrievelResultCache|class .*RetrievalResultCache|interface .*RetrievalResultCache|implements .*RetrievalResultCache' . --glob '*.java' || true

echo
echo "Interface file outline/content slices:"
for f in $(fd 'RetrievalResultCache\.java$' .); do
  echo "--- $f"
  wc -l "$f"
  ast-grep outline "$f" --view expanded || true
  sed -n '1,220p' "$f"
done

echo
echo "Relevant cache implementation files:"
rg -l 'class .*implements .*RetrievalResultCache\b|RetrievalResultCache' . --glob '*.java' | while read -r file; do
  echo "--- $file"
  wc -l "$file"
  rg -n -C5 'RetrievalResultCache|requireValidEntry|void put|Entry put|put\b|doPut|store|save|insert' "$file" || true
done

Repository: kl3inIT/OrgMemory

Length of output: 50373


Enforce requireValidEntry in the put() contract.

The validation is currently opt-in static helper; current implementations call it, but a future implementation could override put() without validation and bypass tenant scoping. Make validation mandatory before storage, e.g. via a default put(Key, Entry) that validates and delegates to an abstract/protected implementation method.

🤖 Prompt for 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.

In
`@components/graph-rag-core/src/main/java/com/orgmemory/graphrag/cache/RetrievalResultCache.java`
around lines 27 - 38, Make validation mandatory in the cache put contract by
changing the public put(Key, Entry) flow to call requireValidEntry before
storage and delegate to a separate abstract or protected implementation method.
Update existing implementations to override only that delegated storage method,
ensuring custom implementations cannot bypass tenant-scoping validation.

fields.put(
"targetEntity", relation.targetEntity().id().toString());
fields.put("type", relation.type());
fields.put("keywords", String.join("\u001f", relation.keywords()));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Use unambiguous framing for nested fingerprint inputs. Delimiter concatenation is not canonical for arbitrary text: for example, ["a\u001fb"] and ["a", "b"] produce the same keyword representation. This can turn distinct curation or projection payloads into the same idempotency fingerprint.

  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphCurationFingerprint.java#L45-L45: encode each keyword with length-prefixing or structured canonical serialization.
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/port/GraphProjectionManifest.java#L29-L59: frame each serialized entity/relation/embedding item before combining the collection.
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/port/GraphProjectionManifest.java#L74-L124: replace pipe and unit-separator joins with an escaped or length-prefixed field encoding.
📍 Affects 2 files
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphCurationFingerprint.java#L45-L45 (this comment)
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/port/GraphProjectionManifest.java#L29-L59
  • components/graph-rag-core/src/main/java/com/orgmemory/graphrag/port/GraphProjectionManifest.java#L74-L124
🤖 Prompt for 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.

In
`@components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphCurationFingerprint.java`
at line 45, The fingerprint construction uses ambiguous delimiter-based
concatenation for nested values. In
components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphCurationFingerprint.java#L45-L45,
update the keyword encoding to use length-prefixing or structured canonical
serialization. In
components/graph-rag-core/src/main/java/com/orgmemory/graphrag/port/GraphProjectionManifest.java#L29-L59,
frame each serialized entity, relation, and embedding item before combining the
collection; in `#L74-L124`, replace pipe and unit-separator joins with escaped or
length-prefixed field encoding, preserving deterministic output.

Comment on lines +85 to +122
record IdentityAlias(
UUID id,
ProjectionNamespace namespace,
GraphIdentityRef source,
GraphIdentityRef target,
CurationProvenance provenance)
implements GraphCurationRecord {

public IdentityAlias {
Objects.requireNonNull(id, "id");
Objects.requireNonNull(namespace, "namespace");
Objects.requireNonNull(source, "source");
Objects.requireNonNull(target, "target");
Objects.requireNonNull(provenance, "provenance");
if (source.kind() != target.kind()) {
throw new IllegalArgumentException(
"an identity alias cannot cross identity kinds");
}
if (source.equals(target)) {
throw new IllegalArgumentException(
"an identity alias must change the identity");
}
}
}

record IdentitySuppression(
UUID id,
ProjectionNamespace namespace,
GraphIdentityRef identity,
CurationProvenance provenance)
implements GraphCurationRecord {

public IdentitySuppression {
Objects.requireNonNull(id, "id");
Objects.requireNonNull(namespace, "namespace");
Objects.requireNonNull(identity, "identity");
Objects.requireNonNull(provenance, "provenance");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Bind aliases and suppressions to governing evidence.

IdentityAlias and IdentitySuppression cannot be filtered by governing evidence: unlike the other variants, they carry no EvidenceReference, and GraphCurationStore.append has no separate evidence input. This permits globally applicable curation controls without a source-ACL proof. Add governing evidence to both variants, validate its organization, and persist/filter it before applying the overlay.

🤖 Prompt for 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.

In
`@components/graph-rag-core/src/main/java/com/orgmemory/graphrag/curation/GraphCurationRecord.java`
around lines 85 - 122, Update the IdentityAlias and IdentitySuppression record
definitions in GraphCurationRecord to include a required EvidenceReference
governing-evidence field, validate that reference’s organization consistently
with the other curation variants, and propagate it through all construction and
persistence paths. Update GraphCurationStore.append and overlay filtering so
each alias or suppression is persisted and applied only when its governing
evidence is allowed; do not add a separate append evidence parameter.

Comment on lines +84 to +114
private static String markdown(GraphExportDocument document) {
StringBuilder output = new StringBuilder("# Graph export\n\n## Entities\n\n");
for (GraphExportDocument.EntityRow entity : document.entities()) {
output.append("- **").append(markdownEscape(entity.name())).append("** (`")
.append(entity.id()).append("`) — ")
.append(markdownEscape(entity.description())).append('\n');
}
output.append("\n## Relations\n\n");
for (GraphExportDocument.RelationRow relation : document.relations()) {
output.append("- `").append(relation.sourceEntityId()).append("` → `")
.append(relation.targetEntityId()).append("` — ")
.append(markdownEscape(relation.description())).append('\n');
}
return output.toString();
}

private static String text(GraphExportDocument document) {
StringBuilder output = new StringBuilder("ENTITIES\n");
for (GraphExportDocument.EntityRow entity : document.entities()) {
output.append(entity.id()).append('\t').append(entity.name()).append('\t')
.append(entity.description()).append('\n');
}
output.append("\nRELATIONS\n");
for (GraphExportDocument.RelationRow relation : document.relations()) {
output.append(relation.id()).append('\t')
.append(relation.sourceEntityId()).append(" -> ")
.append(relation.targetEntityId()).append('\t')
.append(relation.description()).append('\n');
}
return output.toString();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Preserve evidence provenance in Markdown and text exports.

These branches discard every row’s evidence, so consumers of these formats cannot audit the exported graph back to its authorized source material. Render deterministic evidence references for both entities and relations, as the JSON and CSV branches do.

🤖 Prompt for 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.

In
`@components/graph-rag-core/src/main/java/com/orgmemory/graphrag/export/GraphExportFormatter.java`
around lines 84 - 114, Update the markdown and text methods to include each
EntityRow and RelationRow’s evidence references, matching the deterministic
rendering and formatting already used by the JSON and CSV export branches.
Preserve the existing fields and ordering, and render evidence for both entity
and relation rows so provenance remains auditable.

Comment on lines +212 to +234
void failedCurrentJobCanResumeWithFreshRetryBudget() {
coordinator.claimNext("worker-a", Duration.ofMinutes(5)).orElseThrow();
while (job.getStatus() != GraphIndexJobStatus.FAILED) {
coordinator.fail(
job.getId(),
"worker-a",
"TRANSIENT",
"failure");
if (job.getStatus() == GraphIndexJobStatus.PENDING) {
job.claim(
"worker-a",
Instant.now(),
Duration.ofMinutes(5));
}
}

GraphIndexJobView resumed =
coordinator.resume(ORGANIZATION_ID, job.getId());

assertEquals("PENDING", resumed.status());
assertEquals(0, resumed.attempt());
assertTrue(!resumed.cancellationRequested());
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Bound the retry-until-FAILED loop.

The while (job.getStatus() != GraphIndexJobStatus.FAILED) loop has no iteration cap. If a future regression prevents the job from ever reaching FAILED, this test hangs instead of failing fast with a clear message.

🛡️ Proposed fix: cap iterations
-        while (job.getStatus() != GraphIndexJobStatus.FAILED) {
+        int guard = 0;
+        while (job.getStatus() != GraphIndexJobStatus.FAILED) {
+            if (guard++ > 10) {
+                fail("Job never reached FAILED status");
+            }
             coordinator.fail(
                     job.getId(),
                     "worker-a",
                     "TRANSIENT",
                     "failure");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
void failedCurrentJobCanResumeWithFreshRetryBudget() {
coordinator.claimNext("worker-a", Duration.ofMinutes(5)).orElseThrow();
while (job.getStatus() != GraphIndexJobStatus.FAILED) {
coordinator.fail(
job.getId(),
"worker-a",
"TRANSIENT",
"failure");
if (job.getStatus() == GraphIndexJobStatus.PENDING) {
job.claim(
"worker-a",
Instant.now(),
Duration.ofMinutes(5));
}
}
GraphIndexJobView resumed =
coordinator.resume(ORGANIZATION_ID, job.getId());
assertEquals("PENDING", resumed.status());
assertEquals(0, resumed.attempt());
assertTrue(!resumed.cancellationRequested());
}
void failedCurrentJobCanResumeWithFreshRetryBudget() {
coordinator.claimNext("worker-a", Duration.ofMinutes(5)).orElseThrow();
int guard = 0;
while (job.getStatus() != GraphIndexJobStatus.FAILED) {
if (guard++ > 10) {
fail("Job never reached FAILED status");
}
coordinator.fail(
job.getId(),
"worker-a",
"TRANSIENT",
"failure");
if (job.getStatus() == GraphIndexJobStatus.PENDING) {
job.claim(
"worker-a",
Instant.now(),
Duration.ofMinutes(5));
}
}
GraphIndexJobView resumed =
coordinator.resume(ORGANIZATION_ID, job.getId());
assertEquals("PENDING", resumed.status());
assertEquals(0, resumed.attempt());
assertTrue(!resumed.cancellationRequested());
}
🤖 Prompt for 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.

In
`@core/src/test/java/com/orgmemory/core/knowledge/GraphIndexingCoordinatorTests.java`
around lines 212 - 234, Bound the retry loop in
failedCurrentJobCanResumeWithFreshRetryBudget with a finite iteration cap, such
as a for-loop or explicit counter, while preserving the existing
fail-and-reclaim behavior. Ensure the test fails clearly if job.getStatus()
never reaches GraphIndexJobStatus.FAILED instead of hanging indefinitely.

Comment on lines +79 to +90
@Test
void deleteRetiresCanonicalLedgerThenRemovesDerivedGraphAndCaches() {
when(authorization.check(any()))
.thenReturn(AuthorizationDecision.allow("model-v1"));

service.delete(actor, ASSET_ID);

verify(ingestion).retire(ORGANIZATION_ID, ASSET_ID);
verify(graph).removeRevision(ORGANIZATION_ID, REVISION_ID);
verify(modelCache).invalidate(any());
verify(retrievalCache).invalidateNamespace(any());
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Verify the retire→remove→invalidate sequence, not just that each call happened.

Plain verify(...) doesn't confirm ingestion.retire completes before graph.removeRevision/cache invalidation run. If lifecycle ordering ever regresses, this test would still pass.

♻️ Proposed fix: assert sequencing with inOrder
         service.delete(actor, ASSET_ID);

-        verify(ingestion).retire(ORGANIZATION_ID, ASSET_ID);
-        verify(graph).removeRevision(ORGANIZATION_ID, REVISION_ID);
-        verify(modelCache).invalidate(any());
-        verify(retrievalCache).invalidateNamespace(any());
+        var order = org.mockito.Mockito.inOrder(ingestion, graph, modelCache, retrievalCache);
+        order.verify(ingestion).retire(ORGANIZATION_ID, ASSET_ID);
+        order.verify(graph).removeRevision(ORGANIZATION_ID, REVISION_ID);
+        order.verify(modelCache).invalidate(any());
+        order.verify(retrievalCache).invalidateNamespace(any());
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Test
void deleteRetiresCanonicalLedgerThenRemovesDerivedGraphAndCaches() {
when(authorization.check(any()))
.thenReturn(AuthorizationDecision.allow("model-v1"));
service.delete(actor, ASSET_ID);
verify(ingestion).retire(ORGANIZATION_ID, ASSET_ID);
verify(graph).removeRevision(ORGANIZATION_ID, REVISION_ID);
verify(modelCache).invalidate(any());
verify(retrievalCache).invalidateNamespace(any());
}
`@Test`
void deleteRetiresCanonicalLedgerThenRemovesDerivedGraphAndCaches() {
when(authorization.check(any()))
.thenReturn(AuthorizationDecision.allow("model-v1"));
service.delete(actor, ASSET_ID);
var order = org.mockito.Mockito.inOrder(ingestion, graph, modelCache, retrievalCache);
order.verify(ingestion).retire(ORGANIZATION_ID, ASSET_ID);
order.verify(graph).removeRevision(ORGANIZATION_ID, REVISION_ID);
order.verify(modelCache).invalidate(any());
order.verify(retrievalCache).invalidateNamespace(any());
}
🤖 Prompt for 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.

In
`@core/src/test/java/com/orgmemory/core/knowledge/KnowledgeAssetLifecycleServiceTests.java`
around lines 79 - 90, Update
deleteRetiresCanonicalLedgerThenRemovesDerivedGraphAndCaches to verify the
lifecycle calls with an InOrder verifier, asserting ingestion.retire occurs
before graph.removeRevision and both cache invalidations. Keep the existing
authorization setup and call arguments, but replace the independent interaction
checks with ordered verification.

Comment on lines +189 to +211
- name: Graph management is explicit and resource scoped
check:
- user: user:laura
object: knowledge_space:finance
assertions:
can_curate_graph: true
can_export_graph: true
- user: user:minh
object: knowledge_space:finance
assertions:
can_curate_graph: false
can_export_graph: false
- user: user:laura
object: knowledge_asset:finance-private-draft
assertions:
can_delete: true
can_rebuild: true
- user: user:minh
object: knowledge_asset:finance-private-draft
assertions:
can_delete: false
can_rebuild: false

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Add ListObjects coverage for the new permissions.

This adds negative Check cases, but no list_objects assertions for can_curate_graph, can_export_graph, can_delete, or can_rebuild. Add positive and empty-result cases for Laura and Minh so object enumeration cannot broaden access.

As per path instructions, “Require negative Check and ListObjects coverage for every new permission path.”

🤖 Prompt for 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.

In `@integrations/authorization-openfga/src/test/openfga/store.fga.yaml` around
lines 189 - 211, Extend the “Graph management is explicit and resource scoped”
test block with ListObjects assertions for can_curate_graph, can_export_graph,
can_delete, and can_rebuild. Add positive enumeration cases for Laura and
empty-result cases for Minh, covering both knowledge_space and knowledge_asset
resources without changing the existing Check assertions.

Source: Path instructions

Comment on lines +44 to +55
return transactions.execute(status -> {
String fingerprint = GraphCurationFingerprint.fingerprint(record);
GraphCurationRecord existing =
existing(record.namespace(), normalizedKey, fingerprint);
if (existing != null) {
return existing;
}
validateIdentities(record);
insert(normalizedKey, fingerprint, record);
return record;
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Idempotent append is not concurrency-safe on first insert.

existing(...) uses FOR UPDATE, but when no row yet exists there is nothing to lock. Two concurrent requests with the same idempotencyKey can both observe existing == null and both insert(...); the second write then fails the UNIQUE(organization_id, workspace, collection_name, idempotency_key) constraint (V33 L212-217) with a raw DataIntegrityViolationException instead of returning the stored record or a CurationConflictException. A retried identical curation therefore surfaces a 500 rather than an idempotent success.

Consider catching the duplicate-key violation and re-reading via existing(...) to preserve idempotency semantics.

🤖 Prompt for 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.

In
`@integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresGraphCurationStore.java`
around lines 44 - 55, The transaction in the idempotent append flow must handle
concurrent first inserts: update the logic around existing(...) and insert(...)
to catch a duplicate-key DataIntegrityViolationException, re-read the record
with existing(...), and return it when the fingerprint matches or raise
CurationConflictException for a conflicting record. Preserve the current
validation and normal insert behavior.

Comment on lines +39 to +128
@Override
public Optional<ModelInvocationCache.Entry> get(
ModelInvocationCache.Key key, Instant now) {
Objects.requireNonNull(key, "key");
Objects.requireNonNull(now, "now");
List<ModelInvocationCache.Entry> entries = jdbc.query("""
SELECT media_type, payload, created_at, expires_at
FROM graph_model_invocation_cache
WHERE organization_id = :organizationId
AND workspace = :workspace
AND collection_name = :collection
AND operation = :operation
AND input_hash = :inputHash
AND model_route_fingerprint = :modelRouteFingerprint
AND profile_fingerprint = :profileFingerprint
AND expires_at > :now
""",
modelKeyParameters(key).addValue("now", Timestamp.from(now)),
(resultSet, rowNumber) -> new ModelInvocationCache.Entry(
resultSet.getString("media_type"),
resultSet.getString("payload"),
resultSet.getTimestamp("created_at").toInstant(),
resultSet.getTimestamp("expires_at").toInstant()));
return entries.stream().findFirst();
}

@Override
public void put(
ModelInvocationCache.Key key, ModelInvocationCache.Entry entry) {
Objects.requireNonNull(key, "key");
Objects.requireNonNull(entry, "entry");
MapSqlParameterSource parameters = modelKeyParameters(key)
.addValue("mediaType", entry.mediaType())
.addValue("payload", entry.payload())
.addValue("createdAt", Timestamp.from(entry.createdAt()))
.addValue("expiresAt", Timestamp.from(entry.expiresAt()));
jdbc.update("""
INSERT INTO graph_model_invocation_cache (
organization_id,
workspace,
collection_name,
operation,
input_hash,
model_route_fingerprint,
profile_fingerprint,
media_type,
payload,
created_at,
expires_at
)
VALUES (
:organizationId,
:workspace,
:collection,
:operation,
:inputHash,
:modelRouteFingerprint,
:profileFingerprint,
:mediaType,
:payload,
:createdAt,
:expiresAt
)
ON CONFLICT (
organization_id,
workspace,
collection_name,
operation,
input_hash,
model_route_fingerprint,
profile_fingerprint
)
DO UPDATE SET
media_type = excluded.media_type,
payload = excluded.payload,
created_at = excluded.created_at,
expires_at = excluded.expires_at
""", parameters);
}

@Override
public void invalidate(ProjectionNamespace namespace) {
MapSqlParameterSource parameters = namespaceParameters(namespace);
jdbc.update("""
DELETE FROM graph_model_invocation_cache
WHERE organization_id = :organizationId
AND workspace = :workspace
AND collection_name = :collection
""", parameters);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

LGTM on the remaining cache logic (ModelInvocationCache read/write/invalidate, evidence upsert-in-transaction, key/parameter builders).

One operational note: expired rows in graph_model_invocation_cache/graph_retrieval_result_cache are only filtered at read time (expires_at > :now) and never proactively purged; over time both tables will grow unbounded except when a namespace is explicitly invalidated. Consider a scheduled cleanup job.

Also applies to: 190-399

🤖 Prompt for 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.

In
`@integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/PostgresGraphRagCacheStore.java`
around lines 39 - 128, Implement scheduled cleanup for expired rows in both
graph_model_invocation_cache and graph_retrieval_result_cache. Add a maintenance
method near the existing ModelInvocationCache read/write/invalidate logic that
deletes rows where expires_at is at or before the current time, and register it
with the application’s established scheduling mechanism; preserve namespace
invalidation behavior and use the existing JDBC access patterns.

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