fix(ui): align glossary term Related Terms section inside the left panel#29284
Merged
Conversation
Contributor
|
harsh-vador
approved these changes
Jun 22, 2026
Contributor
🟡 Playwright Results — all passed (15 flaky)✅ 4315 passed · ❌ 0 failed · 🟡 15 flaky · ⏭️ 88 skipped
🟡 15 flaky test(s) (passed on retry)
How to debug locally# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip # view trace |
Code Review ✅ ApprovedAligns the Related Terms section within the glossary term left panel by updating the layout configuration. No issues found. OptionsDisplay: compact → Showing less information. Comment with these commands to change:
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Describe your changes:
Fixes #29283
Type of change:
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:
Fixes <issue-number>: <short explanation>Fixes #<issue-number>above.Summary by Gitar
RELATED_TERMSwidget from the main page layout into the left panel container withinCustomizeGlossaryTermBaseClass.ts.CustomizeGlossaryTermBaseClass.test.tsto verify the hierarchical placement and layout configuration of theRELATED_TERMSwidget.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_TERMSwidget from a standalone top-level grid entry into theLEFT_PANELchildren array, and a new test file verifies the corrected placement.CustomizeGlossaryTermBaseClass.ts:RELATED_TERMSis removed from the top-level layout (wasw: 6, y: 7) and inserted as the last child of theLEFT_PANELcontainer aty: 3, w: 1. The panel's existingh: 7is sufficient — the deepest child now bottoms out at row 5.CustomizeGlossaryTermBaseClass.test.ts: New unit tests assert thatRELATED_TERMSis absent from the top-level layout, present as a left-panel child with full width, and positioned afterTAGSin 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
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%%{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:#28a745Reviews (2): Last reviewed commit: "fix lint checks" | Re-trigger Greptile