# Harness-Free Module and Gradle Hygiene Design - **Date:** 2026-07-25 - **Status:** Approved - **Mode:** B reconstruction without `.harness` - **Scope:** all 19 Gradle leaves, dependency declarations, test baselines, Mongo scaffolding, runtime-composition documentation, and dependency locks - **Topology SSOT:** `src/config/architecture/modules.json` ## 1. Context The 19-leaf project dependency graph obeys the registered allowed edges, and the three core production source sets are free of Spring, persistence, transport, logging, and metrics imports. The audit nevertheless found a wider declared graph than the source graph, Spring WebMVC test libraries on pure-core test classpaths, Boot 3-era OpenAPI tooling on Spring Boot 4, example-domain code in the production Mongo adapter, and direct MDC access in sample application services. This design follows the user-approved Mode B reconstruction. It does not recreate or depend on `.harness`; settings and verification continue to consume the JSON registry. ## 2. Goals 1. Keep the exact 19 leaves and all allowed project edges in the JSON registry. 2. Remove only dependencies proven unnecessary by source/test inspection plus focused compile/test verification. 3. Give `domain-core`, `application-core`, and `shared-contract` JUnit/AssertJ-only test classpaths. 4. Keep Spring Boot 4.0.0 and replace `springdoc-openapi` 2.x with the Boot 4-compatible 3.0.0 line. 5. Remove unused direct Jackson 2 declarations from GraphQL and WebSocket. 6. Require the Spring configuration processor exactly in leaves whose main source declares `@ConfigurationProperties`. 7. Remove adapter-local `Example*` business concepts from `persistence-mongo`; retain only opt-in Mongo infrastructure and typed enablement settings. 8. Replace sample application-layer MDC reads with an application-owned correlation-context port implemented by the inbound web adapter. 9. Remove tracked jqwik runtime state and ignore future `.jqwik-database` files. 10. Describe the default bootstrap as the default runtime composition, not as wiring every optional leaf. 11. Regenerate only affected strict dependency locks and finish with the full release gates. ## 3. Non-goals - No endpoint, persistence schema, public response, outbox transition, or sample-domain behavior change. - No version catalog, convention-plugin, `buildSrc`, module rename, or registry schema expansion. - No automatic addition of GraphQL, gRPC, WebSocket, Mongo, file server, or object storage to the default `app-bootstrap` runtime. - No stage, commit, amend, or push. ## 4. Approved dependency decisions An allowed registry edge is permission, not an obligation to declare it. | Leaf | Remove after focused proof | Preserve | | --- | --- | --- | | `application-core` | unused `domain-core` edge | `shared-contract` | | `inbound:web` | unused `domain-core` edge | application/shared and transport dependencies | | `inbound:graphql` | application/domain edges, direct Jackson 2, unused processor | shared and GraphQL/web test transport | | `inbound:grpc` | application/domain edges, unused annotations/direct protobuf declarations | shared, netty, services, configuration processor | | `inbound:websocket` | application/shared edges, direct Jackson 2 | domain, WebSocket, configuration processor | | `outbound:support` | domain/application/shared edges | autoconfigure and SLF4J API | | `outbound:cache-redis` | domain/application, unused Groovy/Spock | shared/support | | `outbound:httpclient` | domain/application | shared/support, actual Groovy/Spock tests | | `outbound:identifier` | domain, `uuid-creator` | application, actual Groovy/Spock tests | | `outbound:messaging` | domain, unused Groovy/Spock | application/shared/support/SLF4J | | `outbound:notification` | domain, unused Groovy/Spock | application/shared/support/web/SLF4J | | `outbound:persistence-jpa` | domain; explicit Flyway core only if focused compile proves the starter sufficient | application/shared/JPA/PostgreSQL | | `outbound:persistence-mongo` | application/shared, `Example*`, example Testcontainers tests | Mongo opt-in infrastructure/settings | | `outbound:fileserver` | broad Boot starter | application/shared, autoconfigure, SLF4J | | `outbound:objectstorage` | broad Boot starter | application/shared/AWS, autoconfigure, SLF4J, vendor IT | Production composition-root dependencies remain even when bootstrap source does not statically import their types: their purpose is runtime assembly. Duplicate test declarations may be removed only when the focused test classpath continues to compile and execute. ## 5. Pure-core test and verification policy `domain-core`, `application-core`, and `shared-contract` receive only JUnit Jupiter, AssertJ, and the JUnit launcher from the root convention. All other leaves keep the existing Spring test baseline in this change; family-wide convention plugins are out of scope. The existing application dependency-purity gate remains. A new registry-driven configuration processor parity gate applies this Boolean invariant to every leaf and is wired into `check`: main source contains one or more exact `@ConfigurationProperties(` occurrences if and only if the leaf `build.gradle` contains exactly one Spring configuration-processor declaration. It must ignore `@ConfigurationPropertiesScan`; the number of settings classes is not compared with the number of processor declarations. ## 6. Spring Boot 4 compatibility The web adapter changes `org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.6` to `3.0.0`, the first stable springdoc line released for Spring Boot 4.0.0. The existing sample tests that boot a real server and call `/v3/api-docs` are the behavior gate. Snapshot changes are accepted only if they are a deterministic library-version result and retain the public API contract. Springdoc 3 otherwise widens `ApiError.details` from the committed `type: object` to an unconstrained OAS 3.1 schema. A web-adapter-owned `OpenApiCustomizer` must restore the object schema in the final generated document. Both real-server test applications import that production configuration. `shared-contract` remains free of Swagger annotations and dependencies. GraphQL and WebSocket remove direct `com.fasterxml.jackson` declarations because neither source set imports them and Spring Boot 4 owns its JSON stack through the relevant starters. The web adapter retains the `JsonNullable` value type, but its `0.2.6` artifact also declares Jackson 2 transitively while this repository supplies explicit Jackson 3 serializers. Before and after dependency insight plus focused present/null/undefined serialization tests determine whether that transitive edge can be excluded. Exclusion is applied only if those tests and the real-server OpenAPI tests pass; springdoc/Swagger's independently required JSON graph is not removed by assumption. ## 7. Mongo production boundary Delete the adapter-local `ExampleRecord`, document, mapper, repository, repository adapter, and their tests. `MongoPersistenceConfig` remains conditional on `ca-skeleton.persistence-mongo.enabled=true` and explicitly imports the Mongo client/data auto-configurations without owning a fake business repository. The starter also registers Mongo auto-configuration directly through Boot metadata, independently of `MongoPersistenceConfig`. A module-level `AutoConfigurationImportFilter`, registered through Boot 4's `META-INF/spring.factories` discovery path, must exclude the Boot 4 sync/reactive client, data, repository, health, and metrics Mongo auto-configurations while the enable property is absent or false. It must allow them unchanged when the property is true; consumers must not need to set `spring.autoconfigure.exclude`. Replacement tests must prove: - an actual `@EnableAutoConfiguration` context in default/false mode creates no Mongo infrastructure; - properties bind the enable flag; - enabled mode can create the infrastructure with a supplied mock `MongoClient`, without a real network connection; - production source contains no `Example*` type. The Testcontainers dependencies leave this module when the example repository IT is removed. ## 8. Correlation context boundary `application-core` owns a framework-free `CorrelationIdPort` whose read result is optional. `adapter:inbound:web` implements it from the sanitized request MDC correlation key. `CreateWorkLogUseCase` and `PosterEventPublisher` depend only on the port and preserve the current fallback to the generated event id when no correlation id exists. Tests first pin present/blank/absent behavior and prove the sample application packages no longer import SLF4J/MDC. Diagnostic storage remains an adapter concern. ## 9. Runtime composition and generated state `app-bootstrap` keeps its current default runtime modules. Its build description and README must state that optional leaves require an explicit registry and composition-root dependency change. Optional adapters remain independently buildable and testable. The tracked four-byte `src/sample-portfolio/.jqwik-database` is generated runtime state. Delete it and add `.jqwik-database` to `src/.gitignore`; retain jqwik itself because property tests use it. ## 10. Verification Run focused compile/tests before and after each dependency group. Regenerate locks only through each affected leaf's `:leaf-path:resolveAndLockAll --write-locks` task, then run: ```bash cd src ./gradlew check --console=plain ./gradlew test --console=plain ./gradlew verifyCleanArchitectureDependencies --console=plain ./gradlew verifyApplicationCoreDependencyPurity --console=plain ./gradlew verifyConfigurationPropertiesProcessor --console=plain ./gradlew verifyDependencyLocks --console=plain ./gradlew verifyPublicPathSnapshot verifyEnvKeys --console=plain ``` Completion requires fresh review, `git diff --check`, and an LLM Wiki branch note or an explicit capture blocker for the mandated exact vault path.