Skip to content

feat: add ContextMemory entity (Context Center memories)#28224

Merged
pmbrull merged 18 commits into
open-metadata:mainfrom
pmbrull:feat/context-memory-entity
May 19, 2026
Merged

feat: add ContextMemory entity (Context Center memories)#28224
pmbrull merged 18 commits into
open-metadata:mainfrom
pmbrull:feat/context-memory-entity

Conversation

@pmbrull
Copy link
Copy Markdown
Member

@pmbrull pmbrull commented May 18, 2026

What

Adds ContextMemory as a first-class OpenMetadata entity: a reusable Context Center memory (question/answer/summary, lifecycle status, sharing/visibility, primary/related-entity links, parent/root memory hierarchy).

  • openmetadata-spec: entity/context/contextMemory.json + api/context/createContextMemory.json (org.openmetadata.schema.entity.context.* / api.context.*, @om-entity-type).
  • openmetadata-service: ContextMemoryRepository (clean EntityRepository, supportsSearch=false), thin ContextMemoryResource (standard CRUD at /v1/contextCenter/memories), ContextMemoryMapper, CollectionDAO.contextMemoryDAO(), Entity.CONTEXT_MEMORY.
  • bootstrap: context_memory table DDL (MySQL + Postgres) in native/2.0.1.
  • Test: ContextMemoryStatusTransitionTest (lifecycle status-transition rules) + ContextMemoryIT (integration CRUD).

Why

This entity was previously implemented downstream and is being upstreamed so the core platform owns the storage/CRUD model. AI/semantic-retrieval, vector embedding, and agent-injection layers are intentionally not included here — they remain a downstream concern that composes on top of this entity (thin resource, no search wiring).

Notes for reviewers

  • Table name is context_memory (not context_memory_entity). Deliberate: it matches the existing downstream table so existing deployments need no data-rename migration. Happy to add the _entity suffix + a rename migration if the project prefers strict convention.
  • Repository uses @Repository(name = "ContextMemoryRepository") (default priority) so downstream can override via the standard priority mechanism.
  • Resource is intentionally minimal (no entity-specific operations); supportsSearch=false.

🤖 Generated with Claude Code


Summary by Gitar

  • Search index monitoring:
    • Added countFailuresByJobId to CollectionDAO to exclude READER_RELATIONSHIP_WARNING events from job failure counts.
  • Repository improvements:
    • Optimized storeRelationships by removing blanket edge deletion to prevent accidental loss of domain-related relationships.
    • Implemented updateFromRelationships in entitySpecificUpdate to perform surgical, idempotent updates for entity links while maintaining proper audit logging.

This will update automatically on new commits.

@github-actions github-actions Bot added Ingestion safe to test Add this label to run secure Github workflows on PRs labels May 18, 2026
@pmbrull pmbrull marked this pull request as ready for review May 18, 2026 12:13
Copilot AI review requested due to automatic review settings May 18, 2026 12:13
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Upstream introduction of a new ContextMemory first-class entity for the Context Center: schema + create payload, repository/resource/mapper, CollectionDAO wiring, Entity.CONTEXT_MEMORY constant, MySQL/Postgres DDL in native/2.0.1, and a small unit test for status-transition rules. The entity is intentionally non-searchable and the resource exposes only standard CRUD at /v1/contextCenter/memories; AI/embedding concerns are left to downstream.

