# Code Conventions

> Skills Gateway build commands, quality gates, Java and TypeScript conventions, and reqstool traceability annotations. Load before writing or reviewing any code in this repo.

- Skill: `skillsgateway/code-conventions` (Agent Skill)
- Install (CLI): `npx skillmds@latest add skillsgateway/code-conventions`
- Raw SKILL.md: https://api.skillmd.com/api/skills/skillsgateway/code-conventions/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: skillsgateway (https://skillmd.com/u/skillsgateway)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/skillsgateway/code-conventions

---


# Code conventions

## Build & gates

```bash
./mvnw clean verify                     # everything: Java tests (Arconia/Testcontainers PostgreSQL),
                                        # Spotless, Checkstyle, CycloneDX SBOM, UI gates, packaged jar
(cd src/main/frontend && pnpm test:stories)  # Storybook story tests in real chromium (axe-as-error)
(cd src/main/frontend && pnpm e2e)    # Playwright vs the real jar + mock OIDC IdP (compose.e2e.yaml)
reqstool status local -p docs/reqstool  # must end "N/N complete · PASS" (run after the two above)
openspec validate --all --strict
./mvnw -Pnative -DskipTests native:compile   # GraalVM native binary (release path; CI does this)
```

- Always `clean` before trusting the reqstool gate: the annotation processor
  writes per-source-set files that incremental compilation truncates.
- The UI is built INSIDE `./mvnw verify` (frontend-maven-plugin, pinned
  node/pnpm); `cd src/main/frontend && pnpm …` is only for UI development loops and e2e.
- `./mvnw -q spotless:apply` before committing Java.

## Java

- Java 25, Spring Boot 4. Constructor injection, no Lombok, records for DTOs.
- Persistence via `JdbcClient` + Flyway (no JPA). Single `V1__init.sql` until
  the owner says otherwise — fold schema changes into it (Testcontainers
  recreate the schema every run).
- Enumerated values are native PostgreSQL enum types, never `TEXT ... CHECK (col
  IN (...))`: `CREATE TYPE <singular table>_<column> AS ENUM (...)` (the column
  name alone collides — three tables carry a `state`). Write through an explicit
  cast (`:state::snapshot_state`); reads are unchanged, the driver returns a
  `String`. The trade is deliberate and permanent-ish: a new value cannot be
  *used* in the transaction that adds it (adding one and backfilling rows with it
  takes two migrations), and no value can ever be dropped — `ALTER TYPE ... DROP
  VALUE` is "not implemented", so removing one means a replacement type and a
  rewrite of every dependent column.
- JGit for all git operations — never subprocess git in production code.
  Tests may run the git binary only via `AbstractGatewayTest.git(...)`
  (isolated from host config).
- Container-backed tests use **Arconia Dev Services** where one exists — one
  container serves both `bootRun` and the tests. This project uses `postgresql`
  (automatic) and `lgtm` (opt-in via the `observability` profile). Check the
  arconia BOM for what else is published before assuming a gap; `floci` (an AWS
  emulator) is there and is not obvious from a Maven Central name search. A raw Testcontainers container is a fallback
  for dependencies Arconia does not cover; justify it in the change's design and
  consider proposing the dev service upstream. See CLAUDE.md, "Process".
- Formatting is Spotless (palantir-java-format) + Checkstyle — non-negotiable
  gates, auto-fix with `spotless:apply`.
- REST errors: `ResponseStatusException` / `ProblemDetail`. New endpoints get
  `@Tag`, `@Operation`, `@ApiResponse`, and `@Schema` on their DTOs (the
  Scalar reference at `/docs` renders them).

## TypeScript / UI (`src/main/frontend/`)

- Strict TS, oxlint, vitest (jsdom `unit` project), Storybook story tests
  with axe-as-error (browser `storybook` project, `pnpm test:stories`),
  Playwright e2e. `pnpm verify` runs the jsdom UI gate; the story and e2e
  suites run outside `mvnw verify` — as their own commands locally and their
  own CI jobs (#103).
- API types are GENERATED: `src/main/frontend/openapi.json` → `src/api/types.gen.ts`
  (`pnpm gen:api-types` — it pins TypeScript 5.9.3 via `dlx`, because
  `openapi-typescript` cannot run against the workspace's TypeScript 7).
  Regenerate after backend API changes (snapshot comes from
  `OpenApiDocsTests` → `target/openapi.json`). Never hand-edit.
  `OpenApiContractTests` fails the build when the committed `openapi.json` is not
  the document the gateway serves, so regenerate it in the same change — it is
  the baseline the breaking-change gate diffs against, not just an input to
  codegen. `info.version` is a placeholder there by design (the served document
  carries the real one).
- MSW handlers are typed from the generated types; mocks never appear in the
  acceptance path (Playwright runs against the real gateway).

## Traceability (reqstool)

- SSOT: `docs/reqstool/requirements.yml` + `software_verification_cases.yml`
  (`GW_*`, `SVC_GW_*`). Follow the reqstool plugin skills' conventions.
- Java: `@Requirements({"GW_XXXX"})` on the implementing method,
  `@SVCs({"SVC_GW_XXXX"})` on the verifying test.
- TypeScript: JSDoc `@Requirements GW_XXXX` on components, `@SVCs SVC_GW_XXXX`
  above Playwright `test(...)` calls — test titles must be snake_case
  identifiers (junit-name matching).
- Never weaken or delete an existing SVC test to make a change pass.

## Git

- Conventional Commits, DCO sign-off (`git commit -s`), branches
  `<type>/<kebab-description>`, PR title = conventional commit (squash merge).

