@@ -50,6 +50,97 @@ pitest {
5050 excludedGroups.set(setOf (" live-llm" , " live-mcp" ))
5151}
5252
53+ // #858 — supply-chain hygiene. After bumping a dependency, Gradle wrapper, or
54+ // plugin, run `./gradlew updateVerificationMetadata` (or `gradlew.bat ...` on
55+ // Windows) to refresh gradle/verification-metadata.xml.
56+ //
57+ // Why a Gradle task instead of a shell script: this works the same on macOS,
58+ // Linux, and Windows. `gradlew` itself is the cross-platform entry point.
59+ //
60+ // What it does: invokes a second `gradlew` process with the right
61+ // --write-verification-metadata flag and the task list that exercises every
62+ // classpath the build actually uses (the bare `help` task only resolves the
63+ // runtime classpath; plugin classpaths, test classpath, and Kotlin compiler
64+ // plugin classpaths get missed).
65+ //
66+ // Sources/javadoc jars are exempted via <trusted-artifacts> in the metadata
67+ // file — they're IDE-only, never on the runtime classpath.
68+ tasks.register(" updateVerificationMetadata" ) {
69+ description = " Regenerates gradle/verification-metadata.xml after a dependency or Gradle update."
70+ group = " verification"
71+
72+ doLast {
73+ val isWindows = System .getProperty(" os.name" ).lowercase().contains(" windows" )
74+ val gradlewCommand = if (isWindows) " gradlew.bat" else " ./gradlew"
75+
76+ val metadataFile = rootProject.file(" gradle/verification-metadata.xml" )
77+ require(metadataFile.exists()) {
78+ " gradle/verification-metadata.xml not found at ${metadataFile.absolutePath} "
79+ }
80+
81+ val backup = File .createTempFile(" verification-metadata" , " .bak" )
82+ metadataFile.copyTo(backup, overwrite = true )
83+ try {
84+ println (" → Snapshotted current metadata to ${backup.absolutePath} " )
85+ println (" → Regenerating with --write-verification-metadata sha256" )
86+ println (" (re-resolves the dependency graph; can take a few minutes" )
87+ println (" on a first run after a Gradle update.)" )
88+ println ()
89+
90+ val process = ProcessBuilder (
91+ gradlewCommand,
92+ " --write-verification-metadata" , " sha256" ,
93+ " --refresh-dependencies" ,
94+ " help" ,
95+ " :dependencies" , " --configuration" , " runtimeClasspath" ,
96+ " :buildEnvironment" ,
97+ " :compileKotlin" ,
98+ " :compileTestKotlin" ,
99+ )
100+ .directory(rootProject.projectDir)
101+ .inheritIO()
102+ .start()
103+ val exitCode = process.waitFor()
104+ if (exitCode != 0 ) {
105+ throw GradleException (" Gradle regeneration exited with code $exitCode " )
106+ }
107+
108+ // Defensive: confirm the trusted-artifacts block survived. Gradle's
109+ // merge behavior should preserve <configuration>, but if a future
110+ // Gradle version regresses we want a loud signal, not a silent
111+ // weakening of the verification posture.
112+ val regenerated = metadataFile.readText()
113+ if (! regenerated.contains(" trusted-artifacts" )) {
114+ println ()
115+ println (" ⚠ <trusted-artifacts> block is missing from the regenerated file." )
116+ println (" Restoring from backup. Investigate the regeneration step." )
117+ backup.copyTo(metadataFile, overwrite = true )
118+ throw GradleException (" regeneration stripped trusted-artifacts; aborted" )
119+ }
120+
121+ println ()
122+ println (" ─" .repeat(60 ))
123+ if (regenerated == backup.readText()) {
124+ println (" ✓ No changes — verification metadata is up to date." )
125+ } else {
126+ println (" → Metadata changed." )
127+ println ()
128+ println (" Review the diff:" )
129+ println (" git diff gradle/verification-metadata.xml" )
130+ println ()
131+ println (" If the new entries look reasonable (only artifacts you" )
132+ println (" expected to appear, with origin=\" Generated by Gradle\" ):" )
133+ println (" git add gradle/verification-metadata.xml" )
134+ println ()
135+ println (" If anything looks off:" )
136+ println (" git restore gradle/verification-metadata.xml" )
137+ }
138+ } finally {
139+ backup.delete()
140+ }
141+ }
142+ }
143+
53144tasks.register<Test >(" integrationTest" ) {
54145 description = " Runs integration tests that require a live LLM (Ollama)"
55146 group = " verification"
0 commit comments