54 KiB
Module and Gradle Hygiene Refactoring Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Reduce the 19-leaf Gradle graph to its characterized minimum while preserving Clean Architecture direction, pure-core classpaths, optional runtime composition, and reproducible locks.
Architecture: .harness/project/modules.yaml remains the only topology authority and gains
validated runtime membership. Project and external dependency removals proceed leaf-by-leaf after a
green compile/test baseline; every production leaf is added to a test-only architecture-analysis
classpath without being added to the application runtime. The approved application outbox
failure-reporting design is completed first and is not reimplemented here.
Tech Stack: Java 21, Spring Boot 4.0.0, Gradle 9.0 Groovy DSL, JUnit Jupiter, Spock where Groovy specifications actually exist, ArchUnit, strict Gradle dependency locking
Spec: docs/superpowers/specs/2026-07-25-module-gradle-hygiene-design.md
Working policy: Human-only commits. Agents do not stage, commit, amend, or push. This plan has no agent commit step.
Task 1: Prove the CI and logging prerequisites are green
Files:
-
Verify only:
.harness/project/modules.yaml -
Verify only:
.tool-versions -
Verify only:
.trivyignore.yaml -
Verify only:
.github/workflows/ci-quality-gates.yml -
Verify only:
.github/workflows/link-check.yml -
Verify only:
docs/superpowers/specs/2026-07-25-application-outbox-failure-reporting-design.md -
Verify only:
src/application-core/build.gradle -
Verify only:
src/application-core/src/main/java/dev/caskeleton/application/outbox/PublishPendingOutboxEventsUseCase.java -
Verify only:
src/adapter/outbound/messaging/build.gradle -
Step 1: Confirm the control-plane files exist
Run:
test -f .harness/project/modules.yaml
test -f .tool-versions
test -f .trivyignore.yaml
test -f .github/workflows/ci-quality-gates.yml
test -f .github/workflows/link-check.yml
Expected: all commands exit 0. If any exits non-zero, stop this plan and complete
docs/superpowers/plans/2026-07-25-ci-control-plane-recovery.md.
- Step 2: Run the recovered configuration and CI contract gates
Run:
cd src
./gradlew projects --console=plain
./gradlew :app-bootstrap:test \
--tests 'dev.caskeleton.bootstrap.contract.DeveloperExperienceContractTest' \
--console=plain
./gradlew :app-bootstrap:test \
--tests 'dev.caskeleton.bootstrap.contract.SampleRemovalSmokeContractTest' \
--console=plain
./gradlew verifyTrivyignore --console=plain
Expected: BUILD SUCCESSFUL for all four invocations and exactly 19 leaf projects in the projects
report.
- Step 3: Confirm the approved outbox reporting refactor is complete
Run:
./gradlew :application-core:test \
--tests 'dev.caskeleton.application.outbox.*' --console=plain
./gradlew :adapter:outbound:messaging:test --console=plain
./gradlew :app-bootstrap:test \
--tests 'dev.caskeleton.bootstrap.outbox.*' --console=plain
./gradlew verifyApplicationCoreDependencyPurity --console=plain
./gradlew :application-core:dependencies \
--configuration runtimeClasspath --console=plain
Expected:
- all focused tests pass;
PublishPendingOutboxEventsUseCaseusesOutboxRelayFailureReportPort;adapter:outbound:messagingowns the reporter implementation;- the application runtime dependency report contains no
org.springframework,org.slf4j, Logback, Log4j, Micrometer, or SnakeYAML coordinate.
If any assertion fails, stop this plan and complete
docs/superpowers/plans/2026-07-25-application-outbox-failure-reporting.md.
- Step 4: Record the untouched baseline
Run:
./gradlew verifyCleanArchitectureDependencies --console=plain
./gradlew :app-bootstrap:test --tests '*CleanArchitectureTest' --console=plain
./gradlew verifyDependencyLocks --console=plain
git status --short
Expected: three successful Gradle gates. git status shows only work already authorized for the
current implementation branch.
Task 2: Resolve the stable packet and write the recovered-control-plane addendum
The .harness registry/API was absent when this plan was written. Its implementation is therefore
not guessed here. CI recovery must expose the real parser, validator, packet, and test APIs before
the control-plane changes receive executable code.
Files:
-
Verify only:
.harness/manifest.yaml -
Verify only:
.harness/project/modules.yaml -
Verify only:
.harness/lib/module_registry.py -
Verify only:
.harness/validators/validate_modules.py -
Verify only:
.harness/tests/test_module_registry.py -
Verify only:
.harness/core/risk-policy.yaml -
Verify only:
.harness/core/evidence-policy.yaml -
Verify only:
/tmp/ca-control-plane-recovery/evidence/stable-task-packet.env -
Add:
docs/superpowers/plans/2026-07-25-module-gradle-hygiene-recovered-control-plane.md -
Step 1: Resolve and freeze the recovered task packet
The completed CI recovery must have run the resolver against a concrete overlay and written
/tmp/ca-control-plane-recovery/evidence/stable-task-packet.env with these literal keys:
overlay_path, packet_path, overlay_sha256, packet_sha256, rule_hash, and
resolve_command. A --help invocation or prose-only controller confirmation does not satisfy this
gate.
Run:
test -s .harness/manifest.yaml
test -s .harness/project/modules.yaml
test -s .harness/lib/module_registry.py
test -s .harness/validators/validate_modules.py
test -s .harness/tests/test_module_registry.py
packet_evidence=/tmp/ca-control-plane-recovery/evidence/stable-task-packet.env
test -s "${packet_evidence}"
for packet_key in \
overlay_path packet_path overlay_sha256 packet_sha256 rule_hash resolve_command
do
test "$(grep -c "^${packet_key}=" "${packet_evidence}")" -eq 1
done
overlay_path="$(sed -n 's/^overlay_path=//p' "${packet_evidence}")"
packet_path="$(sed -n 's/^packet_path=//p' "${packet_evidence}")"
overlay_sha256="$(sed -n 's/^overlay_sha256=//p' "${packet_evidence}")"
packet_sha256="$(sed -n 's/^packet_sha256=//p' "${packet_evidence}")"
rule_hash="$(sed -n 's/^rule_hash=//p' "${packet_evidence}")"
resolve_command="$(sed -n 's/^resolve_command=//p' "${packet_evidence}")"
test -f "${overlay_path}"
test -s "${packet_path}"
printf '%s %s\n' "${overlay_sha256}" "${overlay_path}" | sha256sum -c -
printf '%s %s\n' "${packet_sha256}" "${packet_path}" | sha256sum -c -
hash_count="$(
printf '%s\n' "${overlay_sha256}" "${packet_sha256}" "${rule_hash}" \
| grep -Ec '^[0-9a-f]{64}$'
)"
test "${hash_count}" -eq 3
printf '%s\n' "${resolve_command}" | grep -F '.harness/validators/resolve_task.py'
if printf '%s\n' "${resolve_command}" | grep -q -- '--help'; then
exit 1
fi
printf '%s\n' "${resolve_command}" | grep -F -- "${overlay_path}"
printf '%s\n' "${resolve_command}" | grep -F -- "${packet_path}"
grep -F "${rule_hash}" "${packet_path}" >/dev/null
Expected: every check exits 0; concrete overlay and resolved packet files exist; both content
hashes match; the 64-character rule hash occurs in the packet; and the recorded resolver command is
an actual invocation rather than --help. If the evidence file is absent, return to CI recovery and
perform the controller resolution before continuing.
- Step 2: Read and test the recovered API before planning against it
Run:
cat .harness/manifest.yaml
cat .harness/project/modules.yaml
cat .harness/lib/module_registry.py
cat .harness/validators/validate_modules.py
cat .harness/tests/test_module_registry.py
python3 -m unittest discover -s .harness/tests -p 'test_module_registry.py' -v
Expected: all selected files are read to EOF and the recovered registry tests pass before the addendum is authored.
- Step 3: Write the exact control-plane addendum
Use superpowers:writing-plans. The addendum must have the standard Goal/Architecture/Tech Stack
header, human-only commit policy, checkbox steps, literal file paths, complete compilable code, exact
commands, and expected red/green output. It must contain all of the following:
- the concrete overlay path, resolved packet path, overlay hash, packet hash, and rule hash from
stable-task-packet.env; - the recovered registry's exact serialization for all 19 runtime memberships from this design;
- complete Python tests and implementation for unique ids/paths, membership enum, unknown ids,
cycles, sample isolation, and rejection of any actual project edge absent from the source
module's
allowed_dependencies; - deterministic concrete error fixtures using
adapter-inbound-graphql,adapter-inbound-web,adapter-outbound-cache-redis,app-bootstrap,application-core, anddomain-core; - complete Groovy that extends and renames the existing
verifyApplicationCoreDependencyPuritytask toverifyExternalDependencyPurity, preserves its main/test application rules and:application-core:checkdependency, broadens production compile/runtime rules to registered leaves, and removes the old task registration so only one purity gate remains; - complete Groovy and tests for configuration-processor parity against registered main source,
plus a class-by-class
@ConfigurationPropertiesbehavior-test inventory and complete binding/ validation test code for every uncovered settings class; - complete Groovy for non-consumable test-only
architectureAnalysis, populated from the recovered registry without changingimplementation,runtimeOnly,bootJar, or publication; - complete Java tests for registered-leaf coverage, registry-driven sample removal, runtime composition, and the two-root package scan contract;
- exact before/after commands for registry validation, both verification tasks, architecture coverage, sample-off execution, and runtime composition.
The addendum must explicitly preserve this membership assignment:
core: domain-core, application-core, shared-contract
app-default: adapter-inbound-web, adapter-outbound-persistence-jpa,
adapter-outbound-support, adapter-outbound-messaging, adapter-outbound-cache-redis,
adapter-outbound-notification, adapter-outbound-httpclient, adapter-outbound-identifier
opt-in: adapter-inbound-graphql, adapter-inbound-grpc, adapter-inbound-websocket,
adapter-outbound-fileserver, adapter-outbound-objectstorage, adapter-outbound-persistence-mongo
composition-root: app-bootstrap, sample-portfolio
- Step 4: Review the addendum before any control-plane edit
Run:
python3 - <<'PY'
from pathlib import Path
import re
path = Path(
"docs/superpowers/plans/"
"2026-07-25-module-gradle-hygiene-recovered-control-plane.md"
)
text = path.read_text(encoding="utf-8")
banned = [
"T" + "BD",
"TO" + "DO",
"implement " + "later",
"fill in " + "details",
"as " + "needed",
"similar to " + "Task",
]
violations = [term for term in banned if term in text]
violations.extend(
match.group(0)
for match in re.finditer(chr(60) + "[^" + chr(62) + "]+" + chr(62), text)
)
if violations:
raise SystemExit("placeholder content: " + ", ".join(violations))
PY
git diff --check -- \
docs/superpowers/plans/2026-07-25-module-gradle-hygiene-recovered-control-plane.md
Expected: both commands print nothing. Review confirms every code-changing step has complete code against the recovered API. Mark the addendum approved before proceeding.
Task 3: Execute the approved recovered-control-plane addendum
Files:
-
Modify only the files enumerated by the approved
docs/superpowers/plans/2026-07-25-module-gradle-hygiene-recovered-control-plane.md -
Step 1: Reassert the stable packet and addendum hashes
Run:
packet_evidence=/tmp/ca-control-plane-recovery/evidence/stable-task-packet.env
test -s "${packet_evidence}"
overlay_path="$(sed -n 's/^overlay_path=//p' "${packet_evidence}")"
packet_path="$(sed -n 's/^packet_path=//p' "${packet_evidence}")"
overlay_sha256="$(sed -n 's/^overlay_sha256=//p' "${packet_evidence}")"
packet_sha256="$(sed -n 's/^packet_sha256=//p' "${packet_evidence}")"
rule_hash="$(sed -n 's/^rule_hash=//p' "${packet_evidence}")"
printf '%s %s\n' "${overlay_sha256}" "${overlay_path}" | sha256sum -c -
printf '%s %s\n' "${packet_sha256}" "${packet_path}" | sha256sum -c -
grep -F "${rule_hash}" "${packet_path}" >/dev/null
addendum=docs/superpowers/plans/2026-07-25-module-gradle-hygiene-recovered-control-plane.md
test -s "${addendum}"
grep -F "${overlay_sha256}" "${addendum}" >/dev/null
grep -F "${packet_sha256}" "${addendum}" >/dev/null
grep -F "${rule_hash}" "${addendum}" >/dev/null
Expected: both files still match their recorded hashes, the packet still carries the rule hash, and the approved addendum names all three hashes. Any mismatch invalidates the addendum and returns to Task 2.
- Step 2: Execute the addendum as its own plan
Stop this plan, use superpowers:executing-plans or
superpowers:subagent-driven-development, and complete the recovered-control-plane addendum in
full. Return here only after its registry, purity, processor, architecture-analysis, sample
isolation, runtime composition, and package-scan checks are green.
- Step 3: Prove the addendum superseded the narrow purity task
Run:
cd src
./gradlew tasks --all --console=plain | rg \
'^verify(ExternalDependencyPurity|ConfigurationPropertiesProcessor)\\b'
if ./gradlew tasks --all --console=plain | \
rg -q '^verifyApplicationCoreDependencyPurity\\b'; then
exit 1
fi
./gradlew verifyExternalDependencyPurity \
verifyConfigurationPropertiesProcessor \
verifyCleanArchitectureDependencies \
--console=plain
Expected: the two replacement tasks are listed, the old application-only task is absent, and all three verification tasks pass.
Task 4: Replace global Spring tests with role-specific test conventions
Files:
-
Modify:
src/build.gradle -
Modify:
src/domain-core/build.gradle -
Modify:
src/application-core/build.gradle -
Modify:
src/shared-contract/build.gradle -
Modify:
src/adapter/inbound/web/build.gradle -
Modify:
src/adapter/inbound/graphql/build.gradle -
Modify:
src/adapter/inbound/grpc/build.gradle -
Modify:
src/adapter/inbound/websocket/build.gradle -
Modify:
src/adapter/outbound/support/build.gradle -
Modify:
src/adapter/outbound/cache-redis/build.gradle -
Modify:
src/adapter/outbound/fileserver/build.gradle -
Modify:
src/adapter/outbound/httpclient/build.gradle -
Modify:
src/adapter/outbound/identifier/build.gradle -
Modify:
src/adapter/outbound/messaging/build.gradle -
Modify:
src/adapter/outbound/notification/build.gradle -
Modify:
src/adapter/outbound/objectstorage/build.gradle -
Modify:
src/adapter/outbound/persistence-jpa/build.gradle -
Modify:
src/adapter/outbound/persistence-mongo/build.gradle -
Modify:
src/app-bootstrap/build.gradle -
Modify:
src/sample-portfolio/build.gradle -
Step 1: Characterize test-framework imports per leaf
Run:
for module in \
domain-core application-core shared-contract \
adapter/inbound/web adapter/inbound/graphql adapter/inbound/grpc adapter/inbound/websocket \
adapter/outbound/support adapter/outbound/cache-redis adapter/outbound/fileserver \
adapter/outbound/httpclient adapter/outbound/identifier adapter/outbound/messaging \
adapter/outbound/notification adapter/outbound/objectstorage \
adapter/outbound/persistence-jpa adapter/outbound/persistence-mongo \
app-bootstrap sample-portfolio
do
printf '%s\n' "===== ${module} ====="
rg '^import (org\.springframework|org\.junit|org\.assertj|org\.mockito|spock\.|org\.testcontainers)' \
"${module}/src/test" -g '*.java' -g '*.groovy' 2>/dev/null \
| sed -E 's/^.*:import / /' | cut -d. -f1-3 | sort -u
done
Expected: a deterministic per-leaf list. Save no generated report in source control.
- Step 2: Remove the global Spring test starters
In src/build.gradle, replace the global:
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test'
with the framework-neutral baseline:
testImplementation 'org.junit.jupiter:junit-jupiter'
testImplementation 'org.assertj:assertj-core'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
Run:
./gradlew :domain-core:dependencies --configuration testRuntimeClasspath --console=plain
./gradlew :application-core:dependencies --configuration testRuntimeClasspath --console=plain
./gradlew :shared-contract:dependencies --configuration testRuntimeClasspath --console=plain
Expected: no Spring, Tomcat, servlet, Jackson, or Logback coordinate in these three reports.
- Step 3: Add Spring test support only to the characterized leaves
Add this exact declaration:
testImplementation 'org.springframework.boot:spring-boot-starter-test'
to these leaves:
adapter:inbound:web
adapter:inbound:graphql
adapter:inbound:grpc
adapter:inbound:websocket
adapter:outbound:httpclient
adapter:outbound:persistence-jpa
adapter:outbound:persistence-mongo
app-bootstrap
sample-portfolio
Add this exact declaration only to adapter:inbound:web, app-bootstrap, and
sample-portfolio:
testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test'
Do not add either starter to domain-core, application-core, shared-contract, or
adapter:outbound:identifier.
- Step 4: Compile every test source set
Run:
./gradlew compileTestJava compileTestGroovy --console=plain
Expected: BUILD SUCCESSFUL; Gradle may report NO-SOURCE for leaves without Groovy tests.
- Step 5: Run pure-core tests
Run:
./gradlew :domain-core:test :application-core:test :shared-contract:test --console=plain
Expected: all discovered tests pass and no Spring application context starts.
Task 5: Align configuration processors
Files:
-
Verify:
docs/superpowers/plans/2026-07-25-module-gradle-hygiene-recovered-control-plane.md -
Modify:
src/adapter/inbound/web/build.gradle -
Modify:
src/adapter/inbound/graphql/build.gradle -
Modify:
src/adapter/outbound/cache-redis/build.gradle -
Modify:
src/adapter/outbound/httpclient/build.gradle -
Modify:
src/adapter/outbound/messaging/build.gradle -
Modify:
src/adapter/outbound/notification/build.gradle -
Modify:
src/adapter/outbound/persistence-jpa/build.gradle -
Modify:
src/app-bootstrap/build.gradle -
Modify:
src/sample-portfolio/build.gradle -
Step 1: Close settings behavior-test gaps from the approved addendum
Execute the addendum's complete class-by-class settings-test section before processor edits. The
section must map every main-source @ConfigurationProperties class to a binding/validation test and
contain complete code for every gap; a leaf-wide smoke test is not a substitute.
Run the literal focused commands recorded in that section.
Expected: every settings behavior test passes and the addendum inventory has no uncovered class.
- Step 2: Apply the parity gate's exact additions and removal
Remove the processor from GraphQL because it has no main-source properties class. Add it to each listed settings-owning leaf:
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
Do not change modules already aligned by the gate:
adapter:inbound:grpc
adapter:inbound:websocket
adapter:outbound:fileserver
adapter:outbound:objectstorage
adapter:outbound:persistence-mongo
- Step 3: Run processor parity and focused settings tests
Run:
./gradlew verifyConfigurationPropertiesProcessor --console=plain
./gradlew :adapter:inbound:web:test --tests '*SettingsTest' --console=plain
./gradlew :adapter:outbound:cache-redis:test --tests '*SettingsTest' --console=plain
./gradlew :adapter:outbound:httpclient:test --tests '*SettingsTest' --console=plain
./gradlew :adapter:outbound:messaging:test --tests '*SettingsTest' --console=plain
./gradlew :app-bootstrap:test --tests '*SettingsTest' --console=plain
Expected: processor parity passes and every focused settings test passes. A no-match result fails the step and must be corrected against the approved class-to-test inventory; running a leaf-wide task does not satisfy missing binding coverage.
Task 6: Characterize and prune project edges in every leaf
Files:
-
Modify:
.harness/project/modules.yaml -
Modify the following confirmed-candidate builds and their nearest
CLAUDE.mdfiles:src/adapter/inbound/graphql/build.gradlesrc/adapter/inbound/grpc/build.gradlesrc/adapter/inbound/web/build.gradlesrc/adapter/inbound/websocket/build.gradlesrc/adapter/outbound/cache-redis/build.gradlesrc/adapter/outbound/httpclient/build.gradlesrc/adapter/outbound/identifier/build.gradlesrc/adapter/outbound/messaging/build.gradlesrc/adapter/outbound/notification/build.gradlesrc/adapter/outbound/persistence-jpa/build.gradlesrc/adapter/outbound/support/build.gradle
-
Verify unchanged project edges:
src/domain-core/build.gradlesrc/application-core/build.gradlesrc/shared-contract/build.gradlesrc/adapter/outbound/fileserver/build.gradlesrc/adapter/outbound/objectstorage/build.gradlesrc/app-bootstrap/build.gradlesrc/sample-portfolio/build.gradle
-
Handle Mongo separately in Task 8:
src/adapter/outbound/persistence-mongo/build.gradle
-
Step 1: Capture before-removal dependency reports
Run:
./gradlew \
:adapter:inbound:graphql:dependencies \
:adapter:inbound:grpc:dependencies \
:adapter:inbound:web:dependencies \
:adapter:inbound:websocket:dependencies \
:adapter:outbound:cache-redis:dependencies \
:adapter:outbound:httpclient:dependencies \
:adapter:outbound:identifier:dependencies \
:adapter:outbound:messaging:dependencies \
:adapter:outbound:notification:dependencies \
:adapter:outbound:persistence-jpa:dependencies \
:adapter:outbound:support:dependencies \
--configuration compileClasspath --console=plain
Expected: success and visibility of each declared candidate edge.
- Step 2: Capture the pre-removal compile and focused-test baseline
Run before editing any candidate declaration:
./gradlew \
:adapter:inbound:graphql:compileJava :adapter:inbound:graphql:test \
:adapter:inbound:grpc:compileJava :adapter:inbound:grpc:test \
:adapter:inbound:web:compileJava :adapter:inbound:web:test \
:adapter:inbound:websocket:compileJava :adapter:inbound:websocket:test \
:adapter:outbound:cache-redis:compileJava :adapter:outbound:cache-redis:test \
:adapter:outbound:httpclient:compileJava :adapter:outbound:httpclient:test \
:adapter:outbound:identifier:compileJava :adapter:outbound:identifier:test \
:adapter:outbound:messaging:compileJava :adapter:outbound:messaging:test \
:adapter:outbound:notification:compileJava :adapter:outbound:notification:test \
:adapter:outbound:persistence-jpa:compileJava :adapter:outbound:persistence-jpa:test \
:adapter:outbound:support:compileJava :adapter:outbound:support:test \
--console=plain
Expected: all eleven compilers and focused test tasks pass. Stop on any failure; a red leaf is not a dependency-removal candidate.
- Step 3: Remove only source-proven project edges
Remove these declarations and the corresponding registry allowed_dependencies entries:
graphql: application-core, domain-core
grpc: application-core, domain-core
web: domain-core
websocket: application-core, shared-contract
cache-redis: domain-core, application-core
httpclient: domain-core, application-core
identifier: domain-core
messaging: domain-core
notification: domain-core
persistence-jpa: domain-core
support: domain-core, application-core, shared-contract
The approved reporter remains in messaging, so its application-core, shared-contract, and
support edges remain. Do not alter the four adapter-to-support edges.
- Step 4: Compile and test each changed leaf
Run:
./gradlew \
:adapter:inbound:graphql:test \
:adapter:inbound:grpc:test \
:adapter:inbound:web:test \
:adapter:inbound:websocket:test \
:adapter:outbound:cache-redis:test \
:adapter:outbound:httpclient:test \
:adapter:outbound:identifier:test \
:adapter:outbound:messaging:test \
:adapter:outbound:notification:test \
:adapter:outbound:persistence-jpa:test \
:adapter:outbound:support:test \
--console=plain
Expected: all eleven focused tasks pass.
- Step 5: Verify all 19 leaf project graphs
Run:
./gradlew \
:domain-core:compileJava \
:application-core:compileJava \
:shared-contract:compileJava \
:adapter:inbound:web:compileJava \
:adapter:inbound:graphql:compileJava \
:adapter:inbound:grpc:compileJava \
:adapter:inbound:websocket:compileJava \
:adapter:outbound:support:compileJava \
:adapter:outbound:cache-redis:compileJava \
:adapter:outbound:fileserver:compileJava \
:adapter:outbound:httpclient:compileJava \
:adapter:outbound:identifier:compileJava \
:adapter:outbound:messaging:compileJava \
:adapter:outbound:notification:compileJava \
:adapter:outbound:objectstorage:compileJava \
:adapter:outbound:persistence-jpa:compileJava \
:adapter:outbound:persistence-mongo:compileJava \
:app-bootstrap:compileJava \
:sample-portfolio:compileJava \
verifyCleanArchitectureDependencies \
--console=plain
Expected: all 19 compilers and the registry-backed edge verifier pass.
Task 7: Remove unused plugins/libraries and narrow broad starters
Files:
-
Modify:
src/adapter/outbound/cache-redis/build.gradle -
Modify:
src/adapter/outbound/messaging/build.gradle -
Modify:
src/adapter/outbound/notification/build.gradle -
Modify:
src/adapter/outbound/identifier/build.gradle -
Modify:
src/adapter/inbound/graphql/build.gradle -
Modify:
src/adapter/inbound/grpc/build.gradle -
Modify:
src/adapter/outbound/fileserver/build.gradle -
Modify:
src/adapter/outbound/objectstorage/build.gradle -
Modify:
src/adapter/outbound/persistence-jpa/build.gradle -
Step 1: Remove deterministic unused test/tool dependencies
Remove Groovy plugin and Spock from:
adapter:outbound:cache-redis
adapter:outbound:messaging
adapter:outbound:notification
Remove com.github.f4b6a3:uuid-creator from adapter:outbound:identifier.
Run:
./gradlew \
:adapter:outbound:cache-redis:test \
:adapter:outbound:messaging:test \
:adapter:outbound:notification:test \
:adapter:outbound:identifier:test \
--console=plain
Expected: all four tasks pass and the first three no longer expose compileGroovy or
compileTestGroovy work beyond NO-SOURCE tasks contributed elsewhere.
- Step 2: Characterize GraphQL and gRPC candidates
Run:
rg -n 'com\.fasterxml\.jackson|java\.time' \
adapter/inbound/graphql/src/main -g '*.java'
rg -n 'io\.grpc\.stub|io\.grpc\.protobuf(?!\.services)|javax\.annotation|jakarta\.annotation' \
adapter/inbound/grpc/src/main -g '*.java' --pcre2
./gradlew :adapter:inbound:graphql:test :adapter:inbound:grpc:test --console=plain
Expected: no GraphQL direct JSR-310 use; no gRPC generated-stub or generated-annotation use; focused tests pass before removal.
- Step 3: Remove no-source-use protocol candidates and retest
Remove GraphQL JSR-310 if the schema/controller characterization remains string-only. Remove gRPC
stub and generated-annotation declarations. Remove direct grpc-protobuf only when
grpc-services supplies every required health/reflection type on compile/runtime classpaths.
Run:
./gradlew \
:adapter:inbound:graphql:compileJava :adapter:inbound:graphql:test \
:adapter:inbound:grpc:compileJava :adapter:inbound:grpc:test \
--console=plain
Expected: all four tasks pass. If grpc-protobuf is required by a directly referenced API, restore
that one declaration and document the direct type in the module build comment.
- Step 4: Narrow starter candidates one leaf at a time
For gRPC, fileserver, and objectstorage, replace spring-boot-starter with only the compile APIs
shown by jdeps/imports:
implementation 'org.springframework.boot:spring-boot-autoconfigure'
implementation 'org.springframework:spring-context'
implementation 'org.slf4j:slf4j-api'
Keep each protocol/storage runtime dependency already declared. Run immediately after each leaf:
./gradlew :adapter:inbound:grpc:test --console=plain
./gradlew :adapter:outbound:fileserver:test --console=plain
./gradlew :adapter:outbound:objectstorage:test --console=plain
Expected: each task passes and its runtime report contains no Logback implementation contributed by that leaf.
- Step 5: Test explicit Flyway-core duplication
Temporarily remove only the explicit org.flywaydb:flyway-core declaration from
persistence-jpa; retain spring-boot-starter-flyway.
Run:
./gradlew \
:adapter:outbound:persistence-jpa:compileJava \
:adapter:outbound:persistence-jpa:test \
--console=plain
Expected: success means the explicit core declaration stays removed. A compile failure naming a direct Flyway API means restore it and retain a comment naming that source file.
Task 8: Remove the production Mongo example and generated jqwik state
Files:
-
Delete:
src/adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/ExampleRecord.java -
Delete:
src/adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/ExampleMongoDocument.java -
Delete:
src/adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/ExampleMongoMapper.java -
Delete:
src/adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/ExampleMongoRepository.java -
Delete:
src/adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/ExampleMongoRepositoryAdapter.java -
Delete:
src/adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/ExampleMongoMapperTest.java -
Delete:
src/adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/ExampleMongoRepositoryIT.java -
Modify:
src/adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/MongoPersistenceConfig.java -
Modify:
src/adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/MongoPersistenceProperties.java -
Add:
src/adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/MongoPersistencePropertiesBindingTest.java -
Add:
src/adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/MongoPersistenceDisabledModeTest.java -
Add:
src/adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/MongoPersistenceEnabledModeTest.java -
Add:
src/adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/MongoPersistenceOwnershipTest.java -
Modify:
src/adapter/outbound/persistence-mongo/build.gradle -
Modify:
src/adapter/outbound/persistence-mongo/CLAUDE.md -
Modify:
src/adapter/outbound/persistence-mongo/README.md -
Modify:
.harness/project/modules.yaml -
Delete:
src/sample-portfolio/.jqwik-database -
Modify:
src/.gitignore -
Step 1: Characterize binding/disabled/enabled behavior and add a red ownership test
The binding test loads:
ca-skeleton.persistence-mongo.enabled=true
ca-skeleton.persistence-mongo.database=contract_db
and asserts enabled == true and database == "contract_db".
The disabled-mode test loads MongoPersistenceConfig without the enable property and asserts no
Mongo client, repository, or adapter bean is created.
Create MongoPersistencePropertiesBindingTest.java exactly as:
package dev.caskeleton.adapter.outbound.mongo;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Configuration;
class MongoPersistencePropertiesBindingTest {
private final ApplicationContextRunner runner =
new ApplicationContextRunner().withUserConfiguration(BindingConfig.class);
@Test
void bindsOptInAndDatabaseProperties() {
runner
.withPropertyValues(
"ca-skeleton.persistence-mongo.enabled=true",
"ca-skeleton.persistence-mongo.database=contract_db")
.run(
context -> {
assertThat(context).hasNotFailed();
MongoPersistenceProperties properties =
context.getBean(MongoPersistenceProperties.class);
assertThat(properties.isEnabled()).isTrue();
assertThat(properties.getDatabase()).isEqualTo("contract_db");
});
}
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(MongoPersistenceProperties.class)
static class BindingConfig {}
}
Create MongoPersistenceDisabledModeTest.java exactly as:
package dev.caskeleton.adapter.outbound.mongo;
import static org.assertj.core.api.Assertions.assertThat;
import com.mongodb.client.MongoClient;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.repository.Repository;
class MongoPersistenceDisabledModeTest {
private final ApplicationContextRunner runner =
new ApplicationContextRunner().withUserConfiguration(MongoPersistenceConfig.class);
@Test
void createsNoMongoInfrastructureWhenDisabledByDefault() {
runner.run(
context -> {
assertThat(context)
.hasNotFailed()
.doesNotHaveBean(MongoPersistenceConfig.class)
.doesNotHaveBean(MongoClient.class)
.doesNotHaveBean(MongoTemplate.class);
assertThat(context.getBeanNamesForType(Repository.class)).isEmpty();
});
}
}
Create MongoPersistenceOwnershipTest.java exactly as:
package dev.caskeleton.adapter.outbound.mongo;
import static org.assertj.core.api.Assertions.assertThat;
import java.lang.reflect.Method;
import java.util.Arrays;
import org.junit.jupiter.api.Test;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
class MongoPersistenceOwnershipTest {
@Test
void configurationOwnsNoRepositoryScanOrExampleAdapterFactory() {
assertThat(
MongoPersistenceConfig.class.isAnnotationPresent(EnableMongoRepositories.class))
.isFalse();
assertThat(
Arrays.stream(MongoPersistenceConfig.class.getDeclaredMethods())
.map(Method::getName))
.doesNotContain("exampleMongoRepositoryAdapter");
}
}
Create MongoPersistenceEnabledModeTest.java exactly as:
package dev.caskeleton.adapter.outbound.mongo;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import com.mongodb.client.MongoClient;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.MongoTemplate;
class MongoPersistenceEnabledModeTest {
private final ApplicationContextRunner runner =
new ApplicationContextRunner()
.withUserConfiguration(MockMongoClientConfig.class, MongoPersistenceConfig.class)
.withPropertyValues(
"ca-skeleton.persistence-mongo.enabled=true",
"ca-skeleton.persistence-mongo.database=contract_db",
"spring.mongodb.database=contract_db");
@Test
void createsGenericMongoClientAndTemplateWhenEnabled() {
runner.run(
context -> {
assertThat(context)
.hasNotFailed()
.hasSingleBean(MongoClient.class)
.hasSingleBean(MongoTemplate.class);
assertThat(context.getBean(MongoPersistenceProperties.class).getDatabase())
.isEqualTo("contract_db");
});
}
@Configuration(proxyBeanMethods = false)
static class MockMongoClientConfig {
@Bean
MongoClient mongoClient() {
return mock(MongoClient.class);
}
}
}
The mock satisfies Boot 4's MongoClient back-off and lets DataMongoAutoConfiguration create a
real MongoTemplate without opening a socket.
Run:
./gradlew :adapter:outbound:persistence-mongo:test \
--tests '*MongoPersistencePropertiesBindingTest' \
--tests '*MongoPersistenceDisabledModeTest' \
--tests '*MongoPersistenceEnabledModeTest' \
--console=plain
./gradlew :adapter:outbound:persistence-mongo:test \
--tests '*MongoPersistenceOwnershipTest' \
--console=plain
Expected: the binding, disabled-mode, and mock-backed enabled-mode characterization tests pass
against the current opt-in behavior. The ownership test fails because current production config
declares @EnableMongoRepositories and exampleMongoRepositoryAdapter.
- Step 2: Make Mongo configuration generic
Replace MongoPersistenceConfig.java exactly with:
package dev.caskeleton.adapter.outbound.mongo;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.data.mongodb.autoconfigure.DataMongoAutoConfiguration;
import org.springframework.boot.mongodb.autoconfigure.MongoAutoConfiguration;
import org.springframework.context.annotation.Configuration;
/**
* Generic opt-in Mongo client and template configuration.
*
* Consumers own their document, repository, mapper, adapter, and repository-scan boundary.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(
prefix = "ca-skeleton.persistence-mongo",
name = "enabled",
havingValue = "true")
@EnableConfigurationProperties(MongoPersistenceProperties.class)
@ImportAutoConfiguration({MongoAutoConfiguration.class, DataMongoAutoConfiguration.class})
public class MongoPersistenceConfig {}
Replace MongoPersistenceProperties.java exactly with:
package dev.caskeleton.adapter.outbound.mongo;
import org.springframework.boot.context.properties.ConfigurationProperties;
/** Module-owned opt-in settings; connection settings remain under Spring Boot's Mongo properties. */
@ConfigurationProperties(prefix = "ca-skeleton.persistence-mongo")
public class MongoPersistenceProperties {
private boolean enabled;
private String database = "ca_skeleton";
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getDatabase() {
return database;
}
public void setDatabase(String database) {
this.database = database;
}
}
Delete the five Example* production files and their two tests. Do not copy them into
sample-portfolio.
Run:
./gradlew :adapter:outbound:persistence-mongo:test \
--tests '*MongoPersistenceOwnershipTest' \
--tests '*MongoPersistenceEnabledModeTest' \
--console=plain
Expected: the previously red ownership test passes and enabled mode still creates one generic
MongoClient plus one MongoTemplate without a network connection.
- Step 3: Remove now-unused Mongo core edges
Remove application-core and shared-contract from:
src/adapter/outbound/persistence-mongo/build.gradle- the Mongo entry's registry allowed dependencies
Remove the now-unused Testcontainers dependencies. The final dependency block is:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
Run:
./gradlew \
:adapter:outbound:persistence-mongo:compileJava \
:adapter:outbound:persistence-mongo:test \
verifyCleanArchitectureDependencies \
--console=plain
Expected: all tasks pass and production Mongo source contains only generic configuration/properties.
- Step 4: Remove and ignore jqwik runtime state
Delete src/sample-portfolio/.jqwik-database and add this exact rule to src/.gitignore:
.jqwik-database
Run:
git ls-files '*/.jqwik-database' '*.jqwik-database'
git check-ignore -v src/sample-portfolio/.jqwik-database
./gradlew :sample-portfolio:test --tests '*WorkLogIdPropertyTest' --console=plain
Expected: git ls-files prints nothing, git check-ignore names src/.gitignore, and the property
test passes without a committed database.
Task 9: Verify the addendum's architecture and runtime outcomes after graph cleanup
The exact implementation and test code for this task is owned by the approved recovered-control-plane addendum from Task 2. This task changes no control-plane file; it proves that the addendum remains green after dependency and Mongo cleanup.
Files:
-
Verify only:
.harness/project/modules.yaml -
Verify only:
src/build.gradle -
Verify only:
src/app-bootstrap/build.gradle -
Verify only:
src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/RegisteredLeafCoverageTest.java -
Verify only:
src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/RuntimeCompositionContractTest.java -
Verify only:
src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/SampleRemovalSmokeContractTest.java -
Verify only:
src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/ApplicationPackageScanContractTest.java -
Verify only:
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/CaSkeletonApplication.java -
Step 1: Re-run registered coverage, composition, sample isolation, and scan tests
Run:
./gradlew :app-bootstrap:test \
--tests '*RegisteredLeafCoverageTest' \
--tests '*RuntimeCompositionContractTest' \
--tests '*SampleRemovalSmokeContractTest' \
--tests '*ApplicationPackageScanContractTest' \
--console=plain
./gradlew :sample-portfolio:test \
--tests '*SampleApplicationContextTest' \
--console=plain
Expected: every test passes; all registered production leaves are analyzed; GraphQL, gRPC, WebSocket, fileserver, objectstorage, and Mongo remain absent from the default runtime; the sample context remains bootable with only bootstrap/adapter package scans.
- Step 2: Run the complete architecture suite
Run:
./gradlew \
verifyCleanArchitectureDependencies \
verifyExternalDependencyPurity \
:app-bootstrap:test --tests '*CleanArchitectureTest' \
--console=plain
Expected: all registered production leaves are analyzed and every gate passes.
Task 10: Centralize remaining version ownership and gate locks
Files:
-
Modify:
src/build.gradle -
Modify each leaf
build.gradlethat retains a non-BOM version:src/adapter/inbound/web/build.gradlesrc/adapter/outbound/httpclient/build.gradlesrc/adapter/outbound/identifier/build.gradlesrc/app-bootstrap/build.gradlesrc/sample-portfolio/build.gradle
-
Modify:
.github/workflows/ci-quality-gates.yml -
Step 1: Inventory remaining explicit versions
Run:
rg -n "['\"][A-Za-z0-9_.-]+:[A-Za-z0-9_.-]+:[^'\"]+['\"]" \
. -g 'build.gradle' | sort
Expected: one row per explicit non-project coordinate. BOM-managed coordinates have no version.
- Step 2: Give every remaining non-BOM library one root owner
Add this root map without changing a version:
ext.libraryVersions = [
jacksonDatabindNullable : '0.2.6',
springdoc : '2.8.6',
resilience4j : '2.2.0',
spock : '2.4-groovy-5.0',
uuidCreator : '6.1.1',
springDotenv : '4.0.0',
logstashEncoder : '8.0',
approvalTests : '31.0.0',
archunit : '1.3.0',
springCloudContextFixture: '4.1.4',
jqwik : '1.9.1'
]
Replace the matching leaf literals with ${libraryVersions.key} interpolation, for example:
implementation "org.openapitools:jackson-databind-nullable:${libraryVersions.jacksonDatabindNullable}"
implementation "org.springdoc:springdoc-openapi-starter-webmvc-api:${libraryVersions.springdoc}"
implementation "io.github.resilience4j:resilience4j-retry:${libraryVersions.resilience4j}"
testImplementation "org.spockframework:spock-core:${libraryVersions.spock}"
implementation "com.github.f4b6a3:uuid-creator:${libraryVersions.uuidCreator}"
implementation "me.paulschwarz:spring-dotenv:${libraryVersions.springDotenv}"
implementation "net.logstash.logback:logstash-logback-encoder:${libraryVersions.logstashEncoder}"
testImplementation "com.approvaltests:approvaltests:${libraryVersions.approvalTests}"
testImplementation "com.tngtech.archunit:archunit-junit5:${libraryVersions.archunit}"
testCompileOnly "org.springframework.cloud:spring-cloud-context:${libraryVersions.springCloudContextFixture}"
testImplementation "net.jqwik:jqwik:${libraryVersions.jqwik}"
Use the same resilience4j key for retry, circuit-breaker, and Micrometer coordinates. Keep
grpcVersion, protobufVersion, and awsSdkVersion as the existing BOM owners. The root-owned
FindSecBugs and Error Prone tool versions remain where they are. Do not add libs.versions.toml,
buildSrc, or a convention plugin.
Run:
./gradlew projects --console=plain
./gradlew compileJava compileTestJava compileTestGroovy --console=plain
Expected: both commands pass with no selected dependency version change.
- Step 3: Regenerate strict locks once after all graph changes
Run:
./gradlew resolveAndLockAll --write-locks --console=plain
./gradlew verifyDependencyLocks --console=plain
Expected: success; removed Spring/application, unused Groovy/Spock/UUID, example Mongo, and pruned project coordinates no longer appear in affected production configurations.
- Step 4: Make lock verification release-blocking
Add verifyDependencyLocks to the CI quality-gate job and its release-gate dependency chain. If
project check is the repository's single local release entrypoint after CI recovery, also make the
root/leaf check aggregation depend on verifyDependencyLocks exactly once.
Run:
lock_dry_run="$(./gradlew check --dry-run --console=plain)"
printf '%s\n' "${lock_dry_run}"
lock_task_count="$(
printf '%s\n' "${lock_dry_run}" \
| grep -Ec '^:verifyDependencyLocks([[:space:]]|$)'
)"
test "${lock_task_count}" -eq 1
Expected: the dry run succeeds and the explicit count assertion proves exactly one scheduled root
verifyDependencyLocks task.
Task 11: Synchronize module guidance with the verified graph
Files:
-
Modify:
src/application-core/CLAUDE.md -
Modify:
src/application-core/README.md -
Modify:
src/adapter/inbound/graphql/CLAUDE.md -
Modify:
src/adapter/inbound/graphql/README.md -
Modify:
src/adapter/inbound/grpc/CLAUDE.md -
Modify:
src/adapter/inbound/grpc/README.md -
Modify:
src/adapter/inbound/web/CLAUDE.md -
Modify:
src/adapter/inbound/web/README.md -
Modify:
src/adapter/inbound/websocket/CLAUDE.md -
Modify:
src/adapter/inbound/websocket/README.md -
Modify:
src/adapter/outbound/cache-redis/CLAUDE.md -
Modify:
src/adapter/outbound/cache-redis/README.md -
Modify:
src/adapter/outbound/httpclient/CLAUDE.md -
Modify:
src/adapter/outbound/httpclient/README.md -
Modify:
src/adapter/outbound/identifier/CLAUDE.md -
Modify:
src/adapter/outbound/identifier/README.md -
Modify:
src/adapter/outbound/messaging/CLAUDE.md -
Modify:
src/adapter/outbound/messaging/README.md -
Modify:
src/adapter/outbound/notification/CLAUDE.md -
Modify:
src/adapter/outbound/notification/README.md -
Modify:
src/adapter/outbound/persistence-jpa/CLAUDE.md -
Modify:
src/adapter/outbound/persistence-jpa/README.md -
Modify:
src/adapter/outbound/persistence-mongo/CLAUDE.md -
Modify:
src/adapter/outbound/persistence-mongo/README.md -
Modify:
src/adapter/outbound/support/CLAUDE.md -
Modify:
src/adapter/outbound/support/README.md -
Modify:
src/app-bootstrap/CLAUDE.md -
Modify:
src/app-bootstrap/README.md -
Modify:
src/sample-portfolio/CLAUDE.md -
Modify:
src/sample-portfolio/README.md -
Modify:
src/README.md -
Step 1: Remove stale dependency claims
Replace each changed leaf's production-project-dependency statement with the matching row below and
state immediately after it that .harness/project/modules.yaml is the SSOT:
application-core: domain-core, shared-contract; external production dependencies: none
adapter-inbound-graphql: shared-contract
adapter-inbound-grpc: shared-contract
adapter-inbound-web: application-core, shared-contract
adapter-inbound-websocket: domain-core
adapter-outbound-cache-redis: shared-contract, adapter-outbound-support
adapter-outbound-httpclient: shared-contract, adapter-outbound-support
adapter-outbound-identifier: application-core
adapter-outbound-messaging: application-core, shared-contract, adapter-outbound-support
adapter-outbound-notification: application-core, shared-contract, adapter-outbound-support
adapter-outbound-persistence-jpa: application-core, shared-contract
adapter-outbound-persistence-mongo: none
adapter-outbound-support: none
Delete any application-core sentence allowing Spring stereotypes, any identifier sentence claiming
a domain or uuid-creator dependency, and every Mongo Example* reference.
Use this exact Mongo responsibility paragraph in both Mongo documents:
This opt-in leaf owns generic Mongo client/template configuration and typed enablement properties.
Consumers own documents, repositories, mappers, repository adapters, and repository scanning.
The leaf contains no sample business model and has no production project dependency.
- Step 2: Document runtime and analysis classpaths separately
Add this exact glossary to src/app-bootstrap/README.md and link to it from
src/app-bootstrap/CLAUDE.md and src/README.md:
app-default: present in production boot runtime
opt-in: absent from production boot runtime until explicitly selected
architectureAnalysis: test-only coverage of every registered production leaf
sampleFixture: test-only sample analysis
Add this exact sentence to both sample documents:
Property-test discovery state (`.jqwik-database`) is generated locally, ignored, and never committed.
Do not copy the 19-leaf list into root prose; link to .harness/project/modules.yaml.
- Step 3: Verify guidance and executable commands
Run:
./gradlew verifyReadmeCommands --console=plain
rg -n 'spring-boot-starter.*application-core|sample-ticket|ExampleMongo|PRODUCTION_MODULES' \
AGENTS.md CLAUDE.md src docs/superpowers \
-g '*.md' -g '*.java' -g '*.gradle'
Expected: README command verification passes. The grep finds no active stale claim or hard-coded production-module list; historical evidence inside the approved design documents is allowed only when explicitly labeled as audit evidence.
Task 12: Run final verification and review
Files:
-
Verify all files changed by Tasks 2-11
-
Step 1: Run registry and harness validation
Run:
python3 -m unittest discover -s .harness/tests -v
python3 .harness/validators/validate_modules.py
Expected: all tests pass and validator reports exactly 19 valid leaves.
- Step 2: Run focused module tests for all 19 leaves
Run:
cd src
./gradlew \
:domain-core:test \
:application-core:test \
:shared-contract:test \
:adapter:inbound:web:test \
:adapter:inbound:graphql:test \
:adapter:inbound:grpc:test \
:adapter:inbound:websocket:test \
:adapter:outbound:support:test \
:adapter:outbound:cache-redis:test \
:adapter:outbound:fileserver:test \
:adapter:outbound:httpclient:test \
:adapter:outbound:identifier:test \
:adapter:outbound:messaging:test \
:adapter:outbound:notification:test \
:adapter:outbound:objectstorage:test \
:adapter:outbound:persistence-jpa:test \
:adapter:outbound:persistence-mongo:test \
:app-bootstrap:test \
:sample-portfolio:test \
--console=plain
Expected: all 19 tasks pass. Docker-dependent tests may be reported as skipped only through their
existing disabledWithoutDocker contract.
- Step 3: Run architecture, purity, composition, and lock gates
Run:
./gradlew \
verifyCleanArchitectureDependencies \
verifyExternalDependencyPurity \
verifyConfigurationPropertiesProcessor \
verifyDependencyLocks \
:app-bootstrap:sampleOffTest \
:app-bootstrap:test --tests '*CleanArchitectureTest' \
--console=plain
Expected: every gate passes; sample-off has no sample class, and architecture analysis covers all registered production leaves.
- Step 4: Run repository-wide release verification
Run:
./gradlew test --console=plain
./gradlew check --console=plain
git diff --check
git status --short
Expected: both Gradle commands pass, git diff --check prints nothing, and status contains only
authorized working-tree changes.
- Step 5: Perform human-only handoff review
Review the working-tree diff in this order:
1. registry topology and runtime membership
2. application/core external purity
3. architecture-analysis versus production runtime separation
4. leaf dependency and test-scope removals
5. Mongo/sample isolation and jqwik cleanup
6. lockfile/version-owner changes
7. module guidance parity
Expected: every removal has a matching compile/focused-test result, every static candidate that remained has a direct-use explanation, and no agent has staged or committed the changes.