Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions .claude/docs/behavioral-guidelines.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
## 1. Think Before Coding

**Don't assume. Don't hide confusion. Surface tradeoffs.**

Before implementing:
- State your assumptions explicitly. If uncertain, ask.
- If multiple interpretations exist, present them - don't pick silently.
- If a simpler approach exists, say so. Push back when warranted.
- If something is unclear, stop. Name what's confusing. Ask.

## 2. Simplicity First

**Minimum code that solves the problem. Nothing speculative.**

- No features beyond what was asked.
- No abstractions for single-use code.
- No "flexibility" or "configurability" that wasn't requested.
- No error handling for impossible scenarios.
- If you write 200 lines and it could be 50, rewrite it.

Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.

## 3. Surgical Changes

**Touch only what you must. Clean up only your own mess.**

When editing existing code:
- Don't "improve" adjacent code, comments, or formatting.
- Don't refactor things that aren't broken.
- Match existing style, even if you'd do it differently.
- If you notice unrelated dead code, mention it - don't delete it.

When your changes create orphans:
- Remove imports/variables/functions that YOUR changes made unused.
- Don't remove pre-existing dead code unless asked.

The test: Every changed line should trace directly to the user's request.

## 4. Goal-Driven Execution

**Define success criteria. Loop until verified.**

Transform tasks into verifiable goals:
- "Add validation" → "Write tests for invalid inputs, then make them pass"
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
- "Refactor X" → "Ensure tests pass before and after"

For multi-step tasks, state a brief plan:
```
1. [Step] → verify: [check]
2. [Step] → verify: [check]
3. [Step] → verify: [check]
```

Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.

---

**These guidelines are working if:** fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.

21 changes: 21 additions & 0 deletions .claude/docs/blimpkit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
## Browser UI — BlimpKit gotchas

The IDE shell and most editor perspectives render through **BlimpKit**, a thin AngularJS-on-Fundamental-Styles component library that lives in `components/ui/platform-core/src/main/resources/META-INF/dirigible/platform-core/ui/blimpkit/` (Angular module name **`blimpKit`** — camelCase, declared in `blimpkit.js`). The runnable artifact is the bundled `/webjars/blimpkit__blimpkit/dist/blimpkit.min.js` (~158 KB, currently webjar 2.1.6). Findings below are the ones that have already burned someone — read once, save hours later.

- **`<bk-checkbox>` is invisible without `<bk-checkbox-label>`.** `bk-checkbox` compiles to a bare `<input type="checkbox" class="fd-checkbox">`. Fundamental-Styles' `.fd-checkbox` rule hides the native input (`opacity:0; position:absolute`) on the assumption that a sibling `<bk-checkbox-label>` will draw the visible square via its `.fd-checkbox__checkmark` ::before pseudo. A lone `<bk-checkbox>` is therefore a working click target with zero visible chrome — easy to ship and never catch in code review. Pair it: `<bk-checkbox id="x" ng-model="…">` followed by `<bk-checkbox-label for="x" empty="true">…</bk-checkbox-label>` (the `empty="true"` attribute drops the inner text container so the label provides just the checkmark — use it when the surrounding markup already labels the row).
- **`<bk-dialog>` has an isolate scope.** You can't put `ng-controller="…PopupCtrl"` on the dialog element itself — Angular throws "Multiple directives [bkDialog, ngController] asking for new/isolated scope on: <bk-dialog>". Wrap with a thin `<div ng-controller="…">` and put `<bk-dialog visible="…">` inside.
- **`<bk-select>` doesn't support `ng-options`.** Use `<bk-option ng-repeat>` instead — text via the `text` attribute, model value via `value`. Example: `<bk-option ng-repeat="opt in items" text="{{opt.name}}" value="opt.id">`. When the select sits in a parent with `overflow:hidden` (a dialog, a sidebar), add `dropdown-fixed="true"` so the menu floats via `position:fixed` instead of being clipped.
- **`<bk-option>`'s `text` and `value` bind differently** — `text: '@'` is **interpolation** (use `text="{{ expr }}"` or a literal), `value: '<'` is a **one-way expression** (use `value="expr"`, never `value="{{ expr }}"`). Mixing them up is the canonical bug for this directive:
- `value="{{s}}"` makes Angular try to parse `{{s}}` as a JS expression, the directive's link silently fails, and the dropdown shows raw `{{ text }}` from the unlinked template (one ghost item per ng-repeat iteration, not six). Fix: `value="s"`.
- `value="user"` evaluates `$scope.user`, not the string `"user"` — every option ends up with the same `undefined` value and selection becomes a no-op. For string literals, quote inside: `value="'user'"`. For the empty default option, `value="''"`, not `value=""` (which is the undefined-expression).
- Numeric literals (`value="2"`) and loop variables (`value="s"`) are already expressions — leave them unquoted. Numbers stay numbers, so `selectedValue === '2'` will fail; either store as numbers on the model or coerce in the controller (the refresh-interval dropdowns in `view-jvm-monitoring` / `view-jvm-threads` `parseInt` the model on read).
- **Perspective SVG icons inherit `fill` from CSS — don't hard-code `fill` on the path.** `blimpkit.css` styles `.fd-list__navigation-item i.bk-icon--svg svg` with `fill: var(--fdVerticalNav_Icon_Color, #303030)` (and `var(--sapSelectedColor)` on the active state). The CSS only takes effect on `<path>` elements with **no own `fill`** — adding `fill="#000000"` (the default when you paste an SVG from a web icon set) locks the icon to black and breaks dark-theme adaptability. Strip the fill attribute (jobs.svg / operations.svg pattern) or set `fill="currentColor"` (database.svg pattern). The container svg's other niceties (`width="512"` / `height="512"` / `stroke-width=".99999"`) don't affect rendering through this CSS but are the established style.
- **`<bk-input>` / `<bk-textarea>` / `<bk-button>` use `replace:true`.** The attributes you write on the directive element (ng-model, ng-blur, ng-keypress, ng-disabled, custom directives like `auto-focus` / `select-text`) end up on the underlying native `<input>` / `<textarea>` / `<button>`, so existing controller code keeps working unchanged after migrating native form controls to `bk-*`. ng-model binds against the parent scope — the isolate scope `bk-input` declares only owns `compact` / `state` / `glyph`.
- **Don't put `ng-class` on a `replace:true` directive element that already has its own `ng-class`.** `bk-table-header-cell`, `bk-table-cell`, and most layout-y BlimpKit directives template as `<th ng-class="getClasses()" …>` — Angular's attribute merge **string-concatenates** duplicate `ng-class` values, producing nonsense like `ng-class="{ sorted: sort.key === 'id' } getClasses()"`. The page then throws `$parse:syntax` at compile time and the row never renders. (`class` merges cleanly — only `ng-class` is broken — so `class="no-sort"` on a `<th bk-table-header-cell>` works fine.) The fix: push the conditional class onto a child element instead of the directive root: `<th bk-table-header-cell ng-click="…"><span class="sort-caret" ng-class="{ active: sort.key === 'id' }">{{ caret() }}</span></th>`. Same applies for anything else with a `replace:true` + `ng-class` template (audit `components/ui/platform-core/.../blimpkit/*.js` for the pattern before adding `ng-class` to a `bk-*` directive).
- **The `blimpKit` module's `.config()` block disables three `$compileProvider` flags.** `cssClassDirectivesEnabled(false)`, `commentDirectivesEnabled(false)`, and `debugInfoEnabled(false)` are flipped at module-load when debug info was on — saves per-element scope-tracking overhead in production. The last flag breaks Selenide-style debugging that calls `angular.element(node).scope()`: Angular stops attaching scope refs to DOM nodes, so the lookup returns `undefined`. If your app or its integration tests rely on that, re-enable the flags in a `.config(['$compileProvider', …])` block of your own — module config blocks run in dependency order, so `blimpKit`'s flips happen first and your override sticks.
- **SAP-icons + the "72" body font live in platform-core's `fonts.css`.** Every BlimpKit-using page needs `<link rel="stylesheet" href="/services/web/platform-core/ui/styles/fonts.css">`. Without it `.sap-icon--*` glyphs render as tofu squares because the `@font-face { font-family: "SAP-icons"; … }` declaration is missing. The IDE shell loads this automatically via the `platform-links` injection mechanism (see below); standalone iframes (editor-bpm, embedded views) have to add the link tag explicitly. Other `@font-face` rules in the same file declare the body font: `"72"` (Regular / Light / Bold), `"72-Light"`, `"72-Bold"`, `"72Mono-Regular"`, `"72Mono-Bold"`, plus `"BusinessSuiteInAppSymbols"` and `"SAP-icons-TNT"`.
- **`<meta name="platform-links" category="…">` auto-injects scripts + stylesheets.** Looking at any non-iframe perspective HTML you'll see a single `<meta name="platform-links" category="ng-view,ng-perspective">`-style tag in the `<head>`. `HtmlPlatformLinksInjector` (in `components/engine/engine-web/.../HtmlPlatformLinksInjector.java`) reads it at request time, walks the `category` list, and replaces the meta tag with the bundle of `<link>` and `<script>` tags registered for those categories. Categories are defined in `components/engine/engine-web/src/main/resources/platform-links.json` — `ng-view` is the heavyweight bundle (jQuery, AngularJS, all the platform hubs, BlimpKit, Fundamental-Styles, fonts.css), `ng-perspective` adds split + layout, `ng-editor` adds workspace + repository hubs, etc. Adding new shared platform code → add it to this JSON, not to every perspective HTML.
- **`<bk-dialog>` toggles visibility via the `visible` binding, not a `.modal('show')` plugin.** `<bk-dialog visible="modal.visible">` watches the expression and adds `fd-dialog--active` when true. No backdrop element is added (the dialog's own `.fd-dialog--active` overlay handles z-index + dimming). To dismiss programmatically: flip the bound flag (`scope.modal.visible = false`) inside an `$apply`; let the directive's digest cycle remove the `--active` class; then `$timeout` ~300ms later before tearing down the scope so the close animation completes.
- **Test selectors after a BlimpKit migration.** Native `<input class="form-control">` → `<input class="fd-input fd-input--compact">`. `<div class="modal in">` (Bootstrap-3 visible) → `<section class="fd-dialog fd-dialog--active">`. `body.modal-open` and `.modal-backdrop` are NOT set by `<bk-dialog>` — drop assertions on those, the active overlay handles its own dimming. When fixing Selenide tests that look at `.modal-header .close`, switch to `.fd-dialog__header .fd-button` (or scope to the dialog with `section.fd-dialog--active button.fd-button`).
- **A `<split>` splitter needs the `platformSplit` module in the app's dependency list — loading the script is not enough.** The `<split>`/`<split-pane>` resizable-pane directives are defined in Angular module `platformSplit` (`platform-core/ui/platform/split.js`). The script + `split.css` are already bundled by the `ng-perspective` (and `ng-split`) `platform-links` categories, so a perspective that declares `ng-perspective` does NOT also need `ng-split`. But every app must still list `'platformSplit'` in its `angular.module('app', [...])` deps, or the directives never register: `<split>`/`<split-pane>` stay inert unknown elements and the layout collapses (one pane fills everything, the others vanish — with no console error). Working examples: `editor-csvim`, `perspective-settings`, `resources-inbox`. Layout: `.bk-split` is `height:100%`, so under a persistent `<bk-toolbar>` in a `bk-vbox` body wrap the split in `<div class="bk-stretch">` (see `resources-documents`); wrap each pane's content in `<div class="bk-vbox bk-fill-parent">`; `split-pane size` values should sum to 100; the gutter replaces any manual `bk-border--*`.

24 changes: 24 additions & 0 deletions .claude/docs/ci.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
## CI reference

`.github/workflows/build.yml` is the source of truth for "does this build pass" on **push to master**:

- `code-style`: `mvn -T 1C formatter:validate`
- `tests` (ubuntu + windows matrix): `mvn clean install -P unit-tests`
- `integration-tests-h2` / `-postgresql`: the **full** Selenide IT suite, `mvn clean install -P integration-tests` with the matching `DIRIGIBLE_DATASOURCE_DEFAULT_*` env vars (MSSQL is no longer a CI leg — removed in #6150). Each DB leg is **sharded into four parallel matrix jobs** selected by tag expression — `api` (`!ui & !slow`), `ui` (`ui & !slow & !sample & !camel`), `samples` (`ui & !slow & (sample | camel)`), `slow` (`slow`) — so the run's wall clock is the slowest shard (~35 min, ~40 on PostgreSQL), not the whole ~1h40m suite. The shards partition the suite; keep them disjoint and complete when adding tags.
- `build-deploy`: `mvn clean install -P quick-build` then Docker buildx multi-arch image push to `dirigiblelabs/dirigible`

### PR gate vs full suite (smoke / nightly split)

The full Selenide UI suite takes ~1.5h per DB, so it does **not** run on every PR:

- **`pull-request.yml`** (every PR) runs `code-style`, unit `tests`, `docker-build`, and a single fast **`smoke-tests`** job on H2: `mvn clean install -P integration-tests -Dit.groups="!ui | smoke"`. The tag expression selects the HTTP-level ITs (untagged, so `!ui`) plus the few UI journeys explicitly marked `@Tag("smoke")` - including one full clone->generate->validate app lifecycle (`IntentEditorLoadsIT`, the intent Generate flow). Keep the smoke set small so the PR gate stays fast.
- **`nightly.yml`** (cron `0 2 * * *` + `workflow_dispatch`) and **push to master** (`build.yml`) run the **full** suite on H2 + PostgreSQL.

**Test tagging convention (JUnit 5 `@Tag`, wired to failsafe via the `${it.groups}` / `${it.excludedGroups}` properties in the root `pom.xml`):**
- Every browser-driven IT is `@Tag("ui")` - inherited from the `UserInterfaceIntegrationTest` base (and thus by `SampleProjectsIT` and all sample-project ITs). Do not tag these individually.
- HTTP-level ITs (`extends IntegrationTest` directly) carry no tag, so they are always in the smoke set.
- To force a specific UI IT to run on every PR, add `@Tag("smoke")` to that class (keep the list small - smoke must stay fast).
- Shard-routing tags: `@Tag("sample")` sits on the `SampleProjectsIT` base (inherited by every sample-project IT); `@Tag("camel")` sits on each IT in `ui/tests/camel` (their `PredefinedProjectIT` base is shared with non-camel tests, so the base cannot carry it — tag new camel ITs individually). These route classes into the `samples` CI shard; everything else UI stays in the `ui` shard.
- `@Tag("slow")` is the **fourth shard**, and it is a *balancing* tag, not a semantic one: it holds the long poles of both families (currently the api classes above ~55 s and the browser journeys above ~110 s), because without them `api` and `ui` are the critical path while `samples` idles. Membership is a judgement about measured CI time — re-check it when the shard times drift apart, and note that mistagging can only unbalance the shards, never drop an IT (`api` is the untagged complement). It does **not** affect the PR smoke gate: a `slow` api IT is still untagged-`ui`, so `!ui | smoke` still selects it.

`codeql.yml`, `release.yml` cover CodeQL and Maven Central release respectively.
12 changes: 12 additions & 0 deletions .claude/docs/client-java.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## Client Java code (`engine-java` + `data-store-java`)

Client `.java` under `/registry/public/<project>/...` is synchronized by `JavaSynchronizer`, compiled in-process (one `javac` batch + one fresh `ClientClassLoader` per generation in `JavaLoader.rebuild()`), and run through a Spring-Boot-style **bean container** (PR [#6051](https://github.com/eclipse-dirigible/dirigible/pull/6051)):

- `@Component` beans with **constructor / field / collection** injection; `@Repository`, `@Controller`, `@Websocket` are meta-`@Component`. Reach platform services via the client-facing `Beans` facade (not the platform-internal `BeanProvider`).
- **Two never-mixed handler styles** for jobs/listeners/websockets: a self-describing interface (`JobHandler.cron()`, `MessageHandler.destination()`, `WebsocketHandler.endpoint()`) **or** a method-level annotation (`@Scheduled`/`@Listener` on a `@Component` method; `@Websocket` class + `@OnX` methods). No reflective by-name fallback; the hybrid is rejected.
- **Extension points are plain interfaces** + `@Component` contributions consumed via `List<…>` injection (or `Extensions.find`); there is no `@Extension`/`@ExtensionPoint`.
- All client annotations/facades live in `org.eclipse.dirigible.sdk.*` (`api-modules-java`), not the old `engine.java.annotations.*`. Compile **and** bean-wiring errors surface in the IDE Problems view.
- **Manage entities ONLY through their generated `<Entity>Repository` — never the generic `Store`/`Database` for entity CRUD.** The generated `@Repository extends JavaRepository<T>` is the sole sanctioned load/save/update/delete path; it carries validations, **event publishing** (create/`-updated`/`-deleted` topics that intent triggers/reactions/rollups/notifications consume — recorded in the tenant's `DIRIGIBLE_EVENT_OUTBOX` inside the write's own transaction, so the row and its event commit together and a broker outage neither loses the event nor fails the write; `EventOutboxRelayJob` drains what the in-process publish could not deliver), and — for `multilingual: true` entities — the **read-time translation overlay** (every find translates string properties from the sibling `<TABLE>_LANG` table for the caller's `Accept-Language`, via the SDK `org.eclipse.dirigible.sdk.db.Translator`). The name-keyed `org.eclipse.dirigible.sdk.db.Store` and raw `Database` SQL bypass all of that silently and must not touch a managed entity. (`updateWithoutEvent` is fine — a deliberate repository method that keeps validations/i18n and only omits the event, for workflow-driven system writes.) So a reusable delegate/service that must touch a *specific* entity lives **in that entity's project** (importing its repository); only entity-agnostic helpers belong in a shared project. See the engine-java guide.

**Detailed guide:** [`components/engine/engine-java/CLAUDE.md`](components/engine/engine-java/CLAUDE.md). Read it before changing anything under `engine-java`, `data-store-java`, the `sdk.*` annotations, or the `*-java` templates — it covers the container, the consumers, the two handler styles + no-mixing rule, the `JavaHandler`-as-bean path, controller routing / OpenAPI / `@Roles`, `data-store-java` dynamic-map persistence, error surfacing, the **removed** internals (`RepositoryRegistry` / `RepositoryClassConsumer` / `DependencyResolver` / reflective fallback / `@Extension`), and the three-repo (platform + `dirigiblelabs/sample-java-*` + docs) sequencing.

Loading
Loading