VibeVM
Contents
On this page
en
Publisher
org.vibevm.ai-native
Version
1.0.0latest
Audiences
Reading time
8 min
Rendered
Read aloud
never

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

01Status. 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.

02Framing 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.

03Scope 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

  • 04Version 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

05A 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.

  • 06Import-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.

07Cell manifest (annotation carrier, §5):

08@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

  • 09A 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

10R-001 binding — flag at the seam, never in the veins:

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   -> new SatDepSolver(p, clock);
        case NAIVE -> new NaiveDepSolver(p);
    };
}
  • 12Two 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

13Java'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:

  • 14Expected failures are sealed result types at seams, consumed by compiler-checked exhaustive switch:
15public 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 }
}

16spec 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.

  • 17Unchecked 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

18Annotations — Java is the annotation culture, and the carrier exploits exactly the safe half of it:

19@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

20Design: 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)

  • 21Canonical 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)

22A 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)

23
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

24Javadoc 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.

25First 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.

For an agent

This page has a machine mirror. The citation carries the version rather than latest, so what an agent quotes does not move under it.

spec://org.vibevm.ai-native/core-ai-native@1.0.0/legacy-projections/GUIDE-JAVA-v0.1

.md.xmlllms.txt