From c3205f0a088bb6120943dd7bc17c6290a0ddbe96 Mon Sep 17 00:00:00 2001 From: Nano Taboada Date: Thu, 19 Feb 2026 18:27:00 -0300 Subject: [PATCH] docs(copilot): unify instructions as canonical single-file format Consolidate .github/copilot-instructions.md and AGENTS.md into a single always-on file with a consistent section order (Overview, Tech Stack, Structure, Coding Guidelines, Commands, Agent Mode). - Replace two-file system with one canonical copilot-instructions.md - Absorb AGENTS.md content (autonomy levels, workflows) into Agent Mode --- .github/copilot-instructions.md | 232 +++++++++++++++----------------- AGENTS.md | 209 ---------------------------- 2 files changed, 112 insertions(+), 329 deletions(-) delete mode 100644 AGENTS.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 3befb12..c4058d9 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,132 +1,124 @@ -# Copilot Instructions +# GitHub Copilot Instructions + +## Overview + +REST API for managing football players built with Java and Spring Boot. Implements CRUD operations with a layered architecture, Spring Data JPA + SQLite, Bean Validation, Spring Cache, and Swagger documentation. Part of a cross-language comparison study (.NET, Go, Python, Rust, TypeScript). + +## Tech Stack + +- **Language**: Java 25 (LTS, required) +- **Framework**: Spring Boot 4.0.0 (Spring MVC) +- **ORM**: Spring Data JPA + Hibernate +- **Database**: SQLite (file-based runtime, in-memory for tests) +- **Build**: Maven 3 — always use `./mvnw` wrapper +- **Validation**: Bean Validation (JSR-380) +- **Caching**: Spring `@Cacheable` (1-hour TTL) +- **Mapping**: ModelMapper +- **Logging**: SLF4J +- **Testing**: JUnit 5 + AssertJ + MockMvc + Mockito +- **Coverage**: JaCoCo +- **API Docs**: SpringDoc OpenAPI 3 (Swagger) +- **Boilerplate**: Lombok +- **Containerization**: Docker + +## Structure + +```text +src/main/java/ +├── controllers/ — HTTP handlers; delegate to services, no business logic [HTTP layer] +├── services/ — Business logic + @Cacheable caching [business layer] +├── repositories/ — Spring Data JPA with derived queries [data layer] +├── models/ — Player entity + DTOs +└── converters/ — ModelMapper entity ↔ DTO transformations +src/main/resources/ — application.properties, Logback config +src/test/java/ — test classes mirroring main structure +src/test/resources/ — test config, schema (ddl.sql), seed data (dml.sql) +storage/ — SQLite database file (runtime) +``` + +**Layer rule**: `Controller → Service → Repository → JPA`. Controllers must not access repositories directly. Business logic must not live in controllers. -## Project Overview +## Coding Guidelines -REST API for managing football players built with Java 25 and Spring Boot 4. Demonstrates layered architecture (Controller → Service → Repository), Spring Data JPA with SQLite, comprehensive validation, caching, and Swagger documentation. +- **Naming**: camelCase (methods/variables), PascalCase (classes), UPPER_SNAKE_CASE (constants) +- **Files**: class name matches file name +- **DI**: Constructor injection via Lombok `@RequiredArgsConstructor`; never field injection +- **Annotations**: `@RestController`, `@Service`, `@Repository`, `@Entity`, `@Data`/`@Builder`/`@AllArgsConstructor` (Lombok) +- **Transactions**: `@Transactional(readOnly = true)` on read service methods; `@Transactional` on writes +- **Errors**: `@ControllerAdvice` for global exception handling +- **Logging**: SLF4J only; never `System.out.println` +- **DTOs**: Never expose entities directly in controllers — always use DTOs +- **Tests**: BDD Given-When-Then naming (`givenX_whenY_thenZ`); AssertJ BDD style (`then(result).isNotNull()`); in-memory SQLite auto-clears after each test +- **Avoid**: field injection, `new` for Spring beans, missing `@Transactional`, exposing entities in controllers, hardcoded configuration -## Quick Start +## Commands + +### Quick Start ```bash -# Development -./mvnw spring-boot:run # Run with hot reload (port 9000) -./mvnw clean test # Run tests -./mvnw clean test jacoco:report # Test with coverage - -# Docker -docker compose up # Start in container -docker compose down -v # Reset database - -# Documentation -http://localhost:9000/swagger/index.html # API docs -http://localhost:9001/actuator/health # Health check +./mvnw spring-boot:run # port 9000 +./mvnw clean test # run tests +./mvnw clean test jacoco:report # tests + coverage +open target/site/jacoco/index.html # view coverage report +docker compose up +docker compose down -v ``` -## Stack +### Pre-commit Checks -- Java 25 (LTS, required) -- Spring Boot 4.0.0 (Spring MVC) -- Spring Data JPA + Hibernate -- SQLite (file-based runtime, in-memory tests) -- Maven 3 (use `./mvnw` wrapper) -- Bean Validation (JSR-380) -- Spring Cache (1-hour TTL) -- JUnit 5 + AssertJ + MockMvc -- JaCoCo (coverage) -- SpringDoc OpenAPI 3 (Swagger) -- Lombok (boilerplate reduction) +1. `./mvnw clean install` — must succeed +2. All tests pass +3. Check coverage at `target/site/jacoco/index.html` +4. No compilation warnings +5. Commit message follows Conventional Commits format (enforced by commitlint) -## Project Patterns +### Commits -- **Architecture**: Layered (Controller → Service → Repository → JPA) -- **Dependency Injection**: Constructor injection via Lombok `@RequiredArgsConstructor` -- **Error Handling**: `@ControllerAdvice` for global exception handling -- **Validation**: Bean Validation annotations in DTOs (`@NotNull`, `@Min`, etc.) -- **Caching**: Spring `@Cacheable` on service layer (1-hour TTL) -- **DTO Pattern**: ModelMapper for entity ↔ DTO transformations -- **Repository**: Spring Data JPA with derived queries (prefer over custom JPQL) +Format: `type(scope): description (#issue)` — max 80 chars +Types: `feat` `fix` `chore` `docs` `test` `refactor` `ci` `perf` +Example: `feat(api): add player stats endpoint (#42)` -## Code Conventions +## Agent Mode -- **Naming**: camelCase (methods/variables), PascalCase (classes), UPPER_SNAKE_CASE (constants) -- **Files**: Class name matches file name (e.g., `PlayersController.java`) -- **Package Structure**: - - `controllers/` - REST endpoints (`@RestController`) - - `services/` - Business logic (`@Service`) - - `repositories/` - Data access (`@Repository`, extends `JpaRepository`) - - `models/` - Domain entities (`@Entity`) and DTOs - - `converters/` - JPA attribute converters -- **Annotations**: - - Controllers: `@RestController`, `@RequestMapping`, `@Operation` (OpenAPI) - - Services: `@Service`, `@Transactional`, `@Cacheable` - - Repositories: `@Repository` (Spring Data JPA) - - DTOs: `@NotNull`, `@Min`, `@Max` (Bean Validation) - - Entities: `@Entity`, `@Table`, `@Id`, `@GeneratedValue` -- **Lombok**: `@Data`, `@Builder`, `@AllArgsConstructor`, `@RequiredArgsConstructor` -- **Logging**: SLF4J (never `System.out.println`) - -## Testing - -- **Structure**: `*Tests.java` in `src/test/java/` (mirrors main package structure) -- **Naming Pattern**: `givenX_whenY_thenZ` (BDD Given-When-Then) - - `given`: Preconditions/context (e.g., `givenPlayerExists`, `givenInvalidData`, `givenNoMatches`) - - `when`: Action being tested (e.g., `whenPost`, `whenFindById`, `whenCreate`) - - `then`: Expected outcome (e.g., `thenReturnsPlayer`, `thenReturnsConflict`, `thenReturnsEmpty`) -- **Examples**: - - ```java - // Controller - void givenSquadNumberExists_whenPost_thenReturnsConflict() - - // Service - void givenNoConflict_whenCreate_thenReturnsPlayerDTO() - - // Repository - void givenPlayerExists_whenFindById_thenReturnsPlayer() - ``` - -- **JavaDoc**: BDD Given/When/Then structure in test comments - - ```java - /** - * Given a player with squad number 5 already exists in the database - * When POST /players is called with a new player using squad number 5 - * Then response status is 409 Conflict - */ - @Test - void post_squadNumberExists_returnsConflict() { ... } - ``` - -- **Annotations**: `@SpringBootTest`, `@AutoConfigureMockMvc`, `@Test` -- **Assertions**: AssertJ BDD style (e.g., `then(result).isNotNull()`) -- **Mocking**: Mockito for service layer tests -- **Database**: Tests use in-memory SQLite (auto-cleared after each test) -- **Coverage**: Target high coverage (JaCoCo reports in `target/site/jacoco/`) - -## Avoid - -- Field injection (use constructor injection) -- Using `new` for Spring beans (breaks DI) -- Missing `@Transactional` on service methods that modify data -- Exposing entities directly in controllers (use DTOs) -- `System.out.println` (use SLF4J logging) -- Hardcoded configuration (use `@Value` or `application.properties`) -- Ignoring exceptions (always handle or propagate) -- Testing implementation details (test behavior, not internals) - -## Commit Messages - -Follow Conventional Commits format (enforced by commitlint in CI): - -**Format**: `type(scope): description (#issue)` (max 80 chars) - -**Types**: `feat`, `fix`, `docs`, `test`, `refactor`, `chore`, `ci`, `perf`, `style`, `build` - -**Examples**: - -- `feat(api): add player stats endpoint (#42)` -- `fix(service): resolve cache invalidation bug (#88)` -- `test: adopt BDD Given-When-Then pattern across all tests (#266)` - ---- - -For detailed workflows, troubleshooting, and CI/CD setup, load `#file:AGENTS.md`. +### Proceed freely + +- Route handlers and controller endpoints +- Service layer business logic +- Repository custom queries +- Unit and integration tests +- Exception handling in `@ControllerAdvice` +- Documentation updates, bug fixes, and refactoring +- Utility classes and helpers + +### Ask before changing + +- Database schema (entity fields, relationships) +- Dependencies (`pom.xml`) +- CI/CD configuration (`.github/workflows/`) +- Docker setup +- Application properties +- API contracts (breaking DTO changes) +- Caching strategy or TTL values +- Package structure + +### Never modify + +- `.java-version` (JDK 25 required) +- Maven wrapper scripts (`mvnw`, `mvnw.cmd`) +- Port configuration (9000/9001) +- Test database configuration (in-memory SQLite) +- Production configurations or deployment secrets + +### Key workflows + +**Add an endpoint**: Define DTO in `models/` with Bean Validation → add service method in `services/` with `@Transactional` → create controller endpoint with `@Operation` annotation → add tests → run `./mvnw clean test jacoco:report`. + +**Modify schema**: Update `@Entity` in `models/Player.java` → update DTOs if API changes → manually update `storage/players-sqlite3.db` (preserve 26 players) → update service, repository, and tests → run `./mvnw clean test`. + +**After completing work**: Suggest a branch name (e.g. `feat/add-player-stats`) and a commit message following Conventional Commits including co-author line: + +```text +feat(scope): description (#issue) + +Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> +``` diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 1815408..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,209 +0,0 @@ -# Agent Instructions - -## Project Structure - -```tree -/src/main/java/... - Application code - ├── controllers/ - REST endpoints (@RestController) - ├── services/ - Business logic (@Service, @Cacheable) - ├── repositories/ - Data access (JpaRepository) - ├── models/ - Domain entities (@Entity) and DTOs - └── converters/ - JPA attribute converters -/src/test/java/... - Test classes (mirrors main structure) -/src/main/resources/ - application.properties, logback config -/src/test/resources/ - Test config, schema (ddl.sql), seed data (dml.sql) -/storage/ - SQLite database file (runtime) -/target/ - Build artifacts, test reports, coverage -/.github/workflows/ - CI/CD (maven.yml) -``` - -## Common Workflows - -### Adding a new endpoint - -1. **Define DTO** (if needed) in `models/` with Bean Validation: - - ```java - @Data - @Builder - public class PlayerStatsDTO { - @NotNull private Integer wins; - @NotNull private Integer losses; - } - ``` - -2. **Add service method** in `services/PlayersService.java`: - - ```java - @Transactional(readOnly = true) - public PlayerStatsDTO getPlayerStats(Long id) { - Player player = repository.findById(id) - .orElseThrow(() -> new ResourceNotFoundException(...)); - // Business logic - return PlayerStatsDTO.builder()...build(); - } - ``` - -3. **Create controller endpoint** in `controllers/PlayersController.java`: - - ```java - @Operation(summary = "Get player statistics") - @GetMapping("/{id}/stats") - public ResponseEntity getPlayerStats(@PathVariable Long id) { - return ResponseEntity.ok(service.getPlayerStats(id)); - } - ``` - -4. **Add tests** in `test/controllers/PlayersControllerTests.java`: - - ```java - @Test - void getPlayerStats_playerExists_returnsStats() { - // Given/When/Then with MockMvc - } - ``` - -5. **Validate**: Run `./mvnw clean test jacoco:report` - all tests pass, coverage maintained - -### Modifying database schema - -⚠️ **Manual process** (no migrations framework): - -1. Update `@Entity` class in `models/Player.java` (add/modify fields) -2. Update DTOs in `models/PlayerDTO.java` if API contract changes -3. Manually update `storage/players-sqlite3.db` (SQLite CLI or DB Browser) - - Preserve existing 26 players - - Or delete and reinitialize from seed data -4. Update service layer methods in `services/PlayersService.java` -5. Update repository queries if needed in `repositories/PlayersRepository.java` -6. Update tests (test data, assertions) -7. Run `./mvnw clean test` to verify - -### Running tests - -```bash -# All tests -./mvnw test - -# With coverage report (matches CI) -./mvnw clean test jacoco:report - -# View coverage in browser -open target/site/jacoco/index.html - -# Specific test class -./mvnw test -Dtest=PlayersControllerTests - -# Specific test method -./mvnw test -Dtest=PlayersControllerTests#post_validPlayer_returnsCreated - -# Verbose output for debugging -./mvnw test -X -``` - -## Autonomy Levels - -### Proceed freely - -- Route handlers and controller endpoints (following Spring patterns) -- Service layer business logic -- Repository custom queries (derived or `@Query`) -- Unit and integration tests (maintain naming convention) -- Exception handling in `@ControllerAdvice` -- Documentation updates (README, JavaDoc, OpenAPI) -- Bug fixes in business logic -- Refactoring within existing layers -- Utility classes and helper methods - -### Ask before changing - -- Database schema (entity fields, relationships) -- Dependencies (`pom.xml` - adding new libraries) -- CI/CD configuration (`.github/workflows/maven.yml`) -- Docker setup (`Dockerfile`, `compose.yaml`) -- Application properties (`application.properties` - global config) -- API contracts (breaking changes to DTOs) -- Security configuration -- Caching strategy or TTL values -- Package structure reorganization - -### Never modify - -- Production configurations -- Deployment secrets or credentials -- `.java-version` (JDK 25 required) -- Maven wrapper scripts (`mvnw`, `mvnw.cmd`) -- Core architectural patterns (layered architecture) -- Test database configuration (in-memory SQLite) -- Port configuration (9000/9001 without discussion) - -## Pre-commit Checks - -1. **Build**: `./mvnw clean install` - must pass without errors -2. **Tests**: All tests pass (included in `clean install`) -3. **Coverage**: Check `target/site/jacoco/index.html` - maintain existing levels -4. **No warnings**: Compilation completes without warnings -5. **Commit format**: `type(scope): description (#issue)` (max 80 chars, commitlint validates) -6. **Code style**: Follow conventions (Lombok, constructor injection, etc.) - -**CI validation**: Push triggers GitHub Actions (`.github/workflows/maven.yml`): - -- Lint commit messages (commitlint) -- Build: `./mvnw clean install` -- Test coverage upload to Codecov - -## API Testing - -```bash -# Health check -curl http://localhost:9001/actuator/health - -# Get all players -curl http://localhost:9000/players - -# Get player by ID -curl http://localhost:9000/players/1 - -# Search by league -curl http://localhost:9000/players/search/league/Premier - -# Create player -curl -X POST http://localhost:9000/players \ - -H "Content-Type: application/json" \ - -d '{"firstName":"Lionel","lastName":"Messi",...}' - -# Swagger UI (recommended) -http://localhost:9000/swagger/index.html -``` - -## Troubleshooting - -### Port 9000 in use - -```bash -lsof -ti:9000 | xargs kill -9 -``` - -### Maven dependency issues - -```bash -./mvnw clean install -U # Force update -rm -rf ~/.m2/repository # Nuclear option -``` - -### Database locked - -```bash -pkill -f "spring-boot:run" # Stop all instances -rm storage/players-sqlite3.db # Reset (WARNING: loses data) -``` - -### Test failures - -```bash -./mvnw test -X -Dtest=PlayersControllerTests#specific_test -``` - ---- - -For quick reference and code conventions, see `.github/copilot-instructions.md` (auto-loaded).