Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
c21084f
feat(ui): add AppModeSwitcher component
chirag-madlani Aug 21, 2026
3d08814
feat(ui): make Explore mode-aware; move AI explore header into discov…
karanh37 Aug 22, 2026
5f7a414
refactor(ui): drop AI prefixes from moved Explore header (OSS naming)
karanh37 Aug 22, 2026
fe0d36f
feat(ui): port app-mode shell + nav-module framework into OSS (platfo…
karanh37 Aug 22, 2026
15f38eb
feat(ui): move observability, context-center, marketplace into OSS ap…
karanh37 Aug 22, 2026
067de80
feat(ui): move entity and personal-space (inbox/my-data) into OSS app…
karanh37 Aug 22, 2026
6f94cbe
feat(ui): add classicV1 app mode; rename useIsAiMode -> useIsClassicV…
karanh37 Aug 22, 2026
efb1e08
feat(ui): AppRouter renders ClassicV1 shell by stored mode; drop rout…
karanh37 Aug 22, 2026
702936c
refactor(ui): delete useResolvedAppMode (dead after AppRouter mode sw…
karanh37 Aug 22, 2026
9fc845c
refactor(ui): delete useAppRoutesRegistry; ClassicV1 always available
karanh37 Aug 22, 2026
ab0074b
feat(ui): AppPlugin.getModeModules; ClassicV1 shell merges shared + p…
karanh37 Aug 22, 2026
03e7f5e
feat(schema): allow classicV1 app mode value (default unchanged; rege…
karanh37 Aug 22, 2026
3961e4d
feat(ui): AppModeSwitcher Classic <-> ClassicV1, always enabled, wire…
karanh37 Aug 22, 2026
4a49f50
fix(ui): translate stored app-mode preference wire token to runtime o…
karanh37 Aug 22, 2026
4c61a5c
fix(ui): reinstate persona-forced app-mode resolution in AuthProvider…
karanh37 Aug 22, 2026
4074f21
feat(ui): add app-mode switcher to profile dropdown
karanh37 Aug 23, 2026
2f14238
feat(ui): ClassicV1 standalone shell — nav modules, full page-table f…
karanh37 Aug 23, 2026
e581163
feat(ui): default OSS brand + user card in ClassicV1 sidebar
karanh37 Aug 23, 2026
94dac40
fix(ui): ClassicV1 sidebar polish — icons, collapsed logo, user card,…
karanh37 Aug 23, 2026
f25845d
refactor(ui): source ClassicV1 modules from LeftSidebarClassBase, dro…
karanh37 Aug 24, 2026
bfcb881
fix(ui): ClassicV1 Home/Govern icons + PageLayoutV1 top inset
karanh37 Aug 24, 2026
4605750
fix(ui): Inbox page header uses the inbox nav icon, not @untitledui I…
karanh37 Aug 24, 2026
7c85c0e
feat(ui): move AIUserMenu + Inbox launcher (and personal-space infra)…
karanh37 Aug 24, 2026
a86268e
fix(build): resolve.preserveSymlinks so linked/symlinked deps share o…
karanh37 Aug 24, 2026
0a7afc5
fix(ui): ClassicV1 sidebar polish — govern icons, collapsed rail card…
karanh37 Aug 24, 2026
24f9295
feat(ui): full brand wordmark in ClassicV1 header + install-gated plu…
karanh37 Aug 24, 2026
154bc19
feat(ui): add Default App Mode tenant-default settings page
karanh37 Aug 24, 2026
51eb298
fix(ui): color Inbox header icon with FeaturedIcon tile
karanh37 Aug 24, 2026
b03a7c4
feat(ui): add sidebar-specific brand getters for the app-mode sidebar
karanh37 Aug 24, 2026
f91eb59
refactor(ui): revert app-mode "classicV1" back to "AI" mode
karanh37 Aug 24, 2026
3826c7a
feat(ui): persona-customizable app-mode sidebar with "More" overflow
karanh37 Aug 24, 2026
3eb5ef2
feat(ui): app-mode sidebar recent-activity slot + idempotent contribu…
karanh37 Aug 24, 2026
bbe1339
fix(ui): key AI-mode sidebar customization under `askCollateSidebar`
karanh37 Aug 24, 2026
ec3e574
feat(ui): persona editor for the AI app-mode sidebar (askCollateSidebar)
karanh37 Aug 24, 2026
c1d0526
feat(ui): add "AI Sidebar" card to persona Customize UI
karanh37 Aug 24, 2026
821d644
test(ui): cover app-mode sidebar customization + registry dedup
karanh37 Aug 24, 2026
92bc345
feat(ui): track app-mode page views for analytics/adoption
karanh37 Aug 25, 2026
5a59fb1
Merge remote-tracking branch 'origin/main' into feature/ai-mode-oss-m…
karanh37 Aug 25, 2026
63290a8
fix(ui): adopt cross-tab app-mode hint at boot + add label.no-options
karanh37 Aug 25, 2026
0a052b0
test(ui): fix app-mode coverage-test failures
karanh37 Aug 25, 2026
4ba360b
fix(ui): drop antd from AssistantLayout, add missing labels, token cl…
karanh37 Aug 25, 2026
7ba0e74
fix(ui): resolve eslint errors + apply checkstyle formatting
karanh37 Aug 25, 2026
d0abda6
fix lint
karanh37 Aug 25, 2026
a9eead5
fix(ui): drop antd from app-mode sidebar editor + filter bar
karanh37 Aug 25, 2026
ee2334b
fix(ui): reject nested "on" drops so sidebar items are never lost
karanh37 Aug 25, 2026
028a333
build(ui): raise bundle-chunk ratchets for app-mode migration
karanh37 Aug 25, 2026
b18ba73
feat(ui): migrate observability alerts to app-mode module + fix app-m…
karanh37 Aug 26, 2026
d16c0b8
fix(ui): preserve alert destination config when test redacts it
karanh37 Aug 26, 2026
66d75fc
fix lint
karanh37 Aug 26, 2026
96abe54
fix(ui): render a branded not-found page for app mode /404
karanh37 Aug 26, 2026
1e81611
fix(ui): hold the app-mode route table until installed apps load
karanh37 Aug 26, 2026
3a3a5b0
fix lint
karanh37 Aug 26, 2026
d1bae17
Merge branch 'main' into feature/ai-mode-oss-migration
karanh37 Aug 26, 2026
3d7b9cb
fix(ai-shell): restore Context Center sub-panel Quick Actions / Recen…
karanh37 Aug 26, 2026
2ea2b12
fix(explore): constrain AI Explore search width to 800px usable
karanh37 Aug 26, 2026
1e5e51b
feat(personal-space): move My Data, Profile, Inbox/Triage, and the pr…
karanh37 Aug 26, 2026
ea41b0f
fix(personal-space): resolve inbox hook imports after the move to OSS
karanh37 Aug 26, 2026
0d0e6b1
Merge remote-tracking branch 'origin/main' into feature/ai-mode-oss-m…
karanh37 Aug 27, 2026
fd033bd
fix(inbox): migrate personal-space inbox surface to Conversation V2
karanh37 Aug 27, 2026
fa8a37d
fix(app-mode): render the AI shell for an active app-mode session in …
karanh37 Aug 27, 2026
c867cce
fix(inbox): register the Inbox as one /inbox/* route, not one per sub…
karanh37 Aug 27, 2026
c04facf
fix(ai-shell): submenus start collapsed; sub-nav follows the sticky a…
karanh37 Aug 27, 2026
86ca2df
fix(ai-shell): reorder Observability and Context Center in the app-mo…
karanh37 Aug 27, 2026
0a0f9c7
fix(ai-shell): persist sidebar collapse state across reloads
karanh37 Aug 27, 2026
d86d14d
fix(ai-shell): let a module opt out of per-persona hiding
karanh37 Aug 27, 2026
dfb5752
fix(ai-shell): restore Context Center sub-nav count badges
karanh37 Aug 27, 2026
07fb769
fix(inbox): skeleton only on first reply read, not on refresh
karanh37 Aug 27, 2026
be2cb72
Merge remote-tracking branch 'origin/main' into feature/ai-mode-oss-m…
karanh37 Aug 28, 2026
087977d
fix(inbox): compose task titles from type + entity for titleless tasks
karanh37 Aug 28, 2026
a1f35b2
fix(ai-shell): use relative imports in useContextCenterBadges
karanh37 Aug 28, 2026
f7209e7
Merge remote-tracking branch 'origin/main' into feature/ai-mode-oss-m…
karanh37 Aug 28, 2026
6b35ecd
fix(context-center): point the module at the renamed OVERVIEW route
karanh37 Aug 28, 2026
6a5dc55
fix(ui): stop kept-alive redirect routes from hijacking sub-nav navig…
karanh37 Aug 28, 2026
c1da90d
Merge remote-tracking branch 'origin/main' into feature/ai-mode-oss-m…
karanh37 Aug 28, 2026
12c7e1e
fix(ui): resolve eslint errors in ported AI-mode files
karanh37 Aug 28, 2026
3d25db5
fix(ui): exclude the AskCollate New-Chat landing from keep-alive cache
karanh37 Aug 28, 2026
d2d832d
fix(ui): keep observability quick-action intents alive across sub-rou…
karanh37 Aug 29, 2026
82dd828
fix(ui): prettier-format sidebar.less (trailing newline)
karanh37 Aug 29, 2026
210f2f0
Merge remote-tracking branch 'origin/main' into feature/ai-mode-oss-m…
karanh37 Aug 29, 2026
73aabca
fix(ui): prettier-format files touched by the main merge
karanh37 Aug 29, 2026
0de0a46
fix lint
karanh37 Aug 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# Observability Alerts

Observability alerts watch your data assets and notify your team the moment something happens — a failed test, a schema change, or a pipeline failure — so issues are caught before stakeholders notice them.

An alert is assembled from four parts, in this order:

1. **Source** — the kind of asset to watch
2. **Filters** — which of those assets to watch
3. **Trigger** — what has to happen to them
4. **Destination** — who gets told, and how

$$section
### Name $(id="name")

The display name for this alert. It identifies the alert in the alerts list and in its activity history, and can be changed later.

**Guidelines:**

- Describe what the alert watches, not just the asset it watches
- Include the scope if you run several similar alerts, so they stay distinguishable in the list

**Examples:**

- `Orders Table — Test Failures`
- `Schema Changes — Finance Domain`
- `Nightly Ingestion Failures`

Avoid names like `Alert 1` or `Table Alert`, which become impossible to tell apart once you have a dozen of them.
$$

$$section
### Description $(id="description")

Optional context explaining why this alert exists and who should act on it. This is shown to anyone reviewing or editing the alert later — including the person who has to decide whether it is still needed.

**Worth capturing:**

- What problem the alert is meant to catch
- Which team owns the response
- What the on-call person should do when it fires

**Example:**

> Fires when any test on the orders pipeline fails. Owned by the Data Platform team. On failure, check the nightly ingestion run first — most failures are upstream freshness, not a genuine data defect.
$$

$$section
### Source $(id="source")

The type of asset this alert watches. Choose from **Table**, **Topic**, **Container**, **Pipeline**, **Ingestion Pipeline**, **Test Case**, **Test Suite**, or **Data Contract**.

The source is the first choice because it determines everything after it — each asset type emits its own events, so the available filters and triggers change with it. Changing the source later resets the filters, triggers, and destinations you have configured.

**Examples:**

- **Test Case** — to hear about data quality failures
- **Ingestion Pipeline** — to hear when a scheduled ingestion run fails
- **Table** — to hear about schema changes or metric shifts on a dataset
$$

$$section
### Filters $(id="filters")

Filters scope the alert to the assets you care about. Without them, the alert applies to **every** asset of the selected source type — which is usually far noisier than intended.

Most sources support:

- **Entity Specific Name** — match named assets by fully qualified name
- **Owner Name** — match by the asset's owner
- **Domain** — match everything belonging to a domain
- **Filter By Updater Is Bot** — match changes made by automated processes

Test Case alerts add **Table Name a Test Case Belongs To**, so you can watch every test on a given table without listing them individually.

Each filter has an **Include** toggle. With Include **on**, an event meeting the filter condition sends the alert. With Include **off**, a matching event silences it instead.

**Examples:**

- Include *Domain = Finance* — only assets belonging to the Finance domain
- Include off, *Filter By Updater Is Bot* — silence ingestion-bot churn and only hear about human changes
- Include *Table Name = `prod.sales.orders`* — every test on the orders table

**Guidelines:**

- Start broad, then narrow once you see how often it fires
- Prefer silencing a small noisy set over enumerating everything you want included
$$

$$section
### Trigger $(id="trigger")

The events that cause this alert to fire. Filters decide **which assets** are watched; triggers decide **what has to happen** to them before anyone is notified.

The available triggers depend on the source:

- **Schema Changes** (Table, Topic, Container) — fires on added, deleted, or updated columns
- **Metric Updates** (Table) — fires when table metrics are updated
- **Pipeline Status** (Pipeline, Ingestion Pipeline) — fires when execution is `Failed` or `Pending`
- **Test Case Status** (Test Case) — fires when tests are `Failed`, `Aborted`, or `Queued`, with a variant scoped to a Test Suite

Triggers carry the same **Include** toggle as filters, so a trigger can be inverted to fire on everything *except* the selected events.

**Example:**

> Source **Test Case**, trigger *Test Case Status Updates* with status `Failed`. Pair it with a filter on *Table Name a Test Case Belongs To* so you only hear about failures on the tables you own, rather than every failing test in the platform.

Select only the events your team will act on. Every extra trigger increases how often the alert fires, and an alert that fires too often stops being read.
$$

$$section
### Destination $(id="destinations")

Where notifications are delivered when the alert fires. Each destination is two choices:

- **Destination** — *who* is notified. Either people derived from the asset (**Owners**, **Followers**, **Assignees**, **Mentions**), a fixed audience (**Users**, **Teams**, **Admins**), or **External** for an outside system.
- **Type** — *how* an External destination delivers: **Email**, **Slack**, **MS Teams**, **G Chat**, or a generic **Webhook**.

Routing to **Owners** or **Followers** keeps the alert accurate as ownership changes, without anyone editing it — a good default for alerts that follow a dataset rather than a team.

**Examples:**

- *Owners* → *Email* — tell whoever currently owns the failing asset
- *Teams: Data Platform* → *Slack* — post to the team channel regardless of ownership
- *External* → *Webhook* — forward to PagerDuty or an internal service

**Guidelines:**

- Add more than one destination when different audiences need the same event — for example the owner by email and the team in Slack
- External destinations can carry a custom notification template, configured below, to control the subject and body
$$

$$section
### Notification Template $(id="notificationTemplate")

Controls the wording of the message this alert sends — its subject line and body. The alert still fires on exactly the same events either way; only the text changes.

**The options:**

- **System Default Templates** — the built-in wording, used when you choose nothing else. Sensible for most alerts and requires no upkeep.
- **A saved template** — one already created in Notification Templates and reusable across alerts. Its name, subject, and body are shown below the picker for reference, read-only; edit it from Notification Templates to change it everywhere it is used.
- **Create Custom Template** — wording written here and belonging to this alert alone. Editing it affects no other alert.

**Writing a custom template:**

Subject and body support Handlebars placeholders, so one template adapts to whatever event fired it. Values are nested under two objects:

- `entity` — the asset the event happened to: `{{entity.name}}`, `{{entity.displayName}}`, `{{entity.fullyQualifiedName}}`, `{{entity.updatedBy}}`
- `event` — the event itself: `{{event.eventType}}`, `{{event.entityType}}`, `{{event.userName}}`, `{{event.timestamp}}`

`{{publisherName}}` (the alert's own name) and `{{emailingEntity}}` are also available at the top level.

Helpers cover what a plain value cannot — `{{buildEntityUrl entity}}` for a link back to the asset, `{{formatDate event.timestamp}}` for a readable time, and `{{camelCaseToTitle name}}` to turn a field name into a readable label. Each helper takes an argument — writing one on its own resolves to nothing. Type `{{` in either field to browse the full set.

Use **Validate** before saving. It checks Handlebars *syntax*, not whether a placeholder actually resolves — a misspelt or non-existent one passes validation and renders as empty text in the delivered message.

**Example:**

> Subject: `[{{event.eventType}}] {{entity.displayName}} needs attention`
> Body: `{{event.entityType}} {{entity.fullyQualifiedName}} changed. View it at {{buildEntityUrl entity}}.`

**Guidelines:**

- Reach for a saved template when several alerts should sound alike — it keeps the wording in one place
- Reach for a custom template when this alert needs wording nothing else shares
- Put the entity name in the subject; recipients triage on the subject line alone
$$
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import { fileURLToPath } from 'node:url';
// Ratchets, not knife-edges: each carries a little headroom so one new dynamic import does not
// fail an unrelated PR, while still catching the fragmentation this budget exists to prevent
// (the pre-consolidation build emitted 856 files).
const MAX_EMITTED_JS_FILES = 645;
const MAX_SMALL_JS_FILES = 460;
const MAX_EMITTED_JS_FILES = 720;
const MAX_SMALL_JS_FILES = 540;
const MAX_HTML_BOOTSTRAP_JS_FILES = 8;
const MAX_HTML_BOOTSTRAP_JS_BROTLI_BYTES = 950 * 1024;
const MAX_SINGLE_JS_BYTES = 1.75 * 1024 * 1024;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@
* Deprecation guard for the Antd + Less → UntitledUI + Tailwind migration.
*
* Antd (864 files) and Less (449 files) can't be a blanket error — instead we
* enforce "no NEW debt": fail if a change ADDS a `.less` file or ADDS an
* `import … from 'antd'` specifier that wasn't already imported from that
* same module before the change. Existing usage is untouched and migrates
* over time.
* enforce "no NEW debt": fail if a change ADDS an `import … from 'antd'`
* specifier that wasn't already imported from that same module before the
* change. New `.less` files are reported but do NOT fail the guard while the
* app-mode migration still ships `.less` for the AI shell. Existing usage is
* untouched and migrates over time.
*
* The antd check compares full before/after file contents (via `git show`)
* rather than raw diff lines. A diff-line scan flags
Expand Down Expand Up @@ -185,10 +186,12 @@ function newAntdImports() {
function main() {
const less = newLessFiles();
const antd = newAntdImports();
const problems = less.length + antd.length;
// New `.less` files are reported but do NOT fail the guard — the app-mode
// migration still lands `.less` for the AI shell. Only new `antd` debt blocks.
const problems = antd.length;

if (less.length) {
process.stderr.write(C.red(`\n✖ New .less file(s) are not allowed — style with Tailwind (tw:) + UntitledUI:\n`));
process.stderr.write(C.gray(`\nⓘ New .less file(s) detected (allowed for now — prefer Tailwind (tw:) + UntitledUI):\n`));
less.forEach((f) => process.stderr.write(` ${f}\n`));
}
if (antd.length) {
Expand All @@ -202,7 +205,7 @@ function main() {
);
process.exit(1);
}
process.stdout.write(C.green('✔ No new Antd imports or .less files.\n'));
process.stdout.write(C.green('✔ No new Antd imports.\n'));
}

if (require.main === module) {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading