<?xml version="1.0" encoding="UTF-8"?>
<spec xmlns="https://vibevm.org/spec/1">
  <title>GUIDE — Java under the Discipline, v0.1 (trunk)</title>
  <p p="1">**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.</p>
  <p p="2">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.</p>
  <p p="3">**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).</p>
  <section id="baseline" title="0. Language baseline">
    <list ordered="false" p="4">
      <item>**Version floor: Java 21 LTS; target 25 LTS.** `--release` pinned; `-Werror`; preview features off by default (a carrier may opt in per feature, documented).</item>
      <item>**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.</item>
      <item>**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.</item>
      <item>**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).</item>
      <item>**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.</item>
      <item>**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).</item>
      <item>**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.</item>
    </list>
  </section>
  <section id="cells" title="1. Cells">
    <p p="5">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.</p>
    <list ordered="false" p="6">
      <item>**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.</item>
      <item>**`ServiceLoader` is native registration magic** (`META-INF/services` — Java's inventory/linkme): banned for cells, legal only at boundary plugin points.</item>
      <item>**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.</item>
      <item>Values crossing seams are **records** (immutable by construction); collections crossing seams are unmodifiable.</item>
    </list>
    <p p="7">Cell manifest (annotation carrier, §5):</p>
    <fence lang="java" p="8">@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) { ... }
}</fence>
  </section>
  <section id="seams" title="2. Seams">
    <list ordered="false" p="9">
      <item>A seam is an `interface` in the seams module. Conformance is **nominal** (`implements`) — Java needs no Go-style assertion; the declaration is the declaration.</item>
      <item>**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.</item>
      <item>Closed sets are **sealed** interfaces/hierarchies; open extension points are ordinary interfaces. The distinction is part of the seam's text.</item>
      <item>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).</item>
    </list>
  </section>
  <section id="flags" title="3. Registry and flags">
    <p p="10">R-001 binding — flag at the seam, never in the veins:</p>
    <fence lang="java" p="11">// 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   -&gt; new SatDepSolver(p, clock);
        case NAIVE -&gt; new NaiveDepSolver(p);
    };
}</fence>
    <list ordered="false" p="12">
      <item>**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.)</item>
      <item>No `ServiceLoader`, no reflection-based wiring, no classpath scanning in the trunk; the overlays legalize container machinery **as this composition root and nowhere else**.</item>
    </list>
  </section>
  <section id="errors" title="4. Errors as contract">
    <p p="13">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:</p>
    <list ordered="false" p="14">
      <item>**Expected failures are sealed result types at seams**, consumed by compiler-checked exhaustive `switch`:</item>
    </list>
    <fence lang="java" p="15">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 }
}</fence>
    <p p="16">`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.</p>
    <list ordered="false" p="17">
      <item>**Unchecked exceptions are for invariant violations** — the panic analog. Checked exceptions do not cross seams; boundary adapters translate library exceptions into seam error types.</item>
      <item>**`try-with-resources` is MUST** for anything `AutoCloseable`; resource ownership is part of the seam's Javadoc.</item>
      <item>**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).</item>
    </list>
  </section>
  <section id="specmark" title="5. specmark carrier">
    <p p="18">**Annotations** — Java is the annotation culture, and the carrier exploits exactly the safe half of it:</p>
    <fence p="19">@SpecImplements(value = "&lt;uri&gt;", r = &lt;N&gt;)        repeatable; one edge per annotation
@SpecDeviates(value = "&lt;uri&gt;", r = &lt;N&gt;, reason = "...")   reason mandatory
@SpecVerifies(value = "&lt;uri&gt;", r = &lt;N&gt;)          on test methods/classes
@SpecScope(value = "&lt;uri&gt;", r = &lt;N&gt;)             in package-info.java — package-level inheritance</fence>
    <p p="20">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.</p>
  </section>
  <section id="naming" title="6. Naming (R-020/R-021 bindings)">
    <list ordered="false" p="21">
      <item>Canonical cell class name is computed: `{Variant}{Seam}` → `SatDepSolver`; linted (ArchUnit) against the manifest.</item>
      <item>**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.</item>
    </list>
  </section>
  <section id="replacement" title="7. Replacement protocol (R-040 binding)">
    <p p="22">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).</p>
  </section>
  <section id="risks" title="8. Risk table (what conform must cover for Java)">
    <table p="23">
      <tr>
        <td>Footgun</td>
        <td>Rule</td>
        <td>Tier</td>
      </tr>
      <tr>
        <td>`static { }` side effects / mutable static state in a cell</td>
        <td>§1</td>
        <td>T-syn</td>
      </tr>
      <tr>
        <td>`System.getenv` / ambient `Clock`/`Random`/`HttpClient` in a cell</td>
        <td>§1</td>
        <td>T-syn</td>
      </tr>
      <tr>
        <td>`ServiceLoader` registration for a cell</td>
        <td>§1</td>
        <td>T-syn</td>
      </tr>
      <tr>
        <td>reflection (`setAccessible`, `Class.forName`) on domain types</td>
        <td>§6</td>
        <td>T-syn</td>
      </tr>
      <tr>
        <td>Lombok on the classpath</td>
        <td>§6</td>
        <td>T-lex (build)</td>
      </tr>
      <tr>
        <td>checked exception in a seam signature; expected failure thrown unchecked across a seam</td>
        <td>§4</td>
        <td>T-syn</td>
      </tr>
      <tr>
        <td>`default` arm on a sealed-type `switch`</td>
        <td>§4</td>
        <td>T-syn</td>
      </tr>
      <tr>
        <td>`Optional` as field/parameter; undeclared null crossing a seam</td>
        <td>§2</td>
        <td>T-sem (NullAway)</td>
      </tr>
      <tr>
        <td>naked `new Thread`; executor outside try-with-resources; `ThreadLocal` in a cell</td>
        <td>§4</td>
        <td>T-syn</td>
      </tr>
      <tr>
        <td>reactive type in a seam signature</td>
        <td>§4</td>
        <td>T-syn</td>
      </tr>
      <tr>
        <td>Mockito in a cell test; `@Disabled` on a known-failing test</td>
        <td>§7</td>
        <td>T-syn</td>
      </tr>
      <tr>
        <td>sibling-cell dependency; cell depended on outside the registry</td>
        <td>R-002</td>
        <td>ArchUnit</td>
      </tr>
      <tr>
        <td>bare/over-broad `@SuppressWarnings`; stale suppression</td>
        <td>§0</td>
        <td>T-lex + sweep</td>
      </tr>
      <tr>
        <td>Jackson/persistence annotations on domain types</td>
        <td>§0</td>
        <td>T-syn</td>
      </tr>
      <tr>
        <td>flag read outside the registry</td>
        <td>R-001</td>
        <td>T-syn</td>
      </tr>
      <tr>
        <td>public export without own/inherited spec edge</td>
        <td>PROP-014 §3.2-6</td>
        <td>T-syn + index</td>
      </tr>
    </table>
  </section>
  <section id="docs" title="9. Doc layer">
    <p p="24">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.</p>
    <p p="25">**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.</p>
  </section>
</spec>
