-
Notifications
You must be signed in to change notification settings - Fork 4.6k
chore: optimize AI rules and conventions for Cursor and Claude Code #41671
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
b88fade
chore: replace always-on AI rules with optimized, context-aware conve…
subrata71 706ee22
chore: add targeted backend Java and server test rules, remove old ve…
subrata71 476642f
chore: add targeted frontend, client test, and Cypress E2E rules
subrata71 1a1e67f
chore: replace verbose verification rules with concise checklists, ad…
subrata71 6be75fd
chore: address CodeRabbit review feedback on AI rules
subrata71 7dd5752
chore: remove unused .ai/rules/ shared content directory
subrata71 0636337
chore: update bug-fix rule to enforce TDD red-green workflow
subrata71 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,143 @@ | ||
| --- | ||
| description: | ||
| globs: app/server/**/*.java | ||
| alwaysApply: false | ||
| --- | ||
| # Java / Spring Boot Backend Conventions | ||
|
|
||
| ## Tech Stack | ||
|
|
||
| - Java 25, Spring Boot 3.5, Spring WebFlux (fully reactive), MongoDB, Maven | ||
| - Formatting: Palantir Java Format via `mvn spotless:apply` (auto-runs on pre-commit) | ||
|
|
||
| ## Project Structure | ||
|
|
||
| Multi-module Maven project under `app/server/`: | ||
|
|
||
| ```text | ||
| appsmith-server/ — Main server module (controllers, services, repositories, domains) | ||
| appsmith-interfaces/ — Shared contracts (models, exceptions, plugin interfaces) | ||
| appsmith-plugins/ — 25+ database/API connector plugins | ||
| appsmith-git/ — Git integration module | ||
| reactive-caching/ — Custom reactive caching framework | ||
| ``` | ||
|
|
||
| Within `appsmith-server`, packages are organized by feature domain, each with `ce/` and optionally `ce_compatible/` sub-packages. | ||
|
|
||
| ## CE-EE Service Pattern (Mandatory for all extensible components) | ||
|
|
||
| ### Three-tier inheritance | ||
|
|
||
| ```text | ||
| Interface: ServiceCE → ServiceCECompatible → Service | ||
| Impl: ServiceCEImpl → ServiceCECompatibleImpl → ServiceImpl (@Service) | ||
| Package: com.x.services.ce com.x.services.ce_compatible com.x.services | ||
| ``` | ||
|
|
||
| ### Rules | ||
|
|
||
| - `@Service` / `@Component` annotation on CE-Compatible and final `*Impl` classes (not on CE base classes) | ||
| - CE impl contains all business logic | ||
| - CE-Compatible is an empty pass-through in CE repo (EE overrides it for graceful degradation) | ||
| - Final `*Impl` is an empty pass-through in CE repo (EE overrides it for enterprise features) | ||
| - Use `protected` methods in CE impl to create override hooks for EE | ||
| - This pattern applies to: services, repositories, controllers, solutions, helpers, configurations | ||
|
|
||
| ### When to create the CE-Compatible layer | ||
|
|
||
| Always. Even if there's no current EE override, create the empty scaffolding so EE can extend later without modifying CE files. | ||
|
|
||
| ## Controller Conventions | ||
|
|
||
| ```text | ||
| CE class (in controllers/ce/): All endpoint logic, NOT annotated as @RestController | ||
| EE class (in controllers/): @RestController + @RequestMapping, empty body, extends CE | ||
| ``` | ||
|
|
||
| - URL paths: `/api/v1/{resource}` — all paths defined in `UrlCE.java` constants | ||
| - Response: always `Mono<ResponseDTO<T>>` — every response wrapped in `ResponseDTO` | ||
| - `@JsonView(Views.Public.class)` on every endpoint method | ||
| - Zero business logic in controllers — delegate entirely to services | ||
| - No error handling in controllers — handled by global `@ControllerAdvice` (`GlobalExceptionHandler`) | ||
|
|
||
| ## Repository Conventions | ||
|
|
||
| Dual hierarchy: | ||
| - `BaseRepository` (extends `ReactiveMongoRepository`) — standard CRUD | ||
| - `AppsmithRepository` (custom) — ACL-aware queries via `queryBuilder()` fluent API | ||
|
|
||
| ```java | ||
| queryBuilder() | ||
| .criteria(Bridge.equal(Application.Fields.workspaceId, workspaceId)) | ||
| .permission(aclPermission) | ||
| .all(); | ||
| ``` | ||
|
|
||
| - No `@Query` annotations — use the `Bridge` query builder exclusively | ||
| - Field references via Lombok `@FieldNameConstants` (`Fields` inner class) | ||
| - Soft deletes: `archive()` sets `deletedAt`; queries auto-filter deleted records | ||
|
|
||
| ## Domain / DTO Conventions | ||
|
|
||
| Hierarchy: `BaseDomain` (id, policyMap, timestamps) → `GitSyncedDomain` → `RefAwareDomain` | ||
|
|
||
| - `@Getter`, `@Setter`, `@ToString` on domains — **not** `@Data` (avoids generated equals/hashCode) | ||
| - `@FieldNameConstants` on all domains/DTOs for type-safe field references | ||
| - `@JsonView` annotations control API visibility (`Views.Public.class`, `Views.Internal.class`, `Git.class`, `FromRequest.class`) | ||
| - `@Transient` for computed fields not persisted to MongoDB | ||
| - Domains/DTOs also follow CE-EE split: `ActionDTO extends ActionCE_DTO` | ||
|
|
||
| ## Dependency Injection | ||
|
|
||
| - Constructor-based injection exclusively (no `@Autowired` on fields) | ||
| - Use `@RequiredArgsConstructor` or `@AllArgsConstructor` (Lombok) | ||
| - `@Lazy` to break circular dependencies | ||
| - `@Autowired(required = false)` for optional dependencies | ||
|
|
||
| ## Reactive Patterns | ||
|
|
||
| All service methods return `Mono<T>` or `Flux<T>`. Key patterns: | ||
|
|
||
| - `.switchIfEmpty(Mono.error(new AppsmithException(...)))` — standard "not found" | ||
| - `Mono.cache()` — prevent duplicate subscriptions when a Mono feeds multiple operators | ||
| - `Mono.zip()` / `.zipWith()` / `.zipWhen()` — combine parallel or dependent operations | ||
| - `Mono.defer()` — lazy evaluation inside `.switchIfEmpty()` and `.then()` | ||
| - `.flatMap()` chains — primary mechanism for sequential reactive operations | ||
| - `.onErrorResume()` — graceful degradation | ||
| - `.onErrorMap()` — translate errors to `AppsmithException` | ||
| - Never block reactive streams — no `.block()` in production code | ||
|
|
||
| ## Error Handling | ||
|
|
||
| - `AppsmithException` wraps `AppsmithError` enum (100+ entries with HTTP status, error code `AE-{DOMAIN}-{NUMBER}`, message template) | ||
| - Throw via `new AppsmithException(AppsmithError.SOME_ERROR, args...)` | ||
| - Global handler (`GlobalExceptionHandler`) catches and returns `ResponseDTO<ErrorDTO>` | ||
| - Plugin errors: `AppsmithPluginException` wraps `BasePluginError` | ||
|
|
||
| ## Feature Flags | ||
|
|
||
| - `@FeatureFlagged(featureFlagName = FeatureFlagEnum.XXX)` on EE methods | ||
| - Spring AOP `@Around` advice dispatches: flag ON → EE method; flag OFF → superclass (CE-Compatible/CE) | ||
| - For reactive methods: aspect uses `flatMap` on the flag check Mono | ||
| - For synchronous methods: requires `organizationId` parameter; reads from in-memory org cache | ||
| - New flags: add to `FeatureFlagEnum` in `appsmith-interfaces` | ||
| - Flag naming: `release_*` (rollout), `license_*` (paid), `rollout_*` (gradual), `ab_*` (experiment) | ||
|
|
||
| ## Custom Annotations | ||
|
|
||
| | Annotation | Purpose | | ||
| |------------|---------| | ||
| | `@FeatureFlagged` | Controls method execution via feature flag + AOP | | ||
| | `@Encrypted` | Marks fields for at-rest encryption | | ||
| | `@Cache` / `@CacheEvict` | Reactive method result caching / eviction | | ||
| | `@DistributedLock` | Distributed lock with TTL (default 5 min) | | ||
| | `@RateLimit` | Rate-limits API endpoints | | ||
|
|
||
| ## Build Commands | ||
|
|
||
| ```bash | ||
| cd app/server && mvn spotless:apply # Format code | ||
| cd app/server && mvn spotless:check # Check formatting only | ||
| cd app/server && ./build.sh -DskipTests # Build without tests | ||
| cd app/server && ./build.sh -DskipTests -T 8 # Build without tests (8 threads) | ||
| ``` |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| --- | ||
| description: Checklist for fixing bugs, debugging issues, and verifying bug fixes | ||
| globs: | ||
| alwaysApply: false | ||
| --- | ||
| # Bug Fix Workflow (TDD Approach) | ||
|
|
||
| ## 1. Understand | ||
|
|
||
| - Reproduce the issue locally and understand the bug report | ||
| - Identify root cause through code exploration — fix the cause, not symptoms | ||
| - Check if there's a related issue/ticket to reference | ||
|
|
||
| ## 2. Write Failing Test(s) | ||
|
|
||
| Write test(s) that demonstrate the bug before writing any fix code. | ||
| - Server: add test in the relevant `*Test.java` class | ||
| - Client: add test in the relevant `*.test.ts(x)` file | ||
| - Test both CE and EE paths if the bug touches feature-flagged code | ||
|
|
||
| ## 3. Verify RED | ||
|
|
||
| Run the test(s) and confirm they **FAIL**: | ||
| ```bash | ||
| # Server | ||
| cd app/server && source envs/test.env.example && mvn test -pl appsmith-server -Dtest=ClassName | ||
|
|
||
| # Client | ||
| cd app/client && yarn test:jest -- --testPathPattern=<pattern> | ||
| ``` | ||
|
|
||
| If the test passes without any fix, it does not cover the bug. Rewrite the test. | ||
|
|
||
| ## 4. Implement Fix | ||
|
|
||
| - Address the root cause with minimal, focused changes | ||
| - Follow the CE-EE pattern if the fix touches extensible components | ||
| - No hardcoded values, no debug logging (`console.log`, `System.out.println`) | ||
| - Use safe property access (optional chaining or lodash/get for nested objects) | ||
| - Handle edge cases and null/undefined gracefully | ||
|
|
||
| ## 5. Verify GREEN | ||
|
|
||
| Run the same test(s) and confirm they now **PASS**. Use the same commands from step 3. | ||
|
|
||
| ## 6. Sanity Check | ||
|
|
||
| Run closely related tests to catch obvious regressions. Do NOT run the full test suite locally — that's what CI is for. | ||
| - Find test classes in the same package/module as the changed code | ||
| - Run those specific tests to verify nothing adjacent broke | ||
| - Full regression is validated by CI after push | ||
|
|
||
| ## Before Committing | ||
|
|
||
| - Formatting handled automatically by Husky pre-commit hooks | ||
| - PR title: `fix(scope): description` | ||
| - Reference the issue/ticket in PR description |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| --- | ||
| description: | ||
| globs: app/client/src/**/*.test.ts,app/client/src/**/*.test.tsx | ||
| alwaysApply: false | ||
| --- | ||
| # Client Unit Test Conventions (Jest) | ||
|
|
||
| ## Framework | ||
|
|
||
| - Jest 29 + React Testing Library (no Enzyme, no snapshot testing) | ||
| - `@testing-library/react` for rendering/querying | ||
| - `@testing-library/react` also provides `renderHook` for hook testing | ||
| - `@testing-library/user-event` for user interaction simulation | ||
| - `@testing-library/jest-dom` for DOM matchers | ||
| - MSW (Mock Service Worker) for API mocking | ||
| - `redux-mock-store` for Redux store mocking | ||
| - Test timeout: 9000ms | ||
|
|
||
| ## Running Tests | ||
|
|
||
| ```bash | ||
| cd app/client && yarn test:unit # All unit tests with coverage | ||
| cd app/client && yarn test:jest -- --testPathPattern=<pattern> # Specific tests (watch mode) | ||
| ``` | ||
|
|
||
| ## Test Setup (Automatic) | ||
|
|
||
| Configured in `test/setup.ts`: | ||
| - MSW server starts `beforeAll`, resets handlers `afterEach`, closes `afterAll` | ||
| - Polyfills: `crypto`, `TextDecoder`, `ReadableStream`, `structuredClone` | ||
| - Mocks: `IntersectionObserver`, `ResizeObserver`, `scrollTo` | ||
|
|
||
| ## Test Patterns | ||
|
|
||
| ### Component test (with Redux) | ||
|
|
||
| ```typescript | ||
| import { render, screen } from "test/testUtils"; | ||
|
|
||
| describe("MyComponent", () => { | ||
| it("renders correctly with given props", () => { | ||
| render(<MyComponent label="test" />, { | ||
| initialState: store.getState(), | ||
| url: "/app/page-123", | ||
| }); | ||
|
|
||
| expect(screen.getByText("test")).toBeInTheDocument(); | ||
| }); | ||
| }); | ||
| ``` | ||
|
|
||
| The custom `render` from `test/testUtils` wraps components in `<BrowserRouter>`, `<Provider store>`, and `<ThemeProvider>`. | ||
|
|
||
| ### Component test (simple, no Redux) | ||
|
|
||
| ```typescript | ||
| import { render, screen } from "@testing-library/react"; | ||
|
|
||
| it("renders label", () => { | ||
| render(<Button label="Click me" />); | ||
| expect(screen.getByText("Click me")).toBeInTheDocument(); | ||
| }); | ||
| ``` | ||
|
|
||
| ### Hook test | ||
|
|
||
| ```typescript | ||
| import { renderHook, act } from "@testing-library/react"; | ||
|
|
||
| it("returns updated value", () => { | ||
| const { result } = renderHook(() => useMyHook(initialValue)); | ||
|
|
||
| act(() => { result.current.update(newValue); }); | ||
|
|
||
| expect(result.current.value).toBe(newValue); | ||
| }); | ||
| ``` | ||
|
|
||
| ### Saga test (generator stepping) | ||
|
|
||
| ```typescript | ||
| import { runSaga } from "redux-saga"; | ||
|
|
||
| it("dispatches success on valid input", async () => { | ||
| const dispatched: any[] = []; | ||
| await runSaga( | ||
| { dispatch: (action) => dispatched.push(action), getState: () => mockState }, | ||
| mySaga, | ||
| { type: ReduxActionTypes.TRIGGER, payload: input }, | ||
| ).toPromise(); | ||
|
|
||
| expect(dispatched).toContainEqual({ type: ReduxActionTypes.SUCCESS, payload: expected }); | ||
| }); | ||
| ``` | ||
|
|
||
| ### Parameterized test | ||
|
|
||
| ```typescript | ||
| test.each([ | ||
| ["input1", "expected1"], | ||
| ["input2", "expected2"], | ||
| ])("converts %s to %s", (input, expected) => { | ||
| expect(transform(input)).toBe(expected); | ||
| }); | ||
| ``` | ||
|
|
||
| ### Async assertions | ||
|
|
||
| ```typescript | ||
| import { waitFor } from "@testing-library/react"; | ||
|
|
||
| await waitFor(() => { | ||
| expect(screen.getByText("loaded")).toBeInTheDocument(); | ||
| }); | ||
| ``` | ||
|
|
||
| ## Mocking Patterns | ||
|
|
||
| ### Module mock with partial override | ||
|
|
||
| ```typescript | ||
| jest.mock("ee/utils/someUtil", () => ({ | ||
| ...jest.requireActual("ee/utils/someUtil"), | ||
| specificFunction: jest.fn().mockReturnValue(mockValue), | ||
| })); | ||
| ``` | ||
|
|
||
| ### MSW API mock | ||
|
|
||
| ```typescript | ||
| import { rest } from "msw"; | ||
| import { server } from "test/__mocks__/server"; | ||
|
|
||
| beforeEach(() => { | ||
| server.use( | ||
| rest.get("/api/v1/resource/:id", (req, res, ctx) => | ||
| res(ctx.json({ responseMeta: { status: 200 }, data: mockData })), | ||
| ), | ||
| ); | ||
| }); | ||
| ``` | ||
|
|
||
| ## Key Conventions | ||
|
|
||
| - Always import from `"ee/..."` in tests too — never from `"ce/"` directly | ||
| - Use `test/testUtils` `render` for Redux-connected components; `@testing-library/react` for pure components | ||
| - Test factories in `test/factories/` for deterministic widget DSL data | ||
| - Callback mocking: `jest.fn()`, verify with `toHaveBeenCalledWith(...)` | ||
| - Assertions: `expect(x).toEqual(y)` for deep equality, `expect(x).toBe(y)` for primitives |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| --- | ||
| description: | ||
| globs: | ||
| alwaysApply: true | ||
| --- | ||
| # Commit & PR Conventions | ||
|
|
||
| ## PR Title Format (Conventional Commits) | ||
|
|
||
| `type(scope): description` | ||
|
|
||
| **Types:** feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert | ||
|
|
||
| **Examples:** | ||
| - `feat(widgets): add new table widget capabilities` | ||
| - `fix(auth): resolve login redirect issue` | ||
| - `refactor(api): simplify error handling logic` | ||
|
|
||
| ## Commit Message Style | ||
|
|
||
| - Use descriptive messages with issue reference when applicable | ||
| - Verb-prefixed: "Add ...", "Fix ...", "Update ...", "Remove ..." | ||
|
|
||
| ## Pre-commit Checks (Automatic via Husky) | ||
|
|
||
| When server files are staged → `mvn spotless:apply` runs automatically | ||
| When client files are staged → `eslint --fix --cache` + `prettier --write --cache` run via lint-staged | ||
| All staged files → `gitleaks` secret scanning runs automatically | ||
| Pre-commit checks are skipped for merge commits. | ||
|
|
||
| Do not manually run formatting before commit — Husky handles it. | ||
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.