Changes:

  • Add entity/context/contextMemory.json and api/context/createContextMemory.json plus Entity.CONTEXT_MEMORY registration.
  • Add ContextMemoryRepository, ContextMemoryResource, ContextMemoryMapper, and CollectionDAO.contextMemoryDAO(); relationships and lifecycle status enforcement live in the repository.
  • Add MySQL/Postgres DDL for context_memory in 2.0.1/*/schemaChanges.sql and a unit test for validateStatusTransition.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
openmetadata-spec/src/main/resources/json/schema/entity/context/contextMemory.json New entity schema with enums (type/scope/status/source/visibility/role), share config, machine representation, hierarchy fields, and standard entity properties.
openmetadata-spec/src/main/resources/json/schema/api/context/createContextMemory.json Create payload; requires name, question, answer (inconsistent with the entity's required list).
openmetadata-service/src/main/java/org/openmetadata/service/Entity.java Registers CONTEXT_MEMORY constant.
openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java Adds ContextMemoryDAO and contextMemoryDAO() accessor backed by context_memory table.
openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ContextMemoryRepository.java Implements FQN, prepare/store/relationships, status-transition validation, and a minimal updater (does not maintain relationships on updates).
openmetadata-service/src/main/java/org/openmetadata/service/resources/context/ContextMemoryResource.java Thin EntityResource exposing standard CRUD/patch/version/restore endpoints.
openmetadata-service/src/main/java/org/openmetadata/service/resources/context/ContextMemoryMapper.java Maps CreateContextMemoryContextMemory, resolves domain FQNs and defaults owner to the caller.
bootstrap/sql/migrations/native/2.0.1/mysql/schemaChanges.sql Creates context_memory table for MySQL (deliberately non-_entity suffix).
bootstrap/sql/migrations/native/2.0.1/postgres/schemaChanges.sql Creates context_memory table and updatedAt index for Postgres.
openmetadata-service/src/test/java/org/openmetadata/service/resources/context/ContextMemoryResourceTest.java Unit tests of validateStatusTransition only — does not exercise the REST resource or repository CRUD.
Comments suppressed due to low confidence (1)

openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ContextMemoryRepository.java:227

  • storeRelationships is only invoked once at create time by EntityRepository.createNewEntityFlush; it is not re-invoked during PUT/PATCH updates. ContextMemoryUpdater.entitySpecificUpdate only calls recordChange for scalar fields and does not maintain the HAS/RELATED_TO relationships. As a result, when a client updates primaryEntity, relatedEntities, rootMemory, or parentMemory, the underlying entity_relationship rows are not updated: the old links remain and the new links are never persisted. The JSON payload appears updated but lineage / containment queries that rely on the relationships table will be incorrect. These fields need dedicated update handling in ContextMemoryUpdater (deleting stale relationships and adding new ones) or, at minimum, a re-derivation step in storeRelationships-on-update fashion.
  public class ContextMemoryUpdater extends EntityUpdater {
    public ContextMemoryUpdater(
        ContextMemory original, ContextMemory updated, Operation operation) {
      super(original, updated, operation);
    }

    @Override
    public void entitySpecificUpdate(boolean consolidatingChanges) {
      recordChange("title", original.getTitle(), updated.getTitle());
      recordChange("summary", original.getSummary(), updated.getSummary());
      recordChange("question", original.getQuestion(), updated.getQuestion());
      recordChange("answer", original.getAnswer(), updated.getAnswer());
      recordChange("memoryType", original.getMemoryType(), updated.getMemoryType());
      recordChange("memoryScope", original.getMemoryScope(), updated.getMemoryScope());

      // Validate lifecycle transition before recording status change
      if (original.getStatus() != null
          && updated.getStatus() != null
          && original.getStatus() != updated.getStatus()) {
        validateStatusTransition(original.getStatus(), updated.getStatus());
      }
      recordChange("status", original.getStatus(), updated.getStatus());

      recordChange("shareConfig", original.getShareConfig(), updated.getShareConfig());
    }
  }

Comment thread bootstrap/sql/migrations/native/2.0.1/mysql/schemaChanges.sql Outdated
@github-actions
Copy link
Copy Markdown
Contributor

⚠️ TypeScript Types Need Update

The generated TypeScript types are out of sync with the JSON schema changes.

Since this is a pull request from a forked repository, the types cannot be automatically committed.
Please generate and commit the types manually:

cd openmetadata-ui/src/main/resources/ui
./json2ts-generate-all.sh -l true
git add src/generated/
git commit -m "Update generated TypeScript types"
git push

After pushing the changes, this check will pass automatically.

…status msg, test name)

- storeRelationships: rootMemory -> Relationship.CONTAINS, parentMemory -> Relationship.HAS
  so the root-ancestor and direct-parent hierarchies are distinguishable.
- setFullyQualifiedName: derive from the immutable name only (drop mutable
  primaryEntity/owner derivation that destabilized nameHash on update).
- validateStatusTransition: separate "no transitions defined" from "disallowed transition".
- Rename ContextMemoryResourceTest -> ContextMemoryStatusTransitionTest (pure unit test).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

⚠️ TypeScript Types Need Update

The generated TypeScript types are out of sync with the JSON schema changes.

Since this is a pull request from a forked repository, the types cannot be automatically committed.
Please generate and commit the types manually:

cd openmetadata-ui/src/main/resources/ui
./json2ts-generate-all.sh -l true
git add src/generated/
git commit -m "Update generated TypeScript types"
git push

After pushing the changes, this check will pass automatically.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 18, 2026 12:41
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 11 comments.

Comment on lines +202 to +219
public void entitySpecificUpdate(boolean consolidatingChanges) {
recordChange("title", original.getTitle(), updated.getTitle());
recordChange("summary", original.getSummary(), updated.getSummary());
recordChange("question", original.getQuestion(), updated.getQuestion());
recordChange("answer", original.getAnswer(), updated.getAnswer());
recordChange("memoryType", original.getMemoryType(), updated.getMemoryType());
recordChange("memoryScope", original.getMemoryScope(), updated.getMemoryScope());

// Validate lifecycle transition before recording status change
if (original.getStatus() != null
&& updated.getStatus() != null
&& original.getStatus() != updated.getStatus()) {
validateStatusTransition(original.getStatus(), updated.getStatus());
}
recordChange("status", original.getStatus(), updated.getStatus());

recordChange("shareConfig", original.getShareConfig(), updated.getShareConfig());
}
Comment on lines +109 to +147
public void storeRelationships(ContextMemory entity) {
if (entity.getPrimaryEntity() != null) {
addRelationship(
entity.getPrimaryEntity().getId(),
entity.getId(),
entity.getPrimaryEntity().getType(),
CONTEXT_MEMORY_ENTITY,
Relationship.HAS);
}

for (var relatedEntity : listOrEmpty(entity.getRelatedEntities())) {
addRelationship(
relatedEntity.getId(),
entity.getId(),
relatedEntity.getType(),
CONTEXT_MEMORY_ENTITY,
Relationship.RELATED_TO);
}

// Distinct relationship types so the root-ancestor and direct-parent hierarchies
// can be resolved independently when read back from the relationship table.
if (entity.getRootMemory() != null) {
addRelationship(
entity.getRootMemory().getId(),
entity.getId(),
CONTEXT_MEMORY_ENTITY,
CONTEXT_MEMORY_ENTITY,
Relationship.CONTAINS);
}

if (entity.getParentMemory() != null) {
addRelationship(
entity.getParentMemory().getId(),
entity.getId(),
CONTEXT_MEMORY_ENTITY,
CONTEXT_MEMORY_ENTITY,
Relationship.HAS);
}
}
if (!nullOrEmpty(owners)) {
return owners;
}
return List.of(getEntityReferenceByName(Entity.USER, user, Include.NON_DELETED));
}
}
},
"required": ["name", "question", "answer"],
@@ -0,0 +1,221 @@
/*
* Copyright 2024 Collate
Comment on lines +65 to +70
@Path("/v1/contextCenter/memories")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Collection(name = "contextMemories")
public class ContextMemoryResource extends EntityResource<ContextMemory, ContextMemoryRepository> {
public static final String COLLECTION_PATH = "v1/contextCenter/memories/";
Comment on lines +79 to +85
ContextMemory rootMemory = Entity.getEntity(entity.getRootMemory(), "", Include.NON_DELETED);
entity.setRootMemory(rootMemory.getEntityReference());
}
if (entity.getParentMemory() != null) {
ContextMemory parentMemory =
Entity.getEntity(entity.getParentMemory(), "", Include.NON_DELETED);
entity.setParentMemory(parentMemory.getEntityReference());
Comment on lines +87 to +100
if (entity.getShareConfig() != null && entity.getShareConfig().getSharedWith() != null) {
entity
.getShareConfig()
.getSharedWith()
.forEach(
sharedPrincipal -> {
if (sharedPrincipal.getPrincipal() != null) {
EntityReference principal =
Entity.getEntityReference(
sharedPrincipal.getPrincipal(), Include.NON_DELETED);
sharedPrincipal.setPrincipal(principal);
}
});
}
"description": "Role granted to the principal.",
"$ref": "#/definitions/shareRole"
}
},
// FQN is the (immutable) memory name. Deriving it from mutable fields such as
// primaryEntity or owners would change nameHash on update, risking unique-constraint
// collisions and orphaned references. The link to primaryEntity/owners is captured
// via the relationship table instead.
Copilot AI review requested due to automatic review settings May 18, 2026 16:46
The context_memory table belongs in the 2.0.0 migration. Relocated the
MySQL and Postgres DDL verbatim; the 2.0.1 schemaChanges.sql files are
restored to their original task_migration_mapping-only content.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread bootstrap/sql/migrations/native/2.0.0/mysql/schemaChanges.sql Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 13 out of 15 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (2)

openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ContextMemoryRepository.java:281

  • The PUT createOrUpdate path goes through ContextMemoryMapper.createToEntity, which copies fields straight from CreateContextMemory without consulting the persisted entity. Optional fields that the client omits in the PUT body (e.g. status, memoryType, memoryScope, shareConfig, primaryEntity, summary, title, …) will arrive as null on the updated object. entitySpecificUpdate then calls recordChange(..., updated.get*()), which records and persists those nulls — silently wiping previously-set values on any partial PUT. This is especially problematic for status: the original != null && updated != null guard around validateStatusTransition means lifecycle enforcement is bypassed when the new status is null, and the recordChange immediately after blanks out the status field. Consider either documenting that PUT requires the full payload (and forcing required defaults in the mapper, e.g. status), or coalescing each field against original.get*() when the create payload value is null.
    public void entitySpecificUpdate(boolean consolidatingChanges) {
      recordChange("title", original.getTitle(), updated.getTitle());
      recordChange("summary", original.getSummary(), updated.getSummary());
      recordChange("question", original.getQuestion(), updated.getQuestion());
      recordChange("answer", original.getAnswer(), updated.getAnswer());
      recordChange("memoryType", original.getMemoryType(), updated.getMemoryType());
      recordChange("memoryScope", original.getMemoryScope(), updated.getMemoryScope());
      recordChange("sourceType", original.getSourceType(), updated.getSourceType());
      recordChange(
          "sourceConversation", original.getSourceConversation(), updated.getSourceConversation());
      recordChange(
          "sourceHumanMessage", original.getSourceHumanMessage(), updated.getSourceHumanMessage());
      recordChange(
          "sourceAssistantMessage",
          original.getSourceAssistantMessage(),
          updated.getSourceAssistantMessage());
      recordChange(
          "machineRepresentation",
          original.getMachineRepresentation(),
          updated.getMachineRepresentation());

      // Validate lifecycle transition before recording status change
      if (original.getStatus() != null
          && updated.getStatus() != null
          && original.getStatus() != updated.getStatus()) {
        validateStatusTransition(original.getStatus(), updated.getStatus());
      }
      recordChange("status", original.getStatus(), updated.getStatus());

      recordChange("shareConfig", original.getShareConfig(), updated.getShareConfig());

openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ContextMemoryRepository.java:142

  • setCreatorAsDefaultOwner calls Entity.getEntityReferenceByName(Entity.USER, entity.getUpdatedBy(), Include.NON_DELETED) unconditionally on every create. If the request comes from a bot/service principal that is not registered as a User (e.g., bot-only accounts, JWT subjects, or any caller whose principal name does not map 1:1 to a users row), this lookup will throw EntityNotFoundException and the POST will fail. Other resources that auto-assign the creating user typically guard this lookup (e.g., wrap in try/catch and fall through to leaving owners null, or skip when the principal is a bot). Consider gracefully skipping default-owner assignment when the principal cannot be resolved to a user.
  private void setCreatorAsDefaultOwner(ContextMemory entity, boolean update) {
    if (update || !nullOrEmpty(entity.getOwners())) {
      return;
    }
    entity.setOwners(
        List.of(
            Entity.getEntityReferenceByName(
                Entity.USER, entity.getUpdatedBy(), Include.NON_DELETED)));
  }

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 18, 2026

🟡 Playwright Results — all passed (12 flaky)

✅ 4140 passed · ❌ 0 failed · 🟡 12 flaky · ⏭️ 86 skipped

Shard Passed Failed Flaky Skipped
🟡 Shard 1 298 0 1 4
🟡 Shard 2 779 0 2 8
🟡 Shard 3 777 0 1 7
🟡 Shard 4 779 0 3 12
🟡 Shard 5 771 0 2 47
🟡 Shard 6 736 0 3 8
🟡 12 flaky test(s) (passed on retry)
  • Pages/AuditLogs.spec.ts › should apply both User and EntityType filters simultaneously (shard 1, 1 retry)
  • Features/KnowledgeCenter.spec.ts › Article mentions in description should working for Knowledge Center (shard 2, 1 retry)
  • Features/KnowledgeCenterTextEditor.spec.ts › Rich Text Editor - Text Formatting (shard 2, 1 retry)
  • Features/RTL.spec.ts › Verify Following widget functionality (shard 3, 1 retry)
  • Pages/CustomProperties.spec.ts › Should verify property name is visible for apiCollection in right panel (shard 4, 1 retry)
  • Pages/DataContracts.spec.ts › Create Data Contract and validate for MlModel (shard 4, 1 retry)
  • Pages/DataContractsSemanticRules.spec.ts › Validate Description Rule Is_Not_Set (shard 4, 1 retry)
  • Pages/Entity.spec.ts › Tag and Glossary Term preservation in column detail panel (shard 5, 1 retry)
  • Pages/ExplorePageRightPanel_KnowledgeCenter.spec.ts › Should remove user owner for knowledgeCenter (shard 5, 1 retry)
  • Pages/GlossaryImportExport.spec.ts › Glossary CSV import preserves typed relations (shard 6, 1 retry)
  • Pages/Lineage/LineageFilters.spec.ts › Verify lineage schema filter selection (shard 6, 1 retry)
  • Pages/TasksUIFlow.spec.ts › Verify task lifecycle in activity feed (shard 6, 1 retry)

📦 Download artifacts

How to debug locally
# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip    # view trace

Explicit engine clause, consistent with the task/search-index tables in the
same migration and robust to any server default change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 19, 2026 06:19
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 13 out of 15 changed files in this pull request and generated 5 comments.

Comments suppressed due to low confidence (1)

openmetadata-service/src/main/java/org/openmetadata/service/resources/context/ContextMemoryMapper.java:57

  • .withOwners(create.getOwners()) overwrites the validated owners list produced by EntityMapper.copy(...). Since storeOwners(...) does not validate that owner IDs/types exist, this can persist relationships to non-existent/deleted principals. Similarly, the explicit domains mapping duplicates the domain lookups already done by copy(...) (extra DB calls). Prefer relying on the copy(...)-populated owners/domains/tags values instead of re-setting them here.
        .withMachineRepresentation(create.getMachineRepresentation())
        .withOwners(create.getOwners())
        .withTags(create.getTags())
        .withDomains(
            nullOrEmpty(create.getDomains())
                ? null
                : create.getDomains().stream()
                    .map(
                        domain ->
                            getEntityReferenceByName(Entity.DOMAIN, domain, Include.NON_DELETED))
                    .toList());

Comment thread bootstrap/sql/migrations/native/2.0.0/postgres/schemaChanges.sql
Comment thread bootstrap/sql/migrations/native/2.0.0/mysql/schemaChanges.sql
…latedEntities

Review follow-ups:
- ContextMemoryMapper no longer re-sets description/owners/domains/tags/displayName
  after copy(). copy() sanitizes description (stored-XSS) and validates owners and
  domains; re-setting the raw request values bypassed both. Only ContextMemory-
  specific fields are set now.
- prepare() now assigns the result of EntityUtil.populateEntityReferences back onto
  relatedEntities so orphaned/invalid refs are filtered instead of persisted.
- ContextMemoryIT Javadoc now references ContextMemoryRepository#setCreatorAsDefaultOwner
  (the defaultOwners mapper method no longer exists).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@gitar-bot
Copy link
Copy Markdown

gitar-bot Bot commented May 19, 2026

Code Review ✅ Approved 7 resolved / 7 findings

Introduces the ContextMemory entity for standardized storage of Context Center memories, resolving issues with relationship integrity, FQN mutability, and status transition validation. Database schemas are updated for InnoDB compatibility and correct bootstrap migration.

✅ 7 resolved
Bug: rootMemory and parentMemory use same Relationship type, indistinguishable

📄 openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ContextMemoryRepository.java:140-154
Both rootMemory and parentMemory are stored with Relationship.RELATED_TO between two CONTEXT_MEMORY_ENTITY types. When querying relationships back (e.g., in setFields), there is no way to distinguish which relationship is "root" vs "parent" — they produce identical rows in the relationship table. Other repositories (e.g., FolderRepository) use distinct relationship types like Relationship.CONTAINS for parent-child hierarchies.

This will become a problem as soon as you need to hydrate these fields from the relationship table rather than from inline JSON.

Bug: FQN derived from mutable fields can break on update

📄 openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ContextMemoryRepository.java:62-76
The setFullyQualifiedName method computes the FQN from primaryEntity.fullyQualifiedName or owners[0].name, both of which can change over the entity's lifetime. If a user updates primaryEntity or owners, the FQN changes, the nameHash unique constraint can collide or leave orphaned references, and existing bookmarks/links break.

Other repositories (e.g., GlossaryTermRepository) derive FQN from fixed structural parents or fall back to entity.getName() only. Consider using a stable input (e.g., just the memory name, or owner at creation time stored separately) to avoid instability.

Edge Case: validateStatusTransition NPE when 'from' has no map entry

📄 openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ContextMemoryRepository.java:187-193
In validateStatusTransition, if VALID_TRANSITIONS.get(from) returns null (e.g., a new enum value is added in the future), the error message formats allowed as the literal string "null", which is confusing for API consumers. Additionally, the allowed == null branch conflates "unknown source status" with "no valid transitions" — consider throwing a more specific message.

Quality: Test class should be in integration-tests module per conventions

📄 openmetadata-service/src/test/java/org/openmetadata/service/resources/context/ContextMemoryResourceTest.java:11
Per project conventions, integration tests must reside in openmetadata-integration-tests/ and use *IT.java naming. The current test at openmetadata-service/src/test/.../ContextMemoryResourceTest.java is a pure unit test (no server/DB), so it's fine where it is, but the class name ContextMemoryResourceTest suggests it tests the resource (HTTP layer). Consider renaming to ContextMemoryStatusTransitionTest to better reflect its scope, and adding an actual ContextMemoryResourceIT integration test in the correct module for CRUD coverage.

Bug: deleteTo with null fromEntityType may wipe domain relationships

📄 openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ContextMemoryRepository.java:153
At line 153, deleteTo(entity.getId(), Entity.CONTEXT_MEMORY, Relationship.HAS, null) deletes ALL HAS relationships pointing to this entity regardless of source entity type. The base framework stores domain→entity associations using Relationship.HAS (see EntityRepository.storeDomains). If storeRelationships executes after storeDomains during an update cycle, domain assignments will be silently deleted.

Since primaryEntity can be any entity type (making it hard to enumerate fromEntityTypes), consider restricting the parentMemory cleanup to Entity.CONTEXT_MEMORY specifically and handling primaryEntity cleanup with a query-then-delete approach, or switching primaryEntity/parentMemory to use a different, non-colliding relationship type.

...and 2 more resolved from earlier reviews

Options

Display: compact → Showing less information.

Comment with these commands to change:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@pmbrull
Copy link
Copy Markdown
Member Author

pmbrull commented May 19, 2026

Copilot review disposition (commit 04dad90da3)

# Item Action
1 mapper re-set description/owners/domains/tags/displayName after copy() Fixed — security-relevant. copy() sanitizes description (stored-XSS) and validates owners/domains; the mapper now sets only ContextMemory-specific fields
2 populateEntityReferences(relatedEntities) return ignored Fixed — result assigned back so orphaned/invalid refs are filtered, not persisted
3 / 4 move DDL out of 2.0.0 into 2.0.1 Declined (replied in-thread) — 2.0.0 is the current unreleased in-development migration, not immutable yet; placing it here is deliberate and ensures every deployment applies it
5 IT Javadoc cited ContextMemoryMapper#defaultOwners Fixed — now references ContextMemoryRepository#setCreatorAsDefaultOwner
suppressed owners/domains/tags re-set in mapper Fixed — same root cause as #1

@sonarqubecloud
Copy link
Copy Markdown

@sonarqubecloud
Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ingestion safe to test Add this label to run secure Github workflows on PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants