From 40d33a75c7095236c3b1158c506880c45b53eaf9 Mon Sep 17 00:00:00 2001 From: Kevin Herron Date: Tue, 4 Nov 2025 14:49:35 -0800 Subject: [PATCH 1/3] Add AI coding assistant configuration - Created AGENTS.md with Java coding conventions and project guidelines - Added CLAUDE.md that references AGENTS.md - Added GitHub Copilot setup workflow for automated environment configuration - Added Maven profile to download dependency sources to external/src - Updated .gitignore to exclude external/ directory --- .github/workflows/copilot-setup-steps.yml | 32 +++ .gitignore | 3 + AGENTS.md | 281 ++++++++++++++++++++++ CLAUDE.md | 1 + pom.xml | 35 ++- 5 files changed, 350 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/copilot-setup-steps.yml create mode 100644 AGENTS.md create mode 100644 CLAUDE.md diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml new file mode 100644 index 0000000000..4ef4aefc3d --- /dev/null +++ b/.github/workflows/copilot-setup-steps.yml @@ -0,0 +1,32 @@ +name: "Copilot Setup Steps" + +on: + workflow_dispatch: + push: + paths: + - .github/workflows/copilot-setup-steps.yml + pull_request: + paths: + - .github/workflows/copilot-setup-steps.yml + +jobs: + # The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot. + copilot-setup-steps: + runs-on: ubuntu-latest + + permissions: + contents: read + + steps: + - name: Checkout code + uses: actions/checkout@v5 + + - name: Set up JDK 17 + uses: actions/setup-java@v5 + with: + java-version: "17" + distribution: "temurin" + cache: "maven" + + - name: Download external sources + run: mvn generate-resources -Pdownload-external-src diff --git a/.gitignore b/.gitignore index 08724be344..6f0093c623 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,6 @@ test-output/ # Local Claude Code memory CLAUDE.local.md + +# External src and other files +external/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..e706f09421 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,281 @@ +# Coding Instructions + +## Java Conventions + +Follow these Java conventions when working with this codebase. + +### Core Tenets + +1. **Clarity Over Brevity** — Code should be immediately understandable; use explicit types when + `var` doesn't make intent obvious +2. **Immutable by Default** — Prefer immutable data structures, records, and final fields unless + mutability is required +3. **Modern Java First** — Leverage modern Java features (pattern matching, sealed classes, text + blocks) to write expressive, type-safe code +4. **Purposeful Abstractions** — Use interfaces and composition to create flexible designs, avoiding + inheritance hierarchies +5. **Fail Fast, Fail Clear** — Validate early with specific exceptions; use Optional for absent + values, never for parameters + +### Variables and Types + +#### Type Declarations + +Use `var` for local variable declarations ONLY when the type is immediately obvious from the +right-hand side. + +**Decision Checklist:** + +- ✓ Can you tell the exact type in 1 second? → Use `var` +- ✗ Would you need to check documentation or method signatures? → Use explicit type +- ✗ Is the type generic, an interface, or complex? → Use explicit type +- ✗ Is the variable used far from its declaration? → Use explicit type + +**What counts as "obvious from the right-hand side":** + +- Constructor calls with concrete types: `new ArrayList()`, `new User(...)` +- Literals: strings, numbers, booleans, `null` +- Collection factory methods with only literals: `List.of(1, 2, 3)`, `Map.of("key", "value")` +- Standard library methods with obvious return types: `isEmpty()`, `size()`, `toString()` +- Builder patterns that return the same type: `User.builder().name("John").build()` + +```java +// Good: Type is clear from the right-hand side +var list = new ArrayList(); +var name = "John"; +var count = 42; +var user = new User(id, name, email); +var isEmpty = list.isEmpty(); +var items = List.of("a", "b", "c"); + +// Good: Explicit type when not obvious +InputStream stream = getStream(); +Result result = repository.getUser(id); +Function parser = Integer::parseInt; +List items = Stream.of(item1, item2).collect(toList()); + +// Good: Explicit type for interface/abstract return types +Map config = loadConfiguration(); +Callable task = () -> fetchData(); + +// Good: Explicit type for method chains +ProcessedData result = data.transform().normalize(); + +// Good: Explicit type for factory methods +User user = User.create(name); +Order order = orderService.findById(id); + +// Avoid: Unclear type from the right-hand side +var data = process(); // What type is returned? +var result = calculate(); // Not immediately obvious +var callback = createHandler(); // What functional interface? +``` + +**When in doubt, prefer explicit types.** + +### Records + +- Prefer record classes for immutable data carriers + ```java + record Point(int x, int y) {} + ``` + +- Use records instead of classes with only final fields and accessors + +- Add custom methods to records when needed for behavior + +### Sealed Classes + +- Use sealed classes to restrict inheritance hierarchies + ```java + sealed interface Shape permits Circle, Rectangle, Triangle {} + ``` + +- Combine with records for algebraic data types + +### Pattern Matching + +- Use pattern matching for `instanceof` checks (JDK 16+) + ```java + if (obj instanceof String s) { + return s.length(); + } + ``` + +- Use switch expressions with pattern matching (JDK 17+) + ```java + return switch (shape) { + case Circle c -> c.radius() * c.radius() * Math.PI; + case Rectangle r -> r.width() * r.height(); + }; + ``` + +### Text Blocks + +- Use text blocks for multi-line strings (JDK 15+) + ```java + String json = """ + { + "name": "value" + } + """; + ``` + +### Null Handling + +- Use `Optional` for return types that may be absent, not for parameters + +- Avoid `Optional` fields in classes + +- Use `Objects.requireNonNull()` for parameter validation + +### Collections + +- Prefer `List.of()`, `Set.of()`, `Map.of()` for immutable collections + +- Use `Stream` API for collection transformations when it improves readability + +- Avoid streams for simple iterations + +### Streams + +- Keep stream pipelines short and readable + +- Extract complex lambdas to named methods + +- Consider performance implications for large datasets + +### Modern APIs + +- Use `java.time` API for date/time operations, never `java.util.Date` + +- Prefer `Files` and `Path` over `File` for file operations + +- Use `HttpClient` (JDK 11+) for HTTP operations + +### Exception Handling + +- Prefer specific exception types over generic ones + +- Use try-with-resources for `AutoCloseable` resources + +- Don't catch `Exception` or `Throwable` unless absolutely necessary + +### Naming + +- Classes and interfaces: `PascalCase` +- Methods and variables: `camelCase` +- Constants: `UPPER_SNAKE_CASE` +- Packages: lowercase, no underscores +- Test classes: `ClassNameTest` for unit tests, `ClassNameIT` for integration tests + +### Code Organization + +- Keep methods short and focused on a single responsibility + +- Prefer composition to inheritance + +- Use interfaces for abstraction, not abstract classes + +### Code Quality + +- Single Responsibility Principle — methods focused on one task + +- The appropriate use of immutability and final keywords + +- Composition over inheritance + +- Proper documentation with Javadoc for public APIs + +### Immutability + +- Make classes immutable by default + +- Use `final` for fields that shouldn't change + +- Prefer unmodifiable collections + +### Documentation + +- Document public APIs with Javadoc + +- Focus on why, not what (code should be self-documenting for "what") + +- Keep documentation up to date with code changes + +### Javadoc + +- Javadoc tag descriptions MUST begin with a lowercase letter and MUST end with a period + ```java + /** + * Creates a new connection to the server. + * + * @param endpoint the server endpoint URL. + * @param timeout the connection timeout in milliseconds. + * @return the established connection. + * @throws IOException if the connection fails. + */ + ``` + +### Other + +For any coding practices not explicitly covered by these conventions, defer to established Java best +practices and community standards. + +## Code Formatting + +This project uses Spotless with Google Java Format for code formatting. + +The `spotless:check` goal is bound to the `verify` phase and will fail the build if code is not +properly formatted. + +If the build fails due to formatting issues, run: + +```bash +mvn spotless:apply +``` + +This will automatically format all Java files according to Google Java Format standards. + +## Finding Source Code + +To examine dependency source code, check the `external/src` directory at the project root. This +directory contains unpacked source files from all dependencies, organized by package structure for +easy browsing and searching. + +**If the directory doesn't exist or content is missing:** + +Run this command from the project root to download and unpack all dependency sources: + +```bash +mvn generate-resources -Pdownload-external-src +``` + +This will create the `external/src` directory with sources from all dependencies in a single +top-level location. + +## Git Workflow + +### Commit Messages + +Keep the title of the commit message to ~72 characters. + +Use the body to summarize the changes made in the commit. If the commit contains a number of +unrelated changes, try to generate a brief one-line subject, then summarize using bullet points. Be +concise. + +Avoid subjective justification or explanation for changes; state changes on their own merit. + +Do not include counts (files, tests, lines, changes, etc.). + +Do not include a "generated with Claude Code" line. + +### Pull Requests + +Do not include a "Test Plan" section in PRs. + +Do not mention the build was successful or tests passed. + +Do not include counts (files, tests, lines, changes, etc.). + +Do not include a "generated with Claude Code" line. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..43c994c2d3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/pom.xml b/pom.xml index 92878140f3..23d101da92 100644 --- a/pom.xml +++ b/pom.xml @@ -10,8 +10,8 @@ --> + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.eclipse.milo @@ -78,6 +78,7 @@ 3.6.0 3.5.0 3.14.0 + 3.9.0 3.1.4 3.6.1 3.5.3 @@ -261,6 +262,36 @@ + + + download-external-src + + false + + + + + org.apache.maven.plugins + maven-dependency-plugin + ${maven-dependency-plugin.version} + + + unpack-sources + generate-resources + + unpack-dependencies + + + sources + false + ${maven.multiModuleProjectDirectory}/external/src + + + + + + + From 7214ee37218e74627b38664147f34be928c5ac60 Mon Sep 17 00:00:00 2001 From: Kevin Herron Date: Tue, 4 Nov 2025 14:54:07 -0800 Subject: [PATCH 2/3] ~ don't try to download other `org.eclipse.milo` dependencies? --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 23d101da92..f39bc07a04 100644 --- a/pom.xml +++ b/pom.xml @@ -285,6 +285,7 @@ sources false ${maven.multiModuleProjectDirectory}/external/src + org.eclipse.milo From b276e1114378a67bad34635e95b67614dae74955 Mon Sep 17 00:00:00 2001 From: Kevin Herron Date: Tue, 4 Nov 2025 14:57:56 -0800 Subject: [PATCH 3/3] ~ try `mvn install` first? --- .github/workflows/copilot-setup-steps.yml | 3 +++ pom.xml | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index 4ef4aefc3d..d5f3f14938 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -28,5 +28,8 @@ jobs: distribution: "temurin" cache: "maven" + - name: Install + run: mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V + - name: Download external sources run: mvn generate-resources -Pdownload-external-src diff --git a/pom.xml b/pom.xml index f39bc07a04..23d101da92 100644 --- a/pom.xml +++ b/pom.xml @@ -285,7 +285,6 @@ sources false ${maven.multiModuleProjectDirectory}/external/src - org.eclipse.milo