Java project standard
Skill VoldemortGin/AI-Coding-Skill-Bible/java-project-standard
Enforce a strict, model-agnostic, AI-navigable Java project standard: the Java compiler plus `-Werror` + `-Xlint:all` as the baseline, **Error Prone** (bug-pattern checks as errors, the clippy/detekt analog) and **NullAway** (JSpecify `@Nullable`, null-safety at compile time — the Kotlin null-safety / Swift optional analog) as the static guarantee, **Spotless** with google-java-format as the format gate, sealed error hierarchies + exhaustive `switch` (no silent failures), **TDD with a JaCoCo coverage floor in the gate**, Jackson at boundaries with lightweight record compact-constructor validation, a Gradle multi-module module-per-domain structure, a zero-SDK zero-framework `:domain` module behind provider interface seams with a default `MockProvider`, a manual composition root (no DI framework), a version catalog pinning every version, a CLAUDE.md in every module, and scaffold/conformance scripts. Use whenever starting or scaffolding a Java project; setting up Gradle / settings.gradle.kts / libs.versions.toml / Error Prone / NullAway / Spotless / JaCoCo / CI; deciding module structure; adding an LLM / embedding / vector-store dependency; wiring providers or adapters; or checking that an existing Java project conforms. Apply it even when the user only says "start a Java project", "set up the structure", "add an LLM", or "wire up CI" without naming the standard. This skill governs the Java host's internals; it is complementary to polyglot-core-standard, which governs the cross-language seam (a Rust core + a JNI/JNA/Panama-FFM bridge — Java has no official UniFFI support).From its SKILL.md
npx -y skills add VoldemortGin/AI-Coding-Skill-Bible --skill java-project-standardAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
- runs commandsInstructs the agent to run 5 commands, including `python scripts/scaffold.py com.example.notes --target <dir> --domains retrieval generation` and 4 more.
SKILL.md
16.1 KB, ~3.6k tokens by cl100k_base, as published. Nobody here has run it
Java Project Standard
This skill is the guiding standard for any plain-JVM Java work. Apply it by default; don't wait to be asked.
Its spine: trust is placed not in the model, but in the machine-checkable code that constrains it. In Java a large part of that scaffold is the compiler — a static, non-erased-at-the-API type system, sealed types with exhaustive switch, and records. But javac alone is lax (unchecked casts warn but pass; null is unchecked; many bug patterns slip through), so the standard turns the knobs to the maximum and adds two tools the compiler lacks: Error Prone (bug-pattern checks, promoted to errors — the clippy/detekt analog) and NullAway (compile-time null-safety over JSpecify @Nullable — the Kotlin null-safety / Swift optional analog). The job here is to (a) keep the escape hatches shut and turn on -Werror + -Xlint:all + Error Prone + NullAway, (b) push every external dependency behind an interface so the model is hot-swappable, (c) organize deep with module-per-domain and name-navigable structure, and (d) mechanize the implicit knowledge a human would otherwise hold — via a zero-warning gate with a coverage floor, a per-module contract, and the format + lint gates. The agent's output ceiling equals the tightness of that loop.
Baseline: Java 21+ (LTS; targeted via options.release = 21, so the build is independent of the host JDK version — any JDK 21..25+ on JAVA_HOME), Gradle 9.x (Kotlin DSL) multi-module + a version catalog (gradle/libs.versions.toml) pinning everything, -Werror + -Xlint:all,-processing,-serial, Error Prone + NullAway (JSpecify), Spotless (google-java-format), Jackson at boundaries, SLF4J for logging, JUnit 5 (Jupiter) tests with a JaCoCo coverage floor.
This standard is the project-level engineering charter (the gate + architecture conventions). It is complementary to polyglot-core-standard: that meta-standard governs the cross-language seam (one canonical Rust core + a typed/fallible FFI boundary + a composed gate). This skill governs the Java host's internals — format, lint, strict compilation, module structure, the provider seam, and tests. In a polyglot repo the two compose, and this skill's ./ci.sh plugs into polyglot's make check-java slot.
When starting a new project
python scripts/scaffold.py com.example.notes --target <dir> --domains retrieval generation
This mirrors assets/templates/ into a Gradle multi-module project (settings.gradle.kts + :kernel/:domain/:adapters library modules + a :app application + a java-library module per --domains + a CLAUDE.md per module + ci.sh + ADR), substitutes __PKG__ in file contents and rewrites the __PKG_PATH__ source-package directory, and registers each domain module in settings.gradle.kts + :app dependencies. Module names (:kernel, :domain, …) are project-independent; only the base package, rootProject.name, and CLAUDE.md headers carry the literal name. When adapting an existing repo, copy from assets/templates/ by hand.
Then: gradle wrapper --gradle-version 9.4.1 (once, if not auto-generated), and ./ci.sh (or make check) to verify. Error Prone + NullAway + Spotless are wired via Gradle plugins; no extra install.
When working on existing code
Apply the rules below, keep new code strict, and verify structural invariants:
python scripts/check_conformance.py <project_root>
It parses settings.gradle.kts / build.gradle.kts (text/regex — Gradle has no clean manifest-dump) and checks the mechanically enforceable invariants (multi-module present, :domain zero-SDK + zero-framework + no :adapters dependency, -Werror wired, Error Prone + NullAway wired with NullAway at error(), Spotless configured, JaCoCo jacocoTestCoverageVerification wired on every non-app module, ci.sh chaining spotlessCheck → compileJava → test → coverage, a CLAUDE.md in every module, a src/test in every non-app module). Everything else is enforced by the gate and by applying the standard.
The non-negotiables
Full rationale in references/standard.md.
-
The compiler + strict knobs are the static guarantee; keep the escape hatches shut. Every module compiles with
options.release = 21,-Werror, and-Xlint:all,-processing,-serial, so a warning is fatal (unchecked casts, raw types, deprecation, fall-through all trap here). On top, Error Prone runs its bug-pattern checks (default-error checks stay errors) and NullAway makes null-safety a compile error: annotate the base package tree so it is@NonNullby default (JSpecify), and@Nullableis the explicit, checked opt-out —nullstops being a runtime bomb. A site that genuinely needs an exit takes the controlled exit:@SuppressWarnings("NullAway") // reason(explicit, auditable, greppable — the Java analog of a justified# type: ignore[code] # reason/#[allow(clippy::…)] // reason). -
No silent failures: sealed error hierarchies + exhaustive
switch, not swallowed exceptions. Recoverable failures are a sealedProviderException(make the sealed baseabstractsoswitchover it is compiler-checked exhaustive with nodefault); vendor/network/timeout errors normalize at the adapter boundary into it; program bugs throw (IllegalArgumentException/IllegalStateException) and propagate — never an emptycatch. The Java mirror of Rust'sResult<T,E>+thiserrorand Swift's typedthrows. -
TDD, red-light first; tests are the immutable spec, and coverage is in the gate. Write the failing JUnit 5 test, then implement to green; never weaken a test to pass. This is spine-level here, not advisory: the gate ends with JaCoCo
jacocoTestCoverageVerificationenforcing ≥ 80% line coverage on every non-app module (:kernel/:domain/:adapters/feature).:appis exempt — itsMain/wiring is a composition root exercised by running the app, so keep real logic out of:app. A scaffolded project ships tests that already clear the floor. -
Boundaries: parse at the edge — Jackson into records; keep it lightweight. Values crossing a process boundary (config, LLM output, tool results, files) decode into records via Jackson (compile with
-parametersso it binds by component name). Where a constraint genuinely earns it, encode it in the record's compact constructor (e.g.TopKrejecting out-of-range) so invalid states fail at construction. Do not wrap every value in a newtype — Java's static type system already carries most of that load; reserve compact-constructor validation for real boundaries, not internal plumbing. (This is the deliberately lighter cousin of Rust'sserde+ newtype and pydantic.) -
Model-agnostic: every external AI dependency behind an interface in
:domain; SDKs only in:adapters. The:domainmodule has zero SDK and zero framework dependencies (the checker enforces this); it is a plainjava-library. Real SDKs live in a separate gated module (:adapters-openai, conditionallyincluded by a Gradle property — the analog of Cargo features / SPM traits) and normalize toProviderException. The composition root (:app'sWiring, a manual, framework-free function) is the one place that selects an impl by config. A deterministic MockProvider is the default (not a test stub) so the app, tests, and CI run offline with no SDK or key. AProviderConformanceTestbinds mock and real backends to the same behavioral invariants. -
Completion = one zero-warning gate, the agent's only correctness judge.
./ci.shrunsspotlessCheck(google-java-format) →compileJava(with-Werror+ Error Prone + NullAway) →test(offline, Mock default, JUnit 5 smoke + conformance) →jacocoTestCoverageVerification, underset -euo pipefail. Run it after every change; fix until green; never "looks fine, commit". Pin it with a pre-push hook; CI mirrors it; in a polyglot repo it plugs intomake check-java. Spotless owns formatting (the ktlint /cargo fmtanalog); Error Prone + NullAway own lint and the escape-hatch bans (the detekt / clippy analog) — complementary, both in the gate.
Structure: Gradle multi-module, module-per-domain
The depth comes from the modules, not from one module with deep packages. A Gradle module ≈ a Cargo crate / SPM target: module-level dependency isolation. "Fix the reranker" should resolve to :retrieval/... with no search.
- A module per bounded context, plus the fixed infra modules and a thin app:
:kernel— cross-cutting infra:AppConfig(record; defaults <configs/settings.json< envAPP_*with__nesting, decoded by Jackson),Log(SLF4J facade; payloads never logged;logProvenanceonly takes codes/counts/versions),Prompts(classpath resources + strict rendering — a missing variable throws). Plainjava-library, zero SDK.:domain— ports (interfaces) + models (records) + sealed boundary errors; zero SDK, zero framework.:adapters— interface impls;MockProvideris the default;ProviderFactoryis the assembly seam; real SDKs in a gated sibling module.- domain feature modules (
:retrieval/:generation/…) depend on:domain+:kernel, never on:adaptersor SDKs. :app— theapplicationplugin; a manual composition root (Wiring+Main) that selects concrete adapters by config. No DI framework — introduce one here and nowhere else if wiring outgrows a hand-written function.
- Dependency direction (one-way): feature /
:adapters→:domain+:kernel;:app→ everything.:domainis zero-SDK and never depends on:adapters. - A
CLAUDE.mdin every module: the root one is a routing table (hard constraints + where to look + the polyglot boundary); each module's states its responsibility, dependency direction, and local contract (:domain's "zero SDK",:adapters' "errors normalize",:kernel's "payloads never logged"). The checker requires one per module. - Go deep: split a module into sub-packages by sub-capability; names map to paths.
Navigability
Naming-as-path is to navigation what types are to interface contracts — and Gradle lifts it to the module level. Group by capability, not by models//util/. Nest until leaf files have a single clear responsibility. A name should resolve to a path with no search.
Principles for AI-touching code (advisory)
Beyond the type system — for any code where a model produces output. Upper-level discipline; not all mechanically checkable.
- Constrain, don't ask. Push non-negotiable properties (no fabrication, must-cite, no privilege escalation) into deterministic control flow so the model physically cannot violate them — don't rely on the prompt. Synthesize answers from typed values in code; discard model prose on the critical path.
- Narrow the emission surface. Make the model pick from a typed
enum/ sealed interface or call tools returning a tri-state result; take final values from tool results, not free-form model text. A Javasealed interface+ exhaustiveswitchis the natural controlled surface. - Guardrails are deterministic, independent, never pluggable. Intent parsing can be swapped; safety decisions are deterministic code re-evaluated from raw input, not trusting a pluggable component's output. (In a polyglot repo these live in the core, not the host.)
Driving AI on big work (advisory)
Treat AI as supervisable labor, not unsupervised autopilot.
- Decision-first: a numbered, immutable ADR (
docs/adr/) before coding — context + chosen option + rejected alternatives and why. Rejected-reasons stop it re-walking excluded paths. - TDD is spine-level here (non-negotiable #3). Beyond that: attach the failing test as a subagent's acceptance criteria.
- Numbered steps, each independently green; one commit per step through the full gate. No giant diffs. (In a polyglot repo, a core-contract change + regenerated bindings + the host adaptation is one coherent commit.)
- Adversarial independent review. After writing, run a separate, hostile, multi-perspective review prioritizing what tests can't cover (diagrams, docs, tradeoffs).
Scale to project size
The module split, per-module CLAUDE.md, ADRs, and provider seam are real overhead — overkill for a 200-line tool. Present them as triggered, scalable patterns ("the moment you call an LLM/embedding/vector store, put it behind an interface in :domain"; "split a module out the moment it takes a second responsibility"), not blanket mandates. A single-module app + Spotless + Error Prone + NullAway + the zero-warning gate with a coverage floor is a perfectly good small-scale instantiation.
Scale along two axes, not one — size and domain. The standard has a universal spine that holds for any Java project whether or not it touches AI: -Werror + -Xlint:all + Error Prone + NullAway, the zero-warning gate, TDD + a coverage floor, sealed errors with the escape hatches shut, Jackson + lightweight record validation at boundaries, Gradle module-per-domain + deep naming-as-path, a version catalog, Spotless. The rest is an AI-triggered layer that only switches on once the project actually calls an LLM / embedding / vector store: the :domain interface seam + zero-SDK module, MockProvider-as-default, bundled prompts + strict rendering, logProvenance, and the constrain-don't-ask discipline. A pure app that never calls a model should take the spine in full and skip the AI layer outright — bolting MockProvider onto such a project is cargo-culting, not conformance. (In a polyglot repo the AI seam lives in the one sub-tree that calls the model — usually the core — and the Java host stays spine-only, receiving typed values across the FFI boundary.)
Resources
references/standard.md— the full standard with rationale and complete code for every module (read for the why, edge cases, or exact module contents), plus the mapping to the Python/Rust/Swift/Kotlin sibling standards and the polyglot seam boundary.assets/templates/— exact Gradle boilerplate;__PKG__(content) +__PKG_PATH__(source-package dir) are the only placeholders. Includes:kernel/:domain/:adapters/:app, the version catalog,.editorconfig,ci.sh,Makefile, a per-module CLAUDE.md,configs/settings.json, and smoke/conformance tests.scripts/scaffold.py— generate a conforming multi-module project.scripts/check_conformance.py— verify structural invariants (incl.:domain-zero-SDK, per-module CLAUDE.md, and the coverage-floor wiring).
What ships with it: 45 files
103.2 KB alongside SKILL.md, 3 of them executable
assets/
- templates/adapters/build.gradle.kts1.8 KB
- templates/adapters/CLAUDE.md1.2 KB
- templates/adapters/src/main/java/__PKG_PATH__/adapters/MockEmbedder.java594 B
- templates/adapters/src/main/java/__PKG_PATH__/adapters/MockLlm.java407 B
- templates/adapters/src/main/java/__PKG_PATH__/adapters/ProviderFactory.java1.4 KB
- templates/adapters/src/test/java/__PKG_PATH__/adapters/ProviderConformanceTest.java2.2 KB
- templates/adapters/src/test/java/__PKG_PATH__/adapters/SmokeTest.java1.8 KB
- templates/app/build.gradle.kts2.0 KB
- templates/app/CLAUDE.md1.2 KB
- templates/app/src/main/java/__PKG_PATH__/app/Main.java509 B
- templates/app/src/main/java/__PKG_PATH__/app/Wiring.java964 B
- templates/app/src/test/java/__PKG_PATH__/app/WiringTest.java505 B
- templates/build.gradle.kts556 B
- templates/ci.shruns898 B
- templates/CLAUDE.md3.2 KB
- templates/configs/settings.json98 B
- templates/docs/adr/0001-record-architecture-decisions.md546 B
- templates/domain/build.gradle.kts1.8 KB
- templates/domain/CLAUDE.md910 B
- templates/domain/src/main/java/__PKG_PATH__/domain/Document.java140 B
- templates/domain/src/main/java/__PKG_PATH__/domain/Embedder.java381 B
- templates/domain/src/main/java/__PKG_PATH__/domain/Llm.java390 B
- templates/domain/src/main/java/__PKG_PATH__/domain/ProviderException.java1.4 KB
- templates/domain/src/main/java/__PKG_PATH__/domain/TopK.java408 B
- templates/domain/src/test/java/__PKG_PATH__/domain/DomainTest.java938 B
- templates/.editorconfig657 B
- templates/.env.example278 B
- templates/.gitignore365 B
- templates/gradle/libs.versions.toml2.2 KB
- templates/gradle.properties279 B
- templates/kernel/build.gradle.kts2.9 KB
- templates/kernel/CLAUDE.md1.0 KB
- templates/kernel/src/main/java/__PKG_PATH__/kernel/AppConfig.java2.2 KB
- templates/kernel/src/main/java/__PKG_PATH__/kernel/Log.java914 B
- templates/kernel/src/main/java/__PKG_PATH__/kernel/PromptException.java790 B
- templates/kernel/src/main/java/__PKG_PATH__/kernel/Prompts.java1.9 KB
- templates/kernel/src/main/java/__PKG_PATH__/kernel/RetrieverConfig.java509 B
- templates/kernel/src/main/resources/prompts/rag/answer.md230 B
- templates/kernel/src/test/java/__PKG_PATH__/kernel/KernelTest.java1.6 KB
- templates/Makefile1003 B
5 more files not listed here. See all 45 in the repository.