Skip to content

fix(ui): align glossary term Related Terms section inside the left panel#29284

Merged
anuj-kumary merged 3 commits into
mainfrom
issue-29283
Jun 22, 2026
Merged

fix(ui): align glossary term Related Terms section inside the left panel#29284
anuj-kumary merged 3 commits into
mainfrom
issue-29283

Conversation

@anuj-kumary

@anuj-kumary anuj-kumary commented Jun 22, 2026

Copy link
Copy Markdown
Member

Describe your changes:

Fixes #29283

Screenshot 2026-06-22 at 2 24 42 PM

Type of change:

  • Bug fix
  • Improvement
  • New feature
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation

High-level design:

N/A — small change.

Tests:

Use cases covered

Unit tests

Backend integration tests

Ingestion integration tests

Playwright (UI) tests

Manual testing performed

UI screen recording / screenshots:

Not applicable.

Checklist:

  • I have read the CONTRIBUTING document.
  • My PR title is Fixes <issue-number>: <short explanation>
  • My PR is linked to a GitHub issue via Fixes #<issue-number> above.
  • I have commented on my code, particularly in hard-to-understand areas.
  • For JSON Schema changes: I updated the migration scripts or explained why it is not needed.
  • For UI changes: I attached a screen recording and/or screenshots above.
  • I have added tests (unit / integration / Playwright as applicable) and listed them above.

Summary by Gitar

  • UI layout changes:
    • Moved RELATED_TERMS widget from the main page layout into the left panel container within CustomizeGlossaryTermBaseClass.ts.
  • Testing:
    • Added comprehensive unit tests in CustomizeGlossaryTermBaseClass.test.ts to verify the hierarchical placement and layout configuration of the RELATED_TERMS widget.

This will update automatically on new commits.

Greptile Summary

This PR fixes a visual alignment issue where the Related Terms section was rendered outside the glossary term left panel. The fix moves the RELATED_TERMS widget from a standalone top-level grid entry into the LEFT_PANEL children array, and a new test file verifies the corrected placement.

  • CustomizeGlossaryTermBaseClass.ts: RELATED_TERMS is removed from the top-level layout (was w: 6, y: 7) and inserted as the last child of the LEFT_PANEL container at y: 3, w: 1. The panel's existing h: 7 is sufficient — the deepest child now bottoms out at row 5.
  • CustomizeGlossaryTermBaseClass.test.ts: New unit tests assert that RELATED_TERMS is absent from the top-level layout, present as a left-panel child with full width, and positioned after TAGS in the children array.

Confidence Score: 5/5

Safe to merge — the change is a single widget relocation with no logic side-effects and is covered by new unit tests.

The diff moves one widget entry from a top-level array slot into the LEFT_PANEL children array. The parent container height (h:7) already accommodates the new child (deepest child bottoms at row 5). No data-flow, API surface, or business logic is touched. New tests confirm the corrected placement.

No files require special attention.

Important Files Changed

Filename Overview
openmetadata-ui/src/main/resources/ui/src/utils/CustomizeGlossaryTerm/CustomizeGlossaryTermBaseClass.ts Moves RELATED_TERMS widget from a standalone top-level entry (w=6, y=7) into the LEFT_PANEL children array (w=1, y=3). The left-panel height (h=7) comfortably contains all children including the new entry (deepest child bottom is at y=5).
openmetadata-ui/src/main/resources/ui/src/utils/CustomizeGlossaryTerm/CustomizeGlossaryTermBaseClass.test.ts New test file covering left-panel containment of RELATED_TERMS: verifies it is not a top-level widget, is a left-panel child, has full width (w=1), and appears after TAGS in the children array.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[getDefaultWidgetForTab - OVERVIEW] --> B[LEFT_PANEL container\nh:7, w:6, x:0, y:0]
    A --> C[WORKFLOW_HISTORY\nw:2, x:6, y:0]
    A --> D[DOMAIN\nw:2, x:6, y:1]
    A --> E[OWNER\nw:2, x:6, y:2]
    A --> F[REVIEWER\nw:2, x:6, y:3]
    A --> G[CUSTOM_PROPERTIES\nw:2, x:6, y:4]

    B --> B1[DESCRIPTION\nw:1, y:0]
    B --> B2[SYNONYMS\nw:0.5, y:1]
    B --> B3[REFERENCES\nw:0.5, y:2]
    B --> B4[TAGS\nw:0.5, y:2]
    B --> B5[RELATED_TERMS moved here\nw:1, y:3]

    style B5 fill:#d4edda,stroke:#28a745
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[getDefaultWidgetForTab - OVERVIEW] --> B[LEFT_PANEL container\nh:7, w:6, x:0, y:0]
    A --> C[WORKFLOW_HISTORY\nw:2, x:6, y:0]
    A --> D[DOMAIN\nw:2, x:6, y:1]
    A --> E[OWNER\nw:2, x:6, y:2]
    A --> F[REVIEWER\nw:2, x:6, y:3]
    A --> G[CUSTOM_PROPERTIES\nw:2, x:6, y:4]

    B --> B1[DESCRIPTION\nw:1, y:0]
    B --> B2[SYNONYMS\nw:0.5, y:1]
    B --> B3[REFERENCES\nw:0.5, y:2]
    B --> B4[TAGS\nw:0.5, y:2]
    B --> B5[RELATED_TERMS moved here\nw:1, y:3]

    style B5 fill:#d4edda,stroke:#28a745
Loading

Reviews (2): Last reviewed commit: "fix lint checks" | Re-trigger Greptile

@anuj-kumary anuj-kumary self-assigned this Jun 22, 2026
@anuj-kumary anuj-kumary requested a review from a team as a code owner June 22, 2026 08:59
@anuj-kumary anuj-kumary added UI UI specific issues safe to test Add this label to run secure Github workflows on PRs labels Jun 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 62%
62.27% (66684/107086) 44.06% (37350/84753) 45.39% (11208/24690)

@sonarqubecloud

Copy link
Copy Markdown

@anuj-kumary anuj-kumary enabled auto-merge (squash) June 22, 2026 10:13
@github-actions

Copy link
Copy Markdown
Contributor

🟡 Playwright Results — all passed (15 flaky)

✅ 4315 passed · ❌ 0 failed · 🟡 15 flaky · ⏭️ 88 skipped

Shard Passed Failed Flaky Skipped
🟡 Shard 1 301 0 1 4
✅ Shard 2 815 0 0 9
🟡 Shard 3 813 0 3 8
🟡 Shard 4 867 0 2 12
🟡 Shard 5 731 0 2 47
🟡 Shard 6 788 0 7 8
🟡 15 flaky test(s) (passed on retry)
  • Pages/Roles.spec.ts › Roles page should work properly (shard 1, 1 retry)
  • Features/KnowledgeCenterTextEditor.spec.ts › Rich Text Editor - Text Formatting (shard 3, 1 retry)
  • Features/UserProfileOnlineStatus.spec.ts › Should show online status badge on user profile for active users (shard 3, 1 retry)
  • Flow/ExploreAggregationCountsMatching.spec.ts › should verify left panel counts and tab search results for normal search (shard 3, 2 retries)
  • Pages/CustomProperties.spec.ts › Date Time (shard 4, 1 retry)
  • Pages/CustomProperties.spec.ts › Time Interval (shard 4, 1 retry)
  • Pages/Entity.spec.ts › User as Owner with unsorted list (shard 5, 1 retry)
  • Pages/ExplorePageRightPanel_KnowledgeCenter.spec.ts › Should remove user owner for knowledgeCenter (shard 5, 2 retries)
  • Pages/Glossary.spec.ts › Glossary Term Update in Glossary Page should persist tree (shard 6, 1 retry)
  • Pages/Lineage/LineageFilters.spec.ts › Verify lineage schema filter selection (shard 6, 1 retry)
  • Pages/Lineage/LineageRightPanel.spec.ts › Verify custom properties tab IS visible for supported type: searchIndex (shard 6, 1 retry)
  • Pages/Lineage/PlatformLineage.spec.ts › Verify domain platform view (shard 6, 1 retry)
  • Pages/ODCSImportExport.spec.ts › Schema validation passes for contract without schema definition (shard 6, 1 retry)
  • Pages/ServiceEntity.spec.ts › Tier Add, Update and Remove (shard 6, 1 retry)
  • Pages/ServiceEntity.spec.ts › Tier Add, Update and Remove (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

@anuj-kumary anuj-kumary merged commit b55ab75 into main Jun 22, 2026
44 checks passed
@anuj-kumary anuj-kumary deleted the issue-29283 branch June 22, 2026 12:00
@gitar-bot

gitar-bot Bot commented Jun 22, 2026

Copy link
Copy Markdown
Code Review ✅ Approved

Aligns the Related Terms section within the glossary term left panel by updating the layout configuration. No issues found.

Options

Display: compact → Showing less information.

Comment with these commands to change:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

pmbrull pushed a commit that referenced this pull request Jun 22, 2026
…nel (#29284)

* fix(ui): align glossary term Related Terms section inside the left panel

* added unit test

* fix lint checks
harshach added a commit that referenced this pull request Jun 23, 2026
…cs (#29201)

* feat(ai): add DERIVED_FROM, Metric.provider, ContextMemory.ontologyStats, AISettings schema

- Append DERIVED_FROM as last entry in entityRelationship.json enum (ordinal-safe)
- Add provider (ProviderType) field to metric.json and createMetric.json
- Add ontologyStats definition and property to contextMemory.json (OntologyStats javaType)
- Create configuration/aiSettings.json (AISettings, MemoryExtractionSettings, OntologyAgentSettings, PromptConfig, AIPrompts, AIDeletionPolicy)
- Register aiSettings in settings.json enum and config_value oneOf
- Add default seed openmetadata-service/src/main/resources/json/data/settings/aiSettings.json

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

* feat(ai): AISettings handler, cache seed/merge, and system settings REST branch

Implements Task 2: AISettingsHandler (validate + merge), SettingsCache
seed/merge block for aiSettings.json mirroring searchSettings, and
SystemResource PUT branch + reset extension for AI_SETTINGS.

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

* fix(ai): propagate AISettings default-load IO errors and single-return reset

- loadDefaultAiSettings now declares throws IOException instead of
  swallowing it with an empty AISettings fallback; call sites in
  createOrUpdateSetting and resetSettingToDefault wrap with try/catch
  and re-throw as SystemSettingsException (matching the searchSettings
  error-handling pattern)
- resetSettingToDefault refactored to if/else-if/else with a single
  trailing return, eliminating the two early returns
- AISettingsHandlerTest: add incomingNullReturnsDefaults and
  nullNestedDefaultInheritsIncoming tests covering null-guard branches

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

* feat(ai): gate file/page extraction on AISettings and externalize the extraction prompt

- Add AISettingsUtil: cache-backed AISettings accessor (never null, fails open)
  with isFileExtractionEnabled, isPageExtractionEnabled, isOntologyAgentEnabled,
  memoryExtractionPrompt, ontologyAgentPrompt helpers
- ContextMemoryExtractor.callLlm resolves the system prompt from AISettings at
  runtime, falling back to the SYSTEM_PROMPT constant
- ContextFileProcessingService.process and fileStatusAfterText gate on
  AISettingsUtil.isFileExtractionEnabled in addition to LLMClientHolder.isEnabled
- KnowledgePageRepository.schedulePillExtraction gates on
  AISettingsUtil.isPageExtractionEnabled in addition to LLMClientHolder.isEnabled
- TDD: AISettingsUtilTest written first (RED), then implementation (GREEN)

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

* feat(ai): ontology verdict DTO boundary

Add OntologyDerivation, OntologyVerdict (Jackson record DTOs with @JsonProperty on every component and @JsonIgnoreProperties), and OntologyAction constants (REUSE/CREATE/SKIP) as the anti-corruption boundary between untrusted LLM JSON and the domain model. Mirrors the KnowledgePill pattern. Covered by OntologyDerivationTest (lenient parse, unknown-field tolerance).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ai): ontology grounding + extractor (pure derive)

Add OntologyContext/OntologyCandidate records, OntologyExtractor (calls LLM via completeStructured, returns SKIP/SKIP on empty result), OntologyPromptBuilder (renders memory + candidate lists), and OntologyGrounding (keyword-searches glossary-term/metric/glossary indexes via Entity.getSearchRepository, caps at 20, fails-safe per axis). Covered by OntologyExtractorTest (mocked LLM, two cases: verdict passthrough + empty-→SKIP/SKIP).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ai): use existing search/entity constants, dedupe helper, shorten prompt builder

Replace raw "_score"/"desc" with SearchConstants.DEFAULT_SORT_FIELD/DEFAULT_SORT_ORDER,
remove local FIELD_NAME/FIELD_DESCRIPTION in favour of Entity constants, dedupe
nullToEmpty via StringUtils.defaultString, extract renderMemory() so build() fits 15
lines, use CommonUtil.nullOrEmpty for the candidates guard, and replace raw "CREATE"/"SKIP"
string literals in tests with OntologyAction constants.

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

* feat(ai): ontology reconciler with ownership lifecycle (create/reuse/retire/cascade)

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

* fix(ai): correct RELATED_TO edge direction, honor deletionPolicy on re-derive, no-op on all-SKIP

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

* feat(ai): ontology processing engine with throttle and hash-gate

Adds OntologyProcessingEngine: a trailing-throttle debounce (mirroring
PageContextProcessingEngine) that collapses rapid memory edits into one
derivation run, protected by a SHA-256 content hash-gate so unchanged
memories are never re-derived. stampOntologyStats persists via
recordChange(updateVersion=false) in ContextMemoryUpdater, exactly
mirroring KnowledgePageRepository.recordExtractionStats, so no version
churn occurs and no postUpdate event fires for the stats field.

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

* fix(ai): accurate recursion-contract docs, split term/metric stat counts, dedup scheduler

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

* feat(ai): ContextMemory lifecycle hooks trigger the ontology agent + cascade

postCreate/postUpdate schedule the OntologyProcessingEngine (gated on
AISettingsUtil.isOntologyAgentEnabled). softDelete/hardDelete/restore
AdditionalChildren fire in-edge-window so DERIVED_FROM edges exist when
OntologyReconciler.onMemoryDeleted/onMemoryRestored run. Extracted
AISettingsUtil.deletionPolicy() to remove duplication between the engine
and the repository. Added OntologyReconciler.onMemoryRestored which
restores CASCADE-soft-deleted automation-owned entities (Include.ALL
query; ORPHAN/DEPRECATE-released entities correctly excluded because
their DERIVED_FROM edges were dropped at delete time). Three new unit
tests cover: owned restore, human-adopted skip, orphan-released skip.

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

* feat(ai): ontology-bot seed and adopt-on-touch provider release guards

Adds the ontology-bot principal seed files (bot + botUser JSON, auto-loaded
by BotResource.initialize at startup), the OntologyOwnership utility class
(centralized ONTOLOGY_BOT_NAME constant + releaseIfHumanEdited guard), and
wires the guard into the entitySpecificUpdate of GlossaryTermUpdater,
MetricUpdater and GlossaryUpdater so a human PATCH that changes an
agent-managed field flips provider AUTOMATION → USER permanently.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ai): AISettings admin page with toggles and prompt editors

Adds the AISettingsPage under Settings > Preferences with master enable
toggle, memory-extraction and ontology-agent toggles, a deletion-policy
Select, and two system-prompt Textareas. Registers the route, menu item,
and all i18n keys (synced to all 17 locales).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ai): i18n the AISettings deletion-policy option labels

Replace raw string literals ('cascade'/'orphan'/'deprecate') in
DELETION_POLICY_OPTIONS with i18n labelKey fields; add new keys
label.cascade / label.deprecate / label.orphan to en-us.json (alphabetical)
and sync all 17 other locales via yarn i18n.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ai): derived-entity provenance projections on memory/term/metric

Add three read-only derived fields (non-default, resolved at read, never
persisted) to expose File→Memory→Term/Metric provenance:

- ContextMemory.derivedEntities (entityReferenceList): terms+metrics
  created by the Ontology Agent via DERIVED_FROM edges
  (findFrom(memory, CONTEXT_MEMORY, DERIVED_FROM, GLOSSARY_TERM/METRIC))
- ContextMemory.reusedEntities (entityReferenceList): terms+metrics
  reused via RELATED_TO edges
  (findTo(memory, CONTEXT_MEMORY, RELATED_TO, GLOSSARY_TERM/METRIC))
- GlossaryTerm.derivedFrom (entityReference): memory that created the term
  (findTo(term, GLOSSARY_TERM, DERIVED_FROM, CONTEXT_MEMORY))
- Metric.derivedFrom (entityReference): memory that created the metric
  (findTo(metric, METRIC, DERIVED_FROM, CONTEXT_MEMORY))

Edge directions verified against OntologyReconciler Task-6 code:
addDerivedFromEdge stores from=entity→to=memory; reuse() stores
from=memory→to=entity for RELATED_TO.

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

* test(ai): integration tests for AISettings + ontology agent lifecycle

AISettingsResourceIT: deterministic GET/PUT/reset tests for /system/settings/aiSettings
covering default values (enabled=true, deletionPolicy=cascade), PUT persistence, and reset.

OntologyAgentIT: deterministic lifecycle tests seeding DERIVED_FROM edges via in-process
repository to replicate the reconciler's CREATE path, then driving cascade delete,
adopt-on-touch (provider flip), and derivedFrom/derivedEntities projection fields through
the public REST API without any LLM dependency.

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

* feat(ai): File→Memory→Term/Metric provenance panels in the UI

- Add derivedEntities/reusedEntities to ContextMemory generated type
- Add derivedFrom to GlossaryTerm and Metric generated types
- Add DERIVED_FROM/DERIVED_ENTITIES/REUSED_ENTITIES to TabSpecificField enum
- Add getContextMemoryById to contextMemoryAPI
- Create DerivedOntologyCard component (+ interface + test): fetches
  derivedEntities/reusedEntities from a memory and renders linked lists
- Embed DerivedOntologyCard into CreateMemoryModal view-only mode so
  every memory's derived/reused ontology is visible when viewing a memory
- Add derivedFrom field to GLOSSARY_TERM_DEFAULT_FIELDS and METRIC_DEFAULT_FIELDS
- Add "Derived from memory" link in GlossaryTermsV1 and MetricDetails when
  derivedFrom is present on the entity
- Add i18n keys: label.derived-from-memory, label.derived-ontology,
  label.reused, message.no-derived-ontology (synced to 19 locales)

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

* fix(ai): seed/serve aiSettings correctly so GET /system/settings/aiSettings returns defaults

CollectionDAO$SettingsRowMapper.getSettings lacked an AI_SETTINGS case in its
switch, causing every getConfigWithKey("aiSettings") call to throw
IllegalArgumentException (swallowed, returning null) → HTTP 204 on every GET.
Added the missing case so aiSettings rows deserialise to AISettings.

Also fixed resetSettingToDefault for AI_SETTINGS: it was returning defaults
without persisting them to the DB (unlike the equivalent searchSettings reset
path which calls systemRepository.createOrUpdate). Now it persists first.

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

* fix(ai): seed AUTOMATION provider via createInternal and set id/updatedAt so cascade, adopt-on-touch, and projection ITs all pass

The test helper createAutomationTerm used create-via-REST + termRepo.update to
set provider=AUTOMATION, but the EntityRepository update framework only writes to
DB when entityChanged=true; provider is not tracked by recordChange so the write
was silently no-op'd. Term stayed provider=null in the DB, causing:
- Scenario A timeout: isAutomationOwned read null, cascade skipped the term
- Scenario B: assertEquals(USER, null) failed at assertion after human PATCH
- Scenario C: cascade skipped term, glossary.delete failed with "glossary is not empty"

Fix: replace the two-step seed with termRepo.createInternal() directly, mirroring
OntologyReconciler.createTerm exactly. Also fixed the reconciler itself: createTerm,
createMetric, and resolveOrMintGlossary all called createInternal without setting id
or updatedAt; PostgreSQL GENERATED columns extract both from the stored JSON with
NOT NULL constraints, so omitting them would cause constraint failures in production.

Result: OntologyAgentIT 3/3 GREEN; OntologyReconcilerTest 20/20 + OntologyOwnershipTest
7/7 no regression.

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

* fix(ai): wire AISettings reset-to-default, strengthen util test, import @transaction

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

* docs(ai): document AISettings + Ontology Agent (§19) in the company-context spec

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

* Update generated TypeScript types

* fix(ai): use design tokens and carry source-memory identity in provenance links

Replace palette classes (text-gray-500/400/900, text-brand-600) with semantic
tokens (text-tertiary, text-brand-secondary). Link both provenance anchors to
ROUTES.CONTEXT_CENTER_MEMORIES?memory=<name> so the memories-list auto-opens
the correct memory's view modal on arrival.

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

* fix(ai): gate per-axis derive toggles, validate LLM entity names, narrow catches

- OntologyReconciler: add 5-arg reconcile() with explicit deriveTerms/deriveMetrics
  flags; reconcileAxis() gates BOTH apply and retire per flag, preventing mass-retire
  bug in the reviewer's suggested null-implied approach
- OntologyReconciler: isValidName() guards CREATE paths for term, metric, glossary
  mint — null/blank or FQN-reserved chars (. " /) become a logged SKIP instead of
  thrown exception (Fix #2+#6)
- OntologyProcessingEngine: read AISettings once in derive(), compute axis flags via
  deriveTermsEnabled/deriveMetricsEnabled helpers, pass to 5-arg reconcile() — no
  more settings coupling inside reconciler
- Narrow Exception catches: AISettingsUtil → RuntimeException; OntologyGrounding →
  IOException|RuntimeException; OntologyProcessingEngine#runScheduled → RuntimeException
  with explanatory comment (Fix #5)
- OntologyReconcilerTest: 4 new tests covering null/invalid-name SKIP behavior
  (27 total, 0 failures)
- OntologyAgentIT Scenario E: fully deterministic axis-toggle coverage — seeds
  AUTOMATION-owned metric, calls 5-arg reconcile with deriveMetrics=false, asserts
  0 metrics created + 0 retired + owned metric survives (4 tests, 0 failures)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ai): stamp content hash on reconcile failure to prevent re-derive poison-pill loop

Wrap reconciler.reconcile() in reconcileSafely() which catches RuntimeException,
logs the error with memory id, and returns null so the derive() method always
reaches stampOntologyStats(). buildStats() is made null-safe (zero counts when
result is null). LLM/network stages (fetchCandidates, extractor.derive) remain
outside the guard so transient failures still propagate and retry. Adds
stampsHashEvenWhenReconcileThrows test to prove the loop is broken.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ai): fetch memories list with generic sourceEntity, not deprecated sourceFile

Page-sourced memories resolve their source via the generic sourceEntity ref;
the deprecated sourceFile alias only covers file-sourced ones.

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

* fix(ai): reuse existing term/metric on FQN collision and cancel pending run on memory delete

P1: OntologyReconciler.createTerm/createMetric now precheck the target FQN via
findByFqn(repo, fqn) (NON_DELETED) before calling createInternal; on collision
they call reuseExisting() (same RELATED_TO edge + counts.reused++) instead of
throwing a unique-constraint violation. resolveOrMintGlossary also checks by
newGlossaryName before minting a duplicate glossary. Three new unit tests cover
the term-FQN-collision, metric-FQN-collision, and glossary-reuse-by-name paths.

P2: ContextMemoryRepository.softDeleteAdditionalChildren/hardDeleteAdditionalChildren
now call OntologyProcessingEngine.instance().cancel(memoryId) via a shared
cancelAndCascadeOntology helper before cascadeOntology, so any pending scheduled
derivation is cancelled when a memory is deleted, preventing spurious
EntityNotFoundException in runScheduled.

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

* fix(ai): null-guard the LLM verdict list in OntologyExtractor.derive

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

* fix(ui): align AISettingsPage to codegen DeletionPolicy enum name

The TS-codegen bot regenerated aiSettings.ts exporting the enum as
DeletionPolicy (from the deletionPolicy schema key), not AIDeletionPolicy.

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

* feat(ai): centralize MCP Server and MCP Chat into AI Settings

MCP Server and MCP Chat are no longer internal Applications; they are managed
as platform settings.

- MCP Chat: new aiSettings.mcpChat (enabled + systemPrompt) drives a runtime
  McpChatServiceHolder, re-initialized on AI settings save/reset so chat
  toggles without a restart; McpClientResource reads the holder.
- MCP Server: registerMCPServer gates on mcpConfiguration.enabled (seeded by
  default) instead of an installed app; configurable via /system/mcp/config.
- McpApplicationBot seeded as a system bot with impersonation; McpApplication
  and McpChatApplication entities, marketplace defs and the mcpChatAppConfig
  schema removed.
- MCP usage telemetry re-anchored to a constant identity (read history kept).
- AI Settings page adds MCP Chat and MCP Server sections; chat sidebar gated
  on the setting.
- 2.0.0 migration carries prior app config/enablement into settings, then
  retires the apps (keeping the bots).

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

* Update generated TypeScript types

* refactor(ai): rename Ontology Agent to Memory Agent

Rename the agent's identity from "Ontology Agent" to "Memory Agent"
across all layers: the drive/ontology Java package and its Ontology*
pipeline/DTO classes, OntologyOwnership, the ontology-bot principal,
the aiSettings ontologyAgent config key, the ContextMemory
ontologyStats/OntologyProcessingStatus fields, the OntologyStatusBadge
UI component, and the ontology-agent i18n labels (synced across all
locales). Java models and generated TS types are regenerated from the
renamed schemas.

The unrelated Ontology Explorer (RDF glossary graph) feature and the
output-concept "Derived Ontology" provenance panels are intentionally
left unchanged, since they name the derived term/metric graph rather
than the agent.

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

* Update generated TypeScript types

* fix(ai): show MCP Chat in sidebar via setting + restrict MCP Server config

- MCP Chat sidebar: add authenticated GET /mcp-client/enabled and gate the
  McpChatPlugin sidebar entry on aiSettings.mcpChat.enabled. It was tied to the
  removed app, so it never appeared after enabling chat in AI Settings.
- MCP Server settings: expose only the enable toggle and Origin Header URI; the
  endpoint path is fixed at /api/v1/mcp and no longer editable; drop the origin
  validation and allowed origins fields.
- EnumBackwardCompatibilityTest: account for the appended DERIVED_FROM
  relationship (count 26 -> 27, new last ordinal).

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

* fix(ai): drop MCP Chat config migration; derive MCP usage appId from name

- The MCP Chat app was never shipped to customers, so the 2.0.0 migration no
  longer carries its config into aiSettings.mcpChat — the seeded default shape
  is kept. Server enable-alignment and dead-app cleanup remain.
- McpToolCallUsage.appId: no MCP-usage query reads it (recorder writes, resource
  reads by appName), but apps_extension_time_series.appId is a NOT NULL generated
  column, so a value is required. Derive it deterministically from MCP_APP_NAME
  instead of a hardcoded UUID.

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

* format

* fix(ai): commit missing MemoryRelation DTO blocked by .gitignore

The bare `memory` pattern in .gitignore (a claude-flow tooling entry)
also matched the drive/memory Java package, so the newly added
MemoryRelation.java was silently ignored and never committed — breaking
CI with "cannot find symbol MemoryRelation" in MemoryVerdict and
MemoryReconciler. Scope the ignore to root-level /memory/ and add the file.

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

* fix(ai): make file-extraction LLM gate fully injectable (fix unit tests)

ContextFileProcessingService gated knowledge-pill extraction on BOTH the
injected llmEnabledSupplier AND a direct static
AISettingsUtil.isFileExtractionEnabled(AISettingsUtil.get()). The static
read needs a live SettingsCache, so ContextFileProcessingServiceTest
(which injects the gate as () -> true) could not satisfy it: extraction
was skipped and 3 tests failed (wrong repository.update counts,
runExtraction never invoked).

Fold the AISettings check into the production default supplier and route
both call sites through one shouldExtractContext(...) helper, so the
status machine is unit-testable and the repeated compound condition lives
in one place. Production gate (LLM enabled AND file-extraction toggle) is
unchanged.

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

* fix(ui): add missing label.general i18n key for AI Settings

AISettingsPage referenced t('label.general') but the key was absent
from en-us.json. Added it and synced all locale files via yarn i18n.

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

* fix(ui): repair AISettingsPage and CreateMemoryModal jest mocks

AISettingsPage imports Input from ui-core-components but the test mock
omitted it, so the component rendered an undefined element ("Element type
is invalid"). Add the Input mock plus the missing settingConfigAPI mocks
(getMcpConfiguration/restoreSettingsConfig/updateMcpConfiguration) the
component calls.

CreateMemoryModal's partial DateTimeUtils mock dropped
getEpochMillisForPastDays, which profiler.constant.ts invokes at module
load via a deep import chain, failing the suite at import. Spread
requireActual to preserve the real exports, and stub DerivedOntologyCard
to cut the heavy transitive chain (EntityUtilClassBase -> DataProductsPage
-> ConnectionStepCard) that also pulled in unmocked antd internals.

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

* build(deps): bump undici from 6.25.0 to 6.27.0 and form-data to 4.0.5 in /openmetadata-ui/src/main/resources/ui (#29241)

* build(deps): bump undici in /openmetadata-ui/src/main/resources/ui

Bumps [undici](https://github.com/nodejs/undici) from 6.25.0 to 6.27.0.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](nodejs/undici@v6.25.0...v6.27.0)

---
updated-dependencies:
- dependency-name: undici
  dependency-version: 6.27.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* update yarn

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Harsh Vador <harsh.vador@somaiya.edu>

* change index from all to dataAsset for my data (#29209)

* change index from all to dataAsset for my data

* fix jest tests

* fix playwrights

* fix playwrights

---------

Co-authored-by: Shrabanti Paul <shrabantipaul@Shrabantis-MacBook-Pro.local>

* fix(fqn): support double-quotes in fully qualified names + guard/repair corrupt FQNs (#28697)

* fix(fqn): support double-quotes in fully qualified names + guard/repair corrupt FQNs

Names containing a double-quote could not be represented in an FQN: the Fqn
grammar had no escape mechanism, yet quoteName() backslash-escaped the quote and
stored an unparseable segment. Building the FQN is a pure string op, so such
values were written successfully (insert hashes only the entity's own FQN); they
then detonated later with a 500 (ParseCancellationException) the first time a
nested FQN was hashed (e.g. a tags read), and were painful to migrate.

Three layered fixes:

- Grammar + quoteName: NAME_WITH_RESERVED now allows any character with '"'
  escaped by doubling it (""). quoteName/unquoteName encode/decode accordingly
  and are idempotent. Names without a quote encode identically to before, so
  existing FQNs and their hashes are unchanged (no reindex/migration needed).

- Ingest guard: FullyQualifiedName.validateFqnName() asserts a name round-trips
  through encode->parse->decode, wired into every nested-FQN setter (columns,
  pipeline tasks, topic/searchIndex/apiEndpoint fields, mlFeatures). A name that
  cannot be hashed is now rejected at ingest with a clear 400 instead of being
  stored to fail later.

- Heal-on-read: FullyQualifiedName.isValid() detects legacy-corrupt FQNs;
  PipelineRepository repairs unparseable task FQNs on the fly by re-deriving them
  from the task name, so existing poisoned data reads cleanly (200) without a
  migration. The repair is in-memory and persists on the next update.

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

* fix(fqn): repair corrupt pipeline task FQNs via migration, not heal-on-read

Heal-on-read (PipelineRepository.repairTaskFqns) ran a full ANTLR parse for
every task on every pipeline read to subsidize a finite set of already-corrupt
rows, was incomplete (the bulk/LIST/search path still 500'd), and could NPE on
a null task FQN. Replace it with a one-time migration so the corruption leaves
the stored data and reads pay no per-request cost.

- Remove repairTaskFqns and its setFields() call; keep the validateFqnName
  write-path guard that rejects un-representable names at ingest (400).
- Add migration v11211 (mysql + postgres): re-derive task FQNs where !isValid,
  persist only when changed.
- Harden FullyQualifiedName.isValid to treat null/empty as invalid (no NPE).
- Require >=1 char inside a quoted FQN segment (grammar + not *), rejecting
  empty quoted segments ("").

FullyQualifiedNameTest: 17/17.

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

* fix(fqn): reject empty nested-object names at write time

validateFqnName returned early when quoteName(name) was unchanged, letting
empty names through (quoteName("") == ""). An empty pipeline task name (the
schema sets no minLength on task.name) then produced an unhashable empty FQN
segment ("parent.") that 500'd on the next FQN hash -- the same failure class
as unrepresentable names. Treat null/empty as invalid so every nested-FQN
setter (columns, tasks, fields, mlFeatures) rejects them up front with a 400.

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

* fix(fqn): make pipeline task-FQN migration batched and observable

Address review feedback on the one-time repair migration:

- Performance: scan pipelines in pages of 1000 via listAfterWithOffset instead
  of selecting every id and calling findEntityById per pipeline, dropping the
  N+1 round-trips and the full id list held in memory. Only changed rows are
  written.
- Observability: track scanned/repaired/failed counts and log a prominent WARN
  with up to 100 pipeline ids that could not be repaired, instead of swallowing
  each failure as a lone WARN, so operators get a concrete remediation list.
- Search: document (completion log + schemaChanges) that repaired task FQNs are
  reflected in the search index after the standard post-upgrade reindex, matching
  existing FQN-fix migration behavior.

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

* test(fqn): cover pipeline task-FQN repair migration

Add MigrationUtilTest for the v11211 repairPipelineTaskFqns migration:
repair correctness (re-derive unparseable/null task FQNs, leave valid ones
untouched, skip task-less pipelines) and migration-path resilience -- a single
unreadable row or a failing update must not abort the upgrade.

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

* test(fqn): cover repair-migration pagination across pages

The existing repair-migration tests stubbed listAfterWithOffset for any
offset and returned data only at offset 0, so every case exercised a
single page. Add a test that stubs distinct pages by offset
(0 -> page 1, 1000 -> page 2, 2000 -> empty) and asserts the second page
is scanned and repaired, locking in correct limit/offset ordering and
offset advancement in repairPipelineTaskFqns.

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

* chore(migration): retarget pipeline task-FQN repair to 1.13.1

Move the pipeline task-FQN repair migration from 1.12.11 (package v11211)
to 1.13.1 (package v1131): the native SQL placeholder dir, the mysql and
postgres Migration handlers, the MigrationUtil, and its test. The
framework derives the handler package from the version dir via
MigrationFile.getVersionPackageName(), so 1.13.1 -> v1131; no logic
changes. Pure data migration, no DDL.

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

* fix(fqn): correct repair-migration summary counts; unscope 1.13.1 DDL

Addresses code review on the 1.13.1 pipeline task-FQN repair migration.

1. Failed-to-persist pipelines were double-counted in the summary log.
   repairPipeline kept taskCount > 0 when pipelineDAO.update threw, so a row
   that never persisted was reported as both "re-derived N task FQNs" and
   "could not fix N pipeline(s)", overstating success. Reset taskCount to 0
   on the persistence-failure path so only rows that actually persisted count
   as repaired. repairPipelineTaskFqns now returns a RepairSummary so the
   counts are asserted directly (doesNotCountFailedPersistAsRepaired fails
   without the fix). The Migration handlers ignore the return value.

2. Revert bootstrap/sql/migrations/native/1.13.1/{mysql,postgres}/
   schemaChanges.sql to match main exactly. The intake_form_entity DDL there
   belongs to main (consumed by IntakeFormDAO) and arrived via the main
   merge, not this PR; only a local comment was added on top. Dropping that
   comment makes this PR's net change to those files zero and removes the
   "data migration only" text that contradicted the DDL. The FQN repair runs
   via the Java v1131 Migration handler and needs no SQL.

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

* Add migration for variuos childresn entities

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Sriharsha Chintalapani <harsha@getcollate.io>
Co-authored-by: Sriharsha Chintalapani <harshach@users.noreply.github.com>

* fix(mcp): OAuth login fails with 400 when SSO returns id_token in URL fragment (#29228)

* fix(mcp): handle active-session shortcut and implicit-flow fragment at /mcp/callback

Root cause: when a user has an active Google/Azure SSO session,
AuthenticationCodeFlowHandler.handleLogin() bypasses pac4j entirely and
calls sendRedirectWithToken() directly, committing the response as
302 /mcp/callback#id_token=... (token in URL fragment, no pac4j state).

This caused three failures:
1. UserSSOOAuthProvider.handleSSOAuthorization() found no pac4j state in
   session (expected — pac4j was never invoked) and threw AuthorizeException.
2. AuthorizationHandler.exceptionally() turned the exception into an error
   redirect URL; handleAuthorizeRequest() then called sendRedirect() on an
   already-committed response → IllegalStateException: Committed at line 503.
3. McpCallbackServlet received the callback with #id_token=... in the URL
   fragment (browser-only, server never sees it), so both pac4jState and
   idTokenParam were null → 400 'missing state'.

Fixes:
- UserSSOOAuthProvider: check response.isCommitted() before throwing;
  return SSO_REDIRECT_INITIATED for the active-session path.
- OAuthHttpStatelessServerTransportProvider: guard sendRedirect() with
  response.isCommitted() check to prevent Committed exception.
- McpCallbackServlet: serve a JS fragment-extraction page instead of
  returning 400 — JS reads window.location.hash, extracts id_token,
  retries /mcp/callback?id_token=... so handleDirectIdTokenFlow() runs.

Also adds debug logging throughout the MCP auth flow for easier
diagnosis of future SSO/OAuth callback issues.

* fix(mcp): address review — POST token from fragment, fix log field, import Collections

- serveFragmentExtractionPage: switch from GET redirect to form POST so
  the id_token never appears in a URL, browser history, or access logs
  (RFC 6819 §5.3.5). Add doPost() that reads id_token from the body and
  delegates to handleDirectIdTokenFlow().
- McpCallbackServlet debug log: rename hasFragment→refererPresent with a
  boolean so the field is meaningful (server cannot observe the fragment).
- UserSSOOAuthProvider: replace java.util.Collections FQN with import +
  simple name per project Java standards.

* Update openmetadata-mcp/src/main/java/org/openmetadata/mcp/server/auth/handlers/McpCallbackServlet.java

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix(mcp): address review — if/else in doPost, textContent XSS fix, unit tests

- doPost: replace early-return guard clauses with if/else per project standard
  (one return statement per method)
- serveFragmentExtractionPage: replace innerHTML with textContent in both
  error branches to eliminate the JS XSS antipattern (greptile finding)
- McpCallbackServlet: package-private constructor for test injection;
  resolveSsoHandler() promoted to protected for subclass override in tests
- Add McpCallbackServletTest: 7 tests covering serveFragmentExtractionPage
  (content-type, form POST shape, textContent usage) and doPost edge cases
  (null handler→503, null/empty id_token→400)
- Add OAuthHttpStatelessServerTransportProviderTest: 4 tests covering
  sanitizeRedirectUrlForLogging (with/without query, null) and the
  committed-response guard

* fix(mcp): CSRF protection on doPost; remove vacuous guard test; real CSRF tests

Security (P1): doPost accepted cross-origin form submissions without any
CSRF check. A malicious site holding a valid id_token for a different user
could craft a form targeting /mcp/callback and hijack a victim's pending
MCP auth session (victim's Claude Desktop authenticates as the attacker).

Fix: add isOriginAllowed() — rejects any POST whose Origin header does not
match the server's own base URL (resolved from MCP config or system
settings). Absent Origin (same-origin browsers may omit for non-CORS
requests) is treated as allowed. Package-private for testability.

Test quality: remove the vacuous handleAuthorizeRequest guard test that
never called the method under test and trivially passed — replaced with
a comment noting the guard is covered at integration level. Replace with
4 real CSRF tests: Origin absent, matching, mismatched, and full doPost
403 path verification.

* fix(mcp): CSRF default-port normalization and reject-on-unknown-origin

* refactor(mcp): string constants + lazy-cache server origin

- Extract all sendError message strings to package-visible static final
  constants (ERR_SSO_UNAVAILABLE, ERR_CSRF_ORIGIN_MISMATCH,
  ERR_MISSING_ID_TOKEN, ERR_CALLBACK_FAILED, ERR_MISSING_STATE,
  ERR_STATE_NOT_FOUND). Eliminates magic strings, gives callers a stable
  contract, catches typos at compile time.
- Add cachedServerOrigin volatile field + getServerOrigin() lazy-init
  helper: resolveServerOrigin() is now called at most once per server
  lifetime instead of on every POST. Non-null results are cached;
  null results (transient DB miss at startup) are not cached so the
  next request retries — fail-secure without permanently breaking CSRF.
- Update tests to reference constants instead of duplicating literals.
- Add two cache-behaviour tests: verifies resolveServerOrigin() is
  called exactly once after a successful resolution, and called again
  on each request when it returns null (no caching of null).

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Load learning drawer chunk without a page-loader flash (#29280)

LearningIcon always mounts the lazy LearningDrawer, so its Suspense
fallback rendered the centered page Loader inline next to the icon on
mount, before any user interaction.

Make the fallback configurable on withSuspenseFallback (still defaults to
the existing Loader, so all current callers are unchanged) and have
LearningIcon opt out with null. The chunk now loads silently while the
drawer stays mounted, preserving the close animation and the in-drawer
resource player that relies on the drawer not unmounting.

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

* Fixes #27945: surface exact/prefix matches first in QuickFilter aggregation (#29231)

* fix(search): surface exact/prefix matches first in QuickFilter aggregation

Fixes #27945

The /search/aggregate endpoint used a single terms aggregation with
include: ".*term.*", ordered alphabetically with a fixed size=10. When
more than 10 values matched the pattern, exact matches (e.g. "name")
were silently dropped in favour of alphabetically-earlier contains
matches (e.g. "first_name", "display_name").

Replace the single agg with three targeted sub-aggregations sent in one
ES/OS round-trip:
  • __exact   – include: "term"     (size 1, O(1) dict lookup)
  • __prefix  – include: "term.*"   (size N, B-tree prefix scan)
  • __contains – include: ".*term.*" (size N, full wildcard, unchanged)

The backend merges the three bucket lists in priority order
(exact → prefix → contains), deduplicates, trims to the requested size,
and rewrites the response under the original sterms#field key so the
frontend requires no changes.

Add SearchUtils helpers: isBestMatchSearchPattern, extractRawSearchValue,
exactAggKey/prefixAggKey/containsAggKey, mergeBestMatchAggregations.
Cover all helpers and merge edge cases in SearchUtilsTest (12 new tests).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(search): address PR review — safe fallback, dot-escaping, null guard

- mergeBestMatchAggregations: on merge failure, degrade to renaming
  the __contains sub-agg to sterms#<field> so the UI always receives
  the key it expects; double-catches so even fallback parse failures
  are silent
- buildBestMatchAggregations (ES + OS): escape '.' in rawValue before
  using as Lucene regexp include for exact and prefix sub-aggs, preventing
  field names like 'user.id' from being treated as wildcards and preventing
  unbalanced-regexp 500s
- isBestMatchSearchPattern: guard against null input
- SearchUtilsTest: fix two broken parametrized cases (.* → expected '',
  remove null-passing CSV row), add dedicated null test, add fallback-path
  and dot-escaping tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(search): add missing ObjectNode import in SearchUtilsTest

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(search): fix dot-escaping test expectations — trailing .* wildcard must not be escaped

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(search): cap oversized dataModel column tree at index time (#29212)

* fix(search): cap oversized dataModel column tree at index time

A container/table with a pathologically large dataModel (a wide/nested schema with hundreds of thousands of columns) produces a multi-hundred-MB search document. The existing oversized-doc guard strips lineage but then ships the doc anyway, so the server stores ~196MB, parses it whole on every read and reindex, and can OOM.

Extend stripDocMapIfOversized (live index) and stripLineageForSize (bulk reindex) to also strip the nested column children and the derived columnNames/columnNamesFuzzy when the doc is still over the cap after lineage stripping. Top-level columns are kept, so column search and the column grid still work; the full schema stays available via the entity API. Gated by size, so normal entities are untouched. The nested children are mapped enabled:false (stored, not indexed), so nothing searchable is lost.

This bounds the indexed document at the source, so the server never holds the giant doc on read or reindex — complementing the read-side response streaming.

* perf(search): compute oversized-doc size once per mutation in strip path

Thread the serialized byte size through a local in stripDocMapIfOversized and stripLineageForSize instead of re-serializing the full document for each size gate and log line. On the oversized path this avoids redundant ~hundreds-of-MB pojoToJson/getBytes allocations exactly when memory and CPU are most constrained. Addresses review feedback on #29212.

* refactor(search): guard upstreamLineage strip and reuse post-strip size in logs

Guard the upstreamLineage removal in stripLineageForSize with a null check to match stripDocMapIfOversized, avoiding a wasted full-doc serialization and a misleading WARN when the field is absent on an oversized doc. Store the post-strip serialized size in a local for the column-strip log lines instead of recomputing inline. Addresses review feedback on #29212.

* fix(ui): exclude dataModel from Explore/suggestion search payloads

Complements the index-time column-tree strip (#29212): exclude dataModel from Explore and search-suggestion payloads, lazy-fetch it in the container summary panel via getContainerByFQN when absent, and make the service-insights asset-count query aggregation-only (pageSize 0, fetchSource false). Ported from #29200 so the index-side and UI-side fixes ship together. Relates to #29210.

* fix(search): address PR review — strip docs/logs + summary-panel error handling

Backend: document the column-tree strip in stripLineageForSize JavaDoc and include columnNamesFuzzy in both oversized-doc WARN logs. UI: in the container summary panel's on-demand dataModel fetch, reset previously-fetched columns when the container changes (so the prior container's schema isn't shown) and surface fetch failures via showErrorToast instead of silently rendering 'No data'. Addresses review comments on #29212.

* fix(ui): clear stuck loader on container switch + test on-demand dataModel fetch

Address follow-up review on the summary-panel lazy-fetch: clear isColumnsLoading in the effect's early-return so a now-cancelled in-flight getContainerByFQN can't leave the loader stuck on. Add tests covering the on-demand fetch (fires only when columns are absent from the search hit, not fetched when inline, and surfaces a toast on failure). Addresses review comments on #29212.

* fix(ui): prevent No-data flash on summary-panel mount + fix import order

Lazily initialize isColumnsLoading (loading when columns must be fetched on demand) so the container summary panel shows the loader on first render instead of briefly flashing 'No data available' (greptile P1). Also reorder imports (TablePureUtils before ToastUtils, drop stray blank) to fix UI checkstyle. Addresses review on #29212.

* fix(ui): align glossary term Related Terms section inside the left panel (#29284)

* fix(ui): align glossary term Related Terms section inside the left panel

* added unit test

* fix lint checks

* fix(ui): prevent ontology relations graph from crashing on large glossaries (#29270)

* fix(ui): prevent ontology relations graph from crashing on large glossaries

* nit

* fix(playwright): stop SSORenewal nightly flake from too-short token TTL (#29268)

The SSO Session Renewal suite swaps the server to a short SAML JWT TTL before
logging in. At 10s, on a loaded CI runner the initial app bootstrap
(loggedInUser, config, permissions) outran that window, so the first
/permissions fetch 401'd mid-load; the silent refresh succeeded but the
bootstrap request was not retried, leaving the app wedged on the global loading
spinner. dropdown-profile never rendered and the renewal tests timed out,
exhausting all retries on the 2026-06-22 nightly (run 27929661599).

- Raise SHORT_ACCESS_TTL_SECONDS 10 -> 30 so the token outlives bootstrap. 30s
  stays under EXPIRY_THRESHOLD_MILLES (60s), so the proactive-renewal timer
  still fires immediately and the refresh-on-expiry behavior under test is
  unchanged.
- Wait for dropdown-profile at the end of loginViaSaml so login is only "done"
  once the app shell has rendered, making any future bootstrap hang fail in
  beforeAll with a clear cause instead of mid-test.

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

* fix(exasol): flush statistics before polling Exasol audit tables in integration tests (#29278)

* test(ui): update MCP Playwright specs for settings-driven MCP

- AISettings.spec: MCP Server section now exposes only Enable + Origin Header
  URI (path/originValidation removed), so assert/fill the origin header instead
  of the removed mcp-server-path field.
- McpChat.spec: enable MCP Chat via aiSettings.mcpChat.enabled (PUT
  /system/settings) and reset it afterwards, instead of installing the retired
  McpChatApplication app (which now 404s).

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

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Harsh Vador <harsh.vador@somaiya.edu>
Co-authored-by: shrabantipaul-collate <shrabanti.paul@getcollate.io>
Co-authored-by: Shrabanti Paul <shrabantipaul@Shrabantis-MacBook-Pro.local>
Co-authored-by: Mohit Yadav <105265192+mohityadav766@users.noreply.github.com>
Co-authored-by: Sriharsha Chintalapani <harsha@getcollate.io>
Co-authored-by: Sriharsha Chintalapani <harshach@users.noreply.github.com>
Co-authored-by: Vishnu Jain <121681876+Vishnuujain@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Sid <30566406+siddhant1@users.noreply.github.com>
Co-authored-by: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com>
Co-authored-by: sonika-shah <58761340+sonika-shah@users.noreply.github.com>
Co-authored-by: Anujkumar Yadav <anujf0510@gmail.com>
Co-authored-by: harshsoni2024 <64592571+harshsoni2024@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

safe to test Add this label to run secure Github workflows on PRs UI UI specific issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Related Terms section misaligned and lacks spacing on the Overview tab

2 participants