# Contributing to AgiMate Backend Thanks for taking the time. This page covers what you need before opening a pull request. ## Contributor License Agreement Every contributor signs the [CLA](CLA.md) once, on their first pull request. A bot comments with instructions; you reply in the pull request and it records the signature. Nothing to print, sign or email. The CLA lets the project be distributed under more than one set of terms — the public [FSL-1.1-ALv2](LICENSE) and a commercial licence alongside it. Without it that door closes permanently the moment the first outside change lands, because reopening it would need every contributor's consent. You keep full ownership of your contribution either way; what the boundary between free and commercial use is, is in [LICENSING.md](LICENSING.md). ## Reporting a security issue Do not open an issue. See the [security policy](https://github.com/AgiMateIo/.github/blob/main/SECURITY.md). ## Getting the project running The [README](README.md) has the quick start. You need **JDK 25**: every module sets a Java 25 toolchain. `./gradlew --version` prints the launcher JVM, which may be older than 25 — Gradle finds the right toolchain itself, but exporting `JAVA_HOME` pointing at JDK 25 removes the ambiguity. `build` and `test` need no Docker, database or `services/.env`: the tests are unit tests. The infrastructure stack (`ops/dev-init.sh` + `docker compose --profile infra up -d`) is only needed to run the services. Everything below assumes you are in `services/`. ```bash ./gradlew build # compile and test everything ./gradlew :control-api:test # one module ./gradlew :libs:common:test --tests "ru.agimate.common.util.UUIDUtilsTest" ``` [`docs/`](docs/) is organised by intent — start with [architecture/overview.md](docs/architecture/overview.md). Note that most of the documentation is written in Russian; the code and its comments are in English. ## Working with coding agents The repository ships [AGENTS.md](AGENTS.md), the entry point for AI coding agents, and [docs/development/code-conventions.md](docs/development/code-conventions.md) with the full convention list. Point Claude Code, Codex, Cursor or another agent at `AGENTS.md`; treat both files as documentation and update them with the code. ## Conventions worth knowing before you write code These are the ones a newcomer trips over. The rest you can read off the surrounding code; the full list is in [docs/development/code-conventions.md](docs/development/code-conventions.md). - **Comments are in English**, and they explain *why*, not *what*. A comment restating the next line is worse than no comment. Russian appears in code only as data — a quoted literal, or a non-ASCII example where the example is the point. Test `@DisplayName`s are in Russian. - **Layout**: `controller` → `service` → `database/{repositories,entities}`, plus `config`, `security`, `util`. In `control-api` controllers are split by audience (`agent/`, `app/`, `manage/`, `webhook/`) and DTOs live per area. - **Dependency injection** through `@RequiredArgsConstructor`. `@Autowired` is never used on a field or setter; on a constructor it appears only when a second, test-only constructor exists. - **DTOs are records**: requests carry validation annotations, responses carry `@Schema`. - **Responses are wrapped**: `SuccessResponse` → `{ "response": }`, errors → `ErrorResponse`. Throw `NotFoundStatusException`, `BadRequestStatusException` and friends at the HTTP boundary; inside the connector layer throw `ConnectorException` instead. - **Entities**: `@Enumerated(EnumType.STRING)`, `TEXT` over `VARCHAR(n)`, and flat foreign keys as `UUID` columns. IDs are generated by the database (`uuidv7()`); `UUIDUtils.generateUUIDv8()` only where the id must exist before insert because something else already references it (a connection key, an `agf_` reference, a blob key) — see [decisions/uuid-primary-keys.md](docs/decisions/uuid-primary-keys.md). - **Migrations** go to `updates/YYYY/MM/DD-NN-name.xml`. `updated_at` is always `NOT NULL DEFAULT CURRENT_TIMESTAMP`. Write comments as XML ``, not the `` element, wherever the DDL does not explain itself — a partial unique index, a `COALESCE` in a key, a denormalisation. The schema shows what is true now and `git blame` shows when it changed, but neither records why. Naming rules: [decisions/schema-conventions.md](docs/decisions/schema-conventions.md). - **Tests** are JUnit 5 unit tests with Mockito — no Spring context, database or Testcontainers. Group with `@Nested` and name with a Russian `@DisplayName`. ## Commits ``` : — ``` `` is one of `feat`, `fix`, `refactor`, `perf`, `test`, `docs`, `chore`. Whether a change is a feature or a refactor is decided by whether it is visible from outside — to the API, the agent or the user — not by how much code moved. `` is a noun and comes first: the entity, connector, endpoint or table someone will later grep the history for. `` is the result, not the action — "soft delete for agents instead of hard delete", not "fixed deletion". Keep the subject at 72 characters or less, lower case after the colon, no trailing period, no scope in parentheses. If you need an "and" in the object, the commit wants splitting. An optional body of two to four bullets explains why it was done this way and what follows from it — not which files changed. ## Pull requests Small and focused beats large and complete. Make sure `./gradlew build` passes, say what you changed and why in the description, and update `docs/` if you touched endpoints, environment variables or architecture — request and response schemas are generated from the code and are never duplicated there.