Ctrl K
ComunidadGuías…

CONTRIBUTING.md

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# 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<T>` → `{ "response": <T> }`, 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 `<comment>`
  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

```
<type>: <object> — <delta>
```

`<type>` 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.

`<object>` is a noun and comes first: the entity, connector, endpoint or table someone will
later grep the history for. `<delta>` 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.