# trueref — Code Style ## 1. Language & Toolchain - **Java 21**, source/target 21. - **Maven** with `spring-boot-maven-plugin` for the fat JAR. - **Spotless** with **Palantir Java Format** for formatting (4-space indent, 120 col). - **ErrorProne** + **NullAway** for static analysis. NullAway annotations: `@org.jspecify.annotations.Nullable` / `@NonNull`. - Hexagonal boundaries are enforced by **Maven module dependencies** (no ArchUnit). See ARCHITECTURE §3. ## 2. Records, Sealed Types, Pattern Matching - Prefer **records** for DTOs, value objects, and `port.in`/`port.out` parameter/result types. - Use **sealed interfaces** for closed result/event hierarchies (`sealed interface IngestionEvent permits ...`). - Use **pattern matching** (`switch` expressions, `instanceof`) over visitor pattern. ## 3. Nullability & Optional - All API surfaces (public methods on ports, REST DTOs) are **non-null by default**; mark nullable explicitly with `@Nullable`. - Use `Optional` **only** as a return type from query-style methods. Never as a field, never as a parameter. ## 4. Concurrency - Spawn virtual threads via `Thread.ofVirtual().start(...)` or `Executors.newVirtualThreadPerTaskExecutor()`. **Never** call `Thread.sleep` inside a synchronized block. - Shared mutable state is forbidden in `domain` and discouraged in `application`. When unavoidable, use `java.util.concurrent.atomic.*` or a `ReentrantLock`. - GPU work goes through `GpuSemaphore.acquire()` (a thin wrapper around `Semaphore`). - Long-running orchestration uses **structured concurrency** (`StructuredTaskScope`) where it improves cancellation safety. ## 5. Error Handling - **Domain errors** are sealed exception hierarchies rooted at `TrueRefException`. Adapters translate them to HTTP/JSON-RPC errors centrally (REST: `@ControllerAdvice`; MCP: dedicated translator). - **No checked exceptions** at port boundaries. Wrap third-party checked exceptions at the adapter edge. - Validation errors carry a stable `code` (string) so the UI can localize. - Never `catch (Exception e)` and swallow. Either log + rethrow as a domain exception or let it propagate. ## 6. Logging - **SLF4J** with parameterized messages: `log.info("indexed tag {} of repo {}", tag, repoName);` — never string-concatenate. - Structured fields via MDC: `repoId`, `versionId`, `jobId`, `stage`. Cleared in a try/finally. - Log levels: - `ERROR`: unrecoverable, requires operator attention. - `WARN`: degraded, automatic recovery in progress. - `INFO`: lifecycle events (job started/finished, repo registered). - `DEBUG`: per-file, per-chunk detail. Off by default. ## 7. Naming - Use cases (`port.in`): imperative verb phrases — `IndexVersion`, `ResolveLibraryId`. - SPIs (`port.out`): noun-ish role names — `EmbeddingService`, `ChunkStore`, `GitClient`. - Adapter classes: `` — `LuceneChunkStore`, `OnnxEmbeddingService`, `JGitClient`. - DTOs: `Request` / `Response`. - Records' field names are camelCase (no Hungarian, no `_` prefixes). ## 8. Package & File Discipline - One public type per file. - Internal helpers are package-private. Avoid `public` unless used across packages. - Domain packages export **only** records and interfaces. No Spring annotations, no Lombok, no Jackson annotations. - Adapter packages may use Spring stereotypes (`@Component`, `@Repository`, `@RestController`) but adapters depend on **port interfaces only** when interacting with the application. ## 9. Spring Wiring - Wiring lives in `bootstrap`. Each adapter package may define a `@Configuration` (constructor-injected `@Bean` factories) but **does not** auto-`@ComponentScan` itself; bootstrap explicitly imports. - Use `@ConfigurationProperties` records for typed config; never raw `@Value`. - Prefer constructor injection. **No field injection.** ## 10. Persistence (H2) - Migrations under `src/main/resources/db/migration` named `V__.sql`. Flyway runs at startup. - All access via Spring **`JdbcClient`** (Spring Boot 3.2+, fluent JDBC). **No JPA/Hibernate**, no `JdbcTemplate` directly. - Mappers are explicit `RowMapper` lambdas, not reflection-based. - SQL lives next to the repository class, either as `static final String` constants or in `*.sql` files loaded via `ClassPathResource` for non-trivial queries. ## 11. REST - Controllers in `adapter.in.rest`. They depend only on `port.in` interfaces and DTO records. - DTOs are **separate** from domain records. Mapping via plain `static of(...)` factories. **No MapStruct.** - All endpoints documented with `@Operation`, `@ApiResponses`, `@Schema` (springdoc). - Request validation via `jakarta.validation` annotations on DTOs. - SSE endpoints return `SseEmitter`; subscribe to `JobEventBus`, unsubscribe on completion/timeout. ## 12. MCP - Tool definitions are **records** decorated to produce JSON Schema via Spring AI's MCP support. Schema strings stay verbatim (1:1 with Context7) so LLMs see identical contracts. - Tool handlers depend only on `port.in` (`SearchLibraryDocs`, `ResolveLibraryId`). ## 13. Tests - **JUnit 5** + **AssertJ** + **Mockito** (sparingly). - Unit tests live next to the package they test. Integration tests under `src/test/java/.../it/` and use `@SpringBootTest`. - Use **Testcontainers** only when truly required (we mostly avoid it via embedded stores). - ArchUnit test suite is mandatory and runs in CI. ## 14. Dependency Hygiene - BOM-managed versions only. Add a dependency only if it provides clear value over JDK + Spring Boot + already-included libs. - No Lombok, no Guava (use JDK 21 equivalents), no Reactor (we use virtual threads + blocking). - No Kotlin, no Scala. ## 15. Documentation - All architectural decisions go in **ARCHITECTURE.md**. - All research notes go in **FINDINGS.md** with sources. - All conventions go in **this file**. - Per-package `package-info.java` may exist for non-trivial packages, summarizing role and exported types. - No README sprawl: `README.md` is a quickstart only and links to the three docs above.