# GUIDE — Java under the Discipline, v0.1 (trunk)

[p01] **Status.** Beta; sixth language. Java ships as a **trunk plus three overlays**, in the owner's priority order: this trunk, then `GUIDE-JAVA-SPRING`, `GUIDE-JAVA-GRAALVM`, `GUIDE-JAVA-JAKARTA`. An overlay presupposes the trunk and rewires only the sections it names; everything unstated is inherited. **Composition matrix:** SPRING+GRAALVM legal (Boot AOT); JAKARTA+GRAALVM legal (build-time CDI implementations); SPRING+JAKARTA illegal (one container per target). Sections remain isomorphic to the other language guides — diff them.

[p02] Framing note — the sixth point of the typology. Rust *enforces*; TS *permits but compiles*; Python *trusts*; C++ *demands a subset*; Go *prescribes*; **Java bifurcates**: the strictest mainstream language runs on the most reflective mainstream runtime. Java is two languages — the one javac type-checks, and the one frameworks assemble at runtime out of reflection, dynamic proxies, classpath scanning, and bytecode weaving. The Discipline's whole job here is to keep cells inside the first language; the overlays govern how the second one is allowed to surround them. Patterns dissolve mid-way: Strategy = interface + registry; Visitor = sealed hierarchy + exhaustive `switch` (Java 21 finally has sum types); Decorator = wrapper; Observer = explicit listener seam; Singleton = forbidden — `private static final INSTANCE` is its Java disguise, and the framework-managed bean is its modern one.

[p03] **Scope honesty.** Long-lived backend/system code. Build scripts, one-off tools, and test scaffolding follow lighter rules. Code generated by annotation processors follows the generated-code rule: the processor's *input* is the taggable unit; outputs are excluded, and processors that generate *domain logic* are infra-only (the proc-macro parallel).

## 0. Language baseline {#baseline}

