85 lines
4.0 KiB
Markdown
85 lines
4.0 KiB
Markdown
# Verification Purity Refactoring Design
|
|
|
|
**Date:** 2026-08-02
|
|
**Status:** approved by the user's instruction to apply the P1/P2 review sequentially
|
|
**Scope:** stale traceable JAR verification/cleanup and public-path snapshot verification/update
|
|
|
|
## Context
|
|
|
|
Two root Gradle verification paths currently mutate files while they are expected to be safe gates:
|
|
|
|
- every `Jar` task deletes stale traceable archives in `doFirst`, and
|
|
`verifyNoStaleTraceableJars` depends on `cleanStaleTraceableJars`;
|
|
- `verifyPublicPathSnapshot` creates a missing snapshot and updates drift when
|
|
`-PapprovePublicPathChange` is supplied.
|
|
|
|
That makes `check` capable of hiding the state it is meant to detect. This batch restores the
|
|
standard contract: verification observes and fails, while explicitly named maintenance tasks own
|
|
writes.
|
|
|
|
## Considered Approaches
|
|
|
|
### Keep the root build logic in place and inspect source text in tests
|
|
|
|
This is the smallest diff, but a source assertion cannot prove task side effects. Rejected.
|
|
|
|
### Invoke the entire repository build from a copied checkout
|
|
|
|
This tests the actual root build but requires copying all 19 leaves and resolving every root plugin
|
|
for two small contracts. It is slow and couples the tests to unrelated configuration. Rejected.
|
|
|
|
### Extract only the two task concerns into applied Gradle scripts and exercise them with TestKit
|
|
|
|
Selected. The production root applies the same scripts that an isolated functional fixture uses.
|
|
The fixture observes exit status and filesystem state, so it proves behavior rather than source
|
|
shape. This is a bounded extraction required for testability, not the broad P2 root-build rewrite.
|
|
|
|
## Archive Hygiene Contract
|
|
|
|
`gradle/archive-hygiene.gradle` owns stale traceable archive discovery and the two root tasks:
|
|
|
|
- `verifyNoStaleTraceableJars` reports every stale archive and fails without deleting anything;
|
|
- `cleanStaleTraceableJars` deletes only names matching the traceable archive pattern for a known
|
|
`Jar` task and never deletes the current archive;
|
|
- normal `jar`/`bootJar` execution never performs cleanup.
|
|
|
|
The existing traceable version naming and manifest metadata remain unchanged.
|
|
|
|
## Public-Path Snapshot Contract
|
|
|
|
`gradle/public-path-snapshot.gradle` owns canonicalization and two root tasks:
|
|
|
|
- `verifyPublicPathSnapshot` fails when the env file or committed snapshot is missing, when content
|
|
drifts, or when the update-only approval property is passed to the verifier. It never creates
|
|
directories or writes files;
|
|
- `updatePublicPathSnapshot` requires `-PapprovePublicPathChange` and writes the canonical snapshot.
|
|
|
|
A clean-worktree requirement is intentionally not used: the normal update workflow necessarily has
|
|
an intentional `.env` change. Explicit task naming, the approval property, and the resulting diff
|
|
are the review boundary.
|
|
|
|
The canonical header names `updatePublicPathSnapshot`, so documentation and the committed snapshot
|
|
do not instruct users to mutate through a verification task.
|
|
|
|
## Testing
|
|
|
|
`BuildVerificationPurityContractTest` runs from an isolated `functionalTest` source set using Gradle
|
|
TestKit against temporary projects that apply the production scripts directly. Keeping TestKit off
|
|
the ordinary `testRuntimeClasspath` prevents Gradle's SLF4J provider from replacing Logback during
|
|
Spring tests. It proves:
|
|
|
|
1. a normal `jar` leaves a matching stale archive untouched;
|
|
2. verification fails and preserves the stale archive;
|
|
3. explicit cleanup deletes the stale archive but preserves the current archive;
|
|
4. missing/drifted public-path snapshots cause read-only failure;
|
|
5. the verifier rejects the update approval property;
|
|
6. only the explicit updater with approval creates or changes the snapshot.
|
|
|
|
## Non-Goals
|
|
|
|
- no change to archive naming, versions, manifests, production dependency versions, or project edges;
|
|
- only the new isolated functional-test configurations are added to `app-bootstrap/gradle.lockfile`;
|
|
- no public-path allow-list value change;
|
|
- no broad root Gradle convention-plugin migration;
|
|
- no staging, commit, amend, or push by an agent.
|