<?xml version="1.0" encoding="UTF-8"?>
<spec xmlns="https://vibevm.org/spec/1">
  <title>GUIDE — Java + Spring under the Discipline, v0.1 (overlay)</title>
  <p p="1">**Status.** Beta; overlay on `GUIDE-JAVA-v0.1.xml` (the trunk). Only the sections named here are rewired; everything else — carrier, naming, errors-as-sealed-results, suppression policy, risk rows — is inherited. Composition: combines with the GraalVM overlay (Spring Boot AOT); never with the Jakarta overlay (one container per target).</p>
  <p p="2">Framing note. Spring is the place where the Discipline's oldest ban — *no DI containers* — meets the ecosystem where DI containers won. The overlay does not pretend Spring away and does not surrender to it; it resolves by **ring placement**: Spring is legalized as *machinery of the composition root and the boundary*, and nowhere else. The trunk's second language (runtime assembly via reflection and proxies) gets a territory with a border, and the border is enforced by ArchUnit, not by hope. The slogan of this overlay: **framework-free core** — a cell compiled without Spring on the classpath is the test that it belongs to the first language.</p>
  <p p="3">**Scope honesty.** Spring Boot 3.x-era applications. Legacy XML/Spring 4-5 codebases enter through the brownfield protocol as adopted code, not through this overlay.</p>
  <section id="baseline" title="§0 additions — baseline">
    <list ordered="false" p="4">
      <item>**Boot BOM pinned**; starters are audited dependencies, not magic: every starter admitted to the build is listed with the auto-configurations it activates (one-time inventory, then diffed). `spring-boot-properties-migrator` class tooling at upgrades.</item>
      <item>**ArchUnit ring rule (the overlay's load-bearing check):** `..cells..` may not depend on `org.springframework..` — committed as a test, failing red the day someone "just adds `@Component`".</item>
    </list>
  </section>
  <section id="cells" title="§1&apos; — cells">
    <p p="5">Inherited, plus: **no Spring types in cells, period.** No stereotypes (`@Component`/`@Service`), no `@Autowired`, no `ApplicationContext` awareness, no Spring events, no `@Value`. A cell is constructed by `new`, in a `@Bean` method, with everything it needs — which is precisely why it tests in microseconds without a context (§7').</p>
  </section>
  <section id="seams" title="§2&apos; — seams">
    <p p="6">Inherited, plus: **no Spring types in seam signatures** — no `ResponseEntity`, no Spring Data `Page`/`Pageable`, no `MultipartFile`. Where pagination or streaming is part of the domain contract, the seams module defines its own small records (`Slice`, `PageRequest`); boundary adapters translate. A seam that leaks a framework type has annexed the cell into the second language.</p>
  </section>
  <section id="flags" title="§3&apos; — registry and flags">
    <p p="7">The trunk's hand-written registry **is** idiomatic Spring, written the way Spring's own documentation recommends for libraries:</p>
    <fence lang="java" p="8">@Configuration(proxyBeanMethods = false)
public class SolverConfig {

    @Bean
    @ConditionalOnProperty(name = "app.solver", havingValue = "sat")
    DepSolver satDepSolver(DepProvider p, Clock clock) {     // R-001: the flag, at the seam
        return new SatDepSolver(p, clock);
    }

    @Bean
    @ConditionalOnMissingBean(DepSolver.class)
    DepSolver naiveDepSolver(DepProvider p) {
        return new NaiveDepSolver(p);
    }
}</fence>
    <list ordered="false" p="9">
      <item>**Explicit `@Bean` methods are the registry**; component scanning is confined to boundary packages and never reaches cells — wiring stays readable as code, not discoverable as folklore. `proxyBeanMethods = false` keeps the configuration class itself proxy-free.</item>
      <item>**`@ConditionalOnProperty` is the literal R-001 binding** — the flag is read by the container, at the seam, once, with provenance supplied by Spring's property-source ordering (cli &gt; env &gt; file &gt; default), which maps one-to-one onto the Discipline's provenance chain.</item>
      <item>**Runtime tier:** `@ConfigurationProperties` records with constructor binding, validated at startup — the config object the trunk's §3 demanded, container-built. No `@Value` scatter; no `Environment` lookups outside the boundary.</item>
      <item>**Constructor injection only.** Field injection is banned (it is reflection-through-the-back-door and makes the framework-free test impossible); single-constructor classes need no annotation at all. A circular dependency is a design failure to fix, not a `@Lazy`/setter workaround to apply.</item>
    </list>
  </section>
  <section id="errors" title="§4&apos; — errors and proxies">
    <p p="10">Inherited (sealed results at seams), plus the overlay's two honesty paragraphs:</p>
    <list ordered="false" p="11">
      <item>**Exception translation is boundary work:** Spring's unchecked hierarchies (`DataAccessException` and kin) are translated into seam error types in adapters; framework exceptions never cross inward.</item>
      <item>**Proxy honesty:** `@Transactional`, `@Cacheable`, `@Async`, `@Retryable` are dynamic proxies — **self-invocation silently skips them**, final methods defeat them, and they constitute hidden control flow (R-021) if sprinkled into domain logic. They are legal only on boundary/use-case orchestration classes; transaction boundaries live where use-cases are composed, never inside cells — which is where the DDD mainstream puts them anyway. A cell that needs "a transaction" actually needs a unit-of-work capability on its seam.</item>
    </list>
  </section>
  <section id="tests" title="§7&apos; — replacement and tests">
    <p p="12">Inherited, plus the test pyramid binding: **cell tests are plain JUnit** — no context, no `@SpringBootTest`, no Mockito (trunk rule), constructor + injected fakes, microseconds. Boundary slices use `@WebMvcTest`/`@DataJpaTest`-class slices; **full `@SpringBootTest` contexts are few and live at the top** — each one is counted, because context startup is the test suite's wish-ratio. Testcontainers (MIT) for real-infrastructure boundary tests. A cell test that needs the container running is a misplaced boundary test — move it, don't speed it up.</p>
  </section>
  <section id="risks" title="§8&apos; — additional risk rows">
    <table p="13">
      <tr>
        <td>Footgun</td>
        <td>Rule</td>
        <td>Tier</td>
      </tr>
      <tr>
        <td>`org.springframework..` import inside a cell</td>
        <td>§1'</td>
        <td>ArchUnit</td>
      </tr>
      <tr>
        <td>stereotype annotation (`@Component`/`@Service`) on a domain class</td>
        <td>§3'</td>
        <td>T-syn</td>
      </tr>
      <tr>
        <td>field/setter injection anywhere</td>
        <td>§3'</td>
        <td>T-syn</td>
      </tr>
      <tr>
        <td>component scan reaching cell packages</td>
        <td>§3'</td>
        <td>T-syn (config audit)</td>
      </tr>
      <tr>
        <td>Spring type in a seam signature</td>
        <td>§2'</td>
        <td>ArchUnit</td>
      </tr>
      <tr>
        <td>`@Transactional`/`@Async`/`@Cacheable` inside a cell; self-invocation of a proxied method</td>
        <td>§4'</td>
        <td>T-syn + T-sem</td>
      </tr>
      <tr>
        <td>`@Value` / `Environment` lookup outside boundary</td>
        <td>§3'</td>
        <td>T-syn</td>
      </tr>
      <tr>
        <td>`@SpringBootTest` exercising a single cell</td>
        <td>§7'</td>
        <td>T-syn (test audit)</td>
      </tr>
      <tr>
        <td>unaudited starter / auto-configuration drift</td>
        <td>§0</td>
        <td>build diff</td>
      </tr>
    </table>
    <p p="14">**Overlay note.** Everything this overlay forbids has a sanctioned home one ring out; the discipline is placement, not abstinence. The framework-free-core test (cells compile without Spring) is the overlay's single most valuable artifact — cheap, binary, and brutally honest.</p>
  </section>
</spec>