- [p04] **Version floor: Java 21 LTS; target 25 LTS.** `--release` pinned; `-Werror`; preview features off by default (a carrier may opt in per feature, documented).
- **Compile-time gates:** Error Prone (Apache-2.0) MUST; **nullness via JSpecify annotations (Apache-2.0) + NullAway (MIT)** — `@Nullable` is part of the seam contract, unannotated null crossing a seam is a build failure. Formatting via Spotless with a single pinned formatter — language-adjacent, zero discipline budget.
- **Architecture tests are conform checks:** ArchUnit (Apache-2.0) rules are committed as ordinary tests — sibling-import bans, ring rules ("cells import no framework"), naming grammar. This is the Java T-syn engine, running where developers already live.
- **Build:** Maven primary (Enforcer plugin: dependency convergence, banned-dependency list, version pinning; reproducible-build configuration on) — boring and declarative; Gradle MAY, with dependency locking + version catalogs. Either way the dependency graph is locked (A2).
- **Suppression policy (xfail-strict posture):** bare `@SuppressWarnings` is banned; suppressions carry the narrowest scope, the specific check name, and a `// reason`. Stale suppressions are swept by a conform check that re-runs the named check against the suppressed element — same mechanism as the C++ NOLINT sweep.
- **Tests:** JUnit 5 — **license flag: EPL-2.0**, test-only, acceptable under the Charter's case-by-case zone; AssertJ (Apache-2.0). Property testing: jqwik (**EPL-2.0**, same flag) or QuickTheories (Apache-2.0).
- **Boundary validation (parse, don't validate):** JSON/HTTP/DB inputs decode into boundary DTOs (records) with explicit validation, then convert into domain types. Jackson and friends live at the boundary; their annotations never appear on domain types.

## 1. Cells {#cells}

[p05] A cell is a **Maven/Gradle module** by default; in small repos a package guarded by ArchUnit rules is the starting granularity, with promotion to a module on the usual triggers (heavy optional deps, release cadence, ~2 kLoC). JPMS `module-info` SHOULD be used where the toolchain cooperates — it is the compiler-enforced form of the surface rule — with the ecosystem friction named honestly rather than wished away.

- [p06] **Import-is-execution, Java edition — lazier and therefore worse:** class initialization runs on *first touch*, in an order no one can read from the source. `static { }` blocks with side effects, mutable static fields, eager singletons, and registration-in-initializer are banned in cells. The one tolerated static: `private static final Logger` (fighting it is wish-ratio poison) — but log *sinks* are configured at the boundary.
- **`ServiceLoader` is native registration magic** (`META-INF/services` — Java's inventory/linkme): banned for cells, legal only at boundary plugin points.
- **Platform capabilities are injected**, and the JDK itself blesses the pattern: `java.time.Clock` (the stdlib's own injectable clock), `java.nio.file.FileSystem`, `java.net.http.HttpClient`, `RandomGenerator` — constructor parameters, never ambient. `System.getenv`/`System.exit`/`Runtime` are banned in cells.
- Values crossing seams are **records** (immutable by construction); collections crossing seams are unmodifiable.

[p07] Cell manifest (annotation carrier, §5):

[p08]
```java
@SpecImplements(value = "spec://org.vibevm.core/vibevm/modules/vibe-resolver/PROP-003#solver-upgrade", r = 2)
@Cell(seam = "DepSolver", variant = "sat", replaces = "naive", flag = "solver")
public final class SatDepSolver implements DepSolver {
    private final DepProvider provider;
    private final Clock clock;
    public SatDepSolver(DepProvider provider, Clock clock) { ... }
}
```

## 2. Seams {#seams}

- [p09] A seam is an `interface` in the seams module. Conformance is **nominal** (`implements`) — Java needs no Go-style assertion; the declaration is the declaration.
- **Composition over inheritance is a MUST at seams:** no abstract base classes with behavior, no template methods (R-021). `default` methods are legal only for trivial combinators/adapters, never business flow.
- Closed sets are **sealed** interfaces/hierarchies; open extension points are ordinary interfaces. The distinction is part of the seam's text.
- Seam methods that can fail in expected ways return the seam's sealed result type (§4); `@Nullable` appears explicitly wherever null is part of the contract; `Optional` is a return-type-only construct (never fields or parameters).

## 3. Registry and flags {#flags}

[p10] R-001 binding — flag at the seam, never in the veins:

[p11]
```java
// registry module — the only flag reader and the only module that
// depends on cell modules.
public static DepSolver depSolver(AppConfig cfg, DepProvider p, Clock clock) {
    return switch (cfg.solver()) {           // provenance: default | env | cli | lockfile
        case SAT   -> new SatDepSolver(p, clock);
        case NAIVE -> new NaiveDepSolver(p);
    };
}
```

- [p12] **Two tiers, never confused:** the assembly tier — which modules are on the classpath/module-path (Maven profiles, optional dependencies) — answers *"is the code in the artifact"*; the runtime tier is a config record, constructor-bound, read once in `main`. An assembly choice must not change a seam's surface; a runtime flag must not require reassembly. (The GraalVM overlay shows these tiers *migrating* — see its §3.)
- No `ServiceLoader`, no reflection-based wiring, no classpath scanning in the trunk; the overlays legalize container machinery **as this composition root and nowhere else**.

## 4. Errors as contract {#errors}

[p13] Java's historical irony, handled with respect: the language *invented* the signature-level failure surface — checked exceptions — and the culture buried it under wrappers and lambda-hostility until `throws` clauses became lies. The Discipline does not resurrect them; it takes the modern equivalent the language finally grew:

- [p14] **Expected failures are sealed result types at seams**, consumed by compiler-checked exhaustive `switch`:

[p15]
```java
public sealed interface SolveResult permits SolveResult.Ok, SolveResult.Err {
    record Ok(Resolution resolution) implements SolveResult {}
    record Err(SolveError error)     implements SolveResult {}
}

public record SolveError(Code code, String spec, Throwable cause) {
    public enum Code { CYCLE, UNSATISFIABLE }
}
```

[p16] `spec` carries the violated REQ URI; boundary rendering appends it (PROP-014 §2.6). **No `default` arm on a sealed `switch`** — the default clause silences the compiler's exhaustiveness check, the same graveyard move banned in the C++ Traditional guide; a new permitted subtype must break the build at every consumer.

- [p17] **Unchecked exceptions are for invariant violations** — the panic analog. Checked exceptions do not cross seams; boundary adapters translate library exceptions into seam error types.
- **`try-with-resources` is MUST** for anything `AutoCloseable`; resource ownership is part of the seam's Javadoc.
- **Concurrency in the virtual-thread era:** seams stay **synchronous and blocking** — virtual threads (final since 21) dissolved the function-coloring argument, so reactive types (`Mono`/`Flux`/RxJava) are boundary adapters, never seam signatures. Ownership discipline: executors are created in `try-with-resources` (auto-close awaits completion — structured-by-construction until `StructuredTaskScope` leaves preview); naked `new Thread` is banned; `ThreadLocal` is banned in cells (`ScopedValue` at the boundary where context propagation is truly needed).

## 5. specmark carrier {#specmark}

[p18] **Annotations** — Java is the annotation culture, and the carrier exploits exactly the safe half of it:

[p19]
```
@SpecImplements(value = "<uri>", r = <N>)        repeatable; one edge per annotation
@SpecDeviates(value = "<uri>", r = <N>, reason = "...")   reason mandatory
@SpecVerifies(value = "<uri>", r = <N>)          on test methods/classes
@SpecScope(value = "<uri>", r = <N>)             in package-info.java — package-level inheritance
```

[p20] Design: a tiny `specmark` artifact, `compileOnly`/`provided` scope, zero transitive dependencies, **`RetentionPolicy.CLASS`** — present in bytecode for T-syn/T-sem analysis, invisible to runtime reflection (aligned with the trunk's anti-reflection stance), and **`@Documented`** — the edges render in Javadoc, so the carrier doubles as the doc surface (the TS/Python simplification; the deliberate opposite of Go's hidden directives). The annotation's structure is compile-checked, which no comment carrier in the set can claim. No runtime processor exists or ever will. ≤3 edges per item or split.

## 6. Naming (R-020/R-021 bindings) {#naming}

- [p21] Canonical cell class name is computed: `{Variant}{Seam}` → `SatDepSolver`; linted (ArchUnit) against the manifest.
- **Forbidden in cells regardless of elegance** — the Java theater list: reflection in domain code (`setAccessible`, `Class.forName`, method handles on domain types); bytecode magic (ByteBuddy/cglib) outside infra; **Lombok anywhere** — banned not for license (MIT) but for surgery on javac internals that blinds A3 tooling, and because records/sealed types closed its legitimate niches; exceptions as control flow; serialization magic (`Serializable` on domain types); annotation processors generating domain logic (infra-only); God utility classes; builder ceremonies where a record with defaults suffices.

## 7. Replacement protocol (R-040 binding) {#replacement}

[p22] A cell with `replaces=` ships a differential oracle: jqwik/QuickTheories properties driving both cells through the seam, asserting agreement modulo a documented divergence list, `@SpecVerifies`-tagged. Golden files follow the promotion protocol (CI never updates). **In-source xfail twin — Java can have one:** the profile ships a ~20-line JUnit 5 extension, `@KnownFailing("debt-id")`, with strict semantics — the annotated test *failing* is green, the test *passing* fails the run and forces promotion. `@Disabled` on known-failing tests is banned (it hides both regressions and healings — Go's `t.Skip` problem, here solvable). **Mockito is banned in cell tests** — reflection-based mocking is the `vi.mock`/`mock.patch`/gomock of Java, and capability injection makes it unnecessary; boundary tests may use it (MIT, fine there).

## 8. Risk table (what conform must cover for Java) {#risks}

[p23]
| Footgun | Rule | Tier |
| --- | --- | --- |
| `static { }` side effects / mutable static state in a cell | §1 | T-syn |
| `System.getenv` / ambient `Clock`/`Random`/`HttpClient` in a cell | §1 | T-syn |
| `ServiceLoader` registration for a cell | §1 | T-syn |
| reflection (`setAccessible`, `Class.forName`) on domain types | §6 | T-syn |
| Lombok on the classpath | §6 | T-lex (build) |
| checked exception in a seam signature; expected failure thrown unchecked across a seam | §4 | T-syn |
| `default` arm on a sealed-type `switch` | §4 | T-syn |
| `Optional` as field/parameter; undeclared null crossing a seam | §2 | T-sem (NullAway) |
| naked `new Thread`; executor outside try-with-resources; `ThreadLocal` in a cell | §4 | T-syn |
| reactive type in a seam signature | §4 | T-syn |
| Mockito in a cell test; `@Disabled` on a known-failing test | §7 | T-syn |
| sibling-cell dependency; cell depended on outside the registry | R-002 | ArchUnit |
| bare/over-broad `@SuppressWarnings`; stale suppression | §0 | T-lex + sweep |
| Jackson/persistence annotations on domain types | §0 | T-syn |
| flag read outside the registry | R-001 | T-syn |
| public export without own/inherited spec edge | PROP-014 §3.2-6 | T-syn + index |

## 9. Doc layer {#docs}

[p24] Javadoc on every exported element: behavior, the seam's error codes with their REQ URIs, nullness beyond the annotations, thread-safety and blocking characteristics, resource ownership. The `@Documented` specmark edges render alongside — provenance is visible where readers already look. Spec stays thin; duplication between Javadoc and spec is a defect on the spec side.

[p25] **First carrier note.** No Java exists in vibevm; no carrier is designated. Trunk and overlays ship genre-complete and unexercised under the carrier-relative house clause: rules remain DRAFT until a first Java carrier exists, and at its first milestone any rule without a conform check (or explicit `WISH` mark) is removed rather than carried as aspiration.

