Files
tech-log-backend/docs/superpowers/specs/2026-07-25-module-gradle-hygiene-harness-free-design.md

9.8 KiB

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:

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.