Command: static reachability/wiring/export/package evidence for hibernate, springdata, querydsl
Working directory: /shared/codebase/clean-architecture-backend-template
Executed at: 2026-08-29T08:22:05Z
Source revision: a24ece9cf797f7ea647e33bf846b115208ed1ba5
Observation boundary: Tracked production Java/static configuration only. Zero references do not exclude reflection/external adopters; package export rules and test/integration consumers are shown separately.
--- stdout/stderr ---
=== package denominator ===
hibernate main count= 22 loc= 1111
hibernate test count= 2 loc= 149
springdata main count= 17 loc= 1028
springdata test count= 8 loc= 598
querydsl main count= 3 loc= 139
querydsl test count= 1 loc= 53
=== key implementation production refs ===
--- NamedStatementInspector
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/hibernate/NamedStatementInspector.java:20:public final class NamedStatementInspector implements StatementInspector {
--- HibernateBatchConfigurationGuard
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/hibernate/batch/HibernateBatchConfigurationGuard.java:23:public final class HibernateBatchConfigurationGuard {
--- HibernateJpaBatchExecutor
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/hibernate/batch/HibernateJpaBatchExecutor.java:22:public final class HibernateJpaBatchExecutor implements JpaBatchExecutor {
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/hibernate/batch/HibernateJpaBatchExecutor.java:27:  public HibernateJpaBatchExecutor(
--- JpaBatchProfileRegistry
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/hibernate/batch/JpaBatchProfileRegistry.java:14:public final class JpaBatchProfileRegistry {
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/hibernate/batch/JpaBatchProfileRegistry.java:18:  public JpaBatchProfileRegistry(Map<String, JpaBatchProfile> profiles) {
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/hibernate/batch/JpaBatchProfileRegistry.java:35:  public static JpaBatchProfileRegistry of(JpaBatchProfile... profiles) {
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/hibernate/batch/JpaBatchProfileRegistry.java:42:    return new JpaBatchProfileRegistry(byName);
--- HibernateBulkDmlExecutor
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/hibernate/bulk/HibernateBulkDmlExecutor.java:28:public final class HibernateBulkDmlExecutor implements BulkDmlExecutor {
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/hibernate/bulk/HibernateBulkDmlExecutor.java:38:  public HibernateBulkDmlExecutor(
--- HibernateStatelessSessionRunner
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/hibernate/stateless/HibernateStatelessSessionRunner.java:23:public final class HibernateStatelessSessionRunner implements StatelessSessionRunner {
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/hibernate/stateless/HibernateStatelessSessionRunner.java:28:  public HibernateStatelessSessionRunner(
--- EntityGraphCatalog
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/EntityGraphCatalog.java:20:public final class EntityGraphCatalog {
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/EntityGraphCatalog.java:25:  public EntityGraphCatalog(
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/EntityGraphCatalog.java:40:  public static EntityGraphCatalog empty() {
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/EntityGraphCatalog.java:41:    return new EntityGraphCatalog(Map.of());
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/FetchPlanApplier.java:30:  private final EntityGraphCatalog catalog;
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/FetchPlanApplier.java:33:  public FetchPlanApplier(EntityGraphCatalog catalog, EntityManager entityManager) {
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/FetchPlanApplier.java:61:  public EntityGraphCatalog catalog() {
--- FetchPlanApplier
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/FetchPlanApplier.java:22:public final class FetchPlanApplier {
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/FetchPlanApplier.java:33:  public FetchPlanApplier(EntityGraphCatalog catalog, EntityManager entityManager) {
--- JpaKeysetQuerySupport
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/JpaKeysetQuerySupport.java:20:public final class JpaKeysetQuerySupport {
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/JpaKeysetQuerySupport.java:24:  public JpaKeysetQuerySupport(KeysetSliceAssembler assembler) {
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/JpaKeysetQuerySupport.java:29:  public static JpaKeysetQuerySupport standard() {
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/JpaKeysetQuerySupport.java:30:    return new JpaKeysetQuerySupport(new KeysetSliceAssembler());
--- JpaRepositoryFragmentSupport
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/JpaRepositoryFragmentSupport.java:24:public abstract class JpaRepositoryFragmentSupport {
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/JpaRepositoryFragmentSupport.java:31:  protected JpaRepositoryFragmentSupport(EntityManager entityManager) {
--- JpaStreamExecutor
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/JpaStreamExecutor.java:27:public final class JpaStreamExecutor {
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/JpaStreamExecutor.java:41:  public JpaStreamExecutor(QueryObservation observation) {
--- KeysetPredicateBuilder
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/KeysetPredicateBuilder.java:32:public final class KeysetPredicateBuilder {
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/KeysetPredicateBuilder.java:34:  private KeysetPredicateBuilder() {}
--- SpecificationPolicy
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/SpecificationPolicy.java:18:public final class SpecificationPolicy {
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/SpecificationPolicy.java:23:  private SpecificationPolicy() {}
--- QuerydslJpaSupport
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/querydsl/QuerydslJpaSupport.java:21:public final class QuerydslJpaSupport {
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/querydsl/QuerydslJpaSupport.java:28:  public QuerydslJpaSupport(JPAQueryFactory queryFactory) {
=== querydsl lock/declarations ===
43-com.puppycrawl.tools:checkstyle:13.5.0=checkstyle
44-com.querydsl:querydsl-core:5.1.0=compileClasspath,jpaPlatformPerformanceTestCompileClasspath,jpaPlatformPerformanceTestRuntimeClasspath,postgresqlIntegrationTestCompileClasspath,postgresqlIntegrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath,testkitCompileClasspath,testkitRuntimeClasspath
45:com.querydsl:querydsl-jpa:5.1.0=compileClasspath,jpaPlatformPerformanceTestCompileClasspath,jpaPlatformPerformanceTestRuntimeClasspath,postgresqlIntegrationTestCompileClasspath,postgresqlIntegrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath,testkitCompileClasspath,testkitRuntimeClasspath
46-com.sun.istack:istack-commons-runtime:4.1.2=jpaPlatformPerformanceTestRuntimeClasspath,postgresqlIntegrationTestRuntimeClasspath,runtimeClasspath,testRuntimeClasspath,testkitRuntimeClasspath
47-com.tngtech.archunit:archunit-junit5-api:1.3.0=jpaPlatformPerformanceTestCompileClasspath,jpaPlatformPerformanceTestRuntimeClasspath,postgresqlIntegrationTestCompileClasspath,postgresqlIntegrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath,testkitCompileClasspath,testkitRuntimeClasspath
69:    compileOnly 'com.querydsl:querydsl-jpa:5.1.0:jakarta'
72:    testImplementation 'com.querydsl:querydsl-jpa:5.1.0:jakarta'
=== statement inspector configuration ===
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/hibernate/NamedStatementInspector.java:20:public final class NamedStatementInspector implements StatementInspector {
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/JpaRepositoryFragmentSupport.java:66:   * Runs {@code work} with {@code name} bound as the statement-inspector identity.
=== export lists ===
  /**
   * The packages a leaf outside this one may import from.
   *
   * <p>An export list, because 318 of 324 production files declare a public top-level type and
   * "public" inside one jar is public to everybody who has the jar. The composition root imported
   * twenty-two notification implementation types by name, which turned each of them into a
   * cross-leaf source contract: moving one to another package, or reducing its visibility, broke
   * the root's compile. The notification wiring is now a facade the leaf owns, and this names what
   * the rest of the repository may reach for.
   *
   * <p>Everything absent from this list is internal. It may still be {@code public} — Java has no
   * finer granularity inside one jar — but importing it from another leaf is a boundary violation
   * this test reports, not a compile error the language provides.
   */
  private static final Set<String> EXPORTED_PACKAGES =
      Set.of(
          // The contracts other leaves are meant to speak.
          "api",
          // Capability configuration facades, each assembling its own capability's internals.
          "notification.configuration",
          // Cross-cutting seams a composition root legitimately wires.
          "transaction",
          "security",
          "observation",
          "migration",
          "hibernate",
          "fileserver",
          "failure",
          "config");

  private static final JavaClasses PRODUCTION_CLASSES =
  // Future NoSQL adapter modules (for example adapter-persistence-mongodb) must be added as
  // sibling modules that implement application/domain ports directly. When such a module exists,
  // add an ArchUnit rule forbidding jakarta.persistence.., org.hibernate..,
  // org.springframework.data.jpa.., org.flywaydb.., and
  // dev.caskeleton.adapter.outbound.persistence.postgresql..
  // dependencies from that module.

  /**
   * The composition root reaches only the persistence leaf's exported surface.
   *
   * <p>318 of that leaf's 324 production files declare a public top-level type, and {@code public}
   * inside one jar is public to everyone holding the jar. The root imported twenty-two notification
   * implementation types by name — every store, every repository, the record mapper — which made
   * each of them a cross-leaf source contract: moving one to another package, or reducing its
   * visibility, broke this module's compile. The notification wiring is a facade the leaf owns now,
   * and this keeps the surface from re-growing one convenient import at a time.
   *
   * <p>The list is the leaf's export allowlist, mirrored here because this is the consumer side of
   * the same boundary. {@code JpaModuleBoundaryTest} owns the definition.
   */
  @ArchTest
  static final ArchRule BOOTSTRAP_USES_ONLY_THE_PERSISTENCE_EXPORT_SURFACE =
      noClasses()
          .that()
          .resideInAPackage("dev.caskeleton.bootstrap..")
          .should()
          .dependOnClassesThat(
              new com.tngtech.archunit.base.DescribedPredicate<>(
                  "a persistence type outside the exported surface") {
                private static final java.util.Set<String> EXPORTED =
                    java.util.Set.of(
                        "api",
                        "notification.configuration",
                        "transaction",
                        "security",
                        "observation",
                        "migration",
                        "hibernate",
                        "fileserver",
                        "failure",
                        "config",
                        // The two vendor entry points. A vendor configuration imports the core JPA
                        // config rather than the reverse, so there is no single internal entry the
                        // composition root could gate instead — inverting the import to make one
                        // produced a package cycle. Admitting exactly these two, and no other
                        // vendor
                        // internals, is the narrower price.
                        "postgresql",
                        "h2");
                private static final String ROOT = "dev.caskeleton.adapter.outbound.persistence.";

                @Override
                public boolean test(com.tngtech.archunit.core.domain.JavaClass candidate) {
                  String packageName = candidate.getPackageName();
                  if (!packageName.startsWith(ROOT)) {
                    return false;
                  }
                  String remainder = packageName.substring(ROOT.length());
                  return EXPORTED.stream()
                      .noneMatch(
                          exported ->
                              remainder.equals(exported) || remainder.startsWith(exported + "."));
                }
              })
          .as(
              "app-bootstrap imports only the persistence leaf's exported packages: every other"
                  + " public type there is internal, and importing one freezes it as a contract.")
          .allowEmptyShould(true);

  /**
   * A persistence adapter stores what a use case decided; it does not make the decision.
   *
   * <p>{@code JpaNotificationRequestStore} imported {@code NotificationSubmissionService} and

Exit code: 0
