Skip to content

Commit 7ecc1aa

Browse files
delchevclaude
andcommitted
Split CLAUDE.md into topic files under .claude/docs/
CLAUDE.md grew to a single 545-line file mixing behavioral guidelines, build/run instructions, architecture, UI gotchas, and CI reference. Split it by topic into 18 files under .claude/docs/ and turn CLAUDE.md into a slim index that inlines them via @-imports in the original order, so the assembled context is byte-identical. .gitignore gets a !.claude/docs/ entry next to the existing .claude/* exceptions so the topic files are tracked. Also extend the team permission allowlist in .claude/settings.json with read-only commands observed to prompt frequently (git fetch, gh search, mvn formatter:validate). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 0d3217a commit 7ecc1aa

21 files changed

Lines changed: 568 additions & 541 deletions
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
## 1. Think Before Coding
2+
3+
**Don't assume. Don't hide confusion. Surface tradeoffs.**
4+
5+
Before implementing:
6+
- State your assumptions explicitly. If uncertain, ask.
7+
- If multiple interpretations exist, present them - don't pick silently.
8+
- If a simpler approach exists, say so. Push back when warranted.
9+
- If something is unclear, stop. Name what's confusing. Ask.
10+
11+
## 2. Simplicity First
12+
13+
**Minimum code that solves the problem. Nothing speculative.**
14+
15+
- No features beyond what was asked.
16+
- No abstractions for single-use code.
17+
- No "flexibility" or "configurability" that wasn't requested.
18+
- No error handling for impossible scenarios.
19+
- If you write 200 lines and it could be 50, rewrite it.
20+
21+
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
22+
23+
## 3. Surgical Changes
24+
25+
**Touch only what you must. Clean up only your own mess.**
26+
27+
When editing existing code:
28+
- Don't "improve" adjacent code, comments, or formatting.
29+
- Don't refactor things that aren't broken.
30+
- Match existing style, even if you'd do it differently.
31+
- If you notice unrelated dead code, mention it - don't delete it.
32+
33+
When your changes create orphans:
34+
- Remove imports/variables/functions that YOUR changes made unused.
35+
- Don't remove pre-existing dead code unless asked.
36+
37+
The test: Every changed line should trace directly to the user's request.
38+
39+
## 4. Goal-Driven Execution
40+
41+
**Define success criteria. Loop until verified.**
42+
43+
Transform tasks into verifiable goals:
44+
- "Add validation" → "Write tests for invalid inputs, then make them pass"
45+
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
46+
- "Refactor X" → "Ensure tests pass before and after"
47+
48+
For multi-step tasks, state a brief plan:
49+
```
50+
1. [Step] → verify: [check]
51+
2. [Step] → verify: [check]
52+
3. [Step] → verify: [check]
53+
```
54+
55+
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
56+
57+
---
58+
59+
**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.
60+

.claude/docs/blimpkit.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
## Browser UI — BlimpKit gotchas
2+
3+
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.
4+
5+
- **`<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).
6+
- **`<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.
7+
- **`<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.
8+
- **`<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:
9+
- `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"`.
10+
- `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).
11+
- 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).
12+
- **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.
13+
- **`<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`.
14+
- **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).
15+
- **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.
16+
- **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"`.
17+
- **`<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.
18+
- **`<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.
19+
- **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`).
20+
- **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--*`.
21+

.claude/docs/ci.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
## CI reference
2+
3+
`.github/workflows/build.yml` is the source of truth for "does this build pass" on **push to master**:
4+
5+
- `code-style`: `mvn -T 1C formatter:validate`
6+
- `tests` (ubuntu + windows matrix): `mvn clean install -P unit-tests`
7+
- `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.
8+
- `build-deploy`: `mvn clean install -P quick-build` then Docker buildx multi-arch image push to `dirigiblelabs/dirigible`
9+
10+
### PR gate vs full suite (smoke / nightly split)
11+
12+
The full Selenide UI suite takes ~1.5h per DB, so it does **not** run on every PR:
13+
14+
- **`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.
15+
- **`nightly.yml`** (cron `0 2 * * *` + `workflow_dispatch`) and **push to master** (`build.yml`) run the **full** suite on H2 + PostgreSQL.
16+
17+
**Test tagging convention (JUnit 5 `@Tag`, wired to failsafe via the `${it.groups}` / `${it.excludedGroups}` properties in the root `pom.xml`):**
18+
- 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.
19+
- HTTP-level ITs (`extends IntegrationTest` directly) carry no tag, so they are always in the smoke set.
20+
- 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).
21+
- 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.
22+
- `@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.
23+
24+
`codeql.yml`, `release.yml` cover CodeQL and Maven Central release respectively.

.claude/docs/client-java.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
## Client Java code (`engine-java` + `data-store-java`)
2+
3+
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)):
4+
5+
- `@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`).
6+
- **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.
7+
- **Extension points are plain interfaces** + `@Component` contributions consumed via `List<…>` injection (or `Extensions.find`); there is no `@Extension`/`@ExtensionPoint`.
8+
- 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.
9+
- **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.
10+
11+
**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.
12+

0 commit comments

Comments
 (0)